Finding all objects in a moving object's field of view

I’m looking for a way to find all objects that are within a conal field of view in front of a vehicle object in my scene (ideally up to a certain distance from the vehicle). The vehicle object is movable by the user, so the FOV will move along with the vehicle. My scene uses Bullet bodies.

I’ve had a couple of ideas but have found issues with both:

  • Using Bullet’s sweep test with the FOV cone as the test shape. But it looks like this test only returns one object, and I need to find all objects in the FOV
  • Making a ghost node to hold the cone shape and checking for collisions with it. But there doesn’t seem to be a way to connect this to my moving vehicle, since the cone needs to be in a GhostNode and not the vehicle’s RigidBodyNode; the cone stays stationary when I move the vehicle

Is there a way I can get either of these methods to work, or alternatively is there a better way to go about this?

You could use a ghost node which gets updated on it’s own node scope to the instant position and heading of your BulletVehicle. You don’t need to parent it to the BulletVehicle node.

Can you not parent the NodePath that holds the GhostNode to the NodePath that holds your vehicle?

Thanks for your answer. Sounds a little hacky, but that might be what I’ll have to do. Ideally the ghost node would be attached to the vehicle so I don’t have to update them separately.

1 Like

That doesn’t seem to work. As far as I can tell, the cone is staying in one place while the vehicle moves (though the cone’s getPos() values do act like it’s moving). But for testing purposes I’ve been putting my cone in a RigidBodyNode instead of a ghost so that I can see where it is in the debug renderer, so it’s possible that’s throwing off my tests. (Do you know if there is a way to see ghost node shapes in the debugger? I can’t get them to show up.) Even when I do use a ghost node though, I’m still not getting the correct overlap results, so I believe the cone is in fact not moving then either.

As per the response to this question I asked a while back, it appears that my cone won’t move with the vehicle unless it’s in the same RigidBodyNode as the vehicle. (Which it can’t be since it needs to be in a ghost node instead.)

The thing is, it should work: I’m pretty confident that I’ve done it before. So it’s rather odd that it’s not working in your case!

I’m also a little surprised that you’re not seeing your ghost in the debug renderer. o_0 At the least I would expect to see a bounding-box, which should indicate whether it’s moving or not!

Hmm… But thinking about it, one possibility does occur to me: Perhaps your Bullet-object (whether ghost or otherwise) is being “put to sleep”/“deativated” by Bullet. What happens if you call “setDeactivationEnabled(False)” on your ghost? (With the ghost parented beneath the vehicle, that is.)

Something like this:

node = BulletGhostNode("ghosty!")
node.addShape(<insert shape here>)
node.setDeactivationEnabled(False)
self.vehicleNodePath.attachNewNode(node)

Based on a quick read of your post there, I think that that’s inaccurate.

Whoops, I’d neglected to world.attachGhost() my ghost node! Now I can see the cone in the debugger, and it’s actually moving around with the vehicle. Problem solved! Thanks for your help.

If I change the GhostNode to a RigidBodyNode, though, it doesn’t move with the vehicle. In fact, it doesn’t even position itself relative to the vehicle. (I’ve got my vehicle positioned at (0,0,720), and the cone appears at the origin when I look at it in the debugger; with a ghost node it does appear at the vehicle position.) That’s not an issue I need to solve right now, but from what you said it sounds like that’s not what’s supposed to happen?

1 Like

Ah, sometimes it’s a simple thing that causes such troubles, I fear!

Well, I’m glad that you found the problem, and that you got it working! :slight_smile:

Indeed. And I do think that my guess about Bullet “deactivating” the rigid-body might hold as a potential source for that behaviour.

Here’s how I set up a few collision objects on a BulletVehicle, setDeactivationEnabled(False) and all.

         def make_vehicle_1(vehicle_label, start_pos, heading):
             # make first collision shape
             v_shape = BulletBoxShape(Vec3(1, 2.4, 0.5))
             transform_shape_space = TransformState.makePos(Point3(0, -0.65, 0.6))
             # attach universal vehicle node for automatic compound shape
             vehicle_node = render.attachNewNode(BulletRigidBodyNode(str(vehicle_label)))
             # second vehicle collision shape
             v_shape_2 = BulletBoxShape(Vec3(0.9, 1, 0.5))
             transform_shape_space_2 = TransformState.makePos(Point3(0, -0.4, 1))

             vehicle_node.node().setCcdMotionThreshold(0.000000007)
             vehicle_node.node().setCcdSweptSphereRadius(0.30)

             vehicle_node.node().addShape(v_shape, transform_shape_space)
             vehicle_node.node().addShape(v_shape_2, transform_shape_space_2)

             vehicle_node.setPos(start_pos)
             vehicle_node.setH(heading)
             vehicle_node.node().setMass(1000.0)  # mass in kilograms
             vehicle_node.node().setFriction(10.0)
             # vehicle_node.node().setLinearFactor(3)
             world.attachRigidBody(vehicle_node.node())
             vehicle_node.node().setDeactivationEnabled(False)

             # instantiate vehicle
             self.npc_vehicle_1 = BulletVehicle(world, vehicle_node.node())
             self.npc_vehicle_1.setCoordinateSystem(ZUp)
             print(self.npc_vehicle_1)
             world.attachVehicle(self.npc_vehicle_1)
             # pickup_truck_1 wheels begin
             wheel_model = loader.loadModel('models/wheel_1.gltf')
             wheel_model.reparentTo(render)
             wheel_1 = self.npc_vehicle_1.createWheel()
             wheel_1.setNode(wheel_model.node())
             wheel_1.setChassisConnectionPointCs(Point3(0.75, 1.3, 0.2))
             wheel_1.setFrontWheel(True)
             wheel_1.setWheelDirectionCs(Vec3(0, 0, -1))
             wheel_1.setWheelAxleCs(Vec3(1, 0, 0))
             wheel_1.setWheelRadius(0.5)
             wheel_1.setMaxSuspensionTravelCm(35.0)
             wheel_1.setSuspensionStiffness(70.0)
             wheel_1.setWheelsDampingRelaxation(2.0)
             wheel_1.setWheelsDampingCompression(4.0)
             wheel_1.setFrictionSlip(4)
             wheel_1.setRollInfluence(0.01)
             # wheel_2
             wheel_model = loader.loadModel('models/wheel_1.gltf')
             wheel_model.reparentTo(render)
             wheel_2 = self.npc_vehicle_1.createWheel()
             wheel_2.setNode(wheel_model.node())
             wheel_2.setChassisConnectionPointCs(Point3(-0.75, 1.3, 0.2))
             wheel_2.setFrontWheel(True)
             wheel_2.setWheelDirectionCs(Vec3(0, 0, -1))
             wheel_2.setWheelAxleCs(Vec3(1, 0, 0))
             wheel_2.setWheelRadius(0.5)
             wheel_2.setMaxSuspensionTravelCm(35.0)
             wheel_2.setSuspensionStiffness(70.0)
             wheel_2.setWheelsDampingRelaxation(2.0)
             wheel_2.setWheelsDampingCompression(4.0)
             wheel_2.setFrictionSlip(4)
             wheel_2.setRollInfluence(0.01)
             # steering, engine control, and braking handled in Task section
             # wheel_3
             wheel_model = loader.loadModel('models/wheel_1.gltf')
             wheel_model.reparentTo(render)
             wheel_3 = self.npc_vehicle_1.createWheel()
             wheel_3.setNode(wheel_model.node())
             wheel_3.setChassisConnectionPointCs(Point3(0.75, -2, 0.2))
             wheel_3.setFrontWheel(False)
             wheel_3.setWheelDirectionCs(Vec3(0, 0, -1))
             wheel_3.setWheelAxleCs(Vec3(1, 0, 0))
             wheel_3.setWheelRadius(0.5)
             wheel_3.setMaxSuspensionTravelCm(35.0)
             wheel_3.setSuspensionStiffness(70.0)
             wheel_3.setWheelsDampingRelaxation(2.0)
             wheel_3.setWheelsDampingCompression(4.0)
             wheel_3.setFrictionSlip(4)
             wheel_3.setRollInfluence(0.01)
             # steering, engine control, and braking handled in Task section
             # wheel_4
             wheel_model = loader.loadModel('models/wheel_1.gltf')
             wheel_model.reparentTo(render)
             wheel_4 = self.npc_vehicle_1.createWheel()
             wheel_4.setNode(wheel_model.node())
             wheel_4.setChassisConnectionPointCs(Point3(-0.75, -2, 0.2))
             wheel_4.setFrontWheel(False)
             wheel_4.setWheelDirectionCs(Vec3(0, 0, -1))
             wheel_4.setWheelAxleCs(Vec3(1, 0, 0))
             wheel_4.setWheelRadius(0.5)
             wheel_4.setMaxSuspensionTravelCm(35.0)
             wheel_4.setSuspensionStiffness(70.0)
             wheel_4.setWheelsDampingRelaxation(2.0)
             wheel_4.setWheelsDampingCompression(4.0)
             wheel_4.setFrictionSlip(4)
             wheel_4.setRollInfluence(0.01)
             # steering, engine control, and braking handled in Task section
             # vehicle_node geometry
             truck_1 = loader.loadModel('models/pickup_2.gltf')
             truck_1.setH(180)
             truck_1.setScale(0.6)
             truck_1.reparentTo(vehicle_node)
             truck_1_windows = truck_1.find("**/windows")
             truck_1_windows.setTransparency(TransparencyAttrib.M_multisample)
         
         make_vehicle_1('npc_vehicle_1', (-6, 16.9847, 0.663382), 180)
1 Like

Thanks, my vehicle is actually not a BulletVehicle though. It looks like your shapes are otherwise all in one RigidBody, which is what I’ve ended up doing to make shapes move together and works perfectly well for my purposes. If I don’t put them in the same RigidBody, they don’t move together even if I parent them together in Panda.

Hmm, setting the deactivation to False didn’t help actually.

That’s very strange indeed!

Perhaps there’s some other setting or restriction that I’m not aware of. And indeed, thinking about it again, I think that I largely worked with kinematic objects, not standard rigid bodies, when I worked with this.