@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.
Files changed (263) hide show
  1. package/ASSET-PROVENANCE.md +11 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +95 -0
  5. package/DESKTOP-PLATFORMS.md +6 -1
  6. package/INSTALLING.md +4 -0
  7. package/PLUGIN.md +13 -10
  8. package/PREREQUISITES.md +95 -0
  9. package/PUPPY-MEMORY.md +102 -1
  10. package/README.ko.md +116 -0
  11. package/README.md +121 -59
  12. package/RELEASING.md +33 -13
  13. package/THIRD-PARTY-NOTICES.md +5 -3
  14. package/braille.mjs +35 -9
  15. package/examples/live.mjs +2 -2
  16. package/integrations/linux-overlay/overlay.py +8 -2
  17. package/integrations/macos-overlay/overlay.jxa +9 -7
  18. package/integrations/opencode/runtime.mjs +4 -4
  19. package/integrations/opencode/tui.tsx +1 -1
  20. package/integrations/windows-overlay/Overlay.cs +7 -6
  21. package/integrations/windows-overlay/desktop.mjs +2 -1
  22. package/integrations/windows-overlay/protocol.mjs +13 -7
  23. package/integrations/windows-overlay/worker.mjs +1 -1
  24. package/package.json +34 -13
  25. package/puppy-comfort-sprites.mjs +33 -0
  26. package/puppy-companion-comfort-q5.bin.gz +0 -0
  27. package/puppy-companion-comfort.json +140 -0
  28. package/puppy-companion-life.mjs +58 -18
  29. package/puppy-companion-objects.mjs +12 -0
  30. package/puppy-companion-sprites.mjs +4 -2
  31. package/puppy-frame-predictive.mjs +15 -9
  32. package/puppy-frame-store.mjs +2 -2
  33. package/puppy-paper-actions.mjs +4 -2
  34. package/puppy-paper-behaviors.mjs +1 -1
  35. package/puppy-paper-contact.mjs +4 -2
  36. package/puppy-paper-seat.mjs +4 -2
  37. package/puppy-paper-world.mjs +14 -0
  38. package/puppy-paper.mjs +79 -21
  39. package/retrieviewer-cli.mjs +8 -6
  40. package/retrieviewer-plugin.d.mts +1 -1
  41. package/retrieviewer-plugin.mjs +1 -1
  42. package/retrieviewer.d.mts +12 -4
  43. package/retrieviewer.mjs +20 -14
  44. package/locomotion-core.mjs +0 -21
  45. package/locomotion-puppy.animal +0 -0
  46. package/vendor/procedural-animals/package.json +0 -40
  47. package/vendor/procedural-animals/src/animal.js +0 -146
  48. package/vendor/procedural-animals/src/bake.js +0 -51
  49. package/vendor/procedural-animals/src/build.js +0 -81
  50. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  51. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  52. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  53. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  54. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  55. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  56. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  57. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  58. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  59. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  60. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  61. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  62. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  63. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  64. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  65. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  66. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  67. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  68. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  69. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  70. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  71. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  72. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  73. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  74. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  75. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  76. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  77. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  78. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  79. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  80. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  81. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  82. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  83. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  84. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  85. package/vendor/procedural-animals/src/index.d.ts +0 -98
  86. package/vendor/procedural-animals/src/index.js +0 -29
  87. package/vendor/procedural-animals/src/registry.js +0 -48
  88. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  89. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  90. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  91. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  92. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  93. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  94. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  95. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  96. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  97. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  98. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  99. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  100. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  101. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  102. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  103. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  104. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  105. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  106. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  107. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  108. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  109. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  110. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  111. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  112. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  113. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  114. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  115. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  116. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  117. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  118. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  119. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  120. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  121. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  122. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  123. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  124. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  125. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  126. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  127. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  128. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  129. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  130. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  131. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  132. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  133. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  134. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  135. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  136. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  137. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  138. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  139. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  140. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  141. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  142. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  143. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  144. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  145. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  146. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  147. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  148. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  149. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  150. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  151. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  152. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  153. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  154. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  155. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  156. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  157. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  158. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  159. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  160. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  161. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  162. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  163. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  164. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  165. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  166. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  167. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  168. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  169. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  170. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  171. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  172. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  173. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  174. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  175. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  176. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  177. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  178. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  179. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  180. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  181. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  182. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  183. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  184. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  185. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  186. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  187. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  188. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  189. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  190. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  191. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  192. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  193. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  194. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  195. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  196. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  197. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  198. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  199. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  200. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  201. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  202. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  203. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  204. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  205. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  206. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  207. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  208. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  209. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  210. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  211. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  212. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  213. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  214. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  215. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  216. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  217. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  218. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  219. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  220. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  221. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  222. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  223. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  224. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  225. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  226. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  227. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  228. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  229. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  230. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  231. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  232. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  233. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  234. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  235. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  236. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  237. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  238. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  239. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  240. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  241. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  242. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  243. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  244. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  245. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  246. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  247. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  248. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  249. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  250. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  251. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  252. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  253. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  254. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  255. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  256. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  257. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  258. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  259. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  260. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  261. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  262. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  263. 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
- }