Factory Scene with ODE

ODE is already included in the Panda3D 1.7.0 distribution. The ODE classes are all under pandac.PandaModules, so since you've already imported everything from there into your program (using the '*'), you don't have to do anything to start using them.

  1. The simulation actually takes place in two separate invisible simulation environments, one for the physics (called world) and another for collision handling (called space). So the first thing you do (you can do all of this in the constructor of your World class) is to instantiate and set up these two environments:
            # Set up the physical world
            self.world = OdeWorld()
            self.world.setGravity((0,0,-9.81))
            # Surface properties
            self.world.initSurfaceTable(1) # We define 1 surface material
            self.world.setSurfaceEntry(0, 0, 150, 0.0, 9.1, 0.9, 0.00001, 0.0, 0.002) # Default surface material
            # Simulation space with collision
            self.space = OdeSimpleSpace()
            self.space.setAutoCollideWorld(self.world)        
            self.contactgroup = OdeJointGroup()
            self.space.setAutoCollideJointGroup(self.contactgroup)
            self.space.setCollisionEvent("collision")

    For each object you want to be part of the physics simulation, you have to create a physical body. Here is how the physical body of the box is created (note that you still need your original Panda model of the box, that's the visible version of the box):

            # Physical body for the box
            self.boxbody = OdeBody(self.world)
            self.boxbody.setPosition((1,0,2))
            self.boxbody.addForce((0,0,1000))
            self.boxmass = OdeMass()
            self.boxmass.setBox(500, 0.3, 0.3, 0.3)
            self.boxbody.setMass(self.boxmass)

    Now that you have a physical representation of the box, you still need to make an ODE collision solid for it and put into the collision environment. This is how:

            # Collision body for the box
            self.boxsolid = OdeBoxGeom(self.space, 0.3, 0.3, 0.3)
            self.boxsolid.setCollideBits(BitMask32(0x00000001)) # Filters what objects this Box can collide into (required category)
            self.boxsolid.setCategoryBits(BitMask32(0x00000001)) # The Box itself is of that same category
            self.boxsolid.setBody(self.boxbody)

    To see the effect of gravity on the box, you will now have to start a task that copies the position of the physical box to the position of the visual box at every frame, and advances the physics simulation world by one step:

            taskMgr.add(self.simulate, 'ODE Simulation')        
     
        def simulate(self, task):
            self.space.autoCollide() # Resolve collisions
            # Set the position and rotation of the graphical box from the physical box
            self.box.setPosQuat (render, self.boxbody.getPosition(), Quat(self.boxbody.getQuaternion()))
            self.world.quickStep(globalClock.getDt()) # Advance the simulation
            self.contactgroup.empty() # Clear collected collisions
            return Task.cont

    Test this and make sure gravity is working as expected. Make sure to comment out the LerpPosInterval for the box since you no longer need it! You should also change the left mouse button handler so that it sets the position of both the visual box and the physical box back to the original position (to maintain similar functionality as before). You will also want to set the linear velocity of the box to 0 like this self.boxbody.setLinearVel( (0,0,0) ), otherwise the box will keep going faster and faster

  2. To introduce collision with the door, you will need to create both a physical body for the door and a ODE collision solid for the door, just like you did for the box. Try using size of (2,4,0.1) and density of 1000. Don't forget to update the door's visual position based on the physical position in the simulation task! Your simulate task should now look like this:
        def simulate(self, task):
            self.space.autoCollide() 
            self.box.setPosQuat (render, self.boxbody.getPosition(), Quat(self.boxbody.getQuaternion()))
            self.door.setPosQuat(render, self.doorbody.getPosition(), Quat(self.doorbody.getQuaternion()))
            self.world.quickStep(globalClock.getDt()) 
            self.contactgroup.empty()
            return Task.cont

    You may notice that the door naturally falls down along with the box since there is nothing holding it up! We'll fix that next.

  3. You need to attach the door to the environment so that it doesn't fall. In fact, the door is attached to the environment with a so-called slider joint because it can slide from side-to-side. ODE provides a number of joints for attaching objects to each other or to the environment. You create and attach the slider joint like this:
            # Sliding motor for the door
            self.doorslider = OdeSliderJoint(self.world) # Instantiating a new slider joint
            self.doorslider.attach(self.doorbody, None)  # Using the joint to attach door body to environment (None)
            self.doorslider.setAxis((-1,0,0))            # Slides down the x-axis
            self.doorslider.setParamLoStop(0)            # Minimum position on the slider 
            self.doorslider.setParamHiStop(2)            # Maximum position on the slider
            self.doorslider.setParamFMax(50)             # Sets the force of a motor attached to this joint          

    The last line here actually attaches a little motor to the joint, which we can give a certain velocity whenever we want to move the joint autonomously. Try replacing the LerpPosInterval calls for opening and closing the door, with calls like this: self.doorslider.setParamVel(0.5), where 0.5 is the velocity (can also be negative to go in the other direction).

  4. Finally, make sure to remove all previous collision handling, since ODE will take care of it from now on. Unfortunately, the events generated by the ODE system behave a bit differently than the events created by Panda's collision system, so playing a sound when collision occurs gets tricky. Don't worry about that for now, and enjoy the physical simulation without sounds.
  5. For extra excitement try the following: Create an instance of a box every time you press the mouse button. Maintain a list of created boxes in the world and iterate over that list in the simulation function to update their positions and rotations. This way you can fill the scene with tumbling boxes!