dummy node collision ray

I want to have all my solid objects collide with terrain.
So I want to create a collision ray on a dummy linked to all these objects.
I’m not sure if this is the way to accomplish it.
I tried it, but my balls are in the air.
Could someone show me the right way please?

self.dummyNode = render.attachNewNode(“renderAllModels”)

Ball (big)

    self.ball01 = loader.loadModel("Models/ball_big")
    self.ball01.setPos(-200, 180, 0)
    self.ball01.reparentTo(self.dummyNode)

Ball (small)

    self.ball02 = loader.loadModel("Models/ball_small")
    self.ball02.setPos(200, 180, 0)
    self.ball02.reparentTo(self.dummyNode)
    
    # solid objects
    self.soGroundRay = CollisionRay()
    self.soGroundRay.setOrigin(0,0,1000)
    self.soGroundRay.setDirection(0,0,-1)
    self.soGroundCol = CollisionNode('soRay')
    self.soGroundCol.addSolid(self.soGroundRay)
    self.soGroundCol.setFromCollideMask(BitMask32.bit(0))
    self.soGroundCol.setIntoCollideMask(BitMask32.allOff())
    self.soGroundColNp = self.dummyNode.attachNewNode(self.soGroundCol)
    self.soGroundHandler = CollisionHandlerQueue()
    self.cTrav.addCollider(self.soGroundColNp, self.soGroundHandler)

#In running task
# Adjust solid objects Z coordinate.
entries = []
for i in range(self.soGroundHandler.getNumEntries()):
entry = self.soGroundHandler.getEntry(i)
entries.append(entry)
entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(),
x.getSurfacePoint(render).getZ()))
if (len(entries)>0) and (entries[0].getIntoNode().getName() == “ter01”) or (len(entries)>0) and (entries[0].getIntoNode().getName() == “path01”) or (len(entries)>0) and (entries[0].getIntoNode().getName() == “bridge01”) or (len(entries)>0) and (entries[0].getIntoNode().getName() == “gdn01”):
self.dummyNode.setZ(entries[0].getSurfacePoint(render).getZ())

Hmm… First of all, why are you connecting your ray to a single dummy node above your nodes of interest? Surely you want a ray placed at each of your objects of interest, and directed down at the ground below each particular object, rather than the same point for each object? Of course, if your terrain is perfectly flat, then this might well make sense.

Secondly, what is the purpose of the list “entries”? It looks as though you’re using it to sort the results found in the queue handler, either from highest collision to lowest or vice versa. As it happens, collisionHandlerQueue already includes a method that allows you to sort the collision from nearest to furthest.

As to the reason that they’re not moving at all (I take it), I see that you have your objects at z-position 0; at what level is your terrain?

Err

  1. I don’t quite understand how to use new nodes on node paths.
  2. I am just using the code in the move task of the ralph tut. The list is for the other parts of the terrain I added seperately. Sorry, I was supposed to remove them before I posted.

Hence…
Could someone show me the right way please?

Have you looked at collisionHandlerFloor, as seen at the bottom of this page?

If that doesn’t work for you, perhaps take a look at collisionHandlerQueue, on the same page, and in particular the mention of sortEntries, as well as the entries on collision entries and collision traversers (since I suspect that a separate collision traverser might be useful).

Either way, collision bitmasks might be useful for differentiating objects that you want to consider as “terrain” from those that you do not.

You might find some useful information in the section on clicking on 3D objects, too, as it involves the use of a ray to detect specific objects.

The basic idea, I would say, presuming that collisionHandlerFloor doesn’t work out, would be to fire a ray from each object downwards, collect the results in a list (or queue :wink:) for each object, sort that list, and then take the first (i.e. nearest) result and find the surface collision point.

As to new nodepaths, what did you intend? Do you want all objects to move when that one nodepath moves?

When you parent (or reparent) an object to a nodepath, that object is moved relative to the nodepath; if you move the nodepath, the object moves too, but you can still move the object itself (albeit now relative to the nodepath), if I’m not much mistaken.

Is there anything more specific that you’re uncertain of with regards to nodes and nodepaths?

Thank you very much for replying Thaumaturge, but you I’m not sure you understand what I am trying to accomplish. Let me try to explain.

I do not have a problem performing collisions. The tutorials and manual are not too hard to follow on these.

I can get a thousand models to collide with my terrain if I tried, but herein lies the area I need assistance.

Rather than do this for every object:

    # player
    self.Mod1GroundRay = CollisionRay()
    self.Mod1GroundRay.setOrigin(0,0,1000)
    self.Mod1GroundRay.setDirection(0,0,-1)
    self.Mod1GroundCol = CollisionNode('ralphRay')
    self.Mod1GroundCol.addSolid(self.Mod1GroundRay)
    self.Mod1GroundCol.setFromCollideMask(BitMask32.bit(0))
    self.Mod1GroundCol.setIntoCollideMask(BitMask32.allOff())
    self.Mod1GroundColNp = self.Mod1.attachNewNode(self.Mod1GroundCol)
    self.Mod1GroundHandler = CollisionHandlerQueue()
    self.cTrav.addCollider(self.Mod1GroundColNp, self.Mod1GroundHandler)

    # camera
    self.camGroundRay = CollisionRay()
    self.camGroundRay.setOrigin(0,0,1000)
    self.camGroundRay.setDirection(0,0,-1)
    self.camGroundCol = CollisionNode('camRay')
    self.camGroundCol.addSolid(self.camGroundRay)
    self.camGroundCol.setFromCollideMask(BitMask32.bit(0))
    self.camGroundCol.setIntoCollideMask(BitMask32.allOff())
    self.camGroundColNp = self.my_camera3.attachNewNode(self.camGroundCol)
    self.camGroundHandler = CollisionHandlerQueue()
    self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)

    # ball1
    self.b1GroundRay = CollisionRay()
    self.b1GroundRay.setOrigin(0,0,1000)
    self.b1GroundRay.setDirection(0,0,-1)
    self.b1GroundCol = CollisionNode('ball1Ray')
    self.b1GroundCol.addSolid(self.b1GroundRay)
    self.b1GroundCol.setFromCollideMask(BitMask32.bit(0))
    self.b1GroundCol.setIntoCollideMask(BitMask32.allOff())
    self.b1GroundColNp = self.ball1.attachNewNode(self.b1GroundCol)
    self.b1GroundHandler = CollisionHandlerQueue()
    self.cTrav.addCollider(self.b1GroundColNp, self.b1GroundHandler)

    # ball2
    self.b2GroundRay = CollisionRay()
    self.b2GroundRay.setOrigin(0,0,1000)
    self.b2GroundRay.setDirection(0,0,-1)
    self.b2GroundCol = CollisionNode('ball2Ray')
    self.b2GroundCol.addSolid(self.b2GroundRay)
    self.b2GroundCol.setFromCollideMask(BitMask32.bit(0))
    self.b2GroundCol.setIntoCollideMask(BitMask32.allOff())
    self.b2GroundColNp = self.ball2.attachNewNode(self.b2GroundCol)
    self.b2GroundHandler = CollisionHandlerQueue()
    self.cTrav.addCollider(self.b2GroundColNp, self.b2GroundHandler)

etc.

I want to have a dummy linked to the objects, and use that dummy for collisions. Let’s say my dummy’s name is self.solidObjects, I would just have this one code which would affect all those objects.

    # solid objects
    self.soGroundRay = CollisionRay()
    self.soGroundRay.setOrigin(0,0,1000)
    self.soGroundRay.setDirection(0,0,-1)
    self.soGroundCol = CollisionNode('solidsRay')
    self.soGroundCol.addSolid(self.soGroundRay)
    self.soGroundCol.setFromCollideMask(BitMask32.bit(0))
    self.soGroundCol.setIntoCollideMask(BitMask32.allOff())
    self.soGroundColNp = self.solidObjects.attachNewNode(self.soGroundCol)
    self.soGroundHandler = CollisionHandlerQueue()
    self.cTrav.addCollider(self.soGroundColNp, self.soGroundHandler)

However, I have been trying to use dummies or newNodes unsuccessfully.
So I really need help in creating a dummy which is linked to my solid objects, and which would then be used for collisions.

I hope I have made things clearer, and someone can help.
Thanks

It’s my pleasure. :slight_smile:

Hmm, I see, I think.

Am I correct in thinking that you intend to cut down on calls by having the ray be used for the collision of all objects, instead of using many objects?

If so, then I’m not sure that it’s actually a good thing, since it means moving the ray’s (or the dummy’s) position from object to object within each step so that their positions are used instead of whatever position the ray occupied previously. It would also, I believe, call for re-traversing the collision tree for each object, since the position will have changed. Otherwise it would seem to me that you’re casting a ray from one position and using the results for many objects, resulting in the same change for many objects.

If you do want to do this, then I suggest at least using a separate traverser, so that other collisions aren’t re-run along with each object’s terrain collision.

Perhaps I’m still misunderstanding? :confused: (And I apologise if I am. ^^; )

Of course, if you just want to cut down on lines of code, why not simply move the ray-creation-and-assignment code into a method? Something along these lines (I hope that you don’t mind that I’ve copied and edited your code for this example ^_^):

def assignGroundRay(self, object):
     ray = CollisionRay()
     ray.setOrigin(0,0,1000)
     ray.setDirection(0,0,-1)
     col = CollisionNode('new_ray')
     col.addSolid(ray)
     col.setFromCollideMask(BitMask32.bit(0))
     col.setIntoCollideMask(BitMask32.allOff())
     colNp = self.ball2.attachNewNode(self.b2GroundCol)
     object.handler = CollisionHandlerQueue()
     my_traverser.addCollider(colNp, object.handler)

assignGroundRay(player)
assignGroundRay(camera)
assignGroundRay(ball1)
assignGroundRay(ball2)

etc.

By the way, if I’m not much mistaken, setting the “from” collide mask to zero should prevent any collision being detected - perhaps instead decide on some non-zero value and assign that to the ray’s and terrain’s from and into bitmasks respectively - sorry, I presumably missed that last time. ^^;

Exactly! You got it!

Can’t something to fix that? Would this solve that problem?
Panda3D Manual: Scene Graph Manipulations

I’m not sure.

No

No, you understand perfectly.

(I hope that you don't mind that I've copied and edited your code for this example ^_^)

No, I don’t mind at all.
Did you forget to change something though?

Should that be:
colNp = self.dummy.attachNewNode(col)

I’ll try this and see. Thanks for your patience and help.
One thing though I still haven’t been successful with attaching newNodes on 3d objects. Could you show me a simple example?
Thanks

You need to pass a node to attachNewNode(), not a nodepath. If you have a nodepath, the node is nodepath.node().
And there are attachCollision* methods in libpandaModules.py to cut down those lines further.

OK!
Could you point me to where I can learn about nodepath.node(), or show me an example please?
Where is libpandaModules.py to be found?
Thanks

Don’t you have the API reference already ?
If you want an example, you can start from where you failed.
libpandaModules.py is in pandac directory.

Yes I have the API reference, but the bit of infomation there, still leave me lost. I guess in some cases I need to have things ‘spelled out’ for me before I can understand how to apply it.
:question:

While reparenting as you describe might well work, it’s still a command for each object in the scene, it seems to me, and I’m still pretty confident that a new traversal would still be called for, to take into account the new position.

Not? You have (or had, at this point, if I understand correctly) a set of objects for which you want to find the ground, and you had these set to be children of a single node, to which the ray was attached. That suggests that a single position is being used (that of the parent node).

Oh yes - it appears that I missed a spot, so to speak. Sorry about that. ^^;

Without going back and looking more carefully, I think that it should have read:

colNp = object.attachNewNode(col)

Disclaimer: I’m very tired tonight, so again I may well be missing something. ^^;