Geometry.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. /*
  2. * Copyright (c) 2009-2010 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.asset.AssetNotFoundException;
  34. import com.jme3.bounding.BoundingVolume;
  35. import com.jme3.collision.Collidable;
  36. import com.jme3.collision.CollisionResults;
  37. import com.jme3.export.InputCapsule;
  38. import com.jme3.export.JmeExporter;
  39. import com.jme3.export.JmeImporter;
  40. import com.jme3.export.OutputCapsule;
  41. import com.jme3.material.Material;
  42. import com.jme3.math.Matrix4f;
  43. import com.jme3.math.Transform;
  44. import com.jme3.scene.VertexBuffer.Type;
  45. import com.jme3.util.TempVars;
  46. import java.io.IOException;
  47. import java.util.Queue;
  48. import java.util.logging.Level;
  49. import java.util.logging.Logger;
  50. /**
  51. * <code>Geometry</code> defines a leaf node of the scene graph. The leaf node
  52. * contains the geometric data for rendering objects. It manages all rendering
  53. * information such as a {@link Material} object to define how the surface
  54. * should be shaded and the {@link Mesh} data to contain the actual geometry.
  55. *
  56. * @author Kirill Vainer
  57. */
  58. public class Geometry extends Spatial {
  59. // Version #1: removed shared meshes.
  60. // models loaded with shared mesh will be automatically fixed.
  61. public static final int SAVABLE_VERSION = 1;
  62. private static final Logger logger = Logger.getLogger(Geometry.class.getName());
  63. protected Mesh mesh;
  64. protected transient int lodLevel = 0;
  65. protected Material material;
  66. /**
  67. * When true, the geometry's transform will not be applied.
  68. */
  69. protected boolean ignoreTransform = false;
  70. protected transient Matrix4f cachedWorldMat = new Matrix4f();
  71. /**
  72. * used when geometry is batched
  73. */
  74. protected BatchNode batchNode = null;
  75. /**
  76. * the start index of this geom's mesh in the batchNode mesh
  77. */
  78. protected int startIndex;
  79. /**
  80. * the previous transforms of the geometry used to compute world transforms
  81. */
  82. protected Transform prevBatchTransforms = null;
  83. /**
  84. * the cached offset matrix used when the geometry is batched
  85. */
  86. protected Matrix4f cachedOffsetMat = null;
  87. /**
  88. * Serialization only. Do not use.
  89. */
  90. public Geometry() {
  91. }
  92. /**
  93. * Create a geometry node without any mesh data.
  94. * Both the mesh and the material are null, the geometry
  95. * cannot be rendered until those are set.
  96. *
  97. * @param name The name of this geometry
  98. */
  99. public Geometry(String name) {
  100. super(name);
  101. }
  102. /**
  103. * Create a geometry node with mesh data.
  104. * The material of the geometry is null, it cannot
  105. * be rendered until it is set.
  106. *
  107. * @param name The name of this geometry
  108. * @param mesh The mesh data for this geometry
  109. */
  110. public Geometry(String name, Mesh mesh) {
  111. this(name);
  112. if (mesh == null) {
  113. throw new NullPointerException();
  114. }
  115. this.mesh = mesh;
  116. }
  117. /**
  118. * @return If ignoreTransform mode is set.
  119. *
  120. * @see Geometry#setIgnoreTransform(boolean)
  121. */
  122. public boolean isIgnoreTransform() {
  123. return ignoreTransform;
  124. }
  125. /**
  126. * @param ignoreTransform If true, the geometry's transform will not be applied.
  127. */
  128. public void setIgnoreTransform(boolean ignoreTransform) {
  129. this.ignoreTransform = ignoreTransform;
  130. }
  131. /**
  132. * Sets the LOD level to use when rendering the mesh of this geometry.
  133. * Level 0 indicates that the default index buffer should be used,
  134. * levels [1, LodLevels + 1] represent the levels set on the mesh
  135. * with {@link Mesh#setLodLevels(com.jme3.scene.VertexBuffer[]) }.
  136. *
  137. * @param lod The lod level to set
  138. */
  139. @Override
  140. public void setLodLevel(int lod) {
  141. if (mesh.getNumLodLevels() == 0) {
  142. throw new IllegalStateException("LOD levels are not set on this mesh");
  143. }
  144. if (lod < 0 || lod >= mesh.getNumLodLevels()) {
  145. throw new IllegalArgumentException("LOD level is out of range: " + lod);
  146. }
  147. lodLevel = lod;
  148. }
  149. /**
  150. * Returns the LOD level set with {@link #setLodLevel(int) }.
  151. *
  152. * @return the LOD level set
  153. */
  154. public int getLodLevel() {
  155. return lodLevel;
  156. }
  157. /**
  158. * Returns this geometry's mesh vertex count.
  159. *
  160. * @return this geometry's mesh vertex count.
  161. *
  162. * @see Mesh#getVertexCount()
  163. */
  164. public int getVertexCount() {
  165. return mesh.getVertexCount();
  166. }
  167. /**
  168. * Returns this geometry's mesh triangle count.
  169. *
  170. * @return this geometry's mesh triangle count.
  171. *
  172. * @see Mesh#getTriangleCount()
  173. */
  174. public int getTriangleCount() {
  175. return mesh.getTriangleCount();
  176. }
  177. /**
  178. * Sets the mesh to use for this geometry when rendering.
  179. *
  180. * @param mesh the mesh to use for this geometry
  181. *
  182. * @throws IllegalArgumentException If mesh is null
  183. */
  184. public void setMesh(Mesh mesh) {
  185. if (mesh == null) {
  186. throw new IllegalArgumentException();
  187. }
  188. if (isBatched()) {
  189. throw new UnsupportedOperationException("Cannot set the mesh of a batched geometry");
  190. }
  191. this.mesh = mesh;
  192. setBoundRefresh();
  193. }
  194. /**
  195. * Returns the mseh to use for this geometry
  196. *
  197. * @return the mseh to use for this geometry
  198. *
  199. * @see #setMesh(com.jme3.scene.Mesh)
  200. */
  201. public Mesh getMesh() {
  202. return mesh;
  203. }
  204. /**
  205. * Sets the material to use for this geometry.
  206. *
  207. * @param material the material to use for this geometry
  208. */
  209. @Override
  210. public void setMaterial(Material material) {
  211. if (isBatched()) {
  212. throw new UnsupportedOperationException("Cannot set the material of a batched geometry, change the material of the parent BatchNode.");
  213. }
  214. this.material = material;
  215. }
  216. /**
  217. * Returns the material that is used for this geometry.
  218. *
  219. * @return the material that is used for this geometry
  220. *
  221. * @see #setMaterial(com.jme3.material.Material)
  222. */
  223. public Material getMaterial() {
  224. return material;
  225. }
  226. /**
  227. * @return The bounding volume of the mesh, in model space.
  228. */
  229. public BoundingVolume getModelBound() {
  230. return mesh.getBound();
  231. }
  232. /**
  233. * Updates the bounding volume of the mesh. Should be called when the
  234. * mesh has been modified.
  235. */
  236. public void updateModelBound() {
  237. mesh.updateBound();
  238. setBoundRefresh();
  239. }
  240. /**
  241. * <code>updateWorldBound</code> updates the bounding volume that contains
  242. * this geometry. The location of the geometry is based on the location of
  243. * all this node's parents.
  244. *
  245. * @see Spatial#updateWorldBound()
  246. */
  247. @Override
  248. protected void updateWorldBound() {
  249. super.updateWorldBound();
  250. if (mesh == null) {
  251. throw new NullPointerException("Geometry: " + getName() + " has null mesh");
  252. }
  253. if (mesh.getBound() != null) {
  254. if (ignoreTransform) {
  255. // we do not transform the model bound by the world transform,
  256. // just use the model bound as-is
  257. worldBound = mesh.getBound().clone(worldBound);
  258. } else {
  259. worldBound = mesh.getBound().transform(worldTransform, worldBound);
  260. }
  261. }
  262. }
  263. @Override
  264. protected void updateWorldTransforms() {
  265. super.updateWorldTransforms();
  266. computeWorldMatrix();
  267. if (isBatched()) {
  268. computeOffsetTransform();
  269. batchNode.updateSubBatch(this);
  270. prevBatchTransforms.set(batchNode.getTransforms(this));
  271. }
  272. // geometry requires lights to be sorted
  273. worldLights.sort(true);
  274. }
  275. /**
  276. * Batch this geometry, should only be called by the BatchNode.
  277. * @param node the batchNode
  278. * @param startIndex the starting index of this geometry in the batched mesh
  279. */
  280. protected void batch(BatchNode node, int startIndex) {
  281. this.batchNode = node;
  282. this.startIndex = startIndex;
  283. prevBatchTransforms = new Transform();
  284. cachedOffsetMat = new Matrix4f();
  285. setCullHint(CullHint.Always);
  286. }
  287. /**
  288. * unBatch this geometry.
  289. */
  290. protected void unBatch() {
  291. this.startIndex = 0;
  292. prevBatchTransforms = null;
  293. cachedOffsetMat = null;
  294. //once the geometry is removed from the screnegraph the batchNode needs to be rebatched.
  295. if (batchNode != null) {
  296. this.batchNode.setNeedsFullRebatch(true);
  297. this.batchNode = null;
  298. }
  299. setCullHint(CullHint.Dynamic);
  300. }
  301. @Override
  302. public boolean removeFromParent() {
  303. return super.removeFromParent();
  304. }
  305. @Override
  306. protected void setParent(Node parent) {
  307. super.setParent(parent);
  308. //if the geometry is batched we also have to unbatch it
  309. if (parent == null && isBatched()) {
  310. unBatch();
  311. }
  312. }
  313. /**
  314. * Recomputes the cached offset matrix used when the geometry is batched *
  315. */
  316. public void computeOffsetTransform() {
  317. TempVars vars = TempVars.get();
  318. Matrix4f tmpMat = vars.tempMat42;
  319. // Compute the cached world matrix
  320. cachedOffsetMat.loadIdentity();
  321. cachedOffsetMat.setRotationQuaternion(prevBatchTransforms.getRotation());
  322. cachedOffsetMat.setTranslation(prevBatchTransforms.getTranslation());
  323. Matrix4f scaleMat = vars.tempMat4;
  324. scaleMat.loadIdentity();
  325. scaleMat.scale(prevBatchTransforms.getScale());
  326. cachedOffsetMat.multLocal(scaleMat);
  327. cachedOffsetMat.invertLocal();
  328. tmpMat.loadIdentity();
  329. tmpMat.setRotationQuaternion(batchNode.getTransforms(this).getRotation());
  330. tmpMat.setTranslation(batchNode.getTransforms(this).getTranslation());
  331. scaleMat.loadIdentity();
  332. scaleMat.scale(batchNode.getTransforms(this).getScale());
  333. tmpMat.multLocal(scaleMat);
  334. tmpMat.mult(cachedOffsetMat, cachedOffsetMat);
  335. vars.release();
  336. }
  337. /**
  338. * Indicate that the transform of this spatial has changed and that
  339. * a refresh is required.
  340. */
  341. // NOTE: Spatial has an identical implementation of this method,
  342. // thus it was commented out.
  343. // @Override
  344. // protected void setTransformRefresh() {
  345. // refreshFlags |= RF_TRANSFORM;
  346. // setBoundRefresh();
  347. // }
  348. /**
  349. * Recomputes the matrix returned by {@link Geometry#getWorldMatrix() }.
  350. * This will require a localized transform update for this geometry.
  351. */
  352. public void computeWorldMatrix() {
  353. // Force a local update of the geometry's transform
  354. checkDoTransformUpdate();
  355. // Compute the cached world matrix
  356. cachedWorldMat.loadIdentity();
  357. cachedWorldMat.setRotationQuaternion(worldTransform.getRotation());
  358. cachedWorldMat.setTranslation(worldTransform.getTranslation());
  359. TempVars vars = TempVars.get();
  360. Matrix4f scaleMat = vars.tempMat4;
  361. scaleMat.loadIdentity();
  362. scaleMat.scale(worldTransform.getScale());
  363. cachedWorldMat.multLocal(scaleMat);
  364. vars.release();
  365. }
  366. /**
  367. * A {@link Matrix4f matrix} that transforms the {@link Geometry#getMesh() mesh}
  368. * from model space to world space. This matrix is computed based on the
  369. * {@link Geometry#getWorldTransform() world transform} of this geometry.
  370. * In order to receive updated values, you must call {@link Geometry#computeWorldMatrix() }
  371. * before using this method.
  372. *
  373. * @return Matrix to transform from local space to world space
  374. */
  375. public Matrix4f getWorldMatrix() {
  376. return cachedWorldMat;
  377. }
  378. /**
  379. * Sets the model bound to use for this geometry.
  380. * This alters the bound used on the mesh as well via
  381. * {@link Mesh#setBound(com.jme3.bounding.BoundingVolume) } and
  382. * forces the world bounding volume to be recomputed.
  383. *
  384. * @param modelBound The model bound to set
  385. */
  386. @Override
  387. public void setModelBound(BoundingVolume modelBound) {
  388. this.worldBound = null;
  389. mesh.setBound(modelBound);
  390. setBoundRefresh();
  391. // NOTE: Calling updateModelBound() would cause the mesh
  392. // to recompute the bound based on the geometry thus making
  393. // this call useless!
  394. //updateModelBound();
  395. }
  396. public int collideWith(Collidable other, CollisionResults results) {
  397. // Force bound to update
  398. checkDoBoundUpdate();
  399. // Update transform, and compute cached world matrix
  400. computeWorldMatrix();
  401. assert (refreshFlags & (RF_BOUND | RF_TRANSFORM)) == 0;
  402. if (mesh != null) {
  403. // NOTE: BIHTree in mesh already checks collision with the
  404. // mesh's bound
  405. int prevSize = results.size();
  406. int added = mesh.collideWith(other, cachedWorldMat, worldBound, results);
  407. int newSize = results.size();
  408. for (int i = prevSize; i < newSize; i++) {
  409. results.getCollisionDirect(i).setGeometry(this);
  410. }
  411. return added;
  412. }
  413. return 0;
  414. }
  415. @Override
  416. public void depthFirstTraversal(SceneGraphVisitor visitor) {
  417. visitor.visit(this);
  418. }
  419. @Override
  420. protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {
  421. }
  422. public boolean isBatched() {
  423. return batchNode != null;
  424. }
  425. /**
  426. * This version of clone is a shallow clone, in other words, the
  427. * same mesh is referenced as the original geometry.
  428. * Exception: if the mesh is marked as being a software
  429. * animated mesh, (bind pose is set) then the positions
  430. * and normals are deep copied.
  431. */
  432. @Override
  433. public Geometry clone(boolean cloneMaterial) {
  434. Geometry geomClone = (Geometry) super.clone(cloneMaterial);
  435. //this geometry is batched but the clonned one should not be
  436. if (isBatched()) {
  437. geomClone.batchNode = null;
  438. geomClone.unBatch();
  439. }
  440. geomClone.cachedWorldMat = cachedWorldMat.clone();
  441. if (material != null) {
  442. if (cloneMaterial) {
  443. geomClone.material = material.clone();
  444. } else {
  445. geomClone.material = material;
  446. }
  447. }
  448. if (mesh != null && mesh.getBuffer(Type.BindPosePosition) != null) {
  449. geomClone.mesh = mesh.cloneForAnim();
  450. }
  451. return geomClone;
  452. }
  453. /**
  454. * This version of clone is a shallow clone, in other words, the
  455. * same mesh is referenced as the original geometry.
  456. * Exception: if the mesh is marked as being a software
  457. * animated mesh, (bind pose is set) then the positions
  458. * and normals are deep copied.
  459. */
  460. @Override
  461. public Geometry clone() {
  462. return clone(true);
  463. }
  464. /**
  465. * Creates a deep clone of the geometry,
  466. * this creates an identical copy of the mesh
  467. * with the vertexbuffer data duplicated.
  468. */
  469. @Override
  470. public Spatial deepClone() {
  471. Geometry geomClone = clone(true);
  472. geomClone.mesh = mesh.deepClone();
  473. return geomClone;
  474. }
  475. @Override
  476. public void write(JmeExporter ex) throws IOException {
  477. super.write(ex);
  478. OutputCapsule oc = ex.getCapsule(this);
  479. oc.write(mesh, "mesh", null);
  480. if (material != null) {
  481. oc.write(material.getAssetName(), "materialName", null);
  482. }
  483. oc.write(material, "material", null);
  484. oc.write(ignoreTransform, "ignoreTransform", false);
  485. }
  486. @Override
  487. public void read(JmeImporter im) throws IOException {
  488. super.read(im);
  489. InputCapsule ic = im.getCapsule(this);
  490. mesh = (Mesh) ic.readSavable("mesh", null);
  491. material = null;
  492. String matName = ic.readString("materialName", null);
  493. if (matName != null) {
  494. // Material name is set,
  495. // Attempt to load material via J3M
  496. try {
  497. material = im.getAssetManager().loadMaterial(matName);
  498. } catch (AssetNotFoundException ex) {
  499. // Cannot find J3M file.
  500. logger.log(Level.FINE, "Cannot locate {0} for geometry {1}", new Object[]{matName, key});
  501. }
  502. }
  503. // If material is NULL, try to load it from the geometry
  504. if (material == null) {
  505. material = (Material) ic.readSavable("material", null);
  506. }
  507. ignoreTransform = ic.readBoolean("ignoreTransform", false);
  508. if (ic.getSavableVersion(Geometry.class) == 0) {
  509. // Fix shared mesh (if set)
  510. Mesh sharedMesh = getUserData(UserData.JME_SHAREDMESH);
  511. if (sharedMesh != null) {
  512. getMesh().extractVertexData(sharedMesh);
  513. }
  514. }
  515. }
  516. }