BatchNode.java 24 KB

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