I created a model to test triangle picking.
findPick works well most of the time.
but sometimes some specific triangles doesn’t collide with rays on some direction
It’s very weird. Please look at the images below.
The first image shows some part of a triangle can be picked but other part of a triangle can not.
However, if ray’s direction is changed, all part of the triangle is picked (Second image)
To test it, I modified TestTrianglePick.java test code
After running the test,
type number keys (‘0’, ‘1’, ‘2’ …) then
Picking ray is showed. If picking fails, error message is printed through syserr.
I post the test code and modeling file
http://mulova.tistory.com/attachment/jk0.zip
Appreciate in advance. :lol:
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.FloatBuffer;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme.app.AbstractGame;
import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.AbsoluteMouse;
import com.jme.input.FirstPersonHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.MouseInput;
import com.jme.intersection.PickData;
import com.jme.intersection.TrianglePickResults;
import com.jme.math.Ray;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Line;
import com.jme.scene.Point;
import com.jme.scene.SceneElement;
import com.jme.scene.Spatial;
import com.jme.scene.batch.TriangleBatch;
import com.jme.scene.shape.AxisRods;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.MaterialState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.util.TextureManager;
import com.jme.util.export.binary.BinaryImporter;
import com.jme.util.geom.BufferUtils;
import com.rontab.bim.util.RenderUtil;
/**
* Started Date: Jul 22, 2004 <br>
* <br>
*
* Demonstrates picking with the mouse.
*
* @author Jack Lindamood
*/
public class TestTrianglePick extends SimpleGame {
private static final Logger logger = Logger
.getLogger(TestTrianglePick.class.getName());
private static final String FILE_PATH = "C:/test.jme";
private Line line = new Line();
// This will be my mouse
AbsoluteMouse am;
private Point pointSelection;
Spatial maggie;
private Line[] selection;
public static void main(String[] args) {
Logger.getLogger("").setLevel(Level.WARNING);
TestTrianglePick app = new TestTrianglePick();
app.setDialogBehaviour(AbstractGame.ALWAYS_SHOW_PROPS_DIALOG);
app.start();
}
protected void simpleInitGame() {
cam.setLocation(new Vector3f(-20, 10, -50));
cam.lookAt(new Vector3f(-150, 0, -60), Vector3f.UNIT_Y);
((FirstPersonHandler)input).getMouseLookHandler().requireButtonPress(true);
cam.update();
rootNode.setCullMode(SceneElement.CULL_NEVER);
setupBase();
// Create a new mouse. Restrict its movements to the display screen.
am = new AbsoluteMouse("The Mouse", display.getWidth(), display
.getHeight());
// Get a picture for my mouse.
TextureState ts = display.getRenderer().createTextureState();
URL cursorLoc = TestTrianglePick.class.getClassLoader().getResource(
"jmetest/data/cursor/cursor1.png" );
Texture t = TextureManager.loadTexture(cursorLoc, Texture.MM_LINEAR,
Texture.FM_LINEAR);
ts.setTexture(t);
am.setRenderState(ts);
// Make the mouse's background blend with what's already there
AlphaState as = display.getRenderer().createAlphaState();
as.setBlendEnabled(true);
as.setSrcFunction(AlphaState.SB_SRC_ALPHA);
as.setDstFunction(AlphaState.DB_ONE_MINUS_SRC_ALPHA);
as.setTestEnabled(true);
as.setTestFunction(AlphaState.TF_GREATER);
am.setRenderState(as);
// Move the mouse to the middle of the screen to start with
am.setLocalTranslation(new Vector3f(display.getWidth() / 2, display
.getHeight() / 2, 0));
// Assign the mouse to an input handler
am.registerWithInputHandler(input);
// Create the box in the middle. Give it a bounds
try {
URL model = new File(FILE_PATH).toURI().toURL();
BinaryImporter importer = new BinaryImporter();
maggie = (Spatial) importer.load(model);
} catch (IOException e) { // Just in case anything happens
logger.logp(Level.SEVERE, this.getClass().toString(),
"simpleInitGame()", "Exception", e);
System.exit(0);
}
maggie.setModelBound(new BoundingBox());
maggie.updateModelBound();
// Attach Children
rootNode.attachChild(maggie);
rootNode.attachChild(am);
maggie.lockBounds();
maggie.lockTransforms();
results.setCheckDistance(true);
pointSelection = new Point("selected triangle", new Vector3f[1], null,
new ColorRGBA[1], null);
pointSelection.setSolidColor(new ColorRGBA(1, 0, 0, 1));
pointSelection.setPointSize(10);
pointSelection.setAntialiased(true);
ZBufferState zbs = display.getRenderer().createZBufferState();
zbs.setFunction(ZBufferState.CF_ALWAYS);
pointSelection.setRenderState(zbs);
pointSelection.setLightCombineMode(LightState.OFF);
rootNode.attachChild(pointSelection);
testRay(new Vector3f(-19.677053f, 0.0f, -2.6199493f), new Vector3f(-0.7508563f, 0.0f, -0.66046566f));
KeyBindingManager kbm = KeyBindingManager.getKeyBindingManager();
kbm.add("1", KeyInput.KEY_1);
kbm.add("2", KeyInput.KEY_2);
kbm.add("3", KeyInput.KEY_3);
kbm.add("4", KeyInput.KEY_4);
kbm.add("5", KeyInput.KEY_5);
kbm.add("6", KeyInput.KEY_6);
kbm.add("7", KeyInput.KEY_7);
kbm.add("8", KeyInput.KEY_8);
kbm.add("9", KeyInput.KEY_9);
kbm.add("0", KeyInput.KEY_0);
}
private void createSelectionTriangles(int number) {
clearPreviousSelections();
selection = new Line[number];
for (int i = 0; i < selection.length; i++) {
selection[i] = new Line("selected triangle" + i, new Vector3f[4],
null, new ColorRGBA[4], null);
selection[i].setSolidColor(new ColorRGBA(0, 1, 0, 1));
selection[i].setLineWidth(5);
selection[i].setAntialiased(true);
selection[i].setMode(Line.CONNECTED);
ZBufferState zbs = display.getRenderer().createZBufferState();
zbs.setFunction(ZBufferState.CF_ALWAYS);
selection[i].setRenderState(zbs);
selection[i].setLightCombineMode(LightState.OFF);
rootNode.attachChild(selection[i]);
}
rootNode.updateGeometricState(0, true);
rootNode.updateRenderState();
}
private void clearPreviousSelections() {
if (selection != null) {
for ( Line line : selection ) {
rootNode.detachChild( line );
}
}
}
TrianglePickResults results = new TrianglePickResults() {
public void processPick() {
// initialize selection triangles, this can go across multiple
// target
// meshes.
int total = 0;
for (int i = 0; i < getNumber(); i++) {
total += getPickData(i).getTargetTris().size();
}
if (getNumber() > 0 && total == 0) {
System.err.println("<<<< Can't get triangle >>>>");
}
createSelectionTriangles(total);
if (getNumber() > 0) {
int previous = 0;
for (int num = 0; num < getNumber(); num++) {
PickData pData = getPickData(num);
List<Integer> tris = pData.getTargetTris();
TriangleBatch mesh = (TriangleBatch) pData.getTargetMesh();
for (int i = 0; i < tris.size(); i++) {
int triIndex = tris.get( i );
Vector3f[] vec = new Vector3f[3];
mesh.getTriangle(triIndex, vec);
FloatBuffer buff = selection[i + previous]
.getVertexBuffer(0);
for ( Vector3f v : vec ) {
v.multLocal( mesh.getParentGeom()
.getWorldScale() );
mesh.getParentGeom().getWorldRotation().mult(
v, v );
v.addLocal( mesh.getParentGeom()
.getWorldTranslation() );
}
BufferUtils.setInBuffer(vec[0], buff, 0);
BufferUtils.setInBuffer(vec[1], buff, 1);
BufferUtils.setInBuffer(vec[2], buff, 2);
BufferUtils.setInBuffer(vec[0], buff, 3);
if (num == 0 && i == 0) {
selection[i + previous]
.setSolidColor(new ColorRGBA(1, 0, 0, 1));
Vector3f loc = new Vector3f();
pData.getRay().intersectWhere(vec[0], vec[1],
vec[2], loc);
BufferUtils.setInBuffer(loc, pointSelection
.getVertexBuffer(0), 0);
}
}
previous = tris.size();
}
}
}
};
// This is called every frame. Do changing of values here.
protected void simpleUpdate() {
// Is button 0 down? Button 0 is left click
if (MouseInput.get().isButtonDown(0)) {
Vector2f screenPos = new Vector2f();
// Get the position that the mouse is pointing to
screenPos.set(am.getHotSpotPosition().x, am.getHotSpotPosition().y);
// Get the world location of that X,Y value
Vector3f worldCoords = display.getWorldCoordinates(screenPos, 1.0f);
// Create a ray starting from the camera, and going in the direction
// of the mouse's location
final Ray mouseRay = new Ray(cam.getLocation(), worldCoords
.subtractLocal(cam.getLocation()));
mouseRay.getDirection().normalizeLocal();
results.clear();
maggie.calculatePick(mouseRay, results);
showRay(mouseRay);
}
KeyBindingManager kbm = KeyBindingManager.getKeyBindingManager();
if (kbm.isValidCommand("1", false)) {
testRay(new Vector3f(-19.677053f, 0.2f, -2.6199493f), new Vector3f(-0.7508563f, 0.0f, -0.66046566f));
} else if (kbm.isValidCommand("2", false)) {
testRay(new Vector3f(-14.950614f, 0.2f, -33.370464f), new Vector3f(-0.9732216f, 0.0f, -0.22986898f));
} else if (kbm.isValidCommand("3", false)) {
testRay(new Vector3f(-10.722265f, 0.2f, -26.857378f), new Vector3f(-0.94959575f, 0.0f, -0.31347752f));
} else if (kbm.isValidCommand("4", false)) {
testRay(new Vector3f(-7.654091f, 0.2f, -37.46059f), new Vector3f(-0.9744296f, 0.0f, -0.22469339f));
} else if (kbm.isValidCommand("5", false)) {
testRay(new Vector3f(5.784861f, 0.2f, -18.619741f), new Vector3f(-0.92963105f, 0.0f, -0.3684917f));
} else if (kbm.isValidCommand("6", false)) {
testRay(new Vector3f(1.8931199f, 0.2f, -1.2393173f), new Vector3f(-0.8212465f, 0.0f, -0.5705737f));
} else if (kbm.isValidCommand("7", false)) {
testRay(new Vector3f(-0.8870149f, 0.2f, -3.2509987f), new Vector3f(-0.8220476f, 0.0f, -0.5694188f));
} else if (kbm.isValidCommand("8", false)) {
testRay(new Vector3f(-16.766214f, 0.2f, 0.29477668f), new Vector3f(-0.75086683f, 0.0f, -0.6604537f));
} else if (kbm.isValidCommand("9", false)) {
testRay(new Vector3f(-17.708593f, 0.2f, 0.51814497f), new Vector3f(-0.7047615f, 0.0f, -0.7094444f));
} else if (kbm.isValidCommand("0", false)) {
testRay(new Vector3f(-1.3747561f, 0.2f, -3.5791903f), new Vector3f(-0.84651035f, 0.0f, -0.53237224f));
}
}
private void testRay(Vector3f origin, Vector3f dir) {
Ray mouseRay = new Ray();
mouseRay.getOrigin().set(origin);
mouseRay.getDirection().set(dir);
mouseRay.getDirection().normalizeLocal();
results.clear();
maggie.calculatePick(mouseRay, results);
showRay(mouseRay);
}
protected void setupBase() {
Quad floor = new Quad("floor", 5000f, 5000f);
floor.setLocalTranslation(0, -0.1f, 0);
RenderUtil.makeTransparent(floor);
floor.getLocalRotation().lookAt(Vector3f.UNIT_Y, Vector3f.UNIT_X);
floor.setSolidColor(ColorRGBA.white);
MaterialState ms = RenderUtil.getMaterialState(floor);
ms.setColorMaterial(MaterialState.CM_AMBIENT_AND_DIFFUSE);
AxisRods rods = new AxisRods("axis", true, 1000, 0.05f);
rootNode.attachChild(rods);
rootNode.attachChild(floor);
rootNode.updateRenderState();
rootNode.updateGeometricState(0, true);
}
public void showRay(Ray ray) {
Vector3f start = new Vector3f(ray.origin);
Vector3f end = new Vector3f(ray.direction);
end.multLocal(1000f);
end.addLocal(start);
line.clearBuffers();
line.reconstruct(BufferUtils.createFloatBuffer(new Vector3f[] { start, end }), null,
BufferUtils.createFloatBuffer(new ColorRGBA[2]), null);
line.setSolidColor(ColorRGBA.white);
line.setLineWidth(3);
line.removeFromParent();
rootNode.attachChild(line);
rootNode.updateRenderState();
rootNode.updateGeometricState(0f, true);
}
}