mujoco-react 9.5.0 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -62
- package/dist/{chunk-6MOK6ZWB.js → chunk-QTCAVQS6.js} +29 -29
- package/dist/chunk-QTCAVQS6.js.map +1 -0
- package/dist/index.d.ts +27 -8
- package/dist/index.js +146 -8
- package/dist/index.js.map +1 -1
- package/dist/spark.d.ts +1 -1
- package/dist/spark.js +1 -1
- package/dist/{types-BDB9QT6Z.d.ts → types-BaSMqJHT.d.ts} +88 -34
- package/dist/vite.js +21 -21
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
- package/src/core/SceneLoader.ts +1 -1
- package/src/core/createController.tsx +2 -2
- package/src/hooks/useIkController.ts +8 -1
- package/src/hooks/useJointState.ts +18 -7
- package/src/hooks/useKeyboardIkTarget.ts +170 -0
- package/src/index.ts +21 -13
- package/src/rendering/Reflector.ts +7 -5
- package/src/types.ts +129 -56
- package/src/vite.ts +22 -22
- package/dist/chunk-6MOK6ZWB.js.map +0 -1
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useFrame } from '@react-three/fiber';
|
|
7
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
8
|
+
import * as THREE from 'three';
|
|
9
|
+
import type { KeyboardIkTargetAction, KeyboardIkTargetBinding, KeyboardIkTargetConfig } from '../types';
|
|
10
|
+
|
|
11
|
+
const DEFAULT_TRANSLATE_SPEED = 0.25;
|
|
12
|
+
const DEFAULT_ROTATE_SPEED = 1.0;
|
|
13
|
+
|
|
14
|
+
const _translation = new THREE.Vector3();
|
|
15
|
+
const _axis = new THREE.Vector3();
|
|
16
|
+
const _quat = new THREE.Quaternion();
|
|
17
|
+
|
|
18
|
+
function actionSign(action: KeyboardIkTargetAction): 1 | -1 {
|
|
19
|
+
return action.endsWith('+') ? 1 : -1;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function actionBase(action: KeyboardIkTargetAction) {
|
|
23
|
+
return action.slice(0, -1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function applyRotation(
|
|
27
|
+
target: THREE.Object3D,
|
|
28
|
+
action: KeyboardIkTargetAction,
|
|
29
|
+
amount: number,
|
|
30
|
+
frame: 'world' | 'target',
|
|
31
|
+
) {
|
|
32
|
+
const base = actionBase(action);
|
|
33
|
+
if (base === 'pitch') {
|
|
34
|
+
_axis.set(1, 0, 0);
|
|
35
|
+
} else if (base === 'yaw') {
|
|
36
|
+
_axis.set(0, 1, 0);
|
|
37
|
+
} else if (base === 'roll') {
|
|
38
|
+
_axis.set(0, 0, 1);
|
|
39
|
+
} else {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_quat.setFromAxisAngle(_axis, amount);
|
|
44
|
+
if (frame === 'target') {
|
|
45
|
+
target.quaternion.multiply(_quat);
|
|
46
|
+
} else {
|
|
47
|
+
target.quaternion.premultiply(_quat);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function addTranslation(action: KeyboardIkTargetAction, amount: number) {
|
|
52
|
+
switch (action) {
|
|
53
|
+
case 'x+':
|
|
54
|
+
_translation.x += amount;
|
|
55
|
+
break;
|
|
56
|
+
case 'x-':
|
|
57
|
+
_translation.x -= amount;
|
|
58
|
+
break;
|
|
59
|
+
case 'y+':
|
|
60
|
+
_translation.y += amount;
|
|
61
|
+
break;
|
|
62
|
+
case 'y-':
|
|
63
|
+
_translation.y -= amount;
|
|
64
|
+
break;
|
|
65
|
+
case 'z+':
|
|
66
|
+
_translation.z += amount;
|
|
67
|
+
break;
|
|
68
|
+
case 'z-':
|
|
69
|
+
_translation.z -= amount;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Moves an existing IK target from keyboard input.
|
|
76
|
+
*
|
|
77
|
+
* This hook is intentionally robot-agnostic: it only edits the target owned by
|
|
78
|
+
* `useIkController`, then lets the normal IK solver write robot controls.
|
|
79
|
+
*/
|
|
80
|
+
export function useKeyboardIkTarget(config: KeyboardIkTargetConfig | null) {
|
|
81
|
+
const pressedRef = useRef(new Set<string>());
|
|
82
|
+
const wasActiveRef = useRef(false);
|
|
83
|
+
const configRef = useRef(config);
|
|
84
|
+
configRef.current = config;
|
|
85
|
+
|
|
86
|
+
const boundCodes = useMemo(() => {
|
|
87
|
+
return new Set((config?.bindings ?? []).map((binding) => binding.code));
|
|
88
|
+
}, [config?.bindings]);
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
92
|
+
const current = configRef.current;
|
|
93
|
+
if (!current || current.enabled === false || !boundCodes.has(event.code)) return;
|
|
94
|
+
if (current.preventDefault !== false) event.preventDefault();
|
|
95
|
+
pressedRef.current.add(event.code);
|
|
96
|
+
};
|
|
97
|
+
const onKeyUp = (event: KeyboardEvent) => {
|
|
98
|
+
const current = configRef.current;
|
|
99
|
+
if (boundCodes.has(event.code) && current?.preventDefault !== false) {
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
}
|
|
102
|
+
pressedRef.current.delete(event.code);
|
|
103
|
+
};
|
|
104
|
+
const onBlur = () => {
|
|
105
|
+
pressedRef.current.clear();
|
|
106
|
+
wasActiveRef.current = false;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
window.addEventListener('keydown', onKeyDown);
|
|
110
|
+
window.addEventListener('keyup', onKeyUp);
|
|
111
|
+
window.addEventListener('blur', onBlur);
|
|
112
|
+
|
|
113
|
+
return () => {
|
|
114
|
+
window.removeEventListener('keydown', onKeyDown);
|
|
115
|
+
window.removeEventListener('keyup', onKeyUp);
|
|
116
|
+
window.removeEventListener('blur', onBlur);
|
|
117
|
+
};
|
|
118
|
+
}, [boundCodes]);
|
|
119
|
+
|
|
120
|
+
useFrame((_state, delta) => {
|
|
121
|
+
const current = configRef.current;
|
|
122
|
+
const controller = current?.controller;
|
|
123
|
+
if (!current || current.enabled === false || !controller) {
|
|
124
|
+
wasActiveRef.current = false;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const activeBindings: KeyboardIkTargetBinding[] = [];
|
|
129
|
+
for (const binding of current.bindings) {
|
|
130
|
+
if (pressedRef.current.has(binding.code)) activeBindings.push(binding);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (activeBindings.length === 0) {
|
|
134
|
+
wasActiveRef.current = false;
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!wasActiveRef.current) {
|
|
139
|
+
if (current.syncOnStart !== false) controller.syncTargetToSite();
|
|
140
|
+
if (current.autoEnableIk !== false && !controller.ikEnabledRef.current) {
|
|
141
|
+
controller.setIkEnabled(true);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
wasActiveRef.current = true;
|
|
145
|
+
|
|
146
|
+
const target = controller.ikTargetRef.current;
|
|
147
|
+
if (!target) return;
|
|
148
|
+
|
|
149
|
+
const frame = current.frame ?? 'world';
|
|
150
|
+
_translation.set(0, 0, 0);
|
|
151
|
+
|
|
152
|
+
for (const binding of activeBindings) {
|
|
153
|
+
const translateSpeed = binding.translateSpeed ?? current.translateSpeed ?? DEFAULT_TRANSLATE_SPEED;
|
|
154
|
+
const rotateSpeed = binding.rotateSpeed ?? current.rotateSpeed ?? DEFAULT_ROTATE_SPEED;
|
|
155
|
+
const amount = actionSign(binding.action) * delta;
|
|
156
|
+
const base = actionBase(binding.action);
|
|
157
|
+
|
|
158
|
+
if (base === 'x' || base === 'y' || base === 'z') {
|
|
159
|
+
addTranslation(binding.action, translateSpeed * delta);
|
|
160
|
+
} else {
|
|
161
|
+
applyRotation(target, binding.action, rotateSpeed * amount, frame);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (_translation.lengthSq() > 0) {
|
|
166
|
+
if (frame === 'target') _translation.applyQuaternion(target.quaternion);
|
|
167
|
+
target.position.add(_translation);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -91,6 +91,7 @@ export { useBodyState } from './hooks/useBodyState';
|
|
|
91
91
|
export { useCtrl } from './hooks/useCtrl';
|
|
92
92
|
export { useContacts, useContactEvents } from './hooks/useContacts';
|
|
93
93
|
export { useKeyboardTeleop } from './hooks/useKeyboardTeleop';
|
|
94
|
+
export { useKeyboardIkTarget } from './hooks/useKeyboardIkTarget';
|
|
94
95
|
export { usePolicy } from './hooks/usePolicy';
|
|
95
96
|
export { useObservation } from './hooks/useObservation';
|
|
96
97
|
export { useTrajectoryPlayer } from './hooks/useTrajectoryPlayer';
|
|
@@ -211,6 +212,9 @@ export type {
|
|
|
211
212
|
// Keyboard teleop
|
|
212
213
|
KeyBinding,
|
|
213
214
|
KeyboardTeleopConfig,
|
|
215
|
+
KeyboardIkTargetAction,
|
|
216
|
+
KeyboardIkTargetBinding,
|
|
217
|
+
KeyboardIkTargetConfig,
|
|
214
218
|
// Policy
|
|
215
219
|
PolicyConfig,
|
|
216
220
|
PolicyVector,
|
|
@@ -293,11 +297,15 @@ export type {
|
|
|
293
297
|
SensorHandle,
|
|
294
298
|
BodyStateResult,
|
|
295
299
|
JointStateResult,
|
|
300
|
+
JointStateKind,
|
|
301
|
+
JointStateOptions,
|
|
302
|
+
ScalarJointStateResult,
|
|
303
|
+
ArrayJointStateResult,
|
|
296
304
|
// Register (type-safe named resources)
|
|
297
305
|
Register,
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
306
|
+
RegisteredModelMap,
|
|
307
|
+
ModelResource,
|
|
308
|
+
Models,
|
|
301
309
|
Actuators,
|
|
302
310
|
Sensors,
|
|
303
311
|
Bodies,
|
|
@@ -309,16 +317,16 @@ export type {
|
|
|
309
317
|
} from './types';
|
|
310
318
|
|
|
311
319
|
export {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
320
|
+
registerModelResources,
|
|
321
|
+
ModelResources,
|
|
322
|
+
ModelActuators,
|
|
323
|
+
ModelSensors,
|
|
324
|
+
ModelBodies,
|
|
325
|
+
ModelJoints,
|
|
326
|
+
ModelSites,
|
|
327
|
+
ModelGeoms,
|
|
328
|
+
ModelKeyframes,
|
|
329
|
+
ModelCameras,
|
|
322
330
|
SplatEnvironmentReadinessStatus,
|
|
323
331
|
} from './types';
|
|
324
332
|
|
|
@@ -22,10 +22,13 @@ export interface ReflectorOptions {
|
|
|
22
22
|
interface ReflectorMesh extends THREE.Mesh {
|
|
23
23
|
type: string;
|
|
24
24
|
material: THREE.MeshPhysicalMaterial;
|
|
25
|
-
|
|
26
|
-
onBeforeRender: (renderer: any, scene: any, camera: any) => void;
|
|
25
|
+
onBeforeRender: (renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera) => void;
|
|
27
26
|
}
|
|
28
27
|
|
|
28
|
+
type CameraWithViewport = THREE.Camera & {
|
|
29
|
+
viewport?: THREE.Vector4;
|
|
30
|
+
};
|
|
31
|
+
|
|
29
32
|
/**
|
|
30
33
|
* Reflector
|
|
31
34
|
* Creates a reflective surface.
|
|
@@ -199,8 +202,7 @@ export class Reflector extends THREE.Mesh {
|
|
|
199
202
|
renderer.setRenderTarget(currentRenderTarget);
|
|
200
203
|
|
|
201
204
|
// Restore viewport
|
|
202
|
-
|
|
203
|
-
const viewport = (camera as any).viewport;
|
|
205
|
+
const viewport = (camera as CameraWithViewport).viewport;
|
|
204
206
|
if (viewport !== undefined) {
|
|
205
207
|
renderer.state.viewport(viewport);
|
|
206
208
|
}
|
|
@@ -222,4 +224,4 @@ export class Reflector extends THREE.Mesh {
|
|
|
222
224
|
mesh.material.dispose();
|
|
223
225
|
}
|
|
224
226
|
}
|
|
225
|
-
}
|
|
227
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -17,7 +17,7 @@ import * as THREE from 'three';
|
|
|
17
17
|
* ```ts
|
|
18
18
|
* declare module 'mujoco-react' {
|
|
19
19
|
* interface Register {
|
|
20
|
-
*
|
|
20
|
+
* models: {
|
|
21
21
|
* panda: {
|
|
22
22
|
* actuators: 'joint1' | 'joint2' | 'gripper';
|
|
23
23
|
* sensors: 'force_sensor' | 'torque_sensor';
|
|
@@ -35,45 +35,45 @@ import * as THREE from 'three';
|
|
|
35
35
|
*/
|
|
36
36
|
export interface Register {}
|
|
37
37
|
|
|
38
|
-
export type
|
|
38
|
+
export type RegisteredModelMap = Register extends { models: infer T extends Record<string, Record<string, string>> }
|
|
39
39
|
? T
|
|
40
40
|
: never;
|
|
41
|
-
export type
|
|
42
|
-
export type
|
|
43
|
-
[
|
|
41
|
+
export type Models = [RegisteredModelMap] extends [never] ? string : Extract<keyof RegisteredModelMap, string>;
|
|
42
|
+
export type ModelResource<TModel extends string, TKey extends string> =
|
|
43
|
+
[RegisteredModelMap] extends [never]
|
|
44
44
|
? string
|
|
45
|
-
:
|
|
46
|
-
? TKey extends keyof
|
|
47
|
-
?
|
|
45
|
+
: TModel extends keyof RegisteredModelMap
|
|
46
|
+
? TKey extends keyof RegisteredModelMap[TModel]
|
|
47
|
+
? RegisteredModelMap[TModel][TKey]
|
|
48
48
|
: string
|
|
49
49
|
: never;
|
|
50
|
-
export type
|
|
51
|
-
export type
|
|
52
|
-
export type
|
|
53
|
-
export type
|
|
54
|
-
export type
|
|
55
|
-
export type
|
|
56
|
-
export type
|
|
57
|
-
export type
|
|
50
|
+
export type ModelActuators<TModel extends string> = ModelResource<TModel, 'actuators'>;
|
|
51
|
+
export type ModelSensors<TModel extends string> = ModelResource<TModel, 'sensors'>;
|
|
52
|
+
export type ModelBodies<TModel extends string> = ModelResource<TModel, 'bodies'>;
|
|
53
|
+
export type ModelJoints<TModel extends string> = ModelResource<TModel, 'joints'>;
|
|
54
|
+
export type ModelSites<TModel extends string> = ModelResource<TModel, 'sites'>;
|
|
55
|
+
export type ModelGeoms<TModel extends string> = ModelResource<TModel, 'geoms'>;
|
|
56
|
+
export type ModelKeyframes<TModel extends string> = ModelResource<TModel, 'keyframes'>;
|
|
57
|
+
export type ModelCameras<TModel extends string> = ModelResource<TModel, 'cameras'>;
|
|
58
58
|
|
|
59
59
|
export type RegisterResourceKey = 'actuators' | 'sensors' | 'bodies' | 'joints' | 'sites' | 'geoms' | 'keyframes' | 'cameras';
|
|
60
|
-
export type
|
|
61
|
-
string extends
|
|
60
|
+
export type ModelResourceObject<TModel extends string, TKey extends RegisterResourceKey> =
|
|
61
|
+
string extends ModelResource<TModel, TKey>
|
|
62
62
|
? Record<string, string>
|
|
63
|
-
: { readonly [K in
|
|
64
|
-
export type
|
|
65
|
-
string extends
|
|
63
|
+
: { readonly [K in ModelResource<TModel, TKey>]: K };
|
|
64
|
+
export type ModelResourceCategory<TKey extends RegisterResourceKey> =
|
|
65
|
+
string extends Models
|
|
66
66
|
? Record<string, Record<string, string>>
|
|
67
|
-
: { readonly [
|
|
68
|
-
export type
|
|
69
|
-
string extends
|
|
67
|
+
: { readonly [TModel in Models]: ModelResourceObject<TModel, TKey> };
|
|
68
|
+
export type ModelResourceRegistry =
|
|
69
|
+
string extends Models
|
|
70
70
|
? Record<string, Record<RegisterResourceKey, Record<string, string>>>
|
|
71
|
-
: { readonly [
|
|
71
|
+
: { readonly [TModel in Models]: { readonly [TKey in RegisterResourceKey]: ModelResourceObject<TModel, TKey> } };
|
|
72
72
|
|
|
73
|
-
type
|
|
74
|
-
type
|
|
73
|
+
type RuntimeModelResources = Record<string, Record<RegisterResourceKey, Record<string, string>>>;
|
|
74
|
+
type RuntimeModelResourceRegistration = Readonly<Record<string, Readonly<Record<RegisterResourceKey, Readonly<Record<string, string>>>>>>;
|
|
75
75
|
|
|
76
|
-
const
|
|
76
|
+
const runtimeModelResources: RuntimeModelResources = {};
|
|
77
77
|
const REGISTER_RESOURCE_KEYS: RegisterResourceKey[] = ['actuators', 'sensors', 'bodies', 'joints', 'sites', 'geoms', 'keyframes', 'cameras'];
|
|
78
78
|
|
|
79
79
|
function createEmptyRuntimeResources(): Record<RegisterResourceKey, Record<string, string>> {
|
|
@@ -89,54 +89,54 @@ function createEmptyRuntimeResources(): Record<RegisterResourceKey, Record<strin
|
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
export function
|
|
93
|
-
for (const [
|
|
94
|
-
const existing =
|
|
92
|
+
export function registerModelResources(resources: RuntimeModelResourceRegistration): void {
|
|
93
|
+
for (const [model, modelResources] of Object.entries(resources)) {
|
|
94
|
+
const existing = runtimeModelResources[model] ?? createEmptyRuntimeResources();
|
|
95
95
|
for (const key of REGISTER_RESOURCE_KEYS) {
|
|
96
|
-
existing[key] = { ...existing[key], ...(
|
|
96
|
+
existing[key] = { ...existing[key], ...(modelResources[key] ?? {}) };
|
|
97
97
|
}
|
|
98
|
-
|
|
98
|
+
runtimeModelResources[model] = existing;
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
function createResourceCategory<TKey extends RegisterResourceKey>(key: TKey):
|
|
102
|
+
function createResourceCategory<TKey extends RegisterResourceKey>(key: TKey): ModelResourceCategory<TKey> {
|
|
103
103
|
return new Proxy({}, {
|
|
104
|
-
get(_target,
|
|
105
|
-
if (typeof
|
|
106
|
-
return
|
|
104
|
+
get(_target, model) {
|
|
105
|
+
if (typeof model !== 'string') return undefined;
|
|
106
|
+
return runtimeModelResources[model]?.[key] ?? {};
|
|
107
107
|
},
|
|
108
108
|
ownKeys() {
|
|
109
|
-
return Reflect.ownKeys(
|
|
109
|
+
return Reflect.ownKeys(runtimeModelResources);
|
|
110
110
|
},
|
|
111
|
-
getOwnPropertyDescriptor(_target,
|
|
112
|
-
if (typeof
|
|
111
|
+
getOwnPropertyDescriptor(_target, model) {
|
|
112
|
+
if (typeof model !== 'string' || !(model in runtimeModelResources)) return undefined;
|
|
113
113
|
return { enumerable: true, configurable: true };
|
|
114
114
|
},
|
|
115
|
-
}) as
|
|
115
|
+
}) as ModelResourceCategory<TKey>;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
export const
|
|
119
|
-
get(target,
|
|
120
|
-
if (typeof
|
|
121
|
-
return target[
|
|
118
|
+
export const ModelResources: ModelResourceRegistry = new Proxy(runtimeModelResources, {
|
|
119
|
+
get(target, model) {
|
|
120
|
+
if (typeof model !== 'string') return undefined;
|
|
121
|
+
return target[model] ?? createEmptyRuntimeResources();
|
|
122
122
|
},
|
|
123
123
|
ownKeys(target) {
|
|
124
124
|
return Reflect.ownKeys(target);
|
|
125
125
|
},
|
|
126
|
-
getOwnPropertyDescriptor(target,
|
|
127
|
-
if (typeof
|
|
126
|
+
getOwnPropertyDescriptor(target, model) {
|
|
127
|
+
if (typeof model !== 'string' || !(model in target)) return undefined;
|
|
128
128
|
return { enumerable: true, configurable: true };
|
|
129
129
|
},
|
|
130
|
-
}) as
|
|
130
|
+
}) as ModelResourceRegistry;
|
|
131
131
|
|
|
132
|
-
export const
|
|
133
|
-
export const
|
|
134
|
-
export const
|
|
135
|
-
export const
|
|
136
|
-
export const
|
|
137
|
-
export const
|
|
138
|
-
export const
|
|
139
|
-
export const
|
|
132
|
+
export const ModelActuators: ModelResourceCategory<'actuators'> = createResourceCategory('actuators');
|
|
133
|
+
export const ModelSensors: ModelResourceCategory<'sensors'> = createResourceCategory('sensors');
|
|
134
|
+
export const ModelBodies: ModelResourceCategory<'bodies'> = createResourceCategory('bodies');
|
|
135
|
+
export const ModelJoints: ModelResourceCategory<'joints'> = createResourceCategory('joints');
|
|
136
|
+
export const ModelSites: ModelResourceCategory<'sites'> = createResourceCategory('sites');
|
|
137
|
+
export const ModelGeoms: ModelResourceCategory<'geoms'> = createResourceCategory('geoms');
|
|
138
|
+
export const ModelKeyframes: ModelResourceCategory<'keyframes'> = createResourceCategory('keyframes');
|
|
139
|
+
export const ModelCameras: ModelResourceCategory<'cameras'> = createResourceCategory('cameras');
|
|
140
140
|
|
|
141
141
|
export type Actuators = Register extends { actuators: infer T extends string } ? T : string;
|
|
142
142
|
export type Sensors = Register extends { sensors: infer T extends string } ? T : string;
|
|
@@ -516,6 +516,14 @@ export interface IkConfig {
|
|
|
516
516
|
ikSolveFn?: IKSolveFn;
|
|
517
517
|
/** DLS damping. Default: 0.01. */
|
|
518
518
|
damping?: number;
|
|
519
|
+
/** Position error weight for the built-in DLS solver. Default: 1. */
|
|
520
|
+
posWeight?: number;
|
|
521
|
+
/** Orientation error weight for the built-in DLS solver. Default: 0.3. */
|
|
522
|
+
rotWeight?: number;
|
|
523
|
+
/** Solver convergence tolerance. Default: 1e-3. */
|
|
524
|
+
tolerance?: number;
|
|
525
|
+
/** Finite-difference step used by the built-in DLS solver. Default: 1e-6. */
|
|
526
|
+
epsilon?: number;
|
|
519
527
|
/** Max solver iterations. Default: 50. */
|
|
520
528
|
maxIterations?: number;
|
|
521
529
|
}
|
|
@@ -864,6 +872,48 @@ export interface IkGizmoDragInput {
|
|
|
864
872
|
quaternion: THREE.Quaternion;
|
|
865
873
|
}
|
|
866
874
|
|
|
875
|
+
export type KeyboardIkTargetAction =
|
|
876
|
+
| 'x+'
|
|
877
|
+
| 'x-'
|
|
878
|
+
| 'y+'
|
|
879
|
+
| 'y-'
|
|
880
|
+
| 'z+'
|
|
881
|
+
| 'z-'
|
|
882
|
+
| 'pitch+'
|
|
883
|
+
| 'pitch-'
|
|
884
|
+
| 'yaw+'
|
|
885
|
+
| 'yaw-'
|
|
886
|
+
| 'roll+'
|
|
887
|
+
| 'roll-';
|
|
888
|
+
|
|
889
|
+
export interface KeyboardIkTargetBinding {
|
|
890
|
+
/** KeyboardEvent.code, e.g. `KeyW`, `ArrowUp`, `Space`. */
|
|
891
|
+
code: string;
|
|
892
|
+
action: KeyboardIkTargetAction;
|
|
893
|
+
/** Override translation speed in meters/second for this binding. */
|
|
894
|
+
translateSpeed?: number;
|
|
895
|
+
/** Override rotation speed in radians/second for this binding. */
|
|
896
|
+
rotateSpeed?: number;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
export interface KeyboardIkTargetConfig {
|
|
900
|
+
controller: IkContextValue | null;
|
|
901
|
+
bindings: KeyboardIkTargetBinding[];
|
|
902
|
+
enabled?: boolean;
|
|
903
|
+
/** Default translation speed in meters/second. Default: 0.25. */
|
|
904
|
+
translateSpeed?: number;
|
|
905
|
+
/** Default rotation speed in radians/second. Default: 1.0. */
|
|
906
|
+
rotateSpeed?: number;
|
|
907
|
+
/** Apply translation and rotation axes in world or current target space. Default: `world`. */
|
|
908
|
+
frame?: 'world' | 'target';
|
|
909
|
+
/** Enable IK while keys are active. Default: true. */
|
|
910
|
+
autoEnableIk?: boolean;
|
|
911
|
+
/** Sync target to current site when keyboard control starts. Default: true. */
|
|
912
|
+
syncOnStart?: boolean;
|
|
913
|
+
/** Prevent browser default behavior for bound keys. Default: true. */
|
|
914
|
+
preventDefault?: boolean;
|
|
915
|
+
}
|
|
916
|
+
|
|
867
917
|
export interface DragInteractionProps {
|
|
868
918
|
stiffness?: number;
|
|
869
919
|
showArrow?: boolean;
|
|
@@ -1482,7 +1532,30 @@ export interface BodyStateResult {
|
|
|
1482
1532
|
angularVelocity: React.RefObject<THREE.Vector3>;
|
|
1483
1533
|
}
|
|
1484
1534
|
|
|
1535
|
+
export type JointStateKind = 'auto' | 'scalar' | 'array';
|
|
1536
|
+
|
|
1537
|
+
export interface JointStateOptions {
|
|
1538
|
+
/**
|
|
1539
|
+
* Expected joint value shape.
|
|
1540
|
+
*
|
|
1541
|
+
* - `auto`: scalar joints return numbers, ball/free joints return Float64Array.
|
|
1542
|
+
* - `scalar`: return numeric refs for hinge/slide joints.
|
|
1543
|
+
* - `array`: return Float64Array refs for ball/free joints.
|
|
1544
|
+
*/
|
|
1545
|
+
kind?: JointStateKind;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1485
1548
|
export interface JointStateResult {
|
|
1486
1549
|
position: React.RefObject<number | Float64Array>;
|
|
1487
1550
|
velocity: React.RefObject<number | Float64Array>;
|
|
1488
1551
|
}
|
|
1552
|
+
|
|
1553
|
+
export interface ScalarJointStateResult {
|
|
1554
|
+
position: React.RefObject<number>;
|
|
1555
|
+
velocity: React.RefObject<number>;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
export interface ArrayJointStateResult {
|
|
1559
|
+
position: React.RefObject<Float64Array>;
|
|
1560
|
+
velocity: React.RefObject<Float64Array>;
|
|
1561
|
+
}
|
package/src/vite.ts
CHANGED
|
@@ -221,21 +221,21 @@ function renderRegister(
|
|
|
221
221
|
const fields = REGISTER_KEYS
|
|
222
222
|
.filter((key) => names[key].size > 0)
|
|
223
223
|
.map((key) => ` ${key}: ${renderUnion(names[key])};`);
|
|
224
|
-
const
|
|
225
|
-
.map((model) => ` ${quoteProperty(model.id)}: {\n${
|
|
224
|
+
const modelFields = models
|
|
225
|
+
.map((model) => ` ${quoteProperty(model.id)}: {\n${renderModelFields(model.names)}\n };`);
|
|
226
226
|
const namespaceAliases = renderNamespaceAliases(models);
|
|
227
|
-
const registerImport = declarationsOnly ? '' : `import {
|
|
228
|
-
const resources = declarationsOnly ? '' : `${renderResourceTypes(models)}\n\n${renderResourceConstants(models)}\n\
|
|
227
|
+
const registerImport = declarationsOnly ? '' : `import { registerModelResources } from '${moduleName}';\n`;
|
|
228
|
+
const resources = declarationsOnly ? '' : `${renderResourceTypes(models)}\n\n${renderResourceConstants(models)}\n\nregisterModelResources(generatedModelResources);\n\n`;
|
|
229
229
|
|
|
230
230
|
return `// Auto-generated by mujoco-react. Do not edit.
|
|
231
231
|
// Regenerate by running Vite with the mujocoReact() plugin or \`mujoco-react codegen\`.
|
|
232
232
|
|
|
233
|
-
${registerImport}import type {
|
|
233
|
+
${registerImport}import type { ModelResource } from '${moduleName}';
|
|
234
234
|
|
|
235
235
|
${resources}declare module '${moduleName}' {
|
|
236
236
|
interface Register {
|
|
237
|
-
|
|
238
|
-
${
|
|
237
|
+
models: {
|
|
238
|
+
${modelFields.join('\n')}
|
|
239
239
|
};
|
|
240
240
|
${fields.join('\n')}
|
|
241
241
|
}
|
|
@@ -249,7 +249,7 @@ function renderResourceTypes(models: readonly ModelEntry[]): string {
|
|
|
249
249
|
const modelTypes = models
|
|
250
250
|
.map((model) => ` readonly ${quoteProperty(model.id)}: {\n${renderResourceTypeFields(model.names)}\n };`)
|
|
251
251
|
.join('\n');
|
|
252
|
-
return `type
|
|
252
|
+
return `type GeneratedModelResources = {\n${modelTypes}\n};`;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
function renderResourceTypeFields(names: Record<RegisterKey, Set<string>>): string {
|
|
@@ -268,7 +268,7 @@ function renderResourceConstants(models: readonly ModelEntry[]): string {
|
|
|
268
268
|
const entries = models
|
|
269
269
|
.map((model) => ` ${quoteProperty(model.id)}: {\n${renderResourceConstantFields(model.names)}\n },`)
|
|
270
270
|
.join('\n');
|
|
271
|
-
return `const
|
|
271
|
+
return `const generatedModelResources: GeneratedModelResources = {\n${entries}\n};`;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
function renderResourceConstantFields(names: Record<RegisterKey, Set<string>>): string {
|
|
@@ -283,7 +283,7 @@ function renderResourceObject(values: Set<string>): string {
|
|
|
283
283
|
return entries.length > 0 ? `{\n${entries.join('\n')}\n }` : '{}';
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
-
function
|
|
286
|
+
function renderModelFields(names: Record<RegisterKey, Set<string>>): string {
|
|
287
287
|
return REGISTER_KEYS
|
|
288
288
|
.map((key) => ` ${key}: ${names[key].size > 0 ? renderUnion(names[key]) : 'never'};`)
|
|
289
289
|
.join('\n');
|
|
@@ -295,23 +295,23 @@ function renderUnion(values: Set<string>): string {
|
|
|
295
295
|
|
|
296
296
|
function renderNamespaceAliases(models: readonly ModelEntry[]): string {
|
|
297
297
|
const namespaces: Record<RegisterKey, string> = {
|
|
298
|
-
actuators: '
|
|
299
|
-
sensors: '
|
|
300
|
-
bodies: '
|
|
301
|
-
joints: '
|
|
302
|
-
sites: '
|
|
303
|
-
geoms: '
|
|
304
|
-
keyframes: '
|
|
305
|
-
cameras: '
|
|
298
|
+
actuators: 'ModelActuators',
|
|
299
|
+
sensors: 'ModelSensors',
|
|
300
|
+
bodies: 'ModelBodies',
|
|
301
|
+
joints: 'ModelJoints',
|
|
302
|
+
sites: 'ModelSites',
|
|
303
|
+
geoms: 'ModelGeoms',
|
|
304
|
+
keyframes: 'ModelKeyframes',
|
|
305
|
+
cameras: 'ModelCameras',
|
|
306
306
|
};
|
|
307
307
|
|
|
308
308
|
const blocks = REGISTER_KEYS
|
|
309
309
|
.map((key) => {
|
|
310
|
-
const
|
|
310
|
+
const modelAliases = models
|
|
311
311
|
.filter((model) => isIdentifier(model.id))
|
|
312
|
-
.map((model) => ` export type ${model.id} =
|
|
313
|
-
if (
|
|
314
|
-
return ` export namespace ${namespaces[key]} {\n${
|
|
312
|
+
.map((model) => ` export type ${model.id} = ModelResource<'${escapeTs(model.id)}', '${key}'>;`);
|
|
313
|
+
if (modelAliases.length === 0) return '';
|
|
314
|
+
return ` export namespace ${namespaces[key]} {\n${modelAliases.join('\n')}\n }`;
|
|
315
315
|
})
|
|
316
316
|
.filter(Boolean)
|
|
317
317
|
.join('\n\n');
|