@gnsx/three 0.185.15 → 0.186.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 +10 -82600
- package/build/three.core.js +1303 -728
- package/build/three.module.js +439 -338
- package/build/three.tsl.js +74 -44
- package/build/three.webgpu.js +6036 -2184
- package/build/three.webgpu.nodes.js +6020 -2184
- package/examples/jsm/Addons.js +6 -6
- package/examples/jsm/controls/ArcballControls.js +1 -1
- package/examples/jsm/controls/FirstPersonControls.js +44 -20
- package/examples/jsm/controls/OrbitControls.js +9 -0
- package/examples/jsm/controls/TrackballControls.js +109 -4
- package/examples/jsm/csm/CSMShadowNode.js +13 -0
- package/examples/jsm/exporters/GLTFExporter.js +23 -7
- package/examples/jsm/exporters/USDZExporter.js +2 -2
- package/examples/jsm/generators/CityGenerator.js +377 -45
- package/examples/jsm/generators/ForestGenerator.js +12 -12
- package/examples/jsm/generators/TerrainGenerator.js +49 -37
- package/examples/jsm/generators/TreeGenerator.js +70 -76
- package/examples/jsm/generators/city/BenchGenerator.js +127 -0
- package/examples/jsm/generators/city/CarGenerator.js +479 -0
- package/examples/jsm/generators/city/CityGeneratorUtils.js +31 -0
- package/examples/jsm/generators/city/HydrantGenerator.js +106 -0
- package/examples/jsm/generators/city/InstancedMeshGenerator.js +86 -0
- package/examples/jsm/generators/city/PersonGenerator.js +447 -0
- package/examples/jsm/generators/city/SidewalkGenerator.js +33 -38
- package/examples/jsm/generators/city/SkyscraperGenerator.js +448 -48
- package/examples/jsm/generators/city/StreetTreeGenerator.js +231 -0
- package/examples/jsm/generators/city/StreetlightGenerator.js +100 -0
- package/examples/jsm/generators/city/TrafficlightGenerator.js +106 -0
- package/examples/jsm/generators/city/TrashcanGenerator.js +100 -0
- package/examples/jsm/geometries/BoxLineGeometry.js +1 -1
- package/examples/jsm/geometries/ParametricGeometry.js +1 -1
- package/examples/jsm/geometries/RoundedBoxGeometry.js +1 -1
- package/examples/jsm/gpgpu/CountingSort.js +271 -0
- package/examples/jsm/helpers/LightProbeGridHelper.js +60 -239
- package/examples/jsm/helpers/LightProbeGridHelperWebGL.js +221 -0
- package/examples/jsm/inspector/Extension.js +94 -0
- package/examples/jsm/inspector/Inspector.js +145 -41
- package/examples/jsm/inspector/RendererInspector.js +100 -52
- package/examples/jsm/inspector/extensions/color-grading/ColorGrading.js +2609 -0
- package/examples/jsm/inspector/extensions/color-grading/ColorWheel.js +352 -0
- package/examples/jsm/inspector/extensions/color-grading/CurveEditor.js +599 -0
- package/examples/jsm/inspector/extensions/color-grading/LUT3DStyle.js +1201 -0
- package/examples/jsm/inspector/extensions/color-grading/LUTMath.js +788 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/BrightnessModule.js +72 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/ColorWheelModule.js +112 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/ContrastModule.js +82 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/CurvesModule.js +109 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/ExposureHueModule.js +110 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/ExposureModule.js +73 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/HueModule.js +78 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/ImportedCubeModule.js +89 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/Module.js +285 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/OutputModule.js +101 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/RendererModule.js +225 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/SatVibranceModule.js +102 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/SaturationModule.js +76 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/VibranceModule.js +76 -0
- package/examples/jsm/inspector/extensions/color-grading/modules/WhiteBalanceModule.js +86 -0
- package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphEditor.js +26 -31
- package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphLoader.js +1 -0
- package/examples/jsm/inspector/tabs/Memory.js +0 -2
- package/examples/jsm/inspector/tabs/Settings.js +4 -0
- package/examples/jsm/inspector/tabs/Timeline.js +223 -171
- package/examples/jsm/inspector/tabs/Viewer.js +515 -21
- package/examples/jsm/inspector/ui/List.js +15 -9
- package/examples/jsm/inspector/ui/Profiler.js +170 -14
- package/examples/jsm/inspector/ui/Style.js +269 -23
- package/examples/jsm/inspector/ui/Tab.js +2 -0
- package/examples/jsm/inspector/ui/Values.js +14 -2
- package/examples/jsm/interactive/HTMLMesh.js +19 -3
- package/examples/jsm/lighting/LightProbeGrid.js +484 -889
- package/examples/jsm/lighting/LightProbeGridUtils.js +107 -0
- package/examples/jsm/lighting/LightProbeGridWebGL.js +841 -0
- package/examples/jsm/lighting/vxgi/VXGIConeTracer.js +194 -0
- package/examples/jsm/lighting/vxgi/VXGINode.js +660 -0
- package/examples/jsm/lighting/vxgi/VXGISceneCollector.js +478 -0
- package/examples/jsm/lighting/vxgi/VXGIVolume.js +1430 -0
- package/examples/jsm/lights/SunLight.js +95 -0
- package/examples/jsm/lights/SunLightNode.js +56 -0
- package/examples/jsm/lights/SunLightShadow.js +319 -0
- package/examples/jsm/lights/SunShadowNode.js +312 -0
- package/examples/jsm/loaders/3DMLoader.js +150 -49
- package/examples/jsm/loaders/3MFLoader.js +202 -1
- package/examples/jsm/loaders/FBXLoader.js +81 -5
- package/examples/jsm/loaders/GLTFGaussianSplatLoaderExtension.js +330 -0
- package/examples/jsm/loaders/GLTFLoader.js +79 -14
- package/examples/jsm/loaders/GaussianSplatPLYLoader.js +386 -0
- package/examples/jsm/loaders/KSPLATLoader.js +463 -0
- package/examples/jsm/loaders/MaterialXLoader.js +111 -1010
- package/examples/jsm/loaders/PCDLoader.js +1 -1
- package/examples/jsm/loaders/SPLATLoader.js +139 -0
- package/examples/jsm/loaders/SPZLoader.js +561 -0
- package/examples/jsm/loaders/materialx/MaterialXArchive.js +108 -0
- package/examples/jsm/loaders/materialx/MaterialXDocument.js +878 -0
- package/examples/jsm/loaders/materialx/MaterialXHextile.js +183 -0
- package/examples/jsm/loaders/materialx/MaterialXInterfaceValidation.js +376 -0
- package/examples/jsm/loaders/materialx/MaterialXLog.js +75 -0
- package/examples/jsm/loaders/materialx/MaterialXNodeInterfaceRegistry.js +4 -0
- package/examples/jsm/loaders/materialx/MaterialXNodeLibrary.js +1021 -0
- package/examples/jsm/loaders/materialx/MaterialXSurfaceMappings.js +661 -0
- package/examples/jsm/loaders/materialx/MaterialXUtils.js +57 -0
- package/examples/jsm/loaders/materialx/compile/MaterialXCompileRegistry.js +997 -0
- package/examples/jsm/loaders/materialx/parse/MaterialXParser.js +28 -0
- package/examples/jsm/loaders/usd/USDAParser.js +15 -2
- package/examples/jsm/loaders/usd/USDCParser.js +1 -4
- package/examples/jsm/loaders/usd/USDComposer.js +80 -42
- package/examples/jsm/materials/WoodNodeMaterial.js +6 -108
- package/examples/jsm/misc/RollerCoaster.js +2 -2
- package/examples/jsm/misc/SculptGL.LICENSE.txt +21 -0
- package/examples/jsm/misc/Sculptor.js +1679 -0
- package/examples/jsm/misc/SculptorMesh.js +1504 -0
- package/examples/jsm/misc/SculptorTools.js +1180 -0
- package/examples/jsm/misc/SculptorUtils.js +345 -0
- package/examples/jsm/misc/TubePainter.js +28 -16
- package/examples/jsm/misc/VolumeSlice.js +24 -24
- package/examples/jsm/modifiers/SimplifyModifier.js +63 -556
- package/examples/jsm/objects/GaussianSplat.js +1039 -0
- package/examples/jsm/objects/Sky.js +66 -45
- package/examples/jsm/objects/SkyMesh.js +70 -51
- package/examples/jsm/physics/JoltPhysics.js +1 -1
- package/examples/jsm/postprocessing/AfterimagePass.js +4 -2
- package/examples/jsm/postprocessing/BloomPass.js +2 -2
- package/examples/jsm/postprocessing/GTAOPass.js +1 -1
- package/examples/jsm/postprocessing/OutlinePass.js +5 -5
- package/examples/jsm/postprocessing/SAOPass.js +1 -1
- package/examples/jsm/postprocessing/SSAOPass.js +1 -1
- package/examples/jsm/postprocessing/SSRPass.js +4 -2
- package/examples/jsm/postprocessing/SavePass.js +1 -1
- package/examples/jsm/postprocessing/TAARenderPass.js +1 -1
- package/examples/jsm/postprocessing/UnrealBloomPass.js +30 -13
- package/examples/jsm/renderers/CSS2DRenderer.js +29 -5
- package/examples/jsm/shaders/DepthLimitedBlurShader.js +4 -1
- package/examples/jsm/shaders/ExposureShader.js +1 -1
- package/examples/jsm/shaders/FilmShader.js +2 -1
- package/examples/jsm/transpiler/AST.js +21 -2
- package/examples/jsm/transpiler/GLSLDecoder.js +498 -8
- package/examples/jsm/transpiler/TSLEncoder.js +82 -10
- package/examples/jsm/transpiler/WGSLEncoder.js +33 -4
- package/examples/jsm/tsl/WebGLNodesHandler.js +29 -39
- package/examples/jsm/tsl/display/AfterImageNode.js +2 -0
- package/examples/jsm/tsl/display/AnaglyphPassNode.js +3 -2
- package/examples/jsm/tsl/display/BilateralBlurNode.js +5 -2
- package/examples/jsm/tsl/display/BloomNode.js +55 -30
- package/examples/jsm/tsl/display/DenoiseNode.js +21 -1
- package/examples/jsm/tsl/display/DepthOfFieldNode.js +24 -6
- package/examples/jsm/tsl/display/DotScreenNode.js +11 -11
- package/examples/jsm/tsl/display/FSR1Node.js +8 -4
- package/examples/jsm/tsl/display/GTAONode.js +199 -86
- package/examples/jsm/tsl/display/GaussianBlurNode.js +65 -52
- package/examples/jsm/tsl/display/GodraysNode.js +5 -2
- package/examples/jsm/tsl/display/ImportanceSampledEnvironment.js +2 -2
- package/examples/jsm/tsl/display/LensflareNode.js +5 -2
- package/examples/jsm/tsl/display/OITPassNode.js +352 -0
- package/examples/jsm/tsl/display/OutlineNode.js +2 -0
- package/examples/jsm/tsl/display/ParallaxBarrierPassNode.js +3 -2
- package/examples/jsm/tsl/display/RGBShiftNode.js +9 -9
- package/examples/jsm/tsl/display/RecurrentDenoiseNode.js +7 -4
- package/examples/jsm/tsl/display/RetroPassNode.js +1 -1
- package/examples/jsm/tsl/display/SMAANode.js +22 -61
- package/examples/jsm/tsl/display/SSAAPassNode.js +2 -4
- package/examples/jsm/tsl/display/SSAONode.js +436 -0
- package/examples/jsm/tsl/display/SSGINode.js +5 -2
- package/examples/jsm/tsl/display/SSRNode.js +7 -2
- package/examples/jsm/tsl/display/SSSNode.js +5 -2
- package/examples/jsm/tsl/display/SharpenNode.js +5 -4
- package/examples/jsm/tsl/display/StereoCompositePassNode.js +1 -3
- package/examples/jsm/tsl/display/StereoPassNode.js +1 -3
- package/examples/jsm/tsl/display/TAAUNode.js +22 -125
- package/examples/jsm/tsl/display/TRAANode.js +16 -142
- package/examples/jsm/tsl/display/TemporalReprojectNode.js +21 -15
- package/examples/jsm/tsl/display/depthAwareBlur.js +63 -0
- package/examples/jsm/tsl/display/hashBlur.js +3 -2
- package/examples/jsm/tsl/lighting/ClusteredLightsNode.js +38 -27
- package/examples/jsm/tsl/lighting/LightProbeGridNode.js +136 -0
- package/examples/jsm/tsl/math/voronoiNoise.js +112 -0
- package/examples/jsm/tsl/shadows/TileShadowNode.js +4 -6
- package/examples/jsm/tsl/utils/Raymarching.js +5 -5
- package/examples/jsm/tsl/utils/SoftParticles.js +60 -0
- package/examples/jsm/tsl/utils/TAAUtils.js +195 -0
- package/examples/jsm/utils/BufferGeometryUtils.js +14 -14
- package/examples/jsm/utils/GaussianSplatUtils.js +229 -0
- package/examples/jsm/utils/WebGLTextureUtils.js +1 -1
- package/examples/jsm/utils/WebGPUTextureUtils.js +1 -1
- package/examples/jsm/webxr/WebGLXRFallback.js +2 -16
- package/examples/jsm/webxr/XRControllerModelFactory.js +16 -4
- package/package.json +9 -16
- package/src/Three.Core.js +2 -1
- package/src/Three.TSL.js +73 -43
- package/src/Three.WebGPU.Nodes.js +1 -0
- package/src/Three.WebGPU.js +1 -0
- package/src/animation/AnimationClip.js +15 -2
- package/src/animation/AnimationObjectGroup.js +18 -6
- package/src/animation/AnimationUtils.js +26 -0
- package/src/animation/KeyframeTrack.js +37 -0
- package/src/constants.js +23 -1
- package/src/core/BufferAttribute.js +3 -2
- package/src/core/BufferGeometry.js +1 -1
- package/src/core/InterleavedBuffer.js +5 -1
- package/src/core/Object3D.js +40 -12
- package/src/core/Raycaster.js +4 -0
- package/src/core/RenderTarget.js +99 -9
- package/src/core/RenderTarget3D.js +12 -8
- package/src/extras/Controls.js +0 -8
- package/src/extras/PMREMGenerator.js +99 -205
- package/src/geometries/TorusGeometry.js +1 -1
- package/src/helpers/ArrowHelper.js +2 -0
- package/src/helpers/AxesHelper.js +2 -0
- package/src/helpers/Box3Helper.js +2 -0
- package/src/helpers/BoxHelper.js +2 -0
- package/src/helpers/CameraHelper.js +2 -0
- package/src/helpers/DirectionalLightHelper.js +2 -0
- package/src/helpers/GridHelper.js +2 -0
- package/src/helpers/HemisphereLightHelper.js +2 -0
- package/src/helpers/PlaneHelper.js +2 -0
- package/src/helpers/PointLightHelper.js +2 -0
- package/src/helpers/PolarGridHelper.js +2 -0
- package/src/helpers/SkeletonHelper.js +2 -0
- package/src/helpers/SpotLightHelper.js +2 -0
- package/src/lights/Light.js +0 -10
- package/src/lights/LightShadow.js +43 -12
- package/src/lights/SpotLightShadow.js +18 -0
- package/src/loaders/BufferGeometryLoader.js +5 -0
- package/src/loaders/ImageBitmapLoader.js +3 -1
- package/src/loaders/ObjectLoader.js +8 -5
- package/src/materials/Material.js +80 -57
- package/src/materials/MeshPhysicalMaterial.js +32 -0
- package/src/materials/nodes/Line2NodeMaterial.js +17 -8
- package/src/materials/nodes/MeshPhysicalNodeMaterial.js +42 -5
- package/src/materials/nodes/NodeMaterial.js +19 -13
- package/src/materials/nodes/PointsNodeMaterial.js +2 -1
- package/src/materials/nodes/manager/NodeMaterialObserver.js +282 -55
- package/src/math/Frustum.js +9 -0
- package/src/math/FrustumArray.js +1 -1
- package/src/math/MathUtils.js +5 -3
- package/src/math/Matrix4.js +2 -9
- package/src/math/Plane.js +30 -1
- package/src/math/Quaternion.js +0 -10
- package/src/math/Ray.js +95 -45
- package/src/math/Triangle.js +0 -2
- package/src/math/Vector3.js +2 -2
- package/src/math/Vector4.js +0 -4
- package/src/math/interpolants/BezierInterpolant.js +36 -23
- package/src/nodes/Nodes.js +2 -0
- package/src/nodes/TSL.js +11 -3
- package/src/nodes/accessors/Batch.js +85 -13
- package/src/nodes/accessors/BufferAttributeNode.js +1 -1
- package/src/nodes/accessors/BuiltinNode.js +1 -1
- package/src/nodes/accessors/Instance.js +19 -18
- package/src/nodes/accessors/MaterialNode.js +9 -0
- package/src/nodes/accessors/ModelNode.js +85 -9
- package/src/nodes/accessors/ReferenceBaseNode.js +1 -70
- package/src/nodes/accessors/ReferenceElementNode.js +72 -0
- package/src/nodes/accessors/ReferenceNode.js +1 -70
- package/src/nodes/accessors/Skinning.js +124 -12
- package/src/nodes/accessors/StorageTextureNode.js +7 -3
- package/src/nodes/accessors/Texture3DNode.js +1 -1
- package/src/nodes/accessors/TextureNode.js +51 -21
- package/src/nodes/accessors/TextureSizeNode.js +4 -2
- package/src/nodes/accessors/UniformArrayNode.js +30 -5
- package/src/nodes/code/CodeNode.js +1 -1
- package/src/nodes/code/FunctionNode.js +12 -2
- package/src/nodes/core/ArrayNode.js +2 -2
- package/src/nodes/core/ContextNode.js +57 -4
- package/src/nodes/core/IndexNode.js +69 -4
- package/src/nodes/core/InspectorNode.js +2 -2
- package/src/nodes/core/MRTNode.js +44 -2
- package/src/nodes/core/Node.js +6 -3
- package/src/nodes/core/NodeBuilder.js +65 -50
- package/src/nodes/core/NodeFrame.js +11 -3
- package/src/nodes/core/NodeUtils.js +88 -0
- package/src/nodes/core/PropertyNode.js +8 -0
- package/src/nodes/core/StackNode.js +0 -1
- package/src/nodes/core/StructNode.js +4 -3
- package/src/nodes/core/UniformGroupNode.js +3 -2
- package/src/nodes/core/UniformNode.js +26 -12
- package/src/nodes/core/VarNode.js +0 -1
- package/src/nodes/display/BumpMapNode.js +2 -2
- package/src/nodes/display/ColorAdjustment.js +1 -1
- package/src/nodes/display/PassNode.js +76 -15
- package/src/nodes/display/ScreenNode.js +22 -65
- package/src/nodes/fog/Fog.js +4 -4
- package/src/nodes/functions/BSDF/BRDF_GGX.js +6 -6
- package/src/nodes/functions/BSDF/BRDF_Sheen.js +1 -1
- package/src/nodes/functions/BSDF/DFGLUT.js +2 -1
- package/src/nodes/functions/BSDF/LTC.js +1 -1
- package/src/nodes/functions/PhongLightingModel.js +1 -1
- package/src/nodes/functions/PhysicalLightingModel.js +83 -25
- package/src/nodes/functions/ToonLightingModel.js +2 -2
- package/src/nodes/functions/material/getParallaxCorrectNormal.js +3 -3
- package/src/nodes/gpgpu/AtomicFunctionNode.js +64 -13
- package/src/nodes/gpgpu/BarrierNode.js +13 -4
- package/src/nodes/gpgpu/ComputeBuiltinNode.js +1 -1
- package/src/nodes/gpgpu/ComputeNode.js +20 -17
- package/src/nodes/gpgpu/SubgroupFunctionNode.js +165 -37
- package/src/nodes/gpgpu/WorkgroupInfoNode.js +44 -6
- package/src/nodes/lighting/EnvironmentNode.js +40 -4
- package/src/nodes/lighting/LightsNode.js +20 -4
- package/src/nodes/lighting/ShadowBaseNode.js +158 -1
- package/src/nodes/lighting/ShadowFilterNode.js +2 -106
- package/src/nodes/lighting/ShadowNode.js +22 -91
- package/src/nodes/materialx/{lib/mx_hsv.js → MaterialXColor.js} +3 -6
- package/src/nodes/materialx/{lib/mx_transform_color.js → MaterialXColorTransform.js} +3 -6
- package/src/nodes/materialx/MaterialXCore.js +37 -0
- package/src/nodes/materialx/MaterialXNodes.js +64 -72
- package/src/nodes/materialx/{lib/mx_noise.js → MaterialXNoise.js} +426 -197
- package/src/nodes/math/BitcountNode.js +24 -4
- package/src/nodes/math/MathNode.js +4 -4
- package/src/nodes/math/MathUtils.js +15 -4
- package/src/nodes/math/OperatorNode.js +2 -2
- package/src/nodes/math/PackFloatNode.js +29 -1
- package/src/nodes/math/Packed4x8IntegerNode.js +433 -0
- package/src/nodes/math/UnpackFloatNode.js +30 -2
- package/src/nodes/parsers/GLSLNodeFunction.js +1 -1
- package/src/nodes/pmrem/PMREMNode.js +1 -1
- package/src/nodes/pmrem/PMREMUtils.js +31 -74
- package/src/nodes/shapes/Shapes.js +1 -1
- package/src/nodes/tsl/TSLCore.js +1 -31
- package/src/nodes/utils/EventNode.js +75 -0
- package/src/nodes/utils/LoopNode.js +54 -13
- package/src/nodes/utils/PostProcessingUtils.js +23 -2
- package/src/nodes/utils/RTTNode.js +97 -32
- package/src/nodes/utils/ReflectorNode.js +8 -4
- package/src/nodes/utils/RotateNode.js +85 -8
- package/src/nodes/utils/SpriteUtils.js +58 -23
- package/src/objects/BatchedMesh.js +6 -0
- package/src/objects/InstancedMesh.js +1 -1
- package/src/objects/LOD.js +1 -1
- package/src/objects/Line.js +12 -0
- package/src/objects/Mesh.js +12 -0
- package/src/objects/Points.js +12 -0
- package/src/objects/Sprite.js +12 -0
- package/src/renderers/WebGLRenderer.js +93 -27
- package/src/renderers/common/Animation.js +12 -2
- package/src/renderers/common/Backend.js +38 -10
- package/src/renderers/common/BindGroup.js +0 -1
- package/src/renderers/common/Bindings.js +50 -4
- package/src/renderers/common/BundleGroup.js +4 -0
- package/src/renderers/common/ClippingContext.js +11 -1
- package/src/renderers/common/Color4.js +9 -0
- package/src/renderers/common/Constants.js +0 -5
- package/src/renderers/common/DirectRenderPipeline.js +233 -0
- package/src/renderers/common/Geometries.js +24 -3
- package/src/renderers/common/Info.js +3 -3
- package/src/renderers/common/InspectorBase.js +37 -8
- package/src/renderers/common/Lighting.js +11 -4
- package/src/renderers/common/Pipelines.js +6 -4
- package/src/renderers/common/QuadMesh.js +16 -2
- package/src/renderers/common/RenderContext.js +8 -0
- package/src/renderers/common/RenderList.js +53 -19
- package/src/renderers/common/RenderLists.js +58 -11
- package/src/renderers/common/RenderObject.js +14 -0
- package/src/renderers/common/RenderObjects.js +14 -9
- package/src/renderers/common/RenderPipeline.js +52 -44
- package/src/renderers/common/Renderer.js +226 -55
- package/src/renderers/common/Storage3DTexture.js +16 -0
- package/src/renderers/common/Textures.js +3 -3
- package/src/renderers/common/XRManager.js +79 -72
- package/src/renderers/common/extras/PMREMGenerator.js +76 -144
- package/src/renderers/common/nodes/NodeManager.js +144 -6
- package/src/renderers/common/nodes/NodeStorageBuffer.js +3 -1
- package/src/renderers/common/nodes/NodeUniformBuffer.js +3 -1
- package/src/renderers/shaders/DFGLUTData.js +2 -1
- package/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js +4 -2
- package/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js +48 -0
- package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +53 -4
- package/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js +20 -2
- package/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js +20 -0
- package/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +6 -0
- package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +61 -57
- package/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js +82 -0
- package/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl.js +9 -0
- package/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js +8 -1
- package/src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl.js +15 -0
- package/src/renderers/shaders/ShaderLib/meshphysical.glsl.js +4 -0
- package/src/renderers/shaders/ShaderLib.js +1 -0
- package/src/renderers/shaders/UniformsLib.js +16 -0
- package/src/renderers/webgl/WebGLAnimation.js +2 -2
- package/src/renderers/webgl/WebGLCapabilities.js +2 -2
- package/src/renderers/webgl/WebGLLights.js +74 -4
- package/src/renderers/webgl/WebGLMaterials.js +6 -0
- package/src/renderers/webgl/WebGLOutput.js +36 -24
- package/src/renderers/webgl/WebGLProgram.js +3 -0
- package/src/renderers/webgl/WebGLPrograms.js +8 -0
- package/src/renderers/webgl/WebGLRenderLists.js +6 -10
- package/src/renderers/webgl/WebGLShadowMap.js +47 -44
- package/src/renderers/webgl/WebGLTextures.js +7 -7
- package/src/renderers/webgl-fallback/WebGLBackend.js +153 -45
- package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +83 -29
- package/src/renderers/webgl-fallback/utils/WebGLState.js +163 -17
- package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +32 -5
- package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +11 -4
- package/src/renderers/webgpu/WebGPUBackend.js +513 -145
- package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +99 -20
- package/src/renderers/webgpu/nodes/WGSLNodeFunction.js +2 -1
- package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +3 -12
- package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +24 -14
- package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +80 -14
- package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +62 -7
- package/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +9 -3
- package/src/renderers/webgpu/utils/WebGPUUtils.js +16 -4
- package/src/renderers/webxr/WebXRManager.js +25 -2
- package/src/scenes/Scene.js +3 -3
- package/src/textures/CompressedArrayTexture.js +16 -0
- package/src/textures/Data3DTexture.js +16 -0
- package/src/textures/DataArrayTexture.js +16 -0
- package/src/textures/DepthTexture.js +3 -3
- package/src/textures/Texture.js +3 -3
- package/src/textures/{Source.js → TextureSource.js} +39 -8
- package/build/three.core.min.js +0 -6
- package/build/three.module.min.js +0 -6
- package/build/three.tsl.min.js +0 -6
- package/build/three.webgpu.min.js +0 -6
- package/build/three.webgpu.nodes.min.js +0 -6
- package/examples/jsm/libs/ecsy.module.js +0 -1792
- package/examples/jsm/loaders/LottieLoader.js +0 -130
- package/src/nodes/functions/BSDF/BRDF_GGX_Multiscatter.js +0 -52
|
@@ -20,9 +20,10 @@ import {
|
|
|
20
20
|
} from '@gnsx/three';
|
|
21
21
|
|
|
22
22
|
import { MeshStandardNodeMaterial } from '@gnsx/three/webgpu';
|
|
23
|
-
import { attribute, cameraPosition, color, cross, dot, float, floor, Fn, fract, fwidth, hash as ihash, mix, mod, modelWorldMatrixInverse, mx_fractal_noise_float,
|
|
23
|
+
import { attribute, cameraPosition, color, cross, dot, float, floor, Fn, fract, fwidth, hash as ihash, mix, mod, modelWorldMatrixInverse, mx_fractal_noise_float, normalLocal, normalWorldGeometry, positionLocal, positionWorld, select, smoothstep, step, uint, uv, varying, vec2, vec3, vec4 } from '@gnsx/three/tsl';
|
|
24
24
|
|
|
25
25
|
import { mergeGeometries } from '../../utils/BufferGeometryUtils.js';
|
|
26
|
+
import { bumpNormal } from './CityGeneratorUtils.js';
|
|
26
27
|
|
|
27
28
|
const _scale = /*@__PURE__*/ new Vector3();
|
|
28
29
|
const _point = /*@__PURE__*/ new Vector3();
|
|
@@ -31,8 +32,8 @@ const _identity = /*@__PURE__*/ new Matrix4();
|
|
|
31
32
|
|
|
32
33
|
// material-zone codes baked per vertex into the merged geometry, so one material can
|
|
33
34
|
// branch on partId and shade every zone
|
|
34
|
-
const PartId = { WALL: 0, PIER: 1, FRAME: 2, ORNAMENT: 3, GLASS: 4, AC: 5 };
|
|
35
|
-
const { WALL, PIER, FRAME, ORNAMENT, GLASS, AC } = PartId;
|
|
35
|
+
const PartId = { WALL: 0, PIER: 1, FRAME: 2, ORNAMENT: 3, GLASS: 4, AC: 5, SHOPGLASS: 6, STORE: 7, AWNING: 8 };
|
|
36
|
+
const { WALL, PIER, FRAME, ORNAMENT, GLASS, AC, SHOPGLASS, STORE, AWNING } = PartId;
|
|
36
37
|
|
|
37
38
|
// fraction of a floor's height taken by the glazed opening; the remainder is
|
|
38
39
|
// the spandrel band. shared by the window module and the spandrels so they tile.
|
|
@@ -65,6 +66,9 @@ function nonIndexed( geometry ) {
|
|
|
65
66
|
// the unit box is identical for every building's shell boxes — build it once
|
|
66
67
|
const _unitBox = /*@__PURE__*/ nonIndexed( new BoxGeometry( 1, 1, 1 ) );
|
|
67
68
|
|
|
69
|
+
// a unit quad ( facing +Z ), scaled per placement for the storefront display glazing
|
|
70
|
+
const _unitPlane = /*@__PURE__*/ nonIndexed( new PlaneGeometry( 1, 1 ) );
|
|
71
|
+
|
|
68
72
|
/**
|
|
69
73
|
* Bakes a list of instance groups into one non-indexed BufferGeometry. Each group is a
|
|
70
74
|
* base geometry ( position + normal + uv ), an array of Matrix4 placements and a `partId`
|
|
@@ -223,7 +227,8 @@ function randomStyle( random ) {
|
|
|
223
227
|
windowReveal: 0.12 + random() * 0.1,
|
|
224
228
|
stringCourseHeight: 0.5 + random() * 0.5,
|
|
225
229
|
archBayWidthRatio: Math.round( 1.5 + random() * 1.5 ),
|
|
226
|
-
archRise: 0.4 + random() * 0.5
|
|
230
|
+
archRise: 0.4 + random() * 0.5,
|
|
231
|
+
baseStyle: random() < 0.22 ? 'arcade' : 'storefront' // most towers get retail storefronts; a few take a grand arcade
|
|
227
232
|
};
|
|
228
233
|
|
|
229
234
|
}
|
|
@@ -307,6 +312,11 @@ class SkyscraperGenerator {
|
|
|
307
312
|
const glassRooms = []; // per-glass interior-mapping room ( centre + size ), aligned with `glass`
|
|
308
313
|
const backWalls = []; // the thin wall closing the volume behind the glass
|
|
309
314
|
const bands = []; // spandrel bands, one at each floor line
|
|
315
|
+
const shopGlass = []; // ground-floor display glazing ( scaled unit quads )
|
|
316
|
+
const shopRooms = []; // interior-mapping room per shop, aligned with shopGlass
|
|
317
|
+
const mullions = []; // slim shopfront dividers
|
|
318
|
+
const storeBands = []; // storefront bulkhead and signboard fascia
|
|
319
|
+
const awnings = []; // projecting shop awnings
|
|
310
320
|
const piers = new Map(); // pier height -> matrices, so each tier's continuous piers share one geometry
|
|
311
321
|
const trim = []; // cornices and parapets ( axis-aligned unit boxes )
|
|
312
322
|
const acUnits = []; // window air-conditioner boxes on a random subset of shaft windows
|
|
@@ -340,12 +350,23 @@ class SkyscraperGenerator {
|
|
|
340
350
|
|
|
341
351
|
const crownCornice = p.stringCourseHeight * 1.6; // the crown's heavy cap; its piers stop below it
|
|
342
352
|
|
|
343
|
-
//
|
|
353
|
+
// the street-level retail floor; a few larger towers take a grand arcade instead
|
|
354
|
+
const groundHeight = p.floorHeight;
|
|
355
|
+
const useArcade = p.baseStyle === 'arcade' && baseHeight > groundHeight * 1.5;
|
|
356
|
+
|
|
357
|
+
// shaft and crown are the same facade over different faces, spans and pier heights;
|
|
358
|
+
// the masonry base above the storefront is that facade again
|
|
344
359
|
const tiers = [
|
|
345
360
|
{ faces, bottom: baseTop, height: shaftHeight, pierHeight: shaftHeight, ac: acUnits },
|
|
346
361
|
{ faces: crownFaces, bottom: shaftTop, height: crownHeight, pierHeight: crownHeight - crownCornice, ac: null }
|
|
347
362
|
];
|
|
348
363
|
|
|
364
|
+
if ( ! useArcade && baseHeight > groundHeight + 0.1 ) {
|
|
365
|
+
|
|
366
|
+
tiers.push( { faces, bottom: groundHeight, height: baseHeight - groundHeight, pierHeight: baseHeight - groundHeight, ac: null } );
|
|
367
|
+
|
|
368
|
+
}
|
|
369
|
+
|
|
349
370
|
for ( const t of tiers ) {
|
|
350
371
|
|
|
351
372
|
for ( const frame of t.faces ) {
|
|
@@ -359,10 +380,14 @@ class SkyscraperGenerator {
|
|
|
359
380
|
|
|
360
381
|
}
|
|
361
382
|
|
|
362
|
-
// the
|
|
383
|
+
// the ground floor: NYC retail storefronts, or a grand gothic arcade. either is
|
|
384
|
+
// capped by the base string course
|
|
385
|
+
const storefront = { glass: shopGlass, glassRooms: shopRooms, mullions, storeBands, backWalls, awnings, addPier };
|
|
363
386
|
for ( const frame of faces ) {
|
|
364
387
|
|
|
365
|
-
addArcade( extras, frame, baseHeight, p );
|
|
388
|
+
if ( useArcade ) addArcade( extras, frame, baseHeight, p );
|
|
389
|
+
else addStorefront( frame, groundHeight, p, storefront );
|
|
390
|
+
|
|
366
391
|
addCornice( trim, frame, baseTop - p.stringCourseHeight, p.stringCourseHeight, 0.5 );
|
|
367
392
|
|
|
368
393
|
}
|
|
@@ -399,6 +424,10 @@ class SkyscraperGenerator {
|
|
|
399
424
|
const groups = [
|
|
400
425
|
{ geometry: buildWindowGeometry( p ), matrices: windows, partId: FRAME, rigid: true },
|
|
401
426
|
{ geometry: nonIndexed( buildGlassGeometry( p ) ), matrices: glass, partId: GLASS, rooms: glassRooms, rigid: true },
|
|
427
|
+
{ geometry: _unitPlane, matrices: shopGlass, partId: SHOPGLASS, rooms: shopRooms }, // scaled per shop, so not rigid
|
|
428
|
+
{ geometry: _unitBox, matrices: mullions, partId: FRAME },
|
|
429
|
+
{ geometry: _unitBox, matrices: storeBands, partId: STORE },
|
|
430
|
+
{ geometry: _unitBox, matrices: awnings, partId: AWNING },
|
|
402
431
|
{ geometry: _unitBox, matrices: bands, partId: WALL }
|
|
403
432
|
];
|
|
404
433
|
|
|
@@ -435,6 +464,9 @@ class SkyscraperGenerator {
|
|
|
435
464
|
if ( this.mesh === null ) return;
|
|
436
465
|
|
|
437
466
|
this.mesh.geometry.dispose();
|
|
467
|
+
this.mesh.dispose();
|
|
468
|
+
if ( this.material === null ) this.mesh.material.dispose();
|
|
469
|
+
|
|
438
470
|
this.mesh = null;
|
|
439
471
|
|
|
440
472
|
}
|
|
@@ -630,10 +662,20 @@ function addSpandrelBands( target, frame, vBottom, height, p ) {
|
|
|
630
662
|
// into the plane of the perpendicular face at the corners ( overdraw )
|
|
631
663
|
const bandLength = Math.max( 0.2, frame.length - 0.6 );
|
|
632
664
|
|
|
665
|
+
const vTop = vBottom + height;
|
|
666
|
+
|
|
633
667
|
for ( let f = 0; f <= floors; f ++ ) {
|
|
634
668
|
|
|
669
|
+
// the end bands ( f = 0 / f = floors ) are centred on the tier boundary; clamp
|
|
670
|
+
// them so they don't extend into the neighbouring tier
|
|
671
|
+
const center = vBottom + f * fh;
|
|
672
|
+
const top = Math.min( center + bandHeight / 2, vTop );
|
|
673
|
+
const bottom = Math.max( center - bandHeight / 2, vBottom );
|
|
674
|
+
const h = top - bottom;
|
|
675
|
+
if ( h <= 0 ) continue;
|
|
676
|
+
|
|
635
677
|
// front flush at w = 0, meeting the backing wall behind
|
|
636
|
-
target.push( boxMatrix( frame, frame.length / 2,
|
|
678
|
+
target.push( boxMatrix( frame, frame.length / 2, ( top + bottom ) / 2, - 0.3, bandLength, h, 0.6 ) );
|
|
637
679
|
|
|
638
680
|
}
|
|
639
681
|
|
|
@@ -709,9 +751,49 @@ function addParapet( target, frame, vTop, p ) {
|
|
|
709
751
|
|
|
710
752
|
}
|
|
711
753
|
|
|
754
|
+
// the arch openings' inner reveal walls, each hole outline swept back to the wall
|
|
755
|
+
// thickness; holes wind clockwise, so an edge's inward normal is ( dy, -dx )
|
|
756
|
+
function buildArchReveals( holes, depth, curveSegments ) {
|
|
757
|
+
|
|
758
|
+
const positions = [], normals = [], uvs = [];
|
|
759
|
+
|
|
760
|
+
for ( const hole of holes ) {
|
|
761
|
+
|
|
762
|
+
const points = hole.getPoints( curveSegments );
|
|
763
|
+
|
|
764
|
+
for ( let i = 0; i < points.length - 1; i ++ ) {
|
|
765
|
+
|
|
766
|
+
const a = points[ i ], b = points[ i + 1 ];
|
|
767
|
+
const dx = b.x - a.x, dy = b.y - a.y;
|
|
768
|
+
const inv = 1 / ( Math.hypot( dx, dy ) || 1 );
|
|
769
|
+
const nx = dy * inv, ny = - dx * inv;
|
|
770
|
+
|
|
771
|
+
// the two triangles of the quad a → b, both facing into the opening
|
|
772
|
+
positions.push( a.x, a.y, 0, a.x, a.y, - depth, b.x, b.y, - depth );
|
|
773
|
+
positions.push( a.x, a.y, 0, b.x, b.y, - depth, b.x, b.y, 0 );
|
|
774
|
+
|
|
775
|
+
for ( let v = 0; v < 6; v ++ ) {
|
|
776
|
+
|
|
777
|
+
normals.push( nx, ny, 0 );
|
|
778
|
+
uvs.push( 0, 0 );
|
|
779
|
+
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
const geometry = new BufferGeometry();
|
|
787
|
+
geometry.setAttribute( 'position', new BufferAttribute( new Float32Array( positions ), 3 ) );
|
|
788
|
+
geometry.setAttribute( 'normal', new BufferAttribute( new Float32Array( normals ), 3 ) );
|
|
789
|
+
geometry.setAttribute( 'uv', new BufferAttribute( new Float32Array( uvs ), 2 ) );
|
|
790
|
+
return geometry;
|
|
791
|
+
|
|
792
|
+
}
|
|
793
|
+
|
|
712
794
|
/**
|
|
713
|
-
* The base storey: a wall pierced by tall pointed-arch openings,
|
|
714
|
-
*
|
|
795
|
+
* The base storey: a wall pierced by tall pointed-arch openings, built as a flat front
|
|
796
|
+
* face and the openings' reveals so they read as deep recesses.
|
|
715
797
|
*/
|
|
716
798
|
function addArcade( target, frame, height, p ) {
|
|
717
799
|
|
|
@@ -746,8 +828,12 @@ function addArcade( target, frame, height, p ) {
|
|
|
746
828
|
}
|
|
747
829
|
|
|
748
830
|
const thickness = 1.1;
|
|
749
|
-
const
|
|
750
|
-
|
|
831
|
+
const curveSegments = 8;
|
|
832
|
+
|
|
833
|
+
// flat front face plus the swept reveals; the openings show through to the dark plane behind
|
|
834
|
+
const front = new ShapeGeometry( shape, curveSegments );
|
|
835
|
+
const reveals = buildArchReveals( shape.holes, thickness, curveSegments );
|
|
836
|
+
const geometry = merge( [ front, reveals ] );
|
|
751
837
|
geometry.applyMatrix4( frame.matrix( 0, 0, 0 ) );
|
|
752
838
|
|
|
753
839
|
target.push( geometry );
|
|
@@ -760,6 +846,65 @@ function addArcade( target, frame, height, p ) {
|
|
|
760
846
|
|
|
761
847
|
}
|
|
762
848
|
|
|
849
|
+
/**
|
|
850
|
+
* The street-level retail front: glazed shopfronts on a low bulkhead, under a
|
|
851
|
+
* signboard fascia, framed by the building's ground-floor piers and split by slim
|
|
852
|
+
* mullions, with a projecting awning over some shops. Reads as a row of NYC
|
|
853
|
+
* storefronts. All pieces are pushed into the caller's `parts` accumulators.
|
|
854
|
+
*/
|
|
855
|
+
function addStorefront( frame, height, p, parts ) {
|
|
856
|
+
|
|
857
|
+
const bulkhead = 0.5; // the solid kickplate the glazing stands on
|
|
858
|
+
const fascia = Math.min( 0.9, height * 0.22 ); // the signboard band above the glass
|
|
859
|
+
const glassBottom = bulkhead;
|
|
860
|
+
const glassHeight = height - fascia - bulkhead;
|
|
861
|
+
const len = frame.length;
|
|
862
|
+
|
|
863
|
+
// a stone bulkhead and a projecting signboard band run the full face
|
|
864
|
+
parts.storeBands.push( boxMatrix( frame, len / 2, bulkhead / 2, 0, len, bulkhead, 0.55 ) );
|
|
865
|
+
parts.storeBands.push( boxMatrix( frame, len / 2, height - fascia / 2, 0.08, len, fascia, 0.72 ) );
|
|
866
|
+
|
|
867
|
+
// the shop interior wall behind the glazing
|
|
868
|
+
addWall( parts.backWalls, frame, 0, height, 0.8, - 0.6 );
|
|
869
|
+
|
|
870
|
+
// shopfronts are wider than the window bays above, so group bays into ~4 m shops
|
|
871
|
+
const shopWidth = Math.max( 4, p.bayWidth * 2 );
|
|
872
|
+
const { count, margin, width } = frame.bays( shopWidth );
|
|
873
|
+
|
|
874
|
+
for ( let i = 0; i < count; i ++ ) {
|
|
875
|
+
|
|
876
|
+
const x0 = margin + i * width;
|
|
877
|
+
const cx = x0 + width / 2;
|
|
878
|
+
|
|
879
|
+
// a structural pier at each shopfront edge, full ground-floor height ( the far
|
|
880
|
+
// end is the shared corner the next face piers, like addPiers )
|
|
881
|
+
parts.addPier( frame, x0, 0, height );
|
|
882
|
+
|
|
883
|
+
// the display glazing for this shop, set back behind the frame, with the
|
|
884
|
+
// interior-mapping room it looks into ( one shop-wide, ground-floor-tall box )
|
|
885
|
+
const reveal = 0.18;
|
|
886
|
+
const gw = width - p.pierWidth - 0.12;
|
|
887
|
+
const cy = glassBottom + glassHeight / 2;
|
|
888
|
+
parts.glass.push( boxMatrix( frame, cx, cy, - reveal, gw, glassHeight, 1 ) );
|
|
889
|
+
parts.glassRooms.push( { center: frame.point( cx, cy, - reveal ), size: new Vector2( gw, glassHeight ) } );
|
|
890
|
+
|
|
891
|
+
// two slim mullions dividing the shopfront into three lights
|
|
892
|
+
parts.mullions.push( boxMatrix( frame, x0 + width / 3, glassBottom + glassHeight / 2, 0, 0.08, glassHeight, 0.16 ) );
|
|
893
|
+
parts.mullions.push( boxMatrix( frame, x0 + width * 2 / 3, glassBottom + glassHeight / 2, 0, 0.08, glassHeight, 0.16 ) );
|
|
894
|
+
|
|
895
|
+
// an awning over roughly half the shops, projecting from below the signboard
|
|
896
|
+
const r = Math.sin( i * 23.7 + frame.origin.x * 0.21 + frame.origin.z * 0.11 ) * 43758.5453;
|
|
897
|
+
if ( r - Math.floor( r ) < 0.5 ) {
|
|
898
|
+
|
|
899
|
+
const depth = 1.3;
|
|
900
|
+
parts.awnings.push( boxMatrix( frame, cx, height - fascia - 0.12, depth / 2 + 0.05, width - 0.3, 0.14, depth ) );
|
|
901
|
+
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
}
|
|
907
|
+
|
|
763
908
|
// --- repeating field -----------------------------------------------------
|
|
764
909
|
|
|
765
910
|
function addPiers( frame, vBottom, height, p, addPier ) {
|
|
@@ -960,23 +1105,6 @@ function buildFinialGeometry( p ) {
|
|
|
960
1105
|
|
|
961
1106
|
// --- material ------------------------------------------------------------
|
|
962
1107
|
|
|
963
|
-
// derivative-based bump for a procedural, world-space height field. the built-in bumpMap
|
|
964
|
-
// offsets the UV to read its height, so it returns a zero gradient for a height keyed off
|
|
965
|
-
// world position; this feeds the hardware screen-space derivatives of the height into
|
|
966
|
-
// Mikkelsen's surface-gradient method so the relief actually perturbs the normal.
|
|
967
|
-
function bumpNormal( height ) {
|
|
968
|
-
|
|
969
|
-
const dpdx = positionView.dFdx();
|
|
970
|
-
const dpdy = positionView.dFdy();
|
|
971
|
-
const r1 = dpdy.cross( normalView );
|
|
972
|
-
const r2 = normalView.cross( dpdx );
|
|
973
|
-
const det = dpdx.dot( r1 );
|
|
974
|
-
const grad = det.sign().mul( height.dFdx().mul( r1 ).add( height.dFdy().mul( r2 ) ) );
|
|
975
|
-
|
|
976
|
-
return det.abs().mul( normalView ).sub( grad ).normalize();
|
|
977
|
-
|
|
978
|
-
}
|
|
979
|
-
|
|
980
1108
|
// interior mapping: fakes a furnished room behind each glass pane in the fragment
|
|
981
1109
|
// shader — no geometry, no texture. every pane carries the room it looks into ( centre +
|
|
982
1110
|
// size, baked per window by addWindows ), so neighbouring panes share one interior. the
|
|
@@ -1169,6 +1297,202 @@ const interior = /*@__PURE__*/ Fn( () => {
|
|
|
1169
1297
|
|
|
1170
1298
|
} );
|
|
1171
1299
|
|
|
1300
|
+
// the ground-floor shops' interior mapping: the same box raymarch as interior(), but
|
|
1301
|
+
// dressed as retail space ( tile floor, troffer-lit ceiling, stocked shelving, a sales
|
|
1302
|
+
// counter and a window display ), so storefronts read as shops rather than living rooms.
|
|
1303
|
+
// most shops trade with the lights on; the rest sit dark, some behind a roller grille.
|
|
1304
|
+
// returns vec4( colour, lit ), like interior().
|
|
1305
|
+
const shopInterior = /*@__PURE__*/ Fn( () => {
|
|
1306
|
+
|
|
1307
|
+
// flat for the same reason as interior(): floor() must not split a pane across cells
|
|
1308
|
+
const roomCenter = varying( attribute( 'roomCenter', 'vec3' ) ).setInterpolation( InterpolationSamplingType.FLAT, InterpolationSamplingMode.EITHER );
|
|
1309
|
+
const roomSize = attribute( 'roomSize', 'vec2' );
|
|
1310
|
+
|
|
1311
|
+
// the per-face frame and the view ray in room space ( across, up, depth ), as interior()
|
|
1312
|
+
const n = normalLocal;
|
|
1313
|
+
const up = vec3( 0, 1, 0 );
|
|
1314
|
+
const uAxis = cross( up, n ).normalize();
|
|
1315
|
+
|
|
1316
|
+
const d = positionLocal.sub( roomCenter );
|
|
1317
|
+
const camLocal = modelWorldMatrixInverse.mul( vec4( cameraPosition, 1 ) ).xyz;
|
|
1318
|
+
const rayLocal = positionLocal.sub( camLocal ).normalize();
|
|
1319
|
+
const origin = vec3( dot( d, uAxis ), d.y, 0 );
|
|
1320
|
+
const dir = vec3( dot( rayLocal, uAxis ), rayLocal.y, dot( rayLocal, n ).negate() );
|
|
1321
|
+
|
|
1322
|
+
// the shop box: retail runs deeper than the flats above
|
|
1323
|
+
const setback = float( 0.1 );
|
|
1324
|
+
const depth = roomSize.y.mul( 2.4 );
|
|
1325
|
+
const boxMax = vec3( roomSize.x.mul( 0.5 ), roomSize.y.mul( 0.5 ), setback.add( depth ) );
|
|
1326
|
+
const boxMin = vec3( boxMax.x.negate(), boxMax.y.negate(), setback );
|
|
1327
|
+
const tFar = boxMin.sub( origin ).div( dir ).max( boxMax.sub( origin ).div( dir ) );
|
|
1328
|
+
const t = tFar.x.min( tFar.y ).min( tFar.z );
|
|
1329
|
+
const hit = origin.add( dir.mul( t ) );
|
|
1330
|
+
const q = hit.sub( boxMin ).div( boxMax.sub( boxMin ) ); // 0..1 inside the shop
|
|
1331
|
+
|
|
1332
|
+
const onBack = q.z.greaterThan( 0.998 );
|
|
1333
|
+
const onCeil = q.y.greaterThan( 0.998 );
|
|
1334
|
+
const onFloor = q.y.lessThan( 0.002 );
|
|
1335
|
+
|
|
1336
|
+
// per-shop key + hash, the same scheme as interior()
|
|
1337
|
+
const cell = floor( roomCenter.mul( 2.0 ) );
|
|
1338
|
+
const ckey = uint( cell.x.add( 1 << 21 ) ).mul( uint( 73856093 ) )
|
|
1339
|
+
.bitXor( uint( cell.y.add( 1 << 21 ) ).mul( uint( 19349663 ) ) )
|
|
1340
|
+
.bitXor( uint( cell.z.add( 1 << 21 ) ).mul( uint( 83492791 ) ) ).toVar();
|
|
1341
|
+
const hash = ( kx, ky, kz ) => ihash( ckey.add( uint( Math.round( ( kx + ky * 7 + kz * 13 ) * 100 ) ) ) );
|
|
1342
|
+
const seed = hash( 12.9898, 78.233, 37.719 );
|
|
1343
|
+
const seed2 = hash( 39.346, 11.135, 83.155 );
|
|
1344
|
+
|
|
1345
|
+
const lit = step( 0.18, hash( 63.21, 9.17, 51.43 ) ); // most shops trade with the lights on; the rest sit closed
|
|
1346
|
+
const market = step( 0.45, hash( 71.7, 23.9, 8.3 ) ); // shelf-stocked store, else a sparser showroom / boutique
|
|
1347
|
+
|
|
1348
|
+
// retail lighting: mostly cool fluorescent / LED whites, with a warm halogen minority
|
|
1349
|
+
// ( boutiques, cafes ), so a shopping street reads as a mix of trades
|
|
1350
|
+
const coolLight = mix( color( 0xeef2f6 ), color( 0xdfe8ff ), hash( 8.3, 51.2, 17.6 ) );
|
|
1351
|
+
const warmLight = mix( color( 0xffd9a0 ), color( 0xffeacb ), hash( 27.1, 4.9, 61.7 ) );
|
|
1352
|
+
const lightCol = select( hash( 44.7, 19.3, 6.1 ).greaterThan( 0.65 ), warmLight, coolLight );
|
|
1353
|
+
|
|
1354
|
+
// depth falloff: a lit shop carries its lighting deep into the room, a dark one falls away fast
|
|
1355
|
+
const falloffAt = ( z ) => mix( float( 1.0 ), mix( float( 0.25 ), float( 0.6 ), lit ), z.sub( setback ).div( depth ).clamp( 0, 1 ) );
|
|
1356
|
+
const rect = ( ax, ay, cx, cy, hw, hh ) => smoothstep( hw + 0.006, hw - 0.006, ax.sub( cx ).abs() ).mul( smoothstep( hh + 0.006, hh - 0.006, ay.sub( cy ).abs() ) );
|
|
1357
|
+
|
|
1358
|
+
// merchandise: a goods colour per shelf cell, from a compact retail palette
|
|
1359
|
+
const goods = ( k ) => {
|
|
1360
|
+
|
|
1361
|
+
const pick = k.mul( 6 );
|
|
1362
|
+
let c = color( 0x9e3d33 ); // red
|
|
1363
|
+
c = select( pick.greaterThan( 1 ), color( 0xb2802f ), c ); // amber
|
|
1364
|
+
c = select( pick.greaterThan( 2 ), color( 0x3d6b4f ), c ); // green
|
|
1365
|
+
c = select( pick.greaterThan( 3 ), color( 0x35578a ), c ); // blue
|
|
1366
|
+
c = select( pick.greaterThan( 4 ), color( 0xd6d0c4 ), c ); // white
|
|
1367
|
+
c = select( pick.greaterThan( 5 ), color( 0x2b2830 ), c ); // black
|
|
1368
|
+
return c;
|
|
1369
|
+
|
|
1370
|
+
};
|
|
1371
|
+
|
|
1372
|
+
// a stocked shelf unit over ( ax, ay ) in 0..1: a board at the foot of each band, goods
|
|
1373
|
+
// of per-cell colour / height / width standing on it, dark backing where stock thins out
|
|
1374
|
+
const shelfBoard = mix( color( 0x6b5a43 ), color( 0xcfccc4 ), step( 0.5, seed2 ) ); // wood or white metal, per shop
|
|
1375
|
+
const shelves = ( ax, ay, cols, salt ) => {
|
|
1376
|
+
|
|
1377
|
+
const bands = 4;
|
|
1378
|
+
const cellKey = ckey.add( uint( salt ) ).add( uint( floor( ax.mul( cols ) ) ).mul( uint( 197 ) ) ).add( uint( floor( ay.mul( bands ) ) ).mul( uint( 4099 ) ) );
|
|
1379
|
+
const ch = ihash( cellKey );
|
|
1380
|
+
const ch2 = ihash( cellKey.add( uint( 1 ) ) );
|
|
1381
|
+
const gx = fract( ax.mul( cols ) );
|
|
1382
|
+
const gy = fract( ay.mul( bands ) );
|
|
1383
|
+
const item = step( 0.08, gy ).mul( step( gy, mix( float( 0.4 ), float( 0.85 ), fract( ch2.mul( 5 ) ) ) ) )
|
|
1384
|
+
.mul( step( gx.sub( 0.5 ).abs(), mix( float( 0.26 ), float( 0.46 ), fract( ch2.mul( 13 ) ) ) ) )
|
|
1385
|
+
.mul( step( 0.15, ch2 ) ); // the odd cell sits empty
|
|
1386
|
+
const stocked = mix( color( 0x211e1b ), goods( ch ), item ); // dark backing behind the goods
|
|
1387
|
+
return mix( stocked, shelfBoard, step( gy, 0.08 ) );
|
|
1388
|
+
|
|
1389
|
+
};
|
|
1390
|
+
|
|
1391
|
+
// --- the shop shell -----------------------------------------------------
|
|
1392
|
+
|
|
1393
|
+
// light retail paint; boutiques take a muted brand colour on the back wall
|
|
1394
|
+
const wallCol = mix( color( 0xd8d2c6 ), color( 0xbfb9ae ), seed );
|
|
1395
|
+
|
|
1396
|
+
// floor: pale terrazzo tile with dark seams, or a checker for the odd deli / diner
|
|
1397
|
+
const tileSeam = step( 0.93, fract( q.x.mul( 8 ) ) ).max( step( 0.93, fract( q.z.mul( 10 ) ) ) );
|
|
1398
|
+
const terrazzo = mix( color( 0xb5afa2 ), color( 0x958f83 ), seed2 ).mul( tileSeam.mul( 0.35 ).oneMinus() );
|
|
1399
|
+
const checker = mix( color( 0x24262a ), color( 0xcfccc2 ), mod( floor( q.x.mul( 8 ) ).add( floor( q.z.mul( 10 ) ) ), 2 ) );
|
|
1400
|
+
const floorCol = select( hash( 5.7, 33.1, 2.9 ).greaterThan( 0.75 ), checker, terrazzo );
|
|
1401
|
+
|
|
1402
|
+
// ceiling: a suspended white grid whose recessed troffer panels carry the light
|
|
1403
|
+
const tx = fract( q.x.mul( 3 ) ).sub( 0.5 ).abs();
|
|
1404
|
+
const tz = fract( q.z.mul( 4 ) ).sub( 0.5 ).abs();
|
|
1405
|
+
const troffer = step( tx, 0.3 ).mul( step( tz, 0.15 ) );
|
|
1406
|
+
const ceilCol = mix( color( 0xe9e6df ), lightCol.mul( mix( float( 0.5 ), float( 6.0 ), lit ) ), troffer );
|
|
1407
|
+
|
|
1408
|
+
// back wall: a market is shelved wall to wall; a boutique hangs a framed poster
|
|
1409
|
+
// on its brand-painted wall
|
|
1410
|
+
const posterX = mix( float( 0.3 ), float( 0.7 ), seed );
|
|
1411
|
+
let showroom = mix( goods( seed2 ), wallCol, 0.45 );
|
|
1412
|
+
showroom = mix( showroom, color( 0xe8e4da ), rect( q.x, q.y, posterX, 0.62, 0.1, 0.14 ) ); // the mat
|
|
1413
|
+
showroom = mix( showroom, goods( hash( 3.1, 44.2, 7.7 ) ), rect( q.x, q.y, posterX, 0.62, 0.075, 0.1 ).mul( 0.85 ) ); // the artwork
|
|
1414
|
+
const backCol = select( market, shelves( q.x, q.y, roomSize.x.mul( 2.5 ), 31 ), showroom );
|
|
1415
|
+
|
|
1416
|
+
// side walls: markets shelve those too
|
|
1417
|
+
const sideCol = select( market, shelves( q.z, q.y, depth.mul( 2.5 ), 57 ), wallCol );
|
|
1418
|
+
|
|
1419
|
+
const shellCol = select( onBack, backCol, select( onCeil, ceilCol, select( onFloor, floorCol, sideCol ) ) );
|
|
1420
|
+
|
|
1421
|
+
// the same soft corner shading as interior()
|
|
1422
|
+
const aoBand = 0.15;
|
|
1423
|
+
const aoEdge = ( a ) => smoothstep( 0, aoBand, a ).mul( smoothstep( 0, aoBand, a.oneMinus() ) );
|
|
1424
|
+
const edgeAO = select( onBack, aoEdge( q.x ).mul( aoEdge( q.y ) ), select( onFloor.or( onCeil ), aoEdge( q.x ).mul( aoEdge( q.z ) ), aoEdge( q.y ).mul( aoEdge( q.z ) ) ) );
|
|
1425
|
+
const shellAO = mix( float( 0.72 ), float( 1.0 ), edgeAO );
|
|
1426
|
+
|
|
1427
|
+
// --- nearest surface: the shell, then any fitting that lies closer ------
|
|
1428
|
+
|
|
1429
|
+
let bestT = t;
|
|
1430
|
+
let bestCol = shellCol.mul( shellAO ).mul( falloffAt( hit.z ) );
|
|
1431
|
+
let bestEmit = float( 1 );
|
|
1432
|
+
|
|
1433
|
+
const boxHit = ( bMin, bMax ) => {
|
|
1434
|
+
|
|
1435
|
+
const ta = bMin.sub( origin ).div( dir );
|
|
1436
|
+
const tb = bMax.sub( origin ).div( dir );
|
|
1437
|
+
const lo = ta.min( tb ), hi = ta.max( tb );
|
|
1438
|
+
const tN = lo.x.max( lo.y ).max( lo.z );
|
|
1439
|
+
const p = origin.add( dir.mul( tN ) );
|
|
1440
|
+
return { tN, p, hit: hi.x.min( hi.y ).min( hi.z ).greaterThan( tN ).and( tN.greaterThan( 0 ) ), qb: p.sub( bMin ).div( bMax.sub( bMin ) ) };
|
|
1441
|
+
|
|
1442
|
+
};
|
|
1443
|
+
|
|
1444
|
+
const consider = ( h, tN, c, emit = 1 ) => {
|
|
1445
|
+
|
|
1446
|
+
const near = h.and( tN.lessThan( bestT ) ); bestCol = select( near, c, bestCol ); bestEmit = select( near, float( emit ), bestEmit ); bestT = select( near, tN, bestT );
|
|
1447
|
+
|
|
1448
|
+
};
|
|
1449
|
+
|
|
1450
|
+
const halfU = boxMax.x, floorY = boxMin.y, ceilY = boxMax.y, backZ = boxMax.z;
|
|
1451
|
+
|
|
1452
|
+
// the window display: a low platform just inside the glass, dressed with a few
|
|
1453
|
+
// pedestals, each topped by an item of merchandise
|
|
1454
|
+
const plat = boxHit( vec3( halfU.negate().add( 0.15 ), floorY, setback.add( 0.06 ) ), vec3( halfU.sub( 0.15 ), floorY.add( 0.4 ), setback.add( 0.95 ) ) );
|
|
1455
|
+
const platCol = select( plat.qb.y.greaterThan( 0.93 ), color( 0xd9d4c8 ), color( 0x35302a ) ); // a light top on a dark plinth
|
|
1456
|
+
consider( plat.hit, plat.tN, platCol.mul( falloffAt( plat.p.z ) ) );
|
|
1457
|
+
|
|
1458
|
+
for ( let i = 0; i < 3; i ++ ) {
|
|
1459
|
+
|
|
1460
|
+
const g = hash( 9.1 + i, 17.3, 40.7 );
|
|
1461
|
+
const g2 = hash( 4.7, 29.1 + i, 13.9 );
|
|
1462
|
+
const cx = halfU.mul( ( i - 1 ) * 0.62 );
|
|
1463
|
+
const h = mix( float( 0.5 ), float( 1.2 ), g );
|
|
1464
|
+
const w = mix( float( 0.16 ), float( 0.3 ), g2 );
|
|
1465
|
+
const ped = boxHit( vec3( cx.sub( w ), floorY.add( 0.4 ), setback.add( 0.25 ) ), vec3( cx.add( w ), floorY.add( 0.4 ).add( h ), setback.add( 0.25 ).add( w.mul( 2 ) ) ) );
|
|
1466
|
+
const pedCol = select( ped.qb.y.greaterThan( 0.6 ), goods( g ), color( 0xdad5cb ) ); // the item rides a light pedestal
|
|
1467
|
+
consider( ped.hit.and( g2.greaterThan( 0.15 ) ), ped.tN, pedCol.mul( falloffAt( ped.p.z ) ) );
|
|
1468
|
+
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
// the sales counter, standing mid-shop toward one side
|
|
1472
|
+
const midZ = setback.add( depth.mul( 0.5 ) );
|
|
1473
|
+
const cCx = mix( halfU.negate().add( 0.7 ), halfU.sub( 0.7 ), step( 0.5, seed ) );
|
|
1474
|
+
const counter = boxHit( vec3( cCx.sub( 0.75 ), floorY, midZ ), vec3( cCx.add( 0.75 ), floorY.add( 1.0 ), midZ.add( 0.65 ) ) );
|
|
1475
|
+
const counterCol = mix( color( 0x4a4036 ), color( 0x2c333a ), seed2 ).mul( select( counter.qb.y.greaterThan( 0.92 ), float( 1.3 ), float( 0.85 ) ) );
|
|
1476
|
+
consider( counter.hit, counter.tN, counterCol.mul( falloffAt( counter.p.z ) ) );
|
|
1477
|
+
|
|
1478
|
+
// markets run a central gondola shelf down the aisle, stocked like the walls
|
|
1479
|
+
const gz0 = setback.add( 1.2 ), gz1 = backZ.sub( 1.2 );
|
|
1480
|
+
const gond = boxHit( vec3( - 0.45, floorY, gz0 ), vec3( 0.45, floorY.add( 1.5 ), gz1 ) );
|
|
1481
|
+
const gondCol = shelves( gond.p.z.sub( gz0 ).div( gz1.sub( gz0 ) ), gond.qb.y, 8, 91 );
|
|
1482
|
+
consider( gond.hit.and( market.greaterThan( 0.5 ) ), gond.tN, gondCol.mul( falloffAt( gond.p.z ) ) );
|
|
1483
|
+
|
|
1484
|
+
// some dark shops pull a steel roller grille down just inside the glass
|
|
1485
|
+
const grille = boxHit( vec3( halfU.negate(), floorY, setback ), vec3( halfU, ceilY, setback.add( 0.05 ) ) );
|
|
1486
|
+
const grilleCol = mix( color( 0x4c5158 ), color( 0x191b1e ), step( 0.62, fract( grille.p.y.mul( 7 ) ) ) ); // slats over shadowed gaps
|
|
1487
|
+
const closed = lit.oneMinus().mul( step( 0.45, hash( 15.9, 3.3, 27.5 ) ) );
|
|
1488
|
+
consider( grille.hit.and( closed.greaterThan( 0.5 ) ), grille.tN, grilleCol.mul( 0.8 ), 0 );
|
|
1489
|
+
|
|
1490
|
+
// a trading shop glows with its retail lighting; a closed one sits dim
|
|
1491
|
+
const warmth = mix( vec3( 1.0, 1.0, 1.0 ), lightCol, lit.mul( 0.6 ) );
|
|
1492
|
+
return vec4( bestCol.mul( warmth ).mul( mix( float( 0.45 ), float( 1.5 ), lit ) ), lit.mul( bestEmit ) );
|
|
1493
|
+
|
|
1494
|
+
} );
|
|
1495
|
+
|
|
1172
1496
|
/**
|
|
1173
1497
|
* The NYC masonry palette every tower is dressed from ( hex colours ): limestone-dominant
|
|
1174
1498
|
* with terracotta accents. Shared by the single-tower example and {@link CityGenerator}'s
|
|
@@ -1192,6 +1516,46 @@ function pickBuildingColor( seed ) {
|
|
|
1192
1516
|
|
|
1193
1517
|
}
|
|
1194
1518
|
|
|
1519
|
+
// cheap value noise ( ~[ -1, 1 ] ), a lighter stand-in for gradient mx_noise on the
|
|
1520
|
+
// weathering terms; integer-hashed ( not fract(sin) ) to stay stable across drivers
|
|
1521
|
+
const valueNoise = /*@__PURE__*/ Fn( ( [ p ] ) => {
|
|
1522
|
+
|
|
1523
|
+
const i = floor( p );
|
|
1524
|
+
const f = fract( p );
|
|
1525
|
+
const u = f.mul( f ).mul( f.mul( - 2 ).add( 3 ) ); // 3f2 - 2f3 smooth interpolation
|
|
1526
|
+
const corner = ( ox, oy, oz ) => {
|
|
1527
|
+
|
|
1528
|
+
const c = i.add( vec3( ox, oy, oz ) );
|
|
1529
|
+
return ihash( uint( c.x.add( 1 << 20 ) ).mul( uint( 73856093 ) ).bitXor( uint( c.y.add( 1 << 20 ) ).mul( uint( 19349663 ) ) ).bitXor( uint( c.z.add( 1 << 20 ) ).mul( uint( 83492791 ) ) ) );
|
|
1530
|
+
|
|
1531
|
+
};
|
|
1532
|
+
|
|
1533
|
+
const x00 = mix( corner( 0, 0, 0 ), corner( 1, 0, 0 ), u.x );
|
|
1534
|
+
const x10 = mix( corner( 0, 1, 0 ), corner( 1, 1, 0 ), u.x );
|
|
1535
|
+
const x01 = mix( corner( 0, 0, 1 ), corner( 1, 0, 1 ), u.x );
|
|
1536
|
+
const x11 = mix( corner( 0, 1, 1 ), corner( 1, 1, 1 ), u.x );
|
|
1537
|
+
return mix( mix( x00, x10, u.y ), mix( x01, x11, u.y ), u.z ).mul( 2 ).sub( 1 );
|
|
1538
|
+
|
|
1539
|
+
} ).setLayout( { name: 'valueNoise', type: 'float', inputs: [ { name: 'p', type: 'vec3' } ] } );
|
|
1540
|
+
|
|
1541
|
+
// fractal ( fBm ) of valueNoise, octaves summed like mx_fractal_noise_float
|
|
1542
|
+
// ( amplitude halving, frequency doubling ); unrolled for a compile-time count
|
|
1543
|
+
const valueFractal = ( p, octaves ) => {
|
|
1544
|
+
|
|
1545
|
+
let sum = valueNoise( p );
|
|
1546
|
+
let amp = 0.5, freq = 2;
|
|
1547
|
+
for ( let o = 1; o < octaves; o ++ ) {
|
|
1548
|
+
|
|
1549
|
+
sum = sum.add( valueNoise( p.mul( freq ) ).mul( amp ) );
|
|
1550
|
+
amp *= 0.5;
|
|
1551
|
+
freq *= 2;
|
|
1552
|
+
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
return sum;
|
|
1556
|
+
|
|
1557
|
+
};
|
|
1558
|
+
|
|
1195
1559
|
/**
|
|
1196
1560
|
* The facade material: a single MeshStandardNodeMaterial that reads the baked
|
|
1197
1561
|
* per-vertex `partId` and reproduces every zone — procedural terracotta brickwork
|
|
@@ -1210,8 +1574,8 @@ function createSkyscraperMaterial( buildingBase = color( 0xc6c0b2 ) ) {
|
|
|
1210
1574
|
// across instanced and merged meshes: a slow tonal drift, a fine clay mottle,
|
|
1211
1575
|
// and sooty vertical streaks that pool low down
|
|
1212
1576
|
|
|
1213
|
-
const tone = mx_fractal_noise_float( positionWorld.mul( 0.03 ), 2 ).mul( 0.18 );
|
|
1214
|
-
const mottle =
|
|
1577
|
+
const tone = varying( mx_fractal_noise_float( positionWorld.mul( 0.03 ), 2 ) ).mul( 0.18 ); // very low frequency: evaluate per-vertex and interpolate over the facade's fine tessellation
|
|
1578
|
+
const mottle = valueNoise( positionWorld.mul( 0.7 ) ).mul( 0.06 );
|
|
1215
1579
|
const streak = mx_fractal_noise_float( vec3( positionWorld.x.mul( 1.5 ), positionWorld.y.mul( 0.04 ), positionWorld.z.mul( 1.5 ) ), 2 );
|
|
1216
1580
|
const dirt = smoothstep( - 0.1, 0.45, streak ).mul( smoothstep( 210, 0, positionWorld.y ) ).mul( 0.6 );
|
|
1217
1581
|
|
|
@@ -1225,7 +1589,7 @@ function createSkyscraperMaterial( buildingBase = color( 0xc6c0b2 ) ) {
|
|
|
1225
1589
|
|
|
1226
1590
|
const brickH = BRICK.height;
|
|
1227
1591
|
const brickL = BRICK.length;
|
|
1228
|
-
const mortar = 0.
|
|
1592
|
+
const mortar = 0.010; // joint width, in metres
|
|
1229
1593
|
|
|
1230
1594
|
const nrm = normalWorldGeometry.abs();
|
|
1231
1595
|
const across = positionLocal.x.mul( normalWorldGeometry.z ).sub( positionLocal.z.mul( normalWorldGeometry.x ) );
|
|
@@ -1260,12 +1624,12 @@ function createSkyscraperMaterial( buildingBase = color( 0xc6c0b2 ) ) {
|
|
|
1260
1624
|
// course line, 0 at the joint, 0.5 at the centre ), so bricks read rounded rather than
|
|
1261
1625
|
// scratched. the bevel is widened to at least a screen pixel ( from the world-position
|
|
1262
1626
|
// derivative, our stand-in for a mip LOD ) so the edge never goes sub-pixel and shimmers.
|
|
1263
|
-
const bevel = 0.
|
|
1627
|
+
const bevel = 0.008;
|
|
1264
1628
|
const texel = fwidth( positionWorld ).length(); // on-screen size of a surface pixel — our hand-rolled LOD
|
|
1265
1629
|
const lodBevel = texel.mul( 1.5 ).max( bevel );
|
|
1266
1630
|
const brickFace = smoothstep( 0, lodBevel, distU.mul( brickL ) ).mul( smoothstep( 0, lodBevel, distV.mul( brickH ) ) ).mul( wallFacing );
|
|
1267
|
-
const reliefHeight = brickFace.mul( 0.
|
|
1268
|
-
const rough =
|
|
1631
|
+
const reliefHeight = brickFace.mul( 0.003 );
|
|
1632
|
+
const rough = valueNoise( positionWorld.mul( 0.5 ) ).mul( 0.08 ).add( 0.82 ).add( joint.mul( 0.12 ) );
|
|
1269
1633
|
|
|
1270
1634
|
// the merged geometry carries a per-vertex partId; this material reads it and
|
|
1271
1635
|
// branches to reproduce each zone — no per-part materials, compute-raster friendly
|
|
@@ -1275,6 +1639,9 @@ function createSkyscraperMaterial( buildingBase = color( 0xc6c0b2 ) ) {
|
|
|
1275
1639
|
const isFrame = partId.equal( FRAME );
|
|
1276
1640
|
const isOrnament = partId.equal( ORNAMENT );
|
|
1277
1641
|
const isAC = partId.equal( AC );
|
|
1642
|
+
const isShopGlass = partId.equal( SHOPGLASS );
|
|
1643
|
+
const isStore = partId.equal( STORE );
|
|
1644
|
+
const isAwning = partId.equal( AWNING );
|
|
1278
1645
|
|
|
1279
1646
|
// stone zones: brick + weathering on the building's colour, lightened for
|
|
1280
1647
|
// piers / ornament and darkened for window frames
|
|
@@ -1290,13 +1657,14 @@ function createSkyscraperMaterial( buildingBase = color( 0xc6c0b2 ) ) {
|
|
|
1290
1657
|
// larger-scale grime instead of the wall's streaky soot — confined to those surfaces by a
|
|
1291
1658
|
// branch ( roofMask > 0 ), so the fractal never runs on the vertical facade
|
|
1292
1659
|
const roofMask = wallFacing.oneMinus();
|
|
1293
|
-
const roofGrime = select( roofMask.greaterThan( 0 ), smoothstep( 0.0, 0.55,
|
|
1660
|
+
const roofGrime = select( roofMask.greaterThan( 0 ), smoothstep( 0.0, 0.55, valueFractal( positionWorld.mul( 0.025 ), 3 ) ).mul( 0.22 ), float( 0 ) );
|
|
1294
1661
|
const stoneColor = mix( masonry, soot, mix( dirt, roofGrime, roofMask ) );
|
|
1295
1662
|
|
|
1296
1663
|
// glass: the interior-mapped room is the base colour; the smooth, low-roughness
|
|
1297
1664
|
// surface still lets a faint sky reflection ride over it, and lit rooms glow ( emissive ).
|
|
1298
|
-
//
|
|
1299
|
-
|
|
1665
|
+
// shopfronts march their own retail interior ( select compiles to a branch, so only one
|
|
1666
|
+
// raymarch runs per fragment ). toVar so the result is shared by colour and emissive
|
|
1667
|
+
const room = select( isShopGlass, shopInterior(), interior() ).toVar();
|
|
1300
1668
|
|
|
1301
1669
|
// grimy glazing: the room shows through, but muted by a dusty film and dirt pooled
|
|
1302
1670
|
// along the bottom of each pane, plus a baseline haze, so the panes read as old
|
|
@@ -1306,8 +1674,12 @@ function createSkyscraperMaterial( buildingBase = color( 0xc6c0b2 ) ) {
|
|
|
1306
1674
|
const dustStreak = smoothstep( - 0.15, 0.5, filmNoise ).mul( 0.45 );
|
|
1307
1675
|
const pooled = smoothstep( 0.32, 0.0, uv().y ).mul( 0.4 );
|
|
1308
1676
|
const grime = float( 0.64 ).add( dustStreak ).add( pooled ).clamp( 0, 0.95 ); // baseline haze so the panes read as dirty glass, not open holes
|
|
1309
|
-
const
|
|
1310
|
-
const
|
|
1677
|
+
const glassMottle = valueFractal( positionWorld.mul( 0.3 ), 2 ).mul( 0.5 ).add( 0.5 ); // shared by the upper dirty glass and the shop glazing, so the fractal runs once
|
|
1678
|
+
const dirtyGlass = mix( color( 0x13161a ), color( 0x232b31 ), glassMottle );
|
|
1679
|
+
|
|
1680
|
+
// dirt scatters the reflection too: rougher where grime pools at the sills, so the
|
|
1681
|
+
// panes don't all mirror the sun in unison
|
|
1682
|
+
const glassRough = float( 0.16 ).add( pooled.mul( 0.45 ) ).add( dustStreak.mul( 0.2 ) );
|
|
1311
1683
|
|
|
1312
1684
|
// window frames are smooth dressed stone, not brick
|
|
1313
1685
|
const frameColor = buildingBase.mul( 0.55 );
|
|
@@ -1322,9 +1694,9 @@ function createSkyscraperMaterial( buildingBase = color( 0xc6c0b2 ) ) {
|
|
|
1322
1694
|
const acLouver = acVent.mul( acDetail );
|
|
1323
1695
|
|
|
1324
1696
|
// plastic shell: off-white, some units dingier / yellowed than others
|
|
1325
|
-
const acDinge =
|
|
1697
|
+
const acDinge = valueNoise( positionWorld.mul( 0.4 ) ).mul( 0.5 ).add( 0.5 ); // ~per-unit
|
|
1326
1698
|
const acPaint = mix( color( 0xf2f1ec ), color( 0xcfccc2 ), acDinge ) // bright white → light dingy grey, both lighter than the wall
|
|
1327
|
-
.add(
|
|
1699
|
+
.add( valueNoise( positionWorld.mul( 5 ) ).mul( 0.04 ) );
|
|
1328
1700
|
|
|
1329
1701
|
// a darker recessed grille panel inset into the lighter cabinet, with horizontal louvers
|
|
1330
1702
|
// inside it ( the front vents ) — the white plastic reads as a thin border frame
|
|
@@ -1335,7 +1707,7 @@ function createSkyscraperMaterial( buildingBase = color( 0xc6c0b2 ) ) {
|
|
|
1335
1707
|
const acBody = acPaint.mul( mix( float( 1 ), acFin.mul( 0.42 ), acGrille ) ); // cabinet stays light; recessed grille goes dark grey
|
|
1336
1708
|
|
|
1337
1709
|
// grey-brown condensate grime streaking the lower edge ( plastic doesn't rust ); dirtier units streak more
|
|
1338
|
-
const acStreak =
|
|
1710
|
+
const acStreak = valueFractal( vec3( positionWorld.x.mul( 6 ), positionWorld.y.mul( 0.5 ), positionWorld.z.mul( 6 ) ), 3 ).mul( 0.5 ).add( 0.5 );
|
|
1339
1711
|
const acGrime = smoothstep( 0.4, 0.0, acUv.y ).mul( acStreak ).mul( acDinge.add( 0.3 ) );
|
|
1340
1712
|
const acColor = mix( acBody, color( 0x6f685a ), acGrime.mul( 0.5 ) );
|
|
1341
1713
|
|
|
@@ -1343,12 +1715,40 @@ function createSkyscraperMaterial( buildingBase = color( 0xc6c0b2 ) ) {
|
|
|
1343
1715
|
const acRelief = acGrille.mul( acSlats.mul( 0.012 ).sub( 0.01 ) );
|
|
1344
1716
|
const acRough = float( 0.52 ).add( acGrille.mul( 0.08 ) );
|
|
1345
1717
|
|
|
1718
|
+
// --- storefronts --------------------------------------------------------
|
|
1719
|
+
|
|
1720
|
+
// display glazing: upper-floor and shop panes both show the raymarched room, the
|
|
1721
|
+
// shops behind cleaner, barely-filmed plate glass. the shared `room` toVar must be
|
|
1722
|
+
// referenced only once per output flow ( referencing it from two select branches
|
|
1723
|
+
// miscompiles the interior ), so select the room-free tint / backing / film here and
|
|
1724
|
+
// composite the room a single time below
|
|
1725
|
+
const shopFilm = float( 0.14 ).add( dustStreak.mul( 0.22 ) ); // shops are far cleaner than the grimy upper panes
|
|
1726
|
+
const glassTint = select( isShopGlass, color( 0xccd4cf ), color( 0xb6c6bf ) ); // shop daylight vs the upper soda-lime green
|
|
1727
|
+
const glassBack = select( isShopGlass, color( 0x161a1e ), dirtyGlass );
|
|
1728
|
+
const glassFilm = select( isShopGlass, shopFilm, grime );
|
|
1729
|
+
const glazingColor = mix( room.xyz.mul( glassTint ), glassBack, glassFilm );
|
|
1730
|
+
const glazingEmit = select( isShopGlass, float( 2 ), float( 2.2 ).mul( grime.mul( 0.6 ).oneMinus() ) ); // lit-room glow weight, kept a soft fill so signals and lamps read brighter
|
|
1731
|
+
|
|
1732
|
+
// bulkhead and signboard: a dark painted storefront frame, keyed off the building's colour
|
|
1733
|
+
const storeColor = mix( buildingBase.mul( 0.3 ), color( 0x24201c ), 0.5 );
|
|
1734
|
+
|
|
1735
|
+
// awning fabric, one canvas colour per shop, picked from a coarse street-cell hash
|
|
1736
|
+
const awnCell = floor( vec2( positionWorld.x, positionWorld.z ).mul( 0.2 ) );
|
|
1737
|
+
const awnPick = ihash( uint( awnCell.x.add( 1 << 16 ) ).mul( uint( 73856093 ) ).bitXor( uint( awnCell.y.add( 1 << 16 ) ).mul( uint( 19349663 ) ) ) ).mul( 5 );
|
|
1738
|
+
let awningColor = color( 0x6a2f2f ); // maroon
|
|
1739
|
+
awningColor = select( awnPick.greaterThan( 1 ), color( 0x244a32 ), awningColor ); // green
|
|
1740
|
+
awningColor = select( awnPick.greaterThan( 2 ), color( 0x22384f ), awningColor ); // navy
|
|
1741
|
+
awningColor = select( awnPick.greaterThan( 3 ), color( 0x5a5e58 ), awningColor ); // grey
|
|
1742
|
+
awningColor = select( awnPick.greaterThan( 4 ), color( 0x201f22 ), awningColor ); // black
|
|
1743
|
+
|
|
1744
|
+
const isGlazing = isGlass.or( isShopGlass ); // upper panes and shopfronts share the room compositing
|
|
1745
|
+
|
|
1346
1746
|
const material = new MeshStandardNodeMaterial();
|
|
1347
|
-
material.colorNode = select(
|
|
1348
|
-
material.roughnessNode = select(
|
|
1349
|
-
material.metalnessNode = float( 0 ); // all dielectric — stone, glass and the plastic AC shells
|
|
1350
|
-
material.emissiveNode = select(
|
|
1351
|
-
material.normalNode = bumpNormal( select( isGlass.or( isFrame ).or( isOrnament ), float( 0 ), select( isAC, acRelief, reliefHeight ) ) ); // glass / frames / ornament stay flat; AC has its own louvers
|
|
1747
|
+
material.colorNode = select( isGlazing, glazingColor, select( isStore, storeColor, select( isAwning, awningColor, select( isFrame, frameColor, select( isOrnament, ornamentColor, select( isAC, acColor, stoneColor ) ) ) ) ) );
|
|
1748
|
+
material.roughnessNode = select( isShopGlass, float( 0.14 ), select( isAwning, float( 0.85 ), select( isStore, float( 0.6 ), select( isGlass, glassRough, select( isOrnament, float( 0.8 ), select( isAC, acRough, rough ) ) ) ) ) ); // glass roughness rides on its grime, so dirty panes scatter the reflection
|
|
1749
|
+
material.metalnessNode = float( 0 ); // all dielectric — stone, glass, fabric and the plastic AC shells
|
|
1750
|
+
material.emissiveNode = select( isGlazing, room.xyz.mul( room.w ).mul( glazingEmit ), color( 0x000000 ) ); // lit rooms / shops glow through the panes
|
|
1751
|
+
material.normalNode = bumpNormal( select( isGlass.or( isFrame ).or( isOrnament ).or( isShopGlass ).or( isStore ).or( isAwning ), float( 0 ), select( isAC, acRelief, reliefHeight ) ) ); // glass / frames / ornament / storefronts stay flat; AC has its own louvers
|
|
1352
1752
|
|
|
1353
1753
|
return material;
|
|
1354
1754
|
|