@bitefoundry/retrieviewer 0.0.3 → 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 (254) 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 +6 -1
  6. package/PLUGIN.md +13 -10
  7. package/PREREQUISITES.md +95 -0
  8. package/PUPPY-MEMORY.md +102 -1
  9. package/README.ko.md +110 -0
  10. package/README.md +117 -59
  11. package/RELEASING.md +23 -12
  12. package/THIRD-PARTY-NOTICES.md +5 -3
  13. package/braille.mjs +35 -9
  14. package/examples/live.mjs +2 -2
  15. package/integrations/linux-overlay/overlay.py +2 -2
  16. package/integrations/macos-overlay/overlay.jxa +8 -6
  17. package/integrations/opencode/runtime.mjs +4 -4
  18. package/integrations/opencode/tui.tsx +1 -1
  19. package/integrations/windows-overlay/Overlay.cs +4 -4
  20. package/integrations/windows-overlay/protocol.mjs +11 -7
  21. package/package.json +34 -13
  22. package/puppy-comfort-sprites.mjs +25 -0
  23. package/puppy-companion-comfort-q5.bin.gz +0 -0
  24. package/puppy-companion-comfort.json +77 -0
  25. package/puppy-companion-life.mjs +49 -16
  26. package/puppy-companion-objects.mjs +12 -0
  27. package/puppy-frame-predictive.mjs +15 -9
  28. package/puppy-frame-store.mjs +2 -2
  29. package/puppy-paper-world.mjs +14 -0
  30. package/puppy-paper.mjs +65 -14
  31. package/retrieviewer-plugin.d.mts +1 -1
  32. package/retrieviewer-plugin.mjs +1 -1
  33. package/retrieviewer.d.mts +10 -4
  34. package/retrieviewer.mjs +20 -14
  35. package/locomotion-core.mjs +0 -21
  36. package/locomotion-puppy.animal +0 -0
  37. package/vendor/procedural-animals/package.json +0 -40
  38. package/vendor/procedural-animals/src/animal.js +0 -146
  39. package/vendor/procedural-animals/src/bake.js +0 -51
  40. package/vendor/procedural-animals/src/build.js +0 -81
  41. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  42. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  43. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  44. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  45. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  46. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  47. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  48. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  49. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  50. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  51. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  52. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  53. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  54. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  55. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  56. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  57. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  58. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  59. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  60. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  61. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  62. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  63. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  64. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  65. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  66. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  67. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  68. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  69. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  70. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  71. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  72. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  73. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  74. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  75. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  76. package/vendor/procedural-animals/src/index.d.ts +0 -98
  77. package/vendor/procedural-animals/src/index.js +0 -29
  78. package/vendor/procedural-animals/src/registry.js +0 -48
  79. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  80. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  81. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  82. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  83. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  84. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  85. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  86. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  87. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  88. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  89. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  90. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  91. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  92. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  93. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  94. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  95. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  96. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  97. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  98. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  99. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  100. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  101. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  102. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  103. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  104. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  105. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  106. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  107. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  108. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  109. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  110. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  111. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  112. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  113. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  114. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  115. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  116. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  117. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  118. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  119. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  120. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  121. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  122. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  123. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  124. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  125. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  126. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  127. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  128. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  129. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  130. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  131. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  132. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  133. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  134. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  135. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  136. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  137. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  138. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  139. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  140. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  141. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  142. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  143. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  144. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  145. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  146. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  147. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  148. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  149. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  150. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  151. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  152. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  153. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  154. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  155. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  156. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  157. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  158. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  159. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  160. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  161. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  162. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  163. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  164. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  165. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  166. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  167. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  168. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  169. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  170. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  171. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  172. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  173. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  174. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  175. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  176. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  177. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  178. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  179. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  180. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  181. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  182. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  183. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  184. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  185. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  186. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  187. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  188. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  189. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  190. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  191. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  192. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  193. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  194. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  195. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  196. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  197. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  198. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  199. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  200. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  201. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  202. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  203. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  204. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  205. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  206. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  207. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  208. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  209. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  210. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  211. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  212. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  213. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  214. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  215. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  216. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  217. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  218. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  219. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  220. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  221. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  222. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  223. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  224. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  225. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  226. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  227. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  228. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  229. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  230. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  231. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  232. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  233. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  234. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  235. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  236. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  237. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  238. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  239. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  240. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  241. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  242. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  243. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  244. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  245. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  246. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  247. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  248. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  249. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  250. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  251. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  252. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  253. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  254. package/vendor/procedural-animals/src/worker.js +0 -29
@@ -1,676 +0,0 @@
1
- // Eyes: eyeball meshes with a purpose-built shader and a small behaviour model, driven by the packed
2
- // `data.eyes[i] = { side, bone, c, x, y, z, scale, spec, look }`.
3
- // * cornea: clear coat with scene reflections and sharp light catchlights (MeshPhysicalMaterial,
4
- // so every scene light, shadow, environment and fog applies);
5
- // * iris: procedural stroma (radial fibres, collarette, crypts, pupillary ruff, dark limbal ring)
6
- // seen THROUGH the cornea: the view ray is refracted at the corneal surface and traced to the
7
- // recessed iris plane, so the iris shifts with the viewing angle like a real eye;
8
- // * pupil (look.pupil): 'round', 'slit' (vertical: cats, foxes, snakes) or 'bar' (horizontal
9
- // rounded rectangle: goats, sheep, deer, horses, cattle, frogs); its size follows the light
10
- // (elevation of the scene's main directional light) and arousal within look.pupilSize [min, max];
11
- // * lids (look.lids): 'mammal' (almond aperture, lid-margin shading, blinking upper lid),
12
- // 'bird' (a nictitating membrane sweeps across from the front corner; the lower lid rises for
13
- // sleep), 'none' (fish, snakes: no blinks, glossy spectacle), 'simple' (spiders: glossy black
14
- // domes, any number and size of eyes);
15
- // * behaviour: fixations with quick saccades, blinks every few seconds (sometimes double), gaze that
16
- // leads the head toward motion.state.lookTarget / input.look, motion.state.eyelid (0 open .. 1
17
- // shut) for sleep, half-lidded and still in death. Optional state: motion.state.eyeRoll (0..1, the
18
- // eyeball rolls back and shows its sclera: white shark biting), motion.state.nictitate (0..1, the
19
- // nictitating membrane closes: carcharhinid sharks); look.blink === false turns automatic blinks off.
20
- // * look.cornea (optional, default 1): strength of the cornea's clear coat. Under a bright sky the clear coat's
21
- // reflection can swamp a dark iris (a brown bear's eyes read pale blue-grey in the showcase); a species with
22
- // small, dark, deep-set eyes lowers it (bear 0.2) and keeps a small wet catchlight.
23
- // * look.envSpecular (optional, default 1): scale of the eyeball's environment (indirect) specular. three.js
24
- // replaces a standard material's envMapIntensity with scene.environmentIntensity whenever the scene has an
25
- // environment and the material has no env map of its own (WebGLRenderer), so the material's 0.28 below does not
26
- // apply in such scenes and a bright sky's sheen covers a dark iris (bear 0.2).
27
- import * as THREE from 'three';
28
-
29
- const PUPIL = { round: 0, slit: 1, bar: 2 };
30
- const LIDS = { mammal: 0, bird: 1, none: 2, simple: 3 };
31
- const PUPIL_SIZE = { round: [0.3, 0.5], slit: [0.07, 0.7], bar: [0.07, 0.42] };
32
- const DEFAULT_IRIS = { inner: [0.2, 0.06, 0.007], mid: [0.54, 0.2, 0.022], hi: [0.78, 0.38, 0.055], outer: [0.26, 0.075, 0.009] };
33
-
34
- const PARS = /* glsl */ `
35
- uniform float uIrisZ, uIrisR, uPupil, uLensR, uLensD, uLensOff, uBlink, uLower, uNict, uLidTravel, uFront, uEnvSpec;
36
- uniform float uRimShade, uLidShade, uLimbus, uLidSpec, uLidFur, uGlaze, uAmbient, uLidHair, uIrisLight, uEyeFill, uLidFlat, uLidSeam, uLidClump, uLidFill, uIrisTex, uLidMeet;
37
- float gLidWrap = 0.0; // (look.lidFill) the coat's wrapped diffuse on a closing lid, set per fragment
38
- uniform mat4 uSocketInv;
39
- uniform vec3 uLidColor, uIrisIn, uIrisMid, uIrisHi, uIrisOut, uSclera;
40
- varying vec3 vEyeObj; varying vec3 vCamObj; varying vec3 vEyeWorld; varying vec3 vIrisN;
41
- float eh3(vec3 p) { p = fract(p * 0.1031); p += dot(p, p.zyx + 31.32); return fract((p.x + p.y) * p.z); }
42
- float evn3(vec3 p) {
43
- vec3 i = floor(p), f = fract(p); f = f * f * (3.0 - 2.0 * f);
44
- return mix(mix(mix(eh3(i), eh3(i + vec3(1, 0, 0)), f.x), mix(eh3(i + vec3(0, 1, 0)), eh3(i + vec3(1, 1, 0)), f.x), f.y),
45
- mix(mix(eh3(i + vec3(0, 0, 1)), eh3(i + vec3(1, 0, 1)), f.x), mix(eh3(i + vec3(0, 1, 1)), eh3(i + vec3(1, 1, 1)), f.x), f.y), f.z);
46
- }
47
- // noise sampled on circles around the pupil: periodic in angle, stretched along the radius
48
- float irisN(float a, float r, float k, float kr, float o) { return evn3(vec3(cos(a) * k + o, sin(a) * k - o, r * kr)); }
49
- // signed distance to the pupil edge in iris radii (< 0 inside)
50
- float pupilSD(vec2 qn) {
51
- #if PUPIL_TYPE == 1
52
- // vertical slit: a narrow lens pointed at top and bottom
53
- float w = uPupil, hh = 0.93;
54
- float R = (hh * hh + w * w) / (2.0 * w);
55
- vec2 p = abs(qn);
56
- return length(vec2(p.x + R - w, p.y)) - R;
57
- #elif PUPIL_TYPE == 2
58
- // horizontal bar: rounded rectangle
59
- vec2 ext = vec2(0.58, uPupil);
60
- float rr = uPupil * 0.9;
61
- vec2 d = abs(qn) - ext + rr;
62
- return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0) - rr;
63
- #else
64
- return length(qn) - uPupil;
65
- #endif
66
- }
67
- vec3 irisColor(vec2 q) {
68
- vec2 qn = q / uIrisR;
69
- float r = length(qn);
70
- float a = atan(q.y, q.x);
71
- float pd = pupilSD(qn);
72
- // radial fibre bundles at three scales
73
- float f1 = irisN(a, r, 24.0, 2.5, 0.0);
74
- float f2 = irisN(a, r, 9.0, 1.2, 7.3);
75
- float f3 = irisN(a, r, 55.0, 5.0, 3.1);
76
- float fib = f1 * 0.5 + f2 * 0.3 + f3 * 0.2;
77
- // wavy collarette ring and dark crypts in the mid stroma
78
- float cr = 0.53 + 0.06 * (irisN(a, 0.0, 3.0, 0.0, 1.7) - 0.5);
79
- float coll = smoothstep(0.045, 0.0, abs(r - cr));
80
- float crypt = smoothstep(0.64, 0.8, irisN(a, r, 14.0, 7.0, 9.1)) * smoothstep(0.5, 0.62, r) * (1.0 - smoothstep(0.84, 0.93, r));
81
- vec3 col = mix(uIrisIn, uIrisMid, smoothstep(0.36, 0.62, r));
82
- col = mix(col, uIrisHi, fib * 0.65 * smoothstep(0.42, 0.66, r) * (1.0 - smoothstep(0.8, 0.94, r)));
83
- col = mix(col, uIrisOut, smoothstep(0.78, 0.96, r));
84
- col *= mix(1.0, 0.72 + 0.56 * fib, uIrisTex); // (look.irisTexture: the contrast of the radial fibres and crypts)
85
- col = mix(col, col * 1.3 + 0.015, coll * 0.55);
86
- col *= 1.0 - crypt * 0.5 * uIrisTex;
87
- col *= mix(1.0, uLimbus, smoothstep(0.9, 1.0, r)); // limbal ring
88
- col = mix(col, uIrisIn * 0.3 + 0.004, smoothstep(0.08, 0.01, pd)); // pupillary ruff
89
- float pw = fwidth(pd) + 0.004;
90
- col = mix(col, vec3(0.0025), smoothstep(pw, -pw, pd)); // pupil
91
- return col;
92
- }
93
- float lensSD(vec2 p, float dUp, float dLo) { return max(length(p + vec2(0.0, dUp)) - uLensR, length(p - vec2(0.0, dLo)) - uLensR); }
94
- `;
95
-
96
- const MAP = /* glsl */ `
97
- // ---- iris seen through the cornea
98
- vec3 Pe = vEyeObj;
99
- vec3 Ne = normalize(Pe);
100
- vec3 Ve = normalize(Pe - vCamObj);
101
- float lidCover = 0.0, lidMargin = 0.0, eyeAO = 1.0, eyeLightOcc = 1.0, nictCover = 0.0;
102
- vec3 Sk = (uSocketInv * vec4(vEyeWorld, 1.0)).xyz;
103
- Sk.y -= uLensOff;
104
- #if LID_TYPE == 3
105
- // simple glossy dome (spiders)
106
- diffuseColor.rgb = uIrisOut * 0.06 + 0.003;
107
- #else
108
- float onCornea = smoothstep(uIrisZ - 0.00035, uIrisZ + 0.00035, Pe.z);
109
- vec3 Rr = refract(Ve, Ne, 1.0 / 1.376);
110
- float tI = (uIrisZ - Pe.z) / min(Rr.z, -0.05);
111
- vec2 qI = Pe.xy + Rr.xy * max(tI, 0.0);
112
- vec3 irisC = irisColor(qI);
113
- vec3 scl = uSclera * (0.55 + 0.45 * smoothstep(-0.002, 0.006, Pe.z));
114
- diffuseColor.rgb = mix(scl, irisC, onCornea);
115
- #endif
116
- #if LID_TYPE == 0 || LID_TYPE == 1
117
- // ---- lids: aperture in the socket frame (fixed to the head, not to the moving eyeball)
118
- #if LID_TYPE == 0
119
- float dUp = uLensD + uBlink * uLidTravel * 0.85, dLo = uLensD + uBlink * uLidTravel * 0.15;
120
- #else
121
- float dUp = uLensD + uBlink * uLidTravel * 0.12, dLo = uLensD + uBlink * uLidTravel * 0.88;
122
- #endif
123
- #if LID_TYPE == 0 && LID_ARC == 1
124
- // (look.lidArc) the corners stay put: the upper lid's edge descends as an arc from corner to corner and meets
125
- // the lower lid (which rises a little) along the whole fissure, so a shut eye shows its lid margins as one
126
- // curved seam; the two-circle lens below closes to a single point in the middle of the eye
127
- float hw = sqrt(max(uLensR * uLensR - uLensD * uLensD, 1e-10));
128
- float ax = min(abs(Sk.x), hw * 0.9999);
129
- float sq = sqrt(max(uLensR * uLensR - ax * ax, 1e-10));
130
- float yA = sq - uLensD, s0 = ax / sq; // the open upper edge (the lower is -yA), |slope|
131
- // (look.lidMeet: the lids meet at -lidMeet yA when shut, 0.7 by default; the upper lid travels 1 + lidMeet of yA)
132
- float kUp = 1.0 - (1.0 + uLidMeet) * uBlink, kLo = 1.0 - (1.0 - uLidMeet) * uBlink;
133
- float yUp = yA * kUp, yLo = -yA * kLo;
134
- float sdUp = (Sk.y - yUp) / sqrt(1.0 + kUp * kUp * s0 * s0);
135
- float sdLo = (yLo - Sk.y) / sqrt(1.0 + kLo * kLo * s0 * s0);
136
- float sdB = max(max(sdUp, sdLo), abs(Sk.x) - hw);
137
- #else
138
- float sdB = lensSD(Sk.xy, dUp, dLo);
139
- float sdUp = length(Sk.xy + vec2(0.0, dUp)) - uLensR;
140
- #endif
141
- lidCover = smoothstep(-0.00015, 0.00015, sdB);
142
- // (look.lidSeam > 0: the dark lid margin narrows as the lids close, to a seam of about that half-width when shut:
143
- // the open eye's 0.3-1.4 mm margin fade on both lids drew a shut eye's seam as a ~3 mm dark band)
144
- float mA = 0.0003, mB = 0.0014;
145
- if (uLidSeam > 0.0) { mA = mix(mA, 0.3 * uLidSeam, uBlink); mB = mix(mB, uLidSeam, uBlink); }
146
- lidMargin = 1.0 - smoothstep(mA, mB, sdB);
147
- float aoRim = smoothstep(0.0, -0.003, sdB);
148
- float shadowUp = smoothstep(-0.0075, -0.0004, sdUp);
149
- eyeAO = mix(1.0 - 0.7 * uRimShade, 1.0, aoRim) * (1.0 - 0.6 * uLidShade * shadowUp);
150
- eyeLightOcc = mix(1.0 - 0.45 * uRimShade, 1.0, aoRim) * (1.0 - 0.7 * uLidShade * shadowUp);
151
- #if LID_TYPE == 1
152
- // nictitating membrane: sweeps from the front corner across the eye
153
- float halfW = sqrt(max(uLensR * uLensR - uLensD * uLensD, 1e-10));
154
- float edgeX = mix(halfW * 1.1, -halfW * 1.1, uNict);
155
- nictCover = smoothstep(edgeX - 0.0004, edgeX + 0.0004, Sk.x * uFront) * step(0.001, uNict);
156
- // a milky, translucent sheet: the iris shows through, veiled and cooled
157
- diffuseColor.rgb = mix(diffuseColor.rgb, vec3(0.5, 0.55, 0.6), nictCover * 0.6);
158
- #endif
159
- // short hairs / fine skin lying along the lid, a slightly darker fold near the brow, dark margin
160
- float hairs = 0.8 + 0.4 * evn3(vec3(Sk.x * 2600.0, Sk.y * 650.0, 3.7)) * evn3(vec3(Sk.x * 900.0, Sk.y * 260.0, 9.1));
161
- // (look.lidHair: a furred lid, strands lying along the lid from the nasal corner toward the temple and tufts
162
- // of them, at the contrast of the coat's own base streaks; faded out where they get smaller than a pixel)
163
- if (uLidHair > 0.0) {
164
- vec2 hq = vec2(Sk.x * uFront, Sk.y + 0.2 * Sk.x * uFront);
165
- float fdH = 1.0 - smoothstep(0.35, 0.9, length(fwidth(hq)) * 5200.0);
166
- float st1 = evn3(vec3(hq.x * 900.0, hq.y * 6000.0, 1.3)), st2 = evn3(vec3(hq.x * 450.0, hq.y * 2600.0, 7.9));
167
- float furH = mix(1.0, (0.62 + 0.76 * st1) * (0.84 + 0.32 * st2), fdH);
168
- hairs = mix(hairs, furH, uLidHair);
169
- }
170
- // (look.lidClump: the coat's tufts at a normal viewing distance, where the strands above fade out: tone variation
171
- // at the clump scale (~2-3 mm, drawn out along the lid hair), so a shut eye's lids read as fur, not a smooth disc)
172
- if (uLidClump > 0.0) {
173
- vec2 cq = vec2(Sk.x * uFront, Sk.y + 0.2 * Sk.x * uFront);
174
- float c1 = evn3(vec3(cq.x * 380.0, cq.y * 900.0, 5.3)), c2 = evn3(vec3(cq.x * 170.0, cq.y * 420.0, 2.1));
175
- float fdC = 1.0 - smoothstep(0.35, 0.9, length(fwidth(cq)) * 1300.0);
176
- hairs *= mix(1.0, (0.55 + 0.9 * c1) * (0.8 + 0.4 * c2), uLidClump * fdC);
177
- }
178
- float fold = 1.0 - 0.25 * smoothstep(0.0, 0.004, Sk.y);
179
- vec3 lidAlb = uLidColor * hairs * fold * mix(1.0, 0.06, lidMargin);
180
- diffuseColor.rgb = mix(diffuseColor.rgb, lidAlb, lidCover);
181
- gLidWrap = uLidFill * uLidFur * lidCover;
182
- eyeAO = mix(eyeAO, 1.0, lidCover);
183
- eyeLightOcc = mix(eyeLightOcc, 1.0, lidCover);
184
- #endif
185
- `;
186
-
187
- // (look.lidFill) a furred closing lid takes the direct light like the coat round it: the coat's wrapped diffuse,
188
- // (N.L + 0.4) / 1.4 (coatMaterial.js shadeLight), which lights fur well past the terminator where a Lambert lid went dark
189
- const DIRECT = /* glsl */ `
190
- #include <lights_physical_pars_fragment>
191
- void RE_Direct_Eye(const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
192
- RE_Direct_Physical(directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight);
193
- if (gLidWrap > 0.0) {
194
- float ndl = dot(geometryNormal, directLight.direction);
195
- float extra = max(clamp((ndl + 0.4) / 1.4, 0.0, 1.0) - max(ndl, 0.0), 0.0);
196
- reflectedLight.directDiffuse += gLidWrap * extra * directLight.color * BRDF_Lambert(material.diffuseContribution);
197
- }
198
- }
199
- #undef RE_Direct
200
- #define RE_Direct RE_Direct_Eye
201
- `;
202
-
203
- const MATERIAL = /* glsl */ `
204
- #include <lights_physical_fragment>
205
- // lids are skin, not cornea (look.lidSpec < 1: a furred lid, without the smooth ball's sky sheen)
206
- material.roughness = mix(material.roughness, 0.78, lidCover);
207
- float lidSpecK = mix(1.0, uLidSpec, lidCover);
208
- material.specularColor *= lidSpecK;
209
- material.specularColorBlended *= lidSpecK;
210
- material.specularF90 *= lidSpecK;
211
- #ifdef USE_CLEARCOAT
212
- material.clearcoat *= 1.0 - lidCover;
213
- material.clearcoatRoughness = mix(material.clearcoatRoughness, 0.12, nictCover);
214
- #endif
215
- `;
216
-
217
- const NORMALS = /* glsl */ `
218
- #include <normal_fragment_maps>
219
- #if LID_TYPE != 3
220
- {
221
- // (look.irisLight) the iris and the pupil are a flat disc behind the cornea, lit through it: shaded with the disc's
222
- // own normal (the eye's axis) instead of the cornea's sphere. Seen from below or from the side, the part of the
223
- // cornea in view faces away from a high light, and the iris behind it went black while the face round it was lit.
224
- // The clear coat (the cornea's reflection) keeps the sphere's normal.
225
- float irisK = uIrisLight * onCornea * (1.0 - lidCover);
226
- if (irisK > 0.0) normal = normalize(mix(normal, normalize(vIrisN), irisK));
227
- // (look.lidFlat) a closing lid lies in the face: shaded with the socket's axis (the face's normal round the eye)
228
- // rather than as a lit dome with a hard terminator across it
229
- if (uLidFlat > 0.0 && lidCover > 0.0) {
230
- vec3 sockZ = normalize((viewMatrix * vec4(uSocketInv[0][2], uSocketInv[1][2], uSocketInv[2][2], 0.0)).xyz);
231
- normal = normalize(mix(normal, sockZ, uLidFlat * lidCover));
232
- }
233
- }
234
- #endif
235
- `;
236
-
237
- const MOD = /* glsl */ `
238
- #include <aomap_fragment>
239
- // (look.lidFur > 0: a furred closing lid is lit like the coat round it, by the scene's lights only: the coat
240
- // shader takes no environment map, so a lid that took the sky's light read paler than the fur outdoors and
241
- // darker indoors. look.glaze < 1: less of the base layer's broad sheen over the open eye; the clear coat's
242
- // sharp corneal reflection stays)
243
- float lidFurK = lidCover * uLidFur;
244
- // (look.ambient: the whole eye's indirect diffuse from the scene's ambient lights, as the coat is lit, not the
245
- // environment map's irradiance: a sky map poured blue light into an orange iris and greyed it)
246
- reflectedLight.indirectDiffuse = mix(reflectedLight.indirectDiffuse, irradiance * BRDF_Lambert(material.diffuseColor), max(lidFurK, uAmbient));
247
- // (look.fill: a fill light on the eye where the direct light does not reach, fading out where it does, as the
248
- // coat's camera-side fill lifts the fur round the eye: the sky in front of the eye (the environment map's
249
- // irradiance), or without an environment map the coat's own fill from the ambient lights. With the eye lit by the
250
- // weak ambient lights alone (look.ambient), an iris or a closed lid in shade went 4-10x darker than the fur, which
251
- // also takes light scattered through it; the sky map's full irradiance on the other hand bleached a sunlit iris)
252
- if (uEyeFill > 0.0) {
253
- // the coat's own fill (coatMaterial.js: the ambient and hemisphere lights' camera-side fill at its uFill 0.45)
254
- vec3 coatFill = ambientLightColor * 2.0;
255
- #if NUM_HEMI_LIGHTS > 0
256
- for (int i = 0; i < NUM_HEMI_LIGHTS; i++) coatFill += hemisphereLights[i].skyColor + hemisphereLights[i].groundColor;
257
- #endif
258
- coatFill *= 0.45 * clamp(dot(normal, geometryViewDir) * 0.6 + 0.4, 0.0, 1.0);
259
- #if defined( USE_ENVMAP ) && defined( ENVMAP_TYPE_CUBE_UV )
260
- // (look.lidFill: a furred closing lid takes the coat's fill like the fur round it, not the sky map's irradiance,
261
- // which lit a shut eye in shade 2-7x brighter than the fur: pale discs)
262
- vec3 fillIrr = mix(iblIrradiance, coatFill, uLidFill * lidFurK);
263
- #else
264
- vec3 fillIrr = coatFill;
265
- #endif
266
- vec3 dirC = vec3(0.0);
267
- #if NUM_DIR_LIGHTS > 0
268
- for (int i = 0; i < NUM_DIR_LIGHTS; i++) dirC += directionalLights[i].color;
269
- #endif
270
- #if NUM_SUN_LIGHTS > 0
271
- for (int i = 0; i < NUM_SUN_LIGHTS; i++) dirC += sunLights[i].color;
272
- #endif
273
- vec3 lam = BRDF_Lambert(material.diffuseColor);
274
- float litFrac = clamp(dot(reflectedLight.directDiffuse, vec3(1.0)) / max(dot(dirC * lam, vec3(1.0)), 1e-9), 0.0, 1.0);
275
- // (a furred lid with look.lidFill: the coat's full fill; the iris: look.fill's share of it)
276
- reflectedLight.indirectDiffuse += fillIrr * (mix(uEyeFill, 1.0, uLidFill * lidFurK) * (1.0 - litFrac)) * lam;
277
- }
278
- // (look.lidFill: the light a furred lid scatters toward the viewer from a sun behind it, as the coat's fur glows
279
- // against the light (coatMaterial.js back-scatter, (albedo + 0.04): a black coat's fur glows too), at about the
280
- // coat's level round the eye: backlit, the fur round a shut eye glowed while the lid went black)
281
- #if NUM_DIR_LIGHTS > 0
282
- if (uLidFill > 0.0 && lidFurK > 0.0) {
283
- for (int i = 0; i < NUM_DIR_LIGHTS; i++) {
284
- float bl = pow(clamp(dot(-directionalLights[i].direction, geometryViewDir), 0.0, 1.0), 2.5);
285
- reflectedLight.directDiffuse += (uLidFill * lidFurK * 0.15 * bl) * directionalLights[i].color * BRDF_Lambert(material.diffuseColor + 0.04);
286
- }
287
- }
288
- #endif
289
- reflectedLight.indirectSpecular *= (1.0 - lidFurK) * mix(uGlaze, 1.0, lidCover);
290
- reflectedLight.directSpecular *= mix(uGlaze, 1.0, lidCover);
291
- reflectedLight.directDiffuse *= eyeLightOcc;
292
- reflectedLight.directSpecular *= eyeLightOcc;
293
- reflectedLight.indirectDiffuse *= eyeAO;
294
- reflectedLight.indirectSpecular *= eyeAO * uEnvSpec;
295
- #ifdef USE_CLEARCOAT
296
- clearcoatSpecularDirect *= eyeLightOcc;
297
- clearcoatSpecularIndirect *= eyeAO;
298
- #endif
299
- `;
300
-
301
- const smooth01 = (t) => { t = Math.min(1, Math.max(0, t)); return t * t * (3 - 2 * t); };
302
- const col3 = (c, d) => new THREE.Color(...(c || d));
303
-
304
- // Resolves a species look into complete parameters (defaults = cheetah).
305
- export function resolveLook(look = {}) {
306
- const pupil = PUPIL[look.pupil] !== undefined ? look.pupil : 'round';
307
- const lids = LIDS[look.lids] !== undefined ? look.lids : 'mammal';
308
- return {
309
- pupil, lids,
310
- pupilSize: look.pupilSize || PUPIL_SIZE[pupil],
311
- iris: { ...DEFAULT_IRIS, ...(look.iris || {}) },
312
- sclera: { color: [0.2, 0.15, 0.11], visible: false, ...(look.sclera || {}) },
313
- lidColor: look.lidColor || [0.5, 0.31, 0.15],
314
- // shading of the eyeball by its lids (1 = the default: a deep-set eye; a flat face with prominent
315
- // eyes, the cat, shades less) and the darkening of the limbal ring (iris colour x limbus at its edge)
316
- shade: { rim: 1, lid: 1, ...(look.shade || {}) },
317
- limbus: look.limbus ?? 0.2,
318
- // specular of a closing lid (1 = the default smooth skin; a lid covered in short fur, the cat's, has
319
- // almost none: on a dark coat the smooth lid mirrored the sky and read as a glossy open eye)
320
- lidSpec: look.lidSpec ?? 1,
321
- // (0..1) a closing lid lit like fur: by the scene's lights only, no environment map (0 = the default: the
322
- // lid takes the environment like the eyeball it is painted on)
323
- lidFur: look.lidFur ?? 0,
324
- // (0..1) the base layer's broad specular sheen over the open eye (1 = the default); the clear coat (the
325
- // cornea's sharp reflection) is kept either way
326
- glaze: look.glaze ?? 1,
327
- // (0..1) the eye's indirect diffuse from the scene's ambient lights instead of the environment map (as the
328
- // coat shader is lit; 0 = the default)
329
- ambient: look.ambient ?? 0,
330
- // (0..1) a closing lid textured like fur (strands and tufts along the lid) instead of fine smooth skin
331
- lidHair: look.lidHair ?? 0,
332
- // (0..1) the iris and pupil shaded with the iris disc's normal (the eye's axis) instead of the cornea's sphere
333
- // (0 = the default: the sphere's, which darkens the iris seen from below or aside under a high light)
334
- irisLight: look.irisLight ?? 0,
335
- // (0..1) the coat shader's camera-side fill light on the eye (iris and lids) where the direct light does not
336
- // reach, as on the fur round it (0 = the default: none)
337
- fill: look.fill ?? 0,
338
- // (0..1) a closing lid shaded with the socket's axis, as the face round it, instead of as a dome (0 = the default)
339
- lidFlat: look.lidFlat ?? 0,
340
- // (m) the half-width the dark lid margin narrows to as the lids shut (0 = the default: the open eye's 0.3-1.4 mm
341
- // margin fade at every stage, a ~3 mm dark band along a shut eye's seam)
342
- lidSeam: look.lidSeam ?? 0,
343
- // (0..1) tone variation of a closing lid at the coat's clump scale (0 = the default: none)
344
- lidClump: look.lidClump ?? 0,
345
- // (0..1) the contrast of the iris's radial fibre bundles and dark crypts (1 = the default; a cat's even iris
346
- // with its crypts at full contrast read as the ticks of a clock face)
347
- irisTexture: look.irisTexture ?? 1,
348
- // (0..1, with fill and lidFur) a furred closing lid is lit like the coat round it: the coat's full ambient fill
349
- // instead of the environment map's irradiance, and the coat's wrapped diffuse (0 = the default)
350
- lidFill: look.lidFill ?? 0,
351
- // (mammal lids) true: the corners stay put and the lids meet along the whole fissure when they shut (a
352
- // curved seam); false (the default): the two-circle lens, which closes to a point
353
- lidArc: !!look.lidArc,
354
- // (with lidArc) where the shut lids meet, in units of the open aperture's half-height below its centre (0.7 = the
355
- // default; nearer 1 the upper lid does all the closing and the lower lid shows as a thinner band under the seam)
356
- lidMeet: look.lidMeet ?? 0.7,
357
- // (0..1) the cornea's clear coat strength (1 = the default; see the header: bear 0.2)
358
- cornea: look.cornea ?? 1,
359
- // (0..) scale of the eyeball's environment specular (1 = the default; see the header: bear 0.2). It multiplies
360
- // whatever glaze / lidFur leave of it, so the options compose
361
- envSpecular: look.envSpecular ?? 1,
362
- };
363
- }
364
-
365
- function applyLook(eyes, shared, look) {
366
- const L = resolveLook(look);
367
- shared.uIrisIn.value.setRGB(...L.iris.inner);
368
- shared.uIrisMid.value.setRGB(...L.iris.mid);
369
- shared.uIrisHi.value.setRGB(...L.iris.hi);
370
- shared.uIrisOut.value.setRGB(...L.iris.outer);
371
- const sc = L.sclera.visible ? L.sclera.color : L.sclera.color.map((c) => c * 0.6);
372
- shared.uSclera.value.setRGB(...sc);
373
- shared.uLidColor.value.setRGB(...L.lidColor);
374
- shared.uRimShade.value = L.shade.rim;
375
- shared.uLidShade.value = L.shade.lid;
376
- shared.uLimbus.value = L.limbus;
377
- shared.uLidSpec.value = L.lidSpec;
378
- shared.uLidFur.value = L.lidFur;
379
- shared.uGlaze.value = L.glaze;
380
- shared.uAmbient.value = L.ambient;
381
- shared.uLidHair.value = L.lidHair;
382
- shared.uIrisLight.value = L.irisLight;
383
- shared.uEyeFill.value = L.fill;
384
- shared.uLidFlat.value = L.lidFlat;
385
- shared.uLidSeam.value = L.lidSeam;
386
- shared.uLidClump.value = L.lidClump;
387
- shared.uLidFill.value = L.lidFill;
388
- shared.uIrisTex.value = L.irisTexture;
389
- shared.uLidMeet.value = L.lidMeet;
390
- shared.uEnvSpec.value = L.envSpecular;
391
- for (const e of eyes) {
392
- const m = e.material;
393
- m.defines = { PUPIL_TYPE: PUPIL[L.pupil], LID_TYPE: LIDS[L.lids], LID_ARC: L.lidArc ? 1 : 0 };
394
- m.roughness = L.lids === 'simple' ? 0.25 : 0.6;
395
- m.clearcoatRoughness = L.lids === 'none' || L.lids === 'simple' ? 0.02 : 0.03;
396
- m.clearcoat = L.cornea;
397
- m.needsUpdate = true;
398
- }
399
- return L;
400
- }
401
-
402
- export function createEyes(eyeData) {
403
- eyeData = eyeData || [];
404
- const shared = {
405
- uPupil: { value: 0.36 },
406
- uBlink: { value: 0 },
407
- uNict: { value: 0 },
408
- uLower: { value: 0 },
409
- uLidColor: { value: new THREE.Color() },
410
- uIrisIn: { value: new THREE.Color() },
411
- uIrisMid: { value: new THREE.Color() },
412
- uIrisHi: { value: new THREE.Color() },
413
- uIrisOut: { value: new THREE.Color() },
414
- uSclera: { value: new THREE.Color() },
415
- uRimShade: { value: 1 },
416
- uLidShade: { value: 1 },
417
- uLimbus: { value: 0.2 },
418
- uLidSpec: { value: 1 },
419
- uLidFur: { value: 0 },
420
- uGlaze: { value: 1 },
421
- uAmbient: { value: 0 },
422
- uLidHair: { value: 0 },
423
- uIrisLight: { value: 0 },
424
- uEyeFill: { value: 0 },
425
- uLidFlat: { value: 0 },
426
- uLidSeam: { value: 0 },
427
- uLidClump: { value: 0 },
428
- uLidFill: { value: 0 },
429
- uIrisTex: { value: 1 },
430
- uLidMeet: { value: 0.7 },
431
- uEnvSpec: { value: 1 },
432
- };
433
- const geos = new Map();
434
- // 'simple' eyes (spiders: up to eight still domes on one bone) are merged into one mesh built in
435
- // bind space, so they cost a single draw call
436
- const L0 = resolveLook(eyeData[0]?.look);
437
- if (L0.lids === 'simple' && eyeData.length > 1 && eyeData.every((ed) => (ed.bone || 'head') === (eyeData[0].bone || 'head'))) {
438
- const pos = [], nrm = [], idx = [];
439
- const m = new THREE.Matrix4(), q = new THREE.Quaternion(), sc = new THREE.Vector3();
440
- for (const ed of eyeData) {
441
- const E = ed.spec, g = new THREE.SphereGeometry(E.r, 28, 18);
442
- g.rotateX(Math.PI / 2);
443
- const x = new THREE.Vector3(...ed.x), y = new THREE.Vector3(...ed.y), z = new THREE.Vector3(...ed.z);
444
- q.setFromRotationMatrix(new THREE.Matrix4().makeBasis(x, y, z));
445
- g.applyMatrix4(m.compose(new THREE.Vector3(...ed.c), q, sc.setScalar(ed.scale || 1)));
446
- const o = pos.length / 3;
447
- for (const v of g.attributes.position.array) pos.push(v);
448
- for (const v of g.attributes.normal.array) nrm.push(v);
449
- for (const k of g.index.array) idx.push(k + o);
450
- g.dispose();
451
- }
452
- const geo = new THREE.BufferGeometry();
453
- geo.setAttribute('position', new THREE.Float32BufferAttribute(pos, 3));
454
- geo.setAttribute('normal', new THREE.Float32BufferAttribute(nrm, 3));
455
- geo.setIndex(idx);
456
- geos.set('merged', geo);
457
- eyeData = [{ ...eyeData[0], c: [0, 0, 0], x: [1, 0, 0], y: [0, 1, 0], z: [0, 0, 1], scale: 1, side: 1, spec: { ...eyeData[0].spec, tilt: 0 }, merged: geo }];
458
- }
459
- const eyes = eyeData.map((ed, i) => {
460
- const E = ed.spec;
461
- const own = {
462
- uSocketInv: { value: new THREE.Matrix4() },
463
- uIrisZ: { value: E.irisZ ?? E.r * 0.6 },
464
- uIrisR: { value: E.irisR ?? E.r * 0.8 },
465
- uLensR: { value: E.R ?? E.r * 1.15 },
466
- uLensD: { value: E.d ?? E.r * 0.7 },
467
- uLensOff: { value: E.off || 0 },
468
- uLidTravel: { value: 2 * ((E.R ?? E.r * 1.15) - (E.d ?? E.r * 0.7)) },
469
- uFront: { value: 1 },
470
- };
471
- let geo = ed.merged || geos.get(E.r);
472
- if (!geo) {
473
- geo = new THREE.SphereGeometry(E.r, 64, 40);
474
- geo.rotateX(Math.PI / 2); // pole -> +Z (forward)
475
- geos.set(E.r, geo);
476
- }
477
- const mat = new THREE.MeshPhysicalMaterial({ color: 0xffffff, roughness: 0.6, metalness: 0, clearcoat: 1, clearcoatRoughness: 0.03, specularIntensity: 0.2, envMapIntensity: 0.28 });
478
- mat.onBeforeCompile = (sh) => {
479
- Object.assign(sh.uniforms, shared, own);
480
- sh.vertexShader = sh.vertexShader
481
- .replace('#include <common>', '#include <common>\nvarying vec3 vEyeObj; varying vec3 vCamObj; varying vec3 vEyeWorld; varying vec3 vIrisN;')
482
- .replace('#include <begin_vertex>', '#include <begin_vertex>\n vEyeObj = position;\n vEyeWorld = (modelMatrix * vec4(position, 1.0)).xyz;\n vCamObj = (inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;\n vIrisN = normalize(normalMatrix * vec3(0.0, 0.0, 1.0));');
483
- sh.fragmentShader = sh.fragmentShader
484
- .replace('#include <common>', '#include <common>\n' + PARS)
485
- .replace('#include <map_fragment>', MAP)
486
- .replace('#include <normal_fragment_maps>', NORMALS)
487
- .replace('#include <lights_physical_pars_fragment>', DIRECT)
488
- .replace('#include <lights_physical_fragment>', MATERIAL)
489
- .replace('#include <aomap_fragment>', MOD);
490
- };
491
- mat.customProgramCacheKey = () => 'pa-eye-' + (mat.defines ? mat.defines.PUPIL_TYPE + '-' + mat.defines.LID_TYPE + (mat.defines.LID_ARC ? '-arc' : '') : '');
492
- const e = new THREE.Mesh(geo, mat);
493
- e.name = 'eye' + i;
494
- e.matrixAutoUpdate = false;
495
- e.frustumCulled = false;
496
- e.receiveShadow = true;
497
- // bind-space frames: the socket (aperture, tilted) and the eyeball at rest (upright pupil)
498
- const x = new THREE.Vector3(...ed.x), y = new THREE.Vector3(...ed.y), z = new THREE.Vector3(...ed.z);
499
- const socketQ = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().makeBasis(x, y, z));
500
- const ph = (E.tilt || 0) * ed.side;
501
- const x0 = x.clone().multiplyScalar(Math.cos(ph)).addScaledVector(y, -Math.sin(ph));
502
- const y0 = x.clone().multiplyScalar(Math.sin(ph)).addScaledVector(y, Math.cos(ph));
503
- const ballQ = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().makeBasis(x0, y0, z));
504
- // the front (nasal) corner: +x or -x of the socket frame, whichever points forward
505
- own.uFront.value = x.z >= 0 ? 1 : -1;
506
- e.userData = { side: ed.side, bone: ed.bone || 'head', c: new THREE.Vector3(...ed.c), socketQ, ballQ, own, scale: ed.scale || 1, r: E.r };
507
- return e;
508
- });
509
- const look = applyLook(eyes, shared, eyeData[0]?.look);
510
- return { eyes, uniforms: shared, look, geometries: [...geos.values()] };
511
- }
512
-
513
- // Places the eyes on their bone every frame and runs saccades, blinks, lids and pupils.
514
- export class EyeRig {
515
- constructor(eyes, uniforms, skeleton, look) {
516
- this.eyes = eyes;
517
- this.u = uniforms;
518
- this.sk = skeleton;
519
- this.setLook(look);
520
- this.gaze = { yaw: 0, pitch: 0, tYaw: 0, tPitch: 0, next: 0.6, lookYaw: 0, lookPitch: 0 };
521
- this.blinkWait = 1.5 + Math.random() * 3;
522
- this.blinkAge = -1;
523
- this.pendingDouble = false;
524
- this.pupil = 0.4;
525
- this.lidState = 0;
526
- // test / cinematic overrides (null = automatic): blink 0..1, dilation 0..1, pupil size, gaze [yaw, pitch], eyelid 0..1
527
- this.override = { blink: null, dilation: null, pupil: null, gaze: null, eyelid: null };
528
- this._light = null; this._lightAge = 1e9;
529
- this._qb = new THREE.Quaternion(); this._pb = new THREE.Vector3(); this._sb = new THREE.Vector3();
530
- this._q = new THREE.Quaternion(); this._q2 = new THREE.Quaternion(); this._qe = new THREE.Quaternion();
531
- this._p = new THREE.Vector3(); this._t = new THREE.Vector3(); this._s = new THREE.Vector3(1, 1, 1);
532
- this._Y = new THREE.Vector3(0, 1, 0); this._X = new THREE.Vector3(1, 0, 0);
533
- this._bindQinv = new Map(); this._bindP = new Map();
534
- }
535
-
536
- setLook(look) {
537
- this.look = look;
538
- this.canBlink = (look.lids === 'mammal' || look.lids === 'bird') && look.blink !== false;
539
- }
540
-
541
- _boneRel(name) {
542
- const i = this.sk.index[name] ?? this.sk.index.head ?? 0;
543
- if (!this._bindQinv.has(i)) {
544
- const q = new THREE.Quaternion(), p = new THREE.Vector3(), s = new THREE.Vector3();
545
- this.sk.bind[i].decompose(p, q, s);
546
- this._bindQinv.set(i, q.invert());
547
- this._bindP.set(i, p);
548
- }
549
- this.sk.bones[i].matrixWorld.decompose(this._pb, this._qb, this._sb);
550
- this._qb.multiply(this._bindQinv.get(i)); // rigid rotation: world * bind^-1
551
- this._t.copy(this._bindP.get(i)).applyQuaternion(this._qb).negate().add(this._pb);
552
- return { q: this._qb, t: this._t };
553
- }
554
-
555
- // the scene's main directional light (the shadow caster, else the brightest), refreshed now and then
556
- _mainLight(root, dt) {
557
- this._lightAge += dt;
558
- if (root && (this._lightAge > 1 || root !== this._root || (this._light && !this._light.parent))) {
559
- this._root = root;
560
- this._lightAge = 0;
561
- let best = null, score = -1;
562
- root.traverse((o) => {
563
- if (!o.isDirectionalLight || !o.visible) return;
564
- const s = o.intensity * (o.castShadow ? 10 : 1);
565
- if (s > score) { score = s; best = o; }
566
- });
567
- this._light = best;
568
- }
569
- return this._light;
570
- }
571
-
572
- update(dt, { root = null, state = {}, look = null } = {}) {
573
- const dead = state.action === 'death' || state.dead === true;
574
- const lid = this.override.eyelid ?? Math.max(state.eyelid || 0, dead ? 0.5 : 0);
575
- const g = this.gaze;
576
- // fixations and quick saccades (smaller and rarer at speed, when the gaze locks forward)
577
- const run = Math.min(1, (state.speed || 0) / 10);
578
- g.next -= dt;
579
- if (g.next <= 0 && !dead) {
580
- g.next = (0.5 + Math.random() * 2.3) * (1 + run * 2);
581
- g.tYaw = (Math.random() - 0.5) * 0.18 * (1 - 0.7 * run);
582
- g.tPitch = (Math.random() - 0.5) * 0.08 * (1 - 0.7 * run);
583
- }
584
- // the eyes lead the head toward a look target (then recentre as the head catches up)
585
- let lyaw = 0, lpitch = 0;
586
- const target = state.lookTarget || look;
587
- if (target && !dead && this.eyes.length) {
588
- const e = this.eyes[0], rel = this._boneRel(e.userData.bone);
589
- let ay = 0, ap = 0;
590
- for (const ey of this.eyes) {
591
- const d = ey.userData;
592
- this._p.copy(d.c).applyQuaternion(rel.q).add(rel.t);
593
- this._q.copy(rel.q).multiply(d.ballQ).invert();
594
- const v = this._p.subVectors(target, this._p).applyQuaternion(this._q);
595
- ay += Math.atan2(v.x, v.z); ap += Math.atan2(v.y, Math.hypot(v.x, v.z));
596
- }
597
- const n = this.eyes.length;
598
- lyaw = THREE.MathUtils.clamp(ay / n, -0.35, 0.35);
599
- lpitch = THREE.MathUtils.clamp(ap / n, -0.25, 0.25);
600
- }
601
- const kl = 1 - Math.exp(-dt / 0.03);
602
- g.lookYaw += (lyaw - g.lookYaw) * kl;
603
- g.lookPitch += (lpitch - g.lookPitch) * kl;
604
- const k = 1 - Math.exp(-dt / 0.022);
605
- g.yaw += (g.tYaw - g.yaw) * k;
606
- g.pitch += (g.tPitch - g.pitch) * k;
607
- // blinks: ~70 ms close, 35 ms hold, 130 ms open; sometimes a double blink. Birds blink with
608
- // the nictitating membrane (a bit slower sweep).
609
- let b = 0;
610
- if (this.canBlink && !dead && lid < 0.95) {
611
- this.blinkWait -= dt;
612
- if (this.blinkAge < 0 && this.blinkWait <= 0) {
613
- this.blinkAge = 0;
614
- this.pendingDouble = !this.pendingDouble && Math.random() < 0.15;
615
- this.blinkWait = this.pendingDouble ? 0.1 : 2.5 + Math.random() * 5.5;
616
- }
617
- if (this.blinkAge >= 0) {
618
- this.blinkAge += dt;
619
- const bird = this.look.lids === 'bird';
620
- const t = this.blinkAge, c = bird ? 0.09 : 0.07, h = bird ? 0.05 : 0.035, o = bird ? 0.12 : 0.13;
621
- if (t < c) b = smooth01(t / c);
622
- else if (t < c + h) b = 1;
623
- else if (t < c + h + o) b = 1 - smooth01((t - c - h) / o);
624
- else { b = 0; this.blinkAge = -1; }
625
- }
626
- }
627
- if (state.nictitate > 0 && this.look.lids === 'bird') b = Math.max(b, state.nictitate);
628
- if (this.override.blink !== null) b = this.override.blink;
629
- this.lidState += (lid - this.lidState) * (1 - Math.exp(-dt / 0.15));
630
- if (dt === 0) this.lidState = lid;
631
- if (this.look.lids === 'bird') { this.u.uNict.value = b; this.u.uBlink.value = this.lidState; }
632
- else if (this.look.lids === 'mammal') { this.u.uNict.value = 0; this.u.uBlink.value = Math.max(b, this.lidState); }
633
- else { this.u.uNict.value = 0; this.u.uBlink.value = 0; }
634
- // pupil: small in bright sun, wide when the sun is low or the animal is aroused
635
- let dil = 0.35;
636
- const L = this._mainLight(root, dt);
637
- if (L) {
638
- L.updateMatrixWorld();
639
- this._p.setFromMatrixPosition(L.matrixWorld);
640
- if (L.target) { L.target.updateMatrixWorld(); this._p.sub(this._t.setFromMatrixPosition(L.target.matrixWorld)); }
641
- const elev = this._p.normalize().y;
642
- this.lightElevation = elev;
643
- dil = 1 - Math.min(1, Math.max(0, (elev - 0.05) / 0.35));
644
- dil = dil * 0.85 + 0.15 * (1 - Math.min(1, L.intensity / 3));
645
- }
646
- if (state.action === 'attack' || state.speed > 8) dil = Math.min(1, dil + 0.3);
647
- if (dead) dil = 0.9;
648
- if (this.override.dilation !== null) dil = this.override.dilation;
649
- const kp = dt > 0 ? 1 - Math.exp(-dt / 0.4) : 1;
650
- this.pupil += (dil - this.pupil) * kp;
651
- const ps = this.look.pupilSize;
652
- this.u.uPupil.value = this.override.pupil ?? THREE.MathUtils.lerp(ps[0], ps[1], this.pupil);
653
- // place each eye: socket frame (aperture) and eyeball (rest * gaze)
654
- const gy = this.override.gaze ? this.override.gaze[0] : g.yaw + g.lookYaw;
655
- let gp = this.override.gaze ? this.override.gaze[1] : g.pitch + g.lookPitch;
656
- // ocular rotation: the eyeball rolls back and down (the white shark protects its eye on the bite)
657
- const roll = state.eyeRoll || 0;
658
- if (roll > 0 && !this.override.gaze) gp -= roll * 1.25;
659
- for (const e of this.eyes) {
660
- const d = e.userData;
661
- const rel = this._boneRel(d.bone);
662
- this._p.copy(d.c).applyQuaternion(rel.q).add(rel.t);
663
- this._s.setScalar(d.scale);
664
- this._q.copy(rel.q).multiply(d.socketQ);
665
- d.own.uSocketInv.value.compose(this._p, this._q, this._s).invert();
666
- const still = this.look.lids === 'simple';
667
- this._qe.copy(rel.q).multiply(d.ballQ);
668
- if (!still) this._qe.multiply(this._q2.setFromAxisAngle(this._Y, gy)).multiply(this._q2.setFromAxisAngle(this._X, -gp));
669
- e.matrix.compose(this._p, this._qe, this._s);
670
- e.matrixWorld.copy(e.matrix);
671
- if (e.parent) e.matrixWorld.premultiply(e.parent.matrixWorld);
672
- }
673
- }
674
- }
675
-
676
- export { applyLook as setEyeLook };