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 17 package android.renderscript; 18 19 import java.util.Vector; 20 21 import 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 **/ 40 public 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()); 141 int idxCount = mRS.nMeshGetIndexCount(getID()); 142 143 int[] vtxIDs = new int[vtxCount]; 144 int[] idxIDs = new int[idxCount]; 145 int[] primitives = new int[idxCount]; 146 147 mRS.nMeshGetVertices(getID(), vtxIDs, vtxCount); 148 mRS.nMeshGetIndices(getID(), 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(); 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(); 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(); 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(); 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 short mIndexData[]; 518 int mIndexCount; 519 RenderScript mRS; 520 Element mElement; 521 522 float mNX = 0; 523 float mNY = 0; 524 float mNZ = -1; 525 float mS0 = 0; 526 float mT0 = 0; 527 float mR = 1; 528 float mG = 1; 529 float mB = 1; 530 float mA = 1; 531 532 int mVtxSize; 533 int mFlags; 534 535 public static final int COLOR = 0x0001; 536 public static final int NORMAL = 0x0002; 537 public static final int TEXTURE_0 = 0x0100; 538 539 /** 540 * @param rs Context to which the mesh will belong. 541 * @param vtxSize specifies whether the vertex is a float2 or 542 * float3 543 * @param flags bitfield that is a combination of COLOR, NORMAL, 544 * and TEXTURE_0 that specifies what vertex data 545 * channels are present in the mesh 546 * 547 **/ 548 public TriangleMeshBuilder(RenderScript rs, int vtxSize, int flags) { 549 mRS = rs; 550 mVtxCount = 0; 551 mIndexCount = 0; 552 mVtxData = new float[128]; 553 mIndexData = new short[128]; 554 mVtxSize = vtxSize; 555 mFlags = flags; 556 557 if (vtxSize < 2 || vtxSize > 3) { 558 throw new IllegalArgumentException("Vertex size out of range."); 559 } 560 } 561 562 private void makeSpace(int count) { 563 if ((mVtxCount + count) >= mVtxData.length) { 564 float t[] = new float[mVtxData.length * 2]; 565 System.arraycopy(mVtxData, 0, t, 0, mVtxData.length); 566 mVtxData = t; 567 } 568 } 569 570 private void latch() { 571 if ((mFlags & COLOR) != 0) { 572 makeSpace(4); 573 mVtxData[mVtxCount++] = mR; 574 mVtxData[mVtxCount++] = mG; 575 mVtxData[mVtxCount++] = mB; 576 mVtxData[mVtxCount++] = mA; 577 } 578 if ((mFlags & TEXTURE_0) != 0) { 579 makeSpace(2); 580 mVtxData[mVtxCount++] = mS0; 581 mVtxData[mVtxCount++] = mT0; 582 } 583 if ((mFlags & NORMAL) != 0) { 584 makeSpace(3); 585 mVtxData[mVtxCount++] = mNX; 586 mVtxData[mVtxCount++] = mNY; 587 mVtxData[mVtxCount++] = mNZ; 588 } 589 } 590 591 /** 592 * Adds a float2 vertex to the mesh 593 * 594 * @param x position x 595 * @param y position y 596 * 597 * @return this 598 * 599 **/ 600 public TriangleMeshBuilder addVertex(float x, float y) { 601 if (mVtxSize != 2) { 602 throw new IllegalStateException("add mistmatch with declared components."); 603 } 604 makeSpace(2); 605 mVtxData[mVtxCount++] = x; 606 mVtxData[mVtxCount++] = y; 607 latch(); 608 return this; 609 } 610 611 /** 612 * Adds a float3 vertex to the mesh 613 * 614 * @param x position x 615 * @param y position y 616 * @param z position z 617 * 618 * @return this 619 * 620 **/ 621 public TriangleMeshBuilder addVertex(float x, float y, float z) { 622 if (mVtxSize != 3) { 623 throw new IllegalStateException("add mistmatch with declared components."); 624 } 625 makeSpace(3); 626 mVtxData[mVtxCount++] = x; 627 mVtxData[mVtxCount++] = y; 628 mVtxData[mVtxCount++] = z; 629 latch(); 630 return this; 631 } 632 633 /** 634 * Sets the texture coordinate for the last added vertex 635 * 636 * @param s texture coordinate s 637 * @param t texture coordinate t 638 * 639 * @return this 640 **/ 641 public TriangleMeshBuilder setTexture(float s, float t) { 642 if ((mFlags & TEXTURE_0) == 0) { 643 throw new IllegalStateException("add mistmatch with declared components."); 644 } 645 mS0 = s; 646 mT0 = t; 647 return this; 648 } 649 650 /** 651 * Sets the normal vector for the last added vertex 652 * 653 * @param x normal vector x 654 * @param y normal vector y 655 * @param z normal vector z 656 * 657 * @return this 658 **/ 659 public TriangleMeshBuilder setNormal(float x, float y, float z) { 660 if ((mFlags & NORMAL) == 0) { 661 throw new IllegalStateException("add mistmatch with declared components."); 662 } 663 mNX = x; 664 mNY = y; 665 mNZ = z; 666 return this; 667 } 668 669 /** 670 * Sets the color for the last added vertex 671 * 672 * @param r red component 673 * @param g green component 674 * @param b blue component 675 * @param a alpha component 676 * 677 * @return this 678 **/ 679 public TriangleMeshBuilder setColor(float r, float g, float b, float a) { 680 if ((mFlags & COLOR) == 0) { 681 throw new IllegalStateException("add mistmatch with declared components."); 682 } 683 mR = r; 684 mG = g; 685 mB = b; 686 mA = a; 687 return this; 688 } 689 690 /** 691 * Adds a new triangle to the mesh builder 692 * 693 * @param idx1 index of the first vertex in the triangle 694 * @param idx2 index of the second vertex in the triangle 695 * @param idx3 index of the third vertex in the triangle 696 * 697 * @return this 698 **/ 699 public TriangleMeshBuilder addTriangle(int idx1, int idx2, int idx3) { 700 if((idx1 >= mVtxCount) || (idx1 < 0) || 701 (idx2 >= mVtxCount) || (idx2 < 0) || 702 (idx3 >= mVtxCount) || (idx3 < 0)) { 703 throw new IllegalStateException("Index provided greater than vertex count."); 704 } 705 if ((mIndexCount + 3) >= mIndexData.length) { 706 short t[] = new short[mIndexData.length * 2]; 707 System.arraycopy(mIndexData, 0, t, 0, mIndexData.length); 708 mIndexData = t; 709 } 710 mIndexData[mIndexCount++] = (short)idx1; 711 mIndexData[mIndexCount++] = (short)idx2; 712 mIndexData[mIndexCount++] = (short)idx3; 713 return this; 714 } 715 716 /** 717 * Creates the mesh object from the current state of the builder 718 * 719 * @param uploadToBufferObject specifies whether the vertex data 720 * is to be uploaded into the buffer 721 * object indicating that it's likely 722 * not going to be modified and 723 * rendered many times. 724 * Alternatively, it indicates the 725 * mesh data will be updated 726 * frequently and remain in script 727 * accessible memory 728 * 729 **/ 730 public Mesh create(boolean uploadToBufferObject) { 731 Element.Builder b = new Element.Builder(mRS); 732 int floatCount = mVtxSize; 733 b.add(Element.createVector(mRS, 734 Element.DataType.FLOAT_32, 735 mVtxSize), "position"); 736 if ((mFlags & COLOR) != 0) { 737 floatCount += 4; 738 b.add(Element.F32_4(mRS), "color"); 739 } 740 if ((mFlags & TEXTURE_0) != 0) { 741 floatCount += 2; 742 b.add(Element.F32_2(mRS), "texture0"); 743 } 744 if ((mFlags & NORMAL) != 0) { 745 floatCount += 3; 746 b.add(Element.F32_3(mRS), "normal"); 747 } 748 mElement = b.create(); 749 750 int usage = Allocation.USAGE_SCRIPT; 751 if (uploadToBufferObject) { 752 usage |= Allocation.USAGE_GRAPHICS_VERTEX; 753 } 754 755 Builder smb = new Builder(mRS, usage); 756 smb.addVertexType(mElement, mVtxCount / floatCount); 757 smb.addIndexSetType(Element.U16(mRS), mIndexCount, Primitive.TRIANGLE); 758 759 Mesh sm = smb.create(); 760 761 sm.getVertexAllocation(0).copy1DRangeFromUnchecked(0, mVtxCount / floatCount, mVtxData); 762 if(uploadToBufferObject) { 763 if (uploadToBufferObject) { 764 sm.getVertexAllocation(0).syncAll(Allocation.USAGE_SCRIPT); 765 } 766 } 767 768 sm.getIndexSetAllocation(0).copy1DRangeFromUnchecked(0, mIndexCount, mIndexData); 769 if (uploadToBufferObject) { 770 sm.getIndexSetAllocation(0).syncAll(Allocation.USAGE_SCRIPT); 771 } 772 773 return sm; 774 } 775 } 776 } 777 778