@volter/blender-engine 0.1.147 → 0.1.149

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 (245) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +4 -11
  3. package/browser/addons/vrm/LICENSE_(OPTION1)_MIT.txt +22 -0
  4. package/browser/addons/vrm/LICENSE_(OPTION2)_GPL-3_0.txt +674 -0
  5. package/browser/addons/vrm/LICENSE_MAIN.txt +1 -0
  6. package/browser/addons/vrm/README.txt +3 -0
  7. package/browser/addons/vrm/__init__.py +41 -0
  8. package/browser/addons/vrm/blender_manifest.toml +16 -0
  9. package/browser/addons/vrm/common/__init__.py +1 -0
  10. package/browser/addons/vrm/common/animation.py +58 -0
  11. package/browser/addons/vrm/common/blender_manifest.py +82 -0
  12. package/browser/addons/vrm/common/char.py +16 -0
  13. package/browser/addons/vrm/common/convert.py +287 -0
  14. package/browser/addons/vrm/common/convert_any.py +48 -0
  15. package/browser/addons/vrm/common/debug.py +40 -0
  16. package/browser/addons/vrm/common/deep.py +162 -0
  17. package/browser/addons/vrm/common/error_dialog.py +274 -0
  18. package/browser/addons/vrm/common/fs.py +71 -0
  19. package/browser/addons/vrm/common/gl.py +20 -0
  20. package/browser/addons/vrm/common/gltf.py +1149 -0
  21. package/browser/addons/vrm/common/human_bone_mapper/__init__.py +1 -0
  22. package/browser/addons/vrm/common/human_bone_mapper/biped_mapping.py +84 -0
  23. package/browser/addons/vrm/common/human_bone_mapper/cats_blender_plugin_fix_model_mapping.py +63 -0
  24. package/browser/addons/vrm/common/human_bone_mapper/human_bone_mapper.py +203 -0
  25. package/browser/addons/vrm/common/human_bone_mapper/microsoft_rocketbox_mapping.py +69 -0
  26. package/browser/addons/vrm/common/human_bone_mapper/mixamo_mapping.py +62 -0
  27. package/browser/addons/vrm/common/human_bone_mapper/mmd_mapping.py +115 -0
  28. package/browser/addons/vrm/common/human_bone_mapper/mpfb_mapping.py +120 -0
  29. package/browser/addons/vrm/common/human_bone_mapper/ready_player_me_mapping.py +65 -0
  30. package/browser/addons/vrm/common/human_bone_mapper/reallusion_mapping.py +94 -0
  31. package/browser/addons/vrm/common/human_bone_mapper/rigify_meta_rig_mapping.py +65 -0
  32. package/browser/addons/vrm/common/human_bone_mapper/structure_based_mapping.py +891 -0
  33. package/browser/addons/vrm/common/human_bone_mapper/unreal_mapping.py +62 -0
  34. package/browser/addons/vrm/common/human_bone_mapper/vrm_addon_mapping.py +112 -0
  35. package/browser/addons/vrm/common/human_bone_mapper/vroid_mapping.py +89 -0
  36. package/browser/addons/vrm/common/legacy_gltf.py +16 -0
  37. package/browser/addons/vrm/common/logger.py +84 -0
  38. package/browser/addons/vrm/common/mtoon0.blend +0 -0
  39. package/browser/addons/vrm/common/mtoon1.blend +0 -0
  40. package/browser/addons/vrm/common/mtoon1_outline.blend +0 -0
  41. package/browser/addons/vrm/common/mtoon_auto_setup.blend +0 -0
  42. package/browser/addons/vrm/common/mtoon_unversioned.py +62 -0
  43. package/browser/addons/vrm/common/ops/__init__.py +10 -0
  44. package/browser/addons/vrm/common/ops/export_scene.py +67 -0
  45. package/browser/addons/vrm/common/ops/icyp.py +53 -0
  46. package/browser/addons/vrm/common/ops/import_scene.py +57 -0
  47. package/browser/addons/vrm/common/ops/vrm.py +2380 -0
  48. package/browser/addons/vrm/common/ops/wm.py +167 -0
  49. package/browser/addons/vrm/common/preferences.py +362 -0
  50. package/browser/addons/vrm/common/progress.py +87 -0
  51. package/browser/addons/vrm/common/rotation.py +89 -0
  52. package/browser/addons/vrm/common/safe_removal.py +46 -0
  53. package/browser/addons/vrm/common/scene_watcher.py +230 -0
  54. package/browser/addons/vrm/common/shader.py +2243 -0
  55. package/browser/addons/vrm/common/shape_key_mapper/__init__.py +1 -0
  56. package/browser/addons/vrm/common/shape_key_mapper/arkit_mapping.py +168 -0
  57. package/browser/addons/vrm/common/shape_key_mapper/mmd_mapping.py +62 -0
  58. package/browser/addons/vrm/common/shape_key_mapper/ready_player_me_mapping.py +52 -0
  59. package/browser/addons/vrm/common/shape_key_mapper/vrchat_mapping.py +17 -0
  60. package/browser/addons/vrm/common/third_party_user_extension.py +214 -0
  61. package/browser/addons/vrm/common/transparent_z_write.blend +0 -0
  62. package/browser/addons/vrm/common/version.py +161 -0
  63. package/browser/addons/vrm/common/vrm0/__init__.py +1 -0
  64. package/browser/addons/vrm/common/vrm0/human_bone.py +662 -0
  65. package/browser/addons/vrm/common/vrm0/material_property.py +346 -0
  66. package/browser/addons/vrm/common/vrm1/__init__.py +1 -0
  67. package/browser/addons/vrm/common/vrm1/expression_preset.py +207 -0
  68. package/browser/addons/vrm/common/vrm1/human_bone.py +822 -0
  69. package/browser/addons/vrm/common/workspace.py +227 -0
  70. package/browser/addons/vrm/common/writable_context.py +77 -0
  71. package/browser/addons/vrm/documentation/en-us/animation/index.md +63 -0
  72. package/browser/addons/vrm/documentation/en-us/create-humanoid-vrm-from-scratch/index.md +131 -0
  73. package/browser/addons/vrm/documentation/en-us/create-simple-vrm-from-scratch/index.md +30 -0
  74. package/browser/addons/vrm/documentation/en-us/development/index.md +136 -0
  75. package/browser/addons/vrm/documentation/en-us/index.md +41 -0
  76. package/browser/addons/vrm/documentation/en-us/installation/index.md +152 -0
  77. package/browser/addons/vrm/documentation/en-us/material-mtoon/index.md +122 -0
  78. package/browser/addons/vrm/documentation/en-us/material-pbr/index.md +94 -0
  79. package/browser/addons/vrm/documentation/en-us/scripting-api/index.md +391 -0
  80. package/browser/addons/vrm/documentation/en-us/ui/export_scene.vrm/index.md +57 -0
  81. package/browser/addons/vrm/documentation/en-us/ui/export_scene.vrma/index.md +17 -0
  82. package/browser/addons/vrm/documentation/en-us/ui/import_scene.vrm/index.md +61 -0
  83. package/browser/addons/vrm/documentation/en-us/ui/import_scene.vrma/index.md +13 -0
  84. package/browser/addons/vrm/documentation/ja-jp/animation/index.md +57 -0
  85. package/browser/addons/vrm/documentation/ja-jp/create-humanoid-vrm-from-scratch/index.md +129 -0
  86. package/browser/addons/vrm/documentation/ja-jp/create-simple-vrm-from-scratch/index.md +29 -0
  87. package/browser/addons/vrm/documentation/ja-jp/development/index.md +137 -0
  88. package/browser/addons/vrm/documentation/ja-jp/index.md +37 -0
  89. package/browser/addons/vrm/documentation/ja-jp/installation/index.md +150 -0
  90. package/browser/addons/vrm/documentation/ja-jp/material-mtoon/index.md +113 -0
  91. package/browser/addons/vrm/documentation/ja-jp/material-pbr/index.md +90 -0
  92. package/browser/addons/vrm/documentation/ja-jp/scripting-api/index.md +389 -0
  93. package/browser/addons/vrm/documentation/ja-jp/ui/export_scene.vrm/index.md +55 -0
  94. package/browser/addons/vrm/documentation/ja-jp/ui/export_scene.vrma/index.md +19 -0
  95. package/browser/addons/vrm/documentation/ja-jp/ui/import_scene.vrm/index.md +56 -0
  96. package/browser/addons/vrm/documentation/ja-jp/ui/import_scene.vrma/index.md +14 -0
  97. package/browser/addons/vrm/editor/__init__.py +1 -0
  98. package/browser/addons/vrm/editor/extension.py +425 -0
  99. package/browser/addons/vrm/editor/extension_accessor.py +75 -0
  100. package/browser/addons/vrm/editor/handler.py +84 -0
  101. package/browser/addons/vrm/editor/khr_character/__init__.py +1 -0
  102. package/browser/addons/vrm/editor/khr_character/panel.py +69 -0
  103. package/browser/addons/vrm/editor/khr_character/property_group.py +27 -0
  104. package/browser/addons/vrm/editor/khr_xmp_json_ld/__init__.py +1 -0
  105. package/browser/addons/vrm/editor/khr_xmp_json_ld/ops.py +464 -0
  106. package/browser/addons/vrm/editor/khr_xmp_json_ld/panel.py +136 -0
  107. package/browser/addons/vrm/editor/khr_xmp_json_ld/property_group.py +83 -0
  108. package/browser/addons/vrm/editor/khr_xmp_json_ld/ui_list.py +198 -0
  109. package/browser/addons/vrm/editor/make_armature.py +709 -0
  110. package/browser/addons/vrm/editor/menu.py +147 -0
  111. package/browser/addons/vrm/editor/migration.py +297 -0
  112. package/browser/addons/vrm/editor/mtoon1/__init__.py +1 -0
  113. package/browser/addons/vrm/editor/mtoon1/handler.py +20 -0
  114. package/browser/addons/vrm/editor/mtoon1/migration.py +536 -0
  115. package/browser/addons/vrm/editor/mtoon1/ops.py +304 -0
  116. package/browser/addons/vrm/editor/mtoon1/panel.py +442 -0
  117. package/browser/addons/vrm/editor/mtoon1/property_group.py +4977 -0
  118. package/browser/addons/vrm/editor/mtoon1/scene_watcher.py +381 -0
  119. package/browser/addons/vrm/editor/node_constraint1/__init__.py +1 -0
  120. package/browser/addons/vrm/editor/node_constraint1/panel.py +317 -0
  121. package/browser/addons/vrm/editor/node_constraint1/property_group.py +25 -0
  122. package/browser/addons/vrm/editor/ops.py +471 -0
  123. package/browser/addons/vrm/editor/panel.py +315 -0
  124. package/browser/addons/vrm/editor/property_group.py +888 -0
  125. package/browser/addons/vrm/editor/search.py +682 -0
  126. package/browser/addons/vrm/editor/spring_bone1/__init__.py +1 -0
  127. package/browser/addons/vrm/editor/spring_bone1/handler.py +994 -0
  128. package/browser/addons/vrm/editor/spring_bone1/menu.py +246 -0
  129. package/browser/addons/vrm/editor/spring_bone1/migration.py +86 -0
  130. package/browser/addons/vrm/editor/spring_bone1/ops.py +1679 -0
  131. package/browser/addons/vrm/editor/spring_bone1/panel.py +605 -0
  132. package/browser/addons/vrm/editor/spring_bone1/property_group.py +1457 -0
  133. package/browser/addons/vrm/editor/spring_bone1/ui_list.py +408 -0
  134. package/browser/addons/vrm/editor/subscription.py +123 -0
  135. package/browser/addons/vrm/editor/t_pose.py +713 -0
  136. package/browser/addons/vrm/editor/validation.py +1271 -0
  137. package/browser/addons/vrm/editor/vrm0/__init__.py +1 -0
  138. package/browser/addons/vrm/editor/vrm0/gizmo_group.py +63 -0
  139. package/browser/addons/vrm/editor/vrm0/handler.py +23 -0
  140. package/browser/addons/vrm/editor/vrm0/menu.py +145 -0
  141. package/browser/addons/vrm/editor/vrm0/migration.py +761 -0
  142. package/browser/addons/vrm/editor/vrm0/ops.py +2145 -0
  143. package/browser/addons/vrm/editor/vrm0/panel.py +1333 -0
  144. package/browser/addons/vrm/editor/vrm0/property_group.py +1818 -0
  145. package/browser/addons/vrm/editor/vrm0/ui_list.py +688 -0
  146. package/browser/addons/vrm/editor/vrm1/__init__.py +1 -0
  147. package/browser/addons/vrm/editor/vrm1/handler.py +28 -0
  148. package/browser/addons/vrm/editor/vrm1/menu.py +76 -0
  149. package/browser/addons/vrm/editor/vrm1/migration.py +321 -0
  150. package/browser/addons/vrm/editor/vrm1/ops.py +2679 -0
  151. package/browser/addons/vrm/editor/vrm1/panel.py +1243 -0
  152. package/browser/addons/vrm/editor/vrm1/property_group.py +2087 -0
  153. package/browser/addons/vrm/editor/vrm1/scene_watcher.py +90 -0
  154. package/browser/addons/vrm/editor/vrm1/ui_list.py +533 -0
  155. package/browser/addons/vrm/exporter/__init__.py +1 -0
  156. package/browser/addons/vrm/exporter/abstract_base_vrm_exporter.py +628 -0
  157. package/browser/addons/vrm/exporter/export_scene.py +930 -0
  158. package/browser/addons/vrm/exporter/gltf2_export_user_extension.py +45 -0
  159. package/browser/addons/vrm/exporter/khr_character_exporter.py +34 -0
  160. package/browser/addons/vrm/exporter/vrm0_exporter.py +4020 -0
  161. package/browser/addons/vrm/exporter/vrm1_exporter.py +3682 -0
  162. package/browser/addons/vrm/exporter/vrm_animation_exporter.py +1035 -0
  163. package/browser/addons/vrm/external/__init__.py +1 -0
  164. package/browser/addons/vrm/external/auto_rig_pro_support.py +3 -0
  165. package/browser/addons/vrm/external/io_scene_gltf2_support.py +418 -0
  166. package/browser/addons/vrm/external/rigify_support.py +1 -0
  167. package/browser/addons/vrm/importer/__init__.py +1 -0
  168. package/browser/addons/vrm/importer/abstract_base_vrm_importer.py +1548 -0
  169. package/browser/addons/vrm/importer/file_handler.py +34 -0
  170. package/browser/addons/vrm/importer/gltf2_import_user_extension.py +268 -0
  171. package/browser/addons/vrm/importer/import_scene.py +646 -0
  172. package/browser/addons/vrm/importer/license_validation.py +198 -0
  173. package/browser/addons/vrm/importer/vrm0_importer.py +1723 -0
  174. package/browser/addons/vrm/importer/vrm1_importer.py +2014 -0
  175. package/browser/addons/vrm/importer/vrm_animation_importer.py +763 -0
  176. package/browser/addons/vrm/importer/vrm_diff.py +316 -0
  177. package/browser/addons/vrm/locale/__init__.py +1 -0
  178. package/browser/addons/vrm/locale/ja_jp.py +826 -0
  179. package/browser/addons/vrm/locale/ruff.toml +8 -0
  180. package/browser/addons/vrm/locale/translation_dictionary.py +10 -0
  181. package/browser/addons/vrm/locale/zh_hans.py +928 -0
  182. package/browser/addons/vrm/registration.py +670 -0
  183. package/browser/addons/vrm.json +18 -0
  184. package/browser/blender-emscripten-engine.mts +60 -128
  185. package/browser/blender-engine.mts +20 -186
  186. package/browser/blender-wali-engine.mts +35 -81
  187. package/browser/describe-thrown.mts +3 -6
  188. package/browser/document-chunks.mts +4 -16
  189. package/browser/gltf_export.py +3161 -0
  190. package/browser/heap-growth.mts +23 -0
  191. package/browser/index.ts +2 -0
  192. package/browser/protocol.ts +119 -35
  193. package/browser/pull-job.mts +2 -10
  194. package/browser/rna.ts +190 -73
  195. package/browser/runtime.ts +197 -82
  196. package/browser/session-addons.mts +33 -0
  197. package/browser/session-frame.mts +5 -24
  198. package/browser/session.py +1338 -1427
  199. package/browser/three/attach-presenter.ts +11 -39
  200. package/browser/three/blender-base64.ts +1 -36
  201. package/browser/three/blender-corner-normals.ts +1 -23
  202. package/browser/three/blender-display-shaders.json +1 -1
  203. package/browser/three/blender-display-shaders.lut +0 -0
  204. package/browser/three/blender-display-transform.ts +2 -1
  205. package/browser/three/blender-frame-columns.ts +5 -13
  206. package/browser/three/blender-graph-material.ts +18 -3
  207. package/browser/three/blender-node-graph.ts +10 -34
  208. package/browser/three/blender-physical-material.ts +2 -31
  209. package/browser/three/blender-runtime-armature.ts +7 -60
  210. package/browser/three/blender-runtime-frame.ts +9 -23
  211. package/browser/three/blender-runtime-geometry.ts +22 -26
  212. package/browser/three/blender-runtime-lighting.ts +48 -178
  213. package/browser/three/blender-runtime-shadows.ts +187 -1
  214. package/browser/three/blender-runtime-skeleton.ts +53 -1
  215. package/browser/three/blender-runtime-view.ts +505 -283
  216. package/browser/three/blender-runtime-weights.ts +9 -72
  217. package/browser/three/blender-sky.ts +13 -33
  218. package/browser/three/blender-triangulate.ts +7 -35
  219. package/browser/three/blender-viewport-sampling.ts +235 -0
  220. package/browser/three/blender-workbench-material.ts +25 -29
  221. package/browser/three/blender-workbench-outline.ts +199 -0
  222. package/browser/three/blender-world-volume.ts +62 -10
  223. package/browser/three/presenter.ts +10 -26
  224. package/browser/three/release.ts +5 -21
  225. package/browser/three/sky-precompute-worker.ts +1 -18
  226. package/browser/three/sky-worker.ts +5 -23
  227. package/browser/three/world-field-sampler.ts +12 -26
  228. package/browser/volter_three.py +9 -105
  229. package/browser/worker.ts +156 -151
  230. package/package.json +5 -2
  231. package/wasm/BUNDLE.json +17 -17
  232. package/wasm/DEPENDENCY-LICENSES.txt +14 -0
  233. package/wasm/blender_browser.data.br +0 -0
  234. package/wasm/blender_browser.js +1 -1
  235. package/wasm/blender_browser.wasm.br +0 -0
  236. package/browser/frame-transfer.mts +0 -19
  237. package/browser/three/agx-base-srgb.lut +0 -0
  238. package/browser/three/agx-look-medium-high-contrast.lut +0 -0
  239. package/browser/three/agx-look-punchy.lut +0 -0
  240. package/browser/three/blender-agx.ts +0 -235
  241. package/browser/three/blender-display-lut.ts +0 -145
  242. package/browser/three/blender-filmic.ts +0 -49
  243. package/browser/three/blender-standard.ts +0 -68
  244. package/browser/three/blender-world-visibility.ts +0 -278
  245. package/browser/three/filmic-srgb.lut +0 -0
@@ -0,0 +1,199 @@
1
+ /**
2
+ * BLENDER'S SOLID OBJECT OUTLINE (`View3DShading.show_object_outline`), as Workbench draws it:
3
+ * every object is drawn into an object-id buffer (the background is id 0), and each pixel is
4
+ * darkened toward `object_outline_color` by the share of its four neighbours, one pixel away
5
+ * (`ui_scale` pixels), whose id differs from its own:
6
+ *
7
+ * opacity = 1 - 0.25 * (number of the four neighbours with the pixel's own id)
8
+ * colour = outline_colour * opacity + colour * (1 - opacity) (premultiplied alpha over)
9
+ *
10
+ * (`workbench_effect_outline_frag.glsl`). So a silhouette against the background and the seam
11
+ * between two touching objects both carry a band on each side. It is drawn over the shaded frame
12
+ * in scene-linear, before the view transform, as Blender composites it.
13
+ *
14
+ * One object, one id: a mesh the frame presents is one Blender object or one instance of a
15
+ * collection, each its own draw in Blender's id buffer.
16
+ */
17
+ import * as THREE from 'three';
18
+ import { FullScreenQuad } from 'three/addons/postprocessing/Pass.js';
19
+
20
+ const ID_VERTEX = /* glsl */ `
21
+ #include <common>
22
+ #include <batching_pars_vertex>
23
+ #include <morphtarget_pars_vertex>
24
+ #include <skinning_pars_vertex>
25
+ #include <logdepthbuf_pars_vertex>
26
+ #include <clipping_planes_pars_vertex>
27
+ void main() {
28
+ #include <batching_vertex>
29
+ #include <skinbase_vertex>
30
+ #include <morphinstance_vertex>
31
+ #include <begin_vertex>
32
+ #include <morphtarget_vertex>
33
+ #include <skinning_vertex>
34
+ #include <project_vertex>
35
+ #include <logdepthbuf_vertex>
36
+ #include <clipping_planes_vertex>
37
+ }
38
+ `;
39
+
40
+ const ID_FRAGMENT = /* glsl */ `
41
+ uniform vec3 objectId;
42
+ #include <logdepthbuf_pars_fragment>
43
+ #include <clipping_planes_pars_fragment>
44
+ void main() {
45
+ #include <clipping_planes_fragment>
46
+ #include <logdepthbuf_fragment>
47
+ gl_FragColor = vec4(objectId, 1.0);
48
+ }
49
+ `;
50
+
51
+ /** The outline itself, over the frame: Blender's four-neighbour test on the id buffer. */
52
+ const OUTLINE_FRAGMENT = /* glsl */ `
53
+ precision highp float;
54
+ uniform sampler2D ids;
55
+ uniform int reach;
56
+ uniform vec3 outlineColor;
57
+ in vec2 vUv;
58
+ out vec4 outputColor;
59
+ vec4 idAt(ivec2 p, ivec2 size) { return texelFetch(ids, clamp(p, ivec2(0), size - 1), 0); }
60
+ void main() {
61
+ ivec2 size = textureSize(ids, 0);
62
+ ivec2 p = ivec2(vUv * vec2(size));
63
+ vec4 centre = idAt(p, size);
64
+ float same = 0.0;
65
+ same += all(equal(idAt(p + ivec2(0, reach), size), centre)) ? 1.0 : 0.0;
66
+ same += all(equal(idAt(p - ivec2(0, reach), size), centre)) ? 1.0 : 0.0;
67
+ same += all(equal(idAt(p + ivec2(reach, 0), size), centre)) ? 1.0 : 0.0;
68
+ same += all(equal(idAt(p - ivec2(reach, 0), size), centre)) ? 1.0 : 0.0;
69
+ float opacity = 1.0 - 0.25 * same;
70
+ outputColor = vec4(outlineColor * opacity, opacity);
71
+ }
72
+ `;
73
+
74
+ export class WorkbenchOutline {
75
+ private target: THREE.WebGLRenderTarget | null = null;
76
+ private readonly ids = new THREE.ShaderMaterial({
77
+ name: 'Blender object id',
78
+ uniforms: { objectId: { value: new THREE.Color() } },
79
+ vertexShader: ID_VERTEX,
80
+ fragmentShader: ID_FRAGMENT,
81
+ });
82
+ private readonly composite = new THREE.RawShaderMaterial({
83
+ name: 'Blender object outline',
84
+ glslVersion: THREE.GLSL3,
85
+ uniforms: { ids: { value: null }, reach: { value: 1 }, outlineColor: { value: new THREE.Color() } },
86
+ vertexShader: `precision highp float; in vec3 position; in vec2 uv; out vec2 vUv;
87
+ void main(){vUv=uv;gl_Position=vec4(position,1.0);}`,
88
+ fragmentShader: OUTLINE_FRAGMENT,
89
+ depthTest: false,
90
+ depthWrite: false,
91
+ transparent: true,
92
+ blending: THREE.CustomBlending,
93
+ blendSrc: THREE.OneFactor,
94
+ blendDst: THREE.OneMinusSrcAlphaFactor,
95
+ blendSrcAlpha: THREE.OneFactor,
96
+ blendDstAlpha: THREE.OneMinusSrcAlphaFactor,
97
+ });
98
+ private readonly quad = new FullScreenQuad(this.composite);
99
+ private readonly idOf = new Map<THREE.Object3D, number>();
100
+
101
+ constructor() {
102
+ // Each draw carries its own object's id; the material is shared by every draw of the pass.
103
+ this.ids.onBeforeRender = (_renderer, _scene, _camera, _geometry, object) => {
104
+ const id = this.idOf.get(object) ?? 0;
105
+ (this.ids.uniforms['objectId']!.value as THREE.Color).setRGB(
106
+ (id & 0xff) / 255, ((id >> 8) & 0xff) / 255, ((id >> 16) & 0xff) / 255, THREE.LinearSRGBColorSpace);
107
+ this.ids.uniformsNeedUpdate = true;
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Draw the outline over what the renderer's CURRENT target holds (the frame just drawn through
113
+ * `camera`), `reach` target pixels out. `objects` are the frame's presented objects in its
114
+ * order, each drawn with its own id where `drawn(object)` says it is drawn; `internal` are the
115
+ * presenter's own draws standing in for some of them (batches), hidden for the id pass.
116
+ */
117
+ draw(
118
+ renderer: THREE.WebGLRenderer,
119
+ scene: THREE.Scene,
120
+ camera: THREE.Camera,
121
+ objects: Iterable<THREE.Object3D>,
122
+ drawn: (object: THREE.Object3D) => boolean,
123
+ internal: readonly THREE.Object3D[],
124
+ color: readonly [number, number, number],
125
+ reach: number,
126
+ ): void {
127
+ const output = renderer.getRenderTarget();
128
+ const size = output ? new THREE.Vector2(output.width, output.height) : renderer.getDrawingBufferSize(new THREE.Vector2());
129
+ if (size.x < 1 || size.y < 1) return;
130
+ this.target ??= new THREE.WebGLRenderTarget(size.x, size.y, {
131
+ minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, generateMipmaps: false, depthBuffer: true,
132
+ });
133
+ if (this.target.width !== size.x || this.target.height !== size.y) this.target.setSize(size.x, size.y);
134
+ this.idOf.clear();
135
+ const layers = new Map<THREE.Object3D, number>();
136
+ const shown = new Map<THREE.Object3D, boolean>();
137
+ let next = 1;
138
+ for (const object of objects) {
139
+ if (!(object as THREE.Mesh).isMesh) continue;
140
+ this.idOf.set(object, next++);
141
+ // A batched member is drawn by its batch, with its own layer cleared; the id pass draws
142
+ // the member itself, so it carries its own id.
143
+ layers.set(object, object.layers.mask);
144
+ object.layers.mask = drawn(object) ? 1 : 0;
145
+ }
146
+ for (const object of internal) {
147
+ shown.set(object, object.visible);
148
+ object.visible = false;
149
+ }
150
+ const background = scene.background;
151
+ const override = scene.overrideMaterial;
152
+ const autoClear = renderer.autoClear;
153
+ const shadows = renderer.shadowMap.enabled;
154
+ const clearColor = renderer.getClearColor(new THREE.Color());
155
+ const clearAlpha = renderer.getClearAlpha();
156
+ const cameraLayers = camera.layers.mask;
157
+ try {
158
+ scene.background = null;
159
+ scene.overrideMaterial = this.ids;
160
+ renderer.autoClear = false;
161
+ renderer.shadowMap.enabled = false;
162
+ camera.layers.mask = 1;
163
+ renderer.setRenderTarget(this.target);
164
+ renderer.setClearColor(0x000000, 0);
165
+ renderer.clear(true, true, false);
166
+ renderer.render(scene, camera);
167
+ } finally {
168
+ scene.background = background;
169
+ scene.overrideMaterial = override;
170
+ renderer.autoClear = autoClear;
171
+ renderer.shadowMap.enabled = shadows;
172
+ renderer.setClearColor(clearColor, clearAlpha);
173
+ camera.layers.mask = cameraLayers;
174
+ for (const [object, mask] of layers) object.layers.mask = mask;
175
+ for (const [object, visible] of shown) object.visible = visible;
176
+ renderer.setRenderTarget(output);
177
+ }
178
+ this.composite.uniforms['ids']!.value = this.target.texture;
179
+ this.composite.uniforms['reach']!.value = Math.max(1, Math.round(reach));
180
+ (this.composite.uniforms['outlineColor']!.value as THREE.Color).setRGB(color[0], color[1], color[2], THREE.LinearSRGBColorSpace);
181
+ // Laid OVER the frame: a renderer that clears before every draw (`autoClear`, on wherever no
182
+ // composer has turned it off) would wipe the frame first and leave the outline alone.
183
+ renderer.autoClear = false;
184
+ try {
185
+ this.quad.render(renderer);
186
+ } finally {
187
+ renderer.autoClear = autoClear;
188
+ }
189
+ }
190
+
191
+ dispose(): void {
192
+ this.target?.dispose();
193
+ this.target = null;
194
+ this.ids.dispose();
195
+ this.composite.dispose();
196
+ this.quad.dispose();
197
+ this.idOf.clear();
198
+ }
199
+ }
@@ -131,6 +131,7 @@ export class WorldVolumePass {
131
131
  };
132
132
  let declarations = '', lighting = '';
133
133
  let lightIndex = 0, samplers = 2;
134
+ const shadowKinds = new Set<string>();
134
135
  scene.traverseVisible(object => {
135
136
  if (!(object instanceof THREE.PointLight || object instanceof THREE.SpotLight || object instanceof THREE.RectAreaLight)) return;
136
137
  const light = object, id = lightIndex++;
@@ -145,18 +146,26 @@ export class WorldVolumePass {
145
146
  declarations += `uniform vec3 ld${id}; uniform vec2 cone${id};\n`;
146
147
  spot = `smoothstep(cone${id}.x, max(cone${id}.x+1e-6,cone${id}.y),dot(-L,ld${id}))`;
147
148
  }
148
- if (!(light instanceof THREE.RectAreaLight) && light.castShadow && light.shadow.map) {
149
+ const depthMap = light instanceof THREE.RectAreaLight || !light.castShadow ? null : light.shadow.map?.depthTexture ?? null;
150
+ if (!(light instanceof THREE.RectAreaLight) && depthMap) {
151
+ // THE SHADOW MAP AS THREE.JS (r182+) KEEPS IT: the light's depth, in `shadow.map.depthTexture` (a cube of
152
+ // depths for a point light). With PCF it is a comparison texture (hardware depth compare, sampled through
153
+ // a shadow sampler), otherwise plain depths. The pass samples it with functions of its own: three's
154
+ // `getShadow`/`getPointShadow` exist only for the lights its own programs count, and their signatures
155
+ // follow its internals.
149
156
  if (++samplers > renderer.capabilities.maxTextures) throw new Error('World volume shadow samplers exceed this GPU texture-unit limit');
150
- uniforms[`sm${id}`] = {value: light.shadow.map.texture};
157
+ const point = light instanceof THREE.PointLight, compare = depthMap.compareFunction !== null;
158
+ uniforms[`sm${id}`] = {value: depthMap};
151
159
  uniforms[`sx${id}`] = {value: light.shadow.matrix};
152
- uniforms[`ss${id}`] = {value: light.shadow.mapSize};
153
160
  uniforms[`sb${id}`] = {value: light.shadow.bias};
154
161
  uniforms[`sn${id}`] = {value: light.shadow.camera.near};
155
162
  uniforms[`sf${id}`] = {value: light.shadow.camera.far};
156
- declarations += `uniform sampler2D sm${id}; uniform mat4 sx${id}; uniform vec2 ss${id}; uniform float sb${id},sn${id},sf${id};\n`;
157
- visibility = light instanceof THREE.PointLight
158
- ? `getPointShadow(sm${id},ss${id},1.0,sb${id},1.0,sx${id}*vec4(p,1.0),sn${id},sf${id})`
159
- : `getShadow(sm${id},ss${id},1.0,sb${id},1.0,sx${id}*vec4(p,1.0))`;
163
+ const sampler = point ? (compare ? 'samplerCubeShadow' : 'samplerCube') : (compare ? 'sampler2DShadow' : 'sampler2D');
164
+ declarations += `uniform ${sampler} sm${id}; uniform mat4 sx${id}; uniform float sb${id},sn${id},sf${id};\n`;
165
+ visibility = point
166
+ ? `cubeShadow(sm${id},sb${id},sn${id},sf${id},(sx${id}*vec4(p,1.0)).xyz)`
167
+ : `planeShadow(sm${id},sb${id},sx${id}*vec4(p,1.0))`;
168
+ shadowKinds.add(sampler);
160
169
  }
161
170
  if (light instanceof THREE.RectAreaLight) {
162
171
  const rotation = new THREE.Matrix4().extractRotation(light.matrixWorld);
@@ -189,17 +198,60 @@ export class WorldVolumePass {
189
198
  const positive = this.medium.extinction.toArray().filter(v => v > 0);
190
199
  uniforms['range'] = {value: positive.length ? 20/Math.min(...positive) : 1};
191
200
  this.material?.dispose();
201
+ // Each sampler kind used gets its function; a reversed depth buffer (three's `reversedDepthBuffer`) writes
202
+ // and compares depths the other way round, as three's own shadow functions handle it.
203
+ const reversed = renderer.state.buffers.depth.getReversed();
204
+ let shadowFunctions = reversed ? '#define VOLUME_REVERSED_DEPTH\n' : '';
205
+ const plane = (sampler: string, test: string) => `float planeShadow(${sampler} map,float bias,vec4 c){
206
+ if(c.w<=0.) return 1.; c.xyz/=c.w; ${test} }\n`;
207
+ if (shadowKinds.has('sampler2DShadow'))
208
+ shadowFunctions += plane('sampler2DShadow', `c.z+=bias;
209
+ if(c.x<0.||c.x>1.||c.y<0.||c.y>1.||c.z>1.) return 1.;
210
+ return texture(map,vec3(c.xy,c.z));`);
211
+ if (shadowKinds.has('sampler2D'))
212
+ shadowFunctions += plane('sampler2D', `
213
+ #ifdef VOLUME_REVERSED_DEPTH
214
+ c.z-=bias;
215
+ #else
216
+ c.z+=bias;
217
+ #endif
218
+ if(c.x<0.||c.x>1.||c.y<0.||c.y>1.||c.z>1.) return 1.;
219
+ float stored=texture(map,c.xy).r;
220
+ #ifdef VOLUME_REVERSED_DEPTH
221
+ return step(stored,c.z);
222
+ #else
223
+ return step(c.z,stored);
224
+ #endif`);
225
+ // A point light's cube holds each face's perspective depth along that face's axis (the largest component
226
+ // of the light-to-point vector), between the shadow camera's near and far.
227
+ const cube = (sampler: string, test: string) => `float cubeShadow(${sampler} map,float bias,float near,float far,vec3 v){
228
+ vec3 a=abs(v); float z=max(max(a.x,a.y),a.z);
229
+ if(z>far||z<near) return 1.;
230
+ ${test} }\n`;
231
+ if (shadowKinds.has('samplerCubeShadow'))
232
+ shadowFunctions += cube('samplerCubeShadow', `
233
+ #ifdef VOLUME_REVERSED_DEPTH
234
+ float dp=near*(far-z)/(z*(far-near))-bias;
235
+ #else
236
+ float dp=far*(z-near)/(z*(far-near))+bias;
237
+ #endif
238
+ return texture(map,vec4(normalize(v),dp));`);
239
+ if (shadowKinds.has('samplerCube'))
240
+ shadowFunctions += cube('samplerCube', `float dp=far*(z-near)/(z*(far-near))+bias;
241
+ float stored=texture(map,normalize(v)).r;
242
+ #ifdef VOLUME_REVERSED_DEPTH
243
+ stored=1.-stored;
244
+ #endif
245
+ return step(dp,stored);`);
192
246
  this.material = new THREE.ShaderMaterial({uniforms, depthTest: false, depthWrite: false, toneMapped: false,
193
- defines: {USE_SHADOWMAP: '', NUM_DIR_LIGHT_SHADOWS: 0, NUM_SPOT_LIGHT_SHADOWS: 0, NUM_POINT_LIGHT_SHADOWS: 0, NUM_SPOT_LIGHT_COORDS: 0},
194
247
  vertexShader: 'varying vec2 vUv; void main(){vUv=uv;gl_Position=vec4(position.xy,0.,1.);}',
195
248
  fragmentShader: `
196
249
  #include <common>
197
- #include <packing>
198
- #include <shadowmap_pars_fragment>
199
250
  varying vec2 vUv; uniform sampler2D surface,depth;
200
251
  uniform mat4 inverseProjection,cameraWorld; uniform bool orthographic;
201
252
  uniform vec3 sigma,emission; uniform float range;
202
253
  ${declarations}
254
+ ${shadowFunctions}
203
255
  vec3 phase(float c){${phase} return result;}
204
256
  vec3 incident(vec3 p,vec3 ray){vec3 sum=vec3(0.);${lighting}return sum;}
205
257
  float integral(float s,float d){float x=s*d;return x<.001 ? d*(1.-x*.5+x*x/6.) : (1.-exp(-x))/s;}
@@ -1,27 +1,16 @@
1
1
  import { createBlenderDisplayTransform, blenderDisplaySettingsForRender } from './blender-display-transform';
2
2
  /**
3
- * THE PRESENTER FOR A HOST WITHOUT A STAGE (WS-AC, cut 1b).
4
- *
5
- * "Our Blender renders with three.js" is a translation (`./blender-runtime-view`
6
- * and the modules beside it) plus one act: point a camera at the translated
7
- * scene, render into a scene-linear target, read the pixels, run Blender's
8
- * display transform, answer. The editor performs that act with its own stage
9
- * and renderer (`packages/editor-blender/host/blender-runtime-host.ts`), which is why
10
- * the editor never imports THIS file. A host that has no renderer — a bare
11
- * page with `@volter/browser-wali`, an Emscripten page, the substrate
12
- * workbench — attaches one of these to the Blender it started, and the guest's
13
- * `bpy.ops.render.render` comes back as a three.js photograph exactly as it
14
- * does in the editor.
15
- *
16
- * What it does NOT do, on purpose: overlays (modeling chrome, hidden for a
17
- * render anyway), selection outlines, the editor's supersampled `captureImage`
18
- * for the Standard transform. Every transform here goes through the linear
19
- * capture and the same OCIO GPU display processors the editor uses.
3
+ * Present Blender frames for a host without an editor stage. Translate the scene through
4
+ * BlenderRuntimeView, render it with a dedicated camera into a scene-linear target, then apply
5
+ * Blender’s OCIO display transform and return the photograph. Editor hosts wrap the same
6
+ * translation with their own stage and renderer. This presenter supplies no modeling overlays or
7
+ * selection outlines.
20
8
  */
21
9
  import * as THREE from 'three';
22
10
  import type { CaptureRequest } from '../protocol';
23
11
  import type { PresentAnswer } from '../runtime';
24
12
  import { BlenderRuntimeView } from './blender-runtime-view';
13
+ import { bytesFromBase64 } from './blender-base64';
25
14
 
26
15
  export interface PresenterOptions {
27
16
  /** The canvas to render into; an OffscreenCanvas of 1x1 when absent. */
@@ -81,7 +70,8 @@ export function createPresenter(options: PresenterOptions = {}): Presenter {
81
70
  const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
82
71
  renderer.outputColorSpace = THREE.SRGBColorSpace;
83
72
  renderer.shadowMap.enabled = true;
84
- renderer.shadowMap.type = THREE.PCFSoftShadowMap;
73
+ // PCFShadowMap supplies the soft-shadow filter supported by this three.js release.
74
+ renderer.shadowMap.type = THREE.PCFShadowMap;
85
75
  renderer.setPixelRatio(1);
86
76
  const near = options.near ?? 0.1;
87
77
  const far = options.far ?? 1000;
@@ -107,11 +97,7 @@ export function createPresenter(options: PresenterOptions = {}): Presenter {
107
97
  view: BlenderRuntimeView, scene: THREE.Scene, frame: unknown, description: unknown,
108
98
  capture?: CaptureRequest,
109
99
  ): Promise<PresentAnswer> {
110
- const applied = view.applyFrame(frame) as { held?: unknown } | null | undefined;
111
- const held =
112
- typeof applied === 'object' && applied !== null && 'held' in applied
113
- ? (applied.held as { session: string; revision: number } | null)
114
- : null;
100
+ const { held } = view.applyFrame(frame);
115
101
  view.recordPresentation(description);
116
102
  const answer = (photograph?: unknown): PresentAnswer => ({
117
103
  ...(photograph === undefined ? {} : { capture: photograph }),
@@ -233,8 +219,6 @@ export function createPresenter(options: PresenterOptions = {}): Presenter {
233
219
 
234
220
  /** A composited frame's scene-linear half floats, as `linearInput` carries them. */
235
221
  function halfFloatFrame(base64: string): Uint16Array {
236
- const binary = atob(base64);
237
- const bytes = new Uint8Array(binary.length);
238
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
222
+ const bytes = bytesFromBase64(base64);
239
223
  return new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength >> 1);
240
224
  }
@@ -1,25 +1,9 @@
1
1
  /**
2
- * THE TRANSLATION, AS OUR BLENDER'S RELEASE SHIPS IT (WS-AC, cut 4).
3
- *
4
- * "Our Blender renders with three.js" is one implementation, built once at
5
- * release time out of this package's source into ONE self-contained ES module
6
- * (`blender-three.mjs`, three inlined, the display tables beside it) and
7
- * delivered wherever the wasm is delivered. A host fetches it from the
8
- * artifact base next to `blender.wasm`, imports it, and attaches ONE presenter
9
- * to the Blender it started:
10
- *
11
- * const { createPresenter, attachPresenter } = await import(url);
12
- * const detach = attachPresenter(filesystem, "/tmp/volter-presenter", createPresenter());
13
- *
14
- * NO HOST BUNDLES THIS. The module is GPL because it is part of the Blender
15
- * release; a host loads it the way it loads the wasm -- by URL, under a pinned
16
- * hash -- and its own build graph never touches this package. The editor is
17
- * the one consumer that does NOT load this file: it imports the same source
18
- * with its own three and wraps its own document around it, because two copies
19
- * of three on one page is the thing this arrangement exists to prevent.
20
- *
21
- * `vite.release.config.ts` beside this package's manifest is the build;
22
- * `npm run build:blender-three -w @volter/blender-engine` runs it.
2
+ * Release entry for the self-contained Blender three.js presenter module. The Blender release
3
+ * delivers it beside the Wasm artifact, with three.js inlined and display tables beside it. Hosts
4
+ * load the pinned module URL and attach createPresenter() through attachPresenter(). The editor
5
+ * imports the same source using its shared three.js instance and its own document stage. Build with
6
+ * vite.release.config.ts or build:blender-three.
23
7
  */
24
8
  export { attachPresenter } from './attach-presenter';
25
9
  export type { AttachablePresenter, PresenterFileSystem } from './attach-presenter';
@@ -1,21 +1,4 @@
1
- /**
2
- * The sky precompute, in a Worker.
3
- *
4
- * `precomputeSkyTexture` is 512x256 texels at 64 in-scattering steps each and
5
- * MEASURED 15.4 seconds. On the page's thread that is not "slow", it is a FROZEN
6
- * EDITOR -- the owner saw exactly that, and a spinner could not have helped
7
- * because a blocked main thread cannot paint one.
8
- *
9
- * This module is the whole worker: parameters in, one Float32Array out,
10
- * TRANSFERRED rather than copied. It names no DOM and no renderer, which is what
11
- * lets it be a worker at all; `blender-sky.ts` keeps the arithmetic.
12
- *
13
- * WHAT MAKES THIS DIFFERENT FROM THE ATTEMPT THAT WAS REVERTED (#6609): nothing
14
- * here hands out a half-built texture. The worker answers with NUMBERS, the
15
- * caller primes `blender-sky`'s texture cache with them, and only then does
16
- * anything build a texture -- and `pendingWorld`/`worldReady()` hold the capture
17
- * until it has. See `sky-worker.ts` and `WorldBackground.apply`.
18
- */
1
+ /** Precompute sky texels in a worker and transfer the Float32Array. The caller primes the sky cache before constructing textures, and WorldBackground.ready holds captures until it is complete. */
19
2
  import { precomputeSkyTexture, type SkyParameters } from './blender-sky';
20
3
 
21
4
  export interface SkyWorkerRequest {
@@ -1,17 +1,5 @@
1
- /**
2
- * The page's side of the sky worker: one long-lived worker, one promise per sky.
3
- *
4
- * WHY A CLIENT MODULE AND NOT A `new Worker` AT THE CALL SITE: the worker is
5
- * built once and reused, because constructing one costs a module graph fetch and
6
- * a sky world re-derives on every sun edit. Requests are keyed by id so several
7
- * skies (a world split by `Is Camera Ray` has two) ride one worker.
8
- *
9
- * The type-only import of the worker module is LOAD-BEARING in two ways. It is
10
- * the contract for what crosses the boundary, and it is also how the probe
11
- * project's capability sync finds the worker file at all: that sync follows
12
- * `from './x'` spellings, and `new URL('./x.ts', import.meta.url)` is not one.
13
- * Delete the import and the worker silently stops being copied.
14
- */
1
+ /** Share one long-lived worker across sky requests, keyed by id. The worker request and
2
+ * response types define the transfer contract. */
15
3
  import { precomputeSkyTexture, type SkyParameters } from './blender-sky';
16
4
  import type { SkyWorkerRequest, SkyWorkerResponse } from './sky-precompute-worker';
17
5
 
@@ -46,7 +34,7 @@ function ensureWorker(): Worker | null {
46
34
  else pending.reject(new Error(error ?? 'sky worker returned nothing'));
47
35
  };
48
36
  // A worker that dies takes every outstanding sky with it. Failing them LOUDLY
49
- // is the point: `worldReady()` propagates, so a capture reports the error
37
+ // is the point: `WorldBackground.ready()` propagates, so a capture reports the error
50
38
  // rather than photographing a scene with no sky in it.
51
39
  worker.onerror = (event) => {
52
40
  const failure = new Error(`sky worker failed: ${event.message || 'unknown error'}`);
@@ -59,14 +47,8 @@ function ensureWorker(): Worker | null {
59
47
  return worker;
60
48
  }
61
49
 
62
- /**
63
- * One sky's 512x256 texture, derived OFF the main thread.
64
- *
65
- * Falls back to deriving it inline when there is no Worker (Node, a test, a
66
- * blocked construction). That fallback blocks -- which is the whole complaint --
67
- * but it is CORRECT, and correct-and-slow is the trade this lane already made
68
- * once by hand.
69
- */
50
+ /** Precompute a sky texture off-thread. Environments without a constructible Worker compute
51
+ * the same texture inline; failures after worker startup reject all pending requests. */
70
52
  export function precomputeSkyOffThread(parameters: SkyParameters): Promise<Float32Array> {
71
53
  const active = ensureWorker();
72
54
  if (!active) return Promise.resolve(precomputeSkyTexture(parameters));
@@ -1,18 +1,4 @@
1
- /**
2
- * Sampling a world's radiance field — the expensive half of the world texture,
3
- * kept in its own module so a WORKER can own it.
4
- *
5
- * This is where the time goes. A sky expression derives Blender's
6
- * multiple-scattering model (`blender-sky.ts`, 512x256 with 64 in-scattering
7
- * steps per pixel) and then this samples 32,768 directions out of it. MEASURED:
8
- * 15.4 seconds. On the page's main thread that is not "slow", it is a FROZEN
9
- * EDITOR — the owner saw exactly that, and a status-bar spinner could not have
10
- * helped, because a blocked main thread cannot paint one.
11
- *
12
- * Nothing here touches the DOM, three's renderer, or any editor state: it takes
13
- * a description and returns numbers. That is what makes it movable, and it is
14
- * the reason the split is here rather than around something larger.
15
- */
1
+ /** Sample a world-radiance description into numeric data. This module touches no DOM, renderer or editor state and can run in a worker. */
16
2
  import * as THREE from 'three';
17
3
  import { gradientTexture } from './blender-gradient-texture';
18
4
  // Type-only, so this module keeps NO runtime edge back to the contribution
@@ -100,10 +86,10 @@ export function worldField(
100
86
  // Built ONCE per node: the model precomputes a 512x256 texture, and
101
87
  // rebuilding it per direction would be a 65,536-pixel job per texel.
102
88
  const field = skyField({
103
- ...(expression.sky_model === undefined ? {} : {model: expression.sky_model}),
104
- ...(expression.sun_disc === undefined ? {} : {sunDisc: expression.sun_disc}),
105
- ...(expression.sun_size === undefined ? {} : {sunSize: expression.sun_size}),
106
- ...(expression.sun_intensity === undefined ? {} : {sunIntensity: expression.sun_intensity}),
89
+ model: expression.sky_model,
90
+ sunDisc: expression.sun_disc,
91
+ sunSize: expression.sun_size,
92
+ sunIntensity: expression.sun_intensity,
107
93
  sunElevation: expression.sun_elevation,
108
94
  sunRotation: expression.sun_rotation,
109
95
  altitude: expression.altitude,
@@ -257,10 +243,10 @@ export function collectSkyParameters(expression: WorldExpression): SkyParameters
257
243
  switch (node.kind) {
258
244
  case 'sky':
259
245
  found.push({
260
- ...(node.sky_model === undefined ? {} : {model: node.sky_model}),
261
- ...(node.sun_disc === undefined ? {} : {sunDisc: node.sun_disc}),
262
- ...(node.sun_size === undefined ? {} : {sunSize: node.sun_size}),
263
- ...(node.sun_intensity === undefined ? {} : {sunIntensity: node.sun_intensity}),
246
+ model: node.sky_model,
247
+ sunDisc: node.sun_disc,
248
+ sunSize: node.sun_size,
249
+ sunIntensity: node.sun_intensity,
264
250
  sunElevation: node.sun_elevation,
265
251
  sunRotation: node.sun_rotation,
266
252
  altitude: node.altitude,
@@ -317,7 +303,7 @@ export function withoutSolarDiscs(expression: WorldExpression): WorldExpression
317
303
 
318
304
  export function worldSolarIrradiance(expression: WorldExpression): {direction: THREE.Vector3; irradiance: THREE.Vector3}[] {
319
305
  const suns = collectSkyParameters(expression).filter(p => p.model === 'SINGLE_SCATTERING' &&
320
- p.sunDisc && (p.sunSize ?? 0) > 0 && (p.sunIntensity ?? 1) > 0);
306
+ p.sunDisc && p.sunSize > 0 && p.sunIntensity > 0);
321
307
  if (!suns.length) return [];
322
308
  // Window-dependent world shaders need ray origins as well as directions;
323
309
  // there is no position-independent directional light for that field.
@@ -336,10 +322,10 @@ export function worldSolarIrradiance(expression: WorldExpression): {direction: T
336
322
  ? new THREE.Vector3(0, 0, 1) : new THREE.Vector3(0, 1, 0)).normalize();
337
323
  const v = new THREE.Vector3().crossVectors(direction, u);
338
324
  const irradiance = new THREE.Vector3(), ray = new THREE.Vector3(), delta = new THREE.Vector3();
339
- const solidAngle = 2 * Math.PI * (1 - Math.cos(p.sunSize! / 2));
325
+ const solidAngle = 2 * Math.PI * (1 - Math.cos(p.sunSize / 2));
340
326
  const radial = 32, azimuthal = 32;
341
327
  for (let i = 0; i < radial; i++) {
342
- const cos = 1 - (i + .5) / radial * (1 - Math.cos(p.sunSize! / 2));
328
+ const cos = 1 - (i + .5) / radial * (1 - Math.cos(p.sunSize / 2));
343
329
  const sin = Math.sqrt(1 - cos * cos);
344
330
  for (let j = 0; j < azimuthal; j++) {
345
331
  const phi = (j + .5) / azimuthal * 2 * Math.PI;