@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,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 };