@opsydyn/effect-three 0.1.0 → 0.3.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/CHANGELOG.md +16 -0
- package/README.md +3 -4
- package/dist/{index-ef8pbv9g.js → index-8cf8t6by.js} +328 -25
- package/dist/index-8cf8t6by.js.map +41 -0
- package/dist/index.js +61 -8
- package/dist/index.js.map +3 -3
- package/dist/kernel/behaviours.d.ts.map +1 -1
- package/dist/kernel/build.d.ts +56 -1
- package/dist/kernel/build.d.ts.map +1 -1
- package/dist/kernel/cameraMotion.d.ts +3 -1
- package/dist/kernel/cameraMotion.d.ts.map +1 -1
- package/dist/kernel/cameraShake.d.ts +33 -0
- package/dist/kernel/cameraShake.d.ts.map +1 -0
- package/dist/kernel/description.d.ts +85 -1
- package/dist/kernel/description.d.ts.map +1 -1
- package/dist/kernel/diff.d.ts +3 -1
- package/dist/kernel/diff.d.ts.map +1 -1
- package/dist/kernel/equality.d.ts +5 -1
- package/dist/kernel/equality.d.ts.map +1 -1
- package/dist/kernel/index.d.ts +1 -1
- package/dist/kernel/index.d.ts.map +1 -1
- package/dist/kernel/index.js +12 -2
- package/dist/kernel/index.js.map +1 -1
- package/dist/kernel/labels.d.ts +6 -0
- package/dist/kernel/labels.d.ts.map +1 -1
- package/dist/kernel/pointer.d.ts.map +1 -1
- package/dist/kernel/renderKit.d.ts +5 -4
- package/dist/kernel/renderKit.d.ts.map +1 -1
- package/dist/kernel/resources.d.ts.map +1 -1
- package/dist/kernel/sceneGraph.d.ts +0 -1
- package/dist/kernel/sceneGraph.d.ts.map +1 -1
- package/dist/runtime/SceneSchema.d.ts +296 -5
- package/dist/runtime/SceneSchema.d.ts.map +1 -1
- package/dist/webgl/index.js +2 -2
- package/dist/webgl/index.js.map +3 -3
- package/dist/webgpu/index.js +2 -2
- package/dist/webgpu/index.js.map +3 -3
- package/package.json +1 -1
- package/src/kernel/behaviours.ts +5 -0
- package/src/kernel/bodySources.ts +1 -1
- package/src/kernel/build.ts +98 -0
- package/src/kernel/cameraMotion.ts +3 -0
- package/src/kernel/cameraShake.ts +97 -0
- package/src/kernel/description.ts +91 -1
- package/src/kernel/diff.ts +7 -1
- package/src/kernel/equality.ts +47 -0
- package/src/kernel/index.ts +5 -0
- package/src/kernel/labels.ts +6 -0
- package/src/kernel/pointer.ts +8 -0
- package/src/kernel/renderKit.ts +5 -4
- package/src/kernel/resources.ts +69 -0
- package/src/kernel/sceneGraph.ts +146 -3
- package/src/runtime/SceneSchema.ts +58 -4
- package/src/webgl/index.ts +1 -1
- package/src/webgpu/index.ts +2 -2
- package/dist/index-ef8pbv9g.js.map +0 -40
package/dist/webgpu/index.js.map
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
"/**\n * The WebGPU kit's contact shadow materials, in TSL: the WebGL kit's shadow-from-depth\n * output (colour `color · depth · 2`, alpha `1 − depth`) and three's horizontal and\n * vertical blur shaders (nine taps, their weights). The passes themselves are the\n * kernel's.\n *\n * @module\n */\nimport { type Color, NoBlending, Texture } from 'three';\nimport { depth, float, texture, uniform, uv, vec2 } from 'three/tsl';\nimport { MeshBasicNodeMaterial } from 'three/webgpu';\n\nimport {\n type BlurPass,\n createContactShadowPasses,\n type ShadowMaterials,\n} from '../kernel/contactShadowPasses.ts';\nimport type { SceneRenderer } from '../kernel/renderer.ts';\nimport type { ContactShadowPasses, ShadowedScene } from '../kernel/renderKit.ts';\n\n/** A material writing exactly what its nodes say: no blending, depth or tone mapping. */\nconst plainMaterial = (): MeshBasicNodeMaterial => {\n const material = new MeshBasicNodeMaterial();\n material.transparent = true;\n material.blending = NoBlending;\n material.depthTest = false;\n material.depthWrite = false;\n material.toneMapped = false;\n return material;\n};\n\nconst depthMaterial = (color: Color): MeshBasicNodeMaterial => {\n const material = plainMaterial();\n // Darkest where an object touches the plane: `depth` is linear for the orthographic camera.\n material.colorNode = uniform(color).mul(depth).mul(2);\n material.opacityNode = float(1).sub(depth);\n return material;\n};\n\n/** three's `HorizontalBlurShader` and `VerticalBlurShader` taps: offset and weight. */\nconst taps = [\n [-4, 0.051],\n [-3, 0.0918],\n [-2, 0.12245],\n [-1, 0.1531],\n [0, 0.1633],\n [1, 0.1531],\n [2, 0.12245],\n [3, 0.0918],\n [4, 0.051],\n] as const;\n\nconst blur = (direction: 'horizontal' | 'vertical'): BlurPass => {\n const source = texture(new Texture());\n const step = uniform(0);\n const along = direction === 'horizontal' ? vec2(step, 0) : vec2(0, step);\n const [first, ...rest] = taps.map(([offset, weight]) =>\n source.sample(uv().add(along.mul(offset))).mul(weight),\n );\n const sum = rest.reduce((total, tap) => total.add(tap), first ?? source);\n const material = plainMaterial();\n material.colorNode = sum.rgb;\n material.opacityNode = sum.a;\n return {\n material,\n set: (map, amount) => {\n source.value = map;\n step.value = amount;\n },\n };\n};\n\nconst webgpuShadowMaterials: ShadowMaterials = {\n depth: depthMaterial,\n blur,\n flippedTargets: true,\n};\n\n/** Creates the contact shadow passes for one scene graph and renderer, in TSL. */\nexport const createContactShadows = (\n graph: ShadowedScene,\n renderer: SceneRenderer,\n): ContactShadowPasses => createContactShadowPasses(graph, renderer, webgpuShadowMaterials);\n",
|
|
6
6
|
"/**\n * The WebGPU kit's shader grid: the WebGL kit's grid (Drei's `Grid`, `bf6f4ad`, MIT; after\n * Fyrestar's `THREE.InfiniteGridHelper` and threlte's `Grid`) written in TSL, so that\n * three's `WebGPURenderer` can draw it on either backend. Same lines, same fade, same\n * camera uniforms set in `onBeforeRender`.\n *\n * @module\n */\nimport {\n BackSide,\n Color,\n DoubleSide,\n FrontSide,\n Mesh,\n Plane,\n PlaneGeometry,\n type Side,\n Vector3,\n} from 'three';\nimport {\n abs,\n distance,\n float,\n fract,\n fwidth,\n min,\n mix,\n modelWorldMatrix,\n modelWorldMatrixInverse,\n positionGeometry,\n pow,\n select,\n uniform,\n varying,\n vec4,\n} from 'three/tsl';\nimport { MeshBasicNodeMaterial } from 'three/webgpu';\n\nimport type { MaterialSide, ShaderGridNode } from '../kernel/description.ts';\nimport type { HostedShaderGrid } from '../kernel/renderKit.ts';\n\nconst sides = { front: FrontSide, back: BackSide, double: DoubleSide } satisfies Record<\n MaterialSide,\n Side\n>;\n\n/** The grid's uniforms: one node each, written from the description. */\nconst createUniforms = () => ({\n cellSize: uniform(0.5),\n cellThickness: uniform(0.5),\n cellColor: uniform(new Color()),\n sectionSize: uniform(1),\n sectionThickness: uniform(1),\n sectionColor: uniform(new Color()),\n fadeDistance: uniform(100),\n fadeStrength: uniform(1),\n fadeFrom: uniform(1),\n infinite: uniform(0),\n followCamera: uniform(0),\n cameraOnPlane: uniform(new Vector3()),\n planeOrigin: uniform(new Vector3()),\n});\n\ntype Uniforms = ReturnType<typeof createUniforms>;\n\n/** The grid's material: the WebGL kit's vertex and fragment shaders, as TSL nodes. */\nconst gridMaterial = (u: Uniforms): MeshBasicNodeMaterial => {\n const material = new MeshBasicNodeMaterial({ transparent: true });\n // The plane geometry lies in XY; the grid lies in XZ. Infinite grids reach the fade.\n const lifted = positionGeometry.xzy.mul(\n select(u.infinite.greaterThan(0.5), u.fadeDistance.add(1), 1),\n );\n // Following the camera slides the plane by its foot's offset, in the grid's own space.\n const shift = modelWorldMatrixInverse.mul(vec4(u.cameraOnPlane.sub(u.planeOrigin), 0)).xyz;\n const local = select(u.followCamera.greaterThan(0.5), lifted.add(shift), lifted);\n material.positionNode = local;\n const gridPosition = varying(local);\n const worldPosition = varying(modelWorldMatrix.mul(vec4(local, 1)).xyz);\n // 1 on a line of a grid with this spacing, 0 between, antialiased over a pixel.\n const lines = (spacing: typeof u.cellSize, thickness: typeof u.cellThickness) => {\n const at = gridPosition.xz.div(spacing);\n const away = abs(fract(at.sub(0.5)).sub(0.5)).div(fwidth(at));\n return float(1).sub(min(min(away.x, away.y).add(1).sub(thickness), 1));\n };\n const cell = lines(u.cellSize, u.cellThickness);\n const section = lines(u.sectionSize, u.sectionThickness);\n const away = distance(u.cameraOnPlane.mul(u.fadeFrom), worldPosition);\n const fade = pow(float(1).sub(min(away.div(u.fadeDistance), 1)), u.fadeStrength);\n const alpha = cell.add(section).mul(fade);\n material.colorNode = mix(u.cellColor, u.sectionColor, min(1, u.sectionThickness.mul(section)));\n material.opacityNode = mix(alpha.mul(0.75), alpha, section);\n // Discard where there is no line, as the WebGL kit's shader does.\n material.alphaTest = 1e-4;\n return material;\n};\n\n/** Writes a node's settings into the uniforms and material. */\nconst writeUniforms = (\n u: Uniforms,\n material: MeshBasicNodeMaterial,\n node: ShaderGridNode,\n): void => {\n u.cellSize.value = node.cellSize;\n u.cellThickness.value = node.cellThickness;\n u.cellColor.value.setHex(node.cellColor);\n u.sectionSize.value = node.sectionSize;\n u.sectionThickness.value = node.sectionThickness;\n u.sectionColor.value.setHex(node.sectionColor);\n u.fadeDistance.value = node.fadeDistance;\n u.fadeStrength.value = node.fadeStrength;\n u.fadeFrom.value = node.fadeFrom;\n u.infinite.value = node.infinite ? 1 : 0;\n u.followCamera.value = node.followCamera ? 1 : 0;\n material.side = sides[node.side];\n};\n\nconst up = new Vector3(0, 1, 0);\nconst origin = new Vector3();\nconst plane = new Plane();\nconst cameraPosition = new Vector3();\n\n/** The WebGPU kit's shader grid: a plane with the grid material, placed and updated. */\nexport const hostShaderGrid = (node: ShaderGridNode): HostedShaderGrid => {\n const u = createUniforms();\n const material = gridMaterial(u);\n writeUniforms(u, material, node);\n const mesh = new Mesh(new PlaneGeometry(...node.size), material);\n // The shader moves vertices (infinite, following): the plane's own bounds mean nothing.\n mesh.frustumCulled = false;\n const place = (next: ShaderGridNode): void => {\n mesh.position.fromArray(next.position);\n mesh.rotation.fromArray([...next.rotation]);\n };\n place(node);\n mesh.onBeforeRender = (_renderer, _scene, camera) => {\n plane.setFromNormalAndCoplanarPoint(up, origin).applyMatrix4(mesh.matrixWorld);\n plane.projectPoint(\n cameraPosition.setFromMatrixPosition(camera.matrixWorld),\n u.cameraOnPlane.value,\n );\n u.planeOrigin.value.set(0, 0, 0).applyMatrix4(mesh.matrixWorld);\n };\n return {\n object: mesh,\n update: (previous, next) => {\n if (previous.size[0] !== next.size[0] || previous.size[1] !== next.size[1]) {\n mesh.geometry.dispose();\n mesh.geometry = new PlaneGeometry(...next.size);\n }\n writeUniforms(u, material, next);\n place(next);\n },\n dispose: () => {\n mesh.geometry.dispose();\n material.dispose();\n },\n };\n};\n",
|
|
7
7
|
"/**\n * The WebGPU kit's sky: three.js's `SkyMesh` (`addons/objects/SkyMesh.js` at 0.186.1), the\n * same Preetham sky as the WebGL kit's `Sky`, written in TSL. Never picked.\n *\n * @module\n */\nimport { SkyMesh } from 'three/addons/objects/SkyMesh.js';\n\nimport type { Sky } from '../kernel/description.ts';\nimport type { HostedSky } from '../kernel/renderKit.ts';\n\n/** Writes a sky's description into the mesh's uniforms and scale. */\nconst writeSky = (mesh: SkyMesh, description: Sky): void => {\n mesh.turbidity.value = description.turbidity;\n mesh.rayleigh.value = description.rayleigh;\n mesh.mieCoefficient.value = description.mieCoefficient;\n mesh.mieDirectionalG.value = description.mieDirectionalG;\n mesh.cloudCoverage.value = description.clouds;\n mesh.sunPosition.value.fromArray(description.sunPosition);\n mesh.scale.setScalar(description.distance);\n};\n\n/** Creates the sky mesh; the scene graph adds it and writes its description. */\nexport const hostSky = (): HostedSky => {\n const mesh = new SkyMesh();\n mesh.raycast = () => undefined;\n return {\n object: mesh,\n update: (description) => writeSky(mesh, description),\n dispose: () => {\n mesh.geometry.dispose();\n mesh.material.dispose();\n },\n };\n};\n",
|
|
8
|
-
"/**\n * The WebGPU render kit: the scene's shaded parts (the sky, the shader grid, contact\n * shadows) written in TSL, for three.js's `WebGPURenderer` on either of its backends.\n * The Foldkit adapter's `webgpuRendererFactory` carries it.\n *\n * @module\n */\nimport type { RenderKit } from '../kernel/renderKit.ts';\nimport { createContactShadows } from './contactShadows.ts';\nimport { hostShaderGrid } from './shaderGrid.ts';\nimport { hostSky } from './sky.ts';\n\nexport const webgpuKit: RenderKit = {\n //
|
|
8
|
+
"/**\n * The WebGPU render kit: the scene's shaded parts (the sky, the shader grid, contact\n * shadows) written in TSL, for three.js's `WebGPURenderer` on either of its backends.\n * The Foldkit adapter's `webgpuRendererFactory` carries it.\n *\n * @module\n */\nimport type { RenderKit } from '../kernel/renderKit.ts';\nimport { createContactShadows } from './contactShadows.ts';\nimport { hostShaderGrid } from './shaderGrid.ts';\nimport { hostSky } from './sky.ts';\n\nexport const webgpuKit: RenderKit = {\n // Node materials: label engines draw with their TSL implementation (`createTsl`).\n shading: 'tsl',\n sky: hostSky,\n shaderGrid: hostShaderGrid,\n contactShadows: createContactShadows,\n};\n"
|
|
9
9
|
],
|
|
10
|
-
"mappings": "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
|
|
11
|
-
"debugId": "
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "3D261B4594F0512A64756E2164756E21",
|
|
12
12
|
"names": []
|
|
13
13
|
}
|
package/package.json
CHANGED
package/src/kernel/behaviours.ts
CHANGED
|
@@ -21,6 +21,11 @@ export const writeRotation = (target: Euler, rotation: Rotation, elapsedSeconds:
|
|
|
21
21
|
target.set(rotation.euler[0], rotation.euler[1], rotation.euler[2]);
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
+
// The scene graph turns a billboard towards the camera each frame; until then, none.
|
|
25
|
+
if (rotation._tag === 'Billboard') {
|
|
26
|
+
target.set(0, 0, 0);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
24
29
|
const angle = spinAngle(rotation, elapsedSeconds);
|
|
25
30
|
switch (rotation.axis) {
|
|
26
31
|
case 'x':
|
|
@@ -87,7 +87,7 @@ const scaleText = (from: Matrix4): string =>
|
|
|
87
87
|
.join(',');
|
|
88
88
|
|
|
89
89
|
const rotationText = (rotation: Rotation): string =>
|
|
90
|
-
rotation._tag === 'Fixed' ? rotation.euler.join(',') :
|
|
90
|
+
rotation._tag === 'Fixed' ? rotation.euler.join(',') : rotation._tag;
|
|
91
91
|
|
|
92
92
|
/** A world pose written to an object in its parent's space, keeping its scale. */
|
|
93
93
|
export const placeObject = (object: Object3D, pose: BodyPose): void => {
|
package/src/kernel/build.ts
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import type {
|
|
7
7
|
AmbientLightNode,
|
|
8
8
|
Animation,
|
|
9
|
+
CameraShake,
|
|
10
|
+
Detail,
|
|
9
11
|
BasicMaterial,
|
|
10
12
|
Body,
|
|
11
13
|
Behave,
|
|
@@ -40,6 +42,8 @@ import type {
|
|
|
40
42
|
ColliderDescription,
|
|
41
43
|
ColliderShapeDescription,
|
|
42
44
|
CapsuleGeometry,
|
|
45
|
+
RoundedBoxGeometry,
|
|
46
|
+
BackdropGeometry,
|
|
43
47
|
CircleGeometry,
|
|
44
48
|
ConeGeometry,
|
|
45
49
|
CylinderGeometry,
|
|
@@ -106,6 +110,21 @@ export const spin = (options: {
|
|
|
106
110
|
phase: options.phase ?? 0,
|
|
107
111
|
});
|
|
108
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Faces the camera every frame, as Drei's `Billboard` (`bf6f4ad`, `src/core/Billboard.tsx`):
|
|
115
|
+
* for labels, markers and sprites that should always read face on. Lock an axis to keep
|
|
116
|
+
* it still, `lockX` and `lockZ` together for something that stays upright and only turns
|
|
117
|
+
* round. To rotate it as well, put it in a rotated group.
|
|
118
|
+
*/
|
|
119
|
+
export const billboard = (
|
|
120
|
+
options: { readonly lockX?: boolean; readonly lockY?: boolean; readonly lockZ?: boolean } = {},
|
|
121
|
+
): Rotation => ({
|
|
122
|
+
_tag: 'Billboard',
|
|
123
|
+
lockX: options.lockX ?? false,
|
|
124
|
+
lockY: options.lockY ?? false,
|
|
125
|
+
lockZ: options.lockZ ?? false,
|
|
126
|
+
});
|
|
127
|
+
|
|
109
128
|
/** Box geometry. Each dimension defaults to 1. */
|
|
110
129
|
export const box = (
|
|
111
130
|
size: { readonly width?: number; readonly height?: number; readonly depth?: number } = {},
|
|
@@ -197,6 +216,47 @@ export const capsule = (
|
|
|
197
216
|
radialSegments: options.radialSegments ?? 8,
|
|
198
217
|
});
|
|
199
218
|
|
|
219
|
+
/**
|
|
220
|
+
* A box with rounded edges and corners, as Drei's `RoundedBox` (`bf6f4ad`,
|
|
221
|
+
* `src/core/RoundedBox.tsx`), with its defaults: 1 × 1 × 1, radius 0.05, smoothness 4,
|
|
222
|
+
* 4 bevel segments, 1 step, crease angle 0.4.
|
|
223
|
+
*/
|
|
224
|
+
export const roundedBox = (
|
|
225
|
+
options: {
|
|
226
|
+
readonly width?: number;
|
|
227
|
+
readonly height?: number;
|
|
228
|
+
readonly depth?: number;
|
|
229
|
+
readonly radius?: number;
|
|
230
|
+
readonly smoothness?: number;
|
|
231
|
+
readonly bevelSegments?: number;
|
|
232
|
+
readonly steps?: number;
|
|
233
|
+
readonly creaseAngle?: number;
|
|
234
|
+
} = {},
|
|
235
|
+
): RoundedBoxGeometry => ({
|
|
236
|
+
_tag: 'RoundedBox',
|
|
237
|
+
width: options.width ?? 1,
|
|
238
|
+
height: options.height ?? 1,
|
|
239
|
+
depth: options.depth ?? 1,
|
|
240
|
+
radius: options.radius ?? 0.05,
|
|
241
|
+
smoothness: options.smoothness ?? 4,
|
|
242
|
+
bevelSegments: options.bevelSegments ?? 4,
|
|
243
|
+
steps: options.steps ?? 1,
|
|
244
|
+
creaseAngle: options.creaseAngle ?? 0.4,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* A curved photo backdrop, as Drei's `Backdrop` (`bf6f4ad`, `src/core/Backdrop.tsx`), with
|
|
249
|
+
* its defaults: floor 0.25, 20 segments. One unit each way: scale the mesh to size, and
|
|
250
|
+
* give it `receiveShadow` to catch shadows as Drei's prop does.
|
|
251
|
+
*/
|
|
252
|
+
export const backdrop = (
|
|
253
|
+
options: { readonly floor?: number; readonly segments?: number } = {},
|
|
254
|
+
): BackdropGeometry => ({
|
|
255
|
+
_tag: 'Backdrop',
|
|
256
|
+
floor: options.floor ?? 0.25,
|
|
257
|
+
segments: options.segments ?? 20,
|
|
258
|
+
});
|
|
259
|
+
|
|
200
260
|
/** Ring geometry facing +Z. Defaults (as three.js): radii 0.5 and 1, 32 segments. */
|
|
201
261
|
export const ring = (
|
|
202
262
|
options: {
|
|
@@ -478,6 +538,7 @@ export const group = (
|
|
|
478
538
|
readonly children?: ReadonlyArray<SceneNode>;
|
|
479
539
|
readonly body?: Body | null;
|
|
480
540
|
readonly follow?: XrSpace | null;
|
|
541
|
+
readonly detail?: Detail | null;
|
|
481
542
|
},
|
|
482
543
|
): GroupNode => ({
|
|
483
544
|
_tag: 'Group',
|
|
@@ -491,8 +552,20 @@ export const group = (
|
|
|
491
552
|
children: options.children ?? [],
|
|
492
553
|
body: options.body ?? null,
|
|
493
554
|
follow: options.follow ?? null,
|
|
555
|
+
detail: options.detail ?? null,
|
|
494
556
|
});
|
|
495
557
|
|
|
558
|
+
/**
|
|
559
|
+
* Levels of detail for a group, as Drei's `Detailed` (`bf6f4ad`, `src/core/Detailed.tsx`):
|
|
560
|
+
* `group({ detail: detailed({ distances: [0, 10, 20] }), children: [near, middle, far] })`
|
|
561
|
+
* shows `near` until the camera is 10 away, then `middle`, then `far` from 20. Hysteresis
|
|
562
|
+
* defaults to 0, as Drei's.
|
|
563
|
+
*/
|
|
564
|
+
export const detailed = (options: {
|
|
565
|
+
readonly distances: ReadonlyArray<number>;
|
|
566
|
+
readonly hysteresis?: number;
|
|
567
|
+
}): Detail => ({ distances: options.distances, hysteresis: options.hysteresis ?? 0 });
|
|
568
|
+
|
|
496
569
|
/** The head (the viewer), for content that stays in view. */
|
|
497
570
|
export const headSpace = (): XrSpace => ({ _tag: 'Head' });
|
|
498
571
|
|
|
@@ -859,6 +932,8 @@ export const borrowed = (
|
|
|
859
932
|
readonly interactive?: boolean;
|
|
860
933
|
readonly animation?: Animation | null;
|
|
861
934
|
readonly body?: Body | null;
|
|
935
|
+
readonly castShadow?: boolean;
|
|
936
|
+
readonly receiveShadow?: boolean;
|
|
862
937
|
},
|
|
863
938
|
): BorrowedNode => ({
|
|
864
939
|
_tag: 'Borrowed',
|
|
@@ -872,6 +947,8 @@ export const borrowed = (
|
|
|
872
947
|
behaviour: options.behaviour ?? null,
|
|
873
948
|
interactive: options.interactive ?? false,
|
|
874
949
|
body: options.body ?? null,
|
|
950
|
+
castShadow: options.castShadow ?? false,
|
|
951
|
+
receiveShadow: options.receiveShadow ?? false,
|
|
875
952
|
});
|
|
876
953
|
|
|
877
954
|
/**
|
|
@@ -1060,6 +1137,7 @@ export const perspectiveCamera = (
|
|
|
1060
1137
|
lookAt: options.lookAt ?? origin,
|
|
1061
1138
|
damping: options.damping ?? 0,
|
|
1062
1139
|
autoRotate: options.autoRotate ?? 0,
|
|
1140
|
+
shake: options.shake ?? null,
|
|
1063
1141
|
});
|
|
1064
1142
|
|
|
1065
1143
|
/**
|
|
@@ -1077,6 +1155,26 @@ export const orthographicCamera = (
|
|
|
1077
1155
|
lookAt: options.lookAt ?? origin,
|
|
1078
1156
|
damping: options.damping ?? 0,
|
|
1079
1157
|
autoRotate: options.autoRotate ?? 0,
|
|
1158
|
+
shake: options.shake ?? null,
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* Camera shake, as Drei's `CameraShake` (`bf6f4ad`, `src/core/CameraShake.tsx`), with its
|
|
1163
|
+
* defaults: intensity 1, no decay (rate 0.65 a second when on), and 0.1 radians at 0.1 for
|
|
1164
|
+
* each of yaw, pitch and roll. `perspectiveCamera({ shake: cameraShake({ decay: true }) })`
|
|
1165
|
+
* shakes once and settles; bump `trigger` to shake again.
|
|
1166
|
+
*/
|
|
1167
|
+
export const cameraShake = (options: Partial<CameraShake> = {}): CameraShake => ({
|
|
1168
|
+
intensity: options.intensity ?? 1,
|
|
1169
|
+
decay: options.decay ?? false,
|
|
1170
|
+
decayRate: options.decayRate ?? 0.65,
|
|
1171
|
+
maxYaw: options.maxYaw ?? 0.1,
|
|
1172
|
+
maxPitch: options.maxPitch ?? 0.1,
|
|
1173
|
+
maxRoll: options.maxRoll ?? 0.1,
|
|
1174
|
+
yawFrequency: options.yawFrequency ?? 0.1,
|
|
1175
|
+
pitchFrequency: options.pitchFrequency ?? 0.1,
|
|
1176
|
+
rollFrequency: options.rollFrequency ?? 0.1,
|
|
1177
|
+
trigger: options.trigger ?? 0,
|
|
1080
1178
|
});
|
|
1081
1179
|
|
|
1082
1180
|
const noEffects: ReadonlyArray<PostEffect> = [];
|
|
@@ -94,6 +94,8 @@ export type CameraMotion = {
|
|
|
94
94
|
readonly hold: (held: boolean) => void;
|
|
95
95
|
/** Where turning has taken the camera, when it has turned since its description. */
|
|
96
96
|
readonly turned: () => Vec3 | undefined;
|
|
97
|
+
/** The point the camera looks at now, on its way to the aim. Not to be changed. */
|
|
98
|
+
readonly target: () => Vector3;
|
|
97
99
|
};
|
|
98
100
|
|
|
99
101
|
/** Radians a turning camera turns per second per unit of `autoRotate` (three's `2π / 60`). */
|
|
@@ -198,5 +200,6 @@ export const createCameraMotion = (): CameraMotion => {
|
|
|
198
200
|
position.setFromSpherical(goal.spherical).add(goal.target);
|
|
199
201
|
return [position.x, position.y, position.z];
|
|
200
202
|
},
|
|
203
|
+
target: () => current.target,
|
|
201
204
|
};
|
|
202
205
|
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Camera shake, as Drei's `CameraShake` (`bf6f4ad`, `src/core/CameraShake.tsx`): yaw, pitch
|
|
3
|
+
* and roll added to the camera's rotation from simplex noise, scaled by the square of the
|
|
4
|
+
* intensity. Here it is a pure function of scene time: the noise is seeded (Drei seeds
|
|
5
|
+
* from `Math.random`), and decay counts down from when the shake began, so the same scene
|
|
6
|
+
* time always shakes the same way, at any frame rate. Reduced motion holds it still.
|
|
7
|
+
*
|
|
8
|
+
* The camera always looks at its target here, so each frame it is aimed there again and
|
|
9
|
+
* the shake added on top: whatever moved the camera in between, nothing accumulates.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import type { OrthographicCamera, PerspectiveCamera, Vector3 } from 'three';
|
|
14
|
+
import { SimplexNoise } from 'three/addons/math/SimplexNoise.js';
|
|
15
|
+
|
|
16
|
+
import type { CameraShake } from './description.ts';
|
|
17
|
+
import { cameraShakeEquals } from './equality.ts';
|
|
18
|
+
|
|
19
|
+
/** A small seeded generator (mulberry32), so the noise is the same on every page load. */
|
|
20
|
+
const seeded = (seed: number): { readonly random: () => number } => {
|
|
21
|
+
let state = seed >>> 0;
|
|
22
|
+
return {
|
|
23
|
+
random: () => {
|
|
24
|
+
state = (state + 0x6d2b79f5) >>> 0;
|
|
25
|
+
let t = state;
|
|
26
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
27
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
28
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const yawNoise = new SimplexNoise(seeded(1));
|
|
34
|
+
const pitchNoise = new SimplexNoise(seeded(2));
|
|
35
|
+
const rollNoise = new SimplexNoise(seeded(3));
|
|
36
|
+
|
|
37
|
+
/** The intensity at `seconds` after the shake began: Drei's, clamped to [0, 1], decaying. */
|
|
38
|
+
export const shakeIntensity = (shake: CameraShake, seconds: number): number => {
|
|
39
|
+
const decayed = shake.decay ? shake.intensity - shake.decayRate * seconds : shake.intensity;
|
|
40
|
+
return Math.min(1, Math.max(0, decayed));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type CameraShaker = {
|
|
44
|
+
/**
|
|
45
|
+
* The camera's shake as described at scene time `now`: one that first appears, changes
|
|
46
|
+
* or is triggered again begins then.
|
|
47
|
+
*/
|
|
48
|
+
readonly describe: (shake: CameraShake | null, now: number) => void;
|
|
49
|
+
/**
|
|
50
|
+
* Aims the camera at `target` and adds the shake at scene time `now`, or only aims it
|
|
51
|
+
* when the shake has ended or motion is reduced. Does nothing when nothing was added.
|
|
52
|
+
*/
|
|
53
|
+
readonly apply: (
|
|
54
|
+
camera: PerspectiveCamera | OrthographicCamera,
|
|
55
|
+
target: Vector3,
|
|
56
|
+
now: number,
|
|
57
|
+
animating: boolean,
|
|
58
|
+
) => void;
|
|
59
|
+
/** Whether the camera is shaking at scene time `now`, and so needs frames. */
|
|
60
|
+
readonly shaking: (now: number) => boolean;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** One camera's shake. Allocation-free once created. */
|
|
64
|
+
export const createCameraShaker = (): CameraShaker => {
|
|
65
|
+
let current: CameraShake | null = null;
|
|
66
|
+
let since = 0;
|
|
67
|
+
// Whether the camera wears an offset that a still frame must take off again.
|
|
68
|
+
let offset = false;
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
describe: (shake, now) => {
|
|
72
|
+
if (shake !== null && (current === null || !cameraShakeEquals(current, shake))) since = now;
|
|
73
|
+
current = shake;
|
|
74
|
+
},
|
|
75
|
+
apply: (camera, target, now, animating) => {
|
|
76
|
+
const shake = current;
|
|
77
|
+
const strength =
|
|
78
|
+
shake === null || !animating ? 0 : shakeIntensity(shake, Math.max(0, now - since)) ** 2;
|
|
79
|
+
if (shake === null || strength === 0) {
|
|
80
|
+
if (offset) camera.lookAt(target);
|
|
81
|
+
offset = false;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
camera.lookAt(target);
|
|
85
|
+
camera.rotation.set(
|
|
86
|
+
camera.rotation.x +
|
|
87
|
+
shake.maxPitch * strength * pitchNoise.noise(now * shake.pitchFrequency, 1),
|
|
88
|
+
camera.rotation.y + shake.maxYaw * strength * yawNoise.noise(now * shake.yawFrequency, 1),
|
|
89
|
+
camera.rotation.z +
|
|
90
|
+
shake.maxRoll * strength * rollNoise.noise(now * shake.rollFrequency, 1),
|
|
91
|
+
);
|
|
92
|
+
offset = true;
|
|
93
|
+
},
|
|
94
|
+
shaking: (now) =>
|
|
95
|
+
offset || (current !== null && shakeIntensity(current, Math.max(0, now - since)) > 0),
|
|
96
|
+
};
|
|
97
|
+
};
|
|
@@ -30,6 +30,17 @@ export type Rotation =
|
|
|
30
30
|
readonly axis: Axis;
|
|
31
31
|
readonly radiansPerSecond: number;
|
|
32
32
|
readonly phase: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Faces the scene's camera every frame, as Drei's `Billboard`: the node turns so its
|
|
36
|
+
* world rotation is the camera's. A locked axis stays at 0 in the node's own Euler
|
|
37
|
+
* angles (Drei keeps its previous value, which starts at 0).
|
|
38
|
+
*/
|
|
39
|
+
| {
|
|
40
|
+
readonly _tag: 'Billboard';
|
|
41
|
+
readonly lockX: boolean;
|
|
42
|
+
readonly lockY: boolean;
|
|
43
|
+
readonly lockZ: boolean;
|
|
33
44
|
};
|
|
34
45
|
|
|
35
46
|
/** Box geometry parameters. */
|
|
@@ -90,6 +101,36 @@ export type CapsuleGeometry = {
|
|
|
90
101
|
readonly radialSegments: number;
|
|
91
102
|
};
|
|
92
103
|
|
|
104
|
+
/**
|
|
105
|
+
* A box with rounded edges and corners, centred, as Drei's `RoundedBox`: a rounded
|
|
106
|
+
* rectangle extruded and bevelled, with normals creased at `creaseAngle`.
|
|
107
|
+
*/
|
|
108
|
+
export type RoundedBoxGeometry = {
|
|
109
|
+
readonly _tag: 'RoundedBox';
|
|
110
|
+
readonly width: number;
|
|
111
|
+
readonly height: number;
|
|
112
|
+
readonly depth: number;
|
|
113
|
+
readonly radius: number;
|
|
114
|
+
/** Segments in each rounded corner of the outline. */
|
|
115
|
+
readonly smoothness: number;
|
|
116
|
+
/** Segments across the bevel, which Drei doubles. */
|
|
117
|
+
readonly bevelSegments: number;
|
|
118
|
+
readonly steps: number;
|
|
119
|
+
/** Faces meeting at more than this angle (radians) keep a hard edge. */
|
|
120
|
+
readonly creaseAngle: number;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* A photo backdrop, as Drei's `Backdrop`: a floor that sweeps up into a wall behind it,
|
|
125
|
+
* one unit each way (scale it to size). The floor lies along +Z towards the viewer, the
|
|
126
|
+
* wall rises at its back, and `floor` extends the flat front.
|
|
127
|
+
*/
|
|
128
|
+
export type BackdropGeometry = {
|
|
129
|
+
readonly _tag: 'Backdrop';
|
|
130
|
+
readonly floor: number;
|
|
131
|
+
readonly segments: number;
|
|
132
|
+
};
|
|
133
|
+
|
|
93
134
|
/** A flat ring (annulus) in the XY plane, facing +Z. */
|
|
94
135
|
export type RingGeometry = {
|
|
95
136
|
readonly _tag: 'Ring';
|
|
@@ -150,7 +191,9 @@ export type ShapeGeometry =
|
|
|
150
191
|
| ConeGeometry
|
|
151
192
|
| CapsuleGeometry
|
|
152
193
|
| RingGeometry
|
|
153
|
-
| CircleGeometry
|
|
194
|
+
| CircleGeometry
|
|
195
|
+
| RoundedBoxGeometry
|
|
196
|
+
| BackdropGeometry;
|
|
154
197
|
|
|
155
198
|
/** Which faces a material draws. */
|
|
156
199
|
export type MaterialSide = 'front' | 'back' | 'double';
|
|
@@ -342,6 +385,22 @@ export type GroupNode = {
|
|
|
342
385
|
* not tracked (or no session presents). `null` for the group's own transform.
|
|
343
386
|
*/
|
|
344
387
|
readonly follow: XrSpace | null;
|
|
388
|
+
/**
|
|
389
|
+
* Levels of detail: the group shows one child at a time, chosen by the camera's
|
|
390
|
+
* distance (Drei's `Detailed`, three's `LOD`), or `null` to show them all.
|
|
391
|
+
*/
|
|
392
|
+
readonly detail: Detail | null;
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Levels of detail for a group's children, in order: child `i` shows from
|
|
397
|
+
* `distances[i]` (scene units from the camera, divided by its zoom) until the next level's
|
|
398
|
+
* distance. `hysteresis` (a fraction) keeps a shown level until the camera is that much
|
|
399
|
+
* nearer than its distance, so the switch does not flicker at the boundary.
|
|
400
|
+
*/
|
|
401
|
+
export type Detail = {
|
|
402
|
+
readonly distances: ReadonlyArray<number>;
|
|
403
|
+
readonly hysteresis: number;
|
|
345
404
|
};
|
|
346
405
|
|
|
347
406
|
/** The joints of a tracked hand, as WebXR names them (`XRHandJoint`). */
|
|
@@ -656,6 +715,13 @@ export type BorrowedNode = {
|
|
|
656
715
|
readonly animation: Animation | null;
|
|
657
716
|
/** A rigid body whose colliders are the model's meshes, once it is lent; or `null`. */
|
|
658
717
|
readonly body: Body | null;
|
|
718
|
+
/**
|
|
719
|
+
* Every mesh in what it shows casts (receives) shadows, as Drei's `Clone` shorthands;
|
|
720
|
+
* `false` leaves each mesh's own setting. Set on the lent object itself where it is
|
|
721
|
+
* shown as lent rather than cloned.
|
|
722
|
+
*/
|
|
723
|
+
readonly castShadow: boolean;
|
|
724
|
+
readonly receiveShadow: boolean;
|
|
659
725
|
};
|
|
660
726
|
|
|
661
727
|
/** Where a label's origin is along its width: an edge, the middle, or a fraction of it. */
|
|
@@ -902,6 +968,8 @@ export type PerspectiveCamera = {
|
|
|
902
968
|
* (`autoRotated(camera, detail)`). A new position or `lookAt` turns on from there.
|
|
903
969
|
*/
|
|
904
970
|
readonly autoRotate: number;
|
|
971
|
+
/** Shakes the rendered camera (Drei's `CameraShake`), or `null` for none. */
|
|
972
|
+
readonly shake: CameraShake | null;
|
|
905
973
|
};
|
|
906
974
|
|
|
907
975
|
/**
|
|
@@ -919,6 +987,28 @@ export type OrthographicCamera = {
|
|
|
919
987
|
readonly damping: number;
|
|
920
988
|
/** As on `PerspectiveCamera`. */
|
|
921
989
|
readonly autoRotate: number;
|
|
990
|
+
/** As on `PerspectiveCamera`. */
|
|
991
|
+
readonly shake: CameraShake | null;
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* Camera shake, as Drei's `CameraShake`: yaw, pitch and roll of up to `max*` radians,
|
|
996
|
+
* from noise at `*Frequency`, scaled by `intensity` squared (0 to 1). With `decay` the
|
|
997
|
+
* intensity falls by `decayRate` a second from when the shake began, so a decaying shake
|
|
998
|
+
* is a one-off: it begins when it first appears or changes, and `trigger` (a count the
|
|
999
|
+
* Model bumps) begins the same shake again. Reduced motion holds the camera still.
|
|
1000
|
+
*/
|
|
1001
|
+
export type CameraShake = {
|
|
1002
|
+
readonly intensity: number;
|
|
1003
|
+
readonly decay: boolean;
|
|
1004
|
+
readonly decayRate: number;
|
|
1005
|
+
readonly maxYaw: number;
|
|
1006
|
+
readonly maxPitch: number;
|
|
1007
|
+
readonly maxRoll: number;
|
|
1008
|
+
readonly yawFrequency: number;
|
|
1009
|
+
readonly pitchFrequency: number;
|
|
1010
|
+
readonly rollFrequency: number;
|
|
1011
|
+
readonly trigger: number;
|
|
922
1012
|
};
|
|
923
1013
|
|
|
924
1014
|
/** The single active camera. */
|
package/src/kernel/diff.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
labelMaterialEquals,
|
|
32
32
|
labelTypeEquals,
|
|
33
33
|
xrSpaceEquals,
|
|
34
|
+
detailEquals,
|
|
34
35
|
shaderGridLookEquals,
|
|
35
36
|
instancesEqual,
|
|
36
37
|
materialRefEquals,
|
|
@@ -82,7 +83,9 @@ export type Field =
|
|
|
82
83
|
/** A shader grid's look: anything but where it is. */
|
|
83
84
|
| 'grid'
|
|
84
85
|
/** The tracked space a group follows. */
|
|
85
|
-
| 'follow'
|
|
86
|
+
| 'follow'
|
|
87
|
+
/** A group's levels of detail. */
|
|
88
|
+
| 'detail';
|
|
86
89
|
|
|
87
90
|
/** A single change to apply to the hosted scene graph. */
|
|
88
91
|
export type Op =
|
|
@@ -131,6 +134,7 @@ export const changedFields = (
|
|
|
131
134
|
if (prev.interactive !== next.interactive) fields.push('interactive');
|
|
132
135
|
if (!bodyEquals(prev.body, next.body)) fields.push('body');
|
|
133
136
|
if (!xrSpaceEquals(prev.follow, next.follow)) fields.push('follow');
|
|
137
|
+
if (!detailEquals(prev.detail, next.detail)) fields.push('detail');
|
|
134
138
|
return fields;
|
|
135
139
|
}
|
|
136
140
|
case 'Mesh': {
|
|
@@ -175,6 +179,8 @@ export const changedFields = (
|
|
|
175
179
|
if (!animationEquals(prev.animation, next.animation)) fields.push('animation');
|
|
176
180
|
if (prev.interactive !== next.interactive) fields.push('interactive');
|
|
177
181
|
if (!bodyEquals(prev.body, next.body)) fields.push('body');
|
|
182
|
+
if (prev.castShadow !== next.castShadow) fields.push('castShadow');
|
|
183
|
+
if (prev.receiveShadow !== next.receiveShadow) fields.push('receiveShadow');
|
|
178
184
|
return fields;
|
|
179
185
|
}
|
|
180
186
|
case 'Label': {
|
package/src/kernel/equality.ts
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
import type {
|
|
8
8
|
Animation,
|
|
9
9
|
Behave,
|
|
10
|
+
CameraShake,
|
|
11
|
+
Detail,
|
|
10
12
|
Body,
|
|
11
13
|
BodyRef,
|
|
12
14
|
GeometryRef,
|
|
@@ -46,6 +48,17 @@ import type {
|
|
|
46
48
|
export const vec3Equals = (a: Vec3, b: Vec3): boolean =>
|
|
47
49
|
a === b || (Object.is(a[0], b[0]) && Object.is(a[1], b[1]) && Object.is(a[2], b[2]));
|
|
48
50
|
|
|
51
|
+
/** Levels-of-detail equality: the same distances and hysteresis. */
|
|
52
|
+
export const detailEquals = (a: Detail | null, b: Detail | null): boolean => {
|
|
53
|
+
if (a === b) return true;
|
|
54
|
+
if (a === null || b === null) return false;
|
|
55
|
+
return (
|
|
56
|
+
Object.is(a.hysteresis, b.hysteresis) &&
|
|
57
|
+
a.distances.length === b.distances.length &&
|
|
58
|
+
a.distances.every((distance, index) => Object.is(distance, b.distances[index]))
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
49
62
|
/** Rotation equality, including behaviour parameters. */
|
|
50
63
|
export const rotationEquals = (a: Rotation, b: Rotation): boolean => {
|
|
51
64
|
if (a === b) return true;
|
|
@@ -57,6 +70,9 @@ export const rotationEquals = (a: Rotation, b: Rotation): boolean => {
|
|
|
57
70
|
Object.is(a.phase, b.phase)
|
|
58
71
|
);
|
|
59
72
|
}
|
|
73
|
+
if (a._tag === 'Billboard' && b._tag === 'Billboard') {
|
|
74
|
+
return a.lockX === b.lockX && a.lockY === b.lockY && a.lockZ === b.lockZ;
|
|
75
|
+
}
|
|
60
76
|
return false;
|
|
61
77
|
};
|
|
62
78
|
|
|
@@ -120,6 +136,20 @@ export const shapeEquals = (a: ShapeGeometry, b: ShapeGeometry): boolean => {
|
|
|
120
136
|
);
|
|
121
137
|
case 'Circle':
|
|
122
138
|
return b._tag === 'Circle' && Object.is(a.radius, b.radius) && a.segments === b.segments;
|
|
139
|
+
case 'RoundedBox':
|
|
140
|
+
return (
|
|
141
|
+
b._tag === 'RoundedBox' &&
|
|
142
|
+
Object.is(a.width, b.width) &&
|
|
143
|
+
Object.is(a.height, b.height) &&
|
|
144
|
+
Object.is(a.depth, b.depth) &&
|
|
145
|
+
Object.is(a.radius, b.radius) &&
|
|
146
|
+
a.smoothness === b.smoothness &&
|
|
147
|
+
a.bevelSegments === b.bevelSegments &&
|
|
148
|
+
a.steps === b.steps &&
|
|
149
|
+
Object.is(a.creaseAngle, b.creaseAngle)
|
|
150
|
+
);
|
|
151
|
+
case 'Backdrop':
|
|
152
|
+
return b._tag === 'Backdrop' && Object.is(a.floor, b.floor) && a.segments === b.segments;
|
|
123
153
|
case 'Text':
|
|
124
154
|
return (
|
|
125
155
|
b._tag === 'Text' &&
|
|
@@ -223,6 +253,22 @@ export const materialRefEquals = (a: MaterialRef, b: MaterialRef): boolean => {
|
|
|
223
253
|
};
|
|
224
254
|
|
|
225
255
|
/** Camera equality: same kind, same lens and same placement. */
|
|
256
|
+
/** Camera shake equality: every setting, and the trigger count. */
|
|
257
|
+
export const cameraShakeEquals = (a: CameraShake | null, b: CameraShake | null): boolean =>
|
|
258
|
+
a === b ||
|
|
259
|
+
(a !== null &&
|
|
260
|
+
b !== null &&
|
|
261
|
+
Object.is(a.intensity, b.intensity) &&
|
|
262
|
+
a.decay === b.decay &&
|
|
263
|
+
Object.is(a.decayRate, b.decayRate) &&
|
|
264
|
+
Object.is(a.maxYaw, b.maxYaw) &&
|
|
265
|
+
Object.is(a.maxPitch, b.maxPitch) &&
|
|
266
|
+
Object.is(a.maxRoll, b.maxRoll) &&
|
|
267
|
+
Object.is(a.yawFrequency, b.yawFrequency) &&
|
|
268
|
+
Object.is(a.pitchFrequency, b.pitchFrequency) &&
|
|
269
|
+
Object.is(a.rollFrequency, b.rollFrequency) &&
|
|
270
|
+
Object.is(a.trigger, b.trigger));
|
|
271
|
+
|
|
226
272
|
export const cameraEquals = (a: Camera, b: Camera): boolean => {
|
|
227
273
|
if (a === b) return true;
|
|
228
274
|
const sameLens =
|
|
@@ -234,6 +280,7 @@ export const cameraEquals = (a: Camera, b: Camera): boolean => {
|
|
|
234
280
|
Object.is(a.far, b.far) &&
|
|
235
281
|
Object.is(a.damping, b.damping) &&
|
|
236
282
|
Object.is(a.autoRotate, b.autoRotate) &&
|
|
283
|
+
cameraShakeEquals(a.shake, b.shake) &&
|
|
237
284
|
vec3Equals(a.position, b.position) &&
|
|
238
285
|
vec3Equals(a.lookAt, b.lookAt)
|
|
239
286
|
);
|
package/src/kernel/index.ts
CHANGED
|
@@ -96,12 +96,17 @@ export {
|
|
|
96
96
|
pointLight,
|
|
97
97
|
renderSettings,
|
|
98
98
|
ring,
|
|
99
|
+
roundedBox,
|
|
100
|
+
backdrop,
|
|
99
101
|
scene,
|
|
100
102
|
sharedGeometry,
|
|
101
103
|
lentGeometry,
|
|
102
104
|
sharedMaterial,
|
|
103
105
|
sphere,
|
|
104
106
|
spin,
|
|
107
|
+
cameraShake,
|
|
108
|
+
detailed,
|
|
109
|
+
billboard,
|
|
105
110
|
spotLight,
|
|
106
111
|
standard,
|
|
107
112
|
overlay,
|
package/src/kernel/labels.ts
CHANGED
|
@@ -44,5 +44,11 @@ export type HostedLabel = {
|
|
|
44
44
|
* and glyphs build in the background), so the scene can draw again.
|
|
45
45
|
*/
|
|
46
46
|
export type LabelEngine = {
|
|
47
|
+
/** A label drawn with WebGL's own materials, for a `'webgl'` render kit. */
|
|
47
48
|
readonly create: (label: LabelNode, ready: () => void) => HostedLabel;
|
|
49
|
+
/**
|
|
50
|
+
* A label drawn with three's node materials (TSL), for a `'tsl'` render kit such as
|
|
51
|
+
* `WebGPURenderer`'s. Without it, those renderers draw no labels.
|
|
52
|
+
*/
|
|
53
|
+
readonly createTsl?: (label: LabelNode, ready: () => void) => HostedLabel;
|
|
48
54
|
};
|