@bitefoundry/retrieviewer 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/ASSET-PROVENANCE.md +6 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +65 -0
  5. package/DESKTOP-PLATFORMS.md +75 -0
  6. package/INSTALLING.md +8 -5
  7. package/PLUGIN.md +13 -10
  8. package/PREREQUISITES.md +95 -0
  9. package/PUPPY-MEMORY.md +102 -1
  10. package/README.ko.md +110 -0
  11. package/README.md +119 -58
  12. package/RELEASING.md +27 -14
  13. package/THIRD-PARTY-NOTICES.md +5 -3
  14. package/braille.mjs +35 -9
  15. package/examples/live.mjs +2 -2
  16. package/integrations/desktop/platform.mjs +27 -0
  17. package/integrations/linux-overlay/README.md +83 -0
  18. package/integrations/linux-overlay/overlay.py +1094 -0
  19. package/integrations/macos-overlay/README.md +83 -0
  20. package/integrations/macos-overlay/overlay.jxa +639 -0
  21. package/integrations/opencode/runtime.mjs +4 -4
  22. package/integrations/opencode/tui.tsx +1 -1
  23. package/integrations/windows-overlay/Overlay.cs +4 -4
  24. package/integrations/windows-overlay/desktop.mjs +40 -8
  25. package/integrations/windows-overlay/protocol.mjs +11 -7
  26. package/integrations/windows-overlay/worker.mjs +13 -7
  27. package/package.json +41 -14
  28. package/puppy-comfort-sprites.mjs +25 -0
  29. package/puppy-companion-comfort-q5.bin.gz +0 -0
  30. package/puppy-companion-comfort.json +77 -0
  31. package/puppy-companion-life.mjs +49 -16
  32. package/puppy-companion-objects.mjs +12 -0
  33. package/puppy-frame-predictive.mjs +15 -9
  34. package/puppy-frame-store.mjs +2 -2
  35. package/puppy-paper-world.mjs +14 -0
  36. package/puppy-paper.mjs +65 -14
  37. package/retrieviewer-cli.mjs +10 -3
  38. package/retrieviewer-plugin.d.mts +1 -1
  39. package/retrieviewer-plugin.mjs +1 -1
  40. package/retrieviewer.d.mts +10 -4
  41. package/retrieviewer.mjs +20 -14
  42. package/locomotion-core.mjs +0 -21
  43. package/locomotion-puppy.animal +0 -0
  44. package/vendor/procedural-animals/package.json +0 -40
  45. package/vendor/procedural-animals/src/animal.js +0 -146
  46. package/vendor/procedural-animals/src/bake.js +0 -51
  47. package/vendor/procedural-animals/src/build.js +0 -81
  48. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  49. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  50. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  51. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  52. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  53. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  54. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  55. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  56. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  57. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  58. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  59. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  60. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  61. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  62. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  63. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  64. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  65. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  66. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  67. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  68. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  69. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  70. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  71. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  72. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  73. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  74. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  75. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  76. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  77. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  78. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  79. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  80. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  81. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  82. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  83. package/vendor/procedural-animals/src/index.d.ts +0 -98
  84. package/vendor/procedural-animals/src/index.js +0 -29
  85. package/vendor/procedural-animals/src/registry.js +0 -48
  86. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  87. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  88. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  89. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  90. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  91. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  92. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  93. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  94. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  95. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  96. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  97. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  98. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  99. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  100. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  101. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  102. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  103. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  104. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  105. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  106. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  107. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  108. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  109. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  110. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  111. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  112. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  113. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  114. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  115. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  116. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  117. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  118. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  119. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  120. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  121. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  122. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  123. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  124. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  125. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  126. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  127. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  128. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  129. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  130. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  131. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  132. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  133. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  134. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  135. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  136. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  137. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  138. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  139. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  140. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  141. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  142. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  143. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  144. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  145. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  146. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  147. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  148. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  149. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  150. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  151. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  152. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  153. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  154. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  155. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  156. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  157. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  158. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  159. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  160. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  161. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  162. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  163. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  164. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  165. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  166. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  167. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  168. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  169. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  170. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  171. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  172. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  173. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  174. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  175. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  176. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  177. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  178. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  179. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  180. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  181. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  182. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  183. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  184. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  185. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  186. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  187. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  188. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  189. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  190. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  191. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  192. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  193. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  194. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  195. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  196. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  197. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  198. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  199. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  200. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  201. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  202. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  203. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  204. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  205. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  206. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  207. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  208. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  209. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  210. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  211. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  212. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  213. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  214. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  215. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  216. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  217. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  218. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  219. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  220. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  221. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  222. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  223. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  224. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  225. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  226. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  227. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  228. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  229. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  230. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  231. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  232. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  233. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  234. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  235. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  236. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  237. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  238. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  239. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  240. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  241. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  242. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  243. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  244. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  245. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  246. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  247. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  248. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  249. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  250. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  251. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  252. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  253. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  254. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  255. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  256. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  257. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  258. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  259. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  260. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  261. package/vendor/procedural-animals/src/worker.js +0 -29
@@ -1,146 +0,0 @@
1
- // The public Animal: build data + render object + motion engine behind a small game-facing API.
2
- import * as THREE from 'three';
3
- import { createAnimalObject } from './core/render/animalObject.js';
4
- import { createMotion } from './core/motion/index.js';
5
-
6
- const _v = new THREE.Vector3();
7
-
8
- export class Animal {
9
- constructor(species, data, opts = {}) {
10
- this.species = species;
11
- this.id = species.id;
12
- this.data = data;
13
- this.seed = data.seed;
14
- this.params = data.params;
15
- this.quality = opts.quality || data.quality;
16
- const ground = opts.ground || (() => 0);
17
- this.ground = ground;
18
- this.water = opts.water || null;
19
- this.render = createAnimalObject(data, species, { quality: this.quality, castShadow: opts.castShadow, receiveShadow: opts.receiveShadow });
20
- /** Add this to your scene. Keep its own transform at identity: the animal moves by itself. */
21
- this.object = this.render.group;
22
- this.object.userData.animal = this;
23
- this._listeners = new Map();
24
- this.motion = createMotion(species.plan, {
25
- species, data, skeleton: this.render.skeleton, ground, water: this.water, perches: opts.perches,
26
- position: opts.position, heading: opts.heading ?? 0,
27
- emit: (type, payload) => this._emit(type, payload),
28
- });
29
- this.attachments = {};
30
- this._attachDefs = [];
31
- for (const [name, def] of Object.entries(this.motion.attachmentPoints || {})) {
32
- const o = new THREE.Object3D();
33
- o.name = name;
34
- o.matrixAutoUpdate = false;
35
- this.object.add(o);
36
- this.attachments[name] = o;
37
- this._attachDefs.push({ o, ...def });
38
- }
39
- this.stats = { vertices: data.nV, triangles: data.index.length / 3, bones: data.bones.length, buildMs: data.stats?.buildMs || 0, updateMs: 0 };
40
- this.time = 0;
41
- this._lastDt = 1 / 60;
42
- this.update(0);
43
- }
44
-
45
- // ---------------------------------------------------------------- control
46
- /** Locomotion: the animal moves itself. speed in m/s, heading in radians (0 = +Z). */
47
- move({ speed = 0, heading, climb = 0, gait } = {}) {
48
- const inp = this.motion.input;
49
- inp.follow = null;
50
- inp.target = null;
51
- inp.speed = Math.max(0, speed);
52
- if (heading !== undefined) inp.heading = heading;
53
- inp.climb = climb;
54
- inp.gait = gait || null;
55
- return this;
56
- }
57
- stop() { return this.move({ speed: 0 }); }
58
- /** Walk / run to a point and stop there. */
59
- moveTo(point, { speed = 3 } = {}) {
60
- const inp = this.motion.input;
61
- inp.follow = null;
62
- inp.target = point ? new THREE.Vector3().copy(point) : null;
63
- inp.targetSpeed = speed;
64
- return this;
65
- }
66
- /** Match your own physics body: the animal is placed at `position` and animates for `velocity`. */
67
- follow(position, velocity, { heading } = {}) {
68
- const inp = this.motion.input;
69
- inp.follow = inp.follow || { position: new THREE.Vector3(), velocity: new THREE.Vector3(), heading: null };
70
- inp.follow.position.copy(position);
71
- inp.follow.velocity.copy(velocity || _v.set(0, 0, 0));
72
- inp.follow.heading = heading ?? null;
73
- return this;
74
- }
75
- /** Look at a world point (Vector3) or stop looking (null). The animal keeps tracking the point you pass. */
76
- lookAt(target) {
77
- this.motion.input.look = target ? (target.isVector3 ? target : new THREE.Vector3().copy(target)) : null;
78
- return this;
79
- }
80
- /**
81
- * Play an action ('jump', 'sit', 'lie', 'sleep', 'eat', 'drink', 'attack', 'hit', 'death', 'stand', ...).
82
- * A new one-shot interrupts the running one; postures cross-fade.
83
- * Resolves with 'done' | 'interrupted' | 'stopped' | 'refused'.
84
- */
85
- play(name, opts = {}) { return this.motion.play(name, opts); }
86
- /** End an action (a posture: stand up), or every action but death when no name is given. */
87
- stopAction(name) { this.motion.stop(name); return this; }
88
- get actions() { return this.motion.actions; }
89
- get gaits() { return this.motion.gaits; }
90
- /** Named speeds for UIs ({ walk, trot, ... } in m/s, scaled to this individual's size). */
91
- get gears() { return this.motion.gears || {}; }
92
-
93
- // ---------------------------------------------------------------- events
94
- on(type, fn) {
95
- if (!this._listeners.has(type)) this._listeners.set(type, new Set());
96
- this._listeners.get(type).add(fn);
97
- return () => this.off(type, fn);
98
- }
99
- off(type, fn) { this._listeners.get(type)?.delete(fn); return this; }
100
- _emit(type, payload) {
101
- const set = this._listeners.get(type);
102
- if (set) for (const fn of set) fn({ type, animal: this, ...payload });
103
- }
104
-
105
- // ---------------------------------------------------------------- state
106
- get position() { return this.motion.state.position; }
107
- get heading() { return this.motion.state.heading; }
108
- get speed() { return this.motion.state.speed; }
109
- get velocity() { return this.motion.state.velocity; }
110
- get state() { return this.motion.state; }
111
-
112
- // ---------------------------------------------------------------- frame
113
- /** Call once per frame with the frame time in seconds. */
114
- update(dt) {
115
- const t0 = now();
116
- if (!(dt >= 0) || !Number.isFinite(dt)) dt = 0;
117
- dt = Math.min(dt, 0.1);
118
- this.time += dt;
119
- this.motion.update(dt);
120
- this.render.update({ dt, time: this.time, motion: this.motion });
121
- for (const a of this._attachDefs) {
122
- const b = this.render.skeleton.byName[a.bone];
123
- if (!b) continue;
124
- a.o.matrix.multiplyMatrices(b.matrixWorld, a.local);
125
- a.o.matrixWorld.multiplyMatrices(this.object.matrixWorld, a.o.matrix);
126
- a.o.matrixWorldNeedsUpdate = false;
127
- }
128
- this.stats.updateMs = this.stats.updateMs * 0.9 + (now() - t0) * 0.1;
129
- return this;
130
- }
131
-
132
- setQuality(q) { this.render.setQuality?.(q); this.quality = q; return this; }
133
- /** Motion level of detail: 0 full, 1 reduced secondary motion, 2 crowd (half-rate IK). */
134
- setLod(level) { this.motion.setLod?.(level); this.lod = level; return this; }
135
- setDebug(mode, on) { this.render.setDebug?.(mode, on); return this; }
136
- /** Show or hide the coverings (fur shells and silhouette fins); the base still reads as fur. */
137
- setCoverings(on) { this.render.setCoverings?.(on); return this; }
138
-
139
- dispose() {
140
- this.render.dispose();
141
- this.motion.dispose?.();
142
- this._listeners.clear();
143
- }
144
- }
145
-
146
- const now = () => (typeof performance !== 'undefined' ? performance.now() : Date.now());
@@ -1,51 +0,0 @@
1
- // .animal bake files: a built individual, stored so it loads instantly (no SDF meshing, no weights).
2
- // Layout: "PANM" | u32 version | u32 headerBytes | header JSON (utf-8, padded to 8) | array blobs (8-aligned)
3
- import { ARRAY_KEYS, FORMAT_VERSION } from './core/build/pipeline.js';
4
-
5
- const TYPES = { Float32Array, Uint32Array, Uint16Array, Int16Array, Uint8Array, Int8Array, Int32Array };
6
-
7
- export function writeBake(data) {
8
- const header = {};
9
- for (const [k, v] of Object.entries(data)) if (!ARRAY_KEYS.includes(k)) header[k] = v;
10
- header.arrays = [];
11
- let offset = 0;
12
- for (const k of ARRAY_KEYS) {
13
- const a = data[k];
14
- header.arrays.push({ key: k, type: a.constructor.name, length: a.length, offset });
15
- offset += Math.ceil(a.byteLength / 8) * 8;
16
- }
17
- let json = new TextEncoder().encode(JSON.stringify(header));
18
- const hb = Math.ceil((json.length) / 8) * 8;
19
- const total = 12 + hb + offset;
20
- const buf = new ArrayBuffer(Math.ceil(total / 8) * 8 + 8);
21
- const u8 = new Uint8Array(buf);
22
- u8.set([80, 65, 78, 77], 0); // "PANM"
23
- const dv = new DataView(buf);
24
- dv.setUint32(4, FORMAT_VERSION, true);
25
- dv.setUint32(8, hb, true);
26
- u8.set(json, 12);
27
- for (let i = 12 + json.length; i < 12 + hb; i++) u8[i] = 32;
28
- const base = 16 + hb - ((16 + hb) % 8 === 0 ? 0 : (16 + hb) % 8) ;
29
- // blobs start at an 8-aligned offset after the header
30
- const start = align8(12 + hb);
31
- for (const a of header.arrays) u8.set(new Uint8Array(data[a.key].buffer, data[a.key].byteOffset, data[a.key].byteLength), start + a.offset);
32
- void base;
33
- return buf.slice(0, start + offset);
34
- }
35
-
36
- export function readBake(buf) {
37
- const u8 = new Uint8Array(buf);
38
- if (u8[0] !== 80 || u8[1] !== 65 || u8[2] !== 78 || u8[3] !== 77) throw new Error('not a .animal file');
39
- const dv = new DataView(buf);
40
- const version = dv.getUint32(4, true);
41
- if (version !== FORMAT_VERSION) throw new Error(`.animal version ${version} is not supported (expected ${FORMAT_VERSION})`);
42
- const hb = dv.getUint32(8, true);
43
- const header = JSON.parse(new TextDecoder().decode(u8.subarray(12, 12 + hb)));
44
- const start = align8(12 + hb);
45
- const data = { ...header };
46
- for (const a of header.arrays) data[a.key] = new TYPES[a.type](buf, start + a.offset, a.length);
47
- delete data.arrays;
48
- return data;
49
- }
50
-
51
- const align8 = (n) => Math.ceil(n / 8) * 8;
@@ -1,81 +0,0 @@
1
- // Builds an individual's data: in Web Workers when possible (meshing jobs in parallel, then
2
- // weights + coat), otherwise on the calling thread. Results are cached per (species, seed, quality,
3
- // sex, age) so a herd that reuses individuals shares geometry.
4
- import { buildSync, normaliseOptions } from './core/build/pipeline.js';
5
- import { loadSpecies } from './registry.js';
6
-
7
- let defaultWorkerFactory = null;
8
- export function setWorkerFactory(fn) { defaultWorkerFactory = fn; }
9
-
10
- function makeWorker(opt) {
11
- if (opt === false) return null;
12
- try {
13
- if (typeof opt === 'function') return opt();
14
- if (defaultWorkerFactory) return defaultWorkerFactory();
15
- if (typeof Worker === 'undefined') return null;
16
- return new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
17
- } catch (e) {
18
- return null;
19
- }
20
- }
21
-
22
- const cache = new Map();
23
- export function cacheKey(speciesId, opts) {
24
- const o = normaliseOptions(opts);
25
- return [speciesId, o.seed, o.quality, o.sex || '', o.age || '', o.variant || '', JSON.stringify(o.overrides)].join('|');
26
- }
27
-
28
- export async function buildAnimalData(species, opts = {}, onProgress = () => {}) {
29
- const s = await loadSpecies(species);
30
- const key = cacheKey(s.id, opts);
31
- if (cache.has(key)) return cache.get(key);
32
- const p = buildInWorkers(s, opts, onProgress).catch((e) => { cache.delete(key); throw e; });
33
- cache.set(key, p);
34
- return p;
35
- }
36
- export function clearBuildCache() { cache.clear(); }
37
-
38
- async function buildInWorkers(species, opts, onProgress) {
39
- const o = normaliseOptions(opts);
40
- const nJobs = species.regions ? null : 1;
41
- void nJobs;
42
- const first = makeWorker(opts.worker);
43
- if (!first) {
44
- await new Promise((r) => setTimeout(r, 0));
45
- return buildSync(species, o, onProgress);
46
- }
47
- const t0 = performance.now();
48
- // how many meshing jobs? (cheap: prepare runs the sculpt only)
49
- const { prepare } = await import('./core/build/pipeline.js');
50
- const jobsN = prepare(species, o).reg.jobs.length;
51
- const workers = [first];
52
- for (let i = 1; i < jobsN; i++) { const w = makeWorker(opts.worker); if (w) workers.push(w); }
53
- const call = (w, msg) => new Promise((resolve, reject) => {
54
- w.onmessage = (e) => {
55
- const m = e.data;
56
- if (m.type === 'progress') onProgress(m.msg);
57
- else if (m.type === 'error') reject(new Error(m.message));
58
- else resolve(m);
59
- };
60
- w.onerror = (e) => reject(new Error(e.message || 'procedural-animals worker failed'));
61
- w.postMessage(msg);
62
- });
63
- try {
64
- const jobs = new Array(jobsN);
65
- let next = 0;
66
- await Promise.all(workers.map(async (w) => {
67
- while (next < jobsN) {
68
- const j = next++;
69
- onProgress('mesh ' + j);
70
- const r = await call(w, { task: 'mesh', species: species.id, opts: o, job: j });
71
- jobs[j] = r.parts;
72
- }
73
- }));
74
- onProgress('finish');
75
- const r = await call(first, { task: 'finish', species: species.id, opts: o, jobs });
76
- r.data.stats.buildMs = Math.round(performance.now() - t0);
77
- return r.data;
78
- } finally {
79
- for (const w of workers) w.terminate();
80
- }
81
- }
@@ -1,121 +0,0 @@
1
- // Helpers for species coat painters.
2
- import { SDFModel } from '../sdf/sdf.js';
3
- import { sub, add, mul, dot, len, clamp, mix } from '../math/vec.js';
4
-
5
- // sRGB hex -> linear rgb
6
- export const srgb = (hex) => {
7
- const c = [(hex >> 16) & 255, (hex >> 8) & 255, hex & 255].map((v) => v / 255);
8
- return c.map((v) => (v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4)));
9
- };
10
- export const mix3 = (a, b, t) => [mix(a[0], b[0], t), mix(a[1], b[1], t), mix(a[2], b[2], t)];
11
- export const scale3 = (a, k) => [a[0] * k, a[1] * k, a[2] * k];
12
-
13
- // Material ids stored in tint.w (see render/coatMaterial.js)
14
- export const MAT = { FUR: 0, NOSE: 1, DARK_SKIN: 2, MOUTH: 3, SKIN: 4, KERATIN: 5, SCALES: 6, WET_SKIN: 7, CHITIN: 8, FEATHER: 9, VANE: 10, FIN: 11 }; // 10: flight-feather vane (featherCards.js), 11: fin membrane (render/membranes.js)
15
-
16
- // Newton-project a point onto the zero set of a primitive list
17
- export function projectToSurface(prims, q, iters = 6) {
18
- let x = q.slice();
19
- for (let i = 0; i < iters; i++) {
20
- const d = SDFModel.evalList(prims, x[0], x[1], x[2]);
21
- const e = 0.0005;
22
- const g = [
23
- SDFModel.evalList(prims, x[0] + e, x[1], x[2]) - d,
24
- SDFModel.evalList(prims, x[0], x[1] + e, x[2]) - d,
25
- SDFModel.evalList(prims, x[0], x[1], x[2] + e) - d,
26
- ].map((c) => c / e);
27
- const gl = len(g) || 1;
28
- x = sub(x, mul(g, d / (gl * gl)));
29
- }
30
- return x;
31
- }
32
-
33
- // distance from p to a polyline with per-vertex half widths (negative inside)
34
- export function distPolyline(p, poly, widths) {
35
- let best = 1e9;
36
- for (let i = 0; i < poly.length - 1; i++) {
37
- const a = poly[i], b = poly[i + 1], ab = sub(b, a);
38
- const t = clamp(dot(sub(p, a), ab) / dot(ab, ab), 0, 1);
39
- const d = len(sub(p, add(a, mul(ab, t)))) - mix(widths[i], widths[i + 1], t);
40
- if (d < best) best = d;
41
- }
42
- return best;
43
- }
44
-
45
- // Laplacian smoothing of a scalar field over the mesh graph; `lock(v)` keeps a vertex fixed
46
- export function smoothField(f, neighbors, iters = 2, lock = null) {
47
- const n = f.length, tmp = new Float32Array(n);
48
- for (let it = 0; it < iters; it++) {
49
- for (let v = 0; v < n; v++) {
50
- const ns = neighbors[v];
51
- if (!ns.length || (lock && lock(v))) { tmp[v] = f[v]; continue; }
52
- let a = 0;
53
- for (const u of ns) a += f[u];
54
- tmp[v] = 0.5 * f[v] + (0.5 * a) / ns.length;
55
- }
56
- f.set(tmp);
57
- }
58
- return f;
59
- }
60
-
61
- // Poisson-disc features grown on the surface. radius(v) -> r (0 = no feature here);
62
- // returns { spots, grid, cell, key } for use with featureField.
63
- export function poissonFeatures({ pos, nrm, nV, radius, R, spacing = 1.52, gap = 0.0025, cell = 0.03, skip = null, extra = null }) {
64
- const order = new Uint32Array(nV);
65
- for (let i = 0; i < nV; i++) order[i] = i;
66
- for (let i = nV - 1; i > 0; i--) { const j = Math.floor(R() * (i + 1)); const t = order[i]; order[i] = order[j]; order[j] = t; }
67
- const grid = new Map();
68
- const key = (x, y, z) => ((x + 512) * 1024 + (y + 512)) * 1024 + (z + 512);
69
- const spots = [];
70
- for (let k = 0; k < nV; k++) {
71
- const v = order[k];
72
- if (skip && skip(v)) continue;
73
- let r = radius(v);
74
- if (!(r > 0)) continue;
75
- r *= 0.78 + 0.44 * R();
76
- const p = [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
77
- const gx = Math.floor(p[0] / cell), gy = Math.floor(p[1] / cell), gz = Math.floor(p[2] / cell);
78
- let ok = true;
79
- for (let dz = -1; dz <= 1 && ok; dz++)
80
- for (let dy = -1; dy <= 1 && ok; dy++)
81
- for (let dx = -1; dx <= 1 && ok; dx++) {
82
- const arr = grid.get(key(gx + dx, gy + dy, gz + dz));
83
- if (!arr) continue;
84
- for (const s of arr) {
85
- const minD = spacing * (r + s.r) + gap;
86
- const ddx = p[0] - s.p[0], ddy = p[1] - s.p[1], ddz = p[2] - s.p[2];
87
- if (ddx * ddx + ddy * ddy + ddz * ddz < minD * minD) { ok = false; break; }
88
- }
89
- }
90
- if (!ok) continue;
91
- const s = { v, p, r, n: [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]], ...(extra ? extra(v, R) : {}) };
92
- spots.push(s);
93
- const kk = key(gx, gy, gz);
94
- (grid.get(kk) || grid.set(kk, []).get(kk)).push(s);
95
- }
96
- return { spots, grid, cell, key };
97
- }
98
-
99
- // Signed distance to the nearest feature of a poissonFeatures() set, elongated along each
100
- // feature's comb direction `c` by `el`. Returns [sdf, feature].
101
- export function featureDistance(F, p, n, wob = 1) {
102
- const { grid, cell, key } = F;
103
- let best = 1e9, bs = null;
104
- const gx = Math.floor(p[0] / cell), gy = Math.floor(p[1] / cell), gz = Math.floor(p[2] / cell);
105
- for (let dz = -1; dz <= 1; dz++)
106
- for (let dy = -1; dy <= 1; dy++)
107
- for (let dx = -1; dx <= 1; dx++) {
108
- const arr = grid.get(key(gx + dx, gy + dy, gz + dz));
109
- if (!arr) continue;
110
- for (const s of arr) {
111
- if (dot(s.n, n) < 0.2) continue;
112
- const d = sub(p, s.p);
113
- const el = s.el || 1;
114
- const along = s.c ? dot(d, s.c) : 0;
115
- const dd = Math.sqrt(Math.max(0, dot(d, d) - along * along * (1 - 1 / (el * el))));
116
- const sd = dd - s.r * wob;
117
- if (sd < best) { best = sd; bs = s; }
118
- }
119
- }
120
- return [best, bs];
121
- }
@@ -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
- }