Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
Backend-neutral physics runtime components for OpenUSD-based applications.

> **Status: Phase 1 core extraction.** The repository builds and installs
> `physicsCore` and `physicsJolt`; neutral rigid-transform values and typed
> handles are implemented, but descriptors, rigid-body runtime behavior, an
> OpenUSD bridge, and a solver backend are not. The
> `physicsCore` and `physicsJolt`; neutral rigid-transform values, typed
> handles, and validated box/body/fixed-constraint descriptors are
> implemented, but rigid-body runtime behavior, an OpenUSD bridge, and a
> solver backend are not. The
> [capability matrix](docs/reference/CAPABILITY_MATRIX.md) is the only page
> that states what exists, and the [current roadmap](docs/roadmap/current.md)
> states what comes next.
Expand Down Expand Up @@ -55,7 +56,7 @@ their backend.

| Component | Kind | Role | State |
| --- | --- | --- | --- |
| `physicsCore` | plain C++ library | Handles, descriptors, world lifecycle, state, and optional query contracts; no OpenUSD and no backend SDK | neutral values and handles present; remaining runtime API planned |
| `physicsCore` | plain C++ library | Handles, descriptors, world lifecycle, state, and optional query contracts; no OpenUSD and no backend SDK | neutral values, handles, and validated descriptors present; remaining runtime API planned |
| `physicsJolt` | plain C++ library | First rigid-body backend implementing `physicsCore` | package scaffold present; backend unavailable |
| `physicsUsd` | OpenUSD-facing C++ library | Translate standard `UsdPhysics` declarations to runtime descriptors and maintain transient prim/resource mappings | planned |
| `secondaryMotion` | plain C++ library | Solver-neutral spring-chain and collider contracts | deferred until the rigid-body boundary is extracted |
Expand Down
6 changes: 3 additions & 3 deletions docs/architecture/PACKAGE_CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

> **Status: accepted, revised 2026-09-22.** `physicsCore` and `physicsJolt`
> install and pass a clean-prefix consumer test. `physicsCore` now includes
> neutral values and typed handles; descriptors and world behavior remain
> unimplemented.
> neutral values, typed handles, and validated descriptors; world behavior
> remains unimplemented.

## 1. Purpose

Expand All @@ -15,7 +15,7 @@ not need this repository's source tree.

| Package | Imported target | Provides | Planned phase |
| --- | --- | --- | --- |
| `physicsCore` | `physicsCore::physicsCore` | Version surface plus neutral vector, quaternion, transform, and typed handles; descriptors, world, and query contracts follow in Phase 1 | values and handles present; runtime Phase 1 |
| `physicsCore` | `physicsCore::physicsCore` | Version surface plus neutral vector, quaternion, transform, typed handles, validated box/body/fixed-constraint descriptors, and semantic collision filters; world and query contracts follow in Phase 1 | values, handles, and descriptors present; runtime Phase 1 |
| `physicsJolt` | `physicsJolt::physicsJolt` | Phase 0 explicit unavailable status; Jolt-backed world construction follows in Phase 2 | scaffold present; backend Phase 2 |
| `physicsUsd` | `physicsUsd::physicsUsd` | USD translation, mappings, and synchronization records | 4 |
| `secondaryMotion` | `secondaryMotion::secondaryMotion` | generic secondary-motion contracts | 6, if admitted |
Expand Down
7 changes: 4 additions & 3 deletions docs/architecture/WORKSPACE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ that contradicts it updates this document first, in a focused change.

| Identity | Kind | Directory | Planned manifest | Role | Created in | Status |
| --- | --- | --- | --- | --- | --- | --- |
| `physicsCore` | plain static CMake library | `libs/physicsCore/` | `openstrata.library.yaml` | Solver-neutral value types, handles, descriptors, world lifecycle, state, validation, and optional query contracts. No OpenUSD or backend SDK. | Phase 0 scaffold; Phase 1 runtime | values and handles implemented; remaining runtime planned |
| `physicsCore` | plain static CMake library | `libs/physicsCore/` | `openstrata.library.yaml` | Solver-neutral value types, handles, descriptors, world lifecycle, state, validation, and optional query contracts. No OpenUSD or backend SDK. | Phase 0 scaffold; Phase 1 runtime | values, handles, and validated descriptors implemented; remaining runtime planned |
| `physicsJolt` | plain static CMake library | `backends/physicsJolt/` | `openstrata.library.yaml` | Jolt implementation of `physicsCore`; owns Jolt initialization, filters, jobs, resources, stepping, and queries. | Phase 0 scaffold; Phase 2 backend | scaffolded; backend unavailable |
| `physicsUsd` | plain static CMake library | `libs/physicsUsd/` | `openstrata.library.yaml` | Standard `UsdPhysics` to core descriptors, transform conversion, transient scene/resource mapping, and synchronization records. | Phase 4 | reserved |

The Phase 0 targets established package and dependency boundaries.
`physicsCore` now has supported value and handle slices, while its package as
a whole remains partial. `physicsJolt` remains a partial unavailable scaffold.
`physicsCore` now has supported value, handle, and descriptor slices, while
its package as a whole remains partial. `physicsJolt` remains a partial
unavailable scaffold.

### 1.2 Later, only when the responsibility is real

Expand Down
20 changes: 15 additions & 5 deletions docs/design/RIGID_BODY_CONTRACT.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Rigid-body runtime contract

> **Status: proposed, revised 2026-09-22.** This document describes the
> intended extraction target. Neutral values and handles are implemented; see
> the [capability matrix](../reference/CAPABILITY_MATRIX.md) for the exact
> current boundary.
> intended extraction target. Neutral values, handles, and descriptors are
> implemented; see the [capability matrix](../reference/CAPABILITY_MATRIX.md)
> for the exact current boundary.

## 1. Purpose

Expand Down Expand Up @@ -73,6 +73,15 @@ when a phase requires them.
Collision filtering must be expressed semantically. A Jolt object-layer or
broad-phase-layer index is not a public collision category.

Phase 1 represents filtering with two 64-bit sets: `categories` states the
semantic categories to which a body belongs, and `collidesWith` states the
categories it accepts. A pair is admitted only when each body's categories
intersect the other body's `collidesWith` set. `categories` must be non-zero;
`collidesWith` may be zero for an intentionally non-colliding body. The
default is category bit zero colliding with every category. This represents
Stage Runner's static/moving policy, MMD's 16 groups and per-body masks, and
future bridge-assigned categories without exposing backend layer numbers.

### 5.3 Constraints

Phase 1 preserves fixed constraints. Later descriptors may cover revolute,
Expand Down Expand Up @@ -180,7 +189,8 @@ contract. Broad additions require a consumer scenario and tests.
- **RB-O2 (resolved for Phase 1):** minimal vector, rotation, and transform
values live in `physicsCore`; a separate package requires an independent
consumer and an architecture revision.
- **RB-O3:** collision category/mask vocabulary that serves Stage Runner and
MMD without exposing backend layers.
- **RB-O3 (resolved for Phase 1):** use the 64-bit `categories` and
`collidesWith` sets defined in §5.2. Backend layer assignment is private and
may cache distinct filter combinations without changing this contract.
- **RB-O4:** semantics and ordering of changed-body extraction across create,
sleep, wake, teleport, and destroy.
15 changes: 8 additions & 7 deletions docs/reference/CAPABILITY_MATRIX.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ capability on this page.

> **Tree status (2026-09-22): Phase 1 core extraction.** Buildable and
> installable `physicsCore` and `physicsJolt` package boundaries exist.
> `physicsCore` provides neutral rigid-transform values and typed handles, but
> no descriptors or world contract exists yet. No solver backend or OpenUSD
> `physicsCore` provides neutral rigid-transform values, typed handles, and
> validated box/body/fixed-constraint descriptors with semantic collision
> filters, but no world contract exists yet. No solver backend or OpenUSD
> plugin exists.

## 1. Status vocabulary
Expand Down Expand Up @@ -38,20 +39,20 @@ capability on this page.

| Capability | Status | Evidence / note |
| --- | --- | --- |
| `physicsCore` package | partial | Installable `physicsCore::physicsCore` provides version, value, and handle surfaces; descriptors and runtime contract remain Phase 1 |
| `physicsCore` package | partial | Installable `physicsCore::physicsCore` provides version, value, handle, and descriptor surfaces; the runtime contract remains Phase 1 |
| Neutral vector, quaternion, and transform values | supported | `physicsCore.values_handles` and the installed-consumer test cover the public headers and identity defaults |
| Opaque shape/body/constraint handles | supported | [`physics_core_values_handles_test.cpp`](../../libs/physicsCore/tests/physics_core_values_handles_test.cpp) covers invalid zero, typed identity, comparison, hashing, and representation size |
| Shape descriptors | planned | Box first; sphere/capsule with Phase 4 |
| Static and dynamic bodies | planned | No implementation |
| Fixed constraints | planned | No implementation |
| Shape descriptors | supported | [`physics_core_descriptors_test.cpp`](../../libs/physicsCore/tests/physics_core_descriptors_test.cpp) covers the validated box descriptor; sphere/capsule remain Phase 4 |
| Static and dynamic body descriptors | supported | [`physics_core_descriptors_test.cpp`](../../libs/physicsCore/tests/physics_core_descriptors_test.cpp) covers shape identity, motion type, rigid transform, mass, and collision filter validation; no body lifecycle exists yet |
| Fixed-constraint descriptors | supported | [`physics_core_descriptors_test.cpp`](../../libs/physicsCore/tests/physics_core_descriptors_test.cpp) covers two distinct valid body handles; no constraint lifecycle exists yet |
| Force and velocity operations | planned | No implementation |
| Fixed-step world | planned | No implementation |
| Direct body state | planned | No implementation |
| Changed-body extraction | planned | No implementation |
| Segment query capability | planned | No implementation |
| Ground query capability | planned | No implementation |
| Ray or shape-cast capabilities | planned | Later consumer-driven addition |
| Neutral collision filtering | planned | `RB-O3` unresolved |
| Neutral collision filtering | supported | [`physics_core_descriptors_test.cpp`](../../libs/physicsCore/tests/physics_core_descriptors_test.cpp) covers 64-bit category/mask semantics and pair matching without backend layer identifiers; `RB-O3` is resolved |

## 4. Backends

Expand Down
2 changes: 1 addition & 1 deletion docs/roadmap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ status and ordering.

| Phase | Status | Current outcome |
| --- | --- | --- |
| 1 — extract `physicsCore` | in progress | Neutral values and typed handles are implemented; descriptors and validation are next. |
| 1 — extract `physicsCore` | in progress | Neutral values, typed handles, validated descriptors, and semantic collision filters are implemented; world lifecycle is next. |
| 2 — extract `physicsJolt` | not started | Waits for Phase 1 and an accepted Jolt dependency. |
| 3 — Stage Runner migration | not started | Waits for installable Phase 1–2 packages. |
| 4 — `physicsUsd` foundation | not started | Waits for the neutral rigid-body contract. |
Expand Down
32 changes: 18 additions & 14 deletions docs/roadmap/current.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Current roadmap — Phase 1 `physicsCore` extraction

> **Status: Phase 1 in progress, 2026-09-22.** Phase 0 closed with successful
> hosted graph, Linux, and Windows cells. Neutral values and typed handles are
> the first implemented extraction slice.
> hosted graph, Linux, and Windows cells. Neutral values, typed handles, and
> validated descriptors are the implemented extraction slices.

## 1. Outcome

Expand All @@ -11,8 +11,7 @@ preserves the Stage Runner behavior without depending on Stage Runner,
OpenUSD, or Jolt.

```text
values and handles
-> descriptors and validation
values, handles, and descriptors
-> world lifecycle and state
-> optional segment and ground queries
-> Phase 2 Jolt extraction
Expand All @@ -24,21 +23,26 @@ values and handles
installed `usd_physics/core/` include root.
- Typed 64-bit `ShapeHandle`, `BodyHandle`, and `ConstraintHandle` values
preserve invalid zero, comparison, and hashing behavior.
- `physicsCore.values_handles` and the clean-prefix installed consumer cover
this public slice.
- Box shape, static/dynamic body, and fixed-constraint descriptors preserve
the Stage Runner extraction baseline against neutral transforms and handles.
- Descriptor validation rejects unsupported enum values, non-finite transforms,
invalid dimensions, invalid masses, missing categories, and invalid handles
with `std::invalid_argument`.
- `CollisionFilter` uses 64-bit `categories` and `collidesWith` sets; no
backend layer identifier enters the public contract.
- `physicsCore.values_handles`, `physicsCore.descriptors`, and the clean-prefix
installed consumer cover these public slices.
- `RB-O1` has a proposed mixed error policy in the
[rigid-body contract](../design/RIGID_BODY_CONTRACT.md#9-errors-and-validation).
- `RB-O3` is resolved by the semantic category/mask contract in
[the body descriptor](../design/RIGID_BODY_CONTRACT.md#52-bodies).

## 3. Remaining Phase 1 work

- Extract box shape, static/dynamic body, and fixed-constraint descriptors
against the neutral values and handles.
- Implement and test descriptor, vector, transform, and fixed-step validation;
use those tests to accept or revise `RB-O1`.
- Implement and test fixed-step validation; use the world tests to accept or
revise the remaining `RB-O1` branches.
- Extract the single-owner world lifecycle, force/velocity commands, direct
state, fixed stepping, and changed-state draining contract.
- Settle `RB-O3` collision category/mask vocabulary before collision data is
frozen in the public body descriptor.
- Settle `RB-O4` changed-state ordering across create, sleep, wake, teleport,
and destroy before the world contract is accepted.
- Extract optional segment and ground query capabilities with ignored-body and
Expand All @@ -53,6 +57,6 @@ values and handles
- `physicsCore` public headers and link interfaces contain no OpenUSD, Jolt,
Stage Runner, MMD, or VRM dependency.
- A clean-prefix consumer includes and exercises the runtime contract.
- `RB-O1`, `RB-O3`, and the Phase 1 portion of `RB-O4` are accepted or narrowed
to explicitly deferred behavior.
- `RB-O1` and the Phase 1 portion of `RB-O4` are accepted or narrowed to
explicitly deferred behavior; the accepted `RB-O3` filter remains covered.
- The capability matrix and package contract describe only the tested surface.
6 changes: 5 additions & 1 deletion libs/physicsCore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ if(NOT DEFINED PHYSICSCORE_BUILD_TESTS)
${_physics_core_tests_default})
endif()

add_library(physicsCore STATIC src/version.cpp)
add_library(physicsCore STATIC
src/descriptors.cpp
src/version.cpp)
add_library(physicsCore::physicsCore ALIAS physicsCore)
set_target_properties(physicsCore PROPERTIES
EXPORT_NAME physicsCore
POSITION_INDEPENDENT_CODE ON)
target_compile_features(physicsCore PUBLIC cxx_std_17)
target_compile_options(physicsCore PUBLIC
"$<$<CXX_COMPILER_ID:MSVC>:/EHsc>")
target_compile_definitions(physicsCore PRIVATE
USDPHYSICS_CORE_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
USDPHYSICS_CORE_VERSION_MINOR=${PROJECT_VERSION_MINOR}
Expand Down
59 changes: 59 additions & 0 deletions libs/physicsCore/include/usd_physics/core/descriptors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#pragma once

#include "usd_physics/core/handles.h"
#include "usd_physics/core/value_types.h"

#include <cstdint>

namespace usd_physics::core {

enum class ShapeType {
box,
};

struct ShapeDescriptor {
ShapeType type{ShapeType::box};
Vector3 halfExtents{0.5, 0.5, 0.5};
};

enum class MotionType {
staticBody,
dynamicBody,
};

using CollisionCategories = std::uint64_t;

struct CollisionFilter {
CollisionCategories categories{1};
CollisionCategories collidesWith{~CollisionCategories{0}};
};

[[nodiscard]] constexpr bool
collisionFiltersAllowPair(CollisionFilter first, CollisionFilter second) noexcept {
return (first.categories & second.collidesWith) != 0 &&
(second.categories & first.collidesWith) != 0;
}

struct BodyDescriptor {
ShapeHandle shape;
MotionType motionType{MotionType::staticBody};
Transform initialTransform;
double mass{1.0};
CollisionFilter collisionFilter;
};

enum class ConstraintType {
fixed,
};

struct ConstraintDescriptor {
ConstraintType type{ConstraintType::fixed};
BodyHandle firstBody;
BodyHandle secondBody;
};

void validateShapeDescriptor(const ShapeDescriptor& descriptor);
void validateBodyDescriptor(const BodyDescriptor& descriptor);
void validateConstraintDescriptor(const ConstraintDescriptor& descriptor);

} // namespace usd_physics::core
79 changes: 79 additions & 0 deletions libs/physicsCore/src/descriptors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "usd_physics/core/descriptors.h"

#include <cmath>
#include <stdexcept>

namespace usd_physics::core {
namespace {

bool isFinite(Vector3 value) noexcept {
return std::isfinite(value.x) && std::isfinite(value.y) && std::isfinite(value.z);
}

bool isFinite(Quaternion value) noexcept {
return std::isfinite(value.w) && std::isfinite(value.x) &&
std::isfinite(value.y) && std::isfinite(value.z);
}

void validateTransform(Transform value) {
if (!isFinite(value.translation)) {
throw std::invalid_argument("body translation must be finite");
}
if (!isFinite(value.rotation)) {
throw std::invalid_argument("body rotation must be finite");
}
}

} // namespace

void validateShapeDescriptor(const ShapeDescriptor& descriptor) {
if (descriptor.type != ShapeType::box) {
throw std::invalid_argument("shape type is unsupported");
}
if (!isFinite(descriptor.halfExtents)) {
throw std::invalid_argument("box half extents must be finite");
}
if (descriptor.halfExtents.x <= 0.0 || descriptor.halfExtents.y <= 0.0 ||
descriptor.halfExtents.z <= 0.0) {
throw std::invalid_argument("box half extents must be positive");
}
}

void validateBodyDescriptor(const BodyDescriptor& descriptor) {
if (!descriptor.shape) {
throw std::invalid_argument("a physics body requires a valid shape handle");
}
validateTransform(descriptor.initialTransform);

switch (descriptor.motionType) {
case MotionType::staticBody:
case MotionType::dynamicBody:
break;
default:
throw std::invalid_argument("body motion type is unsupported");
}

if (!std::isfinite(descriptor.mass) ||
(descriptor.motionType == MotionType::dynamicBody && descriptor.mass <= 0.0) ||
(descriptor.motionType == MotionType::staticBody && descriptor.mass < 0.0)) {
throw std::invalid_argument(
"body mass must be finite, non-negative for static bodies, and positive for dynamic bodies");
}
if (descriptor.collisionFilter.categories == 0) {
throw std::invalid_argument("a physics body requires at least one collision category");
}
}

void validateConstraintDescriptor(const ConstraintDescriptor& descriptor) {
if (descriptor.type != ConstraintType::fixed) {
throw std::invalid_argument("constraint type is unsupported");
}
if (!descriptor.firstBody || !descriptor.secondBody) {
throw std::invalid_argument("a physics constraint requires two valid body handles");
}
if (descriptor.firstBody == descriptor.secondBody) {
throw std::invalid_argument("a physics constraint requires two distinct bodies");
}
}

} // namespace usd_physics::core
4 changes: 4 additions & 0 deletions libs/physicsCore/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ add_test(NAME physicsCore.scaffold COMMAND physics_core_scaffold_test)
add_executable(physics_core_values_handles_test physics_core_values_handles_test.cpp)
target_link_libraries(physics_core_values_handles_test PRIVATE physicsCore::physicsCore)
add_test(NAME physicsCore.values_handles COMMAND physics_core_values_handles_test)

add_executable(physics_core_descriptors_test physics_core_descriptors_test.cpp)
target_link_libraries(physics_core_descriptors_test PRIVATE physicsCore::physicsCore)
add_test(NAME physicsCore.descriptors COMMAND physics_core_descriptors_test)
Loading
Loading