Negative scale params cause intersection with Ray to fail ? jme3 r7259

I know I shouldn’t be using negative params when calling setLocalScale right?

But if I do, the Box I’m trying to collide a Ray with won’t intersect in some places depending on the Ray angle.

Unless I’m missing something, I would suggest maybe to either make a comment saying scale params should never be negative or better yet enforce this in jme inside the setLocalScale

method or something? this might be useful to catch bugs if caller(/user) is using parameter variables that mistakenly go negative without user’s knowledge, some collisions won’t happen due to that…



Here’s the testcase with “XXX:” marked on the line with the negative scale params already set, just move the mouse over the Box to generate randomColor normals and see how in some places on the Box no normal will be added (due to no collisions detected); to fix this remove the two “-” signs from params on setLocalScale



http://i.imgur.com/TN1k6.png



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(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(-4f, 11f, -6f);// XXX: make all positive to fix


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();


// rootNode.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));


    // the vector perpendicular on both contact and normal vectors


    // Vector3f upVec = contact.cross(normalVec).normalizeLocal();


    // nodeLine.setLocalTransform(subRoot.getLocalTransform().clone());


    // 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);


    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

I’ve updated the code to properly orient the camera when using latest jme3 from svn, rev 7293 at the moment;

The problem still persists: when using negative scale numbers the corners of the box are non-collidable from certain angles

http://i.imgur.com/GpLR9.jpg

[java]package org.jme3.forum;

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 NegScale 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) {

NegScale app = new NegScale();

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(1.6929102f, 25.419003f, 325.00336f));

cam.setRotation(new Quaternion(0.18181261f, 0.8156827f, -0.3513611f,

0.4220797f));

cam.setFrustumFar(111111f);

// cam.setDirection(new Vector3f(0.1199981f, -0.99238f, 0.027971268f));

subRoot = new Node();

rootNode.attachChild(subRoot);

subRoot.setLocalScale(-4f, 11f, -6f);// XXX: make all positive to fix

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();

// rootNode.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));

// the vector perpendicular on both contact and normal vectors

// Vector3f upVec = contact.cross(normalVec).normalizeLocal();

// nodeLine.setLocalTransform(subRoot.getLocalTransform().clone());

// 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);

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;

}

}[/java]

if you change to

[java]subRoot.setLocalScale(4f, 11f, 6f);[/java]

you need to re orient the camera to see the box, ie.

Camera Position: (-3.721551, 35.522877, 331.6221)
Camera Rotation: (-0.06200697, 0.93627965, -0.27198285, -0.21344994)

EDIT: this still fails even with jme3 r7296