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