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