Skip to content

Commit 06a6a8d

Browse files
Merge pull request #65 from andrewkaufman/akaufman/contact-coefficients-on-material
Add contact response attributes to NewtonMaterialAPI
2 parents d80bb29 + 47351a8 commit 06a6a8d

4 files changed

Lines changed: 156 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
# 0.3.0b1
1+
# 0.3.0
22

33
## Features
44

5+
- Add contact response attributes (`contactStiffness`, `contactDamping`, `contactFrictionStiffness`, `contactAdhesion`) to `NewtonMaterialAPI` for describing how a material behaves on contact.
56
- Added `NewtonSiteAPI`, which can be applied to Gprims to declare a non-colliding, massless reference shape used for sensing, attachment, or coordinate queries.
67
- `NewtonSiteAPI` and `NewtonCollisionAPI` are mutually exclusive; sites cannot be colliders
78
- Added `NewtonMassAPI`, which extends `PhysicsMassAPI`.

newton_usd_schemas/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2025 The Newton Developers
22
# SPDX-License-Identifier: Apache-2.0
33

4-
__version__ = "0.3.0b1"
4+
__version__ = "0.3.0"

newton_usd_schemas/generatedSchema.usda

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,74 @@ class NewtonMaterialAPI "NewtonMaterialAPI" (
656656
}
657657
}
658658
)
659+
660+
float newton:contactStiffness = -inf (
661+
doc = """Normal stiffness of the contact response.
662+
663+
Controls how rigid or compliant the material feels on contact. Higher values
664+
produce stiffer contacts at the cost of requiring smaller time steps.
665+
666+
If `newton:contactStiffness` is set to `-inf`, the engine's default should be used instead.
667+
668+
Range: [0, inf)
669+
Units: force/distance"""
670+
limits = {
671+
dictionary soft = {
672+
float minimum = 0
673+
}
674+
}
675+
)
676+
677+
float newton:contactDamping = -inf (
678+
doc = """Normal damping of the contact response.
679+
680+
Controls how much energy the material absorbs during contact. Higher values
681+
reduce bouncing.
682+
683+
If `newton:contactDamping` is set to `-inf`, the engine's default should be used instead.
684+
685+
Range: [0, inf)
686+
Units: force * time / distance"""
687+
limits = {
688+
dictionary soft = {
689+
float minimum = 0
690+
}
691+
}
692+
)
693+
694+
float newton:contactFrictionStiffness = -inf (
695+
doc = """Stiffness of the tangential friction response.
696+
697+
At low sliding speeds, friction force equals `contactFrictionStiffness * velocity`
698+
(viscous regime); at high speeds it is clamped to the Coulomb limit `mu * normal_force`.
699+
700+
If `newton:contactFrictionStiffness` is set to `-inf`, the engine's default should be used instead.
701+
702+
Range: [0, inf)
703+
Units: force * time / distance"""
704+
limits = {
705+
dictionary soft = {
706+
float minimum = 0
707+
}
708+
}
709+
)
710+
711+
float newton:contactAdhesion = -inf (
712+
doc = """Contact adhesion distance.
713+
714+
When two surfaces are within this distance, an attractive force pulls them together.
715+
A value of 0 or less disables adhesion.
716+
717+
If `newton:contactAdhesion` is set to `-inf`, the engine's default should be used instead.
718+
719+
Range: [0, inf)
720+
Units: distance"""
721+
limits = {
722+
dictionary soft = {
723+
float minimum = 0
724+
}
725+
}
726+
)
659727
}
660728

661729
class NewtonMimicAPI "NewtonMimicAPI" (

tests/test_material.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2025 The Newton Developers
22
# SPDX-License-Identifier: Apache-2.0
33

4+
import math
45
import unittest
56

67
from pxr import Plug, Usd, UsdPhysics, UsdShade
@@ -30,6 +31,10 @@ def test_api_application(self):
3031

3132
self.assertTrue(self.material.HasAttribute("physics:dynamicFriction")) # from PhysicsMaterialAPI
3233
self.assertTrue(self.material.HasAttribute("newton:torsionalFriction")) # from NewtonMaterialAPI
34+
self.assertTrue(self.material.HasAttribute("newton:contactStiffness")) # from NewtonMaterialAPI
35+
self.assertTrue(self.material.HasAttribute("newton:contactDamping")) # from NewtonMaterialAPI
36+
self.assertTrue(self.material.HasAttribute("newton:contactFrictionStiffness")) # from NewtonMaterialAPI
37+
self.assertTrue(self.material.HasAttribute("newton:contactAdhesion")) # from NewtonMaterialAPI
3338

3439
def test_api_limitations(self):
3540
prim: Usd.Prim = self.stage.DefinePrim("/NotMaterial", "Xform")
@@ -75,6 +80,86 @@ def test_rolling_friction(self):
7580
self.assertAlmostEqual(hard.GetMinimum(), 0.0)
7681
self.assertIsNone(hard.GetMaximum())
7782

83+
def test_contact_stiffness(self):
84+
self.assertFalse(self.material.HasAttribute("newton:contactStiffness"))
85+
86+
self.material.ApplyAPI("NewtonMaterialAPI")
87+
attr = self.material.GetAttribute("newton:contactStiffness")
88+
self.assertIsNotNone(attr)
89+
self.assertFalse(attr.HasAuthoredValue())
90+
self.assertEqual(attr.Get(), -math.inf)
91+
92+
success = attr.Set(5000.0)
93+
self.assertTrue(success)
94+
self.assertTrue(attr.HasAuthoredValue())
95+
self.assertAlmostEqual(attr.Get(), 5000.0)
96+
97+
if USD_HAS_LIMITS:
98+
soft = attr.GetSoftLimits()
99+
self.assertTrue(soft.IsValid())
100+
self.assertAlmostEqual(soft.GetMinimum(), 0.0)
101+
self.assertIsNone(soft.GetMaximum())
102+
103+
def test_contact_damping(self):
104+
self.assertFalse(self.material.HasAttribute("newton:contactDamping"))
105+
106+
self.material.ApplyAPI("NewtonMaterialAPI")
107+
attr = self.material.GetAttribute("newton:contactDamping")
108+
self.assertIsNotNone(attr)
109+
self.assertFalse(attr.HasAuthoredValue())
110+
self.assertEqual(attr.Get(), -math.inf)
111+
112+
success = attr.Set(200.0)
113+
self.assertTrue(success)
114+
self.assertTrue(attr.HasAuthoredValue())
115+
self.assertAlmostEqual(attr.Get(), 200.0)
116+
117+
if USD_HAS_LIMITS:
118+
soft = attr.GetSoftLimits()
119+
self.assertTrue(soft.IsValid())
120+
self.assertAlmostEqual(soft.GetMinimum(), 0.0)
121+
self.assertIsNone(soft.GetMaximum())
122+
123+
def test_contact_friction_stiffness(self):
124+
self.assertFalse(self.material.HasAttribute("newton:contactFrictionStiffness"))
125+
126+
self.material.ApplyAPI("NewtonMaterialAPI")
127+
attr = self.material.GetAttribute("newton:contactFrictionStiffness")
128+
self.assertIsNotNone(attr)
129+
self.assertFalse(attr.HasAuthoredValue())
130+
self.assertEqual(attr.Get(), -math.inf)
131+
132+
success = attr.Set(2000.0)
133+
self.assertTrue(success)
134+
self.assertTrue(attr.HasAuthoredValue())
135+
self.assertAlmostEqual(attr.Get(), 2000.0)
136+
137+
if USD_HAS_LIMITS:
138+
soft = attr.GetSoftLimits()
139+
self.assertTrue(soft.IsValid())
140+
self.assertAlmostEqual(soft.GetMinimum(), 0.0)
141+
self.assertIsNone(soft.GetMaximum())
142+
143+
def test_contact_adhesion(self):
144+
self.assertFalse(self.material.HasAttribute("newton:contactAdhesion"))
145+
146+
self.material.ApplyAPI("NewtonMaterialAPI")
147+
attr = self.material.GetAttribute("newton:contactAdhesion")
148+
self.assertIsNotNone(attr)
149+
self.assertFalse(attr.HasAuthoredValue())
150+
self.assertEqual(attr.Get(), -math.inf)
151+
152+
success = attr.Set(0.05)
153+
self.assertTrue(success)
154+
self.assertTrue(attr.HasAuthoredValue())
155+
self.assertAlmostEqual(attr.Get(), 0.05)
156+
157+
if USD_HAS_LIMITS:
158+
soft = attr.GetSoftLimits()
159+
self.assertTrue(soft.IsValid())
160+
self.assertAlmostEqual(soft.GetMinimum(), 0.0)
161+
self.assertIsNone(soft.GetMaximum())
162+
78163

79164
if __name__ == "__main__":
80165
unittest.main()

0 commit comments

Comments
 (0)