BatchNode.java 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  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. package com.jme3.scene;
  33. import com.jme3.export.*;
  34. import com.jme3.material.Material;
  35. import com.jme3.math.Matrix4f;
  36. import com.jme3.math.Transform;
  37. import com.jme3.math.Vector3f;
  38. import com.jme3.scene.mesh.IndexBuffer;
  39. import com.jme3.util.SafeArrayList;
  40. import com.jme3.util.TempVars;
  41. import java.io.IOException;
  42. import java.nio.Buffer;
  43. import java.nio.FloatBuffer;
  44. import java.util.ArrayList;
  45. import java.util.HashMap;
  46. import java.util.List;
  47. import java.util.Map;
  48. import java.util.logging.Level;
  49. import java.util.logging.Logger;
  50. /**
  51. * BatchNode holds geometries that are a batched version of all the geometries that are in its sub scenegraph.
  52. * There is one geometry per different material in the sub tree.
  53. * The geometries are directly attached to the node in the scene graph.
  54. * Usage is like any other node except you have to call the {@link #batch()} method once all the geometries have been attached to the sub scene graph and their material set
  55. * (see todo more automagic for further enhancements)
  56. * All the geometries that have been batched are set to {@link CullHint#Always} to not render them.
  57. * The sub geometries can be transformed as usual, their transforms are used to update the mesh of the geometryBatch.
  58. * Sub geoms can be removed but it may be slower than the normal spatial removing
  59. * Sub geoms can be added after the batch() method has been called but won't be batched and will just be rendered as normal geometries.
  60. * To integrate them in the batch you have to call the batch() method again on the batchNode.
  61. *
  62. * TODO normal or tangents or both looks a bit weird
  63. * TODO more automagic (batch when needed in the updateLogicalState)
  64. * @author Nehon
  65. */
  66. public class BatchNode extends Node implements Savable {
  67. private static final Logger logger = Logger.getLogger(BatchNode.class.getName());
  68. /**
  69. * the list of geometry holding the batched meshes
  70. */
  71. protected SafeArrayList<Batch> batches = new SafeArrayList<Batch>(Batch.class);
  72. /**
  73. * a map storing he batches by geometry to quickly acces the batch when updating
  74. */
  75. protected Map<Geometry, Batch> batchesByGeom = new HashMap<Geometry, Batch>();
  76. /**
  77. * used to store transformed vectors before proceeding to a bulk put into the FloatBuffer
  78. */
  79. private float[] tmpFloat;
  80. private float[] tmpFloatN;
  81. private float[] tmpFloatT;
  82. int maxVertCount = 0;
  83. boolean useTangents = false;
  84. boolean needsFullRebatch = true;
  85. /**
  86. * Construct a batchNode
  87. */
  88. public BatchNode() {
  89. super();
  90. }
  91. public BatchNode(String name) {
  92. super(name);
  93. }
  94. @Override
  95. public void updateGeometricState() {
  96. if ((refreshFlags & RF_LIGHTLIST) != 0) {
  97. updateWorldLightList();
  98. }
  99. if ((refreshFlags & RF_TRANSFORM) != 0) {
  100. // combine with parent transforms- same for all spatial
  101. // subclasses.
  102. updateWorldTransforms();
  103. }
  104. if (!children.isEmpty()) {
  105. // the important part- make sure child geometric state is refreshed
  106. // first before updating own world bound. This saves
  107. // a round-trip later on.
  108. // NOTE 9/19/09
  109. // Although it does save a round trip,
  110. for (Spatial child : children.getArray()) {
  111. child.updateGeometricState();
  112. }
  113. for (Batch batch : batches.getArray()) {
  114. if (batch.needMeshUpdate) {
  115. batch.geometry.getMesh().updateBound();
  116. batch.geometry.updateWorldBound();
  117. batch.needMeshUpdate = false;
  118. }
  119. }
  120. }
  121. if ((refreshFlags & RF_BOUND) != 0) {
  122. updateWorldBound();
  123. }
  124. assert refreshFlags == 0;
  125. }
  126. protected Transform getTransforms(Geometry geom) {
  127. return geom.getWorldTransform();
  128. }
  129. protected void updateSubBatch(Geometry bg) {
  130. Batch batch = batchesByGeom.get(bg);
  131. if (batch != null) {
  132. Mesh mesh = batch.geometry.getMesh();
  133. VertexBuffer pvb = mesh.getBuffer(VertexBuffer.Type.Position);
  134. FloatBuffer posBuf = (FloatBuffer) pvb.getData();
  135. VertexBuffer nvb = mesh.getBuffer(VertexBuffer.Type.Normal);
  136. FloatBuffer normBuf = (FloatBuffer) nvb.getData();
  137. if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) {
  138. VertexBuffer tvb = mesh.getBuffer(VertexBuffer.Type.Tangent);
  139. FloatBuffer tanBuf = (FloatBuffer) tvb.getData();
  140. doTransformsTangents(posBuf, normBuf, tanBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), bg.cachedOffsetMat);
  141. tvb.updateData(tanBuf);
  142. } else {
  143. doTransforms(posBuf, normBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), bg.cachedOffsetMat);
  144. }
  145. pvb.updateData(posBuf);
  146. nvb.updateData(normBuf);
  147. batch.needMeshUpdate = true;
  148. }
  149. }
  150. /**
  151. * Batch this batchNode
  152. * every geometry of the sub scene graph of this node will be batched into a single mesh that will be rendered in one call
  153. */
  154. public void batch() {
  155. doBatch();
  156. //we set the batch geometries to ignore transforms to avoid transforms of parent nodes to be applied twice
  157. for (Batch batch : batches.getArray()) {
  158. batch.geometry.setIgnoreTransform(true);
  159. }
  160. }
  161. protected void doBatch() {
  162. Map<Material, List<Geometry>> matMap = new HashMap<Material, List<Geometry>>();
  163. maxVertCount = 0;
  164. int nbGeoms = 0;
  165. gatherGeomerties(matMap, this, needsFullRebatch);
  166. if (needsFullRebatch) {
  167. for (Batch batch : batches.getArray()) {
  168. batch.geometry.removeFromParent();
  169. }
  170. batches.clear();
  171. batchesByGeom.clear();
  172. }
  173. for (Map.Entry<Material, List<Geometry>> entry : matMap.entrySet()) {
  174. Mesh m = new Mesh();
  175. Material material = entry.getKey();
  176. List<Geometry> list = entry.getValue();
  177. nbGeoms += list.size();
  178. String batchName = name + "-batch" + batches.size();
  179. Batch batch;
  180. if (!needsFullRebatch) {
  181. batch = findBatchByMaterial(material);
  182. if (batch != null) {
  183. list.add(0, batch.geometry);
  184. batchName = batch.geometry.getName();
  185. batch.geometry.removeFromParent();
  186. } else {
  187. batch = new Batch();
  188. }
  189. } else {
  190. batch = new Batch();
  191. }
  192. mergeGeometries(m, list);
  193. m.setDynamic();
  194. batch.updateGeomList(list);
  195. batch.geometry = new Geometry(batchName);
  196. batch.geometry.setMaterial(material);
  197. this.attachChild(batch.geometry);
  198. batch.geometry.setMesh(m);
  199. batch.geometry.getMesh().updateCounts();
  200. batch.geometry.getMesh().updateBound();
  201. batches.add(batch);
  202. }
  203. if (batches.size() > 0) {
  204. needsFullRebatch = false;
  205. }
  206. logger.log(Level.INFO, "Batched {0} geometries in {1} batches.", new Object[]{nbGeoms, batches.size()});
  207. //init temp float arrays
  208. tmpFloat = new float[maxVertCount * 3];
  209. tmpFloatN = new float[maxVertCount * 3];
  210. if (useTangents) {
  211. tmpFloatT = new float[maxVertCount * 4];
  212. }
  213. }
  214. private void gatherGeomerties(Map<Material, List<Geometry>> map, Spatial n, boolean rebatch) {
  215. if (n.getClass() == Geometry.class) {
  216. if (!isBatch(n) && n.getBatchHint() != BatchHint.Never) {
  217. Geometry g = (Geometry) n;
  218. if (!g.isBatched() || rebatch) {
  219. if (g.getMaterial() == null) {
  220. throw new IllegalStateException("No material is set for Geometry: " + g.getName() + " please set a material before batching");
  221. }
  222. List<Geometry> list = map.get(g.getMaterial());
  223. if (list == null) {
  224. //trying to compare materials with the isEqual method
  225. for (Map.Entry<Material, List<Geometry>> mat : map.entrySet()) {
  226. if (g.getMaterial().contentEquals(mat)) {
  227. list = mat.getValue();
  228. }
  229. }
  230. }
  231. if (list == null) {
  232. list = new ArrayList<Geometry>();
  233. map.put(g.getMaterial(), list);
  234. }
  235. g.setTransformRefresh();
  236. list.add(g);
  237. }
  238. }
  239. } else if (n instanceof Node) {
  240. for (Spatial child : ((Node) n).getChildren()) {
  241. if (child instanceof BatchNode) {
  242. continue;
  243. }
  244. gatherGeomerties(map, child, rebatch);
  245. }
  246. }
  247. }
  248. private Batch findBatchByMaterial(Material m) {
  249. for (Batch batch : batches.getArray()) {
  250. if (batch.geometry.getMaterial().contentEquals(m)) {
  251. return batch;
  252. }
  253. }
  254. return null;
  255. }
  256. private boolean isBatch(Spatial s) {
  257. for (Batch batch : batches.getArray()) {
  258. if (batch.geometry == s) {
  259. return true;
  260. }
  261. }
  262. return false;
  263. }
  264. /**
  265. * Sets the material to the all the batches of this BatchNode
  266. * use setMaterial(Material material,int batchIndex) to set a material to a specific batch
  267. *
  268. * @param material the material to use for this geometry
  269. */
  270. @Override
  271. public void setMaterial(Material material) {
  272. // for (Batch batch : batches.values()) {
  273. // batch.geometry.setMaterial(material);
  274. // }
  275. throw new UnsupportedOperationException("Unsupported for now, please set the material on the geoms before batching");
  276. }
  277. /**
  278. * Returns the material that is used for the first batch of this BatchNode
  279. *
  280. * use getMaterial(Material material,int batchIndex) to get a material from a specific batch
  281. *
  282. * @return the material that is used for the first batch of this BatchNode
  283. *
  284. * @see #setMaterial(com.jme3.material.Material)
  285. */
  286. public Material getMaterial() {
  287. if (!batches.isEmpty()) {
  288. Batch b = batches.iterator().next();
  289. return b.geometry.getMaterial();
  290. }
  291. return null;//material;
  292. }
  293. // /**
  294. // * Sets the material to the a specific batch of this BatchNode
  295. // *
  296. // *
  297. // * @param material the material to use for this geometry
  298. // */
  299. // public void setMaterial(Material material,int batchIndex) {
  300. // if (!batches.isEmpty()) {
  301. //
  302. // }
  303. //
  304. // }
  305. //
  306. // /**
  307. // * Returns the material that is used for the first batch of this BatchNode
  308. // *
  309. // * use getMaterial(Material material,int batchIndex) to get a material from a specific batch
  310. // *
  311. // * @return the material that is used for the first batch of this BatchNode
  312. // *
  313. // * @see #setMaterial(com.jme3.material.Material)
  314. // */
  315. // public Material getMaterial(int batchIndex) {
  316. // if (!batches.isEmpty()) {
  317. // Batch b = batches.get(batches.keySet().iterator().next());
  318. // return b.geometry.getMaterial();
  319. // }
  320. // return null;//material;
  321. // }
  322. @Override
  323. public void write(JmeExporter ex) throws IOException {
  324. super.write(ex);
  325. OutputCapsule oc = ex.getCapsule(this);
  326. //
  327. // if (material != null) {
  328. // oc.write(material.getAssetName(), "materialName", null);
  329. // }
  330. // oc.write(material, "material", null);
  331. }
  332. @Override
  333. public void read(JmeImporter im) throws IOException {
  334. super.read(im);
  335. InputCapsule ic = im.getCapsule(this);
  336. // material = null;
  337. // String matName = ic.readString("materialName", null);
  338. // if (matName != null) {
  339. // // Material name is set,
  340. // // Attempt to load material via J3M
  341. // try {
  342. // material = im.getAssetManager().loadMaterial(matName);
  343. // } catch (AssetNotFoundException ex) {
  344. // // Cannot find J3M file.
  345. // logger.log(Level.FINE, "Could not load J3M file {0} for Geometry.",
  346. // matName);
  347. // }
  348. // }
  349. // // If material is NULL, try to load it from the geometry
  350. // if (material == null) {
  351. // material = (Material) ic.readSavable("material", null);
  352. // }
  353. }
  354. /**
  355. * Merges all geometries in the collection into
  356. * the output mesh. Does not take into account materials.
  357. *
  358. * @param geometries
  359. * @param outMesh
  360. */
  361. private void mergeGeometries(Mesh outMesh, List<Geometry> geometries) {
  362. int[] compsForBuf = new int[VertexBuffer.Type.values().length];
  363. VertexBuffer.Format[] formatForBuf = new VertexBuffer.Format[compsForBuf.length];
  364. int totalVerts = 0;
  365. int totalTris = 0;
  366. int totalLodLevels = 0;
  367. Mesh.Mode mode = null;
  368. for (Geometry geom : geometries) {
  369. totalVerts += geom.getVertexCount();
  370. totalTris += geom.getTriangleCount();
  371. totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels());
  372. if (maxVertCount < geom.getVertexCount()) {
  373. maxVertCount = geom.getVertexCount();
  374. }
  375. Mesh.Mode listMode;
  376. int components;
  377. switch (geom.getMesh().getMode()) {
  378. case Points:
  379. listMode = Mesh.Mode.Points;
  380. components = 1;
  381. break;
  382. case LineLoop:
  383. case LineStrip:
  384. case Lines:
  385. listMode = Mesh.Mode.Lines;
  386. components = 2;
  387. break;
  388. case TriangleFan:
  389. case TriangleStrip:
  390. case Triangles:
  391. listMode = Mesh.Mode.Triangles;
  392. components = 3;
  393. break;
  394. default:
  395. throw new UnsupportedOperationException();
  396. }
  397. for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) {
  398. compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents();
  399. formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat();
  400. }
  401. if (mode != null && mode != listMode) {
  402. throw new UnsupportedOperationException("Cannot combine different"
  403. + " primitive types: " + mode + " != " + listMode);
  404. }
  405. mode = listMode;
  406. compsForBuf[VertexBuffer.Type.Index.ordinal()] = components;
  407. }
  408. outMesh.setMode(mode);
  409. if (totalVerts >= 65536) {
  410. // make sure we create an UnsignedInt buffer so
  411. // we can fit all of the meshes
  412. formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedInt;
  413. } else {
  414. formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedShort;
  415. }
  416. // generate output buffers based on retrieved info
  417. for (int i = 0; i < compsForBuf.length; i++) {
  418. if (compsForBuf[i] == 0) {
  419. continue;
  420. }
  421. Buffer data;
  422. if (i == VertexBuffer.Type.Index.ordinal()) {
  423. data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris);
  424. } else {
  425. data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts);
  426. }
  427. VertexBuffer vb = new VertexBuffer(VertexBuffer.Type.values()[i]);
  428. vb.setupData(VertexBuffer.Usage.Dynamic, compsForBuf[i], formatForBuf[i], data);
  429. outMesh.setBuffer(vb);
  430. }
  431. int globalVertIndex = 0;
  432. int globalTriIndex = 0;
  433. for (Geometry geom : geometries) {
  434. Mesh inMesh = geom.getMesh();
  435. if (!isBatch(geom)) {
  436. geom.batch(this, globalVertIndex);
  437. }
  438. int geomVertCount = inMesh.getVertexCount();
  439. int geomTriCount = inMesh.getTriangleCount();
  440. for (int bufType = 0; bufType < compsForBuf.length; bufType++) {
  441. VertexBuffer inBuf = inMesh.getBuffer(VertexBuffer.Type.values()[bufType]);
  442. VertexBuffer outBuf = outMesh.getBuffer(VertexBuffer.Type.values()[bufType]);
  443. if (outBuf == null) {
  444. continue;
  445. }
  446. if (VertexBuffer.Type.Index.ordinal() == bufType) {
  447. int components = compsForBuf[bufType];
  448. IndexBuffer inIdx = inMesh.getIndicesAsList();
  449. IndexBuffer outIdx = outMesh.getIndexBuffer();
  450. for (int tri = 0; tri < geomTriCount; tri++) {
  451. for (int comp = 0; comp < components; comp++) {
  452. int idx = inIdx.get(tri * components + comp) + globalVertIndex;
  453. outIdx.put((globalTriIndex + tri) * components + comp, idx);
  454. }
  455. }
  456. } else if (VertexBuffer.Type.Position.ordinal() == bufType) {
  457. FloatBuffer inPos = (FloatBuffer) inBuf.getData();
  458. FloatBuffer outPos = (FloatBuffer) outBuf.getData();
  459. doCopyBuffer(inPos, globalVertIndex, outPos, 3);
  460. } else if (VertexBuffer.Type.Normal.ordinal() == bufType || VertexBuffer.Type.Tangent.ordinal() == bufType) {
  461. FloatBuffer inPos = (FloatBuffer) inBuf.getData();
  462. FloatBuffer outPos = (FloatBuffer) outBuf.getData();
  463. doCopyBuffer(inPos, globalVertIndex, outPos, compsForBuf[bufType]);
  464. if (VertexBuffer.Type.Tangent.ordinal() == bufType) {
  465. useTangents = true;
  466. }
  467. } else {
  468. inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount);
  469. // for (int vert = 0; vert < geomVertCount; vert++) {
  470. // int curGlobalVertIndex = globalVertIndex + vert;
  471. // inBuf.copyElement(vert, outBuf, curGlobalVertIndex);
  472. // }
  473. }
  474. }
  475. globalVertIndex += geomVertCount;
  476. globalTriIndex += geomTriCount;
  477. }
  478. }
  479. private void doTransforms(FloatBuffer bufPos, FloatBuffer bufNorm, int start, int end, Matrix4f transform) {
  480. TempVars vars = TempVars.get();
  481. Vector3f pos = vars.vect1;
  482. Vector3f norm = vars.vect2;
  483. int length = (end - start) * 3;
  484. // offset is given in element units
  485. // convert to be in component units
  486. int offset = start * 3;
  487. bufPos.position(offset);
  488. bufNorm.position(offset);
  489. bufPos.get(tmpFloat, 0, length);
  490. bufNorm.get(tmpFloatN, 0, length);
  491. int index = 0;
  492. while (index < length) {
  493. pos.x = tmpFloat[index];
  494. norm.x = tmpFloatN[index++];
  495. pos.y = tmpFloat[index];
  496. norm.y = tmpFloatN[index++];
  497. pos.z = tmpFloat[index];
  498. norm.z = tmpFloatN[index];
  499. transform.mult(pos, pos);
  500. transform.multNormal(norm, norm);
  501. index -= 2;
  502. tmpFloat[index] = pos.x;
  503. tmpFloatN[index++] = norm.x;
  504. tmpFloat[index] = pos.y;
  505. tmpFloatN[index++] = norm.y;
  506. tmpFloat[index] = pos.z;
  507. tmpFloatN[index++] = norm.z;
  508. }
  509. vars.release();
  510. bufPos.position(offset);
  511. //using bulk put as it's faster
  512. bufPos.put(tmpFloat, 0, length);
  513. bufNorm.position(offset);
  514. //using bulk put as it's faster
  515. bufNorm.put(tmpFloatN, 0, length);
  516. }
  517. private void doTransformsTangents(FloatBuffer bufPos, FloatBuffer bufNorm, FloatBuffer bufTangents, int start, int end, Matrix4f transform) {
  518. TempVars vars = TempVars.get();
  519. Vector3f pos = vars.vect1;
  520. Vector3f norm = vars.vect2;
  521. Vector3f tan = vars.vect3;
  522. int length = (end - start) * 3;
  523. int tanLength = (end - start) * 4;
  524. // offset is given in element units
  525. // convert to be in component units
  526. int offset = start * 3;
  527. int tanOffset = start * 4;
  528. bufPos.position(offset);
  529. bufNorm.position(offset);
  530. bufTangents.position(tanOffset);
  531. bufPos.get(tmpFloat, 0, length);
  532. bufNorm.get(tmpFloatN, 0, length);
  533. bufTangents.get(tmpFloatT, 0, tanLength);
  534. int index = 0;
  535. int tanIndex = 0;
  536. while (index < length) {
  537. pos.x = tmpFloat[index];
  538. norm.x = tmpFloatN[index++];
  539. pos.y = tmpFloat[index];
  540. norm.y = tmpFloatN[index++];
  541. pos.z = tmpFloat[index];
  542. norm.z = tmpFloatN[index];
  543. tan.x = tmpFloatT[tanIndex++];
  544. tan.y = tmpFloatT[tanIndex++];
  545. tan.z = tmpFloatT[tanIndex++];
  546. transform.mult(pos, pos);
  547. transform.multNormal(norm, norm);
  548. transform.multNormal(tan, tan);
  549. index -= 2;
  550. tanIndex -= 3;
  551. tmpFloat[index] = pos.x;
  552. tmpFloatN[index++] = norm.x;
  553. tmpFloat[index] = pos.y;
  554. tmpFloatN[index++] = norm.y;
  555. tmpFloat[index] = pos.z;
  556. tmpFloatN[index++] = norm.z;
  557. tmpFloatT[tanIndex++] = tan.x;
  558. tmpFloatT[tanIndex++] = tan.y;
  559. tmpFloatT[tanIndex++] = tan.z;
  560. //Skipping 4th element of tangent buffer (handedness)
  561. tanIndex++;
  562. }
  563. vars.release();
  564. bufPos.position(offset);
  565. //using bulk put as it's faster
  566. bufPos.put(tmpFloat, 0, length);
  567. bufNorm.position(offset);
  568. //using bulk put as it's faster
  569. bufNorm.put(tmpFloatN, 0, length);
  570. bufTangents.position(tanOffset);
  571. //using bulk put as it's faster
  572. bufTangents.put(tmpFloatT, 0, tanLength);
  573. }
  574. private void doCopyBuffer(FloatBuffer inBuf, int offset, FloatBuffer outBuf, int componentSize) {
  575. TempVars vars = TempVars.get();
  576. Vector3f pos = vars.vect1;
  577. // offset is given in element units
  578. // convert to be in component units
  579. offset *= componentSize;
  580. for (int i = 0; i < inBuf.limit() / componentSize; i++) {
  581. pos.x = inBuf.get(i * componentSize + 0);
  582. pos.y = inBuf.get(i * componentSize + 1);
  583. pos.z = inBuf.get(i * componentSize + 2);
  584. outBuf.put(offset + i * componentSize + 0, pos.x);
  585. outBuf.put(offset + i * componentSize + 1, pos.y);
  586. outBuf.put(offset + i * componentSize + 2, pos.z);
  587. }
  588. vars.release();
  589. }
  590. protected class Batch {
  591. /**
  592. * update the batchesByGeom map for this batch with the given List of geometries
  593. * @param list
  594. */
  595. void updateGeomList(List<Geometry> list) {
  596. for (Geometry geom : list) {
  597. if (!isBatch(geom)) {
  598. batchesByGeom.put(geom, this);
  599. }
  600. }
  601. }
  602. Geometry geometry;
  603. boolean needMeshUpdate = false;
  604. }
  605. protected void setNeedsFullRebatch(boolean needsFullRebatch) {
  606. this.needsFullRebatch = needsFullRebatch;
  607. }
  608. public int getOffsetIndex(Geometry batchedGeometry) {
  609. return batchedGeometry.startIndex;
  610. }
  611. }