GeometryBatchFactory.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. package jme3tools.optimize;
  2. import com.jme3.material.Material;
  3. import com.jme3.math.Matrix4f;
  4. import com.jme3.math.Transform;
  5. import com.jme3.math.Vector3f;
  6. import com.jme3.scene.Geometry;
  7. import com.jme3.scene.Mesh;
  8. import com.jme3.scene.Mesh.Mode;
  9. import com.jme3.scene.Node;
  10. import com.jme3.scene.Spatial;
  11. import com.jme3.scene.VertexBuffer;
  12. import com.jme3.scene.VertexBuffer.Format;
  13. import com.jme3.scene.VertexBuffer.Type;
  14. import com.jme3.scene.VertexBuffer.Usage;
  15. import com.jme3.scene.mesh.IndexBuffer;
  16. import com.jme3.scene.mesh.VirtualIndexBuffer;
  17. import com.jme3.scene.mesh.WrappedIndexBuffer;
  18. import com.jme3.util.BufferUtils;
  19. import com.jme3.util.IntMap.Entry;
  20. import java.nio.Buffer;
  21. import java.nio.FloatBuffer;
  22. import java.nio.ShortBuffer;
  23. import java.util.ArrayList;
  24. import java.util.Collection;
  25. import java.util.HashMap;
  26. import java.util.Iterator;
  27. import java.util.List;
  28. import java.util.Map;
  29. public class GeometryBatchFactory {
  30. private static void doTransformVerts(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) {
  31. Vector3f pos = new Vector3f();
  32. // offset is given in element units
  33. // convert to be in component units
  34. offset *= 3;
  35. for (int i = 0; i < inBuf.capacity() / 3; i++) {
  36. pos.x = inBuf.get(i * 3 + 0);
  37. pos.y = inBuf.get(i * 3 + 1);
  38. pos.z = inBuf.get(i * 3 + 2);
  39. transform.mult(pos, pos);
  40. outBuf.put(offset + i * 3 + 0, pos.x);
  41. outBuf.put(offset + i * 3 + 1, pos.y);
  42. outBuf.put(offset + i * 3 + 2, pos.z);
  43. }
  44. }
  45. private static void doTransformNorms(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) {
  46. Vector3f norm = new Vector3f();
  47. // offset is given in element units
  48. // convert to be in component units
  49. offset *= 3;
  50. for (int i = 0; i < inBuf.capacity() / 3; i++) {
  51. norm.x = inBuf.get(i * 3 + 0);
  52. norm.y = inBuf.get(i * 3 + 1);
  53. norm.z = inBuf.get(i * 3 + 2);
  54. transform.multNormal(norm, norm);
  55. outBuf.put(offset + i * 3 + 0, norm.x);
  56. outBuf.put(offset + i * 3 + 1, norm.y);
  57. outBuf.put(offset + i * 3 + 2, norm.z);
  58. }
  59. }
  60. /**
  61. * Merges all geometries in the collection into
  62. * the output mesh. Does not take into account materials.
  63. *
  64. * @param geometries
  65. * @param outMesh
  66. */
  67. public static void mergeGeometries(Collection<Geometry> geometries, Mesh outMesh) {
  68. int[] compsForBuf = new int[VertexBuffer.Type.values().length];
  69. Format[] formatForBuf = new Format[compsForBuf.length];
  70. int totalVerts = 0;
  71. int totalTris = 0;
  72. int totalLodLevels = 0;
  73. Mode mode = null;
  74. for (Geometry geom : geometries) {
  75. totalVerts += geom.getVertexCount();
  76. totalTris += geom.getTriangleCount();
  77. totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels());
  78. Mode listMode;
  79. int components;
  80. switch (geom.getMesh().getMode()) {
  81. case Points:
  82. listMode = Mode.Points;
  83. components = 1;
  84. break;
  85. case LineLoop:
  86. case LineStrip:
  87. case Lines:
  88. listMode = Mode.Lines;
  89. components = 2;
  90. break;
  91. case TriangleFan:
  92. case TriangleStrip:
  93. case Triangles:
  94. listMode = Mode.Triangles;
  95. components = 3;
  96. break;
  97. default:
  98. throw new UnsupportedOperationException();
  99. }
  100. for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()) {
  101. compsForBuf[entry.getKey()] = entry.getValue().getNumComponents();
  102. formatForBuf[entry.getKey()] = entry.getValue().getFormat();
  103. }
  104. if (mode != null && mode != listMode) {
  105. throw new UnsupportedOperationException("Cannot combine different"
  106. + " primitive types: " + mode + " != " + listMode);
  107. }
  108. mode = listMode;
  109. compsForBuf[Type.Index.ordinal()] = components;
  110. }
  111. outMesh.setMode(mode);
  112. if (totalVerts >= 65536) {
  113. // make sure we create an UnsignedInt buffer so
  114. // we can fit all of the meshes
  115. formatForBuf[Type.Index.ordinal()] = Format.UnsignedInt;
  116. } else {
  117. formatForBuf[Type.Index.ordinal()] = Format.UnsignedShort;
  118. }
  119. // generate output buffers based on retrieved info
  120. for (int i = 0; i < compsForBuf.length; i++) {
  121. if (compsForBuf[i] == 0) {
  122. continue;
  123. }
  124. Buffer data;
  125. if (i == Type.Index.ordinal()) {
  126. data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris);
  127. } else {
  128. data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts);
  129. }
  130. VertexBuffer vb = new VertexBuffer(Type.values()[i]);
  131. vb.setupData(Usage.Static, compsForBuf[i], formatForBuf[i], data);
  132. outMesh.setBuffer(vb);
  133. }
  134. int globalVertIndex = 0;
  135. int globalTriIndex = 0;
  136. for (Geometry geom : geometries) {
  137. Mesh inMesh = geom.getMesh();
  138. geom.computeWorldMatrix();
  139. Matrix4f worldMatrix = geom.getWorldMatrix();
  140. int geomVertCount = inMesh.getVertexCount();
  141. int geomTriCount = inMesh.getTriangleCount();
  142. for (int bufType = 0; bufType < compsForBuf.length; bufType++) {
  143. VertexBuffer inBuf = inMesh.getBuffer(Type.values()[bufType]);
  144. VertexBuffer outBuf = outMesh.getBuffer(Type.values()[bufType]);
  145. if (outBuf == null) {
  146. continue;
  147. }
  148. if (Type.Index.ordinal() == bufType) {
  149. int components = compsForBuf[bufType];
  150. IndexBuffer inIdx = inMesh.getIndicesAsList();
  151. IndexBuffer outIdx = outMesh.getIndexBuffer();
  152. for (int tri = 0; tri < geomTriCount; tri++) {
  153. for (int comp = 0; comp < components; comp++) {
  154. int idx = inIdx.get(tri * components + comp) + globalVertIndex;
  155. outIdx.put((globalTriIndex + tri) * components + comp, idx);
  156. }
  157. }
  158. } else if (Type.Position.ordinal() == bufType) {
  159. FloatBuffer inPos = (FloatBuffer) inBuf.getData();
  160. FloatBuffer outPos = (FloatBuffer) outBuf.getData();
  161. doTransformVerts(inPos, globalVertIndex, outPos, worldMatrix);
  162. } else if (Type.Normal.ordinal() == bufType || Type.Tangent.ordinal() == bufType) {
  163. FloatBuffer inPos = (FloatBuffer) inBuf.getData();
  164. FloatBuffer outPos = (FloatBuffer) outBuf.getData();
  165. doTransformNorms(inPos, globalVertIndex, outPos, worldMatrix);
  166. } else {
  167. for (int vert = 0; vert < geomVertCount; vert++) {
  168. int curGlobalVertIndex = globalVertIndex + vert;
  169. inBuf.copyElement(vert, outBuf, curGlobalVertIndex);
  170. }
  171. }
  172. }
  173. globalVertIndex += geomVertCount;
  174. globalTriIndex += geomTriCount;
  175. }
  176. }
  177. public static void makeLods(Collection<Geometry> geometries, Mesh outMesh) {
  178. int lodLevels = 0;
  179. int[] lodSize = null;
  180. int index = 0;
  181. for (Geometry g : geometries) {
  182. if (lodLevels == 0) {
  183. lodLevels = g.getMesh().getNumLodLevels();
  184. }
  185. if (lodSize == null) {
  186. lodSize = new int[lodLevels];
  187. }
  188. for (int i = 0; i < lodLevels; i++) {
  189. lodSize[i] += g.getMesh().getLodLevel(i).getData().capacity();
  190. //if( i == 0) System.out.println(index + " " +lodSize[i]);
  191. }
  192. index++;
  193. }
  194. int[][] lodData = new int[lodLevels][];
  195. for (int i = 0; i < lodLevels; i++) {
  196. lodData[i] = new int[lodSize[i]];
  197. }
  198. VertexBuffer[] lods = new VertexBuffer[lodLevels];
  199. int bufferPos[] = new int[lodLevels];
  200. //int index = 0;
  201. int numOfVertices = 0;
  202. int curGeom = 0;
  203. for (Geometry g : geometries) {
  204. if (numOfVertices == 0) {
  205. numOfVertices = g.getVertexCount();
  206. }
  207. for (int i = 0; i < lodLevels; i++) {
  208. ShortBuffer buffer = (ShortBuffer) g.getMesh().getLodLevel(i).getData();
  209. buffer.rewind();
  210. //System.out.println("buffer: " + buffer.capacity() + " limit: " + lodSize[i] + " " + index);
  211. for (int j = 0; j < buffer.capacity(); j++) {
  212. lodData[i][bufferPos[i] + j] = buffer.get() + numOfVertices * curGeom;
  213. //bufferPos[i]++;
  214. }
  215. bufferPos[i] += buffer.capacity();
  216. }
  217. curGeom++;
  218. }
  219. for (int i = 0; i < lodLevels; i++) {
  220. lods[i] = new VertexBuffer(Type.Index);
  221. lods[i].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(lodData[i]));
  222. }
  223. System.out.println(lods.length);
  224. outMesh.setLodLevels(lods);
  225. }
  226. public static List<Geometry> makeBatches(Collection<Geometry> geometries) {
  227. return makeBatches(geometries, false);
  228. }
  229. /**
  230. * Batches a collection of Geometries so that all with the same material get combined.
  231. * @param geometries The Geometries to combine
  232. * @return A List of newly created Geometries, each with a distinct material
  233. */
  234. public static List<Geometry> makeBatches(Collection<Geometry> geometries, boolean useLods) {
  235. ArrayList<Geometry> retVal = new ArrayList<Geometry>();
  236. HashMap<Material, List<Geometry>> matToGeom = new HashMap<Material, List<Geometry>>();
  237. for (Geometry geom : geometries) {
  238. List<Geometry> outList = matToGeom.get(geom.getMaterial());
  239. if (outList == null) {
  240. outList = new ArrayList<Geometry>();
  241. matToGeom.put(geom.getMaterial(), outList);
  242. }
  243. outList.add(geom);
  244. }
  245. int batchNum = 0;
  246. for (Map.Entry<Material, List<Geometry>> entry : matToGeom.entrySet()) {
  247. Material mat = entry.getKey();
  248. List<Geometry> geomsForMat = entry.getValue();
  249. Mesh mesh = new Mesh();
  250. mergeGeometries(geomsForMat, mesh);
  251. // lods
  252. if (useLods) {
  253. makeLods(geomsForMat, mesh);
  254. }
  255. mesh.updateCounts();
  256. mesh.updateBound();
  257. Geometry out = new Geometry("batch[" + (batchNum++) + "]", mesh);
  258. out.setMaterial(mat);
  259. retVal.add(out);
  260. }
  261. return retVal;
  262. }
  263. private static void gatherGeoms(Spatial scene, List<Geometry> geoms) {
  264. if (scene instanceof Node) {
  265. Node node = (Node) scene;
  266. for (Spatial child : node.getChildren()) {
  267. gatherGeoms(child, geoms);
  268. }
  269. } else if (scene instanceof Geometry) {
  270. geoms.add((Geometry) scene);
  271. }
  272. }
  273. /**
  274. * Optimizes a scene by combining Geometry with the same material.
  275. * All Geometries found in the scene are detached from their parent and
  276. * a new Node containing the optimized Geometries is attached.
  277. * @param scene The scene to optimize
  278. * @return The newly created optimized geometries attached to a node
  279. */
  280. public static Spatial optimize(Node scene) {
  281. return optimize(scene, false);
  282. }
  283. /**
  284. * Optimizes a scene by combining Geometry with the same material.
  285. * All Geometries found in the scene are detached from their parent and
  286. * a new Node containing the optimized Geometries is attached.
  287. * @param scene The scene to optimize
  288. * @param useLods true if you want the resulting geometry to keep lod information
  289. * @return The newly created optimized geometries attached to a node
  290. */
  291. public static Node optimize(Node scene, boolean useLods) {
  292. ArrayList<Geometry> geoms = new ArrayList<Geometry>();
  293. gatherGeoms(scene, geoms);
  294. List<Geometry> batchedGeoms = makeBatches(geoms, useLods);
  295. for (Geometry geom : batchedGeoms) {
  296. scene.attachChild(geom);
  297. }
  298. for (Iterator<Geometry> it = geoms.iterator(); it.hasNext();) {
  299. Geometry geometry = it.next();
  300. geometry.removeFromParent();
  301. }
  302. // Since the scene is returned unaltered the transform must be reset
  303. scene.setLocalTransform(Transform.IDENTITY);
  304. return scene;
  305. }
  306. public static void printMesh(Mesh mesh) {
  307. for (int bufType = 0; bufType < Type.values().length; bufType++) {
  308. VertexBuffer outBuf = mesh.getBuffer(Type.values()[bufType]);
  309. if (outBuf == null) {
  310. continue;
  311. }
  312. System.out.println(outBuf.getBufferType() + ": ");
  313. for (int vert = 0; vert < outBuf.getNumElements(); vert++) {
  314. String str = "[";
  315. for (int comp = 0; comp < outBuf.getNumComponents(); comp++) {
  316. Object val = outBuf.getElementComponent(vert, comp);
  317. outBuf.setElementComponent(vert, comp, val);
  318. val = outBuf.getElementComponent(vert, comp);
  319. str += val;
  320. if (comp != outBuf.getNumComponents() - 1) {
  321. str += ", ";
  322. }
  323. }
  324. str += "]";
  325. System.out.println(str);
  326. }
  327. System.out.println("------");
  328. }
  329. }
  330. public static void main(String[] args) {
  331. Mesh mesh = new Mesh();
  332. mesh.setBuffer(Type.Position, 3, new float[]{
  333. 0, 0, 0,
  334. 1, 0, 0,
  335. 1, 1, 0,
  336. 0, 1, 0
  337. });
  338. mesh.setBuffer(Type.Index, 2, new short[]{
  339. 0, 1,
  340. 1, 2,
  341. 2, 3,
  342. 3, 0
  343. });
  344. Geometry g1 = new Geometry("g1", mesh);
  345. ArrayList<Geometry> geoms = new ArrayList<Geometry>();
  346. geoms.add(g1);
  347. Mesh outMesh = new Mesh();
  348. mergeGeometries(geoms, outMesh);
  349. printMesh(outMesh);
  350. }
  351. }