Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change physics material friction/bounce combine modes to match other engines #11715

Open
aaronfranke opened this issue Feb 7, 2025 · 7 comments
Milestone

Comments

@aaronfranke
Copy link
Member

aaronfranke commented Feb 7, 2025

Describe the project you are working on

I am working on glTF import and export in Godot, allowing people to make objects in Godot and export them to glTF for use in any engine, or make objects in any engine and import them into Godot.

Describe the problem or limitation you are having in your project

https://docs.godotengine.org/en/stable/classes/class_physicsmaterial.html

The problem is that Godot's PhysicsMaterial class has combine modes that do not behave like any other game engine.

  • For friction, Godot has a a rough boolean. If true for both, uses the highest rough one (maximum). If false for both, uses the lowest friction (minimum).

  • For bounciness/restitution, Godot has an absorbent boolean. If true, it subtracts the bounciness from the colliding object's bounciness. If false, they are added together.

This is different from everything else I've seen.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

The proposal is to add more options for friction and bounce combine modes.

First and foremost, both of these booleans should be replaced with enums. It is clear that there are more than 2 ways to do this, and Godot's choice seems arbitrary. We can do this without breaking compatibility by keeping the booleans around as wrappers for the underlying enums.

The important discussion here is what should be the allowed values of the enum. We may want to go with an extensive list, since this should be trivially easy to implement, especially in Jolt where we can provide any callback function.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

In a 4.x update, change the underlying data structure for the combine modes to be an enum. Expose this enum, and keep the existing booleans as wrappers for the existing behavior. If the default value of each combine mode is equivalent to its existing behavior, then compatibility is not broken (almost, the behavior of "rough" when one is rough but low is a bit unusual...). The enum could look something like this:

enum CombineMode {
	COMBINE_DEFAULT, // Defers to the other material, or average if both are default (unsure about this one, trying to mimic the behavior of the secret undefined state in KHR_physics_rigid_bodies)
	COMBINE_AVERAGE,
	COMBINE_MINIMUM, // Same as current friction rough=false
	COMBINE_MAXIMUM, // Same as current friction rough=true (when both are rough)
	COMBINE_MULTIPLY,
	COMBINE_GEOMETRIC_MEAN,
	COMBINE_ADDITIVE, // Same as current bounce absorbent=false
	COMBINE_SUBTRACT, // Same as current bounce absorbent=true
};

Or maybe like this, to have the first 4's precedence behavior the same as Unity/Rapier/Bevy (and possibly others, most don't specify):

enum CombineMode {
	COMBINE_AVERAGE,
	COMBINE_MINIMUM, // Same as current friction rough=false
	COMBINE_MULTIPLY,
	COMBINE_MAXIMUM, // Same as current friction rough=true (when both are rough)
	COMBINE_GEOMETRIC_MEAN,
	COMBINE_ADDITIVE, // Same as current bounce absorbent=false
	COMBINE_SUBTRACT, // Same as current bounce absorbent=true
};

We should get feedback from physics experts before going with this, especially with the ordering of the enum, because I really don't know what's best there, and different engines seem to disagree on what takes priority. Also, I'm really unsure about "default" being a thing.

However, as a general note, I don't see any downside to having the list of available options be quite extensive. They should all be very easy to implement, and having so many allows Godot's physics to behave like physics in any other engine, which is cool, and makes Godot very flexible.

In the next compatibility breakage, 5.0, the boolean wrappers could be removed. Also in 5.0, we could change the default values of these enums to something that makes more physical sense. But this is not required to implement this feature.

If this enhancement will not be used often, can it be worked around with a few lines of script?

No, physics is a core feature and working around its limitations is not really possible unless you modify the engine.

Is there a reason why this should be core and not an add-on in the asset library?

Physics is a core feature, and glTF import/export is a core feature.

@aaronfranke
Copy link
Member Author

At the same time, another discussion that we need to have about physics materials is static friction vs dynamic friction. Most physics systems have both, but Godot only has one friction value. Static friction is the friction required for an object to start moving, while dynamic friction is the friction when already moving. Realistic objects have a higher static friction than dynamic friction, as an emergent phenomena of the microscopic grooves in the materials settling into each other.

@erincatto
Copy link

In my opinion the dual friction values are legacy from PhysX. This split is not a useful feature and it adds complexity and overhead to the solver that all users pay for but likely none benefit from.

It is hard enough for designers to understand static friction as it is. I don't think asking them to tune two parameters is justified in this case. What is the scenario where a game would benefit from the lower dynamic friction?

The way friction and materials are simulated in game physics is very far from real materials. For example, friction can be anisotropic or temperature dependent. The way surface geometry is modelled is also highly simplified.

The friction mixing precedence rules seem arbitrary to me. Again, I think presenting these combine modes to designers in frequent tuning scenarios is not justified. In Box2D I've implemented an optional mixing callback that takes the two friction (restitution) values and a surface type integer and returns a mixed value.

typedef float b2FrictionCallback( float frictionA, int materialA, float frictionB, int materialB );
typedef float b2RestitutionCallback( float restitutionA, int materialA, float restitutionB, int materialB );

These intentionally provide no context to keep them light weight and thread safe. Most of the time the user will not care about the combine mode if good defaults are used. A good default for friction is sqrt(frictionA * frictionB) and max(resitutionA, restitutionB).

In general I recommend against trying to have cross platform data for physics in a format such as gltf. Different choices will work better in different engines. Different physics engines will have different features and the even similar features may work differently.

Suppose I carefully author a physics setup in Blender and it is using Bullet. The behavior, performance, and stability may be completely different when setup in PhysX, Jolt, or Havok. The feature set can also be different. So all that careful tuning done in Blender becomes meaningless when the setup is brought into the game engine. Physics engines are not interchangeable.

@aaronfranke
Copy link
Member Author

aaronfranke commented Feb 9, 2025

@erincatto There will be glTF physics. Even if the results are slightly different in some engines, at least most of the data is transferable in a mostly correct way, which means that fewer changes are required after import, which is highly useful. It's better to tweak after import than to re-do all of the physics properties of a 3D asset.

The point about the most useful defaults makes sense. Thinking about practical examples, a bouncy ball on a non-bouncy floor would be best modeled by the maximum operation, so I'd indeed agree with maximum as the default for the restitution combine mode. This does lead me to wonder, should there be 2 sets of enums with 2 sets of precedences, so that we can make a different default for each while making that default low priority so that other things override it? Or should they be one enum, with a "default" option that uses different option for each of friction and restitution, leading to good behavior by default, but allowing overriding it in the same order of precedence?

@erincatto
Copy link

What happens to the tweaks when a user reimports a gltf?

Material mixing is somewhat arbitrary and based on heuristics. Real material interactions are always unique to the surface pair.

If you want to fully generalize material property combinations, I would give each surface an identifier. Then I would have a lookup table based on surface pairs. This could be fairly fast at runtime with a hash table. This is a bit tedious to author because the user needs to fill out a material matrix. That matrix could having mixing rules instead of material values. The default mixing rules could be used if a matrix entry is missing.

@aaronfranke
Copy link
Member Author

What happens to the tweaks when a user reimports a gltf?

Whether you use an inherited scene, editable children, or an import setting, the tweaks should be kept.

Real material interactions are always unique to the surface pair. ... Then I would have a lookup table based on surface pairs.

Correct, these material properties are most accurately described as the interaction between two surfaces. However, for practical reasons, this won't be done. It's not really reasonable to expect users or content creators to make such complex tables, it doesn't work with asset imports bringing in new materials (even ignoring glTF, copying a Godot scene from one project to another can have this problem), and this is different from what every other game engine does.

@erincatto
Copy link

erincatto commented Feb 9, 2025

It may not be reasonable to do this in gltf, but this sort of thing already exists in Unity with their layer system. Some engines also have tables like this for VFX and SFX. For example, what sound should metal hitting felt play?

@aaronfranke
Copy link
Member Author

@erincatto This is a bit off-topic but I want to mention that there is an effort to standardize the acoustic properties of materials use them for calculating effects. A prototype made in Godot is available here: https://github.com/Straven35/godot-spatial-audio

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants