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
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Matrix4 } from 'three';
|
|
2
|
+
import { bufferToHash } from '../../utils/bufferToHash.js';
|
|
3
|
+
|
|
4
|
+
function attributeSort( a, b ) {
|
|
5
|
+
|
|
6
|
+
if ( a.uuid > b.uuid ) return 1;
|
|
7
|
+
if ( a.uuid < b.uuid ) return - 1;
|
|
8
|
+
return 0;
|
|
9
|
+
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function getGeometryHash( geometry ) {
|
|
13
|
+
|
|
14
|
+
let hash = '';
|
|
15
|
+
const attributes = Object.values( geometry.attributes );
|
|
16
|
+
if ( geometry.index ) {
|
|
17
|
+
|
|
18
|
+
attributes.push( geometry.index );
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
attributes.sort( attributeSort );
|
|
23
|
+
|
|
24
|
+
for ( const attr of attributes ) {
|
|
25
|
+
|
|
26
|
+
hash += `${ attr.uuid }_${ attr.version }|`;
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return hash;
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getSkeletonHash( mesh ) {
|
|
35
|
+
|
|
36
|
+
const skeleton = mesh.skeleton;
|
|
37
|
+
if ( skeleton ) {
|
|
38
|
+
|
|
39
|
+
if ( ! skeleton.boneTexture ) {
|
|
40
|
+
|
|
41
|
+
skeleton.computeBoneTexture();
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// we can't use the texture version here because it will change even
|
|
46
|
+
// when the bones haven't
|
|
47
|
+
const dataHash = bufferToHash( skeleton.boneTexture.image.data.buffer );
|
|
48
|
+
return `${ dataHash }_${ skeleton.boneTexture.uuid }`;
|
|
49
|
+
|
|
50
|
+
} else {
|
|
51
|
+
|
|
52
|
+
return null;
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Checks whether the geometry changed between this and last evaluation
|
|
59
|
+
export class MeshDiff {
|
|
60
|
+
|
|
61
|
+
constructor( mesh = null ) {
|
|
62
|
+
|
|
63
|
+
this.matrixWorld = new Matrix4();
|
|
64
|
+
this.geometryHash = null;
|
|
65
|
+
this.skeletonHash = null;
|
|
66
|
+
this.primitiveCount = - 1;
|
|
67
|
+
|
|
68
|
+
if ( mesh !== null ) {
|
|
69
|
+
|
|
70
|
+
this.updateFrom( mesh );
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
updateFrom( mesh ) {
|
|
77
|
+
|
|
78
|
+
const geometry = mesh.geometry;
|
|
79
|
+
const primitiveCount = ( geometry.index ? geometry.index.count : geometry.attributes.position.count ) / 3;
|
|
80
|
+
this.matrixWorld.copy( mesh.matrixWorld );
|
|
81
|
+
this.geometryHash = getGeometryHash( geometry );
|
|
82
|
+
this.primitiveCount = primitiveCount;
|
|
83
|
+
this.skeletonHash = getSkeletonHash( mesh );
|
|
84
|
+
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
didChange( mesh ) {
|
|
88
|
+
|
|
89
|
+
const geometry = mesh.geometry;
|
|
90
|
+
const primitiveCount = ( geometry.index ? geometry.index.count : geometry.attributes.position.count ) / 3;
|
|
91
|
+
|
|
92
|
+
const identical =
|
|
93
|
+
this.matrixWorld.equals( mesh.matrixWorld ) &&
|
|
94
|
+
this.geometryHash === getGeometryHash( geometry ) &&
|
|
95
|
+
this.skeletonHash === getSkeletonHash( mesh ) &&
|
|
96
|
+
this.primitiveCount === primitiveCount;
|
|
97
|
+
|
|
98
|
+
return ! identical;
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { BufferAttribute, BufferGeometry, Mesh, MeshBasicMaterial } from 'three';
|
|
2
|
+
import { mergeGeometries } from './mergeGeometries.js';
|
|
3
|
+
import { setCommonAttributes } from './GeometryPreparationUtils.js';
|
|
4
|
+
import { BakedGeometry } from './BakedGeometry.js';
|
|
5
|
+
|
|
6
|
+
export const NO_CHANGE = 0;
|
|
7
|
+
export const GEOMETRY_ADJUSTED = 1;
|
|
8
|
+
export const GEOMETRY_REBUILT = 2;
|
|
9
|
+
|
|
10
|
+
// iterate over only the meshes in the provided objects
|
|
11
|
+
function flatTraverseMeshes( objects, cb ) {
|
|
12
|
+
|
|
13
|
+
for ( let i = 0, l = objects.length; i < l; i ++ ) {
|
|
14
|
+
|
|
15
|
+
const object = objects[ i ];
|
|
16
|
+
object.traverse( o => {
|
|
17
|
+
|
|
18
|
+
if ( o.isMesh ) {
|
|
19
|
+
|
|
20
|
+
cb( o );
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
} );
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// return the set of materials used by the provided meshes
|
|
31
|
+
function getMaterials( meshes ) {
|
|
32
|
+
|
|
33
|
+
const materials = [];
|
|
34
|
+
for ( let i = 0, l = meshes.length; i < l; i ++ ) {
|
|
35
|
+
|
|
36
|
+
const mesh = meshes[ i ];
|
|
37
|
+
if ( Array.isArray( mesh.material ) ) {
|
|
38
|
+
|
|
39
|
+
materials.push( ...mesh.material );
|
|
40
|
+
|
|
41
|
+
} else {
|
|
42
|
+
|
|
43
|
+
materials.push( mesh.material );
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return materials;
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function mergeGeometryList( geometries, target, options ) {
|
|
54
|
+
|
|
55
|
+
// If we have no geometry to merge then provide an empty geometry.
|
|
56
|
+
if ( geometries.length === 0 ) {
|
|
57
|
+
|
|
58
|
+
// if there are no geometries then just create a fake empty geometry to provide
|
|
59
|
+
target.setIndex( null );
|
|
60
|
+
|
|
61
|
+
// remove all geometry
|
|
62
|
+
const attrs = target.attributes;
|
|
63
|
+
for ( const key in attrs ) {
|
|
64
|
+
|
|
65
|
+
target.deleteAttribute( key );
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// create dummy attributes
|
|
70
|
+
for ( const key in options.attributes ) {
|
|
71
|
+
|
|
72
|
+
target.setAttribute( options.attributes[ key ], new BufferAttribute( new Float32Array( 0 ), 4, false ) );
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
} else {
|
|
77
|
+
|
|
78
|
+
mergeGeometries( geometries, options, target );
|
|
79
|
+
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Mark all attributes as needing an update
|
|
83
|
+
for ( const key in target.attributes ) {
|
|
84
|
+
|
|
85
|
+
target.attributes[ key ].needsUpdate = true;
|
|
86
|
+
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
export class StaticGeometryGenerator {
|
|
93
|
+
|
|
94
|
+
constructor( objects ) {
|
|
95
|
+
|
|
96
|
+
this.objects = null;
|
|
97
|
+
this.useGroups = true;
|
|
98
|
+
this.applyWorldTransforms = true;
|
|
99
|
+
this.generateMissingAttributes = true;
|
|
100
|
+
this.overwriteIndex = true;
|
|
101
|
+
this.attributes = [ 'position', 'normal', 'color', 'tangent', 'uv', 'uv2' ];
|
|
102
|
+
this._intermediateGeometry = new Map();
|
|
103
|
+
this._geometryMergeSets = new WeakMap();
|
|
104
|
+
this._mergeOrder = [];
|
|
105
|
+
this._dummyMesh = null;
|
|
106
|
+
|
|
107
|
+
this.setObjects( objects || [] );
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
_getDummyMesh() {
|
|
112
|
+
|
|
113
|
+
// return a consistent dummy mesh
|
|
114
|
+
if ( ! this._dummyMesh ) {
|
|
115
|
+
|
|
116
|
+
const dummyMaterial = new MeshBasicMaterial();
|
|
117
|
+
const emptyGeometry = new BufferGeometry();
|
|
118
|
+
emptyGeometry.setAttribute( 'position', new BufferAttribute( new Float32Array( 9 ), 3 ) );
|
|
119
|
+
this._dummyMesh = new Mesh( emptyGeometry, dummyMaterial );
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return this._dummyMesh;
|
|
124
|
+
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
_getMeshes() {
|
|
128
|
+
|
|
129
|
+
// iterate over only the meshes in the provided objects
|
|
130
|
+
const meshes = [];
|
|
131
|
+
flatTraverseMeshes( this.objects, mesh => {
|
|
132
|
+
|
|
133
|
+
meshes.push( mesh );
|
|
134
|
+
|
|
135
|
+
} );
|
|
136
|
+
|
|
137
|
+
// Sort the geometry so it's in a reliable order
|
|
138
|
+
meshes.sort( ( a, b ) => {
|
|
139
|
+
|
|
140
|
+
if ( a.uuid > b.uuid ) return 1;
|
|
141
|
+
if ( a.uuid < b.uuid ) return - 1;
|
|
142
|
+
return 0;
|
|
143
|
+
|
|
144
|
+
} );
|
|
145
|
+
|
|
146
|
+
if ( meshes.length === 0 ) {
|
|
147
|
+
|
|
148
|
+
meshes.push( this._getDummyMesh() );
|
|
149
|
+
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return meshes;
|
|
153
|
+
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
_updateIntermediateGeometries() {
|
|
157
|
+
|
|
158
|
+
const { _intermediateGeometry } = this;
|
|
159
|
+
|
|
160
|
+
const meshes = this._getMeshes();
|
|
161
|
+
const unusedMeshKeys = new Set( _intermediateGeometry.keys() );
|
|
162
|
+
const convertOptions = {
|
|
163
|
+
attributes: this.attributes,
|
|
164
|
+
applyWorldTransforms: this.applyWorldTransforms,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
for ( let i = 0, l = meshes.length; i < l; i ++ ) {
|
|
168
|
+
|
|
169
|
+
const mesh = meshes[ i ];
|
|
170
|
+
const meshKey = mesh.uuid;
|
|
171
|
+
unusedMeshKeys.delete( meshKey );
|
|
172
|
+
|
|
173
|
+
// initialize the intermediate geometry
|
|
174
|
+
if ( ! _intermediateGeometry.has( meshKey ) ) {
|
|
175
|
+
|
|
176
|
+
_intermediateGeometry.set( meshKey, new BakedGeometry() );
|
|
177
|
+
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// transform the geometry into the intermediate buffer geometry, saving whether
|
|
181
|
+
// or not it changed.
|
|
182
|
+
const geom = _intermediateGeometry.get( meshKey );
|
|
183
|
+
if ( geom.updateFrom( mesh, convertOptions ) ) {
|
|
184
|
+
|
|
185
|
+
// TODO: provide option for only generating the set of attributes that are present
|
|
186
|
+
// and are in the attributes array
|
|
187
|
+
if ( this.generateMissingAttributes ) {
|
|
188
|
+
|
|
189
|
+
setCommonAttributes( geom, this.attributes );
|
|
190
|
+
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
unusedMeshKeys.forEach( key => {
|
|
198
|
+
|
|
199
|
+
_intermediateGeometry.delete( key );
|
|
200
|
+
|
|
201
|
+
} );
|
|
202
|
+
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
setObjects( objects ) {
|
|
206
|
+
|
|
207
|
+
if ( Array.isArray( objects ) ) {
|
|
208
|
+
|
|
209
|
+
this.objects = [ ...objects ];
|
|
210
|
+
|
|
211
|
+
} else {
|
|
212
|
+
|
|
213
|
+
this.objects = [ objects ];
|
|
214
|
+
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
generate( targetGeometry = new BufferGeometry() ) {
|
|
220
|
+
|
|
221
|
+
// track which attributes have been updated and which to skip to avoid unnecessary attribute copies
|
|
222
|
+
const { useGroups, overwriteIndex, _intermediateGeometry, _geometryMergeSets } = this;
|
|
223
|
+
|
|
224
|
+
const meshes = this._getMeshes();
|
|
225
|
+
const skipAssigningAttributes = [];
|
|
226
|
+
const mergeGeometry = [];
|
|
227
|
+
const previousMergeInfo = _geometryMergeSets.get( targetGeometry ) || [];
|
|
228
|
+
|
|
229
|
+
// update all the intermediate static geometry representations
|
|
230
|
+
this._updateIntermediateGeometries();
|
|
231
|
+
|
|
232
|
+
// get the list of geometries to merge
|
|
233
|
+
let forceUpdate = false;
|
|
234
|
+
for ( let i = 0, l = meshes.length; i < l; i ++ ) {
|
|
235
|
+
|
|
236
|
+
const mesh = meshes[ i ];
|
|
237
|
+
const geom = _intermediateGeometry.get( mesh.uuid );
|
|
238
|
+
mergeGeometry.push( geom );
|
|
239
|
+
|
|
240
|
+
const info = previousMergeInfo[ i ];
|
|
241
|
+
if ( ! info || info.uuid !== geom.uuid ) {
|
|
242
|
+
|
|
243
|
+
skipAssigningAttributes.push( false );
|
|
244
|
+
forceUpdate = true;
|
|
245
|
+
|
|
246
|
+
} else if ( info.version !== geom.version ) {
|
|
247
|
+
|
|
248
|
+
skipAssigningAttributes.push( false );
|
|
249
|
+
|
|
250
|
+
} else {
|
|
251
|
+
|
|
252
|
+
skipAssigningAttributes.push( true );
|
|
253
|
+
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// If we have no geometry to merge then provide an empty geometry.
|
|
259
|
+
mergeGeometryList( mergeGeometry, targetGeometry, { useGroups, forceUpdate, skipAssigningAttributes, overwriteIndex } );
|
|
260
|
+
|
|
261
|
+
// force update means the attribute buffer lengths have changed
|
|
262
|
+
if ( forceUpdate ) {
|
|
263
|
+
|
|
264
|
+
targetGeometry.dispose();
|
|
265
|
+
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
_geometryMergeSets.set( targetGeometry, mergeGeometry.map( g => ( {
|
|
269
|
+
version: g.version,
|
|
270
|
+
uuid: g.uuid,
|
|
271
|
+
} ) ) );
|
|
272
|
+
|
|
273
|
+
let changeType = NO_CHANGE;
|
|
274
|
+
if ( forceUpdate ) changeType = GEOMETRY_REBUILT;
|
|
275
|
+
else if ( skipAssigningAttributes.includes( false ) ) changeType = GEOMETRY_ADJUSTED;
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
changeType,
|
|
279
|
+
materials: getMaterials( meshes ),
|
|
280
|
+
geometry: targetGeometry,
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { BufferGeometry, Matrix3, Matrix4, Vector3, Vector4 } from 'three';
|
|
2
|
+
import { copyAttributeContents, createAttributeClone, validateAttributes } from './BufferAttributeUtils.js';
|
|
3
|
+
|
|
4
|
+
const _positionVector = /*@__PURE__*/ new Vector3();
|
|
5
|
+
const _normalVector = /*@__PURE__*/ new Vector3();
|
|
6
|
+
const _tangentVector = /*@__PURE__*/ new Vector3();
|
|
7
|
+
const _tangentVector4 = /*@__PURE__*/ new Vector4();
|
|
8
|
+
|
|
9
|
+
const _morphVector = /*@__PURE__*/ new Vector3();
|
|
10
|
+
const _temp = /*@__PURE__*/ new Vector3();
|
|
11
|
+
|
|
12
|
+
const _skinIndex = /*@__PURE__*/ new Vector4();
|
|
13
|
+
const _skinWeight = /*@__PURE__*/ new Vector4();
|
|
14
|
+
const _matrix = /*@__PURE__*/ new Matrix4();
|
|
15
|
+
const _boneMatrix = /*@__PURE__*/ new Matrix4();
|
|
16
|
+
|
|
17
|
+
// A version of "SkinnedMesh.boneTransform" for normals
|
|
18
|
+
function boneNormalTransform( mesh, index, target ) {
|
|
19
|
+
|
|
20
|
+
const skeleton = mesh.skeleton;
|
|
21
|
+
const geometry = mesh.geometry;
|
|
22
|
+
const bones = skeleton.bones;
|
|
23
|
+
const boneInverses = skeleton.boneInverses;
|
|
24
|
+
|
|
25
|
+
_skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index );
|
|
26
|
+
_skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index );
|
|
27
|
+
|
|
28
|
+
_matrix.elements.fill( 0 );
|
|
29
|
+
|
|
30
|
+
for ( let i = 0; i < 4; i ++ ) {
|
|
31
|
+
|
|
32
|
+
const weight = _skinWeight.getComponent( i );
|
|
33
|
+
|
|
34
|
+
if ( weight !== 0 ) {
|
|
35
|
+
|
|
36
|
+
const boneIndex = _skinIndex.getComponent( i );
|
|
37
|
+
_boneMatrix.multiplyMatrices( bones[ boneIndex ].matrixWorld, boneInverses[ boneIndex ] );
|
|
38
|
+
|
|
39
|
+
addScaledMatrix( _matrix, _boneMatrix, weight );
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
_matrix.multiply( mesh.bindMatrix ).premultiply( mesh.bindMatrixInverse );
|
|
46
|
+
target.transformDirection( _matrix );
|
|
47
|
+
|
|
48
|
+
return target;
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Applies the morph target data to the target vector
|
|
53
|
+
function applyMorphTarget( morphData, morphInfluences, morphTargetsRelative, i, target ) {
|
|
54
|
+
|
|
55
|
+
_morphVector.set( 0, 0, 0 );
|
|
56
|
+
for ( let j = 0, jl = morphData.length; j < jl; j ++ ) {
|
|
57
|
+
|
|
58
|
+
const influence = morphInfluences[ j ];
|
|
59
|
+
const morphAttribute = morphData[ j ];
|
|
60
|
+
|
|
61
|
+
if ( influence === 0 ) continue;
|
|
62
|
+
|
|
63
|
+
_temp.fromBufferAttribute( morphAttribute, i );
|
|
64
|
+
|
|
65
|
+
if ( morphTargetsRelative ) {
|
|
66
|
+
|
|
67
|
+
_morphVector.addScaledVector( _temp, influence );
|
|
68
|
+
|
|
69
|
+
} else {
|
|
70
|
+
|
|
71
|
+
_morphVector.addScaledVector( _temp.sub( target ), influence );
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
target.add( _morphVector );
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Adds the "matrix" multiplied by "scale" to "target"
|
|
82
|
+
function addScaledMatrix( target, matrix, scale ) {
|
|
83
|
+
|
|
84
|
+
const targetArray = target.elements;
|
|
85
|
+
const matrixArray = matrix.elements;
|
|
86
|
+
for ( let i = 0, l = matrixArray.length; i < l; i ++ ) {
|
|
87
|
+
|
|
88
|
+
targetArray[ i ] += matrixArray[ i ] * scale;
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// inverts the geometry in place
|
|
95
|
+
function invertGeometry( geometry ) {
|
|
96
|
+
|
|
97
|
+
const { index, attributes } = geometry;
|
|
98
|
+
if ( index ) {
|
|
99
|
+
|
|
100
|
+
for ( let i = 0, l = index.count; i < l; i += 3 ) {
|
|
101
|
+
|
|
102
|
+
const v0 = index.getX( i );
|
|
103
|
+
const v2 = index.getX( i + 2 );
|
|
104
|
+
index.setX( i, v2 );
|
|
105
|
+
index.setX( i + 2, v0 );
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
} else {
|
|
110
|
+
|
|
111
|
+
for ( const key in attributes ) {
|
|
112
|
+
|
|
113
|
+
const attr = attributes[ key ];
|
|
114
|
+
const itemSize = attr.itemSize;
|
|
115
|
+
for ( let i = 0, l = attr.count; i < l; i += 3 ) {
|
|
116
|
+
|
|
117
|
+
for ( let j = 0; j < itemSize; j ++ ) {
|
|
118
|
+
|
|
119
|
+
const v0 = attr.getComponent( i, j );
|
|
120
|
+
const v2 = attr.getComponent( i + 2, j );
|
|
121
|
+
attr.setComponent( i, j, v2 );
|
|
122
|
+
attr.setComponent( i + 2, j, v0 );
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return geometry;
|
|
133
|
+
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function convertToStaticGeometry( mesh, options = {}, targetGeometry = new BufferGeometry() ) {
|
|
137
|
+
|
|
138
|
+
options = {
|
|
139
|
+
applyWorldTransforms: true,
|
|
140
|
+
attributes: [],
|
|
141
|
+
...options
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const geometry = mesh.geometry;
|
|
145
|
+
const applyWorldTransforms = options.applyWorldTransforms;
|
|
146
|
+
const includeNormal = options.attributes.includes( 'normal' );
|
|
147
|
+
const includeTangent = options.attributes.includes( 'tangent' );
|
|
148
|
+
const attributes = geometry.attributes;
|
|
149
|
+
const targetAttributes = targetGeometry.attributes;
|
|
150
|
+
|
|
151
|
+
// strip any unused and unneeded attributes
|
|
152
|
+
for ( const key in targetGeometry.attributes ) {
|
|
153
|
+
|
|
154
|
+
if ( ! options.attributes.includes( key ) || ! ( key in geometry.attributes ) ) {
|
|
155
|
+
|
|
156
|
+
targetGeometry.deleteAttribute( key );
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// initialize the attributes if they don't exist
|
|
163
|
+
if ( ! targetGeometry.index && geometry.index ) {
|
|
164
|
+
|
|
165
|
+
targetGeometry.index = geometry.index.clone();
|
|
166
|
+
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if ( ! targetAttributes.position ) {
|
|
170
|
+
|
|
171
|
+
targetGeometry.setAttribute( 'position', createAttributeClone( attributes.position ) );
|
|
172
|
+
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if ( includeNormal && ! targetAttributes.normal && attributes.normal ) {
|
|
176
|
+
|
|
177
|
+
targetGeometry.setAttribute( 'normal', createAttributeClone( attributes.normal ) );
|
|
178
|
+
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if ( includeTangent && ! targetAttributes.tangent && attributes.tangent ) {
|
|
182
|
+
|
|
183
|
+
targetGeometry.setAttribute( 'tangent', createAttributeClone( attributes.tangent ) );
|
|
184
|
+
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ensure the attributes are consistent
|
|
188
|
+
validateAttributes( geometry.index, targetGeometry.index );
|
|
189
|
+
validateAttributes( attributes.position, targetAttributes.position );
|
|
190
|
+
|
|
191
|
+
if ( includeNormal ) {
|
|
192
|
+
|
|
193
|
+
validateAttributes( attributes.normal, targetAttributes.normal );
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if ( includeTangent ) {
|
|
198
|
+
|
|
199
|
+
validateAttributes( attributes.tangent, targetAttributes.tangent );
|
|
200
|
+
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// generate transformed vertex attribute data
|
|
204
|
+
const position = attributes.position;
|
|
205
|
+
const normal = includeNormal ? attributes.normal : null;
|
|
206
|
+
const tangent = includeTangent ? attributes.tangent : null;
|
|
207
|
+
const morphPosition = geometry.morphAttributes.position;
|
|
208
|
+
const morphNormal = geometry.morphAttributes.normal;
|
|
209
|
+
const morphTangent = geometry.morphAttributes.tangent;
|
|
210
|
+
const morphTargetsRelative = geometry.morphTargetsRelative;
|
|
211
|
+
const morphInfluences = mesh.morphTargetInfluences;
|
|
212
|
+
const normalMatrix = new Matrix3();
|
|
213
|
+
normalMatrix.getNormalMatrix( mesh.matrixWorld );
|
|
214
|
+
|
|
215
|
+
// copy the index
|
|
216
|
+
if ( geometry.index ) {
|
|
217
|
+
|
|
218
|
+
targetGeometry.index.array.set( geometry.index.array );
|
|
219
|
+
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// copy and apply other attributes
|
|
223
|
+
for ( let i = 0, l = attributes.position.count; i < l; i ++ ) {
|
|
224
|
+
|
|
225
|
+
_positionVector.fromBufferAttribute( position, i );
|
|
226
|
+
if ( normal ) {
|
|
227
|
+
|
|
228
|
+
_normalVector.fromBufferAttribute( normal, i );
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if ( tangent ) {
|
|
233
|
+
|
|
234
|
+
_tangentVector4.fromBufferAttribute( tangent, i );
|
|
235
|
+
_tangentVector.fromBufferAttribute( tangent, i );
|
|
236
|
+
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// apply morph target transform
|
|
240
|
+
if ( morphInfluences ) {
|
|
241
|
+
|
|
242
|
+
if ( morphPosition ) {
|
|
243
|
+
|
|
244
|
+
applyMorphTarget( morphPosition, morphInfluences, morphTargetsRelative, i, _positionVector );
|
|
245
|
+
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if ( morphNormal ) {
|
|
249
|
+
|
|
250
|
+
applyMorphTarget( morphNormal, morphInfluences, morphTargetsRelative, i, _normalVector );
|
|
251
|
+
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if ( morphTangent ) {
|
|
255
|
+
|
|
256
|
+
applyMorphTarget( morphTangent, morphInfluences, morphTargetsRelative, i, _tangentVector );
|
|
257
|
+
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// apply bone transform
|
|
263
|
+
if ( mesh.isSkinnedMesh ) {
|
|
264
|
+
|
|
265
|
+
mesh.applyBoneTransform( i, _positionVector );
|
|
266
|
+
if ( normal ) {
|
|
267
|
+
|
|
268
|
+
boneNormalTransform( mesh, i, _normalVector );
|
|
269
|
+
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if ( tangent ) {
|
|
273
|
+
|
|
274
|
+
boneNormalTransform( mesh, i, _tangentVector );
|
|
275
|
+
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// update the vectors of the attributes
|
|
281
|
+
if ( applyWorldTransforms ) {
|
|
282
|
+
|
|
283
|
+
_positionVector.applyMatrix4( mesh.matrixWorld );
|
|
284
|
+
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
targetAttributes.position.setXYZ( i, _positionVector.x, _positionVector.y, _positionVector.z );
|
|
288
|
+
|
|
289
|
+
if ( normal ) {
|
|
290
|
+
|
|
291
|
+
if ( applyWorldTransforms ) {
|
|
292
|
+
|
|
293
|
+
_normalVector.applyNormalMatrix( normalMatrix );
|
|
294
|
+
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
targetAttributes.normal.setXYZ( i, _normalVector.x, _normalVector.y, _normalVector.z );
|
|
298
|
+
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if ( tangent ) {
|
|
302
|
+
|
|
303
|
+
if ( applyWorldTransforms ) {
|
|
304
|
+
|
|
305
|
+
_tangentVector.transformDirection( mesh.matrixWorld );
|
|
306
|
+
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
targetAttributes.tangent.setXYZW( i, _tangentVector.x, _tangentVector.y, _tangentVector.z, _tangentVector4.w );
|
|
310
|
+
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// copy other attributes over
|
|
316
|
+
for ( const i in options.attributes ) {
|
|
317
|
+
|
|
318
|
+
const key = options.attributes[ i ];
|
|
319
|
+
if ( key === 'position' || key === 'tangent' || key === 'normal' || ! ( key in attributes ) ) {
|
|
320
|
+
|
|
321
|
+
continue;
|
|
322
|
+
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if ( ! targetAttributes[ key ] ) {
|
|
326
|
+
|
|
327
|
+
targetGeometry.setAttribute( key, createAttributeClone( attributes[ key ] ) );
|
|
328
|
+
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
validateAttributes( attributes[ key ], targetAttributes[ key ] );
|
|
332
|
+
copyAttributeContents( attributes[ key ], targetAttributes[ key ] );
|
|
333
|
+
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if ( mesh.matrixWorld.determinant() < 0 ) {
|
|
337
|
+
|
|
338
|
+
invertGeometry( targetGeometry );
|
|
339
|
+
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return targetGeometry;
|
|
343
|
+
|
|
344
|
+
}
|