Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 76 additions & 1 deletion newton_usd_schemas/generatedSchema.usda
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,81 @@ class NewtonMaterialAPI "NewtonMaterialAPI" (
)
}

class NewtonCurvesDeformableMaterialAPI "NewtonCurvesDeformableMaterialAPI" (
apiSchemas = ["PhysicsMaterialAPI"]
doc = """Newton extension material parameters for AOUSD curve deformables.

Apply this API to the same `Material` prim that carries
`PhysicsCurvesDeformableMaterialAPI`. The standard AOUSD API owns curve thickness
and stretch/shear/bend/twist stiffness; this API only stores Newton-specific
controls that are not part of the AOUSD curve material schema.

Newton's curve-deformable runtime requires Young's modulus `E` and Poisson ratio
`nu` to be provided by the AOUSD curve material schema once those attributes are
standardized; `E` governs stretch and bend response, while `E` and `nu` determine
Comment thread
andrewkaufman marked this conversation as resolved.
Outdated
the shear modulus `G = E / (2 * (1 + nu))`, which governs shear and twist response.
Comment thread
andrewkaufman marked this conversation as resolved.
Outdated
Converting these material moduli into discrete stretch/shear/bend/twist stiffness
also depends on cross-sectional area, second and polar moments of area, shear correction,
segment rest length, and the solver's rod discretization."""
)
{
float newton:stretchDamping = -inf (
doc = """Newton stretch damping for curve deformables.

AOUSD curve materials do not define solver damping. If set to -inf, the importer
Comment thread
andrewkaufman marked this conversation as resolved.
Outdated
should use its simulator default.

Range: [0, inf)"""
limits = {
dictionary soft = {
float minimum = 0
}
}
)

float newton:shearDamping = -inf (
doc = """Newton shear damping for curve deformables.

AOUSD curve materials do not define solver damping. If set to -inf, the importer
should use its simulator default.

Range: [0, inf)"""
limits = {
dictionary soft = {
float minimum = 0
}
}
)

float newton:bendDamping = -inf (
doc = """Newton bend damping for curve deformables.

AOUSD curve materials do not define solver damping. If set to -inf, the importer
should use its simulator default.

Range: [0, inf)"""
limits = {
dictionary soft = {
float minimum = 0
}
}
)

float newton:twistDamping = -inf (
doc = """Newton twist damping for curve deformables.

AOUSD curve materials do not define solver damping. If set to -inf, the importer
should use its simulator default.

Range: [0, inf)"""
limits = {
dictionary soft = {
float minimum = 0
}
}
)
Comment on lines +900 to +954

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These four should carry the curves prefix, to align with the rename of the stiffnesses they pair with. The proposal prefixes properties whose meaning depends on the deformable type; damping qualifies as much as stiffness does.

This also affects the newton:* names in the class doc string and in tests/test_rods.py.

Suggested change
float newton:stretchDamping = -inf (
doc = """Newton stretch damping for curve deformables.
If `newton:stretchDamping` is set to `-inf`, the engine's default should be used instead.
Range: [0, inf)
Units: force * time"""
limits = {
dictionary soft = {
float minimum = 0
}
}
)
float newton:shearDamping = -inf (
doc = """Newton shear damping for curve deformables.
If `newton:shearDamping` is set to `-inf`, the engine's default should be used instead.
Range: [0, inf)
Units: force * time"""
limits = {
dictionary soft = {
float minimum = 0
}
}
)
float newton:bendDamping = -inf (
doc = """Newton bend damping for curve deformables.
If `newton:bendDamping` is set to `-inf`, the engine's default should be used instead.
Range: [0, inf)
Units: force * distance * distance * time"""
limits = {
dictionary soft = {
float minimum = 0
}
}
)
float newton:twistDamping = -inf (
doc = """Newton twist damping for curve deformables.
If `newton:twistDamping` is set to `-inf`, the engine's default should be used instead.
Range: [0, inf)
Units: force * distance * distance * time"""
limits = {
dictionary soft = {
float minimum = 0
}
}
)
float newton:curvesStretchDamping = -inf (
doc = """Newton stretch damping for curve deformables.
Damps the stretch mode whose stiffness is `physics:curvesStretchStiffness`.
If `newton:curvesStretchDamping` is set to `-inf`, the engine's default should be used instead.
Range: [0, inf)
Units: force * time"""
limits = {
dictionary soft = {
float minimum = 0
}
}
)
float newton:curvesShearDamping = -inf (
doc = """Newton transverse shear damping for curve deformables.
Damps the shear mode whose stiffness is `physics:curvesShearStiffness`.
If `newton:curvesShearDamping` is set to `-inf`, the engine's default should be used instead.
Range: [0, inf)
Units: force * time"""
limits = {
dictionary soft = {
float minimum = 0
}
}
)
float newton:curvesBendDamping = -inf (
doc = """Newton bend damping for curve deformables.
Damps the bend mode whose stiffness is `physics:curvesBendStiffness`.
If `newton:curvesBendDamping` is set to `-inf`, the engine's default should be used instead.
Range: [0, inf)
Units: force * distance * distance * time"""
limits = {
dictionary soft = {
float minimum = 0
}
}
)
float newton:curvesTwistDamping = -inf (
doc = """Newton twist damping for curve deformables.
Damps the twist mode whose stiffness is `physics:curvesTwistStiffness`.
If `newton:curvesTwistDamping` is set to `-inf`, the engine's default should be used instead.
Range: [0, inf)
Units: force * distance * distance * time"""
limits = {
dictionary soft = {
float minimum = 0
}
}
)

}

class NewtonMimicAPI "NewtonMimicAPI" (
doc = """`NewtonMimicAPI` applies on top of a `PhysicsJoint`, adding additional constraints to mimic the DOFs of another joint.

Expand Down Expand Up @@ -945,7 +1020,7 @@ class NewtonActuator "NewtonActuator" (

Currently only the first target is honored, and it must identify either a
`PhysicsRevoluteJoint` or `PhysicsPrismaticJoint`.

Multi-DOF `PhysicsJoint` prims are not supported, even when locked via `LimitAPI` to a single axis.
"""
)
Expand Down
14 changes: 14 additions & 0 deletions newton_usd_schemas/plugInfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,20 @@
"apiSchemaCanOnlyApplyTo": [
"NewtonActuator"
]
},
"NewtonPhysicsCurvesDeformableMaterialAPI": {
"schemaIdentifier": "NewtonCurvesDeformableMaterialAPI",
"alias": {
"UsdSchemaBase": "NewtonCurvesDeformableMaterialAPI"
},
"autoGenerated": false,
"bases": [
"UsdAPISchemaBase"
],
"schemaKind": "singleApplyAPI",
"apiSchemaCanOnlyApplyTo": [
"Material"
]
}
}
},
Expand Down
83 changes: 83 additions & 0 deletions tests/test_rods.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 The Newton Developers
# SPDX-License-Identifier: Apache-2.0

import math
import unittest

from pxr import Plug, Usd, UsdShade

import newton_usd_schemas # noqa: F401

USD_HAS_LIMITS = Usd.GetVersion() >= (0, 25, 11)


class TestNewtonCurvesDeformableMaterialAPI(unittest.TestCase):
def setUp(self):
self.stage: Usd.Stage = Usd.Stage.CreateInMemory()
self.material = UsdShade.Material.Define(self.stage, "/CableMaterial").GetPrim()

def test_api_registered(self):
plug_type = Plug.Registry().FindTypeByName("NewtonPhysicsCurvesDeformableMaterialAPI")
self.assertEqual(plug_type.typeName, "NewtonPhysicsCurvesDeformableMaterialAPI")
schema_type = Usd.SchemaRegistry().GetSchemaTypeName("NewtonPhysicsCurvesDeformableMaterialAPI")
self.assertEqual(schema_type, "NewtonCurvesDeformableMaterialAPI")

def test_api_application(self):
self.assertFalse(self.material.HasAPI("NewtonCurvesDeformableMaterialAPI"))
self.material.ApplyAPI("NewtonCurvesDeformableMaterialAPI")
self.assertTrue(self.material.HasAPI("PhysicsMaterialAPI"))
self.assertTrue(self.material.HasAPI("NewtonCurvesDeformableMaterialAPI"))

for name in (
"newton:stretchDamping",
"newton:shearDamping",
"newton:bendDamping",
"newton:twistDamping",
):
Comment on lines +31 to +36

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the curves prefix rename is taken, these names move with it. The rename touches 13 sites in this file: this tuple, plus the HasAttribute / GetAttribute pair in each of test_stretch_damping (L45, L48), test_shear_damping (L65, L68), test_bend_damping (L85, L88) and test_twist_damping (L105, L108).

Renaming the test methods themselves to test_curves_stretch_damping and so on would also keep them aligned with the attributes, matching how tests/test_material.py names each test after the attribute it covers.

Suggested change
for name in (
"newton:stretchDamping",
"newton:shearDamping",
"newton:bendDamping",
"newton:twistDamping",
):
for name in (
"newton:curvesStretchDamping",
"newton:curvesShearDamping",
"newton:curvesBendDamping",
"newton:curvesTwistDamping",
):

self.assertTrue(self.material.HasAttribute(name), name)

def test_api_limitations(self):
prim = self.stage.DefinePrim("/NotMaterial", "Xform")
self.assertFalse(prim.CanApplyAPI("NewtonCurvesDeformableMaterialAPI"))
self.assertTrue(self.material.CanApplyAPI("NewtonCurvesDeformableMaterialAPI"))

def test_material_parameter_defaults(self):
self.material.ApplyAPI("NewtonCurvesDeformableMaterialAPI")
self.assertEqual(self.material.GetAttribute("newton:stretchDamping").Get(), -math.inf)
self.assertEqual(self.material.GetAttribute("newton:shearDamping").Get(), -math.inf)
self.assertEqual(self.material.GetAttribute("newton:bendDamping").Get(), -math.inf)
self.assertEqual(self.material.GetAttribute("newton:twistDamping").Get(), -math.inf)

def test_material_parameter_roundtrip(self):
self.material.ApplyAPI("NewtonCurvesDeformableMaterialAPI")

values = {
"newton:stretchDamping": 0.01,
"newton:shearDamping": 0.02,
"newton:bendDamping": 0.03,
"newton:twistDamping": 0.04,
}
for name, value in values.items():
attr = self.material.GetAttribute(name)
self.assertTrue(attr.Set(value), name)
Comment thread
andrewkaufman marked this conversation as resolved.
Outdated
self.assertAlmostEqual(attr.Get(), value)

def test_limits(self):
if not USD_HAS_LIMITS:
self.skipTest("USD build does not expose schema limits")

self.material.ApplyAPI("NewtonCurvesDeformableMaterialAPI")
for name in (
"newton:stretchDamping",
"newton:shearDamping",
"newton:bendDamping",
"newton:twistDamping",
):
limits = self.material.GetAttribute(name).GetSoftLimits()
self.assertTrue(limits.IsValid(), name)
self.assertEqual(limits.GetMinimum(), 0.0)
self.assertIsNone(limits.GetMaximum())


if __name__ == "__main__":
unittest.main()
Loading