@nice2dev/ui-3d 1.3.14 → 1.3.16

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 (108) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/collaborative/collaborativeScene.js +160 -206
  3. package/dist/cjs/core/editorAsk.js +51 -41
  4. package/dist/cjs/core/i18n.js +13 -14
  5. package/dist/cjs/core/threeCompat.js +29 -42
  6. package/dist/cjs/dance/DanceBridge.js +148 -177
  7. package/dist/cjs/dance/DanceScoreEngine.js +209 -221
  8. package/dist/cjs/dance/PoseDetector.js +302 -275
  9. package/dist/cjs/index.js +110 -113
  10. package/dist/cjs/material/MaterialEditor.module.css.js +242 -5
  11. package/dist/cjs/material/NiceMaterialEditor.js +1213 -757
  12. package/dist/cjs/material/materialEditorTypes.js +35 -49
  13. package/dist/cjs/material/materialEditorUtils.js +684 -747
  14. package/dist/cjs/material/materialNodeDefinitions.js +1411 -1459
  15. package/dist/cjs/model/ModelEditor.js +90 -40
  16. package/dist/cjs/model/ModelEditor.module.css.js +257 -5
  17. package/dist/cjs/model/ModelEditorLeftPanel.js +126 -47
  18. package/dist/cjs/model/ModelEditorMenuBar.js +359 -32
  19. package/dist/cjs/model/ModelEditorRightPanel.js +647 -131
  20. package/dist/cjs/model/ModelEditorSubComponents.js +277 -112
  21. package/dist/cjs/model/ModelEditorTimeline.js +232 -34
  22. package/dist/cjs/model/ModelEditorToolbar.js +170 -14
  23. package/dist/cjs/model/ModelEditorViewport.js +231 -39
  24. package/dist/cjs/model/ModelViewer.js +388 -63
  25. package/dist/cjs/model/ModelViewer.module.css.js +77 -5
  26. package/dist/cjs/model/NiceArmatureEditor.js +473 -274
  27. package/dist/cjs/model/NiceMorphTargetEditor.js +333 -227
  28. package/dist/cjs/model/NiceOctree.js +372 -320
  29. package/dist/cjs/model/NicePhysicsSimulation.js +528 -298
  30. package/dist/cjs/model/NiceProceduralGeometry.js +673 -300
  31. package/dist/cjs/model/NiceTerrainEditor.js +595 -385
  32. package/dist/cjs/model/NiceWeightPainter.js +384 -261
  33. package/dist/cjs/model/NiceXRPreview.js +385 -308
  34. package/dist/cjs/model/cadModeUtils.js +103 -106
  35. package/dist/cjs/model/editorShortcuts.js +171 -194
  36. package/dist/cjs/model/modelEditorTypes.js +100 -118
  37. package/dist/cjs/model/modelEditorUtils.js +1061 -1146
  38. package/dist/cjs/model/simsModeUtils.js +1023 -1053
  39. package/dist/cjs/model/useModelEditor.js +1977 -1933
  40. package/dist/cjs/model/useModelViewer.js +633 -665
  41. package/dist/cjs/nice2dev-ui-3d.css +1 -1
  42. package/dist/cjs/particle/NiceParticleEditor.js +1186 -571
  43. package/dist/cjs/particle/ParticleEditor.module.css.js +122 -5
  44. package/dist/cjs/particle/particleEditorTypes.js +62 -70
  45. package/dist/cjs/particle/particleEditorUtils.js +998 -1013
  46. package/dist/cjs/rendering/NiceCascadedShadows.js +381 -259
  47. package/dist/cjs/rendering/NiceRenderExport.js +424 -337
  48. package/dist/cjs/rendering/NiceSSAO.js +454 -337
  49. package/dist/cjs/rendering/NiceSSR.js +387 -261
  50. package/dist/cjs/rendering/NiceWebGPURenderer.js +359 -230
  51. package/dist/cjs/uv/NiceUVEditor.js +787 -550
  52. package/dist/cjs/uv/UVEditor.module.css.js +53 -5
  53. package/dist/cjs/uv/uvEditorTypes.js +59 -76
  54. package/dist/cjs/uv/uvEditorUtils.js +501 -575
  55. package/dist/esm/collaborative/collaborativeScene.js +163 -205
  56. package/dist/esm/core/editorAsk.js +53 -40
  57. package/dist/esm/core/i18n.js +15 -13
  58. package/dist/esm/core/threeCompat.js +18 -26
  59. package/dist/esm/dance/DanceBridge.js +151 -176
  60. package/dist/esm/dance/DanceScoreEngine.js +211 -220
  61. package/dist/esm/dance/PoseDetector.js +304 -274
  62. package/dist/esm/index.js +258 -36
  63. package/dist/esm/material/MaterialEditor.module.css.js +242 -3
  64. package/dist/esm/material/NiceMaterialEditor.js +1215 -756
  65. package/dist/esm/material/materialEditorTypes.js +45 -48
  66. package/dist/esm/material/materialEditorUtils.js +713 -746
  67. package/dist/esm/material/materialNodeDefinitions.js +1415 -1458
  68. package/dist/esm/model/ModelEditor.js +91 -39
  69. package/dist/esm/model/ModelEditor.module.css.js +257 -3
  70. package/dist/esm/model/ModelEditorLeftPanel.js +126 -45
  71. package/dist/esm/model/ModelEditorMenuBar.js +359 -30
  72. package/dist/esm/model/ModelEditorRightPanel.js +634 -114
  73. package/dist/esm/model/ModelEditorSubComponents.js +265 -94
  74. package/dist/esm/model/ModelEditorTimeline.js +232 -32
  75. package/dist/esm/model/ModelEditorToolbar.js +170 -12
  76. package/dist/esm/model/ModelEditorViewport.js +231 -37
  77. package/dist/esm/model/ModelViewer.js +389 -62
  78. package/dist/esm/model/ModelViewer.module.css.js +77 -3
  79. package/dist/esm/model/NiceArmatureEditor.js +462 -258
  80. package/dist/esm/model/NiceMorphTargetEditor.js +322 -211
  81. package/dist/esm/model/NiceOctree.js +361 -304
  82. package/dist/esm/model/NicePhysicsSimulation.js +517 -282
  83. package/dist/esm/model/NiceProceduralGeometry.js +667 -284
  84. package/dist/esm/model/NiceTerrainEditor.js +584 -369
  85. package/dist/esm/model/NiceWeightPainter.js +373 -245
  86. package/dist/esm/model/NiceXRPreview.js +374 -292
  87. package/dist/esm/model/cadModeUtils.js +95 -88
  88. package/dist/esm/model/editorShortcuts.js +172 -193
  89. package/dist/esm/model/modelEditorTypes.js +90 -100
  90. package/dist/esm/model/modelEditorUtils.js +1056 -1128
  91. package/dist/esm/model/simsModeUtils.js +1021 -1035
  92. package/dist/esm/model/useModelEditor.js +1946 -1920
  93. package/dist/esm/model/useModelViewer.js +619 -647
  94. package/dist/esm/nice2dev-ui-3d.css +1 -1
  95. package/dist/esm/particle/NiceParticleEditor.js +1188 -570
  96. package/dist/esm/particle/ParticleEditor.module.css.js +122 -3
  97. package/dist/esm/particle/particleEditorTypes.js +69 -69
  98. package/dist/esm/particle/particleEditorUtils.js +1027 -1012
  99. package/dist/esm/rendering/NiceCascadedShadows.js +370 -243
  100. package/dist/esm/rendering/NiceRenderExport.js +413 -321
  101. package/dist/esm/rendering/NiceSSAO.js +450 -328
  102. package/dist/esm/rendering/NiceSSR.js +383 -252
  103. package/dist/esm/rendering/NiceWebGPURenderer.js +326 -214
  104. package/dist/esm/uv/NiceUVEditor.js +788 -549
  105. package/dist/esm/uv/UVEditor.module.css.js +53 -3
  106. package/dist/esm/uv/uvEditorTypes.js +68 -75
  107. package/dist/esm/uv/uvEditorUtils.js +515 -557
  108. package/package.json +7 -12
@@ -1,368 +1,485 @@
1
- 'use strict';
2
-
3
- var jsxRuntime = require('react/jsx-runtime');
4
- var ui = require('@nice2dev/ui');
5
- var React = require('react');
6
- var THREE = require('three');
7
- var i18n = require('../core/i18n.js');
8
-
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const ui = require("@nice2dev/ui");
5
+ const React = require("react");
6
+ const THREE = require("three");
7
+ const i18n = require("../core/i18n.js");
9
8
  function _interopNamespaceDefault(e) {
10
- var n = Object.create(null);
9
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
11
10
  if (e) {
12
- Object.keys(e).forEach(function (k) {
13
- if (k !== 'default') {
14
- var d = Object.getOwnPropertyDescriptor(e, k);
11
+ for (const k in e) {
12
+ if (k !== "default") {
13
+ const d = Object.getOwnPropertyDescriptor(e, k);
15
14
  Object.defineProperty(n, k, d.get ? d : {
16
15
  enumerable: true,
17
- get: function () { return e[k]; }
16
+ get: () => e[k]
18
17
  });
19
18
  }
20
- });
19
+ }
21
20
  }
22
21
  n.default = e;
23
22
  return Object.freeze(n);
24
23
  }
24
+ const THREE__namespace = /* @__PURE__ */ _interopNamespaceDefault(THREE);
25
+ const SSAO_VERTEX = `
26
+ varying vec2 vUv;
27
+ void main() {
28
+ vUv = uv;
29
+ gl_Position = vec4(position, 1.0);
30
+ }
31
+ `;
32
+ const SSAO_FRAGMENT = `
33
+ uniform sampler2D tDepth;
34
+ uniform sampler2D tNoise;
35
+ uniform vec3 kernel[128];
36
+ uniform int kernelSize;
37
+ uniform vec2 resolution;
38
+ uniform mat4 projMatrix;
39
+ uniform mat4 projInverseMatrix;
40
+ uniform float cameraNear;
41
+ uniform float cameraFar;
42
+ uniform float radius;
43
+ uniform float bias;
44
+ uniform float intensity;
45
+ uniform float minDistance;
46
+ uniform float maxDistance;
25
47
 
26
- var THREE__namespace = /*#__PURE__*/_interopNamespaceDefault(THREE);
48
+ varying vec2 vUv;
27
49
 
28
- /* ── SSAO Shaders ── */
29
- const SSAO_VERTEX = `
30
- varying vec2 vUv;
31
- void main() {
32
- vUv = uv;
33
- gl_Position = vec4(position, 1.0);
34
- }
35
- `;
36
- const SSAO_FRAGMENT = `
37
- uniform sampler2D tDepth;
38
- uniform sampler2D tNoise;
39
- uniform vec3 kernel[128];
40
- uniform int kernelSize;
41
- uniform vec2 resolution;
42
- uniform mat4 projMatrix;
43
- uniform mat4 projInverseMatrix;
44
- uniform float cameraNear;
45
- uniform float cameraFar;
46
- uniform float radius;
47
- uniform float bias;
48
- uniform float intensity;
49
- uniform float minDistance;
50
- uniform float maxDistance;
51
-
52
- varying vec2 vUv;
53
-
54
- float linearDepth(float d) {
55
- return cameraNear * cameraFar / (cameraFar - d * (cameraFar - cameraNear));
56
- }
57
-
58
- vec3 getViewPos(vec2 uv) {
59
- float d = texture2D(tDepth, uv).r;
60
- vec4 clipPos = vec4(uv * 2.0 - 1.0, d * 2.0 - 1.0, 1.0);
61
- vec4 viewPos = projInverseMatrix * clipPos;
62
- return viewPos.xyz / viewPos.w;
63
- }
64
-
65
- void main() {
66
- float depth = texture2D(tDepth, vUv).r;
67
- if (depth >= 1.0) {
68
- gl_FragColor = vec4(1.0);
69
- return;
70
- }
71
-
72
- vec3 viewPos = getViewPos(vUv);
73
-
74
- // Reconstruct normal from depth buffer
75
- vec3 dpdx = dFdx(viewPos);
76
- vec3 dpdy = dFdy(viewPos);
77
- vec3 normal = normalize(cross(dpdx, dpdy));
78
-
79
- // Random rotation from noise texture
80
- vec2 noiseUv = vUv * resolution / 4.0;
81
- vec3 randomVec = texture2D(tNoise, noiseUv).xyz * 2.0 - 1.0;
82
-
83
- // Create TBN matrix
84
- vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
85
- vec3 bitangent = cross(normal, tangent);
86
- mat3 TBN = mat3(tangent, bitangent, normal);
87
-
88
- float occlusion = 0.0;
89
-
90
- for (int i = 0; i < 128; i++) {
91
- if (i >= kernelSize) break;
92
-
93
- // Transform sample to view space
94
- vec3 samplePos = TBN * kernel[i];
95
- samplePos = viewPos + samplePos * radius;
96
-
97
- // Project sample to screen
98
- vec4 offset = projMatrix * vec4(samplePos, 1.0);
99
- offset.xyz /= offset.w;
100
- vec2 sampleUv = offset.xy * 0.5 + 0.5;
101
-
102
- // Out of screen
103
- if (sampleUv.x < 0.0 || sampleUv.x > 1.0 || sampleUv.y < 0.0 || sampleUv.y > 1.0) continue;
104
-
105
- float sampleDepth = linearDepth(texture2D(tDepth, sampleUv).r);
106
- float rangeCheck = smoothstep(0.0, 1.0, radius / abs(-viewPos.z - sampleDepth));
107
-
108
- float dist = abs(-viewPos.z - sampleDepth);
109
- if (dist >= minDistance && dist <= maxDistance) {
110
- occlusion += (sampleDepth <= -samplePos.z - bias ? 1.0 : 0.0) * rangeCheck;
111
- }
112
- }
113
-
114
- occlusion = 1.0 - (occlusion / float(kernelSize)) * intensity;
115
- gl_FragColor = vec4(vec3(occlusion), 1.0);
116
- }
50
+ float linearDepth(float d) {
51
+ return cameraNear * cameraFar / (cameraFar - d * (cameraFar - cameraNear));
52
+ }
53
+
54
+ vec3 getViewPos(vec2 uv) {
55
+ float d = texture2D(tDepth, uv).r;
56
+ vec4 clipPos = vec4(uv * 2.0 - 1.0, d * 2.0 - 1.0, 1.0);
57
+ vec4 viewPos = projInverseMatrix * clipPos;
58
+ return viewPos.xyz / viewPos.w;
59
+ }
60
+
61
+ void main() {
62
+ float depth = texture2D(tDepth, vUv).r;
63
+ if (depth >= 1.0) {
64
+ gl_FragColor = vec4(1.0);
65
+ return;
66
+ }
67
+
68
+ vec3 viewPos = getViewPos(vUv);
69
+
70
+ // Reconstruct normal from depth buffer
71
+ vec3 dpdx = dFdx(viewPos);
72
+ vec3 dpdy = dFdy(viewPos);
73
+ vec3 normal = normalize(cross(dpdx, dpdy));
74
+
75
+ // Random rotation from noise texture
76
+ vec2 noiseUv = vUv * resolution / 4.0;
77
+ vec3 randomVec = texture2D(tNoise, noiseUv).xyz * 2.0 - 1.0;
78
+
79
+ // Create TBN matrix
80
+ vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
81
+ vec3 bitangent = cross(normal, tangent);
82
+ mat3 TBN = mat3(tangent, bitangent, normal);
83
+
84
+ float occlusion = 0.0;
85
+
86
+ for (int i = 0; i < 128; i++) {
87
+ if (i >= kernelSize) break;
88
+
89
+ // Transform sample to view space
90
+ vec3 samplePos = TBN * kernel[i];
91
+ samplePos = viewPos + samplePos * radius;
92
+
93
+ // Project sample to screen
94
+ vec4 offset = projMatrix * vec4(samplePos, 1.0);
95
+ offset.xyz /= offset.w;
96
+ vec2 sampleUv = offset.xy * 0.5 + 0.5;
97
+
98
+ // Out of screen
99
+ if (sampleUv.x < 0.0 || sampleUv.x > 1.0 || sampleUv.y < 0.0 || sampleUv.y > 1.0) continue;
100
+
101
+ float sampleDepth = linearDepth(texture2D(tDepth, sampleUv).r);
102
+ float rangeCheck = smoothstep(0.0, 1.0, radius / abs(-viewPos.z - sampleDepth));
103
+
104
+ float dist = abs(-viewPos.z - sampleDepth);
105
+ if (dist >= minDistance && dist <= maxDistance) {
106
+ occlusion += (sampleDepth <= -samplePos.z - bias ? 1.0 : 0.0) * rangeCheck;
107
+ }
108
+ }
109
+
110
+ occlusion = 1.0 - (occlusion / float(kernelSize)) * intensity;
111
+ gl_FragColor = vec4(vec3(occlusion), 1.0);
112
+ }
117
113
  `;
118
- const BLUR_FRAGMENT = `
119
- uniform sampler2D tAO;
120
- uniform sampler2D tDepth;
121
- uniform vec2 resolution;
122
- uniform int blurSize;
123
- uniform float cameraNear;
124
- uniform float cameraFar;
125
-
126
- varying vec2 vUv;
127
-
128
- float linearDepth(float d) {
129
- return cameraNear * cameraFar / (cameraFar - d * (cameraFar - cameraNear));
130
- }
131
-
132
- void main() {
133
- float centerDepth = linearDepth(texture2D(tDepth, vUv).r);
134
- float result = 0.0;
135
- float weightSum = 0.0;
136
-
137
- for (int x = -4; x <= 4; x++) {
138
- for (int y = -4; y <= 4; y++) {
139
- if (abs(x) > blurSize || abs(y) > blurSize) continue;
140
-
141
- vec2 offset = vec2(float(x), float(y)) / resolution;
142
- float sampleDepth = linearDepth(texture2D(tDepth, vUv + offset).r);
143
-
144
- // Bilateral weight based on depth difference
145
- float depthWeight = exp(-abs(centerDepth - sampleDepth) * 10.0);
146
- float spatialWeight = exp(-float(x * x + y * y) / (2.0 * float(blurSize * blurSize)));
147
- float weight = depthWeight * spatialWeight;
148
-
149
- result += texture2D(tAO, vUv + offset).r * weight;
150
- weightSum += weight;
151
- }
152
- }
153
-
154
- gl_FragColor = vec4(vec3(result / weightSum), 1.0);
155
- }
114
+ const BLUR_FRAGMENT = `
115
+ uniform sampler2D tAO;
116
+ uniform sampler2D tDepth;
117
+ uniform vec2 resolution;
118
+ uniform int blurSize;
119
+ uniform float cameraNear;
120
+ uniform float cameraFar;
121
+
122
+ varying vec2 vUv;
123
+
124
+ float linearDepth(float d) {
125
+ return cameraNear * cameraFar / (cameraFar - d * (cameraFar - cameraNear));
126
+ }
127
+
128
+ void main() {
129
+ float centerDepth = linearDepth(texture2D(tDepth, vUv).r);
130
+ float result = 0.0;
131
+ float weightSum = 0.0;
132
+
133
+ for (int x = -4; x <= 4; x++) {
134
+ for (int y = -4; y <= 4; y++) {
135
+ if (abs(x) > blurSize || abs(y) > blurSize) continue;
136
+
137
+ vec2 offset = vec2(float(x), float(y)) / resolution;
138
+ float sampleDepth = linearDepth(texture2D(tDepth, vUv + offset).r);
139
+
140
+ // Bilateral weight based on depth difference
141
+ float depthWeight = exp(-abs(centerDepth - sampleDepth) * 10.0);
142
+ float spatialWeight = exp(-float(x * x + y * y) / (2.0 * float(blurSize * blurSize)));
143
+ float weight = depthWeight * spatialWeight;
144
+
145
+ result += texture2D(tAO, vUv + offset).r * weight;
146
+ weightSum += weight;
147
+ }
148
+ }
149
+
150
+ gl_FragColor = vec4(vec3(result / weightSum), 1.0);
151
+ }
156
152
  `;
157
- /* ── Generate kernel and noise ── */
158
153
  function generateSSAOKernel(size) {
159
- const kernel = new Float32Array(size * 3);
160
- for (let i = 0; i < size; i++) {
161
- // Hemisphere samples
162
- let x = Math.random() * 2 - 1;
163
- let y = Math.random() * 2 - 1;
164
- let z = Math.random(); // hemisphere — positive z only
165
- const len = Math.sqrt(x * x + y * y + z * z);
166
- x /= len;
167
- y /= len;
168
- z /= len;
169
- // Scale to distribute more samples closer to origin
170
- let scale = i / size;
171
- scale = 0.1 + scale * scale * 0.9; // lerp(0.1, 1.0, scale^2)
172
- kernel[i * 3] = x * scale;
173
- kernel[i * 3 + 1] = y * scale;
174
- kernel[i * 3 + 2] = z * scale;
175
- }
176
- return kernel;
154
+ const kernel = new Float32Array(size * 3);
155
+ for (let i = 0; i < size; i++) {
156
+ let x = Math.random() * 2 - 1;
157
+ let y = Math.random() * 2 - 1;
158
+ let z = Math.random();
159
+ const len = Math.sqrt(x * x + y * y + z * z);
160
+ x /= len;
161
+ y /= len;
162
+ z /= len;
163
+ let scale = i / size;
164
+ scale = 0.1 + scale * scale * 0.9;
165
+ kernel[i * 3] = x * scale;
166
+ kernel[i * 3 + 1] = y * scale;
167
+ kernel[i * 3 + 2] = z * scale;
168
+ }
169
+ return kernel;
177
170
  }
178
171
  function generateNoiseTexture() {
179
- const size = 4;
180
- const data = new Float32Array(size * size * 4);
181
- for (let i = 0; i < size * size; i++) {
182
- data[i * 4] = Math.random() * 2 - 1;
183
- data[i * 4 + 1] = Math.random() * 2 - 1;
184
- data[i * 4 + 2] = 0;
185
- data[i * 4 + 3] = 1;
186
- }
187
- const tex = new THREE__namespace.DataTexture(data, size, size, THREE__namespace.RGBAFormat, THREE__namespace.FloatType);
188
- tex.wrapS = THREE__namespace.RepeatWrapping;
189
- tex.wrapT = THREE__namespace.RepeatWrapping;
190
- tex.needsUpdate = true;
191
- return tex;
172
+ const size = 4;
173
+ const data = new Float32Array(size * size * 4);
174
+ for (let i = 0; i < size * size; i++) {
175
+ data[i * 4] = Math.random() * 2 - 1;
176
+ data[i * 4 + 1] = Math.random() * 2 - 1;
177
+ data[i * 4 + 2] = 0;
178
+ data[i * 4 + 3] = 1;
179
+ }
180
+ const tex = new THREE__namespace.DataTexture(data, size, size, THREE__namespace.RGBAFormat, THREE__namespace.FloatType);
181
+ tex.wrapS = THREE__namespace.RepeatWrapping;
182
+ tex.wrapT = THREE__namespace.RepeatWrapping;
183
+ tex.needsUpdate = true;
184
+ return tex;
192
185
  }
193
- /* ── Hook ── */
194
186
  const DEFAULT_SSAO = {
195
- enabled: true,
196
- mode: 'ssao',
197
- samples: 32,
198
- radius: 1.0,
199
- bias: 0.025,
200
- intensity: 1.5,
201
- resolution: 0.5,
202
- blurEnabled: true,
203
- blurSize: 2,
204
- minDistance: 0.01,
205
- maxDistance: 10,
187
+ enabled: true,
188
+ mode: "ssao",
189
+ samples: 32,
190
+ radius: 1,
191
+ bias: 0.025,
192
+ intensity: 1.5,
193
+ resolution: 0.5,
194
+ blurEnabled: true,
195
+ blurSize: 2,
196
+ minDistance: 0.01,
197
+ maxDistance: 10
206
198
  };
207
199
  function useSSAO(renderer, scene, camera, configOverride) {
208
- const [config, setConfigState] = React.useState({ ...DEFAULT_SSAO, ...configOverride });
209
- const [aoRenderTarget, setAoRenderTarget] = React.useState(null);
210
- const depthRT = React.useRef(null);
211
- const blurRT = React.useRef(null);
212
- const ssaoMaterial = React.useRef(null);
213
- const blurMaterial = React.useRef(null);
214
- const noiseTexture = React.useRef(generateNoiseTexture());
215
- const kernel = React.useRef(generateSSAOKernel(128));
216
- const orthoScene = React.useRef(new THREE__namespace.Scene());
217
- const orthoCamera = React.useRef(new THREE__namespace.OrthographicCamera(-1, 1, 1, -1, 0, 1));
218
- const quadRef = React.useRef(null);
219
- const setConfig = React.useCallback((partial) => {
220
- setConfigState((prev) => ({ ...prev, ...partial }));
221
- }, []);
222
- // Setup
223
- React.useEffect(() => {
224
- if (!renderer || !camera) {
225
- return;
200
+ const [config, setConfigState] = React.useState({ ...DEFAULT_SSAO, ...configOverride });
201
+ const [aoRenderTarget, setAoRenderTarget] = React.useState(null);
202
+ const depthRT = React.useRef(null);
203
+ const blurRT = React.useRef(null);
204
+ const ssaoMaterial = React.useRef(null);
205
+ const blurMaterial = React.useRef(null);
206
+ const noiseTexture = React.useRef(generateNoiseTexture());
207
+ const kernel = React.useRef(generateSSAOKernel(128));
208
+ const orthoScene = React.useRef(new THREE__namespace.Scene());
209
+ const orthoCamera = React.useRef(new THREE__namespace.OrthographicCamera(-1, 1, 1, -1, 0, 1));
210
+ const quadRef = React.useRef(null);
211
+ const setConfig = React.useCallback((partial) => {
212
+ setConfigState((prev) => ({ ...prev, ...partial }));
213
+ }, []);
214
+ React.useEffect(() => {
215
+ if (!renderer || !camera) {
216
+ return;
217
+ }
218
+ const width = Math.floor(renderer.domElement.width * config.resolution);
219
+ const height = Math.floor(renderer.domElement.height * config.resolution);
220
+ const dRT = new THREE__namespace.WebGLRenderTarget(width, height, {
221
+ minFilter: THREE__namespace.NearestFilter,
222
+ magFilter: THREE__namespace.NearestFilter,
223
+ type: THREE__namespace.FloatType
224
+ });
225
+ dRT.depthTexture = new THREE__namespace.DepthTexture(width, height);
226
+ dRT.depthTexture.type = THREE__namespace.UnsignedIntType;
227
+ depthRT.current = dRT;
228
+ const aoRT = new THREE__namespace.WebGLRenderTarget(width, height, {
229
+ minFilter: THREE__namespace.LinearFilter,
230
+ magFilter: THREE__namespace.LinearFilter
231
+ });
232
+ setAoRenderTarget(aoRT);
233
+ const bRT = new THREE__namespace.WebGLRenderTarget(width, height, {
234
+ minFilter: THREE__namespace.LinearFilter,
235
+ magFilter: THREE__namespace.LinearFilter
236
+ });
237
+ blurRT.current = bRT;
238
+ const kernelUniforms = [];
239
+ for (let i = 0; i < 128; i++) {
240
+ kernelUniforms.push(
241
+ new THREE__namespace.Vector3(
242
+ kernel.current[i * 3],
243
+ kernel.current[i * 3 + 1],
244
+ kernel.current[i * 3 + 2]
245
+ )
246
+ );
247
+ }
248
+ const ssaoMat = new THREE__namespace.ShaderMaterial({
249
+ vertexShader: SSAO_VERTEX,
250
+ fragmentShader: SSAO_FRAGMENT,
251
+ uniforms: {
252
+ tDepth: { value: dRT.depthTexture },
253
+ tNoise: { value: noiseTexture.current },
254
+ kernel: { value: kernelUniforms },
255
+ kernelSize: { value: config.samples },
256
+ resolution: { value: new THREE__namespace.Vector2(width, height) },
257
+ projMatrix: { value: camera.projectionMatrix },
258
+ projInverseMatrix: { value: camera.projectionMatrixInverse },
259
+ cameraNear: { value: camera.near },
260
+ cameraFar: { value: camera.far },
261
+ radius: { value: config.radius },
262
+ bias: { value: config.bias },
263
+ intensity: { value: config.intensity },
264
+ minDistance: { value: config.minDistance },
265
+ maxDistance: { value: config.maxDistance }
266
+ }
267
+ });
268
+ ssaoMaterial.current = ssaoMat;
269
+ const blurMat = new THREE__namespace.ShaderMaterial({
270
+ vertexShader: SSAO_VERTEX,
271
+ fragmentShader: BLUR_FRAGMENT,
272
+ uniforms: {
273
+ tAO: { value: aoRT.texture },
274
+ tDepth: { value: dRT.depthTexture },
275
+ resolution: { value: new THREE__namespace.Vector2(width, height) },
276
+ blurSize: { value: config.blurSize },
277
+ cameraNear: { value: camera.near },
278
+ cameraFar: { value: camera.far }
279
+ }
280
+ });
281
+ blurMaterial.current = blurMat;
282
+ const quad = new THREE__namespace.Mesh(new THREE__namespace.PlaneGeometry(2, 2), ssaoMat);
283
+ quad.frustumCulled = false;
284
+ const ortho = orthoScene.current;
285
+ ortho.add(quad);
286
+ quadRef.current = quad;
287
+ return () => {
288
+ dRT.dispose();
289
+ aoRT.dispose();
290
+ bRT.dispose();
291
+ ssaoMat.dispose();
292
+ blurMat.dispose();
293
+ ortho.remove(quad);
294
+ };
295
+ }, [renderer, camera, config.resolution]);
296
+ React.useEffect(() => {
297
+ if (!ssaoMaterial.current) {
298
+ return;
299
+ }
300
+ const u = ssaoMaterial.current.uniforms;
301
+ u.kernelSize.value = config.samples;
302
+ u.radius.value = config.radius;
303
+ u.bias.value = config.bias;
304
+ u.intensity.value = config.intensity;
305
+ u.minDistance.value = config.minDistance;
306
+ u.maxDistance.value = config.maxDistance;
307
+ }, [config]);
308
+ React.useEffect(() => {
309
+ if (!blurMaterial.current) {
310
+ return;
311
+ }
312
+ blurMaterial.current.uniforms.blurSize.value = config.blurSize;
313
+ }, [config.blurSize]);
314
+ const update = React.useCallback(() => {
315
+ if (!renderer || !scene || !camera || !config.enabled) {
316
+ return;
317
+ }
318
+ if (!depthRT.current || !aoRenderTarget || !quadRef.current) {
319
+ return;
320
+ }
321
+ renderer.setRenderTarget(depthRT.current);
322
+ renderer.render(scene, camera);
323
+ quadRef.current.material = ssaoMaterial.current;
324
+ renderer.setRenderTarget(aoRenderTarget);
325
+ renderer.render(orthoScene.current, orthoCamera.current);
326
+ if (config.blurEnabled && blurMaterial.current && blurRT.current) {
327
+ blurMaterial.current.uniforms.tAO.value = aoRenderTarget.texture;
328
+ quadRef.current.material = blurMaterial.current;
329
+ renderer.setRenderTarget(blurRT.current);
330
+ renderer.render(orthoScene.current, orthoCamera.current);
331
+ }
332
+ renderer.setRenderTarget(null);
333
+ }, [renderer, scene, camera, config.enabled, config.blurEnabled, aoRenderTarget]);
334
+ const dispose = React.useCallback(() => {
335
+ depthRT.current?.dispose();
336
+ aoRenderTarget?.dispose();
337
+ blurRT.current?.dispose();
338
+ ssaoMaterial.current?.dispose();
339
+ blurMaterial.current?.dispose();
340
+ noiseTexture.current?.dispose();
341
+ }, [aoRenderTarget]);
342
+ return { config, setConfig, aoRenderTarget, update, dispose };
343
+ }
344
+ const NiceSSAO = ({
345
+ renderer,
346
+ scene,
347
+ camera,
348
+ config: configProp,
349
+ className = ""
350
+ }) => {
351
+ const { t } = i18n.useNiceTranslation();
352
+ const { config, setConfig } = useSSAO(renderer, scene, camera, configProp);
353
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `nice-ssao ${className}`, style: { display: "grid", gap: 8, fontSize: 13 }, children: [
354
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
355
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
356
+ /* @__PURE__ */ jsxRuntime.jsx(ui.NiceIcon, { name: "circle", size: 14 }),
357
+ " ",
358
+ t("ssao.ambientOcclusion", "Ambient Occlusion")
359
+ ] }),
360
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 4, fontSize: 11 }, children: [
361
+ /* @__PURE__ */ jsxRuntime.jsx(
362
+ "input",
363
+ {
364
+ type: "checkbox",
365
+ checked: config.enabled,
366
+ onChange: (e) => setConfig({ enabled: e.target.checked })
367
+ }
368
+ ),
369
+ t("ssao.enabled", "Enabled")
370
+ ] })
371
+ ] }),
372
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
373
+ t("ssao.mode", "Mode:"),
374
+ /* @__PURE__ */ jsxRuntime.jsxs(
375
+ "select",
376
+ {
377
+ value: config.mode,
378
+ onChange: (e) => setConfig({ mode: e.target.value }),
379
+ style: { width: "100%", marginTop: 2 },
380
+ children: [
381
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "ssao", children: t("ssao.ssao", "SSAO") }),
382
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "hbao", children: t("ssao.hbao", "HBAO") })
383
+ ]
226
384
  }
227
- const width = Math.floor(renderer.domElement.width * config.resolution);
228
- const height = Math.floor(renderer.domElement.height * config.resolution);
229
- // Depth render target
230
- const dRT = new THREE__namespace.WebGLRenderTarget(width, height, {
231
- minFilter: THREE__namespace.NearestFilter,
232
- magFilter: THREE__namespace.NearestFilter,
233
- type: THREE__namespace.FloatType,
234
- });
235
- dRT.depthTexture = new THREE__namespace.DepthTexture(width, height);
236
- dRT.depthTexture.type = THREE__namespace.UnsignedIntType;
237
- depthRT.current = dRT;
238
- // AO render target
239
- const aoRT = new THREE__namespace.WebGLRenderTarget(width, height, {
240
- minFilter: THREE__namespace.LinearFilter,
241
- magFilter: THREE__namespace.LinearFilter,
242
- });
243
- setAoRenderTarget(aoRT);
244
- // Blur render target
245
- const bRT = new THREE__namespace.WebGLRenderTarget(width, height, {
246
- minFilter: THREE__namespace.LinearFilter,
247
- magFilter: THREE__namespace.LinearFilter,
248
- });
249
- blurRT.current = bRT;
250
- // Kernel uniform as vec3 array
251
- const kernelUniforms = [];
252
- for (let i = 0; i < 128; i++) {
253
- kernelUniforms.push(new THREE__namespace.Vector3(kernel.current[i * 3], kernel.current[i * 3 + 1], kernel.current[i * 3 + 2]));
385
+ )
386
+ ] }),
387
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
388
+ t("ssao.samples", "Samples: {samples}").replace("{samples}", String(config.samples)),
389
+ /* @__PURE__ */ jsxRuntime.jsx(
390
+ "input",
391
+ {
392
+ type: "range",
393
+ min: 8,
394
+ max: 128,
395
+ step: 8,
396
+ value: config.samples,
397
+ onChange: (e) => setConfig({ samples: parseInt(e.target.value, 10) }),
398
+ style: { width: "100%" }
254
399
  }
255
- // SSAO material
256
- const ssaoMat = new THREE__namespace.ShaderMaterial({
257
- vertexShader: SSAO_VERTEX,
258
- fragmentShader: SSAO_FRAGMENT,
259
- uniforms: {
260
- tDepth: { value: dRT.depthTexture },
261
- tNoise: { value: noiseTexture.current },
262
- kernel: { value: kernelUniforms },
263
- kernelSize: { value: config.samples },
264
- resolution: { value: new THREE__namespace.Vector2(width, height) },
265
- projMatrix: { value: camera.projectionMatrix },
266
- projInverseMatrix: { value: camera.projectionMatrixInverse },
267
- cameraNear: { value: camera.near },
268
- cameraFar: { value: camera.far },
269
- radius: { value: config.radius },
270
- bias: { value: config.bias },
271
- intensity: { value: config.intensity },
272
- minDistance: { value: config.minDistance },
273
- maxDistance: { value: config.maxDistance },
274
- },
275
- });
276
- ssaoMaterial.current = ssaoMat;
277
- // Blur material
278
- const blurMat = new THREE__namespace.ShaderMaterial({
279
- vertexShader: SSAO_VERTEX,
280
- fragmentShader: BLUR_FRAGMENT,
281
- uniforms: {
282
- tAO: { value: aoRT.texture },
283
- tDepth: { value: dRT.depthTexture },
284
- resolution: { value: new THREE__namespace.Vector2(width, height) },
285
- blurSize: { value: config.blurSize },
286
- cameraNear: { value: camera.near },
287
- cameraFar: { value: camera.far },
288
- },
289
- });
290
- blurMaterial.current = blurMat;
291
- // Full-screen quad
292
- const quad = new THREE__namespace.Mesh(new THREE__namespace.PlaneGeometry(2, 2), ssaoMat);
293
- quad.frustumCulled = false;
294
- const ortho = orthoScene.current;
295
- ortho.add(quad);
296
- quadRef.current = quad;
297
- return () => {
298
- dRT.dispose();
299
- aoRT.dispose();
300
- bRT.dispose();
301
- ssaoMat.dispose();
302
- blurMat.dispose();
303
- ortho.remove(quad);
304
- };
305
- // eslint-disable-next-line react-hooks/exhaustive-deps -- the uniforms' first values; the next effect updates them without rebuilding the render targets
306
- }, [renderer, camera, config.resolution]);
307
- // Update uniforms
308
- React.useEffect(() => {
309
- if (!ssaoMaterial.current) {
310
- return;
400
+ )
401
+ ] }),
402
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
403
+ t("ssao.radius", "Radius: {radius}").replace("{radius}", String(config.radius.toFixed(2))),
404
+ /* @__PURE__ */ jsxRuntime.jsx(
405
+ "input",
406
+ {
407
+ type: "range",
408
+ min: 0.1,
409
+ max: 5,
410
+ step: 0.1,
411
+ value: config.radius,
412
+ onChange: (e) => setConfig({ radius: parseFloat(e.target.value) }),
413
+ style: { width: "100%" }
311
414
  }
312
- const u = ssaoMaterial.current.uniforms;
313
- u.kernelSize.value = config.samples;
314
- u.radius.value = config.radius;
315
- u.bias.value = config.bias;
316
- u.intensity.value = config.intensity;
317
- u.minDistance.value = config.minDistance;
318
- u.maxDistance.value = config.maxDistance;
319
- }, [config]);
320
- React.useEffect(() => {
321
- if (!blurMaterial.current) {
322
- return;
415
+ )
416
+ ] }),
417
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
418
+ t("ssao.intensity", "Intensity: {intensity}").replace(
419
+ "{intensity}",
420
+ String(config.intensity.toFixed(2))
421
+ ),
422
+ /* @__PURE__ */ jsxRuntime.jsx(
423
+ "input",
424
+ {
425
+ type: "range",
426
+ min: 0,
427
+ max: 3,
428
+ step: 0.1,
429
+ value: config.intensity,
430
+ onChange: (e) => setConfig({ intensity: parseFloat(e.target.value) }),
431
+ style: { width: "100%" }
323
432
  }
324
- blurMaterial.current.uniforms.blurSize.value = config.blurSize;
325
- }, [config.blurSize]);
326
- const update = React.useCallback(() => {
327
- if (!renderer || !scene || !camera || !config.enabled) {
328
- return;
433
+ )
434
+ ] }),
435
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
436
+ t("ssao.bias", "Bias: {bias}").replace("{bias}", String(config.bias.toFixed(3))),
437
+ /* @__PURE__ */ jsxRuntime.jsx(
438
+ "input",
439
+ {
440
+ type: "range",
441
+ min: 1e-3,
442
+ max: 0.1,
443
+ step: 1e-3,
444
+ value: config.bias,
445
+ onChange: (e) => setConfig({ bias: parseFloat(e.target.value) }),
446
+ style: { width: "100%" }
329
447
  }
330
- if (!depthRT.current || !aoRenderTarget || !quadRef.current) {
331
- return;
448
+ )
449
+ ] }),
450
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
451
+ t("ssao.resolution", "Resolution: {resolution}").replace(
452
+ "{resolution}",
453
+ String(config.resolution.toFixed(2))
454
+ ),
455
+ /* @__PURE__ */ jsxRuntime.jsx(
456
+ "input",
457
+ {
458
+ type: "range",
459
+ min: 0.25,
460
+ max: 1,
461
+ step: 0.25,
462
+ value: config.resolution,
463
+ onChange: (e) => setConfig({ resolution: parseFloat(e.target.value) }),
464
+ style: { width: "100%" }
332
465
  }
333
- // Render depth
334
- renderer.setRenderTarget(depthRT.current);
335
- renderer.render(scene, camera);
336
- // SSAO pass
337
- quadRef.current.material = ssaoMaterial.current;
338
- renderer.setRenderTarget(aoRenderTarget);
339
- renderer.render(orthoScene.current, orthoCamera.current);
340
- // Blur pass
341
- if (config.blurEnabled && blurMaterial.current && blurRT.current) {
342
- blurMaterial.current.uniforms.tAO.value = aoRenderTarget.texture;
343
- quadRef.current.material = blurMaterial.current;
344
- renderer.setRenderTarget(blurRT.current);
345
- renderer.render(orthoScene.current, orthoCamera.current);
466
+ )
467
+ ] }),
468
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
469
+ /* @__PURE__ */ jsxRuntime.jsx(
470
+ "input",
471
+ {
472
+ type: "checkbox",
473
+ checked: config.blurEnabled,
474
+ onChange: (e) => setConfig({ blurEnabled: e.target.checked })
346
475
  }
347
- renderer.setRenderTarget(null);
348
- }, [renderer, scene, camera, config.enabled, config.blurEnabled, aoRenderTarget]);
349
- const dispose = React.useCallback(() => {
350
- var _a, _b, _c, _d, _e;
351
- (_a = depthRT.current) === null || _a === void 0 ? void 0 : _a.dispose();
352
- aoRenderTarget === null || aoRenderTarget === void 0 ? void 0 : aoRenderTarget.dispose();
353
- (_b = blurRT.current) === null || _b === void 0 ? void 0 : _b.dispose();
354
- (_c = ssaoMaterial.current) === null || _c === void 0 ? void 0 : _c.dispose();
355
- (_d = blurMaterial.current) === null || _d === void 0 ? void 0 : _d.dispose();
356
- (_e = noiseTexture.current) === null || _e === void 0 ? void 0 : _e.dispose();
357
- }, [aoRenderTarget]);
358
- return { config, setConfig, aoRenderTarget, update, dispose };
359
- }
360
- /* ── Component ── */
361
- const NiceSSAO = ({ renderer, scene, camera, config: configProp, className = '', }) => {
362
- const { t } = i18n.useNiceTranslation();
363
- const { config, setConfig } = useSSAO(renderer, scene, camera, configProp);
364
- return (jsxRuntime.jsxs("div", { className: `nice-ssao ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [jsxRuntime.jsxs("strong", { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "circle", size: 14 }), " ", t('ssao.ambientOcclusion', 'Ambient Occlusion')] }), jsxRuntime.jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 4, fontSize: 11 }, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.enabled, onChange: (e) => setConfig({ enabled: e.target.checked }) }), t('ssao.enabled', 'Enabled')] })] }), jsxRuntime.jsxs("label", { children: [t('ssao.mode', 'Mode:'), jsxRuntime.jsxs("select", { value: config.mode, onChange: (e) => setConfig({ mode: e.target.value }), style: { width: '100%', marginTop: 2 }, children: [jsxRuntime.jsx("option", { value: "ssao", children: t('ssao.ssao', 'SSAO') }), jsxRuntime.jsx("option", { value: "hbao", children: t('ssao.hbao', 'HBAO') })] })] }), jsxRuntime.jsxs("label", { children: [t('ssao.samples', 'Samples: {samples}').replace('{samples}', String(config.samples)), jsxRuntime.jsx("input", { type: "range", min: 8, max: 128, step: 8, value: config.samples, onChange: (e) => setConfig({ samples: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: [t('ssao.radius', 'Radius: {radius}').replace('{radius}', String(config.radius.toFixed(2))), jsxRuntime.jsx("input", { type: "range", min: 0.1, max: 5, step: 0.1, value: config.radius, onChange: (e) => setConfig({ radius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: [t('ssao.intensity', 'Intensity: {intensity}').replace('{intensity}', String(config.intensity.toFixed(2))), jsxRuntime.jsx("input", { type: "range", min: 0, max: 3, step: 0.1, value: config.intensity, onChange: (e) => setConfig({ intensity: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: [t('ssao.bias', 'Bias: {bias}').replace('{bias}', String(config.bias.toFixed(3))), jsxRuntime.jsx("input", { type: "range", min: 0.001, max: 0.1, step: 0.001, value: config.bias, onChange: (e) => setConfig({ bias: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: [t('ssao.resolution', 'Resolution: {resolution}').replace('{resolution}', String(config.resolution.toFixed(2))), jsxRuntime.jsx("input", { type: "range", min: 0.25, max: 1, step: 0.25, value: config.resolution, onChange: (e) => setConfig({ resolution: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.blurEnabled, onChange: (e) => setConfig({ blurEnabled: e.target.checked }) }), t('ssao.bilateralBlurSize', 'Bilateral Blur (size: {blurSize})').replace('{blurSize}', String(config.blurSize))] })] }));
476
+ ),
477
+ t("ssao.bilateralBlurSize", "Bilateral Blur (size: {blurSize})").replace(
478
+ "{blurSize}",
479
+ String(config.blurSize)
480
+ )
481
+ ] })
482
+ ] });
365
483
  };
366
-
367
484
  exports.NiceSSAO = NiceSSAO;
368
485
  exports.useSSAO = useSSAO;