Simple: in this post:
EDIT3: ignore only Problem 2, due to WireBox is not a collidable object (as per this post)
EDIT2: apparently I can’t think right, ignore EDIT1, still do look at Problem 1, it might still be worldToLocal … I mean if I do this:
[java]Vector3f localContact = markNode.getParent().worldToLocal(
worldContact, null);
markNode.setLocalTranslation(localContact);[/java]
shouldn’t markNode’s world position be exactly at worldContact? if a markNode’s child is at child’s local translation 0,0,0 then would this child’s world position be worldContact ? if yes, if it should then it’s jme3 issue, else… I fail (again! geez) it’s my code; NOTE: when the fix is undone then it’s “yes” to what I just said, always.
EDIT1: ignore 1. (aka Problem 1) below - if I comment out this line it works, thus it’s not worldToLocal or anything jme related: [java]// markParent.setLocalScale(8f, 1.9f, 3f);[/java]
1. possibly
worldToLocal or
transformInverseVector don't compute right with the new fix ? (OR bug in my code? but only affected by the fix)
2. WireBox collisions with Ray happen on the inside of this wirebox (as opposed to outside of it, as it works for Box)? (regardless of new fix)
=========
Detailed:
Ok here's the testcase(at the end of post) with a collision marker, steps to test:
Problem 1: when the non-uniform scale fix is applied (jme3 r7291 that is) I seem to fail to properly position it with setLocalTranslation (but clearly I need to consider something like some scale) though the same code when the fix is undone works fine, marker is properly positioned.
EDIT:
The thing is, that while I obtain a world coord for the collision, I am trying to use that to position the marker markNode, which has parents rootNode->markParent, but markParent has transforms (rot/scale/transl) and so I am trying to apply some local transforms for my markNode such its transforms and parent's transforms nullify making it seem as if markNode is untransformed and so I can apply that world coord properly...
steps to test:
a) run the code without the r7291 fix notice how the marker (purple arrow) is properly positioned at the collision point (and this unchanged: is always perpendicular on the yellow box (Box not WireBox) no matter on what surface the middle of the screen(crosshairs) are pointing at)
http://i.imgur.com/4bXkb.jpg
b) apply the r7291 fix and run the code again and notice that the marker's position is off and it moves scaled (though its orientation is the same, ie. only the local translation needs to be fixed)
http://i.imgur.com/Zc99r.jpg
Problem 2: apparently unrelated, I noticed that the WireBox 's collision normal is apparently opposite than what it should be, at least when compared with Box;
I think the collision happens on the inner sides of the wireBox(?? unsure)
steps to test:
undo the r7291 fix,
a) then run the code (should be only the Box attached to rootNode, not the wireBox, by default)
ie.
[java]// cosmos.attachChild( wireboxGeo );
cosmos.attachChild( boxGeom );[/java]
notice the normal is working fine (see first screenie in this post)
b) now change the code to attach only the wireBox and not the Box ie.
[java] cosmos.attachChild( wireboxGeo );
//cosmos.attachChild( boxGeom );[/java]
now you notice the purple wire box
the marker normal appears to be detected as if the r7291 fix was already there in place, but it's not, and the normal and box are inconsistent OR something else I can't understand
c) apply the r7291 fix and redo a) then b)
I really don't understand what is happening now, but the marker's position is again off and the normal seems to be in opposite direction than if it were a Box instead of WireBox
http://i.imgur.com/G0BMi.jpg
=========
Bottom line:
1. the changes in jme3 r7291 either must be applied somewhere else also ie. worldToLocal, OR I need to change my code (which was previously wrongly calculating the mark's position and worked perfectly with the unfixed bug) to recalculate considering the new fix, therefore this is just a bug in my own code
2. WireBox and Box 's normal don't seem to be the same, the CollisionResult.getContactNormal() that is; this
appears to be a bug with that method (?)
[java]package org.jme3.forum;
import java.util.prefs.BackingStoreException;
import com.jme3.app.SimpleApplication;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.font.BitmapFont;
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.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.control.BillboardControl;
import com.jme3.scene.debug.Arrow;
import com.jme3.scene.debug.WireBox;
import com.jme3.scene.shape.Box;
import com.jme3.system.AppSettings;
public class NonUniformScaleFail2 extends SimpleApplication {
private Node markNode;
private final ColorRGBA markColor = ColorRGBA.Magenta;
public static void main(String[] args) throws BackingStoreException {
NonUniformScaleFail2 app = new NonUniformScaleFail2();
AppSettings aps = new AppSettings(true);
aps.load(aps.getTitle());
aps.setVSync(true);
app.setSettings(aps);
app.setShowSettings(false);
app.start();
}
private Node cosmos;
private final static String mapChangeScale = "mapChangeScale";
private BitmapText chgScaleText;
/**
* node relationship: rootNode->markParent->markNode (the magenta Arrow)
* rootNode->cosmos->Box,WireBox rootNode is never transformed, for
* simplicity or something where markParent & cosmos are transformed
*/
protected void initMark() {
// Node toNode =
// new Node();
markNode = new Node("markNode");
putArrow(Vector3f.UNIT_Z, markLen, 3, markColor, markNode, "mark",
Vector3f.ZERO, markColor);
Node markParent = new Node("markParent");
markParent.attachChild(markNode);
markParent.setLocalTranslation(900f, 1200f, -300f);
markParent.setLocalScale(8f, 1.9f, 3f);
markParent.setLocalRotation(new Quaternion().fromAngles(
FastMath.DEG_TO_RAD * 159, FastMath.DEG_TO_RAD * 18,
FastMath.DEG_TO_RAD * 310));
markParent.attachChild(textMe(markParent.getName(), markColor));
attachCoordinateAxes(Vector3f.ZERO, markParent, markColor);
rootNode.attachChild(markParent);
}
@Override
public void simpleInitApp() {
flyCam.setMoveSpeed(2000f);
setDisplayStatView(false);
cam.setLocation(new Vector3f(2749.566f, 1039.3307f, -6154.8726f));
cam.setRotation(new Quaternion(-0.18015513f, -0.44012898f,
-0.09062232f, 0.8749961f));
Vector3f boxSize = new Vector3f(100, 100, 500);
Box boxMesh = new Box(boxSize, boxSize.getX(), boxSize.getY(),
boxSize.getZ());
WireBox wireboxMesh = new WireBox(boxSize.getX(), boxSize.getY(),
boxSize.getZ());
Geometry wireboxGeo = new Geometry("wire", wireboxMesh);
Geometry boxGeom = new Geometry("Box", boxMesh);
Material boxMat = new Material(assetManager,
"Common/MatDefs/Misc/WireColor.j3md");
boxMat.setColor("m_Color", ColorRGBA.Yellow);
boxGeom.setMaterial(boxMat);
Material wireboxMat = new Material(assetManager,
"Common/MatDefs/Misc/WireColor.j3md");
wireboxMat.setColor("m_Color", ColorRGBA.Magenta);
wireboxGeo.setMaterial(wireboxMat);
cosmos = new Node("cosmos");
// rootNode.setLocalScale(2, 3, 4);
rootNode.attachChild(cosmos);
// XXX: attach only one of the following two boxes to see collision
// normal being inverse for wirebox and normal for Box
// cosmos.attachChild( wireboxGeo );
cosmos.attachChild(boxGeom);
transformCosmos(false);// I even transform before, just in case
wireboxGeo.setLocalTranslation(boxSize);
// this local transform(above) is affected by parents' transforms
// but the Box's specified center in Mesh isn't(?) so it appears
attachCoordinateAxes(Vector3f.ZERO, cosmos, ColorRGBA.Pink);
attachCoordinateAxes(Vector3f.ZERO, rootNode, ColorRGBA.Pink);
cam.setFrustumFar(115000.0f);
cam.lookAt(boxGeom.localToWorld(boxMesh.getCenter(), null),
Vector3f.UNIT_Y);
ColorRGBA rootColor = ColorRGBA.White;
ColorRGBA cosmosColor = ColorRGBA.White;
rootNode.attachChild(textMe(rootNode.getName(), rootColor));
cosmos.attachChild(textMe(cosmos.getName(), cosmosColor));
inputManager.addMapping(mapChangeScale, new KeyTrigger(
KeyInput.KEY_SPACE));
inputManager.addListener(actionListener, mapChangeScale);
chgScaleText = new BitmapText(guiFont, false);
chgScaleText.setSize(guiFont.getCharSet().getRenderedSize());
chgScaleText.setLocalTranslation(180, chgScaleText.getLineHeight(), 0);
chgScaleText
.setText("Press SPACE to toggle between unform & non-uniform scales");
guiNode.attachChild(chgScaleText);
initCrossHairs();
initMark();
}
/*
* (non-Javadoc)
*
* @see com.jme3.app.SimpleApplication#simpleUpdate(float)
*/
@Override
public void simpleUpdate(float tpf) {
Vector2f middleOfScreen = new Vector2f(cam.getWidth() / 2,
cam.getHeight() / 2);
Vector3f origin = cam.getWorldCoordinates(middleOfScreen,
// inputManager.getCursorPosition(),
0.0f);
Vector3f direction = cam.getWorldCoordinates(middleOfScreen,
// inputManager.getCursorPosition(),
0.3f).subtractLocal(origin).normalizeLocal();
Ray ray = new Ray(origin, direction);
CollisionResults results = new CollisionResults();
cosmos.collideWith(ray, results);
if (results.size() > 0) {
CollisionResult closest = results.getClosestCollision();
Vector3f worldContact = closest.getContactPoint();
Vector3f normNormal = closest.getContactNormal();
Quaternion q = new Quaternion();
q.lookAt(
// mark.getParent().worldToLocal(
// normNormal,
// null ),
normNormal, Vector3f.UNIT_Y);
Quaternion parentRotInversion = markNode.getParent()
.getWorldRotation().inverse();
// q.multLocal( parentRotInversion );
q = parentRotInversion.mult(q);
markNode.setLocalRotation(q);
Vector3f localContact = markNode.getParent().worldToLocal(
worldContact, null);
// Vector3f localPointOnNormal =
// worldContact.add( normNormal.mult( 1000f
// // getting far point for low precision
// ) );
Vector3f parentScale = markNode.getParent().getWorldScale().clone();
markNode.setLocalScale(Vector3f.UNIT_XYZ.divide(parentScale));
// XXX: when non-uniform scale fix code is applied, this fails?:
markNode.setLocalTranslation(localContact);
}
}
private void initCrossHairs() {
guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
BitmapText ch = new BitmapText(guiFont, false);
ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
ch.setText("+"); // fake crosshairs :)
ch.setLocalTranslation(
// center
settings.getWidth() / 2
- guiFont.getCharSet().getRenderedSize() / 3 * 2,
settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
guiNode.attachChild(ch);
}
private final ActionListener actionListener = new ActionListener() {
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if ((mapChangeScale == name) && (isPressed)) {
if (cosmos.getLocalScale().equals(nonUniformScale)) {
// was non-uniform
// make it uniform
transformCosmos(true);
} else {
// make cosmos' scale non-uniform
transformCosmos(false);
}
chgScaleText.setText("cosmos' world scale now:"
+ cosmos.getWorldScale());
}
}
};
private final float uniformScale = 3f;
private final Vector3f nonUniformScale = new Vector3f(3f, 1f, 3f);
/**
*
*/
private void transformCosmos(boolean uniform) {
cosmos.setLocalTranslation(1000, 2000, -4000);
cosmos.setLocalRotation(new Quaternion().fromAngles(
FastMath.DEG_TO_RAD * 100, FastMath.DEG_TO_RAD * 23,
FastMath.DEG_TO_RAD * 230));
//
if (uniform) {
// cosmos.getWorldTransform();// checkDoTransformUpdate
System.out.println("setting cosmos's local scale to:"
+ uniformScale);
cosmos.setLocalScale(uniformScale);
// cosmos.getWorldTransform();// checkDoTransformUpdate
} else {
// cosmos.getWorldTransform();// checkDoTransformUpdate
System.out.println("setting cosmos's local scale to:"
+ nonUniformScale);
cosmos.setLocalScale(nonUniformScale);
// cosmos.getWorldTransform();// checkDoTransformUpdate
}
//
}
private static final float lineWidth = 5f;
private static final float lineLen = 400f;
private static final float markLen = lineLen * 4;
private void attachCoordinateAxes(Vector3f pos, Node toNode,
ColorRGBA textColor) {
putArrow(Vector3f.UNIT_X, lineLen, lineWidth, ColorRGBA.Red, toNode,
"x", pos, textColor);
putArrow(Vector3f.UNIT_Y, lineLen, lineWidth, ColorRGBA.Green, toNode,
"y", pos, textColor);
putArrow(Vector3f.UNIT_Z, lineLen, lineWidth, ColorRGBA.Blue, toNode,
"z", pos, textColor);
toNode.attachChild(gimmeBox("origin" + " for `" + toNode.getName()
+ "`", lineWidth, ColorRGBA.Cyan));
}
private Geometry putArrow(Vector3f direction, float lineLen1,
float lineWidth1, ColorRGBA color, Node onNode, String name,
Vector3f pos, ColorRGBA textColor) {
String for1 = " for `" + onNode.getName() + "`";
Vector3f dir = direction.normalize();
//
make Box
System.out.println("box dir:" + dir);
Vector3f boxSize = dir.mult(lineLen1);
System.out.println("box size:" + boxSize);
// WireBox wireBoxMesh =
// new WireBox(
// lineWidth1
// + boxSize.getX()
// / 2,
// lineWidth1
// + boxSize.getY()
// / 2,
// lineWidth1
// + boxSize.getZ()
// / 2 );
Box boxMesh = new Box(boxSize.divide(
// center
2), lineWidth1 + boxSize.getX() / 2, lineWidth1
+ boxSize.getY() / 2, lineWidth1 + boxSize.getZ() / 2);// only
// for
// Box
System.out.println("box center=" + boxSize.divide(2));
System.out.println("x=" + lineWidth1 + "/" + boxSize.getX() / 2);
System.out.println("xE=" + boxMesh.getXExtent());
Geometry boxGeo = new Geometry("Box axis " + name + for1, boxMesh);
// boxGeo.move( boxSize.divide( 2 ) );
// boxGeo.setLocalTranslation( boxSize.divide( 2 ) );
// boxGeo.move( offset )
// Quaternion boxRotation =
// new Quaternion();
// boxRotation.lookAt(
// dir,
// Vector3f.UNIT_Y );
// boxGeo.setLocalRotation( boxRotation );//this doesn't work like for
// Arrow
Material boxMat = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
boxMat.setColor("Color", color);
boxMat.getAdditionalRenderState().setWireframe(true);
boxGeo.setMaterial(boxMat);
onNode.attachChild(boxGeo);
//
make Arrow
Vector3f arrowExtent = dir.mult(lineLen1);
Arrow arrowMesh = new Arrow(arrowExtent);// only for Arrow
arrowMesh.setLineWidth(lineWidth1);// only for Arrow
Geometry arrowGeo = new Geometry("Arrow axis " + name + for1, arrowMesh);
Material arrowMat = boxMat.clone();
arrowMat.getAdditionalRenderState().setWireframe(true);
arrowGeo.setMaterial(arrowMat);
arrowGeo.setLocalTranslation(pos);// only for Arrow
onNode.attachChild(arrowGeo);
//
make text
Node txtNode = textMe(name, color
// textColor
);
Vector3f boxEdge = new Vector3f(
(boxMesh.getXExtent() - lineWidth1) * 2,
(boxMesh.getYExtent() - lineWidth1) * 2,
(boxMesh.getZExtent() - lineWidth1) * 2);
System.out.println("boxEdge=" + boxEdge + " boxPos now="
+ boxGeo.getLocalTranslation());
Geometry whiteTip = gimmeBox("whiteTip " + name + for1, lineWidth1,
ColorRGBA.White);
whiteTip.setLocalTranslation(boxEdge);
onNode.attachChild(whiteTip);
txtNode.setLocalTranslation(boxEdge);
onNode.attachChild(txtNode);
return boxGeo;
}
private Geometry gimmeBox(String name, float extent, ColorRGBA color) {
Box b = new Box(extent, extent, extent);
Geometry g = new Geometry(name, b);
Material m = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
m.setColor("Color", color);
g.setMaterial(m);
return g;
}
private Node textMe(String theText, ColorRGBA color) {
Node r = new Node();
BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt");
BitmapText txt = new BitmapText(fnt, false);
// txt.setBox( new Rectangle(
// 0,
// 0,
// 16,
// 3 ) );
txt.setQueueBucket(Bucket.Transparent);
txt.setSize(110.5f);
txt.setText(theText);
txt.setColor(color);
// txt.setAlignment( Align.Center );
r.attachChild(txt);
r.addControl(new BillboardControl());
return r;
}
}
[/java]