@fps-games/editor 0.1.4-beta.4 → 0.1.4-beta.5

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 (144) hide show
  1. package/dist/build-info.json +45 -0
  2. package/dist/local-editor-harness.d.ts +2 -1
  3. package/dist/local-editor-harness.d.ts.map +1 -1
  4. package/dist/local-editor-harness.js +62 -0
  5. package/dist/local-editor-harness.js.map +1 -1
  6. package/dist/playable-babylon-facade.d.ts +4 -4
  7. package/dist/playable-babylon-facade.d.ts.map +1 -1
  8. package/dist/playable-babylon-facade.js +2 -2
  9. package/dist/playable-babylon-facade.js.map +1 -1
  10. package/dist/playable-babylon-rendering.d.ts +14 -2
  11. package/dist/playable-babylon-rendering.d.ts.map +1 -1
  12. package/dist/playable-babylon-rendering.js +48 -3
  13. package/dist/playable-babylon-rendering.js.map +1 -1
  14. package/dist/playable-local-editor-host.d.ts +2 -0
  15. package/dist/playable-local-editor-host.d.ts.map +1 -1
  16. package/dist/playable-local-editor-host.js.map +1 -1
  17. package/dist/playable-sdk-vite.d.ts +2 -0
  18. package/dist/playable-sdk-vite.d.ts.map +1 -0
  19. package/dist/playable-sdk-vite.js +2 -0
  20. package/dist/playable-sdk-vite.js.map +1 -0
  21. package/dist/playable-sdk.d.ts +4 -4
  22. package/dist/playable-sdk.d.ts.map +1 -1
  23. package/dist/playable-sdk.js +2 -2
  24. package/dist/playable-sdk.js.map +1 -1
  25. package/node_modules/@fps-games/babylon-renderer/dist/index.d.ts +3 -0
  26. package/node_modules/@fps-games/babylon-renderer/dist/index.d.ts.map +1 -1
  27. package/node_modules/@fps-games/babylon-renderer/dist/index.js +3 -0
  28. package/node_modules/@fps-games/babylon-renderer/dist/index.js.map +1 -1
  29. package/node_modules/@fps-games/babylon-renderer/dist/shadows/blob-shadow-shaders.d.ts +5 -0
  30. package/node_modules/@fps-games/babylon-renderer/dist/shadows/blob-shadow-shaders.d.ts.map +1 -0
  31. package/node_modules/@fps-games/babylon-renderer/dist/shadows/blob-shadow-shaders.js +46 -0
  32. package/node_modules/@fps-games/babylon-renderer/dist/shadows/blob-shadow-shaders.js.map +1 -0
  33. package/node_modules/@fps-games/babylon-renderer/dist/shadows/blob-shadow-system.d.ts +5 -0
  34. package/node_modules/@fps-games/babylon-renderer/dist/shadows/blob-shadow-system.d.ts.map +1 -0
  35. package/node_modules/@fps-games/babylon-renderer/dist/shadows/blob-shadow-system.js +398 -0
  36. package/node_modules/@fps-games/babylon-renderer/dist/shadows/blob-shadow-system.js.map +1 -0
  37. package/node_modules/@fps-games/babylon-renderer/dist/shadows/static-projected-shadow-system.d.ts +6 -0
  38. package/node_modules/@fps-games/babylon-renderer/dist/shadows/static-projected-shadow-system.d.ts.map +1 -0
  39. package/node_modules/@fps-games/babylon-renderer/dist/shadows/static-projected-shadow-system.js +717 -0
  40. package/node_modules/@fps-games/babylon-renderer/dist/shadows/static-projected-shadow-system.js.map +1 -0
  41. package/node_modules/@fps-games/babylon-renderer/dist/shadows/types.d.ts +131 -0
  42. package/node_modules/@fps-games/babylon-renderer/dist/shadows/types.d.ts.map +1 -1
  43. package/node_modules/@fps-games/babylon-renderer/dist/shadows/types.js +76 -0
  44. package/node_modules/@fps-games/babylon-renderer/dist/shadows/types.js.map +1 -1
  45. package/node_modules/@fps-games/babylon-renderer/package.json +1 -1
  46. package/node_modules/@fps-games/editor-babylon/dist/index.d.ts +1 -0
  47. package/node_modules/@fps-games/editor-babylon/dist/index.d.ts.map +1 -1
  48. package/node_modules/@fps-games/editor-babylon/dist/index.js +1 -0
  49. package/node_modules/@fps-games/editor-babylon/dist/index.js.map +1 -1
  50. package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.d.ts +49 -0
  51. package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.d.ts.map +1 -0
  52. package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.js +141 -0
  53. package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.js.map +1 -0
  54. package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts +5 -0
  55. package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts.map +1 -1
  56. package/node_modules/@fps-games/editor-babylon/dist/projection.js +72 -5
  57. package/node_modules/@fps-games/editor-babylon/dist/projection.js.map +1 -1
  58. package/node_modules/@fps-games/editor-babylon/dist/shadow-preview-controller.d.ts +14 -3
  59. package/node_modules/@fps-games/editor-babylon/dist/shadow-preview-controller.d.ts.map +1 -1
  60. package/node_modules/@fps-games/editor-babylon/dist/shadow-preview-controller.js +223 -8
  61. package/node_modules/@fps-games/editor-babylon/dist/shadow-preview-controller.js.map +1 -1
  62. package/node_modules/@fps-games/editor-babylon/package.json +5 -5
  63. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts.map +1 -1
  64. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js +33 -6
  65. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js.map +1 -1
  66. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts +3 -1
  67. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts.map +1 -1
  68. package/node_modules/@fps-games/editor-browser/package.json +3 -3
  69. package/node_modules/@fps-games/editor-core/package.json +2 -2
  70. package/node_modules/@fps-games/editor-forge-play/package.json +2 -2
  71. package/node_modules/@fps-games/editor-playable-sdk/dist/document/types.d.ts +2 -0
  72. package/node_modules/@fps-games/editor-playable-sdk/dist/document/types.d.ts.map +1 -1
  73. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.d.ts +1 -1
  74. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.d.ts.map +1 -1
  75. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.js +1 -1
  76. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.js.map +1 -1
  77. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/schema.d.ts.map +1 -1
  78. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/schema.js +2 -1
  79. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/schema.js.map +1 -1
  80. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.d.ts +1 -0
  81. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.d.ts.map +1 -1
  82. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.js +8 -0
  83. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.js.map +1 -1
  84. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/document-sections.d.ts +1 -0
  85. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/document-sections.d.ts.map +1 -1
  86. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/document-sections.js +56 -0
  87. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/document-sections.js.map +1 -1
  88. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/forge-play.d.ts.map +1 -1
  89. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/forge-play.js +0 -4
  90. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/forge-play.js.map +1 -1
  91. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/field-patch.d.ts.map +1 -1
  92. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/field-patch.js +8 -1
  93. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/field-patch.js.map +1 -1
  94. package/node_modules/@fps-games/editor-playable-sdk/dist/package-resolution/aliases.d.ts +1 -1
  95. package/node_modules/@fps-games/editor-playable-sdk/dist/package-resolution/aliases.d.ts.map +1 -1
  96. package/node_modules/@fps-games/editor-playable-sdk/dist/package-resolution/aliases.js +5 -0
  97. package/node_modules/@fps-games/editor-playable-sdk/dist/package-resolution/aliases.js.map +1 -1
  98. package/node_modules/@fps-games/editor-playable-sdk/dist/rendering-profile/panel.d.ts.map +1 -1
  99. package/node_modules/@fps-games/editor-playable-sdk/dist/rendering-profile/panel.js +412 -0
  100. package/node_modules/@fps-games/editor-playable-sdk/dist/rendering-profile/panel.js.map +1 -1
  101. package/node_modules/@fps-games/editor-playable-sdk/dist/rendering-profile/profile.d.ts +6 -2
  102. package/node_modules/@fps-games/editor-playable-sdk/dist/rendering-profile/profile.d.ts.map +1 -1
  103. package/node_modules/@fps-games/editor-playable-sdk/dist/rendering-profile/profile.js +272 -5
  104. package/node_modules/@fps-games/editor-playable-sdk/dist/rendering-profile/profile.js.map +1 -1
  105. package/node_modules/@fps-games/editor-playable-sdk/dist/rendering-profile/types.d.ts +108 -1
  106. package/node_modules/@fps-games/editor-playable-sdk/dist/rendering-profile/types.d.ts.map +1 -1
  107. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/nodes.d.ts.map +1 -1
  108. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/nodes.js +1 -0
  109. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/nodes.js.map +1 -1
  110. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/types.d.ts +2 -1
  111. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/types.d.ts.map +1 -1
  112. package/node_modules/@fps-games/editor-playable-sdk/dist/upgrade-doctor/index.d.ts +1 -1
  113. package/node_modules/@fps-games/editor-playable-sdk/dist/upgrade-doctor/index.d.ts.map +1 -1
  114. package/node_modules/@fps-games/editor-playable-sdk/dist/upgrade-doctor/index.js +1 -1
  115. package/node_modules/@fps-games/editor-playable-sdk/dist/upgrade-doctor/index.js.map +1 -1
  116. package/node_modules/@fps-games/editor-playable-sdk/dist/upgrade-doctor/inspectors.d.ts +2 -1
  117. package/node_modules/@fps-games/editor-playable-sdk/dist/upgrade-doctor/inspectors.d.ts.map +1 -1
  118. package/node_modules/@fps-games/editor-playable-sdk/dist/upgrade-doctor/inspectors.js +37 -0
  119. package/node_modules/@fps-games/editor-playable-sdk/dist/upgrade-doctor/inspectors.js.map +1 -1
  120. package/node_modules/@fps-games/editor-playable-sdk/dist/upgrade-doctor/types.d.ts +15 -0
  121. package/node_modules/@fps-games/editor-playable-sdk/dist/upgrade-doctor/types.d.ts.map +1 -1
  122. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/build-info-core.d.ts +36 -0
  123. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/build-info-core.d.ts.map +1 -0
  124. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/build-info-core.js +65 -0
  125. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/build-info-core.js.map +1 -0
  126. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/build-info-node.d.ts +4 -0
  127. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/build-info-node.d.ts.map +1 -0
  128. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/build-info-node.js +87 -0
  129. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/build-info-node.js.map +1 -0
  130. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.d.ts +5 -0
  131. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.d.ts.map +1 -0
  132. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.js +5 -0
  133. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.js.map +1 -0
  134. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/plugin.d.ts +8 -0
  135. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/plugin.d.ts.map +1 -0
  136. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/plugin.js +226 -0
  137. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/plugin.js.map +1 -0
  138. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/types.d.ts +64 -0
  139. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/types.d.ts.map +1 -0
  140. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/types.js +2 -0
  141. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/types.js.map +1 -0
  142. package/node_modules/@fps-games/editor-playable-sdk/package.json +7 -3
  143. package/node_modules/@fps-games/editor-protocol/package.json +1 -1
  144. package/package.json +12 -8
@@ -0,0 +1,717 @@
1
+ import { Camera } from '@babylonjs/core/Cameras/camera.js';
2
+ import { FreeCamera } from '@babylonjs/core/Cameras/freeCamera.js';
3
+ import { Constants } from '@babylonjs/core/Engines/constants.js';
4
+ import { DirectionalLight } from '@babylonjs/core/Lights/directionalLight.js';
5
+ import { Color3, Color4 } from '@babylonjs/core/Maths/math.color.js';
6
+ import { Vector2, Vector3 } from '@babylonjs/core/Maths/math.vector.js';
7
+ import { Effect } from '@babylonjs/core/Materials/effect.js';
8
+ import { Material } from '@babylonjs/core/Materials/material.js';
9
+ import { ShaderMaterial } from '@babylonjs/core/Materials/shaderMaterial.js';
10
+ import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial.js';
11
+ import { RenderTargetTexture } from '@babylonjs/core/Materials/Textures/renderTargetTexture.js';
12
+ import { Texture } from '@babylonjs/core/Materials/Textures/texture.js';
13
+ import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh.js';
14
+ import { InstancedMesh } from '@babylonjs/core/Meshes/instancedMesh.js';
15
+ import { Mesh } from '@babylonjs/core/Meshes/mesh.js';
16
+ import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
17
+ import { VertexBuffer } from '@babylonjs/core/Buffers/buffer.js';
18
+ import { VertexData } from '@babylonjs/core/Meshes/mesh.vertexData.js';
19
+ import { DEFAULT_STATIC_PROJECTED_SHADOW_OPTIONS, } from './types.js';
20
+ const STATIC_PROJECTED_SHADOW_ALPHA_INDEX = 2;
21
+ const STATIC_PROJECTED_BAKE_LAYER_MASK = 0x10000000;
22
+ const STATIC_PROJECTED_SHADOW_SHADER_NAME = 'fpsStaticProjectedShadow';
23
+ const DEFAULT_SHADOW_DIRECTION = new Vector3(-0.3, -1, -0.2).normalize();
24
+ export function createStaticProjectedShadowSystem(scene, directionalLight, options = {}) {
25
+ return new BabylonStaticProjectedShadowSystem(scene, directionalLight, options);
26
+ }
27
+ class BabylonStaticProjectedShadowSystem {
28
+ constructor(scene, directionalLight, options) {
29
+ this.scene = scene;
30
+ this.directionalLight = directionalLight;
31
+ this.casters = new Map();
32
+ this.meshAddedObserver = null;
33
+ this.initialized = false;
34
+ this.casterPatterns = [];
35
+ this.options = resolveStaticProjectedShadowOptions(options);
36
+ this.casterPatterns = [...this.options.casters.includePatterns];
37
+ }
38
+ initialize() {
39
+ if (this.initialized || !this.options.enabled)
40
+ return;
41
+ this.initialized = true;
42
+ this.refresh();
43
+ }
44
+ addCaster(mesh) {
45
+ const root = this.findCasterRoot(mesh);
46
+ if (!this.initialized || this.casters.has(root))
47
+ return;
48
+ if (!this.isValidCasterRoot(root))
49
+ return;
50
+ const caster = this.createCaster(root);
51
+ if (caster)
52
+ this.casters.set(root, caster);
53
+ }
54
+ removeCaster(mesh) {
55
+ this.disposeCaster(this.findCasterRoot(mesh));
56
+ }
57
+ invalidateCaster(mesh) {
58
+ const root = this.findCasterRoot(mesh);
59
+ const wasTracked = this.casters.has(root);
60
+ this.disposeCaster(root);
61
+ if (wasTracked)
62
+ this.addCaster(root);
63
+ }
64
+ enableCasterAutoDetection(patterns) {
65
+ this.options = {
66
+ ...this.options,
67
+ casters: {
68
+ ...this.options.casters,
69
+ autoDetectAll: false,
70
+ includePatterns: normalizeStringList(patterns),
71
+ },
72
+ };
73
+ this.casterPatterns = [...this.options.casters.includePatterns];
74
+ this.refresh();
75
+ this.ensureMeshObserver();
76
+ }
77
+ enableAutoDetectionForAll() {
78
+ this.options = {
79
+ ...this.options,
80
+ casters: {
81
+ ...this.options.casters,
82
+ autoDetectAll: true,
83
+ },
84
+ };
85
+ this.casterPatterns = [];
86
+ this.refresh();
87
+ this.ensureMeshObserver();
88
+ }
89
+ refresh() {
90
+ if (!this.initialized)
91
+ return;
92
+ this.pruneDisposedCasters();
93
+ for (const mesh of this.scene.meshes)
94
+ this.tryAutoAddCaster(mesh);
95
+ if (this.options.casters.autoDetectAll || this.casterPatterns.length > 0) {
96
+ this.ensureMeshObserver();
97
+ }
98
+ }
99
+ setOptions(options) {
100
+ const wasEnabled = this.options.enabled;
101
+ this.options = resolveStaticProjectedShadowOptions(options, this.options);
102
+ this.casterPatterns = [...this.options.casters.includePatterns];
103
+ if (!wasEnabled && this.options.enabled)
104
+ this.initialize();
105
+ else if (wasEnabled && !this.options.enabled)
106
+ this.dispose();
107
+ else
108
+ this.rebuildCasters();
109
+ }
110
+ getOptions() {
111
+ return cloneResolvedOptions(this.options);
112
+ }
113
+ getCasterInfo(mesh) {
114
+ const caster = this.casters.get(this.findCasterRoot(mesh));
115
+ if (!caster)
116
+ return null;
117
+ return {
118
+ source: caster.source,
119
+ shadow: caster.shadow,
120
+ };
121
+ }
122
+ getCasterCount() {
123
+ return this.casters.size;
124
+ }
125
+ dispose() {
126
+ if (this.meshAddedObserver) {
127
+ this.scene.onNewMeshAddedObservable.remove(this.meshAddedObserver);
128
+ this.meshAddedObserver = null;
129
+ }
130
+ for (const root of [...this.casters.keys()])
131
+ this.disposeCaster(root);
132
+ this.initialized = false;
133
+ }
134
+ rebuildCasters() {
135
+ const roots = [...this.casters.keys()].filter((root) => !root.isDisposed());
136
+ for (const root of roots)
137
+ this.disposeCaster(root);
138
+ for (const root of roots)
139
+ this.addCaster(root);
140
+ this.refresh();
141
+ }
142
+ createCaster(root) {
143
+ const meshes = collectNodeMeshes(root).filter((mesh) => this.isValidShadowSource(mesh));
144
+ if (meshes.length === 0)
145
+ return null;
146
+ const bounds = buildProjectedBounds(meshes, {
147
+ lightDir: this.resolveShadowDirection(),
148
+ planeY: this.options.plane.height + this.options.plane.yOffset,
149
+ padding: this.options.bake.padding,
150
+ maxSize: this.options.bake.maxSize,
151
+ });
152
+ if (!bounds)
153
+ return null;
154
+ const planeY = this.options.plane.height + this.options.plane.yOffset;
155
+ const lightDir = this.resolveShadowDirection();
156
+ const baked = createBakedShadowMask(meshes, {
157
+ scene: this.scene,
158
+ name: root.name,
159
+ bounds,
160
+ lightDir,
161
+ planeY,
162
+ resolution: this.options.bake.resolution,
163
+ });
164
+ if (!baked)
165
+ return null;
166
+ const material = this.createShadowMaterial(root.name, baked.texture);
167
+ const shadow = this.createShadowRoot(root.name, bounds);
168
+ baked.mesh.name = `${root.name}_staticProjectedShape_0`;
169
+ baked.mesh.material = material;
170
+ baked.mesh.parent = shadow;
171
+ baked.mesh.alphaIndex = STATIC_PROJECTED_SHADOW_ALPHA_INDEX;
172
+ return {
173
+ source: root,
174
+ shadow,
175
+ material,
176
+ texture: baked.texture,
177
+ shadowMeshes: [baked.mesh],
178
+ };
179
+ }
180
+ createShadowMaterial(name, texture) {
181
+ registerStaticProjectedShadowShaders();
182
+ const material = new ShaderMaterial(`${name}_staticProjectedShadowMaterial`, this.scene, { vertex: STATIC_PROJECTED_SHADOW_SHADER_NAME, fragment: STATIC_PROJECTED_SHADOW_SHADER_NAME }, {
183
+ needAlphaBlending: true,
184
+ needAlphaTesting: false,
185
+ attributes: ['position', 'uv'],
186
+ uniforms: ['worldViewProjection', 'u_shadowColor', 'u_texelSize', 'u_blurRadius'],
187
+ samplers: ['shadowSampler'],
188
+ });
189
+ const color = this.options.appearance.color;
190
+ material.backFaceCulling = false;
191
+ material.disableDepthWrite = true;
192
+ material.depthFunction = Constants.LEQUAL;
193
+ material.transparencyMode = Material.MATERIAL_ALPHABLEND;
194
+ material.alphaMode = Constants.ALPHA_COMBINE;
195
+ material.needAlphaBlending = () => true;
196
+ material.setTexture('shadowSampler', texture);
197
+ material.setColor4('u_shadowColor', new Color4(clamp01(color.r), clamp01(color.g), clamp01(color.b), clamp01(color.a)));
198
+ material.setVector2('u_texelSize', new Vector2(1 / texture.getSize().width, 1 / texture.getSize().height));
199
+ material.setFloat('u_blurRadius', clamp(this.options.bake.blur, 0, 32));
200
+ return material;
201
+ }
202
+ createShadowRoot(name, bounds) {
203
+ const shadow = new Mesh(`${name}_staticProjectedShadow`, this.scene);
204
+ const vertexData = new VertexData();
205
+ vertexData.positions = [bounds.center.x, bounds.center.y, bounds.center.z];
206
+ vertexData.indices = [];
207
+ vertexData.applyToMesh(shadow);
208
+ shadow.isPickable = false;
209
+ shadow.receiveShadows = false;
210
+ shadow.alphaIndex = STATIC_PROJECTED_SHADOW_ALPHA_INDEX;
211
+ shadow.renderOutline = false;
212
+ shadow.renderOverlay = false;
213
+ shadow.metadata = {
214
+ ...(shadow.metadata && typeof shadow.metadata === 'object' ? shadow.metadata : {}),
215
+ disableStaticProjectedShadow: true,
216
+ staticProjectedShadowInternal: true,
217
+ };
218
+ return shadow;
219
+ }
220
+ disposeCaster(root) {
221
+ const caster = this.casters.get(root);
222
+ if (!caster)
223
+ return;
224
+ for (const mesh of caster.shadowMeshes) {
225
+ if (!mesh.isDisposed())
226
+ mesh.dispose();
227
+ }
228
+ caster.shadow.dispose();
229
+ const targetIndex = this.scene.customRenderTargets.indexOf(caster.texture);
230
+ if (targetIndex >= 0)
231
+ this.scene.customRenderTargets.splice(targetIndex, 1);
232
+ caster.texture.dispose();
233
+ caster.material.dispose();
234
+ this.casters.delete(root);
235
+ }
236
+ tryAutoAddCaster(mesh) {
237
+ if (!this.initialized || this.casters.has(mesh))
238
+ return;
239
+ if (!this.isValidShadowSource(mesh))
240
+ return;
241
+ if (!this.options.casters.autoDetectAll && !this.matchesCasterPattern(mesh))
242
+ return;
243
+ const root = this.findCasterRoot(mesh);
244
+ if (this.isAlreadyCoveredByCaster(root))
245
+ return;
246
+ this.addCaster(root);
247
+ }
248
+ ensureMeshObserver() {
249
+ if (this.meshAddedObserver)
250
+ return;
251
+ this.meshAddedObserver = this.scene.onNewMeshAddedObservable.add((mesh) => {
252
+ queueMicrotask(() => {
253
+ if (!this.initialized || mesh.isDisposed())
254
+ return;
255
+ this.tryAutoAddCaster(mesh);
256
+ });
257
+ });
258
+ }
259
+ pruneDisposedCasters() {
260
+ for (const [root, caster] of [...this.casters]) {
261
+ if (root.isDisposed() || caster.shadow.isDisposed())
262
+ this.disposeCaster(root);
263
+ }
264
+ }
265
+ resolveShadowDirection() {
266
+ const direction = this.directionalLight.direction;
267
+ if (direction?.lengthSquared?.() > 1e-6)
268
+ return direction.clone().normalize();
269
+ return DEFAULT_SHADOW_DIRECTION.clone();
270
+ }
271
+ isValidShadowSource(mesh) {
272
+ if (mesh.isDisposed() || !mesh.isEnabled(false))
273
+ return false;
274
+ if (!(mesh instanceof Mesh) && !(mesh instanceof InstancedMesh))
275
+ return false;
276
+ const geometrySource = mesh instanceof InstancedMesh ? mesh.sourceMesh : mesh;
277
+ if (!geometrySource.geometry)
278
+ return false;
279
+ if (!mesh.isVisible)
280
+ return false;
281
+ if (this.isInternalMesh(mesh))
282
+ return false;
283
+ if (this.matchesExcludedPattern(mesh))
284
+ return false;
285
+ mesh.computeWorldMatrix(true);
286
+ const bounds = mesh.getHierarchyBoundingVectors(true);
287
+ const size = bounds.max.subtract(bounds.min);
288
+ const volume = Math.abs(size.x * size.y * size.z);
289
+ return Number.isFinite(volume) && volume >= this.options.casters.minVolume;
290
+ }
291
+ isValidCasterRoot(mesh) {
292
+ if (mesh.isDisposed() || !mesh.isEnabled(false))
293
+ return false;
294
+ if (this.isInternalMesh(mesh))
295
+ return false;
296
+ if (this.matchesExcludedPattern(mesh))
297
+ return false;
298
+ return true;
299
+ }
300
+ matchesCasterPattern(mesh) {
301
+ return this.matchesAnyPattern(mesh, this.casterPatterns);
302
+ }
303
+ matchesExcludedPattern(mesh) {
304
+ return this.matchesAnyPattern(mesh, this.options.casters.excludePatterns);
305
+ }
306
+ matchesAnyPattern(mesh, patterns) {
307
+ if (patterns.length === 0)
308
+ return false;
309
+ const names = this.collectMeshNames(mesh);
310
+ return patterns.some((pattern) => {
311
+ const normalized = pattern.trim().toLowerCase();
312
+ return normalized.length > 0 && names.some((name) => name.includes(normalized));
313
+ });
314
+ }
315
+ collectMeshNames(mesh) {
316
+ const names = new Set();
317
+ let cursor = mesh;
318
+ while (cursor && typeof cursor === 'object') {
319
+ const name = typeof cursor.name === 'string' ? cursor.name : '';
320
+ if (name)
321
+ names.add(name.toLowerCase());
322
+ cursor = cursor.parent;
323
+ }
324
+ return [...names];
325
+ }
326
+ isInternalMesh(mesh) {
327
+ const metadata = mesh.metadata;
328
+ return !!metadata
329
+ && typeof metadata === 'object'
330
+ && (metadata.disableStaticProjectedShadow === true
331
+ || metadata.staticProjectedShadowInternal === true
332
+ || metadata.disableBlobShadow === true
333
+ || metadata.blobShadowInternal === true
334
+ || metadata.disablePlanarShadow === true
335
+ || metadata.planarShadowInternal === true);
336
+ }
337
+ findCasterRoot(mesh) {
338
+ let cursor = mesh;
339
+ let parent = mesh.parent;
340
+ while (parent instanceof AbstractMesh || parent instanceof TransformNode) {
341
+ if (parent instanceof AbstractMesh && this.matchesAnyPattern(parent, this.options.casters.rootBoundaryPatterns)) {
342
+ return parent;
343
+ }
344
+ if (parent instanceof AbstractMesh)
345
+ cursor = parent;
346
+ parent = parent.parent;
347
+ }
348
+ return cursor;
349
+ }
350
+ isAlreadyCoveredByCaster(root) {
351
+ if (this.casters.has(root))
352
+ return true;
353
+ for (const caster of this.casters.keys()) {
354
+ if (root.isDescendantOf(caster) || caster.isDescendantOf(root))
355
+ return true;
356
+ }
357
+ return false;
358
+ }
359
+ }
360
+ export function resolveStaticProjectedShadowOptions(options = {}, fallback = DEFAULT_STATIC_PROJECTED_SHADOW_OPTIONS) {
361
+ return {
362
+ enabled: readBoolean(options.enabled, fallback.enabled),
363
+ plane: {
364
+ height: readFiniteNumber(options.plane?.height, fallback.plane.height),
365
+ yOffset: readFiniteNumber(options.plane?.yOffset, fallback.plane.yOffset),
366
+ },
367
+ appearance: {
368
+ color: {
369
+ r: readClampedNumber(options.appearance?.color?.r, fallback.appearance.color.r, 0, 1),
370
+ g: readClampedNumber(options.appearance?.color?.g, fallback.appearance.color.g, 0, 1),
371
+ b: readClampedNumber(options.appearance?.color?.b, fallback.appearance.color.b, 0, 1),
372
+ a: readClampedNumber(options.appearance?.color?.a, fallback.appearance.color.a, 0, 1),
373
+ },
374
+ },
375
+ direction: {
376
+ mode: 'follow-light',
377
+ },
378
+ bake: {
379
+ resolution: readPositiveNumber(options.bake?.resolution, fallback.bake.resolution),
380
+ padding: readNonNegativeNumber(options.bake?.padding, fallback.bake.padding),
381
+ blur: readNonNegativeNumber(options.bake?.blur, fallback.bake.blur),
382
+ maxSize: readPositiveNumber(options.bake?.maxSize, fallback.bake.maxSize),
383
+ },
384
+ casters: {
385
+ autoDetectAll: readBoolean(options.casters?.autoDetectAll, fallback.casters.autoDetectAll),
386
+ includePatterns: normalizeStringList(options.casters?.includePatterns ?? fallback.casters.includePatterns),
387
+ excludePatterns: normalizeStringList(options.casters?.excludePatterns ?? fallback.casters.excludePatterns),
388
+ rootBoundaryPatterns: normalizeStringList(options.casters?.rootBoundaryPatterns ?? fallback.casters.rootBoundaryPatterns),
389
+ minVolume: readNonNegativeNumber(options.casters?.minVolume, fallback.casters.minVolume),
390
+ },
391
+ debug: readBoolean(options.debug, fallback.debug),
392
+ };
393
+ }
394
+ function buildProjectedBounds(meshes, options) {
395
+ const lightDir = options.lightDir.clone().normalize();
396
+ if (Math.abs(lightDir.y) < 1e-5)
397
+ return null;
398
+ const points = [];
399
+ for (const mesh of meshes) {
400
+ const geometrySource = mesh instanceof InstancedMesh ? mesh.sourceMesh : mesh;
401
+ if (!(geometrySource instanceof Mesh))
402
+ continue;
403
+ const positions = geometrySource.getVerticesData(VertexBuffer.PositionKind);
404
+ if (!positions || positions.length < 3)
405
+ continue;
406
+ mesh.computeWorldMatrix(true);
407
+ const matrix = mesh.getWorldMatrix();
408
+ for (let index = 0; index < positions.length; index += 3) {
409
+ const point = Vector3.TransformCoordinates(new Vector3(Number(positions[index]), Number(positions[index + 1]), Number(positions[index + 2])), matrix);
410
+ const t = (options.planeY - point.y) / lightDir.y;
411
+ if (t < -1e-4)
412
+ continue;
413
+ points.push(point.add(lightDir.scale(Math.max(0, t))));
414
+ }
415
+ }
416
+ if (points.length === 0)
417
+ return null;
418
+ let minX = Infinity;
419
+ let maxX = -Infinity;
420
+ let minZ = Infinity;
421
+ let maxZ = -Infinity;
422
+ for (const point of points) {
423
+ minX = Math.min(minX, point.x);
424
+ maxX = Math.max(maxX, point.x);
425
+ minZ = Math.min(minZ, point.z);
426
+ maxZ = Math.max(maxZ, point.z);
427
+ }
428
+ if (![minX, maxX, minZ, maxZ].every(Number.isFinite))
429
+ return null;
430
+ const paddingX = Math.max(options.padding, (maxX - minX) * options.padding);
431
+ const paddingZ = Math.max(options.padding, (maxZ - minZ) * options.padding);
432
+ minX -= paddingX;
433
+ maxX += paddingX;
434
+ minZ -= paddingZ;
435
+ maxZ += paddingZ;
436
+ const centerX = (minX + maxX) * 0.5;
437
+ const centerZ = (minZ + maxZ) * 0.5;
438
+ let width = maxX - minX;
439
+ let depth = maxZ - minZ;
440
+ if (width > options.maxSize) {
441
+ width = options.maxSize;
442
+ minX = centerX - width * 0.5;
443
+ maxX = centerX + width * 0.5;
444
+ }
445
+ if (depth > options.maxSize) {
446
+ depth = options.maxSize;
447
+ minZ = centerZ - depth * 0.5;
448
+ maxZ = centerZ + depth * 0.5;
449
+ }
450
+ return {
451
+ center: new Vector3((minX + maxX) * 0.5, options.planeY, (minZ + maxZ) * 0.5),
452
+ width,
453
+ depth,
454
+ minX,
455
+ maxX,
456
+ minZ,
457
+ maxZ,
458
+ };
459
+ }
460
+ function collectNodeMeshes(root) {
461
+ const meshes = new Set();
462
+ meshes.add(root);
463
+ for (const child of root.getChildMeshes(false))
464
+ meshes.add(child);
465
+ return [...meshes];
466
+ }
467
+ function createBakedShadowMask(meshes, options) {
468
+ const resolution = clamp(Math.round(options.resolution), 16, 2048);
469
+ const bakeMeshes = createProjectedBakeMeshes(meshes, {
470
+ scene: options.scene,
471
+ name: options.name,
472
+ lightDir: options.lightDir,
473
+ planeY: options.planeY,
474
+ });
475
+ if (bakeMeshes.length === 0)
476
+ return null;
477
+ const bakeCamera = createBakeCamera(`${options.name}_staticProjectedBakeCamera`, options.scene, options.bounds);
478
+ const bakeMaterial = createBakeMaterial(`${options.name}_staticProjectedBakeMaterial`, options.scene);
479
+ const texture = new RenderTargetTexture(`${options.name}_staticProjectedShadowMask`, resolution, options.scene, false, false);
480
+ texture.clearColor = new Color4(0, 0, 0, 0);
481
+ texture.hasAlpha = true;
482
+ texture.wrapU = Texture.CLAMP_ADDRESSMODE;
483
+ texture.wrapV = Texture.CLAMP_ADDRESSMODE;
484
+ texture.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
485
+ texture.renderList = bakeMeshes;
486
+ texture.activeCamera = bakeCamera;
487
+ texture.cameraForLOD = bakeCamera;
488
+ texture.disableImageProcessing = true;
489
+ texture.renderParticles = false;
490
+ texture.renderSprites = false;
491
+ texture.enableOutlineRendering = false;
492
+ texture.refreshRate = RenderTargetTexture.REFRESHRATE_RENDER_ONCE;
493
+ texture.setMaterialForRendering(bakeMeshes, bakeMaterial);
494
+ try {
495
+ texture.render(false);
496
+ }
497
+ catch {
498
+ for (const mesh of bakeMeshes)
499
+ mesh.dispose();
500
+ const targetIndex = options.scene.customRenderTargets.indexOf(texture);
501
+ if (targetIndex >= 0)
502
+ options.scene.customRenderTargets.splice(targetIndex, 1);
503
+ texture.dispose();
504
+ bakeCamera.dispose();
505
+ bakeMaterial.dispose();
506
+ return null;
507
+ }
508
+ texture.renderList = [];
509
+ texture.activeCamera = null;
510
+ texture.cameraForLOD = null;
511
+ for (const mesh of bakeMeshes)
512
+ mesh.dispose();
513
+ bakeCamera.dispose();
514
+ bakeMaterial.dispose();
515
+ const mesh = createShadowQuad(`${options.name}_staticProjectedShadowBake`, options.scene, options.bounds);
516
+ return { mesh, texture };
517
+ }
518
+ function createProjectedBakeMeshes(meshes, options) {
519
+ const lightDir = options.lightDir.clone().normalize();
520
+ if (Math.abs(lightDir.y) < 1e-5)
521
+ return [];
522
+ const bakeMeshes = [];
523
+ meshes.forEach((mesh, meshIndex) => {
524
+ const geometrySource = mesh instanceof InstancedMesh ? mesh.sourceMesh : mesh;
525
+ if (!(geometrySource instanceof Mesh))
526
+ return;
527
+ const positions = geometrySource.getVerticesData(VertexBuffer.PositionKind);
528
+ const indices = geometrySource.getIndices();
529
+ if (!positions || positions.length < 3 || !indices || indices.length < 3)
530
+ return;
531
+ mesh.computeWorldMatrix(true);
532
+ const matrix = mesh.getWorldMatrix();
533
+ const projectedPositions = [];
534
+ for (let index = 0; index < positions.length; index += 3) {
535
+ const point = Vector3.TransformCoordinates(new Vector3(Number(positions[index]), Number(positions[index + 1]), Number(positions[index + 2])), matrix);
536
+ const t = (options.planeY - point.y) / lightDir.y;
537
+ const projected = point.add(lightDir.scale(Math.max(0, t)));
538
+ projectedPositions.push(projected.x, options.planeY, projected.z);
539
+ }
540
+ const bakeMesh = new Mesh(`${options.name}_staticProjectedBakeProjection_${meshIndex}`, options.scene);
541
+ const vertexData = new VertexData();
542
+ vertexData.positions = projectedPositions;
543
+ vertexData.indices = [...indices];
544
+ vertexData.applyToMesh(bakeMesh);
545
+ bakeMesh.isPickable = false;
546
+ bakeMesh.receiveShadows = false;
547
+ bakeMesh.alphaIndex = STATIC_PROJECTED_SHADOW_ALPHA_INDEX;
548
+ bakeMesh.layerMask = STATIC_PROJECTED_BAKE_LAYER_MASK;
549
+ bakeMesh.metadata = {
550
+ ...(bakeMesh.metadata && typeof bakeMesh.metadata === 'object' ? bakeMesh.metadata : {}),
551
+ disableStaticProjectedShadow: true,
552
+ staticProjectedShadowInternal: true,
553
+ };
554
+ bakeMeshes.push(bakeMesh);
555
+ });
556
+ return bakeMeshes;
557
+ }
558
+ function createBakeCamera(name, scene, bounds) {
559
+ const cameraHeight = Math.max(bounds.width, bounds.depth, 1) + 10;
560
+ const camera = new FreeCamera(name, new Vector3(bounds.center.x, bounds.center.y + cameraHeight, bounds.center.z), scene);
561
+ camera.mode = Camera.ORTHOGRAPHIC_CAMERA;
562
+ camera.orthoLeft = -Math.max(bounds.width, 1e-4) * 0.5;
563
+ camera.orthoRight = Math.max(bounds.width, 1e-4) * 0.5;
564
+ camera.orthoBottom = -Math.max(bounds.depth, 1e-4) * 0.5;
565
+ camera.orthoTop = Math.max(bounds.depth, 1e-4) * 0.5;
566
+ camera.minZ = 0.01;
567
+ camera.maxZ = cameraHeight + 1;
568
+ camera.layerMask = STATIC_PROJECTED_BAKE_LAYER_MASK;
569
+ camera.upVector = Vector3.Forward();
570
+ camera.setTarget(bounds.center);
571
+ return camera;
572
+ }
573
+ function createBakeMaterial(name, scene) {
574
+ const material = new StandardMaterial(name, scene);
575
+ material.diffuseColor = Color3.White();
576
+ material.emissiveColor = Color3.White();
577
+ material.alpha = 1;
578
+ material.backFaceCulling = false;
579
+ material.disableLighting = true;
580
+ material.disableDepthWrite = true;
581
+ material.transparencyMode = Material.MATERIAL_OPAQUE;
582
+ return material;
583
+ }
584
+ function registerStaticProjectedShadowShaders() {
585
+ const vertexKey = `${STATIC_PROJECTED_SHADOW_SHADER_NAME}VertexShader`;
586
+ if (Effect.ShadersStore[vertexKey])
587
+ return;
588
+ Effect.ShadersStore[vertexKey] = `
589
+ precision highp float;
590
+
591
+ attribute vec3 position;
592
+ attribute vec2 uv;
593
+
594
+ uniform mat4 worldViewProjection;
595
+
596
+ varying vec2 v_uv;
597
+
598
+ void main() {
599
+ v_uv = uv;
600
+ gl_Position = worldViewProjection * vec4(position, 1.0);
601
+ }
602
+ `;
603
+ Effect.ShadersStore[`${STATIC_PROJECTED_SHADOW_SHADER_NAME}FragmentShader`] = `
604
+ precision highp float;
605
+
606
+ uniform sampler2D shadowSampler;
607
+ uniform vec4 u_shadowColor;
608
+ uniform vec2 u_texelSize;
609
+ uniform float u_blurRadius;
610
+
611
+ varying vec2 v_uv;
612
+
613
+ float sampleMask(vec2 uv) {
614
+ vec4 mask = texture2D(shadowSampler, clamp(uv, vec2(0.0), vec2(1.0)));
615
+ return max(max(mask.r, mask.g), max(mask.b, mask.a));
616
+ }
617
+
618
+ void main() {
619
+ float radius = clamp(u_blurRadius, 0.0, 32.0);
620
+ float alpha = 0.0;
621
+ if (radius < 0.5) {
622
+ alpha = sampleMask(v_uv);
623
+ } else {
624
+ vec2 stepSize = u_texelSize * radius;
625
+ alpha += sampleMask(v_uv) * 0.24;
626
+ alpha += sampleMask(v_uv + vec2(stepSize.x, 0.0)) * 0.12;
627
+ alpha += sampleMask(v_uv - vec2(stepSize.x, 0.0)) * 0.12;
628
+ alpha += sampleMask(v_uv + vec2(0.0, stepSize.y)) * 0.12;
629
+ alpha += sampleMask(v_uv - vec2(0.0, stepSize.y)) * 0.12;
630
+ alpha += sampleMask(v_uv + stepSize) * 0.07;
631
+ alpha += sampleMask(v_uv - stepSize) * 0.07;
632
+ alpha += sampleMask(v_uv + vec2(stepSize.x, -stepSize.y)) * 0.07;
633
+ alpha += sampleMask(v_uv + vec2(-stepSize.x, stepSize.y)) * 0.07;
634
+ }
635
+ alpha *= u_shadowColor.a;
636
+ if (alpha <= 0.001) discard;
637
+ gl_FragColor = vec4(u_shadowColor.rgb, alpha);
638
+ }
639
+ `;
640
+ }
641
+ function createShadowQuad(name, scene, bounds) {
642
+ const mesh = new Mesh(name, scene);
643
+ const vertexData = new VertexData();
644
+ vertexData.positions = [
645
+ bounds.minX, bounds.center.y, bounds.minZ,
646
+ bounds.maxX, bounds.center.y, bounds.minZ,
647
+ bounds.maxX, bounds.center.y, bounds.maxZ,
648
+ bounds.minX, bounds.center.y, bounds.maxZ,
649
+ ];
650
+ vertexData.indices = [0, 1, 2, 0, 2, 3];
651
+ vertexData.normals = [
652
+ 0, 1, 0,
653
+ 0, 1, 0,
654
+ 0, 1, 0,
655
+ 0, 1, 0,
656
+ ];
657
+ vertexData.uvs = [
658
+ 0, 0,
659
+ 1, 0,
660
+ 1, 1,
661
+ 0, 1,
662
+ ];
663
+ vertexData.applyToMesh(mesh);
664
+ mesh.isPickable = false;
665
+ mesh.metadata = {
666
+ ...(mesh.metadata && typeof mesh.metadata === 'object' ? mesh.metadata : {}),
667
+ disableStaticProjectedShadow: true,
668
+ staticProjectedShadowInternal: true,
669
+ };
670
+ return mesh;
671
+ }
672
+ function cloneResolvedOptions(options) {
673
+ return {
674
+ enabled: options.enabled,
675
+ plane: { ...options.plane },
676
+ appearance: {
677
+ color: { ...options.appearance.color },
678
+ },
679
+ direction: { ...options.direction },
680
+ bake: { ...options.bake },
681
+ casters: {
682
+ autoDetectAll: options.casters.autoDetectAll,
683
+ includePatterns: [...options.casters.includePatterns],
684
+ excludePatterns: [...options.casters.excludePatterns],
685
+ rootBoundaryPatterns: [...options.casters.rootBoundaryPatterns],
686
+ minVolume: options.casters.minVolume,
687
+ },
688
+ debug: options.debug,
689
+ };
690
+ }
691
+ function normalizeStringList(value) {
692
+ return [...new Set((value ?? []).map((item) => item.trim()).filter(Boolean))];
693
+ }
694
+ function readBoolean(value, fallback) {
695
+ return typeof value === 'boolean' ? value : fallback;
696
+ }
697
+ function readFiniteNumber(value, fallback) {
698
+ return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
699
+ }
700
+ function readNonNegativeNumber(value, fallback) {
701
+ const next = readFiniteNumber(value, fallback);
702
+ return next >= 0 ? next : fallback;
703
+ }
704
+ function readPositiveNumber(value, fallback) {
705
+ const next = readFiniteNumber(value, fallback);
706
+ return next > 0 ? next : fallback;
707
+ }
708
+ function readClampedNumber(value, fallback, min, max) {
709
+ return clamp(readFiniteNumber(value, fallback), min, max);
710
+ }
711
+ function clamp01(value) {
712
+ return clamp(value, 0, 1);
713
+ }
714
+ function clamp(value, min, max) {
715
+ return Math.min(max, Math.max(min, value));
716
+ }
717
+ //# sourceMappingURL=static-projected-shadow-system.js.map