Scaling Spatial Sometimes Doesn't Work

Hi Guys,
Here is my loading model code:

final Spatial model = assetManager.loadModel(resource.path);
for(Spatial sp:((Node) model).getChildren()) {

        if(sp instanceof Geometry) {
            sp.scale(resource.scaleX, resource.scaleY, resource.scaleZ);
            sp.updateModelBound();
            RigidBodyControl rc = new RigidBodyControl(0);
            sp.addControl(rc);
            
            //sp.getControl(RigidBodyControl.class).getCollisionShape().setScale(new Vector3f(resource.scaleX, resource.scaleY, resource.scaleZ));
            sp.getControl(RigidBodyControl.class).getCollisionShape().setScale(sp.getLocalScale());
            
            bulletAppState.getPhysicsSpace().add(sp);
        }
    }

So the problem is that scaling works for the sample Sinbad model but not for the Ninja model…
What am I missing?

  • I have read most of the threads here, tried to detach & attach the RigidBodyControl but still - NinJa model is not scaled while Sinbad does.
1 Like

I think maybe if you logged (or viewed in a debugger) what the children were, what their collision shapes were, etc. then you will see what the issue is.

Your code is making about a bunch of assumptions that it shouldn’t be.

agree, you should check variables yourself or provide full testcase.

I would suggest something like(i dont tested, can have errors):

Spatial model = assetManager.loadModel(resource.path);
model.scale(resource.scaleX, resource.scaleY, resource.scaleZ);

SceneGraphVisitor visitor = (Spatial spatial) -> {
    if(spatial instanceof Geometry) {
            Geometry geometry = (Geometry)spatial;
            CollisionShape shape = CollisionShapeFactory.createMeshShape(geometry);
            shape.setMargin(0.01f);
            RigidBodyControl collisionControl = new RigidBodyControl(shape, 0f);
            collisionControl.setPhysicsSpace(PhysicsSpace.getPhysicsSpace());
            geometry.addControl(collisionControl);
    }
};
model.breadthFirstTraversal(visitor);

also i dont remember now, but i remember that physics space i required to add little later for some reasons, so maybe you might too.

myself i made something like you want i think, but i also provide Geometry names for kinematic physics (for example door)

you can even add controls based on something, for example name:

        if(spatTraversal.getName().matches("Door*")){
            spatTraversal.addControl(new DoorControl());
        }

I changed my code to use your visitor and it seems to work fine now. Can I detect collision at this point without adding the Spatial to the bulletAppState’s Physics Space?

i never did something like this before.

i know you can use bulletAppState.physicsTick(space, tpf); yourself, but anyway you will need add to Physics Space.

But when i think… you can always create new bulletAppState / physics space (virtual one) and test it there with physicsTick and collisionListener. while new bulletAppState would not be added to appStateManager(or just disabled), then it will not update itself, but only if you do physicsTick

In the documentation there is an article about non-physical collision detection:
https://wiki.jmonkeyengine.org/jme3/advanced/collision_and_intersection.html
I guess that in that case I do not need to add the Spatials to bulletAppState right?
At this point I’m only interested in collision detection between spatials and I thought that bullet is needed for that but now I see that maybe not…

you found documentation, i dont think they lie there :smiley:
right, quick look at documentation it do not say adding to bulllet Physics space is required. its even named “Non-physical”

so i belive you can just do same like in documentation code.

Note: not all shape → shape collisions are supported this way and some that are will not always provide useful contact information.

For example, Ray to anything will work… but I think Sphere to Axis-aligned Box will not. Sphere to Sphere should work well enough, I think but JME bounding shapes default to axis-aligned bounding boxes.

So it just depends on what you are really trying to do and how careful you want to be setting up your scene graph. Or you can just try it and see.

I have some use-cases. for example one of them is to check whether Ninja A “Head” geometry was hit by Ninja B “foot” geometry while B is performing the “Sidekick” animation. I’ll be glad if you can point me to some sample code or other documentation about it.

Even the physics engine wouldn’t really support that “out of the box” because it would use capsules for the characters.

You will probably need to deconstruct what you want your hit zones to be and either attach invisible spheres to them or just track them yourself. (Either can be done with attachment nodes for the bones.)

Collision with the spatial is not going to help you because in the scene graph the best you’ll get is bounds to bounds collisions… which in the case you mention would just be two big axis-aligned bounding boxes, one per character.

Is it too naive to adjust the prev. suggested code to something like this?:

SceneGraphVisitor visitor = new SceneGraphVisitor() {

        @Override
        public void visit(Spatial spatial) {
            if(spatial instanceof Geometry) {
                Geometry geometry = (Geometry)spatial;
                CollisionShape shape = CollisionShapeFactory.createMeshShape(geometry);
                shape.setMargin(0.01f);
                CollisionRigidBodyControl collisionControl = new CollisionRigidBodyControl(shape, 0f);
                collisionControl.setPhysicsSpace(PhysicsSpace.getPhysicsSpace());

                geometry.addControl(collisionControl);
                bulletAppState.getPhysicsSpace().addCollisionListener(collisionControl);

            }
        }
    };
    model.breadthFirstTraversal(visitor);

While CollisionRigidBodyControl is a custom RigidBodyControl:
public class CollisionRigidBodyControl extends RigidBodyControl implements PhysicsCollisionListener {

public CollisionRigidBodyControl(CollisionShape shape, float mass) {
    super(shape,mass);
}

@Override
public void collision(PhysicsCollisionEvent e) {
    String a = e.getNodeA().getName();
    String b = e.getNodeB().getName();
}

}

And depending on:
bulletAppState.getPhysicsSpace().addCollisionListener(collisionControl);

to trigger an event whenever part of the body (some inner geometry) will collide with some other geometry?

Or am I miss understand the entire collision detection thing… :slight_smile:

Why would you have a collision listener per control which will always be notified about every collision everywhere instead of just one global collision listener that figures out how to properly dispatch things?

Edit: in case it wasn’t clear, with your approach if you have 100 objects and 5 of them are colliding then 500 events will be sent around and you’ll ignore most of them.

So you mean I should create just one CollisionRigidBodyControl and attach it to the root Spatial (Ninja A) and one more CollisionRigidBodyControl and attach it to the other Spatial (Ninja B)?

Just want to be clear: I assume Ninja (or Sinbad or whatever) Spatial is made of of some inner geometries and I want to check whether these inner geometries where collided with some other Ninja’s inner geometries.

I hope i’m using the correct terminology…

PhysicsSpace.getPhysicsSpace().addCollisionListener(listener); - you can do it in some main initialize method

where PhysicsSpace.getPhysicsSpace() is not single object.

this is global physics space for multiple objects. you can have multiple physics spaces, but then objects from A space will not collide with object from B space as logic tell me.

No I 100000% definitely do not mean that.

I would argue that at this point in your learning, if you find yourself extending Spatial or RigidBodyControl or any of these classes that you’ve already painted yourself into a weird corner.

When a collision happens, ALL (all of them) of the collision listeners will be notified… regardless of whether or not they were involved in the collision.

Usually you register just ONE (just one, as in single, one.) listener to do your hit detection. Not one per object. One total for your whole application.

OK, OK, OK :smile: I get your point. Just one listener. I guess what confused me was the reading of this doc:
https://wiki.jmonkeyengine.org/jme3/advanced/physics_listeners.html

it shows: extends RigidBodyControl implements PhysicsCollisionListener
and since RigidBodyControl can be attached to many geometries as seen in the above visitor it made me think that listener should be registered per each geometry.

So back to the original question - if I’ll have this single listener, can it detect collision between inner (children) geometries of Ninja A and Ninja B ?

The tutorials often do stupid things in the interest of brevity… sometimes the tradeoff is not good.

It will get notified about any collision between rigid bodies (or ghosts), however you’ve constructed them.

Please see the below video:
Two Sinbads collision detection test
I have set:
bulletAppState.setDebugEnabled(true);
So I was able to see that the Physics collision shapes are aligned (location+scale) with all geometries. I have also attached one more RigidBodyControl to the root model Spatial:

    CollisionShape modelShape =
    CollisionShapeFactory.createMeshShape(model);
    RigidBodyControl modelCtl = new RigidBodyControl(modelShape,0);
    modelShape.setScale(scale);
    modelCtl.setPhysicsSpace(PhysicsSpace.getPhysicsSpace());
    model.addControl(modelCtl);
    bulletAppState.getPhysicsSpace().add(modelCtl);

All of those RigidBodyControls are added to bullet’s physics space.
Now when I move the models it looks fine, I can see that physics controls are moving.
I have also registered ONE collision listener:
bulletAppState.getPhysicsSpace().addCollisionListener(new CollisionRigidBodyControl());
Problem is that I’m not getting notifications about geometries collision in my listener…

Here is the entire model load procedure:

    SceneGraphVisitor visitor = new SceneGraphVisitor() {

        @Override
        public void visit(Spatial spatial) {
            if(spatial instanceof Geometry) {
                Geometry geometry = (Geometry)spatial;
                CollisionShape shape = CollisionShapeFactory.createMeshShape(geometry);
                shape.setMargin(0.01f);
                shape.setScale(scale);
                RigidBodyControl collisionControl = new RigidBodyControl(shape, 0f);
                collisionControl.setPhysicsSpace(PhysicsSpace.getPhysicsSpace());

                geometry.addControl(collisionControl);
                bulletAppState.getPhysicsSpace().add(collisionControl);

            }
        }
    };
    model.breadthFirstTraversal(visitor);

    CollisionShape modelShape =
            CollisionShapeFactory.createMeshShape(model);
    RigidBodyControl modelCtl = new RigidBodyControl(modelShape,0);
    modelShape.setScale(scale);
    modelCtl.setPhysicsSpace(PhysicsSpace.getPhysicsSpace());
    model.addControl(modelCtl);
    bulletAppState.getPhysicsSpace().add(modelCtl);

What am I missing?

Things that are not related to your issue but look “silly” in your code:

Probably this is not a control… it definitely should not be a control. It will confuse anyone looking at your code thinking you are trying to add a control as a listener. Just make it a listener. Name it that way. Let the control thing go.

Buggy and redundant. At best, you are about to add the spatial/control to the physics space which will already set this for you. At worst, you forget to add the spatial/control to the physics space but the control incorrectly assumes it has been added.

We don’t see where you do that. Are you sure it is being called? (I assume yes.)

I remember in my own code having to add the listener from the same thread that created the physics space… but JME code doesn’t seem to account for that and I don’t remember anyone else complaining.

Only other thing I can think of is some folks mentioning that an all kinematic scene doesn’t always generate collision events. If all of your objects are 0 mass then make sure they are also kinematic (docs says it’s the default but you can check your objects to see)… and maybe try adding a non-kinematic (mass > 0) object to the scene to see if you get collisions for it.

I thought that this line of code registers the listener and makes it work right away:
bulletAppState.getPhysicsSpace().addCollisionListener(new CollisionRigidBodyControl());

OK, I will change the class name but I thought from the documentation that a listener must inherit from either RigidBodyControl or GhostControl:
(from the docs) “…
You need to add the PhysicsCollisionListener to the physics space before collisions will be listened for. Here’s an example of a Physics Control that uses a collision listener. (The example shows a RigidBodyControl, but it can also be GhostControl.) …”

Thanks for taking the time to answer. Is there a good working sample code which I can use as reference?