r3f-peridot 0.1.0

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.
package/dist/index.js ADDED
@@ -0,0 +1,817 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var fiber = require('@react-three/fiber');
5
+ var THREE2 = require('three');
6
+ var EffectComposer_js = require('three/examples/jsm/postprocessing/EffectComposer.js');
7
+ var RenderPass_js = require('three/examples/jsm/postprocessing/RenderPass.js');
8
+ var ShaderPass_js = require('three/examples/jsm/postprocessing/ShaderPass.js');
9
+ var FXAAShader_js = require('three/examples/jsm/shaders/FXAAShader.js');
10
+ var Pass_js = require('three/examples/jsm/postprocessing/Pass.js');
11
+
12
+ function _interopNamespace(e) {
13
+ if (e && e.__esModule) return e;
14
+ var n = Object.create(null);
15
+ if (e) {
16
+ Object.keys(e).forEach(function (k) {
17
+ if (k !== 'default') {
18
+ var d = Object.getOwnPropertyDescriptor(e, k);
19
+ Object.defineProperty(n, k, d.get ? d : {
20
+ enumerable: true,
21
+ get: function () { return e[k]; }
22
+ });
23
+ }
24
+ });
25
+ }
26
+ n.default = e;
27
+ return Object.freeze(n);
28
+ }
29
+
30
+ var THREE2__namespace = /*#__PURE__*/_interopNamespace(THREE2);
31
+
32
+ // src/components/OutlineEffect.tsx
33
+ var FindSurfaces = class {
34
+ constructor() {
35
+ this.surfaceId = 0;
36
+ this.surfaceId = 0;
37
+ }
38
+ /*
39
+ * Returns the surface Ids as a Float32Array that can be inserted as a vertex attribute
40
+ */
41
+ getSurfaceIdAttribute(mesh) {
42
+ const bufferGeometry = mesh.geometry;
43
+ const numVertices = bufferGeometry.attributes.position.count;
44
+ const vertexIdToSurfaceId = this._generateSurfaceIds(mesh);
45
+ const colors = [];
46
+ for (let i = 0; i < numVertices; i++) {
47
+ const vertexId = i;
48
+ const surfaceId = vertexIdToSurfaceId[vertexId];
49
+ colors.push(surfaceId, 0, 0, 1);
50
+ }
51
+ const colorsTypedArray = new Float32Array(colors);
52
+ return colorsTypedArray;
53
+ }
54
+ /*
55
+ * Returns a `vertexIdToSurfaceId` map
56
+ * given a vertex, returns the surfaceId
57
+ */
58
+ _generateSurfaceIds(mesh) {
59
+ const bufferGeometry = mesh.geometry;
60
+ if (!bufferGeometry.index) {
61
+ const indices = [];
62
+ const numVertices = bufferGeometry.attributes.position.count;
63
+ for (let i = 0; i < numVertices; i++) {
64
+ indices.push(i);
65
+ }
66
+ bufferGeometry.setIndex(indices);
67
+ }
68
+ const numIndices = bufferGeometry.index.count;
69
+ const indexBuffer = bufferGeometry.index.array;
70
+ const vertexMap = {};
71
+ for (let i = 0; i < numIndices; i += 3) {
72
+ const i1 = indexBuffer[i + 0];
73
+ const i2 = indexBuffer[i + 1];
74
+ const i3 = indexBuffer[i + 2];
75
+ add(i1, i2);
76
+ add(i1, i3);
77
+ add(i2, i3);
78
+ }
79
+ function add(a, b) {
80
+ if (vertexMap[a] == void 0) vertexMap[a] = [];
81
+ if (vertexMap[b] == void 0) vertexMap[b] = [];
82
+ if (vertexMap[a].indexOf(b) == -1) vertexMap[a].push(b);
83
+ if (vertexMap[b].indexOf(a) == -1) vertexMap[b].push(a);
84
+ }
85
+ const frontierNodes = Object.keys(vertexMap).map((v) => Number(v));
86
+ const exploredNodes = {};
87
+ const vertexIdToSurfaceId = {};
88
+ while (frontierNodes.length > 0) {
89
+ const node = frontierNodes.pop();
90
+ if (exploredNodes[node]) continue;
91
+ const surfaceVertices = getNeighborsNonRecursive(node);
92
+ for (const v of surfaceVertices) {
93
+ exploredNodes[v] = true;
94
+ vertexIdToSurfaceId[v] = this.surfaceId;
95
+ }
96
+ this.surfaceId += 1;
97
+ }
98
+ function getNeighborsNonRecursive(node) {
99
+ const frontier = [node];
100
+ const explored = {};
101
+ const result = [];
102
+ while (frontier.length > 0) {
103
+ const currentNode = frontier.pop();
104
+ if (explored[currentNode]) continue;
105
+ const neighbors = vertexMap[currentNode];
106
+ result.push(currentNode);
107
+ explored[currentNode] = true;
108
+ for (const n of neighbors) {
109
+ if (!explored[n]) {
110
+ frontier.push(n);
111
+ }
112
+ }
113
+ }
114
+ return result;
115
+ }
116
+ return vertexIdToSurfaceId;
117
+ }
118
+ };
119
+ var FindSurfaces_default = FindSurfaces;
120
+ function getSurfaceIdMaterial() {
121
+ return new THREE2__namespace.ShaderMaterial({
122
+ uniforms: {
123
+ maxSurfaceId: { value: 1 }
124
+ },
125
+ vertexShader: getVertexShader(),
126
+ fragmentShader: getFragmentShader(),
127
+ vertexColors: true
128
+ });
129
+ }
130
+ function getVertexShader() {
131
+ return `
132
+ varying vec2 v_uv;
133
+ varying vec4 vColor;
134
+
135
+ void main() {
136
+ v_uv = uv;
137
+ #ifdef USE_COLOR_ALPHA
138
+ vColor = color;
139
+ #else
140
+ vColor = vec4(color, 1.0);
141
+ #endif
142
+
143
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
144
+ }
145
+ `;
146
+ }
147
+ function getFragmentShader() {
148
+ return `
149
+ varying vec2 v_uv;
150
+ varying vec4 vColor;
151
+ uniform float maxSurfaceId;
152
+
153
+ void main() {
154
+ // Normalize the surfaceId when writing to texture
155
+ // Surface ID needs rounding as precision can be lost in perspective correct interpolation
156
+ // - see https://github.com/OmarShehata/webgl-outlines/issues/9 for other solutions eg. flat interpolation.
157
+ float surfaceId = round(vColor.r) / maxSurfaceId;
158
+ gl_FragColor = vec4(surfaceId, 0.0, 0.0, 1.0);
159
+ }
160
+ `;
161
+ }
162
+ function getDebugSurfaceIdMaterial() {
163
+ return new THREE2__namespace.ShaderMaterial({
164
+ uniforms: {},
165
+ vertexShader: getVertexShader(),
166
+ fragmentShader: `
167
+ varying vec2 v_uv;
168
+ varying vec4 vColor;
169
+
170
+ void main() {
171
+ int surfaceId = int(round(vColor.r) * 100.0);
172
+ float R = float(surfaceId % 255) / 255.0;
173
+ float G = float((surfaceId + 50) % 255) / 255.0;
174
+ float B = float((surfaceId * 20) % 255) / 255.0;
175
+
176
+ gl_FragColor = vec4(R, G, B, 1.0);
177
+ }
178
+ `,
179
+ vertexColors: true
180
+ });
181
+ }
182
+
183
+ // src/utils/CustomOutlinePass.ts
184
+ var CustomOutlinePass = class extends Pass_js.Pass {
185
+ constructor(resolution, scene, camera) {
186
+ super();
187
+ this.renderScene = scene;
188
+ this.renderCamera = camera;
189
+ this.resolution = new THREE2__namespace.Vector2(resolution.x, resolution.y);
190
+ this.fsQuad = new Pass_js.FullScreenQuad(null);
191
+ this.fsQuad.material = this.createOutlinePostProcessMaterial();
192
+ const surfaceBuffer = new THREE2__namespace.WebGLRenderTarget(this.resolution.x, this.resolution.y);
193
+ surfaceBuffer.texture.format = THREE2__namespace.RGBAFormat;
194
+ surfaceBuffer.texture.type = THREE2__namespace.HalfFloatType;
195
+ surfaceBuffer.texture.minFilter = THREE2__namespace.NearestFilter;
196
+ surfaceBuffer.texture.magFilter = THREE2__namespace.NearestFilter;
197
+ surfaceBuffer.texture.generateMipmaps = false;
198
+ surfaceBuffer.stencilBuffer = false;
199
+ this.surfaceBuffer = surfaceBuffer;
200
+ this.normalOverrideMaterial = new THREE2__namespace.MeshNormalMaterial();
201
+ this.surfaceIdOverrideMaterial = getSurfaceIdMaterial();
202
+ this.surfaceIdDebugOverrideMaterial = getDebugSurfaceIdMaterial();
203
+ }
204
+ dispose() {
205
+ this.surfaceBuffer.dispose();
206
+ this.fsQuad.dispose();
207
+ }
208
+ updateMaxSurfaceId(maxSurfaceId) {
209
+ this.surfaceIdOverrideMaterial.uniforms.maxSurfaceId.value = maxSurfaceId;
210
+ }
211
+ setSize(width, height) {
212
+ this.surfaceBuffer.setSize(width, height);
213
+ this.resolution.set(width, height);
214
+ const screenSize = new THREE2__namespace.Vector4(
215
+ this.resolution.x,
216
+ this.resolution.y,
217
+ 1 / this.resolution.x,
218
+ 1 / this.resolution.y
219
+ );
220
+ const material = this.fsQuad.material;
221
+ material.uniforms.screenSize.value.set(screenSize.x, screenSize.y, screenSize.z, screenSize.w);
222
+ }
223
+ getDebugVisualizeValue() {
224
+ return this.fsQuad.material.uniforms.debugVisualize.value;
225
+ }
226
+ isUsingSurfaceIds() {
227
+ const debugVisualize = this.getDebugVisualizeValue();
228
+ return debugVisualize == 0 || // Main outlines v2 mode
229
+ debugVisualize == 5 || // Render just surfaceID debug buffer
230
+ debugVisualize == 6;
231
+ }
232
+ render(renderer, writeBuffer, readBuffer) {
233
+ const debugVisualize = this.getDebugVisualizeValue();
234
+ const isUsingSurfaceIds = this.isUsingSurfaceIds();
235
+ const depthBufferValue = writeBuffer.depthBuffer;
236
+ writeBuffer.depthBuffer = false;
237
+ renderer.setRenderTarget(this.surfaceBuffer);
238
+ const overrideMaterialValue = this.renderScene.overrideMaterial;
239
+ if (isUsingSurfaceIds) {
240
+ if (debugVisualize == 5) {
241
+ this.renderScene.overrideMaterial = this.surfaceIdDebugOverrideMaterial;
242
+ } else {
243
+ this.renderScene.overrideMaterial = this.surfaceIdOverrideMaterial;
244
+ }
245
+ } else {
246
+ this.renderScene.overrideMaterial = this.normalOverrideMaterial;
247
+ }
248
+ renderer.render(this.renderScene, this.renderCamera);
249
+ this.renderScene.overrideMaterial = overrideMaterialValue;
250
+ const material = this.fsQuad.material;
251
+ material.uniforms["depthBuffer"].value = readBuffer.depthTexture;
252
+ material.uniforms["surfaceBuffer"].value = this.surfaceBuffer.texture;
253
+ material.uniforms["sceneColorBuffer"].value = readBuffer.texture;
254
+ if (this.renderToScreen) {
255
+ renderer.setRenderTarget(null);
256
+ this.fsQuad.render(renderer);
257
+ } else {
258
+ renderer.setRenderTarget(writeBuffer);
259
+ this.fsQuad.render(renderer);
260
+ }
261
+ writeBuffer.depthBuffer = depthBufferValue;
262
+ }
263
+ get vertexShader() {
264
+ return `
265
+ varying vec2 vUv;
266
+ void main() {
267
+ vUv = uv;
268
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
269
+ }
270
+ `;
271
+ }
272
+ get fragmentShader() {
273
+ return `
274
+ #include <packing>
275
+ // The above include imports "perspectiveDepthToViewZ"
276
+ // and other GLSL functions from ThreeJS we need for reading depth.
277
+ uniform sampler2D sceneColorBuffer;
278
+ uniform sampler2D depthBuffer;
279
+ uniform sampler2D surfaceBuffer;
280
+ uniform float cameraNear;
281
+ uniform float cameraFar;
282
+ uniform vec4 screenSize;
283
+ uniform vec3 outlineColor;
284
+ uniform vec4 multiplierParameters;
285
+ uniform int debugVisualize;
286
+
287
+ varying vec2 vUv;
288
+
289
+ // Helper functions for reading from depth buffer.
290
+ float readDepth (sampler2D depthSampler, vec2 coord) {
291
+ float fragCoordZ = texture2D(depthSampler, coord).x;
292
+ float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );
293
+ return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );
294
+ }
295
+ float getLinearDepth(vec3 pos) {
296
+ return -(viewMatrix * vec4(pos, 1.0)).z;
297
+ }
298
+
299
+ float getLinearScreenDepth(sampler2D map) {
300
+ vec2 uv = gl_FragCoord.xy * screenSize.zw;
301
+ return readDepth(map,uv);
302
+ }
303
+ // Helper functions for reading normals and depth of neighboring pixels.
304
+ float getPixelDepth(int x, int y) {
305
+ // screenSize.zw is pixel size
306
+ // vUv is current position
307
+ return readDepth(depthBuffer, vUv + screenSize.zw * vec2(x, y));
308
+ }
309
+ // "surface value" is either the normal or the "surfaceID"
310
+ vec3 getSurfaceValue(int x, int y) {
311
+ vec3 val = texture2D(surfaceBuffer, vUv + screenSize.zw * vec2(x, y)).rgb;
312
+ return val;
313
+ }
314
+
315
+ float saturateValue(float num) {
316
+ return clamp(num, 0.0, 1.0);
317
+ }
318
+
319
+ float getSufaceIdDiff(vec3 surfaceValue) {
320
+ float surfaceIdDiff = 0.0;
321
+ surfaceIdDiff += distance(surfaceValue, getSurfaceValue(1, 0));
322
+ surfaceIdDiff += distance(surfaceValue, getSurfaceValue(0, 1));
323
+ surfaceIdDiff += distance(surfaceValue, getSurfaceValue(0, 1));
324
+ surfaceIdDiff += distance(surfaceValue, getSurfaceValue(0, -1));
325
+
326
+ surfaceIdDiff += distance(surfaceValue, getSurfaceValue(1, 1));
327
+ surfaceIdDiff += distance(surfaceValue, getSurfaceValue(1, -1));
328
+ surfaceIdDiff += distance(surfaceValue, getSurfaceValue(-1, 1));
329
+ surfaceIdDiff += distance(surfaceValue, getSurfaceValue(-1, -1));
330
+ return surfaceIdDiff;
331
+ }
332
+
333
+ void main() {
334
+ vec4 sceneColor = texture2D(sceneColorBuffer, vUv);
335
+ float depth = getPixelDepth(0, 0);
336
+ // "surfaceValue" is either the normal or the surfaceId
337
+ vec3 surfaceValue = getSurfaceValue(0, 0);
338
+
339
+ // Get the difference between depth of neighboring pixels and current.
340
+ float depthDiff = 0.0;
341
+ depthDiff += abs(depth - getPixelDepth(1, 0));
342
+ depthDiff += abs(depth - getPixelDepth(-1, 0));
343
+ depthDiff += abs(depth - getPixelDepth(0, 1));
344
+ depthDiff += abs(depth - getPixelDepth(0, -1));
345
+
346
+ // Get the difference between surface values of neighboring pixels
347
+ // and current
348
+ float surfaceValueDiff = getSufaceIdDiff(surfaceValue);
349
+
350
+ // Apply multiplier & bias to each
351
+ float depthBias = multiplierParameters.x;
352
+ float depthMultiplier = multiplierParameters.y;
353
+ float normalBias = multiplierParameters.z;
354
+ float normalMultiplier = multiplierParameters.w;
355
+
356
+ depthDiff = depthDiff * depthMultiplier;
357
+ depthDiff = saturateValue(depthDiff);
358
+ depthDiff = pow(depthDiff, depthBias);
359
+
360
+ if (debugVisualize != 0 && debugVisualize != 6) {
361
+ // Apply these params when using
362
+ // normals instead of surfaceIds
363
+ surfaceValueDiff = surfaceValueDiff * normalMultiplier;
364
+ surfaceValueDiff = saturateValue(surfaceValueDiff);
365
+ surfaceValueDiff = pow(surfaceValueDiff, normalBias);
366
+ } else {
367
+ if (surfaceValueDiff != 0.0) surfaceValueDiff = 1.0;
368
+ }
369
+
370
+ float outline = saturateValue(surfaceValueDiff + depthDiff);
371
+
372
+ // Combine outline with scene color.
373
+ vec4 outlineColor = vec4(outlineColor, 1.0);
374
+ gl_FragColor = vec4(mix(sceneColor, outlineColor, outline));
375
+
376
+ //// For debug visualization of the different inputs to this shader.
377
+ if (debugVisualize == 2) {
378
+ gl_FragColor = sceneColor;
379
+ }
380
+ if (debugVisualize == 3) {
381
+ gl_FragColor = vec4(vec3(depth), 1.0);
382
+ }
383
+ if (debugVisualize == 4 || debugVisualize == 5) {
384
+ // 4 visualizes the normal buffer
385
+ // 5 visualizes the surfaceID buffer
386
+ // Either way they are the same buffer, we change
387
+ // what we render into it
388
+ gl_FragColor = vec4(surfaceValue, 1.0);
389
+ }
390
+ if (debugVisualize == 6 || debugVisualize == 7) {
391
+ // Outlines only
392
+ gl_FragColor = vec4(vec3(outline * outlineColor), 1.0);
393
+ }
394
+ }
395
+ `;
396
+ }
397
+ createOutlinePostProcessMaterial() {
398
+ const camera = this.renderCamera;
399
+ return new THREE2__namespace.ShaderMaterial({
400
+ uniforms: {
401
+ debugVisualize: { value: 0 },
402
+ sceneColorBuffer: { value: null },
403
+ depthBuffer: { value: null },
404
+ surfaceBuffer: { value: null },
405
+ outlineColor: { value: new THREE2__namespace.Color(16777215) },
406
+ //4 scalar values packed in one uniform: depth multiplier, depth bias, and same for normals.
407
+ multiplierParameters: {
408
+ value: new THREE2__namespace.Vector4(0.9, 20, 1, 1)
409
+ },
410
+ cameraNear: { value: camera.near },
411
+ cameraFar: { value: camera.far },
412
+ screenSize: {
413
+ value: new THREE2__namespace.Vector4(
414
+ this.resolution.x,
415
+ this.resolution.y,
416
+ 1 / this.resolution.x,
417
+ 1 / this.resolution.y
418
+ )
419
+ }
420
+ },
421
+ vertexShader: this.vertexShader,
422
+ fragmentShader: this.fragmentShader
423
+ });
424
+ }
425
+ };
426
+
427
+ // src/components/OutlineEffect.tsx
428
+ var OutlineEffect = react.forwardRef(
429
+ ({
430
+ enabled = true,
431
+ outlineColor = "#ffffff",
432
+ depthBias = 0.9,
433
+ depthMultiplier = 20,
434
+ normalBias = 1,
435
+ normalMultiplier = 1,
436
+ debugVisualize = 0
437
+ }, ref) => {
438
+ const { gl, scene, camera, size, invalidate } = fiber.useThree();
439
+ const composerRef = react.useRef(null);
440
+ const outlinePassRef = react.useRef(null);
441
+ const fxaaPassRef = react.useRef(null);
442
+ const surfaceIdsComputedRef = react.useRef(false);
443
+ react.useEffect(() => {
444
+ if (!enabled) {
445
+ if (composerRef.current) {
446
+ composerRef.current.dispose();
447
+ composerRef.current = null;
448
+ }
449
+ return;
450
+ }
451
+ const width = Math.max(1, size.width * gl.getPixelRatio());
452
+ const height = Math.max(1, size.height * gl.getPixelRatio());
453
+ const depthTexture = new THREE2__namespace.DepthTexture(width, height);
454
+ const renderTarget = new THREE2__namespace.WebGLRenderTarget(width, height, {
455
+ depthTexture,
456
+ depthBuffer: true
457
+ });
458
+ const composer = new EffectComposer_js.EffectComposer(gl, renderTarget);
459
+ composerRef.current = composer;
460
+ const renderPass = new RenderPass_js.RenderPass(scene, camera);
461
+ composer.addPass(renderPass);
462
+ const customOutline = new CustomOutlinePass(new THREE2__namespace.Vector2(width, height), scene, camera);
463
+ customOutline.renderToScreen = true;
464
+ outlinePassRef.current = customOutline;
465
+ composer.addPass(customOutline);
466
+ const effectFXAA = new ShaderPass_js.ShaderPass(FXAAShader_js.FXAAShader);
467
+ effectFXAA.uniforms["resolution"].value.set(1 / width, 1 / height);
468
+ effectFXAA.renderToScreen = true;
469
+ customOutline.renderToScreen = false;
470
+ fxaaPassRef.current = effectFXAA;
471
+ composer.addPass(effectFXAA);
472
+ return () => {
473
+ composer.dispose();
474
+ renderTarget.dispose();
475
+ depthTexture.dispose();
476
+ };
477
+ }, [enabled, scene, camera, gl, size.width, size.height, invalidate]);
478
+ react.useEffect(() => {
479
+ if (!enabled) return;
480
+ const isUsingSurfaceIds = debugVisualize === 0 || debugVisualize === 5 || debugVisualize === 6;
481
+ if (isUsingSurfaceIds && !surfaceIdsComputedRef.current) {
482
+ const findSurfaces = new FindSurfaces_default();
483
+ let maxSurfaceId = 0;
484
+ scene.traverse((object) => {
485
+ if (object instanceof THREE2__namespace.Mesh && object.geometry) {
486
+ try {
487
+ const geometry = object.geometry;
488
+ const surfaceIdAttribute = findSurfaces.getSurfaceIdAttribute(object);
489
+ geometry.setAttribute("color", new THREE2__namespace.BufferAttribute(surfaceIdAttribute, 4));
490
+ maxSurfaceId = Math.max(maxSurfaceId, findSurfaces.surfaceId);
491
+ } catch (error) {
492
+ console.warn("Failed to compute surface IDs for mesh:", error);
493
+ }
494
+ }
495
+ });
496
+ if (outlinePassRef.current && maxSurfaceId > 0) {
497
+ outlinePassRef.current.updateMaxSurfaceId(maxSurfaceId + 1);
498
+ }
499
+ surfaceIdsComputedRef.current = true;
500
+ } else if (!isUsingSurfaceIds && surfaceIdsComputedRef.current) {
501
+ scene.traverse((object) => {
502
+ if (object instanceof THREE2__namespace.Mesh && object.geometry) {
503
+ object.geometry.deleteAttribute("color");
504
+ }
505
+ });
506
+ surfaceIdsComputedRef.current = false;
507
+ }
508
+ }, [enabled, scene, debugVisualize]);
509
+ fiber.useFrame(() => {
510
+ if (!enabled || !outlinePassRef.current || !composerRef.current) {
511
+ return;
512
+ }
513
+ const outlinePass = outlinePassRef.current;
514
+ const material = outlinePass.fsQuad.material;
515
+ const uniforms = material.uniforms;
516
+ if (typeof outlineColor === "string") {
517
+ uniforms.outlineColor.value.set(outlineColor);
518
+ } else {
519
+ uniforms.outlineColor.value.copy(outlineColor);
520
+ }
521
+ uniforms.multiplierParameters.value.set(
522
+ depthBias,
523
+ depthMultiplier,
524
+ normalBias,
525
+ normalMultiplier
526
+ );
527
+ uniforms.debugVisualize.value = debugVisualize;
528
+ const cam = camera;
529
+ if ("near" in cam) {
530
+ uniforms.cameraNear.value = cam.near;
531
+ }
532
+ if ("far" in cam) {
533
+ uniforms.cameraFar.value = cam.far;
534
+ }
535
+ composerRef.current.render();
536
+ }, 1);
537
+ react.useEffect(() => {
538
+ if (!composerRef.current || !outlinePassRef.current || !fxaaPassRef.current) return;
539
+ const width = Math.max(1, size.width * gl.getPixelRatio());
540
+ const height = Math.max(1, size.height * gl.getPixelRatio());
541
+ composerRef.current.setSize(width, height);
542
+ outlinePassRef.current.setSize(width, height);
543
+ fxaaPassRef.current.uniforms["resolution"].value.set(1 / width, 1 / height);
544
+ }, [size, gl]);
545
+ react.useImperativeHandle(ref, () => ({
546
+ updateMaxSurfaceId: (maxSurfaceId) => {
547
+ if (outlinePassRef.current) {
548
+ outlinePassRef.current.updateMaxSurfaceId(maxSurfaceId);
549
+ }
550
+ }
551
+ }));
552
+ return null;
553
+ }
554
+ );
555
+ OutlineEffect.displayName = "OutlineEffect";
556
+
557
+ // src/utils/VertexWelder.ts
558
+ function weldVertices(vertices, indices, thresholdAngle = 1) {
559
+ const mergedMap = {};
560
+ const vertexAliases = {};
561
+ function merge(i1, i2) {
562
+ if (mergedMap[i1] == void 0) mergedMap[i1] = [];
563
+ mergedMap[i1].push(i2);
564
+ }
565
+ function aliasDeletedVertex(deletedVertex, remainingVertex) {
566
+ if (deletedVertex == remainingVertex) return;
567
+ vertexAliases[deletedVertex] = remainingVertex;
568
+ }
569
+ const edgesToMerge = computeEdgesToMerge(vertices, indices, thresholdAngle);
570
+ const edgesToMergeMap = {};
571
+ for (let i = 0; i < edgesToMerge.length; i++) {
572
+ const edgesList = edgesToMerge[i];
573
+ for (const index of edgesList) {
574
+ const key = `${index[0]}_${index[1]}`;
575
+ edgesToMergeMap[key] = edgesList;
576
+ }
577
+ }
578
+ for (let i = 0; i < indices.length; i += 3) {
579
+ const i1 = indices[i + 0];
580
+ const i2 = indices[i + 1];
581
+ const i3 = indices[i + 2];
582
+ const edges = [];
583
+ edges.push([i1, i2]);
584
+ edges.push([i1, i3]);
585
+ edges.push([i2, i3]);
586
+ for (const edge of edges) {
587
+ let index0 = edge[0];
588
+ let index1 = edge[1];
589
+ const reverseEdge = [index1, index0];
590
+ let isReverse = false;
591
+ let edgeToMerge;
592
+ const edgeKey = `${edge[0]}_${edge[1]}`;
593
+ const reverseEdgeKey = `${reverseEdge[0]}_${reverseEdge[1]}`;
594
+ if (edgesToMergeMap[edgeKey]) {
595
+ edgeToMerge = edge;
596
+ }
597
+ if (edgesToMergeMap[reverseEdgeKey]) {
598
+ edgeToMerge = reverseEdge;
599
+ isReverse = true;
600
+ }
601
+ if (edgeToMerge) {
602
+ const edgeKeyToUse = isReverse ? reverseEdgeKey : edgeKey;
603
+ const possibleEdges = edgesToMergeMap[edgeKeyToUse];
604
+ const possibleEdge1 = possibleEdges[0];
605
+ const possibleEdge2 = possibleEdges[1];
606
+ let otherEdge = possibleEdge1;
607
+ let originalEdge = possibleEdge2;
608
+ if (possibleEdge1[0] == index0 && possibleEdge1[1] == index1 || possibleEdge1[0] == index1 && possibleEdge1[1] == index0) {
609
+ otherEdge = possibleEdge2;
610
+ originalEdge = possibleEdge1;
611
+ }
612
+ let index2 = otherEdge[0];
613
+ let index3 = otherEdge[1];
614
+ index0 = originalEdge[0];
615
+ index1 = originalEdge[1];
616
+ if (index0 == index2 && index1 == index3) {
617
+ continue;
618
+ }
619
+ const v0 = getVertexFromIndexBuffer(index0, vertices);
620
+ const v2 = getVertexFromIndexBuffer(index2, vertices);
621
+ if (v0.distanceTo(v2) > 0.1) {
622
+ const tmp = index3;
623
+ index3 = index2;
624
+ index2 = tmp;
625
+ }
626
+ if (vertexAliases[index0]) index0 = vertexAliases[index0];
627
+ if (vertexAliases[index1]) index1 = vertexAliases[index1];
628
+ if (vertexAliases[index2]) index2 = vertexAliases[index2];
629
+ if (vertexAliases[index3]) index3 = vertexAliases[index3];
630
+ merge(index0, index2);
631
+ merge(index1, index3);
632
+ aliasDeletedVertex(index2, index0);
633
+ aliasDeletedVertex(index3, index1);
634
+ const mergedEdgeKey = `${index2}_${index3}`;
635
+ delete edgesToMergeMap[edgeKeyToUse];
636
+ delete edgesToMergeMap[mergedEdgeKey];
637
+ }
638
+ }
639
+ }
640
+ const finalMergeMap = fillOutMergeMap(mergedMap);
641
+ const newIndexBuffer = [];
642
+ for (let i = 0; i < indices.length; i++) {
643
+ const index = indices[i];
644
+ let newIndex = index;
645
+ if (finalMergeMap[index] != void 0) {
646
+ newIndex = finalMergeMap[index];
647
+ }
648
+ newIndexBuffer.push(newIndex);
649
+ }
650
+ return newIndexBuffer;
651
+ }
652
+ function getVertexFromIndexBuffer(index, positionAttr) {
653
+ return new Vector3(
654
+ positionAttr[index * 3 + 0],
655
+ positionAttr[index * 3 + 1],
656
+ positionAttr[index * 3 + 2]
657
+ );
658
+ }
659
+ function fillOutMergeMap(mergeMap) {
660
+ const newMergeMap = {};
661
+ for (let i = 0; i < Object.keys(mergeMap).length; i++) {
662
+ const key = Number(Object.keys(mergeMap)[i]);
663
+ const indices = mergeMap[key];
664
+ for (const ind of indices) {
665
+ newMergeMap[ind] = key;
666
+ }
667
+ }
668
+ return newMergeMap;
669
+ }
670
+ var Vector3 = class _Vector3 {
671
+ constructor(x = 0, y = 0, z = 0) {
672
+ this.x = x;
673
+ this.y = y;
674
+ this.z = z;
675
+ }
676
+ clone() {
677
+ return new _Vector3(this.x, this.y, this.z);
678
+ }
679
+ set(x, y, z) {
680
+ this.x = x;
681
+ this.y = y;
682
+ this.z = z;
683
+ return this;
684
+ }
685
+ distanceTo(v) {
686
+ const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
687
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
688
+ }
689
+ lengthSq() {
690
+ return this.x * this.x + this.y * this.y + this.z * this.z;
691
+ }
692
+ multiplyScalar(scalar) {
693
+ this.x *= scalar;
694
+ this.y *= scalar;
695
+ this.z *= scalar;
696
+ return this;
697
+ }
698
+ subVectors(a, b) {
699
+ this.x = a.x - b.x;
700
+ this.y = a.y - b.y;
701
+ this.z = a.z - b.z;
702
+ return this;
703
+ }
704
+ dot(v) {
705
+ return this.x * v.x + this.y * v.y + this.z * v.z;
706
+ }
707
+ cross(v) {
708
+ return this.crossVectors(this, v);
709
+ }
710
+ crossVectors(a, b) {
711
+ const ax = a.x, ay = a.y, az = a.z;
712
+ const bx = b.x, by = b.y, bz = b.z;
713
+ this.x = ay * bz - az * by;
714
+ this.y = az * bx - ax * bz;
715
+ this.z = ax * by - ay * bx;
716
+ return this;
717
+ }
718
+ };
719
+ var _v0 = new Vector3();
720
+ var _normal = new Vector3();
721
+ var precisionPoints = 4;
722
+ var precision = Math.pow(10, precisionPoints);
723
+ function hashVertex(v) {
724
+ return `${Math.round(v.x * precision)},${Math.round(v.y * precision)},${Math.round(
725
+ v.z * precision
726
+ )}`;
727
+ }
728
+ function getNormal(a, b, c, resultNormal) {
729
+ resultNormal.subVectors(c, b);
730
+ _v0.subVectors(a, b);
731
+ resultNormal.cross(_v0);
732
+ const targetLengthSq = resultNormal.lengthSq();
733
+ if (targetLengthSq > 0) {
734
+ return resultNormal.multiplyScalar(1 / Math.sqrt(targetLengthSq));
735
+ }
736
+ return resultNormal.set(0, 0, 0);
737
+ }
738
+ function computeEdgesToMerge(vertices, indices, thresholdAngle = 1) {
739
+ const DEG2RAD = Math.PI / 180;
740
+ const thresholdDot = Math.cos(DEG2RAD * thresholdAngle);
741
+ const indexCount = indices.length;
742
+ const indexArr = [0, 0, 0];
743
+ const hashes = new Array(3);
744
+ const edgeData = {};
745
+ const edgesToMerge = [];
746
+ for (let i = 0; i < indexCount; i += 3) {
747
+ indexArr[0] = indices[i];
748
+ indexArr[1] = indices[i + 1];
749
+ indexArr[2] = indices[i + 2];
750
+ const a = getVertexFromIndexBuffer(indexArr[0], vertices);
751
+ const b = getVertexFromIndexBuffer(indexArr[1], vertices);
752
+ const c = getVertexFromIndexBuffer(indexArr[2], vertices);
753
+ getNormal(a, b, c, _normal);
754
+ hashes[0] = hashVertex(a);
755
+ hashes[1] = hashVertex(b);
756
+ hashes[2] = hashVertex(c);
757
+ if (hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0]) {
758
+ continue;
759
+ }
760
+ for (let j = 0; j < 3; j++) {
761
+ const jNext = (j + 1) % 3;
762
+ const vecHash0 = hashes[j];
763
+ const vecHash1 = hashes[jNext];
764
+ const hash = `${vecHash0}_${vecHash1}`;
765
+ const reverseHash = `${vecHash1}_${vecHash0}`;
766
+ if (reverseHash in edgeData && edgeData[reverseHash]) {
767
+ if (_normal.dot(edgeData[reverseHash].normal) > thresholdDot) {
768
+ const edge1 = [edgeData[reverseHash].index0, edgeData[reverseHash].index1];
769
+ const edge2 = [indexArr[j], indexArr[jNext]];
770
+ edgesToMerge.push([edge1, edge2]);
771
+ }
772
+ edgeData[reverseHash] = null;
773
+ } else if (!(hash in edgeData)) {
774
+ edgeData[hash] = {
775
+ index0: indexArr[j],
776
+ index1: indexArr[jNext],
777
+ normal: _normal.clone()
778
+ };
779
+ }
780
+ }
781
+ }
782
+ return edgesToMerge;
783
+ }
784
+ function useSurfaceIds(enabled = true) {
785
+ const { scene } = fiber.useThree();
786
+ react.useEffect(() => {
787
+ if (!enabled) return;
788
+ const findSurfaces = new FindSurfaces_default();
789
+ let maxSurfaceId = 0;
790
+ scene.traverse((object) => {
791
+ if (object instanceof THREE2__namespace.Mesh && object.geometry) {
792
+ const geometry = object.geometry;
793
+ const surfaceIdAttribute = findSurfaces.getSurfaceIdAttribute(object);
794
+ geometry.setAttribute("color", new THREE2__namespace.BufferAttribute(surfaceIdAttribute, 4));
795
+ maxSurfaceId = Math.max(maxSurfaceId, findSurfaces.surfaceId);
796
+ }
797
+ });
798
+ return () => {
799
+ scene.traverse((object) => {
800
+ if (object instanceof THREE2__namespace.Mesh && object.geometry) {
801
+ object.geometry.deleteAttribute("color");
802
+ }
803
+ });
804
+ };
805
+ }, [scene, enabled]);
806
+ }
807
+ var useSurfaceIds_default = useSurfaceIds;
808
+
809
+ exports.CustomOutlinePass = CustomOutlinePass;
810
+ exports.FindSurfaces = FindSurfaces_default;
811
+ exports.OutlineEffect = OutlineEffect;
812
+ exports.getDebugSurfaceIdMaterial = getDebugSurfaceIdMaterial;
813
+ exports.getSurfaceIdMaterial = getSurfaceIdMaterial;
814
+ exports.useSurfaceIds = useSurfaceIds_default;
815
+ exports.weldVertices = weldVertices;
816
+ //# sourceMappingURL=index.js.map
817
+ //# sourceMappingURL=index.js.map