@needle-tools/engine 5.1.0-alpha → 5.1.0-alpha.2

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 (193) hide show
  1. package/.needle/generated/needle-bindings.gen.d.ts +5 -0
  2. package/CHANGELOG.md +27 -1
  3. package/SKILL.md +39 -21
  4. package/components.needle.json +1 -1
  5. package/dist/{gltf-progressive-DJBMx-zB.umd.cjs → gltf-progressive-BmblPzFj.umd.cjs} +4 -4
  6. package/dist/{gltf-progressive-BryRjllq.min.js → gltf-progressive-CN_mbb66.min.js} +2 -2
  7. package/dist/{gltf-progressive-Cl167Vjx.js → gltf-progressive-DUlhxdv4.js} +5 -2
  8. package/dist/{needle-engine.bundle-qDahLTqW.min.js → needle-engine.bundle-B-5Q2CpC.min.js} +249 -173
  9. package/dist/{needle-engine.bundle-CwhCzjep.js → needle-engine.bundle-dit3f1l5.js} +13238 -12724
  10. package/dist/{needle-engine.bundle-wM-BWPX9.umd.cjs → needle-engine.bundle-qZfVf_v-.umd.cjs} +250 -174
  11. package/dist/needle-engine.d.ts +295 -31
  12. package/dist/needle-engine.js +569 -563
  13. package/dist/needle-engine.min.js +1 -1
  14. package/dist/needle-engine.umd.cjs +1 -1
  15. package/dist/{postprocessing-B_9sKVU7.min.js → postprocessing-B571qGWR.min.js} +34 -34
  16. package/dist/{postprocessing-WDc9WwI3.js → postprocessing-CfrLAbLX.js} +0 -1
  17. package/dist/{postprocessing-B2wb6pzI.umd.cjs → postprocessing-CiGkAeM9.umd.cjs} +17 -17
  18. package/dist/{vendor-CAcsI0eU.js → vendor-BFrMaK9q.js} +8983 -9136
  19. package/dist/vendor-CJmyOrCq.min.js +1116 -0
  20. package/dist/vendor-DkMW3WY4.umd.cjs +1116 -0
  21. package/lib/engine/api.d.ts +12 -0
  22. package/lib/engine/api.js +2 -0
  23. package/lib/engine/api.js.map +1 -1
  24. package/lib/engine/debug/debug_environment.js +1 -1
  25. package/lib/engine/debug/debug_environment.js.map +1 -1
  26. package/lib/engine/engine_application.js +8 -6
  27. package/lib/engine/engine_application.js.map +1 -1
  28. package/lib/engine/engine_components.js +5 -1
  29. package/lib/engine/engine_components.js.map +1 -1
  30. package/lib/engine/engine_constants.js +6 -0
  31. package/lib/engine/engine_constants.js.map +1 -1
  32. package/lib/engine/engine_context.d.ts +33 -7
  33. package/lib/engine/engine_context.js +40 -2
  34. package/lib/engine/engine_context.js.map +1 -1
  35. package/lib/engine/engine_context_registry.js +1 -1
  36. package/lib/engine/engine_context_registry.js.map +1 -1
  37. package/lib/engine/engine_init.js +7 -0
  38. package/lib/engine/engine_init.js.map +1 -1
  39. package/lib/engine/engine_input.d.ts +3 -2
  40. package/lib/engine/engine_input.js +3 -2
  41. package/lib/engine/engine_input.js.map +1 -1
  42. package/lib/engine/engine_license.d.ts +2 -0
  43. package/lib/engine/engine_license.js +25 -15
  44. package/lib/engine/engine_license.js.map +1 -1
  45. package/lib/engine/engine_lifecycle_functions_internal.js +5 -0
  46. package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
  47. package/lib/engine/engine_networking_blob.d.ts +1 -1
  48. package/lib/engine/engine_networking_blob.js +5 -11
  49. package/lib/engine/engine_networking_blob.js.map +1 -1
  50. package/lib/engine/engine_physics_rapier.d.ts +3 -0
  51. package/lib/engine/engine_physics_rapier.js +13 -10
  52. package/lib/engine/engine_physics_rapier.js.map +1 -1
  53. package/lib/engine/engine_pmrem.js +2 -2
  54. package/lib/engine/engine_pmrem.js.map +1 -1
  55. package/lib/engine/engine_scenedata.d.ts +30 -0
  56. package/lib/engine/engine_scenedata.js +136 -0
  57. package/lib/engine/engine_scenedata.js.map +1 -0
  58. package/lib/engine/engine_ssr.d.ts +18 -0
  59. package/lib/engine/engine_ssr.js +40 -0
  60. package/lib/engine/engine_ssr.js.map +1 -0
  61. package/lib/engine/engine_three_utils.d.ts +14 -7
  62. package/lib/engine/engine_three_utils.js +14 -7
  63. package/lib/engine/engine_three_utils.js.map +1 -1
  64. package/lib/engine/engine_types.d.ts +2 -0
  65. package/lib/engine/engine_types.js.map +1 -1
  66. package/lib/engine/engine_utils.js +4 -2
  67. package/lib/engine/engine_utils.js.map +1 -1
  68. package/lib/engine/engine_utils_hash.d.ts +9 -0
  69. package/lib/engine/engine_utils_hash.js +112 -0
  70. package/lib/engine/engine_utils_hash.js.map +1 -0
  71. package/lib/engine/webcomponents/jsx.d.ts +51 -0
  72. package/lib/engine/webcomponents/logo-element.d.ts +2 -1
  73. package/lib/engine/webcomponents/logo-element.js +2 -1
  74. package/lib/engine/webcomponents/logo-element.js.map +1 -1
  75. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +4 -4
  76. package/lib/engine/webcomponents/needle menu/needle-menu.js +2 -1
  77. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  78. package/lib/engine/webcomponents/needle-button.d.ts +2 -1
  79. package/lib/engine/webcomponents/needle-button.js +2 -1
  80. package/lib/engine/webcomponents/needle-button.js.map +1 -1
  81. package/lib/engine/webcomponents/needle-engine.d.ts +2 -1
  82. package/lib/engine/webcomponents/needle-engine.js +2 -1
  83. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  84. package/lib/engine/xr/NeedleXRSession.d.ts +1 -0
  85. package/lib/engine/xr/NeedleXRSession.js +5 -5
  86. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  87. package/lib/engine/xr/events.d.ts +30 -3
  88. package/lib/engine/xr/events.js +38 -0
  89. package/lib/engine/xr/events.js.map +1 -1
  90. package/lib/engine/xr/init.js +1 -7
  91. package/lib/engine/xr/init.js.map +1 -1
  92. package/lib/engine-components/AnimatorController.d.ts +135 -2
  93. package/lib/engine-components/AnimatorController.js +218 -2
  94. package/lib/engine-components/AnimatorController.js.map +1 -1
  95. package/lib/engine-components/GroundProjection.d.ts +1 -0
  96. package/lib/engine-components/GroundProjection.js +184 -48
  97. package/lib/engine-components/GroundProjection.js.map +1 -1
  98. package/lib/engine-components/Light.d.ts +6 -8
  99. package/lib/engine-components/Light.js +40 -27
  100. package/lib/engine-components/Light.js.map +1 -1
  101. package/lib/engine-components/RigidBody.js +3 -3
  102. package/lib/engine-components/RigidBody.js.map +1 -1
  103. package/lib/engine-components/SceneSwitcher.js +2 -0
  104. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  105. package/lib/engine-components/api.d.ts +1 -0
  106. package/lib/engine-components/api.js +1 -0
  107. package/lib/engine-components/api.js.map +1 -1
  108. package/lib/engine-components/codegen/components.d.ts +1 -0
  109. package/lib/engine-components/codegen/components.js +1 -0
  110. package/lib/engine-components/codegen/components.js.map +1 -1
  111. package/lib/engine-components/postprocessing/Effects/BloomEffect.d.ts +1 -1
  112. package/lib/engine-components/postprocessing/Effects/Sharpening.js +1 -2
  113. package/lib/engine-components/postprocessing/Effects/Sharpening.js.map +1 -1
  114. package/lib/engine-components/postprocessing/PostProcessingHandler.js +5 -6
  115. package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
  116. package/lib/engine-components/web/ScrollFollow.d.ts +0 -1
  117. package/lib/engine-components/web/ScrollFollow.js +3 -2
  118. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  119. package/lib/needle-engine.d.ts +2 -0
  120. package/lib/needle-engine.js +2 -0
  121. package/lib/needle-engine.js.map +1 -1
  122. package/package.json +6 -4
  123. package/plugins/common/logger.js +42 -19
  124. package/plugins/dts-generator/dts.codegen.js +334 -0
  125. package/plugins/dts-generator/dts.scan.js +99 -0
  126. package/plugins/dts-generator/dts.writer.js +59 -0
  127. package/plugins/dts-generator/glb.discovery.js +279 -0
  128. package/plugins/dts-generator/glb.extractor.js +215 -0
  129. package/plugins/dts-generator/glb.reader.js +167 -0
  130. package/plugins/dts-generator/index.js +36 -0
  131. package/plugins/dts-generator/manifest.types.js +174 -0
  132. package/plugins/types/index.d.ts +2 -1
  133. package/plugins/types/needle-bindings.d.ts +30 -0
  134. package/plugins/types/userconfig.d.ts +21 -2
  135. package/plugins/vite/asap.js +1 -1
  136. package/plugins/vite/dependency-watcher.d.ts +2 -2
  137. package/plugins/vite/dependency-watcher.js +3 -4
  138. package/plugins/vite/drop.d.ts +2 -2
  139. package/plugins/vite/drop.js +3 -4
  140. package/plugins/vite/dts-generator.d.ts +7 -0
  141. package/plugins/vite/dts-generator.js +191 -0
  142. package/plugins/vite/index.d.ts +10 -3
  143. package/plugins/vite/index.js +27 -10
  144. package/plugins/vite/logger.client.js +4 -3
  145. package/plugins/vite/logging.js +2 -2
  146. package/plugins/vite/meta.js +4 -2
  147. package/plugins/vite/poster.d.ts +2 -2
  148. package/plugins/vite/poster.js +3 -5
  149. package/plugins/vite/reload.d.ts +2 -2
  150. package/plugins/vite/reload.js +23 -22
  151. package/src/engine/api.ts +15 -1
  152. package/src/engine/debug/debug_environment.ts +1 -1
  153. package/src/engine/engine_application.ts +8 -6
  154. package/src/engine/engine_components.ts +7 -4
  155. package/src/engine/engine_constants.ts +11 -6
  156. package/src/engine/engine_context.ts +50 -7
  157. package/src/engine/engine_context_registry.ts +1 -1
  158. package/src/engine/engine_init.ts +6 -0
  159. package/src/engine/engine_input.ts +3 -2
  160. package/src/engine/engine_license.ts +23 -19
  161. package/src/engine/engine_lifecycle_functions_internal.ts +7 -0
  162. package/src/engine/engine_networking_blob.ts +5 -11
  163. package/src/engine/engine_physics_rapier.ts +14 -12
  164. package/src/engine/engine_pmrem.ts +3 -3
  165. package/src/engine/engine_scenedata.ts +134 -0
  166. package/src/engine/engine_ssr.ts +48 -0
  167. package/src/engine/engine_three_utils.ts +15 -7
  168. package/src/engine/engine_types.ts +2 -0
  169. package/src/engine/engine_utils.ts +3 -2
  170. package/src/engine/engine_utils_hash.ts +65 -0
  171. package/src/engine/webcomponents/jsx.d.ts +51 -0
  172. package/src/engine/webcomponents/logo-element.ts +3 -1
  173. package/src/engine/webcomponents/needle menu/needle-menu.ts +4 -2
  174. package/src/engine/webcomponents/needle-button.ts +3 -1
  175. package/src/engine/webcomponents/needle-engine.ts +3 -1
  176. package/src/engine/xr/NeedleXRSession.ts +6 -6
  177. package/src/engine/xr/events.ts +44 -1
  178. package/src/engine/xr/init.ts +0 -7
  179. package/src/engine-components/AnimatorController.ts +286 -4
  180. package/src/engine-components/GroundProjection.ts +226 -52
  181. package/src/engine-components/Light.ts +40 -26
  182. package/src/engine-components/RigidBody.ts +3 -3
  183. package/src/engine-components/SceneSwitcher.ts +1 -0
  184. package/src/engine-components/api.ts +1 -0
  185. package/src/engine-components/codegen/components.ts +1 -0
  186. package/src/engine-components/postprocessing/Effects/BloomEffect.ts +1 -1
  187. package/src/engine-components/postprocessing/Effects/Sharpening.ts +1 -2
  188. package/src/engine-components/postprocessing/PostProcessingHandler.ts +4 -8
  189. package/src/engine-components/web/ScrollFollow.ts +2 -2
  190. package/src/needle-engine.ts +3 -0
  191. package/src/vite-env.d.ts +16 -0
  192. package/dist/vendor-CEM38hLE.umd.cjs +0 -1116
  193. package/dist/vendor-HRlxIBga.min.js +0 -1116
@@ -1,4 +1,4 @@
1
- import { ShaderMaterial, Texture } from "three";
1
+ import { CubeUVReflectionMapping, ShaderMaterial, Texture } from "three";
2
2
  import { GroundedSkybox as GroundProjection } from 'three/examples/jsm/objects/GroundedSkybox.js';
3
3
 
4
4
  import { Gizmos } from "../engine/engine_gizmos.js";
@@ -12,6 +12,174 @@ import type { ContactShadows } from "./ContactShadows.js";
12
12
 
13
13
  const debug = getParam("debuggroundprojection");
14
14
 
15
+ type GroundProjectionMaterial = GroundProjection["material"] & {
16
+ defines?: Record<string, string | number>;
17
+ };
18
+
19
+ type GroundProjectionShaderUniforms = {
20
+ needleGroundProjectionBlurriness: { value: number };
21
+ needleGroundProjectionBlending: { value: number };
22
+ needleGroundProjectionAlphaFactor: { value: number };
23
+ needleGroundProjectionBackgroundIntensity: { value: number };
24
+ };
25
+
26
+ const needleCubeUvMapVarying = /* glsl */`
27
+ #ifdef NEEDLE_USE_CUBE_UV_MAP
28
+ varying vec3 vNeedleGroundProjectionWorldDirection;
29
+ #endif
30
+ `;
31
+
32
+ const needleGroundProjectionFragmentPars = /* glsl */`
33
+ ${needleCubeUvMapVarying}
34
+ uniform float needleGroundProjectionBlurriness;
35
+ uniform float needleGroundProjectionBlending;
36
+ uniform float needleGroundProjectionAlphaFactor;
37
+ uniform float needleGroundProjectionBackgroundIntensity;
38
+
39
+ float needleGroundProjectionSmoothstep(float edge0, float edge1, float x) {
40
+ float t = clamp((x - edge0) / max(edge1 - edge0, 0.000001), 0.0, 1.0);
41
+ return t * t * (3.0 - 2.0 * t);
42
+ }
43
+
44
+ float needleGroundProjectionDistance() {
45
+ return length(vec2(0.0, vMapUv.y));
46
+ }
47
+
48
+ float needleGroundProjectionBlurFactor(float needleGroundProjectionDistanceValue) {
49
+ return clamp(needleGroundProjectionSmoothstep(0.5, 1.0, needleGroundProjectionDistanceValue * 2.0), 0.0, 1.0);
50
+ }
51
+ `;
52
+
53
+ const needleCubeUvMapFragment = /* glsl */`
54
+ #ifdef USE_MAP
55
+
56
+ float needleGroundProjectionDistanceValue = needleGroundProjectionDistance();
57
+ float needleGroundProjectionBlurFactorValue = needleGroundProjectionBlurFactor(needleGroundProjectionDistanceValue);
58
+ vec4 sampledDiffuseColor;
59
+
60
+ #ifdef NEEDLE_USE_CUBE_UV_MAP
61
+ sampledDiffuseColor = textureCubeUV(
62
+ map,
63
+ normalize( vNeedleGroundProjectionWorldDirection ),
64
+ needleGroundProjectionBlurriness * needleGroundProjectionBlurFactorValue
65
+ );
66
+ #else
67
+ #ifdef USE_MIPMAP_BIAS
68
+ sampledDiffuseColor = texture2D( map, vMapUv, mipmapBias );
69
+ #else
70
+ sampledDiffuseColor = texture2D( map, vMapUv );
71
+ #endif
72
+ #endif
73
+
74
+ #ifdef DECODE_VIDEO_TEXTURE
75
+
76
+ // use inline sRGB decode until browsers properly support SRGB8_ALPHA8 with video textures (#26516)
77
+
78
+ sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w );
79
+
80
+ #endif
81
+
82
+ sampledDiffuseColor.rgb *= mix(1.0, needleGroundProjectionBackgroundIntensity, needleGroundProjectionBlurFactorValue);
83
+ diffuseColor *= sampledDiffuseColor;
84
+
85
+ #endif
86
+ `;
87
+
88
+ const needleGroundProjectionAlphaFragment = /* glsl */`
89
+ #ifdef USE_MAP
90
+ if (needleGroundProjectionBlending > 0.000001) {
91
+ float needleGroundProjectionBrightness = dot(diffuseColor.rgb, vec3(0.299, 0.587, 0.114));
92
+ float needleGroundProjectionStepFactor = needleGroundProjectionBlending - needleGroundProjectionBrightness * 0.1;
93
+ diffuseColor.a *= pow(
94
+ 1.0 - needleGroundProjectionBlending * needleGroundProjectionSmoothstep(
95
+ 0.35 * needleGroundProjectionStepFactor,
96
+ 0.45 * needleGroundProjectionStepFactor,
97
+ needleGroundProjectionDistanceValue
98
+ ),
99
+ 5.0
100
+ );
101
+ }
102
+ #endif
103
+ diffuseColor.a *= needleGroundProjectionAlphaFactor;
104
+ `;
105
+
106
+ function getCubeUvSize(texture: Texture) {
107
+ const imageHeight = texture.image?.height;
108
+ if (!imageHeight) return null;
109
+
110
+ const maxMip = Math.log2(imageHeight) - 2;
111
+ const texelHeight = 1 / imageHeight;
112
+ const texelWidth = 1 / (3 * Math.max(Math.pow(2, maxMip), 7 * 16));
113
+ return { texelWidth, texelHeight, maxMip };
114
+ }
115
+
116
+ function getGroundProjectionShaderUniforms(material: GroundProjectionMaterial): GroundProjectionShaderUniforms {
117
+ const userData = material.userData as GroundProjectionMaterial["userData"] & {
118
+ needleGroundProjectionUniforms?: GroundProjectionShaderUniforms;
119
+ };
120
+
121
+ return userData.needleGroundProjectionUniforms ??= {
122
+ needleGroundProjectionBlurriness: { value: 0 },
123
+ needleGroundProjectionBlending: { value: 0 },
124
+ needleGroundProjectionAlphaFactor: { value: 1 },
125
+ needleGroundProjectionBackgroundIntensity: { value: 1 }
126
+ };
127
+ }
128
+
129
+ function configureGroundProjectionMaterial(material: GroundProjectionMaterial, texture: Texture) {
130
+ const projectionUniforms = getGroundProjectionShaderUniforms(material);
131
+ material.onBeforeCompile = shader => {
132
+ shader.uniforms.needleGroundProjectionBlurriness = projectionUniforms.needleGroundProjectionBlurriness;
133
+ shader.uniforms.needleGroundProjectionBlending = projectionUniforms.needleGroundProjectionBlending;
134
+ shader.uniforms.needleGroundProjectionAlphaFactor = projectionUniforms.needleGroundProjectionAlphaFactor;
135
+ shader.uniforms.needleGroundProjectionBackgroundIntensity = projectionUniforms.needleGroundProjectionBackgroundIntensity;
136
+
137
+ shader.vertexShader = shader.vertexShader
138
+ .replace("#include <uv_pars_vertex>", `#include <uv_pars_vertex>\n${needleCubeUvMapVarying}`)
139
+ .replace(
140
+ "#include <worldpos_vertex>",
141
+ `#include <worldpos_vertex>
142
+ #ifdef NEEDLE_USE_CUBE_UV_MAP
143
+ // GroundedSkybox mirrors geometry on Z, so undo that before deriving the sampling direction.
144
+ vNeedleGroundProjectionWorldDirection = transformDirection( vec3( position.x, position.y, -position.z ), modelMatrix );
145
+ #endif`
146
+ );
147
+
148
+ shader.fragmentShader = shader.fragmentShader
149
+ .replace(
150
+ "#include <map_pars_fragment>",
151
+ `#include <map_pars_fragment>
152
+ ${needleGroundProjectionFragmentPars}
153
+ #include <cube_uv_reflection_fragment>`
154
+ )
155
+ .replace("#include <map_fragment>", needleCubeUvMapFragment)
156
+ .replace("#include <opaque_fragment>", `${needleGroundProjectionAlphaFragment}\n#include <opaque_fragment>`);
157
+ };
158
+
159
+ const defines = material.defines ??= {};
160
+ const prevDefineState = JSON.stringify(defines);
161
+ const cubeUvSize = texture.mapping === CubeUVReflectionMapping ? getCubeUvSize(texture) : null;
162
+
163
+ if (cubeUvSize) {
164
+ defines.NEEDLE_USE_CUBE_UV_MAP = 1;
165
+ defines.ENVMAP_TYPE_CUBE_UV = 1;
166
+ defines.CUBEUV_TEXEL_WIDTH = cubeUvSize.texelWidth;
167
+ defines.CUBEUV_TEXEL_HEIGHT = cubeUvSize.texelHeight;
168
+ defines.CUBEUV_MAX_MIP = `${cubeUvSize.maxMip}.0`;
169
+ }
170
+ else {
171
+ delete defines.NEEDLE_USE_CUBE_UV_MAP;
172
+ delete defines.ENVMAP_TYPE_CUBE_UV;
173
+ delete defines.CUBEUV_TEXEL_WIDTH;
174
+ delete defines.CUBEUV_TEXEL_HEIGHT;
175
+ delete defines.CUBEUV_MAX_MIP;
176
+ }
177
+
178
+ if (prevDefineState !== JSON.stringify(defines)) {
179
+ material.needsUpdate = true;
180
+ }
181
+ }
182
+
15
183
  /**
16
184
  * The [GroundProjectedEnv](https://engine.needle.tools/docs/api/GroundProjectedEnv) projects the environment map onto a virtual ground plane.
17
185
  * Creates a realistic floor from 360° panoramas/HDRIs by deforming the skybox
@@ -147,12 +315,10 @@ export class GroundProjectedEnv extends Behaviour {
147
315
  this._projection.rotation.copy(this.scene.backgroundRotation);
148
316
  }
149
317
 
150
- const blurrinessChanged = this.context.scene.backgroundBlurriness !== undefined && this._lastBlurriness != this.context.scene.backgroundBlurriness && this.context.scene.backgroundBlurriness > 0.001;
151
- if (blurrinessChanged) {
152
- this.updateProjection();
153
- }
154
- else if (this._needsTextureUpdate && this.context.scene.background instanceof Texture) {
155
- this.updateBlurriness(this.context.scene.background, this.context.scene.backgroundBlurriness);
318
+ if (this._projection && this.context.scene.background instanceof Texture) {
319
+ const blurriness = this.context.scene.backgroundBlurriness ?? 0;
320
+ const blurrinessChanged = this._lastBlurriness !== blurriness;
321
+ this.updateProjectionMaterial(this.context.scene.background, blurrinessChanged || this._needsTextureUpdate);
156
322
  }
157
323
  }
158
324
 
@@ -202,6 +368,7 @@ export class GroundProjectedEnv extends Behaviour {
202
368
 
203
369
  try {
204
370
  this._projection = new GroundProjection(backgroundTexture, this._height, this._radius, 64);
371
+ configureGroundProjectionMaterial(this._projection.material, backgroundTexture);
205
372
  }
206
373
  catch (e) {
207
374
  console.error("Error creating three GroundProjection", e);
@@ -242,9 +409,7 @@ export class GroundProjectedEnv extends Behaviour {
242
409
  this.env.height = this._height;
243
410
  */
244
411
 
245
- if (this.context.scene.backgroundBlurriness > 0.001 && this._needsTextureUpdate) {
246
- this.updateBlurriness(backgroundTexture, this.context.scene.backgroundBlurriness);
247
- }
412
+ this.updateProjectionMaterial(backgroundTexture, true);
248
413
 
249
414
  this._lastBackground = backgroundTexture;
250
415
  this._lastHeight = this._height;
@@ -254,24 +419,60 @@ export class GroundProjectedEnv extends Behaviour {
254
419
 
255
420
  private _blurrynessShader: ShaderMaterial | null = null;
256
421
  private _lastBlurriness: number = -1;
257
-
258
- private updateBlurriness(texture: Texture, blurriness: number) {
422
+
423
+ private updateProjectionMaterial(texture: Texture, forceTextureUpdate = false) {
259
424
  if (!this._projection) {
260
425
  return;
261
426
  }
262
- else if (!texture) {
263
- return;
427
+
428
+ const blurriness = this.context.scene.backgroundBlurriness ?? 0;
429
+ const useCubeUvBlur = texture.mapping === CubeUVReflectionMapping;
430
+
431
+ let targetTexture = texture;
432
+ if (!useCubeUvBlur && blurriness > 0.001) {
433
+ const hasBlurredTextureAssigned = !!this._projection.material.map && this._projection.material.map !== texture;
434
+ if (forceTextureUpdate || !hasBlurredTextureAssigned) {
435
+ targetTexture = this.updateBlurriness(texture, blurriness);
436
+ }
437
+ else if (this._projection.material.map) {
438
+ targetTexture = this._projection.material.map;
439
+ }
440
+ }
441
+
442
+ if (this._projection.material.map !== targetTexture) {
443
+ this._projection.material.map = targetTexture;
444
+ }
445
+
446
+ const appliedTexture = this._projection.material.map ?? texture;
447
+ appliedTexture.mapping = texture.mapping;
448
+ configureGroundProjectionMaterial(this._projection.material, appliedTexture);
449
+
450
+ const shaderUniforms = getGroundProjectionShaderUniforms(this._projection.material);
451
+ shaderUniforms.needleGroundProjectionBlurriness.value = useCubeUvBlur ? blurriness : 0;
452
+ shaderUniforms.needleGroundProjectionBackgroundIntensity.value = this.context.scene.backgroundIntensity ?? 1;
453
+
454
+ const wasTransparent = this._projection.material.transparent;
455
+ this._projection.material.transparent = (this.context.xr?.isAR === true && this.arBlending > 0.000001) ?? false;
456
+ shaderUniforms.needleGroundProjectionBlending.value = this._projection.material.transparent ? this.arBlending : 0;
457
+ shaderUniforms.needleGroundProjectionAlphaFactor.value = this.context.isInPassThrough ? 0.95 : 1;
458
+
459
+ if (wasTransparent !== this._projection.material.transparent) {
460
+ this._projection.material.needsUpdate = true;
264
461
  }
265
462
 
463
+ this._projection.material.depthTest = true;
464
+ this._projection.material.depthWrite = false;
465
+ this._lastBlurriness = blurriness;
266
466
  this._needsTextureUpdate = false;
467
+ }
468
+
469
+ private updateBlurriness(texture: Texture, blurriness: number): Texture {
267
470
  if (debug) console.log("Update Blurriness", blurriness);
268
471
  this._blurrynessShader ??= new ShaderMaterial({
269
472
  name: "GroundProjectionBlurriness",
270
473
  uniforms: {
271
474
  map: { value: texture },
272
- blurriness: { value: blurriness },
273
- blending: { value: 0 },
274
- alphaFactor: { value: 1 }
475
+ blurriness: { value: blurriness }
275
476
  },
276
477
  vertexShader: blurVertexShader,
277
478
  fragmentShader: blurFragmentShader
@@ -279,33 +480,10 @@ export class GroundProjectedEnv extends Behaviour {
279
480
  this._blurrynessShader.depthWrite = false;
280
481
  this._blurrynessShader.uniforms.map.value = texture;
281
482
  this._blurrynessShader.uniforms.blurriness.value = blurriness;
282
- this._lastBlurriness = blurriness;
283
483
  texture.needsUpdate = true;
284
-
285
- const wasTransparent = this._projection.material.transparent;
286
- this._projection.material.transparent = (this.context.xr?.isAR === true && this.arBlending > 0.000001) ?? false;
287
- if (this._projection.material.transparent) {
288
- this._blurrynessShader.uniforms.blending.value = this.arBlending;
289
- }
290
- else { this._blurrynessShader.uniforms.blending.value = 0; }
291
-
292
- if (this.context.isInPassThrough) {
293
- // Make the ground slightly transparent in passthrough mode
294
- this._blurrynessShader.uniforms.alphaFactor.value = 0.95;
295
- }
296
- else {
297
- this._blurrynessShader.uniforms.alphaFactor.value = 1;
298
- }
299
-
300
- // Make sure the material is updated if the transparency changed
301
- if (wasTransparent !== this._projection.material.transparent) {
302
- this._projection.material.needsUpdate = true;
303
- }
304
-
305
- // Update the texture
306
- this._projection.material.map = Graphics.copyTexture(texture, this._blurrynessShader);
307
- this._projection.material.depthTest = true;
308
- this._projection.material.depthWrite = false;
484
+ const blurredTexture = Graphics.copyTexture(texture, this._blurrynessShader);
485
+ blurredTexture.mapping = texture.mapping;
486
+ return blurredTexture;
309
487
  }
310
488
 
311
489
  }
@@ -324,8 +502,6 @@ const blurVertexShader = `
324
502
  const blurFragmentShader = `
325
503
  uniform sampler2D map;
326
504
  uniform float blurriness;
327
- uniform float alphaFactor;
328
- uniform float blending;
329
505
  varying vec2 vUv;
330
506
 
331
507
  const float PI = 3.14159265359;
@@ -356,6 +532,10 @@ const blurFragmentShader = `
356
532
  vec4 color = vec4(0.0);
357
533
  float totalWeight = 0.0;
358
534
  int blurSize = int(60.0 * min(1.0, blurriness) * blurAmount); // Adjust blur size based on distance and blurriness
535
+ if (blurSize <= 0) {
536
+ gl_FragColor = texture2D(map, vUv);
537
+ return;
538
+ }
359
539
  float lodLevel = log2(float(blurSize)) * 0.5; // Compute LOD level
360
540
 
361
541
  for (int x = -blurSize; x <= blurSize; x++) {
@@ -371,16 +551,10 @@ const blurFragmentShader = `
371
551
 
372
552
  gl_FragColor = color;
373
553
 
374
- float brightness = dot(gl_FragColor.rgb, vec3(0.299, 0.587, 0.114));
375
- float stepFactor = blending - brightness * .1;
376
- gl_FragColor.a = pow(1.0 - blending * customSmoothstep(0.35 * stepFactor, 0.45 * stepFactor, distance), 5.);
377
- gl_FragColor.a *= alphaFactor;
378
- // gl_FragColor.rgb = vec3(1.0);
379
-
380
554
  // #include <tonemapping_fragment>
381
555
  // #include <colorspace_fragment>
382
556
 
383
557
  // Uncomment to visualize blur amount
384
558
  // gl_FragColor = vec4(blurAmount, 0.0, 0.0, 1.0);
385
559
  }
386
- `;
560
+ `;
@@ -106,11 +106,40 @@ enum LightShadows {
106
106
  export class Light extends Behaviour implements ILight {
107
107
 
108
108
  /**
109
- * The type of light (spot, directional, point, etc.)
110
- * Can not be changed at runtime.
109
+ * The type of light as a lowercase string: `"directional"`, `"point"`, `"spot"`.
110
+ * Implements {@link ILight.type}. Can not be changed at runtime.
111
111
  */
112
+ get type(): ILight["type"] {
113
+ return this._type;
114
+ }
115
+
116
+ /** Numeric LightType serialized from Unity/Blender — converts to string on write */
112
117
  @serializable()
113
- private type: LightType = 0;
118
+ set type(value: LightType | ILight["type"]) {
119
+ if (this.light && this.__didAwake) {
120
+ throw new Error("Changing the light type at runtime is not supported");
121
+ }
122
+ switch (value) {
123
+ case LightType.Directional:
124
+ this._type = "directional";
125
+ break;
126
+ case LightType.Point:
127
+ this._type = "point";
128
+ break;
129
+ case LightType.Spot:
130
+ this._type = "spot";
131
+ break;
132
+ case "directional":
133
+ case "point":
134
+ case "spot":
135
+ this._type = value;
136
+ break;
137
+ default:
138
+ throw new Error("Invalid light type: " + value);
139
+ }
140
+ }
141
+ private _type: ILight["type"] = "point";
142
+
114
143
 
115
144
  /**
116
145
  * The maximum distance the light affects.
@@ -345,7 +374,7 @@ export class Light extends Behaviour implements ILight {
345
374
  */
346
375
  public getWorldPosition(vec: Vector3): Vector3 {
347
376
  if (this.light) {
348
- if (this.type === LightType.Directional) {
377
+ if (this.type === "directional") {
349
378
  return this.light.getWorldPosition(vec).multiplyScalar(1);
350
379
  }
351
380
  return this.light.getWorldPosition(vec);
@@ -372,12 +401,13 @@ export class Light extends Behaviour implements ILight {
372
401
  else if (this.light.parent !== this.gameObject)
373
402
  this.gameObject.add(this.light);
374
403
  }
375
- if (this.type === LightType.Directional)
376
- this.startCoroutine(this.updateMainLightRoutine(), FrameEvent.LateUpdate);
404
+ this.context.lights.push(this);
377
405
  }
378
406
 
379
407
  onDisable() {
380
408
  if (debug) console.log("DISABLE LIGHT", this.name);
409
+ const index = this.context.lights.indexOf(this);
410
+ if (index !== -1) this.context.lights.splice(index, 1);
381
411
  if (this.light) {
382
412
  if (this.selfIsLight)
383
413
  this.light.intensity = 0;
@@ -399,14 +429,14 @@ export class Light extends Behaviour implements ILight {
399
429
  this._intensity = this.light.intensity;
400
430
 
401
431
  switch (this.type) {
402
- case LightType.Directional:
432
+ case "directional":
403
433
  this.setDirectionalLight(this.light as DirectionalLight);
404
434
  break;
405
435
  }
406
436
  }
407
437
  else if (!this.light) {
408
438
  switch (this.type) {
409
- case LightType.Directional:
439
+ case "directional":
410
440
  // console.log(this);
411
441
  const dirLight = new DirectionalLight(this.color, this.intensity * Math.PI);
412
442
  // directional light target is at 0 0 0 by default
@@ -425,7 +455,7 @@ export class Light extends Behaviour implements ILight {
425
455
  }
426
456
  break;
427
457
 
428
- case LightType.Spot:
458
+ case "spot":
429
459
  const spotLight = new SpotLight(this.color, this.intensity * Math.PI, this.range, toRadians(this.spotAngle / 2), 1 - toRadians(this.innerSpotAngle / 2) / toRadians(this.spotAngle / 2), 2);
430
460
  spotLight.position.set(0, 0, 0);
431
461
  spotLight.rotation.set(0, 0, 0);
@@ -437,7 +467,7 @@ export class Light extends Behaviour implements ILight {
437
467
  spotLightTarget.rotation.set(0, 0, 0);
438
468
  break;
439
469
 
440
- case LightType.Point:
470
+ case "point":
441
471
  const pointLight = new PointLight(this.color, this.intensity * Math.PI, this.range);
442
472
  this.light = pointLight;
443
473
 
@@ -526,22 +556,6 @@ export class Light extends Behaviour implements ILight {
526
556
 
527
557
  }
528
558
 
529
- /**
530
- * Coroutine that updates the main light reference in the context
531
- * if this directional light should be the main light
532
- */
533
- *updateMainLightRoutine() {
534
- while (true) {
535
- if (this.type === LightType.Directional) {
536
- if (!this.context.mainLight || this.intensity > this.context.mainLight.intensity) {
537
- this.context.mainLight = this;
538
- }
539
- yield;
540
- }
541
- break;
542
- }
543
- }
544
-
545
559
  /**
546
560
  * Controls whether the renderer's shadow map type can be changed when soft shadows are used
547
561
  */
@@ -382,12 +382,12 @@ export class Rigidbody extends Behaviour implements IRigidbody {
382
382
  this._watch.start(true, true);
383
383
  this.startCoroutine(this.beforePhysics(), FrameEvent.LateUpdate);
384
384
  if (isDevEnvironment()) {
385
- if (!globalThis["NEEDLE_USE_RAPIER"])
386
- console.warn(`Rigidbody could not be created: Rapier physics are explicitly disabled.`);
385
+ if (globalThis["NEEDLE_USE_RAPIER"] === false)
386
+ console.warn(`RAPIER physics are disabled in your build. Enable them by setting NEEDLE_USE_RAPIER to true in your build config: Rigidbody could not be created.`);
387
387
  else {
388
388
  MODULES.RAPIER_PHYSICS.ready().then(async () => {
389
389
  await delayForFrames(3);
390
- if (!this.context.physics.engine?.getBody(this))
390
+ if (this.activeAndEnabled && !this.context.physics.engine?.getBody(this))
391
391
  console.warn(`Rigidbody could not be created. Ensure \"${this.name}\" has a Collider component.`);
392
392
  })
393
393
  }
@@ -723,6 +723,7 @@ export class SceneSwitcher extends Behaviour {
723
723
  // unless the user defines that he wants to use the scene name
724
724
  if (this.useSceneName) {
725
725
  if (scene instanceof Object3D) queryParameterValue = scene.name;
726
+ else if (scene instanceof AssetReference && scene.asset?.name) queryParameterValue = scene.asset.name;
726
727
  else if (scene.url) queryParameterValue = sceneUriToName(scene.url);
727
728
  }
728
729
  // save the loaded scene as an url parameter
@@ -35,6 +35,7 @@
35
35
  */
36
36
 
37
37
  export * from "./codegen/components.js";
38
+ export { AnimatorControllerBuilder, type ConditionMode, type StateOptions, type TransitionOptions } from "./AnimatorController.js";
38
39
  export { Collider } from "./Collider.js"; // export abstract type
39
40
  export { Behaviour, Component, GameObject } from "./Component.js";
40
41
 
@@ -6,6 +6,7 @@ export { Animation } from "../Animation.js";
6
6
  export { Keyframe } from "../AnimationCurve.js";
7
7
  export { AnimationCurve } from "../AnimationCurve.js";
8
8
  export { Animator } from "../Animator.js";
9
+ export { AnimatorControllerBuilder } from "../AnimatorController.js";
9
10
  export { AnimatorController } from "../AnimatorController.js";
10
11
  export { AudioListener } from "../AudioListener.js";
11
12
  export { AudioSource } from "../AudioSource.js";
@@ -1,4 +1,4 @@
1
- import { BloomEffect as _BloomEffect, EffectAttribute } from "postprocessing";
1
+ import type { BloomEffect as _BloomEffect } from "postprocessing";
2
2
  import { MathUtils } from "three";
3
3
 
4
4
  import { MODULES } from "../../../engine/engine_modules.js";
@@ -1,4 +1,3 @@
1
- import { EffectAttribute } from "postprocessing";
2
1
  import { Uniform } from "three";
3
2
 
4
3
  import { MODULES } from "../../../engine/engine_modules.js";
@@ -138,7 +137,7 @@ function createSharpeningEffectType() {
138
137
  ["radius", new Uniform(1)],
139
138
  // ["threshold", new Uniform(0)],
140
139
  ]),
141
- attributes: EffectAttribute.CONVOLUTION
140
+ attributes: MODULES.POSTPROCESSING.MODULE.EffectAttribute.CONVOLUTION
142
141
  });
143
142
  }
144
143
  }
@@ -14,10 +14,6 @@ import { threeToneMappingToEffectMode } from "./Effects/Tonemapping.utils.js";
14
14
  import { PostProcessingEffect, PostProcessingEffectContext } from "./PostProcessingEffect.js";
15
15
  import { orderEffects, PostprocessingEffectData, PostProcessingEffectOrder } from "./utils.js";
16
16
 
17
- declare const NEEDLE_USE_POSTPROCESSING: boolean;
18
- globalThis["NEEDLE_USE_POSTPROCESSING"] = globalThis["NEEDLE_USE_POSTPROCESSING"] !== undefined ? globalThis["NEEDLE_USE_POSTPROCESSING"] : true;
19
-
20
-
21
17
  const debug = getParam("debugpost");
22
18
 
23
19
  const activeKey = Symbol("needle:postprocessing-handler");
@@ -58,13 +54,13 @@ export class PostProcessingHandler implements IPostProcessingHandler {
58
54
 
59
55
  apply(components: PostProcessingEffect[]): Promise<void> {
60
56
  if ("env" in import.meta && (import.meta as any /* webpack support */ ).env.VITE_NEEDLE_USE_POSTPROCESSING === "false") {
61
- if (debug) console.warn("Postprocessing is disabled via vite env setting");
62
- else console.debug("Postprocessing is disabled via vite env setting");
57
+ if (debug) console.warn("POSTPROCESSING is disabled via vite env setting");
58
+ else console.debug("POSTPROCESSING is disabled via vite env setting");
63
59
  return Promise.resolve();
64
60
  }
65
61
  if (!NEEDLE_USE_POSTPROCESSING) {
66
- if (debug) console.warn("Postprocessing is disabled via global vite define setting");
67
- else console.debug("Postprocessing is disabled via vite define");
62
+ if (debug || isDevEnvironment()) console.warn("POSTPROCESSING is disabled via global vite define setting");
63
+ else console.debug("POSTPROCESSING is disabled via vite define");
68
64
  return Promise.resolve();
69
65
  }
70
66
 
@@ -1,5 +1,5 @@
1
- // For firefox ViewTimeline support
2
- import "scroll-timeline-polyfill/dist/scroll-timeline.js";
1
+ // For firefox ViewTimeline support — dynamic import to avoid SSR crashes (polyfill accesses window at module level)
2
+ if (typeof window !== "undefined") import("scroll-timeline-polyfill/dist/scroll-timeline.js");
3
3
 
4
4
  import { Box3, Object3D } from "three";
5
5
 
@@ -1,3 +1,6 @@
1
+ /// <reference path="../plugins/types/needle-bindings.d.ts" />
2
+ /// <reference path="./engine/webcomponents/jsx.d.ts" />
3
+
1
4
  import { initEngine } from "./engine/engine_init.js";
2
5
  initEngine();
3
6
  import "./engine/webcomponents/needle-engine.js";
@@ -0,0 +1,16 @@
1
+ // Vite global defines — see plugins/vite/defines.js
2
+ // declare const entries here are picked up globally (ambient, no import/export).
3
+ // Vite sets these as globals in dev and statically replaces them at build time.
4
+ // Webpack DefinePlugin replaces them at build time too.
5
+ // The globalThis fallbacks for vanilla JS are in engine_constants.ts.
6
+
7
+ declare const NEEDLE_ENGINE_VERSION: string;
8
+ declare const NEEDLE_ENGINE_GENERATOR: string;
9
+ declare const NEEDLE_PROJECT_BUILD_TIME: string;
10
+ declare const NEEDLE_PUBLIC_KEY: string;
11
+
12
+ // #region treeshake flags
13
+ // declare var (not const) so globalThis["NEEDLE_USE_*"] access is also type-safe
14
+ declare var NEEDLE_USE_RAPIER: boolean;
15
+ declare var NEEDLE_USE_POSTPROCESSING: boolean;
16
+ // #endregion treeshake flags