Thank you very much for your help and usefull hints. I have develop a Mesh which almost satified my needs.
package mygame;
import com.jme3.app.SimpleApplication;
import com.jme3.bounding.BoundingVolume;
import com.jme3.collision.Collidable;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.ChaseCamera;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Matrix4f;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.VertexBuffer;
import java.util.Arrays;
/**
* test
*
* @author normenhansen
*/
public class Main extends SimpleApplication {
private ChaseCamera cc;
public static class IndexLineArray extends Mesh {
private Vector3f[] points = null;
private float tolerance;
public IndexLineArray(Vector3f[] points, float tolerance) {
this.points = points;
this.tolerance = tolerance;
float[] position = new float[3 * points.length];
int[] indexes = new int[(2 * points.length) - 2];
int positionIndex = 0;
for (Vector3f point : points) {
position[positionIndex] = point.x;
position[positionIndex + 1] = point.y;
position[positionIndex + 2] = point.z;
positionIndex += 3;
}
for (int i = 0, j = 0; i < (2 * points.length) - 2; i += 2, j++) {
indexes[i] = j;
indexes[i + 1] = j + 1;
}
System.out.println(Arrays.toString(indexes));
setBuffer(VertexBuffer.Type.Position, 3, position);
setBuffer(VertexBuffer.Type.Index, 2, indexes);
setMode(Mode.Lines);
updateBound();
}
// https://github.com/gavalian/clasrec-geometry/blob/master/src/org/jlab/geom/prim/Line3D.java
public static float distanceSegments(Vector3f line1Start, Vector3f line1End, Vector3f line2Start, Vector3f line2End) {
Vector3f u = line1End.subtract(line1Start);
Vector3f v = line2End.subtract(line2Start);
Vector3f w = line1Start.subtract(line2Start);
float a = u.dot(u); // always >= 0
float b = u.dot(v);
float c = v.dot(v); // always >= 0
float d = u.dot(w);
float e = v.dot(w);
float D = a * c - b * b; // always >= 0
float sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0
float tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0
// compute the line parameters of the two closest points
final float SMALL_NUM = 0.00000001f;
if (D < SMALL_NUM) {
// the lines are almost parallel
sN = 0; // force using point P0 on segment S1
sD = 1; // to prevent possible division by 0 later
tN = e;
tD = c;
} else {
// the lines are not parallel
// get the closest points on the infinite lines
sN = (b * e - c * d);
tN = (a * e - b * d);
if (sN < 0) {
// sc < 0 => the s=0 edge is visible
sN = 0;
tN = e;
tD = c;
} else if (sN > sD) {
// sc > 1 => the s=1 edge is visible
sN = sD;
tN = e + b;
tD = c;
}
}
if (tN < 0) {
// tc < 0 => the t=0 edge is visible
tN = 0;
// recompute sc for this edge
if (-d < 0) {
sN = 0;
} else if (-d > a) {
sN = sD;
} else {
sN = -d;
sD = a;
}
} else if (tN > tD) {
// tc > 1 => the t=1 edge is visible
tN = tD;
// recompute sc for this edge
if ((-d + b) < 0) {
sN = 0;
} else if ((-d + b) > a) {
sN = sD;
} else {
sN = (-d + b);
sD = a;
}
}
// finally do the division to get sc and tc
sc = (Math.abs(sN) < SMALL_NUM ? 0 : sN / sD);
tc = (Math.abs(tN) < SMALL_NUM ? 0 : tN / tD);
Vector3f p = lerp(line1Start, line1End, sc);
Vector3f q = lerp(line2Start, line2End, tc);
return p.distance(q);
}
public static Vector3f lerp(Vector3f point1, Vector3f point2, float t) {
return new Vector3f(
point1.x + (point2.x - point1.x) * t,
point1.y + (point2.y - point1.y) * t,
point1.z + (point2.z - point1.z) * t);
}
private float distance(Ray ray, Vector3f start, Vector3f end) {
Vector3f rayEnd = ray.direction.mult(100).add(ray.origin);
return distanceSegments(ray.origin, rayEnd, start, end);
}
static int c = 0;
@Override
public int collideWith(Collidable other, Matrix4f worldMatrix, BoundingVolume worldBound, CollisionResults results) {
if (other instanceof Ray) {
Ray ray = (Ray) other;
for (int i = 0; i < points.length - 1; i++) {
Vector3f start = points[i].clone();
Vector3f end = points[i + 1].clone();
float dist = distance(ray, start, end);
System.out.println(c++ + ": Line start" + points[i] + " Line end" + points[i + 1] + " " + dist);
if (dist < tolerance) {
results.addCollision(new CollisionResult());
return 1;
}
}
}
return 0;
}
}
public static void main(String[] args) {
Main app = new Main();
app.start();
}
@Override
public void simpleInitApp() {
IndexLineArray lines = new IndexLineArray(new Vector3f[]{
Vector3f.ZERO.add(0, 0, 0),
Vector3f.ZERO.add(0, 1, 0),
Vector3f.ZERO.add(1, 0, 0),
Vector3f.ZERO.add(1, 1, 0),
Vector3f.ZERO.add(2, 0, 0),
Vector3f.ZERO.add(2, 1, 0),
Vector3f.ZERO.add(3, 0, 0),
Vector3f.ZERO.add(3, 1, 0),
Vector3f.ZERO.add(4, 0, 0),
Vector3f.ZERO.add(4, 1, 0),
Vector3f.ZERO.add(5, 0, 0),
Vector3f.ZERO.add(5, 1, 0),
Vector3f.ZERO.add(6, 0, 0)
}, 0.1f);
lines.setLineWidth(5);
Geometry geo = new Geometry("IndexLineArray", lines); // using Quad object
Material mat = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Blue);
mat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
geo.setMaterial(mat);
s.attachChild(geo);
rootNode.attachChild(s);
flyCam.setEnabled(false);
cc = new ChaseCamera(cam, s, inputManager);
inputManager.addMapping("mouse", new MouseAxisTrigger(MouseInput.AXIS_X, true),
new MouseAxisTrigger(MouseInput.AXIS_X, false),
new MouseAxisTrigger(MouseInput.AXIS_Y, true),
new MouseAxisTrigger(MouseInput.AXIS_Y, false));
inputManager.addListener(mouseClick, "mouse");
}
Node s = new Node();
private Geometry pickElement(Node shootables) {
inputManager.setCursorVisible(true);
CollisionResults results = new CollisionResults();
Vector2f click2d = inputManager.getCursorPosition();
Vector3f click3d = cam.getWorldCoordinates(
new Vector2f(click2d.x, click2d.y), 0f).clone();
Vector3f dir = cam.getWorldCoordinates(
new Vector2f(click2d.x, click2d.y), 0.3f).subtractLocal(click3d).normalizeLocal();
Ray ray = new Ray(click3d, dir);
System.out.println(ray);
shootables.collideWith(ray, results);
if (results.size() > 0) {
return results.getClosestCollision().getGeometry();
}
return null;
}
AnalogListener mouseClick = new AnalogListener() {
public void onAnalog(String name, float value, float tpf) {
Geometry pickedElem = pickElement(s);
if (pickedElem != null) {
System.out.println("Elemet found: " + pickedElem.getName());
}
}
};
}
This works fine. If I move over a line by the mouse pointer then my node is correctly selected.
But the big problem is: If I scale and rotate my model then it doesn’t work anymore. I know the reason but I have no solution for that. The error is I use in my collideWith method the orginal coordinates but after a transformation of my IndexLineArray, the coordinates has other values.
It is possible to transform coordinates by the current Rotation, Scale and Translation? Or has anyboday a better solution.