Mesh.java revision e07694b24f7d12d72b084b6651356681ebd0efd6
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.renderscript; 18 19import java.util.Vector; 20 21import android.util.Log; 22 23/** 24 * <p>This class is a container for geometric data displayed with 25 * Renderscript. Internally, a mesh is a collection of allocations that 26 * represent vertex data (positions, normals, texture 27 * coordinates) and index data such as triangles and lines. </p> 28 * <p> 29 * Vertex data could either be interleaved within one 30 * allocation that is provided separately, as multiple allocation 31 * objects, or done as a combination of both. When a 32 * vertex channel name matches an input in the vertex program, 33 * Renderscript automatically connects the two together. 34 * </p> 35 * <p> 36 * Parts of the mesh can be rendered with either explicit 37 * index sets or primitive types. 38 * </p> 39 **/ 40public class Mesh extends BaseObj { 41 42 /** 43 * Describes the way mesh vertex data is interpreted when rendering 44 * 45 **/ 46 public enum Primitive { 47 /** 48 * Vertex data will be rendered as a series of points 49 */ 50 POINT (0), 51 /** 52 * Vertex pairs will be rendered as lines 53 */ 54 LINE (1), 55 /** 56 * Vertex data will be rendered as a connected line strip 57 */ 58 LINE_STRIP (2), 59 /** 60 * Vertices will be rendered as individual triangles 61 */ 62 TRIANGLE (3), 63 /** 64 * Vertices will be rendered as a connected triangle strip 65 * defined by the first three vertices with each additional 66 * triangle defined by a new vertex 67 */ 68 TRIANGLE_STRIP (4), 69 /** 70 * Vertices will be rendered as a sequence of triangles that all 71 * share first vertex as the origin 72 */ 73 TRIANGLE_FAN (5); 74 75 int mID; 76 Primitive(int id) { 77 mID = id; 78 } 79 } 80 81 Allocation[] mVertexBuffers; 82 Allocation[] mIndexBuffers; 83 Primitive[] mPrimitives; 84 85 Mesh(int id, RenderScript rs) { 86 super(id, rs); 87 } 88 89 /** 90 * @return number of allocations containing vertex data 91 * 92 **/ 93 public int getVertexAllocationCount() { 94 if(mVertexBuffers == null) { 95 return 0; 96 } 97 return mVertexBuffers.length; 98 } 99 /** 100 * @param slot index in the list of allocations to return 101 * @return vertex data allocation at the given index 102 * 103 **/ 104 public Allocation getVertexAllocation(int slot) { 105 return mVertexBuffers[slot]; 106 } 107 108 /** 109 * @return number of primitives or index sets in the mesh 110 * 111 **/ 112 public int getPrimitiveCount() { 113 if(mIndexBuffers == null) { 114 return 0; 115 } 116 return mIndexBuffers.length; 117 } 118 119 /** 120 * @param slot locaton within the list of index set allocation 121 * @return allocation containing primtive index data or null if 122 * the index data is not specified explicitly 123 * 124 **/ 125 public Allocation getIndexSetAllocation(int slot) { 126 return mIndexBuffers[slot]; 127 } 128 /** 129 * @param slot locaiton within the list of index set primitives 130 * @return index set primitive type 131 * 132 **/ 133 public Primitive getPrimitive(int slot) { 134 return mPrimitives[slot]; 135 } 136 137 @Override 138 void updateFromNative() { 139 super.updateFromNative(); 140 int vtxCount = mRS.nMeshGetVertexBufferCount(getID(mRS)); 141 int idxCount = mRS.nMeshGetIndexCount(getID(mRS)); 142 143 int[] vtxIDs = new int[vtxCount]; 144 int[] idxIDs = new int[idxCount]; 145 int[] primitives = new int[idxCount]; 146 147 mRS.nMeshGetVertices(getID(mRS), vtxIDs, vtxCount); 148 mRS.nMeshGetIndices(getID(mRS), idxIDs, primitives, idxCount); 149 150 mVertexBuffers = new Allocation[vtxCount]; 151 mIndexBuffers = new Allocation[idxCount]; 152 mPrimitives = new Primitive[idxCount]; 153 154 for(int i = 0; i < vtxCount; i ++) { 155 if(vtxIDs[i] != 0) { 156 mVertexBuffers[i] = new Allocation(vtxIDs[i], mRS, null, Allocation.USAGE_SCRIPT); 157 mVertexBuffers[i].updateFromNative(); 158 } 159 } 160 161 for(int i = 0; i < idxCount; i ++) { 162 if(idxIDs[i] != 0) { 163 mIndexBuffers[i] = new Allocation(idxIDs[i], mRS, null, Allocation.USAGE_SCRIPT); 164 mIndexBuffers[i].updateFromNative(); 165 } 166 mPrimitives[i] = Primitive.values()[primitives[i]]; 167 } 168 } 169 170 /** 171 * Mesh builder object. It starts empty and requires you to 172 * add the types necessary to create vertex and index 173 * allocations. 174 * 175 */ 176 public static class Builder { 177 RenderScript mRS; 178 int mUsage; 179 180 class Entry { 181 Type t; 182 Element e; 183 int size; 184 Primitive prim; 185 int usage; 186 } 187 188 int mVertexTypeCount; 189 Entry[] mVertexTypes; 190 Vector mIndexTypes; 191 192 /** 193 * Creates builder object 194 * @param rs Context to which the mesh will belong. 195 * @param usage specifies how the mesh allocations are to be 196 * handled, whether they need to be uploaded to a 197 * buffer on the gpu, maintain a cpu copy, etc 198 */ 199 public Builder(RenderScript rs, int usage) { 200 mRS = rs; 201 mUsage = usage; 202 mVertexTypeCount = 0; 203 mVertexTypes = new Entry[16]; 204 mIndexTypes = new Vector(); 205 } 206 207 /** 208 * @return internal index of the last vertex buffer type added to 209 * builder 210 **/ 211 public int getCurrentVertexTypeIndex() { 212 return mVertexTypeCount - 1; 213 } 214 215 /** 216 * @return internal index of the last index set added to the 217 * builder 218 **/ 219 public int getCurrentIndexSetIndex() { 220 return mIndexTypes.size() - 1; 221 } 222 223 /** 224 * Adds a vertex data type to the builder object 225 * 226 * @param t type of the vertex data allocation to be created 227 * 228 * @return this 229 **/ 230 public Builder addVertexType(Type t) throws IllegalStateException { 231 if (mVertexTypeCount >= mVertexTypes.length) { 232 throw new IllegalStateException("Max vertex types exceeded."); 233 } 234 235 mVertexTypes[mVertexTypeCount] = new Entry(); 236 mVertexTypes[mVertexTypeCount].t = t; 237 mVertexTypes[mVertexTypeCount].e = null; 238 mVertexTypeCount++; 239 return this; 240 } 241 242 /** 243 * Adds a vertex data type to the builder object 244 * 245 * @param e element describing the vertex data layout 246 * @param size number of elements in the buffer 247 * 248 * @return this 249 **/ 250 public Builder addVertexType(Element e, int size) throws IllegalStateException { 251 if (mVertexTypeCount >= mVertexTypes.length) { 252 throw new IllegalStateException("Max vertex types exceeded."); 253 } 254 255 mVertexTypes[mVertexTypeCount] = new Entry(); 256 mVertexTypes[mVertexTypeCount].t = null; 257 mVertexTypes[mVertexTypeCount].e = e; 258 mVertexTypes[mVertexTypeCount].size = size; 259 mVertexTypeCount++; 260 return this; 261 } 262 263 /** 264 * Adds an index set data type to the builder object 265 * 266 * @param t type of the index set data, could be null 267 * @param p primitive type 268 * 269 * @return this 270 **/ 271 public Builder addIndexSetType(Type t, Primitive p) { 272 Entry indexType = new Entry(); 273 indexType.t = t; 274 indexType.e = null; 275 indexType.size = 0; 276 indexType.prim = p; 277 mIndexTypes.addElement(indexType); 278 return this; 279 } 280 281 /** 282 * Adds an index set primitive type to the builder object 283 * 284 * @param p primitive type 285 * 286 * @return this 287 **/ 288 public Builder addIndexSetType(Primitive p) { 289 Entry indexType = new Entry(); 290 indexType.t = null; 291 indexType.e = null; 292 indexType.size = 0; 293 indexType.prim = p; 294 mIndexTypes.addElement(indexType); 295 return this; 296 } 297 298 /** 299 * Adds an index set data type to the builder object 300 * 301 * @param e element describing the index set data layout 302 * @param size number of elements in the buffer 303 * @param p primitive type 304 * 305 * @return this 306 **/ 307 public Builder addIndexSetType(Element e, int size, Primitive p) { 308 Entry indexType = new Entry(); 309 indexType.t = null; 310 indexType.e = e; 311 indexType.size = size; 312 indexType.prim = p; 313 mIndexTypes.addElement(indexType); 314 return this; 315 } 316 317 Type newType(Element e, int size) { 318 Type.Builder tb = new Type.Builder(mRS, e); 319 tb.setX(size); 320 return tb.create(); 321 } 322 323 /** 324 * Create a Mesh object from the current state of the builder 325 * 326 **/ 327 public Mesh create() { 328 mRS.validate(); 329 int[] vtx = new int[mVertexTypeCount]; 330 int[] idx = new int[mIndexTypes.size()]; 331 int[] prim = new int[mIndexTypes.size()]; 332 333 Allocation[] vertexBuffers = new Allocation[mVertexTypeCount]; 334 Allocation[] indexBuffers = new Allocation[mIndexTypes.size()]; 335 Primitive[] primitives = new Primitive[mIndexTypes.size()]; 336 337 for(int ct = 0; ct < mVertexTypeCount; ct ++) { 338 Allocation alloc = null; 339 Entry entry = mVertexTypes[ct]; 340 if (entry.t != null) { 341 alloc = Allocation.createTyped(mRS, entry.t, mUsage); 342 } else if(entry.e != null) { 343 alloc = Allocation.createSized(mRS, entry.e, entry.size, mUsage); 344 } 345 vertexBuffers[ct] = alloc; 346 vtx[ct] = alloc.getID(mRS); 347 } 348 349 for(int ct = 0; ct < mIndexTypes.size(); ct ++) { 350 Allocation alloc = null; 351 Entry entry = (Entry)mIndexTypes.elementAt(ct); 352 if (entry.t != null) { 353 alloc = Allocation.createTyped(mRS, entry.t, mUsage); 354 } else if(entry.e != null) { 355 alloc = Allocation.createSized(mRS, entry.e, entry.size, mUsage); 356 } 357 int allocID = (alloc == null) ? 0 : alloc.getID(mRS); 358 indexBuffers[ct] = alloc; 359 primitives[ct] = entry.prim; 360 361 idx[ct] = allocID; 362 prim[ct] = entry.prim.mID; 363 } 364 365 int id = mRS.nMeshCreate(vtx, idx, prim); 366 Mesh newMesh = new Mesh(id, mRS); 367 newMesh.mVertexBuffers = vertexBuffers; 368 newMesh.mIndexBuffers = indexBuffers; 369 newMesh.mPrimitives = primitives; 370 371 return newMesh; 372 } 373 } 374 375 /** 376 * Mesh builder object. It starts empty and requires the user to 377 * add all the vertex and index allocations that comprise the 378 * mesh 379 * 380 */ 381 public static class AllocationBuilder { 382 RenderScript mRS; 383 384 class Entry { 385 Allocation a; 386 Primitive prim; 387 } 388 389 int mVertexTypeCount; 390 Entry[] mVertexTypes; 391 392 Vector mIndexTypes; 393 394 public AllocationBuilder(RenderScript rs) { 395 mRS = rs; 396 mVertexTypeCount = 0; 397 mVertexTypes = new Entry[16]; 398 mIndexTypes = new Vector(); 399 } 400 401 /** 402 * @return internal index of the last vertex buffer type added to 403 * builder 404 **/ 405 public int getCurrentVertexTypeIndex() { 406 return mVertexTypeCount - 1; 407 } 408 409 /** 410 * @return internal index of the last index set added to the 411 * builder 412 **/ 413 public int getCurrentIndexSetIndex() { 414 return mIndexTypes.size() - 1; 415 } 416 417 /** 418 * Adds an allocation containing vertex buffer data to the 419 * builder 420 * 421 * @param a vertex data allocation 422 * 423 * @return this 424 **/ 425 public AllocationBuilder addVertexAllocation(Allocation a) throws IllegalStateException { 426 if (mVertexTypeCount >= mVertexTypes.length) { 427 throw new IllegalStateException("Max vertex types exceeded."); 428 } 429 430 mVertexTypes[mVertexTypeCount] = new Entry(); 431 mVertexTypes[mVertexTypeCount].a = a; 432 mVertexTypeCount++; 433 return this; 434 } 435 436 /** 437 * Adds an allocation containing index buffer data and index type 438 * to the builder 439 * 440 * @param a index set data allocation, could be null 441 * @param p index set primitive type 442 * 443 * @return this 444 **/ 445 public AllocationBuilder addIndexSetAllocation(Allocation a, Primitive p) { 446 Entry indexType = new Entry(); 447 indexType.a = a; 448 indexType.prim = p; 449 mIndexTypes.addElement(indexType); 450 return this; 451 } 452 453 /** 454 * Adds an index set type to the builder 455 * 456 * @param p index set primitive type 457 * 458 * @return this 459 **/ 460 public AllocationBuilder addIndexSetType(Primitive p) { 461 Entry indexType = new Entry(); 462 indexType.a = null; 463 indexType.prim = p; 464 mIndexTypes.addElement(indexType); 465 return this; 466 } 467 468 /** 469 * Create a Mesh object from the current state of the builder 470 * 471 **/ 472 public Mesh create() { 473 mRS.validate(); 474 475 int[] vtx = new int[mVertexTypeCount]; 476 int[] idx = new int[mIndexTypes.size()]; 477 int[] prim = new int[mIndexTypes.size()]; 478 479 Allocation[] indexBuffers = new Allocation[mIndexTypes.size()]; 480 Primitive[] primitives = new Primitive[mIndexTypes.size()]; 481 Allocation[] vertexBuffers = new Allocation[mVertexTypeCount]; 482 483 for(int ct = 0; ct < mVertexTypeCount; ct ++) { 484 Entry entry = mVertexTypes[ct]; 485 vertexBuffers[ct] = entry.a; 486 vtx[ct] = entry.a.getID(mRS); 487 } 488 489 for(int ct = 0; ct < mIndexTypes.size(); ct ++) { 490 Entry entry = (Entry)mIndexTypes.elementAt(ct); 491 int allocID = (entry.a == null) ? 0 : entry.a.getID(mRS); 492 indexBuffers[ct] = entry.a; 493 primitives[ct] = entry.prim; 494 495 idx[ct] = allocID; 496 prim[ct] = entry.prim.mID; 497 } 498 499 int id = mRS.nMeshCreate(vtx, idx, prim); 500 Mesh newMesh = new Mesh(id, mRS); 501 newMesh.mVertexBuffers = vertexBuffers; 502 newMesh.mIndexBuffers = indexBuffers; 503 newMesh.mPrimitives = primitives; 504 505 return newMesh; 506 } 507 } 508 509 /** 510 * Builder that allows creation of a mesh object point by point 511 * and triangle by triangle 512 * 513 **/ 514 public static class TriangleMeshBuilder { 515 float mVtxData[]; 516 int mVtxCount; 517 int mMaxIndex; 518 short mIndexData[]; 519 int mIndexCount; 520 RenderScript mRS; 521 Element mElement; 522 523 float mNX = 0; 524 float mNY = 0; 525 float mNZ = -1; 526 float mS0 = 0; 527 float mT0 = 0; 528 float mR = 1; 529 float mG = 1; 530 float mB = 1; 531 float mA = 1; 532 533 int mVtxSize; 534 int mFlags; 535 536 public static final int COLOR = 0x0001; 537 public static final int NORMAL = 0x0002; 538 public static final int TEXTURE_0 = 0x0100; 539 540 /** 541 * @param rs Context to which the mesh will belong. 542 * @param vtxSize specifies whether the vertex is a float2 or 543 * float3 544 * @param flags bitfield that is a combination of COLOR, NORMAL, 545 * and TEXTURE_0 that specifies what vertex data 546 * channels are present in the mesh 547 * 548 **/ 549 public TriangleMeshBuilder(RenderScript rs, int vtxSize, int flags) { 550 mRS = rs; 551 mVtxCount = 0; 552 mMaxIndex = 0; 553 mIndexCount = 0; 554 mVtxData = new float[128]; 555 mIndexData = new short[128]; 556 mVtxSize = vtxSize; 557 mFlags = flags; 558 559 if (vtxSize < 2 || vtxSize > 3) { 560 throw new IllegalArgumentException("Vertex size out of range."); 561 } 562 } 563 564 private void makeSpace(int count) { 565 if ((mVtxCount + count) >= mVtxData.length) { 566 float t[] = new float[mVtxData.length * 2]; 567 System.arraycopy(mVtxData, 0, t, 0, mVtxData.length); 568 mVtxData = t; 569 } 570 } 571 572 private void latch() { 573 if ((mFlags & COLOR) != 0) { 574 makeSpace(4); 575 mVtxData[mVtxCount++] = mR; 576 mVtxData[mVtxCount++] = mG; 577 mVtxData[mVtxCount++] = mB; 578 mVtxData[mVtxCount++] = mA; 579 } 580 if ((mFlags & TEXTURE_0) != 0) { 581 makeSpace(2); 582 mVtxData[mVtxCount++] = mS0; 583 mVtxData[mVtxCount++] = mT0; 584 } 585 if ((mFlags & NORMAL) != 0) { 586 makeSpace(4); 587 mVtxData[mVtxCount++] = mNX; 588 mVtxData[mVtxCount++] = mNY; 589 mVtxData[mVtxCount++] = mNZ; 590 mVtxData[mVtxCount++] = 0.0f; 591 } 592 mMaxIndex ++; 593 } 594 595 /** 596 * Adds a float2 vertex to the mesh 597 * 598 * @param x position x 599 * @param y position y 600 * 601 * @return this 602 * 603 **/ 604 public TriangleMeshBuilder addVertex(float x, float y) { 605 if (mVtxSize != 2) { 606 throw new IllegalStateException("add mistmatch with declared components."); 607 } 608 makeSpace(2); 609 mVtxData[mVtxCount++] = x; 610 mVtxData[mVtxCount++] = y; 611 latch(); 612 return this; 613 } 614 615 /** 616 * Adds a float3 vertex to the mesh 617 * 618 * @param x position x 619 * @param y position y 620 * @param z position z 621 * 622 * @return this 623 * 624 **/ 625 public TriangleMeshBuilder addVertex(float x, float y, float z) { 626 if (mVtxSize != 3) { 627 throw new IllegalStateException("add mistmatch with declared components."); 628 } 629 makeSpace(4); 630 mVtxData[mVtxCount++] = x; 631 mVtxData[mVtxCount++] = y; 632 mVtxData[mVtxCount++] = z; 633 mVtxData[mVtxCount++] = 1.0f; 634 latch(); 635 return this; 636 } 637 638 /** 639 * Sets the texture coordinate for the vertices that are added after this method call. 640 * 641 * @param s texture coordinate s 642 * @param t texture coordinate t 643 * 644 * @return this 645 **/ 646 public TriangleMeshBuilder setTexture(float s, float t) { 647 if ((mFlags & TEXTURE_0) == 0) { 648 throw new IllegalStateException("add mistmatch with declared components."); 649 } 650 mS0 = s; 651 mT0 = t; 652 return this; 653 } 654 655 /** 656 * Sets the normal vector for the vertices that are added after this method call. 657 * 658 * @param x normal vector x 659 * @param y normal vector y 660 * @param z normal vector z 661 * 662 * @return this 663 **/ 664 public TriangleMeshBuilder setNormal(float x, float y, float z) { 665 if ((mFlags & NORMAL) == 0) { 666 throw new IllegalStateException("add mistmatch with declared components."); 667 } 668 mNX = x; 669 mNY = y; 670 mNZ = z; 671 return this; 672 } 673 674 /** 675 * Sets the color for the vertices that are added after this method call. 676 * 677 * @param r red component 678 * @param g green component 679 * @param b blue component 680 * @param a alpha component 681 * 682 * @return this 683 **/ 684 public TriangleMeshBuilder setColor(float r, float g, float b, float a) { 685 if ((mFlags & COLOR) == 0) { 686 throw new IllegalStateException("add mistmatch with declared components."); 687 } 688 mR = r; 689 mG = g; 690 mB = b; 691 mA = a; 692 return this; 693 } 694 695 /** 696 * Adds a new triangle to the mesh builder 697 * 698 * @param idx1 index of the first vertex in the triangle 699 * @param idx2 index of the second vertex in the triangle 700 * @param idx3 index of the third vertex in the triangle 701 * 702 * @return this 703 **/ 704 public TriangleMeshBuilder addTriangle(int idx1, int idx2, int idx3) { 705 if((idx1 >= mMaxIndex) || (idx1 < 0) || 706 (idx2 >= mMaxIndex) || (idx2 < 0) || 707 (idx3 >= mMaxIndex) || (idx3 < 0)) { 708 throw new IllegalStateException("Index provided greater than vertex count."); 709 } 710 if ((mIndexCount + 3) >= mIndexData.length) { 711 short t[] = new short[mIndexData.length * 2]; 712 System.arraycopy(mIndexData, 0, t, 0, mIndexData.length); 713 mIndexData = t; 714 } 715 mIndexData[mIndexCount++] = (short)idx1; 716 mIndexData[mIndexCount++] = (short)idx2; 717 mIndexData[mIndexCount++] = (short)idx3; 718 return this; 719 } 720 721 /** 722 * Creates the mesh object from the current state of the builder 723 * 724 * @param uploadToBufferObject specifies whether the vertex data 725 * is to be uploaded into the buffer 726 * object indicating that it's likely 727 * not going to be modified and 728 * rendered many times. 729 * Alternatively, it indicates the 730 * mesh data will be updated 731 * frequently and remain in script 732 * accessible memory 733 * 734 **/ 735 public Mesh create(boolean uploadToBufferObject) { 736 Element.Builder b = new Element.Builder(mRS); 737 b.add(Element.createVector(mRS, 738 Element.DataType.FLOAT_32, 739 mVtxSize), "position"); 740 if ((mFlags & COLOR) != 0) { 741 b.add(Element.F32_4(mRS), "color"); 742 } 743 if ((mFlags & TEXTURE_0) != 0) { 744 b.add(Element.F32_2(mRS), "texture0"); 745 } 746 if ((mFlags & NORMAL) != 0) { 747 b.add(Element.F32_3(mRS), "normal"); 748 } 749 mElement = b.create(); 750 751 int usage = Allocation.USAGE_SCRIPT; 752 if (uploadToBufferObject) { 753 usage |= Allocation.USAGE_GRAPHICS_VERTEX; 754 } 755 756 Builder smb = new Builder(mRS, usage); 757 smb.addVertexType(mElement, mMaxIndex); 758 smb.addIndexSetType(Element.U16(mRS), mIndexCount, Primitive.TRIANGLE); 759 760 Mesh sm = smb.create(); 761 762 sm.getVertexAllocation(0).copy1DRangeFromUnchecked(0, mMaxIndex, mVtxData); 763 if(uploadToBufferObject) { 764 if (uploadToBufferObject) { 765 sm.getVertexAllocation(0).syncAll(Allocation.USAGE_SCRIPT); 766 } 767 } 768 769 sm.getIndexSetAllocation(0).copy1DRangeFromUnchecked(0, mIndexCount, mIndexData); 770 if (uploadToBufferObject) { 771 sm.getIndexSetAllocation(0).syncAll(Allocation.USAGE_SCRIPT); 772 } 773 774 return sm; 775 } 776 } 777} 778 779