- Added a BatchNode that can batch geometries in its sub graph - added support for matching to Geometry - created propper test cases - added a refresh flag to spatial RF_REFRESHBATCH to refresh the batch only when needed git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8478 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
0868244b9a
commit
63b62269e8
@ -0,0 +1,508 @@ |
|||||||
|
/* |
||||||
|
* To change this template, choose Tools | Templates |
||||||
|
* and open the template in the editor. |
||||||
|
*/ |
||||||
|
package com.jme3.scene; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetNotFoundException; |
||||||
|
import com.jme3.bounding.BoundingVolume; |
||||||
|
import com.jme3.export.InputCapsule; |
||||||
|
import com.jme3.export.JmeExporter; |
||||||
|
import com.jme3.export.JmeImporter; |
||||||
|
import com.jme3.export.OutputCapsule; |
||||||
|
import com.jme3.export.Savable; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.Matrix4f; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.mesh.IndexBuffer; |
||||||
|
import com.jme3.util.IntMap.Entry; |
||||||
|
import com.jme3.util.SafeArrayList; |
||||||
|
import com.jme3.util.TempVars; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.Buffer; |
||||||
|
import java.nio.FloatBuffer; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.logging.Level; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
/** |
||||||
|
* BatchNode holds a geometry that is a batched version off all geometries that are in its sub scenegraph. |
||||||
|
* this geometry is directly attached to the node in the scene graph. |
||||||
|
* usage is like any other node except you have to call the batch() method once all geoms have been attached to the sub scene graph (see todo more automagic for further enhancements) |
||||||
|
* all the geometry that have been batched are set to 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. |
||||||
|
* 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. |
||||||
|
* To integrated 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 more automagic (batch when needed in the updateLigicalState) |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class BatchNode extends Node implements Savable { |
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(BatchNode.class.getName()); |
||||||
|
/** |
||||||
|
* the geometry holding the batched mesh |
||||||
|
*/ |
||||||
|
protected Geometry batch; |
||||||
|
protected Material material; |
||||||
|
private boolean needMeshUpdate = false; |
||||||
|
|
||||||
|
public BatchNode() { |
||||||
|
super(); |
||||||
|
} |
||||||
|
|
||||||
|
public BatchNode(String name) { |
||||||
|
super(name); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void updateGeometricState() { |
||||||
|
if ((refreshFlags & RF_LIGHTLIST) != 0) { |
||||||
|
updateWorldLightList(); |
||||||
|
} |
||||||
|
|
||||||
|
if ((refreshFlags & RF_TRANSFORM) != 0) { |
||||||
|
// combine with parent transforms- same for all spatial
|
||||||
|
// subclasses.
|
||||||
|
updateWorldTransforms(); |
||||||
|
} |
||||||
|
|
||||||
|
if (!children.isEmpty()) { |
||||||
|
// the important part- make sure child geometric state is refreshed
|
||||||
|
// first before updating own world bound. This saves
|
||||||
|
// a round-trip later on.
|
||||||
|
// NOTE 9/19/09
|
||||||
|
// Although it does save a round trip,
|
||||||
|
|
||||||
|
for (Spatial child : children.getArray()) { |
||||||
|
child.updateGeometricState(); |
||||||
|
} |
||||||
|
if (needMeshUpdate) { |
||||||
|
batch.getMesh().updateBound(); |
||||||
|
batch.updateWorldBound(); |
||||||
|
needMeshUpdate = false; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
if ((refreshFlags & RF_BOUND) != 0) { |
||||||
|
updateWorldBound(); |
||||||
|
} |
||||||
|
|
||||||
|
assert refreshFlags == 0; |
||||||
|
} |
||||||
|
|
||||||
|
protected void updateSubBatch(Geometry bg) { |
||||||
|
if (batch != null) { |
||||||
|
Mesh mesh = batch.getMesh(); |
||||||
|
|
||||||
|
FloatBuffer buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Position).getData(); |
||||||
|
doTransformVerts(buf, 0, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat); |
||||||
|
mesh.getBuffer(VertexBuffer.Type.Position).updateData(buf); |
||||||
|
|
||||||
|
buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Normal).getData(); |
||||||
|
doTransformNorm(buf, 0, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat); |
||||||
|
mesh.getBuffer(VertexBuffer.Type.Normal).updateData(buf); |
||||||
|
|
||||||
|
|
||||||
|
if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) { |
||||||
|
|
||||||
|
buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Tangent).getData(); |
||||||
|
doTransformNorm(buf, 0, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat); |
||||||
|
mesh.getBuffer(VertexBuffer.Type.Tangent).updateData(buf); |
||||||
|
} |
||||||
|
|
||||||
|
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 |
||||||
|
* every geometry of the sub scene graph of this node will be batched into a single mesh that will be render in one call |
||||||
|
*/ |
||||||
|
public void batch() { |
||||||
|
|
||||||
|
List<Geometry> tmpList = new ArrayList<Geometry>(); |
||||||
|
Mesh m = new Mesh(); |
||||||
|
populateList(tmpList, this); |
||||||
|
mergeGeometries(m, tmpList); |
||||||
|
|
||||||
|
if (batch == null) { |
||||||
|
batch = new Geometry(name + "-batch"); |
||||||
|
batch.setMaterial(material); |
||||||
|
this.attachChild(batch); |
||||||
|
} |
||||||
|
batch.setMesh(m); |
||||||
|
batch.getMesh().updateCounts(); |
||||||
|
batch.getMesh().updateBound(); |
||||||
|
} |
||||||
|
|
||||||
|
private void populateList(List<Geometry> list, Spatial n) { |
||||||
|
|
||||||
|
if (n instanceof Geometry) { |
||||||
|
if (n != batch) { |
||||||
|
list.add((Geometry) n); |
||||||
|
} |
||||||
|
|
||||||
|
} else if (n instanceof Node) { |
||||||
|
for (Spatial child : ((Node) n).getChildren()) { |
||||||
|
populateList(list, child); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the material to use for this geometry. |
||||||
|
* |
||||||
|
* @param material the material to use for this geometry |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void setMaterial(Material material) { |
||||||
|
super.setMaterial(material); |
||||||
|
if (batch != null) { |
||||||
|
batch.setMaterial(material); |
||||||
|
} |
||||||
|
this.material = material; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the material that is used for this geometry. |
||||||
|
* |
||||||
|
* @return the material that is used for this geometry |
||||||
|
* |
||||||
|
* @see #setMaterial(com.jme3.material.Material) |
||||||
|
*/ |
||||||
|
public Material getMaterial() { |
||||||
|
return material; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return The bounding volume of the mesh, in model space. |
||||||
|
*/ |
||||||
|
public BoundingVolume getModelBound() { |
||||||
|
if (batch != null) { |
||||||
|
return batch.getMesh().getBound(); |
||||||
|
} |
||||||
|
return super.getWorldBound(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 |
||||||
|
* animated mesh, (bind pose is set) then the positions |
||||||
|
* and normals are deep copied. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public BatchNode clone(boolean cloneMaterial) { |
||||||
|
BatchNode clone = (BatchNode) super.clone(cloneMaterial); |
||||||
|
clone.batch = batch.clone(cloneMaterial); |
||||||
|
return clone; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 |
||||||
|
* 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 |
||||||
|
public void write(JmeExporter ex) throws IOException { |
||||||
|
super.write(ex); |
||||||
|
OutputCapsule oc = ex.getCapsule(this); |
||||||
|
|
||||||
|
if (material != null) { |
||||||
|
oc.write(material.getAssetName(), "materialName", null); |
||||||
|
} |
||||||
|
oc.write(material, "material", null); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void read(JmeImporter im) throws IOException { |
||||||
|
super.read(im); |
||||||
|
InputCapsule ic = im.getCapsule(this); |
||||||
|
|
||||||
|
|
||||||
|
material = null; |
||||||
|
String matName = ic.readString("materialName", null); |
||||||
|
if (matName != null) { |
||||||
|
// Material name is set,
|
||||||
|
// Attempt to load material via J3M
|
||||||
|
try { |
||||||
|
material = im.getAssetManager().loadMaterial(matName); |
||||||
|
} catch (AssetNotFoundException ex) { |
||||||
|
// Cannot find J3M file.
|
||||||
|
logger.log(Level.FINE, "Could not load J3M file {0} for Geometry.", |
||||||
|
matName); |
||||||
|
} |
||||||
|
} |
||||||
|
// If material is NULL, try to load it from the geometry
|
||||||
|
if (material == null) { |
||||||
|
material = (Material) ic.readSavable("material", null); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Merges all geometries in the collection into |
||||||
|
* the output mesh. Does not take into account materials. |
||||||
|
* |
||||||
|
* @param geometries |
||||||
|
* @param outMesh |
||||||
|
*/ |
||||||
|
private void mergeGeometries(Mesh outMesh, List<Geometry> geometries) { |
||||||
|
int[] compsForBuf = new int[VertexBuffer.Type.values().length]; |
||||||
|
VertexBuffer.Format[] formatForBuf = new VertexBuffer.Format[compsForBuf.length]; |
||||||
|
|
||||||
|
int totalVerts = 0; |
||||||
|
int totalTris = 0; |
||||||
|
int totalLodLevels = 0; |
||||||
|
|
||||||
|
Mesh.Mode mode = null; |
||||||
|
for (Geometry geom : geometries) { |
||||||
|
totalVerts += geom.getVertexCount(); |
||||||
|
totalTris += geom.getTriangleCount(); |
||||||
|
totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels()); |
||||||
|
|
||||||
|
Mesh.Mode listMode; |
||||||
|
int components; |
||||||
|
switch (geom.getMesh().getMode()) { |
||||||
|
case Points: |
||||||
|
listMode = Mesh.Mode.Points; |
||||||
|
components = 1; |
||||||
|
break; |
||||||
|
case LineLoop: |
||||||
|
case LineStrip: |
||||||
|
case Lines: |
||||||
|
listMode = Mesh.Mode.Lines; |
||||||
|
components = 2; |
||||||
|
break; |
||||||
|
case TriangleFan: |
||||||
|
case TriangleStrip: |
||||||
|
case Triangles: |
||||||
|
listMode = Mesh.Mode.Triangles; |
||||||
|
components = 3; |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new UnsupportedOperationException(); |
||||||
|
} |
||||||
|
|
||||||
|
for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()) { |
||||||
|
compsForBuf[entry.getKey()] = entry.getValue().getNumComponents(); |
||||||
|
formatForBuf[entry.getKey()] = entry.getValue().getFormat(); |
||||||
|
} |
||||||
|
|
||||||
|
if (mode != null && mode != listMode) { |
||||||
|
throw new UnsupportedOperationException("Cannot combine different" |
||||||
|
+ " primitive types: " + mode + " != " + listMode); |
||||||
|
} |
||||||
|
mode = listMode; |
||||||
|
compsForBuf[VertexBuffer.Type.Index.ordinal()] = components; |
||||||
|
} |
||||||
|
|
||||||
|
outMesh.setMode(mode); |
||||||
|
if (totalVerts >= 65536) { |
||||||
|
// make sure we create an UnsignedInt buffer so
|
||||||
|
// we can fit all of the meshes
|
||||||
|
formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedInt; |
||||||
|
} else { |
||||||
|
formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedShort; |
||||||
|
} |
||||||
|
|
||||||
|
// generate output buffers based on retrieved info
|
||||||
|
for (int i = 0; i < compsForBuf.length; i++) { |
||||||
|
if (compsForBuf[i] == 0) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
Buffer data; |
||||||
|
if (i == VertexBuffer.Type.Index.ordinal()) { |
||||||
|
data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris); |
||||||
|
} else { |
||||||
|
data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts); |
||||||
|
} |
||||||
|
|
||||||
|
VertexBuffer vb = new VertexBuffer(VertexBuffer.Type.values()[i]); |
||||||
|
vb.setupData(VertexBuffer.Usage.Static, compsForBuf[i], formatForBuf[i], data); |
||||||
|
outMesh.setBuffer(vb); |
||||||
|
} |
||||||
|
|
||||||
|
int globalVertIndex = 0; |
||||||
|
int globalTriIndex = 0; |
||||||
|
|
||||||
|
for (Geometry geom : geometries) { |
||||||
|
Mesh inMesh = geom.getMesh(); |
||||||
|
if (geom.getMaterial() != null && material == null) { |
||||||
|
material = geom.getMaterial(); |
||||||
|
} |
||||||
|
geom.batch(this, globalVertIndex); |
||||||
|
|
||||||
|
int geomVertCount = inMesh.getVertexCount(); |
||||||
|
int geomTriCount = inMesh.getTriangleCount(); |
||||||
|
|
||||||
|
for (int bufType = 0; bufType < compsForBuf.length; bufType++) { |
||||||
|
VertexBuffer inBuf = inMesh.getBuffer(VertexBuffer.Type.values()[bufType]); |
||||||
|
VertexBuffer outBuf = outMesh.getBuffer(VertexBuffer.Type.values()[bufType]); |
||||||
|
|
||||||
|
if (outBuf == null) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (VertexBuffer.Type.Index.ordinal() == bufType) { |
||||||
|
int components = compsForBuf[bufType]; |
||||||
|
|
||||||
|
IndexBuffer inIdx = inMesh.getIndicesAsList(); |
||||||
|
IndexBuffer outIdx = outMesh.getIndexBuffer(); |
||||||
|
|
||||||
|
for (int tri = 0; tri < geomTriCount; tri++) { |
||||||
|
for (int comp = 0; comp < components; comp++) { |
||||||
|
int idx = inIdx.get(tri * components + comp) + globalVertIndex; |
||||||
|
outIdx.put((globalTriIndex + tri) * components + comp, idx); |
||||||
|
} |
||||||
|
} |
||||||
|
} else if (VertexBuffer.Type.Position.ordinal() == bufType) { |
||||||
|
FloatBuffer inPos = (FloatBuffer) inBuf.getData(); |
||||||
|
FloatBuffer outPos = (FloatBuffer) outBuf.getData(); |
||||||
|
doCopyBuffer(inPos, globalVertIndex, outPos); |
||||||
|
} else if (VertexBuffer.Type.Normal.ordinal() == bufType || VertexBuffer.Type.Tangent.ordinal() == bufType) { |
||||||
|
FloatBuffer inPos = (FloatBuffer) inBuf.getData(); |
||||||
|
FloatBuffer outPos = (FloatBuffer) outBuf.getData(); |
||||||
|
doCopyBuffer(inPos, globalVertIndex, outPos); |
||||||
|
} else { |
||||||
|
for (int vert = 0; vert < geomVertCount; vert++) { |
||||||
|
int curGlobalVertIndex = globalVertIndex + vert; |
||||||
|
inBuf.copyElement(vert, outBuf, curGlobalVertIndex); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
globalVertIndex += geomVertCount; |
||||||
|
globalTriIndex += geomTriCount; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void doTransformVerts(FloatBuffer inBuf, int offset, int start, int end, FloatBuffer outBuf, Matrix4f transform) { |
||||||
|
TempVars vars = TempVars.get(); |
||||||
|
Vector3f pos = vars.vect1; |
||||||
|
|
||||||
|
// offset is given in element units
|
||||||
|
// convert to be in component units
|
||||||
|
offset *= 3; |
||||||
|
|
||||||
|
for (int i = start; i < end; i++) { |
||||||
|
int index = i * 3; |
||||||
|
pos.x = inBuf.get(index); |
||||||
|
pos.y = inBuf.get(index + 1); |
||||||
|
pos.z = inBuf.get(index + 2); |
||||||
|
|
||||||
|
transform.mult(pos, pos); |
||||||
|
index += offset; |
||||||
|
outBuf.put(index, pos.x); |
||||||
|
outBuf.put(index + 1, pos.y); |
||||||
|
outBuf.put(index + 2, pos.z); |
||||||
|
} |
||||||
|
vars.release(); |
||||||
|
} |
||||||
|
|
||||||
|
private void doTransformNorm(FloatBuffer inBuf, int offset, int start, int end, FloatBuffer outBuf, Matrix4f transform) { |
||||||
|
TempVars vars = TempVars.get(); |
||||||
|
Vector3f pos = vars.vect1; |
||||||
|
|
||||||
|
// offset is given in element units
|
||||||
|
// convert to be in component units
|
||||||
|
offset *= 3; |
||||||
|
|
||||||
|
for (int i = start; i < end; i++) { |
||||||
|
int index = i * 3; |
||||||
|
pos.x = inBuf.get(index); |
||||||
|
pos.y = inBuf.get(index + 1); |
||||||
|
pos.z = inBuf.get(index + 2); |
||||||
|
|
||||||
|
transform.multNormal(pos, pos); |
||||||
|
index += offset; |
||||||
|
outBuf.put(index, pos.x); |
||||||
|
outBuf.put(index + 1, pos.y); |
||||||
|
outBuf.put(index + 2, pos.z); |
||||||
|
} |
||||||
|
vars.release(); |
||||||
|
} |
||||||
|
|
||||||
|
private void doCopyBuffer(FloatBuffer inBuf, int offset, FloatBuffer outBuf) { |
||||||
|
TempVars vars = TempVars.get(); |
||||||
|
Vector3f pos = vars.vect1; |
||||||
|
|
||||||
|
// offset is given in element units
|
||||||
|
// convert to be in component units
|
||||||
|
offset *= 3; |
||||||
|
|
||||||
|
for (int i = 0; i < inBuf.capacity() / 3; i++) { |
||||||
|
pos.x = inBuf.get(i * 3 + 0); |
||||||
|
pos.y = inBuf.get(i * 3 + 1); |
||||||
|
pos.z = inBuf.get(i * 3 + 2); |
||||||
|
|
||||||
|
outBuf.put(offset + i * 3 + 0, pos.x); |
||||||
|
outBuf.put(offset + i * 3 + 1, pos.y); |
||||||
|
outBuf.put(offset + i * 3 + 2, pos.z); |
||||||
|
} |
||||||
|
vars.release(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,95 @@ |
|||||||
|
/* |
||||||
|
* To change this template, choose Tools | Templates and open the template in |
||||||
|
* the editor. |
||||||
|
*/ |
||||||
|
package jme3test.batching; |
||||||
|
|
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.light.DirectionalLight; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.BatchNode; |
||||||
|
import com.jme3.scene.BatchedGeometry; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.util.TangentBinormalGenerator; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class TestBatchNode extends SimpleApplication { |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
|
||||||
|
TestBatchNode app = new TestBatchNode(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
BatchNode batch; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
batch = new BatchNode("theBatchNode"); |
||||||
|
|
||||||
|
/** |
||||||
|
* A cube with a color "bleeding" through transparent texture. Uses |
||||||
|
* Texture from jme3-test-data library! |
||||||
|
*/ |
||||||
|
Box boxshape4 = new Box(Vector3f.ZERO, 1f, 1f, 1f ); |
||||||
|
cube = new Geometry("cube1", boxshape4); |
||||||
|
Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m"); |
||||||
|
// Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
// mat.setColor("Diffuse", ColorRGBA.Blue);
|
||||||
|
// mat.setBoolean("UseMaterialColors", true);
|
||||||
|
/** |
||||||
|
* A cube with a color "bleeding" through transparent texture. Uses |
||||||
|
* Texture from jme3-test-data library! |
||||||
|
*/ |
||||||
|
Box box = new Box(Vector3f.ZERO, 1f, 1f, 1f); |
||||||
|
cube2 = new Geometry("cube2", box); |
||||||
|
|
||||||
|
|
||||||
|
TangentBinormalGenerator.generate(cube); |
||||||
|
TangentBinormalGenerator.generate(cube2); |
||||||
|
|
||||||
|
|
||||||
|
n = new Node("aNode"); |
||||||
|
// n.attachChild(cube2);
|
||||||
|
batch.attachChild(cube); |
||||||
|
batch.attachChild(cube2); |
||||||
|
batch.setMaterial(mat); |
||||||
|
batch.batch(); |
||||||
|
rootNode.attachChild(batch); |
||||||
|
cube.setLocalTranslation(3, 0, 0); |
||||||
|
cube2.setLocalTranslation(0, 3, 0); |
||||||
|
|
||||||
|
|
||||||
|
dl=new DirectionalLight(); |
||||||
|
dl.setColor(ColorRGBA.White.mult(2)); |
||||||
|
dl.setDirection(new Vector3f(1, -1, -1)); |
||||||
|
rootNode.addLight(dl); |
||||||
|
flyCam.setMoveSpeed(10); |
||||||
|
} |
||||||
|
Node n; |
||||||
|
Geometry cube; |
||||||
|
Geometry cube2; |
||||||
|
float time = 0; |
||||||
|
DirectionalLight dl; |
||||||
|
@Override |
||||||
|
public void simpleUpdate(float tpf) { |
||||||
|
time += tpf; |
||||||
|
dl.setDirection(cam.getDirection()); |
||||||
|
cube2.setLocalTranslation(FastMath.sin(-time)*3, FastMath.cos(time)*3, 0); |
||||||
|
cube2.setLocalRotation(new Quaternion().fromAngleAxis(time, Vector3f.UNIT_Z)); |
||||||
|
cube2.setLocalScale(Math.max(FastMath.sin(time),0.5f)); |
||||||
|
|
||||||
|
batch.setLocalRotation(new Quaternion().fromAngleAxis(time, Vector3f.UNIT_Z)); |
||||||
|
|
||||||
|
} |
||||||
|
//
|
||||||
|
} |
@ -0,0 +1,364 @@ |
|||||||
|
/* |
||||||
|
* To change this template, choose Tools | Templates and open the template in |
||||||
|
* the editor. |
||||||
|
*/ |
||||||
|
package jme3test.batching; |
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.input.KeyInput; |
||||||
|
import com.jme3.input.controls.ActionListener; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.system.AppSettings; |
||||||
|
import com.jme3.input.controls.KeyTrigger; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Random; |
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.math.Quaternion; |
||||||
|
import com.jme3.post.FilterPostProcessor; |
||||||
|
import com.jme3.post.filters.BloomFilter; |
||||||
|
import com.jme3.scene.BatchNode; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.scene.debug.Arrow; |
||||||
|
import com.jme3.system.NanoTimer; |
||||||
|
|
||||||
|
public class TestBatchNodeCluster extends SimpleApplication { |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
TestBatchNodeCluster app = new TestBatchNodeCluster(); |
||||||
|
settingst = new AppSettings(true); |
||||||
|
//settingst.setFrameRate(75);
|
||||||
|
settingst.setResolution(640, 480); |
||||||
|
settingst.setVSync(false); |
||||||
|
settingst.setFullscreen(false); |
||||||
|
app.setSettings(settingst); |
||||||
|
app.setShowSettings(false); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
private ActionListener al = new ActionListener() { |
||||||
|
|
||||||
|
public void onAction(String name, boolean isPressed, float tpf) { |
||||||
|
if (name.equals("Start Game")) { |
||||||
|
// randomGenerator();
|
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
protected Random rand = new Random(); |
||||||
|
protected int maxCubes = 2000; |
||||||
|
protected int startAt = 0; |
||||||
|
protected static int xPositions = 0, yPositions = 0, zPositions = 0; |
||||||
|
protected int returner = 0; |
||||||
|
protected ArrayList<Integer> xPosition = new ArrayList<Integer>(); |
||||||
|
protected ArrayList<Integer> yPosition = new ArrayList<Integer>(); |
||||||
|
protected ArrayList<Integer> zPosition = new ArrayList<Integer>(); |
||||||
|
protected int xLimitf = 60, xLimits = -60, yLimitf = 60, yLimits = -20, zLimitf = 60, zLimits = -60; |
||||||
|
protected int circ = 8;//increases by 8 every time.
|
||||||
|
protected int dynamic = 4; |
||||||
|
protected static AppSettings settingst; |
||||||
|
protected boolean isTrue = true; |
||||||
|
|
||||||
|
private int lineLength = 50; |
||||||
|
protected BatchNode blue; |
||||||
|
protected BatchNode brown; |
||||||
|
protected BatchNode pink; |
||||||
|
protected BatchNode orange; |
||||||
|
|
||||||
|
Material mat1; |
||||||
|
Material mat2; |
||||||
|
Material mat3; |
||||||
|
Material mat4; |
||||||
|
Node terrain; |
||||||
|
//protected
|
||||||
|
// protected Geometry player;
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
timer = new NanoTimer(); |
||||||
|
|
||||||
|
blue = new BatchNode("blue"); |
||||||
|
brown = new BatchNode("brown"); |
||||||
|
pink = new BatchNode("pink"); |
||||||
|
orange = new BatchNode("orange"); |
||||||
|
|
||||||
|
xPosition.add(0); |
||||||
|
yPosition.add(0); |
||||||
|
zPosition.add(0); |
||||||
|
randomGenerator(); |
||||||
|
|
||||||
|
mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
mat1.setColor("Color", ColorRGBA.White); |
||||||
|
mat1.setColor("GlowColor", ColorRGBA.Blue.mult(10)); |
||||||
|
blue.setMaterial(mat1); |
||||||
|
mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
mat2.setColor("Color", ColorRGBA.White); |
||||||
|
mat2.setColor("GlowColor", ColorRGBA.Red.mult(10)); |
||||||
|
brown.setMaterial(mat2); |
||||||
|
mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
mat3.setColor("Color", ColorRGBA.White); |
||||||
|
mat3.setColor("GlowColor", ColorRGBA.Yellow.mult(10)); |
||||||
|
pink.setMaterial(mat3); |
||||||
|
mat4 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
mat4.setColor("Color", ColorRGBA.White); |
||||||
|
mat4.setColor("GlowColor", ColorRGBA.Orange.mult(10)); |
||||||
|
orange.setMaterial(mat4); |
||||||
|
//rootNode.attachChild(SkyFactory.createSky(
|
||||||
|
// assetManager, "Textures/SKY02.zip", false));
|
||||||
|
inputManager.addMapping("Start Game", new KeyTrigger(KeyInput.KEY_J)); |
||||||
|
inputManager.addListener(al, new String[]{"Start Game"}); |
||||||
|
|
||||||
|
|
||||||
|
cam.setLocation(new Vector3f(-34.403286f, 126.65158f, 434.791f)); |
||||||
|
cam.setRotation(new Quaternion(0.022630932f, 0.9749435f, -0.18736298f, 0.11776358f)); |
||||||
|
|
||||||
|
|
||||||
|
blue.batch(); |
||||||
|
brown.batch(); |
||||||
|
pink.batch(); |
||||||
|
orange.batch(); |
||||||
|
|
||||||
|
terrain = new Node("terrain"); |
||||||
|
terrain.setLocalTranslation(50, 0, 50); |
||||||
|
terrain.attachChild(blue); |
||||||
|
terrain.attachChild(brown); |
||||||
|
terrain.attachChild(pink); |
||||||
|
terrain.attachChild(orange); |
||||||
|
flyCam.setMoveSpeed(100); |
||||||
|
rootNode.attachChild(terrain); |
||||||
|
Vector3f pos = new Vector3f(-40, 0, -40); |
||||||
|
blue.setLocalTranslation(pos); |
||||||
|
brown.setLocalTranslation(pos); |
||||||
|
pink.setLocalTranslation(pos); |
||||||
|
orange.setLocalTranslation(pos); |
||||||
|
|
||||||
|
|
||||||
|
Arrow a = new Arrow(new Vector3f(0, 50, 0)); |
||||||
|
Geometry g = new Geometry("a", a); |
||||||
|
g.setLocalTranslation(terrain.getLocalTranslation()); |
||||||
|
Material m = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
m.setColor("Color", ColorRGBA.Blue); |
||||||
|
g.setMaterial(m); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FilterPostProcessor fpp = new FilterPostProcessor(assetManager); |
||||||
|
fpp.addFilter(new BloomFilter(BloomFilter.GlowMode.Objects)); |
||||||
|
// SSAOFilter ssao = new SSAOFilter(8.630104f,22.970434f,2.9299977f,0.2999997f);
|
||||||
|
// fpp.addFilter(ssao);
|
||||||
|
viewPort.addProcessor(fpp); |
||||||
|
// viewPort.setBackgroundColor(ColorRGBA.DarkGray);
|
||||||
|
} |
||||||
|
|
||||||
|
public void randomGenerator() { |
||||||
|
for (int i = startAt; i < maxCubes - 1; i++) { |
||||||
|
randomize(); |
||||||
|
Geometry box = new Geometry("Box" + i, new Box(Vector3f.ZERO, 1, 1, 1)); |
||||||
|
box.setLocalTranslation(new Vector3f(xPosition.get(xPosition.size() - 1), |
||||||
|
yPosition.get(yPosition.size() - 1), |
||||||
|
zPosition.get(zPosition.size() - 1))); |
||||||
|
|
||||||
|
if (i < 500) { |
||||||
|
blue.attachChild(box); |
||||||
|
} else if (i < 1000) { |
||||||
|
brown.attachChild(box); |
||||||
|
} else if (i < 1500) { |
||||||
|
pink.attachChild(box); |
||||||
|
} else { |
||||||
|
orange.attachChild(box); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public BatchNode randomBatch() { |
||||||
|
|
||||||
|
int randomn = rand.nextInt(4); |
||||||
|
if (randomn == 0) { |
||||||
|
return blue; |
||||||
|
} else if (randomn == 1) { |
||||||
|
return brown; |
||||||
|
} else if (randomn == 2) { |
||||||
|
return pink; |
||||||
|
} else if (randomn == 3) { |
||||||
|
return orange; |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public ColorRGBA randomColor() { |
||||||
|
ColorRGBA color = ColorRGBA.Black; |
||||||
|
int randomn = rand.nextInt(4); |
||||||
|
if (randomn == 0) { |
||||||
|
color = ColorRGBA.Orange; |
||||||
|
} else if (randomn == 1) { |
||||||
|
color = ColorRGBA.Blue; |
||||||
|
} else if (randomn == 2) { |
||||||
|
color = ColorRGBA.Brown; |
||||||
|
} else if (randomn == 3) { |
||||||
|
color = ColorRGBA.Magenta; |
||||||
|
} |
||||||
|
return color; |
||||||
|
} |
||||||
|
|
||||||
|
public void randomize() { |
||||||
|
int xpos = xPosition.get(xPosition.size() - 1); |
||||||
|
int ypos = yPosition.get(yPosition.size() - 1); |
||||||
|
int zpos = zPosition.get(zPosition.size() - 1); |
||||||
|
int x = 0; |
||||||
|
int y = 0; |
||||||
|
int z = 0; |
||||||
|
boolean unTrue = true; |
||||||
|
while (unTrue) { |
||||||
|
unTrue = false; |
||||||
|
boolean xChanged = false; |
||||||
|
x = 0; |
||||||
|
y = 0; |
||||||
|
z = 0; |
||||||
|
if (xpos >= lineLength * 2) { |
||||||
|
x = 2; |
||||||
|
xChanged = true; |
||||||
|
} else { |
||||||
|
x = xPosition.get(xPosition.size() - 1) + 2; |
||||||
|
} |
||||||
|
if (xChanged) { |
||||||
|
//y = yPosition.get(yPosition.size() - lineLength) + 2;
|
||||||
|
} else { |
||||||
|
y = rand.nextInt(3); |
||||||
|
if (yPosition.size() > lineLength) { |
||||||
|
if (yPosition.size() > 51) { |
||||||
|
if (y == 0 && ypos < yLimitf && getym(lineLength) > ypos - 2) { |
||||||
|
y = ypos + 2; |
||||||
|
} else if (y == 1 && ypos > yLimits && getym(lineLength) < ypos + 2) { |
||||||
|
y = ypos - 2; |
||||||
|
} else if (y == 2 && getym(lineLength) > ypos - 2 && getym(lineLength) < ypos + 2) { |
||||||
|
y = ypos; |
||||||
|
} else { |
||||||
|
if (ypos >= yLimitf) { |
||||||
|
y = ypos - 2; |
||||||
|
} else if (ypos <= yLimits) { |
||||||
|
y = ypos + 2; |
||||||
|
} else if (y == 0 && getym(lineLength) >= ypos - 4) { |
||||||
|
y = ypos - 2; |
||||||
|
} else if (y == 0 && getym(lineLength) >= ypos - 2) { |
||||||
|
y = ypos; |
||||||
|
} else if (y == 1 && getym(lineLength) >= ypos + 4) { |
||||||
|
y = ypos + 2; |
||||||
|
} else if (y == 1 && getym(lineLength) >= ypos + 2) { |
||||||
|
y = ypos; |
||||||
|
} else if (y == 2 && getym(lineLength) <= ypos - 2) { |
||||||
|
y = ypos - 2; |
||||||
|
} else if (y == 2 && getym(lineLength) >= ypos + 2) { |
||||||
|
y = ypos + 2; |
||||||
|
} else { |
||||||
|
System.out.println("wtf"); |
||||||
|
} |
||||||
|
} |
||||||
|
} else if (yPosition.size() == lineLength) { |
||||||
|
if (y == 0 && ypos < yLimitf) { |
||||||
|
y = getym(lineLength) + 2; |
||||||
|
} else if (y == 1 && ypos > yLimits) { |
||||||
|
y = getym(lineLength) - 2; |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (y == 0 && ypos < yLimitf) { |
||||||
|
y = ypos + 2; |
||||||
|
} else if (y == 1 && ypos > yLimits) { |
||||||
|
y = ypos - 2; |
||||||
|
} else if (y == 2) { |
||||||
|
y = ypos; |
||||||
|
} else if (y == 0 && ypos >= yLimitf) { |
||||||
|
y = ypos - 2; |
||||||
|
} else if (y == 1 && ypos <= yLimits) { |
||||||
|
y = ypos + 2; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (xChanged) { |
||||||
|
z = zpos + 2; |
||||||
|
} else { |
||||||
|
z = zpos; |
||||||
|
} |
||||||
|
// for (int i = 0; i < xPosition.size(); i++)
|
||||||
|
// {
|
||||||
|
// if (x - xPosition.get(i) <= 1 && x - xPosition.get(i) >= -1 &&
|
||||||
|
// y - yPosition.get(i) <= 1 && y - yPosition.get(i) >= -1
|
||||||
|
// &&z - zPosition.get(i) <= 1 && z - zPosition.get(i) >=
|
||||||
|
// -1)
|
||||||
|
// {
|
||||||
|
// unTrue = true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} |
||||||
|
xPosition.add(x); |
||||||
|
yPosition.add(y); |
||||||
|
zPosition.add(z); |
||||||
|
} |
||||||
|
|
||||||
|
public int getxm(int i) { |
||||||
|
return xPosition.get(xPosition.size() - i); |
||||||
|
} |
||||||
|
|
||||||
|
public int getym(int i) { |
||||||
|
return yPosition.get(yPosition.size() - i); |
||||||
|
} |
||||||
|
|
||||||
|
public int getzm(int i) { |
||||||
|
return zPosition.get(zPosition.size() - i); |
||||||
|
} |
||||||
|
|
||||||
|
public int getx(int i) { |
||||||
|
return xPosition.get(i); |
||||||
|
} |
||||||
|
|
||||||
|
public int gety(int i) { |
||||||
|
return yPosition.get(i); |
||||||
|
} |
||||||
|
|
||||||
|
public int getz(int i) { |
||||||
|
return zPosition.get(i); |
||||||
|
} |
||||||
|
long nbFrames = 0; |
||||||
|
long cullTime = 0; |
||||||
|
float time = 0; |
||||||
|
Vector3f lookAtPos = new Vector3f(0, 0, 0); |
||||||
|
float xpos = 0; |
||||||
|
Spatial box; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleUpdate(float tpf) { |
||||||
|
time += tpf; |
||||||
|
int random = rand.nextInt(2000); |
||||||
|
float mult1 = 1.0f; |
||||||
|
float mult2 = 1.0f; |
||||||
|
BatchNode b = null; |
||||||
|
if (random < 500) { |
||||||
|
b = blue; |
||||||
|
mult1 = 1.0f; |
||||||
|
mult2 = 1.0f; |
||||||
|
} else if (random < 1000) { |
||||||
|
b = brown; |
||||||
|
mult1 = -1.0f; |
||||||
|
mult2 = 1.0f; |
||||||
|
} else if (random < 1500) { |
||||||
|
b = pink; |
||||||
|
mult1 = 1.0f; |
||||||
|
mult2 = -1.0f; |
||||||
|
} else if (random <= 2000) { |
||||||
|
b = orange; |
||||||
|
mult1 = -1.0f; |
||||||
|
mult2 = -1.0f; |
||||||
|
} |
||||||
|
box = b.getChild("Box" + random); |
||||||
|
if (box != null) { |
||||||
|
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); |
||||||
|
} |
||||||
|
terrain.setLocalRotation(new Quaternion().fromAngleAxis(time, Vector3f.UNIT_Y)); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,245 @@ |
|||||||
|
/* |
||||||
|
* To change this template, choose Tools | Templates |
||||||
|
* and open the template in the editor. |
||||||
|
*/ |
||||||
|
|
||||||
|
package jme3test.batching; |
||||||
|
|
||||||
|
/* |
||||||
|
* Copyright (c) 2009-2010 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
import jme3test.bullet.*; |
||||||
|
import com.jme3.bullet.BulletAppState; |
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.asset.TextureKey; |
||||||
|
import com.jme3.bullet.PhysicsSpace; |
||||||
|
import com.jme3.bullet.collision.shapes.SphereCollisionShape; |
||||||
|
import com.jme3.bullet.control.RigidBodyControl; |
||||||
|
import com.jme3.font.BitmapText; |
||||||
|
import com.jme3.input.MouseInput; |
||||||
|
import com.jme3.input.controls.ActionListener; |
||||||
|
import com.jme3.input.controls.MouseButtonTrigger; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.Vector2f; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
||||||
|
import com.jme3.scene.BatchNode; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.scene.shape.Sphere; |
||||||
|
import com.jme3.scene.shape.Sphere.TextureMode; |
||||||
|
import com.jme3.shadow.PssmShadowRenderer; |
||||||
|
import com.jme3.shadow.PssmShadowRenderer.CompareMode; |
||||||
|
import com.jme3.shadow.PssmShadowRenderer.FilterMode; |
||||||
|
import com.jme3.texture.Texture; |
||||||
|
import com.jme3.texture.Texture.WrapMode; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author double1984 (tower mod by atom) |
||||||
|
*/ |
||||||
|
public class TestBatchNodeTower extends SimpleApplication { |
||||||
|
|
||||||
|
int bricksPerLayer = 8; |
||||||
|
int brickLayers = 30; |
||||||
|
|
||||||
|
static float brickWidth = .75f, brickHeight = .25f, brickDepth = .25f; |
||||||
|
float radius = 3f; |
||||||
|
float angle = 0; |
||||||
|
|
||||||
|
|
||||||
|
Material mat; |
||||||
|
Material mat2; |
||||||
|
Material mat3; |
||||||
|
PssmShadowRenderer bsr; |
||||||
|
private Sphere bullet; |
||||||
|
private Box brick; |
||||||
|
private SphereCollisionShape bulletCollisionShape; |
||||||
|
|
||||||
|
private BulletAppState bulletAppState; |
||||||
|
BatchNode batchNode = new BatchNode("batch Node"); |
||||||
|
|
||||||
|
public static void main(String args[]) { |
||||||
|
TestBatchNodeTower f = new TestBatchNodeTower(); |
||||||
|
f.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void simpleInitApp() { |
||||||
|
bulletAppState = new BulletAppState(); |
||||||
|
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL); |
||||||
|
// bulletAppState.setEnabled(false);
|
||||||
|
stateManager.attach(bulletAppState); |
||||||
|
bullet = new Sphere(32, 32, 0.4f, true, false); |
||||||
|
bullet.setTextureMode(TextureMode.Projected); |
||||||
|
bulletCollisionShape = new SphereCollisionShape(0.4f); |
||||||
|
|
||||||
|
brick = new Box(Vector3f.ZERO, brickWidth, brickHeight, brickDepth); |
||||||
|
brick.scaleTextureCoordinates(new Vector2f(1f, .5f)); |
||||||
|
//bulletAppState.getPhysicsSpace().enableDebug(assetManager);
|
||||||
|
initMaterial(); |
||||||
|
initTower(); |
||||||
|
initFloor(); |
||||||
|
initCrossHairs(); |
||||||
|
this.cam.setLocation(new Vector3f(0, 25f, 8f)); |
||||||
|
cam.lookAt(Vector3f.ZERO, new Vector3f(0, 1, 0)); |
||||||
|
cam.setFrustumFar(80); |
||||||
|
inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); |
||||||
|
inputManager.addListener(actionListener, "shoot"); |
||||||
|
rootNode.setShadowMode(ShadowMode.Off); |
||||||
|
|
||||||
|
batchNode.batch(); |
||||||
|
batchNode.setShadowMode(ShadowMode.CastAndReceive); |
||||||
|
rootNode.attachChild(batchNode); |
||||||
|
|
||||||
|
|
||||||
|
bsr = new PssmShadowRenderer(assetManager, 1024, 2); |
||||||
|
bsr.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); |
||||||
|
bsr.setLambda(0.55f); |
||||||
|
bsr.setShadowIntensity(0.6f); |
||||||
|
bsr.setCompareMode(CompareMode.Hardware); |
||||||
|
bsr.setFilterMode(FilterMode.PCF4); |
||||||
|
viewPort.addProcessor(bsr); |
||||||
|
} |
||||||
|
|
||||||
|
private PhysicsSpace getPhysicsSpace() { |
||||||
|
return bulletAppState.getPhysicsSpace(); |
||||||
|
} |
||||||
|
private ActionListener actionListener = new ActionListener() { |
||||||
|
|
||||||
|
public void onAction(String name, boolean keyPressed, float tpf) { |
||||||
|
if (name.equals("shoot") && !keyPressed) { |
||||||
|
Geometry bulletg = new Geometry("bullet", bullet); |
||||||
|
bulletg.setMaterial(mat2); |
||||||
|
bulletg.setShadowMode(ShadowMode.CastAndReceive); |
||||||
|
bulletg.setLocalTranslation(cam.getLocation()); |
||||||
|
RigidBodyControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1); |
||||||
|
// RigidBodyControl bulletNode = new RigidBodyControl(bulletCollisionShape, 1);
|
||||||
|
bulletNode.setLinearVelocity(cam.getDirection().mult(25)); |
||||||
|
bulletg.addControl(bulletNode); |
||||||
|
rootNode.attachChild(bulletg); |
||||||
|
getPhysicsSpace().add(bulletNode); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public void initTower() { |
||||||
|
double tempX = 0; |
||||||
|
double tempY = 0; |
||||||
|
double tempZ = 0; |
||||||
|
angle = 0f; |
||||||
|
for (int i = 0; i < brickLayers; i++){ |
||||||
|
// Increment rows
|
||||||
|
if(i!=0) |
||||||
|
tempY+=brickHeight*2; |
||||||
|
else |
||||||
|
tempY=brickHeight; |
||||||
|
// Alternate brick seams
|
||||||
|
angle = 360.0f / bricksPerLayer * i/2f; |
||||||
|
for (int j = 0; j < bricksPerLayer; j++){ |
||||||
|
tempZ = Math.cos(Math.toRadians(angle))*radius; |
||||||
|
tempX = Math.sin(Math.toRadians(angle))*radius; |
||||||
|
System.out.println("x="+((float)(tempX))+" y="+((float)(tempY))+" z="+(float)(tempZ)); |
||||||
|
Vector3f vt = new Vector3f((float)(tempX), (float)(tempY), (float)(tempZ)); |
||||||
|
// Add crenelation
|
||||||
|
if (i==brickLayers-1){ |
||||||
|
if (j%2 == 0){ |
||||||
|
addBrick(vt); |
||||||
|
} |
||||||
|
} |
||||||
|
// Create main tower
|
||||||
|
else { |
||||||
|
addBrick(vt); |
||||||
|
} |
||||||
|
angle += 360.0/bricksPerLayer; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void initFloor() { |
||||||
|
Box floorBox = new Box(Vector3f.ZERO, 10f, 0.1f, 5f); |
||||||
|
floorBox.scaleTextureCoordinates(new Vector2f(3, 6)); |
||||||
|
|
||||||
|
Geometry floor = new Geometry("floor", floorBox); |
||||||
|
floor.setMaterial(mat3); |
||||||
|
floor.setShadowMode(ShadowMode.Receive); |
||||||
|
floor.setLocalTranslation(0, 0, 0); |
||||||
|
floor.addControl(new RigidBodyControl(0)); |
||||||
|
this.rootNode.attachChild(floor); |
||||||
|
this.getPhysicsSpace().add(floor); |
||||||
|
} |
||||||
|
|
||||||
|
public void initMaterial() { |
||||||
|
mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg"); |
||||||
|
key.setGenerateMips(true); |
||||||
|
Texture tex = assetManager.loadTexture(key); |
||||||
|
mat.setTexture("ColorMap", tex); |
||||||
|
|
||||||
|
mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG"); |
||||||
|
key2.setGenerateMips(true); |
||||||
|
Texture tex2 = assetManager.loadTexture(key2); |
||||||
|
mat2.setTexture("ColorMap", tex2); |
||||||
|
|
||||||
|
mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg"); |
||||||
|
key3.setGenerateMips(true); |
||||||
|
Texture tex3 = assetManager.loadTexture(key3); |
||||||
|
tex3.setWrap(WrapMode.Repeat); |
||||||
|
mat3.setTexture("ColorMap", tex3); |
||||||
|
} |
||||||
|
|
||||||
|
public void addBrick(Vector3f ori) { |
||||||
|
Geometry reBoxg = new Geometry("brick", brick); |
||||||
|
reBoxg.setMaterial(mat); |
||||||
|
reBoxg.setLocalTranslation(ori); |
||||||
|
reBoxg.rotate(0f, (float)Math.toRadians(angle) , 0f ); |
||||||
|
reBoxg.addControl(new RigidBodyControl(1.5f)); |
||||||
|
reBoxg.setShadowMode(ShadowMode.CastAndReceive); |
||||||
|
reBoxg.getControl(RigidBodyControl.class).setFriction(1.6f); |
||||||
|
this.batchNode.attachChild(reBoxg); |
||||||
|
this.getPhysicsSpace().add(reBoxg); |
||||||
|
} |
||||||
|
|
||||||
|
protected void initCrossHairs() { |
||||||
|
guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); |
||||||
|
BitmapText ch = new BitmapText(guiFont, false); |
||||||
|
ch.setSize(guiFont.getCharSet().getRenderedSize() * 2); |
||||||
|
ch.setText("+"); // crosshairs
|
||||||
|
ch.setLocalTranslation( // center
|
||||||
|
settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2, |
||||||
|
settings.getHeight() / 2 + ch.getLineHeight() / 2, 0); |
||||||
|
guiNode.attachChild(ch); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue