super-three 0.169.1 → 0.170.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.
Files changed (193) hide show
  1. package/build/three.cjs +974 -562
  2. package/build/three.module.js +975 -559
  3. package/build/three.module.min.js +1 -1
  4. package/build/three.webgpu.js +8398 -9842
  5. package/build/three.webgpu.min.js +1 -1
  6. package/build/three.webgpu.nodes.js +8398 -9842
  7. package/build/three.webgpu.nodes.min.js +1 -1
  8. package/examples/jsm/Addons.js +3 -3
  9. package/examples/jsm/animation/MMDAnimationHelper.js +2 -0
  10. package/examples/jsm/animation/MMDPhysics.js +2 -0
  11. package/examples/jsm/controls/TransformControls.js +22 -6
  12. package/examples/jsm/csm/CSM.js +2 -2
  13. package/examples/jsm/csm/CSMFrustum.js +7 -4
  14. package/examples/jsm/csm/CSMHelper.js +2 -0
  15. package/examples/jsm/csm/CSMShadowNode.js +435 -0
  16. package/examples/jsm/curves/NURBSCurve.js +34 -3
  17. package/examples/jsm/exporters/GLTFExporter.js +138 -71
  18. package/examples/jsm/exporters/KTX2Exporter.js +35 -12
  19. package/examples/jsm/exporters/MMDExporter.js +6 -0
  20. package/examples/jsm/exporters/USDZExporter.js +21 -3
  21. package/examples/jsm/geometries/DecalGeometry.js +65 -20
  22. package/examples/jsm/geometries/InstancedPointsGeometry.js +2 -0
  23. package/examples/jsm/helpers/TextureHelperGPU.js +175 -0
  24. package/examples/jsm/interactive/HTMLMesh.js +1 -0
  25. package/examples/jsm/lighting/TiledLighting.js +18 -0
  26. package/examples/jsm/lights/RectAreaLightTexturesLib.js +1 -1
  27. package/examples/jsm/lines/LineMaterial.js +7 -2
  28. package/examples/jsm/lines/LineSegmentsGeometry.js +2 -0
  29. package/examples/jsm/lines/webgpu/LineSegments2.js +0 -15
  30. package/examples/jsm/lines/webgpu/Wireframe.js +56 -0
  31. package/examples/jsm/loaders/3DMLoader.js +1 -0
  32. package/examples/jsm/loaders/3MFLoader.js +91 -0
  33. package/examples/jsm/loaders/FBXLoader.js +12 -2
  34. package/examples/jsm/loaders/GLTFLoader.js +2 -0
  35. package/examples/jsm/loaders/KTX2Loader.js +140 -49
  36. package/examples/jsm/loaders/LDrawLoader.js +22 -136
  37. package/examples/jsm/loaders/LottieLoader.js +1 -0
  38. package/examples/jsm/loaders/MMDLoader.js +9 -2
  39. package/examples/jsm/materials/LDrawConditionalLineMaterial.js +143 -0
  40. package/examples/jsm/materials/LDrawConditionalLineNodeMaterial.js +114 -0
  41. package/examples/jsm/materials/MeshGouraudMaterial.js +6 -2
  42. package/examples/jsm/math/ColorSpaces.js +76 -0
  43. package/examples/jsm/misc/ProgressiveLightMap.js +52 -40
  44. package/examples/jsm/misc/ProgressiveLightMapGPU.js +293 -0
  45. package/examples/jsm/misc/VolumeSlice.js +1 -0
  46. package/examples/jsm/objects/WaterMesh.js +8 -8
  47. package/examples/jsm/postprocessing/AfterimagePass.js +14 -3
  48. package/examples/jsm/postprocessing/SSRPass.js +6 -0
  49. package/examples/jsm/renderers/CSS2DRenderer.js +6 -3
  50. package/examples/jsm/renderers/CSS3DRenderer.js +7 -4
  51. package/examples/jsm/shaders/FXAAShader.js +225 -224
  52. package/examples/jsm/transpiler/ShaderToyDecoder.js +1 -1
  53. package/{src/nodes → examples/jsm/tsl}/display/AfterImageNode.js +17 -20
  54. package/{src/nodes → examples/jsm/tsl}/display/AnaglyphPassNode.js +2 -6
  55. package/{src/nodes → examples/jsm/tsl}/display/AnamorphicNode.js +12 -18
  56. package/{src/nodes → examples/jsm/tsl}/display/BleachBypass.js +1 -3
  57. package/{src/nodes → examples/jsm/tsl}/display/BloomNode.js +11 -40
  58. package/{src/nodes → examples/jsm/tsl}/display/DenoiseNode.js +18 -41
  59. package/{src/nodes → examples/jsm/tsl}/display/DepthOfFieldNode.js +2 -8
  60. package/{src/nodes → examples/jsm/tsl}/display/DotScreenNode.js +2 -9
  61. package/examples/jsm/tsl/display/FXAANode.js +316 -0
  62. package/{src/nodes → examples/jsm/tsl}/display/FilmNode.js +2 -6
  63. package/{src/nodes → examples/jsm/tsl}/display/GTAONode.js +30 -70
  64. package/{src/nodes → examples/jsm/tsl}/display/GaussianBlurNode.js +74 -36
  65. package/examples/jsm/tsl/display/LensflareNode.js +161 -0
  66. package/{src/nodes → examples/jsm/tsl}/display/Lut3DNode.js +1 -4
  67. package/{src/nodes → examples/jsm/tsl}/display/MotionBlur.js +2 -3
  68. package/examples/jsm/tsl/display/OutlineNode.js +434 -0
  69. package/{src/nodes → examples/jsm/tsl}/display/ParallaxBarrierPassNode.js +1 -5
  70. package/{src/nodes → examples/jsm/tsl}/display/PixelationPassNode.js +3 -15
  71. package/{src/nodes → examples/jsm/tsl}/display/RGBShiftNode.js +1 -6
  72. package/examples/jsm/tsl/display/SMAANode.js +620 -0
  73. package/{src/nodes → examples/jsm/tsl}/display/SSAAPassNode.js +10 -25
  74. package/examples/jsm/tsl/display/SSRNode.js +343 -0
  75. package/{src/nodes → examples/jsm/tsl}/display/Sepia.js +1 -2
  76. package/{src/nodes → examples/jsm/tsl}/display/SobelOperatorNode.js +3 -11
  77. package/{src/nodes → examples/jsm/tsl}/display/StereoCompositePassNode.js +9 -11
  78. package/{src/nodes → examples/jsm/tsl}/display/StereoPassNode.js +10 -11
  79. package/examples/jsm/tsl/display/TRAAPassNode.js +355 -0
  80. package/{src/nodes → examples/jsm/tsl}/display/TransitionNode.js +1 -6
  81. package/examples/jsm/tsl/display/hashBlur.js +24 -0
  82. package/examples/jsm/tsl/lighting/TiledLightsNode.js +392 -0
  83. package/examples/jsm/utils/{TextureUtilsGPU.js → WebGPUTextureUtils.js} +2 -1
  84. package/examples/jsm/webxr/OculusHandPointerModel.js +21 -21
  85. package/examples/jsm/webxr/Text2D.js +6 -6
  86. package/package.json +2 -2
  87. package/src/Three.WebGPU.Nodes.js +4 -0
  88. package/src/Three.WebGPU.js +4 -0
  89. package/src/constants.js +1 -6
  90. package/src/core/BufferGeometry.js +44 -6
  91. package/src/geometries/CylinderGeometry.js +2 -2
  92. package/src/materials/LineBasicMaterial.js +6 -2
  93. package/src/materials/LineDashedMaterial.js +6 -2
  94. package/src/materials/Material.js +14 -1
  95. package/src/materials/MeshBasicMaterial.js +6 -2
  96. package/src/materials/MeshDepthMaterial.js +6 -2
  97. package/src/materials/MeshDistanceMaterial.js +6 -2
  98. package/src/materials/MeshLambertMaterial.js +6 -2
  99. package/src/materials/MeshMatcapMaterial.js +6 -2
  100. package/src/materials/MeshNormalMaterial.js +6 -2
  101. package/src/materials/MeshPhongMaterial.js +6 -2
  102. package/src/materials/MeshPhysicalMaterial.js +6 -2
  103. package/src/materials/MeshStandardMaterial.js +6 -2
  104. package/src/materials/MeshToonMaterial.js +6 -2
  105. package/src/materials/PointsMaterial.js +6 -2
  106. package/src/materials/RawShaderMaterial.js +6 -2
  107. package/src/materials/ShaderMaterial.js +6 -2
  108. package/src/materials/ShadowMaterial.js +6 -2
  109. package/src/materials/SpriteMaterial.js +6 -2
  110. package/src/materials/nodes/NodeMaterial.js +31 -9
  111. package/src/materials/nodes/SpriteNodeMaterial.js +11 -2
  112. package/src/materials/nodes/manager/NodeMaterialObserver.js +111 -2
  113. package/src/math/ColorManagement.js +118 -85
  114. package/src/math/Vector4.js +11 -0
  115. package/src/nodes/Nodes.js +1 -21
  116. package/src/nodes/TSL.js +6 -24
  117. package/src/nodes/accessors/Camera.js +0 -1
  118. package/src/nodes/accessors/SceneNode.js +35 -1
  119. package/src/nodes/accessors/StorageBufferNode.js +32 -10
  120. package/src/nodes/accessors/VelocityNode.js +13 -3
  121. package/src/nodes/code/FunctionCallNode.js +16 -5
  122. package/src/nodes/code/ScriptableNode.js +4 -4
  123. package/src/nodes/core/Node.js +1 -0
  124. package/src/nodes/core/NodeBuilder.js +29 -5
  125. package/src/nodes/core/VarNode.js +15 -3
  126. package/src/nodes/display/ColorAdjustment.js +53 -4
  127. package/src/nodes/display/ColorSpaceFunctions.js +5 -5
  128. package/src/nodes/display/ColorSpaceNode.js +27 -39
  129. package/src/nodes/display/PassNode.js +6 -4
  130. package/src/nodes/display/ToneMappingNode.js +1 -1
  131. package/src/nodes/display/ViewportDepthNode.js +33 -1
  132. package/src/nodes/functions/PhysicalLightingModel.js +21 -15
  133. package/src/nodes/functions/material/getAlphaHashThreshold.js +64 -0
  134. package/src/nodes/functions/material/getGeometryRoughness.js +8 -2
  135. package/src/nodes/functions/material/getParallaxCorrectNormal.js +22 -0
  136. package/src/nodes/gpgpu/ComputeNode.js +9 -1
  137. package/src/nodes/lighting/AnalyticLightNode.js +12 -416
  138. package/src/nodes/lighting/LightsNode.js +24 -12
  139. package/src/nodes/lighting/PointLightNode.js +36 -26
  140. package/src/nodes/lighting/RectAreaLightNode.js +3 -0
  141. package/src/nodes/lighting/ShadowNode.js +484 -0
  142. package/src/nodes/utils/LoopNode.js +2 -2
  143. package/src/nodes/utils/Oscillators.js +6 -0
  144. package/src/nodes/utils/PostProcessingUtils.js +89 -0
  145. package/src/nodes/utils/ReflectorNode.js +101 -15
  146. package/src/nodes/utils/Timer.js +29 -0
  147. package/src/objects/BatchedMesh.js +458 -241
  148. package/src/renderers/WebGLRenderer.js +120 -117
  149. package/src/renderers/common/Attributes.js +4 -0
  150. package/src/renderers/common/Backend.js +2 -0
  151. package/src/renderers/common/Background.js +14 -4
  152. package/src/renderers/common/Bindings.js +3 -2
  153. package/src/renderers/common/Constants.js +2 -1
  154. package/src/renderers/common/Geometries.js +20 -0
  155. package/src/renderers/common/IndirectStorageBufferAttribute.js +15 -0
  156. package/src/renderers/common/Lighting.js +45 -0
  157. package/src/renderers/common/PostProcessingUtils.js +86 -0
  158. package/src/renderers/common/RenderList.js +39 -11
  159. package/src/renderers/common/RenderLists.js +4 -2
  160. package/src/renderers/common/RenderObject.js +18 -1
  161. package/src/renderers/common/Renderer.js +165 -26
  162. package/src/renderers/common/Textures.js +30 -14
  163. package/src/renderers/common/nodes/NodeLibrary.js +1 -14
  164. package/src/renderers/common/nodes/Nodes.js +2 -2
  165. package/src/renderers/shaders/ShaderChunk/colorspace_pars_fragment.glsl.js +4 -24
  166. package/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl.js +8 -0
  167. package/src/renderers/shaders/ShaderChunk/map_fragment.glsl.js +2 -2
  168. package/src/renderers/webgl/WebGLBufferRenderer.js +2 -6
  169. package/src/renderers/webgl/WebGLCapabilities.js +0 -7
  170. package/src/renderers/webgl/WebGLIndexedBufferRenderer.js +2 -6
  171. package/src/renderers/webgl/WebGLProgram.js +24 -28
  172. package/src/renderers/webgl/WebGLPrograms.js +5 -2
  173. package/src/renderers/webgl/WebGLState.js +37 -1
  174. package/src/renderers/webgl/WebGLTextures.js +29 -12
  175. package/src/renderers/webgl-fallback/WebGLBackend.js +98 -31
  176. package/src/renderers/webgl-fallback/WebGLBufferRenderer.js +2 -7
  177. package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +33 -8
  178. package/src/renderers/webgpu/WebGPUBackend.js +105 -25
  179. package/src/renderers/webgpu/WebGPURenderer.Nodes.js +1 -1
  180. package/src/renderers/webgpu/WebGPURenderer.js +1 -1
  181. package/src/renderers/webgpu/nodes/BasicNodeLibrary.js +0 -8
  182. package/src/renderers/webgpu/nodes/StandardNodeLibrary.js +0 -8
  183. package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +10 -20
  184. package/src/renderers/webgpu/nodes/WGSLNodeFunction.js +12 -4
  185. package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +29 -9
  186. package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +24 -6
  187. package/src/renderers/webgpu/utils/WebGPUUtils.js +0 -1
  188. package/src/renderers/webxr/WebXRManager.js +4 -4
  189. package/examples/jsm/cameras/CinematicCamera.js +0 -208
  190. package/src/nodes/display/FXAANode.js +0 -332
  191. package/src/nodes/utils/OscNode.js +0 -85
  192. package/src/nodes/utils/TimerNode.js +0 -97
  193. /package/examples/jsm/utils/{TextureUtils.js → WebGLTextureUtils.js} +0 -0
@@ -12,6 +12,12 @@ import { Frustum } from '../math/Frustum.js';
12
12
  import { Vector3 } from '../math/Vector3.js';
13
13
  import { Color } from '../math/Color.js';
14
14
 
15
+ function ascIdSort( a, b ) {
16
+
17
+ return a - b;
18
+
19
+ }
20
+
15
21
  function sortOpaque( a, b ) {
16
22
 
17
23
  return a.z - b.z;
@@ -34,7 +40,7 @@ class MultiDrawRenderList {
34
40
 
35
41
  }
36
42
 
37
- push( drawRange, z, index ) {
43
+ push( start, count, z, index ) {
38
44
 
39
45
  const pool = this.pool;
40
46
  const list = this.list;
@@ -55,8 +61,8 @@ class MultiDrawRenderList {
55
61
  list.push( item );
56
62
  this.index ++;
57
63
 
58
- item.start = drawRange.start;
59
- item.count = drawRange.count;
64
+ item.start = start;
65
+ item.count = count;
60
66
  item.z = z;
61
67
  item.index = index;
62
68
 
@@ -72,10 +78,7 @@ class MultiDrawRenderList {
72
78
  }
73
79
 
74
80
  const _matrix = /*@__PURE__*/ new Matrix4();
75
- const _invMatrixWorld = /*@__PURE__*/ new Matrix4();
76
- const _identityMatrix = /*@__PURE__*/ new Matrix4();
77
81
  const _whiteColor = /*@__PURE__*/ new Color( 1, 1, 1 );
78
- const _projScreenMatrix = /*@__PURE__*/ new Matrix4();
79
82
  const _frustum = /*@__PURE__*/ new Frustum();
80
83
  const _box = /*@__PURE__*/ new Box3();
81
84
  const _sphere = /*@__PURE__*/ new Sphere();
@@ -86,13 +89,6 @@ const _renderList = /*@__PURE__*/ new MultiDrawRenderList();
86
89
  const _mesh = /*@__PURE__*/ new Mesh();
87
90
  const _batchIntersects = [];
88
91
 
89
- // @TODO: SkinnedMesh support?
90
- // @TODO: geometry.groups support?
91
- // @TODO: geometry.drawRange support?
92
- // @TODO: geometry.morphAttributes support?
93
- // @TODO: Support uniform parameter per geometry
94
- // @TODO: Add an "optimize" function to pack geometry and remove data gaps
95
-
96
92
  // copies data from attribute "src" into "target" starting at "targetOffset"
97
93
  function copyAttributeData( src, target, targetOffset = 0 ) {
98
94
 
@@ -123,6 +119,29 @@ function copyAttributeData( src, target, targetOffset = 0 ) {
123
119
 
124
120
  }
125
121
 
122
+ // safely copies array contents to a potentially smaller array
123
+ function copyArrayContents( src, target ) {
124
+
125
+ if ( src.constructor !== target.constructor ) {
126
+
127
+ // if arrays are of a different type (eg due to index size increasing) then data must be per-element copied
128
+ const len = Math.min( src.length, target.length );
129
+ for ( let i = 0; i < len; i ++ ) {
130
+
131
+ target[ i ] = src[ i ];
132
+
133
+ }
134
+
135
+ } else {
136
+
137
+ // if the arrays use the same data layout we can use a fast block copy
138
+ const len = Math.min( src.length, target.length );
139
+ target.set( new src.constructor( src.buffer, 0, len ) );
140
+
141
+ }
142
+
143
+ }
144
+
126
145
  class BatchedMesh extends Mesh {
127
146
 
128
147
  get maxInstanceCount() {
@@ -131,6 +150,24 @@ class BatchedMesh extends Mesh {
131
150
 
132
151
  }
133
152
 
153
+ get instanceCount() {
154
+
155
+ return this._instanceInfo.length - this._availableInstanceIds.length;
156
+
157
+ }
158
+
159
+ get unusedVertexCount() {
160
+
161
+ return this._maxVertexCount - this._nextVertexStart;
162
+
163
+ }
164
+
165
+ get unusedIndexCount() {
166
+
167
+ return this._maxIndexCount - this._nextIndexStart;
168
+
169
+ }
170
+
134
171
  constructor( maxInstanceCount, maxVertexCount, maxIndexCount = maxVertexCount * 2, material ) {
135
172
 
136
173
  super( new BufferGeometry(), material );
@@ -142,28 +179,33 @@ class BatchedMesh extends Mesh {
142
179
  this.boundingSphere = null;
143
180
  this.customSort = null;
144
181
 
145
- // stores visible, active, and geometry id per object
146
- this._drawInfo = [];
182
+ // stores visible, active, and geometry id per instance and reserved buffer ranges for geometries
183
+ this._instanceInfo = [];
184
+ this._geometryInfo = [];
147
185
 
148
- // instance ids that have been set as inactive, and are available to be overwritten
186
+ // instance, geometry ids that have been set as inactive, and are available to be overwritten
149
187
  this._availableInstanceIds = [];
188
+ this._availableGeometryIds = [];
150
189
 
151
- // geometry information
152
- this._drawRanges = [];
153
- this._reservedRanges = [];
154
- this._bounds = [];
190
+ // used to track where the next point is that geometry should be inserted
191
+ this._nextIndexStart = 0;
192
+ this._nextVertexStart = 0;
193
+ this._geometryCount = 0;
155
194
 
195
+ // flags
196
+ this._visibilityChanged = true;
197
+ this._geometryInitialized = false;
198
+
199
+ // cached user options
156
200
  this._maxInstanceCount = maxInstanceCount;
157
201
  this._maxVertexCount = maxVertexCount;
158
202
  this._maxIndexCount = maxIndexCount;
159
203
 
160
- this._geometryInitialized = false;
161
- this._geometryCount = 0;
204
+ // buffers for multi draw
162
205
  this._multiDrawCounts = new Int32Array( maxInstanceCount );
163
206
  this._multiDrawStarts = new Int32Array( maxInstanceCount );
164
207
  this._multiDrawCount = 0;
165
208
  this._multiDrawInstances = null;
166
- this._visibilityChanged = true;
167
209
 
168
210
  // Local matrix per geometry by using data texture
169
211
  this._matricesTexture = null;
@@ -304,14 +346,14 @@ class BatchedMesh extends Mesh {
304
346
  }
305
347
 
306
348
  const boundingBox = this.boundingBox;
307
- const drawInfo = this._drawInfo;
349
+ const instanceInfo = this._instanceInfo;
308
350
 
309
351
  boundingBox.makeEmpty();
310
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
352
+ for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
311
353
 
312
- if ( drawInfo[ i ].active === false ) continue;
354
+ if ( instanceInfo[ i ].active === false ) continue;
313
355
 
314
- const geometryId = drawInfo[ i ].geometryIndex;
356
+ const geometryId = instanceInfo[ i ].geometryIndex;
315
357
  this.getMatrixAt( i, _matrix );
316
358
  this.getBoundingBoxAt( geometryId, _box ).applyMatrix4( _matrix );
317
359
  boundingBox.union( _box );
@@ -329,14 +371,14 @@ class BatchedMesh extends Mesh {
329
371
  }
330
372
 
331
373
  const boundingSphere = this.boundingSphere;
332
- const drawInfo = this._drawInfo;
374
+ const instanceInfo = this._instanceInfo;
333
375
 
334
376
  boundingSphere.makeEmpty();
335
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
377
+ for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
336
378
 
337
- if ( drawInfo[ i ].active === false ) continue;
379
+ if ( instanceInfo[ i ].active === false ) continue;
338
380
 
339
- const geometryId = drawInfo[ i ].geometryIndex;
381
+ const geometryId = instanceInfo[ i ].geometryIndex;
340
382
  this.getMatrixAt( i, _matrix );
341
383
  this.getBoundingSphereAt( geometryId, _sphere ).applyMatrix4( _matrix );
342
384
  boundingSphere.union( _sphere );
@@ -347,7 +389,7 @@ class BatchedMesh extends Mesh {
347
389
 
348
390
  addInstance( geometryId ) {
349
391
 
350
- const atCapacity = this._drawInfo.length >= this.maxInstanceCount;
392
+ const atCapacity = this._instanceInfo.length >= this.maxInstanceCount;
351
393
 
352
394
  // ensure we're not over geometry
353
395
  if ( atCapacity && this._availableInstanceIds.length === 0 ) {
@@ -356,7 +398,7 @@ class BatchedMesh extends Mesh {
356
398
 
357
399
  }
358
400
 
359
- const instanceDrawInfo = {
401
+ const instanceInfo = {
360
402
  visible: true,
361
403
  active: true,
362
404
  geometryIndex: geometryId,
@@ -367,19 +409,20 @@ class BatchedMesh extends Mesh {
367
409
  // Prioritize using previously freed instance ids
368
410
  if ( this._availableInstanceIds.length > 0 ) {
369
411
 
370
- drawId = this._availableInstanceIds.pop();
371
- this._drawInfo[ drawId ] = instanceDrawInfo;
412
+ this._availableInstanceIds.sort( ascIdSort );
413
+
414
+ drawId = this._availableInstanceIds.shift();
415
+ this._instanceInfo[ drawId ] = instanceInfo;
372
416
 
373
417
  } else {
374
418
 
375
- drawId = this._drawInfo.length;
376
- this._drawInfo.push( instanceDrawInfo );
419
+ drawId = this._instanceInfo.length;
420
+ this._instanceInfo.push( instanceInfo );
377
421
 
378
422
  }
379
423
 
380
424
  const matricesTexture = this._matricesTexture;
381
- const matricesArray = matricesTexture.image.data;
382
- _identityMatrix.toArray( matricesArray, drawId * 16 );
425
+ _matrix.identity().toArray( matricesTexture.image.data, drawId * 16 );
383
426
  matricesTexture.needsUpdate = true;
384
427
 
385
428
  const colorsTexture = this._colorsTexture;
@@ -390,118 +433,85 @@ class BatchedMesh extends Mesh {
390
433
 
391
434
  }
392
435
 
436
+ this._visibilityChanged = true;
393
437
  return drawId;
394
438
 
395
439
  }
396
440
 
397
- addGeometry( geometry, vertexCount = - 1, indexCount = - 1 ) {
441
+ addGeometry( geometry, reservedVertexCount = - 1, reservedIndexCount = - 1 ) {
398
442
 
399
443
  this._initializeGeometry( geometry );
400
444
 
401
445
  this._validateGeometry( geometry );
402
446
 
403
- // ensure we're not over geometry
404
- if ( this._drawInfo.length >= this._maxInstanceCount ) {
405
-
406
- throw new Error( 'BatchedMesh: Maximum item count reached.' );
407
-
408
- }
409
-
410
- // get the necessary range fo the geometry
411
- const reservedRange = {
447
+ const geometryInfo = {
448
+ // geometry information
412
449
  vertexStart: - 1,
413
450
  vertexCount: - 1,
451
+ reservedVertexCount: - 1,
452
+
414
453
  indexStart: - 1,
415
454
  indexCount: - 1,
416
- };
417
-
418
- let lastRange = null;
419
- const reservedRanges = this._reservedRanges;
420
- const drawRanges = this._drawRanges;
421
- const bounds = this._bounds;
422
- if ( this._geometryCount !== 0 ) {
423
-
424
- lastRange = reservedRanges[ reservedRanges.length - 1 ];
425
-
426
- }
427
-
428
- if ( vertexCount === - 1 ) {
429
-
430
- reservedRange.vertexCount = geometry.getAttribute( 'position' ).count;
431
-
432
- } else {
455
+ reservedIndexCount: - 1,
433
456
 
434
- reservedRange.vertexCount = vertexCount;
457
+ // draw range information
458
+ start: - 1,
459
+ count: - 1,
435
460
 
436
- }
437
-
438
- if ( lastRange === null ) {
439
-
440
- reservedRange.vertexStart = 0;
441
-
442
- } else {
443
-
444
- reservedRange.vertexStart = lastRange.vertexStart + lastRange.vertexCount;
461
+ // state
462
+ boundingBox: null,
463
+ boundingSphere: null,
464
+ active: true,
465
+ };
445
466
 
446
- }
467
+ const geometryInfoList = this._geometryInfo;
468
+ geometryInfo.vertexStart = this._nextVertexStart;
469
+ geometryInfo.reservedVertexCount = reservedVertexCount === - 1 ? geometry.getAttribute( 'position' ).count : reservedVertexCount;
447
470
 
448
471
  const index = geometry.getIndex();
449
472
  const hasIndex = index !== null;
450
473
  if ( hasIndex ) {
451
474
 
452
- if ( indexCount === - 1 ) {
475
+ geometryInfo.indexStart = this._nextIndexStart;
476
+ geometryInfo.reservedIndexCount = reservedIndexCount === - 1 ? index.count : reservedIndexCount;
453
477
 
454
- reservedRange.indexCount = index.count;
455
-
456
- } else {
457
-
458
- reservedRange.indexCount = indexCount;
478
+ }
459
479
 
460
- }
480
+ if (
481
+ geometryInfo.indexStart !== - 1 &&
482
+ geometryInfo.indexStart + geometryInfo.reservedIndexCount > this._maxIndexCount ||
483
+ geometryInfo.vertexStart + geometryInfo.reservedVertexCount > this._maxVertexCount
484
+ ) {
461
485
 
462
- if ( lastRange === null ) {
486
+ throw new Error( 'BatchedMesh: Reserved space request exceeds the maximum buffer size.' );
463
487
 
464
- reservedRange.indexStart = 0;
488
+ }
465
489
 
466
- } else {
490
+ // update id
491
+ let geometryId;
492
+ if ( this._availableGeometryIds.length > 0 ) {
467
493
 
468
- reservedRange.indexStart = lastRange.indexStart + lastRange.indexCount;
494
+ this._availableGeometryIds.sort( ascIdSort );
469
495
 
470
- }
496
+ geometryId = this._availableGeometryIds.shift();
497
+ geometryInfoList[ geometryId ] = geometryInfo;
471
498
 
472
- }
473
499
 
474
- if (
475
- reservedRange.indexStart !== - 1 &&
476
- reservedRange.indexStart + reservedRange.indexCount > this._maxIndexCount ||
477
- reservedRange.vertexStart + reservedRange.vertexCount > this._maxVertexCount
478
- ) {
500
+ } else {
479
501
 
480
- throw new Error( 'BatchedMesh: Reserved space request exceeds the maximum buffer size.' );
502
+ geometryId = this._geometryCount;
503
+ this._geometryCount ++;
504
+ geometryInfoList.push( geometryInfo );
481
505
 
482
506
  }
483
507
 
484
- // update id
485
- const geometryId = this._geometryCount;
486
- this._geometryCount ++;
487
-
488
- // add the reserved range and draw range objects
489
- reservedRanges.push( reservedRange );
490
- drawRanges.push( {
491
- start: hasIndex ? reservedRange.indexStart : reservedRange.vertexStart,
492
- count: - 1
493
- } );
494
- bounds.push( {
495
- boxInitialized: false,
496
- box: new Box3(),
497
-
498
- sphereInitialized: false,
499
- sphere: new Sphere()
500
- } );
501
-
502
508
  // update the geometry
503
509
  this.setGeometryAt( geometryId, geometry );
504
510
 
511
+ // increment the next geometry position
512
+ this._nextIndexStart = geometryInfo.indexStart + geometryInfo.reservedIndexCount;
513
+ this._nextVertexStart = geometryInfo.vertexStart + geometryInfo.reservedVertexCount;
514
+
505
515
  return geometryId;
506
516
 
507
517
  }
@@ -520,20 +530,22 @@ class BatchedMesh extends Mesh {
520
530
  const hasIndex = batchGeometry.getIndex() !== null;
521
531
  const dstIndex = batchGeometry.getIndex();
522
532
  const srcIndex = geometry.getIndex();
523
- const reservedRange = this._reservedRanges[ geometryId ];
533
+ const geometryInfo = this._geometryInfo[ geometryId ];
524
534
  if (
525
535
  hasIndex &&
526
- srcIndex.count > reservedRange.indexCount ||
527
- geometry.attributes.position.count > reservedRange.vertexCount
536
+ srcIndex.count > geometryInfo.reservedIndexCount ||
537
+ geometry.attributes.position.count > geometryInfo.reservedVertexCount
528
538
  ) {
529
539
 
530
540
  throw new Error( 'BatchedMesh: Reserved space not large enough for provided geometry.' );
531
541
 
532
542
  }
533
543
 
534
- // copy geometry over
535
- const vertexStart = reservedRange.vertexStart;
536
- const vertexCount = reservedRange.vertexCount;
544
+ // copy geometry buffer data over
545
+ const vertexStart = geometryInfo.vertexStart;
546
+ const reservedVertexCount = geometryInfo.reservedVertexCount;
547
+ geometryInfo.vertexCount = geometry.getAttribute( 'position' ).count;
548
+
537
549
  for ( const attributeName in batchGeometry.attributes ) {
538
550
 
539
551
  // copy attribute data
@@ -543,7 +555,7 @@ class BatchedMesh extends Mesh {
543
555
 
544
556
  // fill the rest in with zeroes
545
557
  const itemSize = srcAttribute.itemSize;
546
- for ( let i = srcAttribute.count, l = vertexCount; i < l; i ++ ) {
558
+ for ( let i = srcAttribute.count, l = reservedVertexCount; i < l; i ++ ) {
547
559
 
548
560
  const index = vertexStart + i;
549
561
  for ( let c = 0; c < itemSize; c ++ ) {
@@ -555,14 +567,16 @@ class BatchedMesh extends Mesh {
555
567
  }
556
568
 
557
569
  dstAttribute.needsUpdate = true;
558
- dstAttribute.addUpdateRange( vertexStart * itemSize, vertexCount * itemSize );
570
+ dstAttribute.addUpdateRange( vertexStart * itemSize, reservedVertexCount * itemSize );
559
571
 
560
572
  }
561
573
 
562
574
  // copy index
563
575
  if ( hasIndex ) {
564
576
 
565
- const indexStart = reservedRange.indexStart;
577
+ const indexStart = geometryInfo.indexStart;
578
+ const reservedIndexCount = geometryInfo.reservedIndexCount;
579
+ geometryInfo.indexCount = geometry.getIndex().count;
566
580
 
567
581
  // copy index data over
568
582
  for ( let i = 0; i < srcIndex.count; i ++ ) {
@@ -572,69 +586,80 @@ class BatchedMesh extends Mesh {
572
586
  }
573
587
 
574
588
  // fill the rest in with zeroes
575
- for ( let i = srcIndex.count, l = reservedRange.indexCount; i < l; i ++ ) {
589
+ for ( let i = srcIndex.count, l = reservedIndexCount; i < l; i ++ ) {
576
590
 
577
591
  dstIndex.setX( indexStart + i, vertexStart );
578
592
 
579
593
  }
580
594
 
581
595
  dstIndex.needsUpdate = true;
582
- dstIndex.addUpdateRange( indexStart, reservedRange.indexCount );
596
+ dstIndex.addUpdateRange( indexStart, geometryInfo.reservedIndexCount );
583
597
 
584
598
  }
585
599
 
600
+ // update the draw range
601
+ geometryInfo.start = hasIndex ? geometryInfo.indexStart : geometryInfo.vertexStart;
602
+ geometryInfo.count = hasIndex ? geometryInfo.indexCount : geometryInfo.vertexCount;
603
+
586
604
  // store the bounding boxes
587
- const bound = this._bounds[ geometryId ];
605
+ geometryInfo.boundingBox = null;
588
606
  if ( geometry.boundingBox !== null ) {
589
607
 
590
- bound.box.copy( geometry.boundingBox );
591
- bound.boxInitialized = true;
592
-
593
- } else {
594
-
595
- bound.boxInitialized = false;
608
+ geometryInfo.boundingBox = geometry.boundingBox.clone();
596
609
 
597
610
  }
598
611
 
612
+ geometryInfo.boundingSphere = null;
599
613
  if ( geometry.boundingSphere !== null ) {
600
614
 
601
- bound.sphere.copy( geometry.boundingSphere );
602
- bound.sphereInitialized = true;
603
-
604
- } else {
605
-
606
- bound.sphereInitialized = false;
615
+ geometryInfo.boundingSphere = geometry.boundingSphere.clone();
607
616
 
608
617
  }
609
618
 
610
- // set drawRange count
611
- const drawRange = this._drawRanges[ geometryId ];
612
- const posAttr = geometry.getAttribute( 'position' );
613
- drawRange.count = hasIndex ? srcIndex.count : posAttr.count;
614
619
  this._visibilityChanged = true;
615
-
616
620
  return geometryId;
617
621
 
618
622
  }
619
623
 
620
- /*
621
624
  deleteGeometry( geometryId ) {
622
625
 
623
- // TODO: delete geometry and associated instances
626
+ const geometryInfoList = this._geometryInfo;
627
+ if ( geometryId >= geometryInfoList.length || geometryInfoList[ geometryId ].active === false ) {
628
+
629
+ return this;
630
+
631
+ }
632
+
633
+ // delete any instances associated with this geometry
634
+ const instanceInfo = this._instanceInfo;
635
+ for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
636
+
637
+ if ( instanceInfo[ i ].geometryIndex === geometryId ) {
638
+
639
+ this.deleteInstance( i );
640
+
641
+ }
642
+
643
+ }
644
+
645
+ geometryInfoList[ geometryId ].active = false;
646
+ this._availableGeometryIds.push( geometryId );
647
+ this._visibilityChanged = true;
648
+
649
+ return this;
624
650
 
625
651
  }
626
- */
627
652
 
628
653
  deleteInstance( instanceId ) {
629
654
 
630
- const drawInfo = this._drawInfo;
631
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
655
+ const instanceInfo = this._instanceInfo;
656
+ if ( instanceId >= instanceInfo.length || instanceInfo[ instanceId ].active === false ) {
632
657
 
633
658
  return this;
634
659
 
635
660
  }
636
661
 
637
- drawInfo[ instanceId ].active = false;
662
+ instanceInfo[ instanceId ].active = false;
638
663
  this._availableInstanceIds.push( instanceId );
639
664
  this._visibilityChanged = true;
640
665
 
@@ -642,6 +667,95 @@ class BatchedMesh extends Mesh {
642
667
 
643
668
  }
644
669
 
670
+ optimize() {
671
+
672
+ // track the next indices to copy data to
673
+ let nextVertexStart = 0;
674
+ let nextIndexStart = 0;
675
+
676
+ // Iterate over all geometry ranges in order sorted from earliest in the geometry buffer to latest
677
+ // in the geometry buffer. Because draw range objects can be reused there is no guarantee of their order.
678
+ const geometryInfoList = this._geometryInfo;
679
+ const indices = geometryInfoList
680
+ .map( ( e, i ) => i )
681
+ .sort( ( a, b ) => {
682
+
683
+ return geometryInfoList[ a ].vertexStart - geometryInfoList[ b ].vertexStart;
684
+
685
+ } );
686
+
687
+ const geometry = this.geometry;
688
+ for ( let i = 0, l = geometryInfoList.length; i < l; i ++ ) {
689
+
690
+ // if a geometry range is inactive then don't copy anything
691
+ const index = indices[ i ];
692
+ const geometryInfo = geometryInfoList[ index ];
693
+ if ( geometryInfo.active === false ) {
694
+
695
+ continue;
696
+
697
+ }
698
+
699
+ // if a geometry contains an index buffer then shift it, as well
700
+ if ( geometry.index !== null ) {
701
+
702
+ if ( geometryInfo.indexStart !== nextIndexStart ) {
703
+
704
+ const { indexStart, vertexStart, reservedIndexCount } = geometryInfo;
705
+ const index = geometry.index;
706
+ const array = index.array;
707
+
708
+ // shift the index pointers based on how the vertex data will shift
709
+ // adjusting the index must happen first so the original vertex start value is available
710
+ const elementDelta = nextVertexStart - vertexStart;
711
+ for ( let j = indexStart; j < indexStart + reservedIndexCount; j ++ ) {
712
+
713
+ array[ j ] = array[ j ] + elementDelta;
714
+
715
+ }
716
+
717
+ index.array.copyWithin( nextIndexStart, indexStart, indexStart + reservedIndexCount );
718
+ index.addUpdateRange( nextIndexStart, reservedIndexCount );
719
+
720
+ geometryInfo.indexStart = nextIndexStart;
721
+
722
+ }
723
+
724
+ nextIndexStart += geometryInfo.reservedIndexCount;
725
+
726
+ }
727
+
728
+ // if a geometry needs to be moved then copy attribute data to overwrite unused space
729
+ if ( geometryInfo.vertexStart !== nextVertexStart ) {
730
+
731
+ const { vertexStart, reservedVertexCount } = geometryInfo;
732
+ const attributes = geometry.attributes;
733
+ for ( const key in attributes ) {
734
+
735
+ const attribute = attributes[ key ];
736
+ const { array, itemSize } = attribute;
737
+ array.copyWithin( nextVertexStart * itemSize, vertexStart * itemSize, ( vertexStart + reservedVertexCount ) * itemSize );
738
+ attribute.addUpdateRange( nextVertexStart * itemSize, reservedVertexCount * itemSize );
739
+
740
+ }
741
+
742
+ geometryInfo.vertexStart = nextVertexStart;
743
+
744
+ }
745
+
746
+ nextVertexStart += geometryInfo.reservedVertexCount;
747
+ geometryInfo.start = geometry.index ? geometryInfo.indexStart : geometryInfo.vertexStart;
748
+
749
+ // step the next geometry points to the shifted position
750
+ this._nextIndexStart = geometry.index ? geometryInfo.indexStart + geometryInfo.reservedIndexCount : 0;
751
+ this._nextVertexStart = geometryInfo.vertexStart + geometryInfo.reservedVertexCount;
752
+
753
+ }
754
+
755
+ return this;
756
+
757
+ }
758
+
645
759
  // get bounding box and compute it if it doesn't exist
646
760
  getBoundingBoxAt( geometryId, target ) {
647
761
 
@@ -652,17 +766,14 @@ class BatchedMesh extends Mesh {
652
766
  }
653
767
 
654
768
  // compute bounding box
655
- const bound = this._bounds[ geometryId ];
656
- const box = bound.box;
657
769
  const geometry = this.geometry;
658
- if ( bound.boxInitialized === false ) {
659
-
660
- box.makeEmpty();
770
+ const geometryInfo = this._geometryInfo[ geometryId ];
771
+ if ( geometryInfo.boundingBox === null ) {
661
772
 
773
+ const box = new Box3();
662
774
  const index = geometry.index;
663
775
  const position = geometry.attributes.position;
664
- const drawRange = this._drawRanges[ geometryId ];
665
- for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i ++ ) {
776
+ for ( let i = geometryInfo.start, l = geometryInfo.start + geometryInfo.count; i < l; i ++ ) {
666
777
 
667
778
  let iv = i;
668
779
  if ( index ) {
@@ -675,11 +786,11 @@ class BatchedMesh extends Mesh {
675
786
 
676
787
  }
677
788
 
678
- bound.boxInitialized = true;
789
+ geometryInfo.boundingBox = box;
679
790
 
680
791
  }
681
792
 
682
- target.copy( box );
793
+ target.copy( geometryInfo.boundingBox );
683
794
  return target;
684
795
 
685
796
  }
@@ -694,22 +805,19 @@ class BatchedMesh extends Mesh {
694
805
  }
695
806
 
696
807
  // compute bounding sphere
697
- const bound = this._bounds[ geometryId ];
698
- const sphere = bound.sphere;
699
808
  const geometry = this.geometry;
700
- if ( bound.sphereInitialized === false ) {
701
-
702
- sphere.makeEmpty();
809
+ const geometryInfo = this._geometryInfo[ geometryId ];
810
+ if ( geometryInfo.boundingSphere === null ) {
703
811
 
812
+ const sphere = new Sphere();
704
813
  this.getBoundingBoxAt( geometryId, _box );
705
814
  _box.getCenter( sphere.center );
706
815
 
707
816
  const index = geometry.index;
708
817
  const position = geometry.attributes.position;
709
- const drawRange = this._drawRanges[ geometryId ];
710
818
 
711
819
  let maxRadiusSq = 0;
712
- for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i ++ ) {
820
+ for ( let i = geometryInfo.start, l = geometryInfo.start + geometryInfo.count; i < l; i ++ ) {
713
821
 
714
822
  let iv = i;
715
823
  if ( index ) {
@@ -724,24 +832,21 @@ class BatchedMesh extends Mesh {
724
832
  }
725
833
 
726
834
  sphere.radius = Math.sqrt( maxRadiusSq );
727
- bound.sphereInitialized = true;
835
+ geometryInfo.boundingSphere = sphere;
728
836
 
729
837
  }
730
838
 
731
- target.copy( sphere );
839
+ target.copy( geometryInfo.boundingSphere );
732
840
  return target;
733
841
 
734
842
  }
735
843
 
736
844
  setMatrixAt( instanceId, matrix ) {
737
845
 
738
- // @TODO: Map geometryId to index of the arrays because
739
- // optimize() can make geometryId mismatch the index
740
-
741
- const drawInfo = this._drawInfo;
846
+ const instanceInfo = this._instanceInfo;
742
847
  const matricesTexture = this._matricesTexture;
743
848
  const matricesArray = this._matricesTexture.image.data;
744
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
849
+ if ( instanceId >= instanceInfo.length || instanceInfo[ instanceId ].active === false ) {
745
850
 
746
851
  return this;
747
852
 
@@ -756,9 +861,9 @@ class BatchedMesh extends Mesh {
756
861
 
757
862
  getMatrixAt( instanceId, matrix ) {
758
863
 
759
- const drawInfo = this._drawInfo;
864
+ const instanceInfo = this._instanceInfo;
760
865
  const matricesArray = this._matricesTexture.image.data;
761
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
866
+ if ( instanceId >= instanceInfo.length || instanceInfo[ instanceId ].active === false ) {
762
867
 
763
868
  return null;
764
869
 
@@ -776,13 +881,10 @@ class BatchedMesh extends Mesh {
776
881
 
777
882
  }
778
883
 
779
- // @TODO: Map id to index of the arrays because
780
- // optimize() can make id mismatch the index
781
-
782
884
  const colorsTexture = this._colorsTexture;
783
885
  const colorsArray = this._colorsTexture.image.data;
784
- const drawInfo = this._drawInfo;
785
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
886
+ const instanceInfo = this._instanceInfo;
887
+ if ( instanceId >= instanceInfo.length || instanceInfo[ instanceId ].active === false ) {
786
888
 
787
889
  return this;
788
890
 
@@ -798,8 +900,8 @@ class BatchedMesh extends Mesh {
798
900
  getColorAt( instanceId, color ) {
799
901
 
800
902
  const colorsArray = this._colorsTexture.image.data;
801
- const drawInfo = this._drawInfo;
802
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
903
+ const instanceInfo = this._instanceInfo;
904
+ if ( instanceId >= instanceInfo.length || instanceInfo[ instanceId ].active === false ) {
803
905
 
804
906
  return null;
805
907
 
@@ -813,18 +915,18 @@ class BatchedMesh extends Mesh {
813
915
 
814
916
  // if the geometry is out of range, not active, or visibility state
815
917
  // does not change then return early
816
- const drawInfo = this._drawInfo;
918
+ const instanceInfo = this._instanceInfo;
817
919
  if (
818
- instanceId >= drawInfo.length ||
819
- drawInfo[ instanceId ].active === false ||
820
- drawInfo[ instanceId ].visible === value
920
+ instanceId >= instanceInfo.length ||
921
+ instanceInfo[ instanceId ].active === false ||
922
+ instanceInfo[ instanceId ].visible === value
821
923
  ) {
822
924
 
823
925
  return this;
824
926
 
825
927
  }
826
928
 
827
- drawInfo[ instanceId ].visible = value;
929
+ instanceInfo[ instanceId ].visible = value;
828
930
  this._visibilityChanged = true;
829
931
 
830
932
  return this;
@@ -834,35 +936,36 @@ class BatchedMesh extends Mesh {
834
936
  getVisibleAt( instanceId ) {
835
937
 
836
938
  // return early if the geometry is out of range or not active
837
- const drawInfo = this._drawInfo;
838
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
939
+ const instanceInfo = this._instanceInfo;
940
+ if ( instanceId >= instanceInfo.length || instanceInfo[ instanceId ].active === false ) {
839
941
 
840
942
  return false;
841
943
 
842
944
  }
843
945
 
844
- return drawInfo[ instanceId ].visible;
946
+ return instanceInfo[ instanceId ].visible;
845
947
 
846
948
  }
847
949
 
848
950
  setGeometryIdAt( instanceId, geometryId ) {
849
951
 
850
952
  // return early if the geometry is out of range or not active
851
- const drawInfo = this._drawInfo;
852
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
953
+ const instanceInfo = this._instanceInfo;
954
+ const geometryInfoList = this._geometryInfo;
955
+ if ( instanceId >= instanceInfo.length || instanceInfo[ instanceId ].active === false ) {
853
956
 
854
957
  return null;
855
958
 
856
959
  }
857
960
 
858
961
  // check if the provided geometryId is within the valid range
859
- if ( geometryId < 0 || geometryId >= this._geometryCount ) {
962
+ if ( geometryId >= geometryInfoList.length || geometryInfoList[ geometryId ].active === false ) {
860
963
 
861
964
  return null;
862
965
 
863
966
  }
864
967
 
865
- drawInfo[ instanceId ].geometryIndex = geometryId;
968
+ instanceInfo[ instanceId ].geometryIndex = geometryId;
866
969
 
867
970
  return this;
868
971
 
@@ -870,14 +973,14 @@ class BatchedMesh extends Mesh {
870
973
 
871
974
  getGeometryIdAt( instanceId ) {
872
975
 
873
- const drawInfo = this._drawInfo;
874
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
976
+ const instanceInfo = this._instanceInfo;
977
+ if ( instanceId >= instanceInfo.length || instanceInfo[ instanceId ].active === false ) {
875
978
 
876
979
  return - 1;
877
980
 
878
981
  }
879
982
 
880
- return drawInfo[ instanceId ].geometryIndex;
983
+ return instanceInfo[ instanceId ].geometryIndex;
881
984
 
882
985
  }
883
986
 
@@ -889,19 +992,136 @@ class BatchedMesh extends Mesh {
889
992
 
890
993
  }
891
994
 
892
- const drawRange = this._drawRanges[ geometryId ];
995
+ const geometryInfo = this._geometryInfo[ geometryId ];
996
+ target.vertexStart = geometryInfo.vertexStart;
997
+ target.vertexCount = geometryInfo.vertexCount;
998
+ target.reservedVertexCount = geometryInfo.reservedVertexCount;
999
+
1000
+ target.indexStart = geometryInfo.indexStart;
1001
+ target.indexCount = geometryInfo.indexCount;
1002
+ target.reservedIndexCount = geometryInfo.reservedIndexCount;
893
1003
 
894
- target.start = drawRange.start;
895
- target.count = drawRange.count;
1004
+ target.start = geometryInfo.start;
1005
+ target.count = geometryInfo.count;
896
1006
 
897
1007
  return target;
898
1008
 
899
1009
  }
900
1010
 
1011
+ setInstanceCount( maxInstanceCount ) {
1012
+
1013
+ // shrink the available instances as much as possible
1014
+ const availableInstanceIds = this._availableInstanceIds;
1015
+ const instanceInfo = this._instanceInfo;
1016
+ availableInstanceIds.sort( ascIdSort );
1017
+ while ( availableInstanceIds[ availableInstanceIds.length - 1 ] === instanceInfo.length ) {
1018
+
1019
+ instanceInfo.pop();
1020
+ availableInstanceIds.pop();
1021
+
1022
+ }
1023
+
1024
+ // throw an error if it can't be shrunk to the desired size
1025
+ if ( maxInstanceCount < instanceInfo.length ) {
1026
+
1027
+ throw new Error( `BatchedMesh: Instance ids outside the range ${ maxInstanceCount } are being used. Cannot shrink instance count.` );
1028
+
1029
+ }
1030
+
1031
+ // copy the multi draw counts
1032
+ const multiDrawCounts = new Int32Array( maxInstanceCount );
1033
+ const multiDrawStarts = new Int32Array( maxInstanceCount );
1034
+ copyArrayContents( this._multiDrawCounts, multiDrawCounts );
1035
+ copyArrayContents( this._multiDrawStarts, multiDrawStarts );
1036
+
1037
+ this._multiDrawCounts = multiDrawCounts;
1038
+ this._multiDrawStarts = multiDrawStarts;
1039
+ this._maxInstanceCount = maxInstanceCount;
1040
+
1041
+ // update texture data for instance sampling
1042
+ const indirectTexture = this._indirectTexture;
1043
+ const matricesTexture = this._matricesTexture;
1044
+ const colorsTexture = this._colorsTexture;
1045
+
1046
+ indirectTexture.dispose();
1047
+ this._initIndirectTexture();
1048
+ copyArrayContents( indirectTexture.image.data, this._indirectTexture.image.data );
1049
+
1050
+ matricesTexture.dispose();
1051
+ this._initMatricesTexture();
1052
+ copyArrayContents( matricesTexture.image.data, this._matricesTexture.image.data );
1053
+
1054
+ if ( colorsTexture ) {
1055
+
1056
+ colorsTexture.dispose();
1057
+ this._initColorsTexture();
1058
+ copyArrayContents( colorsTexture.image.data, this._colorsTexture.image.data );
1059
+
1060
+ }
1061
+
1062
+ }
1063
+
1064
+ setGeometrySize( maxVertexCount, maxIndexCount ) {
1065
+
1066
+ // Check if we can shrink to the requested vertex attribute size
1067
+ const validRanges = [ ...this._geometryInfo ].filter( info => info.active );
1068
+ const requiredVertexLength = Math.max( ...validRanges.map( range => range.vertexStart + range.reservedVertexCount ) );
1069
+ if ( requiredVertexLength > maxVertexCount ) {
1070
+
1071
+ throw new Error( `BatchedMesh: Geometry vertex values are being used outside the range ${ maxIndexCount }. Cannot shrink further.` );
1072
+
1073
+ }
1074
+
1075
+ // Check if we can shrink to the requested index attribute size
1076
+ if ( this.geometry.index ) {
1077
+
1078
+ const requiredIndexLength = Math.max( ...validRanges.map( range => range.indexStart + range.reservedIndexCount ) );
1079
+ if ( requiredIndexLength > maxIndexCount ) {
1080
+
1081
+ throw new Error( `BatchedMesh: Geometry index values are being used outside the range ${ maxIndexCount }. Cannot shrink further.` );
1082
+
1083
+ }
1084
+
1085
+ }
1086
+
1087
+ //
1088
+
1089
+ // dispose of the previous geometry
1090
+ const oldGeometry = this.geometry;
1091
+ oldGeometry.dispose();
1092
+
1093
+ // recreate the geometry needed based on the previous variant
1094
+ this._maxVertexCount = maxVertexCount;
1095
+ this._maxIndexCount = maxIndexCount;
1096
+
1097
+ if ( this._geometryInitialized ) {
1098
+
1099
+ this._geometryInitialized = false;
1100
+ this.geometry = new BufferGeometry();
1101
+ this._initializeGeometry( oldGeometry );
1102
+
1103
+ }
1104
+
1105
+ // copy data from the previous geometry
1106
+ const geometry = this.geometry;
1107
+ if ( oldGeometry.index ) {
1108
+
1109
+ copyArrayContents( oldGeometry.index.array, geometry.index.array );
1110
+
1111
+ }
1112
+
1113
+ for ( const key in oldGeometry.attributes ) {
1114
+
1115
+ copyArrayContents( oldGeometry.attributes[ key ].array, geometry.attributes[ key ].array );
1116
+
1117
+ }
1118
+
1119
+ }
1120
+
901
1121
  raycast( raycaster, intersects ) {
902
1122
 
903
- const drawInfo = this._drawInfo;
904
- const drawRanges = this._drawRanges;
1123
+ const instanceInfo = this._instanceInfo;
1124
+ const geometryInfoList = this._geometryInfo;
905
1125
  const matrixWorld = this.matrixWorld;
906
1126
  const batchGeometry = this.geometry;
907
1127
 
@@ -921,19 +1141,19 @@ class BatchedMesh extends Mesh {
921
1141
 
922
1142
  }
923
1143
 
924
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
1144
+ for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
925
1145
 
926
- if ( ! drawInfo[ i ].visible || ! drawInfo[ i ].active ) {
1146
+ if ( ! instanceInfo[ i ].visible || ! instanceInfo[ i ].active ) {
927
1147
 
928
1148
  continue;
929
1149
 
930
1150
  }
931
1151
 
932
- const geometryId = drawInfo[ i ].geometryIndex;
933
- const drawRange = drawRanges[ geometryId ];
934
- _mesh.geometry.setDrawRange( drawRange.start, drawRange.count );
1152
+ const geometryId = instanceInfo[ i ].geometryIndex;
1153
+ const geometryInfo = geometryInfoList[ geometryId ];
1154
+ _mesh.geometry.setDrawRange( geometryInfo.start, geometryInfo.count );
935
1155
 
936
- // ge the intersects
1156
+ // get the intersects
937
1157
  this.getMatrixAt( i, _mesh.matrixWorld ).premultiply( matrixWorld );
938
1158
  this.getBoundingBoxAt( geometryId, _mesh.geometry.boundingBox );
939
1159
  this.getBoundingSphereAt( geometryId, _mesh.geometry.boundingSphere );
@@ -970,17 +1190,13 @@ class BatchedMesh extends Mesh {
970
1190
  this.boundingBox = source.boundingBox !== null ? source.boundingBox.clone() : null;
971
1191
  this.boundingSphere = source.boundingSphere !== null ? source.boundingSphere.clone() : null;
972
1192
 
973
- this._drawRanges = source._drawRanges.map( range => ( { ...range } ) );
974
- this._reservedRanges = source._reservedRanges.map( range => ( { ...range } ) );
975
-
976
- this._drawInfo = source._drawInfo.map( inf => ( { ...inf } ) );
977
- this._bounds = source._bounds.map( bound => ( {
978
- boxInitialized: bound.boxInitialized,
979
- box: bound.box.clone(),
1193
+ this._geometryInfo = source._geometryInfo.map( info => ( {
1194
+ ...info,
980
1195
 
981
- sphereInitialized: bound.sphereInitialized,
982
- sphere: bound.sphere.clone()
1196
+ boundingBox: info.boundingBox !== null ? info.boundingBox.clone() : null,
1197
+ boundingSphere: info.boundingSphere !== null ? info.boundingSphere.clone() : null,
983
1198
  } ) );
1199
+ this._instanceInfo = source._instanceInfo.map( info => ( { ...info } ) );
984
1200
 
985
1201
  this._maxInstanceCount = source._maxInstanceCount;
986
1202
  this._maxVertexCount = source._maxVertexCount;
@@ -1042,10 +1258,10 @@ class BatchedMesh extends Mesh {
1042
1258
  const index = geometry.getIndex();
1043
1259
  const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT;
1044
1260
 
1045
- const drawInfo = this._drawInfo;
1261
+ const instanceInfo = this._instanceInfo;
1046
1262
  const multiDrawStarts = this._multiDrawStarts;
1047
1263
  const multiDrawCounts = this._multiDrawCounts;
1048
- const drawRanges = this._drawRanges;
1264
+ const geometryInfoList = this._geometryInfo;
1049
1265
  const perObjectFrustumCulled = this.perObjectFrustumCulled;
1050
1266
  const indirectTexture = this._indirectTexture;
1051
1267
  const indirectArray = indirectTexture.image.data;
@@ -1053,29 +1269,29 @@ class BatchedMesh extends Mesh {
1053
1269
  // prepare the frustum in the local frame
1054
1270
  if ( perObjectFrustumCulled ) {
1055
1271
 
1056
- _projScreenMatrix
1272
+ _matrix
1057
1273
  .multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse )
1058
1274
  .multiply( this.matrixWorld );
1059
1275
  _frustum.setFromProjectionMatrix(
1060
- _projScreenMatrix,
1276
+ _matrix,
1061
1277
  renderer.coordinateSystem
1062
1278
  );
1063
1279
 
1064
1280
  }
1065
1281
 
1066
- let count = 0;
1282
+ let multiDrawCount = 0;
1067
1283
  if ( this.sortObjects ) {
1068
1284
 
1069
1285
  // get the camera position in the local frame
1070
- _invMatrixWorld.copy( this.matrixWorld ).invert();
1071
- _vector.setFromMatrixPosition( camera.matrixWorld ).applyMatrix4( _invMatrixWorld );
1072
- _forward.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ).transformDirection( _invMatrixWorld );
1286
+ _matrix.copy( this.matrixWorld ).invert();
1287
+ _vector.setFromMatrixPosition( camera.matrixWorld ).applyMatrix4( _matrix );
1288
+ _forward.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ).transformDirection( _matrix );
1073
1289
 
1074
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
1290
+ for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
1075
1291
 
1076
- if ( drawInfo[ i ].visible && drawInfo[ i ].active ) {
1292
+ if ( instanceInfo[ i ].visible && instanceInfo[ i ].active ) {
1077
1293
 
1078
- const geometryId = drawInfo[ i ].geometryIndex;
1294
+ const geometryId = instanceInfo[ i ].geometryIndex;
1079
1295
 
1080
1296
  // get the bounds in world space
1081
1297
  this.getMatrixAt( i, _matrix );
@@ -1092,8 +1308,9 @@ class BatchedMesh extends Mesh {
1092
1308
  if ( ! culled ) {
1093
1309
 
1094
1310
  // get the distance from camera used for sorting
1311
+ const geometryInfo = geometryInfoList[ geometryId ];
1095
1312
  const z = _temp.subVectors( _sphere.center, _vector ).dot( _forward );
1096
- _renderList.push( drawRanges[ geometryId ], z, i );
1313
+ _renderList.push( geometryInfo.start, geometryInfo.count, z, i );
1097
1314
 
1098
1315
  }
1099
1316
 
@@ -1117,10 +1334,10 @@ class BatchedMesh extends Mesh {
1117
1334
  for ( let i = 0, l = list.length; i < l; i ++ ) {
1118
1335
 
1119
1336
  const item = list[ i ];
1120
- multiDrawStarts[ count ] = item.start * bytesPerElement;
1121
- multiDrawCounts[ count ] = item.count;
1122
- indirectArray[ count ] = item.index;
1123
- count ++;
1337
+ multiDrawStarts[ multiDrawCount ] = item.start * bytesPerElement;
1338
+ multiDrawCounts[ multiDrawCount ] = item.count;
1339
+ indirectArray[ multiDrawCount ] = item.index;
1340
+ multiDrawCount ++;
1124
1341
 
1125
1342
  }
1126
1343
 
@@ -1128,11 +1345,11 @@ class BatchedMesh extends Mesh {
1128
1345
 
1129
1346
  } else {
1130
1347
 
1131
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
1348
+ for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
1132
1349
 
1133
- if ( drawInfo[ i ].visible && drawInfo[ i ].active ) {
1350
+ if ( instanceInfo[ i ].visible && instanceInfo[ i ].active ) {
1134
1351
 
1135
- const geometryId = drawInfo[ i ].geometryIndex;
1352
+ const geometryId = instanceInfo[ i ].geometryIndex;
1136
1353
 
1137
1354
  // determine whether the batched geometry is within the frustum
1138
1355
  let culled = false;
@@ -1147,11 +1364,11 @@ class BatchedMesh extends Mesh {
1147
1364
 
1148
1365
  if ( ! culled ) {
1149
1366
 
1150
- const range = drawRanges[ geometryId ];
1151
- multiDrawStarts[ count ] = range.start * bytesPerElement;
1152
- multiDrawCounts[ count ] = range.count;
1153
- indirectArray[ count ] = i;
1154
- count ++;
1367
+ const geometryInfo = geometryInfoList[ geometryId ];
1368
+ multiDrawStarts[ multiDrawCount ] = geometryInfo.start * bytesPerElement;
1369
+ multiDrawCounts[ multiDrawCount ] = geometryInfo.count;
1370
+ indirectArray[ multiDrawCount ] = i;
1371
+ multiDrawCount ++;
1155
1372
 
1156
1373
  }
1157
1374
 
@@ -1162,7 +1379,7 @@ class BatchedMesh extends Mesh {
1162
1379
  }
1163
1380
 
1164
1381
  indirectTexture.needsUpdate = true;
1165
- this._multiDrawCount = count;
1382
+ this._multiDrawCount = multiDrawCount;
1166
1383
  this._visibilityChanged = false;
1167
1384
 
1168
1385
  }