Here’s an example (code by @ehnohpe ) where I don’t know how to detect which cube (out of all 2000 of them) was hit, that is, after I ran GeometryBatchFactory.optimize() , though the cubes’ geoms are still stored in the field cube
they cannot be found when getting the collided geom ie. via CollisionResult.getClosestCollision().getGeometry():
Well I mean, you are detecting the geometry but no idea of which cube it is part of, or rather which cube is it, to remove it also from the internally kept “map”
pre type="java"
package org.jme3.tests;
import java.util.ArrayList;
import java.util.Random;
import jme3tools.optimize.GeometryBatchFactory;
import com.jme3.app.SimpleApplication;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Ray;
import com.jme3.math.Triangle;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Cylinder;
import com.jme3.system.AppSettings;
public class LotsaCubesOptimized extends SimpleApplication {
public static void main(String args) {
LotsaCubesOptimized app = new LotsaCubesOptimized();
settingst = new AppSettings(true);
// settingst.setFrameRate( 75 );
settingst.setResolution(1280, 700);
settingst.setVSync(true);
settingst.setFullscreen(false);
app.setSettings(settingst);
app.setShowSettings(false);
app.start();
}
private final ActionListener al = new ActionListener() {
@Override
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 ArrayList cube = new ArrayList();
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 final int lineLength = 50;
protected Node terrain;
Material mat1;
Material mat2;
Material mat3;
Material mat4;
// protected
// protected Geometry player;
@Override
public void simpleInitApp() {
// guiNode.detachAllChildren();
// guiFont = assetManager.loadFont(“Interface/Fonts/Default.fnt”);
// BitmapText helloText = new BitmapText(guiFont, false);
// helloText.setSize(guiFont.getCharSet().getRenderedSize());
// helloText.setText(“Created by: Veenen, Inc.”);
// helloText.setLocalTranslation(5, helloText.getLineHeight(), 0);
// helloText2 = new BitmapText(guiFont, false);
// helloText2.setSize(guiFont.getCharSet().getRenderedSize());
// helloText2.setText(“Cubes: " + startAt + 1);
// helloText2.move(5, 52, 0);
// fpsText.move(5, 15, 0);
// guiNode.attachChild(helloText);
// guiNode.attachChild(helloText2);
// guiNode.attachChild(fpsText);
terrain = new Node();
mat1 = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
mat1.setColor(“Color”, ColorRGBA.Blue);
mat2 = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
mat2.setColor(“Color”, ColorRGBA.Brown);
mat3 = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
mat3.setColor(“Color”, ColorRGBA.Pink);
mat4 = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
mat4.setColor(“Color”, ColorRGBA.Orange);
// 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” });
flyCam.setMoveSpeed(25);
// ChaseCamera c = new ChaseCamera();
// flyCam.setEnabled(false);
xPosition.add(0);
yPosition.add(0);
zPosition.add(0);
Box box1 = new Box(new Vector3f(xPosition.get(xPosition.size() - 1),
yPosition.get(yPosition.size() - 1), zPosition.get(zPosition
.size() - 1)), 1, 1, 1);
Geometry blue = new Geometry(“Box”, box1);
Material mat11 = new Material(assetManager,
“Common/MatDefs/Misc/Unshaded.j3md”);
mat11.setColor(“Color”, ColorRGBA.Cyan);
blue.setMaterial(mat11);
terrain.attachChild(blue);
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(-0.1f, 50, -1.0f));
sun.setLastDistance(500);
rootNode.addLight(sun);
randomGenerator();
GeometryBatchFactory.optimize(terrain);
Geometry geo = new Geometry(“xx”, new Box(19, 1, 1));
geo.setMaterial(randomMaterial());
terrain.detachAllChildren();
terrain.attachChild(geo);
for (int i = 0; i < cube.size(); i++) {
terrain.attachChild(cube.get(i));
}
GeometryBatchFactory.optimize(terrain);
System.out.println(terrain.getQuantity());
rootNode.attachChild(terrain);
inputManager.addMapping(mappingBoom,
new KeyTrigger(KeyInput.KEY_SPACE), new MouseButtonTrigger(0));
inputManager.addListener(actionListener, mappingBoom);
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);
cylinderMat = new Material(assetManager,
“Common/MatDefs/Misc/SolidColor.j3md”);
cylinderMat.setColor(“Color”, ColorRGBA.Red);
}
private Material cylinderMat;
private final static String mappingBoom = “boom”;
/**
*/
private final ActionListener actionListener = new ActionListener() {
@SuppressWarnings(“synthetic-access”)
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if ((name == mappingBoom) && (isPressed)) {
// 1. Reset results list.
CollisionResults results = new CollisionResults();
// 2. Aim the ray from cam loc to cam direction.
Ray ray = new Ray(cam.getLocation(), cam.getDirection());
terrain.collideWith(ray, results);
if (results.size() > 0) {
// The closest collision point is what was truly hit:
CollisionResult closest = results.getClosestCollision();
Vector3f target = closest.getContactPoint().clone();
Vector3f from = cam.getLocation().clone();
// move 20% more in front of camera
from = FastMath.interpolateLinear(0.2f, from, target);
// and now calculate 50% distance of that from to target
Vector3f halfDistance = FastMath.interpolateLinear(0.5f,
from, target);
float len = target.subtract(from).length();
Cylinder l = new Cylinder(10, 10, 0.2f, len, true);
Geometry geo = new Geometry(“ray”, l);
geo.setMaterial(cylinderMat);
geo.setLocalTranslation(halfDistance);
rootNode.attachChild(geo);
geo.lookAt(closest.getContactPoint().clone(), cam.getUp());
// already cloned getUp()
System.out.println("geoName: "
- closest.getGeometry().getName() + " in
cube
:" - cube.contains(closest.getGeometry())
- " userData:"
- closest.getGeometry().getUserData(“a”));
// Triangle store = null;
// closest.getTriangle(store);
// closest.getGeometry().getMesh().
// mark.setLocalTranslation(closest.getContactPoint());
}
// for (int i = 0; i < results.size(); i++) {
// // For each hit, we know distance, impact point, name of
// // geometry.
// CollisionResult cr = results.getCollision(i);
// float dist = cr
//
// .getDistance();
// Vector3f pt = cr.getContactPoint();
// String hit = cr.getGeometry().getName();
// System.out.println(“* Collision #” + i);
// System.out.println(" You shot " + hit + " at " + pt + ", “
// + dist + " wu away.”);
// Geometry oneSphere = mark.clone();
// oneSphere.setLocalTranslation(cr.getContactPoint());
// marks.attachChild(oneSphere);
// }
}
}
};
public void randomGenerator() {
for (int i = startAt; i < maxCubes - 1; i++) {
randomize();
Geometry box = new Geometry(“Box”, new Box(new Vector3f(
xPosition.get(xPosition.size() - 1),
yPosition.get(yPosition.size() - 1),
zPosition.get(zPosition.size() - 1)), 1, 1, 1));
box.setMaterial(randomMaterial());
box.setUserData(“a”, “” + i);
cube.add(box);
// GeometryBatchFactory.optimize( box );
terrain.attachChild(box);
}
}
public Material randomMaterial() {
Material returnermat = new Material();
int randomn = rand.nextInt(4);
if (randomn == 0) {
returnermat = mat1;
} else if (randomn == 1) {
returnermat = mat2;
} else if (randomn == 2) {
returnermat = mat3;
} else if (randomn == 3) {
returnermat = mat4;
}
return returnermat;
}
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);
}
@Override
public void simpleUpdate(float tpf) {
// helloText2.setText("Cubes: " + xPosition.size() + ", " +
// settingst.getWidth());
}
}
/pre
EDIT:
meanwhile I found this:
Side-effects
Using GeometryBatchFactory merges individual Geometries into a single mesh. Thereby it becomes hard to apply specific Materials or to remove a single Geometry. Therefore it should be used for static Geometry only that does not require frequent changes or individual materials/texturing.
on this page: https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:intermediate:optimization?s[]=geometrybatchfactory&s[]=optimize