Simple: I have a Line in World coords and I want it to look the same (same pos/rotation/scale) after I add it to a node N that has its own transformations(node’s own pos/rotation/scale).
Assumption: I need to transform this Line such that when applying the node N’s transform it will look exactly the same as before the Line was transformed. By transform I mean scale/translation/rotation
Example:
ie. nodes:
rootNode->N->nodeLine->geoLine
nodeLine’s transform and N’s transform nullify such that it would appear that geoLine is the child of rootNode ie. rootNode->geoLine
Detailed:
For example I have 2 vectors in world coords (due to Ray/collideWith results) and I want to draw a Line between them. And so I do and it looks great if I add them to rootNode and rootNode has no transformations (ie. Transform.IDENTITY) but if I want to add that line to a subnode of rootNode which has rotation/scale/translations I need to transform my Line before adding it to that subnode such that the newly transformed line has exactly the inverse transformation when compared to the subnode so that when inside subnode these 2 transformations would combine and cause my Line to look exactly the same as if it was in the World coords.
I did notice that there is a Node.worldToLocal(in,out) for Vector3f though, and Transform.transformInverseVector(in,out) for Vector3f, these don’t seem to work for this case of Line
I’d need a Transform.inverse() ?
Anyway I did try this:
Transform t = subRoot.getLocalTransform().clone();
nodeLine.setLocalRotation(t.getRotation().inverse()); //also tried opposite()
nodeLine.setLocalTranslation(t.getTranslation().negate());
nodeLine.setLocalScale(t.getScale().negate());
then I created the Line using world coords
where nodeLine was a node containing the Line in world coords and subRoot was the node I was trying to attach the line to such that it would look exactly the same after addition; subRoot had it’s own transformation which I was trying to have in exact opposite in nodeLine such that they would nullify so my Line would look as if it was in world coords (or part of rootNode[assuming rootNode had no transforms] even though it was now part of subRoot[which had its own transforms])
here’s the failing code(just hover mouse over that long box):
rootNode->subRoot->collidables->orange Box
rootNode->subRoot->nodeLine->Line
only subRoot is already transformed
and I’m trying to transform nodeLine in exact opposition of subRoot so that it would appear as if nodeLine is part of rootNode
When code works, those randomcolored lines would be normals on that long box
pre type="java"
package org.jme3.tests;
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.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Ray;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.debug.Arrow;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Line;
import com.jme3.system.AppSettings;
public class TranslationWickedtry extends SimpleApplication {
private Node collidables;
private Node nodeLine;
private Node subRoot;
private final static String mapToggleSubNodeTransform = “mapToggleSubNodeTransform”;
private static final float lineWidth = 5f;
private BitmapText helloText;
public static void main(String[] args) {
TranslationWickedtry app = new TranslationWickedtry();
AppSettings aps = new AppSettings(true);
// aps.load(aps.getTitle());
aps.setVSync(true);
app.setShowSettings(false);
app.setSettings(aps);
app.start();
}
@Override
public void simpleInitApp() {
flyCam.setDragToRotate(true);
flyCam.setMoveSpeed(20f);
cam.setLocation(new Vector3f(0f, 0f, 52f));
// cam.setLocation(new Vector3f(61.42587f, -145.20616f, 13.512871f));
// cam.setRotation(new Quaternion(0.5186105f, 0.4658943f, -0.41162565f,
// 0.5869838f));
// cam.setDirection(new Vector3f(0.1199981f, -0.99238f, 0.027971268f));
subRoot = new Node();
rootNode.attachChild(subRoot);
subRoot.setLocalScale(0.4f, 11f, 0.6f);// must be all positive
subRoot.setLocalRotation(new Quaternion().fromAngles(
FastMath.DEG_TO_RAD * 38, FastMath.DEG_TO_RAD * 12,
FastMath.DEG_TO_RAD * 154));
subRoot.setLocalTranslation(3f, -8f, -12f);
nodeLine = new Node();
subRoot.attachChild(nodeLine);
/** create four colored boxes and a floor to shoot at: */
collidables = new Node(“collidables”);
subRoot.attachChild(collidables);
Box meshBox = new Box(new Vector3f(-2, 0, 1), 1, 1, 1);
Geometry geoBox = new Geometry(“Boxy”, meshBox);
Material matBox = new Material(assetManager,
“Common/MatDefs/Misc/WireColor.j3md”);
matBox.setColor(“Color”, ColorRGBA.Orange);
geoBox.setMaterial(matBox);
collidables.attachChild(geoBox);
// transformSubNode();
Node coord = new Node();
attachCoordinateAxes(Vector3f.ZERO, coord);
rootNode.attachChild(coord);
inputManager.addMapping(mapToggleSubNodeTransform, new KeyTrigger(
KeyInput.KEY_SPACE));
inputManager.addListener(actionListener, mapToggleSubNodeTransform);
// ========
helloText = new BitmapText(guiFont, false);
helloText.setSize(guiFont.getCharSet().getRenderedSize());
helloText.setLocalTranslation(300, helloText.getLineHeight(), 0);
helloText.setText(“press SPACE to toggle subNode’s transform”);
guiNode.attachChild(helloText);
}
private void transformSubNode() {
collidables.setLocalTranslation(11, 17, -29);
collidables.scale(2.3f, 4.2f, 7.1f);
collidables.rotate(FastMath.DEG_TO_RAD * 35f,
FastMath.DEG_TO_RAD * 35f, FastMath.DEG_TO_RAD * 35f);
}
private final ActionListener actionListener = new ActionListener() {
@SuppressWarnings(“synthetic-access”)
@Override
public void onAction(String name, boolean isPressed, float tpf) {
do {
if ((mapToggleSubNodeTransform == name) && (isPressed)) {
// FIXME: implement Transform.equals() or .isIdentity()?
// if
// (subNode.getLocalTransform().equals(Transform.IDENTITY))
// {
// subNode.getLocalRotation().equals(o)
if (collidables.getLocalTransform().getTranslation().getX() == 0) {
helloText.setText(“subNode is transformed”);
transformSubNode();
} else {
helloText.setText(“subNode is NOT transformed”);
collidables.setLocalTransform(Transform.IDENTITY);
}
break;
}
} while (false);
}
};
@Override
public void simpleUpdate(float tpf) {
Vector3f origin = cam.getWorldCoordinates(
inputManager.getCursorPosition(), 0.0f);
Vector3f direction = cam
.getWorldCoordinates(inputManager.getCursorPosition(), 0.3f)
.subtractLocal(origin).normalizeLocal();
Ray ray = new Ray(origin, direction);
CollisionResults results = new CollisionResults();
collidables.collideWith(ray, results);
if (results.size() > 0) {
CollisionResult closest = results.getClosestCollision();
// world coord of the contact
Vector3f normalStartPoint = closest.getContactPoint().clone();
// the direction of the normal, normalized already:
Vector3f normalDirection = closest.getContactNormal();
if (!normalDirection.isUnitVector()) {
// was not normalized?! impossible
throw null;
}
// normalVec is a point on the
normal to the surface
Vector3f normalEndPoint = normalStartPoint.add(normalDirection
.mult(10f));
// these 2 fail too
// normalStartPoint = subRoot.worldToLocal(normalStartPoint, null);
// normalEndPoint = subRoot.worldToLocal(normalEndPoint, null);
// subRoot.getWorldTransform().
// the vector perpendicular on both
contact
and normal
vectors// Vector3f upVec = contact.cross(normalVec).normalizeLocal();
// XXX: I fail to get the inverse transform for nodeLine
Transform t =
// new Transform();
subRoot.getLocalTransform().clone();
// // // subRoot.getLocalToWorldMatrix(store)
// // // subRoot.getW
nodeLine.setLocalRotation(t.getRotation().inverse());
nodeLine.setLocalTranslation(t.getTranslation().negate());
nodeLine.setLocalScale(t.getScale().negate());
// nodeLine
// .getLocalTransform()
// .clone()
// t.interpolateTransforms(subRoot.getWorldTransform(),
// subRoot.getLocalTransform(), 1);
// nodeLine.setLocalTransform(t);
// nodeLine.setLocalTransform(t);
// normalStartPoint = nodeLine.worldToLocal(normalStartPoint, null);
// normalEndPoint = nodeLine.worldToLocal(normalEndPoint, null);
Line line = new Line(normalStartPoint, normalEndPoint);
Geometry geoLine = new Geometry(“line1”, line);
Material mark_mat = new Material(assetManager,
“Common/MatDefs/Misc/SolidColor.j3md”);
mark_mat.setColor(“Color”, ColorRGBA.randomColor());
geoLine.setMaterial(mark_mat);
nodeLine.attachChild(geoLine);// nodeLine is not transformed
// rootNode.attachChild(geoLine);// rootNode is not transformed
// Q.assumedTrue(nodeLine.getLocalTransform().equals(Transform.IDENTITY));
}
}
private void attachCoordinateAxes(Vector3f pos, Node toNode) {
Arrow arrow = new Arrow(Vector3f.UNIT_X);
// make arrow thicker,
arrow.setLineWidth(lineWidth);
putShape(arrow, ColorRGBA.Red, toNode).setLocalTranslation(pos);
arrow = new Arrow(Vector3f.UNIT_Y);
arrow.setLineWidth(lineWidth); // make arrow thicker
putShape(arrow, ColorRGBA.Green, toNode).setLocalTranslation(pos);
arrow = new Arrow(Vector3f.UNIT_Z);
arrow.setLineWidth(lineWidth); // make arrow thicker
putShape(arrow, ColorRGBA.Blue, toNode).setLocalTranslation(pos);
}
private Geometry putShape(Mesh shape, ColorRGBA color, Node onNode) {
Geometry g = new Geometry(“coordinate axis”, shape);
Material mat = new Material(assetManager,
“Common/MatDefs/Misc/Unshaded.j3md”);
mat.getAdditionalRenderState().setWireframe(true);
mat.setColor(“Color”, color);
g.setMaterial(mat);
onNode.attachChild(g);
// g.setCullHint(CullHint.Inherit);
return g;
}
}
/pre