@viggle/splat-engine-three 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +450 -2
- package/dist/chunks/chunk-WN5WBKM4.js +8606 -0
- package/dist/chunks/chunk-WN5WBKM4.js.map +7 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +7 -0
- package/dist/types/anim/animation-conversion.d.ts +25 -0
- package/dist/types/anim/bind-pose.d.ts +15 -0
- package/dist/types/anim/keyframe-splines.d.ts +59 -0
- package/dist/types/anim/rest-relative.d.ts +32 -0
- package/dist/types/anim/spline.d.ts +14 -0
- package/dist/types/animation.d.ts +216 -0
- package/dist/types/armature.d.ts +524 -0
- package/dist/types/character.d.ts +582 -0
- package/dist/types/engine.d.ts +166 -0
- package/dist/types/environment.d.ts +53 -0
- package/dist/types/index.d.ts +40 -0
- package/dist/types/lighting/color.d.ts +13 -0
- package/dist/types/lighting/point-light.d.ts +33 -0
- package/dist/types/lighting/sky-light.d.ts +22 -0
- package/dist/types/lighting/sun-light.d.ts +34 -0
- package/dist/types/loader/animation/animation-glb.d.ts +117 -0
- package/dist/types/loader/character/index.d.ts +33 -0
- package/dist/types/loader/character/legacy/index.d.ts +34 -0
- package/dist/types/loader/character/legacy/interleaved-splat.d.ts +101 -0
- package/dist/types/loader/character/legacy/planar-splat.d.ts +139 -0
- package/dist/types/loader/character/load.d.ts +33 -0
- package/dist/types/loader/character/types.d.ts +129 -0
- package/dist/types/loader/character/v1/decode.d.ts +32 -0
- package/dist/types/loader/environment/ply.d.ts +16 -0
- package/dist/types/loader/shared/bin-cache.d.ts +19 -0
- package/dist/types/loader/skeleton/index.d.ts +19 -0
- package/dist/types/loader/skeleton/skeleton-data-441.d.ts +5 -0
- package/dist/types/loader/skeleton/skeleton-data.d.ts +6 -0
- package/dist/types/math.d.ts +82 -0
- package/dist/types/skeletal/animation-player.d.ts +185 -0
- package/dist/types/skeletal/bone-mask.d.ts +27 -0
- package/dist/types/skeletal/bone-stretch.d.ts +65 -0
- package/dist/types/skeletal/pose.d.ts +27 -0
- package/dist/types/skeletal/skeleton.d.ts +57 -0
- package/dist/types/skeletal/splat-skinning.d.ts +69 -0
- package/dist/types/splat/backend.d.ts +57 -0
- package/dist/types/splat/bone-binding.d.ts +38 -0
- package/dist/types/splat/depth-sort.d.ts +76 -0
- package/dist/types/splat/importance.d.ts +60 -0
- package/dist/types/splat/packed-splats.d.ts +36 -0
- package/dist/types/splat/prune.d.ts +60 -0
- package/dist/types/splat/quality.d.ts +175 -0
- package/dist/types/splat/splat-mesh.d.ts +71 -0
- package/dist/types/splat/splat.d.ts +252 -0
- package/dist/types/splat/transform-palette.d.ts +42 -0
- package/dist/types/splat/webgl/backend.d.ts +17 -0
- package/dist/types/splat/webgl/shaders.d.ts +2 -0
- package/dist/types/splat/webgl/splat-mesh.d.ts +54 -0
- package/dist/types/splat/webgpu/backend.d.ts +25 -0
- package/dist/types/splat/webgpu/lighting.d.ts +34 -0
- package/dist/types/splat/webgpu/radix-sort.d.ts +46 -0
- package/dist/types/splat/webgpu/splat-mesh.d.ts +73 -0
- package/dist/types/three/loaders.d.ts +77 -0
- package/dist/types/three/splat-character.d.ts +220 -0
- package/dist/types/types.d.ts +30 -0
- package/dist/types/webgpu.d.ts +5 -0
- package/dist/webgpu.js +843 -0
- package/dist/webgpu.js.map +7 -0
- package/package.json +46 -4
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SplatEngine } from '../../engine';
|
|
2
|
+
import type { Mesh } from 'three/webgpu';
|
|
3
|
+
import type { SplatBackend, SplatRenderImpl } from '../backend';
|
|
4
|
+
import type { Splat } from '../splat';
|
|
5
|
+
/**
|
|
6
|
+
* Splat rendering for `WebGPURenderer` (`three/webgpu`), running on WebGPU.
|
|
7
|
+
*
|
|
8
|
+
* Skinning, projection and the depth sort all run in compute shaders, so the
|
|
9
|
+
* CPU does no per-splat work: adding an animated character costs a pose
|
|
10
|
+
* evaluation and a palette upload, not a pass over its splats.
|
|
11
|
+
*
|
|
12
|
+
* Importing `@viggle/splat-engine-three/webgpu` registers this backend, and
|
|
13
|
+
* splats then use it whenever a `WebGPURenderer` draws them; there is nothing
|
|
14
|
+
* to construct or pass.
|
|
15
|
+
*
|
|
16
|
+
* Requires the renderer's WebGPU backend. `WebGPURenderer`'s WebGL2 fallback
|
|
17
|
+
* (`forceWebGL`, or a browser without WebGPU) has no compute shaders; use
|
|
18
|
+
* `THREE.WebGLRenderer` there instead.
|
|
19
|
+
*
|
|
20
|
+
* @group Engine
|
|
21
|
+
*/
|
|
22
|
+
export declare class WebGPUSplatBackend implements SplatBackend {
|
|
23
|
+
private readonly _lighting;
|
|
24
|
+
createImpl(host: Mesh, splat: Splat, engine: SplatEngine): SplatRenderImpl;
|
|
25
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SplatEngine } from '../../engine';
|
|
2
|
+
type Node = any;
|
|
3
|
+
/** Point lights the WebGPU backend evaluates per splat. Extra lights are ignored. */
|
|
4
|
+
export declare const MAX_POINT_LIGHTS = 16;
|
|
5
|
+
/**
|
|
6
|
+
* The engine's lighting registry as TSL uniforms, shared by every WebGPU splat
|
|
7
|
+
* drawable of one engine.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export declare class WebGPULighting {
|
|
12
|
+
private readonly _engine;
|
|
13
|
+
private readonly _sunColor;
|
|
14
|
+
private readonly _sunIntensity;
|
|
15
|
+
private readonly _ambient;
|
|
16
|
+
private readonly _pointCount;
|
|
17
|
+
private readonly _pointPosRange;
|
|
18
|
+
private readonly _pointColor;
|
|
19
|
+
private readonly _pointPosRangeNode;
|
|
20
|
+
private readonly _pointColorNode;
|
|
21
|
+
private _version;
|
|
22
|
+
constructor(engine: SplatEngine);
|
|
23
|
+
/**
|
|
24
|
+
* Refresh the uniforms from the engine's lighting registry.
|
|
25
|
+
* @returns A number that changes whenever the lighting does.
|
|
26
|
+
*/
|
|
27
|
+
sync(): number;
|
|
28
|
+
/**
|
|
29
|
+
* The multiplier applied to a splat's baked colour: ambient, plus sun, plus
|
|
30
|
+
* range-attenuated point lights. With no lights it is exactly 1.
|
|
31
|
+
*/
|
|
32
|
+
litFactor(worldCenter: Node): Node;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
type Node = any;
|
|
2
|
+
/** Sort keys are 16 bits, sorted as two 8-bit digits. */
|
|
3
|
+
export declare const SORT_KEY_BITS = 16;
|
|
4
|
+
/**
|
|
5
|
+
* A stable least-significant-digit radix sort over 16-bit keys, in compute.
|
|
6
|
+
*
|
|
7
|
+
* Stability is the point. The obvious GPU counting sort - histogram, prefix
|
|
8
|
+
* sum, then every element claiming its slot with an atomic add - leaves the
|
|
9
|
+
* order inside a bucket to whichever thread wins the race, and that changes
|
|
10
|
+
* from frame to frame. For splats sharing a depth bucket (a surface seen
|
|
11
|
+
* face-on is full of them) the result is visible shimmer on a perfectly still
|
|
12
|
+
* frame. Here ties always resolve in element-index order, exactly as a CPU
|
|
13
|
+
* counting sort does, so a still frame is bit-stable.
|
|
14
|
+
*
|
|
15
|
+
* Each digit takes three steps:
|
|
16
|
+
*
|
|
17
|
+
* histogram count digits per block of {@link BLOCK} consecutive elements
|
|
18
|
+
* (atomic adds; counts do not depend on thread order).
|
|
19
|
+
* prefix for each digit, in parallel, walk the blocks turning counts
|
|
20
|
+
* into starting offsets; then one thread turns the 256 digit
|
|
21
|
+
* totals into digit bases.
|
|
22
|
+
* scatter one invocation per block walks its elements in order, placing
|
|
23
|
+
* each at its digit's running offset.
|
|
24
|
+
*
|
|
25
|
+
* The first digit's histogram is folded into the caller's own per-element
|
|
26
|
+
* pass through {@link GpuRadixSort.countFirstDigit}, saving a dispatch.
|
|
27
|
+
*/
|
|
28
|
+
export declare class GpuRadixSort {
|
|
29
|
+
readonly count: number;
|
|
30
|
+
/** Write each element's key here (`uint`, low 16 bits significant) before {@link GpuRadixSort.sortNodes} run. */
|
|
31
|
+
readonly keysWrite: Node;
|
|
32
|
+
/** Element indices in ascending key order, valid after {@link GpuRadixSort.sortNodes} have run. */
|
|
33
|
+
readonly orderRead: Node;
|
|
34
|
+
/** Run before the pass that writes keys: clears both digit histograms. */
|
|
35
|
+
readonly resetNode: Node;
|
|
36
|
+
/** Run, in order, after the pass that writes keys. */
|
|
37
|
+
readonly sortNodes: Node[];
|
|
38
|
+
private readonly _firstHistogram;
|
|
39
|
+
constructor(count: number);
|
|
40
|
+
/**
|
|
41
|
+
* Count `key`'s low digit for the element at `index`. Call from inside the
|
|
42
|
+
* compute pass that writes the keys.
|
|
43
|
+
*/
|
|
44
|
+
countFirstDigit(index: Node, key: Node): void;
|
|
45
|
+
}
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { InstancedBufferGeometry, NodeMaterial } from 'three/webgpu';
|
|
2
|
+
import type { Camera, Mesh } from 'three/webgpu';
|
|
3
|
+
import type { SplatRenderImpl } from '../backend';
|
|
4
|
+
import type { Splat } from '../splat';
|
|
5
|
+
import type { WebGPULighting } from './lighting';
|
|
6
|
+
/** Groups a variant mask can address. The mask travels as a uniform array of this size. */
|
|
7
|
+
export declare const MAX_VARIANT_GROUPS = 64;
|
|
8
|
+
/**
|
|
9
|
+
* How one {@link Splat} is drawn under `WebGPURenderer`.
|
|
10
|
+
*
|
|
11
|
+
* Everything per-splat happens in compute, once per splat per frame:
|
|
12
|
+
*
|
|
13
|
+
* 1. project - skin, lift to world space, project the covariance to a
|
|
14
|
+
* screen-space ellipse, shade; write the result to a work
|
|
15
|
+
* buffer along with a 16-bit depth key.
|
|
16
|
+
* 2. sort - a stable radix sort of the keys ({@link GpuRadixSort}),
|
|
17
|
+
* giving splat indices in back-to-front order.
|
|
18
|
+
*
|
|
19
|
+
* Buffers are interleaved so no stage binds more than seven storage buffers:
|
|
20
|
+
* WebGPU only guarantees eight per stage, and some browsers stop there.
|
|
21
|
+
*
|
|
22
|
+
* The vertex stage then only expands each work-buffer entry into a quad. No
|
|
23
|
+
* per-splat work happens on the CPU, which is what lets this backend carry
|
|
24
|
+
* many animated characters: the WebGL backend re-skins every centre on the
|
|
25
|
+
* main thread each frame just to feed its sort.
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
export declare class WebGPUSplatImpl implements SplatRenderImpl {
|
|
30
|
+
readonly geometry: InstancedBufferGeometry;
|
|
31
|
+
readonly material: NodeMaterial;
|
|
32
|
+
/** Skinning for the sort happens in compute; the CPU never touches a splat. */
|
|
33
|
+
readonly needsCpuCenters = false;
|
|
34
|
+
private readonly _host;
|
|
35
|
+
private readonly _splat;
|
|
36
|
+
private readonly _lighting;
|
|
37
|
+
private _paletteArray;
|
|
38
|
+
private _paletteAttribute;
|
|
39
|
+
private _paletteVersion;
|
|
40
|
+
private _boneAttribute;
|
|
41
|
+
private _groupAttribute;
|
|
42
|
+
private readonly _variantMask;
|
|
43
|
+
/** [sort reset, project] - everything that depends on the camera or the pose. */
|
|
44
|
+
private _projectNodes;
|
|
45
|
+
/** The sort proper - needed only when the depth ORDER may have changed. */
|
|
46
|
+
private _sortNodes;
|
|
47
|
+
private readonly _lastInputs;
|
|
48
|
+
private readonly _inputs;
|
|
49
|
+
private readonly _lastSortDirection;
|
|
50
|
+
private _buildId;
|
|
51
|
+
private _sortedBuildId;
|
|
52
|
+
private _allNodes;
|
|
53
|
+
private readonly _u;
|
|
54
|
+
constructor(host: Mesh, splat: Splat, lighting: WebGPULighting);
|
|
55
|
+
/** The sort runs in the same frame as the draw; the order is never stale. */
|
|
56
|
+
get sortPending(): boolean;
|
|
57
|
+
/** Force the next frame to re-project and re-sort even if nothing appears to have changed. */
|
|
58
|
+
requestSort(): void;
|
|
59
|
+
syncDefines(): void;
|
|
60
|
+
syncBounds(): void;
|
|
61
|
+
syncBoneData(): void;
|
|
62
|
+
syncVariantGroups(): void;
|
|
63
|
+
/**
|
|
64
|
+
* (Re)create the storage buffers, compute nodes and material nodes. Runs at
|
|
65
|
+
* construction, and again when a feature toggle changes the shader or the
|
|
66
|
+
* palette outgrows its buffer.
|
|
67
|
+
*/
|
|
68
|
+
private _build;
|
|
69
|
+
beforeRender(renderer: any, camera: Camera): void;
|
|
70
|
+
/** Refresh this splat's uniforms and run whatever compute this frame needs. */
|
|
71
|
+
private _prepare;
|
|
72
|
+
dispose(): void;
|
|
73
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Loader } from 'three';
|
|
2
|
+
import type { LoadingManager } from 'three';
|
|
3
|
+
import { Animation } from '../animation';
|
|
4
|
+
import { SplatEngine } from '../engine';
|
|
5
|
+
import type { SplatMesh } from '../splat/splat-mesh';
|
|
6
|
+
import { SplatCharacter } from './splat-character';
|
|
7
|
+
/**
|
|
8
|
+
* Shared plumbing: fetch bytes the three.js way - through a `FileLoader`, so
|
|
9
|
+
* the `LoadingManager`, base path, request headers and credentials mode all
|
|
10
|
+
* apply - then hand them to an asynchronous `parseAsync`.
|
|
11
|
+
*/
|
|
12
|
+
declare abstract class SplatLoaderBase<T> extends Loader<T> {
|
|
13
|
+
/**
|
|
14
|
+
* The engine loaded objects belong to: the skeleton library, clip registry
|
|
15
|
+
* and lights they share. Leave unset to use {@link SplatEngine.shared}.
|
|
16
|
+
*/
|
|
17
|
+
engine: SplatEngine | null;
|
|
18
|
+
constructor(manager?: LoadingManager);
|
|
19
|
+
setEngine(engine: SplatEngine | null): this;
|
|
20
|
+
/** Decode bytes already in memory. `name` labels the result; for a loaded file it is the file's base name. */
|
|
21
|
+
abstract parseAsync(data: ArrayBuffer, name?: string): Promise<T>;
|
|
22
|
+
load(url: string, onLoad: (result: T) => void, onProgress?: (event: ProgressEvent) => void, onError?: (error: unknown) => void): void;
|
|
23
|
+
protected get resolvedEngine(): SplatEngine;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Loads a `.vsplat` character bundle (or a legacy `.bin` one - the container
|
|
27
|
+
* is recognised from the file's own bytes) as a {@link SplatCharacter}.
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* const character = await new VSplatLoader().loadAsync('/assets/hero.vsplat');
|
|
31
|
+
* scene.add(character);
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @group Loaders
|
|
35
|
+
*/
|
|
36
|
+
export declare class VSplatLoader extends SplatLoaderBase<SplatCharacter> {
|
|
37
|
+
parseAsync(data: ArrayBuffer, name?: string): Promise<SplatCharacter>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Loads an animation clip for {@link SplatCharacter.play}: a MetaHuman-rig
|
|
41
|
+
* `.glb`, or an `.anim` file.
|
|
42
|
+
*
|
|
43
|
+
* ```ts
|
|
44
|
+
* const run = await new SplatClipLoader().loadAsync('/assets/run.glb');
|
|
45
|
+
* character.play(run);
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* A clip is not tied to one character; play the same one on as many as you like.
|
|
49
|
+
*
|
|
50
|
+
* @group Loaders
|
|
51
|
+
*/
|
|
52
|
+
export declare class SplatClipLoader extends SplatLoaderBase<Animation> {
|
|
53
|
+
/** For a `.glb` holding several animations, the one to take. Default: the first. */
|
|
54
|
+
animationName: string | undefined;
|
|
55
|
+
parseAsync(data: ArrayBuffer, name?: string): Promise<Animation>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Loads an uncompressed 3D Gaussian Splatting `.ply` as a static
|
|
59
|
+
* {@link SplatMesh} - an environment, a prop, a scanned object.
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* const room = await new SplatPLYLoader().loadAsync('/assets/room.ply');
|
|
63
|
+
* room.rotation.x = Math.PI; // many captures are authored Y-down
|
|
64
|
+
* scene.add(room);
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* The mesh is given `renderOrder = -1` so it draws before characters, which
|
|
68
|
+
* then always composite on top of it. Splats do not write depth, so without
|
|
69
|
+
* that a large environment whose origin happens to be nearer the camera would
|
|
70
|
+
* be drawn over a character standing in it.
|
|
71
|
+
*
|
|
72
|
+
* @group Loaders
|
|
73
|
+
*/
|
|
74
|
+
export declare class SplatPLYLoader extends SplatLoaderBase<SplatMesh> {
|
|
75
|
+
parseAsync(data: ArrayBuffer, name?: string): Promise<SplatMesh>;
|
|
76
|
+
}
|
|
77
|
+
export {};
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Matrix4, Object3D, Vector3 } from 'three';
|
|
2
|
+
import type { ColorRepresentation, Object3DEventMap } from 'three';
|
|
3
|
+
import type { Animation } from '../animation';
|
|
4
|
+
import type { Armature } from '../armature';
|
|
5
|
+
import { Character } from '../character';
|
|
6
|
+
import type { SplatEngine } from '../engine';
|
|
7
|
+
import type { CharacterBundle } from '../loader/character/load';
|
|
8
|
+
import type { BodyRegion, BoneMask } from '../skeletal/bone-mask';
|
|
9
|
+
import type { Splat } from '../splat/splat';
|
|
10
|
+
/**
|
|
11
|
+
* What a clip's horizontal travel does to the character.
|
|
12
|
+
*
|
|
13
|
+
* - `'off'` - the clip plays in place.
|
|
14
|
+
* - `'apply'` - the character's body moves with the clip, beneath
|
|
15
|
+
* {@link SplatCharacter.position}, which stays where you put it.
|
|
16
|
+
* - `'extract'` - the clip plays in place and its travel is handed to you by
|
|
17
|
+
* {@link SplatCharacter.consumeRootMotion}, to feed a controller or physics.
|
|
18
|
+
*
|
|
19
|
+
* @group Characters & Animation
|
|
20
|
+
*/
|
|
21
|
+
export type SplatRootMotion = 'off' | 'apply' | 'extract';
|
|
22
|
+
/** @group Characters & Animation */
|
|
23
|
+
export interface SplatPlayOptions {
|
|
24
|
+
/** Repeat when the clip ends. Default `true`. */
|
|
25
|
+
loop?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** @group Characters & Animation */
|
|
28
|
+
export interface SplatCrossFadeOptions extends SplatPlayOptions {
|
|
29
|
+
/** Start the new clip at the same fraction of its length the old one had reached. Default `false`. */
|
|
30
|
+
preserveTime?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** @group Characters & Animation */
|
|
33
|
+
export interface SplatLayerOptions {
|
|
34
|
+
/** The bones the layer drives, as a named region. Give this or `mask`. */
|
|
35
|
+
region?: BodyRegion;
|
|
36
|
+
/** The bones the layer drives, as an explicit mask. Give this or `region`. */
|
|
37
|
+
mask?: BoneMask;
|
|
38
|
+
/** Repeat when the clip ends. Default `true`. */
|
|
39
|
+
loop?: boolean;
|
|
40
|
+
/** Seconds to blend the layer in. Default `0`. */
|
|
41
|
+
fadeDuration?: number;
|
|
42
|
+
/** How strongly the layer overrides the base pose, 0..1. Default `1`. */
|
|
43
|
+
weight?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Events a {@link SplatCharacter} dispatches, alongside the usual `Object3D` ones.
|
|
47
|
+
* @group Characters & Animation
|
|
48
|
+
*/
|
|
49
|
+
export interface SplatCharacterEventMap extends Object3DEventMap {
|
|
50
|
+
/** A non-looping clip reached its last frame. */
|
|
51
|
+
finished: {
|
|
52
|
+
clip: Animation;
|
|
53
|
+
};
|
|
54
|
+
/** A looping clip wrapped around to its start. */
|
|
55
|
+
loop: {
|
|
56
|
+
clip: Animation;
|
|
57
|
+
};
|
|
58
|
+
/** An event authored into the clip was crossed. */
|
|
59
|
+
clipevent: {
|
|
60
|
+
clip: Animation | null;
|
|
61
|
+
name: string;
|
|
62
|
+
frame: number;
|
|
63
|
+
payload?: unknown;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A gaussian-splat character, as a three.js object.
|
|
68
|
+
*
|
|
69
|
+
* Add it to a scene, move it with `position` / `rotation` / `scale`, and call
|
|
70
|
+
* {@link SplatCharacter.update} once per frame - the role `AnimationMixer.update`
|
|
71
|
+
* plays for a skinned mesh. Create one with {@link VSplatLoader}.
|
|
72
|
+
*
|
|
73
|
+
* ```ts
|
|
74
|
+
* const character = await new VSplatLoader().loadAsync('/assets/hero.vsplat');
|
|
75
|
+
* scene.add(character);
|
|
76
|
+
* character.position.set(1, 0, 0);
|
|
77
|
+
*
|
|
78
|
+
* const run = await new SplatClipLoader().loadAsync('/assets/run.glb');
|
|
79
|
+
* character.play(run);
|
|
80
|
+
*
|
|
81
|
+
* renderer.setAnimationLoop(() => {
|
|
82
|
+
* character.update(clock.getDelta());
|
|
83
|
+
* renderer.render(scene, camera);
|
|
84
|
+
* });
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* `position` is the model origin. A character in its rest pose stands on it;
|
|
88
|
+
* how closely a clip keeps the feet on it depends on the clip.
|
|
89
|
+
*
|
|
90
|
+
* Animation is driven by this class rather than by `AnimationMixer`: a splat
|
|
91
|
+
* character has no `Bone` hierarchy for a mixer to write to. Clips are
|
|
92
|
+
* {@link Animation} objects from {@link SplatClipLoader}.
|
|
93
|
+
*
|
|
94
|
+
* @group Characters & Animation
|
|
95
|
+
*/
|
|
96
|
+
export declare class SplatCharacter extends Object3D<SplatCharacterEventMap> {
|
|
97
|
+
/** Type flag, in the three.js manner. */
|
|
98
|
+
readonly isSplatCharacter = true;
|
|
99
|
+
readonly type: string;
|
|
100
|
+
/**
|
|
101
|
+
* The engine-level character this object wraps. Everything not surfaced
|
|
102
|
+
* here - multi-level LOD, variant splats, animation LOD - is reachable
|
|
103
|
+
* through it.
|
|
104
|
+
*/
|
|
105
|
+
readonly character: Character;
|
|
106
|
+
private readonly _engine;
|
|
107
|
+
private readonly _bundles;
|
|
108
|
+
private _clip;
|
|
109
|
+
private _loop;
|
|
110
|
+
private _finishedSent;
|
|
111
|
+
private _rootMotion;
|
|
112
|
+
/** Optional world-space floor height. When set, the lowest visible splats
|
|
113
|
+
* are aligned to it after each pose. Useful for dances with no airborne
|
|
114
|
+
* phase; leave null for clips with intentional jumps or flight. */
|
|
115
|
+
groundLockY: number | null;
|
|
116
|
+
private _disposed;
|
|
117
|
+
/**
|
|
118
|
+
* @param bundles - Decoded character bundles, finest level of detail first.
|
|
119
|
+
* They are read, never modified, so clones share them.
|
|
120
|
+
* @internal Use {@link VSplatLoader}.
|
|
121
|
+
*/
|
|
122
|
+
constructor(engine: SplatEngine, bundles: CharacterBundle[], name?: string);
|
|
123
|
+
/** The rig: bone names, bone matrices, bone-length scaling. */
|
|
124
|
+
get armature(): Armature;
|
|
125
|
+
/** The splats that draw this character, one per level of detail. */
|
|
126
|
+
get splats(): readonly Splat[];
|
|
127
|
+
/**
|
|
128
|
+
* Advance the animation by `delta` seconds and pose the splats. Call once
|
|
129
|
+
* per frame, before rendering.
|
|
130
|
+
*/
|
|
131
|
+
update(delta: number): void;
|
|
132
|
+
/** Start `clip` from its first frame, cutting from whatever was playing. */
|
|
133
|
+
play(clip: Animation, options?: SplatPlayOptions): this;
|
|
134
|
+
/** Blend from the current pose into `clip` over `duration` seconds. */
|
|
135
|
+
crossFadeTo(clip: Animation, duration?: number, options?: SplatCrossFadeOptions): this;
|
|
136
|
+
/** Stop playback and return to the rest pose, blending over `fadeDuration` seconds. */
|
|
137
|
+
stop(fadeDuration?: number): this;
|
|
138
|
+
/**
|
|
139
|
+
* Play `clip` on a named layer that overrides part of the body - an upper
|
|
140
|
+
* body wave over a full-body run, say. Calling it again with the same layer
|
|
141
|
+
* name replaces what that layer plays.
|
|
142
|
+
*/
|
|
143
|
+
playLayer(layer: string, clip: Animation, options: SplatLayerOptions): this;
|
|
144
|
+
setLayerWeight(layer: string, weight: number): this;
|
|
145
|
+
stopLayer(layer: string, fadeDuration?: number): this;
|
|
146
|
+
/** The clip the base track is playing, or `null`. */
|
|
147
|
+
get clip(): Animation | null;
|
|
148
|
+
get isPlaying(): boolean;
|
|
149
|
+
get paused(): boolean;
|
|
150
|
+
set paused(value: boolean);
|
|
151
|
+
/** Playback speed multiplier. `1` is the authored speed. */
|
|
152
|
+
get timeScale(): number;
|
|
153
|
+
set timeScale(value: number);
|
|
154
|
+
/** Position in the current clip, in seconds. */
|
|
155
|
+
get time(): number;
|
|
156
|
+
set time(seconds: number);
|
|
157
|
+
/** Length of the current clip in seconds, or `0` when nothing is playing. */
|
|
158
|
+
get duration(): number;
|
|
159
|
+
/** See {@link SplatRootMotion}. Applies to the current clip and every later one. */
|
|
160
|
+
get rootMotion(): SplatRootMotion;
|
|
161
|
+
set rootMotion(mode: SplatRootMotion);
|
|
162
|
+
/**
|
|
163
|
+
* The clip's horizontal travel since the last call, in this object's local
|
|
164
|
+
* space. Non-zero only in `'extract'` mode. `y` is always `0`.
|
|
165
|
+
*/
|
|
166
|
+
consumeRootMotion(target?: Vector3): Vector3;
|
|
167
|
+
/**
|
|
168
|
+
* Blend the character's colour toward `color` by `amount` (0..1) - a hit
|
|
169
|
+
* flash, a selection highlight. `amount = 0` restores the authored colour.
|
|
170
|
+
*/
|
|
171
|
+
setTint(color: ColorRepresentation, amount?: number): this;
|
|
172
|
+
/** Contrast and saturation multipliers for the character's colour. `(1, 1)` is unchanged. */
|
|
173
|
+
setGrade(contrast: number, saturation: number): this;
|
|
174
|
+
/**
|
|
175
|
+
* Draw fewer of this character's splats as it gets further away.
|
|
176
|
+
*
|
|
177
|
+
* `nearFraction` of them at `nearDist` and closer, `farFraction` at
|
|
178
|
+
* `farDist` and beyond, interpolated in between. Which splats those are is
|
|
179
|
+
* decided by importance within each 4 cm cell, so the character thins evenly
|
|
180
|
+
* rather than losing a region of itself. See {@link Character.setSplatCountLod}.
|
|
181
|
+
*
|
|
182
|
+
* Needs a camera on the engine to measure distance from - the same one the
|
|
183
|
+
* other distance throttles use:
|
|
184
|
+
*
|
|
185
|
+
* ```ts
|
|
186
|
+
* SplatEngine.shared.camera = camera;
|
|
187
|
+
* character.setSplatCountLod({ nearDist: 4, farDist: 16, farFraction: 0.25 });
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
setSplatCountLod(opts: {
|
|
191
|
+
nearDist: number;
|
|
192
|
+
farDist: number;
|
|
193
|
+
nearFraction?: number;
|
|
194
|
+
farFraction?: number;
|
|
195
|
+
}): this;
|
|
196
|
+
/** Stop thinning: every splat is drawn again, at any distance. */
|
|
197
|
+
disableSplatCountLod(): this;
|
|
198
|
+
/** Bone names, in rig order. */
|
|
199
|
+
get boneNames(): readonly string[];
|
|
200
|
+
/**
|
|
201
|
+
* World matrix of a bone as of the last {@link SplatCharacter.update} - for
|
|
202
|
+
* attaching a prop to a hand, or aiming a camera at a head.
|
|
203
|
+
*
|
|
204
|
+
* @returns `target`, or `null` when the rig has no bone of that name.
|
|
205
|
+
*/
|
|
206
|
+
getBoneWorldMatrix(name: string, target?: Matrix4): Matrix4 | null;
|
|
207
|
+
/**
|
|
208
|
+
* A second character from the same decoded data, at the same transform. The
|
|
209
|
+
* copy starts in the rest pose with nothing playing; it does not share this
|
|
210
|
+
* one's animation state.
|
|
211
|
+
*/
|
|
212
|
+
clone(): this;
|
|
213
|
+
/**
|
|
214
|
+
* Release every GPU resource the character holds. The object stays in the
|
|
215
|
+
* scene graph, empty; remove it yourself if you no longer want it there.
|
|
216
|
+
*/
|
|
217
|
+
dispose(): void;
|
|
218
|
+
private _keyFor;
|
|
219
|
+
private _started;
|
|
220
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared data shapes used across the loader, skeletal and splat layers.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Rig description handed to {@link Armature}.
|
|
6
|
+
*
|
|
7
|
+
* The `stdMale*` field names are inherited from the skeleton library and mean
|
|
8
|
+
* "the rest pose this armature binds to", whichever rig that is.
|
|
9
|
+
*/
|
|
10
|
+
export interface ArmatureData {
|
|
11
|
+
numBones: number;
|
|
12
|
+
/** Flat `[x,y,z] * numBones` rest translations carried by the character bundle. */
|
|
13
|
+
joints?: Float32Array;
|
|
14
|
+
/** Whether the bundle's geometric corrections are baked into its splats and joints. */
|
|
15
|
+
corrected?: boolean;
|
|
16
|
+
/** Whether `joints` is this character's own vetted rest pose. */
|
|
17
|
+
restIsPerCharacter?: boolean;
|
|
18
|
+
skeleton?: Int32Array;
|
|
19
|
+
stdMaleRestTranslations?: Float32Array;
|
|
20
|
+
stdMaleRestRotations?: Float32Array;
|
|
21
|
+
stdMaleParents?: Int32Array;
|
|
22
|
+
}
|
|
23
|
+
/** Per-splat skinning influences, `K` slots per splat. */
|
|
24
|
+
export interface SplatWeights {
|
|
25
|
+
/** `numSplats * K` rig bone indices. */
|
|
26
|
+
indices: Uint16Array;
|
|
27
|
+
/** `numSplats * K` weights, matching `indices`. */
|
|
28
|
+
weights: Float32Array;
|
|
29
|
+
}
|
|
30
|
+
export type { SkeletonLibrary } from './loader/skeleton';
|