Touch mechanisms and world tools
The final V1 table families turn ROCK's contact/ownership model into reusable physical mechanisms and bounded world tooling. They remain owner-scoped and game-thread-only; none of them exposes writable Havok or scene pointers.
Required extents and capabilities
| Family | Minimum table extent | Capability |
|---|---|---|
| Touch targets/state/yield | ROCK_PROVIDER_API_V1_TOUCH_GRAB_TARGETS_TABLE_BYTES | TouchGrabTargets |
| Exact equipped hand | ROCK_PROVIDER_API_V1_EQUIPPED_WEAPON_HAND_REQUEST_TABLE_BYTES | EquippedWeaponHandlingAuthority |
| World raycast | ROCK_PROVIDER_API_V1_WORLD_RAYCASTS_TABLE_BYTES | WorldRaycasts |
| Collider focus | ROCK_PROVIDER_API_V1_COLLIDER_VISUALIZATION_OVERRIDE_TABLE_BYTES | ColliderVisualizationOverride |
Use supportsTouchGrabTargetsV1, supportsEquippedWeaponHandRequestV1,
supportsWorldRaycastsV1, and supportsColliderVisualizationOverrideV1
before enabling each optional feature.
Touch-grab target kinds
| Kind | Use |
|---|---|
FixedAnchor | Attach a contacting hand to an exact body or bounded wildcard surface without changing that body's motion type. |
LimitedHinge | Expose bounded angular motion around pivotWorldGame and axisWorldGame. |
LimitedPrismatic | Expose bounded linear motion along axisWorldGame. |
minimumCoordinate, maximumCoordinate, and currentCoordinate describe the
mechanism's authored range and current position. All positions are game units;
hinge coordinates use the mechanism's documented angular convention in the
header/source that authored the target.
Target identity and matching
Every RockProviderTouchGrabTargetV1 has a caller-stable targetId and a
nonzero targetGeneration. Republishing the same ID/generation refreshes its
lease and values. Increment the generation whenever the backing object,
mechanism lifetime, or matching meaning changes.
An exact target supplies bodyId. A wildcard FixedAnchor supplies
MatchAnyBody, an intentional allowedLayerMask, and one or more motion flags:
MatchStaticMotion, MatchKeyframedMotion, or MatchDynamicMotion.
Exact bodies win before wildcard targets. One wildcard descriptor can own only one resolved body at a time. For two-hand climbing, publish separate right and left descriptors with distinct IDs so each hand can resolve a different wall.
Transactional scopes and rolling leases
setTouchGrabTargetsForScopeV1 replaces one scope atomically. A zero-count set
or clearTouchGrabTargetsForScopeV1 removes it. Refresh a short lease inside
the owner frame callback only while PhysicsWriteAllowed and your feature's
own activation condition are true.
std::array<RockProviderTouchGrabTargetV1, 2> targets{};
targets[0].targetId = rightTargetId;
targets[0].targetGeneration = generation;
targets[0].kind = RockProviderTouchGrabKindV1::FixedAnchor;
targets[0].flags =
static_cast<std::uint32_t>(RockProviderTouchGrabTargetFlagV1::AllowRightHand) |
static_cast<std::uint32_t>(RockProviderTouchGrabTargetFlagV1::MatchAnyBody) |
static_cast<std::uint32_t>(RockProviderTouchGrabTargetFlagV1::MatchStaticMotion);
targets[1] = targets[0];
targets[1].targetId = leftTargetId;
targets[1].flags =
static_cast<std::uint32_t>(RockProviderTouchGrabTargetFlagV1::AllowLeftHand) |
static_cast<std::uint32_t>(RockProviderTouchGrabTargetFlagV1::MatchAnyBody) |
static_cast<std::uint32_t>(RockProviderTouchGrabTargetFlagV1::MatchStaticMotion);
for (auto& target : targets) {
target.allowedLayerMask = climbableLayerMask;
target.leaseFrames = 2;
target.worldGeneration = frame.worldGeneration;
target.skeletonGeneration = frame.skeletonGeneration;
target.providerGeneration = frame.providerGeneration;
}
const auto published = RockProviderApi::inst->setTouchGrabTargetsForScopeV1(
ownerToken, scopeToken, targets.data(),
static_cast<std::uint32_t>(targets.size()));
State and safe yield
copyTouchGrabStatesForScopeV1 reports Inactive, Armed, Held, Latched,
Yielded, or Invalidated, plus the resolved body/reference, active hand mask,
contact point/normal, mechanism coordinate/velocity, release reason,
generations, and a transition sequence.
For a FixedAnchor target, surfaceGripMode has the same three-state contract
as the aggregate hand snapshot:
CollisionAnchor— the Havok shell contact is authoritative;MeshAnchor—contactPointGameis ROCK's visible-mesh anchor;CollisionFallback— mesh acquisition was enabled but ROCK safely retained the Havok shell contact.
Check ContactPointValid before reading contactPointGame. The additional
MeshSurfaceAnchor, MeshFingerPose, and MeshCollisionFallback state flags
identify mesh authority, an actually active final mesh finger pose, and fallback
respectively. A successful mesh anchor does not require a successful finger
pose: consumers must not infer MeshFingerPose from MeshSurfaceAnchor.
The target descriptor still answers which target and body ROCK grabbed;
surfaceGripMode and contactPointGame answer where and how that fixed surface
is anchored. The owner-scoped state and getHandInteractionStateV1 expose the
same mode, so a climbing or mechanism consumer can use either view without
reconstructing a render-mesh point from Havok geometry.
If another system needs to animate or script a held mechanism, call
requestTouchGrabYieldV1. It blocks new acquisition immediately, but release
is asynchronous. Wait until the copied state is Yielded before moving the
body. A yielded/invalidated descriptor remains non-acquirable until removed or
republished with a new target generation.
Exact equipped-weapon hand request
requestEquippedWeaponHandV1 applies only to the weapon already equipped. The
caller must own the current handling-authority lease; a left-hand request also
requires AmbidextrousHandoff. Supply current weapon form/generation and
world/skeleton/provider generations, call on the owner thread, then observe
getEquippedWeaponHandlingStateV1 until the effective hand settles.
The request does not equip an inventory item and does not create a second weapon attachment path.
Bounded world raycasts
queryWorldRaycastV1 is callback-only. ROCK normalizes the direction, caps the
distance at 8192 game units, applies its validated far-world filter, and limits
each owner to the extended-limit budget (currently eight queries per frame).
RockProviderWorldRaycastRequestV1 request{};
request.startGame = start;
request.directionGame = direction;
request.maxDistanceGame = 2048.0f;
request.worldGeneration = frame.worldGeneration;
request.skeletonGeneration = frame.skeletonGeneration;
request.providerGeneration = frame.providerGeneration;
RockProviderWorldRaycastResultV1 hit{};
const auto result = RockProviderApi::inst->queryWorldRaycastV1(
ownerToken, &request, &hit);
A miss is still Ok: hit == 0, hitFraction == 1, and hitPointGame is the
requested endpoint. NormalValid is set only when hitNormalGame is usable.
Exact collider visualization focus
setColliderVisualizationOverrideV1 temporarily replaces the complete
config-driven collider overlay with one exact body from the current equipped
weapon generation. The body may come from the complete evidence catalog, not
only the compact eight-ID frame array. Refresh its short lease while focused;
clear or let it expire to restore the unchanged normal overlay.
The optional partKind is a label. bodyId plus weaponGenerationKey is the
authoritative selection identity.