[SOLVED] Minie: Collision Mesh does not respect parent node rotation

Hello @sgold,
I am probably doing something wrong, but I think I found a bug in Minie.

If I have a spatial (with a RidgedBodyControl) attached to a parent node, and the node has a rotation on it, the collision mesh on the ridged body control will not be rotated with the spatial. But translation on the parent node does work, just not rotation.

I have built a test example:

public class PhysicsTest extends SimpleApplication {

    private boolean cursor = false;
    private BulletAppState physics;

    @Override
    public void simpleInitApp() {
        Node world = new Node("world");
        //Load lighting
        JmeLightingState lightingState = new JmeLightingState();
        stateManager.attach(lightingState);

        //Physics
        physics = new BulletAppState();
        physics.setDebugEnabled(true);
        //Enable multi-threading
        physics.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
        stateManager.attach(physics);

        //Import Avocado
        Node rotateMe = new Node();
        Spatial building = assetManager.loadModel("Models/kitbash3d/KB3D_Highways-Native_seperate_tex/KB3D_HGW_Highway45Turn_A_grp.j3o");
        rotateMe.attachChild(building);
        rotateMe.setLocalRotation(new Quaternion(Quaternion.ZERO));
        rotateMe.setLocalTranslation(0f, 5f, 0f);

        RigidBodyControl control = new RigidBodyControl(CollisionShapeFactory.createMeshShape(building), 1);
        control.setKinematic(true);
        world.attachChild(rotateMe);


        rootNode.attachChild(world);
        
        building.addControl(control);
        physics.getPhysicsSpace().add(control);

        cam.setLocation(new Vector3f(0, 200f, 600f));
        cam.setFrustumFar(5000f);
        flyCam.setMoveSpeed(100f);

        ActionListener tab = (name, isPressed, tpf) -> {
            if (!isPressed) {
                cursor = !cursor;
                flyCam.setEnabled(cursor);
            }
        };
        
        inputManager.addMapping("tab", new KeyTrigger(KeyInput.KEY_TAB));
        inputManager.addListener(tab, "tab");
    }

    public static void main(String[] args) {
        PhysicsTest main = new PhysicsTest();
        main.start();
    }
}

My tests have PBR lighting in a separate app state here:

JmeLightingState
public class JmeLightingState extends BaseAppState {

    private DirectionalLight dl;
    private SimpleApplication app;
    private FilterPostProcessor fpp;
    private EnvironmentCamera envCam;
    private LightProbe probe;
    private Node lightingNode;

    private short renderSteps = 0;
    private int probeRadius = 5000;
    private boolean probing = true;

    @Override
    protected void initialize(Application a) {
        app = (SimpleApplication) a;
        app.getViewPort().setBackgroundColor(ColorRGBA.White);
        lightingNode = (Node) app.getRootNode().getChild("world"); //For iso-test
        lightingNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);

        AmbientLight al = new AmbientLight();
        al.setColor(ColorRGBA.White.mult(1.3f));
        lightingNode.addLight(al);

        dl = new DirectionalLight();
        Vector3f lightDir = new Vector3f(-0.12f, -0.3729129f, 0.74847335f);
        dl.setDirection(lightDir);
        lightingNode.addLight(dl);
        dl.setColor(ColorRGBA.White);

        final int SHADOWMAP_SIZE=4096;
        /*

        DirectionalLightShadowRenderer dlsr = new DirectionalLightShadowRenderer(app.getAssetManager(), SHADOWMAP_SIZE, 4);
        dlsr.setLight(dl);
        dlsr.setLambda(0.55f);
        dlsr.setShadowIntensity(0.8f);
        dlsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
        //dlsr.displayDebug();
        app.getViewPort().addProcessor(dlsr);
        */

        DirectionalLightShadowFilter dlsf = new DirectionalLightShadowFilter(app.getAssetManager(), SHADOWMAP_SIZE, 4);
        dlsf.setLight(dl);
        dlsf.setLambda(1f);
        dlsf.setShadowIntensity(0.4f);
        dlsf.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON);
        dlsf.setEnabled(true);

        LightScatteringFilter godRays = new LightScatteringFilter(lightDir.multLocal(-3000));
        godRays.setNbSamples(50);
        godRays.setBlurStart(-0.037999995f);
        godRays.setBlurWidth(0.8170011f);
        godRays.setLightDensity(0.36198944f);
        //LightScatteringUI ui = new LightScatteringUI(app.getInputManager(), godRays);

        //Filter
        fpp = new FilterPostProcessor(app.getAssetManager());
        fpp.addFilter(new FXAAFilter());
        fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(4.0f)));
        fpp.addFilter(dlsf);
        fpp.addFilter(godRays);
        fpp.addFilter(new SSAOFilter(5.1f, 1.2f, 0.2f, 0.1f));
        app.getViewPort().addProcessor(fpp);

        Spatial sky = SkyFactory.createSky(app.getAssetManager(), "Textures/Path.hdr", SkyFactory.EnvMapType.EquirectMap);
        app.getRootNode().attachChild(sky);

        //Env Cam
        envCam = new EnvironmentCamera(256, new Vector3f(0, 3f, 0));
        app.getStateManager().attach(envCam);

        //LightsDebugState debugState = new LightsDebugState();
        //app.getStateManager().attach(debugState);

        //MaterialDebugAppState debug = new MaterialDebugAppState();
        //debug.registerBinding("Common/MatDefs/Light/PBRLighting.frag", app.getRootNode());
        //debug.registerBinding("Common/ShaderLib/PBR.glsllib", app.getRootNode());
        //getStateManager().attach(debug);

    }

    @Override
    protected void cleanup(Application a) {
        app.getRootNode().removeLight(dl);
        app.getViewPort().removeProcessor(fpp);
        app.getStateManager().detach(envCam);
    }

    @Override
    public void update(float tpf) {
        if (probing) {
            renderSteps++;
            if (renderSteps == 2) { //Give the scene a frame to update
                //System.out.println("Starting PBR Probe");

                lightingNode.removeFromParent();
                probe = LightProbeFactory.makeProbe(app.getStateManager().getState(EnvironmentCamera.class), app.getRootNode(), new JobProgressAdapter<LightProbe>() {

                    @Override
                    public void done(LightProbe result) {
                        //System.out.println("PBR Probe results in");
                    }
                });
                ((SphereProbeArea) probe.getArea()).setRadius(probeRadius);
                app.getRootNode().addLight(probe);
            } else if (renderSteps > 10) {
                app.getRootNode().attachChild(lightingNode);
                probing = false;
                renderSteps = 0;
                //System.out.println("PBR Probe Done");
            }
        }
    }

    public void reprobe() {
        probing = true;
    }

    public int getProbeRadius() {
        return probeRadius;
    }

    public void setProbeRadius(int probeRadius) {
        this.probeRadius = probeRadius;
    }

    @Override
    protected void onEnable() {
        dl.setEnabled(true);
        envCam.setEnabled(true);
        reprobe();
        app.getViewPort().addProcessor(fpp);
    }

    @Override
    protected void onDisable() {
        dl.setEnabled(false);
        envCam.setEnabled(false);
        app.getViewPort().removeProcessor(fpp);
        if (probe != null) {
            app.getRootNode().removeLight(probe);
        }
    }

}

Here I have applied both a translation and rotation to the parent node, but the collision mesh only respected the translation, not the rotation.

EDIT: I am using Minie 4.0.0

Any help is greatly appreciated.

Thank you,
Trevor

1 Like

I can confirm that the debug mesh is being rendered in the correct place. Here I have used gravity to place a very ugly boxish object on top of the road lamps


Quaternion.ZERO is not a valid rotation. Does the same issue manifest with valid rotations?

1 Like

Good morning Stephen,
Yes, with a valid rotation the mesh begins to deviate and as the rotation becomes larger the mesh deviates larger:

I am now wondering if this is actually a jme rotation issue. I am noticing that the geometries in the model do not rotate together as expected, the model begins to come apart:

You can see that here with the street lights now floating. Where with no rotation applied:

Seeing as I am rotating the parent node, all the children should rotate accordingly and not come apart…

Perhaps Minie is actually handling the rotation correctly and jme is not?

EDIT:
Here is an example of a small amount of rotation: 0.5f, 0.5f, 0.5f, 1f

Again, the lights move away from the road for some reason and nothing lines up to the collision mesh correctly, I am really starting to think this is a jme issue…

1 Like

0.5f, 0.5f, 0.5f, 1f

That’s a non-normalized quaternion. Do the discrepancies persist if you normalize the quaternion before applying it as a rotation?

1 Like

Ah, normalizing the quaternion does fix it!
I knew I must have been doing something quite simple wrong.

Now I have other issues to investigate as that fixes the test case but not my actual app, meaning I have something messed up in my real code somewhere.

I really appreciate the help Stephen, this proves out what I am trying to do will work, I just have to figure out why my real code does not do this correctly now.

1 Like

If you identify the root cause, let me know. Perhaps the software can detect it and issue a diagnostic.

1 Like

Will do, I am running into an issue in my real app where my collision mesh moves with my object, but won’t rotate with it:

EDIT: I should note, it looks like the player is standing on the road, but that is a ghost control I manually moved there to test if the debug mesh was in the wrong location.

Unfortunately the code is quite complex to troubleshoot, which is why I built the test case. So now I am back to square one. :sweat_smile:

I finally found the issue :joy:. (I’m sad to say this is day 3 of tracking this down)

Some fallback code was taking over and instead of my physics system using
CollisionShapeFactory.createMeshShape(node)
it was using
new MeshCollisionShape(getMeshes(node).toArray(new Mesh[0]))

As it turns out, the constructor for the mesh collision shapes does not correct for mesh rotation.

Now to undo all the debugging code I put in over the last 3 days. :sob:

1 Like