1 /*
2  * Copyright (c) 2009-2012 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 package jme3tools.optimize;
34 
35 import com.jme3.light.Light;
36 import com.jme3.scene.Geometry;
37 import com.jme3.scene.Mesh;
38 import com.jme3.scene.VertexBuffer;
39 import com.jme3.scene.VertexBuffer.Type;
40 import com.jme3.util.BufferUtils;
41 import com.jme3.util.IntMap;
42 import com.jme3.util.IntMap.Entry;
43 import java.nio.Buffer;
44 import java.nio.ShortBuffer;
45 import java.util.*;
46 
47 public class TriangleCollector {
48 
49     private static final GeomTriComparator comparator = new GeomTriComparator();
50 
51     private static class GeomTriComparator implements Comparator<OCTTriangle> {
compare(OCTTriangle a, OCTTriangle b)52         public int compare(OCTTriangle a, OCTTriangle b) {
53             if (a.getGeometryIndex() < b.getGeometryIndex()){
54                 return -1;
55             }else if (a.getGeometryIndex() > b.getGeometryIndex()){
56                 return 1;
57             }else{
58                 return 0;
59             }
60         }
61     }
62 
63     private static class Range {
64 
65         private int start, length;
66 
Range(int start, int length)67         public Range(int start, int length) {
68             this.start = start;
69             this.length = length;
70         }
71 
getLength()72         public int getLength() {
73             return length;
74         }
75 
setLength(int length)76         public void setLength(int length) {
77             this.length = length;
78         }
79 
getStart()80         public int getStart() {
81             return start;
82         }
83 
setStart(int start)84         public void setStart(int start) {
85             this.start = start;
86         }
87 
88     }
89 
90     /**
91      * Grabs all the triangles specified in <code>tris</code> from the input array
92      * (using the indices OCTTriangle.getGeometryIndex() & OCTTriangle.getTriangleIndex())
93      * then organizes them into output geometry.
94      *
95      * @param inGeoms
96      * @param tris
97      * @return
98      */
gatherTris(Geometry[] inGeoms, List<OCTTriangle> tris)99     public static final List<Geometry> gatherTris(Geometry[] inGeoms, List<OCTTriangle> tris){
100         Collections.sort(tris, comparator);
101         HashMap<Integer, Range> ranges = new HashMap<Integer, Range>();
102 
103         for (int i = 0; i < tris.size(); i++){
104             Range r = ranges.get(tris.get(i).getGeometryIndex());
105             if (r != null){
106                 // incremenet length
107                 r.setLength(r.getLength()+1);
108             }else{
109                 // set offset, length is 1
110                 ranges.put(tris.get(i).getGeometryIndex(), new Range(i, 1));
111             }
112         }
113 
114         List<Geometry> newGeoms = new ArrayList<Geometry>();
115         int[] vertIndicies = new int[3];
116         int[] newIndices = new int[3];
117         boolean[] vertexCreated = new boolean[3];
118         HashMap<Integer, Integer> indexCache = new HashMap<Integer, Integer>();
119         for (Map.Entry<Integer, Range> entry : ranges.entrySet()){
120             int inGeomIndex = entry.getKey().intValue();
121             int outOffset = entry.getValue().start;
122             int outLength = entry.getValue().length;
123 
124             Geometry inGeom = inGeoms[inGeomIndex];
125             Mesh in = inGeom.getMesh();
126             Mesh out = new Mesh();
127 
128             int outElementCount = outLength * 3;
129             ShortBuffer ib = BufferUtils.createShortBuffer(outElementCount);
130             out.setBuffer(Type.Index, 3, ib);
131 
132             // generate output buffers based on input buffers
133             IntMap<VertexBuffer> bufs = in.getBuffers();
134             for (Entry<VertexBuffer> ent : bufs){
135                 VertexBuffer vb = ent.getValue();
136                 if (vb.getBufferType() == Type.Index)
137                     continue;
138 
139                 // NOTE: we are not actually sure
140                 // how many elements will be in this buffer.
141                 // It will be compacted later.
142                 Buffer b = VertexBuffer.createBuffer(vb.getFormat(),
143                                                      vb.getNumComponents(),
144                                                      outElementCount);
145 
146                 VertexBuffer outVb = new VertexBuffer(vb.getBufferType());
147                 outVb.setNormalized(vb.isNormalized());
148                 outVb.setupData(vb.getUsage(), vb.getNumComponents(), vb.getFormat(), b);
149                 out.setBuffer(outVb);
150             }
151 
152             int currentVertex = 0;
153             for (int i = outOffset; i < outOffset + outLength; i++){
154                 OCTTriangle t = tris.get(i);
155 
156                 // find vertex indices for triangle t
157                 in.getTriangle(t.getTriangleIndex(), vertIndicies);
158 
159                 // find indices in new buf
160                 Integer i0 = indexCache.get(vertIndicies[0]);
161                 Integer i1 = indexCache.get(vertIndicies[1]);
162                 Integer i2 = indexCache.get(vertIndicies[2]);
163 
164                 // check which ones were not created
165                 // if not created in new IB, create them
166                 if (i0 == null){
167                     vertexCreated[0] = true;
168                     newIndices[0] = currentVertex++;
169                     indexCache.put(vertIndicies[0], newIndices[0]);
170                 }else{
171                     newIndices[0] = i0.intValue();
172                     vertexCreated[0] = false;
173                 }
174                 if (i1 == null){
175                     vertexCreated[1] = true;
176                     newIndices[1] = currentVertex++;
177                     indexCache.put(vertIndicies[1], newIndices[1]);
178                 }else{
179                     newIndices[1] = i1.intValue();
180                     vertexCreated[1] = false;
181                 }
182                 if (i2 == null){
183                     vertexCreated[2] = true;
184                     newIndices[2] = currentVertex++;
185                     indexCache.put(vertIndicies[2], newIndices[2]);
186                 }else{
187                     newIndices[2] = i2.intValue();
188                     vertexCreated[2] = false;
189                 }
190 
191                 // if any verticies were created for this triangle
192                 // copy them to the output mesh
193                 IntMap<VertexBuffer> inbufs = in.getBuffers();
194                 for (Entry<VertexBuffer> ent : inbufs){
195                     VertexBuffer vb = ent.getValue();
196                     if (vb.getBufferType() == Type.Index)
197                         continue;
198 
199                     VertexBuffer outVb = out.getBuffer(vb.getBufferType());
200                     // copy verticies that were created for this triangle
201                     for (int v = 0; v < 3; v++){
202                         if (!vertexCreated[v])
203                             continue;
204 
205                         // copy triangle's attribute from one
206                         // buffer to another
207                         vb.copyElement(vertIndicies[v], outVb, newIndices[v]);
208                     }
209                 }
210 
211                 // write the indices onto the output index buffer
212                 ib.put((short)newIndices[0])
213                   .put((short)newIndices[1])
214                   .put((short)newIndices[2]);
215             }
216             ib.clear();
217             indexCache.clear();
218 
219             // since some verticies were cached, it means there's
220             // extra data in some buffers
221             IntMap<VertexBuffer> outbufs = out.getBuffers();
222             for (Entry<VertexBuffer> ent : outbufs){
223                 VertexBuffer vb = ent.getValue();
224                 if (vb.getBufferType() == Type.Index)
225                     continue;
226 
227                 vb.compact(currentVertex);
228             }
229 
230             out.updateBound();
231             out.updateCounts();
232             out.setStatic();
233             //out.setInterleaved();
234             Geometry outGeom = new Geometry("Geom"+entry.getKey(), out);
235             outGeom.setLocalTransform(inGeom.getWorldTransform());
236             outGeom.setMaterial(inGeom.getMaterial());
237             for (Light light : inGeom.getWorldLightList()){
238                 outGeom.addLight(light);
239             }
240 
241             outGeom.updateGeometricState();
242             newGeoms.add(outGeom);
243         }
244 
245         return newGeoms;
246     }
247 
248 }
249