@viggle/splat-engine-three 0.0.0-stage → 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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +290 -2
  3. package/dist/chunks/chunk-MM6DJU6S.js +7801 -0
  4. package/dist/chunks/chunk-MM6DJU6S.js.map +7 -0
  5. package/dist/index.js +52 -0
  6. package/dist/index.js.map +7 -0
  7. package/dist/types/anim/animation-conversion.d.ts +25 -0
  8. package/dist/types/anim/bind-pose.d.ts +15 -0
  9. package/dist/types/anim/keyframe-splines.d.ts +59 -0
  10. package/dist/types/anim/rest-relative.d.ts +32 -0
  11. package/dist/types/anim/spline.d.ts +14 -0
  12. package/dist/types/animation.d.ts +216 -0
  13. package/dist/types/armature.d.ts +524 -0
  14. package/dist/types/character.d.ts +540 -0
  15. package/dist/types/engine.d.ts +166 -0
  16. package/dist/types/environment.d.ts +53 -0
  17. package/dist/types/index.d.ts +37 -0
  18. package/dist/types/lighting/color.d.ts +13 -0
  19. package/dist/types/lighting/point-light.d.ts +33 -0
  20. package/dist/types/lighting/sky-light.d.ts +22 -0
  21. package/dist/types/lighting/sun-light.d.ts +34 -0
  22. package/dist/types/loader/animation/animation-glb.d.ts +117 -0
  23. package/dist/types/loader/character/index.d.ts +33 -0
  24. package/dist/types/loader/character/legacy/index.d.ts +34 -0
  25. package/dist/types/loader/character/legacy/interleaved-splat.d.ts +101 -0
  26. package/dist/types/loader/character/legacy/planar-splat.d.ts +139 -0
  27. package/dist/types/loader/character/load.d.ts +33 -0
  28. package/dist/types/loader/character/types.d.ts +129 -0
  29. package/dist/types/loader/character/v1/decode.d.ts +32 -0
  30. package/dist/types/loader/environment/ply.d.ts +16 -0
  31. package/dist/types/loader/shared/bin-cache.d.ts +19 -0
  32. package/dist/types/loader/skeleton/index.d.ts +19 -0
  33. package/dist/types/loader/skeleton/skeleton-data-441.d.ts +5 -0
  34. package/dist/types/loader/skeleton/skeleton-data.d.ts +6 -0
  35. package/dist/types/math.d.ts +82 -0
  36. package/dist/types/skeletal/animation-player.d.ts +185 -0
  37. package/dist/types/skeletal/bone-mask.d.ts +27 -0
  38. package/dist/types/skeletal/bone-stretch.d.ts +65 -0
  39. package/dist/types/skeletal/pose.d.ts +27 -0
  40. package/dist/types/skeletal/skeleton.d.ts +57 -0
  41. package/dist/types/skeletal/splat-skinning.d.ts +69 -0
  42. package/dist/types/splat/backend.d.ts +57 -0
  43. package/dist/types/splat/bone-binding.d.ts +38 -0
  44. package/dist/types/splat/depth-sort.d.ts +58 -0
  45. package/dist/types/splat/packed-splats.d.ts +36 -0
  46. package/dist/types/splat/splat-mesh.d.ts +71 -0
  47. package/dist/types/splat/splat.d.ts +186 -0
  48. package/dist/types/splat/transform-palette.d.ts +42 -0
  49. package/dist/types/splat/webgl/backend.d.ts +17 -0
  50. package/dist/types/splat/webgl/shaders.d.ts +2 -0
  51. package/dist/types/splat/webgl/splat-mesh.d.ts +52 -0
  52. package/dist/types/splat/webgpu/backend.d.ts +25 -0
  53. package/dist/types/splat/webgpu/lighting.d.ts +34 -0
  54. package/dist/types/splat/webgpu/radix-sort.d.ts +46 -0
  55. package/dist/types/splat/webgpu/splat-mesh.d.ts +71 -0
  56. package/dist/types/three/loaders.d.ts +77 -0
  57. package/dist/types/three/splat-character.d.ts +192 -0
  58. package/dist/types/types.d.ts +30 -0
  59. package/dist/types/webgpu.d.ts +5 -0
  60. package/dist/webgpu.js +813 -0
  61. package/dist/webgpu.js.map +7 -0
  62. package/package.json +46 -4
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Bone-mask utilities for layered animation. A {@link BoneMask} selects a
3
+ * subset of an armature's bones by index; the {@link Armature} layer system
4
+ * uses it to gate an overlay animation to (e.g.) just the upper body while a
5
+ * base full-body clip drives the rest.
6
+ *
7
+ * Built-in regions ({@link UPPER_BODY_BONE_NAMES} / {@link LOWER_BODY_BONE_NAMES})
8
+ * follow the canonical 86-bone rig naming. The split bone is the pelvis:
9
+ * - UPPER covers everything from `spine_01` up (spine 01-05, neck, head,
10
+ * clavicles, arms, hands, all fingers) so the overlay fully replaces
11
+ * the torso without leak from the base's spine rotation.
12
+ * - LOWER covers `pelvis` plus the leg chain (thighs, calves, calf
13
+ * twists, feet, ball, all toes). Including pelvis means an overlay's
14
+ * pelvis Y motion drives the rig and routes through `rootMotionMode`,
15
+ * so the lower-body overlay owns root motion when active.
16
+ *
17
+ * @group Characters & Animation
18
+ */
19
+ export declare const UPPER_BODY_BONE_NAMES: readonly ["spine_01", "spine_02", "spine_03", "spine_04", "spine_05", "neck_01", "neck_02", "head", "clavicle_l", "clavicle_r", "upperarm_l", "upperarm_r", "lowerarm_l", "lowerarm_r", "hand_l", "hand_r", "thumb_01_l", "thumb_02_l", "thumb_03_l", "index_metacarpal_l", "index_01_l", "index_02_l", "index_03_l", "middle_metacarpal_l", "middle_01_l", "middle_02_l", "middle_03_l", "ring_metacarpal_l", "ring_01_l", "ring_02_l", "ring_03_l", "pinky_metacarpal_l", "pinky_01_l", "pinky_02_l", "pinky_03_l", "thumb_01_r", "thumb_02_r", "thumb_03_r", "index_metacarpal_r", "index_01_r", "index_02_r", "index_03_r", "middle_metacarpal_r", "middle_01_r", "middle_02_r", "middle_03_r", "ring_metacarpal_r", "ring_01_r", "ring_02_r", "ring_03_r", "pinky_metacarpal_r", "pinky_01_r", "pinky_02_r", "pinky_03_r"];
20
+ export declare const LOWER_BODY_BONE_NAMES: readonly ["pelvis", "thigh_l", "thigh_r", "calf_l", "calf_r", "calf_twist_01_l", "calf_twist_01_r", "foot_l", "foot_r", "ball_l", "ball_r", "bigtoe_01_l", "bigtoe_02_l", "indextoe_01_l", "indextoe_02_l", "middletoe_01_l", "middletoe_02_l", "ringtoe_01_l", "ringtoe_02_l", "littletoe_01_l", "littletoe_02_l", "bigtoe_01_r", "bigtoe_02_r", "indextoe_01_r", "indextoe_02_r", "middletoe_01_r", "middletoe_02_r", "ringtoe_01_r", "ringtoe_02_r", "littletoe_01_r", "littletoe_02_r"];
21
+ export interface BoneMask {
22
+ name: string;
23
+ boneIndices: Int32Array;
24
+ }
25
+ export declare function resolveBoneIndices(allBoneNames: readonly string[], maskNames: readonly string[]): Int32Array;
26
+ export type BodyRegion = 'upperBody' | 'lowerBody';
27
+ export declare function makeRegionMask(region: BodyRegion, allBoneNames: readonly string[]): BoneMask;
@@ -0,0 +1,65 @@
1
+ import { Mat4 } from '../math';
2
+ import type { Skeleton } from './skeleton';
3
+ /**
4
+ * Per-axis scale for a single bone, in a frame aligned to the bone:
5
+ * - `along` - scale down the bone's own axis (lengthen / shorten),
6
+ * - `width` / `thickness` - scale across the bone (the two cross-section axes;
7
+ * their world orientation is bone-dependent, so treat them as "the two
8
+ * perpendicular directions" rather than a fixed front/side).
9
+ *
10
+ * Each defaults to `1`. A bare `number` is shorthand for a uniform scale on all
11
+ * three axes.
12
+ *
13
+ * @group Characters & Animation
14
+ */
15
+ export interface BoneScale {
16
+ along: number;
17
+ width: number;
18
+ thickness: number;
19
+ }
20
+ /** A bone scale, or a number meaning uniform scale on all three axes. */
21
+ export type BoneScaleInput = Partial<BoneScale> | number;
22
+ /**
23
+ * Per-bone data that realizes a set of bone scales. Produced by
24
+ * {@link computeBoneStretch}, consumed by {@link Skeleton} (lengthening) and
25
+ * {@link SplatSkinning} (flesh/covariance stretch).
26
+ *
27
+ * @group Characters & Animation
28
+ */
29
+ export interface BoneStretch {
30
+ /**
31
+ * Per-bone multiplier on the local rest translation. A bone's local
32
+ * translation is its offset from its parent (the parent segment's vector),
33
+ * so scaling it lengthens that segment by the parent's `along` factor.
34
+ * Translation-only → never inflates covariance, never compounds down a
35
+ * chain. Indexed by bone; `1` = unchanged.
36
+ */
37
+ lengthScale: Float32Array;
38
+ /**
39
+ * Per-bone local stretch matrix (`F·diag(along,width,thickness)·Fᵀ` in the
40
+ * bone-aligned frame `F`), folded into the skinning palette so the bone's
41
+ * Gaussians scale on all three axes and their covariances follow. Identity
42
+ * for unscaled bones. Indexed by bone.
43
+ */
44
+ covStretch: Mat4[];
45
+ /** True when at least one bone is actually scaled. */
46
+ any: boolean;
47
+ }
48
+ /**
49
+ * Translate a map of per-bone scales into the {@link BoneStretch} arrays.
50
+ * `factors[boneName]` scales that bone's flesh + segment (e.g.
51
+ * `{ thigh_l: { along: 1.3 }, calf_l: { along: 1.3 } }` → a 1.3× longer left
52
+ * leg; `{ upperarm_l: { width: 1.4, thickness: 1.4 } }` → a beefier arm).
53
+ * Bones not listed stay at `1`.
54
+ *
55
+ * Correct for arbitrary bone sets (limbs, spine, …) with no boundary
56
+ * compensation: lengthening lives in per-segment translations (no cumulative
57
+ * scale), and the flesh scale is per-bone in the palette (non-propagating).
58
+ * Each bone scales in ITS OWN frame, so bones at any orientation scale
59
+ * correctly.
60
+ *
61
+ * @param skeleton Rig to read the rest pose / hierarchy from.
62
+ * @param boneNames Bone names in index order (from the armature).
63
+ * @param factors Per-bone scales keyed by bone name.
64
+ */
65
+ export declare function computeBoneStretch(skeleton: Skeleton, boneNames: readonly string[], factors: Record<string, BoneScaleInput>): BoneStretch;
@@ -0,0 +1,27 @@
1
+ import { Quat, Vec3 } from '../math';
2
+ /**
3
+ * Per-bone local TRS for a whole rig. The seam between
4
+ * {@link AnimationPlayer} (which writes pose values) and
5
+ * {@link Skeleton} (which composes them into world transforms).
6
+ *
7
+ * Three parallel arrays of length `numBones`. Values live in armature-local
8
+ * bone space - absolute, not rest-relative. Allocation happens once;
9
+ * AnimationPlayer mutates entries in place each frame.
10
+ *
11
+ * @group Characters & Animation
12
+ */
13
+ export declare class Pose {
14
+ readonly positions: Vec3[];
15
+ readonly rotations: Quat[];
16
+ readonly scales: Vec3[];
17
+ constructor(numBones: number);
18
+ get numBones(): number;
19
+ /** Copy every bone from `other` into this pose. */
20
+ copyFrom(other: Pose): void;
21
+ /**
22
+ * Reset every bone to the bind-pose values held in `bindT` (flat
23
+ * `[x,y,z]*numBones`) and `bindR` (flat `[x,y,z,w]*numBones`). Scales reset
24
+ * to unit.
25
+ */
26
+ setToBind(bindT: Float32Array, bindR: Float32Array): void;
27
+ }
@@ -0,0 +1,57 @@
1
+ import { Mat4 } from '../math';
2
+ import type { RestPose } from '../anim/rest-relative';
3
+ import type { ArmatureData } from '../types';
4
+ import type { Pose } from './pose';
5
+ /**
6
+ * Rig data and skeletal math for one character. Owns the bone hierarchy, the
7
+ * rest pose, and the (lazy) inverse-bind pose. Composes per-bone local
8
+ * transforms into world-space matrices.
9
+ *
10
+ * Pure data + math - no three.js object, no `SplatEngine`, no `Splat`. Safe to
11
+ * construct in tests with a fixture rig.
12
+ *
13
+ * @group Characters & Animation
14
+ */
15
+ export declare class Skeleton {
16
+ readonly numBones: number;
17
+ /** Parent bone index per bone. `-1` for root bones. */
18
+ readonly parents: Int32Array;
19
+ /** Flat bind-pose translations: `[x,y,z]*numBones`. */
20
+ readonly bindT: Float32Array;
21
+ /** Flat bind-pose rotations (quaternions): `[x,y,z,w]*numBones`. */
22
+ readonly bindR: Float32Array;
23
+ /** Rest pose as parallel `Vec3`/`Quat` arrays. */
24
+ readonly restPose: RestPose;
25
+ /**
26
+ * Optional per-bone multiplier on the local rest translation, applied during
27
+ * FK. A bone's local translation is its offset from its parent, so scaling
28
+ * it lengthens that segment - this is the lengthening half of bone-length
29
+ * scaling (see {@link computeBoneStretch}). It deliberately affects only the
30
+ * translation, not the bone's orientation/scale, so it never inflates
31
+ * Gaussian covariance and never compounds down a chain. `null` = unscaled
32
+ * (the normal path). The bind/inverse-bind are intentionally NOT rescaled,
33
+ * so the palette carries the standard→stretched delta.
34
+ */
35
+ lengthScale: Float32Array | null;
36
+ private readonly _children;
37
+ private readonly _rootBones;
38
+ private _inverseBindPose;
39
+ private readonly _tmpLocalMat;
40
+ private readonly _tmpPos;
41
+ private readonly _kfStack;
42
+ constructor(armatureData: ArmatureData);
43
+ /**
44
+ * World-space inverse-bind pose, one matrix per bone. Lazy - bind pose is
45
+ * only needed once splat skinning starts pushing transforms.
46
+ */
47
+ get inverseBindPose(): Mat4[];
48
+ /**
49
+ * Compose local-space `pose` into world-space matrices, prefixed by
50
+ * `entityWorldTransform`. Walks the hierarchy parent-first so child world
51
+ * transforms read the freshly-written parent.
52
+ *
53
+ * `out` is reused - entries are created lazily if missing, but the caller
54
+ * owns the buffer (no per-frame allocation).
55
+ */
56
+ applyLocal(pose: Pose, entityWorldTransform: Mat4, out: Mat4[]): void;
57
+ }
@@ -0,0 +1,69 @@
1
+ import type { Frustum } from 'three';
2
+ import { Mat4 } from '../math';
3
+ import type { Splat } from '../splat/splat';
4
+ import type { SkeletonLibrary } from '../types';
5
+ import type { Skeleton } from './skeleton';
6
+ /**
7
+ * Per-character splat skinning coordinator. Owns the set of {@link Splat}s
8
+ * driven by an armature, their palette-slot maps, and the GPU push that
9
+ * uploads bone matrices each frame.
10
+ *
11
+ * Also owns the frustum-visibility gate: the armature can skip the expensive
12
+ * pose-eval + skinning path when no linked splat is on-screen.
13
+ *
14
+ * @group Characters & Animation
15
+ */
16
+ export declare class SplatSkinning {
17
+ /** Splats currently driven by this skinning unit. */
18
+ readonly linkedSplats: Set<Splat>;
19
+ /**
20
+ * Optional per-bone local stretch matrices (the covariance half of
21
+ * bone-length scaling - see {@link computeBoneStretch}). When set, each
22
+ * bone's palette entry gets this stretch folded in, the per-bone scale is
23
+ * NOT normalized out of the palette, and linked splats render with the
24
+ * `USE_NONRIGID_COV` shader path so Gaussian covariances follow the stretch.
25
+ * `null` = rigid skinning (the normal path). Set via {@link setCovStretch}.
26
+ */
27
+ covStretch: Mat4[] | null;
28
+ private readonly _skeleton;
29
+ private readonly _skeletonLibrary;
30
+ private readonly _bonePaletteFirstIdx;
31
+ private readonly _boneIndexLists;
32
+ private readonly _finalBoneTransforms;
33
+ private readonly _armatureWorldToLocal;
34
+ constructor(skeleton: Skeleton, skeletonLibrary: SkeletonLibrary | undefined);
35
+ link(splat: Splat): void;
36
+ unlink(splat: Splat): void;
37
+ /**
38
+ * Set (or clear with `null`) the per-bone covariance-stretch matrices for
39
+ * bone-length scaling. Also flips the `USE_NONRIGID_COV` shader path on every
40
+ * linked splat so covariances follow the stretch. Pass `null` to return to
41
+ * rigid skinning.
42
+ */
43
+ setCovStretch(mats: Mat4[] | null): void;
44
+ /**
45
+ * Returns `true` when at least one linked splat's bounding sphere intersects
46
+ * the camera frustum. Used by the armature to short-circuit the per-frame
47
+ * skinning math for off-screen characters.
48
+ *
49
+ * When `frustum` is missing (e.g. before the first `engine.update()`) returns `true` -
50
+ * the conservative choice is to do the extra work rather than freeze a
51
+ * character's pose.
52
+ */
53
+ anyVisible(frustum: Frustum | undefined): boolean;
54
+ /**
55
+ * Compose `worldTransforms` (one per bone) with the pivot's inverse world
56
+ * transform and the inverse-bind pose, then upload the resulting palette to
57
+ * every linked splat. `worldTransforms[i]` is the i-th bone's matrix in
58
+ * world space, as produced by {@link Skeleton.applyLocal}.
59
+ *
60
+ * Strips scale from the per-bone linear part before composing with the
61
+ * inverse bind. Without this, any net scale (from `splat.object3D` world scale
62
+ * or animated bone scale) inflates Gaussian sigmas on the GPU via the
63
+ * palette's column lengths - visible as radial streaks on hands/fists, and
64
+ * desyncs CPU-computed centers from GPU-rendered positions.
65
+ */
66
+ pushTransforms(worldTransforms: Mat4[], entityWorldTransform: Mat4, skipPositions?: boolean): void;
67
+ /** Request a re-sort on every linked splat (e.g. after a transform change). */
68
+ requestSortAll(): void;
69
+ }
@@ -0,0 +1,57 @@
1
+ import type { BufferGeometry, Camera, Material, Mesh } from 'three';
2
+ import type { SplatEngine } from '../engine';
3
+ import type { Splat } from './splat';
4
+ /**
5
+ * The renderer-specific half of a {@link SplatMesh}: the geometry and material
6
+ * one kind of three.js renderer needs, plus the per-frame work to keep them
7
+ * current.
8
+ *
9
+ * Everything a splat is - its packed data, weights, palette, tint - lives on
10
+ * the `Splat`. An implementation only turns that into something one renderer
11
+ * can draw, and owns whatever that takes (sort state, GPU buffers).
12
+ *
13
+ * @internal
14
+ */
15
+ export interface SplatRenderImpl {
16
+ readonly geometry: BufferGeometry;
17
+ readonly material: Material;
18
+ /**
19
+ * Whether the splat's centres must be re-skinned on the CPU every frame.
20
+ * True when the depth sort runs on the CPU; false when the GPU skins for its
21
+ * own sort, in which case that pass - the dominant per-character CPU cost -
22
+ * is skipped entirely.
23
+ */
24
+ readonly needsCpuCenters: boolean;
25
+ /** True while the drawn order is stale or absent because a sort is outstanding. */
26
+ readonly sortPending: boolean;
27
+ /** Force a re-sort on the next render even if nothing appears to have changed. */
28
+ requestSort(): void;
29
+ /** Rebuild the shader variant after a feature toggle on the splat. */
30
+ syncDefines(): void;
31
+ /** Refresh culling bounds from the splat's current local bounds. */
32
+ syncBounds(): void;
33
+ /** Re-upload bone indices/weights after the splat's arrays were edited. */
34
+ syncBoneData(): void;
35
+ /** Re-upload per-splat group ids after {@link Splat.setSplatGroups}. */
36
+ syncVariantGroups(): void;
37
+ /** Called from the host mesh's `onBeforeRender`, once per draw. */
38
+ beforeRender(renderer: unknown, camera: Camera): void;
39
+ /** Release GPU resources. The splat's own textures are not this object's to free. */
40
+ dispose(): void;
41
+ }
42
+ /**
43
+ * Creates {@link SplatRenderImpl}s for one kind of three.js renderer.
44
+ *
45
+ * You rarely need to name one: a {@link SplatMesh} picks the backend that
46
+ * matches whichever renderer first draws it.
47
+ *
48
+ * @group Engine
49
+ */
50
+ export interface SplatBackend {
51
+ /**
52
+ * @param host - The mesh in the scene graph the implementation draws through.
53
+ * @param asyncSort - The splat is static, so a sort that lags the camera by
54
+ * a frame or two is acceptable. Backends that sort on the GPU ignore this.
55
+ */
56
+ createImpl(host: Mesh, splat: Splat, engine: SplatEngine, asyncSort: boolean): SplatRenderImpl;
57
+ }
@@ -0,0 +1,38 @@
1
+ import type { SkeletonLibrary } from '../types';
2
+ import type { Splat } from './splat';
3
+ /**
4
+ * Build the source→target bone remap when the splat's `.bin` bundle was
5
+ * authored against a wider skeleton than the armature uses (e.g. a 1185-bone
6
+ * source on the 86-bone std-male rig, or a 441-bone full body+facial source
7
+ * on the 86-bone body rig).
8
+ *
9
+ * Returns `null` when no remap is needed (source and target bone counts
10
+ * match) or when the skeleton library doesn't carry enough naming metadata to
11
+ * resolve the mapping. Unmapped bones in the result are `-1`, except facial
12
+ * bones (`FACIAL_*`), which fall back to the `head` bone so the splats track
13
+ * head motion rather than snapping to root.
14
+ */
15
+ export declare function buildSourceBoneRemap(splat: Splat, numBones: number, skeletonLibrary: SkeletonLibrary | undefined): Int32Array | null;
16
+ /**
17
+ * Allocate the splat's slice of the shared transform palette and rewrite its
18
+ * bone-index texture so each splat's K influences point at the palette slots
19
+ * the armature will write into.
20
+ *
21
+ * Splats with no resolvable target bone (typically facial influences on a
22
+ * body-only rig that can't fall back to `head`) get weight 0 - they stay at
23
+ * rest instead of snapping to bone 0.
24
+ *
25
+ * Returns the first palette index allocated for this splat's bone block.
26
+ * Palette slot for bone `i` is always `firstIdx + i`.
27
+ */
28
+ export declare function initializeSplatBoneMapping(splat: Splat, numBones: number, skeletonLibrary: SkeletonLibrary | undefined): number;
29
+ /**
30
+ * Walk the splat's per-influence bone-index buffer and return the sorted list
31
+ * of distinct bone indices it actually references. Used to skip palette writes
32
+ * for bones no splat influences (e.g. a 441-bone rig where ~half the bones are
33
+ * facial and the splat has no facial weights).
34
+ *
35
+ * Returns an empty `Int32Array` when the splat has no weights data - callers
36
+ * treat that as "fall back to walking every bone."
37
+ */
38
+ export declare function buildSplatBoneIndexList(splat: Splat, numBones: number): Int32Array;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Back-to-front depth ordering for splats: a counting sort over quantised
3
+ * distance along the view direction.
4
+ *
5
+ * The same routine runs in two places. Skinned characters are small and move
6
+ * every frame, so they sort synchronously on the main thread and never show a
7
+ * stale order. Environments are large and only need a new order when the camera
8
+ * moves, so they sort in a worker ({@link AsyncDepthSorter}) and keep drawing
9
+ * the previous order until the new one lands.
10
+ */
11
+ /**
12
+ * Range of `dot(p, dir)` over an axis-aligned box - the projection of the box
13
+ * onto the view direction.
14
+ */
15
+ export declare function projectBoundsOnDirection(min: ArrayLike<number>, max: ArrayLike<number>, dx: number, dy: number, dz: number): {
16
+ depthMin: number;
17
+ depthMax: number;
18
+ };
19
+ /** Synchronous sorter with its own scratch buffers. One per splat. */
20
+ export declare class DepthSorter {
21
+ private readonly _scratch;
22
+ sort(centers: Float32Array, count: number, dx: number, dy: number, dz: number, depthMin: number, depthMax: number, order: Uint32Array): void;
23
+ }
24
+ export interface SortRequest {
25
+ dx: number;
26
+ dy: number;
27
+ dz: number;
28
+ depthMin: number;
29
+ depthMax: number;
30
+ }
31
+ /**
32
+ * Worker-backed sorter for static splats.
33
+ *
34
+ * At most one sort is in flight. A request made while the worker is busy
35
+ * replaces any earlier pending one, so a fast-moving camera never queues work
36
+ * it no longer wants. A single order buffer is transferred to the worker and
37
+ * back, never copied.
38
+ *
39
+ * The worker is built from a Blob of this module's own sort routine, so there
40
+ * is no second file for a bundler to lose.
41
+ */
42
+ export declare class AsyncDepthSorter {
43
+ private _worker;
44
+ private _pending;
45
+ /** The order buffer, when it is on this side of the worker boundary. */
46
+ private _order;
47
+ /**
48
+ * @param centers - Flat `[x,y,z] * count`. Copied into the worker once.
49
+ * @param onSorted - Called with each finished order. The array is only valid
50
+ * for the duration of the call - it goes back to the worker on the next
51
+ * request - so copy out of it before returning.
52
+ */
53
+ constructor(centers: Float32Array, count: number, onSorted: (order: Uint32Array) => void);
54
+ /** True while a sort is running in the worker. */
55
+ get busy(): boolean;
56
+ request(req: SortRequest): void;
57
+ dispose(): void;
58
+ }
@@ -0,0 +1,36 @@
1
+ import type { SplatAttributes } from '../loader/character/types';
2
+ /**
3
+ * Splat attributes packed into the three texel streams the shaders read.
4
+ *
5
+ * Renderer-agnostic on purpose: these are plain typed arrays laid out as
6
+ * `width x height` RGBA texels, so the WebGL and WebGPU backends wrap the same
7
+ * bytes in their own texture objects.
8
+ *
9
+ * The layout is 32 bytes per splat:
10
+ *
11
+ * - `color` RGBA16F - `rgb` display-referred colour, `a` opacity (post-sigmoid).
12
+ * - `transformA` RGBA32UI - `xyz` the centre as raw float32 bits, `w` two packed
13
+ * half floats: rotation `x` (low 16) and `y` (high 16).
14
+ * - `transformB` RGBA16F - `xyz` linear scale, `w` rotation `z`.
15
+ *
16
+ * Rotation `w` is not stored: quaternions are normalised and flipped to
17
+ * `w >= 0` here, so the shader recovers it as `sqrt(1 - |xyz|^2)`.
18
+ */
19
+ export interface PackedSplats {
20
+ numSplats: number;
21
+ width: number;
22
+ height: number;
23
+ color: Uint16Array;
24
+ transformA: Uint32Array;
25
+ transformB: Uint16Array;
26
+ /** Rest-pose centres, flat `[x,y,z] * numSplats`. The depth sort reads these. */
27
+ centers: Float32Array;
28
+ boundsMin: [number, number, number];
29
+ boundsMax: [number, number, number];
30
+ }
31
+ /** Texture dimensions for `count` texels: near-square, wide enough to hold every row. */
32
+ export declare function textureSizeFor(count: number): {
33
+ width: number;
34
+ height: number;
35
+ };
36
+ export declare function packSplats(numSplats: number, s: SplatAttributes): PackedSplats;
@@ -0,0 +1,71 @@
1
+ import { Mesh } from 'three';
2
+ import type { Camera } from 'three';
3
+ import type { SplatEngine } from '../engine';
4
+ import type { SplatBackend } from './backend';
5
+ import type { Splat } from './splat';
6
+ /**
7
+ * Make the WebGPU backend available to every {@link SplatMesh}. Called by the
8
+ * `@viggle/splat-engine-three/webgpu` entry point when it is imported; the
9
+ * default entry point leaves it out so WebGL applications never load
10
+ * `three/webgpu`.
11
+ *
12
+ * @internal
13
+ */
14
+ export declare function registerWebGPUBackend(backend: SplatBackend): void;
15
+ /**
16
+ * The object that draws one {@link Splat}: an ordinary `THREE.Mesh` in your
17
+ * scene graph, so `visible`, `renderOrder`, `layers` and parent transforms all
18
+ * work as they do for any mesh.
19
+ *
20
+ * It works with both `THREE.WebGLRenderer` and `WebGPURenderer`. Which one is
21
+ * not known until something renders it, so the renderer-specific geometry and
22
+ * material are attached the first time it is drawn; the mesh shows from the
23
+ * following frame.
24
+ *
25
+ * @group Splats
26
+ */
27
+ export declare class SplatMesh extends Mesh {
28
+ /** Type flag, in the three.js manner. */
29
+ readonly isSplatMesh = true;
30
+ /** The splat this mesh draws: its data, tint, grade and render options. */
31
+ readonly splat: Splat;
32
+ private readonly _engine;
33
+ private readonly _asyncSort;
34
+ private _impl;
35
+ /**
36
+ * @param asyncSort - See {@link SplatBackend.createImpl}.
37
+ * @internal Construct a {@link Splat} instead; it creates its own mesh.
38
+ */
39
+ constructor(splat: Splat, engine: SplatEngine, asyncSort: boolean);
40
+ private _attach;
41
+ /** See {@link SplatRenderImpl.needsCpuCenters}. False until a renderer has drawn this mesh. */
42
+ get needsCpuCenters(): boolean;
43
+ /** True while the drawn order is stale or absent, including before the first draw. */
44
+ get sortPending(): boolean;
45
+ requestSort(): void;
46
+ syncDefines(): void;
47
+ syncBounds(): void;
48
+ syncBoneData(): void;
49
+ syncVariantGroups(): void;
50
+ onBeforeRender(renderer: unknown, _scene: unknown, camera: Camera): void;
51
+ /**
52
+ * A second mesh drawing the same splat data, at the same transform. The
53
+ * decoded data is shared; the GPU copy of it is not.
54
+ *
55
+ * Only for static splats. A mesh that belongs to a character is driven by
56
+ * that character's rig and cannot stand alone - clone the `SplatCharacter`.
57
+ */
58
+ clone(): this;
59
+ /**
60
+ * Splats are not pickable. The inherited implementation would test the ray
61
+ * against this mesh's own geometry, which is a single proxy quad at the
62
+ * local origin - every instance is positioned in the shader - and report
63
+ * hits that have nothing to do with where the splats are drawn.
64
+ */
65
+ raycast(): void;
66
+ /**
67
+ * Release every GPU resource behind this mesh - the splat's data textures as
68
+ * well as the renderer-specific buffers - and remove it from its parent.
69
+ */
70
+ dispose(): void;
71
+ }