mujoco-react 0.1.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.
Files changed (42) hide show
  1. package/LICENSE +177 -0
  2. package/README.md +510 -0
  3. package/dist/index.d.ts +1080 -0
  4. package/dist/index.js +3518 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +64 -0
  7. package/src/components/ContactListener.tsx +26 -0
  8. package/src/components/ContactMarkers.tsx +81 -0
  9. package/src/components/Debug.tsx +227 -0
  10. package/src/components/DragInteraction.tsx +227 -0
  11. package/src/components/FlexRenderer.tsx +102 -0
  12. package/src/components/IkGizmo.tsx +146 -0
  13. package/src/components/SceneLights.tsx +131 -0
  14. package/src/components/SceneRenderer.tsx +104 -0
  15. package/src/components/SelectionHighlight.tsx +69 -0
  16. package/src/components/TendonRenderer.tsx +84 -0
  17. package/src/components/TrajectoryPlayer.tsx +44 -0
  18. package/src/core/GenericIK.ts +339 -0
  19. package/src/core/MujocoCanvas.tsx +72 -0
  20. package/src/core/MujocoProvider.tsx +78 -0
  21. package/src/core/MujocoSimProvider.tsx +1201 -0
  22. package/src/core/SceneLoader.ts +275 -0
  23. package/src/hooks/useActuators.ts +36 -0
  24. package/src/hooks/useBodyState.ts +56 -0
  25. package/src/hooks/useContacts.ts +125 -0
  26. package/src/hooks/useCtrl.ts +40 -0
  27. package/src/hooks/useCtrlNoise.ts +59 -0
  28. package/src/hooks/useGamepad.ts +77 -0
  29. package/src/hooks/useGravityCompensation.ts +22 -0
  30. package/src/hooks/useJointState.ts +64 -0
  31. package/src/hooks/useKeyboardTeleop.ts +97 -0
  32. package/src/hooks/usePolicy.ts +56 -0
  33. package/src/hooks/useSensor.ts +83 -0
  34. package/src/hooks/useSitePosition.ts +62 -0
  35. package/src/hooks/useTrajectoryPlayer.ts +105 -0
  36. package/src/hooks/useTrajectoryRecorder.ts +97 -0
  37. package/src/hooks/useVideoRecorder.ts +82 -0
  38. package/src/index.ts +108 -0
  39. package/src/rendering/CapsuleGeometry.ts +35 -0
  40. package/src/rendering/GeomBuilder.ts +140 -0
  41. package/src/rendering/Reflector.ts +225 -0
  42. package/src/types.ts +619 -0
package/src/index.ts ADDED
@@ -0,0 +1,108 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ // Core
7
+ export { MujocoProvider, useMujoco } from './core/MujocoProvider';
8
+ export { MujocoCanvas } from './core/MujocoCanvas';
9
+ export { MujocoSimProvider, useMujocoSim, useBeforePhysicsStep, useAfterPhysicsStep } from './core/MujocoSimProvider';
10
+ export {
11
+ loadScene,
12
+ getName,
13
+ findSiteByName,
14
+ findActuatorByName,
15
+ findKeyframeByName,
16
+ findBodyByName,
17
+ findJointByName,
18
+ findGeomByName,
19
+ findSensorByName,
20
+ findTendonByName,
21
+ } from './core/SceneLoader';
22
+
23
+ // Components
24
+ export { SceneRenderer } from './components/SceneRenderer';
25
+ export { IkGizmo } from './components/IkGizmo';
26
+ export { ContactMarkers } from './components/ContactMarkers';
27
+ export { DragInteraction } from './components/DragInteraction';
28
+ export { SceneLights } from './components/SceneLights';
29
+ export { Debug } from './components/Debug';
30
+ export { TendonRenderer } from './components/TendonRenderer';
31
+ export { FlexRenderer } from './components/FlexRenderer';
32
+ export { ContactListener } from './components/ContactListener';
33
+ export { TrajectoryPlayer } from './components/TrajectoryPlayer';
34
+ export { SelectionHighlight } from './components/SelectionHighlight';
35
+
36
+ // Hooks
37
+ export { useActuators } from './hooks/useActuators';
38
+ export { useSitePosition } from './hooks/useSitePosition';
39
+ export { useGravityCompensation } from './hooks/useGravityCompensation';
40
+ export { useSensor, useSensors } from './hooks/useSensor';
41
+ export { useJointState } from './hooks/useJointState';
42
+ export { useBodyState } from './hooks/useBodyState';
43
+ export { useCtrl } from './hooks/useCtrl';
44
+ export { useContacts, useContactEvents } from './hooks/useContacts';
45
+ export { useKeyboardTeleop } from './hooks/useKeyboardTeleop';
46
+ export { usePolicy } from './hooks/usePolicy';
47
+ export { useTrajectoryPlayer } from './hooks/useTrajectoryPlayer';
48
+ export { useTrajectoryRecorder } from './hooks/useTrajectoryRecorder';
49
+ export { useGamepad } from './hooks/useGamepad';
50
+ export { useVideoRecorder } from './hooks/useVideoRecorder';
51
+ export { useCtrlNoise } from './hooks/useCtrlNoise';
52
+
53
+ // Types
54
+ export type {
55
+ // Scene config
56
+ SceneConfig,
57
+ SceneObject,
58
+ XmlPatch,
59
+ SceneMarker,
60
+ PhysicsConfig,
61
+ // IK
62
+ IKSolveFn,
63
+ // Callbacks
64
+ PhysicsStepCallback,
65
+ // State management
66
+ StateSnapshot,
67
+ // Model introspection
68
+ BodyInfo,
69
+ JointInfo,
70
+ GeomInfo,
71
+ SiteInfo,
72
+ ActuatorInfo,
73
+ SensorInfo,
74
+ // Contacts
75
+ ContactInfo,
76
+ // Raycast
77
+ RayHit,
78
+ // Model options
79
+ ModelOptions,
80
+ // Trajectory
81
+ TrajectoryFrame,
82
+ TrajectoryData,
83
+ // Keyboard teleop
84
+ KeyBinding,
85
+ KeyboardTeleopConfig,
86
+ // Policy
87
+ PolicyConfig,
88
+ // Component props
89
+ IkGizmoProps,
90
+ DragInteractionProps,
91
+ DebugProps,
92
+ SceneLightsProps,
93
+ TrajectoryPlayerProps,
94
+ SelectionHighlightProps,
95
+ ContactListenerProps,
96
+ // API
97
+ MujocoSimAPI,
98
+ MujocoCanvasProps,
99
+ MujocoContextValue,
100
+ // Hook return types
101
+ SitePositionResult,
102
+ SensorResult,
103
+ BodyStateResult,
104
+ JointStateResult,
105
+ } from './types';
106
+
107
+ // Re-export MuJoCo types for convenience
108
+ export type { MujocoModule, MujocoModel, MujocoData } from './types';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+
7
+ import * as THREE from 'three';
8
+
9
+ interface InternalBufferGeometry extends THREE.BufferGeometry {
10
+ type: string;
11
+ }
12
+
13
+ /**
14
+ * CapsuleGeometry
15
+ * Custom geometry for capsule shape.
16
+ */
17
+ export class CapsuleGeometry extends THREE.BufferGeometry {
18
+ parameters: { radius: number; length: number; capSegments: number; radialSegments: number; };
19
+
20
+ constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) {
21
+ super();
22
+ (this as unknown as InternalBufferGeometry).type = 'CapsuleGeometry';
23
+ this.parameters = { radius, length, capSegments, radialSegments };
24
+ const path = new THREE.Path();
25
+ path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0, false);
26
+ path.absarc(0, length / 2, radius, 0, Math.PI * 0.5, false);
27
+ const latheGeometry = new THREE.LatheGeometry(path.getPoints(capSegments), radialSegments);
28
+
29
+ const self = this as THREE.BufferGeometry;
30
+ self.setIndex(latheGeometry.getIndex());
31
+ self.setAttribute('position', latheGeometry.getAttribute('position'));
32
+ self.setAttribute('normal', latheGeometry.getAttribute('normal'));
33
+ self.setAttribute('uv', latheGeometry.getAttribute('uv'));
34
+ }
35
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+
7
+ import * as THREE from 'three';
8
+ import { CapsuleGeometry } from './CapsuleGeometry';
9
+ import { Reflector } from './Reflector';
10
+ import { MujocoModel, MujocoModule } from '../types';
11
+
12
+ /**
13
+ * GeomBuilder
14
+ * RESPONSIBILITY: Manufacturing visual objects.
15
+ *
16
+ * This class knows how to read a single MuJoCo 'geom' (collision shape) definition
17
+ * and build the corresponding Three.js Mesh for it.
18
+ * It handles all the different shape types (Box, Sphere, Cylinder, generic Mesh, etc.).
19
+ */
20
+ export class GeomBuilder {
21
+ private mujoco: MujocoModule;
22
+
23
+ constructor(mujoco: MujocoModule) {
24
+ this.mujoco = mujoco;
25
+ }
26
+
27
+ /**
28
+ * Creates a Three.js Object3D (usually a Mesh) for a specific geometry in the MuJoCo model.
29
+ * Returns null if the geometry shouldn't be rendered (e.g., invisible collision triggers).
30
+ */
31
+ create(mjModel: MujocoModel, g: number): THREE.Object3D | null {
32
+ // 1. Check if this geom is meant to be visible
33
+ // Group 3 in MuJoCo is conventionally used for invisible 'helper' geoms.
34
+ if (mjModel.geom_group[g] === 3) return null;
35
+
36
+ // 2. Read raw data from MuJoCo's WASM memory arrays
37
+ const type = mjModel.geom_type[g];
38
+ const size = mjModel.geom_size.subarray(g * 3, g * 3 + 3); // [x, y, z] size parameters
39
+ const pos = mjModel.geom_pos.subarray(g * 3, g * 3 + 3); // [x, y, z] local position
40
+ const quat = mjModel.geom_quat.subarray(g * 4, g * 4 + 4); // [w, x, y, z] local rotation
41
+
42
+ // 3. Determine material color
43
+ // Sometimes color is on the geom itself, sometimes it uses a shared material definition.
44
+ const matId = mjModel.geom_matid[g];
45
+ const color = new THREE.Color(0xffffff);
46
+ let opacity = 1.0;
47
+
48
+ if (matId >= 0) {
49
+ // Use shared material
50
+ const rgba = mjModel.mat_rgba.subarray(matId * 4, matId * 4 + 4);
51
+ color.setRGB(rgba[0], rgba[1], rgba[2]);
52
+ opacity = rgba[3];
53
+ } else {
54
+ // Use geom-specific color
55
+ const rgba = mjModel.geom_rgba.subarray(g * 4, g * 4 + 4);
56
+ color.setRGB(rgba[0], rgba[1], rgba[2]);
57
+ opacity = rgba[3];
58
+ }
59
+
60
+ // 4. Build the Geometry based on type
61
+ const MG = this.mujoco.mjtGeom; // Short alias for MuJoCo Geometry Types enum
62
+ let geo: THREE.BufferGeometry | null = null;
63
+
64
+ // The '.value ?? MG.XYZ' pattern handles slightly different versions of the mujoco-js bindings.
65
+ const getVal = (v: unknown) => (v as { value: number })?.value ?? v;
66
+
67
+ if (type === getVal(MG.mjGEOM_PLANE)) {
68
+ // Planes are infinite in MuJoCo, but we need a finite mesh for Three.js.
69
+ // Fallback reduced to 5m to match grid as requested.
70
+ geo = new THREE.PlaneGeometry(size[0] * 2 || 5, size[1] * 2 || 5);
71
+ } else if (type === getVal(MG.mjGEOM_SPHERE)) {
72
+ geo = new THREE.SphereGeometry(size[0], 24, 24);
73
+ } else if (type === getVal(MG.mjGEOM_CAPSULE)) {
74
+ // Capsules in MuJoCo are Z-axis aligned by default.
75
+ // Our custom CapsuleGeometry might need rotation to match.
76
+ geo = new CapsuleGeometry(size[0], size[1] * 2, 24, 12);
77
+ geo.rotateX(Math.PI / 2);
78
+ } else if (type === getVal(MG.mjGEOM_BOX)) {
79
+ // MuJoCo defines box size as "half-extents" (center to edge). Three.js uses full width.
80
+ geo = new THREE.BoxGeometry(size[0] * 2, size[1] * 2, size[2] * 2);
81
+ } else if (type === getVal(MG.mjGEOM_CYLINDER)) {
82
+ geo = new THREE.CylinderGeometry(size[0], size[0], size[1] * 2, 24);
83
+ geo.rotateX(Math.PI / 2);
84
+ } else if (type === getVal(MG.mjGEOM_MESH)) {
85
+ // Arbitrary 3D meshes (like the robot parts).
86
+ // We must read the vertex and face data directly from MuJoCo's buffers.
87
+ const mId = mjModel.geom_dataid[g];
88
+ const vAdr = mjModel.mesh_vertadr[mId];
89
+ const vNum = mjModel.mesh_vertnum[mId];
90
+ const fAdr = mjModel.mesh_faceadr[mId];
91
+ const fNum = mjModel.mesh_facenum[mId];
92
+
93
+ geo = new THREE.BufferGeometry();
94
+ // 'position' attribute = vertices
95
+ geo.setAttribute('position', new THREE.Float32BufferAttribute(mjModel.mesh_vert.subarray(vAdr * 3, (vAdr + vNum) * 3), 3));
96
+ // 'index' = faces (triangles connecting vertices)
97
+ geo.setIndex(Array.from(mjModel.mesh_face.subarray(fAdr * 3, (fAdr + fNum) * 3)));
98
+ geo.computeVertexNormals(); // Auto-calculate smooth lighting normals
99
+ }
100
+
101
+ // 5. Construct the final Mesh
102
+ if (geo) {
103
+ let mesh;
104
+ // Special handling for the floor plane to make it shiny
105
+ if (type === getVal(MG.mjGEOM_PLANE)) {
106
+ mesh = new Reflector(geo, {
107
+ clipBias: 0.003,
108
+ textureWidth: 1024, textureHeight: 1024,
109
+ color,
110
+ mixStrength: 0.25
111
+ });
112
+ } else {
113
+ // Standard physical material for everything else
114
+ mesh = new THREE.Mesh(geo, new THREE.MeshStandardMaterial({
115
+ color,
116
+ transparent: opacity < 1,
117
+ opacity,
118
+ roughness: 0.6,
119
+ metalness: 0.2
120
+ }));
121
+ // Enable shadows
122
+ mesh.castShadow = true;
123
+ mesh.receiveShadow = true;
124
+ }
125
+
126
+ // Apply the local position offset and rotation specified in the MJCF XML
127
+ mesh.position.set(pos[0], pos[1], pos[2]);
128
+ // MuJoCo quaternions are [w, x, y, z], Three.js are [x, y, z, w]
129
+ mesh.quaternion.set(quat[1], quat[2], quat[3], quat[0]);
130
+
131
+ // Tag the mesh with its MuJoCo body and geom IDs for interaction (picking/dragging)
132
+ mesh.userData.bodyID = mjModel.geom_bodyid[g];
133
+ mesh.userData.geomID = g;
134
+
135
+ return mesh;
136
+ }
137
+
138
+ return null;
139
+ }
140
+ }
@@ -0,0 +1,225 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+
7
+ import * as THREE from 'three';
8
+
9
+ /**
10
+ * Options for configuring the Reflector.
11
+ */
12
+ export interface ReflectorOptions {
13
+ color?: THREE.ColorRepresentation;
14
+ textureWidth?: number;
15
+ textureHeight?: number;
16
+ clipBias?: number;
17
+ multisample?: number;
18
+ texture?: THREE.Texture;
19
+ mixStrength?: number; // How strong the reflection is (0.0 - 1.0)
20
+ }
21
+
22
+ interface ReflectorMesh extends THREE.Mesh {
23
+ type: string;
24
+ material: THREE.MeshPhysicalMaterial;
25
+ // tslint:disable-next-line:no-any
26
+ onBeforeRender: (renderer: any, scene: any, camera: any) => void;
27
+ }
28
+
29
+ /**
30
+ * Reflector
31
+ * Creates a reflective surface.
32
+ */
33
+ export class Reflector extends THREE.Mesh {
34
+ isReflector = true;
35
+ camera: THREE.PerspectiveCamera;
36
+ private reflectorPlane = new THREE.Plane();
37
+ private normal = new THREE.Vector3();
38
+ private reflectorWorldPosition = new THREE.Vector3();
39
+ private cameraWorldPosition = new THREE.Vector3();
40
+ private rotationMatrix = new THREE.Matrix4();
41
+ private lookAtPosition = new THREE.Vector3(0, 0, -1);
42
+ private clipPlane = new THREE.Vector4();
43
+ private view = new THREE.Vector3();
44
+ private target = new THREE.Vector3();
45
+ private q = new THREE.Vector4();
46
+ private textureMatrix = new THREE.Matrix4();
47
+ private virtualCamera: THREE.PerspectiveCamera;
48
+ private renderTarget: THREE.WebGLRenderTarget;
49
+
50
+ constructor(geometry: THREE.BufferGeometry, options: ReflectorOptions = {}) {
51
+ super(geometry);
52
+
53
+ (this as unknown as ReflectorMesh).type = 'Reflector';
54
+ this.camera = new THREE.PerspectiveCamera();
55
+
56
+ const color = (options.color !== undefined) ? new THREE.Color(options.color) : new THREE.Color(0x7F7F7F);
57
+ const textureWidth = options.textureWidth || 512;
58
+ const textureHeight = options.textureHeight || 512;
59
+ const clipBias = options.clipBias || 0;
60
+ const multisample = (options.multisample !== undefined) ? options.multisample : 4;
61
+ const blendTexture = options.texture || undefined;
62
+ const mixStrength = (options.mixStrength !== undefined) ? options.mixStrength : 0.25;
63
+
64
+ this.virtualCamera = this.camera;
65
+
66
+ this.renderTarget = new THREE.WebGLRenderTarget(textureWidth, textureHeight, {
67
+ samples: multisample,
68
+ type: THREE.HalfFloatType
69
+ });
70
+
71
+ (this as unknown as ReflectorMesh).material = new THREE.MeshPhysicalMaterial({
72
+ map: blendTexture,
73
+ color,
74
+ roughness: 0.5,
75
+ metalness: 0.1,
76
+ });
77
+
78
+ (this as unknown as ReflectorMesh).material.onBeforeCompile = (shader) => {
79
+ shader.uniforms.tDiffuse = { value: this.renderTarget.texture };
80
+ shader.uniforms.textureMatrix = { value: this.textureMatrix };
81
+ shader.uniforms.mixStrength = { value: mixStrength };
82
+
83
+ // Vertex Shader: Set Vertex Positions to the Unwrapped UV Positions
84
+ const bodyStart = shader.vertexShader.indexOf('void main() {');
85
+ shader.vertexShader =
86
+ 'uniform mat4 textureMatrix;\n' +
87
+ 'varying vec4 vUvReflection;\n' +
88
+ shader.vertexShader.slice(0, bodyStart) +
89
+ shader.vertexShader.slice(bodyStart, -1) +
90
+ ' vUvReflection = textureMatrix * vec4( position, 1.0 );\n' +
91
+ '}';
92
+
93
+ // Fragment Shader: Mix reflection with base material
94
+ const fragmentBodyStart = shader.fragmentShader.indexOf('void main() {');
95
+ shader.fragmentShader =
96
+ 'uniform sampler2D tDiffuse;\n' +
97
+ 'uniform float mixStrength;\n' +
98
+ 'varying vec4 vUvReflection;\n' +
99
+ shader.fragmentShader.slice(0, fragmentBodyStart) +
100
+ shader.fragmentShader.slice(fragmentBodyStart, -1) +
101
+ ' vec4 reflectionColor = texture2DProj( tDiffuse, vUvReflection );\n' +
102
+ ' gl_FragColor = vec4( mix( gl_FragColor.rgb, reflectionColor.rgb, mixStrength ), gl_FragColor.a );\n' +
103
+ '}';
104
+ };
105
+
106
+ (this as THREE.Object3D).receiveShadow = true;
107
+
108
+ (this as unknown as ReflectorMesh).onBeforeRender = (renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera) => {
109
+ this.reflectorWorldPosition.setFromMatrixPosition((this as THREE.Object3D).matrixWorld);
110
+ this.cameraWorldPosition.setFromMatrixPosition(camera.matrixWorld);
111
+
112
+ this.rotationMatrix.extractRotation((this as THREE.Object3D).matrixWorld);
113
+
114
+ this.normal.set(0, 0, 1);
115
+ this.normal.applyMatrix4(this.rotationMatrix);
116
+
117
+ this.view.subVectors(this.reflectorWorldPosition, this.cameraWorldPosition);
118
+
119
+ // Avoid rendering when reflector is facing away
120
+ if (this.view.dot(this.normal) > 0) return;
121
+
122
+ this.view.reflect(this.normal).negate();
123
+ this.view.add(this.reflectorWorldPosition);
124
+
125
+ this.rotationMatrix.extractRotation(camera.matrixWorld);
126
+
127
+ this.lookAtPosition.set(0, 0, -1);
128
+ this.lookAtPosition.applyMatrix4(this.rotationMatrix);
129
+ this.lookAtPosition.add(this.cameraWorldPosition);
130
+
131
+ this.target.subVectors(this.reflectorWorldPosition, this.lookAtPosition);
132
+ this.target.reflect(this.normal).negate();
133
+ this.target.add(this.reflectorWorldPosition);
134
+
135
+ this.virtualCamera.position.copy(this.view);
136
+ this.virtualCamera.up.set(0, 1, 0);
137
+ this.virtualCamera.up.applyMatrix4(this.rotationMatrix);
138
+ this.virtualCamera.up.reflect(this.normal);
139
+ this.virtualCamera.lookAt(this.target);
140
+
141
+ this.virtualCamera.far = (camera as THREE.PerspectiveCamera).far;
142
+
143
+ this.virtualCamera.updateMatrixWorld();
144
+ this.virtualCamera.projectionMatrix.copy((camera as THREE.PerspectiveCamera).projectionMatrix);
145
+
146
+ // Update the texture matrix
147
+ this.textureMatrix.set(
148
+ 0.5, 0.0, 0.0, 0.5,
149
+ 0.0, 0.5, 0.0, 0.5,
150
+ 0.0, 0.0, 0.5, 0.5,
151
+ 0.0, 0.0, 0.0, 1.0
152
+ );
153
+ this.textureMatrix.multiply(this.virtualCamera.projectionMatrix);
154
+ this.textureMatrix.multiply(this.virtualCamera.matrixWorldInverse);
155
+ this.textureMatrix.multiply((this as THREE.Object3D).matrixWorld);
156
+
157
+ // Now update projection matrix with new clip plane
158
+ this.reflectorPlane.setFromNormalAndCoplanarPoint(this.normal, this.reflectorWorldPosition);
159
+ this.reflectorPlane.applyMatrix4(this.virtualCamera.matrixWorldInverse);
160
+
161
+ this.clipPlane.set(this.reflectorPlane.normal.x, this.reflectorPlane.normal.y, this.reflectorPlane.normal.z, this.reflectorPlane.constant);
162
+
163
+ const projectionMatrix = this.virtualCamera.projectionMatrix;
164
+
165
+ this.q.x = (Math.sign(this.clipPlane.x) + projectionMatrix.elements[8]) / projectionMatrix.elements[0];
166
+ this.q.y = (Math.sign(this.clipPlane.y) + projectionMatrix.elements[9]) / projectionMatrix.elements[5];
167
+ this.q.z = -1.0;
168
+ this.q.w = (1.0 + projectionMatrix.elements[10]) / projectionMatrix.elements[14];
169
+
170
+ // Calculate the scaled plane vector
171
+ this.clipPlane.multiplyScalar(2.0 / this.clipPlane.dot(this.q));
172
+
173
+ // Replacing the third row of the projection matrix
174
+ projectionMatrix.elements[2] = this.clipPlane.x;
175
+ projectionMatrix.elements[6] = this.clipPlane.y;
176
+ projectionMatrix.elements[10] = this.clipPlane.z + 1.0 - clipBias;
177
+ projectionMatrix.elements[14] = this.clipPlane.w;
178
+
179
+ // Render
180
+ (this as THREE.Object3D).visible = false;
181
+
182
+ const currentRenderTarget = renderer.getRenderTarget();
183
+ const currentXrEnabled = renderer.xr.enabled;
184
+ const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
185
+
186
+ renderer.xr.enabled = false;
187
+ renderer.shadowMap.autoUpdate = false;
188
+
189
+ renderer.setRenderTarget(this.renderTarget);
190
+
191
+ renderer.state.buffers.depth.setMask(true);
192
+
193
+ if (renderer.autoClear === false) renderer.clear();
194
+ renderer.render(scene, this.virtualCamera);
195
+
196
+ renderer.xr.enabled = currentXrEnabled;
197
+ renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
198
+
199
+ renderer.setRenderTarget(currentRenderTarget);
200
+
201
+ // Restore viewport
202
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
203
+ const viewport = (camera as any).viewport;
204
+ if (viewport !== undefined) {
205
+ renderer.state.viewport(viewport);
206
+ }
207
+
208
+ (this as THREE.Object3D).visible = true;
209
+ };
210
+ }
211
+
212
+ getRenderTarget() {
213
+ return this.renderTarget;
214
+ }
215
+
216
+ dispose() {
217
+ this.renderTarget.dispose();
218
+ const mesh = this as THREE.Mesh;
219
+ if (Array.isArray(mesh.material)) {
220
+ mesh.material.forEach((m) => m.dispose());
221
+ } else {
222
+ mesh.material.dispose();
223
+ }
224
+ }
225
+ }