@bitefoundry/retrieviewer 0.0.3 → 0.0.4
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/ASSET-PROVENANCE.md +6 -0
- package/ASSETS-LICENSE.md +2 -1
- package/ATTRIBUTION.md +6 -1
- package/COMFORT-ART.md +65 -0
- package/DESKTOP-PLATFORMS.md +6 -1
- package/PLUGIN.md +13 -10
- package/PREREQUISITES.md +95 -0
- package/PUPPY-MEMORY.md +102 -1
- package/README.ko.md +110 -0
- package/README.md +117 -59
- package/RELEASING.md +23 -12
- package/THIRD-PARTY-NOTICES.md +5 -3
- package/braille.mjs +35 -9
- package/examples/live.mjs +2 -2
- package/integrations/linux-overlay/overlay.py +2 -2
- package/integrations/macos-overlay/overlay.jxa +8 -6
- package/integrations/opencode/runtime.mjs +4 -4
- package/integrations/opencode/tui.tsx +1 -1
- package/integrations/windows-overlay/Overlay.cs +4 -4
- package/integrations/windows-overlay/protocol.mjs +11 -7
- package/package.json +34 -13
- package/puppy-comfort-sprites.mjs +25 -0
- package/puppy-companion-comfort-q5.bin.gz +0 -0
- package/puppy-companion-comfort.json +77 -0
- package/puppy-companion-life.mjs +49 -16
- package/puppy-companion-objects.mjs +12 -0
- package/puppy-frame-predictive.mjs +15 -9
- package/puppy-frame-store.mjs +2 -2
- package/puppy-paper-world.mjs +14 -0
- package/puppy-paper.mjs +65 -14
- package/retrieviewer-plugin.d.mts +1 -1
- package/retrieviewer-plugin.mjs +1 -1
- package/retrieviewer.d.mts +10 -4
- package/retrieviewer.mjs +20 -14
- package/locomotion-core.mjs +0 -21
- package/locomotion-puppy.animal +0 -0
- package/vendor/procedural-animals/package.json +0 -40
- package/vendor/procedural-animals/src/animal.js +0 -146
- package/vendor/procedural-animals/src/bake.js +0 -51
- package/vendor/procedural-animals/src/build.js +0 -81
- package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
- package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
- package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
- package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
- package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
- package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
- package/vendor/procedural-animals/src/core/build/seams.js +0 -183
- package/vendor/procedural-animals/src/core/build/warp.js +0 -112
- package/vendor/procedural-animals/src/core/build/weights.js +0 -393
- package/vendor/procedural-animals/src/core/math/vec.js +0 -81
- package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
- package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
- package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
- package/vendor/procedural-animals/src/core/motion/common.js +0 -158
- package/vendor/procedural-animals/src/core/motion/index.js +0 -22
- package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
- package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
- package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
- package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
- package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
- package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
- package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
- package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
- package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
- package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
- package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
- package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
- package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
- package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
- package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
- package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
- package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
- package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
- package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
- package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
- package/vendor/procedural-animals/src/index.d.ts +0 -98
- package/vendor/procedural-animals/src/index.js +0 -29
- package/vendor/procedural-animals/src/registry.js +0 -48
- package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
- package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
- package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
- package/vendor/procedural-animals/src/species/bear/index.js +0 -139
- package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
- package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
- package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
- package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
- package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
- package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
- package/vendor/procedural-animals/src/species/boar/index.js +0 -110
- package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
- package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
- package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
- package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
- package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
- package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
- package/vendor/procedural-animals/src/species/cat/index.js +0 -211
- package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
- package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
- package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
- package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
- package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
- package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
- package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
- package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
- package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
- package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
- package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
- package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
- package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
- package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
- package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
- package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
- package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
- package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
- package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
- package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
- package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
- package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
- package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
- package/vendor/procedural-animals/src/species/cow/index.js +0 -118
- package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
- package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
- package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
- package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
- package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
- package/vendor/procedural-animals/src/species/crow/index.js +0 -70
- package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
- package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
- package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
- package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
- package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
- package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
- package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
- package/vendor/procedural-animals/src/species/deer/index.js +0 -106
- package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
- package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
- package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
- package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
- package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
- package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
- package/vendor/procedural-animals/src/species/dog/index.js +0 -144
- package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
- package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
- package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
- package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
- package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
- package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
- package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
- package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
- package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
- package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
- package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
- package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
- package/vendor/procedural-animals/src/species/fish/index.js +0 -88
- package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
- package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
- package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
- package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
- package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
- package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
- package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
- package/vendor/procedural-animals/src/species/fox/index.js +0 -120
- package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
- package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
- package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
- package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
- package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
- package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
- package/vendor/procedural-animals/src/species/frog/index.js +0 -97
- package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
- package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
- package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
- package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
- package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
- package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
- package/vendor/procedural-animals/src/species/goat/index.js +0 -149
- package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
- package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
- package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
- package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
- package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
- package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
- package/vendor/procedural-animals/src/species/horse/index.js +0 -80
- package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
- package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
- package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
- package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
- package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
- package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
- package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
- package/vendor/procedural-animals/src/species/lion/index.js +0 -111
- package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
- package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
- package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
- package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
- package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
- package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
- package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
- package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
- package/vendor/procedural-animals/src/species/pig/index.js +0 -161
- package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
- package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
- package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
- package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
- package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
- package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
- package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
- package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
- package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
- package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
- package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
- package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
- package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
- package/vendor/procedural-animals/src/species/rat/index.js +0 -111
- package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
- package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
- package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
- package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
- package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
- package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
- package/vendor/procedural-animals/src/species/shark/index.js +0 -104
- package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
- package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
- package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
- package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
- package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
- package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
- package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
- package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
- package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
- package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
- package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
- package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
- package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
- package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
- package/vendor/procedural-animals/src/species/snake/index.js +0 -73
- package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
- package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
- package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
- package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
- package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
- package/vendor/procedural-animals/src/species/spider/index.js +0 -78
- package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
- package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
- package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
- package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
- package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
- package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
- package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
- package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
- package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
- package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
- package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
- package/vendor/procedural-animals/src/worker.js +0 -29
package/retrieviewer.d.mts
CHANGED
|
@@ -35,16 +35,22 @@ export interface PetState {
|
|
|
35
35
|
[key: string]: unknown;
|
|
36
36
|
}
|
|
37
37
|
export interface LifeReport {
|
|
38
|
+
/** Last selected autonomous episode; null before the first decision. */
|
|
39
|
+
episode: string | null;
|
|
38
40
|
intent: string;
|
|
39
41
|
label: string;
|
|
40
|
-
interaction: 'ball' | 'treat' | null;
|
|
42
|
+
interaction: 'ball' | 'treat' | 'tug' | null;
|
|
41
43
|
busy: boolean;
|
|
42
44
|
energy: number;
|
|
43
45
|
decisions: number;
|
|
44
|
-
cooldowns: {ball: number; treat: number};
|
|
46
|
+
cooldowns: {ball: number; treat: number; tug: number};
|
|
45
47
|
gesture: {clip: string; progress: number} | null;
|
|
46
48
|
ball: {x: number; z: number; height: number; carried: boolean} | null;
|
|
47
49
|
treat: {x: number; z: number; height: number} | null;
|
|
50
|
+
/** Tug-of-war state while a tug interaction is active. */
|
|
51
|
+
tug: {progress: number; grip: number} | null;
|
|
52
|
+
/** Silent rising sleep cue, normalized cycle progress; no audio process. */
|
|
53
|
+
snore: number | null;
|
|
48
54
|
}
|
|
49
55
|
export interface PetFrame {
|
|
50
56
|
artProfile: 'original' | 'q5';
|
|
@@ -70,7 +76,7 @@ export interface Pet {
|
|
|
70
76
|
step(dt: number): number;
|
|
71
77
|
frame(options: FrameOptions): PetFrame;
|
|
72
78
|
/** False when busy or cooling down. */
|
|
73
|
-
interact(kind: 'ball' | 'treat'): boolean;
|
|
79
|
+
interact(kind: 'ball' | 'treat' | 'tug'): boolean;
|
|
74
80
|
readonly disposed: boolean;
|
|
75
81
|
dispose(): void;
|
|
76
82
|
}
|
|
@@ -78,7 +84,7 @@ export function createPet(options?: PetOptions): Promise<Pet>;
|
|
|
78
84
|
export function defaultLoadAsset(name: string): Promise<ArrayBuffer | Uint8Array>;
|
|
79
85
|
export const STEP_SECONDS: number;
|
|
80
86
|
export const MAX_CATCH_UP_SECONDS: number;
|
|
81
|
-
export const INTERACTIONS: readonly ['ball', 'treat'];
|
|
87
|
+
export const INTERACTIONS: readonly ['ball', 'treat', 'tug'];
|
|
82
88
|
export const ART_PROFILES: readonly ['original', 'q5'];
|
|
83
89
|
export const DEFAULT_ART_PROFILE: 'q5';
|
|
84
90
|
export function getPetAssetFiles(artProfile?: 'original' | 'q5'): readonly string[];
|
package/retrieviewer.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {createPaperProjection} from './puppy-paper-world.mjs';
|
|
1
|
+
import {createPaperActor,createPaperRenderer,createPaperComfortTimings} from './puppy-paper.mjs';
|
|
2
|
+
import {createPaperProjection,createPaperBody} from './puppy-paper-world.mjs';
|
|
4
3
|
import {rgbaToBraille} from './braille.mjs';
|
|
5
4
|
import {createSnappyTrickTimings} from './puppy-paper-trick-timing.mjs';
|
|
6
5
|
import {createCompanionLife} from './puppy-companion-life.mjs';
|
|
@@ -11,24 +10,25 @@ import {readPackedFrames} from './puppy-frame-store.mjs';
|
|
|
11
10
|
// or listener side effects: the host owns the loop and calls step() then frame().
|
|
12
11
|
export const STEP_SECONDS=1/60;
|
|
13
12
|
export const MAX_CATCH_UP_SECONDS=.1;
|
|
14
|
-
export const INTERACTIONS=Object.freeze(['ball','treat']);
|
|
13
|
+
export const INTERACTIONS=Object.freeze(['ball','treat','tug']);
|
|
15
14
|
// Approved 32-level art ships by default; preserved original art is optional.
|
|
16
15
|
export const DEFAULT_ART_PROFILE='q5';
|
|
17
16
|
export const ART_PROFILES=Object.freeze(['original','q5']);
|
|
18
17
|
// Current companion runtime: v9 tricks + interactions-v3, same as puppy-paper.html.
|
|
19
18
|
export const PET_ASSETS=Object.freeze({
|
|
19
|
+
// Kept as a legacy identifier only; createPet/getPetAssetFiles no longer load it.
|
|
20
20
|
animal:'locomotion-puppy.animal',
|
|
21
21
|
stride:'puppy-stride-tweens-q5.bin.gz',strideInfo:'puppy-stride-meta.json',stableInfo:'puppy-stable-meta.json',
|
|
22
22
|
rear:'puppy-paper-rear-gallop',approach:'puppy-paper-approach',seat:'puppy-paper-seat',arrivalSeat:'puppy-paper-arrival-seat',
|
|
23
|
-
behaviors:'puppy-paper-behaviors',actions:'puppy-paper-actions-v9',interactions:'puppy-companion-interactions-v3',
|
|
23
|
+
behaviors:'puppy-paper-behaviors',actions:'puppy-paper-actions-v9',interactions:'puppy-companion-interactions-v3',comfort:'puppy-companion-comfort',
|
|
24
24
|
});
|
|
25
|
-
const BUNDLES=['rear','approach','seat','arrivalSeat','behaviors','actions','interactions'];
|
|
25
|
+
const BUNDLES=['rear','approach','seat','arrivalSeat','behaviors','actions','interactions','comfort'];
|
|
26
26
|
const frameFile=(base,profile)=>base+(profile==='q5'?'-q5.bin.gz':'-compact.bin.gz');
|
|
27
27
|
/** Exact files a host/bundler must copy, including metadata. Never loads assets. */
|
|
28
28
|
export function getPetAssetFiles(artProfile=DEFAULT_ART_PROFILE){
|
|
29
29
|
if(!ART_PROFILES.includes(artProfile))throw RangeError('artProfile must be original or q5');
|
|
30
30
|
return Object.freeze([PET_ASSETS.strideInfo,PET_ASSETS.stableInfo,frameFile('puppy-stride-tweens',artProfile),
|
|
31
|
-
...BUNDLES.flatMap(key=>[PET_ASSETS[key]+'.json',frameFile(PET_ASSETS[key],artProfile)])
|
|
31
|
+
...BUNDLES.flatMap(key=>[PET_ASSETS[key]+'.json',frameFile(PET_ASSETS[key],artProfile)])]);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// Relative to this module, never the process cwd: file: URLs in Node, HTTP in browsers.
|
|
@@ -62,13 +62,19 @@ const sharedAssets=new Map();
|
|
|
62
62
|
async function loadPetAssets(loadAsset,artProfile){
|
|
63
63
|
const load=async name=>bytesOf(name,await loadAsset(name));
|
|
64
64
|
const json=async name=>JSON.parse(new TextDecoder().decode(await load(name)));
|
|
65
|
-
const frames=async name=>readPackedFrames(await gunzip(await load(name)));
|
|
65
|
+
const frames=async (name,options)=>readPackedFrames(await gunzip(await load(name)),options);
|
|
66
66
|
// One family at a time: never inflate all eight legacy-sized buffers together.
|
|
67
67
|
const data={assets:{}};
|
|
68
68
|
data.info=await json(PET_ASSETS.strideInfo);data.meta=await json(PET_ASSETS.stableInfo);
|
|
69
69
|
data.cache=await frames(frameFile('puppy-stride-tweens',artProfile));
|
|
70
|
-
for(const key of BUNDLES){
|
|
71
|
-
|
|
70
|
+
for(const key of BUNDLES){
|
|
71
|
+
const base=PET_ASSETS[key],info=await json(base+'.json');
|
|
72
|
+
// Old interaction sheets include preparation frames no runtime clip can reach.
|
|
73
|
+
// Keep exact active samples only, leaving room for small new gestures without
|
|
74
|
+
// keeping another multi-megabyte art family resident. Original RVF1 unchanged.
|
|
75
|
+
const options=key==='interactions'&&artProfile==='q5'?{frameIndices:[...new Set(Object.values(info.clips).flatMap(list=>list.flatMap(p=>Array.from({length:info.samples},(_,s)=>p*info.samples+s))))]}:undefined;
|
|
76
|
+
data.assets[key]={info,bytes:await frames(frameFile(base,artProfile),options)};
|
|
77
|
+
}
|
|
72
78
|
return data;
|
|
73
79
|
}
|
|
74
80
|
async function acquireAssets(loadAsset,artProfile){
|
|
@@ -90,12 +96,12 @@ export async function createPet({loadAsset=defaultLoadAsset,seed,energy,pawTrans
|
|
|
90
96
|
if(!ART_PROFILES.includes(artProfile))throw RangeError('artProfile must be original or q5');
|
|
91
97
|
let lease=await acquireAssets(loadAsset,artProfile),data=lease.data;
|
|
92
98
|
const release=lease.release;lease=null;
|
|
93
|
-
let {cache,info,meta
|
|
99
|
+
let {cache,info,meta}=data;const assets={...data.assets};data=null;
|
|
94
100
|
let animal,actor,life;
|
|
95
101
|
try{
|
|
96
102
|
if(!assets.actions.info.clips?.['lie-down']||!assets.actions.info.clips?.['lie-up'])throw Error('v9 action cache lacks lie-down/lie-up clips');
|
|
97
|
-
animal=
|
|
98
|
-
actor=createPaperActor(animal,info,{manualTricks:true,trickTimings:createSnappyTrickTimings(assets.actions.info),walkTransitions:true});
|
|
103
|
+
animal=createPaperBody();
|
|
104
|
+
actor=createPaperActor(animal,info,{manualTricks:true,trickTimings:createSnappyTrickTimings(assets.actions.info),comfortTimings:createPaperComfortTimings(assets.actions.info),walkTransitions:true});
|
|
99
105
|
life=createCompanionLife(actor,{seed,...energy===undefined?{}:{energy}});
|
|
100
106
|
}catch(e){animal?.dispose?.();release();throw e;}
|
|
101
107
|
let paint=null,paintKey='',time=0,acc=0,disposed=false;
|
|
@@ -127,7 +133,7 @@ export async function createPet({loadAsset=defaultLoadAsset,seed,energy,pawTrans
|
|
|
127
133
|
const {text}=rgbaToBraille(pixels,width,height,{method:'stable',brightness:1.18,contrast:1.1});
|
|
128
134
|
return {text,columns,rows,pixels,state,report,placement,projection,artProfile,mouth:paint.lastMouth,contacts:paint.report()};
|
|
129
135
|
},
|
|
130
|
-
// 'ball' | 'treat'. False when busy or cooling down; unknown kinds throw.
|
|
136
|
+
// 'ball' | 'treat' | 'tug'. False when busy or cooling down; unknown kinds throw.
|
|
131
137
|
interact(kind){alive();if(!INTERACTIONS.includes(kind))throw TypeError(`Unknown interaction: ${kind}`);return life.interact(kind);},
|
|
132
138
|
get disposed(){return disposed;},
|
|
133
139
|
dispose(){
|
package/locomotion-core.mjs
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import * as THREE from 'three';
|
|
2
|
-
import {createAnimal} from './vendor/procedural-animals/src/index.js';
|
|
3
|
-
export const LAB_OPTIONS={variant:'retriever',age:'juvenile',seed:2,quality:'low',worker:false,overrides:{colour:'yellow',stanceSoftMargin:.005}};
|
|
4
|
-
export async function makeLabAnimal(extra={}){
|
|
5
|
-
return createAnimal('dog',{...LAB_OPTIONS,...extra,overrides:{...LAB_OPTIONS.overrides,...extra.overrides}});
|
|
6
|
-
}
|
|
7
|
-
// Never translate the mesh externally. The gait engine owns root motion + IK.
|
|
8
|
-
export function makeLabController(animal){
|
|
9
|
-
let target=null,speed=animal.gears.walk;
|
|
10
|
-
return {get target(){return target;},get speed(){return speed;},setSpeed(kind){
|
|
11
|
-
if(!['walk','trot','gallop'].includes(kind))throw Error('Unknown gait');
|
|
12
|
-
speed=animal.gears[kind];if(target)animal.moveTo(target,{speed});
|
|
13
|
-
},go(x,z){
|
|
14
|
-
if(![x,z].every(Number.isFinite))throw Error('Invalid ground target');
|
|
15
|
-
target=new THREE.Vector3(Math.max(-.8,Math.min(.8,x)),0,Math.max(-1.7,Math.min(.3,z)));
|
|
16
|
-
animal.stopAction();animal.moveTo(target,{speed});
|
|
17
|
-
},stop(){target=null;animal.stop();},update(dt){
|
|
18
|
-
if(!Number.isFinite(dt)||dt<0||dt>.1)throw Error('Invalid lab step');animal.update(dt);
|
|
19
|
-
if(target&&animal.position.distanceTo(target)<.025&&animal.speed<.03){target=null;animal.stop();}
|
|
20
|
-
}};
|
|
21
|
-
}
|
package/locomotion-puppy.animal
DELETED
|
Binary file
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "procedural-animals",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"description": "24 fully procedural animals for three.js: SDF-sculpted bodies, dual-quaternion skinning, GPU fur, feathers and scales, IK locomotion and actions. No models, no textures, no downloads.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Majid Manzarpour",
|
|
7
|
-
"homepage": "https://majidmanzarpour.github.io/threejs-procedural-animals/",
|
|
8
|
-
"repository": { "type": "git", "url": "git+https://github.com/majidmanzarpour/threejs-procedural-animals.git" },
|
|
9
|
-
"bugs": { "url": "https://github.com/majidmanzarpour/threejs-procedural-animals/issues" },
|
|
10
|
-
"type": "module",
|
|
11
|
-
"main": "./src/index.js",
|
|
12
|
-
"module": "./src/index.js",
|
|
13
|
-
"types": "./src/index.d.ts",
|
|
14
|
-
"exports": {
|
|
15
|
-
".": { "types": "./src/index.d.ts", "default": "./src/index.js" },
|
|
16
|
-
"./species/*": "./src/species/*/index.js",
|
|
17
|
-
"./worker": "./src/worker.js",
|
|
18
|
-
"./bake": "./src/bake.js",
|
|
19
|
-
"./package.json": "./package.json"
|
|
20
|
-
},
|
|
21
|
-
"bin": { "procedural-animals-bake": "./tools/bake.mjs" },
|
|
22
|
-
"sideEffects": ["./src/worker.js"],
|
|
23
|
-
"files": [
|
|
24
|
-
"src/**/*.js",
|
|
25
|
-
"src/index.d.ts",
|
|
26
|
-
"tools/bake.mjs",
|
|
27
|
-
"README.md",
|
|
28
|
-
"LICENSE",
|
|
29
|
-
"llms.txt"
|
|
30
|
-
],
|
|
31
|
-
"engines": { "node": ">=18" },
|
|
32
|
-
"peerDependencies": { "three": ">=0.160.0" },
|
|
33
|
-
"devDependencies": { "esbuild": "^0.28.2", "playwright-core": "1.56", "three": "^0.186.0" },
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build:showcase": "node showcase/build.mjs",
|
|
36
|
-
"build:pages": "node showcase/build.mjs --pages",
|
|
37
|
-
"test": "node tools/test.mjs"
|
|
38
|
-
},
|
|
39
|
-
"keywords": ["three", "threejs", "procedural", "animals", "animation", "ik", "fur", "sdf", "creatures", "gamedev", "webgl"]
|
|
40
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
// The public Animal: build data + render object + motion engine behind a small game-facing API.
|
|
2
|
-
import * as THREE from 'three';
|
|
3
|
-
import { createAnimalObject } from './core/render/animalObject.js';
|
|
4
|
-
import { createMotion } from './core/motion/index.js';
|
|
5
|
-
|
|
6
|
-
const _v = new THREE.Vector3();
|
|
7
|
-
|
|
8
|
-
export class Animal {
|
|
9
|
-
constructor(species, data, opts = {}) {
|
|
10
|
-
this.species = species;
|
|
11
|
-
this.id = species.id;
|
|
12
|
-
this.data = data;
|
|
13
|
-
this.seed = data.seed;
|
|
14
|
-
this.params = data.params;
|
|
15
|
-
this.quality = opts.quality || data.quality;
|
|
16
|
-
const ground = opts.ground || (() => 0);
|
|
17
|
-
this.ground = ground;
|
|
18
|
-
this.water = opts.water || null;
|
|
19
|
-
this.render = createAnimalObject(data, species, { quality: this.quality, castShadow: opts.castShadow, receiveShadow: opts.receiveShadow });
|
|
20
|
-
/** Add this to your scene. Keep its own transform at identity: the animal moves by itself. */
|
|
21
|
-
this.object = this.render.group;
|
|
22
|
-
this.object.userData.animal = this;
|
|
23
|
-
this._listeners = new Map();
|
|
24
|
-
this.motion = createMotion(species.plan, {
|
|
25
|
-
species, data, skeleton: this.render.skeleton, ground, water: this.water, perches: opts.perches,
|
|
26
|
-
position: opts.position, heading: opts.heading ?? 0,
|
|
27
|
-
emit: (type, payload) => this._emit(type, payload),
|
|
28
|
-
});
|
|
29
|
-
this.attachments = {};
|
|
30
|
-
this._attachDefs = [];
|
|
31
|
-
for (const [name, def] of Object.entries(this.motion.attachmentPoints || {})) {
|
|
32
|
-
const o = new THREE.Object3D();
|
|
33
|
-
o.name = name;
|
|
34
|
-
o.matrixAutoUpdate = false;
|
|
35
|
-
this.object.add(o);
|
|
36
|
-
this.attachments[name] = o;
|
|
37
|
-
this._attachDefs.push({ o, ...def });
|
|
38
|
-
}
|
|
39
|
-
this.stats = { vertices: data.nV, triangles: data.index.length / 3, bones: data.bones.length, buildMs: data.stats?.buildMs || 0, updateMs: 0 };
|
|
40
|
-
this.time = 0;
|
|
41
|
-
this._lastDt = 1 / 60;
|
|
42
|
-
this.update(0);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// ---------------------------------------------------------------- control
|
|
46
|
-
/** Locomotion: the animal moves itself. speed in m/s, heading in radians (0 = +Z). */
|
|
47
|
-
move({ speed = 0, heading, climb = 0, gait } = {}) {
|
|
48
|
-
const inp = this.motion.input;
|
|
49
|
-
inp.follow = null;
|
|
50
|
-
inp.target = null;
|
|
51
|
-
inp.speed = Math.max(0, speed);
|
|
52
|
-
if (heading !== undefined) inp.heading = heading;
|
|
53
|
-
inp.climb = climb;
|
|
54
|
-
inp.gait = gait || null;
|
|
55
|
-
return this;
|
|
56
|
-
}
|
|
57
|
-
stop() { return this.move({ speed: 0 }); }
|
|
58
|
-
/** Walk / run to a point and stop there. */
|
|
59
|
-
moveTo(point, { speed = 3 } = {}) {
|
|
60
|
-
const inp = this.motion.input;
|
|
61
|
-
inp.follow = null;
|
|
62
|
-
inp.target = point ? new THREE.Vector3().copy(point) : null;
|
|
63
|
-
inp.targetSpeed = speed;
|
|
64
|
-
return this;
|
|
65
|
-
}
|
|
66
|
-
/** Match your own physics body: the animal is placed at `position` and animates for `velocity`. */
|
|
67
|
-
follow(position, velocity, { heading } = {}) {
|
|
68
|
-
const inp = this.motion.input;
|
|
69
|
-
inp.follow = inp.follow || { position: new THREE.Vector3(), velocity: new THREE.Vector3(), heading: null };
|
|
70
|
-
inp.follow.position.copy(position);
|
|
71
|
-
inp.follow.velocity.copy(velocity || _v.set(0, 0, 0));
|
|
72
|
-
inp.follow.heading = heading ?? null;
|
|
73
|
-
return this;
|
|
74
|
-
}
|
|
75
|
-
/** Look at a world point (Vector3) or stop looking (null). The animal keeps tracking the point you pass. */
|
|
76
|
-
lookAt(target) {
|
|
77
|
-
this.motion.input.look = target ? (target.isVector3 ? target : new THREE.Vector3().copy(target)) : null;
|
|
78
|
-
return this;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Play an action ('jump', 'sit', 'lie', 'sleep', 'eat', 'drink', 'attack', 'hit', 'death', 'stand', ...).
|
|
82
|
-
* A new one-shot interrupts the running one; postures cross-fade.
|
|
83
|
-
* Resolves with 'done' | 'interrupted' | 'stopped' | 'refused'.
|
|
84
|
-
*/
|
|
85
|
-
play(name, opts = {}) { return this.motion.play(name, opts); }
|
|
86
|
-
/** End an action (a posture: stand up), or every action but death when no name is given. */
|
|
87
|
-
stopAction(name) { this.motion.stop(name); return this; }
|
|
88
|
-
get actions() { return this.motion.actions; }
|
|
89
|
-
get gaits() { return this.motion.gaits; }
|
|
90
|
-
/** Named speeds for UIs ({ walk, trot, ... } in m/s, scaled to this individual's size). */
|
|
91
|
-
get gears() { return this.motion.gears || {}; }
|
|
92
|
-
|
|
93
|
-
// ---------------------------------------------------------------- events
|
|
94
|
-
on(type, fn) {
|
|
95
|
-
if (!this._listeners.has(type)) this._listeners.set(type, new Set());
|
|
96
|
-
this._listeners.get(type).add(fn);
|
|
97
|
-
return () => this.off(type, fn);
|
|
98
|
-
}
|
|
99
|
-
off(type, fn) { this._listeners.get(type)?.delete(fn); return this; }
|
|
100
|
-
_emit(type, payload) {
|
|
101
|
-
const set = this._listeners.get(type);
|
|
102
|
-
if (set) for (const fn of set) fn({ type, animal: this, ...payload });
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// ---------------------------------------------------------------- state
|
|
106
|
-
get position() { return this.motion.state.position; }
|
|
107
|
-
get heading() { return this.motion.state.heading; }
|
|
108
|
-
get speed() { return this.motion.state.speed; }
|
|
109
|
-
get velocity() { return this.motion.state.velocity; }
|
|
110
|
-
get state() { return this.motion.state; }
|
|
111
|
-
|
|
112
|
-
// ---------------------------------------------------------------- frame
|
|
113
|
-
/** Call once per frame with the frame time in seconds. */
|
|
114
|
-
update(dt) {
|
|
115
|
-
const t0 = now();
|
|
116
|
-
if (!(dt >= 0) || !Number.isFinite(dt)) dt = 0;
|
|
117
|
-
dt = Math.min(dt, 0.1);
|
|
118
|
-
this.time += dt;
|
|
119
|
-
this.motion.update(dt);
|
|
120
|
-
this.render.update({ dt, time: this.time, motion: this.motion });
|
|
121
|
-
for (const a of this._attachDefs) {
|
|
122
|
-
const b = this.render.skeleton.byName[a.bone];
|
|
123
|
-
if (!b) continue;
|
|
124
|
-
a.o.matrix.multiplyMatrices(b.matrixWorld, a.local);
|
|
125
|
-
a.o.matrixWorld.multiplyMatrices(this.object.matrixWorld, a.o.matrix);
|
|
126
|
-
a.o.matrixWorldNeedsUpdate = false;
|
|
127
|
-
}
|
|
128
|
-
this.stats.updateMs = this.stats.updateMs * 0.9 + (now() - t0) * 0.1;
|
|
129
|
-
return this;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
setQuality(q) { this.render.setQuality?.(q); this.quality = q; return this; }
|
|
133
|
-
/** Motion level of detail: 0 full, 1 reduced secondary motion, 2 crowd (half-rate IK). */
|
|
134
|
-
setLod(level) { this.motion.setLod?.(level); this.lod = level; return this; }
|
|
135
|
-
setDebug(mode, on) { this.render.setDebug?.(mode, on); return this; }
|
|
136
|
-
/** Show or hide the coverings (fur shells and silhouette fins); the base still reads as fur. */
|
|
137
|
-
setCoverings(on) { this.render.setCoverings?.(on); return this; }
|
|
138
|
-
|
|
139
|
-
dispose() {
|
|
140
|
-
this.render.dispose();
|
|
141
|
-
this.motion.dispose?.();
|
|
142
|
-
this._listeners.clear();
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const now = () => (typeof performance !== 'undefined' ? performance.now() : Date.now());
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// .animal bake files: a built individual, stored so it loads instantly (no SDF meshing, no weights).
|
|
2
|
-
// Layout: "PANM" | u32 version | u32 headerBytes | header JSON (utf-8, padded to 8) | array blobs (8-aligned)
|
|
3
|
-
import { ARRAY_KEYS, FORMAT_VERSION } from './core/build/pipeline.js';
|
|
4
|
-
|
|
5
|
-
const TYPES = { Float32Array, Uint32Array, Uint16Array, Int16Array, Uint8Array, Int8Array, Int32Array };
|
|
6
|
-
|
|
7
|
-
export function writeBake(data) {
|
|
8
|
-
const header = {};
|
|
9
|
-
for (const [k, v] of Object.entries(data)) if (!ARRAY_KEYS.includes(k)) header[k] = v;
|
|
10
|
-
header.arrays = [];
|
|
11
|
-
let offset = 0;
|
|
12
|
-
for (const k of ARRAY_KEYS) {
|
|
13
|
-
const a = data[k];
|
|
14
|
-
header.arrays.push({ key: k, type: a.constructor.name, length: a.length, offset });
|
|
15
|
-
offset += Math.ceil(a.byteLength / 8) * 8;
|
|
16
|
-
}
|
|
17
|
-
let json = new TextEncoder().encode(JSON.stringify(header));
|
|
18
|
-
const hb = Math.ceil((json.length) / 8) * 8;
|
|
19
|
-
const total = 12 + hb + offset;
|
|
20
|
-
const buf = new ArrayBuffer(Math.ceil(total / 8) * 8 + 8);
|
|
21
|
-
const u8 = new Uint8Array(buf);
|
|
22
|
-
u8.set([80, 65, 78, 77], 0); // "PANM"
|
|
23
|
-
const dv = new DataView(buf);
|
|
24
|
-
dv.setUint32(4, FORMAT_VERSION, true);
|
|
25
|
-
dv.setUint32(8, hb, true);
|
|
26
|
-
u8.set(json, 12);
|
|
27
|
-
for (let i = 12 + json.length; i < 12 + hb; i++) u8[i] = 32;
|
|
28
|
-
const base = 16 + hb - ((16 + hb) % 8 === 0 ? 0 : (16 + hb) % 8) ;
|
|
29
|
-
// blobs start at an 8-aligned offset after the header
|
|
30
|
-
const start = align8(12 + hb);
|
|
31
|
-
for (const a of header.arrays) u8.set(new Uint8Array(data[a.key].buffer, data[a.key].byteOffset, data[a.key].byteLength), start + a.offset);
|
|
32
|
-
void base;
|
|
33
|
-
return buf.slice(0, start + offset);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function readBake(buf) {
|
|
37
|
-
const u8 = new Uint8Array(buf);
|
|
38
|
-
if (u8[0] !== 80 || u8[1] !== 65 || u8[2] !== 78 || u8[3] !== 77) throw new Error('not a .animal file');
|
|
39
|
-
const dv = new DataView(buf);
|
|
40
|
-
const version = dv.getUint32(4, true);
|
|
41
|
-
if (version !== FORMAT_VERSION) throw new Error(`.animal version ${version} is not supported (expected ${FORMAT_VERSION})`);
|
|
42
|
-
const hb = dv.getUint32(8, true);
|
|
43
|
-
const header = JSON.parse(new TextDecoder().decode(u8.subarray(12, 12 + hb)));
|
|
44
|
-
const start = align8(12 + hb);
|
|
45
|
-
const data = { ...header };
|
|
46
|
-
for (const a of header.arrays) data[a.key] = new TYPES[a.type](buf, start + a.offset, a.length);
|
|
47
|
-
delete data.arrays;
|
|
48
|
-
return data;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const align8 = (n) => Math.ceil(n / 8) * 8;
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// Builds an individual's data: in Web Workers when possible (meshing jobs in parallel, then
|
|
2
|
-
// weights + coat), otherwise on the calling thread. Results are cached per (species, seed, quality,
|
|
3
|
-
// sex, age) so a herd that reuses individuals shares geometry.
|
|
4
|
-
import { buildSync, normaliseOptions } from './core/build/pipeline.js';
|
|
5
|
-
import { loadSpecies } from './registry.js';
|
|
6
|
-
|
|
7
|
-
let defaultWorkerFactory = null;
|
|
8
|
-
export function setWorkerFactory(fn) { defaultWorkerFactory = fn; }
|
|
9
|
-
|
|
10
|
-
function makeWorker(opt) {
|
|
11
|
-
if (opt === false) return null;
|
|
12
|
-
try {
|
|
13
|
-
if (typeof opt === 'function') return opt();
|
|
14
|
-
if (defaultWorkerFactory) return defaultWorkerFactory();
|
|
15
|
-
if (typeof Worker === 'undefined') return null;
|
|
16
|
-
return new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
|
|
17
|
-
} catch (e) {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const cache = new Map();
|
|
23
|
-
export function cacheKey(speciesId, opts) {
|
|
24
|
-
const o = normaliseOptions(opts);
|
|
25
|
-
return [speciesId, o.seed, o.quality, o.sex || '', o.age || '', o.variant || '', JSON.stringify(o.overrides)].join('|');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export async function buildAnimalData(species, opts = {}, onProgress = () => {}) {
|
|
29
|
-
const s = await loadSpecies(species);
|
|
30
|
-
const key = cacheKey(s.id, opts);
|
|
31
|
-
if (cache.has(key)) return cache.get(key);
|
|
32
|
-
const p = buildInWorkers(s, opts, onProgress).catch((e) => { cache.delete(key); throw e; });
|
|
33
|
-
cache.set(key, p);
|
|
34
|
-
return p;
|
|
35
|
-
}
|
|
36
|
-
export function clearBuildCache() { cache.clear(); }
|
|
37
|
-
|
|
38
|
-
async function buildInWorkers(species, opts, onProgress) {
|
|
39
|
-
const o = normaliseOptions(opts);
|
|
40
|
-
const nJobs = species.regions ? null : 1;
|
|
41
|
-
void nJobs;
|
|
42
|
-
const first = makeWorker(opts.worker);
|
|
43
|
-
if (!first) {
|
|
44
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
45
|
-
return buildSync(species, o, onProgress);
|
|
46
|
-
}
|
|
47
|
-
const t0 = performance.now();
|
|
48
|
-
// how many meshing jobs? (cheap: prepare runs the sculpt only)
|
|
49
|
-
const { prepare } = await import('./core/build/pipeline.js');
|
|
50
|
-
const jobsN = prepare(species, o).reg.jobs.length;
|
|
51
|
-
const workers = [first];
|
|
52
|
-
for (let i = 1; i < jobsN; i++) { const w = makeWorker(opts.worker); if (w) workers.push(w); }
|
|
53
|
-
const call = (w, msg) => new Promise((resolve, reject) => {
|
|
54
|
-
w.onmessage = (e) => {
|
|
55
|
-
const m = e.data;
|
|
56
|
-
if (m.type === 'progress') onProgress(m.msg);
|
|
57
|
-
else if (m.type === 'error') reject(new Error(m.message));
|
|
58
|
-
else resolve(m);
|
|
59
|
-
};
|
|
60
|
-
w.onerror = (e) => reject(new Error(e.message || 'procedural-animals worker failed'));
|
|
61
|
-
w.postMessage(msg);
|
|
62
|
-
});
|
|
63
|
-
try {
|
|
64
|
-
const jobs = new Array(jobsN);
|
|
65
|
-
let next = 0;
|
|
66
|
-
await Promise.all(workers.map(async (w) => {
|
|
67
|
-
while (next < jobsN) {
|
|
68
|
-
const j = next++;
|
|
69
|
-
onProgress('mesh ' + j);
|
|
70
|
-
const r = await call(w, { task: 'mesh', species: species.id, opts: o, job: j });
|
|
71
|
-
jobs[j] = r.parts;
|
|
72
|
-
}
|
|
73
|
-
}));
|
|
74
|
-
onProgress('finish');
|
|
75
|
-
const r = await call(first, { task: 'finish', species: species.id, opts: o, jobs });
|
|
76
|
-
r.data.stats.buildMs = Math.round(performance.now() - t0);
|
|
77
|
-
return r.data;
|
|
78
|
-
} finally {
|
|
79
|
-
for (const w of workers) w.terminate();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
// Helpers for species coat painters.
|
|
2
|
-
import { SDFModel } from '../sdf/sdf.js';
|
|
3
|
-
import { sub, add, mul, dot, len, clamp, mix } from '../math/vec.js';
|
|
4
|
-
|
|
5
|
-
// sRGB hex -> linear rgb
|
|
6
|
-
export const srgb = (hex) => {
|
|
7
|
-
const c = [(hex >> 16) & 255, (hex >> 8) & 255, hex & 255].map((v) => v / 255);
|
|
8
|
-
return c.map((v) => (v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4)));
|
|
9
|
-
};
|
|
10
|
-
export const mix3 = (a, b, t) => [mix(a[0], b[0], t), mix(a[1], b[1], t), mix(a[2], b[2], t)];
|
|
11
|
-
export const scale3 = (a, k) => [a[0] * k, a[1] * k, a[2] * k];
|
|
12
|
-
|
|
13
|
-
// Material ids stored in tint.w (see render/coatMaterial.js)
|
|
14
|
-
export const MAT = { FUR: 0, NOSE: 1, DARK_SKIN: 2, MOUTH: 3, SKIN: 4, KERATIN: 5, SCALES: 6, WET_SKIN: 7, CHITIN: 8, FEATHER: 9, VANE: 10, FIN: 11 }; // 10: flight-feather vane (featherCards.js), 11: fin membrane (render/membranes.js)
|
|
15
|
-
|
|
16
|
-
// Newton-project a point onto the zero set of a primitive list
|
|
17
|
-
export function projectToSurface(prims, q, iters = 6) {
|
|
18
|
-
let x = q.slice();
|
|
19
|
-
for (let i = 0; i < iters; i++) {
|
|
20
|
-
const d = SDFModel.evalList(prims, x[0], x[1], x[2]);
|
|
21
|
-
const e = 0.0005;
|
|
22
|
-
const g = [
|
|
23
|
-
SDFModel.evalList(prims, x[0] + e, x[1], x[2]) - d,
|
|
24
|
-
SDFModel.evalList(prims, x[0], x[1] + e, x[2]) - d,
|
|
25
|
-
SDFModel.evalList(prims, x[0], x[1], x[2] + e) - d,
|
|
26
|
-
].map((c) => c / e);
|
|
27
|
-
const gl = len(g) || 1;
|
|
28
|
-
x = sub(x, mul(g, d / (gl * gl)));
|
|
29
|
-
}
|
|
30
|
-
return x;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// distance from p to a polyline with per-vertex half widths (negative inside)
|
|
34
|
-
export function distPolyline(p, poly, widths) {
|
|
35
|
-
let best = 1e9;
|
|
36
|
-
for (let i = 0; i < poly.length - 1; i++) {
|
|
37
|
-
const a = poly[i], b = poly[i + 1], ab = sub(b, a);
|
|
38
|
-
const t = clamp(dot(sub(p, a), ab) / dot(ab, ab), 0, 1);
|
|
39
|
-
const d = len(sub(p, add(a, mul(ab, t)))) - mix(widths[i], widths[i + 1], t);
|
|
40
|
-
if (d < best) best = d;
|
|
41
|
-
}
|
|
42
|
-
return best;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Laplacian smoothing of a scalar field over the mesh graph; `lock(v)` keeps a vertex fixed
|
|
46
|
-
export function smoothField(f, neighbors, iters = 2, lock = null) {
|
|
47
|
-
const n = f.length, tmp = new Float32Array(n);
|
|
48
|
-
for (let it = 0; it < iters; it++) {
|
|
49
|
-
for (let v = 0; v < n; v++) {
|
|
50
|
-
const ns = neighbors[v];
|
|
51
|
-
if (!ns.length || (lock && lock(v))) { tmp[v] = f[v]; continue; }
|
|
52
|
-
let a = 0;
|
|
53
|
-
for (const u of ns) a += f[u];
|
|
54
|
-
tmp[v] = 0.5 * f[v] + (0.5 * a) / ns.length;
|
|
55
|
-
}
|
|
56
|
-
f.set(tmp);
|
|
57
|
-
}
|
|
58
|
-
return f;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Poisson-disc features grown on the surface. radius(v) -> r (0 = no feature here);
|
|
62
|
-
// returns { spots, grid, cell, key } for use with featureField.
|
|
63
|
-
export function poissonFeatures({ pos, nrm, nV, radius, R, spacing = 1.52, gap = 0.0025, cell = 0.03, skip = null, extra = null }) {
|
|
64
|
-
const order = new Uint32Array(nV);
|
|
65
|
-
for (let i = 0; i < nV; i++) order[i] = i;
|
|
66
|
-
for (let i = nV - 1; i > 0; i--) { const j = Math.floor(R() * (i + 1)); const t = order[i]; order[i] = order[j]; order[j] = t; }
|
|
67
|
-
const grid = new Map();
|
|
68
|
-
const key = (x, y, z) => ((x + 512) * 1024 + (y + 512)) * 1024 + (z + 512);
|
|
69
|
-
const spots = [];
|
|
70
|
-
for (let k = 0; k < nV; k++) {
|
|
71
|
-
const v = order[k];
|
|
72
|
-
if (skip && skip(v)) continue;
|
|
73
|
-
let r = radius(v);
|
|
74
|
-
if (!(r > 0)) continue;
|
|
75
|
-
r *= 0.78 + 0.44 * R();
|
|
76
|
-
const p = [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
|
|
77
|
-
const gx = Math.floor(p[0] / cell), gy = Math.floor(p[1] / cell), gz = Math.floor(p[2] / cell);
|
|
78
|
-
let ok = true;
|
|
79
|
-
for (let dz = -1; dz <= 1 && ok; dz++)
|
|
80
|
-
for (let dy = -1; dy <= 1 && ok; dy++)
|
|
81
|
-
for (let dx = -1; dx <= 1 && ok; dx++) {
|
|
82
|
-
const arr = grid.get(key(gx + dx, gy + dy, gz + dz));
|
|
83
|
-
if (!arr) continue;
|
|
84
|
-
for (const s of arr) {
|
|
85
|
-
const minD = spacing * (r + s.r) + gap;
|
|
86
|
-
const ddx = p[0] - s.p[0], ddy = p[1] - s.p[1], ddz = p[2] - s.p[2];
|
|
87
|
-
if (ddx * ddx + ddy * ddy + ddz * ddz < minD * minD) { ok = false; break; }
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (!ok) continue;
|
|
91
|
-
const s = { v, p, r, n: [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]], ...(extra ? extra(v, R) : {}) };
|
|
92
|
-
spots.push(s);
|
|
93
|
-
const kk = key(gx, gy, gz);
|
|
94
|
-
(grid.get(kk) || grid.set(kk, []).get(kk)).push(s);
|
|
95
|
-
}
|
|
96
|
-
return { spots, grid, cell, key };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Signed distance to the nearest feature of a poissonFeatures() set, elongated along each
|
|
100
|
-
// feature's comb direction `c` by `el`. Returns [sdf, feature].
|
|
101
|
-
export function featureDistance(F, p, n, wob = 1) {
|
|
102
|
-
const { grid, cell, key } = F;
|
|
103
|
-
let best = 1e9, bs = null;
|
|
104
|
-
const gx = Math.floor(p[0] / cell), gy = Math.floor(p[1] / cell), gz = Math.floor(p[2] / cell);
|
|
105
|
-
for (let dz = -1; dz <= 1; dz++)
|
|
106
|
-
for (let dy = -1; dy <= 1; dy++)
|
|
107
|
-
for (let dx = -1; dx <= 1; dx++) {
|
|
108
|
-
const arr = grid.get(key(gx + dx, gy + dy, gz + dz));
|
|
109
|
-
if (!arr) continue;
|
|
110
|
-
for (const s of arr) {
|
|
111
|
-
if (dot(s.n, n) < 0.2) continue;
|
|
112
|
-
const d = sub(p, s.p);
|
|
113
|
-
const el = s.el || 1;
|
|
114
|
-
const along = s.c ? dot(d, s.c) : 0;
|
|
115
|
-
const dd = Math.sqrt(Math.max(0, dot(d, d) - along * along * (1 - 1 / (el * el))));
|
|
116
|
-
const sd = dd - s.r * wob;
|
|
117
|
-
if (sd < best) { best = sd; bs = s; }
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return [best, bs];
|
|
121
|
-
}
|