@bitefoundry/retrieviewer 0.0.0-stage → 0.0.2
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 +43 -0
- package/ASSETS-LICENSE.md +58 -0
- package/ATTRIBUTION.md +54 -0
- package/EMBEDDING.md +96 -0
- package/INSTALLING.md +126 -0
- package/LICENSE +16 -0
- package/LICENSE-CODE +21 -0
- package/PLUGIN.md +110 -0
- package/PUPPY-MEMORY.md +57 -0
- package/PUPPY-Q5-COMPARISON.md +73 -0
- package/README.md +86 -2
- package/RELEASING.md +70 -0
- package/THIRD-PARTY-NOTICES.md +17 -0
- package/TRADEMARKS.md +21 -0
- package/WINDOWS-COMPANION.md +113 -0
- package/braille.mjs +45 -0
- package/examples/bridge.py +62 -0
- package/examples/embed.mjs +15 -0
- package/examples/live.mjs +40 -0
- package/examples/plugin.mjs +21 -0
- package/integrations/opencode/runtime.mjs +195 -0
- package/integrations/opencode/tui.tsx +59 -0
- package/integrations/windows-overlay/Overlay.cs +259 -0
- package/integrations/windows-overlay/desktop.mjs +87 -0
- package/integrations/windows-overlay/protocol.mjs +39 -0
- package/integrations/windows-overlay/worker.mjs +60 -0
- package/locomotion-core.mjs +21 -0
- package/locomotion-puppy.animal +0 -0
- package/package.json +131 -4
- package/pet-q5-manifest.json +118 -0
- package/puppy-clean.mjs +51 -0
- package/puppy-companion-interactions-v3-q5.bin.gz +0 -0
- package/puppy-companion-interactions-v3.json +1 -0
- package/puppy-companion-life.mjs +182 -0
- package/puppy-companion-objects.mjs +30 -0
- package/puppy-companion-sprites.mjs +14 -0
- package/puppy-continuous.mjs +57 -0
- package/puppy-face.mjs +46 -0
- package/puppy-frame-predictive.mjs +147 -0
- package/puppy-frame-store.mjs +108 -0
- package/puppy-mouth-anchor.json +1 -0
- package/puppy-mouth-anchor.mjs +33 -0
- package/puppy-paper-actions-v9-q5.bin.gz +0 -0
- package/puppy-paper-actions-v9.json +1 -0
- package/puppy-paper-actions.mjs +48 -0
- package/puppy-paper-approach-q5.bin.gz +0 -0
- package/puppy-paper-approach.json +1 -0
- package/puppy-paper-area.mjs +15 -0
- package/puppy-paper-arrival-seat-q5.bin.gz +0 -0
- package/puppy-paper-arrival-seat.json +1 -0
- package/puppy-paper-behaviors-q5.bin.gz +0 -0
- package/puppy-paper-behaviors.json +1 -0
- package/puppy-paper-behaviors.mjs +8 -0
- package/puppy-paper-contact.mjs +103 -0
- package/puppy-paper-director.mjs +21 -0
- package/puppy-paper-paw-transport.mjs +76 -0
- package/puppy-paper-rear-gallop-q5.bin.gz +0 -0
- package/puppy-paper-rear-gallop.json +1 -0
- package/puppy-paper-seat-q5.bin.gz +0 -0
- package/puppy-paper-seat.json +14 -0
- package/puppy-paper-seat.mjs +13 -0
- package/puppy-paper-trick-timing.mjs +58 -0
- package/puppy-paper-world.mjs +108 -0
- package/puppy-paper.mjs +271 -0
- package/puppy-stable-meta.json +1606 -0
- package/puppy-stable.mjs +72 -0
- package/puppy-stride-meta.json +343 -0
- package/puppy-stride-tweens-q5.bin.gz +0 -0
- package/puppy-zoomies.mjs +27 -0
- package/puppy-zoomies30.mjs +12 -0
- package/retrieviewer-cli.mjs +94 -0
- package/retrieviewer-config.mjs +49 -0
- package/retrieviewer-install.mjs +157 -0
- package/retrieviewer-plugin.d.mts +44 -0
- package/retrieviewer-plugin.mjs +97 -0
- package/retrieviewer-stdio.mjs +38 -0
- package/retrieviewer.d.mts +85 -0
- package/retrieviewer.mjs +142 -0
- package/vendor/procedural-animals/LICENSE +21 -0
- package/vendor/procedural-animals/package.json +40 -0
- package/vendor/procedural-animals/src/animal.js +146 -0
- package/vendor/procedural-animals/src/bake.js +51 -0
- package/vendor/procedural-animals/src/build.js +81 -0
- package/vendor/procedural-animals/src/core/build/coatKit.js +121 -0
- package/vendor/procedural-animals/src/core/build/featherCards.js +189 -0
- package/vendor/procedural-animals/src/core/build/finEdges.js +85 -0
- package/vendor/procedural-animals/src/core/build/furSlope.js +115 -0
- package/vendor/procedural-animals/src/core/build/mesh.js +67 -0
- package/vendor/procedural-animals/src/core/build/pipeline.js +272 -0
- package/vendor/procedural-animals/src/core/build/seams.js +183 -0
- package/vendor/procedural-animals/src/core/build/warp.js +112 -0
- package/vendor/procedural-animals/src/core/build/weights.js +393 -0
- package/vendor/procedural-animals/src/core/math/vec.js +81 -0
- package/vendor/procedural-animals/src/core/motion/actions.js +800 -0
- package/vendor/procedural-animals/src/core/motion/bird.js +3063 -0
- package/vendor/procedural-animals/src/core/motion/birdActions.js +1059 -0
- package/vendor/procedural-animals/src/core/motion/common.js +158 -0
- package/vendor/procedural-animals/src/core/motion/index.js +22 -0
- package/vendor/procedural-animals/src/core/motion/quadruped.js +2875 -0
- package/vendor/procedural-animals/src/core/motion/snake.js +1594 -0
- package/vendor/procedural-animals/src/core/motion/spider.js +1810 -0
- package/vendor/procedural-animals/src/core/motion/swimmer.js +1047 -0
- package/vendor/procedural-animals/src/core/motion/swimmerActions.js +613 -0
- package/vendor/procedural-animals/src/core/render/animalObject.js +262 -0
- package/vendor/procedural-animals/src/core/render/coatMaterial.js +1301 -0
- package/vendor/procedural-animals/src/core/render/dqs.js +159 -0
- package/vendor/procedural-animals/src/core/render/eyes.js +676 -0
- package/vendor/procedural-animals/src/core/render/membranes.js +77 -0
- package/vendor/procedural-animals/src/core/render/skeleton.js +41 -0
- package/vendor/procedural-animals/src/core/rig/bird.js +327 -0
- package/vendor/procedural-animals/src/core/rig/quadruped.js +76 -0
- package/vendor/procedural-animals/src/core/rig/rig.js +79 -0
- package/vendor/procedural-animals/src/core/rig/snake.js +76 -0
- package/vendor/procedural-animals/src/core/rig/spider.js +110 -0
- package/vendor/procedural-animals/src/core/rig/swimmer.js +181 -0
- package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +53 -0
- package/vendor/procedural-animals/src/core/sdf/mesher.js +238 -0
- package/vendor/procedural-animals/src/core/sdf/sdf.js +263 -0
- package/vendor/procedural-animals/src/index.d.ts +98 -0
- package/vendor/procedural-animals/src/index.js +29 -0
- package/vendor/procedural-animals/src/registry.js +48 -0
- package/vendor/procedural-animals/src/species/bear/behaviour.js +463 -0
- package/vendor/procedural-animals/src/species/bear/claws.js +147 -0
- package/vendor/procedural-animals/src/species/bear/coat.js +726 -0
- package/vendor/procedural-animals/src/species/bear/index.js +139 -0
- package/vendor/procedural-animals/src/species/bear/motion.js +112 -0
- package/vendor/procedural-animals/src/species/bear/regions.js +78 -0
- package/vendor/procedural-animals/src/species/bear/rig.js +112 -0
- package/vendor/procedural-animals/src/species/bear/sculpt.js +272 -0
- package/vendor/procedural-animals/src/species/boar/behaviour.js +335 -0
- package/vendor/procedural-animals/src/species/boar/coat.js +372 -0
- package/vendor/procedural-animals/src/species/boar/index.js +110 -0
- package/vendor/procedural-animals/src/species/boar/motion.js +85 -0
- package/vendor/procedural-animals/src/species/boar/regions.js +88 -0
- package/vendor/procedural-animals/src/species/boar/rig.js +111 -0
- package/vendor/procedural-animals/src/species/boar/sculpt.js +285 -0
- package/vendor/procedural-animals/src/species/cat/behaviour.js +559 -0
- package/vendor/procedural-animals/src/species/cat/coat.js +841 -0
- package/vendor/procedural-animals/src/species/cat/index.js +211 -0
- package/vendor/procedural-animals/src/species/cat/motion.js +101 -0
- package/vendor/procedural-animals/src/species/cat/regions.js +82 -0
- package/vendor/procedural-animals/src/species/cat/rig.js +100 -0
- package/vendor/procedural-animals/src/species/cat/sculpt.js +266 -0
- package/vendor/procedural-animals/src/species/cat/whiskers.js +110 -0
- package/vendor/procedural-animals/src/species/cheetah/coat.js +427 -0
- package/vendor/procedural-animals/src/species/cheetah/index.js +51 -0
- package/vendor/procedural-animals/src/species/cheetah/joints.js +3 -0
- package/vendor/procedural-animals/src/species/cheetah/motion.js +77 -0
- package/vendor/procedural-animals/src/species/cheetah/regions.js +95 -0
- package/vendor/procedural-animals/src/species/cheetah/rig.js +66 -0
- package/vendor/procedural-animals/src/species/cheetah/sculpt.js +158 -0
- package/vendor/procedural-animals/src/species/chicken/coat.js +313 -0
- package/vendor/procedural-animals/src/species/chicken/coverts.js +320 -0
- package/vendor/procedural-animals/src/species/chicken/index.js +110 -0
- package/vendor/procedural-animals/src/species/chicken/motion.js +177 -0
- package/vendor/procedural-animals/src/species/chicken/regions.js +66 -0
- package/vendor/procedural-animals/src/species/chicken/rig.js +144 -0
- package/vendor/procedural-animals/src/species/chicken/sculpt.js +389 -0
- package/vendor/procedural-animals/src/species/chicken/wingLook.js +66 -0
- package/vendor/procedural-animals/src/species/chicken/wingStack.js +124 -0
- package/vendor/procedural-animals/src/species/cow/behaviour.js +521 -0
- package/vendor/procedural-animals/src/species/cow/coat.js +465 -0
- package/vendor/procedural-animals/src/species/cow/index.js +118 -0
- package/vendor/procedural-animals/src/species/cow/motion.js +81 -0
- package/vendor/procedural-animals/src/species/cow/regions.js +98 -0
- package/vendor/procedural-animals/src/species/cow/rig.js +116 -0
- package/vendor/procedural-animals/src/species/cow/sculpt.js +339 -0
- package/vendor/procedural-animals/src/species/crow/coat.js +145 -0
- package/vendor/procedural-animals/src/species/crow/index.js +70 -0
- package/vendor/procedural-animals/src/species/crow/joints.js +3 -0
- package/vendor/procedural-animals/src/species/crow/motion.js +101 -0
- package/vendor/procedural-animals/src/species/crow/regions.js +50 -0
- package/vendor/procedural-animals/src/species/crow/rig.js +83 -0
- package/vendor/procedural-animals/src/species/crow/sculpt.js +118 -0
- package/vendor/procedural-animals/src/species/deer/behaviour.js +513 -0
- package/vendor/procedural-animals/src/species/deer/coat.js +391 -0
- package/vendor/procedural-animals/src/species/deer/index.js +106 -0
- package/vendor/procedural-animals/src/species/deer/motion.js +97 -0
- package/vendor/procedural-animals/src/species/deer/regions.js +60 -0
- package/vendor/procedural-animals/src/species/deer/rig.js +124 -0
- package/vendor/procedural-animals/src/species/deer/sculpt.js +297 -0
- package/vendor/procedural-animals/src/species/dog/behaviour.js +521 -0
- package/vendor/procedural-animals/src/species/dog/coat.js +495 -0
- package/vendor/procedural-animals/src/species/dog/index.js +144 -0
- package/vendor/procedural-animals/src/species/dog/motion.js +118 -0
- package/vendor/procedural-animals/src/species/dog/regions.js +61 -0
- package/vendor/procedural-animals/src/species/dog/rig.js +129 -0
- package/vendor/procedural-animals/src/species/dog/sculpt.js +246 -0
- package/vendor/procedural-animals/src/species/eagle/coat.js +250 -0
- package/vendor/procedural-animals/src/species/eagle/index.js +76 -0
- package/vendor/procedural-animals/src/species/eagle/joints.js +3 -0
- package/vendor/procedural-animals/src/species/eagle/motion.js +137 -0
- package/vendor/procedural-animals/src/species/eagle/regions.js +57 -0
- package/vendor/procedural-animals/src/species/eagle/rig.js +107 -0
- package/vendor/procedural-animals/src/species/eagle/sculpt.js +137 -0
- package/vendor/procedural-animals/src/species/fish/coat.js +271 -0
- package/vendor/procedural-animals/src/species/fish/index.js +88 -0
- package/vendor/procedural-animals/src/species/fish/motion.js +92 -0
- package/vendor/procedural-animals/src/species/fish/regions.js +27 -0
- package/vendor/procedural-animals/src/species/fish/rig.js +143 -0
- package/vendor/procedural-animals/src/species/fish/sculpt.js +214 -0
- package/vendor/procedural-animals/src/species/fish/variants.js +126 -0
- package/vendor/procedural-animals/src/species/fox/behaviour.js +314 -0
- package/vendor/procedural-animals/src/species/fox/coat.js +444 -0
- package/vendor/procedural-animals/src/species/fox/index.js +120 -0
- package/vendor/procedural-animals/src/species/fox/motion.js +81 -0
- package/vendor/procedural-animals/src/species/fox/regions.js +59 -0
- package/vendor/procedural-animals/src/species/fox/rig.js +91 -0
- package/vendor/procedural-animals/src/species/fox/sculpt.js +202 -0
- package/vendor/procedural-animals/src/species/frog/behaviour.js +590 -0
- package/vendor/procedural-animals/src/species/frog/coat.js +305 -0
- package/vendor/procedural-animals/src/species/frog/index.js +97 -0
- package/vendor/procedural-animals/src/species/frog/motion.js +69 -0
- package/vendor/procedural-animals/src/species/frog/regions.js +46 -0
- package/vendor/procedural-animals/src/species/frog/rig.js +156 -0
- package/vendor/procedural-animals/src/species/frog/sculpt.js +240 -0
- package/vendor/procedural-animals/src/species/goat/behaviour.js +509 -0
- package/vendor/procedural-animals/src/species/goat/coat.js +590 -0
- package/vendor/procedural-animals/src/species/goat/index.js +149 -0
- package/vendor/procedural-animals/src/species/goat/motion.js +95 -0
- package/vendor/procedural-animals/src/species/goat/regions.js +98 -0
- package/vendor/procedural-animals/src/species/goat/rig.js +181 -0
- package/vendor/procedural-animals/src/species/goat/sculpt.js +547 -0
- package/vendor/procedural-animals/src/species/horse/coat.js +361 -0
- package/vendor/procedural-animals/src/species/horse/coatSeam.js +122 -0
- package/vendor/procedural-animals/src/species/horse/index.js +80 -0
- package/vendor/procedural-animals/src/species/horse/motion.js +245 -0
- package/vendor/procedural-animals/src/species/horse/regions.js +60 -0
- package/vendor/procedural-animals/src/species/horse/rig.js +117 -0
- package/vendor/procedural-animals/src/species/horse/sculpt.js +316 -0
- package/vendor/procedural-animals/src/species/horse/seamWeights.js +272 -0
- package/vendor/procedural-animals/src/species/lion/behaviour.js +509 -0
- package/vendor/procedural-animals/src/species/lion/coat.js +1016 -0
- package/vendor/procedural-animals/src/species/lion/index.js +111 -0
- package/vendor/procedural-animals/src/species/lion/motion.js +87 -0
- package/vendor/procedural-animals/src/species/lion/regions.js +106 -0
- package/vendor/procedural-animals/src/species/lion/rig.js +110 -0
- package/vendor/procedural-animals/src/species/lion/sculpt.js +384 -0
- package/vendor/procedural-animals/src/species/lion/whiskers.js +132 -0
- package/vendor/procedural-animals/src/species/pig/behaviour.js +575 -0
- package/vendor/procedural-animals/src/species/pig/coat.js +730 -0
- package/vendor/procedural-animals/src/species/pig/ears.js +242 -0
- package/vendor/procedural-animals/src/species/pig/index.js +161 -0
- package/vendor/procedural-animals/src/species/pig/motion.js +105 -0
- package/vendor/procedural-animals/src/species/pig/regions.js +100 -0
- package/vendor/procedural-animals/src/species/pig/rig.js +152 -0
- package/vendor/procedural-animals/src/species/pig/sculpt.js +382 -0
- package/vendor/procedural-animals/src/species/rabbit/behaviour.js +605 -0
- package/vendor/procedural-animals/src/species/rabbit/coat.js +285 -0
- package/vendor/procedural-animals/src/species/rabbit/index.js +85 -0
- package/vendor/procedural-animals/src/species/rabbit/motion.js +85 -0
- package/vendor/procedural-animals/src/species/rabbit/regions.js +48 -0
- package/vendor/procedural-animals/src/species/rabbit/rig.js +99 -0
- package/vendor/procedural-animals/src/species/rabbit/sculpt.js +148 -0
- package/vendor/procedural-animals/src/species/rat/behaviour.js +539 -0
- package/vendor/procedural-animals/src/species/rat/coat.js +341 -0
- package/vendor/procedural-animals/src/species/rat/index.js +111 -0
- package/vendor/procedural-animals/src/species/rat/motion.js +90 -0
- package/vendor/procedural-animals/src/species/rat/regions.js +92 -0
- package/vendor/procedural-animals/src/species/rat/rig.js +112 -0
- package/vendor/procedural-animals/src/species/rat/sculpt.js +163 -0
- package/vendor/procedural-animals/src/species/rat/whiskers.js +119 -0
- package/vendor/procedural-animals/src/species/shark/coat.js +383 -0
- package/vendor/procedural-animals/src/species/shark/index.js +104 -0
- package/vendor/procedural-animals/src/species/shark/motion.js +69 -0
- package/vendor/procedural-animals/src/species/shark/regions.js +34 -0
- package/vendor/procedural-animals/src/species/shark/rig.js +194 -0
- package/vendor/procedural-animals/src/species/shark/sculpt.js +343 -0
- package/vendor/procedural-animals/src/species/shark/variants.js +105 -0
- package/vendor/procedural-animals/src/species/sheep/behaviour.js +522 -0
- package/vendor/procedural-animals/src/species/sheep/coat.js +529 -0
- package/vendor/procedural-animals/src/species/sheep/index.js +152 -0
- package/vendor/procedural-animals/src/species/sheep/motion.js +86 -0
- package/vendor/procedural-animals/src/species/sheep/neck.js +41 -0
- package/vendor/procedural-animals/src/species/sheep/regions.js +62 -0
- package/vendor/procedural-animals/src/species/sheep/rig.js +163 -0
- package/vendor/procedural-animals/src/species/sheep/sculpt.js +542 -0
- package/vendor/procedural-animals/src/species/snake/coat.js +315 -0
- package/vendor/procedural-animals/src/species/snake/index.js +73 -0
- package/vendor/procedural-animals/src/species/snake/motion.js +51 -0
- package/vendor/procedural-animals/src/species/snake/regions.js +51 -0
- package/vendor/procedural-animals/src/species/snake/rig.js +108 -0
- package/vendor/procedural-animals/src/species/snake/sculpt.js +122 -0
- package/vendor/procedural-animals/src/species/spider/coat.js +279 -0
- package/vendor/procedural-animals/src/species/spider/index.js +78 -0
- package/vendor/procedural-animals/src/species/spider/motion.js +72 -0
- package/vendor/procedural-animals/src/species/spider/regions.js +87 -0
- package/vendor/procedural-animals/src/species/spider/rig.js +118 -0
- package/vendor/procedural-animals/src/species/spider/sculpt.js +182 -0
- package/vendor/procedural-animals/src/species/wolf/behaviour.js +160 -0
- package/vendor/procedural-animals/src/species/wolf/coat.js +749 -0
- package/vendor/procedural-animals/src/species/wolf/index.js +107 -0
- package/vendor/procedural-animals/src/species/wolf/motion.js +80 -0
- package/vendor/procedural-animals/src/species/wolf/regions.js +57 -0
- package/vendor/procedural-animals/src/species/wolf/rig.js +87 -0
- package/vendor/procedural-animals/src/species/wolf/sculpt.js +339 -0
- package/vendor/procedural-animals/src/worker.js +29 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
// Brown bear behaviour layer for the quadruped engine (species.motion.hooks, see core/motion/actions.js):
|
|
2
|
+
//
|
|
3
|
+
// update(P, dt, layer) every frame over the action layer: the lumbering walk (head swinging with
|
|
4
|
+
// the forelegs, shoulders and hump rolling), nose working while idle, automatic
|
|
5
|
+
// idle variants (sniffing the air, standing up to look, digging), standing up
|
|
6
|
+
// on the hind legs to look at a far target (input.look)
|
|
7
|
+
// pose(engine, dt) nostril / muzzle twitch while sniffing (a small head-bone nod is enough)
|
|
8
|
+
// actions standup (bipedal: looks and sniffs), sniff, dig (alternate forepaw rakes),
|
|
9
|
+
// sit (on the rump like a person, hind legs forward, forepaws in the lap),
|
|
10
|
+
// sleep (curled on the side, or prone with the head on the forepaws),
|
|
11
|
+
// attack (charge, rear up, swat with a forepaw, come down biting),
|
|
12
|
+
// hit (flinch, huff and a head swing, jaw popping), eat (rooting / grazing)
|
|
13
|
+
//
|
|
14
|
+
// Distances are bear metres at the reference size (x cfg.s); posture lengths that the engine
|
|
15
|
+
// multiplies by cfg.k (fwd, side, lift, headRaise, neckReach) are divided by cfg.unit.
|
|
16
|
+
import * as THREE from 'three';
|
|
17
|
+
import { clamp, lerp, smooth, TAU, tv, tableAt } from '../../core/motion/common.js';
|
|
18
|
+
import { mx, mxLeg, ONESHOTS, POSTURES } from '../../core/motion/actions.js';
|
|
19
|
+
|
|
20
|
+
const PI = Math.PI;
|
|
21
|
+
|
|
22
|
+
function B(e) {
|
|
23
|
+
if (!e._bear) e._bear = { idleT: 8 + e.rand() * 8, lookStandT: 0, sniff: 0, sway: 0, huffT: -1, standW: 0 };
|
|
24
|
+
return e._bear;
|
|
25
|
+
}
|
|
26
|
+
// A bear looking at something keeps its muzzle below its line of sight (face1 / face2 / tq1 / front1: looking at
|
|
27
|
+
// the camera, the nose pad's bottom 0.91-0.98 eye separations under the eye line; face3_profile: the nose centre
|
|
28
|
+
// 0.48 of the eye-nose distance under it). The engine aims the head bone's long axis at a look target and so
|
|
29
|
+
// replaced the nose-down carriage with a level head whenever the bear looked at anything, ~70 % of a standing
|
|
30
|
+
// bear's time (the nose pad then sat 0.19 / 0.58 eye separations under the eyes). The look pitch gets this offset.
|
|
31
|
+
const GAZE = 0.25;
|
|
32
|
+
// A bear looking to the side bends its short, thick neck toward the look as well as turning its head (m of lateral
|
|
33
|
+
// neck bend at the poll per rad of head yaw, reference size). The engine carries the neck with the chest and yaws the
|
|
34
|
+
// head bone alone: a 45 deg look turned the head ~40 deg about the poll, the skin behind the ear on the look side
|
|
35
|
+
// folded into a crease and the nape's ruff stood along it as a hard edge over a bald-looking trench
|
|
36
|
+
const NECK_SWAY = 0.18;
|
|
37
|
+
const busy = (L) => L.postures.length > 0 || L.oneshots.some((a) => !a.stopping);
|
|
38
|
+
|
|
39
|
+
// ------------------------------------------------------------------------------------------------
|
|
40
|
+
// bipedal stand: the body rotates up about the hips (hind feet flat), forelegs hang in front of the
|
|
41
|
+
// chest, head up and looking, nose working. `w` is the rise (0..1).
|
|
42
|
+
const STAND_PITCH = 1.2; // torso pitch standing up (rad, ~69 deg)
|
|
43
|
+
const SIT_PITCH = 0.95; // sitting on the rump
|
|
44
|
+
const HEAD_SIDE = 0.09; // extra height of the occiput of a head lying on its side (m)
|
|
45
|
+
|
|
46
|
+
// Pitches the torso up about the hips to pT (body frame pitch) with the hips displaced dispH (m) from
|
|
47
|
+
// their bind height and carried to body-local z hipZt. The girdle height targets are set to agree
|
|
48
|
+
// with that pitch (the engine pitches the body by atan2(shoulder - hip displacement, girdle distance)
|
|
49
|
+
// + P.pitch), and the body is carried forward (P.fwd) so the hips land where asked.
|
|
50
|
+
function tiltBody(P, g, e, pT, dispH, hipZt) {
|
|
51
|
+
const cfg = e.cfg;
|
|
52
|
+
const dz = cfg.zS - cfg.zH;
|
|
53
|
+
const restH = cfg.pelvis.hh - (cfg.pelvis.y - cfg.yH);
|
|
54
|
+
const dispS = dispH + (cfg.yS - cfg.yH) * (Math.cos(pT) - 1) + dz * Math.sin(pT);
|
|
55
|
+
if (dispH !== null) mx(P, 'dropH', -dispH / (cfg.yH - restH), g);
|
|
56
|
+
// (the shoulders by absolute height: the engine's resting height behind dropF changes with the pitch)
|
|
57
|
+
mx(P, 'frontH', cfg.yS + dispS, g / Math.max(1e-3, P.frontHW + g)); mx(P, 'frontHW', 1, g);
|
|
58
|
+
mx(P, 'pitch', pT - Math.atan2(dispS - dispH, dz), g);
|
|
59
|
+
const hipZ = cfg.zH * Math.cos(pT) - (cfg.yH + dispH) * Math.sin(pT);
|
|
60
|
+
mx(P, 'fwd', (hipZt - hipZ) / cfg.k, g);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function applyStand(P, w, e, headUp = 1) {
|
|
64
|
+
if (w <= 1e-4) return;
|
|
65
|
+
const cfg = e.cfg, s = cfg.s;
|
|
66
|
+
const g = smooth(0, 0.2, w);
|
|
67
|
+
mx(P, 'gaitW', 0, g); mx(P, 'legGait', 0, g);
|
|
68
|
+
// the hips stay over the planted hind feet (heels down), the torso rises toward vertical
|
|
69
|
+
const r = smooth(0.3, 1, w);
|
|
70
|
+
// (over where the hind feet actually stand: the midpoint of their plants in the heading frame)
|
|
71
|
+
const zPl = 0.5 * (e.worldToBody(e.legs[2].plant, tv()).z + e.worldToBody(e.legs[3].plant, tv()).z);
|
|
72
|
+
// (the hips a little behind the heels: the knees flex slightly and the hip does not over-extend)
|
|
73
|
+
tiltBody(P, g, e, STAND_PITCH * r, -0.02 * s * r, zPl - (0.04 + 0.08 * r) * s);
|
|
74
|
+
mx(P, 'bodyOmega', 1.4, g);
|
|
75
|
+
// forelegs hang in front of the chest, paws at the belly; they leave from and come back down to
|
|
76
|
+
// their plants (the reach target starts and ends there, so nothing slides when the gait takes the
|
|
77
|
+
// feet back)
|
|
78
|
+
const hang = smooth(0.45, 0.9, w), kR = smooth(0, 0.3, w);
|
|
79
|
+
const fwd = e.forward(e.heading, tv()), lat = e.left(e.heading, tv());
|
|
80
|
+
for (let i = 0; i < 2; i++) {
|
|
81
|
+
const Ld = e.legs[i].def;
|
|
82
|
+
const tp = tv().copy(e.fr.shC).addScaledVector(fwd, 0.26 * s).addScaledVector(lat, Ld.s * 0.1 * s);
|
|
83
|
+
tp.y -= 0.34 * s;
|
|
84
|
+
tp.lerp(e.legs[i].plant, 1 - hang);
|
|
85
|
+
P.legs[i].target.lerp(tp, kR / Math.max(1e-3, P.legs[i].reach + kR));
|
|
86
|
+
mxLeg(P, i, 'reach', 1, kR);
|
|
87
|
+
}
|
|
88
|
+
// head held up and level, looking ahead (detail_bipedal); the neck carries it forward of the chest
|
|
89
|
+
// (at a 0.85 rad nose-down pitch the chin was tucked to the chest and the nape stretched over the poll)
|
|
90
|
+
// (the stand sets its own head pitch: the gaze offset of a look is taken back while it rises, update())
|
|
91
|
+
mx(P, 'headRaise', 0.03 / cfg.unit * headUp, w); mx(P, 'headPitch', 0.35 * headUp, w);
|
|
92
|
+
const b = B(e); b.standW = Math.max(b.standW, w);
|
|
93
|
+
mx(P, 'earFlat', -0.1, w); mx(P, 'tailIdle', 0.2, w);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const standup = {
|
|
97
|
+
kind: 'oneshot', fade: 0.6, needsStand: true,
|
|
98
|
+
start(inst, L) {
|
|
99
|
+
const e = L.engine;
|
|
100
|
+
inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 4 + e.rand() * 3);
|
|
101
|
+
const a = e.heading + (e.rand() - 0.5) * 1.4, d = 25 * e.cfg.s;
|
|
102
|
+
inst.look = inst.opts.target ? inst.opts.target.clone() : new THREE.Vector3(e.pos.x + Math.sin(a) * d, e.pos.y + 1.5 * e.cfg.s, e.pos.z + Math.cos(a) * d);
|
|
103
|
+
},
|
|
104
|
+
update(inst) { return inst.t >= inst.dur; },
|
|
105
|
+
apply(P, w, inst, L) {
|
|
106
|
+
const e = L.engine, t = inst.t;
|
|
107
|
+
const up = w * smooth(0, 1.1, t) * (1 - smooth(inst.dur - 0.9, inst.dur, t));
|
|
108
|
+
applyStand(P, up, e);
|
|
109
|
+
if (!e.input.look) { P.look = inst.look; mx(P, 'lookW', 1, up); }
|
|
110
|
+
// scenting: short nose-up sniffs
|
|
111
|
+
const sn = Math.max(0, Math.sin(t * TAU * 0.45)) * up;
|
|
112
|
+
mx(P, 'headPitch', P.headPitch - 0.25, sn);
|
|
113
|
+
mx(P, 'breathRate', 3, sn); mx(P, 'breathAmp', 0.5, sn);
|
|
114
|
+
B(e).sniff = Math.max(B(e).sniff, sn);
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// sniffing the air on all fours: head raised, nose up, working
|
|
119
|
+
const sniff = {
|
|
120
|
+
kind: 'oneshot', fade: 0.4,
|
|
121
|
+
start(inst, L) { inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 3 + L.engine.rand() * 2); inst.yaw = (L.engine.rand() - 0.5) * 0.8; },
|
|
122
|
+
update(inst) { return inst.t >= inst.dur; },
|
|
123
|
+
apply(P, w, inst, L) {
|
|
124
|
+
const e = L.engine, t = inst.t;
|
|
125
|
+
const k = w * smooth(0, 0.6, t);
|
|
126
|
+
mx(P, 'headRaise', 0.1 / e.cfg.unit, k); mx(P, 'headPitch', -0.35 + 0.08 * Math.sin(t * 2.3), k);
|
|
127
|
+
mx(P, 'headYaw', inst.yaw + 0.25 * Math.sin(t * 0.9), k); mx(P, 'lookW', 0, k);
|
|
128
|
+
mx(P, 'breathRate', 3.5, k); mx(P, 'breathAmp', 0.5, k);
|
|
129
|
+
mx(P, 'jaw', 0.02 + 0.02 * Math.max(0, Math.sin(t * 17)), k);
|
|
130
|
+
B(e).sniff = Math.max(B(e).sniff, k);
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// digging / rooting: head low, alternate forepaw rakes toward the body (1.4 Hz), nose in the hole.
|
|
135
|
+
// The paws stay in reach mode for the whole dig (claws down, wrist raised): between strokes a paw rests
|
|
136
|
+
// where its rake ended. (Fading the reach out after every stroke swung the metacarpus from the claws-
|
|
137
|
+
// down rake to the flat plantigrade palm, 93 deg in 55 ms: an 84 rad/s* pop.)
|
|
138
|
+
function rakeTargets(P, k, e, t, rate) {
|
|
139
|
+
const cfg = e.cfg, s = cfg.s;
|
|
140
|
+
const fwd = e.forward(e.heading, tv()), lat = e.left(e.heading, tv());
|
|
141
|
+
for (let i = 0; i < 2; i++) {
|
|
142
|
+
const L = e.legs[i];
|
|
143
|
+
// each paw in turn: reach forward and up, strike down, rake back along the ground; the path is
|
|
144
|
+
// continuous from stroke to stroke (it starts and ends at the resting point 2 cm ahead of the plant)
|
|
145
|
+
const ph = (t * rate + (i === 0 ? 0 : 0.5)) % 1;
|
|
146
|
+
const u = Math.min(1, ph / 0.55);
|
|
147
|
+
const z = 0.02 + 0.3 * (smooth(0, 0.35, u) - smooth(0.45, 1, u));
|
|
148
|
+
const y = 0.13 * smooth(0, 0.22, u) * (1 - smooth(0.28, 0.55, u));
|
|
149
|
+
const tp = tv().copy(e.legs[i].plant).addScaledVector(fwd, z * s).addScaledVector(lat, -L.def.s * 0.03 * s);
|
|
150
|
+
tp.y = e.terrainH(tp.x, tp.z) + (y + 0.025) * s;
|
|
151
|
+
P.legs[i].target.lerp(tp, k / Math.max(1e-3, P.legs[i].reach + k));
|
|
152
|
+
mxLeg(P, i, 'reach', 1, k);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const dig = {
|
|
156
|
+
kind: 'oneshot', fade: 0.5,
|
|
157
|
+
start(inst, L) { inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 5); },
|
|
158
|
+
update(inst) { return inst.t >= inst.dur; },
|
|
159
|
+
apply(P, w, inst, L) {
|
|
160
|
+
const e = L.engine, t = inst.t;
|
|
161
|
+
const k = w * smooth(0, 0.6, t);
|
|
162
|
+
mx(P, 'gaitW', 0, k); mx(P, 'legGait', 0, k);
|
|
163
|
+
mx(P, 'dropF', 0.25, k); mx(P, 'dropH', -0.05, k);
|
|
164
|
+
mx(P, 'headRaise', -0.2 / e.cfg.unit, k); mx(P, 'headPitch', 0.9, k); mx(P, 'lookW', 0, k);
|
|
165
|
+
mx(P, 'headYaw', 0.15 * Math.sin(t * 1.4 * PI), k);
|
|
166
|
+
const ks = k * smooth(0.5, 1.0, t);
|
|
167
|
+
if (ks > 0) rakeTargets(P, ks, e, Math.max(0, t - 0.5), 1.4);
|
|
168
|
+
mx(P, 'breathRate', 2, k);
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// sit on the rump like a person: hips down on the ground, the torso up at ~55 deg, the hind legs
|
|
173
|
+
// stretched forward along the ground, the forepaws resting in the lap between them
|
|
174
|
+
const sit = {
|
|
175
|
+
kind: 'posture', fadeIn: 1.6, fadeOut: 1.3,
|
|
176
|
+
apply(P, w, inst, L) {
|
|
177
|
+
const e = L.engine, cfg = e.cfg, s = cfg.s;
|
|
178
|
+
const wH = smooth(0, 0.7, w), wF = smooth(0.3, 1, w);
|
|
179
|
+
mx(P, 'gaitW', 0, w); mx(P, 'legGait', 0, w);
|
|
180
|
+
// the rump goes down first, then the torso comes up (the hips roll back a little onto the rump)
|
|
181
|
+
const restH = cfg.pelvis.hh - (cfg.pelvis.y - cfg.yH);
|
|
182
|
+
const pT = SIT_PITCH * wF;
|
|
183
|
+
tiltBody(P, w, e, pT, -(cfg.yH - restH) * wH + 0.08 * s * Math.sin(pT), cfg.zH - 0.06 * s * wH);
|
|
184
|
+
mx(P, 'groundW', 1, wH);
|
|
185
|
+
const fwd = e.forward(e.heading, tv()), lat = e.left(e.heading, tv());
|
|
186
|
+
// hind feet: forward of the hips, soles facing forward-up (heels on the ground)
|
|
187
|
+
const wl = smooth(0.1, 0.8, w);
|
|
188
|
+
for (let i = 2; i < 4; i++) {
|
|
189
|
+
const Ld = e.legs[i].def;
|
|
190
|
+
const tp = e.bodyToWorld(Ld.cx * Ld.s * 1.25, 0, cfg.zH + Ld.len * 0.95, e.heading, e.pos, tv());
|
|
191
|
+
tp.y = e.terrainH(tp.x, tp.z) + 0.03 * s;
|
|
192
|
+
P.legs[i].target.copy(tp);
|
|
193
|
+
mxLeg(P, i, 'reach', 1, wl);
|
|
194
|
+
}
|
|
195
|
+
// forepaws in the lap
|
|
196
|
+
const wp = smooth(0.45, 1, w);
|
|
197
|
+
for (let i = 0; i < 2; i++) {
|
|
198
|
+
const Ld = e.legs[i].def;
|
|
199
|
+
const tp = e.bodyToWorld(Ld.cx * Ld.s * 0.7, 0, cfg.zH + Ld.len * 0.55, e.heading, e.pos, tv());
|
|
200
|
+
tp.y = e.terrainH(tp.x, tp.z) + 0.28 * s;
|
|
201
|
+
P.legs[i].target.lerp(tp, wp / Math.max(1e-3, P.legs[i].reach + wp));
|
|
202
|
+
mxLeg(P, i, 'reach', 1, wp);
|
|
203
|
+
}
|
|
204
|
+
void fwd; void lat;
|
|
205
|
+
mx(P, 'headRaise', 0.02 / cfg.unit, wF); mx(P, 'headPitch', 0.45, wF);
|
|
206
|
+
mx(P, 'tailGround', 1, wH);
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// sleep: curled on the side (built-in curl), or prone on the belly with the chin on the forepaws
|
|
211
|
+
const sleep = {
|
|
212
|
+
kind: 'posture', fadeIn: 1.4, fadeOut: 1.2,
|
|
213
|
+
apply(P, w, inst, L) {
|
|
214
|
+
if (inst.style === undefined) inst.style = inst.opts.style || (L.engine.rand() < 0.5 ? 'curl' : 'prone');
|
|
215
|
+
if (inst.style === 'curl') { POSTURES.sleep.apply(P, w, { ...inst, tune: { ...inst.tune, style: 'curl' } }, L); return; }
|
|
216
|
+
POSTURES.lie.apply(P, w, inst, L);
|
|
217
|
+
const e = L.engine, cfg = e.cfg, s = cfg.k, fr = e.fr;
|
|
218
|
+
const c = smooth(0.8, 2.6, inst.t) * w;
|
|
219
|
+
// chin resting on the crossed forepaws in front of the chest
|
|
220
|
+
const hp = tv().copy(fr.neckBase).addScaledVector(fr.F, cfg.neckLen * 0.8);
|
|
221
|
+
hp.y = e.terrainH(hp.x, hp.z) + 0.1 * s;
|
|
222
|
+
P.headPos.lerp(hp, c / Math.max(1e-3, P.headW + c)); mx(P, 'headW', 1, c);
|
|
223
|
+
mx(P, 'headPitch', 0.3, c); mx(P, 'headYaw', 0, c); mx(P, 'headLimp', 0.2, c);
|
|
224
|
+
mx(P, 'eyelid', 1, smooth(1.6, 3.2, inst.t) * w);
|
|
225
|
+
mx(P, 'breathRate', 0.45, c); mx(P, 'breathAmp', 1.4, c);
|
|
226
|
+
mx(P, 'earFlat', 0.2, c); mx(P, 'earTwitch', 0.3, c); mx(P, 'lookW', 0, c);
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// attack: a charge, then it rears up, swats with one forepaw, comes down and bites
|
|
231
|
+
const attack = {
|
|
232
|
+
kind: 'oneshot', fade: 0.15, needsStand: true,
|
|
233
|
+
start(inst, L) {
|
|
234
|
+
const e = L.engine;
|
|
235
|
+
inst.run = smooth(1.5, 3.5, e.speed / e.cfg.sq);
|
|
236
|
+
inst.target = inst.opts.target ? inst.opts.target.clone() : null;
|
|
237
|
+
// (from standing a lunge on the spot, the forefeet planted, then straight up: a rush from standing cost
|
|
238
|
+
// 1.5-2 s of stepping to a stop before the rear-up could start, and the swat came 3.4 s after the
|
|
239
|
+
// command; now ~1.2 s. Already running: it brakes and rears, ~1.7 s)
|
|
240
|
+
inst.charge = inst.run > 0.5 ? 0.2 : 0;
|
|
241
|
+
inst.lunge = inst.run > 0.5 ? 0 : 1;
|
|
242
|
+
inst.dur = Infinity;
|
|
243
|
+
inst.T0 = Infinity; // rear-up start: once the charge has stopped
|
|
244
|
+
inst.side = e.rand() < 0.5 ? 0 : 1; // swatting paw
|
|
245
|
+
inst.swat = false; inst.bit = false;
|
|
246
|
+
},
|
|
247
|
+
update(inst, dt, L) {
|
|
248
|
+
const e = L.engine, cfg = e.cfg, t = inst.t;
|
|
249
|
+
// (a short lumbering rush at the amble: the canter's footfall order would have to change twice)
|
|
250
|
+
const vC = cfg.gears.amble || 2.4 * cfg.sq;
|
|
251
|
+
if (t < inst.charge) e.wantSpeed = Math.max(e.wantSpeed, vC);
|
|
252
|
+
else {
|
|
253
|
+
// the charge brakes hard; it rears up once it has (nearly) stopped
|
|
254
|
+
// (braking over ~0.4 s from the rush: the feet keep up; from a run it stops like stop())
|
|
255
|
+
if (inst.v0 === undefined) inst.v0 = Math.max(vC, e.speed);
|
|
256
|
+
e.wantSpeed = inst.run > 0.5 || inst.lunge ? 0 : inst.v0 * (1 - smooth(inst.charge, inst.charge + 0.4, t));
|
|
257
|
+
// (standing, all four feet down: the gait has gathered the feet under the body first; the reach that
|
|
258
|
+
// lifts the forepaws starts from their plants, so a forefoot still in the air snapped to it: a
|
|
259
|
+
// 160-320 rad/s* pop. A quarter second of stillness is not needed: 0.08 s)
|
|
260
|
+
if (e.speed > 0.08 * cfg.sq || !e.legs.every((l) => l.state === 'stance')) inst.still = 0;
|
|
261
|
+
else inst.still = (inst.still || 0) + dt;
|
|
262
|
+
if (inst.T0 === Infinity && inst.still > 0.08 && t > 0.3 * inst.lunge) inst.T0 = t;
|
|
263
|
+
if (inst.T0 === Infinity && t > inst.charge + 4) inst.T0 = t;
|
|
264
|
+
}
|
|
265
|
+
const T0 = inst.T0;
|
|
266
|
+
if (t >= T0 + 2.6) return true;
|
|
267
|
+
if (!inst.swat && t >= T0 + 0.85) {
|
|
268
|
+
inst.swat = true;
|
|
269
|
+
const p = e.legs[inst.side].contact.clone();
|
|
270
|
+
const dir = inst.target ? inst.target.clone().sub(e.pos).setY(0).normalize() : e.forward(e.heading, new THREE.Vector3());
|
|
271
|
+
e.emit('attackHit', { position: p, direction: dir, style: 'swat' });
|
|
272
|
+
}
|
|
273
|
+
if (!inst.bit && t >= T0 + 1.85) {
|
|
274
|
+
inst.bit = true;
|
|
275
|
+
const p = new THREE.Vector3().copy(cfg.mouthLocal).applyQuaternion(e.headPose.quaternion).add(e.headPose.position);
|
|
276
|
+
e.emit('attackHit', { position: p, direction: e.forward(e.heading, new THREE.Vector3()), style: 'bite' });
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
},
|
|
280
|
+
apply(P, w, inst, L) {
|
|
281
|
+
const e = L.engine, cfg = e.cfg, s = cfg.s, t = inst.t, T0 = inst.T0;
|
|
282
|
+
if (inst.target) { P.look = inst.target; mx(P, 'lookW', 1, w); }
|
|
283
|
+
mx(P, 'earFlat', 1, w * smooth(0, 0.2, t)); mx(P, 'earTwitch', 0, w);
|
|
284
|
+
// charge: head low, ears back
|
|
285
|
+
const ch = w * (1 - smooth(inst.charge - 0.1, inst.charge + 0.4, t));
|
|
286
|
+
mx(P, 'headRaise', -0.06 / cfg.unit, ch); mx(P, 'headPitch', 0.2, ch);
|
|
287
|
+
// lunge from standing: the body surges forward over the planted feet, head low, a huff
|
|
288
|
+
if (inst.lunge) {
|
|
289
|
+
const lg = w * smooth(0, 0.25, t) * (1 - smooth(0.35, 0.7, t));
|
|
290
|
+
mx(P, 'fwd', 0.1 / cfg.unit, lg); mx(P, 'dropF', 0.08, lg); mx(P, 'headRaise', -0.06 / cfg.unit, lg); mx(P, 'headPitch', 0.25, lg);
|
|
291
|
+
mx(P, 'jaw', 0.2, lg * smooth(0.1, 0.25, t)); mx(P, 'breathAmp', 2.5, lg);
|
|
292
|
+
}
|
|
293
|
+
// rear up (not as high as the bipedal stand), swat, come down into the bite
|
|
294
|
+
const u = Number.isFinite(T0) ? t - T0 : -10;
|
|
295
|
+
const rear = w * smooth(0.1, 0.6, u) * (1 - smooth(1.0, 1.8, u));
|
|
296
|
+
applyStand(P, rear * 0.8, e, 0.4);
|
|
297
|
+
mx(P, 'jaw', 0.35, rear * smooth(0.3, 0.6, u));
|
|
298
|
+
// the swat: the paw goes out and up to the side, then sweeps across and down
|
|
299
|
+
const sw = rear * smooth(0.45, 0.7, u) * (1 - smooth(0.95, 1.15, u));
|
|
300
|
+
if (sw > 0) {
|
|
301
|
+
const i = inst.side, Ld = e.legs[i].def, sd = Ld.s;
|
|
302
|
+
const fwd = e.forward(e.heading, tv()), lat = e.left(e.heading, tv());
|
|
303
|
+
const a = smooth(0.6, 0.9, u); // 0 wound up to the side .. 1 swept across
|
|
304
|
+
const tp = tv().copy(e.fr.shC).addScaledVector(fwd, lerp(0.25, 0.55, a) * s).addScaledVector(lat, sd * lerp(0.4, -0.15, a) * s);
|
|
305
|
+
tp.y += lerp(0.25, -0.2, a) * s;
|
|
306
|
+
P.legs[i].target.lerp(tp, sw / Math.max(1e-3, P.legs[i].reach + sw));
|
|
307
|
+
mxLeg(P, i, 'reach', 1, sw);
|
|
308
|
+
mx(P, 'bend', -sd * 0.25 * (a - 0.5), sw); mx(P, 'headYaw', sd * 0.3 * (0.5 - a), sw);
|
|
309
|
+
}
|
|
310
|
+
// bite: lunge forward and down, jaws open then snap shut, a shake of the head
|
|
311
|
+
const bite = w * smooth(1.5, 1.75, u) * (1 - smooth(2.0, 2.5, u));
|
|
312
|
+
mx(P, 'fwd', 0.18 / cfg.unit * 0.3, bite); mx(P, 'neckReach', 0.06 / cfg.unit, bite); mx(P, 'headRaise', -0.05 / cfg.unit, bite);
|
|
313
|
+
mx(P, 'jaw', 0.55 * (1 - smooth(1.8, 1.9, u)), bite); mx(P, 'jawOmega', 30, w);
|
|
314
|
+
mx(P, 'headRoll', 0.25 * Math.sin((u - 1.85) * 22) * smooth(1.85, 1.95, u), bite);
|
|
315
|
+
void s;
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// hit: flinch back, then a loud huff with the head swung and the jaw popping
|
|
320
|
+
const hit = {
|
|
321
|
+
...ONESHOTS.hit,
|
|
322
|
+
kind: 'oneshot',
|
|
323
|
+
start(inst, L) {
|
|
324
|
+
ONESHOTS.hit.start(inst, L);
|
|
325
|
+
inst.dur = 1.5;
|
|
326
|
+
inst.opts = { ...inst.opts, strength: (inst.opts.strength ?? 1) * 0.6 };
|
|
327
|
+
},
|
|
328
|
+
update(inst) { return inst.t >= inst.dur; },
|
|
329
|
+
apply(P, w, inst, L) {
|
|
330
|
+
const t = inst.t;
|
|
331
|
+
const k = smooth(0, 0.1, t) * Math.exp(-Math.max(0, t - 0.1) * 4) * w;
|
|
332
|
+
mx(P, 'roll', inst.lx * 0.12, k); mx(P, 'bend', -inst.lx * 0.2, k);
|
|
333
|
+
mx(P, 'pitch', inst.lz * 0.06, k); mx(P, 'dropF', 0.15, k); mx(P, 'dropH', 0.1, k);
|
|
334
|
+
P.headLocal.set(inst.lx * 0.06, 0.02, inst.lz * 0.06 - 0.03); mx(P, 'headLocalW', 1, k);
|
|
335
|
+
mx(P, 'eyelid', 0.6, k);
|
|
336
|
+
// huff: head swings side to side (the swing that follows the blow), jaw pops twice
|
|
337
|
+
const hs = w * smooth(0.25, 0.45, t) * (1 - smooth(1.1, 1.5, t));
|
|
338
|
+
mx(P, 'headYaw', 0.45 * Math.sin((t - 0.25) * 7.5) * (inst.lx >= 0 ? -1 : 1), hs);
|
|
339
|
+
mx(P, 'headRaise', -0.02, hs); mx(P, 'headPitch', 0.3, hs); mx(P, 'lookW', 0, hs);
|
|
340
|
+
const pop = Math.max(smooth(0.35, 0.42, t) * (1 - smooth(0.46, 0.55, t)), smooth(0.7, 0.77, t) * (1 - smooth(0.81, 0.9, t)));
|
|
341
|
+
mx(P, 'jaw', 0.3 * pop, w); mx(P, 'jawOmega', 28, w);
|
|
342
|
+
mx(P, 'earFlat', 1, w * (1 - smooth(0.9, 1.5, t)));
|
|
343
|
+
mx(P, 'breathAmp', 3, hs); mx(P, 'breathRate', 2.5, hs);
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// ------------------------------------------------------------------------------------------------
|
|
348
|
+
function update(P, dt, L) {
|
|
349
|
+
const e = L.engine, b = B(e), cfg = e.cfg;
|
|
350
|
+
const vn = e.speed / cfg.sq;
|
|
351
|
+
b.sniff *= Math.exp(-dt * 3);
|
|
352
|
+
// the big broad head (cheeks ~15 cm from the midline with the ruff) rests higher on its side than
|
|
353
|
+
// the engine's default dead-head height; the grazing / drinking mouth works just above the ground
|
|
354
|
+
for (const p of L.postures) {
|
|
355
|
+
if (p.name === 'death' && P.headW > 0) P.headPos.y += HEAD_SIDE * cfg.s * smooth(0.3, 1.0, p.t) * p.w;
|
|
356
|
+
if (p.name === 'death') {
|
|
357
|
+
// the broad paws lie on their sides: the engine's side-lying leg rest (3 cm up) put their edges
|
|
358
|
+
// into the ground; rest them a paw's half width up (same place, reach mode)
|
|
359
|
+
const fr = e.fr;
|
|
360
|
+
for (let i = 0; i < 4; i++) {
|
|
361
|
+
const pl = P.legs[i], k = pl.side;
|
|
362
|
+
if (k < 1e-3) continue;
|
|
363
|
+
const Ld = e.legs[i].def, gy = Ld.front ? cfg.yS : cfg.yH, out = pl.spread;
|
|
364
|
+
const lz = (Ld.front ? cfg.zS + 0.18 * Ld.len : cfg.zH - 0.02 * Ld.len) + (Ld.s * Math.sign(P.roll || 1) > 0 ? 0.12 : 0) * Ld.len * out;
|
|
365
|
+
const tp = tv().set(Ld.cx * Ld.s * 1.2, gy - Ld.len * lerp(0.85, 0.95, out), lz).applyQuaternion(fr.qB).add(fr.origin);
|
|
366
|
+
const gh = e.terrainH(tp.x, tp.z) + 0.09 * cfg.s;
|
|
367
|
+
tp.y = Math.max(gh, lerp(tp.y, gh, 0.85));
|
|
368
|
+
pl.target.copy(tp);
|
|
369
|
+
mxLeg(P, i, 'reach', 1, k);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
for (const a of L.oneshots) {
|
|
374
|
+
if ((a.name === 'eat' || a.name === 'drink') && P.headW > 0) P.headPos.y += 0.02 * cfg.s * a.w;
|
|
375
|
+
}
|
|
376
|
+
// lumbering walk: the head swings with the forelegs, the shoulders and hump roll over each
|
|
377
|
+
// planted foreleg (walk and amble only)
|
|
378
|
+
const walk = smooth(0.1, 0.5, vn) * (1 - smooth(2.4, 3.2, vn)) * P.gaitW;
|
|
379
|
+
if (walk > 1e-3) {
|
|
380
|
+
const ph = TAU * (e.phase - 0.25);
|
|
381
|
+
// (+-6 deg with the forelegs, NOTES: the engine's own head stabilisation takes back half of it)
|
|
382
|
+
mx(P, 'headYaw', P.headYaw + 0.2 * Math.sin(ph), walk);
|
|
383
|
+
P.roll += 0.06 * Math.sin(ph) * walk;
|
|
384
|
+
P.headRoll += 0.05 * Math.sin(ph) * walk;
|
|
385
|
+
}
|
|
386
|
+
// looking up raises the neck with the head: the head hangs low at rest (0.34 rad nose down, 8 cm under its
|
|
387
|
+
// bind height), and a head pitched up about the poll alone to look level extended the poll ~18 deg past its
|
|
388
|
+
// bind angle: the nape skin folded and its long hair stood up as a crack across the crown. The neck rises
|
|
389
|
+
// 0.25 m per rad the look is above the carriage (about the neck's length: the poll keeps its angle)
|
|
390
|
+
// (eased like the head's own pitch: a look target that jumps must not step the neck, 105 rad/s* pops)
|
|
391
|
+
// The look pitch carries the gaze offset (GAZE: the muzzle below the line of sight), except while the bear
|
|
392
|
+
// rises onto its hind legs, whose pose sets the head's pitch itself; the neck rises only for the part of the
|
|
393
|
+
// look above the carriage that the offset leaves
|
|
394
|
+
const look = e.input.look || P.look;
|
|
395
|
+
const standW = b.standW; b.standW = 0;
|
|
396
|
+
let upT = 0;
|
|
397
|
+
if (look && P.lookW > 0.01) {
|
|
398
|
+
const lw = clamp(P.lookW, 0, 1), gz = GAZE * (1 - smooth(0, 0.3, standW));
|
|
399
|
+
P.headPitch += gz * lw;
|
|
400
|
+
const hp = e.headPose.position;
|
|
401
|
+
const lp = -Math.atan2(look.y - hp.y, Math.hypot(look.x - hp.x, look.z - hp.z));
|
|
402
|
+
const cp = tableAt(cfg.headCarry, vn, b.carry || (b.carry = [0, 0, 0]))[2];
|
|
403
|
+
upT = clamp(cp - (Math.max(lp, -cfg.head.pitchUp) + gz), 0, 0.5) * lw;
|
|
404
|
+
}
|
|
405
|
+
b.up = (b.up || 0) + (upT - (b.up || 0)) * (1 - Math.exp(-dt * 3));
|
|
406
|
+
P.headRaise += 0.25 * b.up / cfg.unit;
|
|
407
|
+
// the neck bends toward the head's yaw (the engine's eased head yaw: looks, the walk's head swing, turns), so the turn
|
|
408
|
+
// is shared between the neck and the poll (added to any posture's occiput offset, e.g. the hit's flinch)
|
|
409
|
+
const yawH = e.lookYaw ? e.lookYaw.x : 0;
|
|
410
|
+
if (Math.abs(yawH) > 1e-4) {
|
|
411
|
+
P.headLocal.multiplyScalar(P.headLocalW);
|
|
412
|
+
P.headLocal.x += NECK_SWAY * clamp(yawH, -1.2, 1.2) / cfg.unit;
|
|
413
|
+
P.headLocalW = 1;
|
|
414
|
+
}
|
|
415
|
+
// any other action interrupts grazing / drinking / digging
|
|
416
|
+
if (L.oneshots.some((a) => !a.stopping && !/^(eat|drink|dig|hit|sniff)$/.test(a.name))) for (const a of L.oneshots) if (/^(eat|drink|dig)$/.test(a.name)) a.stopping = true;
|
|
417
|
+
// a far look target: stand up on the hind legs to see over the grass (now and then)
|
|
418
|
+
b.lookStandT -= dt;
|
|
419
|
+
if (e.input.look && L.idle.w > 0.9 && !busy(L) && b.lookStandT <= 0) {
|
|
420
|
+
const d = Math.hypot(e.input.look.x - e.pos.x, e.input.look.z - e.pos.z);
|
|
421
|
+
b.lookStandT = 6 + e.rand() * 6;
|
|
422
|
+
if (d > 12 * cfg.s && e.rand() < 0.7) L.play('standup', { target: e.input.look, duration: 3.5 + e.rand() * 2 });
|
|
423
|
+
}
|
|
424
|
+
// automatic idle variants
|
|
425
|
+
if (e.idleVariants !== false && L.idle.w > 0.98 && !busy(L) && !e.input.look && !e.input.follow) {
|
|
426
|
+
b.idleT -= dt;
|
|
427
|
+
if (b.idleT <= 0) {
|
|
428
|
+
b.idleT = 9 + e.rand() * 12;
|
|
429
|
+
const r = e.rand();
|
|
430
|
+
if (r < 0.35) L.play('sniff');
|
|
431
|
+
else if (r < 0.6) L.play('standup');
|
|
432
|
+
else if (r < 0.8) L.play('dig', { duration: 3 + e.rand() * 3 });
|
|
433
|
+
}
|
|
434
|
+
} else if (busy(L)) b.idleT = Math.max(b.idleT, 4);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// jump: the built-in jump, but the push-off never extends the legs past standing (the flat
|
|
438
|
+
// plantigrade feet cannot roll up onto their toes while the gait is held: the knee snapped straight).
|
|
439
|
+
// In the air a bear bounds (research 4.2: a poor jumper): the hind legs trail behind after the push-off
|
|
440
|
+
// and swing forward only for the landing, the forelegs reach ahead to land first (the built-in flight
|
|
441
|
+
// tucked all four feet under the body like a cat)
|
|
442
|
+
const jump = {
|
|
443
|
+
...ONESHOTS.jump,
|
|
444
|
+
kind: 'oneshot', airborne: true,
|
|
445
|
+
apply(P, w, inst, L) {
|
|
446
|
+
ONESHOTS.jump.apply(P, w, inst, L);
|
|
447
|
+
if (inst.phase === 'crouch' || inst.phase === 'push') { P.dropH = Math.max(P.dropH, 0); P.dropF = Math.max(P.dropF, 0); }
|
|
448
|
+
if (inst.phase === 'air') {
|
|
449
|
+
const T = (2 * inst.vy) / 9.81, x = clamp((inst.t - inst.tAir) / T, 0, 1);
|
|
450
|
+
const k = smooth(0, 0.12, inst.t - inst.tAir);
|
|
451
|
+
// (both end at the built-in landing's 0.8, which the landing phase starts from: a hind leg arriving
|
|
452
|
+
// at 0.35 snapped to it, 225 rad/s* at the hind paw)
|
|
453
|
+
const extF = 0.3 + 0.5 * smooth(0.15, 0.85, x);
|
|
454
|
+
const extH = 1 - 0.2 * smooth(0.4, 1, x);
|
|
455
|
+
for (let i = 0; i < 4; i++) P.legs[i].extend = lerp(P.legs[i].extend, i < 2 ? extF : extH, k);
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
export const hooks = {
|
|
461
|
+
update,
|
|
462
|
+
actions: { standup, sniff, dig, sit, sleep, attack, hit, jump },
|
|
463
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Claws: real geometry (species.surfaces, appended to the base mesh), rigidly skinned to the paw bones.
|
|
2
|
+
//
|
|
3
|
+
// A brown bear's fore claws are 5-6 cm straight / 7-10 cm along the curve (x1.15 in the grizzly, x0.65 in the
|
|
4
|
+
// black bear), the hind claws ~4 cm [NOTES 1]: thick horn at the base, laterally compressed (taller than wide),
|
|
5
|
+
// tapering to a blunt point, curving down from the toe so they lie close along the ground in front of the toes
|
|
6
|
+
// (side1, side3, tq2, detail_bipedal); dark horn with paler tips (in grizzlies paler horn, still darker at the base).
|
|
7
|
+
// (As SDF cones in the body mesh, at its 7.8-15.6 mm cells, they were faceted spikes 0.6-0.9 cm thick sticking
|
|
8
|
+
// straight out 5-6 cm ahead of the toes at toe height, with the mouth's pink interpolated round their bases where
|
|
9
|
+
// the material id switched from fur to keratin.)
|
|
10
|
+
// Each claw is a closed tube along a circular arc in the paw's sagittal plane: an elliptical cross-section
|
|
11
|
+
// (height 1.5 x width at the base) that tapers to an apex, its root buried in the toe.
|
|
12
|
+
import { srgb, MAT } from '../../core/build/coatKit.js';
|
|
13
|
+
import { add, mul, norm, cross } from '../../core/math/vec.js';
|
|
14
|
+
import { TOE_F, TOE_H } from './sculpt.js';
|
|
15
|
+
import { clawColour } from './coat.js';
|
|
16
|
+
|
|
17
|
+
// digits I..V of the left paw (x from the midline out: I is medial), toe sphere offsets as in sculpt.js
|
|
18
|
+
const FORE = { x: [-0.056, -0.028, 0.0, 0.028, 0.056], z: [-0.02, -0.004, 0.002, -0.004, -0.02], y: 0.024, len: [0.78, 0.94, 1, 0.97, 0.84] };
|
|
19
|
+
const HIND = { x: [-0.048, -0.024, 0.0, 0.024, 0.048], z: [-0.018, -0.004, 0.002, -0.004, -0.016], y: 0.022, len: [0.8, 0.95, 1, 0.97, 0.85] };
|
|
20
|
+
|
|
21
|
+
// the claw's centre line: pitch below horizontal from th0 at the root to th1 at the tip, along an arc of length L;
|
|
22
|
+
// th1 is chosen so the tip ends `drop` below the root (clamped to 75 deg: short black-bear claws hook more steeply)
|
|
23
|
+
function arcFor(L, drop, th0) {
|
|
24
|
+
const dropOf = (th1) => (Math.abs(th1 - th0) < 1e-4 ? L * Math.sin(th0) : (L * (Math.cos(th0) - Math.cos(th1))) / (th1 - th0));
|
|
25
|
+
let lo = th0, hi = (75 * Math.PI) / 180;
|
|
26
|
+
if (dropOf(hi) < drop) return hi;
|
|
27
|
+
if (dropOf(lo) > drop) return lo;
|
|
28
|
+
for (let i = 0; i < 30; i++) { const m = (lo + hi) / 2; if (dropOf(m) < drop) lo = m; else hi = m; }
|
|
29
|
+
return (lo + hi) / 2;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function clawSpecs(rig, params = {}) {
|
|
33
|
+
const J = rig.J, k = params.claw ?? 1;
|
|
34
|
+
const out = [];
|
|
35
|
+
for (const S of ['L', 'R']) {
|
|
36
|
+
const s = S === 'L' ? 1 : -1;
|
|
37
|
+
for (const fore of [true, false]) {
|
|
38
|
+
const D = fore ? FORE : HIND;
|
|
39
|
+
const T = fore ? J['ftoe' + S] : J['htoe' + S];
|
|
40
|
+
const back = fore ? TOE_F : TOE_H;
|
|
41
|
+
for (let i = 0; i < 5; i++) {
|
|
42
|
+
const tp = [T[0] + D.x[i] * s, D.y, T[2] - back - 0.012 + D.z[i]];
|
|
43
|
+
// root inside the toe, at its front, at the toe's mid height (the toe's hair is 8 mm: the claw shows from there on)
|
|
44
|
+
// (rooted at the top of the toe the claws dropped ~3 cm to the ground and hung from the toes like talons)
|
|
45
|
+
const root = add(tp, [D.x[i] * s * 0.08, 0.001, fore ? 0.012 : 0.01]);
|
|
46
|
+
// fore middle claw 5.8 cm along the curve (~4.2 cm of it outside the toe), hind 2.6 cm (~1.4 cm showing: the hind
|
|
47
|
+
// claws are much shorter, mostly hidden in the toe hair)
|
|
48
|
+
const L = (fore ? 0.058 : 0.026) * k * D.len[i];
|
|
49
|
+
// the tip 5 mm above the ground (standing), at most: in the stance the claws lie along the ground
|
|
50
|
+
const th0 = ((fore ? 3 : 10) * Math.PI) / 180;
|
|
51
|
+
const th1 = arcFor(L, root[1] - (fore ? 0.006 : 0.008), th0);
|
|
52
|
+
// splay: the outer claws turn a little outward with their toes
|
|
53
|
+
const yaw = D.x[i] * s * 2.4;
|
|
54
|
+
// (the width grows nearly with the length: at sqrt(claw) the grizzly's long claws read as thin needles)
|
|
55
|
+
const kk = k ** 0.75;
|
|
56
|
+
out.push({
|
|
57
|
+
bone: (fore ? 'fpaw' : 'hpaw') + S, root, L, th0, th1, yaw,
|
|
58
|
+
// (thick at the base: at 1.05 cm wide the fore claws of a lifted paw read as thin spikes in the live page)
|
|
59
|
+
h0: (fore ? 0.0175 : 0.0125) * kk * (0.85 + 0.15 * D.len[i]), w0: (fore ? 0.012 : 0.009) * kk * (0.85 + 0.15 * D.len[i]),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// centre-line point, tangent and arc normal (toward the claw's dorsal side) at t in [0, 1]
|
|
68
|
+
function frameAt(c, t) {
|
|
69
|
+
const n = 16;
|
|
70
|
+
let x = 0, y = 0;
|
|
71
|
+
const thAt = (u) => c.th0 + (c.th1 - c.th0) * u;
|
|
72
|
+
for (let i = 0; i < n; i++) { const u = ((i + 0.5) / n) * t; x += Math.cos(thAt(u)) * (c.L * t) / n; y -= Math.sin(thAt(u)) * (c.L * t) / n; }
|
|
73
|
+
const th = thAt(t);
|
|
74
|
+
const cy = Math.cos(c.yaw), sy = Math.sin(c.yaw);
|
|
75
|
+
const fwd = [sy, 0, cy];
|
|
76
|
+
const P = add(c.root, add(mul(fwd, x), [0, y, 0]));
|
|
77
|
+
const Tn = norm(add(mul(fwd, Math.cos(th)), [0, -Math.sin(th), 0]));
|
|
78
|
+
const Up = norm(add(mul(fwd, Math.sin(th)), [0, Math.cos(th), 0]));
|
|
79
|
+
return { P, T: Tn, U: Up, X: norm(cross(Up, Tn)) };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function bearClaws({ rig, params, Q }) {
|
|
83
|
+
if (!Q || Q.shells <= 0) return null;
|
|
84
|
+
const SEG = Q.res <= 1.05 ? 8 : Q.res <= 1.5 ? 7 : 4;
|
|
85
|
+
const SIDES = Q.res <= 1.05 ? 8 : Q.res <= 1.5 ? 7 : Q.res <= 2.5 ? 6 : 5;
|
|
86
|
+
const { base, tip } = clawColour(params);
|
|
87
|
+
const pos = [], nrm = [], idx = [], comb = [], skinIndex = [], tint = [];
|
|
88
|
+
for (const c of clawSpecs(rig, params)) {
|
|
89
|
+
const bi = rig.BONE[c.bone].index;
|
|
90
|
+
const v0 = pos.length / 3;
|
|
91
|
+
const push = (p, n, T, t) => {
|
|
92
|
+
pos.push(...p); nrm.push(...n); comb.push(...T); skinIndex.push(bi, 0, 0, 0);
|
|
93
|
+
// dark horn at the base, paler toward the tip (worn keratin; the tip's last third turns down out of the sun, so the
|
|
94
|
+
// paler horn starts a third of the way out)
|
|
95
|
+
const w = Math.min(1, Math.max(0, (t - 0.3) / 0.7)) ** 1.2;
|
|
96
|
+
tint.push(base[0] + (tip[0] - base[0]) * w, base[1] + (tip[1] - base[1]) * w, base[2] + (tip[2] - base[2]) * w, MAT.KERATIN);
|
|
97
|
+
};
|
|
98
|
+
// root cap centre (buried in the toe)
|
|
99
|
+
const f0 = frameAt(c, 0);
|
|
100
|
+
push(f0.P, mul(f0.T, -1), f0.X, 0);
|
|
101
|
+
for (let k = 0; k < SEG; k++) {
|
|
102
|
+
// rings closer together toward the tip, where the claw turns and narrows
|
|
103
|
+
const t = 1 - (1 - k / SEG) ** 1.25;
|
|
104
|
+
const f = frameAt(c, t);
|
|
105
|
+
// the section tapers to a blunt point (height 1.8 mm, width 1.4 mm, at the last ring)
|
|
106
|
+
const taper = (1 - t) ** 0.6;
|
|
107
|
+
const a = Math.max(0.0007, c.w0 * 0.5 * taper), b = Math.max(0.0009, c.h0 * 0.5 * taper);
|
|
108
|
+
for (let q = 0; q < SIDES; q++) {
|
|
109
|
+
const ang = (q / SIDES) * Math.PI * 2;
|
|
110
|
+
const ca = Math.cos(ang), sa = Math.sin(ang);
|
|
111
|
+
// (the dorsal edge rounder, the sole a little flatter: the centre sits slightly below the section's middle)
|
|
112
|
+
const p = add(f.P, add(mul(f.X, a * ca), mul(f.U, b * sa - (sa < 0 ? 0.1 * b * sa : 0))));
|
|
113
|
+
const n = norm(add(mul(f.X, ca / a), mul(f.U, sa / b)));
|
|
114
|
+
// the comb runs round the claw, not along it: the keratin's anisotropic lobe (a sheen wherever the half vector
|
|
115
|
+
// is square to the comb) then lies in one thin line instead of silvering the claw's whole lit side
|
|
116
|
+
push(p, n, norm(add(mul(f.X, -a * sa), mul(f.U, b * ca))), t);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const f1 = frameAt(c, 1);
|
|
120
|
+
push(f1.P, f1.T, f1.X, 1);
|
|
121
|
+
const ring = (k) => v0 + 1 + k * SIDES;
|
|
122
|
+
for (let q = 0; q < SIDES; q++) { const q1 = (q + 1) % SIDES; idx.push(v0, ring(0) + q1, ring(0) + q); }
|
|
123
|
+
for (let k = 0; k < SEG - 1; k++) {
|
|
124
|
+
for (let q = 0; q < SIDES; q++) {
|
|
125
|
+
const q1 = (q + 1) % SIDES;
|
|
126
|
+
const a0 = ring(k) + q, b0 = ring(k) + q1, a1 = ring(k + 1) + q, b1 = ring(k + 1) + q1;
|
|
127
|
+
idx.push(a0, b0, a1, b0, b1, a1);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const apex = pos.length / 3 - 1, last = ring(SEG - 1);
|
|
131
|
+
for (let q = 0; q < SIDES; q++) { const q1 = (q + 1) % SIDES; idx.push(last + q, last + q1, apex); }
|
|
132
|
+
}
|
|
133
|
+
const nV = pos.length / 3;
|
|
134
|
+
const surf = new Float32Array(nV * 4);
|
|
135
|
+
// matte horn (the keratin's roughest, 0.55): at 0.6 (the default) the tips flashed white in the sun, at 0.35 the
|
|
136
|
+
// anisotropic sheen ran along each claw as a silver stripe and the sky's reflection silvered their sides
|
|
137
|
+
for (let v = 0; v < nV; v++) surf[v * 4] = 0.001;
|
|
138
|
+
const skinWeight = new Float32Array(nV * 4);
|
|
139
|
+
for (let v = 0; v < nV; v++) skinWeight[v * 4] = 1;
|
|
140
|
+
return {
|
|
141
|
+
nV, pos: new Float32Array(pos), nrm: new Float32Array(nrm), index: new Uint32Array(idx),
|
|
142
|
+
skinIndex: new Uint16Array(skinIndex), skinWeight, comb: new Float32Array(comb), tint: new Float32Array(tint),
|
|
143
|
+
pattern: new Float32Array(nV).fill(1), mark: new Float32Array(nV).fill(1), furLen: new Float32Array(nV),
|
|
144
|
+
patternColor: new Float32Array(nV * 4), surf,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
void srgb;
|