Batching :
- BatchNode can now batch a scene graph with several materials. It creates a batch by material - Added a SimpleBatchNode that batch only geometries (no sub node graph) for better performances - removed RF_REFRESHBATCH from Spatial as it's no longer needed - changed test cases a bit git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8527 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
d81cb1f812
commit
7193c0b2d3
@ -31,8 +31,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.scene;
|
package com.jme3.scene;
|
||||||
|
|
||||||
import com.jme3.asset.AssetNotFoundException;
|
|
||||||
import com.jme3.bounding.BoundingVolume;
|
|
||||||
import com.jme3.export.InputCapsule;
|
import com.jme3.export.InputCapsule;
|
||||||
import com.jme3.export.JmeExporter;
|
import com.jme3.export.JmeExporter;
|
||||||
import com.jme3.export.JmeImporter;
|
import com.jme3.export.JmeImporter;
|
||||||
@ -40,31 +38,33 @@ import com.jme3.export.OutputCapsule;
|
|||||||
import com.jme3.export.Savable;
|
import com.jme3.export.Savable;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.math.Matrix4f;
|
import com.jme3.math.Matrix4f;
|
||||||
|
import com.jme3.math.Transform;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.scene.mesh.IndexBuffer;
|
import com.jme3.scene.mesh.IndexBuffer;
|
||||||
import com.jme3.util.IntMap.Entry;
|
import com.jme3.util.IntMap.Entry;
|
||||||
import com.jme3.util.SafeArrayList;
|
|
||||||
import com.jme3.util.TempVars;
|
import com.jme3.util.TempVars;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.Buffer;
|
import java.nio.Buffer;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BatchNode holds a geometry that is a batched version of all geometries that are in its sub scenegraph.
|
* BatchNode holds geometrie that are batched version of all geometries that are in its sub scenegraph.
|
||||||
* this geometry is directly attached to the node in the scene graph.
|
* There is one geometry per different material in the sub tree.
|
||||||
* 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 (see todo more automagic for further enhancements)
|
* this geometries are directly attached to the node in the scene graph.
|
||||||
|
* 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
|
||||||
|
* (see todo more automagic for further enhancements)
|
||||||
* all the geometry that have been batched are set to {@link CullHint#Always} to not render them.
|
* all the geometry that have been batched are set to {@link CullHint#Always} to not render them.
|
||||||
* the sub geometries can be transformed as usual their transforms are used to update the mesh of the geometryBatch.
|
* the sub geometries can be transformed as usual their transforms are used to update the mesh of the geometryBatch.
|
||||||
* sub geoms can be removed but it may be slower than the normal spatial removing
|
* sub geoms can be removed but it may be slower than the normal spatial removing
|
||||||
* Sub geoms can be added after the batch() method has been called but won't be batched and will be rendered as normal geometries.
|
* Sub geoms can be added after the batch() method has been called but won't be batched and will be rendered as normal geometries.
|
||||||
* To integrated them in the batch you have to call the batch() method again on the batchNode.
|
* To integrate them in the batch you have to call the batch() method again on the batchNode.
|
||||||
*
|
*
|
||||||
* TODO account for sub-BatchNodes
|
|
||||||
* TODO account for geometries that have different materials
|
|
||||||
* TODO normal or tangents or both looks a bit weird
|
* TODO normal or tangents or both looks a bit weird
|
||||||
* TODO more automagic (batch when needed in the updateLigicalState)
|
* TODO more automagic (batch when needed in the updateLigicalState)
|
||||||
* @author Nehon
|
* @author Nehon
|
||||||
@ -73,12 +73,13 @@ public class BatchNode extends Node implements Savable {
|
|||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(BatchNode.class.getName());
|
private static final Logger logger = Logger.getLogger(BatchNode.class.getName());
|
||||||
/**
|
/**
|
||||||
* the geometry holding the batched mesh
|
* the map of geometry holding the batched meshes
|
||||||
*/
|
*/
|
||||||
protected Geometry batch;
|
protected Map<Material, Batch> batches = new HashMap<Material, Batch>();
|
||||||
protected Material material;
|
|
||||||
private boolean needMeshUpdate = false;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a batchNode
|
||||||
|
*/
|
||||||
public BatchNode() {
|
public BatchNode() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -109,11 +110,16 @@ public class BatchNode extends Node implements Savable {
|
|||||||
for (Spatial child : children.getArray()) {
|
for (Spatial child : children.getArray()) {
|
||||||
child.updateGeometricState();
|
child.updateGeometricState();
|
||||||
}
|
}
|
||||||
if (needMeshUpdate) {
|
|
||||||
batch.getMesh().updateBound();
|
for (Batch batch : batches.values()) {
|
||||||
batch.updateWorldBound();
|
if (batch.needMeshUpdate) {
|
||||||
needMeshUpdate = false;
|
batch.geometry.getMesh().updateBound();
|
||||||
|
batch.geometry.updateWorldBound();
|
||||||
|
batch.needMeshUpdate = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,9 +130,14 @@ public class BatchNode extends Node implements Savable {
|
|||||||
assert refreshFlags == 0;
|
assert refreshFlags == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Transform getTransforms(Geometry geom){
|
||||||
|
return geom.getWorldTransform();
|
||||||
|
}
|
||||||
|
|
||||||
protected void updateSubBatch(Geometry bg) {
|
protected void updateSubBatch(Geometry bg) {
|
||||||
|
Batch batch = batches.get(bg.getMaterial());
|
||||||
if (batch != null) {
|
if (batch != null) {
|
||||||
Mesh mesh = batch.getMesh();
|
Mesh mesh = batch.geometry.getMesh();
|
||||||
|
|
||||||
FloatBuffer buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Position).getData();
|
FloatBuffer buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Position).getData();
|
||||||
doTransformVerts(buf, 0, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat);
|
doTransformVerts(buf, 0, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat);
|
||||||
@ -144,162 +155,155 @@ public class BatchNode extends Node implements Savable {
|
|||||||
mesh.getBuffer(VertexBuffer.Type.Tangent).updateData(buf);
|
mesh.getBuffer(VertexBuffer.Type.Tangent).updateData(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
needMeshUpdate = true;
|
batch.needMeshUpdate = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setTransformRefresh() {
|
|
||||||
refreshFlags |= RF_TRANSFORM;
|
|
||||||
setBoundRefresh();
|
|
||||||
|
|
||||||
for (Spatial child : children.getArray()) {
|
|
||||||
if ((child.refreshFlags & RF_TRANSFORM) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
innerTransformRefresh(child);
|
|
||||||
//child.setTransformRefresh();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
private void innerTransformRefresh(Spatial s) {
|
|
||||||
s.refreshFlags |= RF_TRANSFORM;
|
|
||||||
s.setBoundRefresh();
|
|
||||||
if (s instanceof Node) {
|
|
||||||
Node n = (Node) s;
|
|
||||||
|
|
||||||
for (Spatial child :((SafeArrayList<Spatial>) n.getChildren()).getArray()) {
|
|
||||||
if ((child.refreshFlags & RF_TRANSFORM) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
innerTransformRefresh(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Batch this batchNode
|
* Batch this batchNode
|
||||||
* every geometry of the sub scene graph of this node will be batched into a single mesh that will be rendered in one call
|
* every geometry of the sub scene graph of this node will be batched into a single mesh that will be rendered in one call
|
||||||
*/
|
*/
|
||||||
public void batch() {
|
public void batch() {
|
||||||
|
doBatch();
|
||||||
|
//we set the batch geometries to ignore transforms to avoid transforms of parent nodes to be applied twice
|
||||||
|
for (Batch batch : batches.values()) {
|
||||||
|
batch.geometry.setIgnoreTransform(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<Geometry> tmpList = new ArrayList<Geometry>();
|
protected void doBatch() {
|
||||||
|
///List<Geometry> tmpList = new ArrayList<Geometry>();
|
||||||
|
Map<Material, List<Geometry>> matMap = new HashMap<Material, List<Geometry>>();
|
||||||
|
|
||||||
|
gatherGeomerties(matMap, this);
|
||||||
|
batches.clear();
|
||||||
|
int nbGeoms = 0;
|
||||||
|
for (Material material : matMap.keySet()) {
|
||||||
Mesh m = new Mesh();
|
Mesh m = new Mesh();
|
||||||
populateList(tmpList, this);
|
List<Geometry> list = matMap.get(material);
|
||||||
mergeGeometries(m, tmpList);
|
nbGeoms += list.size();
|
||||||
|
mergeGeometries(m, list);
|
||||||
|
Batch batch = new Batch();
|
||||||
|
|
||||||
if (batch == null) {
|
batch.geometry = new Geometry(name + "-batch" + batches.size());
|
||||||
batch = new Geometry(name + "-batch");
|
batch.geometry.setMaterial(material);
|
||||||
batch.setMaterial(material);
|
this.attachChild(batch.geometry);
|
||||||
this.attachChild(batch);
|
|
||||||
|
|
||||||
|
batch.geometry.setMesh(m);
|
||||||
|
batch.geometry.getMesh().updateCounts();
|
||||||
|
batch.geometry.getMesh().updateBound();
|
||||||
|
batches.put(material, batch);
|
||||||
}
|
}
|
||||||
batch.setMesh(m);
|
logger.log(Level.INFO, "Batched {0} geometries in {1} batches.", new Object[]{nbGeoms, batches.size()});
|
||||||
batch.getMesh().updateCounts();
|
|
||||||
batch.getMesh().updateBound();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateList(List<Geometry> list, Spatial n) {
|
private void gatherGeomerties(Map<Material, List<Geometry>> map, Spatial n) {
|
||||||
|
|
||||||
if (n instanceof Geometry) {
|
if (n.getClass() == Geometry.class) {
|
||||||
if (n != batch) {
|
if (!isBatch(n)) {
|
||||||
list.add((Geometry) n);
|
Geometry g = (Geometry) n;
|
||||||
|
if (g.getMaterial() == null) {
|
||||||
|
throw new IllegalStateException("No material is set for Geometry: " + g.getName() + " please set a material before batching");
|
||||||
|
}
|
||||||
|
List<Geometry> list = map.get(g.getMaterial());
|
||||||
|
if (list == null) {
|
||||||
|
list = new ArrayList<Geometry>();
|
||||||
|
map.put(g.getMaterial(), list);
|
||||||
|
}
|
||||||
|
list.add(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (n instanceof Node) {
|
} else if (n instanceof Node) {
|
||||||
for (Spatial child : ((Node) n).getChildren()) {
|
for (Spatial child : ((Node) n).getChildren()) {
|
||||||
populateList(list, child);
|
if (child instanceof BatchNode) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
gatherGeomerties(map, child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isBatch(Spatial s) {
|
||||||
|
for (Batch batch : batches.values()) {
|
||||||
|
if (batch.geometry == s) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the material to use for this geometry.
|
* Sets the material to the all the batches of this BatchNode
|
||||||
|
* use setMaterial(Material material,int batchIndex) to set a material to a specific batch
|
||||||
*
|
*
|
||||||
* @param material the material to use for this geometry
|
* @param material the material to use for this geometry
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setMaterial(Material material) {
|
public void setMaterial(Material material) {
|
||||||
super.setMaterial(material);
|
// for (Batch batch : batches.values()) {
|
||||||
if (batch != null) {
|
// batch.geometry.setMaterial(material);
|
||||||
batch.setMaterial(material);
|
// }
|
||||||
}
|
throw new UnsupportedOperationException("Unsupported for now, please set the material on the geoms before batching");
|
||||||
this.material = material;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the material that is used for this geometry.
|
* Returns the material that is used for the first batch of this BatchNode
|
||||||
*
|
*
|
||||||
* @return the material that is used for this geometry
|
* use getMaterial(Material material,int batchIndex) to get a material from a specific batch
|
||||||
|
*
|
||||||
|
* @return the material that is used for the first batch of this BatchNode
|
||||||
*
|
*
|
||||||
* @see #setMaterial(com.jme3.material.Material)
|
* @see #setMaterial(com.jme3.material.Material)
|
||||||
*/
|
*/
|
||||||
public Material getMaterial() {
|
public Material getMaterial() {
|
||||||
return material;
|
if (!batches.isEmpty()) {
|
||||||
|
Batch b = batches.get(batches.keySet().iterator().next());
|
||||||
|
return b.geometry.getMaterial();
|
||||||
|
}
|
||||||
|
return null;//material;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @return The bounding volume of the mesh, in model space.
|
// * Sets the material to the a specific batch of this BatchNode
|
||||||
*/
|
// *
|
||||||
public BoundingVolume getModelBound() {
|
// *
|
||||||
if (batch != null) {
|
// * @param material the material to use for this geometry
|
||||||
return batch.getMesh().getBound();
|
// */
|
||||||
}
|
// public void setMaterial(Material material,int batchIndex) {
|
||||||
return super.getWorldBound();
|
// if (!batches.isEmpty()) {
|
||||||
|
//
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// }
|
||||||
* This version of clone is a shallow clone, in other words, the
|
//
|
||||||
* same mesh is referenced as the original geometry.
|
// /**
|
||||||
* Exception: if the mesh is marked as being a software
|
// * Returns the material that is used for the first batch of this BatchNode
|
||||||
* animated mesh, (bind pose is set) then the positions
|
// *
|
||||||
* and normals are deep copied.
|
// * use getMaterial(Material material,int batchIndex) to get a material from a specific batch
|
||||||
*/
|
// *
|
||||||
@Override
|
// * @return the material that is used for the first batch of this BatchNode
|
||||||
public BatchNode clone(boolean cloneMaterial) {
|
// *
|
||||||
BatchNode clone = (BatchNode) super.clone(cloneMaterial);
|
// * @see #setMaterial(com.jme3.material.Material)
|
||||||
clone.batch = batch.clone(cloneMaterial);
|
// */
|
||||||
return clone;
|
// public Material getMaterial(int batchIndex) {
|
||||||
}
|
// if (!batches.isEmpty()) {
|
||||||
|
// Batch b = batches.get(batches.keySet().iterator().next());
|
||||||
/**
|
// return b.geometry.getMaterial();
|
||||||
* This version of clone is a shallow clone, in other words, the
|
// }
|
||||||
* same mesh is referenced as the original geometry.
|
// return null;//material;
|
||||||
* Exception: if the mesh is marked as being a software
|
// }
|
||||||
* animated mesh, (bind pose is set) then the positions
|
|
||||||
* and normals are deep copied.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public BatchNode clone() {
|
|
||||||
return clone(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a deep clone of the geometry,
|
|
||||||
* this creates an identical copy of the mesh
|
|
||||||
* with the vertexbuffer data duplicated.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Spatial deepClone() {
|
|
||||||
BatchNode clone = clone(true);
|
|
||||||
clone.batch = (Geometry) batch.deepClone();
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
super.write(ex);
|
super.write(ex);
|
||||||
OutputCapsule oc = ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
|
//
|
||||||
if (material != null) {
|
// if (material != null) {
|
||||||
oc.write(material.getAssetName(), "materialName", null);
|
// oc.write(material.getAssetName(), "materialName", null);
|
||||||
}
|
// }
|
||||||
oc.write(material, "material", null);
|
// oc.write(material, "material", null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,23 +313,23 @@ public class BatchNode extends Node implements Savable {
|
|||||||
InputCapsule ic = im.getCapsule(this);
|
InputCapsule ic = im.getCapsule(this);
|
||||||
|
|
||||||
|
|
||||||
material = null;
|
// material = null;
|
||||||
String matName = ic.readString("materialName", null);
|
// String matName = ic.readString("materialName", null);
|
||||||
if (matName != null) {
|
// if (matName != null) {
|
||||||
// Material name is set,
|
// // Material name is set,
|
||||||
// Attempt to load material via J3M
|
// // Attempt to load material via J3M
|
||||||
try {
|
// try {
|
||||||
material = im.getAssetManager().loadMaterial(matName);
|
// material = im.getAssetManager().loadMaterial(matName);
|
||||||
} catch (AssetNotFoundException ex) {
|
// } catch (AssetNotFoundException ex) {
|
||||||
// Cannot find J3M file.
|
// // Cannot find J3M file.
|
||||||
logger.log(Level.FINE, "Could not load J3M file {0} for Geometry.",
|
// logger.log(Level.FINE, "Could not load J3M file {0} for Geometry.",
|
||||||
matName);
|
// matName);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
// If material is NULL, try to load it from the geometry
|
// // If material is NULL, try to load it from the geometry
|
||||||
if (material == null) {
|
// if (material == null) {
|
||||||
material = (Material) ic.readSavable("material", null);
|
// material = (Material) ic.readSavable("material", null);
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,9 +422,6 @@ public class BatchNode extends Node implements Savable {
|
|||||||
|
|
||||||
for (Geometry geom : geometries) {
|
for (Geometry geom : geometries) {
|
||||||
Mesh inMesh = geom.getMesh();
|
Mesh inMesh = geom.getMesh();
|
||||||
if (geom.getMaterial() != null && material == null) {
|
|
||||||
material = geom.getMaterial();
|
|
||||||
}
|
|
||||||
geom.batch(this, globalVertIndex);
|
geom.batch(this, globalVertIndex);
|
||||||
|
|
||||||
int geomVertCount = inMesh.getVertexCount();
|
int geomVertCount = inMesh.getVertexCount();
|
||||||
@ -532,4 +533,10 @@ public class BatchNode extends Node implements Savable {
|
|||||||
}
|
}
|
||||||
vars.release();
|
vars.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected class Batch {
|
||||||
|
|
||||||
|
Geometry geometry;
|
||||||
|
boolean needMeshUpdate = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ public class Geometry extends Spatial {
|
|||||||
/**
|
/**
|
||||||
* the previous transforms of the geometry used to compute world transforms
|
* the previous transforms of the geometry used to compute world transforms
|
||||||
*/
|
*/
|
||||||
protected Transform prevLocalTransform = null;
|
protected Transform prevBatchTransforms = null;
|
||||||
/**
|
/**
|
||||||
* the cached offset matrix used when the geometry is batched
|
* the cached offset matrix used when the geometry is batched
|
||||||
*/
|
*/
|
||||||
@ -287,11 +287,12 @@ public class Geometry extends Spatial {
|
|||||||
|
|
||||||
super.updateWorldTransforms();
|
super.updateWorldTransforms();
|
||||||
computeWorldMatrix();
|
computeWorldMatrix();
|
||||||
if ((refreshFlags & RF_REFRESHBATCH) != 0) {
|
|
||||||
|
if (isBatched()) {
|
||||||
computeOffsetTransform();
|
computeOffsetTransform();
|
||||||
batchNode.updateSubBatch(this);
|
batchNode.updateSubBatch(this);
|
||||||
prevLocalTransform.set(localTransform);
|
prevBatchTransforms.set(batchNode.getTransforms(this));
|
||||||
refreshFlags &= ~RF_REFRESHBATCH;
|
|
||||||
}
|
}
|
||||||
// geometry requires lights to be sorted
|
// geometry requires lights to be sorted
|
||||||
worldLights.sort(true);
|
worldLights.sort(true);
|
||||||
@ -305,10 +306,9 @@ public class Geometry extends Spatial {
|
|||||||
protected void batch(BatchNode node, int startIndex) {
|
protected void batch(BatchNode node, int startIndex) {
|
||||||
this.batchNode = node;
|
this.batchNode = node;
|
||||||
this.startIndex = startIndex;
|
this.startIndex = startIndex;
|
||||||
prevLocalTransform = new Transform();
|
prevBatchTransforms = new Transform();
|
||||||
cachedOffsetMat = new Matrix4f();
|
cachedOffsetMat = new Matrix4f();
|
||||||
setCullHint(CullHint.Always);
|
setCullHint(CullHint.Always);
|
||||||
refreshFlags |= RF_REFRESHBATCH;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -316,7 +316,7 @@ public class Geometry extends Spatial {
|
|||||||
*/
|
*/
|
||||||
protected void unBatch() {
|
protected void unBatch() {
|
||||||
this.startIndex = 0;
|
this.startIndex = 0;
|
||||||
prevLocalTransform = null;
|
prevBatchTransforms = null;
|
||||||
cachedOffsetMat = null;
|
cachedOffsetMat = null;
|
||||||
//once the geometry is removed from the screnegraph we call batch on the batchNode before unreferencing it.
|
//once the geometry is removed from the screnegraph we call batch on the batchNode before unreferencing it.
|
||||||
this.batchNode.batch();
|
this.batchNode.batch();
|
||||||
@ -343,21 +343,21 @@ public class Geometry extends Spatial {
|
|||||||
|
|
||||||
// Compute the cached world matrix
|
// Compute the cached world matrix
|
||||||
cachedOffsetMat.loadIdentity();
|
cachedOffsetMat.loadIdentity();
|
||||||
cachedOffsetMat.setRotationQuaternion(prevLocalTransform.getRotation());
|
cachedOffsetMat.setRotationQuaternion(prevBatchTransforms.getRotation());
|
||||||
cachedOffsetMat.setTranslation(prevLocalTransform.getTranslation());
|
cachedOffsetMat.setTranslation(prevBatchTransforms.getTranslation());
|
||||||
|
|
||||||
|
|
||||||
Matrix4f scaleMat = vars.tempMat4;
|
Matrix4f scaleMat = vars.tempMat4;
|
||||||
scaleMat.loadIdentity();
|
scaleMat.loadIdentity();
|
||||||
scaleMat.scale(prevLocalTransform.getScale());
|
scaleMat.scale(prevBatchTransforms.getScale());
|
||||||
cachedOffsetMat.multLocal(scaleMat);
|
cachedOffsetMat.multLocal(scaleMat);
|
||||||
cachedOffsetMat.invertLocal();
|
cachedOffsetMat.invertLocal();
|
||||||
|
|
||||||
tmpMat.loadIdentity();
|
tmpMat.loadIdentity();
|
||||||
tmpMat.setRotationQuaternion(localTransform.getRotation());
|
tmpMat.setRotationQuaternion(batchNode.getTransforms(this).getRotation());
|
||||||
tmpMat.setTranslation(localTransform.getTranslation());
|
tmpMat.setTranslation(batchNode.getTransforms(this).getTranslation());
|
||||||
scaleMat.loadIdentity();
|
scaleMat.loadIdentity();
|
||||||
scaleMat.scale(localTransform.getScale());
|
scaleMat.scale(batchNode.getTransforms(this).getScale());
|
||||||
tmpMat.multLocal(scaleMat);
|
tmpMat.multLocal(scaleMat);
|
||||||
|
|
||||||
tmpMat.mult(cachedOffsetMat, cachedOffsetMat);
|
tmpMat.mult(cachedOffsetMat, cachedOffsetMat);
|
||||||
@ -373,9 +373,6 @@ public class Geometry extends Spatial {
|
|||||||
protected void setTransformRefresh() {
|
protected void setTransformRefresh() {
|
||||||
refreshFlags |= RF_TRANSFORM;
|
refreshFlags |= RF_TRANSFORM;
|
||||||
setBoundRefresh();
|
setBoundRefresh();
|
||||||
if (isBatched()) {
|
|
||||||
refreshFlags |= RF_REFRESHBATCH;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
56
engine/src/core/com/jme3/scene/SimpleBatchNode.java
Normal file
56
engine/src/core/com/jme3/scene/SimpleBatchNode.java
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* To change this template, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package com.jme3.scene;
|
||||||
|
|
||||||
|
import com.jme3.math.Transform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* SimpleBatchNode comes with some restrictions, but can yield better performances.
|
||||||
|
* Geometries to be batched has to be attached directly to the BatchNode
|
||||||
|
* You can't attach a Node to a SimpleBatchNode
|
||||||
|
* SimpleBatchNode is recommended when you have a large number of geometries using the same material that does not require a complex scene graph structure.
|
||||||
|
* @see BatchNode
|
||||||
|
* @author Nehon
|
||||||
|
*/
|
||||||
|
public class SimpleBatchNode extends BatchNode {
|
||||||
|
|
||||||
|
public SimpleBatchNode() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleBatchNode(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int attachChild(Spatial child) {
|
||||||
|
|
||||||
|
if (!(child instanceof Geometry)) {
|
||||||
|
throw new UnsupportedOperationException("BatchNode is BatchMode.Simple only support child of type Geometry, use BatchMode.Complex to use a complex structure");
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.attachChild(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setTransformRefresh() {
|
||||||
|
|
||||||
|
refreshFlags |= RF_TRANSFORM;
|
||||||
|
setBoundRefresh();
|
||||||
|
for (Batch batch : batches.values()) {
|
||||||
|
batch.geometry.setTransformRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Transform getTransforms(Geometry geom){
|
||||||
|
return geom.getLocalTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void batch() {
|
||||||
|
doBatch();
|
||||||
|
}
|
||||||
|
}
|
@ -112,8 +112,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset {
|
|||||||
*/
|
*/
|
||||||
protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
|
protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
|
||||||
RF_BOUND = 0x02,
|
RF_BOUND = 0x02,
|
||||||
RF_LIGHTLIST = 0x04, // changes in light lists
|
RF_LIGHTLIST = 0x04; // changes in light lists
|
||||||
RF_REFRESHBATCH = 0x08; // chamge in geometry transforms that require refreshing the batched mesh
|
|
||||||
|
|
||||||
protected CullHint cullHint = CullHint.Inherit;
|
protected CullHint cullHint = CullHint.Inherit;
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import com.jme3.scene.BatchNode;
|
|||||||
import com.jme3.scene.Geometry;
|
import com.jme3.scene.Geometry;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.shape.Box;
|
import com.jme3.scene.shape.Box;
|
||||||
|
import com.jme3.system.NanoTimer;
|
||||||
import com.jme3.util.TangentBinormalGenerator;
|
import com.jme3.util.TangentBinormalGenerator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,6 +34,7 @@ public class TestBatchNode extends SimpleApplication {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
|
timer = new NanoTimer();
|
||||||
batch = new BatchNode("theBatchNode");
|
batch = new BatchNode("theBatchNode");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,6 +44,7 @@ public class TestBatchNode extends SimpleApplication {
|
|||||||
Box boxshape4 = new Box(Vector3f.ZERO, 1f, 1f, 1f );
|
Box boxshape4 = new Box(Vector3f.ZERO, 1f, 1f, 1f );
|
||||||
cube = new Geometry("cube1", boxshape4);
|
cube = new Geometry("cube1", boxshape4);
|
||||||
Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
|
Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
|
||||||
|
cube.setMaterial(mat);
|
||||||
// Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
// Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
// mat.setColor("Diffuse", ColorRGBA.Blue);
|
// mat.setColor("Diffuse", ColorRGBA.Blue);
|
||||||
// mat.setBoolean("UseMaterialColors", true);
|
// mat.setBoolean("UseMaterialColors", true);
|
||||||
@ -51,7 +54,7 @@ public class TestBatchNode extends SimpleApplication {
|
|||||||
*/
|
*/
|
||||||
Box box = new Box(Vector3f.ZERO, 1f, 1f, 1f);
|
Box box = new Box(Vector3f.ZERO, 1f, 1f, 1f);
|
||||||
cube2 = new Geometry("cube2", box);
|
cube2 = new Geometry("cube2", box);
|
||||||
|
cube2.setMaterial(mat);
|
||||||
|
|
||||||
TangentBinormalGenerator.generate(cube);
|
TangentBinormalGenerator.generate(cube);
|
||||||
TangentBinormalGenerator.generate(cube2);
|
TangentBinormalGenerator.generate(cube2);
|
||||||
@ -61,7 +64,7 @@ public class TestBatchNode extends SimpleApplication {
|
|||||||
// n.attachChild(cube2);
|
// n.attachChild(cube2);
|
||||||
batch.attachChild(cube);
|
batch.attachChild(cube);
|
||||||
batch.attachChild(cube2);
|
batch.attachChild(cube2);
|
||||||
batch.setMaterial(mat);
|
// batch.setMaterial(mat);
|
||||||
batch.batch();
|
batch.batch();
|
||||||
rootNode.attachChild(batch);
|
rootNode.attachChild(batch);
|
||||||
cube.setLocalTranslation(3, 0, 0);
|
cube.setLocalTranslation(3, 0, 0);
|
||||||
|
@ -22,6 +22,7 @@ import com.jme3.post.FilterPostProcessor;
|
|||||||
import com.jme3.post.filters.BloomFilter;
|
import com.jme3.post.filters.BloomFilter;
|
||||||
import com.jme3.scene.BatchNode;
|
import com.jme3.scene.BatchNode;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.SimpleBatchNode;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.scene.debug.Arrow;
|
import com.jme3.scene.debug.Arrow;
|
||||||
import com.jme3.system.NanoTimer;
|
import com.jme3.system.NanoTimer;
|
||||||
@ -60,13 +61,8 @@ public class TestBatchNodeCluster extends SimpleApplication {
|
|||||||
protected int dynamic = 4;
|
protected int dynamic = 4;
|
||||||
protected static AppSettings settingst;
|
protected static AppSettings settingst;
|
||||||
protected boolean isTrue = true;
|
protected boolean isTrue = true;
|
||||||
|
|
||||||
private int lineLength = 50;
|
private int lineLength = 50;
|
||||||
protected BatchNode blue;
|
protected BatchNode batchNode;
|
||||||
protected BatchNode brown;
|
|
||||||
protected BatchNode pink;
|
|
||||||
protected BatchNode orange;
|
|
||||||
|
|
||||||
Material mat1;
|
Material mat1;
|
||||||
Material mat2;
|
Material mat2;
|
||||||
Material mat3;
|
Material mat3;
|
||||||
@ -77,34 +73,33 @@ public class TestBatchNodeCluster extends SimpleApplication {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
|
timer = new NanoTimer();
|
||||||
|
|
||||||
|
batchNode = new SimpleBatchNode("BatchNode");
|
||||||
|
|
||||||
blue = new BatchNode("blue");
|
|
||||||
brown = new BatchNode("brown");
|
|
||||||
pink = new BatchNode("pink");
|
|
||||||
orange = new BatchNode("orange");
|
|
||||||
|
|
||||||
xPosition.add(0);
|
xPosition.add(0);
|
||||||
yPosition.add(0);
|
yPosition.add(0);
|
||||||
zPosition.add(0);
|
zPosition.add(0);
|
||||||
randomGenerator();
|
|
||||||
|
|
||||||
mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
mat1.setColor("Color", ColorRGBA.White);
|
mat1.setColor("Color", ColorRGBA.White);
|
||||||
mat1.setColor("GlowColor", ColorRGBA.Blue.mult(10));
|
mat1.setColor("GlowColor", ColorRGBA.Blue.mult(10));
|
||||||
blue.setMaterial(mat1);
|
|
||||||
mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
mat2.setColor("Color", ColorRGBA.White);
|
mat2.setColor("Color", ColorRGBA.White);
|
||||||
mat2.setColor("GlowColor", ColorRGBA.Red.mult(10));
|
mat2.setColor("GlowColor", ColorRGBA.Red.mult(10));
|
||||||
brown.setMaterial(mat2);
|
|
||||||
mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
mat3.setColor("Color", ColorRGBA.White);
|
mat3.setColor("Color", ColorRGBA.White);
|
||||||
mat3.setColor("GlowColor", ColorRGBA.Yellow.mult(10));
|
mat3.setColor("GlowColor", ColorRGBA.Yellow.mult(10));
|
||||||
pink.setMaterial(mat3);
|
|
||||||
mat4 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
mat4 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
mat4.setColor("Color", ColorRGBA.White);
|
mat4.setColor("Color", ColorRGBA.White);
|
||||||
mat4.setColor("GlowColor", ColorRGBA.Orange.mult(10));
|
mat4.setColor("GlowColor", ColorRGBA.Orange.mult(10));
|
||||||
orange.setMaterial(mat4);
|
|
||||||
|
randomGenerator();
|
||||||
|
|
||||||
//rootNode.attachChild(SkyFactory.createSky(
|
//rootNode.attachChild(SkyFactory.createSky(
|
||||||
// assetManager, "Textures/SKY02.zip", false));
|
// assetManager, "Textures/SKY02.zip", false));
|
||||||
inputManager.addMapping("Start Game", new KeyTrigger(KeyInput.KEY_J));
|
inputManager.addMapping("Start Game", new KeyTrigger(KeyInput.KEY_J));
|
||||||
@ -115,24 +110,17 @@ public class TestBatchNodeCluster extends SimpleApplication {
|
|||||||
cam.setRotation(new Quaternion(0.022630932f, 0.9749435f, -0.18736298f, 0.11776358f));
|
cam.setRotation(new Quaternion(0.022630932f, 0.9749435f, -0.18736298f, 0.11776358f));
|
||||||
|
|
||||||
|
|
||||||
blue.batch();
|
batchNode.batch();
|
||||||
brown.batch();
|
|
||||||
pink.batch();
|
|
||||||
orange.batch();
|
|
||||||
|
|
||||||
terrain = new Node("terrain");
|
terrain = new Node("terrain");
|
||||||
terrain.setLocalTranslation(50, 0, 50);
|
terrain.setLocalTranslation(50, 0, 50);
|
||||||
terrain.attachChild(blue);
|
terrain.attachChild(batchNode);
|
||||||
terrain.attachChild(brown);
|
|
||||||
terrain.attachChild(pink);
|
|
||||||
terrain.attachChild(orange);
|
|
||||||
flyCam.setMoveSpeed(100);
|
flyCam.setMoveSpeed(100);
|
||||||
rootNode.attachChild(terrain);
|
rootNode.attachChild(terrain);
|
||||||
Vector3f pos = new Vector3f(-40, 0, -40);
|
Vector3f pos = new Vector3f(-40, 0, -40);
|
||||||
blue.setLocalTranslation(pos);
|
batchNode.setLocalTranslation(pos);
|
||||||
brown.setLocalTranslation(pos);
|
|
||||||
pink.setLocalTranslation(pos);
|
|
||||||
orange.setLocalTranslation(pos);
|
|
||||||
|
|
||||||
|
|
||||||
Arrow a = new Arrow(new Vector3f(0, 50, 0));
|
Arrow a = new Arrow(new Vector3f(0, 50, 0));
|
||||||
@ -159,35 +147,37 @@ public class TestBatchNodeCluster extends SimpleApplication {
|
|||||||
box.setLocalTranslation(new Vector3f(xPosition.get(xPosition.size() - 1),
|
box.setLocalTranslation(new Vector3f(xPosition.get(xPosition.size() - 1),
|
||||||
yPosition.get(yPosition.size() - 1),
|
yPosition.get(yPosition.size() - 1),
|
||||||
zPosition.get(zPosition.size() - 1)));
|
zPosition.get(zPosition.size() - 1)));
|
||||||
|
batchNode.attachChild(box);
|
||||||
if (i < 500) {
|
if (i < 500) {
|
||||||
blue.attachChild(box);
|
box.setMaterial(mat1);
|
||||||
} else if (i < 1000) {
|
} else if (i < 1000) {
|
||||||
brown.attachChild(box);
|
|
||||||
|
box.setMaterial(mat2);
|
||||||
} else if (i < 1500) {
|
} else if (i < 1500) {
|
||||||
pink.attachChild(box);
|
|
||||||
|
box.setMaterial(mat3);
|
||||||
} else {
|
} else {
|
||||||
orange.attachChild(box);
|
|
||||||
|
box.setMaterial(mat4);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BatchNode randomBatch() {
|
// public BatchNode randomBatch() {
|
||||||
|
//
|
||||||
int randomn = rand.nextInt(4);
|
// int randomn = rand.nextInt(4);
|
||||||
if (randomn == 0) {
|
// if (randomn == 0) {
|
||||||
return blue;
|
// return blue;
|
||||||
} else if (randomn == 1) {
|
// } else if (randomn == 1) {
|
||||||
return brown;
|
// return brown;
|
||||||
} else if (randomn == 2) {
|
// } else if (randomn == 2) {
|
||||||
return pink;
|
// return pink;
|
||||||
} else if (randomn == 3) {
|
// } else if (randomn == 3) {
|
||||||
return orange;
|
// return orange;
|
||||||
}
|
// }
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public ColorRGBA randomColor() {
|
public ColorRGBA randomColor() {
|
||||||
ColorRGBA color = ColorRGBA.Black;
|
ColorRGBA color = ColorRGBA.Black;
|
||||||
int randomn = rand.nextInt(4);
|
int randomn = rand.nextInt(4);
|
||||||
@ -334,28 +324,23 @@ public class TestBatchNodeCluster extends SimpleApplication {
|
|||||||
int random = rand.nextInt(2000);
|
int random = rand.nextInt(2000);
|
||||||
float mult1 = 1.0f;
|
float mult1 = 1.0f;
|
||||||
float mult2 = 1.0f;
|
float mult2 = 1.0f;
|
||||||
BatchNode b = null;
|
|
||||||
if (random < 500) {
|
if (random < 500) {
|
||||||
b = blue;
|
|
||||||
mult1 = 1.0f;
|
mult1 = 1.0f;
|
||||||
mult2 = 1.0f;
|
mult2 = 1.0f;
|
||||||
} else if (random < 1000) {
|
} else if (random < 1000) {
|
||||||
b = brown;
|
|
||||||
mult1 = -1.0f;
|
mult1 = -1.0f;
|
||||||
mult2 = 1.0f;
|
mult2 = 1.0f;
|
||||||
} else if (random < 1500) {
|
} else if (random < 1500) {
|
||||||
b = pink;
|
|
||||||
mult1 = 1.0f;
|
mult1 = 1.0f;
|
||||||
mult2 = -1.0f;
|
mult2 = -1.0f;
|
||||||
} else if (random <= 2000) {
|
} else if (random <= 2000) {
|
||||||
b = orange;
|
|
||||||
mult1 = -1.0f;
|
mult1 = -1.0f;
|
||||||
mult2 = -1.0f;
|
mult2 = -1.0f;
|
||||||
}
|
}
|
||||||
box = b.getChild("Box" + random);
|
box = batchNode.getChild("Box" + random);
|
||||||
if (box != null) {
|
if (box != null) {
|
||||||
Vector3f v = box.getLocalTranslation();
|
Vector3f v = box.getLocalTranslation();
|
||||||
box.setLocalTranslation(v.x + FastMath.sin(time * mult1) * 20, v.y +( FastMath.sin(time * mult1)* FastMath.cos(time * mult1)* 20), v.z + FastMath.cos(time * mult2) * 20);
|
box.setLocalTranslation(v.x + FastMath.sin(time * mult1) * 20, v.y + (FastMath.sin(time * mult1) * FastMath.cos(time * mult1) * 20), v.z + FastMath.cos(time * mult2) * 20);
|
||||||
}
|
}
|
||||||
terrain.setLocalRotation(new Quaternion().fromAngleAxis(time, Vector3f.UNIT_Y));
|
terrain.setLocalRotation(new Quaternion().fromAngleAxis(time, Vector3f.UNIT_Y));
|
||||||
|
|
||||||
|
@ -54,12 +54,15 @@ import com.jme3.math.Vector3f;
|
|||||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
||||||
import com.jme3.scene.BatchNode;
|
import com.jme3.scene.BatchNode;
|
||||||
import com.jme3.scene.Geometry;
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.SimpleBatchNode;
|
||||||
import com.jme3.scene.shape.Box;
|
import com.jme3.scene.shape.Box;
|
||||||
import com.jme3.scene.shape.Sphere;
|
import com.jme3.scene.shape.Sphere;
|
||||||
import com.jme3.scene.shape.Sphere.TextureMode;
|
import com.jme3.scene.shape.Sphere.TextureMode;
|
||||||
import com.jme3.shadow.PssmShadowRenderer;
|
import com.jme3.shadow.PssmShadowRenderer;
|
||||||
import com.jme3.shadow.PssmShadowRenderer.CompareMode;
|
import com.jme3.shadow.PssmShadowRenderer.CompareMode;
|
||||||
import com.jme3.shadow.PssmShadowRenderer.FilterMode;
|
import com.jme3.shadow.PssmShadowRenderer.FilterMode;
|
||||||
|
import com.jme3.system.AppSettings;
|
||||||
|
import com.jme3.system.NanoTimer;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import com.jme3.texture.Texture.WrapMode;
|
import com.jme3.texture.Texture.WrapMode;
|
||||||
|
|
||||||
@ -90,11 +93,14 @@ public class TestBatchNodeTower extends SimpleApplication {
|
|||||||
|
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
TestBatchNodeTower f = new TestBatchNodeTower();
|
TestBatchNodeTower f = new TestBatchNodeTower();
|
||||||
|
AppSettings s = new AppSettings(true);
|
||||||
|
f.setSettings(s);
|
||||||
f.start();
|
f.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
|
timer = new NanoTimer();
|
||||||
bulletAppState = new BulletAppState();
|
bulletAppState = new BulletAppState();
|
||||||
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
|
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
|
||||||
// bulletAppState.setEnabled(false);
|
// bulletAppState.setEnabled(false);
|
||||||
@ -219,7 +225,7 @@ public class TestBatchNodeTower extends SimpleApplication {
|
|||||||
tex3.setWrap(WrapMode.Repeat);
|
tex3.setWrap(WrapMode.Repeat);
|
||||||
mat3.setTexture("ColorMap", tex3);
|
mat3.setTexture("ColorMap", tex3);
|
||||||
}
|
}
|
||||||
|
int nbBrick =0;
|
||||||
public void addBrick(Vector3f ori) {
|
public void addBrick(Vector3f ori) {
|
||||||
Geometry reBoxg = new Geometry("brick", brick);
|
Geometry reBoxg = new Geometry("brick", brick);
|
||||||
reBoxg.setMaterial(mat);
|
reBoxg.setMaterial(mat);
|
||||||
@ -230,6 +236,7 @@ public class TestBatchNodeTower extends SimpleApplication {
|
|||||||
reBoxg.getControl(RigidBodyControl.class).setFriction(1.6f);
|
reBoxg.getControl(RigidBodyControl.class).setFriction(1.6f);
|
||||||
this.batchNode.attachChild(reBoxg);
|
this.batchNode.attachChild(reBoxg);
|
||||||
this.getPhysicsSpace().add(reBoxg);
|
this.getPhysicsSpace().add(reBoxg);
|
||||||
|
nbBrick++;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initCrossHairs() {
|
protected void initCrossHairs() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user