super-three 0.184.0 → 0.185.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.
- package/build/three.cjs +1260 -927
- package/build/three.core.js +1104 -829
- package/build/three.core.min.js +1 -1
- package/build/three.module.js +156 -98
- package/build/three.module.min.js +1 -1
- package/build/three.tsl.js +11 -4
- package/build/three.tsl.min.js +1 -1
- package/build/three.webgpu.js +7538 -3367
- package/build/three.webgpu.min.js +1 -1
- package/build/three.webgpu.nodes.js +7538 -3366
- package/build/three.webgpu.nodes.min.js +1 -1
- package/examples/jsm/Addons.js +3 -0
- package/examples/jsm/controls/ArcballControls.js +6 -2
- package/examples/jsm/controls/FirstPersonControls.js +79 -42
- package/examples/jsm/controls/TransformControls.js +68 -15
- package/examples/jsm/csm/CSMFrustum.js +31 -6
- package/examples/jsm/csm/CSMShadowNode.js +4 -1
- package/examples/jsm/effects/AsciiEffect.js +14 -1
- package/examples/jsm/exporters/DRACOExporter.js +27 -6
- package/examples/jsm/exporters/GLTFExporter.js +114 -12
- package/examples/jsm/exporters/PLYExporter.js +286 -73
- package/examples/jsm/exporters/USDZExporter.js +347 -112
- package/examples/jsm/generators/CityGenerator.js +346 -0
- package/examples/jsm/generators/ForestGenerator.js +347 -0
- package/examples/jsm/generators/TerrainGenerator.js +504 -0
- package/examples/jsm/generators/TreeGenerator.js +377 -0
- package/examples/jsm/generators/city/SidewalkGenerator.js +253 -0
- package/examples/jsm/generators/city/SkyscraperGenerator.js +1357 -0
- package/examples/jsm/geometries/LoftGeometry.js +355 -0
- package/examples/jsm/helpers/LightProbeGridHelper.js +1 -1
- package/examples/jsm/helpers/LightProbeHelper.js +5 -4
- package/examples/jsm/helpers/ViewHelper.js +16 -6
- package/examples/jsm/inspector/Inspector.js +53 -15
- package/examples/jsm/inspector/RendererInspector.js +152 -36
- package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphEditor.js +1 -1
- package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphLoader.js +1 -1
- package/examples/jsm/inspector/tabs/Console.js +200 -18
- package/examples/jsm/inspector/tabs/Memory.js +8 -0
- package/examples/jsm/inspector/tabs/Parameters.js +64 -4
- package/examples/jsm/inspector/tabs/Performance.js +21 -6
- package/examples/jsm/inspector/tabs/Settings.js +51 -9
- package/examples/jsm/inspector/tabs/Timeline.js +178 -93
- package/examples/jsm/inspector/tabs/Viewer.js +628 -16
- package/examples/jsm/inspector/ui/Graph.js +162 -25
- package/examples/jsm/inspector/ui/Item.js +92 -13
- package/examples/jsm/inspector/ui/List.js +1 -1
- package/examples/jsm/inspector/ui/Profiler.js +90 -45
- package/examples/jsm/inspector/ui/Style.js +1788 -1401
- package/examples/jsm/inspector/ui/Tab.js +5 -3
- package/examples/jsm/inspector/ui/Values.js +71 -6
- package/examples/jsm/inspector/ui/utils.js +130 -10
- package/examples/jsm/libs/basis/README.md +0 -1
- package/examples/jsm/libs/demuxer_mp4.js +3 -3
- package/examples/jsm/libs/draco/README.md +0 -1
- package/examples/jsm/libs/meshopt_clusterizer.module.js +421 -0
- package/examples/jsm/libs/meshopt_simplifier.module.js +627 -0
- package/examples/jsm/libs/mikktspace.module.js +34 -9
- package/examples/jsm/lighting/ClusteredLighting.js +55 -0
- package/examples/jsm/lighting/LightProbeGrid.js +106 -76
- package/examples/jsm/lines/LineMaterial.js +38 -15
- package/examples/jsm/lines/LineSegments2.js +8 -0
- package/examples/jsm/lines/webgpu/LineSegments2.js +8 -0
- package/examples/jsm/loaders/3DMLoader.js +1 -1
- package/examples/jsm/loaders/DRACOLoader.js +46 -13
- package/examples/jsm/loaders/EXRLoader.js +27 -55
- package/examples/jsm/loaders/HDRLoader.js +6 -28
- package/examples/jsm/loaders/KTX2Loader.js +31 -12
- package/examples/jsm/loaders/LDrawLoader.js +8 -8
- package/examples/jsm/loaders/LUT3dlLoader.js +2 -2
- package/examples/jsm/loaders/LUTCubeLoader.js +2 -2
- package/examples/jsm/loaders/LWOLoader.js +4 -0
- package/examples/jsm/loaders/LottieLoader.js +1 -1
- package/examples/jsm/loaders/MaterialXLoader.js +31 -9
- package/examples/jsm/loaders/NRRDLoader.js +3 -3
- package/examples/jsm/loaders/PCDLoader.js +7 -7
- package/examples/jsm/loaders/PLYLoader.js +218 -55
- package/examples/jsm/loaders/SVGLoader.js +547 -495
- package/examples/jsm/loaders/TTFLoader.js +1 -1
- package/examples/jsm/loaders/USDLoader.js +33 -9
- package/examples/jsm/loaders/VRMLLoader.js +1 -1
- package/examples/jsm/loaders/usd/USDAParser.js +93 -20
- package/examples/jsm/loaders/usd/USDCParser.js +1 -1
- package/examples/jsm/loaders/usd/USDComposer.js +52 -19
- package/examples/jsm/misc/TileCreasedNormalsPlugin.js +270 -0
- package/examples/jsm/misc/Volume.js +2 -2
- package/examples/jsm/objects/GroundedSkybox.js +1 -1
- package/examples/jsm/objects/Reflector.js +2 -3
- package/examples/jsm/objects/Water2Mesh.js +1 -1
- package/examples/jsm/physics/AmmoPhysics.js +15 -8
- package/examples/jsm/physics/RapierPhysics.js +27 -2
- package/examples/jsm/shaders/VolumeShader.js +31 -44
- package/examples/jsm/transpiler/GLSLDecoder.js +6 -6
- package/examples/jsm/transpiler/Linker.js +1 -1
- package/examples/jsm/tsl/display/BloomNode.js +59 -19
- package/examples/jsm/tsl/display/DepthOfFieldNode.js +2 -2
- package/examples/jsm/tsl/display/FXAANode.js +11 -12
- package/examples/jsm/tsl/display/GTAONode.js +34 -13
- package/examples/jsm/tsl/display/GodraysNode.js +1 -1
- package/examples/jsm/tsl/display/ImportanceSampledEnvironment.js +560 -0
- package/examples/jsm/tsl/display/PixelationPassNode.js +2 -2
- package/examples/jsm/tsl/display/RecurrentDenoiseNode.js +912 -0
- package/examples/jsm/tsl/display/SSAAPassNode.js +13 -25
- package/examples/jsm/tsl/display/SSGINode.js +89 -39
- package/examples/jsm/tsl/display/SSRNode.js +829 -132
- package/examples/jsm/tsl/display/SSSNode.js +2 -2
- package/examples/jsm/tsl/display/TemporalReprojectNode.js +1023 -0
- package/examples/jsm/tsl/lighting/ClusteredLightsNode.js +622 -0
- package/examples/jsm/tsl/lighting/DynamicLightsNode.js +1 -1
- package/examples/jsm/tsl/math/curlNoise.js +107 -0
- package/examples/jsm/tsl/shadows/TileShadowNode.js +1 -1
- package/examples/jsm/tsl/shadows/TileShadowNodeHelper.js +3 -3
- package/examples/jsm/tsl/utils/GroundedSkybox.js +62 -0
- package/examples/jsm/tsl/utils/RNoise.js +51 -0
- package/examples/jsm/tsl/utils/SpecularHelpers.js +325 -0
- package/examples/jsm/utils/BufferGeometryUtils.js +126 -59
- package/examples/jsm/utils/GeometryCompressionUtils.js +1 -1
- package/examples/jsm/utils/SceneOptimizer.js +1 -1
- package/examples/jsm/webxr/ARButton.js +1 -1
- package/examples/jsm/webxr/WebGLXRFallback.js +89 -0
- package/examples/jsm/webxr/XRControllerModelFactory.js +2 -2
- package/package.json +7 -7
- package/src/Three.TSL.js +10 -3
- package/src/Three.WebGPU.js +4 -0
- package/src/animation/AnimationAction.js +15 -11
- package/src/animation/AnimationClip.js +0 -139
- package/src/animation/KeyframeTrack.js +2 -2
- package/src/animation/PropertyBinding.js +2 -2
- package/src/cameras/Camera.js +2 -2
- package/src/cameras/StereoCamera.js +5 -2
- package/src/constants.js +1 -1
- package/src/core/BufferGeometry.js +29 -7
- package/src/core/Object3D.js +17 -7
- package/src/core/Raycaster.js +1 -1
- package/src/core/RenderTarget.js +15 -2
- package/src/extras/PMREMGenerator.js +6 -4
- package/src/extras/TextureUtils.js +1 -1
- package/src/extras/core/ShapePath.js +149 -160
- package/src/geometries/SphereGeometry.js +8 -3
- package/src/helpers/DirectionalLightHelper.js +2 -0
- package/src/helpers/HemisphereLightHelper.js +2 -0
- package/src/helpers/PointLightHelper.js +2 -0
- package/src/helpers/SpotLightHelper.js +1 -1
- package/src/loaders/DataTextureLoader.js +69 -38
- package/src/loaders/LoadingManager.js +5 -0
- package/src/loaders/MaterialLoader.js +36 -266
- package/src/loaders/ObjectLoader.js +3 -1
- package/src/materials/LineBasicMaterial.js +4 -0
- package/src/materials/Material.js +196 -1
- package/src/materials/MeshBasicMaterial.js +24 -0
- package/src/materials/MeshDepthMaterial.js +10 -0
- package/src/materials/MeshDistanceMaterial.js +10 -0
- package/src/materials/MeshLambertMaterial.js +40 -1
- package/src/materials/MeshMatcapMaterial.js +25 -1
- package/src/materials/MeshNormalMaterial.js +9 -1
- package/src/materials/MeshPhongMaterial.js +40 -1
- package/src/materials/MeshPhysicalMaterial.js +38 -0
- package/src/materials/MeshStandardMaterial.js +42 -1
- package/src/materials/MeshToonMaterial.js +34 -1
- package/src/materials/PointsMaterial.js +7 -0
- package/src/materials/ShaderMaterial.js +86 -0
- package/src/materials/SpriteMaterial.js +7 -0
- package/src/materials/nodes/Line2NodeMaterial.js +378 -322
- package/src/materials/nodes/MeshBasicNodeMaterial.js +2 -2
- package/src/materials/nodes/MeshNormalNodeMaterial.js +2 -2
- package/src/materials/nodes/MeshPhongNodeMaterial.js +0 -9
- package/src/materials/nodes/MeshPhysicalNodeMaterial.js +0 -30
- package/src/materials/nodes/MeshSSSNodeMaterial.js +0 -13
- package/src/materials/nodes/MeshStandardNodeMaterial.js +0 -11
- package/src/materials/nodes/NodeMaterial.js +115 -71
- package/src/materials/nodes/SpriteNodeMaterial.js +0 -10
- package/src/materials/nodes/manager/NodeMaterialObserver.js +49 -20
- package/src/math/Box3.js +5 -0
- package/src/math/FrustumArray.js +103 -133
- package/src/math/Interpolant.js +1 -1
- package/src/math/MathUtils.js +2 -2
- package/src/math/Matrix3.js +11 -0
- package/src/math/Matrix4.js +28 -3
- package/src/math/Vector2.js +2 -2
- package/src/math/Vector3.js +2 -2
- package/src/math/Vector4.js +2 -2
- package/src/math/interpolants/BezierInterpolant.js +4 -6
- package/src/nodes/Nodes.js +2 -5
- package/src/nodes/TSL.js +8 -6
- package/src/nodes/accessors/Arrays.js +4 -4
- package/src/nodes/accessors/Batch.js +108 -0
- package/src/nodes/accessors/Bitangent.js +2 -2
- package/src/nodes/accessors/BufferAttributeNode.js +13 -2
- package/src/nodes/accessors/Camera.js +7 -7
- package/src/nodes/accessors/ClippingNode.js +1 -1
- package/src/nodes/accessors/Instance.js +264 -0
- package/src/nodes/accessors/MaterialNode.js +18 -2
- package/src/nodes/accessors/MaterialProperties.js +4 -3
- package/src/nodes/accessors/{MorphNode.js → Morph.js} +99 -112
- package/src/nodes/accessors/Normal.js +13 -13
- package/src/nodes/accessors/Position.js +5 -1
- package/src/nodes/accessors/ReferenceNode.js +1 -0
- package/src/nodes/accessors/ReflectVector.js +3 -3
- package/src/nodes/accessors/SceneProperties.js +1 -1
- package/src/nodes/accessors/Skinning.js +263 -0
- package/src/nodes/accessors/StorageBufferNode.js +2 -2
- package/src/nodes/accessors/StorageTexture3DNode.js +100 -0
- package/src/nodes/accessors/StorageTextureNode.js +39 -7
- package/src/nodes/accessors/Tangent.js +4 -4
- package/src/nodes/accessors/TextureNode.js +60 -5
- package/src/nodes/code/FunctionNode.js +2 -16
- package/src/nodes/core/MRTNode.js +7 -2
- package/src/nodes/core/Node.js +39 -2
- package/src/nodes/core/NodeBuilder.js +201 -41
- package/src/nodes/core/NodeUtils.js +13 -13
- package/src/nodes/core/OutputStructNode.js +1 -1
- package/src/nodes/core/OverrideContextNode.js +153 -0
- package/src/nodes/core/PropertyNode.js +51 -5
- package/src/nodes/core/StackNode.js +56 -34
- package/src/nodes/core/StructNode.js +4 -6
- package/src/nodes/core/StructTypeNode.js +6 -6
- package/src/nodes/core/VarNode.js +8 -0
- package/src/nodes/core/VaryingNode.js +12 -2
- package/src/nodes/display/BlendModes.js +1 -41
- package/src/nodes/display/BumpMapNode.js +6 -1
- package/src/nodes/display/ColorAdjustment.js +10 -9
- package/src/nodes/display/FrontFacingNode.js +38 -9
- package/src/nodes/display/NormalMapNode.js +2 -2
- package/src/nodes/display/PassNode.js +35 -43
- package/src/nodes/display/PremultiplyAlphaFunctions.js +39 -0
- package/src/nodes/display/RenderOutputNode.js +12 -3
- package/src/nodes/functions/VolumetricLightingModel.js +40 -7
- package/src/nodes/gpgpu/WorkgroupInfoNode.js +1 -1
- package/src/nodes/lighting/EnvironmentNode.js +2 -2
- package/src/nodes/lighting/IESSpotLightNode.js +40 -2
- package/src/nodes/lighting/LightingContextNode.js +10 -2
- package/src/nodes/lighting/LightsNode.js +90 -37
- package/src/nodes/lighting/PointShadowNode.js +6 -1
- package/src/nodes/lighting/ShadowFilterNode.js +21 -52
- package/src/nodes/lighting/ShadowNode.js +34 -14
- package/src/nodes/materialx/lib/mx_noise.js +2 -2
- package/src/nodes/math/ConditionalNode.js +3 -3
- package/src/nodes/math/MathNode.js +39 -11
- package/src/nodes/math/OperatorNode.js +23 -23
- package/src/nodes/parsers/GLSLNodeFunction.js +1 -1
- package/src/nodes/pmrem/PMREMNode.js +33 -2
- package/src/nodes/tsl/TSLCore.js +63 -18
- package/src/nodes/utils/EquirectUV.js +30 -5
- package/src/nodes/utils/FunctionOverloadingNode.js +1 -1
- package/src/nodes/utils/Packing.js +35 -4
- package/src/nodes/utils/RTTNode.js +40 -24
- package/src/nodes/utils/ReflectorNode.js +1 -0
- package/src/nodes/utils/StorageArrayElementNode.js +1 -1
- package/src/objects/BatchedMesh.js +22 -14
- package/src/objects/InstancedMesh.js +0 -11
- package/src/objects/Skeleton.js +0 -9
- package/src/renderers/WebGLRenderer.js +19 -15
- package/src/renderers/common/Backend.js +49 -5
- package/src/renderers/common/Bindings.js +114 -45
- package/src/renderers/common/Buffer.js +9 -0
- package/src/renderers/common/ClippingContext.js +10 -2
- package/src/renderers/common/Info.js +63 -22
- package/src/renderers/common/Lighting.js +53 -5
- package/src/renderers/common/RenderList.js +41 -4
- package/src/renderers/common/RenderObject.js +73 -13
- package/src/renderers/common/Renderer.js +281 -108
- package/src/renderers/common/Sampler.js +16 -48
- package/src/renderers/common/Textures.js +32 -5
- package/src/renderers/common/TimestampQueryPool.js +1 -1
- package/src/renderers/common/UniformsGroup.js +31 -9
- package/src/renderers/common/XRManager.js +340 -22
- package/src/renderers/common/extras/PMREMGenerator.js +15 -38
- package/src/renderers/common/nodes/NodeBuilderState.js +9 -1
- package/src/renderers/common/nodes/NodeLibrary.js +4 -4
- package/src/renderers/common/nodes/NodeManager.js +63 -14
- package/src/renderers/common/nodes/NodeUniformBuffer.js +13 -0
- package/src/renderers/shaders/ShaderChunk/common.glsl.js +13 -4
- package/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js +0 -6
- package/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js +2 -2
- package/src/renderers/shaders/ShaderChunk/envmap_vertex.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js +2 -2
- package/src/renderers/shaders/ShaderChunk/normal_vertex.glsl.js +6 -0
- package/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/transmission_fragment.glsl.js +1 -1
- package/src/renderers/shaders/ShaderLib/distance.glsl.js +1 -1
- package/src/renderers/webgl/WebGLOutput.js +2 -1
- package/src/renderers/webgl/WebGLProgram.js +5 -4
- package/src/renderers/webgl/WebGLPrograms.js +12 -5
- package/src/renderers/webgl/WebGLRenderLists.js +9 -1
- package/src/renderers/webgl/WebGLShaderCache.js +5 -11
- package/src/renderers/webgl/WebGLTextures.js +20 -7
- package/src/renderers/webgl/WebGLUniformsGroups.js +66 -38
- package/src/renderers/webgl-fallback/WebGLBackend.js +84 -25
- package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +138 -8
- package/src/renderers/webgl-fallback/utils/WebGLAttributeUtils.js +1 -1
- package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +52 -22
- package/src/renderers/webgl-fallback/utils/WebGLUtils.js +4 -2
- package/src/renderers/webgpu/WebGPUBackend.js +480 -169
- package/src/renderers/webgpu/descriptors/GPUBindGroupDescriptor.js +48 -0
- package/src/renderers/webgpu/descriptors/GPUBufferDescriptor.js +57 -0
- package/src/renderers/webgpu/descriptors/GPUCommandEncoderDescriptor.js +30 -0
- package/src/renderers/webgpu/descriptors/GPUComputePassDescriptor.js +38 -0
- package/src/renderers/webgpu/descriptors/GPUComputePipelineDescriptor.js +48 -0
- package/src/renderers/webgpu/descriptors/GPUCopyExternalImageDestInfo.js +47 -0
- package/src/renderers/webgpu/descriptors/GPUCopyExternalImageSourceInfo.js +50 -0
- package/src/renderers/webgpu/descriptors/GPUExtent3D.js +51 -0
- package/src/renderers/webgpu/descriptors/GPUPipelineLayoutDescriptor.js +39 -0
- package/src/renderers/webgpu/descriptors/GPUQuerySetDescriptor.js +47 -0
- package/src/renderers/webgpu/descriptors/GPURenderBundleEncoderDescriptor.js +74 -0
- package/src/renderers/webgpu/descriptors/GPURenderPassColorAttachment.js +72 -0
- package/src/renderers/webgpu/descriptors/GPURenderPassDepthStencilAttachment.js +99 -0
- package/src/renderers/webgpu/descriptors/GPURenderPassDescriptor.js +72 -0
- package/src/renderers/webgpu/descriptors/GPURenderPassTimestampWrites.js +49 -0
- package/src/renderers/webgpu/descriptors/GPURenderPipelineDescriptor.js +130 -0
- package/src/renderers/webgpu/descriptors/GPUSamplerDescriptor.js +119 -0
- package/src/renderers/webgpu/descriptors/GPUShaderModuleDescriptor.js +46 -0
- package/src/renderers/webgpu/descriptors/GPUTexelCopyBufferInfo.js +57 -0
- package/src/renderers/webgpu/descriptors/GPUTexelCopyBufferLayout.js +48 -0
- package/src/renderers/webgpu/descriptors/GPUTexelCopyTextureInfo.js +61 -0
- package/src/renderers/webgpu/descriptors/GPUTextureDescriptor.js +99 -0
- package/src/renderers/webgpu/descriptors/GPUTextureViewDescriptor.js +108 -0
- package/src/renderers/webgpu/nodes/StandardNodeLibrary.js +0 -1
- package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +164 -7
- package/src/renderers/webgpu/nodes/WGSLNodeFunction.js +1 -1
- package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +98 -38
- package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +72 -68
- package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +164 -69
- package/src/renderers/webgpu/utils/WebGPUTexturePassUtils.js +158 -95
- package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +297 -141
- package/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +33 -18
- package/src/renderers/webgpu/utils/WebGPUUtils.js +38 -3
- package/src/textures/DepthTexture.js +1 -1
- package/src/textures/ExternalTexture.js +0 -3
- package/examples/fonts/LICENSE +0 -13
- package/examples/fonts/MPLUSRounded1c/MPLUSRounded1c-Regular.typeface.json.zip +0 -0
- package/examples/fonts/MPLUSRounded1c/OFL.txt +0 -91
- package/examples/fonts/README.md +0 -11
- package/examples/fonts/droid/NOTICE +0 -190
- package/examples/fonts/droid/README.txt +0 -18
- package/examples/fonts/droid/droid_sans_bold.typeface.json +0 -1
- package/examples/fonts/droid/droid_sans_mono_regular.typeface.json +0 -1
- package/examples/fonts/droid/droid_sans_regular.typeface.json +0 -1
- package/examples/fonts/droid/droid_serif_bold.typeface.json +0 -1
- package/examples/fonts/droid/droid_serif_regular.typeface.json +0 -1
- package/examples/fonts/gentilis_bold.typeface.json +0 -1
- package/examples/fonts/gentilis_regular.typeface.json +0 -1
- package/examples/fonts/helvetiker_bold.typeface.json +0 -1
- package/examples/fonts/helvetiker_regular.typeface.json +0 -1
- package/examples/fonts/optimer_bold.typeface.json +0 -1
- package/examples/fonts/optimer_regular.typeface.json +0 -1
- package/examples/fonts/ttf/README.md +0 -9
- package/examples/fonts/ttf/kenpixel.ttf +0 -0
- package/examples/jsm/inspector/extensions/extensions.json +0 -6
- package/examples/jsm/libs/ammo.wasm.js +0 -822
- package/examples/jsm/libs/ammo.wasm.wasm +0 -0
- package/examples/jsm/libs/draco/draco_encoder.js +0 -33
- package/examples/jsm/libs/draco/gltf/draco_encoder.js +0 -33
- package/examples/jsm/libs/lottie_canvas.module.js +0 -14849
- package/examples/jsm/libs/opentype.module.js +0 -14506
- package/examples/jsm/libs/rhino3dm/rhino3dm.js +0 -21
- package/examples/jsm/libs/rhino3dm/rhino3dm.module.js +0 -16
- package/examples/jsm/libs/rhino3dm/rhino3dm.wasm +0 -0
- package/examples/jsm/lighting/TiledLighting.js +0 -42
- package/examples/jsm/tsl/display/AnamorphicNode.js +0 -293
- package/examples/jsm/tsl/lighting/TiledLightsNode.js +0 -442
- package/src/nodes/accessors/BatchNode.js +0 -163
- package/src/nodes/accessors/InstanceNode.js +0 -349
- package/src/nodes/accessors/InstancedMeshNode.js +0 -50
- package/src/nodes/accessors/SkinningNode.js +0 -328
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BufferAttribute,
|
|
3
|
+
BufferGeometry,
|
|
4
|
+
Mesh,
|
|
5
|
+
Vector3
|
|
6
|
+
} from 'three';
|
|
7
|
+
|
|
8
|
+
import { MeshStandardNodeMaterial } from 'three/webgpu';
|
|
9
|
+
import { color, float, mx_fractal_noise_float, positionLocal, vec3 } from 'three/tsl';
|
|
10
|
+
|
|
11
|
+
// the golden angle ( 137.5° ): rolling each sibling branch by this much around the
|
|
12
|
+
// parent axis spreads them like a real stem, so they never line up
|
|
13
|
+
const GOLDEN_ANGLE = Math.PI * ( 3 - Math.sqrt( 5 ) );
|
|
14
|
+
const DEG2RAD = Math.PI / 180;
|
|
15
|
+
const TAU = Math.PI * 2;
|
|
16
|
+
|
|
17
|
+
const UP = /*@__PURE__*/ new Vector3( 0, 1, 0 );
|
|
18
|
+
const _axis = /*@__PURE__*/ new Vector3();
|
|
19
|
+
|
|
20
|
+
// reusable scratch for one tube's ring vertices ( grows to the largest tube seen )
|
|
21
|
+
let _ring = new Float32Array( 0 );
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Grows a procedural tree skeleton — trunk, branches and twigs, each swept as a tapered
|
|
25
|
+
* tube — and bakes it into one non-indexed {@link BufferGeometry} (position and normal
|
|
26
|
+
* only), ready to instance into a forest. It produces *branches only*; add foliage as a
|
|
27
|
+
* separate layer.
|
|
28
|
+
*
|
|
29
|
+
* The branching is deterministic for a given `seed`: a recursive sweep lays down gently
|
|
30
|
+
* curved tubes with a parallel-transport frame (so they never twist), forking by the
|
|
31
|
+
* pipe model (each child much thinner than its parent), spreading children along the
|
|
32
|
+
* upper part of each branch with a golden-angle roll, and pulling them back up toward
|
|
33
|
+
* the light. A flared root, non-linear taper and gravity droop fill in the character.
|
|
34
|
+
*
|
|
35
|
+
* Parameters are set with a fluent builder: a `set<Param>()` exists for every default
|
|
36
|
+
* ( `setSeed`, `setLevels`, `setChildren`, … ), each returning `this` for chaining.
|
|
37
|
+
*
|
|
38
|
+
* Each `build()` returns a fresh, independent mesh that the caller owns, so one
|
|
39
|
+
* generator can be re-parametrized and built repeatedly to grow a varied stand:
|
|
40
|
+
*
|
|
41
|
+
* ```js
|
|
42
|
+
* const generator = new TreeGenerator( material );
|
|
43
|
+
* const oak = generator.setSeed( 1 ).setLevels( 4 ).build();
|
|
44
|
+
* const pine = generator.setSeed( 2 ).setLevels( 5 ).build();
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
class TreeGenerator {
|
|
48
|
+
|
|
49
|
+
constructor( material = null ) {
|
|
50
|
+
|
|
51
|
+
this.material = material;
|
|
52
|
+
this.parameters = {}; // overrides; defaults fill the rest at build time
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
build() {
|
|
57
|
+
|
|
58
|
+
const p = Object.assign( {}, TreeGenerator.defaults, this.parameters );
|
|
59
|
+
const random = createRandom( p.seed );
|
|
60
|
+
|
|
61
|
+
// grow the skeleton into a flat list of tubes, then size and fill the geometry in
|
|
62
|
+
// one pass — no per-vertex objects, no array growth
|
|
63
|
+
|
|
64
|
+
const tubes = [];
|
|
65
|
+
growBranch( tubes, new Vector3(), UP, p.trunkLength, p.trunkRadius, 0, p, random );
|
|
66
|
+
|
|
67
|
+
let vertexCount = 0;
|
|
68
|
+
for ( const tube of tubes ) vertexCount += ( tube.rings.length - 1 ) * tube.radial * 6;
|
|
69
|
+
|
|
70
|
+
const positions = new Float32Array( vertexCount * 3 );
|
|
71
|
+
const normals = new Float32Array( vertexCount * 3 );
|
|
72
|
+
|
|
73
|
+
let offset = 0;
|
|
74
|
+
for ( const tube of tubes ) offset = emitTube( positions, normals, offset, tube.rings, tube.radial );
|
|
75
|
+
|
|
76
|
+
const geometry = new BufferGeometry();
|
|
77
|
+
geometry.setAttribute( 'position', new BufferAttribute( positions, 3 ) );
|
|
78
|
+
geometry.setAttribute( 'normal', new BufferAttribute( normals, 3 ) );
|
|
79
|
+
geometry.computeBoundingSphere();
|
|
80
|
+
|
|
81
|
+
const mesh = new Mesh( geometry, this.material || createTreeMaterial() );
|
|
82
|
+
mesh.name = 'Tree';
|
|
83
|
+
|
|
84
|
+
return mesh;
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
TreeGenerator.defaults = {
|
|
91
|
+
seed: 1,
|
|
92
|
+
levels: 4, // recursion depth: trunk, branch, twig, sub-twig
|
|
93
|
+
children: [ 3, 12, 8 ], // sub-branches per level; density comes from many children spread along each parent, not depth
|
|
94
|
+
branchAngle: [ 38, 50, 58 ], // degrees a child tilts off its parent axis, per level
|
|
95
|
+
angleVariance: 14, // degrees of random jitter on the branch angle, breaks fractal regularity
|
|
96
|
+
lengthRatio: 0.62, // child length / parent length
|
|
97
|
+
trunkLength: 9, // trunk length in world units; sets the tree's height
|
|
98
|
+
trunkRadius: 0.42, // base radius of the trunk
|
|
99
|
+
taper: 0.55, // a branch thins to ( 1 - taper ) of its base radius along its own length
|
|
100
|
+
taperCurve: 0.7, // < 1 keeps the bole full then tapers ( real trees ), 1 = straight cone
|
|
101
|
+
rootFlare: 0.6, // how much the trunk swells at the very base
|
|
102
|
+
flareFrac: 0.18, // fraction of the trunk over which the flare acts
|
|
103
|
+
radiusExponent: 2.3, // pipe model ( da Vinci ): childBase = parentBase × ( 1 / children )^( 1 / radiusExponent )
|
|
104
|
+
minRadius: 0.05, // hair-thin floor so twigs don't taper to a sliver
|
|
105
|
+
minLength: 0.6, // branches shorter than this stop recursing
|
|
106
|
+
droop: 0.05, // gravity sag per branch ( ≈ droop × length ); the trunk stays upright
|
|
107
|
+
upPull: 0.3, // phototropism: 0 = a bare spread cone ( many branches aim down ), 1 = straight up
|
|
108
|
+
gnarl: [ 0.05, 0.16, 0.26, 0.32 ], // per-level random wobble on each tube segment
|
|
109
|
+
radialSegments: 6, // ring vertices around a tube ( drops by one per level for thin twigs )
|
|
110
|
+
sectionLength: 1.3, // world units per tube segment, so a tall trunk stays smooth
|
|
111
|
+
childStart: 0.12, // fraction up a sub-branch before children appear
|
|
112
|
+
trunkClear: 0.25 // fraction of the trunk kept bare before the crown ( raise for a tall clean bole )
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// a fluent setter for every default — setSeed(), setLevels(), setChildren(), … — each
|
|
116
|
+
// storing its value and returning `this`, so the API stays in sync with the parameters
|
|
117
|
+
for ( const key of Object.keys( TreeGenerator.defaults ) ) {
|
|
118
|
+
|
|
119
|
+
TreeGenerator.prototype[ 'set' + key[ 0 ].toUpperCase() + key.slice( 1 ) ] = function ( value ) {
|
|
120
|
+
|
|
121
|
+
this.parameters[ key ] = value;
|
|
122
|
+
return this;
|
|
123
|
+
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// --- skeleton ------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
// Grows one branch as a gently curved, tapered tube and recurses, collecting the tube
|
|
131
|
+
// into `tubes`. The tube is swept with a parallel-transport frame ( rotated by the same
|
|
132
|
+
// rotation that bends the tangent each step ) so it never twists, unlike a naive Frenet
|
|
133
|
+
// frame. Children fork off the upper part of the branch by the pipe model.
|
|
134
|
+
function growBranch( tubes, base, dir, length, baseRadius, level, p, random ) {
|
|
135
|
+
|
|
136
|
+
const sections = Math.max( 3, Math.min( 24, Math.round( length / p.sectionLength ) ) ); // ring count tracks length
|
|
137
|
+
const radial = Math.max( 3, p.radialSegments - level );
|
|
138
|
+
const step = length / sections;
|
|
139
|
+
const gnarl = p.gnarl[ Math.min( level, p.gnarl.length - 1 ) ];
|
|
140
|
+
const start = level === 0 ? p.trunkClear : p.childStart; // the trunk carries a clean bole below its crown
|
|
141
|
+
|
|
142
|
+
let tangent = dir.clone().normalize();
|
|
143
|
+
const normal = perpendicular( tangent );
|
|
144
|
+
|
|
145
|
+
const rings = [];
|
|
146
|
+
const pos = base.clone();
|
|
147
|
+
|
|
148
|
+
for ( let s = 0; s <= sections; s ++ ) {
|
|
149
|
+
|
|
150
|
+
const t = s / sections;
|
|
151
|
+
|
|
152
|
+
// non-linear taper down to ( 1 - taper ) of the base, with a flared root on the trunk
|
|
153
|
+
let radius = baseRadius * ( ( 1 - p.taper ) + p.taper * Math.pow( 1 - t, p.taperCurve ) );
|
|
154
|
+
if ( level === 0 && p.rootFlare > 0 ) {
|
|
155
|
+
|
|
156
|
+
const flare = Math.max( 0, ( p.flareFrac - t ) / p.flareFrac );
|
|
157
|
+
radius *= 1 + p.rootFlare * flare * flare * flare; // sharp knee, confined to the base
|
|
158
|
+
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
rings.push( {
|
|
162
|
+
pos: pos.clone(),
|
|
163
|
+
tangent: tangent.clone(),
|
|
164
|
+
normal: normal.clone(),
|
|
165
|
+
binormal: new Vector3().crossVectors( tangent, normal ),
|
|
166
|
+
radius
|
|
167
|
+
} );
|
|
168
|
+
|
|
169
|
+
if ( s < sections ) {
|
|
170
|
+
|
|
171
|
+
const next = tangent.clone();
|
|
172
|
+
next.x += ( random() * 2 - 1 ) * gnarl;
|
|
173
|
+
next.y += ( random() * 2 - 1 ) * gnarl;
|
|
174
|
+
next.z += ( random() * 2 - 1 ) * gnarl;
|
|
175
|
+
if ( level > 0 ) next.y -= p.droop * step; // branches sag; the trunk stays vertical
|
|
176
|
+
next.normalize();
|
|
177
|
+
|
|
178
|
+
transport( tangent, next, normal ); // keep the frame torsion-free
|
|
179
|
+
pos.addScaledVector( next, step );
|
|
180
|
+
tangent = next;
|
|
181
|
+
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
tubes.push( { rings, radial } );
|
|
187
|
+
|
|
188
|
+
if ( level >= p.levels - 1 || length < p.minLength ) return;
|
|
189
|
+
|
|
190
|
+
// fork: children spread along the upper branch, each tilted off the local tangent,
|
|
191
|
+
// rolled by the golden angle, and much thinner than the parent ( pipe model )
|
|
192
|
+
|
|
193
|
+
const n = p.children[ Math.min( level, p.children.length - 1 ) ];
|
|
194
|
+
const angle = p.branchAngle[ Math.min( level, p.branchAngle.length - 1 ) ];
|
|
195
|
+
const pipeDrop = Math.pow( 1 / n, 1 / p.radiusExponent );
|
|
196
|
+
|
|
197
|
+
for ( let i = 0; i < n; i ++ ) {
|
|
198
|
+
|
|
199
|
+
const t = start + ( i + 0.5 + ( random() - 0.5 ) * 0.6 ) / n * ( 1 - start );
|
|
200
|
+
const ring = ringAt( rings, t );
|
|
201
|
+
|
|
202
|
+
const tilt = ( angle + ( random() * 2 - 1 ) * p.angleVariance ) * DEG2RAD;
|
|
203
|
+
const roll = i * GOLDEN_ANGLE + ( random() * 2 - 1 ) * 0.4;
|
|
204
|
+
|
|
205
|
+
// tilt off a perpendicular axis FIRST, then roll about the parent axis, then pull
|
|
206
|
+
// back toward the light ( else the roll sends half the children downward )
|
|
207
|
+
const childDir = ring.tangent.clone()
|
|
208
|
+
.applyAxisAngle( ring.normal, tilt )
|
|
209
|
+
.applyAxisAngle( ring.tangent, roll );
|
|
210
|
+
if ( p.upPull > 0 ) childDir.lerp( UP, p.upPull ).normalize();
|
|
211
|
+
|
|
212
|
+
// the pipe-model drop, but never fatter than the wood it leaves nor below the floor
|
|
213
|
+
const childBase = Math.max( p.minRadius, Math.min( baseRadius * pipeDrop, ring.radius ) );
|
|
214
|
+
|
|
215
|
+
growBranch( tubes, ring.pos, childDir, length * p.lengthRatio, childBase, level + 1, p, random );
|
|
216
|
+
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// a unit vector perpendicular to v ( cross with the least-aligned axis )
|
|
222
|
+
function perpendicular( v ) {
|
|
223
|
+
|
|
224
|
+
const a = Math.abs( v.x ) < 0.9 ? _axis.set( 1, 0, 0 ) : _axis.set( 0, 1, 0 );
|
|
225
|
+
return new Vector3().crossVectors( v, a ).normalize();
|
|
226
|
+
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// rotate frame vector n by the rotation that maps tangent t0 onto t1
|
|
230
|
+
function transport( t0, t1, n ) {
|
|
231
|
+
|
|
232
|
+
_axis.crossVectors( t0, t1 );
|
|
233
|
+
const sin = _axis.length();
|
|
234
|
+
if ( sin < 1e-6 ) return; // already parallel
|
|
235
|
+
_axis.divideScalar( sin );
|
|
236
|
+
n.applyAxisAngle( _axis, Math.atan2( sin, t0.dot( t1 ) ) );
|
|
237
|
+
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// sample the branch frame at fraction t ( 0..1 ) for spawning a child
|
|
241
|
+
function ringAt( rings, t ) {
|
|
242
|
+
|
|
243
|
+
const f = Math.max( 0, Math.min( 0.999, t ) ) * ( rings.length - 1 );
|
|
244
|
+
const i = Math.floor( f );
|
|
245
|
+
const frac = f - i;
|
|
246
|
+
const a = rings[ i ];
|
|
247
|
+
const b = rings[ Math.min( i + 1, rings.length - 1 ) ];
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
pos: a.pos.clone().lerp( b.pos, frac ),
|
|
251
|
+
tangent: a.tangent.clone().lerp( b.tangent, frac ).normalize(),
|
|
252
|
+
normal: a.normal.clone().lerp( b.normal, frac ).normalize(),
|
|
253
|
+
radius: a.radius + ( b.radius - a.radius ) * frac
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// --- geometry ------------------------------------------------------------
|
|
259
|
+
|
|
260
|
+
// Sweeps a tube through the rings: each ring is a loop of `radial` vertices in its
|
|
261
|
+
// ( normal, binormal ) plane, the outward radial direction being the vertex normal.
|
|
262
|
+
// Ring vertices are computed once into a reused scratch, then stitched straight into the
|
|
263
|
+
// preallocated geometry arrays — no per-vertex objects.
|
|
264
|
+
function emitTube( positions, normals, offset, rings, radial ) {
|
|
265
|
+
|
|
266
|
+
const stride = ( radial + 1 ) * 6; // one ring loop: ( position, normal ) per vertex
|
|
267
|
+
const needed = rings.length * stride;
|
|
268
|
+
if ( _ring.length < needed ) _ring = new Float32Array( needed );
|
|
269
|
+
|
|
270
|
+
const ring = _ring;
|
|
271
|
+
|
|
272
|
+
for ( let r = 0; r < rings.length; r ++ ) {
|
|
273
|
+
|
|
274
|
+
const { pos, normal, binormal, radius } = rings[ r ];
|
|
275
|
+
let o = r * stride;
|
|
276
|
+
|
|
277
|
+
for ( let j = 0; j <= radial; j ++ ) {
|
|
278
|
+
|
|
279
|
+
const a = j / radial * TAU;
|
|
280
|
+
const c = Math.cos( a );
|
|
281
|
+
const s = Math.sin( a );
|
|
282
|
+
const nx = c * normal.x + s * binormal.x;
|
|
283
|
+
const ny = c * normal.y + s * binormal.y;
|
|
284
|
+
const nz = c * normal.z + s * binormal.z;
|
|
285
|
+
|
|
286
|
+
ring[ o ++ ] = pos.x + nx * radius;
|
|
287
|
+
ring[ o ++ ] = pos.y + ny * radius;
|
|
288
|
+
ring[ o ++ ] = pos.z + nz * radius;
|
|
289
|
+
ring[ o ++ ] = nx;
|
|
290
|
+
ring[ o ++ ] = ny;
|
|
291
|
+
ring[ o ++ ] = nz;
|
|
292
|
+
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// stitch consecutive rings into quads ( two triangles ), wound so normals face out
|
|
298
|
+
|
|
299
|
+
for ( let r = 0; r < rings.length - 1; r ++ ) {
|
|
300
|
+
|
|
301
|
+
const a = r * stride;
|
|
302
|
+
const b = ( r + 1 ) * stride;
|
|
303
|
+
|
|
304
|
+
for ( let j = 0; j < radial; j ++ ) {
|
|
305
|
+
|
|
306
|
+
const aL = a + j * 6, aR = a + ( j + 1 ) * 6;
|
|
307
|
+
const bL = b + j * 6, bR = b + ( j + 1 ) * 6;
|
|
308
|
+
|
|
309
|
+
offset = copyVertex( positions, normals, offset, ring, aL );
|
|
310
|
+
offset = copyVertex( positions, normals, offset, ring, bR );
|
|
311
|
+
offset = copyVertex( positions, normals, offset, ring, bL );
|
|
312
|
+
|
|
313
|
+
offset = copyVertex( positions, normals, offset, ring, aL );
|
|
314
|
+
offset = copyVertex( positions, normals, offset, ring, aR );
|
|
315
|
+
offset = copyVertex( positions, normals, offset, ring, bR );
|
|
316
|
+
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return offset;
|
|
322
|
+
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// copies one ( position, normal ) vertex from the ring scratch into the geometry arrays
|
|
326
|
+
function copyVertex( positions, normals, offset, ring, i ) {
|
|
327
|
+
|
|
328
|
+
const o = offset * 3;
|
|
329
|
+
positions[ o ] = ring[ i ]; positions[ o + 1 ] = ring[ i + 1 ]; positions[ o + 2 ] = ring[ i + 2 ];
|
|
330
|
+
normals[ o ] = ring[ i + 3 ]; normals[ o + 1 ] = ring[ i + 4 ]; normals[ o + 2 ] = ring[ i + 5 ];
|
|
331
|
+
|
|
332
|
+
return offset + 1;
|
|
333
|
+
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// --- deterministic PRNG ( mulberry32 ) -----------------------------------
|
|
337
|
+
|
|
338
|
+
function createRandom( seed ) {
|
|
339
|
+
|
|
340
|
+
let s = ( seed >>> 0 ) || 1;
|
|
341
|
+
|
|
342
|
+
return function () {
|
|
343
|
+
|
|
344
|
+
s = ( s + 0x6D2B79F5 ) | 0;
|
|
345
|
+
let t = Math.imul( s ^ ( s >>> 15 ), 1 | s );
|
|
346
|
+
t = ( t + Math.imul( t ^ ( t >>> 7 ), 61 | t ) ) ^ t;
|
|
347
|
+
return ( ( t ^ ( t >>> 14 ) ) >>> 0 ) / 4294967296;
|
|
348
|
+
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// --- material ------------------------------------------------------------
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* A simple bark material for a {@link TreeGenerator} mesh: a low-saturation brown with a
|
|
357
|
+
* faint, vertically-stretched grain, so trunks read near-black against bright fog.
|
|
358
|
+
*
|
|
359
|
+
* @param {Object} [parameters] - `barkColor` ( a hex, THREE.Color or TSL node ).
|
|
360
|
+
* @return {MeshStandardNodeMaterial}
|
|
361
|
+
*/
|
|
362
|
+
function createTreeMaterial( parameters = {} ) {
|
|
363
|
+
|
|
364
|
+
const c = parameters.barkColor;
|
|
365
|
+
const barkColor = c === undefined ? color( 0x4b3a2b ) : ( c.isColor || typeof c === 'number' ? color( c ) : c );
|
|
366
|
+
|
|
367
|
+
const material = new MeshStandardNodeMaterial();
|
|
368
|
+
const grain = mx_fractal_noise_float( positionLocal.mul( vec3( 2.5, 0.4, 2.5 ) ), 3 ).mul( 0.18 );
|
|
369
|
+
material.colorNode = barkColor.mul( grain.add( 0.9 ) );
|
|
370
|
+
material.roughnessNode = float( 0.95 );
|
|
371
|
+
material.metalnessNode = float( 0 );
|
|
372
|
+
|
|
373
|
+
return material;
|
|
374
|
+
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export { TreeGenerator, createTreeMaterial };
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ExtrudeGeometry,
|
|
3
|
+
Group,
|
|
4
|
+
InstancedMesh,
|
|
5
|
+
MeshStandardNodeMaterial,
|
|
6
|
+
Shape
|
|
7
|
+
} from 'three/webgpu';
|
|
8
|
+
|
|
9
|
+
import { cameraPosition, color, float, floor, Fn, fract, fwidth, If, mix, mx_noise_float, normalView, normalWorldGeometry, positionView, positionWorld, sin, smoothstep } from 'three/tsl';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generates the raised sidewalk for a city's blocks: per block, a rounded-corner concrete
|
|
13
|
+
* slab rimmed by a distinct granite kerbstone that stands proud of the walking surface and
|
|
14
|
+
* drops to the road. Instanced across a list of placements and dressed with its own
|
|
15
|
+
* procedural material ( poured concrete flags, scored expansion joints, granite curb ).
|
|
16
|
+
* Returns a `THREE.Group` of two instanced meshes — the walking slab and the curb.
|
|
17
|
+
*
|
|
18
|
+
* Unlike the building generator, this one owns its materials: the slab and curb
|
|
19
|
+
* geometry and the TSL that shades them live together here.
|
|
20
|
+
*
|
|
21
|
+
* ```js
|
|
22
|
+
* const sidewalk = new SidewalkGenerator( { width: 90, depth: 60, height: 0.5 } );
|
|
23
|
+
* scene.add( sidewalk.build( placements ) ); // placements: Matrix4[]
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
class SidewalkGenerator {
|
|
27
|
+
|
|
28
|
+
constructor( parameters = {} ) {
|
|
29
|
+
|
|
30
|
+
this.parameters = Object.assign( {}, SidewalkGenerator.defaults, parameters );
|
|
31
|
+
|
|
32
|
+
this.material = null; // the procedural concrete, built once and reused across rebuilds
|
|
33
|
+
this.curbMaterial = null; // the procedural granite curb, likewise
|
|
34
|
+
this.mesh = null;
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
build( placements ) {
|
|
39
|
+
|
|
40
|
+
this.dispose();
|
|
41
|
+
|
|
42
|
+
const { width, depth, height, radius, curbWidth, curbLip } = this.parameters;
|
|
43
|
+
|
|
44
|
+
if ( this.material === null ) this.material = createSidewalkMaterial();
|
|
45
|
+
if ( this.curbMaterial === null ) this.curbMaterial = createCurbMaterial();
|
|
46
|
+
|
|
47
|
+
// the walking slab and the curb are separate meshes so each carries its own material
|
|
48
|
+
const slab = new InstancedMesh( slabGeometry( width, depth, height, radius, curbWidth ), this.material, placements.length );
|
|
49
|
+
const curb = new InstancedMesh( curbGeometry( width, depth, height, radius, curbWidth, curbLip ), this.curbMaterial, placements.length );
|
|
50
|
+
|
|
51
|
+
for ( let i = 0; i < placements.length; i ++ ) {
|
|
52
|
+
|
|
53
|
+
slab.setMatrixAt( i, placements[ i ] );
|
|
54
|
+
curb.setMatrixAt( i, placements[ i ] );
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
slab.computeBoundingSphere();
|
|
59
|
+
curb.computeBoundingSphere();
|
|
60
|
+
slab.receiveShadow = curb.receiveShadow = true;
|
|
61
|
+
|
|
62
|
+
const group = new Group();
|
|
63
|
+
group.name = 'Sidewalk';
|
|
64
|
+
group.add( slab, curb );
|
|
65
|
+
|
|
66
|
+
this.mesh = group;
|
|
67
|
+
|
|
68
|
+
return group;
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
dispose() {
|
|
73
|
+
|
|
74
|
+
if ( this.mesh === null ) return;
|
|
75
|
+
|
|
76
|
+
this.mesh.traverse( ( o ) => o.geometry && o.geometry.dispose() );
|
|
77
|
+
this.mesh = null;
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
SidewalkGenerator.defaults = {
|
|
84
|
+
width: 90, // the block footprint each slab covers
|
|
85
|
+
depth: 60,
|
|
86
|
+
height: 0.5, // walking-surface height above the road
|
|
87
|
+
radius: 5, // corner radius, so the sidewalk turns each intersection instead of a hard 90°
|
|
88
|
+
curbWidth: 0.13, // top width of the granite kerbstone rimming the block ( ~5 in )
|
|
89
|
+
curbLip: 0.01 // how far the curb stands proud of the walking surface ( near-flush )
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// --- geometry ------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
// the block footprint as a rounded-corner rectangle ( centred at the origin ), so the
|
|
95
|
+
// sidewalk turns each intersection instead of meeting the kerb at a hard 90°
|
|
96
|
+
function roundedRect( width, depth, radius ) {
|
|
97
|
+
|
|
98
|
+
const w = width / 2;
|
|
99
|
+
const d = depth / 2;
|
|
100
|
+
const r = Math.min( radius, w, d );
|
|
101
|
+
|
|
102
|
+
const shape = new Shape();
|
|
103
|
+
shape.moveTo( - w + r, - d );
|
|
104
|
+
shape.lineTo( w - r, - d );
|
|
105
|
+
shape.quadraticCurveTo( w, - d, w, - d + r );
|
|
106
|
+
shape.lineTo( w, d - r );
|
|
107
|
+
shape.quadraticCurveTo( w, d, w - r, d );
|
|
108
|
+
shape.lineTo( - w + r, d );
|
|
109
|
+
shape.quadraticCurveTo( - w, d, - w, d - r );
|
|
110
|
+
shape.lineTo( - w, - d + r );
|
|
111
|
+
shape.quadraticCurveTo( - w, - d, - w + r, - d );
|
|
112
|
+
|
|
113
|
+
return shape;
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// extrude a footprint outline up by `height` ( the extrusion runs +Z; stand it up so height is +Y )
|
|
118
|
+
function extrudeUp( shape, height ) {
|
|
119
|
+
|
|
120
|
+
const geometry = new ExtrudeGeometry( shape, { depth: height, bevelEnabled: false, curveSegments: 6 } );
|
|
121
|
+
geometry.rotateX( - Math.PI / 2 );
|
|
122
|
+
|
|
123
|
+
return geometry;
|
|
124
|
+
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// the walking slab: the inner concrete surface, inset to sit inside the curb and overlapping
|
|
128
|
+
// it slightly so the seam is buried. base at y = 0, walking surface at `height`.
|
|
129
|
+
function slabGeometry( width, depth, height, radius, curbWidth ) {
|
|
130
|
+
|
|
131
|
+
const innerRadius = Math.max( 0.5, radius - curbWidth );
|
|
132
|
+
return extrudeUp( roundedRect( width - 2 * curbWidth + 0.06, depth - 2 * curbWidth + 0.06, innerRadius ), height );
|
|
133
|
+
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// the curb: a distinct full-height kerbstone band rimming the block ( the outline with an
|
|
137
|
+
// inset hole ), standing proud of the walking slab by `curbLip` and dropping to the road.
|
|
138
|
+
function curbGeometry( width, depth, height, radius, curbWidth, curbLip ) {
|
|
139
|
+
|
|
140
|
+
const innerRadius = Math.max( 0.5, radius - curbWidth );
|
|
141
|
+
const shape = roundedRect( width, depth, radius );
|
|
142
|
+
shape.holes.push( roundedRect( width - 2 * curbWidth, depth - 2 * curbWidth, innerRadius ) );
|
|
143
|
+
return extrudeUp( shape, height + curbLip );
|
|
144
|
+
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// --- material ------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
// derivative-based bump for a procedural, world-space height field. the built-in bumpMap
|
|
150
|
+
// offsets the UV to read its height, so it returns a zero gradient for a height keyed off
|
|
151
|
+
// world position; this feeds the hardware screen-space derivatives of the height into
|
|
152
|
+
// Mikkelsen's surface-gradient method so the relief actually perturbs the normal.
|
|
153
|
+
function bumpNormal( height ) {
|
|
154
|
+
|
|
155
|
+
const dpdx = positionView.dFdx();
|
|
156
|
+
const dpdy = positionView.dFdy();
|
|
157
|
+
const r1 = dpdy.cross( normalView );
|
|
158
|
+
const r2 = normalView.cross( dpdx );
|
|
159
|
+
const det = dpdx.dot( r1 );
|
|
160
|
+
const grad = det.sign().mul( height.dFdx().mul( r1 ).add( height.dFdy().mul( r2 ) ) );
|
|
161
|
+
|
|
162
|
+
return det.abs().mul( normalView ).sub( grad ).normalize();
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// an antialiased line repeated at every multiple of `period` ( the scored joints )
|
|
167
|
+
function gridLine( coord, period, halfWidth ) {
|
|
168
|
+
|
|
169
|
+
const g = coord.div( period );
|
|
170
|
+
const d = float( 0.5 ).sub( fract( g ).sub( 0.5 ).abs() ); // distance to nearest line, in periods
|
|
171
|
+
const aa = fwidth( g ).max( 0.0001 );
|
|
172
|
+
const hw = halfWidth / period;
|
|
173
|
+
return smoothstep( float( hw ).add( aa ), float( hw ).sub( aa ), d );
|
|
174
|
+
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// a noise term that only resolves up close: sampled inside a detail branch ( and kept in its
|
|
178
|
+
// own single-output Fn, so it is evaluated only in the output flow that consumes it )
|
|
179
|
+
function detailNoise( p, detail, scale, amp ) {
|
|
180
|
+
|
|
181
|
+
return Fn( () => {
|
|
182
|
+
|
|
183
|
+
const n = float( 0 ).toVar();
|
|
184
|
+
|
|
185
|
+
If( detail.greaterThan( 0 ), () => {
|
|
186
|
+
|
|
187
|
+
n.assign( mx_noise_float( p.mul( scale ) ).mul( amp ) );
|
|
188
|
+
|
|
189
|
+
} );
|
|
190
|
+
|
|
191
|
+
return n;
|
|
192
|
+
|
|
193
|
+
} )();
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function createSidewalkMaterial() {
|
|
198
|
+
|
|
199
|
+
// concrete flags: each poured slab a slightly different tone, fine aggregate speckle
|
|
200
|
+
// and expansion joints scored on a grid both ways
|
|
201
|
+
|
|
202
|
+
const p = positionWorld;
|
|
203
|
+
const detail = smoothstep( 200, 18, p.distance( cameraPosition ) );
|
|
204
|
+
|
|
205
|
+
const panel = 1.5; // flag size ( ~5 ft NYC sidewalk flags )
|
|
206
|
+
const panelHash = fract( sin( floor( p.x.div( panel ) ).mul( 127.1 ).add( floor( p.z.div( panel ) ).mul( 311.7 ) ) ).mul( 43758.5453 ) );
|
|
207
|
+
|
|
208
|
+
const tone = mx_noise_float( p.mul( 0.5 ) ).mul( 0.5 ).add( 0.5 );
|
|
209
|
+
|
|
210
|
+
// fine aggregate speckle ( grit, tinting the colour ) and grain relief ( driving the normal )
|
|
211
|
+
const grit = detailNoise( p, detail, 14, 0.07 ).mul( detail );
|
|
212
|
+
const grain = detailNoise( p, detail, 3, 0.003 );
|
|
213
|
+
|
|
214
|
+
const base = mix( color( 0x6f6f68 ), color( 0x8c8c82 ), tone ).mul( panelHash.sub( 0.5 ).mul( 0.16 ).add( 1 ) ); // per-flag tone
|
|
215
|
+
const concrete = base.add( grit );
|
|
216
|
+
|
|
217
|
+
const joints = gridLine( p.x, panel, 0.045 ).max( gridLine( p.z, panel, 0.045 ) ).mul( detail );
|
|
218
|
+
|
|
219
|
+
const material = new MeshStandardNodeMaterial();
|
|
220
|
+
material.colorNode = concrete.mul( joints.mul( 0.45 ).oneMinus() );
|
|
221
|
+
material.roughnessNode = float( 0.92 ).sub( panelHash.mul( 0.05 ) );
|
|
222
|
+
material.normalNode = bumpNormal( grain.sub( joints.mul( 0.012 ) ).mul( detail ) ); // world units: ~3 mm grain, ~12 mm scored joints
|
|
223
|
+
|
|
224
|
+
return material;
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function createCurbMaterial() {
|
|
229
|
+
|
|
230
|
+
// granite kerbstone: a dense, cool grey stone — darker and smoother than the concrete
|
|
231
|
+
// flags — with a fine speckle, segment joints every ~1.5 m and a grimier road-facing face
|
|
232
|
+
|
|
233
|
+
const p = positionWorld;
|
|
234
|
+
const detail = smoothstep( 200, 18, p.distance( cameraPosition ) );
|
|
235
|
+
|
|
236
|
+
const tone = mx_noise_float( p.mul( 0.6 ) ).mul( 0.5 ).add( 0.5 );
|
|
237
|
+
const stone = mix( color( 0x46463f ), color( 0x5c5c54 ), tone ).add( detailNoise( p, detail, 18, 0.05 ).mul( detail ) ); // dark cool granite, fine speckle
|
|
238
|
+
|
|
239
|
+
const seg = 1.5; // kerbstone segment length
|
|
240
|
+
const joints = gridLine( p.x, seg, 0.04 ).max( gridLine( p.z, seg, 0.04 ) ).mul( detail );
|
|
241
|
+
const top = smoothstep( 0.5, 0.85, normalWorldGeometry.y ); // 1 on the curb top, 0 on its walls
|
|
242
|
+
const dressed = mix( stone.mul( 0.7 ), stone, top ).mul( joints.mul( 0.4 ).oneMinus() ); // grimier on the road-facing face
|
|
243
|
+
|
|
244
|
+
const material = new MeshStandardNodeMaterial();
|
|
245
|
+
material.colorNode = dressed;
|
|
246
|
+
material.roughnessNode = float( 0.7 ).add( tone.mul( 0.1 ) ); // flamed granite: matte, a touch smoother than the concrete sidewalk
|
|
247
|
+
material.normalNode = bumpNormal( detailNoise( p, detail, 4, 0.002 ).mul( detail ) ); // fine granite grain
|
|
248
|
+
|
|
249
|
+
return material;
|
|
250
|
+
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export { SidewalkGenerator };
|