====== 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. \\
- 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\\
- 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. \\
- 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 [[http://opende.sourceforge.net/mediawiki-1.6.10/index.php/Manual_%28Joint_Types_and_Functions%29|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). \\
- 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.
- 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!