three-gpu-pathtracer 0.0.20 → 0.0.21
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/README.md +111 -464
- package/build/index.module.js +5691 -5312
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +5369 -5003
- package/build/index.umd.cjs.map +1 -1
- package/package.json +12 -6
- package/src/core/PathTracingRenderer.js +59 -46
- package/src/core/PathTracingSceneGenerator.js +245 -10
- package/src/core/WebGLPathTracer.js +472 -0
- package/src/core/utils/BakedGeometry.js +35 -0
- package/src/core/utils/BufferAttributeUtils.js +64 -0
- package/src/{utils → core/utils}/GeometryPreparationUtils.js +35 -35
- package/src/core/utils/MeshDiff.js +102 -0
- package/src/core/utils/StaticGeometryGenerator.js +285 -0
- package/src/core/utils/convertToStaticGeometry.js +344 -0
- package/src/core/utils/mergeGeometries.js +218 -0
- package/src/core/utils/sceneUpdateUtils.js +96 -0
- package/src/index.d.ts +274 -0
- package/src/index.js +4 -20
- package/src/materials/MaterialBase.js +4 -0
- package/src/materials/fullscreen/ClampedInterpolationMaterial.js +112 -0
- package/src/materials/fullscreen/DenoiseMaterial.js +4 -0
- package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +73 -76
- package/src/materials/pathtracing/glsl/{attenuateHit.glsl.js → attenuate_hit_function.glsl.js} +1 -1
- package/src/materials/pathtracing/glsl/{cameraUtils.glsl.js → camera_util_functions.glsl.js} +1 -1
- package/src/materials/pathtracing/glsl/{directLightContribution.glsl.js → direct_light_contribution_function.glsl.js} +1 -1
- package/src/materials/pathtracing/glsl/{getSurfaceRecord.glsl.js → get_surface_record_function.glsl.js} +1 -1
- package/src/materials/pathtracing/glsl/index.js +6 -0
- package/src/materials/pathtracing/glsl/{renderStructs.glsl.js → render_structs.glsl.js} +1 -1
- package/src/materials/pathtracing/glsl/{traceScene.glsl.js → trace_scene_function.glsl.js} +1 -3
- package/src/materials/surface/AmbientOcclusionMaterial.js +8 -8
- package/src/objects/PhysicalSpotLight.js +2 -2
- package/src/shader/bsdf/{bsdfSampling.glsl.js → bsdf_functions.glsl.js} +19 -72
- package/src/shader/bsdf/{fog.glsl.js → fog_functions.glsl.js} +1 -1
- package/src/shader/bsdf/{ggx.glsl.js → ggx_functions.glsl.js} +1 -1
- package/src/shader/bsdf/index.js +5 -0
- package/src/shader/bsdf/{iridescence.glsl.js → iridescence_functions.glsl.js} +1 -1
- package/src/shader/bsdf/{sheen.glsl.js → sheen_functions.glsl.js} +1 -1
- package/src/shader/bvh/index.js +2 -0
- package/src/shader/{structs/fogMaterialBvh.glsl.js → bvh/inside_fog_volume_function.glsl.js} +1 -1
- package/src/shader/{common/bvhAnyHit.glsl.js → bvh/ray_any_hit_function.glsl.js} +1 -1
- package/src/shader/common/{fresnel.glsl.js → fresnel_functions.glsl.js} +1 -1
- package/src/shader/common/index.js +5 -0
- package/src/shader/common/{math.glsl.js → math_functions.glsl.js} +1 -1
- package/src/shader/common/{intersectShapes.glsl.js → shape_intersection_functions.glsl.js} +1 -1
- package/src/shader/common/{arraySamplerTexelFetch.glsl.js → texture_sample_functions.glsl.js} +1 -1
- package/src/shader/common/{utils.glsl.js → util_functions.glsl.js} +1 -1
- package/src/shader/rand/index.js +3 -0
- package/src/shader/rand/pcg.glsl.js +1 -1
- package/src/shader/rand/sobol.glsl.js +4 -4
- package/src/shader/rand/{stratifiedTexture.glsl.js → stratified.glsl.js} +7 -2
- package/src/shader/sampling/{equirectSampling.glsl.js → equirect_sampling_functions.glsl.js} +1 -2
- package/src/shader/sampling/index.js +3 -0
- package/src/shader/sampling/{lightSampling.glsl.js → light_sampling_functions.glsl.js} +3 -3
- package/src/shader/sampling/{shapeSampling.glsl.js → shape_sampling_functions.glsl.js} +1 -1
- package/src/shader/structs/{cameraStruct.glsl.js → camera_struct.glsl.js} +1 -1
- package/src/shader/structs/{equirectStruct.glsl.js → equirect_struct.glsl.js} +1 -1
- package/src/shader/structs/index.js +5 -0
- package/src/shader/structs/{lightsStruct.glsl.js → lights_struct.glsl.js} +1 -1
- package/src/shader/structs/{materialStruct.glsl.js → material_struct.glsl.js} +2 -2
- package/src/shader/structs/surface_record_struct.glsl.js +63 -0
- package/src/uniforms/EquirectHdrInfoUniform.js +16 -11
- package/src/uniforms/LightsInfoUniformStruct.js +21 -10
- package/src/uniforms/MaterialsTexture.js +27 -86
- package/src/uniforms/RenderTarget2DArray.js +60 -20
- package/src/utils/BlurredEnvMapGenerator.js +12 -5
- package/src/utils/SobolNumberMapGenerator.js +3 -3
- package/src/utils/bufferToHash.js +22 -0
- package/src/core/DynamicPathTracingSceneGenerator.js +0 -164
- package/src/core/MaterialReducer.js +0 -256
- package/src/materials/pathtracing/LambertPathTracingMaterial.js +0 -297
- package/src/uniforms/IESProfilesTexture.js +0 -100
- package/src/uniforms/utils.js +0 -30
- package/src/utils/IESLoader.js +0 -327
- package/src/workers/PathTracingSceneWorker.js +0 -52
|
@@ -9,37 +9,63 @@ import {
|
|
|
9
9
|
ShaderMaterial,
|
|
10
10
|
} from 'three';
|
|
11
11
|
import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
|
|
12
|
-
import { reduceTexturesToUniqueSources } from './utils.js';
|
|
13
12
|
|
|
14
13
|
const prevColor = new Color();
|
|
14
|
+
function getTextureHash( texture ) {
|
|
15
|
+
|
|
16
|
+
return texture ? `${ texture.uuid }:${ texture.version }` : null;
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function assignOptions( target, options ) {
|
|
21
|
+
|
|
22
|
+
for ( const key in options ) {
|
|
23
|
+
|
|
24
|
+
if ( key in target ) {
|
|
25
|
+
|
|
26
|
+
target[ key ] = options[ key ];
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
15
34
|
export class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
16
35
|
|
|
17
|
-
constructor(
|
|
36
|
+
constructor( width, height, options ) {
|
|
37
|
+
|
|
38
|
+
const textureOptions = {
|
|
39
|
+
format: RGBAFormat,
|
|
40
|
+
type: UnsignedByteType,
|
|
41
|
+
minFilter: LinearFilter,
|
|
42
|
+
magFilter: LinearFilter,
|
|
43
|
+
wrapS: RepeatWrapping,
|
|
44
|
+
wrapT: RepeatWrapping,
|
|
45
|
+
generateMipmaps: false,
|
|
46
|
+
...options,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
super( width, height, 1, textureOptions );
|
|
18
50
|
|
|
19
|
-
|
|
51
|
+
// manually assign the options because passing options into the
|
|
52
|
+
// constructor does not work
|
|
53
|
+
assignOptions( this.texture, textureOptions );
|
|
20
54
|
|
|
21
|
-
|
|
22
|
-
tex.format = RGBAFormat;
|
|
23
|
-
tex.type = UnsignedByteType;
|
|
24
|
-
tex.minFilter = LinearFilter;
|
|
25
|
-
tex.magFilter = LinearFilter;
|
|
26
|
-
tex.wrapS = RepeatWrapping;
|
|
27
|
-
tex.wrapT = RepeatWrapping;
|
|
28
|
-
tex.setTextures = ( ...args ) => {
|
|
55
|
+
this.texture.setTextures = ( ...args ) => {
|
|
29
56
|
|
|
30
57
|
this.setTextures( ...args );
|
|
31
58
|
|
|
32
59
|
};
|
|
33
60
|
|
|
61
|
+
this.hashes = [ null ];
|
|
62
|
+
|
|
34
63
|
const fsQuad = new FullScreenQuad( new CopyMaterial() );
|
|
35
64
|
this.fsQuad = fsQuad;
|
|
36
65
|
|
|
37
66
|
}
|
|
38
67
|
|
|
39
|
-
setTextures( renderer, width, height
|
|
40
|
-
|
|
41
|
-
// get the list of textures with unique sources
|
|
42
|
-
const uniqueTextures = reduceTexturesToUniqueSources( textures );
|
|
68
|
+
setTextures( renderer, textures, width = this.width, height = this.height ) {
|
|
43
69
|
|
|
44
70
|
// save previous renderer state
|
|
45
71
|
const prevRenderTarget = renderer.getRenderTarget();
|
|
@@ -49,17 +75,26 @@ export class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
|
49
75
|
|
|
50
76
|
// resize the render target and ensure we don't have an empty texture
|
|
51
77
|
// render target depth must be >= 1 to avoid unbound texture error on android devices
|
|
52
|
-
const depth =
|
|
53
|
-
|
|
78
|
+
const depth = textures.length || 1;
|
|
79
|
+
if ( width !== this.width || height !== this.height || this.depth !== depth ) {
|
|
80
|
+
|
|
81
|
+
this.setSize( width, height, depth );
|
|
82
|
+
this.hashes = new Array( depth ).fill( null );
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|
|
54
86
|
renderer.setClearColor( 0, 0 );
|
|
55
87
|
renderer.toneMapping = NoToneMapping;
|
|
56
88
|
|
|
57
89
|
// render each texture into each layer of the target
|
|
58
90
|
const fsQuad = this.fsQuad;
|
|
91
|
+
const hashes = this.hashes;
|
|
92
|
+
let updated = false;
|
|
59
93
|
for ( let i = 0, l = depth; i < l; i ++ ) {
|
|
60
94
|
|
|
61
|
-
const texture =
|
|
62
|
-
|
|
95
|
+
const texture = textures[ i ];
|
|
96
|
+
const hash = getTextureHash( texture );
|
|
97
|
+
if ( texture && ( hashes[ i ] !== hash || texture.isWebGLRenderTarget ) ) {
|
|
63
98
|
|
|
64
99
|
// revert to default texture transform before rendering
|
|
65
100
|
texture.matrixAutoUpdate = false;
|
|
@@ -74,6 +109,10 @@ export class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
|
74
109
|
texture.updateMatrix();
|
|
75
110
|
texture.matrixAutoUpdate = true;
|
|
76
111
|
|
|
112
|
+
// ensure textures are not updated unnecessarily
|
|
113
|
+
hashes[ i ] = hash;
|
|
114
|
+
updated = true;
|
|
115
|
+
|
|
77
116
|
}
|
|
78
117
|
|
|
79
118
|
}
|
|
@@ -84,6 +123,8 @@ export class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
|
84
123
|
renderer.setRenderTarget( prevRenderTarget );
|
|
85
124
|
renderer.toneMapping = prevToneMapping;
|
|
86
125
|
|
|
126
|
+
return updated;
|
|
127
|
+
|
|
87
128
|
}
|
|
88
129
|
|
|
89
130
|
dispose() {
|
|
@@ -140,5 +181,4 @@ class CopyMaterial extends ShaderMaterial {
|
|
|
140
181
|
|
|
141
182
|
}
|
|
142
183
|
|
|
143
|
-
|
|
144
184
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { WebGLRenderTarget, RGBAFormat, HalfFloatType, PMREMGenerator, DataTexture, EquirectangularReflectionMapping } from 'three';
|
|
1
|
+
import { WebGLRenderTarget, RGBAFormat, HalfFloatType, PMREMGenerator, DataTexture, EquirectangularReflectionMapping, FloatType, DataUtils } from 'three';
|
|
2
2
|
import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
|
|
3
3
|
import { MaterialBase } from '../materials/MaterialBase.js';
|
|
4
|
-
import
|
|
4
|
+
import * as CommonGLSL from '../shader/common/index.js';
|
|
5
5
|
|
|
6
6
|
class PMREMCopyMaterial extends MaterialBase {
|
|
7
7
|
|
|
@@ -31,7 +31,7 @@ class PMREMCopyMaterial extends MaterialBase {
|
|
|
31
31
|
#include <common>
|
|
32
32
|
#include <cube_uv_reflection_fragment>
|
|
33
33
|
|
|
34
|
-
${
|
|
34
|
+
${ CommonGLSL.util_functions }
|
|
35
35
|
|
|
36
36
|
uniform sampler2D envMap;
|
|
37
37
|
uniform float blur;
|
|
@@ -58,7 +58,7 @@ export class BlurredEnvMapGenerator {
|
|
|
58
58
|
this.renderer = renderer;
|
|
59
59
|
this.pmremGenerator = new PMREMGenerator( renderer );
|
|
60
60
|
this.copyQuad = new FullScreenQuad( new PMREMCopyMaterial() );
|
|
61
|
-
this.renderTarget = new WebGLRenderTarget( 1, 1, { type:
|
|
61
|
+
this.renderTarget = new WebGLRenderTarget( 1, 1, { type: FloatType, format: RGBAFormat } );
|
|
62
62
|
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -96,7 +96,14 @@ export class BlurredEnvMapGenerator {
|
|
|
96
96
|
|
|
97
97
|
// read the data back
|
|
98
98
|
const buffer = new Uint16Array( width * height * 4 );
|
|
99
|
-
|
|
99
|
+
const readBuffer = new Float32Array( width * height * 4 );
|
|
100
|
+
renderer.readRenderTargetPixels( renderTarget, 0, 0, width, height, readBuffer );
|
|
101
|
+
|
|
102
|
+
for ( let i = 0, l = readBuffer.length; i < l; i ++ ) {
|
|
103
|
+
|
|
104
|
+
buffer[ i ] = DataUtils.toHalfFloat( readBuffer[ i ] );
|
|
105
|
+
|
|
106
|
+
}
|
|
100
107
|
|
|
101
108
|
const result = new DataTexture( buffer, width, height, RGBAFormat, HalfFloatType );
|
|
102
109
|
result.minFilter = texture.minFilter;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FloatType, NearestFilter, NoBlending, RGBAFormat, Vector2, WebGLRenderTarget } from 'three';
|
|
2
2
|
import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
|
|
3
3
|
import { MaterialBase } from '../materials/MaterialBase.js';
|
|
4
|
-
import {
|
|
4
|
+
import { sobol_common, sobol_point_generation } from '../shader/rand/sobol.glsl.js';
|
|
5
5
|
|
|
6
6
|
class SobolNumbersMaterial extends MaterialBase {
|
|
7
7
|
|
|
@@ -30,8 +30,8 @@ class SobolNumbersMaterial extends MaterialBase {
|
|
|
30
30
|
|
|
31
31
|
fragmentShader: /* glsl */`
|
|
32
32
|
|
|
33
|
-
${
|
|
34
|
-
${
|
|
33
|
+
${ sobol_common }
|
|
34
|
+
${ sobol_point_generation }
|
|
35
35
|
|
|
36
36
|
varying vec2 vUv;
|
|
37
37
|
uniform vec2 resolution;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// https://www.geeksforgeeks.org/how-to-create-hash-from-string-in-javascript/
|
|
2
|
+
// https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript
|
|
3
|
+
export function bufferToHash( buffer ) {
|
|
4
|
+
|
|
5
|
+
let hash = 0;
|
|
6
|
+
|
|
7
|
+
if ( buffer.byteLength !== 0 ) {
|
|
8
|
+
|
|
9
|
+
const uintArray = new Uint8Array( buffer );
|
|
10
|
+
for ( let i = 0; i < buffer.byteLength; i ++ ) {
|
|
11
|
+
|
|
12
|
+
const byte = uintArray[ i ];
|
|
13
|
+
hash = ( ( hash << 5 ) - hash ) + byte;
|
|
14
|
+
hash |= 0;
|
|
15
|
+
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return hash;
|
|
21
|
+
|
|
22
|
+
}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { BufferGeometry, MeshBasicMaterial, BufferAttribute, Mesh } from 'three';
|
|
2
|
-
import { StaticGeometryGenerator, MeshBVH, SAH } from 'three-mesh-bvh';
|
|
3
|
-
import { setCommonAttributes, getGroupMaterialIndicesAttribute } from '../utils/GeometryPreparationUtils.js';
|
|
4
|
-
|
|
5
|
-
const dummyMaterial = new MeshBasicMaterial();
|
|
6
|
-
export function getDummyMesh() {
|
|
7
|
-
|
|
8
|
-
const emptyGeometry = new BufferGeometry();
|
|
9
|
-
emptyGeometry.setAttribute( 'position', new BufferAttribute( new Float32Array( 9 ), 3 ) );
|
|
10
|
-
return new Mesh( emptyGeometry, dummyMaterial );
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class DynamicPathTracingSceneGenerator {
|
|
15
|
-
|
|
16
|
-
get initialized() {
|
|
17
|
-
|
|
18
|
-
return Boolean( this.bvh );
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
constructor( objects ) {
|
|
23
|
-
|
|
24
|
-
// ensure the objects is an array
|
|
25
|
-
if ( ! Array.isArray( objects ) ) {
|
|
26
|
-
|
|
27
|
-
objects = [ objects ];
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// use a dummy object for a fallback
|
|
32
|
-
const finalObjects = [ ...objects ];
|
|
33
|
-
if ( finalObjects.length === 0 ) {
|
|
34
|
-
|
|
35
|
-
finalObjects.push( getDummyMesh() );
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// options
|
|
40
|
-
this.bvhOptions = {};
|
|
41
|
-
this.attributes = [ 'position', 'normal', 'tangent', 'color', 'uv', 'uv2' ];
|
|
42
|
-
|
|
43
|
-
// state
|
|
44
|
-
this.objects = finalObjects;
|
|
45
|
-
this.bvh = null;
|
|
46
|
-
this.geometry = new BufferGeometry();
|
|
47
|
-
this.materials = null;
|
|
48
|
-
this.textures = null;
|
|
49
|
-
this.lights = [];
|
|
50
|
-
this.staticGeometryGenerator = new StaticGeometryGenerator( this.objects );
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
reset() {
|
|
55
|
-
|
|
56
|
-
this.bvh = null;
|
|
57
|
-
this.geometry.dispose();
|
|
58
|
-
this.geometry = new BufferGeometry();
|
|
59
|
-
this.materials = null;
|
|
60
|
-
this.textures = null;
|
|
61
|
-
this.lights = [];
|
|
62
|
-
this.staticGeometryGenerator = new StaticGeometryGenerator( this.objects );
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
dispose() {}
|
|
67
|
-
|
|
68
|
-
prepScene() {
|
|
69
|
-
|
|
70
|
-
if ( this.bvh !== null ) {
|
|
71
|
-
|
|
72
|
-
return;
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const { objects, staticGeometryGenerator, geometry, lights, attributes } = this;
|
|
77
|
-
for ( let i = 0, l = objects.length; i < l; i ++ ) {
|
|
78
|
-
|
|
79
|
-
objects[ i ].traverse( c => {
|
|
80
|
-
|
|
81
|
-
if ( c.isMesh ) {
|
|
82
|
-
|
|
83
|
-
const normalMapRequired = ! ! c.material.normalMap;
|
|
84
|
-
setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
|
|
85
|
-
|
|
86
|
-
} else if (
|
|
87
|
-
c.isRectAreaLight ||
|
|
88
|
-
c.isSpotLight ||
|
|
89
|
-
c.isPointLight ||
|
|
90
|
-
c.isDirectionalLight
|
|
91
|
-
) {
|
|
92
|
-
|
|
93
|
-
lights.push( c );
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
} );
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const textureSet = new Set();
|
|
102
|
-
const materials = staticGeometryGenerator.getMaterials();
|
|
103
|
-
materials.forEach( material => {
|
|
104
|
-
|
|
105
|
-
for ( const key in material ) {
|
|
106
|
-
|
|
107
|
-
const value = material[ key ];
|
|
108
|
-
if ( value && value.isTexture ) {
|
|
109
|
-
|
|
110
|
-
textureSet.add( value );
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
} );
|
|
117
|
-
|
|
118
|
-
staticGeometryGenerator.attributes = attributes;
|
|
119
|
-
staticGeometryGenerator.generate( geometry );
|
|
120
|
-
|
|
121
|
-
const materialIndexAttribute = getGroupMaterialIndicesAttribute( geometry, materials, materials );
|
|
122
|
-
geometry.setAttribute( 'materialIndex', materialIndexAttribute );
|
|
123
|
-
geometry.clearGroups();
|
|
124
|
-
|
|
125
|
-
this.materials = materials;
|
|
126
|
-
this.textures = Array.from( textureSet );
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
generate() {
|
|
131
|
-
|
|
132
|
-
const { objects, staticGeometryGenerator, geometry, bvhOptions } = this;
|
|
133
|
-
if ( this.bvh === null ) {
|
|
134
|
-
|
|
135
|
-
this.prepScene();
|
|
136
|
-
this.bvh = new MeshBVH( geometry, { strategy: SAH, maxLeafTris: 1, ...bvhOptions } );
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
lights: this.lights,
|
|
140
|
-
bvh: this.bvh,
|
|
141
|
-
materials: this.materials,
|
|
142
|
-
textures: this.textures,
|
|
143
|
-
objects,
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
} else {
|
|
147
|
-
|
|
148
|
-
const { bvh } = this;
|
|
149
|
-
staticGeometryGenerator.generate( geometry );
|
|
150
|
-
bvh.refit();
|
|
151
|
-
return {
|
|
152
|
-
lights: this.lights,
|
|
153
|
-
bvh: this.bvh,
|
|
154
|
-
materials: this.materials,
|
|
155
|
-
textures: this.textures,
|
|
156
|
-
objects,
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
// https://github.com/gkjohnson/webxr-sandbox/blob/main/skinned-mesh-batching/src/MaterialReducer.js
|
|
2
|
-
|
|
3
|
-
function isTypedArray( arr ) {
|
|
4
|
-
|
|
5
|
-
return arr.buffer instanceof ArrayBuffer && 'BYTES_PER_ELEMENT' in arr;
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class MaterialReducer {
|
|
10
|
-
|
|
11
|
-
constructor() {
|
|
12
|
-
|
|
13
|
-
const ignoreKeys = new Set();
|
|
14
|
-
ignoreKeys.add( 'uuid' );
|
|
15
|
-
|
|
16
|
-
this.ignoreKeys = ignoreKeys;
|
|
17
|
-
this.shareTextures = true;
|
|
18
|
-
this.textures = [];
|
|
19
|
-
this.materials = [];
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
areEqual( objectA, objectB ) {
|
|
24
|
-
|
|
25
|
-
const keySet = new Set();
|
|
26
|
-
const traverseSet = new Set();
|
|
27
|
-
const ignoreKeys = this.ignoreKeys;
|
|
28
|
-
|
|
29
|
-
const traverse = ( a, b ) => {
|
|
30
|
-
|
|
31
|
-
if ( a === b ) {
|
|
32
|
-
|
|
33
|
-
return true;
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if ( a && b && a instanceof Object && b instanceof Object ) {
|
|
38
|
-
|
|
39
|
-
if ( traverseSet.has( a ) || traverseSet.has( b ) ) {
|
|
40
|
-
|
|
41
|
-
throw new Error( 'MaterialReducer: Material is recursive.' );
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const aIsElement = a instanceof Element;
|
|
46
|
-
const bIsElement = b instanceof Element;
|
|
47
|
-
if ( aIsElement || bIsElement ) {
|
|
48
|
-
|
|
49
|
-
if ( aIsElement !== bIsElement || ! ( a instanceof Image ) || ! ( b instanceof Image ) ) {
|
|
50
|
-
|
|
51
|
-
return false;
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return a.src === b.src;
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const aIsImageBitmap = a instanceof ImageBitmap;
|
|
60
|
-
const bIsImageBitmap = b instanceof ImageBitmap;
|
|
61
|
-
if ( aIsImageBitmap || bIsImageBitmap ) {
|
|
62
|
-
|
|
63
|
-
return false;
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if ( a.equals ) {
|
|
68
|
-
|
|
69
|
-
return a.equals( b );
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const aIsTypedArray = isTypedArray( a );
|
|
74
|
-
const bIsTypedArray = isTypedArray( b );
|
|
75
|
-
if ( aIsTypedArray || bIsTypedArray ) {
|
|
76
|
-
|
|
77
|
-
if ( aIsTypedArray !== bIsTypedArray || a.constructor !== b.constructor || a.length !== b.length ) {
|
|
78
|
-
|
|
79
|
-
return false;
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
for ( let i = 0, l = a.length; i < l; i ++ ) {
|
|
84
|
-
|
|
85
|
-
if ( a[ i ] !== b[ i ] ) return false;
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return true;
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
traverseSet.add( a );
|
|
94
|
-
traverseSet.add( b );
|
|
95
|
-
|
|
96
|
-
keySet.clear();
|
|
97
|
-
for ( const key in a ) {
|
|
98
|
-
|
|
99
|
-
if ( ! a.hasOwnProperty( key ) || a[ key ] instanceof Function || ignoreKeys.has( key ) ) {
|
|
100
|
-
|
|
101
|
-
continue;
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
keySet.add( key );
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
for ( const key in b ) {
|
|
110
|
-
|
|
111
|
-
if ( ! b.hasOwnProperty( key ) || b[ key ] instanceof Function || ignoreKeys.has( key ) ) {
|
|
112
|
-
|
|
113
|
-
continue;
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
keySet.add( key );
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const keys = Array.from( keySet.values() );
|
|
122
|
-
let result = true;
|
|
123
|
-
for ( const i in keys ) {
|
|
124
|
-
|
|
125
|
-
const key = keys[ i ];
|
|
126
|
-
if ( ignoreKeys.has( key ) ) {
|
|
127
|
-
|
|
128
|
-
continue;
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
result = traverse( a[ key ], b[ key ] );
|
|
133
|
-
if ( ! result ) {
|
|
134
|
-
|
|
135
|
-
break;
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
traverseSet.delete( a );
|
|
142
|
-
traverseSet.delete( b );
|
|
143
|
-
return result;
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return false;
|
|
148
|
-
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
return traverse( objectA, objectB );
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
process( object ) {
|
|
156
|
-
|
|
157
|
-
const { textures, materials } = this;
|
|
158
|
-
let replaced = 0;
|
|
159
|
-
|
|
160
|
-
const processMaterial = material => {
|
|
161
|
-
|
|
162
|
-
// Check if another material matches this one
|
|
163
|
-
let foundMaterial = null;
|
|
164
|
-
for ( const i in materials ) {
|
|
165
|
-
|
|
166
|
-
const otherMaterial = materials[ i ];
|
|
167
|
-
if ( this.areEqual( material, otherMaterial ) ) {
|
|
168
|
-
|
|
169
|
-
foundMaterial = otherMaterial;
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if ( foundMaterial ) {
|
|
176
|
-
|
|
177
|
-
replaced ++;
|
|
178
|
-
return foundMaterial;
|
|
179
|
-
|
|
180
|
-
} else {
|
|
181
|
-
|
|
182
|
-
materials.push( material );
|
|
183
|
-
|
|
184
|
-
if ( this.shareTextures ) {
|
|
185
|
-
|
|
186
|
-
// See if there's another texture that matches the ones on this material
|
|
187
|
-
for ( const key in material ) {
|
|
188
|
-
|
|
189
|
-
if ( ! material.hasOwnProperty( key ) ) continue;
|
|
190
|
-
|
|
191
|
-
const value = material[ key ];
|
|
192
|
-
if ( value && value.isTexture && value.image instanceof Image ) {
|
|
193
|
-
|
|
194
|
-
let foundTexture = null;
|
|
195
|
-
for ( const i in textures ) {
|
|
196
|
-
|
|
197
|
-
const texture = textures[ i ];
|
|
198
|
-
if ( this.areEqual( texture, value ) ) {
|
|
199
|
-
|
|
200
|
-
foundTexture = texture;
|
|
201
|
-
break;
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if ( foundTexture ) {
|
|
208
|
-
|
|
209
|
-
material[ key ] = foundTexture;
|
|
210
|
-
|
|
211
|
-
} else {
|
|
212
|
-
|
|
213
|
-
textures.push( value );
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
return material;
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
object.traverse( c => {
|
|
230
|
-
|
|
231
|
-
if ( c.isMesh && c.material ) {
|
|
232
|
-
|
|
233
|
-
const material = c.material;
|
|
234
|
-
if ( Array.isArray( material ) ) {
|
|
235
|
-
|
|
236
|
-
for ( let i = 0; i < material.length; i ++ ) {
|
|
237
|
-
|
|
238
|
-
material[ i ] = processMaterial( material[ i ] );
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
} else {
|
|
243
|
-
|
|
244
|
-
c.material = processMaterial( material );
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
} );
|
|
251
|
-
|
|
252
|
-
return { replaced, retained: materials.length };
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
}
|