@bitefoundry/retrieviewer 0.0.3 → 0.0.5
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 +11 -0
- package/ASSETS-LICENSE.md +2 -1
- package/ATTRIBUTION.md +6 -1
- package/COMFORT-ART.md +95 -0
- package/DESKTOP-PLATFORMS.md +6 -1
- package/INSTALLING.md +4 -0
- package/PLUGIN.md +13 -10
- package/PREREQUISITES.md +95 -0
- package/PUPPY-MEMORY.md +102 -1
- package/README.ko.md +116 -0
- package/README.md +121 -59
- package/RELEASING.md +33 -13
- package/THIRD-PARTY-NOTICES.md +5 -3
- package/braille.mjs +35 -9
- package/examples/live.mjs +2 -2
- package/integrations/linux-overlay/overlay.py +8 -2
- package/integrations/macos-overlay/overlay.jxa +9 -7
- package/integrations/opencode/runtime.mjs +4 -4
- package/integrations/opencode/tui.tsx +1 -1
- package/integrations/windows-overlay/Overlay.cs +7 -6
- package/integrations/windows-overlay/desktop.mjs +2 -1
- package/integrations/windows-overlay/protocol.mjs +13 -7
- package/integrations/windows-overlay/worker.mjs +1 -1
- package/package.json +34 -13
- package/puppy-comfort-sprites.mjs +33 -0
- package/puppy-companion-comfort-q5.bin.gz +0 -0
- package/puppy-companion-comfort.json +140 -0
- package/puppy-companion-life.mjs +58 -18
- package/puppy-companion-objects.mjs +12 -0
- package/puppy-companion-sprites.mjs +4 -2
- package/puppy-frame-predictive.mjs +15 -9
- package/puppy-frame-store.mjs +2 -2
- package/puppy-paper-actions.mjs +4 -2
- package/puppy-paper-behaviors.mjs +1 -1
- package/puppy-paper-contact.mjs +4 -2
- package/puppy-paper-seat.mjs +4 -2
- package/puppy-paper-world.mjs +14 -0
- package/puppy-paper.mjs +79 -21
- package/retrieviewer-cli.mjs +8 -6
- package/retrieviewer-plugin.d.mts +1 -1
- package/retrieviewer-plugin.mjs +1 -1
- package/retrieviewer.d.mts +12 -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/puppy-paper.mjs
CHANGED
|
@@ -8,13 +8,31 @@ import {createPaperAreaSampler} from './puppy-paper-area.mjs';
|
|
|
8
8
|
import {PAPER_ACTION_DURATIONS,createPaperActionSampler,paperActionPose} from './puppy-paper-actions.mjs';
|
|
9
9
|
import {createPawTransport} from './puppy-paper-paw-transport.mjs';
|
|
10
10
|
import {createCompanionSpriteSampler} from './puppy-companion-sprites.mjs';
|
|
11
|
+
import {createComfortSpriteSampler} from './puppy-comfort-sprites.mjs';
|
|
11
12
|
import {mouthSourceFrame,mouthAnchor,projectMouth} from './puppy-mouth-anchor.mjs';
|
|
13
|
+
import {trickClipEdges} from './puppy-paper-trick-timing.mjs';
|
|
12
14
|
|
|
13
15
|
// Fixed viewer: slightly closer/wider lens gives depth without changing the art.
|
|
14
16
|
export {PAPER_CAMERA} from './puppy-paper-world.mjs';
|
|
15
17
|
|
|
18
|
+
// Comfort poses reuse existing clips only. Belly holds the calibrated roll-down
|
|
19
|
+
// source exactly at the start of the edge leaving `key` (paws-up belly), easing
|
|
20
|
+
// in/out with zero speed at the hold. `hold` defaults to the v9 keyOrder/edge
|
|
21
|
+
// boundary of roll7; createPaperComfortTimings re-derives it from action info.
|
|
22
|
+
export const PAPER_COMFORT_LIMITS=Object.freeze({belly:30,sleep:120,tug:30});
|
|
23
|
+
export const PAPER_COMFORT_TIMINGS=Object.freeze({belly:Object.freeze({key:'roll7',hold:.4449823183425605,enter:1.3,exit:1.7}),tugExit:.35});
|
|
24
|
+
export function createPaperComfortTimings(actionInfo,{key=PAPER_COMFORT_TIMINGS.belly.key}={}){
|
|
25
|
+
const {keys,bounds}=trickClipEdges('roll-down',actionInfo),i=keys.findIndex(k=>k[0]===key);
|
|
26
|
+
if(i<=0)throw Error(`Invalid comfort timing: no roll-down edge leaves ${key}`);
|
|
27
|
+
return Object.freeze({...PAPER_COMFORT_TIMINGS,belly:Object.freeze({...PAPER_COMFORT_TIMINGS.belly,key,hold:bounds[i]})});
|
|
28
|
+
}
|
|
29
|
+
const comfortDuration=(name,seconds)=>{if(typeof seconds!=='number'||!Number.isFinite(seconds)||seconds<=0||seconds>PAPER_COMFORT_LIMITS[name])throw Error(`Invalid ${name} duration`);};
|
|
30
|
+
const comfortEase=u=>u<=0?0:u>=1?1:u*u*(3-2*u);
|
|
31
|
+
|
|
16
32
|
// Original artwork only. Physics supplies ground travel; no procedural dog is drawn.
|
|
17
|
-
export function createPaperActor(animal,info,{manualTricks=false,trickTimings=null,walkTransitions=false}={}){
|
|
33
|
+
export function createPaperActor(animal,info,{manualTricks=false,trickTimings=null,walkTransitions=false,comfortTimings=PAPER_COMFORT_TIMINGS}={}){
|
|
34
|
+
const bellyTiming=comfortTimings?.belly;
|
|
35
|
+
if(!bellyTiming||![bellyTiming.hold,bellyTiming.enter,bellyTiming.exit,comfortTimings.tugExit].every(Number.isFinite)||bellyTiming.hold<=0||bellyTiming.hold>=1||bellyTiming.enter<=0||bellyTiming.exit<=0||comfortTimings.tugExit<=0)throw Error('Invalid comfort timing');
|
|
18
36
|
let state='idle',age=0,phase=0,flip=false,nextFlip=false,target=null,route=false,routeIndex=0,view='front',nextView='front',turnView='front',turnFlip=false;
|
|
19
37
|
let held=null,waypoints=[];
|
|
20
38
|
let preparationRate=1;
|
|
@@ -22,6 +40,7 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
22
40
|
let straight=false,seatProgress=0,seatVariant='default',travel=null,approachPlan=null,travelAge=0,travelDistance=0,motionSpeed=0,motionStage='rest',completed=false;
|
|
23
41
|
let watchSide='left',restProgress=0,idleHold=2.5;
|
|
24
42
|
let pendingAction=null,activeAction=null,actionDone=null,deferred=null,movementMode='run',stayDown=false,manualDown=false,spinReady=false;
|
|
43
|
+
let comfort=null,comfortSeconds=0,sleepLook=null,nextSleepLook={variant:'side',right:false};
|
|
25
44
|
const director=createPaperDirector();
|
|
26
45
|
const points=[[.12,PAPER_GROUND.away]];
|
|
27
46
|
const enter=s=>{state=s;age=0;};
|
|
@@ -33,13 +52,31 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
33
52
|
const turnSeconds=()=>snappyPreparation()?.22:.85;
|
|
34
53
|
const launchSeconds=()=>snappyPreparation()?.10:.25;
|
|
35
54
|
const dispatch=(fn,{keepDown=false}={})=>{
|
|
36
|
-
if(activeAction||(movementMode==='walk'&&state==='run')){route=false;deferred=()=>dispatch(fn,{keepDown});return true;}
|
|
55
|
+
if(activeAction||state==='tug'||(movementMode==='walk'&&state==='run')){route=false;deferred=()=>dispatch(fn,{keepDown});hurryComfort();return true;}
|
|
37
56
|
// The calibrated trick down pose has its own byte-shared recovery; never
|
|
38
57
|
// jump back into the older/larger rest illustration on another command.
|
|
39
58
|
if(manualDown&&!keepDown){route=false;deferred=fn;playAction('lie-up',()=>{manualDown=false;view='approach';seatProgress=1;seatVariant='default';enter('idle');drain();},.55);return true;}
|
|
40
59
|
return fn();
|
|
41
60
|
};
|
|
42
61
|
const drain=()=>{const fn=deferred;deferred=null;if(fn){fn();return true;}return false;};
|
|
62
|
+
// Belly: 0 -> hold eased to rest, held exact source sample, eased hold -> 1.
|
|
63
|
+
// Queued commands cut the remaining hold only; the roll always finishes at seat.
|
|
64
|
+
const bellyAction=seconds=>{
|
|
65
|
+
const {hold:H,enter:a,exit:b}=bellyTiming,t={comfort:'belly',hold:[a,a+seconds],duration:a+seconds+b};
|
|
66
|
+
t.progressAt=age=>{if(!Number.isFinite(age))throw Error('Invalid comfort age');return age<t.hold[0]?H*comfortEase(age/a):age<t.hold[1]?H:H+(1-H)*comfortEase((age-t.hold[1])/b);};
|
|
67
|
+
return t;
|
|
68
|
+
};
|
|
69
|
+
const startBelly=seconds=>{
|
|
70
|
+
manualDown=false;stayDown=false;restProgress=0;comfort=null;
|
|
71
|
+
const timing=bellyAction(seconds);activeAction={name:'roll-down',duration:timing.duration,timing,comfort:'belly'};actionDone=()=>finishTrick('roll');stop();enter('action');
|
|
72
|
+
if(deferred)hurryComfort();
|
|
73
|
+
};
|
|
74
|
+
const tugProgress=()=>{if(!comfort||comfort.kind!=='tug')return 0;const h=comfort.hurry;return h?(h.length>0?Math.min(1,h.progress+(1-h.progress)*(age-h.age)/h.length):1):Math.min(1,age/comfort.seconds);};
|
|
75
|
+
function hurryComfort(){
|
|
76
|
+
const t=activeAction?.timing;
|
|
77
|
+
if(t?.comfort==='belly'&&age<t.hold[1]){t.hold[1]=Math.max(t.hold[0],age);t.duration=activeAction.duration=t.hold[1]+bellyTiming.exit;}
|
|
78
|
+
else if(state==='tug'&&comfort&&!comfort.hurry){const progress=tugProgress();comfort.hurry={progress,age,length:Math.min(comfortTimings.tugExit,(1-progress)*comfort.seconds)};}
|
|
79
|
+
}
|
|
43
80
|
const playAction=(name,done,duration=PAPER_ACTION_DURATIONS[name])=>{const timing=trickTimings?.[name];duration=timing?.duration??duration;if(!Number.isFinite(duration)||duration<=0)throw Error('Invalid paper action duration');activeAction={name,duration,timing};actionDone=done;stop();enter('action');};
|
|
44
81
|
const finishTrick=name=>{if(name!=='spin'){view='approach';flip=false;seatProgress=1;seatVariant='default';enter('idle');}else{view='front';flip=false;seatProgress=0;enter('settle');}drain();};
|
|
45
82
|
const issue=(skipEntry=false)=>{
|
|
@@ -61,6 +98,9 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
61
98
|
if(walkTransitions&&name==='spin'&&!spinReady){spinReady=true;enter('launch');return;}
|
|
62
99
|
pendingAction=null;
|
|
63
100
|
if(name==='sit'){enter('idle');return;}
|
|
101
|
+
if(name==='belly'){const seconds=comfortSeconds;playAction('lie-down',()=>startBelly(seconds),.65);return;}
|
|
102
|
+
if(name==='sleep'){stayDown=false;sleepLook={...nextSleepLook};comfort={kind:'sleep',seconds:comfortSeconds};enter('recline');return;}
|
|
103
|
+
if(name==='tug'){stop();comfort={kind:'tug',seconds:comfortSeconds};enter('tug');return;}
|
|
64
104
|
if(name==='lie'){
|
|
65
105
|
if(manualTricks){playAction('lie-down',()=>{manualDown=true;enter('down');drain();},.65);}
|
|
66
106
|
else{stayDown=true;enter('recline');}
|
|
@@ -73,7 +113,9 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
73
113
|
preparationRate=1;
|
|
74
114
|
if(name==='roll'&&manualDown&&state==='down'){route=false;target=null;waypoints=[];pendingAction=null;stayDown=false;manualDown=false;restProgress=0;playAction('roll-down',()=>finishTrick('roll'));return true;}
|
|
75
115
|
if(name==='roll'&&state==='down'&&restProgress===.25){route=false;target=null;waypoints=[];pendingAction=null;stayDown=false;restProgress=0;playAction('roll-down',()=>finishTrick('roll'));return true;}
|
|
76
|
-
route=false;target=null;waypoints=[];pendingAction=
|
|
116
|
+
if(name==='belly'&&state==='down'&&(manualDown||restProgress===.25)){route=false;target=null;waypoints=[];pendingAction=null;startBelly(comfortSeconds);return true;}
|
|
117
|
+
if(name==='sleep'&&state==='sleep'&&comfort?.kind==='sleep'){comfort.seconds=age+comfortSeconds;return true;}
|
|
118
|
+
comfort=null;route=false;target=null;waypoints=[];pendingAction=name;spinReady=false;completed=false;stayDown=false;movementMode='run';stop();
|
|
77
119
|
if(restProgress>0||state==='recline'||state==='wake')enter('wake');
|
|
78
120
|
else if(['watch','view-turn','turn','arrival-exit'].includes(state)){/* reach a shared endpoint */}
|
|
79
121
|
else if(['run','finish','launch','settle'].includes(state))enter('finish');
|
|
@@ -87,7 +129,7 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
87
129
|
if(![x,z].every(Number.isFinite))throw Error('Invalid paper target');
|
|
88
130
|
preparationRate=urgent?3:1;
|
|
89
131
|
target=paperGroundTarget(x,z);
|
|
90
|
-
pendingAction=null;stayDown=false;movementMode=walking?'walk':'run';
|
|
132
|
+
pendingAction=null;stayDown=false;comfort=null;movementMode=walking?'walk':'run';
|
|
91
133
|
const dz=target[1]-animal.position.z,dx=target[0]-animal.position.x;
|
|
92
134
|
waypoints=[];
|
|
93
135
|
// Do not drag a straight rear/front drawing diagonally across the floor.
|
|
@@ -116,10 +158,14 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
116
158
|
walk(side,distance=1.05){if(!['left','right'].includes(side)||!Number.isFinite(distance)||distance<=0)throw Error('Invalid walk command');return dispatch(()=>{const [x,z]=paperWalkTarget(animal.position.x,animal.position.z,side==='left'?-1:1,distance);if(Math.abs(x-animal.position.x)<1e-6)return false;route=false;go(x,z,{walking:true});return true;});},
|
|
117
159
|
look(side='left'){if(state!=='idle'||seatProgress!==1)return false;route=false;watchSide=side==='right'?'right':'left';enter('watch');return true;},
|
|
118
160
|
lie(){const name=manualDown||activeAction?.name==='lie-down'||restProgress>0?'sit':'lie';return dispatch(()=>requestAction(name));},
|
|
119
|
-
|
|
161
|
+
// Comfort poses: bounded finite seconds; same dispatch safety as tricks.
|
|
162
|
+
belly(seconds=6){comfortDuration('belly',seconds);return dispatch(()=>{comfortSeconds=seconds;return requestAction('belly');},{keepDown:true});},
|
|
163
|
+
sleep(seconds=25,variant='side',right=false){comfortDuration('sleep',seconds);if(!['side','belly','curl','chin'].includes(variant)||typeof right!=='boolean')throw Error('Invalid sleep pose');return dispatch(()=>{comfortSeconds=seconds;nextSleepLook={variant,right};return requestAction('sleep');});},
|
|
164
|
+
tug(seconds=6){comfortDuration('tug',seconds);return dispatch(()=>{comfortSeconds=seconds;return requestAction('tug');});},
|
|
165
|
+
rest(){return dispatch(()=>{route=false;target=null;waypoints=[];pendingAction=null;stayDown=false;comfort=null;stop();if(restProgress>0){enter('wake');}else if(state==='watch'||state==='arrival-exit'){/* finish shared return */}else if(seatProgress>0||state==='rise'||state==='sit-down')enter('sit-down');else if(!['idle','view-turn','turn'].includes(state))enter('finish');});},
|
|
120
166
|
update(dt){
|
|
121
167
|
if(!Number.isFinite(dt)||dt<0||dt>.1)throw Error('Invalid paper step');
|
|
122
|
-
const
|
|
168
|
+
const beforeX=animal.position.x,beforeZ=animal.position.z;
|
|
123
169
|
if(state==='run'&&travel&&target){
|
|
124
170
|
travelAge+=dt;const p=travel.plan.at(travelAge),ratio=travel.plan.distance?p.distance/travel.plan.distance:1;
|
|
125
171
|
travelDistance=p.distance;motionSpeed=p.speed;motionStage=p.stage;
|
|
@@ -130,7 +176,7 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
130
176
|
animal.update(dt);
|
|
131
177
|
const prepareDt=dt*preparationRate;
|
|
132
178
|
age+=['view-turn','turn','rise','watch','arrival-exit','launch','wake'].includes(state)?prepareDt:dt;
|
|
133
|
-
const distance=Math.hypot(animal.position.x-
|
|
179
|
+
const distance=Math.hypot(animal.position.x-beforeX,animal.position.z-beforeZ);
|
|
134
180
|
director.update(dt,state==='run',distance);
|
|
135
181
|
if(state==='run'){
|
|
136
182
|
phase+=distance/travel.stride;
|
|
@@ -151,7 +197,9 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
151
197
|
else if(state==='rise'){seatProgress=Math.max(0,seatProgress-prepareDt/(snappyPreparation()?.28:.65));if(seatProgress===0){if(seatVariant==='arrival')enter('arrival-exit');else prepareDirection();}}
|
|
152
198
|
else if(state==='arrival-exit'&&age>=.22){seatVariant='default';prepareDirection();}
|
|
153
199
|
else if(state==='watch'&&age>=1.4){if(pendingAction)prepareDirection();else if(target)enter('rise');else enter('idle');}
|
|
154
|
-
else if(state==='recline'){const end=stayDown?.25:1;restProgress=Math.min(end,restProgress+dt/1.8);if(restProgress===end)enter(stayDown?'down':'nap');}
|
|
200
|
+
else if(state==='recline'){const end=stayDown?.25:1;restProgress=Math.min(end,restProgress+dt/1.8);if(restProgress===end)enter(stayDown?'down':comfort?.kind==='sleep'?'sleep':'nap');}
|
|
201
|
+
else if(state==='sleep'&&age>=comfort.seconds){comfort=null;enter('wake');}
|
|
202
|
+
else if(state==='tug'&&tugProgress()>=1){comfort=null;enter('idle');drain();}
|
|
155
203
|
else if(state==='nap'&&age>=5&&!stayDown){enter('wake');}
|
|
156
204
|
else if(state==='wake'){restProgress=Math.max(0,restProgress-prepareDt/1.2);if(restProgress===0){if(pendingAction)prepareDirection();else if(target)enter('rise');else enter('idle');}}
|
|
157
205
|
else if(state==='idle'&&route&&age>=idleHold)nextRoute();
|
|
@@ -161,7 +209,9 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
161
209
|
if(![12,24,30].includes(poseFps))throw Error('Invalid pose cadence');
|
|
162
210
|
let pair=20,blend=0,kind=state,rearPair=null,approachPair=null;
|
|
163
211
|
let seat=state==='sit-down'||state==='rise'||seatProgress>0?Math.round(seatProgress*48)/48:null;
|
|
164
|
-
|
|
212
|
+
if(restProgress===0&&state!=='recline')sleepLook=null;
|
|
213
|
+
let behavior=null,behaviorProgress=0,comfortPose=state==='tug'?{clip:'tug',progress:tugProgress()}:sleepLook&&restProgress>.72?{clip:'sleep',...sleepLook,blend:comfortEase((restProgress-.72)/.28)}:null;
|
|
214
|
+
if(activeAction?.comfort)kind=activeAction.comfort;
|
|
165
215
|
let actionName=activeAction?.name??(state==='run'&&movementMode==='walk'?'walk':null),actionProgress=activeAction?(activeAction.timing?activeAction.timing.progressAt(age):Math.min(1,age/activeAction.duration)):phase;
|
|
166
216
|
if(manualDown&&state==='down'){actionName='lie-down';actionProgress=1;}
|
|
167
217
|
if(walkTransitions&&state==='view-turn'&&turnView==='front'){
|
|
@@ -192,23 +242,30 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
192
242
|
// Deliberate held source poses: no live frame blending or face/body deformation.
|
|
193
243
|
blend=Math.round(blend*16)/16;
|
|
194
244
|
const key=`${state}/${actionName}/${view}/${turnView}/${poseFps}/${Math.floor(clock*poseFps)}`;
|
|
195
|
-
if(held?.key===key){pair=held.pair;blend=held.blend;rearPair=held.rearPair;approachPair=held.approachPair;seat=held.seat;behavior=held.behavior;behaviorProgress=held.behaviorProgress;actionName=held.actionName;actionProgress=held.actionProgress;}else held={key,pair,blend,rearPair,approachPair,seat,behavior,behaviorProgress,actionName,actionProgress,posePhase:phase};
|
|
196
|
-
return{state:state==='run'||state==='finish'?'run':kind,pair,blend,rearPair,approachPair,seatProgress:seat,seatVariant,behavior,behaviorProgress,actionName,actionProgress,queued:!!deferred,view,straight,flip:state==='turn'&&!walkTransitions?false:flip,phase,posePhase:held.posePhase,stride:travel?.stride??PAPER_GAITS.front.stride,heading:travel?.heading??0,speed:motionSpeed,motionStage,travelDistance,energy:director.report.energy,x:animal.position.x,z:animal.position.z};
|
|
245
|
+
if(held?.key===key){pair=held.pair;blend=held.blend;rearPair=held.rearPair;approachPair=held.approachPair;seat=held.seat;behavior=held.behavior;behaviorProgress=held.behaviorProgress;actionName=held.actionName;actionProgress=held.actionProgress;comfortPose=held.comfortPose;}else held={key,pair,blend,rearPair,approachPair,seat,behavior,behaviorProgress,actionName,actionProgress,comfortPose,posePhase:phase};
|
|
246
|
+
return{state:state==='run'||state==='finish'?'run':kind,pair,blend,rearPair,approachPair,seatProgress:seat,seatVariant,behavior,behaviorProgress,actionName,actionProgress,queued:!!deferred,view,straight,flip:state==='turn'&&!walkTransitions?false:flip,phase,posePhase:held.posePhase,stride:travel?.stride??PAPER_GAITS.front.stride,heading:travel?.heading??0,speed:motionSpeed,motionStage,travelDistance,energy:director.report.energy,x:animal.position.x,z:animal.position.z,...(comfortPose?{comfortPose:{...comfortPose}}:{})};
|
|
197
247
|
},get state(){return state;}
|
|
198
248
|
};
|
|
199
249
|
}
|
|
200
250
|
|
|
201
|
-
export function createPaperRenderer(cache,info,meta,width,height,{rear,approach,seat,arrivalSeat,behaviors,actions,interactions,contacts=true,pawTransport=false}={}){
|
|
251
|
+
export function createPaperRenderer(cache,info,meta,width,height,{rear,approach,seat,arrivalSeat,behaviors,actions,interactions,comfort,contacts=true,pawTransport=false}={}){
|
|
202
252
|
if(![width,height].every(Number.isInteger)||width<2||height<2||width*height>1_000_000)throw Error('Invalid paper grid');
|
|
203
253
|
const tween=createCachedTween(cache,info),renderMeta={...meta,pairs:info.pair_map,run_sequence:info.run_sequence};
|
|
204
254
|
const out=new Uint8ClampedArray(width*height*4),area=createPaperAreaSampler();
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
const
|
|
255
|
+
// Only one authored source is drawn at a time. Small samplers borrow a view of
|
|
256
|
+
// the action-sized scratch. A sleep blend reads each base pixel before writing
|
|
257
|
+
// that same pixel, so it needs no second image or per-frame temporary buffer.
|
|
258
|
+
const actionScratch=new Uint8ClampedArray(Math.max(320*320,(actions?.info.width??320)*(actions?.info.height??320))*4);
|
|
259
|
+
const spriteScratch=actionScratch.subarray(0,320*320*4);
|
|
260
|
+
for(let i=3;i<actionScratch.length;i+=4)actionScratch[i]=255;
|
|
261
|
+
const rearSample=rear?createRearSampler(rear.bytes,rear.info,spriteScratch):null,lock=createPaperFootLock({maxImageStretch:pawTransport?1.45:Infinity});
|
|
262
|
+
const approachSample=approach?createRearSampler(approach.bytes,approach.info,spriteScratch):null;
|
|
263
|
+
const seatSample=seat?createPaperSeatSampler(seat.bytes,seat.info,spriteScratch):null;
|
|
264
|
+
const arrivalSeatSample=arrivalSeat?createPaperSeatSampler(arrivalSeat.bytes,arrivalSeat.info,spriteScratch):null;
|
|
265
|
+
const behaviorSample=behaviors?createPaperBehaviorSampler(behaviors.bytes,behaviors.info,spriteScratch):null;
|
|
266
|
+
const interactionSample=interactions?createCompanionSpriteSampler(interactions.bytes,interactions.info,spriteScratch):null;
|
|
267
|
+
const comfortSample=comfort?createComfortSpriteSampler(comfort.bytes,comfort.info,spriteScratch):null;
|
|
268
|
+
const actionSample=actions?createPaperActionSampler(actions.bytes,actions.info,actionScratch.subarray(0,actions.info.width*actions.info.height*4)):null,actionArea=actions?createPaperAreaSampler(actions.info.width):null;
|
|
212
269
|
const walkEpisodes=actions?createPaperSupportEpisodes(actions.info.feet,actions.info.clips.walk,9):null;
|
|
213
270
|
const sourceEpisodes=rear?{
|
|
214
271
|
front:createPaperSupportEpisodes(rear.info.frontFeet,[0,1,2,3,4,5,6,7],17),
|
|
@@ -222,10 +279,11 @@ export function createPaperRenderer(cache,info,meta,width,height,{rear,approach,
|
|
|
222
279
|
const isApproach=state.approachPair!==null&&state.approachPair!==undefined&&approachSample;
|
|
223
280
|
const isSeat=state.seatProgress!==null&&state.seatProgress!==undefined&&seatSample;
|
|
224
281
|
if(!Number.isFinite(pixelAspect)||pixelAspect<.1||pixelAspect>10)throw Error('Invalid dot pixel aspect');
|
|
225
|
-
const
|
|
282
|
+
const isComfort=state.comfortPose&&comfortSample;
|
|
283
|
+
const isInteraction=isComfort||state.interactionPose&&interactionSample;
|
|
226
284
|
const isBehavior=!isInteraction&&state.behavior&&behaviorSample;
|
|
227
285
|
const isAction=!isInteraction&&state.actionName&&actionSample,sourceN=isAction?actions.info.width:320,originX=(sourceN-320)/2,originY=(sourceN/320-1)*300;
|
|
228
|
-
const image=isInteraction?interactionSample(state.interactionPose):isAction?actionSample(state):isBehavior?behaviorSample(state):isSeat?(state.seatVariant==='arrival'&&arrivalSeatSample?arrivalSeatSample:seatSample)(state.seatProgress):isApproach?approachSample(state.approachPair,state.blend):isRear?rearSample(state.rearPair,state.blend):tween(state),side=height*size,sideX=side/pixelAspect;
|
|
286
|
+
const image=isComfort?comfortSample({...state.comfortPose,...state.comfortPose.clip==='sleep'&&state.comfortPose.blend<1&&behaviorSample?{base:behaviorSample({behavior:'rest',behaviorProgress:state.behaviorProgress})}:{}}):isInteraction?interactionSample(state.interactionPose):isAction?actionSample(state):isBehavior?behaviorSample(state):isSeat?(state.seatVariant==='arrival'&&arrivalSeatSample?arrivalSeatSample:seatSample)(state.seatProgress):isApproach?approachSample(state.approachPair,state.blend):isRear?rearSample(state.rearPair,state.blend):tween(state),side=height*size,sideX=side/pixelAspect;
|
|
229
287
|
// Quantize source lookup only, never the projected rectangle. Rounding side,
|
|
230
288
|
// left and top first introduced whole-body scale/position jumps at thresholds.
|
|
231
289
|
const left=width*x-sideX/2,floor=isInteraction||isAction||isBehavior||isSeat||isRear||isApproach?0:cleanGroundOffset(state,renderMeta),top=height*y-(300-floor)/320*side;
|
package/retrieviewer-cli.mjs
CHANGED
|
@@ -13,7 +13,7 @@ Usage:
|
|
|
13
13
|
retrieviewer doctor [--agent ID] [--json]
|
|
14
14
|
retrieviewer agents [--json]
|
|
15
15
|
retrieviewer preview [--original]
|
|
16
|
-
retrieviewer desktop
|
|
16
|
+
retrieviewer desktop [--ink cream|gold|dark]
|
|
17
17
|
retrieviewer desktop-status [--json]
|
|
18
18
|
retrieviewer desktop-stop
|
|
19
19
|
retrieviewer desktop-clean --yes
|
|
@@ -32,15 +32,17 @@ export function parseArguments(args){
|
|
|
32
32
|
const [command='help',...rest]=args,options={};
|
|
33
33
|
for(let i=0;i<rest.length;i++){
|
|
34
34
|
const token=rest[i];
|
|
35
|
-
if(token==='--agent'){
|
|
36
|
-
|
|
37
|
-
options
|
|
35
|
+
if(token==='--agent'||token==='--ink'){
|
|
36
|
+
const key=token.slice(2);
|
|
37
|
+
if(options[key]||!rest[i+1]||rest[i+1].startsWith('-'))throw Error(token+' requires one value');
|
|
38
|
+
options[key]=rest[++i];
|
|
38
39
|
}else if(['--yes','--json','--dry-run','--original'].includes(token))options[token.slice(2)]=true;
|
|
39
40
|
else throw Error('Unknown argument: '+token);
|
|
40
41
|
}
|
|
41
42
|
if(options.agent&&!AGENTS.some(a=>a.id===options.agent))throw Error('Unknown agent: '+options.agent);
|
|
43
|
+
if(options.ink&&!['cream','gold','dark'].includes(options.ink))throw Error('--ink must be cream, gold or dark');
|
|
42
44
|
if(!['help','--help','-h','enable','disable','disconnect','status','doctor','agents','preview','desktop','desktop-status','desktop-stop','desktop-clean','desktop-remove'].includes(command))throw Error('Unknown command: '+command);
|
|
43
|
-
const allowed={enable:['agent','yes','json','dry-run'],disconnect:['agent','yes','json','dry-run'],disable:['agent','json'],status:['agent','json'],doctor:['agent','json'],agents:['json'],preview:['original'],'desktop-status':['json'],'desktop-clean':['yes'],'desktop-remove':['yes']};
|
|
45
|
+
const allowed={desktop:['ink'],enable:['agent','yes','json','dry-run'],disconnect:['agent','yes','json','dry-run'],disable:['agent','json'],status:['agent','json'],doctor:['agent','json'],agents:['json'],preview:['original'],'desktop-status':['json'],'desktop-clean':['yes'],'desktop-remove':['yes']};
|
|
44
46
|
for(const key of Object.keys(options))if(!(allowed[command]??[]).includes(key))throw Error(`--${key} is not supported by ${command}`);
|
|
45
47
|
return {command,options};
|
|
46
48
|
}
|
|
@@ -51,7 +53,7 @@ export async function runCli(args,{input=process.stdin,output=process.stdout,err
|
|
|
51
53
|
if(['desktop-clean','desktop-remove'].includes(command)&&!options.yes)throw Error('Use '+command+' --yes to remove only the companion cache.');
|
|
52
54
|
const api=desktop??await import('./integrations/windows-overlay/desktop.mjs');
|
|
53
55
|
const action={'desktop':'startDesktop','desktop-status':'desktopStatus','desktop-stop':'stopDesktop','desktop-clean':'cleanDesktop','desktop-remove':'removeDesktop'}[command];
|
|
54
|
-
emit(await api[action]());return 0;
|
|
56
|
+
emit(await api[action](options.ink?{ink:options.ink}:{}));return 0;
|
|
55
57
|
}
|
|
56
58
|
if(command==='preview'){await import('./examples/live.mjs');return 0;}
|
|
57
59
|
if(['status','doctor','agents'].includes(command)){
|
|
@@ -36,7 +36,7 @@ export interface PetPlugin {
|
|
|
36
36
|
/** Host-owned loop: advance seconds then render. False while hidden. Throws while running. */
|
|
37
37
|
tick(dtSeconds: number): boolean;
|
|
38
38
|
/** False when busy or cooling down. */
|
|
39
|
-
interact(kind: 'ball' | 'treat'): boolean;
|
|
39
|
+
interact(kind: 'ball' | 'treat' | 'tug'): boolean;
|
|
40
40
|
readonly running: boolean;
|
|
41
41
|
readonly disposed: boolean;
|
|
42
42
|
dispose(): void;
|
package/retrieviewer-plugin.mjs
CHANGED
|
@@ -82,7 +82,7 @@ export async function createPetPlugin({host,petOptions,fps=30,scheduler}={}){
|
|
|
82
82
|
if(typeof dtSeconds!=='number'||!Number.isFinite(dtSeconds)||dtSeconds<0)throw RangeError('dtSeconds must be finite non-negative seconds');
|
|
83
83
|
try{return draw(dtSeconds);}catch(error){halt();throw error;}
|
|
84
84
|
},
|
|
85
|
-
// 'ball' | 'treat'. False when busy or cooling down; shows on the next frame.
|
|
85
|
+
// 'ball' | 'treat' | 'tug'. False when busy or cooling down; shows on the next frame.
|
|
86
86
|
interact(kind){alive();return pet.interact(kind);},
|
|
87
87
|
get running(){return running;},
|
|
88
88
|
get disposed(){return disposed;},
|
package/retrieviewer.d.mts
CHANGED
|
@@ -35,16 +35,24 @@ 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;
|
|
45
|
+
/** Awake-time sleep pressure; ordinary sitting does not erase it. */
|
|
46
|
+
drowsiness: number;
|
|
43
47
|
decisions: number;
|
|
44
|
-
cooldowns: {ball: number; treat: number};
|
|
48
|
+
cooldowns: {ball: number; treat: number; tug: number};
|
|
45
49
|
gesture: {clip: string; progress: number} | null;
|
|
46
50
|
ball: {x: number; z: number; height: number; carried: boolean} | null;
|
|
47
51
|
treat: {x: number; z: number; height: number} | null;
|
|
52
|
+
/** Tug-of-war state while a tug interaction is active. */
|
|
53
|
+
tug: {progress: number; grip: number} | null;
|
|
54
|
+
/** Silent rising sleep cue, normalized cycle progress; no audio process. */
|
|
55
|
+
snore: number | null;
|
|
48
56
|
}
|
|
49
57
|
export interface PetFrame {
|
|
50
58
|
artProfile: 'original' | 'q5';
|
|
@@ -70,7 +78,7 @@ export interface Pet {
|
|
|
70
78
|
step(dt: number): number;
|
|
71
79
|
frame(options: FrameOptions): PetFrame;
|
|
72
80
|
/** False when busy or cooling down. */
|
|
73
|
-
interact(kind: 'ball' | 'treat'): boolean;
|
|
81
|
+
interact(kind: 'ball' | 'treat' | 'tug'): boolean;
|
|
74
82
|
readonly disposed: boolean;
|
|
75
83
|
dispose(): void;
|
|
76
84
|
}
|
|
@@ -78,7 +86,7 @@ export function createPet(options?: PetOptions): Promise<Pet>;
|
|
|
78
86
|
export function defaultLoadAsset(name: string): Promise<ArrayBuffer | Uint8Array>;
|
|
79
87
|
export const STEP_SECONDS: number;
|
|
80
88
|
export const MAX_CATCH_UP_SECONDS: number;
|
|
81
|
-
export const INTERACTIONS: readonly ['ball', 'treat'];
|
|
89
|
+
export const INTERACTIONS: readonly ['ball', 'treat', 'tug'];
|
|
82
90
|
export const ART_PROFILES: readonly ['original', 'q5'];
|
|
83
91
|
export const DEFAULT_ART_PROFILE: 'q5';
|
|
84
92
|
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());
|