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.
Files changed (75) hide show
  1. package/README.md +111 -464
  2. package/build/index.module.js +5691 -5312
  3. package/build/index.module.js.map +1 -1
  4. package/build/index.umd.cjs +5369 -5003
  5. package/build/index.umd.cjs.map +1 -1
  6. package/package.json +12 -6
  7. package/src/core/PathTracingRenderer.js +59 -46
  8. package/src/core/PathTracingSceneGenerator.js +245 -10
  9. package/src/core/WebGLPathTracer.js +472 -0
  10. package/src/core/utils/BakedGeometry.js +35 -0
  11. package/src/core/utils/BufferAttributeUtils.js +64 -0
  12. package/src/{utils → core/utils}/GeometryPreparationUtils.js +35 -35
  13. package/src/core/utils/MeshDiff.js +102 -0
  14. package/src/core/utils/StaticGeometryGenerator.js +285 -0
  15. package/src/core/utils/convertToStaticGeometry.js +344 -0
  16. package/src/core/utils/mergeGeometries.js +218 -0
  17. package/src/core/utils/sceneUpdateUtils.js +96 -0
  18. package/src/index.d.ts +274 -0
  19. package/src/index.js +4 -20
  20. package/src/materials/MaterialBase.js +4 -0
  21. package/src/materials/fullscreen/ClampedInterpolationMaterial.js +112 -0
  22. package/src/materials/fullscreen/DenoiseMaterial.js +4 -0
  23. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +73 -76
  24. package/src/materials/pathtracing/glsl/{attenuateHit.glsl.js → attenuate_hit_function.glsl.js} +1 -1
  25. package/src/materials/pathtracing/glsl/{cameraUtils.glsl.js → camera_util_functions.glsl.js} +1 -1
  26. package/src/materials/pathtracing/glsl/{directLightContribution.glsl.js → direct_light_contribution_function.glsl.js} +1 -1
  27. package/src/materials/pathtracing/glsl/{getSurfaceRecord.glsl.js → get_surface_record_function.glsl.js} +1 -1
  28. package/src/materials/pathtracing/glsl/index.js +6 -0
  29. package/src/materials/pathtracing/glsl/{renderStructs.glsl.js → render_structs.glsl.js} +1 -1
  30. package/src/materials/pathtracing/glsl/{traceScene.glsl.js → trace_scene_function.glsl.js} +1 -3
  31. package/src/materials/surface/AmbientOcclusionMaterial.js +8 -8
  32. package/src/objects/PhysicalSpotLight.js +2 -2
  33. package/src/shader/bsdf/{bsdfSampling.glsl.js → bsdf_functions.glsl.js} +19 -72
  34. package/src/shader/bsdf/{fog.glsl.js → fog_functions.glsl.js} +1 -1
  35. package/src/shader/bsdf/{ggx.glsl.js → ggx_functions.glsl.js} +1 -1
  36. package/src/shader/bsdf/index.js +5 -0
  37. package/src/shader/bsdf/{iridescence.glsl.js → iridescence_functions.glsl.js} +1 -1
  38. package/src/shader/bsdf/{sheen.glsl.js → sheen_functions.glsl.js} +1 -1
  39. package/src/shader/bvh/index.js +2 -0
  40. package/src/shader/{structs/fogMaterialBvh.glsl.js → bvh/inside_fog_volume_function.glsl.js} +1 -1
  41. package/src/shader/{common/bvhAnyHit.glsl.js → bvh/ray_any_hit_function.glsl.js} +1 -1
  42. package/src/shader/common/{fresnel.glsl.js → fresnel_functions.glsl.js} +1 -1
  43. package/src/shader/common/index.js +5 -0
  44. package/src/shader/common/{math.glsl.js → math_functions.glsl.js} +1 -1
  45. package/src/shader/common/{intersectShapes.glsl.js → shape_intersection_functions.glsl.js} +1 -1
  46. package/src/shader/common/{arraySamplerTexelFetch.glsl.js → texture_sample_functions.glsl.js} +1 -1
  47. package/src/shader/common/{utils.glsl.js → util_functions.glsl.js} +1 -1
  48. package/src/shader/rand/index.js +3 -0
  49. package/src/shader/rand/pcg.glsl.js +1 -1
  50. package/src/shader/rand/sobol.glsl.js +4 -4
  51. package/src/shader/rand/{stratifiedTexture.glsl.js → stratified.glsl.js} +7 -2
  52. package/src/shader/sampling/{equirectSampling.glsl.js → equirect_sampling_functions.glsl.js} +1 -2
  53. package/src/shader/sampling/index.js +3 -0
  54. package/src/shader/sampling/{lightSampling.glsl.js → light_sampling_functions.glsl.js} +3 -3
  55. package/src/shader/sampling/{shapeSampling.glsl.js → shape_sampling_functions.glsl.js} +1 -1
  56. package/src/shader/structs/{cameraStruct.glsl.js → camera_struct.glsl.js} +1 -1
  57. package/src/shader/structs/{equirectStruct.glsl.js → equirect_struct.glsl.js} +1 -1
  58. package/src/shader/structs/index.js +5 -0
  59. package/src/shader/structs/{lightsStruct.glsl.js → lights_struct.glsl.js} +1 -1
  60. package/src/shader/structs/{materialStruct.glsl.js → material_struct.glsl.js} +2 -2
  61. package/src/shader/structs/surface_record_struct.glsl.js +63 -0
  62. package/src/uniforms/EquirectHdrInfoUniform.js +16 -11
  63. package/src/uniforms/LightsInfoUniformStruct.js +21 -10
  64. package/src/uniforms/MaterialsTexture.js +27 -86
  65. package/src/uniforms/RenderTarget2DArray.js +60 -20
  66. package/src/utils/BlurredEnvMapGenerator.js +12 -5
  67. package/src/utils/SobolNumberMapGenerator.js +3 -3
  68. package/src/utils/bufferToHash.js +22 -0
  69. package/src/core/DynamicPathTracingSceneGenerator.js +0 -164
  70. package/src/core/MaterialReducer.js +0 -256
  71. package/src/materials/pathtracing/LambertPathTracingMaterial.js +0 -297
  72. package/src/uniforms/IESProfilesTexture.js +0 -100
  73. package/src/uniforms/utils.js +0 -30
  74. package/src/utils/IESLoader.js +0 -327
  75. 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
+ }