TriggerVolume that can be moved

A place to discuss everything related to Newton Dynamics.

Moderators: Sascha Willems, walaber

TriggerVolume that can be moved

Postby Esharc » Wed Nov 05, 2025 2:06 am

Good morning Julio,

We have a concept of a ghost object in our system. This is basically a "body" without a mass or visuals but can have collisions. These collisions should not affect the "real" bodies in the world, but we use it to know whether a certain body is in a specific area.

Currently to get this right, we basically have a shape associated with the ghost object, and then use your contact solver to determine if the shape in intersecting another shape (could be another ghost or a real body). This works well as it currently is, but the problem with it is that on each frame we iterate over each ghost object and check for intersections.

But I noticed you have a trigger volume which seems to be exactly what we need, and I started implementing it, but I noticed that it cannot be moved with SetMatrix. So I assume your intention was that the trigger volume would be placed somewhere and it would not be able to be moved.

I then thought I should try moving it with a kinematic joint, but because it is static (has no mass) it cannot be moved in that way.

Will it be possible to have the trigger volume moveable using SetMatrix and have its bounds updated so that the collision with it are calculated correctly?
Esharc
 
Posts: 141
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: TriggerVolume that can be moved

Postby Julio Jerez » Wed Nov 05, 2025 10:39 am

the engine solvers do not have the notion of integration of a physic body with no mass.
kinematic body are those kind of bodies. Triggers and player capsule are two examples.

they have all the kinematic properties of a dynamics body, but no the dynamics properties.
ex, they are no integrated or can be attacked to other bodies with joints either
yes, kinematic bodies seem are limited. but they has a purpose for special effects and app logic.

Kinematic bodies can be integrated by the application.
there are functions in the ndMatrix and ndQuaternion class to help with that kind of things.

Basically, you can make a trigger.
them, to move the trigger you get a new desired transform but you do not teleport it, instead you use functions
    ndVector ndQuaternion::CalcAverageOmega(const ndQuaternion &q1, ndFloat32 invdt) const;
to get an angular velocity and the equivalent in the ndVector to get a linear velocity.
you set that to the trigger and you call body::Integrate() manually,

maybe that helps.
Julio Jerez
Moderator
Moderator
 
Posts: 12469
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: TriggerVolume that can be moved

Postby Esharc » Thu Nov 06, 2025 7:52 am

That works somewhat.

I noticed that if I have a dynamic body that the trigger body is intersecting with, if the body is in sleeping state when the trigger body starts intersecting with it then the trigger does not work.

But if the body has sleeping disabled when the trigger body starts intersecting with it, then the trigger does work.

If you need an example to test with I can create one and send it to you. But I think it has to do with the CalculateContacts function in ndScene, it tests if both bodies are in equilibrium before continuing with the contact. Since the trigger volume is always in equilibrium, this part will never execute if the other body is also in equilibrium.
Esharc
 
Posts: 141
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: TriggerVolume that can be moved

Postby Julio Jerez » Thu Nov 06, 2025 12:26 pm

I see, you got it right for the most part, just a subtle clarification, a kinematic body is not a static body.
I think that to justify what seems like arbitrary and contrive system, some background might help 8)

Remember when I said, kinematic bodies have the property of a rigid body but not the properties of a dynamics rigid body. This definition is based on Newton's laws of motion.
From Wikipedia:
1-A body remains at rest, or in motion at a constant speed in a straight line, unless it is acted upon by a force.
2-At any instant of time, the net force on a body is equal to the body's acceleration multiplied by its mass or, equivalently, the rate at which the body's momentum is changing with time.
In classical mechanic, a kinematic body is on that obey Newton first law of physics.

I particularly do not like the second definition.
I prefer. A body acceleration is the product of its mass and the net force acting on it.
But the Wikipedia definition is fine.

form those laws, we can say that the state of a rigid body is determine by its velocity and acceleration. (this is different than what impulse based engine define as state with is velocity and position)

In the newton library. A kinematic body is one that abides by newton's first law of motion and optionally, the third, but not the second. A dynamics body is one that obey first, second and third laws of motion.

This mean that a kinematic body can have a velocity and still considered in equilibrium.
note that we use the term "equilibrium" instead of at "rest".

Normally if a dynamics body comes in contact with a kinematic body, the third law of motion takes place and the bodies collide, not problem there.

Now if a dynamics body is at rest, them it is also in equilibrium, so, how do we make it interact with other bodies that are also in equilibrium?

first: let me correct what I said before about kinematic body integration.
if a kinematic body has mass, they are integrated by the engine.
it is only static body that does not get integrated.
(static body defined as one with inverse mass zero)


so, how to make moving kinematic bodies interact with bodies at rest?
for that we are still appealing to all CPP objective programing.
Yes, this is still and legacy form 3.14 when we had base classes and special sub classes.

Anyway, to fix this all you need to do is this.

1-Sub class the trigger and make you own base trigger class.
2-Implement the three function: onEnter, OnTrigger and OnExit
3-give the trigger object a mass and inertia.
4 implement method ::SpecialUpdate(ndFloat32 timestep)

with that, a moving trigger will interact with bodies at rest. If the trigger has mass.
I modified the buoyancy demo to show how to do it, these are the modifications.

Code: Select all
ndArchimedesBuoyancyVolume::ndArchimedesBuoyancyVolume()
   :ndBodyTriggerVolume()
   ,m_plane(ndVector::m_zero)
   ,m_density(1.0f)
   ,m_hasPlane(0)
{
   // gives the trigger some mass to make it acc as a dynamics body
   SetMassMatrix(1.0f, 1.0f, 1.0f, 1.0f);
}

void ndArchimedesBuoyancyVolume::SpecialUpdate(ndFloat32 timestep)
{
   ndBodyTriggerVolume::SpecialUpdate(timestep);

   // here the app can do manipulate the body ex: setting velocity
   // in this examnple we do nothing
   //SetVelocity(ndVector(1.0f, 0.0f, 0.0f, 0.0f));
}


if you do that, it saves you the iteration logic, and the problem reduces to calculate the trigger's velocity and angular velocity, in the SpecialUpdate method. :shock:

To calculate velocity there are functions like velocity at a point to help with that:
basically, you associate the trigger with some reference body, then, the angular velocity will the angular velocity of the reference body, and the linear velocity is the VelocityAtaPoint(trigger center of mass in global space)

tell me if this helps?
Julio Jerez
Moderator
Moderator
 
Posts: 12469
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: TriggerVolume that can be moved

Postby Esharc » Fri Nov 07, 2025 5:43 am

Thanks, that works perfectly.

The problem I am having now is I do not have the contact position, normal, penetration and collision shape instance available in the trigger functions that gets called.

Will it be possible to get this data sent in the OnTrigger funtion?
Esharc
 
Posts: 141
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: TriggerVolume that can be moved

Postby Julio Jerez » Fri Nov 07, 2025 12:09 pm

Will it be possible to get this data sent in the OnTrigger funtion?

yes, that's partially possible, but it won report a full contact set. I explain.

Clarification:
In the Newton library, a trigger is a kinematic body used to sense intersections in the scene, not to physically collide with the intersected objects.
This behavior is controlled by the flag m_contactTestOnly on the body. You can see this in the constructor:

Code: Select all
ndBodyKinematicBase::ndBodyKinematicBase()
    : ndBodyKinematic()
{
    m_contactTestOnly = 1;
}


If you override that variable in your subclass, the shape will start colliding normally, but I assume that’s not what you want.
It sounds like you’re trying to detect contact points, even during deep trigger penetrations.

There are two ways to handle that:

1. Calculate contacts manually from OnTrigger
You can call the contact calculation directly from your OnTrigger callback.
All the information you need is available there, but this approach is expensive, it effectively does the contact work twice.
Also, contact calculations for deeply penetrating objects are more costly than for near-surface ones.

2. Use the existing contact data
this is a special case which is more efficient.
Contact calculation actually happens in two stages, let’s call them Part A and Part B:

Part A:
Computes the closest distance between two shapes.
This returns two surface points (one on each shape) and a scalar distance.
The scalar isn’t the magnitude of the vector between the points, it’s the distance the two bodies must travel along the contact normal to just touch each other. This is a relative distance.

Part B:
If the joint object does not have the “intersection-only” flag set, it proceeds to calculate the actual intersection geometry, this is the expensive part and that’s why m_contactTestOnly is important.

In most cases, the points calculated in Part A already represent meaningful contact points, which is probably all you need.
However, that data wasn’t originally save with the contact joint because, if two bodies were colliding, those points would already appear in the contact list.
It just didn’t occur to me that triggers could make good use of that same contact info.

Change needed for this option

To enable this behavior, here’s what I did:
Updated the OnTrigger callback to:
Code: Select all
void OnTrigger(const ndContact* const contact, ndFloat32)


From this, the callback can access both bodies through the contact.

Added the two surface points to the contact joint structure and modified the collision routines to copy them over.
This was the missing part, and it actually improves the system overall, so thanks for the idea!

With this change, you’ll get the contact point at the collision center.
If that’s not sufficient for your application, then you’ll need to fall back to option 1, where you explicitly call the contact solver in your trigger callback to perform the full contact calculation.

you will have to sync, because I made that change official.
Julio Jerez
Moderator
Moderator
 
Posts: 12469
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: TriggerVolume that can be moved

Postby Esharc » Mon Nov 10, 2025 9:29 am

Thank you, this worked for me.

I am now verifying that the compound intersections work with the trigger volume, but unfortunately they do not.

I see you have an explicit check in the CompoundToConvexContactsDiscrete() function

Code: Select all
 if (!m_intersectionTestOnly)


which skips over with the trigger volume and does not return any intersections.

Will we be able to have intersections with sub shapes (or static meshes, I see the same check is there as well)?
Esharc
 
Posts: 141
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: TriggerVolume that can be moved

Postby Julio Jerez » Mon Nov 10, 2025 11:14 am

Esharc wrote:I see you have an explicit check in the CompoundToConvexContactsDiscrete() function
Code: Select all
 if (!m_intersectionTestOnly)

which skips over with the trigger volume and does not return any intersections.

all contact routine has that flag. that flag is what prevent the contact from coping the contact points from the contact solver to the contact joint.

Esharc wrote:Will we be able to have intersections with sub shapes (or static meshes, I see the same check is there as well)?


If you comment out the solid shapes in the trigger demo, you’ll notice that it registers one collision with the ground, which is a static body. This only occurs when the trigger has mass.

I also added a compound shape and noticed an issue: it should return a contact count, but it currently doesn’t. I’m investigating the cause of this and will fix it.

The expected behavior is for the system to generate a contact point representing the shortest distance traveled to the surface of the trigger, identifying the sub-shape responsible for that distance.

If that’s not sufficient for your needs, you can directly get the shape of the rigid body and, if it is a compound, you can use the contact solver for a more detailed result.
Typically, you can perform a local ray cast, convex cast, or full collision test.
If you check the compound shape routine, you’ll see that this is exactly what it does.
Julio Jerez
Moderator
Moderator
 
Posts: 12469
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: TriggerVolume that can be moved

Postby Julio Jerez » Mon Nov 10, 2025 12:04 pm

alright, I modified the trigger demo.
-Added a simple compound shape
-Fixed the compound/convex function so that it reports the number of hits.
-also have to fix some util functions, like the one that compute the volume under a plane of a compound, (this is just a weighted sum of the volume and center of the individual sub shapes).
-I commented out the simple convex, so that you can see that the code captures the interaction with the static ground. I have not tested the case when the ground was a collision tree.
I expect that should just work, since the trigger is just a simple convex shape.

I will leave the demo like that for a few days so that you can check it out.
Julio Jerez
Moderator
Moderator
 
Posts: 12469
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: TriggerVolume that can be moved

Postby JernejL » Tue Nov 11, 2025 4:17 am

I use compound shape in 3.15 with contact callback to specify which parts of vehicles collide and which don't, that approach can be used as a type of trigger as well.
Help improving the Newton Game Dynamics WIKI
User avatar
JernejL
 
Posts: 1588
Joined: Mon Dec 06, 2004 2:00 pm
Location: Slovenia

Re: TriggerVolume that can be moved

Postby Julio Jerez » Tue Nov 11, 2025 11:58 am

Yes, that is correct.
But that's not the issue, the issue is functionality.
the trigger object in 4:00 provide that native functionality.
Making simpler and more natural to code it.

In 3:14, as Esharc said, you would have to iterate over the scene to get the information,
Now the broad and narrow phase provides the info.

The second problem is that the functionality was incomplete since triggered it did not sense compounds.
So that was part of the fixed.

Maybe that's a good motivation to try 4:xx.
The more people try it in different scenarios, the more polish it gets :D
Julio Jerez
Moderator
Moderator
 
Posts: 12469
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: TriggerVolume that can be moved

Postby Esharc » Thu Nov 13, 2025 4:15 am

Thanks Julio, that fix works for ConvexToCompoundContactsDiscrete.

But I need it for CompoundToConvexContactsDiscrete as well please.
Esharc
 
Posts: 141
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: TriggerVolume that can be moved

Postby Julio Jerez » Thu Nov 13, 2025 9:53 am

ah, yes.
Fixed
thanks.
Julio Jerez
Moderator
Moderator
 
Posts: 12469
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: TriggerVolume that can be moved

Postby Esharc » Fri Nov 14, 2025 1:44 am

Thank you Julio.

All of my test cases are passing now for the ghost bodies. I will let you know if there are any issues that I missed during the test case testing when I put it into use with the main system.
Esharc
 
Posts: 141
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa


Return to General Discussion

Who is online

Users browsing this forum: No registered users and 0 guests

cron