Attaching 2 bodies after collision

hihi, does anyone have any idea how do i attach 2 seperate bodies/shape after i make them collide into each other

Hello,

Assuming that you already know how to detect a collision
(either by Panda Collision System, either by your own system),
One possible options would be to reparent the node path of one of your colliding entities to the nodepath of the other entities.

ex : entitie A is an Arrow and entities C is a car
When A collide in C, you reparent A to C with a setPos/setHPR that ensure that A is properly placed at the collision point.
(the set Pos/Hpr is called once at collision time,then A will follow C due to the parentship)

You have also to disable the tasks that where moving the entity that will now follow the other one …

thanks, but i’m using pyode as my physics engine and i know how to detech collision but can i apply the attach node to pyode?

I dont know PyODE

but in NxPanda you need to attach the Physics node that u use.
attach the actor node wont do anything cause ur actor are updated by your Physic node

if PyODE is simmilar might this help u very very little

Basically, in your main loop using pyode you have this:

# Detect collisions and create contact joints
 space.collide((world,contactgroup), near_callback)

 # Simulation step
 world.step(dt/n)

 # Remove all contact joints
 contactgroup.empty()

And the callback method that will be called in a collision is like:

# Collision callback
def near_callback(args, geom1, geom2):
    """Callback function for the collide() method.

    This function checks if the given geoms do collide and
    creates contact joints if they do.
    """

    # Check if the objects do collide
    contacts = ode.collide(geom1, geom2)

    # Create contact joints
    world,contactgroup = args
    for c in contacts:
        c.setBounce(0.2)
        c.setMu(5000)
        j = ode.ContactJoint(world, contactgroup, c)
        j.attach(geom1.getBody(), geom2.getBody())

There creates the contacts between the geoms, and then it makes a ContactJoint with them so it doesn’t interpenetrate each other.

And somewhere in the main loop you update the position of the nodepaths using the position from the bodies of ode.

hmmm but what are nodepath and is there any codes that i can use??? and also where do i add them in the code??

www.panda3d.org/apiref.php?page=NodePath
www.panda3d.org/manual/index.php/The_Scene_Graph

in the main loop I call the method ‘updatePanda’ of every object in my scene, that update the object from Panda using their body from ode.

def updatePanda(self):
        self.np.setPos(Point3(*self.body.getPosition()))
        self.np.setQuat(Quat(*self.body.getQuaternion()))

hi sorry to take so long to reply, i tried using the codes but they do not work. Can u show me the whole code so i can know where to put them?? thank you very much

actually, I’m also trying a code to make 2 ODE objects link together.

        self.gorillaX = self.cuby.getX()
        self.gorillaY = self.cuby.getY()
        self.gorillaZ = self.cuby.getZ()
        
        self.cubeX = self.cuby1.getX()
        self.cubeY = self.cuby1.getY()
        self.cubeZ = self.cuby1.getZ()
        
        if ((self.gorillaX - self.cubeX) < 5) and ((self.gorillaY - self.cubeY) < 5) and ((self.gorillaZ - self.cubeZ) < 5):
            self.cuby1.setPos(self.cuby.getX()+5,self.cuby.getY()+5,self.cuby.getZ()+5)
            self.cuby1.setQuat(render,Quat(self.cuby.getQuat()))

In the code above, I picture in my mind that whenever my gorilla comes within 5 units of the cube, they will get stuck together. The simulation still runs but the code above doesnt seem to have any effect on the simulation. Could someone tell me what is it that i’m doing wrong?

Honestly very hard to tell.

  1. when is run your code?
    If it’s not run in a task , when it will be run only once so he will not keep the two pieces together at all time, only once

  2. maybe it would be more simple either :

-A) remove the tracking pieces from ode, reparent it in panda to the tracked piece (so in ode you update only one body and the visual of the second body always follow the visual of the body you update in ode)

-B) set a joint in ode that keep the second body at the same position than the first body.

woozer, show your code here, so we can see what’s wrong. Maybe this tutorial from pyode can help you: pyode.sourceforge.net/tutorials/tutorial3.html
Try to do this tutorial in Panda.

Valor, if you are using ode, maybe you should use contactJoint?

I’m not really familiar on how the joints work.

how do i actually use the contact joints? as far as i know contact joints are used to prevent the penetration of the objects

In the tutorial 3, to my understanding, the contactjoints are created whenever it detects the geoms colliding and then it is attached to the 2 geoms thus preventing intersection. However i am not sure on how to make use of the joints to make them stick to each other whenever they collide.

on a side note, what does setBounce and setMu do? i did some experimenting with setMu and i think it has something to do with friction of the geoms? i tried changing the setBounce settings but it didnt seem to affect the simulation at all.

There is a joint for that purpose, it’s FixedJoint.
Usage :

fJ=ode.FixedJoint(ode_WORLD)
fJ.attach(body1,body2)
fJ.setFixed()
self.myJoints.append(fJ)

You have to save the joint so it won’t be garbage collected by Python. self.myJoints is an ordinary Python list.

You’re right, contactJoint is to prevent the penetration of the objects. Like ynjh_jo said, FixedJoint must do the job :slight_smile:

setMu is about friction and setBounce set the bounciness of the objects when they contact each other. Try changing the mass or others settings to see the setBounce effect.

Hey thx for the Fixed Joint tip. I am now able to link my objects together. However, i am still encountering some problems. I am unable to link them ONLY when they collide. Now i can only link them right at the start.

I’m thinking that in order for them to link only when they collide, i would need to make use of the ode.collide method. However, i am puzzled to what exactly does that method accept?

I tried using my ode bodies.

contacts = ode.collide(self.SIM_cuby.body, self.SIM_cuby1.body)

However an error kept saying that those instances had no attribute ‘_id’. What exactly does ode.collide accept?

First, body doesn’t responsible for collision, it’s geom’s job.
As you can see in the API reference, or in ODEsim.near_callback :

contacts = ode.collide(geom1, geom2)

Second, are you using my ODEobject class ?
If so, look in ODEobject.storeProps, in there I added an attribute for every geom, it’s noColl, which is used to ignore the collision between 2 geoms if they have the same noColl value.

self.geom.noColl=noColl_ID

Then in ODEsim.near_callback, before performing collision check, first I check those noColl values to early-out and avoid time waste for collision check.

You could follow this scheme to glue them at the 1st time they collide, and mark them so in the future, if they still glued together, you wouldn’t need to glue them again and again.
I would do it this way :

  1. change storeProps to :
def storeProps(self, realObj, density, friction, noColl_ID=None, sticky_ID=None):

and of course change ODEbox/sphere/etc’s init and their storeProps calls.
2. still in storeProps, add :

        if sticky_ID==None:
           self.geom.stickyID = id(self.geom)
        else:
           self.geom.stickyID = sticky_ID
           self.geom.isSticking = False
  1. in ODEsim.near_callback, after ode.collide call :
        # Check if the objects do collide 
        contacts = ode.collide(geom1, geom2) 
        if not len(contacts): 
           return 

        if geom1.stickyID==geom2.stickyID:
           # both objects already stick to each other
           if geom1.isSticking and geom2.isSticking:
              # if the connection must be broken
              if geom1.breakConn and geom2.breakConn:
                 del self.myGlueJoints[str(geom1.stickyID)+str(geom2.stickyID)]
                 geom1.isSticking = geom2.isSticking = False
              return
           fJ=ode.FixedJoint(self.ode_WORLD)
           fJ.attach(geom1.getBody(),geom2.getBody())
           fJ.setFixed()
           self.myGlueJoints[str(geom1.stickyID)+str(geom2.stickyID)] = fJ
           # mark them to avoid multiple FixedJoints in the future
           geom1.isSticking = geom2.isSticking = True
           geom1.breakConn = geom2.breakConn = False
           return
  1. in ODEsim’s init, define
self.myGlueJoints = {}

Yes, I would use a dictionary instead of a list, to allow easy indexing the joints, based on the geoms’ stickyIDs, so breaking the glue would be easy.

Once you need to break the glue, do this :

self.SIM_cuby.geom.breakConn = self.SIM_cuby1.geom.breakConn = 1

Note : you’re responsible to separate the unglued bodies (by applying an explosive force or so), so they wouldn’t immediately glued again, since they would still colliding.

I didn’t try the whole thing above, but I’m sure it will work.

Yes i am using your ODEobject class. I think its really incredible :smiley: .

I tried the code out, but whenever i run it, this error occurs endlessly though the simulation is still able to run without the sticking function.

Exception exceptions.AttributeError: "geom has no attribute 'stickyID'." in 'ode.collide_callback' ignored

I added an additional condition to the following code.

if hasattr(geom1, 'stickyID'):
            if hasattr(geom2, 'stickyID'):
                if geom1.stickyID==geom2.stickyID: 
                    # both objects already stick to each other 
                    if geom1.isSticking and geom2.isSticking: 
                        # if the connection must be broken 
                        if geom1.breakConn and geom2.breakConn: 
                            del self.myGlueJoints[str(geom1.stickyID)+str(geom2.stickyID)] 
                            geom1.isSticking = geom2.isSticking = False 
                        return 
                    fJ=ode.FixedJoint(self.ode_WORLD) 
                    fJ.attach(geom1.getBody(),geom2.getBody()) 
                    fJ.setFixed() 
                    self.myGlueJoints[str(geom1.stickyID)+str(geom2.stickyID)] = fJ 
                    # mark them to avoid multiple FixedJoints in the future 
                    geom1.isSticking = geom2.isSticking = True 
                    geom1.breakConn = geom2.breakConn = False 
                    return

When i added the part where it checks to see if it has the attribute stickyID, the error disappears.

I suspect that there is something wrong with the code where the attribute was created, though i could see nothing wrong with it, seeing how you created the noColl attribute the same way. Perhaps there is something wrong with the id(self.geom)? I do not understand how the id() works though.

It must not be set for all geoms in your sim. But it’s OK to set it only for the geoms you need, in conjuction with hasattr check.
If you want to know what id() is, try help(id).

Thx it worked! Thank you everyone for all the help!