Best Practices - Detailed Environ & Simple Collision

What is the best way to go about making a detailed environment and a simplified collision mesh for it? Say for example I have a detailed staircase, but I want a simple plane to represent where the character can walk up it.
Would I:
In Blender, create the environment as one object
in Blender, create an object to represent the collision on top of it
Export the environment object
Export the collision object
Load both models, using the collision egg for collision things, but hiding the collision model

?

That’s what I’ve been doing so far, but I’m just starting a really detailed map and don’t want to get too far doing things the wrong way. Also, in the collision egg, I’ve specified one part of it as the wall, and the other as the floor (for the pusher and floor collision handlers). All of the modeling is done in blender.

What should I do about parts of the environment that have the same location as the collision part? (i.e. with the staircase example, it is easy to differentiate the stairs from the plane while I am creating it; however, the collision for the side of a building will be exactly the same as it’s visual model.

My code for setting up the collision goes like this:

        # Load the collision model and hide it
        self.collisionModel = loader.loadModel("maps/TestMapCollision")
        self.collisionModel.reparentTo(render)
        self.collisionModel.hide()
        self.collisionModel.setCollideMask(BitMask32.allOff())
        
        # Get the parts of the environment that are collidable
        self.walls = self.collisionModel.find("**/wall")
        self.floors = self.collisionModel.find("**/floor")
        
        # Mark them as things you can collide into
        self.floorMask = BitMask32.bit(FLOOR_BITMASK)
        self.wallMask = BitMask32.bit(WALL_BITMASK)
        self.walls.node().setIntoCollideMask(self.wallMask)
        self.floors.node().setIntoCollideMask(self.floorMask)

        # Load the visible environment
        self.model = loader.loadModel("maps/TestMap")
        self.model.reparentTo(render)

By calling setIntoCollideMask on the geometry, you’re enable collisions with the visible geometry. This is inefficient - it is best if you use actual collision geometry.

Here’s what I’d do. I’d have two layers in Blender: one layer would contain the visible geometry, and the other will contain the collision geometry. The collision geometry will have the “Collide” property set to “Polyset descend” (under Properties).

When exporting, select both objects (by enabling both layers) and export them into the same .egg file. (You can also export them to separate .egg files if you wish, but it is not strictly necessary).

When importing, Panda3D will automatically generate optimised collision geometry for the geometry tagged with “Collide”, and will then remove that geometry so that it’s not shown (if you add the “keep” flag, the geometry will be both collidable and visible). Remember not to set any into collide mask on the visible geometry.

Thanks for clarifying things, I was not aware of what ‘keep’ meant.

Quick aside:
For the collision, is using a CollisionHandlerGravity for the geometry that can be stood on and CollisionHandlerPusher for walls a good idea?

So for walls that will act as both visible and collidable geometry, I use the ‘keep’ property.

If I continue to use bitmasks for the collision, so I can keep track of several different types (e.g. walls, floors, between players, bullets), would the following be a good idea?

In blender, I create 5 layers:
Visible Geometry
Visible Floor Collision Geometry
Hidden Floor Collision Geometry
Visible Wall Collision Geometry
Hidden Wall Collision Geometry

Then in panda, I extract the collision geometry:

visibleWalls = self.collisionModel.find("**/wallVisible")
hiddenWalls = self.collisionModel.find("**/wallHidden")
visibleFloors = self.collisionModel.find("**/floorVisible")
hiddenFloors = self.collisionModel.find("**/floorHidden")

Then I can set the proper bitmasks on each. (Both walls will have the same bit, and both floors have the same bit)

Does this sound like a good idea?

Thanks for the quick response, btw

(FWIW: When I was setting the into masks in the code above, I was setting them on the node that I had created for the sole purpose of being simple collision geometry, not the visible geometry)

Meh, I typed a long reply twice now, but both times FireFox managed to erase my post.

2 layers sounds enough for me: one for the visible geometry, one for the collision geometry. I don’t understand why you’d have both visible and hidden collision geometry - that makes no sense to me.

You can either set the bitmasks in blender (if Chicken supports that) or on the Panda side. You should only set the into masks on the CollisionNode objects! You can get a list of them using findAllMatches("**/+CollisionNode"), and find out whether it’s a wall or floor based either on the name or on a custom tag that you set on the model (e.g. a “type” property that is either “floor” or “wall”).

My reasoning for having both visible and hidden collision geometry was so that (for example) I could use the same wall for both collision and visible geometry, instead of creating two planes (one for collision and one for visible geometry) that are in the exact same place. I guess I’m afraid that it will be hard for me to line up the separate objects correctly.

The point is that collisions with visible geometry can be expensive.

Is it expensive because you can see it, or because visible geometry is often very complex?

If you want to use the same geometry for both visible geometry and collision geometry, just tag it as “Polyset keep descend”, and only import it once. Then both GeomNodes and CollisionNodes will be generated for it, and the CollisionNodes are the only ones you’ll want to set the bitmask on.