Hello,

my goal is to implement arrow representation (consisting of a cylinder and a cone) that connects two arbitrary Points. The two arrow spatials are grouped in an node. The cylinder and the cone are translated in a way with the result that they look like a arrow. Then I have written a method that computes the rotation between the two points. The idea was to apply this rotation to the arrow node. But this comes to the problem that the arrow spatials are invisible. I figured out that the problem does nor occur, when I skip the translation of the arrow spatials. But this has the drawback that the arrow does not look like an arrow.

A small example for this problem you can see here:

[java]

package mygame;

import com.jme3.app.SimpleApplication;

import com.jme3.bounding.BoundingBox;

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.Geometry;

import com.jme3.scene.Node;

import com.jme3.scene.debug.WireBox;

import com.jme3.scene.shape.Cylinder;

public class ArrowTest extends SimpleApplication {

Node arrowNode;

Material blueMat;

Material redMat;

Material greenMat;

Material whiteMat;

Vector3f middlePoint;

float length;

Quaternion arrowRot;

```
public static void main(String[] args) {
ArrowTest app = new ArrowTest();
app.start();
}
@Override
public void simpleInitApp() {
flyCam.setMoveSpeed(100f);
cam.setLocation(new Vector3f(0, 100, 200));
cam.lookAt(new Vector3f(0, 0, 0), Vector3f.ZERO);
arrowNode = new Node("ArrowNode");
blueMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
blueMat.setColor("Color", ColorRGBA.Blue);
redMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
redMat.setColor("Color", ColorRGBA.Red);
greenMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
greenMat.setColor("Color", ColorRGBA.Green);
whiteMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
whiteMat.setColor("Color", ColorRGBA.White);
Vector3f startPos = new Vector3f(0, 0, 0);
Vector3f endPos = new Vector3f(50, 30, 0);
BoundingBox bb1 = new BoundingBox(startPos, 10, 10, 10);
WireBox wireBox1 = new WireBox();
wireBox1.fromBoundingBox(bb1);
wireBox1.setLineWidth(1.5f);
Geometry bbGeom1 = new Geometry("bbGeom2", wireBox1);
bbGeom1.setMaterial(blueMat);
bbGeom1.setLocalTranslation(startPos);
BoundingBox bb2 = new BoundingBox(endPos, 10, 10, 10);
WireBox wireBox2 = new WireBox();
wireBox2.fromBoundingBox(bb2);
wireBox2.setLineWidth(1.5f);
Geometry bbGeom2 = new Geometry("bbGeom2", wireBox2);
bbGeom2.setMaterial(redMat);
bbGeom2.setLocalTranslation(endPos);
computeOrientation(startPos, endPos);
buildArrow();
arrowNode.setLocalTranslation(middlePoint);
BoundingBox bounding = (BoundingBox) arrowNode.getWorldBound();
WireBox wire = new WireBox();
wire.fromBoundingBox(bounding);
wire.setLineWidth(2f);
Geometry wireGeom = new Geometry("wireGeom", wire);
wireGeom.setMaterial(whiteMat);
arrowNode.attachChild(wireGeom);
arrowNode.rotate(arrowRot);
rootNode.attachChild(bbGeom1);
rootNode.attachChild(bbGeom2);
rootNode.attachChild(arrowNode);
}
public void buildArrow() {
float shaftRadius = 1.5f;
float shaftLength = .8f*length;
float tipRadius = 3f;
float tipLength = .2f*length;
Cylinder shaft = new Cylinder(32, 32, shaftRadius, shaftLength, true);
Geometry shaftGeom = new Geometry("shaft", shaft);
shaftGeom.setMaterial(blueMat);
shaftGeom.setLocalTranslation(shaftGeom.getLocalTranslation().add(new Vector3f(0, 0, -tipLength/2)));
Cylinder tip = new Cylinder(32, 32, tipRadius, 0, tipLength, true, false);
Geometry tipGeom = new Geometry("tip", tip);
tipGeom.setMaterial(redMat);
Quaternion q = new Quaternion();
q.fromAngleAxis(-180*FastMath.DEG_TO_RAD, new Vector3f(1,0,0));
tipGeom.rotate(q);
tipGeom.setLocalTranslation(tipGeom.getLocalTranslation().add(new Vector3f(0, 0, shaftLength/2)));
arrowNode.attachChild(shaftGeom);
arrowNode.attachChild(tipGeom);
}
public void computeOrientation(Vector3f fromPos, Vector3f toPos) {
middlePoint = FastMath.interpolateLinear(0.5f, fromPos, toPos);
length = fromPos.distance(toPos);
arrowRot = computeRotationBetweenTwoPoints(fromPos, toPos);
}
public Quaternion computeRotationBetweenTwoPoints(Vector3f p1, Vector3f p2) {
Vector3f z = Vector3f.UNIT_Z;
Vector3f connectionVector = getConnectionVector(p1, p2);
Vector3f a = z.cross(connectionVector);
float w = FastMath.sqrt(FastMath.pow(z.length(), 2) * FastMath.pow(connectionVector.length(), 2)) + z.dot(connectionVector);
Quaternion q = new Quaternion(a.x, a.y, a.z, w);
return q;
}
public Vector3f getConnectionVector(Vector3f p1, Vector3f p2) {
return new Vector3f(p2.x-p1.x, p2.y-p1.y, p2.z-p1.z);
}
```

}

[/java]

The start and end point of the arrow are the center of two bounding boxes. For debug purposes I added a bounding box that visualized the bounding of the arrow’s spatials. By the help of this you can see that the rotation is applied to the arrow node; but the arrow spatials are not visible.

Did some know what I make wrong? What transformations do I need to connect the points by the arrow?