super-three 0.169.1 → 0.170.1

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 (255) hide show
  1. package/build/three.cjs +24628 -24228
  2. package/build/three.core.js +37002 -0
  3. package/build/three.core.min.js +6 -0
  4. package/build/three.module.js +10519 -47108
  5. package/build/three.module.min.js +1 -1
  6. package/build/three.tsl.js +533 -0
  7. package/build/three.tsl.min.js +6 -0
  8. package/build/three.webgpu.js +17071 -53583
  9. package/build/three.webgpu.min.js +1 -1
  10. package/build/three.webgpu.nodes.js +17134 -53623
  11. package/build/three.webgpu.nodes.min.js +1 -1
  12. package/examples/jsm/Addons.js +3 -3
  13. package/examples/jsm/animation/MMDAnimationHelper.js +2 -0
  14. package/examples/jsm/animation/MMDPhysics.js +2 -0
  15. package/examples/jsm/capabilities/WebGPU.js +1 -10
  16. package/examples/jsm/controls/ArcballControls.js +25 -21
  17. package/examples/jsm/controls/OrbitControls.js +41 -9
  18. package/examples/jsm/controls/PointerLockControls.js +2 -5
  19. package/examples/jsm/controls/TransformControls.js +22 -6
  20. package/examples/jsm/csm/CSM.js +2 -2
  21. package/examples/jsm/csm/CSMFrustum.js +7 -4
  22. package/examples/jsm/csm/CSMHelper.js +2 -0
  23. package/examples/jsm/csm/CSMShadowNode.js +437 -0
  24. package/examples/jsm/curves/NURBSCurve.js +34 -3
  25. package/examples/jsm/exporters/GLTFExporter.js +138 -71
  26. package/examples/jsm/exporters/KTX2Exporter.js +35 -12
  27. package/examples/jsm/exporters/MMDExporter.js +6 -0
  28. package/examples/jsm/exporters/USDZExporter.js +21 -3
  29. package/examples/jsm/geometries/DecalGeometry.js +70 -21
  30. package/examples/jsm/geometries/InstancedPointsGeometry.js +2 -0
  31. package/examples/jsm/helpers/TextureHelperGPU.js +175 -0
  32. package/examples/jsm/interactive/HTMLMesh.js +1 -0
  33. package/examples/jsm/lighting/TiledLighting.js +18 -0
  34. package/examples/jsm/lights/RectAreaLightTexturesLib.js +1 -1
  35. package/examples/jsm/lines/LineGeometry.js +25 -0
  36. package/examples/jsm/lines/LineMaterial.js +0 -1
  37. package/examples/jsm/lines/LineSegmentsGeometry.js +2 -0
  38. package/examples/jsm/lines/webgpu/Line2.js +2 -1
  39. package/examples/jsm/lines/webgpu/LineSegments2.js +2 -16
  40. package/examples/jsm/lines/webgpu/Wireframe.js +57 -0
  41. package/examples/jsm/loaders/3DMLoader.js +1 -0
  42. package/examples/jsm/loaders/3MFLoader.js +91 -0
  43. package/examples/jsm/loaders/FBXLoader.js +12 -2
  44. package/examples/jsm/loaders/GLTFLoader.js +2 -0
  45. package/examples/jsm/loaders/KTX2Loader.js +143 -49
  46. package/examples/jsm/loaders/LDrawLoader.js +22 -136
  47. package/examples/jsm/loaders/LottieLoader.js +1 -0
  48. package/examples/jsm/loaders/MMDLoader.js +3 -0
  49. package/examples/jsm/loaders/MaterialXLoader.js +2 -2
  50. package/examples/jsm/materials/LDrawConditionalLineMaterial.js +143 -0
  51. package/examples/jsm/materials/LDrawConditionalLineNodeMaterial.js +114 -0
  52. package/examples/jsm/math/ColorSpaces.js +76 -0
  53. package/examples/jsm/misc/ProgressiveLightMap.js +52 -40
  54. package/examples/jsm/misc/ProgressiveLightMapGPU.js +293 -0
  55. package/examples/jsm/misc/VolumeSlice.js +1 -0
  56. package/examples/jsm/objects/InstancedPoints.js +2 -4
  57. package/examples/jsm/objects/LensflareMesh.js +4 -2
  58. package/examples/jsm/objects/SkyMesh.js +5 -3
  59. package/examples/jsm/objects/Water2Mesh.js +7 -3
  60. package/examples/jsm/objects/WaterMesh.js +12 -10
  61. package/examples/jsm/postprocessing/AfterimagePass.js +14 -3
  62. package/examples/jsm/postprocessing/SSRPass.js +6 -0
  63. package/examples/jsm/renderers/CSS2DRenderer.js +6 -3
  64. package/examples/jsm/renderers/CSS3DRenderer.js +7 -4
  65. package/examples/jsm/shaders/FXAAShader.js +225 -224
  66. package/examples/jsm/transpiler/AST.js +2 -2
  67. package/examples/jsm/transpiler/GLSLDecoder.js +57 -25
  68. package/examples/jsm/transpiler/ShaderToyDecoder.js +1 -1
  69. package/examples/jsm/transpiler/TSLEncoder.js +11 -6
  70. package/{src/nodes → examples/jsm/tsl}/display/AfterImageNode.js +18 -21
  71. package/{src/nodes → examples/jsm/tsl}/display/AnaglyphPassNode.js +2 -6
  72. package/{src/nodes → examples/jsm/tsl}/display/AnamorphicNode.js +12 -18
  73. package/{src/nodes → examples/jsm/tsl}/display/BleachBypass.js +1 -3
  74. package/{src/nodes → examples/jsm/tsl}/display/BloomNode.js +12 -41
  75. package/{src/nodes → examples/jsm/tsl}/display/DenoiseNode.js +60 -47
  76. package/{src/nodes → examples/jsm/tsl}/display/DepthOfFieldNode.js +4 -9
  77. package/{src/nodes → examples/jsm/tsl}/display/DotScreenNode.js +2 -9
  78. package/examples/jsm/tsl/display/FXAANode.js +316 -0
  79. package/{src/nodes → examples/jsm/tsl}/display/FilmNode.js +4 -7
  80. package/{src/nodes → examples/jsm/tsl}/display/GTAONode.js +73 -78
  81. package/{src/nodes → examples/jsm/tsl}/display/GaussianBlurNode.js +78 -42
  82. package/examples/jsm/tsl/display/LensflareNode.js +161 -0
  83. package/{src/nodes → examples/jsm/tsl}/display/Lut3DNode.js +3 -5
  84. package/{src/nodes → examples/jsm/tsl}/display/MotionBlur.js +2 -3
  85. package/examples/jsm/tsl/display/OutlineNode.js +434 -0
  86. package/{src/nodes → examples/jsm/tsl}/display/ParallaxBarrierPassNode.js +2 -5
  87. package/{src/nodes → examples/jsm/tsl}/display/PixelationPassNode.js +5 -17
  88. package/{src/nodes → examples/jsm/tsl}/display/RGBShiftNode.js +2 -6
  89. package/examples/jsm/tsl/display/SMAANode.js +620 -0
  90. package/{src/nodes → examples/jsm/tsl}/display/SSAAPassNode.js +10 -25
  91. package/examples/jsm/tsl/display/SSRNode.js +366 -0
  92. package/{src/nodes → examples/jsm/tsl}/display/Sepia.js +1 -2
  93. package/{src/nodes → examples/jsm/tsl}/display/SobelOperatorNode.js +4 -12
  94. package/{src/nodes → examples/jsm/tsl}/display/StereoCompositePassNode.js +9 -11
  95. package/{src/nodes → examples/jsm/tsl}/display/StereoPassNode.js +10 -11
  96. package/examples/jsm/tsl/display/TRAAPassNode.js +355 -0
  97. package/{src/nodes → examples/jsm/tsl}/display/TransitionNode.js +3 -7
  98. package/examples/jsm/tsl/display/hashBlur.js +24 -0
  99. package/examples/jsm/tsl/lighting/TiledLightsNode.js +389 -0
  100. package/examples/jsm/utils/SceneOptimizer.js +410 -0
  101. package/examples/jsm/utils/UVsDebug.js +1 -1
  102. package/examples/jsm/utils/{TextureUtilsGPU.js → WebGPUTextureUtils.js} +2 -1
  103. package/examples/jsm/webxr/OculusHandPointerModel.js +21 -21
  104. package/examples/jsm/webxr/Text2D.js +6 -6
  105. package/package.json +4 -4
  106. package/src/Three.Core.js +178 -0
  107. package/src/Three.TSL.js +526 -0
  108. package/src/Three.WebGPU.Nodes.js +9 -186
  109. package/src/Three.WebGPU.js +8 -186
  110. package/src/Three.js +1 -177
  111. package/src/animation/AnimationClip.js +2 -2
  112. package/src/animation/AnimationObjectGroup.js +2 -2
  113. package/src/audio/Audio.js +38 -0
  114. package/src/cameras/PerspectiveCamera.js +6 -6
  115. package/src/cameras/StereoCamera.js +2 -2
  116. package/src/constants.js +1 -6
  117. package/src/core/BufferGeometry.js +48 -8
  118. package/src/core/InterleavedBuffer.js +4 -4
  119. package/src/core/Object3D.js +2 -2
  120. package/src/extras/core/Curve.js +3 -3
  121. package/src/extras/core/Shape.js +2 -2
  122. package/src/geometries/CylinderGeometry.js +2 -2
  123. package/src/geometries/EdgesGeometry.js +2 -2
  124. package/src/geometries/LatheGeometry.js +2 -2
  125. package/src/lights/SpotLightShadow.js +2 -2
  126. package/src/materials/LineDashedMaterial.js +0 -1
  127. package/src/materials/Material.js +2 -2
  128. package/src/materials/MeshPhongMaterial.js +1 -1
  129. package/src/materials/MeshPhysicalMaterial.js +2 -2
  130. package/src/materials/MeshStandardMaterial.js +2 -2
  131. package/src/materials/nodes/Line2NodeMaterial.js +32 -13
  132. package/src/materials/nodes/LineDashedNodeMaterial.js +4 -2
  133. package/src/materials/nodes/NodeMaterial.js +93 -23
  134. package/src/materials/nodes/SpriteNodeMaterial.js +11 -2
  135. package/src/materials/nodes/manager/NodeMaterialObserver.js +119 -7
  136. package/src/math/ColorManagement.js +143 -102
  137. package/src/math/Line3.js +2 -2
  138. package/src/math/Quaternion.js +2 -2
  139. package/src/math/Spherical.js +3 -3
  140. package/src/math/Vector2.js +7 -7
  141. package/src/math/Vector3.js +9 -9
  142. package/src/math/Vector4.js +22 -9
  143. package/src/nodes/Nodes.js +2 -21
  144. package/src/nodes/TSL.js +9 -25
  145. package/src/nodes/accessors/Arrays.js +27 -0
  146. package/src/nodes/accessors/BuiltinNode.js +26 -0
  147. package/src/nodes/accessors/Camera.js +0 -1
  148. package/src/nodes/accessors/ClippingNode.js +76 -42
  149. package/src/nodes/accessors/InstanceNode.js +17 -20
  150. package/src/nodes/accessors/InstancedMeshNode.js +26 -0
  151. package/src/nodes/accessors/Lights.js +32 -0
  152. package/src/nodes/accessors/SceneNode.js +35 -1
  153. package/src/nodes/accessors/StorageBufferNode.js +61 -19
  154. package/src/nodes/accessors/StorageTextureNode.js +10 -4
  155. package/src/nodes/accessors/TextureNode.js +1 -1
  156. package/src/nodes/accessors/VelocityNode.js +13 -3
  157. package/src/nodes/code/FunctionCallNode.js +16 -5
  158. package/src/nodes/code/ScriptableNode.js +4 -4
  159. package/src/nodes/core/Node.js +1 -0
  160. package/src/nodes/core/NodeBuilder.js +31 -14
  161. package/src/nodes/core/NodeUtils.js +28 -0
  162. package/src/nodes/core/VarNode.js +15 -3
  163. package/src/nodes/core/constants.js +6 -0
  164. package/src/nodes/display/BlendModes.js +99 -0
  165. package/src/nodes/display/ColorAdjustment.js +53 -4
  166. package/src/nodes/display/ColorSpaceFunctions.js +5 -5
  167. package/src/nodes/display/ColorSpaceNode.js +27 -39
  168. package/src/nodes/display/PassNode.js +6 -4
  169. package/src/nodes/display/ToneMappingNode.js +1 -1
  170. package/src/nodes/display/ToonOutlinePassNode.js +3 -3
  171. package/src/nodes/display/ViewportDepthNode.js +48 -1
  172. package/src/nodes/functions/PhysicalLightingModel.js +21 -15
  173. package/src/nodes/functions/material/getAlphaHashThreshold.js +70 -0
  174. package/src/nodes/functions/material/getGeometryRoughness.js +8 -2
  175. package/src/nodes/functions/material/getParallaxCorrectNormal.js +22 -0
  176. package/src/nodes/gpgpu/ComputeNode.js +9 -1
  177. package/src/nodes/lighting/AnalyticLightNode.js +23 -419
  178. package/src/nodes/lighting/LightsNode.js +24 -12
  179. package/src/nodes/lighting/PointLightNode.js +41 -22
  180. package/src/nodes/lighting/PointShadowNode.js +254 -0
  181. package/src/nodes/lighting/RectAreaLightNode.js +3 -0
  182. package/src/nodes/lighting/ShadowNode.js +593 -0
  183. package/src/nodes/lighting/SpotLightNode.js +14 -2
  184. package/src/nodes/utils/LoopNode.js +2 -2
  185. package/src/nodes/utils/Oscillators.js +6 -0
  186. package/src/nodes/utils/PostProcessingUtils.js +89 -0
  187. package/src/nodes/utils/RTTNode.js +9 -1
  188. package/src/nodes/utils/ReflectorNode.js +101 -15
  189. package/src/nodes/utils/StorageArrayElementNode.js +2 -2
  190. package/src/nodes/utils/Timer.js +29 -0
  191. package/src/objects/BatchedMesh.js +472 -296
  192. package/src/objects/ClippingGroup.js +19 -0
  193. package/src/objects/Skeleton.js +2 -2
  194. package/src/renderers/WebGLRenderer.js +183 -109
  195. package/src/renderers/common/Animation.js +23 -11
  196. package/src/renderers/common/Attributes.js +4 -0
  197. package/src/renderers/common/Backend.js +2 -0
  198. package/src/renderers/common/Background.js +15 -5
  199. package/src/renderers/common/Bindings.js +23 -6
  200. package/src/renderers/common/ClippingContext.js +77 -85
  201. package/src/renderers/common/Constants.js +2 -1
  202. package/src/renderers/common/Geometries.js +20 -0
  203. package/src/renderers/common/IndirectStorageBufferAttribute.js +15 -0
  204. package/src/renderers/common/Lighting.js +45 -0
  205. package/src/renderers/common/PostProcessingUtils.js +86 -0
  206. package/src/renderers/common/RenderList.js +48 -17
  207. package/src/renderers/common/RenderLists.js +4 -2
  208. package/src/renderers/common/RenderObject.js +45 -28
  209. package/src/renderers/common/RenderObjects.js +12 -6
  210. package/src/renderers/common/Renderer.js +222 -77
  211. package/src/renderers/common/Textures.js +41 -17
  212. package/src/renderers/common/UniformsGroup.js +16 -4
  213. package/src/renderers/common/extras/PMREMGenerator.js +89 -13
  214. package/src/renderers/common/nodes/NodeLibrary.js +2 -15
  215. package/src/renderers/common/nodes/NodeStorageBuffer.js +2 -3
  216. package/src/renderers/common/nodes/NodeUniform.js +42 -0
  217. package/src/renderers/common/nodes/Nodes.js +2 -2
  218. package/src/renderers/shaders/ShaderChunk/colorspace_pars_fragment.glsl.js +4 -24
  219. package/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl.js +8 -0
  220. package/src/renderers/shaders/ShaderChunk/map_fragment.glsl.js +2 -2
  221. package/src/renderers/webgl/WebGLBackground.js +20 -1
  222. package/src/renderers/webgl/WebGLBufferRenderer.js +2 -6
  223. package/src/renderers/webgl/WebGLCapabilities.js +0 -7
  224. package/src/renderers/webgl/WebGLGeometries.js +0 -28
  225. package/src/renderers/webgl/WebGLIndexedBufferRenderer.js +2 -6
  226. package/src/renderers/webgl/WebGLProgram.js +24 -28
  227. package/src/renderers/webgl/WebGLPrograms.js +5 -2
  228. package/src/renderers/webgl/WebGLState.js +37 -1
  229. package/src/renderers/webgl/WebGLTextures.js +29 -12
  230. package/src/renderers/webgl-fallback/WebGLBackend.js +111 -37
  231. package/src/renderers/webgl-fallback/WebGLBufferRenderer.js +2 -7
  232. package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +43 -12
  233. package/src/renderers/webgl-fallback/utils/WebGLState.js +26 -1
  234. package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +37 -9
  235. package/src/renderers/webgpu/WebGPUBackend.js +152 -56
  236. package/src/renderers/webgpu/WebGPURenderer.Nodes.js +1 -1
  237. package/src/renderers/webgpu/WebGPURenderer.js +1 -1
  238. package/src/renderers/webgpu/nodes/BasicNodeLibrary.js +0 -8
  239. package/src/renderers/webgpu/nodes/StandardNodeLibrary.js +13 -34
  240. package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +192 -92
  241. package/src/renderers/webgpu/nodes/WGSLNodeFunction.js +12 -4
  242. package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +15 -2
  243. package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +72 -7
  244. package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +29 -9
  245. package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +29 -7
  246. package/src/renderers/webgpu/utils/WebGPUUtils.js +0 -1
  247. package/src/renderers/webxr/WebXRManager.js +4 -4
  248. package/src/textures/Source.js +2 -2
  249. package/src/textures/Texture.js +2 -2
  250. package/examples/jsm/cameras/CinematicCamera.js +0 -208
  251. package/src/nodes/display/BlendMode.js +0 -54
  252. package/src/nodes/display/FXAANode.js +0 -332
  253. package/src/nodes/utils/OscNode.js +0 -85
  254. package/src/nodes/utils/TimerNode.js +0 -97
  255. /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 = [];
189
+
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;
150
194
 
151
- // geometry information
152
- this._drawRanges = [];
153
- this._reservedRanges = [];
154
- this._bounds = [];
195
+ // flags
196
+ this._visibilityChanged = true;
197
+ this._geometryInitialized = false;
155
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;
@@ -264,7 +306,7 @@ class BatchedMesh extends Mesh {
264
306
  const batchGeometry = this.geometry;
265
307
  if ( Boolean( geometry.getIndex() ) !== Boolean( batchGeometry.getIndex() ) ) {
266
308
 
267
- throw new Error( 'BatchedMesh: All geometries must consistently have "index".' );
309
+ throw new Error( 'THREE.BatchedMesh: All geometries must consistently have "index".' );
268
310
 
269
311
  }
270
312
 
@@ -272,7 +314,7 @@ class BatchedMesh extends Mesh {
272
314
 
273
315
  if ( ! geometry.hasAttribute( attributeName ) ) {
274
316
 
275
- throw new Error( `BatchedMesh: Added geometry missing "${ attributeName }". All geometries must have consistent attributes.` );
317
+ throw new Error( `THREE.BatchedMesh: Added geometry missing "${ attributeName }". All geometries must have consistent attributes.` );
276
318
 
277
319
  }
278
320
 
@@ -280,7 +322,7 @@ class BatchedMesh extends Mesh {
280
322
  const dstAttribute = batchGeometry.getAttribute( attributeName );
281
323
  if ( srcAttribute.itemSize !== dstAttribute.itemSize || srcAttribute.normalized !== dstAttribute.normalized ) {
282
324
 
283
- throw new Error( 'BatchedMesh: All attributes must have a consistent itemSize and normalized value.' );
325
+ throw new Error( 'THREE.BatchedMesh: All attributes must have a consistent itemSize and normalized value.' );
284
326
 
285
327
  }
286
328
 
@@ -288,6 +330,29 @@ class BatchedMesh extends Mesh {
288
330
 
289
331
  }
290
332
 
333
+ validateInstanceId( instanceId ) {
334
+
335
+ const instanceInfo = this._instanceInfo;
336
+ if ( instanceId < 0 || instanceId >= instanceInfo.length || instanceInfo[ instanceId ].active === false ) {
337
+
338
+ throw new Error( `THREE.BatchedMesh: Invalid instanceId ${instanceId}. Instance is either out of range or has been deleted.` );
339
+
340
+ }
341
+
342
+ }
343
+
344
+ validateGeometryId( geometryId ) {
345
+
346
+ const geometryInfoList = this._geometryInfo;
347
+ if ( geometryId < 0 || geometryId >= geometryInfoList.length || geometryInfoList[ geometryId ].active === false ) {
348
+
349
+ throw new Error( `THREE.BatchedMesh: Invalid geometryId ${geometryId}. Geometry is either out of range or has been deleted.` );
350
+
351
+ }
352
+
353
+ }
354
+
355
+
291
356
  setCustomSort( func ) {
292
357
 
293
358
  this.customSort = func;
@@ -304,14 +369,14 @@ class BatchedMesh extends Mesh {
304
369
  }
305
370
 
306
371
  const boundingBox = this.boundingBox;
307
- const drawInfo = this._drawInfo;
372
+ const instanceInfo = this._instanceInfo;
308
373
 
309
374
  boundingBox.makeEmpty();
310
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
375
+ for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
311
376
 
312
- if ( drawInfo[ i ].active === false ) continue;
377
+ if ( instanceInfo[ i ].active === false ) continue;
313
378
 
314
- const geometryId = drawInfo[ i ].geometryIndex;
379
+ const geometryId = instanceInfo[ i ].geometryIndex;
315
380
  this.getMatrixAt( i, _matrix );
316
381
  this.getBoundingBoxAt( geometryId, _box ).applyMatrix4( _matrix );
317
382
  boundingBox.union( _box );
@@ -329,14 +394,14 @@ class BatchedMesh extends Mesh {
329
394
  }
330
395
 
331
396
  const boundingSphere = this.boundingSphere;
332
- const drawInfo = this._drawInfo;
397
+ const instanceInfo = this._instanceInfo;
333
398
 
334
399
  boundingSphere.makeEmpty();
335
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
400
+ for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
336
401
 
337
- if ( drawInfo[ i ].active === false ) continue;
402
+ if ( instanceInfo[ i ].active === false ) continue;
338
403
 
339
- const geometryId = drawInfo[ i ].geometryIndex;
404
+ const geometryId = instanceInfo[ i ].geometryIndex;
340
405
  this.getMatrixAt( i, _matrix );
341
406
  this.getBoundingSphereAt( geometryId, _sphere ).applyMatrix4( _matrix );
342
407
  boundingSphere.union( _sphere );
@@ -347,16 +412,16 @@ class BatchedMesh extends Mesh {
347
412
 
348
413
  addInstance( geometryId ) {
349
414
 
350
- const atCapacity = this._drawInfo.length >= this.maxInstanceCount;
415
+ const atCapacity = this._instanceInfo.length >= this.maxInstanceCount;
351
416
 
352
417
  // ensure we're not over geometry
353
418
  if ( atCapacity && this._availableInstanceIds.length === 0 ) {
354
419
 
355
- throw new Error( 'BatchedMesh: Maximum item count reached.' );
420
+ throw new Error( 'THREE.BatchedMesh: Maximum item count reached.' );
356
421
 
357
422
  }
358
423
 
359
- const instanceDrawInfo = {
424
+ const instanceInfo = {
360
425
  visible: true,
361
426
  active: true,
362
427
  geometryIndex: geometryId,
@@ -367,19 +432,20 @@ class BatchedMesh extends Mesh {
367
432
  // Prioritize using previously freed instance ids
368
433
  if ( this._availableInstanceIds.length > 0 ) {
369
434
 
370
- drawId = this._availableInstanceIds.pop();
371
- this._drawInfo[ drawId ] = instanceDrawInfo;
435
+ this._availableInstanceIds.sort( ascIdSort );
436
+
437
+ drawId = this._availableInstanceIds.shift();
438
+ this._instanceInfo[ drawId ] = instanceInfo;
372
439
 
373
440
  } else {
374
441
 
375
- drawId = this._drawInfo.length;
376
- this._drawInfo.push( instanceDrawInfo );
442
+ drawId = this._instanceInfo.length;
443
+ this._instanceInfo.push( instanceInfo );
377
444
 
378
445
  }
379
446
 
380
447
  const matricesTexture = this._matricesTexture;
381
- const matricesArray = matricesTexture.image.data;
382
- _identityMatrix.toArray( matricesArray, drawId * 16 );
448
+ _matrix.identity().toArray( matricesTexture.image.data, drawId * 16 );
383
449
  matricesTexture.needsUpdate = true;
384
450
 
385
451
  const colorsTexture = this._colorsTexture;
@@ -390,118 +456,85 @@ class BatchedMesh extends Mesh {
390
456
 
391
457
  }
392
458
 
459
+ this._visibilityChanged = true;
393
460
  return drawId;
394
461
 
395
462
  }
396
463
 
397
- addGeometry( geometry, vertexCount = - 1, indexCount = - 1 ) {
464
+ addGeometry( geometry, reservedVertexCount = - 1, reservedIndexCount = - 1 ) {
398
465
 
399
466
  this._initializeGeometry( geometry );
400
467
 
401
468
  this._validateGeometry( geometry );
402
469
 
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 = {
470
+ const geometryInfo = {
471
+ // geometry information
412
472
  vertexStart: - 1,
413
473
  vertexCount: - 1,
474
+ reservedVertexCount: - 1,
475
+
414
476
  indexStart: - 1,
415
477
  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 {
433
-
434
- reservedRange.vertexCount = vertexCount;
435
-
436
- }
478
+ reservedIndexCount: - 1,
437
479
 
438
- if ( lastRange === null ) {
480
+ // draw range information
481
+ start: - 1,
482
+ count: - 1,
439
483
 
440
- reservedRange.vertexStart = 0;
441
-
442
- } else {
443
-
444
- reservedRange.vertexStart = lastRange.vertexStart + lastRange.vertexCount;
484
+ // state
485
+ boundingBox: null,
486
+ boundingSphere: null,
487
+ active: true,
488
+ };
445
489
 
446
- }
490
+ const geometryInfoList = this._geometryInfo;
491
+ geometryInfo.vertexStart = this._nextVertexStart;
492
+ geometryInfo.reservedVertexCount = reservedVertexCount === - 1 ? geometry.getAttribute( 'position' ).count : reservedVertexCount;
447
493
 
448
494
  const index = geometry.getIndex();
449
495
  const hasIndex = index !== null;
450
496
  if ( hasIndex ) {
451
497
 
452
- if ( indexCount === - 1 ) {
453
-
454
- reservedRange.indexCount = index.count;
455
-
456
- } else {
498
+ geometryInfo.indexStart = this._nextIndexStart;
499
+ geometryInfo.reservedIndexCount = reservedIndexCount === - 1 ? index.count : reservedIndexCount;
457
500
 
458
- reservedRange.indexCount = indexCount;
501
+ }
459
502
 
460
- }
503
+ if (
504
+ geometryInfo.indexStart !== - 1 &&
505
+ geometryInfo.indexStart + geometryInfo.reservedIndexCount > this._maxIndexCount ||
506
+ geometryInfo.vertexStart + geometryInfo.reservedVertexCount > this._maxVertexCount
507
+ ) {
461
508
 
462
- if ( lastRange === null ) {
509
+ throw new Error( 'THREE.BatchedMesh: Reserved space request exceeds the maximum buffer size.' );
463
510
 
464
- reservedRange.indexStart = 0;
511
+ }
465
512
 
466
- } else {
513
+ // update id
514
+ let geometryId;
515
+ if ( this._availableGeometryIds.length > 0 ) {
467
516
 
468
- reservedRange.indexStart = lastRange.indexStart + lastRange.indexCount;
517
+ this._availableGeometryIds.sort( ascIdSort );
469
518
 
470
- }
519
+ geometryId = this._availableGeometryIds.shift();
520
+ geometryInfoList[ geometryId ] = geometryInfo;
471
521
 
472
- }
473
522
 
474
- if (
475
- reservedRange.indexStart !== - 1 &&
476
- reservedRange.indexStart + reservedRange.indexCount > this._maxIndexCount ||
477
- reservedRange.vertexStart + reservedRange.vertexCount > this._maxVertexCount
478
- ) {
523
+ } else {
479
524
 
480
- throw new Error( 'BatchedMesh: Reserved space request exceeds the maximum buffer size.' );
525
+ geometryId = this._geometryCount;
526
+ this._geometryCount ++;
527
+ geometryInfoList.push( geometryInfo );
481
528
 
482
529
  }
483
530
 
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
531
  // update the geometry
503
532
  this.setGeometryAt( geometryId, geometry );
504
533
 
534
+ // increment the next geometry position
535
+ this._nextIndexStart = geometryInfo.indexStart + geometryInfo.reservedIndexCount;
536
+ this._nextVertexStart = geometryInfo.vertexStart + geometryInfo.reservedVertexCount;
537
+
505
538
  return geometryId;
506
539
 
507
540
  }
@@ -510,7 +543,7 @@ class BatchedMesh extends Mesh {
510
543
 
511
544
  if ( geometryId >= this._geometryCount ) {
512
545
 
513
- throw new Error( 'BatchedMesh: Maximum geometry count reached.' );
546
+ throw new Error( 'THREE.BatchedMesh: Maximum geometry count reached.' );
514
547
 
515
548
  }
516
549
 
@@ -520,20 +553,22 @@ class BatchedMesh extends Mesh {
520
553
  const hasIndex = batchGeometry.getIndex() !== null;
521
554
  const dstIndex = batchGeometry.getIndex();
522
555
  const srcIndex = geometry.getIndex();
523
- const reservedRange = this._reservedRanges[ geometryId ];
556
+ const geometryInfo = this._geometryInfo[ geometryId ];
524
557
  if (
525
558
  hasIndex &&
526
- srcIndex.count > reservedRange.indexCount ||
527
- geometry.attributes.position.count > reservedRange.vertexCount
559
+ srcIndex.count > geometryInfo.reservedIndexCount ||
560
+ geometry.attributes.position.count > geometryInfo.reservedVertexCount
528
561
  ) {
529
562
 
530
- throw new Error( 'BatchedMesh: Reserved space not large enough for provided geometry.' );
563
+ throw new Error( 'THREE.BatchedMesh: Reserved space not large enough for provided geometry.' );
531
564
 
532
565
  }
533
566
 
534
- // copy geometry over
535
- const vertexStart = reservedRange.vertexStart;
536
- const vertexCount = reservedRange.vertexCount;
567
+ // copy geometry buffer data over
568
+ const vertexStart = geometryInfo.vertexStart;
569
+ const reservedVertexCount = geometryInfo.reservedVertexCount;
570
+ geometryInfo.vertexCount = geometry.getAttribute( 'position' ).count;
571
+
537
572
  for ( const attributeName in batchGeometry.attributes ) {
538
573
 
539
574
  // copy attribute data
@@ -543,7 +578,7 @@ class BatchedMesh extends Mesh {
543
578
 
544
579
  // fill the rest in with zeroes
545
580
  const itemSize = srcAttribute.itemSize;
546
- for ( let i = srcAttribute.count, l = vertexCount; i < l; i ++ ) {
581
+ for ( let i = srcAttribute.count, l = reservedVertexCount; i < l; i ++ ) {
547
582
 
548
583
  const index = vertexStart + i;
549
584
  for ( let c = 0; c < itemSize; c ++ ) {
@@ -555,14 +590,16 @@ class BatchedMesh extends Mesh {
555
590
  }
556
591
 
557
592
  dstAttribute.needsUpdate = true;
558
- dstAttribute.addUpdateRange( vertexStart * itemSize, vertexCount * itemSize );
593
+ dstAttribute.addUpdateRange( vertexStart * itemSize, reservedVertexCount * itemSize );
559
594
 
560
595
  }
561
596
 
562
597
  // copy index
563
598
  if ( hasIndex ) {
564
599
 
565
- const indexStart = reservedRange.indexStart;
600
+ const indexStart = geometryInfo.indexStart;
601
+ const reservedIndexCount = geometryInfo.reservedIndexCount;
602
+ geometryInfo.indexCount = geometry.getIndex().count;
566
603
 
567
604
  // copy index data over
568
605
  for ( let i = 0; i < srcIndex.count; i ++ ) {
@@ -572,69 +609,75 @@ class BatchedMesh extends Mesh {
572
609
  }
573
610
 
574
611
  // fill the rest in with zeroes
575
- for ( let i = srcIndex.count, l = reservedRange.indexCount; i < l; i ++ ) {
612
+ for ( let i = srcIndex.count, l = reservedIndexCount; i < l; i ++ ) {
576
613
 
577
614
  dstIndex.setX( indexStart + i, vertexStart );
578
615
 
579
616
  }
580
617
 
581
618
  dstIndex.needsUpdate = true;
582
- dstIndex.addUpdateRange( indexStart, reservedRange.indexCount );
619
+ dstIndex.addUpdateRange( indexStart, geometryInfo.reservedIndexCount );
583
620
 
584
621
  }
585
622
 
623
+ // update the draw range
624
+ geometryInfo.start = hasIndex ? geometryInfo.indexStart : geometryInfo.vertexStart;
625
+ geometryInfo.count = hasIndex ? geometryInfo.indexCount : geometryInfo.vertexCount;
626
+
586
627
  // store the bounding boxes
587
- const bound = this._bounds[ geometryId ];
628
+ geometryInfo.boundingBox = null;
588
629
  if ( geometry.boundingBox !== null ) {
589
630
 
590
- bound.box.copy( geometry.boundingBox );
591
- bound.boxInitialized = true;
592
-
593
- } else {
594
-
595
- bound.boxInitialized = false;
631
+ geometryInfo.boundingBox = geometry.boundingBox.clone();
596
632
 
597
633
  }
598
634
 
635
+ geometryInfo.boundingSphere = null;
599
636
  if ( geometry.boundingSphere !== null ) {
600
637
 
601
- bound.sphere.copy( geometry.boundingSphere );
602
- bound.sphereInitialized = true;
603
-
604
- } else {
605
-
606
- bound.sphereInitialized = false;
638
+ geometryInfo.boundingSphere = geometry.boundingSphere.clone();
607
639
 
608
640
  }
609
641
 
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
642
  this._visibilityChanged = true;
615
-
616
643
  return geometryId;
617
644
 
618
645
  }
619
646
 
620
- /*
621
647
  deleteGeometry( geometryId ) {
622
648
 
623
- // TODO: delete geometry and associated instances
649
+ const geometryInfoList = this._geometryInfo;
650
+ if ( geometryId >= geometryInfoList.length || geometryInfoList[ geometryId ].active === false ) {
624
651
 
625
- }
626
- */
652
+ return this;
627
653
 
628
- deleteInstance( instanceId ) {
654
+ }
629
655
 
630
- const drawInfo = this._drawInfo;
631
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
656
+ // delete any instances associated with this geometry
657
+ const instanceInfo = this._instanceInfo;
658
+ for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
632
659
 
633
- return this;
660
+ if ( instanceInfo[ i ].geometryIndex === geometryId ) {
661
+
662
+ this.deleteInstance( i );
663
+
664
+ }
634
665
 
635
666
  }
636
667
 
637
- drawInfo[ instanceId ].active = false;
668
+ geometryInfoList[ geometryId ].active = false;
669
+ this._availableGeometryIds.push( geometryId );
670
+ this._visibilityChanged = true;
671
+
672
+ return this;
673
+
674
+ }
675
+
676
+ deleteInstance( instanceId ) {
677
+
678
+ this.validateInstanceId( instanceId );
679
+
680
+ this._instanceInfo[ instanceId ].active = false;
638
681
  this._availableInstanceIds.push( instanceId );
639
682
  this._visibilityChanged = true;
640
683
 
@@ -642,6 +685,95 @@ class BatchedMesh extends Mesh {
642
685
 
643
686
  }
644
687
 
688
+ optimize() {
689
+
690
+ // track the next indices to copy data to
691
+ let nextVertexStart = 0;
692
+ let nextIndexStart = 0;
693
+
694
+ // Iterate over all geometry ranges in order sorted from earliest in the geometry buffer to latest
695
+ // in the geometry buffer. Because draw range objects can be reused there is no guarantee of their order.
696
+ const geometryInfoList = this._geometryInfo;
697
+ const indices = geometryInfoList
698
+ .map( ( e, i ) => i )
699
+ .sort( ( a, b ) => {
700
+
701
+ return geometryInfoList[ a ].vertexStart - geometryInfoList[ b ].vertexStart;
702
+
703
+ } );
704
+
705
+ const geometry = this.geometry;
706
+ for ( let i = 0, l = geometryInfoList.length; i < l; i ++ ) {
707
+
708
+ // if a geometry range is inactive then don't copy anything
709
+ const index = indices[ i ];
710
+ const geometryInfo = geometryInfoList[ index ];
711
+ if ( geometryInfo.active === false ) {
712
+
713
+ continue;
714
+
715
+ }
716
+
717
+ // if a geometry contains an index buffer then shift it, as well
718
+ if ( geometry.index !== null ) {
719
+
720
+ if ( geometryInfo.indexStart !== nextIndexStart ) {
721
+
722
+ const { indexStart, vertexStart, reservedIndexCount } = geometryInfo;
723
+ const index = geometry.index;
724
+ const array = index.array;
725
+
726
+ // shift the index pointers based on how the vertex data will shift
727
+ // adjusting the index must happen first so the original vertex start value is available
728
+ const elementDelta = nextVertexStart - vertexStart;
729
+ for ( let j = indexStart; j < indexStart + reservedIndexCount; j ++ ) {
730
+
731
+ array[ j ] = array[ j ] + elementDelta;
732
+
733
+ }
734
+
735
+ index.array.copyWithin( nextIndexStart, indexStart, indexStart + reservedIndexCount );
736
+ index.addUpdateRange( nextIndexStart, reservedIndexCount );
737
+
738
+ geometryInfo.indexStart = nextIndexStart;
739
+
740
+ }
741
+
742
+ nextIndexStart += geometryInfo.reservedIndexCount;
743
+
744
+ }
745
+
746
+ // if a geometry needs to be moved then copy attribute data to overwrite unused space
747
+ if ( geometryInfo.vertexStart !== nextVertexStart ) {
748
+
749
+ const { vertexStart, reservedVertexCount } = geometryInfo;
750
+ const attributes = geometry.attributes;
751
+ for ( const key in attributes ) {
752
+
753
+ const attribute = attributes[ key ];
754
+ const { array, itemSize } = attribute;
755
+ array.copyWithin( nextVertexStart * itemSize, vertexStart * itemSize, ( vertexStart + reservedVertexCount ) * itemSize );
756
+ attribute.addUpdateRange( nextVertexStart * itemSize, reservedVertexCount * itemSize );
757
+
758
+ }
759
+
760
+ geometryInfo.vertexStart = nextVertexStart;
761
+
762
+ }
763
+
764
+ nextVertexStart += geometryInfo.reservedVertexCount;
765
+ geometryInfo.start = geometry.index ? geometryInfo.indexStart : geometryInfo.vertexStart;
766
+
767
+ // step the next geometry points to the shifted position
768
+ this._nextIndexStart = geometry.index ? geometryInfo.indexStart + geometryInfo.reservedIndexCount : 0;
769
+ this._nextVertexStart = geometryInfo.vertexStart + geometryInfo.reservedVertexCount;
770
+
771
+ }
772
+
773
+ return this;
774
+
775
+ }
776
+
645
777
  // get bounding box and compute it if it doesn't exist
646
778
  getBoundingBoxAt( geometryId, target ) {
647
779
 
@@ -652,17 +784,14 @@ class BatchedMesh extends Mesh {
652
784
  }
653
785
 
654
786
  // compute bounding box
655
- const bound = this._bounds[ geometryId ];
656
- const box = bound.box;
657
787
  const geometry = this.geometry;
658
- if ( bound.boxInitialized === false ) {
659
-
660
- box.makeEmpty();
788
+ const geometryInfo = this._geometryInfo[ geometryId ];
789
+ if ( geometryInfo.boundingBox === null ) {
661
790
 
791
+ const box = new Box3();
662
792
  const index = geometry.index;
663
793
  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 ++ ) {
794
+ for ( let i = geometryInfo.start, l = geometryInfo.start + geometryInfo.count; i < l; i ++ ) {
666
795
 
667
796
  let iv = i;
668
797
  if ( index ) {
@@ -675,11 +804,11 @@ class BatchedMesh extends Mesh {
675
804
 
676
805
  }
677
806
 
678
- bound.boxInitialized = true;
807
+ geometryInfo.boundingBox = box;
679
808
 
680
809
  }
681
810
 
682
- target.copy( box );
811
+ target.copy( geometryInfo.boundingBox );
683
812
  return target;
684
813
 
685
814
  }
@@ -694,22 +823,19 @@ class BatchedMesh extends Mesh {
694
823
  }
695
824
 
696
825
  // compute bounding sphere
697
- const bound = this._bounds[ geometryId ];
698
- const sphere = bound.sphere;
699
826
  const geometry = this.geometry;
700
- if ( bound.sphereInitialized === false ) {
701
-
702
- sphere.makeEmpty();
827
+ const geometryInfo = this._geometryInfo[ geometryId ];
828
+ if ( geometryInfo.boundingSphere === null ) {
703
829
 
830
+ const sphere = new Sphere();
704
831
  this.getBoundingBoxAt( geometryId, _box );
705
832
  _box.getCenter( sphere.center );
706
833
 
707
834
  const index = geometry.index;
708
835
  const position = geometry.attributes.position;
709
- const drawRange = this._drawRanges[ geometryId ];
710
836
 
711
837
  let maxRadiusSq = 0;
712
- for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i ++ ) {
838
+ for ( let i = geometryInfo.start, l = geometryInfo.start + geometryInfo.count; i < l; i ++ ) {
713
839
 
714
840
  let iv = i;
715
841
  if ( index ) {
@@ -724,29 +850,21 @@ class BatchedMesh extends Mesh {
724
850
  }
725
851
 
726
852
  sphere.radius = Math.sqrt( maxRadiusSq );
727
- bound.sphereInitialized = true;
853
+ geometryInfo.boundingSphere = sphere;
728
854
 
729
855
  }
730
856
 
731
- target.copy( sphere );
857
+ target.copy( geometryInfo.boundingSphere );
732
858
  return target;
733
859
 
734
860
  }
735
861
 
736
862
  setMatrixAt( instanceId, matrix ) {
737
863
 
738
- // @TODO: Map geometryId to index of the arrays because
739
- // optimize() can make geometryId mismatch the index
864
+ this.validateInstanceId( instanceId );
740
865
 
741
- const drawInfo = this._drawInfo;
742
866
  const matricesTexture = this._matricesTexture;
743
867
  const matricesArray = this._matricesTexture.image.data;
744
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
745
-
746
- return this;
747
-
748
- }
749
-
750
868
  matrix.toArray( matricesArray, instanceId * 16 );
751
869
  matricesTexture.needsUpdate = true;
752
870
 
@@ -756,152 +874,213 @@ class BatchedMesh extends Mesh {
756
874
 
757
875
  getMatrixAt( instanceId, matrix ) {
758
876
 
759
- const drawInfo = this._drawInfo;
760
- const matricesArray = this._matricesTexture.image.data;
761
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
762
-
763
- return null;
764
-
765
- }
766
-
767
- return matrix.fromArray( matricesArray, instanceId * 16 );
877
+ this.validateInstanceId( instanceId );
878
+ return matrix.fromArray( this._matricesTexture.image.data, instanceId * 16 );
768
879
 
769
880
  }
770
881
 
771
882
  setColorAt( instanceId, color ) {
772
883
 
884
+ this.validateInstanceId( instanceId );
885
+
773
886
  if ( this._colorsTexture === null ) {
774
887
 
775
888
  this._initColorsTexture();
776
889
 
777
890
  }
778
891
 
779
- // @TODO: Map id to index of the arrays because
780
- // optimize() can make id mismatch the index
892
+ color.toArray( this._colorsTexture.image.data, instanceId * 4 );
893
+ this._colorsTexture.needsUpdate = true;
781
894
 
782
- const colorsTexture = this._colorsTexture;
783
- const colorsArray = this._colorsTexture.image.data;
784
- const drawInfo = this._drawInfo;
785
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
895
+ return this;
896
+
897
+ }
898
+
899
+ getColorAt( instanceId, color ) {
900
+
901
+ this.validateInstanceId( instanceId );
902
+ return color.fromArray( this._colorsTexture.image.data, instanceId * 4 );
903
+
904
+ }
905
+
906
+ setVisibleAt( instanceId, value ) {
907
+
908
+ this.validateInstanceId( instanceId );
909
+
910
+ if ( this._instanceInfo[ instanceId ].visible === value ) {
786
911
 
787
912
  return this;
788
913
 
789
914
  }
790
915
 
791
- color.toArray( colorsArray, instanceId * 4 );
792
- colorsTexture.needsUpdate = true;
916
+ this._instanceInfo[ instanceId ].visible = value;
917
+ this._visibilityChanged = true;
793
918
 
794
919
  return this;
795
920
 
796
921
  }
797
922
 
798
- getColorAt( instanceId, color ) {
923
+ getVisibleAt( instanceId ) {
799
924
 
800
- const colorsArray = this._colorsTexture.image.data;
801
- const drawInfo = this._drawInfo;
802
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
925
+ this.validateInstanceId( instanceId );
803
926
 
804
- return null;
927
+ return this._instanceInfo[ instanceId ].visible;
805
928
 
806
- }
929
+ }
807
930
 
808
- return color.fromArray( colorsArray, instanceId * 4 );
931
+ setGeometryIdAt( instanceId, geometryId ) {
809
932
 
810
- }
933
+ this.validateInstanceId( instanceId );
934
+ this.validateGeometryId( geometryId );
811
935
 
812
- setVisibleAt( instanceId, value ) {
936
+ this._instanceInfo[ instanceId ].geometryIndex = geometryId;
813
937
 
814
- // if the geometry is out of range, not active, or visibility state
815
- // does not change then return early
816
- const drawInfo = this._drawInfo;
817
- if (
818
- instanceId >= drawInfo.length ||
819
- drawInfo[ instanceId ].active === false ||
820
- drawInfo[ instanceId ].visible === value
821
- ) {
938
+ return this;
822
939
 
823
- return this;
940
+ }
824
941
 
825
- }
942
+ getGeometryIdAt( instanceId ) {
826
943
 
827
- drawInfo[ instanceId ].visible = value;
828
- this._visibilityChanged = true;
944
+ this.validateInstanceId( instanceId );
829
945
 
830
- return this;
946
+ return this._instanceInfo[ instanceId ].geometryIndex;
831
947
 
832
948
  }
833
949
 
834
- getVisibleAt( instanceId ) {
950
+ getGeometryRangeAt( geometryId, target = {} ) {
835
951
 
836
- // 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 ) {
952
+ this.validateGeometryId( geometryId );
839
953
 
840
- return false;
954
+ const geometryInfo = this._geometryInfo[ geometryId ];
955
+ target.vertexStart = geometryInfo.vertexStart;
956
+ target.vertexCount = geometryInfo.vertexCount;
957
+ target.reservedVertexCount = geometryInfo.reservedVertexCount;
841
958
 
842
- }
959
+ target.indexStart = geometryInfo.indexStart;
960
+ target.indexCount = geometryInfo.indexCount;
961
+ target.reservedIndexCount = geometryInfo.reservedIndexCount;
962
+
963
+ target.start = geometryInfo.start;
964
+ target.count = geometryInfo.count;
843
965
 
844
- return drawInfo[ instanceId ].visible;
966
+ return target;
845
967
 
846
968
  }
847
969
 
848
- setGeometryIdAt( instanceId, geometryId ) {
970
+ setInstanceCount( maxInstanceCount ) {
849
971
 
850
- // 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 ) {
972
+ // shrink the available instances as much as possible
973
+ const availableInstanceIds = this._availableInstanceIds;
974
+ const instanceInfo = this._instanceInfo;
975
+ availableInstanceIds.sort( ascIdSort );
976
+ while ( availableInstanceIds[ availableInstanceIds.length - 1 ] === instanceInfo.length ) {
853
977
 
854
- return null;
978
+ instanceInfo.pop();
979
+ availableInstanceIds.pop();
855
980
 
856
981
  }
857
982
 
858
- // check if the provided geometryId is within the valid range
859
- if ( geometryId < 0 || geometryId >= this._geometryCount ) {
983
+ // throw an error if it can't be shrunk to the desired size
984
+ if ( maxInstanceCount < instanceInfo.length ) {
860
985
 
861
- return null;
986
+ throw new Error( `BatchedMesh: Instance ids outside the range ${ maxInstanceCount } are being used. Cannot shrink instance count.` );
862
987
 
863
988
  }
864
989
 
865
- drawInfo[ instanceId ].geometryIndex = geometryId;
990
+ // copy the multi draw counts
991
+ const multiDrawCounts = new Int32Array( maxInstanceCount );
992
+ const multiDrawStarts = new Int32Array( maxInstanceCount );
993
+ copyArrayContents( this._multiDrawCounts, multiDrawCounts );
994
+ copyArrayContents( this._multiDrawStarts, multiDrawStarts );
866
995
 
867
- return this;
996
+ this._multiDrawCounts = multiDrawCounts;
997
+ this._multiDrawStarts = multiDrawStarts;
998
+ this._maxInstanceCount = maxInstanceCount;
999
+
1000
+ // update texture data for instance sampling
1001
+ const indirectTexture = this._indirectTexture;
1002
+ const matricesTexture = this._matricesTexture;
1003
+ const colorsTexture = this._colorsTexture;
1004
+
1005
+ indirectTexture.dispose();
1006
+ this._initIndirectTexture();
1007
+ copyArrayContents( indirectTexture.image.data, this._indirectTexture.image.data );
1008
+
1009
+ matricesTexture.dispose();
1010
+ this._initMatricesTexture();
1011
+ copyArrayContents( matricesTexture.image.data, this._matricesTexture.image.data );
1012
+
1013
+ if ( colorsTexture ) {
1014
+
1015
+ colorsTexture.dispose();
1016
+ this._initColorsTexture();
1017
+ copyArrayContents( colorsTexture.image.data, this._colorsTexture.image.data );
1018
+
1019
+ }
868
1020
 
869
1021
  }
870
1022
 
871
- getGeometryIdAt( instanceId ) {
1023
+ setGeometrySize( maxVertexCount, maxIndexCount ) {
872
1024
 
873
- const drawInfo = this._drawInfo;
874
- if ( instanceId >= drawInfo.length || drawInfo[ instanceId ].active === false ) {
1025
+ // Check if we can shrink to the requested vertex attribute size
1026
+ const validRanges = [ ...this._geometryInfo ].filter( info => info.active );
1027
+ const requiredVertexLength = Math.max( ...validRanges.map( range => range.vertexStart + range.reservedVertexCount ) );
1028
+ if ( requiredVertexLength > maxVertexCount ) {
875
1029
 
876
- return - 1;
1030
+ throw new Error( `BatchedMesh: Geometry vertex values are being used outside the range ${ maxIndexCount }. Cannot shrink further.` );
877
1031
 
878
1032
  }
879
1033
 
880
- return drawInfo[ instanceId ].geometryIndex;
1034
+ // Check if we can shrink to the requested index attribute size
1035
+ if ( this.geometry.index ) {
881
1036
 
882
- }
1037
+ const requiredIndexLength = Math.max( ...validRanges.map( range => range.indexStart + range.reservedIndexCount ) );
1038
+ if ( requiredIndexLength > maxIndexCount ) {
883
1039
 
884
- getGeometryRangeAt( geometryId, target = {} ) {
1040
+ throw new Error( `BatchedMesh: Geometry index values are being used outside the range ${ maxIndexCount }. Cannot shrink further.` );
885
1041
 
886
- if ( geometryId < 0 || geometryId >= this._geometryCount ) {
1042
+ }
887
1043
 
888
- return null;
1044
+ }
1045
+
1046
+ //
1047
+
1048
+ // dispose of the previous geometry
1049
+ const oldGeometry = this.geometry;
1050
+ oldGeometry.dispose();
1051
+
1052
+ // recreate the geometry needed based on the previous variant
1053
+ this._maxVertexCount = maxVertexCount;
1054
+ this._maxIndexCount = maxIndexCount;
1055
+
1056
+ if ( this._geometryInitialized ) {
1057
+
1058
+ this._geometryInitialized = false;
1059
+ this.geometry = new BufferGeometry();
1060
+ this._initializeGeometry( oldGeometry );
889
1061
 
890
1062
  }
891
1063
 
892
- const drawRange = this._drawRanges[ geometryId ];
1064
+ // copy data from the previous geometry
1065
+ const geometry = this.geometry;
1066
+ if ( oldGeometry.index ) {
893
1067
 
894
- target.start = drawRange.start;
895
- target.count = drawRange.count;
1068
+ copyArrayContents( oldGeometry.index.array, geometry.index.array );
896
1069
 
897
- return target;
1070
+ }
1071
+
1072
+ for ( const key in oldGeometry.attributes ) {
1073
+
1074
+ copyArrayContents( oldGeometry.attributes[ key ].array, geometry.attributes[ key ].array );
1075
+
1076
+ }
898
1077
 
899
1078
  }
900
1079
 
901
1080
  raycast( raycaster, intersects ) {
902
1081
 
903
- const drawInfo = this._drawInfo;
904
- const drawRanges = this._drawRanges;
1082
+ const instanceInfo = this._instanceInfo;
1083
+ const geometryInfoList = this._geometryInfo;
905
1084
  const matrixWorld = this.matrixWorld;
906
1085
  const batchGeometry = this.geometry;
907
1086
 
@@ -921,19 +1100,19 @@ class BatchedMesh extends Mesh {
921
1100
 
922
1101
  }
923
1102
 
924
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
1103
+ for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
925
1104
 
926
- if ( ! drawInfo[ i ].visible || ! drawInfo[ i ].active ) {
1105
+ if ( ! instanceInfo[ i ].visible || ! instanceInfo[ i ].active ) {
927
1106
 
928
1107
  continue;
929
1108
 
930
1109
  }
931
1110
 
932
- const geometryId = drawInfo[ i ].geometryIndex;
933
- const drawRange = drawRanges[ geometryId ];
934
- _mesh.geometry.setDrawRange( drawRange.start, drawRange.count );
1111
+ const geometryId = instanceInfo[ i ].geometryIndex;
1112
+ const geometryInfo = geometryInfoList[ geometryId ];
1113
+ _mesh.geometry.setDrawRange( geometryInfo.start, geometryInfo.count );
935
1114
 
936
- // ge the intersects
1115
+ // get the intersects
937
1116
  this.getMatrixAt( i, _mesh.matrixWorld ).premultiply( matrixWorld );
938
1117
  this.getBoundingBoxAt( geometryId, _mesh.geometry.boundingBox );
939
1118
  this.getBoundingSphereAt( geometryId, _mesh.geometry.boundingSphere );
@@ -970,17 +1149,13 @@ class BatchedMesh extends Mesh {
970
1149
  this.boundingBox = source.boundingBox !== null ? source.boundingBox.clone() : null;
971
1150
  this.boundingSphere = source.boundingSphere !== null ? source.boundingSphere.clone() : null;
972
1151
 
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(),
1152
+ this._geometryInfo = source._geometryInfo.map( info => ( {
1153
+ ...info,
980
1154
 
981
- sphereInitialized: bound.sphereInitialized,
982
- sphere: bound.sphere.clone()
1155
+ boundingBox: info.boundingBox !== null ? info.boundingBox.clone() : null,
1156
+ boundingSphere: info.boundingSphere !== null ? info.boundingSphere.clone() : null,
983
1157
  } ) );
1158
+ this._instanceInfo = source._instanceInfo.map( info => ( { ...info } ) );
984
1159
 
985
1160
  this._maxInstanceCount = source._maxInstanceCount;
986
1161
  this._maxVertexCount = source._maxVertexCount;
@@ -1042,10 +1217,10 @@ class BatchedMesh extends Mesh {
1042
1217
  const index = geometry.getIndex();
1043
1218
  const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT;
1044
1219
 
1045
- const drawInfo = this._drawInfo;
1220
+ const instanceInfo = this._instanceInfo;
1046
1221
  const multiDrawStarts = this._multiDrawStarts;
1047
1222
  const multiDrawCounts = this._multiDrawCounts;
1048
- const drawRanges = this._drawRanges;
1223
+ const geometryInfoList = this._geometryInfo;
1049
1224
  const perObjectFrustumCulled = this.perObjectFrustumCulled;
1050
1225
  const indirectTexture = this._indirectTexture;
1051
1226
  const indirectArray = indirectTexture.image.data;
@@ -1053,29 +1228,29 @@ class BatchedMesh extends Mesh {
1053
1228
  // prepare the frustum in the local frame
1054
1229
  if ( perObjectFrustumCulled ) {
1055
1230
 
1056
- _projScreenMatrix
1231
+ _matrix
1057
1232
  .multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse )
1058
1233
  .multiply( this.matrixWorld );
1059
1234
  _frustum.setFromProjectionMatrix(
1060
- _projScreenMatrix,
1235
+ _matrix,
1061
1236
  renderer.coordinateSystem
1062
1237
  );
1063
1238
 
1064
1239
  }
1065
1240
 
1066
- let count = 0;
1241
+ let multiDrawCount = 0;
1067
1242
  if ( this.sortObjects ) {
1068
1243
 
1069
1244
  // 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 );
1245
+ _matrix.copy( this.matrixWorld ).invert();
1246
+ _vector.setFromMatrixPosition( camera.matrixWorld ).applyMatrix4( _matrix );
1247
+ _forward.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ).transformDirection( _matrix );
1073
1248
 
1074
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
1249
+ for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
1075
1250
 
1076
- if ( drawInfo[ i ].visible && drawInfo[ i ].active ) {
1251
+ if ( instanceInfo[ i ].visible && instanceInfo[ i ].active ) {
1077
1252
 
1078
- const geometryId = drawInfo[ i ].geometryIndex;
1253
+ const geometryId = instanceInfo[ i ].geometryIndex;
1079
1254
 
1080
1255
  // get the bounds in world space
1081
1256
  this.getMatrixAt( i, _matrix );
@@ -1092,8 +1267,9 @@ class BatchedMesh extends Mesh {
1092
1267
  if ( ! culled ) {
1093
1268
 
1094
1269
  // get the distance from camera used for sorting
1270
+ const geometryInfo = geometryInfoList[ geometryId ];
1095
1271
  const z = _temp.subVectors( _sphere.center, _vector ).dot( _forward );
1096
- _renderList.push( drawRanges[ geometryId ], z, i );
1272
+ _renderList.push( geometryInfo.start, geometryInfo.count, z, i );
1097
1273
 
1098
1274
  }
1099
1275
 
@@ -1117,10 +1293,10 @@ class BatchedMesh extends Mesh {
1117
1293
  for ( let i = 0, l = list.length; i < l; i ++ ) {
1118
1294
 
1119
1295
  const item = list[ i ];
1120
- multiDrawStarts[ count ] = item.start * bytesPerElement;
1121
- multiDrawCounts[ count ] = item.count;
1122
- indirectArray[ count ] = item.index;
1123
- count ++;
1296
+ multiDrawStarts[ multiDrawCount ] = item.start * bytesPerElement;
1297
+ multiDrawCounts[ multiDrawCount ] = item.count;
1298
+ indirectArray[ multiDrawCount ] = item.index;
1299
+ multiDrawCount ++;
1124
1300
 
1125
1301
  }
1126
1302
 
@@ -1128,11 +1304,11 @@ class BatchedMesh extends Mesh {
1128
1304
 
1129
1305
  } else {
1130
1306
 
1131
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
1307
+ for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
1132
1308
 
1133
- if ( drawInfo[ i ].visible && drawInfo[ i ].active ) {
1309
+ if ( instanceInfo[ i ].visible && instanceInfo[ i ].active ) {
1134
1310
 
1135
- const geometryId = drawInfo[ i ].geometryIndex;
1311
+ const geometryId = instanceInfo[ i ].geometryIndex;
1136
1312
 
1137
1313
  // determine whether the batched geometry is within the frustum
1138
1314
  let culled = false;
@@ -1147,11 +1323,11 @@ class BatchedMesh extends Mesh {
1147
1323
 
1148
1324
  if ( ! culled ) {
1149
1325
 
1150
- const range = drawRanges[ geometryId ];
1151
- multiDrawStarts[ count ] = range.start * bytesPerElement;
1152
- multiDrawCounts[ count ] = range.count;
1153
- indirectArray[ count ] = i;
1154
- count ++;
1326
+ const geometryInfo = geometryInfoList[ geometryId ];
1327
+ multiDrawStarts[ multiDrawCount ] = geometryInfo.start * bytesPerElement;
1328
+ multiDrawCounts[ multiDrawCount ] = geometryInfo.count;
1329
+ indirectArray[ multiDrawCount ] = i;
1330
+ multiDrawCount ++;
1155
1331
 
1156
1332
  }
1157
1333
 
@@ -1162,7 +1338,7 @@ class BatchedMesh extends Mesh {
1162
1338
  }
1163
1339
 
1164
1340
  indirectTexture.needsUpdate = true;
1165
- this._multiDrawCount = count;
1341
+ this._multiDrawCount = multiDrawCount;
1166
1342
  this._visibilityChanged = false;
1167
1343
 
1168
1344
  }