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