From cd474b2be92f6b67bb11509cea7e045d768e133a Mon Sep 17 00:00:00 2001 From: "rem..om" Date: Mon, 29 Aug 2011 18:35:14 +0000 Subject: [PATCH] Engine : - WIP : Added new batching system : GeometryBatch, that allow batched geometries to be transformed git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8128 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../core/com/jme3/scene/BatchedGeometry.java | 135 ++++++ .../core/com/jme3/scene/GeometryBatch.java | 398 +++++++++++++++++ .../jme3test/batching/TestBatchedTower.java | 246 +++++++++++ .../jme3test/batching/TestCubeCluster.java | 399 ++++++++++++++++++ .../jme3test/batching/TestGeometryBatch.java | 92 ++++ 5 files changed, 1270 insertions(+) create mode 100644 engine/src/core/com/jme3/scene/BatchedGeometry.java create mode 100644 engine/src/core/com/jme3/scene/GeometryBatch.java create mode 100644 engine/src/test/jme3test/batching/TestBatchedTower.java create mode 100644 engine/src/test/jme3test/batching/TestCubeCluster.java create mode 100644 engine/src/test/jme3test/batching/TestGeometryBatch.java diff --git a/engine/src/core/com/jme3/scene/BatchedGeometry.java b/engine/src/core/com/jme3/scene/BatchedGeometry.java new file mode 100644 index 000000000..ea63b4001 --- /dev/null +++ b/engine/src/core/com/jme3/scene/BatchedGeometry.java @@ -0,0 +1,135 @@ +/* + * To change this template, choose Tools | Templates and open the template in + * the editor. + */ +package com.jme3.scene; + +import com.jme3.bounding.BoundingVolume; +import com.jme3.collision.Collidable; +import com.jme3.collision.CollisionResults; +import com.jme3.collision.UnsupportedCollisionException; +import com.jme3.math.Matrix4f; +import com.jme3.math.Transform; +import com.jme3.util.TempVars; +import java.util.Queue; + +/** + * + * @author Nehon + */ +public class BatchedGeometry extends Spatial { + + private GeometryBatch batch; + protected int startIndex; + protected int vertexCount; + protected int triangleCount; + protected Transform prevLocalTransform = new Transform(); + protected Matrix4f cachedOffsetMat = new Matrix4f(); + protected Matrix4f tmpMat = new Matrix4f(); + + + protected BatchedGeometry(GeometryBatch batch, Geometry geom) { + this.batch = batch; + vertexCount = geom.getVertexCount(); + triangleCount = geom.getTriangleCount(); + name = geom.getName(); + } + + /** + * Should only be called from updateGeometricState(). + * In most cases should not be subclassed. + */ + @Override + protected void updateWorldTransforms() { + if (batch == null) { + worldTransform.set(localTransform); + refreshFlags &= ~RF_TRANSFORM; + } else { + // check if transform for parent is updated + assert ((batch.refreshFlags & RF_TRANSFORM) == 0); + worldTransform.set(localTransform); + worldTransform.combineWithParent(batch.worldTransform); + computeOffsetTransform(); + batch.updateSubBatch(this); + prevLocalTransform.set(localTransform); + refreshFlags &= ~RF_TRANSFORM; + } + } + + @Override + public Node getParent() { + return batch.getParent(); + } + + + /** + * Recomputes the matrix returned by {@link Geometry#getWorldMatrix() }. + * This will require a localized transform update for this geometry. + */ + public void computeOffsetTransform() { + + + // Compute the cached world matrix + cachedOffsetMat.loadIdentity(); + cachedOffsetMat.setRotationQuaternion(prevLocalTransform.getRotation()); + cachedOffsetMat.setTranslation(prevLocalTransform.getTranslation()); + + TempVars vars = TempVars.get(); + Matrix4f scaleMat = vars.tempMat4; + scaleMat.loadIdentity(); + scaleMat.scale(prevLocalTransform.getScale()); + cachedOffsetMat.multLocal(scaleMat); + cachedOffsetMat.invertLocal(); + + tmpMat.loadIdentity(); + tmpMat.setRotationQuaternion(localTransform.getRotation()); + tmpMat.setTranslation(localTransform.getTranslation()); + scaleMat.loadIdentity(); + scaleMat.scale(localTransform.getScale()); + tmpMat.multLocal(scaleMat); + + tmpMat.mult(cachedOffsetMat,cachedOffsetMat); + + vars.release(); + } + + + @Override + public void updateModelBound() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void setModelBound(BoundingVolume modelBound) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public int getVertexCount() { + return vertexCount; + } + + @Override + public int getTriangleCount() { + return triangleCount; + } + + @Override + public Spatial deepClone() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void depthFirstTraversal(SceneGraphVisitor visitor) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue) { + throw new UnsupportedOperationException("Not supported."); + } + + public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException { + throw new UnsupportedOperationException("Not supported yet."); + } +} diff --git a/engine/src/core/com/jme3/scene/GeometryBatch.java b/engine/src/core/com/jme3/scene/GeometryBatch.java new file mode 100644 index 000000000..8f14431c1 --- /dev/null +++ b/engine/src/core/com/jme3/scene/GeometryBatch.java @@ -0,0 +1,398 @@ +/* + * To change this template, choose Tools | Templates and open the template in + * the editor. + */ +package com.jme3.scene; + +import com.jme3.math.Matrix4f; +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.control.AbstractControl; +import com.jme3.scene.mesh.IndexBuffer; +import com.jme3.util.IntMap.Entry; +import com.jme3.util.SafeArrayList; +import com.jme3.util.TempVars; +import java.nio.Buffer; +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Nehon + */ +public class GeometryBatch extends Geometry { + + private SafeArrayList children = new SafeArrayList(BatchedGeometry.class); + private List tmpList = new ArrayList(); + private boolean needMeshUpdate = false; + + public GeometryBatch() { + this("GeometryBatch"); + } + + public GeometryBatch(String name) { + this.name = name; + addControl(new ControlUpdate()); + } + + public BatchedGeometry batch(Geometry geom) { + + tmpList.clear(); + Mesh m = new Mesh(); + if (mesh != null) { + + tmpList.add(this); + } + tmpList.add(geom); + List l = mergeGeometries(m, tmpList); + mesh = m; + mesh.updateCounts(); + mesh.updateBound(); + return l.get(0); + + } + + public List batch(List geom) { + if (mesh != null) { + geom.add(0, this); + } + + Mesh m = new Mesh(); + List l = mergeGeometries(m, geom); + mesh = m; + mesh.updateCounts(); + mesh.updateBound(); + return l; + } + + public List batch(Geometry... geoms) { + tmpList.clear(); + Mesh m = new Mesh(); + if (mesh != null) { + tmpList.add(this); + + } + for (Geometry geometry : geoms) { + tmpList.add(geometry); + } + + List l = mergeGeometries(m, tmpList); + mesh = m; + mesh.updateCounts(); + mesh.updateBound(); + return l; + } + + @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) { + updateModelBound(); + needMeshUpdate = false; + } + } + + if ((refreshFlags & RF_BOUND) != 0) { + updateWorldBound(); + } + + assert refreshFlags == 0; + } + + protected void updateSubBatch(BatchedGeometry bg) { + 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; + } + + /** + * getChild returns the first child found with exactly the + * given name (case sensitive.) + * + * @param name + * the name of the child to retrieve. If null, we'll return null. + * @return the child if found, or null. + */ + public Spatial getChild(String name) { + if (name == null) { + return null; + } + + for (Spatial child : children.getArray()) { + if (name.equals(child.getName())) { + return child; + } else if (child instanceof Node) { + Spatial out = ((Node) child).getChild(name); + if (out != null) { + return out; + } + } + } + return null; + } + + /** + * Merges all geometries in the collection into + * the output mesh. Does not take into account materials. + * + * @param geometries + * @param outMesh + */ + private List mergeGeometries(Mesh outMesh, List geometries) { + int[] compsForBuf = new int[VertexBuffer.Type.values().length]; + VertexBuffer.Format[] formatForBuf = new VertexBuffer.Format[compsForBuf.length]; + List batchedGeoms = new ArrayList(); + + 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 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) { + if (geom != this) { + BatchedGeometry bg = new BatchedGeometry(this, geom); + bg.startIndex = globalVertIndex; + bg.setLocalTransform(geom.getLocalTransform()); + children.add(bg); + batchedGeoms.add(bg); + } + Mesh inMesh = geom.getMesh(); + + 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; + } + return batchedGeoms; + } + + 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++) { + pos.x = inBuf.get(i * 3 + 0); + pos.y = inBuf.get(i * 3 + 1); + pos.z = inBuf.get(i * 3 + 2); + + transform.mult(pos, pos); + + 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(); + } + + 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++) { + pos.x = inBuf.get(i * 3 + 0); + pos.y = inBuf.get(i * 3 + 1); + pos.z = inBuf.get(i * 3 + 2); + + transform.multNormal(pos, pos); + + 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(); + } + + 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(); + } + + public class ControlUpdate extends AbstractControl { + + @Override + protected void controlUpdate(float tpf) { + for (BatchedGeometry batchedGeometry : children) { + for (int i = 0; i < batchedGeometry.getNumControls(); i++) { + batchedGeometry.getControl(i).update(tpf); + } + } + } + + @Override + protected void controlRender(RenderManager rm, ViewPort vp) { + for (BatchedGeometry batchedGeometry : children) { + for (int i = 0; i < batchedGeometry.getNumControls(); i++) { + batchedGeometry.getControl(i).render(rm, vp); + } + } + } + + public com.jme3.scene.control.Control cloneForSpatial(Spatial spatial) { + return null; + } + } +} diff --git a/engine/src/test/jme3test/batching/TestBatchedTower.java b/engine/src/test/jme3test/batching/TestBatchedTower.java new file mode 100644 index 000000000..0225dc759 --- /dev/null +++ b/engine/src/test/jme3test/batching/TestBatchedTower.java @@ -0,0 +1,246 @@ +/* + * 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 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.BoxCollisionShape; +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.BatchedGeometry; +import com.jme3.scene.Geometry; +import com.jme3.scene.GeometryBatch; +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; +import java.util.ArrayList; +import java.util.List; +import jme3test.bullet.BombControl; + +/** + * + * @author double1984 (tower mod by atom) + */ +public class TestBatchedTower extends SimpleApplication { + + int bricksPerLayer = 8; + int brickLayers = 30; + static float brickWidth = .75f, brickHeight = .25f, brickDepth = .25f; + float radius = 3f; + float angle = 0; + List geoms = new ArrayList(); + Material mat; + Material mat2; + Material mat3; + PssmShadowRenderer bsr; + private Sphere bullet; + private Box brick; + private SphereCollisionShape bulletCollisionShape; + private GeometryBatch batch = new GeometryBatch(); + private BulletAppState bulletAppState; + + public static void main(String args[]) { + TestBatchedTower f = new TestBatchedTower(); + 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(); + + List bgs = batch.batch(geoms); + for (BatchedGeometry bg : bgs) { + RigidBodyControl rb = new RigidBodyControl(1.5f); + rb.setCollisionShape(new BoxCollisionShape(new Vector3f(brick.getXExtent(), brick.getYExtent(), brick.getZExtent()))); + rb.setFriction(1.6f); + bg.addControl(rb); + this.getPhysicsSpace().add(bg); + } + + batch.setMaterial(mat); + batch.setShadowMode(ShadowMode.CastAndReceive); + + 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); + bsr = new PssmShadowRenderer(assetManager, 1024, 2); + bsr.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); + bsr.setLambda(0.50f); + bsr.setShadowIntensity(0.6f); + bsr.setCompareMode(CompareMode.Hardware); + bsr.setFilterMode(FilterMode.PCF4); + viewPort.addProcessor(bsr); + flyCam.setMoveSpeed(50); + rootNode.attachChild(batch); + } + + 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.setLocalTranslation(ori); + reBoxg.rotate(0f, (float) Math.toRadians(angle), 0f); + geoms.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); + } +} \ No newline at end of file diff --git a/engine/src/test/jme3test/batching/TestCubeCluster.java b/engine/src/test/jme3test/batching/TestCubeCluster.java new file mode 100644 index 000000000..cf37870cd --- /dev/null +++ b/engine/src/test/jme3test/batching/TestCubeCluster.java @@ -0,0 +1,399 @@ +/* + * 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.font.BitmapText; +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.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +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.light.DirectionalLight; +import com.jme3.math.FastMath; +import com.jme3.math.Matrix3f; +import com.jme3.math.Quaternion; +import com.jme3.post.FilterPostProcessor; +import com.jme3.post.filters.BloomFilter; +import com.jme3.post.ssao.SSAOFilter; +import com.jme3.scene.BatchedGeometry; +import com.jme3.scene.GeometryBatch; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.control.AbstractControl; +import com.jme3.scene.control.Control; +import com.jme3.scene.debug.Arrow; +import java.util.List; +import jme3test.post.SSAOUI; + +public class TestCubeCluster extends SimpleApplication { + + public static void main(String[] args) { + TestCubeCluster app = new TestCubeCluster(); + 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 xPosition = new ArrayList(); + protected ArrayList yPosition = new ArrayList(); + protected ArrayList zPosition = new ArrayList(); + 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 BitmapText helloText2; + private int lineLength = 50; + protected GeometryBatch blue; + protected GeometryBatch brown; + protected GeometryBatch pink; + protected GeometryBatch orange; + protected List blueList = new ArrayList(); + protected List brownList = new ArrayList(); + protected List pinkList = new ArrayList(); + protected List orangeList = new ArrayList(); + Material mat1; + Material mat2; + Material mat3; + Material mat4; + Node terrain; + //protected +// protected Geometry player; + + @Override + public void simpleInitApp() { + + + blue = new GeometryBatch("blue"); + brown = new GeometryBatch("brown"); + pink = new GeometryBatch("pink"); + orange = new GeometryBatch("orange"); + 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)); + + + //ChaseCamera c = new ChaseCamera(); + //flyCam.setEnabled(false); + xPosition.add(0); + yPosition.add(0); + zPosition.add(0); + + + randomGenerator(); + + + List l = blue.batch(blueList); +// for (Iterator it = l.iterator(); it.hasNext();) { +// BatchedGeometry batchedGeometry = it.next(); +// batchedGeometry.addControl(new MyControl()); +// } + l = brown.batch(brownList); +// for (Iterator it = l.iterator(); it.hasNext();) { +// BatchedGeometry batchedGeometry = it.next(); +// batchedGeometry.addControl(new MyControl()); +// } + l = pink.batch(pinkList); +// for (Iterator it = l.iterator(); it.hasNext();) { +// BatchedGeometry batchedGeometry = it.next(); +// batchedGeometry.addControl(new MyControl()); +// } + + l = orange.batch(orangeList); +// for (Iterator it = l.iterator(); it.hasNext();) { +// BatchedGeometry batchedGeometry = it.next(); +// batchedGeometry.addControl(new MyControl()); +// +// } + // GeometryBatchFactory.optimize(terrain); + 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); + // rootNode.attachChild(g); + + + 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) { + blueList.add(box); + } else if (i < 1000) { + brownList.add(box); + } else if (i < 1500) { + pinkList.add(box); + } else { + orangeList.add(box); + } + + } + } + + public GeometryBatch 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; + GeometryBatch 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)); + + + } +} diff --git a/engine/src/test/jme3test/batching/TestGeometryBatch.java b/engine/src/test/jme3test/batching/TestGeometryBatch.java new file mode 100644 index 000000000..98df0a1e3 --- /dev/null +++ b/engine/src/test/jme3test/batching/TestGeometryBatch.java @@ -0,0 +1,92 @@ +/* + * 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.BatchedGeometry; +import com.jme3.scene.Geometry; +import com.jme3.scene.GeometryBatch; +import com.jme3.scene.shape.Box; +import com.jme3.util.TangentBinormalGenerator; + +/** + * + * @author Nehon + */ +public class TestGeometryBatch extends SimpleApplication { + + public static void main(String[] args) { + + TestGeometryBatch app = new TestGeometryBatch(); + app.start(); + } + GeometryBatch batch; + + @Override + public void simpleInitApp() { + batch = new GeometryBatch("MyBatch"); + + /** + * 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); + Geometry 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); + cube.setLocalTranslation(3, 0, 0); + cube2.setLocalTranslation(0, 3, 0); + + TangentBinormalGenerator.generate(cube); + TangentBinormalGenerator.generate(cube2); + + + batch.batch(cube,cube2); + + batchedCube1 = (BatchedGeometry) batch.getChild("cube1"); + batchedCube2 = (BatchedGeometry) batch.getChild("cube2"); + + batch.setMaterial(mat); + rootNode.attachChild(batch); + + + dl=new DirectionalLight(); + dl.setColor(ColorRGBA.White.mult(2)); + dl.setDirection(new Vector3f(1, -1, -1)); + rootNode.addLight(dl); + flyCam.setMoveSpeed(10); + } + BatchedGeometry batchedCube1; + BatchedGeometry batchedCube2; + Geometry cube2; + float time = 0; + DirectionalLight dl; + @Override + public void simpleUpdate(float tpf) { + time += tpf; + dl.setDirection(cam.getDirection()); + batchedCube2.setLocalTranslation(FastMath.sin(-time)*3, FastMath.cos(time)*3, 0); + batchedCube2.setLocalRotation(new Quaternion().fromAngleAxis(time, Vector3f.UNIT_Z)); + batchedCube2.setLocalScale(Math.max(FastMath.sin(time),0.5f)); + + batch.setLocalRotation(new Quaternion().fromAngleAxis(time, Vector3f.UNIT_Z)); + + } +// +}