@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
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// .animal bake files: a built individual, stored so it loads instantly (no SDF meshing, no weights).
|
|
2
|
-
// Layout: "PANM" | u32 version | u32 headerBytes | header JSON (utf-8, padded to 8) | array blobs (8-aligned)
|
|
3
|
-
import { ARRAY_KEYS, FORMAT_VERSION } from './core/build/pipeline.js';
|
|
4
|
-
|
|
5
|
-
const TYPES = { Float32Array, Uint32Array, Uint16Array, Int16Array, Uint8Array, Int8Array, Int32Array };
|
|
6
|
-
|
|
7
|
-
export function writeBake(data) {
|
|
8
|
-
const header = {};
|
|
9
|
-
for (const [k, v] of Object.entries(data)) if (!ARRAY_KEYS.includes(k)) header[k] = v;
|
|
10
|
-
header.arrays = [];
|
|
11
|
-
let offset = 0;
|
|
12
|
-
for (const k of ARRAY_KEYS) {
|
|
13
|
-
const a = data[k];
|
|
14
|
-
header.arrays.push({ key: k, type: a.constructor.name, length: a.length, offset });
|
|
15
|
-
offset += Math.ceil(a.byteLength / 8) * 8;
|
|
16
|
-
}
|
|
17
|
-
let json = new TextEncoder().encode(JSON.stringify(header));
|
|
18
|
-
const hb = Math.ceil((json.length) / 8) * 8;
|
|
19
|
-
const total = 12 + hb + offset;
|
|
20
|
-
const buf = new ArrayBuffer(Math.ceil(total / 8) * 8 + 8);
|
|
21
|
-
const u8 = new Uint8Array(buf);
|
|
22
|
-
u8.set([80, 65, 78, 77], 0); // "PANM"
|
|
23
|
-
const dv = new DataView(buf);
|
|
24
|
-
dv.setUint32(4, FORMAT_VERSION, true);
|
|
25
|
-
dv.setUint32(8, hb, true);
|
|
26
|
-
u8.set(json, 12);
|
|
27
|
-
for (let i = 12 + json.length; i < 12 + hb; i++) u8[i] = 32;
|
|
28
|
-
const base = 16 + hb - ((16 + hb) % 8 === 0 ? 0 : (16 + hb) % 8) ;
|
|
29
|
-
// blobs start at an 8-aligned offset after the header
|
|
30
|
-
const start = align8(12 + hb);
|
|
31
|
-
for (const a of header.arrays) u8.set(new Uint8Array(data[a.key].buffer, data[a.key].byteOffset, data[a.key].byteLength), start + a.offset);
|
|
32
|
-
void base;
|
|
33
|
-
return buf.slice(0, start + offset);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function readBake(buf) {
|
|
37
|
-
const u8 = new Uint8Array(buf);
|
|
38
|
-
if (u8[0] !== 80 || u8[1] !== 65 || u8[2] !== 78 || u8[3] !== 77) throw new Error('not a .animal file');
|
|
39
|
-
const dv = new DataView(buf);
|
|
40
|
-
const version = dv.getUint32(4, true);
|
|
41
|
-
if (version !== FORMAT_VERSION) throw new Error(`.animal version ${version} is not supported (expected ${FORMAT_VERSION})`);
|
|
42
|
-
const hb = dv.getUint32(8, true);
|
|
43
|
-
const header = JSON.parse(new TextDecoder().decode(u8.subarray(12, 12 + hb)));
|
|
44
|
-
const start = align8(12 + hb);
|
|
45
|
-
const data = { ...header };
|
|
46
|
-
for (const a of header.arrays) data[a.key] = new TYPES[a.type](buf, start + a.offset, a.length);
|
|
47
|
-
delete data.arrays;
|
|
48
|
-
return data;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const align8 = (n) => Math.ceil(n / 8) * 8;
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// Builds an individual's data: in Web Workers when possible (meshing jobs in parallel, then
|
|
2
|
-
// weights + coat), otherwise on the calling thread. Results are cached per (species, seed, quality,
|
|
3
|
-
// sex, age) so a herd that reuses individuals shares geometry.
|
|
4
|
-
import { buildSync, normaliseOptions } from './core/build/pipeline.js';
|
|
5
|
-
import { loadSpecies } from './registry.js';
|
|
6
|
-
|
|
7
|
-
let defaultWorkerFactory = null;
|
|
8
|
-
export function setWorkerFactory(fn) { defaultWorkerFactory = fn; }
|
|
9
|
-
|
|
10
|
-
function makeWorker(opt) {
|
|
11
|
-
if (opt === false) return null;
|
|
12
|
-
try {
|
|
13
|
-
if (typeof opt === 'function') return opt();
|
|
14
|
-
if (defaultWorkerFactory) return defaultWorkerFactory();
|
|
15
|
-
if (typeof Worker === 'undefined') return null;
|
|
16
|
-
return new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
|
|
17
|
-
} catch (e) {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const cache = new Map();
|
|
23
|
-
export function cacheKey(speciesId, opts) {
|
|
24
|
-
const o = normaliseOptions(opts);
|
|
25
|
-
return [speciesId, o.seed, o.quality, o.sex || '', o.age || '', o.variant || '', JSON.stringify(o.overrides)].join('|');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export async function buildAnimalData(species, opts = {}, onProgress = () => {}) {
|
|
29
|
-
const s = await loadSpecies(species);
|
|
30
|
-
const key = cacheKey(s.id, opts);
|
|
31
|
-
if (cache.has(key)) return cache.get(key);
|
|
32
|
-
const p = buildInWorkers(s, opts, onProgress).catch((e) => { cache.delete(key); throw e; });
|
|
33
|
-
cache.set(key, p);
|
|
34
|
-
return p;
|
|
35
|
-
}
|
|
36
|
-
export function clearBuildCache() { cache.clear(); }
|
|
37
|
-
|
|
38
|
-
async function buildInWorkers(species, opts, onProgress) {
|
|
39
|
-
const o = normaliseOptions(opts);
|
|
40
|
-
const nJobs = species.regions ? null : 1;
|
|
41
|
-
void nJobs;
|
|
42
|
-
const first = makeWorker(opts.worker);
|
|
43
|
-
if (!first) {
|
|
44
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
45
|
-
return buildSync(species, o, onProgress);
|
|
46
|
-
}
|
|
47
|
-
const t0 = performance.now();
|
|
48
|
-
// how many meshing jobs? (cheap: prepare runs the sculpt only)
|
|
49
|
-
const { prepare } = await import('./core/build/pipeline.js');
|
|
50
|
-
const jobsN = prepare(species, o).reg.jobs.length;
|
|
51
|
-
const workers = [first];
|
|
52
|
-
for (let i = 1; i < jobsN; i++) { const w = makeWorker(opts.worker); if (w) workers.push(w); }
|
|
53
|
-
const call = (w, msg) => new Promise((resolve, reject) => {
|
|
54
|
-
w.onmessage = (e) => {
|
|
55
|
-
const m = e.data;
|
|
56
|
-
if (m.type === 'progress') onProgress(m.msg);
|
|
57
|
-
else if (m.type === 'error') reject(new Error(m.message));
|
|
58
|
-
else resolve(m);
|
|
59
|
-
};
|
|
60
|
-
w.onerror = (e) => reject(new Error(e.message || 'procedural-animals worker failed'));
|
|
61
|
-
w.postMessage(msg);
|
|
62
|
-
});
|
|
63
|
-
try {
|
|
64
|
-
const jobs = new Array(jobsN);
|
|
65
|
-
let next = 0;
|
|
66
|
-
await Promise.all(workers.map(async (w) => {
|
|
67
|
-
while (next < jobsN) {
|
|
68
|
-
const j = next++;
|
|
69
|
-
onProgress('mesh ' + j);
|
|
70
|
-
const r = await call(w, { task: 'mesh', species: species.id, opts: o, job: j });
|
|
71
|
-
jobs[j] = r.parts;
|
|
72
|
-
}
|
|
73
|
-
}));
|
|
74
|
-
onProgress('finish');
|
|
75
|
-
const r = await call(first, { task: 'finish', species: species.id, opts: o, jobs });
|
|
76
|
-
r.data.stats.buildMs = Math.round(performance.now() - t0);
|
|
77
|
-
return r.data;
|
|
78
|
-
} finally {
|
|
79
|
-
for (const w of workers) w.terminate();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
// Helpers for species coat painters.
|
|
2
|
-
import { SDFModel } from '../sdf/sdf.js';
|
|
3
|
-
import { sub, add, mul, dot, len, clamp, mix } from '../math/vec.js';
|
|
4
|
-
|
|
5
|
-
// sRGB hex -> linear rgb
|
|
6
|
-
export const srgb = (hex) => {
|
|
7
|
-
const c = [(hex >> 16) & 255, (hex >> 8) & 255, hex & 255].map((v) => v / 255);
|
|
8
|
-
return c.map((v) => (v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4)));
|
|
9
|
-
};
|
|
10
|
-
export const mix3 = (a, b, t) => [mix(a[0], b[0], t), mix(a[1], b[1], t), mix(a[2], b[2], t)];
|
|
11
|
-
export const scale3 = (a, k) => [a[0] * k, a[1] * k, a[2] * k];
|
|
12
|
-
|
|
13
|
-
// Material ids stored in tint.w (see render/coatMaterial.js)
|
|
14
|
-
export const MAT = { FUR: 0, NOSE: 1, DARK_SKIN: 2, MOUTH: 3, SKIN: 4, KERATIN: 5, SCALES: 6, WET_SKIN: 7, CHITIN: 8, FEATHER: 9, VANE: 10, FIN: 11 }; // 10: flight-feather vane (featherCards.js), 11: fin membrane (render/membranes.js)
|
|
15
|
-
|
|
16
|
-
// Newton-project a point onto the zero set of a primitive list
|
|
17
|
-
export function projectToSurface(prims, q, iters = 6) {
|
|
18
|
-
let x = q.slice();
|
|
19
|
-
for (let i = 0; i < iters; i++) {
|
|
20
|
-
const d = SDFModel.evalList(prims, x[0], x[1], x[2]);
|
|
21
|
-
const e = 0.0005;
|
|
22
|
-
const g = [
|
|
23
|
-
SDFModel.evalList(prims, x[0] + e, x[1], x[2]) - d,
|
|
24
|
-
SDFModel.evalList(prims, x[0], x[1] + e, x[2]) - d,
|
|
25
|
-
SDFModel.evalList(prims, x[0], x[1], x[2] + e) - d,
|
|
26
|
-
].map((c) => c / e);
|
|
27
|
-
const gl = len(g) || 1;
|
|
28
|
-
x = sub(x, mul(g, d / (gl * gl)));
|
|
29
|
-
}
|
|
30
|
-
return x;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// distance from p to a polyline with per-vertex half widths (negative inside)
|
|
34
|
-
export function distPolyline(p, poly, widths) {
|
|
35
|
-
let best = 1e9;
|
|
36
|
-
for (let i = 0; i < poly.length - 1; i++) {
|
|
37
|
-
const a = poly[i], b = poly[i + 1], ab = sub(b, a);
|
|
38
|
-
const t = clamp(dot(sub(p, a), ab) / dot(ab, ab), 0, 1);
|
|
39
|
-
const d = len(sub(p, add(a, mul(ab, t)))) - mix(widths[i], widths[i + 1], t);
|
|
40
|
-
if (d < best) best = d;
|
|
41
|
-
}
|
|
42
|
-
return best;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Laplacian smoothing of a scalar field over the mesh graph; `lock(v)` keeps a vertex fixed
|
|
46
|
-
export function smoothField(f, neighbors, iters = 2, lock = null) {
|
|
47
|
-
const n = f.length, tmp = new Float32Array(n);
|
|
48
|
-
for (let it = 0; it < iters; it++) {
|
|
49
|
-
for (let v = 0; v < n; v++) {
|
|
50
|
-
const ns = neighbors[v];
|
|
51
|
-
if (!ns.length || (lock && lock(v))) { tmp[v] = f[v]; continue; }
|
|
52
|
-
let a = 0;
|
|
53
|
-
for (const u of ns) a += f[u];
|
|
54
|
-
tmp[v] = 0.5 * f[v] + (0.5 * a) / ns.length;
|
|
55
|
-
}
|
|
56
|
-
f.set(tmp);
|
|
57
|
-
}
|
|
58
|
-
return f;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Poisson-disc features grown on the surface. radius(v) -> r (0 = no feature here);
|
|
62
|
-
// returns { spots, grid, cell, key } for use with featureField.
|
|
63
|
-
export function poissonFeatures({ pos, nrm, nV, radius, R, spacing = 1.52, gap = 0.0025, cell = 0.03, skip = null, extra = null }) {
|
|
64
|
-
const order = new Uint32Array(nV);
|
|
65
|
-
for (let i = 0; i < nV; i++) order[i] = i;
|
|
66
|
-
for (let i = nV - 1; i > 0; i--) { const j = Math.floor(R() * (i + 1)); const t = order[i]; order[i] = order[j]; order[j] = t; }
|
|
67
|
-
const grid = new Map();
|
|
68
|
-
const key = (x, y, z) => ((x + 512) * 1024 + (y + 512)) * 1024 + (z + 512);
|
|
69
|
-
const spots = [];
|
|
70
|
-
for (let k = 0; k < nV; k++) {
|
|
71
|
-
const v = order[k];
|
|
72
|
-
if (skip && skip(v)) continue;
|
|
73
|
-
let r = radius(v);
|
|
74
|
-
if (!(r > 0)) continue;
|
|
75
|
-
r *= 0.78 + 0.44 * R();
|
|
76
|
-
const p = [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
|
|
77
|
-
const gx = Math.floor(p[0] / cell), gy = Math.floor(p[1] / cell), gz = Math.floor(p[2] / cell);
|
|
78
|
-
let ok = true;
|
|
79
|
-
for (let dz = -1; dz <= 1 && ok; dz++)
|
|
80
|
-
for (let dy = -1; dy <= 1 && ok; dy++)
|
|
81
|
-
for (let dx = -1; dx <= 1 && ok; dx++) {
|
|
82
|
-
const arr = grid.get(key(gx + dx, gy + dy, gz + dz));
|
|
83
|
-
if (!arr) continue;
|
|
84
|
-
for (const s of arr) {
|
|
85
|
-
const minD = spacing * (r + s.r) + gap;
|
|
86
|
-
const ddx = p[0] - s.p[0], ddy = p[1] - s.p[1], ddz = p[2] - s.p[2];
|
|
87
|
-
if (ddx * ddx + ddy * ddy + ddz * ddz < minD * minD) { ok = false; break; }
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (!ok) continue;
|
|
91
|
-
const s = { v, p, r, n: [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]], ...(extra ? extra(v, R) : {}) };
|
|
92
|
-
spots.push(s);
|
|
93
|
-
const kk = key(gx, gy, gz);
|
|
94
|
-
(grid.get(kk) || grid.set(kk, []).get(kk)).push(s);
|
|
95
|
-
}
|
|
96
|
-
return { spots, grid, cell, key };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Signed distance to the nearest feature of a poissonFeatures() set, elongated along each
|
|
100
|
-
// feature's comb direction `c` by `el`. Returns [sdf, feature].
|
|
101
|
-
export function featureDistance(F, p, n, wob = 1) {
|
|
102
|
-
const { grid, cell, key } = F;
|
|
103
|
-
let best = 1e9, bs = null;
|
|
104
|
-
const gx = Math.floor(p[0] / cell), gy = Math.floor(p[1] / cell), gz = Math.floor(p[2] / cell);
|
|
105
|
-
for (let dz = -1; dz <= 1; dz++)
|
|
106
|
-
for (let dy = -1; dy <= 1; dy++)
|
|
107
|
-
for (let dx = -1; dx <= 1; dx++) {
|
|
108
|
-
const arr = grid.get(key(gx + dx, gy + dy, gz + dz));
|
|
109
|
-
if (!arr) continue;
|
|
110
|
-
for (const s of arr) {
|
|
111
|
-
if (dot(s.n, n) < 0.2) continue;
|
|
112
|
-
const d = sub(p, s.p);
|
|
113
|
-
const el = s.el || 1;
|
|
114
|
-
const along = s.c ? dot(d, s.c) : 0;
|
|
115
|
-
const dd = Math.sqrt(Math.max(0, dot(d, d) - along * along * (1 - 1 / (el * el))));
|
|
116
|
-
const sd = dd - s.r * wob;
|
|
117
|
-
if (sd < best) { best = sd; bs = s; }
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return [best, bs];
|
|
121
|
-
}
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
// Flight-feather cards: real feather geometry (primaries, secondaries, rectrices and their coverts)
|
|
2
|
-
// appended to the base surface. Each card is a thin two-sided vane (top and bottom layer, rachis
|
|
3
|
-
// down the middle, asymmetric outer / inner vane, rounded or emarginated tip, curved rachis) rigidly
|
|
4
|
-
// skinned to its own feather bone, so the engine can fan, fold, twist and splay every feather while
|
|
5
|
-
// the base draw call renders it (one draw, shadows and the crowd tier included). Material id 10
|
|
6
|
-
// (vane) is shaded in core/render/coatMaterial.js from surf = [gloss, iridescence, across, along]:
|
|
7
|
-
// across -1 (outer vane edge) .. 0 (rachis) .. 1 (inner vane edge), along 0 (root) .. 1 (tip).
|
|
8
|
-
// colour(kind, i, side, t, c, layer) -> { rgb, gloss, irid, bar? }: bar { rgb, period (m) } draws bars
|
|
9
|
-
// across the vane in the shader (patternColor = [rgb, -bar count]; opt-in, e.g. barred poultry).
|
|
10
|
-
//
|
|
11
|
-
// Species usage (surfaces hook, see core/build/pipeline.js):
|
|
12
|
-
// surfaces: (ctx) => featherCards({ ...ctx, wing: motion.wing, feathers: motion.feathers,
|
|
13
|
-
// tail: motion.tail, colour: (kind, i, side, t, c) => ({ rgb, gloss, irid }) })
|
|
14
|
-
import { featherJoints, DEG } from '../rig/bird.js';
|
|
15
|
-
import { MAT } from './coatKit.js';
|
|
16
|
-
|
|
17
|
-
const V = (x, y, z) => [x, y, z];
|
|
18
|
-
const addS = (a, b, k) => [a[0] + b[0] * k, a[1] + b[1] * k, a[2] + b[2] * k];
|
|
19
|
-
const cross = (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
|
20
|
-
const norm = (a) => { const l = Math.hypot(a[0], a[1], a[2]) || 1; return [a[0] / l, a[1] / l, a[2] / l]; };
|
|
21
|
-
const mirror = (p) => [-p[0], p[1], p[2]];
|
|
22
|
-
|
|
23
|
-
// vane half-widths at t (0 root .. 1 tip): [outer, inner] (also used by the bird engine's ground checks)
|
|
24
|
-
// (f.base0: the vane's width at the root as a fraction of its full width, default 0.3)
|
|
25
|
-
export function vaneWidth(f, t, w) {
|
|
26
|
-
const b0 = f.base0 ?? 0.3;
|
|
27
|
-
const base = b0 + (1 - b0) * smooth(0.0, 0.16, t);
|
|
28
|
-
const tr = f.tipRound;
|
|
29
|
-
let tip = 1;
|
|
30
|
-
if (t > 1 - tr) { const x = (t - (1 - tr)) / tr; tip = Math.sqrt(Math.max(0, 1 - x * x)); }
|
|
31
|
-
let wo = w * f.outer * base * tip, wi = w * (1 - f.outer) * base * tip;
|
|
32
|
-
if (f.emarg > 0) {
|
|
33
|
-
// emarginated primaries: both vanes step in beyond the notch, leaving a narrow "finger"
|
|
34
|
-
const k = smooth(0.5, 0.64, t) * f.emarg;
|
|
35
|
-
wi *= 1 - 0.62 * k; wo *= 1 - 0.3 * k;
|
|
36
|
-
}
|
|
37
|
-
return [wo, Math.max(wi, 0.0006)];
|
|
38
|
-
}
|
|
39
|
-
function smooth(a, b, x) { const t = Math.min(1, Math.max(0, (x - a) / (b - a))); return t * t * (3 - 2 * t); }
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @param ctx { rig, params, Q, wing, feathers, tail, colour, thickness? }
|
|
43
|
-
* @returns surfaces block { nV, pos, nrm, index, skinIndex, skinWeight, comb, tint, pattern, mark, furLen, patternColor, surf }
|
|
44
|
-
*/
|
|
45
|
-
export function featherCards(ctx) {
|
|
46
|
-
const { rig, Q, wing, feathers, tail } = ctx;
|
|
47
|
-
const colour = ctx.colour || (() => ({ rgb: [0.02, 0.02, 0.02], gloss: 0.5, irid: 0 }));
|
|
48
|
-
// the bind layout (the rig built the joints from the same call)
|
|
49
|
-
const Jl = {};
|
|
50
|
-
for (const k of ['shoulderL', 'tailBase', 'tailTip']) Jl[k] = rig.J[k];
|
|
51
|
-
const lay = featherJoints(Jl, wing, feathers, tail);
|
|
52
|
-
const res = Q?.res ?? 1;
|
|
53
|
-
const nA = res <= 1.35 ? 9 : res <= 2.1 ? 6 : res <= 3.1 ? 4 : 3;
|
|
54
|
-
const th = ctx.thickness ?? 0.0005;
|
|
55
|
-
const coverts = res < 4 || ctx.crowdCoverts;
|
|
56
|
-
const P = [], N = [], C = [], T = [], S = [], SI = [], I = [], X = [];
|
|
57
|
-
const PCL = []; // vane bars: [vertex, rgb, -count]
|
|
58
|
-
const cov = lay.F.coverts;
|
|
59
|
-
|
|
60
|
-
const card = (bone, side, fr, f, opts) => {
|
|
61
|
-
const b = rig.BONE[bone];
|
|
62
|
-
if (!b) throw new Error('featherCards: missing bone ' + bone);
|
|
63
|
-
const s = side;
|
|
64
|
-
const m = (p) => (s > 0 ? p : mirror(p));
|
|
65
|
-
const L = f.len * (opts.lenK || 1), W = f.width * (opts.widthK || 1);
|
|
66
|
-
const A = m(V(fr.axis.x, fr.axis.y, fr.axis.z)), Nn = m(V(fr.normal.x, fr.normal.y, fr.normal.z));
|
|
67
|
-
// inner vane side (+across): s * (N x A)
|
|
68
|
-
const Tv = norm(cross(Nn, A)).map((x) => x * s);
|
|
69
|
-
const root0 = m(V(fr.root.x, fr.root.y, fr.root.z));
|
|
70
|
-
const root = addS(addS(root0, Nn, opts.lift || 0), A, opts.shift || 0);
|
|
71
|
-
const fo = opts.shape ? { ...f, ...opts.shape } : f;
|
|
72
|
-
// vane arch: the edges sit this fraction of the half-width below the rachis (feather spec `arch`,
|
|
73
|
-
// default 0.12; a flatter vane lets overlapping neighbours stack on smaller lift steps)
|
|
74
|
-
const arch = fo.arch ?? 0.12;
|
|
75
|
-
const v0 = P.length / 3;
|
|
76
|
-
const ts = [];
|
|
77
|
-
for (let j = 0; j <= nA; j++) { const u = j / nA; ts.push(0.5 - 0.5 * Math.cos(Math.PI * u)); }
|
|
78
|
-
const pt = (t, c) => {
|
|
79
|
-
const [wo, wi] = vaneWidth(fo, t, W);
|
|
80
|
-
const w = c < 0 ? wo : wi;
|
|
81
|
-
if (fo.arc) {
|
|
82
|
-
// rachis bent along a circular arc of total angle `arc` (rad) toward the inner vane, length
|
|
83
|
-
// kept (long display feathers: a rooster's sickles); the vane width turns with the rachis
|
|
84
|
-
const a = fo.arc * t, R = L / fo.arc, ca = Math.cos(a), sa = Math.sin(a);
|
|
85
|
-
let p = addS(root, A, R * sa);
|
|
86
|
-
p = addS(p, Tv, R * (1 - ca));
|
|
87
|
-
p = addS(p, A, -sa * c * w);
|
|
88
|
-
p = addS(p, Tv, ca * c * w);
|
|
89
|
-
return addS(p, Nn, -fo.droop * L * t * t - Math.abs(c) * w * arch);
|
|
90
|
-
}
|
|
91
|
-
// curved rachis (toward the inner vane, drooping), vanes slightly convex (edges lower)
|
|
92
|
-
const along = t * L, curve = fo.curve * L * t * t, droop = fo.droop * L * t * t;
|
|
93
|
-
let p = addS(root, A, along);
|
|
94
|
-
p = addS(p, Tv, curve + c * w);
|
|
95
|
-
p = addS(p, Nn, -droop - Math.abs(c) * w * arch);
|
|
96
|
-
return p;
|
|
97
|
-
};
|
|
98
|
-
const cols = [-1, 0, 1];
|
|
99
|
-
for (const layer of [1, -1]) {
|
|
100
|
-
for (let j = 0; j <= nA; j++) {
|
|
101
|
-
const t = ts[j];
|
|
102
|
-
for (const c of cols) {
|
|
103
|
-
const p = addS(pt(t, c), Nn, layer * th * 0.5 * (1 - 0.6 * t));
|
|
104
|
-
// surface tangents for the normal
|
|
105
|
-
const e = 1e-3;
|
|
106
|
-
const pa = pt(Math.min(1, t + e), c), pb = pt(Math.max(0, t - e), c);
|
|
107
|
-
const pc = pt(t, Math.min(1, c + 0.05)), pd = pt(t, Math.max(-1, c - 0.05));
|
|
108
|
-
const dt = [pa[0] - pb[0], pa[1] - pb[1], pa[2] - pb[2]], dc = [pc[0] - pd[0], pc[1] - pd[1], pc[2] - pd[2]];
|
|
109
|
-
let n = norm(cross(dt, dc));
|
|
110
|
-
const sgn = (n[0] * Nn[0] + n[1] * Nn[1] + n[2] * Nn[2]) < 0 ? -1 : 1;
|
|
111
|
-
n = n.map((x) => x * sgn * layer);
|
|
112
|
-
if (!Number.isFinite(n[0])) n = Nn.map((x) => x * layer);
|
|
113
|
-
P.push(...p); N.push(...n);
|
|
114
|
-
C.push(...norm(dt));
|
|
115
|
-
const col = colour(opts.kind, f.i, s, t, c, layer);
|
|
116
|
-
T.push(col.rgb[0], col.rgb[1], col.rgb[2], MAT.VANE);
|
|
117
|
-
// opt-in bars across the vane (colour() returns bar: { rgb, period (m) }): shader-drawn
|
|
118
|
-
if (col.bar) PCL.push([P.length / 3 - 1, col.bar.rgb, -L / col.bar.period]);
|
|
119
|
-
S.push(col.gloss ?? 0.5, col.irid ?? 0, c, t);
|
|
120
|
-
const wv = vaneWidth(fo, t, W);
|
|
121
|
-
X.push(c * (c < 0 ? wv[0] : wv[1]) * s);
|
|
122
|
-
SI.push(b.index, 0, 0, 0);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
const base = v0 + (layer > 0 ? 0 : (nA + 1) * 3);
|
|
126
|
-
for (let j = 0; j < nA; j++) {
|
|
127
|
-
for (let k = 0; k < 2; k++) {
|
|
128
|
-
const a = base + j * 3 + k, bq = a + 1, c2 = a + 3, d = a + 4;
|
|
129
|
-
// top layer faces +N; the bottom layer is wound the other way
|
|
130
|
-
if ((layer > 0) === (s > 0)) I.push(a, c2, bq, bq, c2, d);
|
|
131
|
-
else I.push(a, bq, c2, bq, d, c2);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
// winding sanity: flip this card's triangles if their geometric normal disagrees with the layer normal
|
|
136
|
-
fixWinding(P, N, I, v0);
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
// per-feather covert overrides (spec field `cov`: { lift, shift, lenK, widthK, shape }, merged over
|
|
140
|
-
// the defaults below; opt-in: a species can restack its coverts, e.g. reverse their overlap order;
|
|
141
|
-
// `cov: { skip: true }` drops the covert card, e.g. where the species sculpts the coverts as skin)
|
|
142
|
-
const covOpts = (f, o) => (f.cov ? { ...o, ...f.cov, shape: { ...o.shape, ...(f.cov.shape || {}) } } : o);
|
|
143
|
-
const wEH = lay.frames;
|
|
144
|
-
for (const side of [1, -1]) {
|
|
145
|
-
const S2 = side > 0 ? 'L' : 'R';
|
|
146
|
-
for (const fr of wEH.pri) {
|
|
147
|
-
const f = fr.f;
|
|
148
|
-
card(`pri${f.i + 1}${S2}`, side, fr, f, { kind: 'primary' });
|
|
149
|
-
if (coverts && !f.cov?.skip) card(`pri${f.i + 1}${S2}`, side, fr, f, covOpts(f, { kind: 'primaryCovert', lenK: cov.primary * (1 - 0.25 * f.i / Math.max(1, f.n - 1)), widthK: 1.05, lift: 0.0022, shift: -0.004, shape: { emarg: 0, tipRound: 0.6, outer: 0.45, curve: 0.02 } }));
|
|
150
|
-
}
|
|
151
|
-
for (const fr of wEH.sec) {
|
|
152
|
-
const f = fr.f;
|
|
153
|
-
card(`sec${f.i + 1}${S2}`, side, fr, f, { kind: 'secondary' });
|
|
154
|
-
if (coverts && !f.cov?.skip) card(`sec${f.i + 1}${S2}`, side, fr, f, covOpts(f, { kind: 'secondaryCovert', lenK: cov.secondary, widthK: 1.15, lift: 0.0028, shift: -0.006, shape: { tipRound: 0.65, outer: 0.45, curve: 0.02 } }));
|
|
155
|
-
}
|
|
156
|
-
for (const fr of wEH.rec) {
|
|
157
|
-
const f = fr.f;
|
|
158
|
-
card(`rec${f.i + 1}${S2}`, side, fr, f, { kind: 'rectrix' });
|
|
159
|
-
if (coverts && f.i < 4 && !f.cov?.skip) card(`rec${f.i + 1}${S2}`, side, fr, f, covOpts(f, { kind: 'tailCovert', lenK: cov.tail * (1 - 0.1 * f.i), widthK: 1.2, lift: 0.0025, shift: -0.012, shape: { tipRound: 0.7, outer: 0.5 } }));
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
const nV = P.length / 3;
|
|
163
|
-
const out = {
|
|
164
|
-
nV,
|
|
165
|
-
pos: Float32Array.from(P), nrm: Float32Array.from(N), index: Uint32Array.from(I),
|
|
166
|
-
skinIndex: Uint16Array.from(SI), skinWeight: new Float32Array(nV * 4),
|
|
167
|
-
comb: Float32Array.from(C), tint: Float32Array.from(T), surf: Float32Array.from(S),
|
|
168
|
-
pattern: Float32Array.from(X), mark: new Float32Array(nV).fill(1), furLen: new Float32Array(nV),
|
|
169
|
-
patternColor: new Float32Array(nV * 4),
|
|
170
|
-
};
|
|
171
|
-
for (const [v, rgb, n] of PCL) out.patternColor.set([rgb[0], rgb[1], rgb[2], n], v * 4);
|
|
172
|
-
for (let v = 0; v < nV; v++) out.skinWeight[v * 4] = 1;
|
|
173
|
-
return out;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function fixWinding(P, N, I, v0) {
|
|
177
|
-
// triangles are appended per card; check the first triangle referencing v0's range
|
|
178
|
-
for (let t = 0; t < I.length; t += 3) {
|
|
179
|
-
const a = I[t], b = I[t + 1], c = I[t + 2];
|
|
180
|
-
if (a < v0) continue;
|
|
181
|
-
const e1 = [P[b * 3] - P[a * 3], P[b * 3 + 1] - P[a * 3 + 1], P[b * 3 + 2] - P[a * 3 + 2]];
|
|
182
|
-
const e2 = [P[c * 3] - P[a * 3], P[c * 3 + 1] - P[a * 3 + 1], P[c * 3 + 2] - P[a * 3 + 2]];
|
|
183
|
-
const g = cross(e1, e2);
|
|
184
|
-
const d = g[0] * N[a * 3] + g[1] * N[a * 3 + 1] + g[2] * N[a * 3 + 2];
|
|
185
|
-
if (d < 0) { I[t + 1] = c; I[t + 2] = b; }
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export { DEG };
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
// Knife-thin fin edges below the mesh cell size.
|
|
2
|
-
//
|
|
3
|
-
// A fin slab (SDFModel.fin) meshes with a rim no thinner than ~2 cells (thinner slabs alias into
|
|
4
|
-
// holes), so at 1 cm cells a shark's fins end in 2 cm rounded, rubbery edges. A fin primitive tagged
|
|
5
|
-
// `sharpen: { edge, band, rim }` has its meshed surface squeezed toward its mid-plane near its free
|
|
6
|
-
// outline: every vertex whose nearest fin slab is that primitive has its distance from the plane
|
|
7
|
-
// scaled by `edge` (e.g. 0.15) at the outline, easing back to 1 at `band` mesh cells (of the vertex's
|
|
8
|
-
// region, so every quality tier) inside it; the rounded rim beyond the outline is drawn in toward it
|
|
9
|
-
// by `rim` (0.4: to 0.4 of its width), so the squeezed edge runs straight instead of following the
|
|
10
|
-
// cells' sampling of the round cap. Where the fin grows out of the body (the field of the non-fin
|
|
11
|
-
// primitives within ~1.5-4 cells of the vertex) nothing moves, so the root fillet keeps its shape.
|
|
12
|
-
// Same vertex count; the field's analytic normals are carried through the squeeze (inverse transpose
|
|
13
|
-
// of its Jacobian), so the shading stays as smooth as the field's. Opt-in: no tagged primitive, no
|
|
14
|
-
// change.
|
|
15
|
-
import { SDFModel, T_FIN, polySD } from '../sdf/sdf.js';
|
|
16
|
-
|
|
17
|
-
const smooth = (a, b, x) => { const t = Math.min(1, Math.max(0, (x - a) / (b - a))); return t * t * (3 - 2 * t); };
|
|
18
|
-
const dsmooth = (a, b, x) => { if (x <= a || x >= b) return 0; const t = (x - a) / (b - a); return (6 * t * (1 - t)) / (b - a); };
|
|
19
|
-
|
|
20
|
-
/** @returns the number of vertices moved */
|
|
21
|
-
export function sharpenFinEdges({ pos, nrm, lists, nV, regionOf }, hOfRegion) {
|
|
22
|
-
let n = 0;
|
|
23
|
-
for (let v = 0; v < nV; v++) {
|
|
24
|
-
const L = lists[v];
|
|
25
|
-
if (!L) continue;
|
|
26
|
-
const h = hOfRegion[regionOf[v]];
|
|
27
|
-
let fin = null, best = Infinity;
|
|
28
|
-
const x = pos[v * 3], y = pos[v * 3 + 1], z = pos[v * 3 + 2];
|
|
29
|
-
for (const pr of L) {
|
|
30
|
-
if (pr.type !== T_FIN || !pr.sharpen || pr.carve) continue;
|
|
31
|
-
const d = Math.abs(SDFModel.dist(pr, x, y, z));
|
|
32
|
-
if (d < best) { best = d; fin = pr; }
|
|
33
|
-
}
|
|
34
|
-
if (!fin) continue;
|
|
35
|
-
const P = fin.P, S = fin.sharpen, e = S.edge, B = S.band * h, c = S.rim ?? 1;
|
|
36
|
-
const dx = x - P[0], dy = y - P[1], dz = z - P[2];
|
|
37
|
-
const pu = dx * P[3] + dy * P[4] + dz * P[5], pv = dx * P[6] + dy * P[7] + dz * P[8];
|
|
38
|
-
const pn = dx * P[9] + dy * P[10] + dz * P[11];
|
|
39
|
-
// depth inside the outline (< 0 on the rounded rim beyond it)
|
|
40
|
-
const din = -polySD(P, pu, pv);
|
|
41
|
-
if (din >= B) continue;
|
|
42
|
-
// the body around the root: the field of every non-fin primitive near the vertex
|
|
43
|
-
const rest = L.filter((pr) => pr.type !== T_FIN && !pr.carve);
|
|
44
|
-
const dBody = rest.length ? SDFModel.evalList(rest, x, y, z) : 1e9;
|
|
45
|
-
const wr = smooth(1.5 * h, 4 * h, dBody);
|
|
46
|
-
if (wr < 1e-3) continue;
|
|
47
|
-
// squeeze factor s(din) = 1 - wr (1 - k(din)), k = e + (1 - e) smooth(0, B, din)
|
|
48
|
-
const s = 1 - wr * (1 - e) * (1 - smooth(0, B, din));
|
|
49
|
-
const ds = wr * (1 - e) * dsmooth(0, B, din);
|
|
50
|
-
// in-plane gradient of din (unit, pointing into the fin)
|
|
51
|
-
const eps = 0.25 * h;
|
|
52
|
-
let gu = -(polySD(P, pu + eps, pv) - polySD(P, pu - eps, pv)) / (2 * eps);
|
|
53
|
-
let gv = -(polySD(P, pu, pv + eps) - polySD(P, pu, pv - eps)) / (2 * eps);
|
|
54
|
-
const gl = Math.hypot(gu, gv) || 1;
|
|
55
|
-
gu /= gl; gv /= gl;
|
|
56
|
-
const gx = P[3] * gu + P[6] * gv, gy = P[4] * gu + P[7] * gv, gz = P[5] * gu + P[8] * gv;
|
|
57
|
-
// the rim beyond the outline drawn in toward it (to c of its width)
|
|
58
|
-
const pull = din < 0 ? (1 - c) * -din * wr : 0;
|
|
59
|
-
const off = pn * (s - 1);
|
|
60
|
-
pos[v * 3] += P[9] * off + gx * pull; pos[v * 3 + 1] += P[10] * off + gy * pull; pos[v * 3 + 2] += P[11] * off + gz * pull;
|
|
61
|
-
// normal: J = I + nf a^T (a = (s - 1) nf + pn ds g) -> J^-T n0 = n0 - a (nf . n0) / s; the rim pull
|
|
62
|
-
// scales along g by c' = 1 - (1 - c) wr -> divide the g component by c'
|
|
63
|
-
let nx = nrm[v * 3], ny = nrm[v * 3 + 1], nz = nrm[v * 3 + 2];
|
|
64
|
-
const fn = (nx * P[9] + ny * P[10] + nz * P[11]) / s;
|
|
65
|
-
const ax = (s - 1) * P[9] + pn * ds * gx, ay = (s - 1) * P[10] + pn * ds * gy, az = (s - 1) * P[11] + pn * ds * gz;
|
|
66
|
-
nx -= ax * fn; ny -= ay * fn; nz -= az * fn;
|
|
67
|
-
if (din < 0 && c < 1) {
|
|
68
|
-
const cc = 1 - (1 - c) * wr, gn = (nx * gx + ny * gy + nz * gz) * (1 / cc - 1);
|
|
69
|
-
nx += gx * gn; ny += gy * gn; nz += gz * gn;
|
|
70
|
-
}
|
|
71
|
-
// at the knife edge itself the faces above and below meet: a vertex there (on the field's rounded
|
|
72
|
-
// cap, close to the mid-plane) takes the edge's in-plane normal; otherwise vertices a hair above and
|
|
73
|
-
// below the plane along the rim would alternate up / down normals (stripes across the edge)
|
|
74
|
-
const q = Math.abs(pn) / Math.max(1e-9, P[12]);
|
|
75
|
-
const fq = smooth(0.15, 0.7, q);
|
|
76
|
-
if (fq < 1) {
|
|
77
|
-
const nfc = (nx * P[9] + ny * P[10] + nz * P[11]) * (1 - fq);
|
|
78
|
-
nx -= P[9] * nfc; ny -= P[10] * nfc; nz -= P[11] * nfc;
|
|
79
|
-
}
|
|
80
|
-
const l = Math.hypot(nx, ny, nz) || 1;
|
|
81
|
-
nrm[v * 3] = nx / l; nrm[v * 3 + 1] = ny / l; nrm[v * 3 + 2] = nz / l;
|
|
82
|
-
n++;
|
|
83
|
-
}
|
|
84
|
-
return n;
|
|
85
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
// Fur-length slope limit (shorten only), applied by the pipeline to every coat.
|
|
2
|
-
//
|
|
3
|
-
// The coat shells are offset copies of the skin at hair height x (0..1) x furLen, so where the hair
|
|
4
|
-
// length jumps from one vertex to the next the shells stand up as a wall: a round pad with a hard
|
|
5
|
-
// rim (a cheek switched from 5 to 11 mm across a plane), an eye sunk in a ring of long fur, a strand
|
|
6
|
-
// pattern smeared into a smooth plate over the steep triangles. Real hair changes length gradually
|
|
7
|
-
// and lies over the shorter hair around it, so the coat surface has no cliffs.
|
|
8
|
-
//
|
|
9
|
-
// The limit: hair may grow no faster than `slope` metres of hair per metre of skin above the hair
|
|
10
|
-
// around it, U(v) = min(L(v), min_u U(u) + s(v) |p_u - p_v|) over the furred, hairy vertices of the
|
|
11
|
-
// same surface (a Dijkstra sweep from the shortest hair outward: exact, one pass). Long hair may rise
|
|
12
|
-
// proportionally faster, s(v) = slope x max(1, L(v) / free): every boundary then bevels over at most
|
|
13
|
-
// ~free / slope of skin whatever the hair length, so manes, ruffs, forelocks and fleeces keep
|
|
14
|
-
// standing off (their edge is beveled, not flattened) while a face loses its walls.
|
|
15
|
-
//
|
|
16
|
-
// Species control (optional):
|
|
17
|
-
// species.render.furSlope slope for hair up to `furSlopeFree` long (default 1); false / 0: off
|
|
18
|
-
// species.render.furSlopeFree hair length (m) above which the allowed slope grows (default 0.025)
|
|
19
|
-
// coat().furSlope per-vertex slope (Float32Array) replacing the default at that vertex;
|
|
20
|
-
// Infinity (or <= 0) leaves that vertex's hair as painted (a crest meant
|
|
21
|
-
// to stand up as a wall)
|
|
22
|
-
// Bare vertices (no hair, or a material without shells) neither limit nor get limited: fur may meet a
|
|
23
|
-
// lid margin, the nose leather or a hoof at full length.
|
|
24
|
-
|
|
25
|
-
export const FUR_SLOPE_DEFAULTS = { slope: 1.0, free: 0.025 };
|
|
26
|
-
|
|
27
|
-
const HAIRY = (m) => m === 0 || m === 4 || m === 8 || m === 9; // materials that grow shells (coatMaterial.js hairy())
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Shortens furLen in place so that no hair rises faster than the slope above its neighbours.
|
|
31
|
-
* @param {Float32Array} furLen per-vertex hair length (m), modified in place
|
|
32
|
-
* @param {Float32Array} pos per-vertex positions (reference space)
|
|
33
|
-
* @param {Uint32Array} index triangle indices
|
|
34
|
-
* @param {number} nV vertex count (vertices >= nV are ignored)
|
|
35
|
-
* @param {Float32Array} tint per-vertex rgba, a = material id
|
|
36
|
-
* @param {object} o { slope, free, perVertex }
|
|
37
|
-
* @returns {{ shortened: number, maxCut: number }} vertices shortened by more than 0.1 mm, the largest cut (m)
|
|
38
|
-
*/
|
|
39
|
-
export function limitFurSlope(furLen, pos, index, nV, tint, { slope = FUR_SLOPE_DEFAULTS.slope, free = FUR_SLOPE_DEFAULTS.free, perVertex = null } = {}) {
|
|
40
|
-
const res = { shortened: 0, maxCut: 0 };
|
|
41
|
-
if (!furLen || !(slope > 0) || !(free > 0)) return res;
|
|
42
|
-
const L0 = Float32Array.from(furLen.subarray(0, nV));
|
|
43
|
-
const active = new Uint8Array(nV);
|
|
44
|
-
let any = false;
|
|
45
|
-
for (let v = 0; v < nV; v++) {
|
|
46
|
-
if (L0[v] > 0 && HAIRY(Math.round(tint[v * 4 + 3]))) { active[v] = 1; any = true; }
|
|
47
|
-
}
|
|
48
|
-
if (!any) return res;
|
|
49
|
-
// the allowed slope of each vertex's hair (0 = unlimited)
|
|
50
|
-
const sOf = new Float32Array(nV);
|
|
51
|
-
for (let v = 0; v < nV; v++) {
|
|
52
|
-
if (!active[v]) continue;
|
|
53
|
-
let s = perVertex ? perVertex[v] : slope;
|
|
54
|
-
if (perVertex && !(s > 0)) s = Infinity; // a species' explicit "leave as painted"
|
|
55
|
-
sOf[v] = Number.isFinite(s) ? s * Math.max(1, L0[v] / free) : 0;
|
|
56
|
-
}
|
|
57
|
-
// adjacency (CSR) over the active vertices
|
|
58
|
-
const deg = new Uint32Array(nV + 1);
|
|
59
|
-
for (let i = 0; i < index.length; i += 3) {
|
|
60
|
-
for (let e = 0; e < 3; e++) {
|
|
61
|
-
const u = index[i + e], v = index[i + ((e + 1) % 3)];
|
|
62
|
-
if (u < nV && v < nV && active[u] && active[v]) { deg[u + 1]++; deg[v + 1]++; }
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
for (let v = 0; v < nV; v++) deg[v + 1] += deg[v];
|
|
66
|
-
const adj = new Uint32Array(deg[nV]), fill = deg.slice(0, nV);
|
|
67
|
-
for (let i = 0; i < index.length; i += 3) {
|
|
68
|
-
for (let e = 0; e < 3; e++) {
|
|
69
|
-
const u = index[i + e], v = index[i + ((e + 1) % 3)];
|
|
70
|
-
if (u < nV && v < nV && active[u] && active[v]) { adj[fill[u]++] = v; adj[fill[v]++] = u; }
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// Dijkstra: settle vertices in increasing (limited) length; a settled vertex bounds its neighbours
|
|
74
|
-
const U = furLen;
|
|
75
|
-
let hk = new Float64Array(4096), hv = new Int32Array(4096), n = 0;
|
|
76
|
-
const push = (k, v) => {
|
|
77
|
-
if (n === hk.length) { const k2 = new Float64Array(n * 2); k2.set(hk); hk = k2; const v2 = new Int32Array(n * 2); v2.set(hv); hv = v2; }
|
|
78
|
-
let i = n++;
|
|
79
|
-
while (i > 0) { const p = (i - 1) >> 1; if (hk[p] <= k) break; hk[i] = hk[p]; hv[i] = hv[p]; i = p; }
|
|
80
|
-
hk[i] = k; hv[i] = v;
|
|
81
|
-
};
|
|
82
|
-
const pop = () => {
|
|
83
|
-
const top = hv[0], k = hk[--n], v = hv[n];
|
|
84
|
-
let i = 0;
|
|
85
|
-
for (;;) {
|
|
86
|
-
const l = 2 * i + 1;
|
|
87
|
-
if (l >= n) break;
|
|
88
|
-
const r = l + 1, m = r < n && hk[r] < hk[l] ? r : l;
|
|
89
|
-
if (hk[m] >= k) break;
|
|
90
|
-
hk[i] = hk[m]; hv[i] = hv[m]; i = m;
|
|
91
|
-
}
|
|
92
|
-
hk[i] = k; hv[i] = v;
|
|
93
|
-
return top;
|
|
94
|
-
};
|
|
95
|
-
const done = new Uint8Array(nV);
|
|
96
|
-
for (let v = 0; v < nV; v++) if (active[v]) push(U[v], v);
|
|
97
|
-
while (n) {
|
|
98
|
-
const v = pop();
|
|
99
|
-
if (done[v]) continue;
|
|
100
|
-
done[v] = 1;
|
|
101
|
-
const Uv = U[v], px = pos[v * 3], py = pos[v * 3 + 1], pz = pos[v * 3 + 2];
|
|
102
|
-
for (let k = deg[v]; k < deg[v + 1]; k++) {
|
|
103
|
-
const u = adj[k];
|
|
104
|
-
if (done[u] || sOf[u] === 0) continue;
|
|
105
|
-
const cand = Uv + sOf[u] * Math.hypot(pos[u * 3] - px, pos[u * 3 + 1] - py, pos[u * 3 + 2] - pz);
|
|
106
|
-
if (cand < U[u]) { U[u] = cand; push(cand, u); }
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
for (let v = 0; v < nV; v++) {
|
|
110
|
-
const cut = L0[v] - U[v];
|
|
111
|
-
if (cut > 0.0001) res.shortened++;
|
|
112
|
-
if (cut > res.maxCut) res.maxCut = cut;
|
|
113
|
-
}
|
|
114
|
-
return res;
|
|
115
|
-
}
|