@gnsx/three 0.185.16 → 0.186.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. package/build/three.cjs +10 -82600
  2. package/build/three.core.js +1303 -728
  3. package/build/three.module.js +439 -338
  4. package/build/three.tsl.js +74 -44
  5. package/build/three.webgpu.js +6036 -2184
  6. package/build/three.webgpu.nodes.js +6020 -2184
  7. package/examples/jsm/Addons.js +6 -6
  8. package/examples/jsm/controls/ArcballControls.js +1 -1
  9. package/examples/jsm/controls/FirstPersonControls.js +44 -20
  10. package/examples/jsm/controls/OrbitControls.js +9 -0
  11. package/examples/jsm/controls/TrackballControls.js +109 -4
  12. package/examples/jsm/csm/CSMShadowNode.js +13 -0
  13. package/examples/jsm/exporters/GLTFExporter.js +23 -7
  14. package/examples/jsm/exporters/USDZExporter.js +2 -2
  15. package/examples/jsm/generators/CityGenerator.js +377 -45
  16. package/examples/jsm/generators/ForestGenerator.js +12 -12
  17. package/examples/jsm/generators/TerrainGenerator.js +49 -37
  18. package/examples/jsm/generators/TreeGenerator.js +70 -76
  19. package/examples/jsm/generators/city/BenchGenerator.js +127 -0
  20. package/examples/jsm/generators/city/CarGenerator.js +479 -0
  21. package/examples/jsm/generators/city/CityGeneratorUtils.js +31 -0
  22. package/examples/jsm/generators/city/HydrantGenerator.js +106 -0
  23. package/examples/jsm/generators/city/InstancedMeshGenerator.js +86 -0
  24. package/examples/jsm/generators/city/PersonGenerator.js +447 -0
  25. package/examples/jsm/generators/city/SidewalkGenerator.js +33 -38
  26. package/examples/jsm/generators/city/SkyscraperGenerator.js +448 -48
  27. package/examples/jsm/generators/city/StreetTreeGenerator.js +231 -0
  28. package/examples/jsm/generators/city/StreetlightGenerator.js +100 -0
  29. package/examples/jsm/generators/city/TrafficlightGenerator.js +106 -0
  30. package/examples/jsm/generators/city/TrashcanGenerator.js +100 -0
  31. package/examples/jsm/geometries/BoxLineGeometry.js +1 -1
  32. package/examples/jsm/geometries/ParametricGeometry.js +1 -1
  33. package/examples/jsm/geometries/RoundedBoxGeometry.js +1 -1
  34. package/examples/jsm/gpgpu/CountingSort.js +271 -0
  35. package/examples/jsm/helpers/LightProbeGridHelper.js +60 -239
  36. package/examples/jsm/helpers/LightProbeGridHelperWebGL.js +221 -0
  37. package/examples/jsm/inspector/Extension.js +94 -0
  38. package/examples/jsm/inspector/Inspector.js +145 -41
  39. package/examples/jsm/inspector/RendererInspector.js +100 -52
  40. package/examples/jsm/inspector/extensions/color-grading/ColorGrading.js +2609 -0
  41. package/examples/jsm/inspector/extensions/color-grading/ColorWheel.js +352 -0
  42. package/examples/jsm/inspector/extensions/color-grading/CurveEditor.js +599 -0
  43. package/examples/jsm/inspector/extensions/color-grading/LUT3DStyle.js +1201 -0
  44. package/examples/jsm/inspector/extensions/color-grading/LUTMath.js +788 -0
  45. package/examples/jsm/inspector/extensions/color-grading/modules/BrightnessModule.js +72 -0
  46. package/examples/jsm/inspector/extensions/color-grading/modules/ColorWheelModule.js +112 -0
  47. package/examples/jsm/inspector/extensions/color-grading/modules/ContrastModule.js +82 -0
  48. package/examples/jsm/inspector/extensions/color-grading/modules/CurvesModule.js +109 -0
  49. package/examples/jsm/inspector/extensions/color-grading/modules/ExposureHueModule.js +110 -0
  50. package/examples/jsm/inspector/extensions/color-grading/modules/ExposureModule.js +73 -0
  51. package/examples/jsm/inspector/extensions/color-grading/modules/HueModule.js +78 -0
  52. package/examples/jsm/inspector/extensions/color-grading/modules/ImportedCubeModule.js +89 -0
  53. package/examples/jsm/inspector/extensions/color-grading/modules/Module.js +285 -0
  54. package/examples/jsm/inspector/extensions/color-grading/modules/OutputModule.js +101 -0
  55. package/examples/jsm/inspector/extensions/color-grading/modules/RendererModule.js +225 -0
  56. package/examples/jsm/inspector/extensions/color-grading/modules/SatVibranceModule.js +102 -0
  57. package/examples/jsm/inspector/extensions/color-grading/modules/SaturationModule.js +76 -0
  58. package/examples/jsm/inspector/extensions/color-grading/modules/VibranceModule.js +76 -0
  59. package/examples/jsm/inspector/extensions/color-grading/modules/WhiteBalanceModule.js +86 -0
  60. package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphEditor.js +26 -31
  61. package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphLoader.js +1 -0
  62. package/examples/jsm/inspector/tabs/Memory.js +0 -2
  63. package/examples/jsm/inspector/tabs/Settings.js +4 -0
  64. package/examples/jsm/inspector/tabs/Timeline.js +223 -171
  65. package/examples/jsm/inspector/tabs/Viewer.js +515 -21
  66. package/examples/jsm/inspector/ui/List.js +15 -9
  67. package/examples/jsm/inspector/ui/Profiler.js +170 -14
  68. package/examples/jsm/inspector/ui/Style.js +269 -23
  69. package/examples/jsm/inspector/ui/Tab.js +2 -0
  70. package/examples/jsm/inspector/ui/Values.js +14 -2
  71. package/examples/jsm/interactive/HTMLMesh.js +19 -3
  72. package/examples/jsm/lighting/LightProbeGrid.js +484 -889
  73. package/examples/jsm/lighting/LightProbeGridUtils.js +107 -0
  74. package/examples/jsm/lighting/LightProbeGridWebGL.js +841 -0
  75. package/examples/jsm/lighting/vxgi/VXGIConeTracer.js +194 -0
  76. package/examples/jsm/lighting/vxgi/VXGINode.js +660 -0
  77. package/examples/jsm/lighting/vxgi/VXGISceneCollector.js +478 -0
  78. package/examples/jsm/lighting/vxgi/VXGIVolume.js +1430 -0
  79. package/examples/jsm/lights/SunLight.js +95 -0
  80. package/examples/jsm/lights/SunLightNode.js +56 -0
  81. package/examples/jsm/lights/SunLightShadow.js +319 -0
  82. package/examples/jsm/lights/SunShadowNode.js +312 -0
  83. package/examples/jsm/loaders/3DMLoader.js +150 -49
  84. package/examples/jsm/loaders/3MFLoader.js +202 -1
  85. package/examples/jsm/loaders/FBXLoader.js +81 -5
  86. package/examples/jsm/loaders/GLTFGaussianSplatLoaderExtension.js +330 -0
  87. package/examples/jsm/loaders/GLTFLoader.js +79 -14
  88. package/examples/jsm/loaders/GaussianSplatPLYLoader.js +386 -0
  89. package/examples/jsm/loaders/KSPLATLoader.js +463 -0
  90. package/examples/jsm/loaders/MaterialXLoader.js +111 -1010
  91. package/examples/jsm/loaders/PCDLoader.js +1 -1
  92. package/examples/jsm/loaders/SPLATLoader.js +139 -0
  93. package/examples/jsm/loaders/SPZLoader.js +561 -0
  94. package/examples/jsm/loaders/materialx/MaterialXArchive.js +108 -0
  95. package/examples/jsm/loaders/materialx/MaterialXDocument.js +878 -0
  96. package/examples/jsm/loaders/materialx/MaterialXHextile.js +183 -0
  97. package/examples/jsm/loaders/materialx/MaterialXInterfaceValidation.js +376 -0
  98. package/examples/jsm/loaders/materialx/MaterialXLog.js +75 -0
  99. package/examples/jsm/loaders/materialx/MaterialXNodeInterfaceRegistry.js +4 -0
  100. package/examples/jsm/loaders/materialx/MaterialXNodeLibrary.js +1021 -0
  101. package/examples/jsm/loaders/materialx/MaterialXSurfaceMappings.js +661 -0
  102. package/examples/jsm/loaders/materialx/MaterialXUtils.js +57 -0
  103. package/examples/jsm/loaders/materialx/compile/MaterialXCompileRegistry.js +997 -0
  104. package/examples/jsm/loaders/materialx/parse/MaterialXParser.js +28 -0
  105. package/examples/jsm/loaders/usd/USDAParser.js +15 -2
  106. package/examples/jsm/loaders/usd/USDCParser.js +1 -4
  107. package/examples/jsm/loaders/usd/USDComposer.js +80 -42
  108. package/examples/jsm/materials/WoodNodeMaterial.js +6 -108
  109. package/examples/jsm/misc/RollerCoaster.js +2 -2
  110. package/examples/jsm/misc/SculptGL.LICENSE.txt +21 -0
  111. package/examples/jsm/misc/Sculptor.js +1679 -0
  112. package/examples/jsm/misc/SculptorMesh.js +1504 -0
  113. package/examples/jsm/misc/SculptorTools.js +1180 -0
  114. package/examples/jsm/misc/SculptorUtils.js +345 -0
  115. package/examples/jsm/misc/TubePainter.js +28 -16
  116. package/examples/jsm/misc/VolumeSlice.js +24 -24
  117. package/examples/jsm/modifiers/SimplifyModifier.js +63 -556
  118. package/examples/jsm/objects/GaussianSplat.js +1039 -0
  119. package/examples/jsm/objects/Sky.js +66 -45
  120. package/examples/jsm/objects/SkyMesh.js +70 -51
  121. package/examples/jsm/physics/JoltPhysics.js +1 -1
  122. package/examples/jsm/postprocessing/AfterimagePass.js +4 -2
  123. package/examples/jsm/postprocessing/BloomPass.js +2 -2
  124. package/examples/jsm/postprocessing/GTAOPass.js +1 -1
  125. package/examples/jsm/postprocessing/OutlinePass.js +5 -5
  126. package/examples/jsm/postprocessing/SAOPass.js +1 -1
  127. package/examples/jsm/postprocessing/SSAOPass.js +1 -1
  128. package/examples/jsm/postprocessing/SSRPass.js +4 -2
  129. package/examples/jsm/postprocessing/SavePass.js +1 -1
  130. package/examples/jsm/postprocessing/TAARenderPass.js +1 -1
  131. package/examples/jsm/postprocessing/UnrealBloomPass.js +30 -13
  132. package/examples/jsm/renderers/CSS2DRenderer.js +29 -5
  133. package/examples/jsm/shaders/DepthLimitedBlurShader.js +4 -1
  134. package/examples/jsm/shaders/ExposureShader.js +1 -1
  135. package/examples/jsm/shaders/FilmShader.js +2 -1
  136. package/examples/jsm/transpiler/AST.js +21 -2
  137. package/examples/jsm/transpiler/GLSLDecoder.js +498 -8
  138. package/examples/jsm/transpiler/TSLEncoder.js +82 -10
  139. package/examples/jsm/transpiler/WGSLEncoder.js +33 -4
  140. package/examples/jsm/tsl/WebGLNodesHandler.js +29 -39
  141. package/examples/jsm/tsl/display/AfterImageNode.js +2 -0
  142. package/examples/jsm/tsl/display/AnaglyphPassNode.js +3 -2
  143. package/examples/jsm/tsl/display/BilateralBlurNode.js +5 -2
  144. package/examples/jsm/tsl/display/BloomNode.js +55 -30
  145. package/examples/jsm/tsl/display/DenoiseNode.js +21 -1
  146. package/examples/jsm/tsl/display/DepthOfFieldNode.js +24 -6
  147. package/examples/jsm/tsl/display/DotScreenNode.js +11 -11
  148. package/examples/jsm/tsl/display/FSR1Node.js +8 -4
  149. package/examples/jsm/tsl/display/GTAONode.js +199 -86
  150. package/examples/jsm/tsl/display/GaussianBlurNode.js +65 -52
  151. package/examples/jsm/tsl/display/GodraysNode.js +5 -2
  152. package/examples/jsm/tsl/display/ImportanceSampledEnvironment.js +2 -2
  153. package/examples/jsm/tsl/display/LensflareNode.js +5 -2
  154. package/examples/jsm/tsl/display/OITPassNode.js +352 -0
  155. package/examples/jsm/tsl/display/OutlineNode.js +2 -0
  156. package/examples/jsm/tsl/display/ParallaxBarrierPassNode.js +3 -2
  157. package/examples/jsm/tsl/display/RGBShiftNode.js +9 -9
  158. package/examples/jsm/tsl/display/RecurrentDenoiseNode.js +7 -4
  159. package/examples/jsm/tsl/display/RetroPassNode.js +1 -1
  160. package/examples/jsm/tsl/display/SMAANode.js +22 -61
  161. package/examples/jsm/tsl/display/SSAAPassNode.js +2 -4
  162. package/examples/jsm/tsl/display/SSAONode.js +436 -0
  163. package/examples/jsm/tsl/display/SSGINode.js +5 -2
  164. package/examples/jsm/tsl/display/SSRNode.js +7 -2
  165. package/examples/jsm/tsl/display/SSSNode.js +5 -2
  166. package/examples/jsm/tsl/display/SharpenNode.js +5 -4
  167. package/examples/jsm/tsl/display/StereoCompositePassNode.js +1 -3
  168. package/examples/jsm/tsl/display/StereoPassNode.js +1 -3
  169. package/examples/jsm/tsl/display/TAAUNode.js +22 -125
  170. package/examples/jsm/tsl/display/TRAANode.js +16 -142
  171. package/examples/jsm/tsl/display/TemporalReprojectNode.js +21 -15
  172. package/examples/jsm/tsl/display/depthAwareBlur.js +63 -0
  173. package/examples/jsm/tsl/display/hashBlur.js +3 -2
  174. package/examples/jsm/tsl/lighting/ClusteredLightsNode.js +38 -27
  175. package/examples/jsm/tsl/lighting/LightProbeGridNode.js +136 -0
  176. package/examples/jsm/tsl/math/voronoiNoise.js +112 -0
  177. package/examples/jsm/tsl/shadows/TileShadowNode.js +4 -6
  178. package/examples/jsm/tsl/utils/Raymarching.js +5 -5
  179. package/examples/jsm/tsl/utils/SoftParticles.js +60 -0
  180. package/examples/jsm/tsl/utils/TAAUtils.js +195 -0
  181. package/examples/jsm/utils/BufferGeometryUtils.js +14 -14
  182. package/examples/jsm/utils/GaussianSplatUtils.js +229 -0
  183. package/examples/jsm/utils/WebGLTextureUtils.js +1 -1
  184. package/examples/jsm/utils/WebGPUTextureUtils.js +1 -1
  185. package/examples/jsm/webxr/WebGLXRFallback.js +2 -16
  186. package/examples/jsm/webxr/XRControllerModelFactory.js +16 -4
  187. package/package.json +9 -16
  188. package/src/Three.Core.js +2 -1
  189. package/src/Three.TSL.js +73 -43
  190. package/src/Three.WebGPU.Nodes.js +1 -0
  191. package/src/Three.WebGPU.js +1 -0
  192. package/src/animation/AnimationClip.js +15 -2
  193. package/src/animation/AnimationObjectGroup.js +18 -6
  194. package/src/animation/AnimationUtils.js +26 -0
  195. package/src/animation/KeyframeTrack.js +37 -0
  196. package/src/constants.js +23 -1
  197. package/src/core/BufferAttribute.js +3 -2
  198. package/src/core/BufferGeometry.js +1 -1
  199. package/src/core/InterleavedBuffer.js +5 -1
  200. package/src/core/Object3D.js +40 -12
  201. package/src/core/Raycaster.js +4 -0
  202. package/src/core/RenderTarget.js +99 -9
  203. package/src/core/RenderTarget3D.js +12 -8
  204. package/src/extras/Controls.js +0 -8
  205. package/src/extras/PMREMGenerator.js +99 -205
  206. package/src/geometries/TorusGeometry.js +1 -1
  207. package/src/helpers/ArrowHelper.js +2 -0
  208. package/src/helpers/AxesHelper.js +2 -0
  209. package/src/helpers/Box3Helper.js +2 -0
  210. package/src/helpers/BoxHelper.js +2 -0
  211. package/src/helpers/CameraHelper.js +2 -0
  212. package/src/helpers/DirectionalLightHelper.js +2 -0
  213. package/src/helpers/GridHelper.js +2 -0
  214. package/src/helpers/HemisphereLightHelper.js +2 -0
  215. package/src/helpers/PlaneHelper.js +2 -0
  216. package/src/helpers/PointLightHelper.js +2 -0
  217. package/src/helpers/PolarGridHelper.js +2 -0
  218. package/src/helpers/SkeletonHelper.js +2 -0
  219. package/src/helpers/SpotLightHelper.js +2 -0
  220. package/src/lights/Light.js +0 -10
  221. package/src/lights/LightShadow.js +43 -12
  222. package/src/lights/SpotLightShadow.js +18 -0
  223. package/src/loaders/BufferGeometryLoader.js +5 -0
  224. package/src/loaders/ImageBitmapLoader.js +3 -1
  225. package/src/loaders/ObjectLoader.js +8 -5
  226. package/src/materials/Material.js +80 -57
  227. package/src/materials/MeshPhysicalMaterial.js +32 -0
  228. package/src/materials/nodes/Line2NodeMaterial.js +17 -8
  229. package/src/materials/nodes/MeshPhysicalNodeMaterial.js +42 -5
  230. package/src/materials/nodes/NodeMaterial.js +19 -13
  231. package/src/materials/nodes/PointsNodeMaterial.js +2 -1
  232. package/src/materials/nodes/manager/NodeMaterialObserver.js +282 -55
  233. package/src/math/Frustum.js +9 -0
  234. package/src/math/FrustumArray.js +1 -1
  235. package/src/math/MathUtils.js +5 -3
  236. package/src/math/Matrix4.js +2 -9
  237. package/src/math/Plane.js +30 -1
  238. package/src/math/Quaternion.js +0 -10
  239. package/src/math/Ray.js +95 -45
  240. package/src/math/Triangle.js +0 -2
  241. package/src/math/Vector3.js +2 -2
  242. package/src/math/Vector4.js +0 -4
  243. package/src/math/interpolants/BezierInterpolant.js +36 -23
  244. package/src/nodes/Nodes.js +2 -0
  245. package/src/nodes/TSL.js +11 -3
  246. package/src/nodes/accessors/Batch.js +85 -13
  247. package/src/nodes/accessors/BufferAttributeNode.js +1 -1
  248. package/src/nodes/accessors/BuiltinNode.js +1 -1
  249. package/src/nodes/accessors/Instance.js +19 -18
  250. package/src/nodes/accessors/MaterialNode.js +9 -0
  251. package/src/nodes/accessors/ModelNode.js +85 -9
  252. package/src/nodes/accessors/ReferenceBaseNode.js +1 -70
  253. package/src/nodes/accessors/ReferenceElementNode.js +72 -0
  254. package/src/nodes/accessors/ReferenceNode.js +1 -70
  255. package/src/nodes/accessors/Skinning.js +124 -12
  256. package/src/nodes/accessors/StorageTextureNode.js +7 -3
  257. package/src/nodes/accessors/Texture3DNode.js +1 -1
  258. package/src/nodes/accessors/TextureNode.js +51 -21
  259. package/src/nodes/accessors/TextureSizeNode.js +4 -2
  260. package/src/nodes/accessors/UniformArrayNode.js +30 -5
  261. package/src/nodes/code/CodeNode.js +1 -1
  262. package/src/nodes/code/FunctionNode.js +12 -2
  263. package/src/nodes/core/ArrayNode.js +2 -2
  264. package/src/nodes/core/ContextNode.js +57 -4
  265. package/src/nodes/core/IndexNode.js +69 -4
  266. package/src/nodes/core/InspectorNode.js +2 -2
  267. package/src/nodes/core/MRTNode.js +44 -2
  268. package/src/nodes/core/Node.js +6 -3
  269. package/src/nodes/core/NodeBuilder.js +65 -50
  270. package/src/nodes/core/NodeFrame.js +11 -3
  271. package/src/nodes/core/NodeUtils.js +88 -0
  272. package/src/nodes/core/PropertyNode.js +8 -0
  273. package/src/nodes/core/StackNode.js +0 -1
  274. package/src/nodes/core/StructNode.js +4 -3
  275. package/src/nodes/core/UniformGroupNode.js +3 -2
  276. package/src/nodes/core/UniformNode.js +26 -12
  277. package/src/nodes/core/VarNode.js +0 -1
  278. package/src/nodes/display/BumpMapNode.js +2 -2
  279. package/src/nodes/display/ColorAdjustment.js +1 -1
  280. package/src/nodes/display/PassNode.js +76 -15
  281. package/src/nodes/display/ScreenNode.js +22 -65
  282. package/src/nodes/fog/Fog.js +4 -4
  283. package/src/nodes/functions/BSDF/BRDF_GGX.js +6 -6
  284. package/src/nodes/functions/BSDF/BRDF_Sheen.js +1 -1
  285. package/src/nodes/functions/BSDF/DFGLUT.js +2 -1
  286. package/src/nodes/functions/BSDF/LTC.js +1 -1
  287. package/src/nodes/functions/PhongLightingModel.js +1 -1
  288. package/src/nodes/functions/PhysicalLightingModel.js +83 -25
  289. package/src/nodes/functions/ToonLightingModel.js +2 -2
  290. package/src/nodes/functions/material/getParallaxCorrectNormal.js +3 -3
  291. package/src/nodes/gpgpu/AtomicFunctionNode.js +64 -13
  292. package/src/nodes/gpgpu/BarrierNode.js +13 -4
  293. package/src/nodes/gpgpu/ComputeBuiltinNode.js +1 -1
  294. package/src/nodes/gpgpu/ComputeNode.js +20 -17
  295. package/src/nodes/gpgpu/SubgroupFunctionNode.js +165 -37
  296. package/src/nodes/gpgpu/WorkgroupInfoNode.js +44 -6
  297. package/src/nodes/lighting/EnvironmentNode.js +40 -4
  298. package/src/nodes/lighting/LightsNode.js +20 -4
  299. package/src/nodes/lighting/ShadowBaseNode.js +158 -1
  300. package/src/nodes/lighting/ShadowFilterNode.js +2 -106
  301. package/src/nodes/lighting/ShadowNode.js +22 -91
  302. package/src/nodes/materialx/{lib/mx_hsv.js → MaterialXColor.js} +3 -6
  303. package/src/nodes/materialx/{lib/mx_transform_color.js → MaterialXColorTransform.js} +3 -6
  304. package/src/nodes/materialx/MaterialXCore.js +37 -0
  305. package/src/nodes/materialx/MaterialXNodes.js +64 -72
  306. package/src/nodes/materialx/{lib/mx_noise.js → MaterialXNoise.js} +426 -197
  307. package/src/nodes/math/BitcountNode.js +24 -4
  308. package/src/nodes/math/MathNode.js +4 -4
  309. package/src/nodes/math/MathUtils.js +15 -4
  310. package/src/nodes/math/OperatorNode.js +2 -2
  311. package/src/nodes/math/PackFloatNode.js +29 -1
  312. package/src/nodes/math/Packed4x8IntegerNode.js +433 -0
  313. package/src/nodes/math/UnpackFloatNode.js +30 -2
  314. package/src/nodes/parsers/GLSLNodeFunction.js +1 -1
  315. package/src/nodes/pmrem/PMREMNode.js +1 -1
  316. package/src/nodes/pmrem/PMREMUtils.js +31 -74
  317. package/src/nodes/shapes/Shapes.js +1 -1
  318. package/src/nodes/tsl/TSLCore.js +1 -31
  319. package/src/nodes/utils/EventNode.js +75 -0
  320. package/src/nodes/utils/LoopNode.js +54 -13
  321. package/src/nodes/utils/PostProcessingUtils.js +23 -2
  322. package/src/nodes/utils/RTTNode.js +97 -32
  323. package/src/nodes/utils/ReflectorNode.js +8 -4
  324. package/src/nodes/utils/RotateNode.js +85 -8
  325. package/src/nodes/utils/SpriteUtils.js +58 -23
  326. package/src/objects/BatchedMesh.js +6 -0
  327. package/src/objects/InstancedMesh.js +1 -1
  328. package/src/objects/LOD.js +1 -1
  329. package/src/objects/Line.js +12 -0
  330. package/src/objects/Mesh.js +12 -0
  331. package/src/objects/Points.js +12 -0
  332. package/src/objects/Sprite.js +12 -0
  333. package/src/renderers/WebGLRenderer.js +93 -27
  334. package/src/renderers/common/Animation.js +12 -2
  335. package/src/renderers/common/Backend.js +38 -10
  336. package/src/renderers/common/BindGroup.js +0 -1
  337. package/src/renderers/common/Bindings.js +50 -4
  338. package/src/renderers/common/BundleGroup.js +4 -0
  339. package/src/renderers/common/ClippingContext.js +11 -1
  340. package/src/renderers/common/Color4.js +9 -0
  341. package/src/renderers/common/Constants.js +0 -5
  342. package/src/renderers/common/DirectRenderPipeline.js +233 -0
  343. package/src/renderers/common/Geometries.js +24 -3
  344. package/src/renderers/common/Info.js +3 -3
  345. package/src/renderers/common/InspectorBase.js +37 -8
  346. package/src/renderers/common/Lighting.js +11 -4
  347. package/src/renderers/common/Pipelines.js +6 -4
  348. package/src/renderers/common/QuadMesh.js +16 -2
  349. package/src/renderers/common/RenderContext.js +8 -0
  350. package/src/renderers/common/RenderList.js +53 -19
  351. package/src/renderers/common/RenderLists.js +58 -11
  352. package/src/renderers/common/RenderObject.js +14 -0
  353. package/src/renderers/common/RenderObjects.js +14 -9
  354. package/src/renderers/common/RenderPipeline.js +52 -44
  355. package/src/renderers/common/Renderer.js +226 -55
  356. package/src/renderers/common/Storage3DTexture.js +16 -0
  357. package/src/renderers/common/Textures.js +3 -3
  358. package/src/renderers/common/XRManager.js +79 -72
  359. package/src/renderers/common/extras/PMREMGenerator.js +76 -144
  360. package/src/renderers/common/nodes/NodeManager.js +144 -6
  361. package/src/renderers/common/nodes/NodeStorageBuffer.js +3 -1
  362. package/src/renderers/common/nodes/NodeUniformBuffer.js +3 -1
  363. package/src/renderers/shaders/DFGLUTData.js +2 -1
  364. package/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js +4 -2
  365. package/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js +48 -0
  366. package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +53 -4
  367. package/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js +20 -2
  368. package/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js +20 -0
  369. package/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +6 -0
  370. package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +61 -57
  371. package/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js +82 -0
  372. package/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl.js +9 -0
  373. package/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js +8 -1
  374. package/src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl.js +15 -0
  375. package/src/renderers/shaders/ShaderLib/meshphysical.glsl.js +4 -0
  376. package/src/renderers/shaders/ShaderLib.js +1 -0
  377. package/src/renderers/shaders/UniformsLib.js +16 -0
  378. package/src/renderers/webgl/WebGLAnimation.js +2 -2
  379. package/src/renderers/webgl/WebGLCapabilities.js +2 -2
  380. package/src/renderers/webgl/WebGLLights.js +74 -4
  381. package/src/renderers/webgl/WebGLMaterials.js +6 -0
  382. package/src/renderers/webgl/WebGLOutput.js +36 -24
  383. package/src/renderers/webgl/WebGLProgram.js +3 -0
  384. package/src/renderers/webgl/WebGLPrograms.js +8 -0
  385. package/src/renderers/webgl/WebGLRenderLists.js +6 -10
  386. package/src/renderers/webgl/WebGLShadowMap.js +47 -44
  387. package/src/renderers/webgl/WebGLTextures.js +7 -7
  388. package/src/renderers/webgl-fallback/WebGLBackend.js +153 -45
  389. package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +83 -29
  390. package/src/renderers/webgl-fallback/utils/WebGLState.js +163 -17
  391. package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +32 -5
  392. package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +11 -4
  393. package/src/renderers/webgpu/WebGPUBackend.js +513 -145
  394. package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +99 -20
  395. package/src/renderers/webgpu/nodes/WGSLNodeFunction.js +2 -1
  396. package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +3 -12
  397. package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +24 -14
  398. package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +80 -14
  399. package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +62 -7
  400. package/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +9 -3
  401. package/src/renderers/webgpu/utils/WebGPUUtils.js +16 -4
  402. package/src/renderers/webxr/WebXRManager.js +25 -2
  403. package/src/scenes/Scene.js +3 -3
  404. package/src/textures/CompressedArrayTexture.js +16 -0
  405. package/src/textures/Data3DTexture.js +16 -0
  406. package/src/textures/DataArrayTexture.js +16 -0
  407. package/src/textures/DepthTexture.js +3 -3
  408. package/src/textures/Texture.js +3 -3
  409. package/src/textures/{Source.js → TextureSource.js} +39 -8
  410. package/build/three.core.min.js +0 -6
  411. package/build/three.module.min.js +0 -6
  412. package/build/three.tsl.min.js +0 -6
  413. package/build/three.webgpu.min.js +0 -6
  414. package/build/three.webgpu.nodes.min.js +0 -6
  415. package/examples/jsm/libs/ecsy.module.js +0 -1792
  416. package/examples/jsm/loaders/LottieLoader.js +0 -130
  417. 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, mx_noise_float, normalLocal, normalView, normalWorldGeometry, positionLocal, positionView, positionWorld, select, smoothstep, step, uint, uv, varying, vec2, vec3, vec4 } from '@gnsx/three/tsl';
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
- // shaft and crown are the same facade over different faces, spans and pier heights
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 base: a gothic arcade, capped by a string course
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, vBottom + f * fh, - 0.3, bandLength, bandHeight, 0.6 ) );
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, extruded with
714
- * thickness so the openings read as deep recesses.
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 geometry = new ExtrudeGeometry( shape, { depth: thickness, bevelEnabled: false, curveSegments: 8 } );
750
- geometry.translate( 0, 0, - thickness );
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 = mx_noise_float( positionWorld.mul( 0.7 ) ).mul( 0.06 );
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.025; // joint width, in metres
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.02;
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.008 );
1268
- const rough = mx_noise_float( positionWorld.mul( 0.5 ) ).mul( 0.08 ).add( 0.82 ).add( joint.mul( 0.12 ) );
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, mx_fractal_noise_float( positionWorld.mul( 0.025 ), 3 ) ).mul( 0.22 ), float( 0 ) );
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
- // toVar so the raymarch runs once, shared by the colour and emissive outputs
1299
- const room = interior().toVar();
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 dirtyGlass = mix( color( 0x13161a ), color( 0x232b31 ), mx_noise_float( positionWorld.mul( 0.3 ) ).mul( 0.5 ).add( 0.5 ) );
1310
- const glassColor = mix( room.xyz.mul( color( 0xb6c6bf ) ), dirtyGlass, grime ); // faint green-grey ( soda-lime ) room tint, dirtied toward grimy glass
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 = mx_noise_float( positionWorld.mul( 0.4 ) ).mul( 0.5 ).add( 0.5 ); // ~per-unit
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( mx_noise_float( positionWorld.mul( 5 ) ).mul( 0.04 ) );
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 = mx_fractal_noise_float( vec3( positionWorld.x.mul( 6 ), positionWorld.y.mul( 0.5 ), positionWorld.z.mul( 6 ) ), 3 ).mul( 0.5 ).add( 0.5 );
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( isGlass, glassColor, select( isFrame, frameColor, select( isOrnament, ornamentColor, select( isAC, acColor, stoneColor ) ) ) );
1348
- material.roughnessNode = select( isGlass, float( 0.18 ), select( isOrnament, float( 0.8 ), select( isAC, acRough, rough ) ) ); // glass kept smooth for a sky reflection, but soft enough not to alias over the interior
1349
- material.metalnessNode = float( 0 ); // all dielectric — stone, glass and the plastic AC shells
1350
- material.emissiveNode = select( isGlass, room.xyz.mul( room.w ).mul( 4 ).mul( grime.mul( 0.6 ).oneMinus() ), color( 0x000000 ) ); // room.w = emissive weight ( 0 unlit, < 1 behind curtains ), muted further by grime
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