CollisionSphere/CollisionCapsule and CollisionPolygon

I would like to use a ground mesh consisting of CollisionPolygon as a ground object (into object).
My from objects are CollisionSphere and CollisionCapsules.

If the size of the CollisionPolygon is for example 10mx10m (flat surface on the ground) the CollisionSphere and the CollisionCapsules behave different.

When the sphere falls onto the CollisionPolygon from above, it reports an collision => correct
When the sphere falls down next to the CollisionPolygon (for example 50m to the left of the CollisionPolygon) it does not report an collision => also correct.
But the CollisionCapsules report an collision in both cases (where I would expect it to just report an collision in the first case(when the capsule falls down on the CollisionCapsule from above)).

It seems, that in the case of the CollisionCapsule the surfaces created from the CollisionPolygon are extended to all sides which leads to problems in my case when I create a ground mesh with objects like a small hill in it.

Thank you

Do you perhaps have a scaling factor applied to any of your collision-objects?

If so, try removing that factor. (Such as by “applying” it in your 3D modelling program or, in the case of the sphere or capsule, simply constructing the object with larger base dimensions rather than scaling it.) Scaling can really mess with the results of collisions, I do believe.

Thanks for the quick answer.
I just did a get_scale() on the CollisionSphere, CollisionCapsules and the ground mesh which reported 1,1,1 in each case.

In the case of the ground-mesh, the scale might be applied at a level lower than the top-most NodePath–models loaded from disk tend to be more than a single node. It might be worth either going through all children (and children’s children, etc.) of that mesh just to be safe, or checking manually in your 3D modelling package.

Another thought is that the scaling factor might be applied at a level above those NodePaths–for example, if your sphere has a parent-node, and that parent-node has a scale, then the scale will apply to the sphere even though a call to “getScale” on the sphere returns (1, 1, 1). You can perhaps check this by calling “getScale(render)” (presuming that your scene-root is “render”) on your objects.

I have an update on this: The issue only appears when using set_fluid_pos on the from object (CollisionCapsule) and only with CollisionCapsule, not with CollisionSpheres.

1 Like

There is a rule if you use CollisionPolygon before extracting Geom nodes. The first thing you need to do is reset the node transformation using the .flatten_light() method

Secondly, use the bullet debugging node to see what’s really going on.

I tried flatten_light(), but I couldn’t spot a difference.

I made a small test case. The lines to adjust are 23/24 for the sphere and 32/33 for the capsule. If you change set_fluid_pos to set_pos then the program behaves as I would expect.

Thanks for taking your time.

collision_capsule_sphere.zip (793 Bytes)

I seem to have made a mistake with the advice, as I confused the bullet with the panda’s collisions.

I also noticed that you use a strong transformation of the collision body shape, which in itself is already a problem.

#include "pandaFramework.h"
#include "pandaSystem.h"

#include "collisionSphere.h"
#include "collisionPolygon.h"
#include "collisionCapsule.h"
#include "collisionNode.h"
#include "collisionHandlerQueue.h"

int main(int argc, char* argv[]) {

    PandaFramework framework;
    framework.open_framework(argc, argv);

    // open the window
    WindowFramework *window = framework.open_window();

    // create ground plande
    PT(CollisionPolygon) gcp = new CollisionPolygon(LVecBase3(-3, -3, -1.0), LVecBase3(3, -3, -1.0),
                                                    LVecBase3(3, 3, -1.0), LVecBase3(-3, 3, -1.0));
    PT(CollisionNode) gcp_cn = new CollisionNode("coll polygon collision node");
    gcp_cn->add_solid(gcp);
    NodePath gcp_np = window->get_render().attach_new_node(gcp_cn);
    gcp_np.set_y(10);
    gcp_np.show();

    // add sphere
    PT(CollisionSphere) cs = new CollisionSphere(0.0f, 0.0f, 0.0f, 0.25f);
    PT(CollisionNode) sphere_cn = new CollisionNode("Sphere CN");
    sphere_cn->add_solid(cs);
    NodePath sphere_np = window->get_render().attach_new_node(sphere_cn);
    sphere_np.set_fluid_pos(0,50,-1);
    sphere_np.show();

    // add capsule
    PT(CollisionCapsule) cc = new CollisionCapsule(-1, 0, 0, 1, 0, 0, 0.3);
    PT(CollisionNode) capsule_cn = new CollisionNode("Capsule CN");
    capsule_cn->add_solid(cc);
    NodePath capsule_np = window->get_render().attach_new_node(capsule_cn);
    capsule_np.set_fluid_pos(0,10,-1);
    capsule_np.show();

    // create queue + traverser
    PT(CollisionHandlerQueue) coll_queue = new CollisionHandlerQueue();
    CollisionTraverser traverser("Collision Traverser");
    traverser.set_respect_prev_transform(true);
    traverser.add_collider(sphere_np, coll_queue);
    traverser.add_collider(capsule_np, coll_queue);

    // traverse
    traverser.traverse(window->get_render());

    framework.main_loop();
    framework.close_framework();

    return (0);
}

I get a static scene, replacing set_fluid_pos with set_pos, doesn’t change anything.

1 Like

I am using “notify-level-collide debug” in the config.prc to see if the traverser sees a collision.
If you uncomment the line
capsule_np.set_fluid_pos(0,100,-1); // ==> collides (not expected)
and comment the previous line, then there will be a collision (between the capsule and the ground plane), although the capsule is out of the ground plane.

This is the output of the console:
:collide(debug): intersection detected from render/Capsule CN into render/coll polygon collision node
:collide(debug): intersection detected from render/Sphere CN into render/Capsule CN

It is also strange, that the sphere collides with the capsule although they are 50 units apart from each other.

Actually, that’s a good point. Your initial placement of the capsule uses “set_fluid_pos”, which means that the capsule is ostensibly being moved through the intervening space to that position. As a result, it may collide, not at the start- or end- points, but “along the way”.

And indeed, intuitively, it seems like its path from (0, 0, 0) to (0, 100, -1) might well collide, where the its path from (0, 0, 0) to (0, 10, -1) might happen to “miss”, the former having a lower slope than the latter.

What happens if you call “reset_prev_transform” directly after setting the capsule’s position to (0, 100, -1)?

(I’m not set up for C++ building right now, so I’m not in a good position to test the above myself.)

2 Likes

Those are very good points! I need to do some further digging. Thanks so far.

1 Like

I should have just read the manual…
In Rapidly-Moving Objects — Panda3D Manual there is:

At the present, the CollisionTraverser only uses the previous transform information when the “from” object is a CollisionSphere. Other kinds of collision solids currently do not consider the previous transform.

Also I had to call reset_all_prev_transform() each frame to update the previous frame position.

Thanks again for all of your input

1 Like

I am not using set_fluid_pos now. And with the updated code:

int main(int argc, char* argv[]) {

	PandaFramework framework;
	framework.open_framework(argc, argv);

	// open the window
	WindowFramework* window = framework.open_window();

	// create ground plane
	PT(CollisionPolygon) gcp = new CollisionPolygon(LVecBase3(-3, 15, -6), LVecBase3(3, 15, -6), 
												    LVecBase3(3, 15, -1), LVecBase3(-3, 15, -1));
	PT(CollisionNode) gcp_cn = new CollisionNode("coll polygon collision node");
	gcp_cn->add_solid(gcp);
	NodePath gcp_np = window->get_render().attach_new_node(gcp_cn);
	//gcp_np.set_y(10);
	gcp_np.show();
	gcp_np.flatten_light();
	//gcp_np.show_bounds();

	// add sphere
	PT(CollisionSphere) cs = new CollisionSphere(0.0f, 0.0f, 0.0f, 0.5f);
	PT(CollisionNode) sphere_cn = new CollisionNode("Sphere CN");
	sphere_cn->add_solid(cs);
	NodePath sphere_np = window->get_render().attach_new_node(sphere_cn);
	sphere_np.set_pos(-1,15,1.1);
	sphere_np.show();
	//sphere_np.show_bounds();

	// add capsule
	PT(CollisionCapsule) cc = new CollisionCapsule(-0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f);
	PT(CollisionNode) capsule_cn = new CollisionNode("Capsule CN");
	capsule_cn->add_solid(cc);
	NodePath capsule_np = window->get_render().attach_new_node(capsule_cn);
	capsule_np.set_pos(1,15,1.1);	// ==> collides (as expected)
	capsule_np.show();
	//capsule_np.show_bounds();
	
	// create queue + traverser
	PT(CollisionHandlerQueue) coll_queue = new CollisionHandlerQueue();
	CollisionTraverser traverser("Collision Traverser");
	//traverser.set_respect_prev_transform(true);
	traverser.add_collider(sphere_np, coll_queue);
	traverser.add_collider(capsule_np, coll_queue);

	// traverse
	traverser.traverse(window->get_render());


	framework.main_loop();
	framework.close_framework();

	return (0);
}

I get the following output:

and panda reports a collision between the capsule and the polygon:

:collide(debug): intersection detected from render/Capsule CN into render/coll polygon collision node

The sphere does not report a collision.
Is there something I could do to improve the accuracy of the capsule/polygon collision detection?

Thanks again

Let me check, if I may: As you have a CollisionHandlerQueue handling the objects in question, are you finding CollisionEntries in the queue? Or are you only seeing debug output?

Further, what happens if you don’t flatten the CollisionPolygon?

Before the traverser runs, there are 0 entries in the queue. Afterwards there is one entry. I checked with: coll_queue->get_num_entries()
The behavior is the same even if the CollisionPolygon is not flattened.

Hmm, very odd.

And I’ve replicated your example program (more or less) in Python, and can confirm this odd behaviour.

For reference, my setup is as follows:
OS: Ubuntu 18.04.6
Python: 3.6.9
Panda: 1.10.12

This may be a bug in Panda’s collision-detection–it might thus be worth your posting an issue for it, along with the relevant code, in the issue-tracker.

In case it helps, here is a trimmed-and-cleaned version of my Python implementation of the example code given above:


from direct.showbase.ShowBase import ShowBase

from panda3d.core import Vec3, CollisionSphere, CollisionHandlerEvent, CollisionTraverser, CollisionNode, CollisionPolygon, CollisionCapsule
from direct.gui.DirectGui import *

from panda3d import __version__ as pandaVersion
print (pandaVersion)

import sys
print (sys.version)

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.cTrav = CollisionTraverser()
        self.handler = CollisionHandlerEvent()
        self.handler.addInPattern("collision")
        self.accept("collision", self.bulletCollides)

        plane = CollisionPolygon(Vec3(-3, 15, -6), Vec3(3, 15, -6), 
								Vec3(3, 15, -1), Vec3(-3, 15, -1))
        wall = CollisionNode("poly")
        wall.addSolid(plane)
        wall.setIntoCollideMask(1)
        wall.setFromCollideMask(0)
        wallNP = render.attachNewNode(wall)
        wallNP.show()

        collider = CollisionNode("sphere")
        shape = CollisionSphere(0, 0, 0, 0.5)
        collider.addSolid(shape)
        collider.setIntoCollideMask(0)
        collider.setFromCollideMask(1)
        collisionNP = render.attachNewNode(collider)
        collisionNP.setPos(-1, 15, 1.1)
        collisionNP.show()
        self.cTrav.addCollider(collisionNP, self.handler)

        collider = CollisionNode("capsule")
        shape = CollisionCapsule(-0.5, 0, 0, 0.5, 0, 0, 0.5)
        collider.addSolid(shape)
        collider.setIntoCollideMask(0)
        collider.setFromCollideMask(1)
        collisionNP = render.attachNewNode(collider)
        collisionNP.setPos(1, 15, 10.1)
        collisionNP.show()
        self.cTrav.addCollider(collisionNP, self.handler)

        self.mew = collisionNP

        render.setShaderAuto()

        self.updateTask = self.taskMgr.add(self.update, "update task")

    def bulletCollides(self, entry):
        print ("COLLISION!", entry)

    def update(self, task):
        dt = self.clock.getDt()

        self.mew.setZ(self.mew, -dt)

        return task.cont


app = Game()
app.run()

I’m using a CollisionHandlerEvent there simply because I already had one set up in my test-code. It makes little difference, I daresay.

Note too that I’ve arranged for the capsule to start at a higher z-coordinate, and to then slowly descend. This demonstrates that the issue isn’t present for all positions, but does occur well beyond the edge of the CollisionPolygon.

2 Likes

Thank you.
I opened issue: CollisionCapsule colliding with CollisionPolygon where it should not · Issue #1369 · panda3d/panda3d · GitHub

1 Like

For the record, Panda 1.10 did not actually implement a capsule-into-polygon test, as is shown in the table here:
https://docs.panda3d.org/1.10/python/programming/collision-detection/collision-solids

But, I just went ahead and implemented the test, since it’s the last remaining missing into-poly test. It will be in 1.10.13.

4 Likes

Ah, that’s excellent–thank you for doing that! :slight_smile:

I did a quick test, and it works! Thank you.

1 Like