@plastic-software/three 0.181.3 → 0.182.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 (253) hide show
  1. package/README.md +3 -4
  2. package/build/three.cjs +1192 -522
  3. package/build/three.core.js +345 -219
  4. package/build/three.core.min.js +1 -1
  5. package/build/three.module.js +864 -328
  6. package/build/three.module.min.js +1 -1
  7. package/build/three.tsl.js +15 -3
  8. package/build/three.tsl.min.js +1 -1
  9. package/build/three.webgpu.js +3660 -1545
  10. package/build/three.webgpu.min.js +1 -1
  11. package/build/three.webgpu.nodes.js +3659 -1544
  12. package/build/three.webgpu.nodes.min.js +1 -1
  13. package/examples/jsm/controls/MapControls.js +55 -1
  14. package/examples/jsm/controls/OrbitControls.js +6 -6
  15. package/examples/jsm/controls/TrackballControls.js +6 -6
  16. package/examples/jsm/csm/CSM.js +2 -1
  17. package/examples/jsm/environments/RoomEnvironment.js +2 -0
  18. package/examples/jsm/geometries/DecalGeometry.js +1 -1
  19. package/examples/jsm/helpers/LightProbeHelperGPU.js +1 -1
  20. package/examples/jsm/helpers/TextureHelperGPU.js +1 -1
  21. package/examples/jsm/inspector/Inspector.js +53 -9
  22. package/examples/jsm/inspector/RendererInspector.js +12 -2
  23. package/examples/jsm/inspector/tabs/Console.js +2 -2
  24. package/examples/jsm/inspector/tabs/Parameters.js +2 -2
  25. package/examples/jsm/inspector/tabs/Performance.js +2 -2
  26. package/examples/jsm/inspector/tabs/Viewer.js +4 -4
  27. package/examples/jsm/inspector/ui/Profiler.js +1836 -31
  28. package/examples/jsm/inspector/ui/Style.js +948 -13
  29. package/examples/jsm/inspector/ui/Tab.js +188 -1
  30. package/examples/jsm/inspector/ui/Values.js +17 -1
  31. package/examples/jsm/loaders/3DMLoader.js +5 -4
  32. package/examples/jsm/loaders/DRACOLoader.js +5 -5
  33. package/examples/jsm/loaders/FBXLoader.js +0 -2
  34. package/examples/jsm/loaders/HDRLoader.js +0 -1
  35. package/examples/jsm/loaders/KTX2Loader.js +16 -0
  36. package/examples/jsm/loaders/LDrawLoader.js +2 -3
  37. package/examples/jsm/loaders/PCDLoader.js +1 -0
  38. package/examples/jsm/loaders/SVGLoader.js +1 -1
  39. package/examples/jsm/loaders/TDSLoader.js +0 -2
  40. package/examples/jsm/loaders/TGALoader.js +0 -2
  41. package/examples/jsm/loaders/UltraHDRLoader.js +110 -137
  42. package/examples/jsm/loaders/VOXLoader.js +660 -117
  43. package/examples/jsm/loaders/VRMLLoader.js +2 -2
  44. package/examples/jsm/loaders/usd/USDCParser.js +1 -1
  45. package/examples/jsm/materials/LDrawConditionalLineNodeMaterial.js +1 -1
  46. package/examples/jsm/materials/MeshGouraudMaterial.js +0 -1
  47. package/examples/jsm/materials/WoodNodeMaterial.js +11 -11
  48. package/examples/jsm/math/Octree.js +131 -1
  49. package/examples/jsm/misc/Volume.js +0 -1
  50. package/examples/jsm/misc/VolumeSlice.js +0 -1
  51. package/examples/jsm/objects/SkyMesh.js +13 -3
  52. package/examples/jsm/physics/AmmoPhysics.js +12 -7
  53. package/examples/jsm/physics/JoltPhysics.js +3 -1
  54. package/examples/jsm/physics/RapierPhysics.js +3 -1
  55. package/examples/jsm/postprocessing/OutputPass.js +9 -0
  56. package/examples/jsm/postprocessing/RenderPass.js +10 -0
  57. package/examples/jsm/postprocessing/UnrealBloomPass.js +48 -18
  58. package/examples/jsm/renderers/Projector.js +268 -30
  59. package/examples/jsm/renderers/SVGRenderer.js +191 -58
  60. package/examples/jsm/shaders/UnpackDepthRGBAShader.js +2 -4
  61. package/examples/jsm/transpiler/AST.js +44 -0
  62. package/examples/jsm/transpiler/GLSLDecoder.js +61 -4
  63. package/examples/jsm/transpiler/ShaderToyDecoder.js +2 -0
  64. package/examples/jsm/transpiler/TSLEncoder.js +46 -3
  65. package/examples/jsm/transpiler/TranspilerUtils.js +3 -3
  66. package/examples/jsm/transpiler/WGSLEncoder.js +27 -0
  67. package/examples/jsm/tsl/display/AnaglyphPassNode.js +2 -0
  68. package/examples/jsm/tsl/display/BloomNode.js +11 -1
  69. package/examples/jsm/tsl/display/GTAONode.js +3 -2
  70. package/examples/jsm/tsl/display/PixelationPassNode.js +2 -1
  71. package/examples/jsm/tsl/display/SSGINode.js +7 -19
  72. package/examples/jsm/tsl/display/SSRNode.js +1 -1
  73. package/examples/jsm/tsl/display/SSSNode.js +4 -2
  74. package/examples/jsm/tsl/display/StereoCompositePassNode.js +8 -1
  75. package/examples/jsm/tsl/display/TRAANode.js +265 -114
  76. package/examples/jsm/tsl/display/radialBlur.js +68 -0
  77. package/examples/jsm/utils/ShadowMapViewer.js +24 -10
  78. package/examples/jsm/utils/ShadowMapViewerGPU.js +1 -1
  79. package/examples/jsm/utils/WebGPUTextureUtils.js +1 -1
  80. package/package.json +14 -12
  81. package/src/Three.Core.js +1 -0
  82. package/src/Three.TSL.js +14 -2
  83. package/src/animation/AnimationUtils.js +1 -12
  84. package/src/animation/KeyframeTrack.js +1 -1
  85. package/src/animation/tracks/BooleanKeyframeTrack.js +1 -1
  86. package/src/animation/tracks/ColorKeyframeTrack.js +1 -1
  87. package/src/animation/tracks/NumberKeyframeTrack.js +1 -1
  88. package/src/animation/tracks/QuaternionKeyframeTrack.js +1 -1
  89. package/src/animation/tracks/StringKeyframeTrack.js +1 -1
  90. package/src/animation/tracks/VectorKeyframeTrack.js +1 -1
  91. package/src/constants.js +61 -5
  92. package/src/core/BufferGeometry.js +14 -2
  93. package/src/core/Raycaster.js +2 -2
  94. package/src/extras/PMREMGenerator.js +3 -10
  95. package/src/extras/TextureUtils.js +5 -1
  96. package/src/geometries/ExtrudeGeometry.js +2 -2
  97. package/src/geometries/PolyhedronGeometry.js +1 -1
  98. package/src/helpers/PointLightHelper.js +1 -1
  99. package/src/lights/DirectionalLight.js +13 -0
  100. package/src/lights/HemisphereLight.js +10 -0
  101. package/src/lights/Light.js +1 -11
  102. package/src/lights/LightProbe.js +0 -15
  103. package/src/lights/LightShadow.js +0 -3
  104. package/src/lights/PointLight.js +15 -0
  105. package/src/lights/PointLightShadow.js +0 -86
  106. package/src/lights/SpotLight.js +22 -1
  107. package/src/loaders/MaterialLoader.js +2 -1
  108. package/src/loaders/ObjectLoader.js +3 -1
  109. package/src/loaders/nodes/NodeLoader.js +2 -2
  110. package/src/materials/Material.js +2 -0
  111. package/src/materials/ShaderMaterial.js +20 -1
  112. package/src/materials/nodes/Line2NodeMaterial.js +2 -2
  113. package/src/materials/nodes/MeshPhysicalNodeMaterial.js +3 -2
  114. package/src/materials/nodes/MeshStandardNodeMaterial.js +5 -4
  115. package/src/materials/nodes/NodeMaterial.js +59 -3
  116. package/src/materials/nodes/manager/NodeMaterialObserver.js +1 -1
  117. package/src/math/Matrix4.js +40 -40
  118. package/src/math/Sphere.js +1 -1
  119. package/src/math/Vector3.js +0 -2
  120. package/src/nodes/TSL.js +4 -1
  121. package/src/nodes/accessors/BatchNode.js +10 -10
  122. package/src/nodes/accessors/BufferAttributeNode.js +98 -12
  123. package/src/nodes/accessors/BufferNode.js +29 -2
  124. package/src/nodes/accessors/ClippingNode.js +4 -4
  125. package/src/nodes/accessors/CubeTextureNode.js +20 -1
  126. package/src/nodes/accessors/InstanceNode.js +69 -29
  127. package/src/nodes/accessors/MaterialNode.js +9 -1
  128. package/src/nodes/accessors/MaterialReferenceNode.js +1 -2
  129. package/src/nodes/accessors/ModelNode.js +1 -1
  130. package/src/nodes/accessors/Normal.js +2 -2
  131. package/src/nodes/accessors/ReferenceBaseNode.js +4 -4
  132. package/src/nodes/accessors/ReferenceNode.js +4 -4
  133. package/src/nodes/accessors/RendererReferenceNode.js +1 -2
  134. package/src/nodes/accessors/SkinningNode.js +15 -2
  135. package/src/nodes/accessors/StorageBufferNode.js +4 -2
  136. package/src/nodes/accessors/Tangent.js +1 -11
  137. package/src/nodes/accessors/Texture3DNode.js +26 -1
  138. package/src/nodes/accessors/UniformArrayNode.js +2 -2
  139. package/src/nodes/accessors/UserDataNode.js +1 -2
  140. package/src/nodes/accessors/VertexColorNode.js +1 -2
  141. package/src/nodes/code/FunctionNode.js +1 -2
  142. package/src/nodes/core/ArrayNode.js +20 -1
  143. package/src/nodes/core/AssignNode.js +2 -2
  144. package/src/nodes/core/AttributeNode.js +2 -2
  145. package/src/nodes/core/ContextNode.js +103 -4
  146. package/src/nodes/core/NodeBuilder.js +56 -14
  147. package/src/nodes/core/NodeFrame.js +12 -4
  148. package/src/nodes/core/NodeUtils.js +5 -5
  149. package/src/nodes/core/ParameterNode.js +1 -2
  150. package/src/nodes/core/PropertyNode.js +19 -3
  151. package/src/nodes/core/StackNode.js +56 -8
  152. package/src/nodes/core/StructNode.js +1 -2
  153. package/src/nodes/core/StructTypeNode.js +11 -17
  154. package/src/nodes/core/UniformNode.js +19 -4
  155. package/src/nodes/core/VarNode.js +46 -21
  156. package/src/nodes/display/NormalMapNode.js +37 -2
  157. package/src/nodes/display/PassNode.js +77 -7
  158. package/src/nodes/display/ScreenNode.js +1 -0
  159. package/src/nodes/functions/BSDF/BRDF_GGX_Multiscatter.js +3 -3
  160. package/src/nodes/functions/BSDF/DFGLUT.js +56 -0
  161. package/src/nodes/functions/BSDF/EnvironmentBRDF.js +2 -2
  162. package/src/nodes/functions/BSDF/V_GGX_SmithCorrelated_Anisotropic.js +1 -1
  163. package/src/nodes/functions/PhysicalLightingModel.js +102 -43
  164. package/src/nodes/gpgpu/ComputeBuiltinNode.js +1 -2
  165. package/src/nodes/gpgpu/SubgroupFunctionNode.js +1 -1
  166. package/src/nodes/gpgpu/WorkgroupInfoNode.js +2 -3
  167. package/src/nodes/lighting/AnalyticLightNode.js +53 -0
  168. package/src/nodes/lighting/LightsNode.js +2 -2
  169. package/src/nodes/lighting/PointShadowNode.js +141 -140
  170. package/src/nodes/lighting/ShadowFilterNode.js +53 -37
  171. package/src/nodes/lighting/ShadowNode.js +53 -19
  172. package/src/nodes/math/BitcountNode.js +433 -0
  173. package/src/nodes/math/PackFloatNode.js +98 -0
  174. package/src/nodes/math/UnpackFloatNode.js +96 -0
  175. package/src/nodes/pmrem/PMREMNode.js +1 -1
  176. package/src/nodes/tsl/TSLCore.js +4 -4
  177. package/src/nodes/utils/ArrayElementNode.js +13 -0
  178. package/src/nodes/utils/EventNode.js +1 -2
  179. package/src/nodes/utils/Packing.js +13 -1
  180. package/src/nodes/utils/PostProcessingUtils.js +33 -1
  181. package/src/nodes/utils/ReflectorNode.js +1 -1
  182. package/src/nodes/utils/SampleNode.js +1 -1
  183. package/src/nodes/utils/UVUtils.js +26 -0
  184. package/src/objects/BatchedMesh.js +5 -2
  185. package/src/objects/Line.js +1 -1
  186. package/src/objects/Mesh.js +1 -1
  187. package/src/objects/Points.js +1 -1
  188. package/src/objects/Skeleton.js +9 -0
  189. package/src/renderers/WebGLRenderer.js +145 -33
  190. package/src/renderers/common/Backend.js +8 -0
  191. package/src/renderers/common/Background.js +19 -9
  192. package/src/renderers/common/Binding.js +11 -0
  193. package/src/renderers/common/Bindings.js +7 -7
  194. package/src/renderers/common/Buffer.js +40 -0
  195. package/src/renderers/common/ChainMap.js +30 -6
  196. package/src/renderers/common/Geometries.js +12 -0
  197. package/src/renderers/common/RenderContexts.js +8 -1
  198. package/src/renderers/common/RenderObject.js +14 -1
  199. package/src/renderers/common/Renderer.js +53 -35
  200. package/src/renderers/common/Textures.js +1 -1
  201. package/src/renderers/common/UniformsGroup.js +1 -0
  202. package/src/renderers/common/XRManager.js +1 -0
  203. package/src/renderers/common/extras/PMREMGenerator.js +2 -8
  204. package/src/renderers/common/nodes/NodeUniformBuffer.js +52 -0
  205. package/src/renderers/shaders/DFGLUTData.js +19 -34
  206. package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +5 -2
  207. package/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +8 -4
  208. package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +90 -51
  209. package/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js +194 -186
  210. package/src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl.js +1 -1
  211. package/src/renderers/shaders/ShaderChunk/transmission_fragment.glsl.js +1 -1
  212. package/src/renderers/shaders/ShaderChunk.js +3 -3
  213. package/src/renderers/shaders/ShaderLib/depth.glsl.js +3 -0
  214. package/src/renderers/shaders/ShaderLib/{distanceRGBA.glsl.js → distance.glsl.js} +1 -2
  215. package/src/renderers/shaders/ShaderLib/meshlambert.glsl.js +0 -1
  216. package/src/renderers/shaders/ShaderLib/meshnormal.glsl.js +1 -2
  217. package/src/renderers/shaders/ShaderLib/meshphong.glsl.js +0 -1
  218. package/src/renderers/shaders/ShaderLib/meshphysical.glsl.js +4 -9
  219. package/src/renderers/shaders/ShaderLib/meshtoon.glsl.js +0 -1
  220. package/src/renderers/shaders/ShaderLib/shadow.glsl.js +0 -1
  221. package/src/renderers/shaders/ShaderLib/vsm.glsl.js +4 -6
  222. package/src/renderers/shaders/ShaderLib.js +3 -3
  223. package/src/renderers/webgl/WebGLCapabilities.js +3 -4
  224. package/src/renderers/webgl/WebGLLights.js +18 -1
  225. package/src/renderers/webgl/WebGLOutput.js +267 -0
  226. package/src/renderers/webgl/WebGLProgram.js +43 -107
  227. package/src/renderers/webgl/WebGLPrograms.js +35 -45
  228. package/src/renderers/webgl/WebGLShadowMap.js +188 -25
  229. package/src/renderers/webgl/WebGLState.js +20 -20
  230. package/src/renderers/webgl/WebGLTextures.js +89 -28
  231. package/src/renderers/webgl/WebGLUniforms.js +40 -3
  232. package/src/renderers/webgl/WebGLUtils.js +6 -2
  233. package/src/renderers/webgl-fallback/WebGLBackend.js +79 -13
  234. package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +59 -7
  235. package/src/renderers/webgl-fallback/utils/WebGLState.js +18 -3
  236. package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +5 -3
  237. package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +9 -9
  238. package/src/renderers/webgl-fallback/utils/WebGLUtils.js +6 -2
  239. package/src/renderers/webgpu/WebGPUBackend.js +61 -4
  240. package/src/renderers/webgpu/WebGPURenderer.js +1 -1
  241. package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +65 -23
  242. package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +4 -17
  243. package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +354 -186
  244. package/src/renderers/webgpu/utils/WebGPUConstants.js +2 -0
  245. package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +20 -7
  246. package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +40 -17
  247. package/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +7 -7
  248. package/src/renderers/webgpu/utils/WebGPUUtils.js +7 -5
  249. package/src/textures/CubeDepthTexture.js +76 -0
  250. package/src/textures/Source.js +1 -1
  251. package/src/textures/Texture.js +1 -1
  252. package/src/utils.js +13 -1
  253. package/src/nodes/functions/BSDF/DFGApprox.js +0 -71
@@ -139,7 +139,7 @@ class Projector {
139
139
  _face, _faceCount, _facePoolLength = 0,
140
140
  _line, _lineCount, _linePoolLength = 0,
141
141
  _sprite, _spriteCount, _spritePoolLength = 0,
142
- _modelMatrix;
142
+ _modelMatrix, _clipInput = [], _clipOutput = [];
143
143
 
144
144
  const
145
145
 
@@ -159,7 +159,19 @@ class Projector {
159
159
 
160
160
  _frustum = new Frustum(),
161
161
 
162
- _objectPool = [], _vertexPool = [], _facePool = [], _linePool = [], _spritePool = [];
162
+ _objectPool = [], _vertexPool = [], _facePool = [], _linePool = [], _spritePool = [],
163
+
164
+ _clipVertexPool = [],
165
+ _clipPos1 = new Vector4(),
166
+ _clipPos2 = new Vector4(),
167
+ _clipPos3 = new Vector4(),
168
+ _screenVertexPool = [],
169
+ _clipInputVertices = [ null, null, null ],
170
+
171
+ _clipPlanes = [
172
+ { sign: + 1 },
173
+ { sign: - 1 }
174
+ ];
163
175
 
164
176
  //
165
177
 
@@ -298,48 +310,165 @@ class Projector {
298
310
  const v2 = _vertexPool[ b ];
299
311
  const v3 = _vertexPool[ c ];
300
312
 
301
- if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return;
313
+ // Get homogeneous clip space positions (before perspective divide)
314
+ _clipPos1.copy( v1.positionWorld ).applyMatrix4( _viewProjectionMatrix );
315
+ _clipPos2.copy( v2.positionWorld ).applyMatrix4( _viewProjectionMatrix );
316
+ _clipPos3.copy( v3.positionWorld ).applyMatrix4( _viewProjectionMatrix );
317
+
318
+ // Check if triangle needs clipping
319
+ const nearDist1 = _clipPos1.z + _clipPos1.w;
320
+ const nearDist2 = _clipPos2.z + _clipPos2.w;
321
+ const nearDist3 = _clipPos3.z + _clipPos3.w;
322
+ const farDist1 = - _clipPos1.z + _clipPos1.w;
323
+ const farDist2 = - _clipPos2.z + _clipPos2.w;
324
+ const farDist3 = - _clipPos3.z + _clipPos3.w;
325
+
326
+ // Check if completely outside
327
+ if ( ( nearDist1 < 0 && nearDist2 < 0 && nearDist3 < 0 ) ||
328
+ ( farDist1 < 0 && farDist2 < 0 && farDist3 < 0 ) ) {
329
+
330
+ return; // Triangle completely clipped
331
+
332
+ }
333
+
334
+ // Check if completely inside (no clipping needed)
335
+ if ( nearDist1 >= 0 && nearDist2 >= 0 && nearDist3 >= 0 &&
336
+ farDist1 >= 0 && farDist2 >= 0 && farDist3 >= 0 ) {
337
+
338
+ // No clipping needed - use original path
339
+ if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return;
340
+
341
+ if ( material.side === DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) {
342
+
343
+ _face = getNextFaceInPool();
344
+
345
+ _face.id = object.id;
346
+ _face.v1.copy( v1 );
347
+ _face.v2.copy( v2 );
348
+ _face.v3.copy( v3 );
349
+ _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3;
350
+ _face.renderOrder = object.renderOrder;
302
351
 
303
- if ( material.side === DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) {
352
+ // face normal
353
+ _vector3.subVectors( v3.position, v2.position );
354
+ _vector4.subVectors( v1.position, v2.position );
355
+ _vector3.cross( _vector4 );
356
+ _face.normalModel.copy( _vector3 );
357
+ _face.normalModel.applyMatrix3( normalMatrix ).normalize();
304
358
 
305
- _face = getNextFaceInPool();
359
+ for ( let i = 0; i < 3; i ++ ) {
306
360
 
307
- _face.id = object.id;
308
- _face.v1.copy( v1 );
309
- _face.v2.copy( v2 );
310
- _face.v3.copy( v3 );
311
- _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3;
312
- _face.renderOrder = object.renderOrder;
361
+ const normal = _face.vertexNormalsModel[ i ];
362
+ normal.fromArray( normals, arguments[ i ] * 3 );
363
+ normal.applyMatrix3( normalMatrix ).normalize();
313
364
 
314
- // face normal
315
- _vector3.subVectors( v3.position, v2.position );
316
- _vector4.subVectors( v1.position, v2.position );
317
- _vector3.cross( _vector4 );
318
- _face.normalModel.copy( _vector3 );
319
- _face.normalModel.applyMatrix3( normalMatrix ).normalize();
365
+ const uv = _face.uvs[ i ];
366
+ uv.fromArray( uvs, arguments[ i ] * 2 );
320
367
 
321
- for ( let i = 0; i < 3; i ++ ) {
368
+ }
369
+
370
+ _face.vertexNormalsLength = 3;
371
+
372
+ _face.material = material;
322
373
 
323
- const normal = _face.vertexNormalsModel[ i ];
324
- normal.fromArray( normals, arguments[ i ] * 3 );
325
- normal.applyMatrix3( normalMatrix ).normalize();
374
+ if ( material.vertexColors ) {
375
+
376
+ _face.color.fromArray( colors, a * 3 );
377
+
378
+ }
326
379
 
327
- const uv = _face.uvs[ i ];
328
- uv.fromArray( uvs, arguments[ i ] * 2 );
380
+ _renderData.elements.push( _face );
329
381
 
330
382
  }
331
383
 
332
- _face.vertexNormalsLength = 3;
384
+ return;
333
385
 
334
- _face.material = material;
386
+ }
387
+
388
+ // Triangle needs clipping
389
+ _clipInputVertices[ 0 ] = _clipPos1;
390
+ _clipInputVertices[ 1 ] = _clipPos2;
391
+ _clipInputVertices[ 2 ] = _clipPos3;
392
+ const clippedCount = clipTriangle( _clipInputVertices );
393
+
394
+ if ( clippedCount < 3 ) return; // Triangle completely clipped
335
395
 
336
- if ( material.vertexColors ) {
396
+ // Perform perspective divide on clipped vertices and create screen vertices
397
+ for ( let i = 0; i < clippedCount; i ++ ) {
337
398
 
338
- _face.color.fromArray( colors, a * 3 );
399
+ const cv = _clipInput[ i ];
400
+
401
+ // Get or create renderable vertex from pool
402
+ let sv = _screenVertexPool[ i ];
403
+ if ( ! sv ) {
404
+
405
+ sv = new RenderableVertex();
406
+ _screenVertexPool[ i ] = sv;
339
407
 
340
408
  }
341
409
 
342
- _renderData.elements.push( _face );
410
+ // Perform perspective divide
411
+ const invW = 1 / cv.w;
412
+ sv.positionScreen.set( cv.x * invW, cv.y * invW, cv.z * invW, 1 );
413
+
414
+ // Interpolate world position (simplified - using weighted average based on barycentric-like coords)
415
+ // For a proper implementation, we'd need to track interpolation weights
416
+ sv.positionWorld.copy( v1.positionWorld );
417
+
418
+ sv.visible = true;
419
+
420
+ }
421
+
422
+ // Triangulate the clipped polygon (simple fan triangulation)
423
+ for ( let i = 1; i < clippedCount - 1; i ++ ) {
424
+
425
+ const tv1 = _screenVertexPool[ 0 ];
426
+ const tv2 = _screenVertexPool[ i ];
427
+ const tv3 = _screenVertexPool[ i + 1 ];
428
+
429
+ if ( material.side === DoubleSide || checkBackfaceCulling( tv1, tv2, tv3 ) === true ) {
430
+
431
+ _face = getNextFaceInPool();
432
+
433
+ _face.id = object.id;
434
+ _face.v1.copy( tv1 );
435
+ _face.v2.copy( tv2 );
436
+ _face.v3.copy( tv3 );
437
+ _face.z = ( tv1.positionScreen.z + tv2.positionScreen.z + tv3.positionScreen.z ) / 3;
438
+ _face.renderOrder = object.renderOrder;
439
+
440
+ // face normal - use original triangle's normal
441
+ _vector3.subVectors( v3.position, v2.position );
442
+ _vector4.subVectors( v1.position, v2.position );
443
+ _vector3.cross( _vector4 );
444
+ _face.normalModel.copy( _vector3 );
445
+ _face.normalModel.applyMatrix3( normalMatrix ).normalize();
446
+
447
+ // Use original vertex normals and UVs (simplified - proper impl would interpolate)
448
+ for ( let j = 0; j < 3; j ++ ) {
449
+
450
+ const normal = _face.vertexNormalsModel[ j ];
451
+ normal.fromArray( normals, arguments[ j ] * 3 );
452
+ normal.applyMatrix3( normalMatrix ).normalize();
453
+
454
+ const uv = _face.uvs[ j ];
455
+ uv.fromArray( uvs, arguments[ j ] * 2 );
456
+
457
+ }
458
+
459
+ _face.vertexNormalsLength = 3;
460
+
461
+ _face.material = material;
462
+
463
+ if ( material.vertexColors ) {
464
+
465
+ _face.color.fromArray( colors, a * 3 );
466
+
467
+ }
468
+
469
+ _renderData.elements.push( _face );
470
+
471
+ }
343
472
 
344
473
  }
345
474
 
@@ -448,7 +577,7 @@ class Projector {
448
577
 
449
578
  if ( sortObjects === true ) {
450
579
 
451
- _renderData.objects.sort( painterSort );
580
+ painterSortStable( _renderData.objects, 0, _renderData.objects.length );
452
581
 
453
582
  }
454
583
 
@@ -714,7 +843,7 @@ class Projector {
714
843
 
715
844
  if ( sortElements === true ) {
716
845
 
717
- _renderData.elements.sort( painterSort );
846
+ painterSortStable( _renderData.elements, 0, _renderData.elements.length );
718
847
 
719
848
  }
720
849
 
@@ -858,6 +987,115 @@ class Projector {
858
987
 
859
988
  }
860
989
 
990
+ function painterSortStable( array, start, length ) {
991
+
992
+ // A stable insertion sort for sorting render items
993
+ // This avoids the GC overhead of Array.prototype.sort()
994
+
995
+ for ( let i = start + 1; i < start + length; i ++ ) {
996
+
997
+ const item = array[ i ];
998
+ let j = i - 1;
999
+
1000
+ while ( j >= start && painterSort( array[ j ], item ) > 0 ) {
1001
+
1002
+ array[ j + 1 ] = array[ j ];
1003
+ j --;
1004
+
1005
+ }
1006
+
1007
+ array[ j + 1 ] = item;
1008
+
1009
+ }
1010
+
1011
+ }
1012
+
1013
+ // Sutherland-Hodgman triangle clipping in homogeneous clip space
1014
+ // Returns count of vertices in clipped polygon (0 if completely clipped, 3+ if partially clipped)
1015
+ // Result vertices are in _clipInput array
1016
+ function clipTriangle( vertices ) {
1017
+
1018
+ // Initialize input with the three input vertices
1019
+ _clipInput[ 0 ] = vertices[ 0 ];
1020
+ _clipInput[ 1 ] = vertices[ 1 ];
1021
+ _clipInput[ 2 ] = vertices[ 2 ];
1022
+
1023
+ let inputCount = 3;
1024
+ let outputCount = 0;
1025
+
1026
+ for ( let p = 0; p < _clipPlanes.length; p ++ ) {
1027
+
1028
+ const plane = _clipPlanes[ p ];
1029
+ outputCount = 0;
1030
+
1031
+ if ( inputCount === 0 ) break;
1032
+
1033
+ for ( let i = 0; i < inputCount; i ++ ) {
1034
+
1035
+ const v1 = _clipInput[ i ];
1036
+ const v2 = _clipInput[ ( i + 1 ) % inputCount ];
1037
+
1038
+ const d1 = plane.sign * v1.z + v1.w;
1039
+ const d2 = plane.sign * v2.z + v2.w;
1040
+
1041
+ const v1Inside = d1 >= 0;
1042
+ const v2Inside = d2 >= 0;
1043
+
1044
+ if ( v1Inside && v2Inside ) {
1045
+
1046
+ // Both inside - add v1
1047
+ _clipOutput[ outputCount ++ ] = v1;
1048
+
1049
+ } else if ( v1Inside && ! v2Inside ) {
1050
+
1051
+ // v1 inside, v2 outside - add v1 and intersection
1052
+ _clipOutput[ outputCount ++ ] = v1;
1053
+
1054
+ const t = d1 / ( d1 - d2 );
1055
+ let intersection = _clipVertexPool[ outputCount ];
1056
+ if ( ! intersection ) {
1057
+
1058
+ intersection = new Vector4();
1059
+ _clipVertexPool[ outputCount ] = intersection;
1060
+
1061
+ }
1062
+
1063
+ intersection.lerpVectors( v1, v2, t );
1064
+ _clipOutput[ outputCount ++ ] = intersection;
1065
+
1066
+ } else if ( ! v1Inside && v2Inside ) {
1067
+
1068
+ // v1 outside, v2 inside - add intersection only
1069
+ const t = d1 / ( d1 - d2 );
1070
+ let intersection = _clipVertexPool[ outputCount ];
1071
+ if ( ! intersection ) {
1072
+
1073
+ intersection = new Vector4();
1074
+ _clipVertexPool[ outputCount ] = intersection;
1075
+
1076
+ }
1077
+
1078
+ intersection.lerpVectors( v1, v2, t );
1079
+ _clipOutput[ outputCount ++ ] = intersection;
1080
+
1081
+ }
1082
+
1083
+ // Both outside - add nothing
1084
+
1085
+ }
1086
+
1087
+ // Swap input/output
1088
+ const temp = _clipInput;
1089
+ _clipInput = _clipOutput;
1090
+ _clipOutput = temp;
1091
+ inputCount = outputCount;
1092
+
1093
+ }
1094
+
1095
+ return inputCount;
1096
+
1097
+ }
1098
+
861
1099
  function clipLine( s1, s2 ) {
862
1100
 
863
1101
  let alpha1 = 0, alpha2 = 1;
@@ -8,12 +8,13 @@ import {
8
8
  SRGBColorSpace,
9
9
  Vector3
10
10
  } from 'three';
11
+
11
12
  import {
12
13
  Projector,
13
14
  RenderableFace,
14
15
  RenderableLine,
15
16
  RenderableSprite
16
- } from '../renderers/Projector.js';
17
+ } from './Projector.js';
17
18
 
18
19
  /**
19
20
  * Can be used to wrap SVG elements into a 3D object.
@@ -87,6 +88,8 @@ class SVGRenderer {
87
88
 
88
89
  _svgNode,
89
90
  _pathCount = 0,
91
+ _svgObjectCount = 0,
92
+ _renderListCount = 0,
90
93
 
91
94
  _precision = null,
92
95
  _quality = 1,
@@ -113,6 +116,8 @@ class SVGRenderer {
113
116
  _viewProjectionMatrix = new Matrix4(),
114
117
 
115
118
  _svgPathPool = [],
119
+ _svgObjectsPool = [],
120
+ _renderListPool = [],
116
121
 
117
122
  _projector = new Projector(),
118
123
  _svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
@@ -275,6 +280,49 @@ class SVGRenderer {
275
280
 
276
281
  }
277
282
 
283
+ function renderSort( a, b ) {
284
+
285
+ const aOrder = a.data.renderOrder !== undefined ? a.data.renderOrder : 0;
286
+ const bOrder = b.data.renderOrder !== undefined ? b.data.renderOrder : 0;
287
+
288
+ if ( aOrder !== bOrder ) {
289
+
290
+ return aOrder - bOrder;
291
+
292
+ } else {
293
+
294
+ const aZ = a.data.z !== undefined ? a.data.z : 0;
295
+ const bZ = b.data.z !== undefined ? b.data.z : 0;
296
+
297
+ return bZ - aZ; // Painter's algorithm: far to near
298
+
299
+ }
300
+
301
+ }
302
+
303
+ function arraySortStable( array, start, length ) {
304
+
305
+ // A stable insertion sort for sorting the render list
306
+ // This avoids the GC overhead of Array.prototype.sort()
307
+
308
+ for ( let i = start + 1; i < start + length; i ++ ) {
309
+
310
+ const item = array[ i ];
311
+ let j = i - 1;
312
+
313
+ while ( j >= start && renderSort( array[ j ], item ) > 0 ) {
314
+
315
+ array[ j + 1 ] = array[ j ];
316
+ j --;
317
+
318
+ }
319
+
320
+ array[ j + 1 ] = item;
321
+
322
+ }
323
+
324
+ }
325
+
278
326
  /**
279
327
  * Performs a manual clear with the defined clear color.
280
328
  */
@@ -327,10 +375,7 @@ class SVGRenderer {
327
375
 
328
376
  calculateLights( _lights );
329
377
 
330
- // reset accumulated path
331
-
332
- _currentPath = '';
333
- _currentStyle = '';
378
+ _renderListCount = 0;
334
379
 
335
380
  for ( let e = 0, el = _elements.length; e < el; e ++ ) {
336
381
 
@@ -339,88 +384,126 @@ class SVGRenderer {
339
384
 
340
385
  if ( material === undefined || material.opacity === 0 ) continue;
341
386
 
342
- _elemBox.makeEmpty();
387
+ getRenderItem( _renderListCount ++, 'element', element, material );
343
388
 
344
- if ( element instanceof RenderableSprite ) {
389
+ }
345
390
 
346
- _v1 = element;
347
- _v1.x *= _svgWidthHalf; _v1.y *= - _svgHeightHalf;
391
+ _svgObjectCount = 0;
348
392
 
349
- renderSprite( _v1, element, material );
393
+ scene.traverseVisible( function ( object ) {
350
394
 
351
- } else if ( element instanceof RenderableLine ) {
395
+ if ( object.isSVGObject ) {
352
396
 
353
- _v1 = element.v1; _v2 = element.v2;
397
+ _vector3.setFromMatrixPosition( object.matrixWorld );
398
+ _vector3.applyMatrix4( _viewProjectionMatrix );
354
399
 
355
- _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf;
356
- _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf;
400
+ if ( _vector3.z < - 1 || _vector3.z > 1 ) return;
357
401
 
358
- _elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] );
402
+ const x = _vector3.x * _svgWidthHalf;
403
+ const y = - _vector3.y * _svgHeightHalf;
359
404
 
360
- if ( _clipBox.intersectsBox( _elemBox ) === true ) {
405
+ const svgObject = getSVGObjectData( _svgObjectCount ++ );
361
406
 
362
- renderLine( _v1, _v2, material );
407
+ svgObject.node = object.node;
408
+ svgObject.x = x;
409
+ svgObject.y = y;
410
+ svgObject.z = _vector3.z;
411
+ svgObject.renderOrder = object.renderOrder;
363
412
 
364
- }
413
+ getRenderItem( _renderListCount ++, 'svgObject', svgObject, null );
365
414
 
366
- } else if ( element instanceof RenderableFace ) {
415
+ }
367
416
 
368
- _v1 = element.v1; _v2 = element.v2; _v3 = element.v3;
417
+ } );
369
418
 
370
- if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue;
371
- if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue;
372
- if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue;
419
+ if ( this.sortElements ) {
373
420
 
374
- _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf;
375
- _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf;
376
- _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf;
421
+ arraySortStable( _renderListPool, 0, _renderListCount );
377
422
 
378
- if ( this.overdraw > 0 ) {
423
+ }
379
424
 
380
- expand( _v1.positionScreen, _v2.positionScreen, this.overdraw );
381
- expand( _v2.positionScreen, _v3.positionScreen, this.overdraw );
382
- expand( _v3.positionScreen, _v1.positionScreen, this.overdraw );
425
+ // Reset accumulated path
426
+ _currentPath = '';
427
+ _currentStyle = '';
383
428
 
384
- }
429
+ // Render in sorted order
430
+ for ( let i = 0; i < _renderListCount; i ++ ) {
385
431
 
386
- _elemBox.setFromPoints( [
387
- _v1.positionScreen,
388
- _v2.positionScreen,
389
- _v3.positionScreen
390
- ] );
432
+ const item = _renderListPool[ i ];
391
433
 
392
- if ( _clipBox.intersectsBox( _elemBox ) === true ) {
434
+ if ( item.type === 'svgObject' ) {
393
435
 
394
- renderFace3( _v1, _v2, _v3, element, material );
436
+ flushPath(); // Flush any accumulated paths before inserting SVG node
395
437
 
396
- }
438
+ const svgObject = item.data;
439
+ const node = svgObject.node;
440
+ node.setAttribute( 'transform', 'translate(' + svgObject.x + ',' + svgObject.y + ')' );
441
+ _svg.appendChild( node );
397
442
 
398
- }
443
+ } else {
399
444
 
400
- }
445
+ const element = item.data;
446
+ const material = item.material;
401
447
 
402
- flushPath(); // just to flush last svg:path
448
+ _elemBox.makeEmpty();
403
449
 
404
- scene.traverseVisible( function ( object ) {
450
+ if ( element instanceof RenderableSprite ) {
405
451
 
406
- if ( object.isSVGObject ) {
452
+ _v1 = element;
453
+ _v1.x *= _svgWidthHalf; _v1.y *= - _svgHeightHalf;
407
454
 
408
- _vector3.setFromMatrixPosition( object.matrixWorld );
409
- _vector3.applyMatrix4( _viewProjectionMatrix );
455
+ renderSprite( _v1, element, material );
410
456
 
411
- if ( _vector3.z < - 1 || _vector3.z > 1 ) return;
457
+ } else if ( element instanceof RenderableLine ) {
412
458
 
413
- const x = _vector3.x * _svgWidthHalf;
414
- const y = - _vector3.y * _svgHeightHalf;
459
+ _v1 = element.v1; _v2 = element.v2;
415
460
 
416
- const node = object.node;
417
- node.setAttribute( 'transform', 'translate(' + x + ',' + y + ')' );
461
+ _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf;
462
+ _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf;
418
463
 
419
- _svg.appendChild( node );
464
+ _elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] );
465
+
466
+ if ( _clipBox.intersectsBox( _elemBox ) === true ) {
467
+
468
+ renderLine( _v1, _v2, material );
469
+
470
+ }
471
+
472
+ } else if ( element instanceof RenderableFace ) {
473
+
474
+ _v1 = element.v1; _v2 = element.v2; _v3 = element.v3;
475
+
476
+ _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf;
477
+ _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf;
478
+ _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf;
479
+
480
+ if ( this.overdraw > 0 ) {
481
+
482
+ expand( _v1.positionScreen, _v2.positionScreen, this.overdraw );
483
+ expand( _v2.positionScreen, _v3.positionScreen, this.overdraw );
484
+ expand( _v3.positionScreen, _v1.positionScreen, this.overdraw );
485
+
486
+ }
487
+
488
+ _elemBox.setFromPoints( [
489
+ _v1.positionScreen,
490
+ _v2.positionScreen,
491
+ _v3.positionScreen
492
+ ] );
493
+
494
+ if ( _clipBox.intersectsBox( _elemBox ) === true ) {
495
+
496
+ renderFace3( _v1, _v2, _v3, element, material );
497
+
498
+ }
499
+
500
+ }
420
501
 
421
502
  }
422
503
 
423
- } );
504
+ }
505
+
506
+ flushPath(); // Flush any remaining paths
424
507
 
425
508
  };
426
509
 
@@ -660,21 +743,71 @@ class SVGRenderer {
660
743
 
661
744
  function getPathNode( id ) {
662
745
 
663
- if ( _svgPathPool[ id ] == null ) {
746
+ let path = _svgPathPool[ id ];
664
747
 
665
- _svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );
748
+ if ( path === undefined ) {
749
+
750
+ path = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );
666
751
 
667
752
  if ( _quality == 0 ) {
668
753
 
669
- _svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed
754
+ path.setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed
670
755
 
671
756
  }
672
757
 
673
- return _svgPathPool[ id ];
758
+ _svgPathPool[ id ] = path;
759
+
760
+ }
761
+
762
+ return path;
763
+
764
+ }
765
+
766
+ function getSVGObjectData( id ) {
767
+
768
+ let svgObject = _svgObjectsPool[ id ];
769
+
770
+ if ( svgObject === undefined ) {
771
+
772
+ svgObject = {
773
+ node: null,
774
+ x: 0,
775
+ y: 0,
776
+ z: 0,
777
+ renderOrder: 0
778
+ };
779
+
780
+ _svgObjectsPool[ id ] = svgObject;
674
781
 
675
782
  }
676
783
 
677
- return _svgPathPool[ id ];
784
+ return svgObject;
785
+
786
+ }
787
+
788
+ function getRenderItem( id, type, data, material ) {
789
+
790
+ let item = _renderListPool[ id ];
791
+
792
+ if ( item === undefined ) {
793
+
794
+ item = {
795
+ type: type,
796
+ data: data,
797
+ material: material
798
+ };
799
+
800
+ _renderListPool[ id ] = item;
801
+
802
+ return item;
803
+
804
+ }
805
+
806
+ item.type = type;
807
+ item.data = data;
808
+ item.material = material;
809
+
810
+ return item;
678
811
 
679
812
  }
680
813