super-three 0.181.0 → 0.185.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (693) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -4
  3. package/build/three.cjs +21332 -19201
  4. package/build/three.core.js +20908 -19853
  5. package/build/three.core.min.js +2 -2
  6. package/build/three.module.js +1947 -862
  7. package/build/three.module.min.js +2 -2
  8. package/build/three.tsl.js +37 -16
  9. package/build/three.tsl.min.js +2 -2
  10. package/build/three.webgpu.js +33628 -25197
  11. package/build/three.webgpu.min.js +2 -2
  12. package/build/three.webgpu.nodes.js +33394 -25000
  13. package/build/three.webgpu.nodes.min.js +2 -2
  14. package/examples/jsm/Addons.js +14 -3
  15. package/examples/jsm/animation/CCDIKSolver.js +7 -3
  16. package/examples/jsm/controls/ArcballControls.js +13 -6
  17. package/examples/jsm/controls/DragControls.js +2 -2
  18. package/examples/jsm/controls/FirstPersonControls.js +127 -86
  19. package/examples/jsm/controls/FlyControls.js +4 -0
  20. package/examples/jsm/controls/MapControls.js +55 -1
  21. package/examples/jsm/controls/OrbitControls.js +111 -8
  22. package/examples/jsm/controls/TrackballControls.js +8 -8
  23. package/examples/jsm/controls/TransformControls.js +102 -17
  24. package/examples/jsm/csm/CSM.js +2 -1
  25. package/examples/jsm/csm/CSMFrustum.js +31 -6
  26. package/examples/jsm/csm/CSMShadowNode.js +10 -3
  27. package/examples/jsm/effects/AnaglyphEffect.js +102 -7
  28. package/examples/jsm/effects/AsciiEffect.js +14 -1
  29. package/examples/jsm/environments/ColorEnvironment.js +59 -0
  30. package/examples/jsm/environments/RoomEnvironment.js +3 -0
  31. package/examples/jsm/exporters/DRACOExporter.js +27 -6
  32. package/examples/jsm/exporters/EXRExporter.js +1 -1
  33. package/examples/jsm/exporters/GLTFExporter.js +266 -21
  34. package/examples/jsm/exporters/PLYExporter.js +286 -73
  35. package/examples/jsm/exporters/USDZExporter.js +353 -99
  36. package/examples/jsm/generators/CityGenerator.js +346 -0
  37. package/examples/jsm/generators/ForestGenerator.js +347 -0
  38. package/examples/jsm/generators/TerrainGenerator.js +504 -0
  39. package/examples/jsm/generators/TreeGenerator.js +377 -0
  40. package/examples/jsm/generators/city/SidewalkGenerator.js +253 -0
  41. package/examples/jsm/generators/city/SkyscraperGenerator.js +1357 -0
  42. package/examples/jsm/geometries/DecalGeometry.js +1 -1
  43. package/examples/jsm/geometries/LoftGeometry.js +355 -0
  44. package/examples/jsm/geometries/TextGeometry.js +18 -0
  45. package/examples/jsm/helpers/AnimationPathHelper.js +302 -0
  46. package/examples/jsm/helpers/LightProbeGridHelper.js +221 -0
  47. package/examples/jsm/helpers/LightProbeHelper.js +5 -4
  48. package/examples/jsm/helpers/LightProbeHelperGPU.js +1 -1
  49. package/examples/jsm/helpers/TextureHelperGPU.js +1 -1
  50. package/examples/jsm/helpers/ViewHelper.js +83 -14
  51. package/examples/jsm/inspector/Extension.js +13 -0
  52. package/examples/jsm/inspector/Inspector.js +244 -91
  53. package/examples/jsm/inspector/RendererInspector.js +165 -39
  54. package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphEditor.js +916 -0
  55. package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphLoader.js +281 -0
  56. package/examples/jsm/inspector/tabs/Console.js +241 -25
  57. package/examples/jsm/inspector/tabs/Memory.js +136 -0
  58. package/examples/jsm/inspector/tabs/Parameters.js +115 -7
  59. package/examples/jsm/inspector/tabs/Performance.js +24 -9
  60. package/examples/jsm/inspector/tabs/Settings.js +306 -0
  61. package/examples/jsm/inspector/tabs/Timeline.js +1696 -0
  62. package/examples/jsm/inspector/tabs/Viewer.js +723 -9
  63. package/examples/jsm/inspector/ui/Graph.js +162 -25
  64. package/examples/jsm/inspector/ui/Item.js +92 -13
  65. package/examples/jsm/inspector/ui/List.js +2 -2
  66. package/examples/jsm/inspector/ui/Profiler.js +1991 -44
  67. package/examples/jsm/inspector/ui/Style.js +1943 -543
  68. package/examples/jsm/inspector/ui/Tab.js +226 -5
  69. package/examples/jsm/inspector/ui/Values.js +126 -8
  70. package/examples/jsm/inspector/ui/utils.js +143 -10
  71. package/examples/jsm/interaction/InteractionManager.js +226 -0
  72. package/examples/jsm/libs/basis/README.md +0 -1
  73. package/examples/jsm/libs/demuxer_mp4.js +3 -3
  74. package/examples/jsm/libs/draco/README.md +0 -1
  75. package/examples/jsm/libs/meshopt_clusterizer.module.js +421 -0
  76. package/examples/jsm/libs/meshopt_decoder.module.js +9 -8
  77. package/examples/jsm/libs/meshopt_simplifier.module.js +627 -0
  78. package/examples/jsm/libs/mikktspace.module.js +34 -9
  79. package/examples/jsm/lighting/ClusteredLighting.js +55 -0
  80. package/examples/jsm/lighting/DynamicLighting.js +82 -0
  81. package/examples/jsm/lighting/LightProbeGrid.js +681 -0
  82. package/examples/jsm/lines/LineMaterial.js +45 -16
  83. package/examples/jsm/lines/LineSegments2.js +8 -0
  84. package/examples/jsm/lines/webgpu/LineSegments2.js +8 -0
  85. package/examples/jsm/loaders/3DMLoader.js +6 -5
  86. package/examples/jsm/loaders/3MFLoader.js +2 -2
  87. package/examples/jsm/loaders/AMFLoader.js +2 -2
  88. package/examples/jsm/loaders/ColladaLoader.js +24 -4026
  89. package/examples/jsm/loaders/DRACOLoader.js +51 -18
  90. package/examples/jsm/loaders/EXRLoader.js +701 -90
  91. package/examples/jsm/loaders/FBXLoader.js +235 -37
  92. package/examples/jsm/loaders/GCodeLoader.js +34 -8
  93. package/examples/jsm/loaders/GLTFLoader.js +146 -178
  94. package/examples/jsm/loaders/HDRLoader.js +7 -30
  95. package/examples/jsm/loaders/KMZLoader.js +5 -5
  96. package/examples/jsm/loaders/KTX2Loader.js +58 -17
  97. package/examples/jsm/loaders/LDrawLoader.js +49 -58
  98. package/examples/jsm/loaders/LUT3dlLoader.js +2 -2
  99. package/examples/jsm/loaders/LUTCubeLoader.js +2 -2
  100. package/examples/jsm/loaders/LWOLoader.js +11 -39
  101. package/examples/jsm/loaders/LottieLoader.js +1 -1
  102. package/examples/jsm/loaders/MaterialXLoader.js +31 -9
  103. package/examples/jsm/loaders/NRRDLoader.js +5 -5
  104. package/examples/jsm/loaders/PCDLoader.js +11 -9
  105. package/examples/jsm/loaders/PLYLoader.js +218 -55
  106. package/examples/jsm/loaders/SVGLoader.js +549 -497
  107. package/examples/jsm/loaders/TDSLoader.js +0 -2
  108. package/examples/jsm/loaders/TGALoader.js +0 -2
  109. package/examples/jsm/loaders/TTFLoader.js +1 -1
  110. package/examples/jsm/loaders/USDLoader.js +127 -43
  111. package/examples/jsm/loaders/UltraHDRLoader.js +285 -160
  112. package/examples/jsm/loaders/VOXLoader.js +660 -117
  113. package/examples/jsm/loaders/VRMLLoader.js +80 -3
  114. package/examples/jsm/loaders/VTKLoader.js +42 -25
  115. package/examples/jsm/loaders/collada/ColladaComposer.js +3044 -0
  116. package/examples/jsm/loaders/collada/ColladaParser.js +1977 -0
  117. package/examples/jsm/loaders/usd/USDAParser.js +504 -344
  118. package/examples/jsm/loaders/usd/USDCParser.js +1867 -6
  119. package/examples/jsm/loaders/usd/USDComposer.js +4627 -0
  120. package/examples/jsm/materials/LDrawConditionalLineNodeMaterial.js +2 -2
  121. package/examples/jsm/materials/WoodNodeMaterial.js +11 -11
  122. package/examples/jsm/math/Octree.js +131 -1
  123. package/examples/jsm/misc/GPUComputationRenderer.js +2 -0
  124. package/examples/jsm/misc/RollerCoaster.js +42 -4
  125. package/examples/jsm/misc/TileCreasedNormalsPlugin.js +270 -0
  126. package/examples/jsm/misc/Volume.js +2 -3
  127. package/examples/jsm/misc/VolumeSlice.js +0 -1
  128. package/examples/jsm/modifiers/TessellateModifier.js +1 -1
  129. package/examples/jsm/objects/GroundedSkybox.js +1 -1
  130. package/examples/jsm/objects/LensflareMesh.js +1 -1
  131. package/examples/jsm/objects/Reflector.js +72 -25
  132. package/examples/jsm/objects/Sky.js +90 -6
  133. package/examples/jsm/objects/SkyMesh.js +150 -16
  134. package/examples/jsm/objects/Water.js +4 -3
  135. package/examples/jsm/objects/Water2.js +5 -3
  136. package/examples/jsm/objects/Water2Mesh.js +1 -1
  137. package/examples/jsm/objects/WaterMesh.js +5 -7
  138. package/examples/jsm/physics/AmmoPhysics.js +27 -15
  139. package/examples/jsm/physics/JoltPhysics.js +10 -6
  140. package/examples/jsm/physics/RapierPhysics.js +36 -7
  141. package/examples/jsm/postprocessing/EffectComposer.js +7 -5
  142. package/examples/jsm/postprocessing/OutputPass.js +9 -0
  143. package/examples/jsm/postprocessing/RenderPass.js +10 -0
  144. package/examples/jsm/postprocessing/RenderTransitionPass.js +1 -1
  145. package/examples/jsm/postprocessing/UnrealBloomPass.js +48 -18
  146. package/examples/jsm/renderers/CSS3DRenderer.js +1 -1
  147. package/examples/jsm/renderers/Projector.js +246 -28
  148. package/examples/jsm/renderers/SVGRenderer.js +174 -60
  149. package/examples/jsm/shaders/GTAOShader.js +19 -6
  150. package/examples/jsm/shaders/HalftoneShader.js +12 -1
  151. package/examples/jsm/shaders/PoissonDenoiseShader.js +6 -2
  152. package/examples/jsm/shaders/SAOShader.js +17 -4
  153. package/examples/jsm/shaders/SSAOShader.js +11 -1
  154. package/examples/jsm/shaders/SSRShader.js +6 -5
  155. package/examples/jsm/shaders/UnpackDepthRGBAShader.js +2 -4
  156. package/examples/jsm/shaders/VignetteShader.js +1 -1
  157. package/examples/jsm/shaders/VolumeShader.js +31 -44
  158. package/examples/jsm/transpiler/AST.js +44 -0
  159. package/examples/jsm/transpiler/GLSLDecoder.js +65 -8
  160. package/examples/jsm/transpiler/Linker.js +1 -1
  161. package/examples/jsm/transpiler/ShaderToyDecoder.js +2 -0
  162. package/examples/jsm/transpiler/TSLEncoder.js +46 -3
  163. package/examples/jsm/transpiler/TranspilerUtils.js +3 -3
  164. package/examples/jsm/transpiler/WGSLEncoder.js +27 -0
  165. package/examples/jsm/tsl/WebGLNodesHandler.js +605 -0
  166. package/examples/jsm/tsl/display/AfterImageNode.js +11 -1
  167. package/examples/jsm/tsl/display/AnaglyphPassNode.js +458 -16
  168. package/examples/jsm/tsl/display/BilateralBlurNode.js +374 -0
  169. package/examples/jsm/tsl/display/BloomNode.js +75 -25
  170. package/examples/jsm/tsl/display/CRT.js +150 -0
  171. package/examples/jsm/tsl/display/ChromaticAberrationNode.js +3 -36
  172. package/examples/jsm/tsl/display/DenoiseNode.js +1 -1
  173. package/examples/jsm/tsl/display/DepthOfFieldNode.js +3 -3
  174. package/examples/jsm/tsl/display/DotScreenNode.js +1 -1
  175. package/examples/jsm/tsl/display/FSR1Node.js +477 -0
  176. package/examples/jsm/tsl/display/FXAANode.js +13 -14
  177. package/examples/jsm/tsl/display/GTAONode.js +40 -17
  178. package/examples/jsm/tsl/display/GaussianBlurNode.js +21 -2
  179. package/examples/jsm/tsl/display/GodraysNode.js +615 -0
  180. package/examples/jsm/tsl/display/ImportanceSampledEnvironment.js +560 -0
  181. package/examples/jsm/tsl/display/LensflareNode.js +1 -1
  182. package/examples/jsm/tsl/display/Lut3DNode.js +1 -1
  183. package/examples/jsm/tsl/display/OutlineNode.js +69 -19
  184. package/examples/jsm/tsl/display/ParallaxBarrierPassNode.js +2 -2
  185. package/examples/jsm/tsl/display/PixelationPassNode.js +9 -8
  186. package/examples/jsm/tsl/display/RGBShiftNode.js +2 -2
  187. package/examples/jsm/tsl/display/RecurrentDenoiseNode.js +912 -0
  188. package/examples/jsm/tsl/display/RetroPassNode.js +263 -0
  189. package/examples/jsm/tsl/display/SMAANode.js +2 -2
  190. package/examples/jsm/tsl/display/SSAAPassNode.js +15 -27
  191. package/examples/jsm/tsl/display/SSGINode.js +96 -62
  192. package/examples/jsm/tsl/display/SSRNode.js +831 -134
  193. package/examples/jsm/tsl/display/SSSNode.js +8 -6
  194. package/examples/jsm/tsl/display/Shape.js +29 -0
  195. package/examples/jsm/tsl/display/SharpenNode.js +283 -0
  196. package/examples/jsm/tsl/display/SobelOperatorNode.js +2 -2
  197. package/examples/jsm/tsl/display/StereoCompositePassNode.js +8 -1
  198. package/examples/jsm/tsl/display/StereoPassNode.js +1 -2
  199. package/examples/jsm/tsl/display/TAAUNode.js +835 -0
  200. package/examples/jsm/tsl/display/TRAANode.js +315 -126
  201. package/examples/jsm/tsl/display/TemporalReprojectNode.js +1023 -0
  202. package/examples/jsm/tsl/display/TransitionNode.js +1 -1
  203. package/examples/jsm/tsl/display/depthAwareBlend.js +80 -0
  204. package/examples/jsm/tsl/display/radialBlur.js +68 -0
  205. package/examples/jsm/tsl/lighting/ClusteredLightsNode.js +622 -0
  206. package/examples/jsm/tsl/lighting/DynamicLightsNode.js +300 -0
  207. package/examples/jsm/tsl/lighting/data/AmbientLightDataNode.js +61 -0
  208. package/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js +111 -0
  209. package/examples/jsm/tsl/lighting/data/HemisphereLightDataNode.js +99 -0
  210. package/examples/jsm/tsl/lighting/data/PointLightDataNode.js +134 -0
  211. package/examples/jsm/tsl/lighting/data/SpotLightDataNode.js +161 -0
  212. package/examples/jsm/tsl/math/Bayer.js +53 -3
  213. package/examples/jsm/tsl/math/curlNoise.js +107 -0
  214. package/examples/jsm/tsl/shadows/TileShadowNode.js +1 -1
  215. package/examples/jsm/tsl/shadows/TileShadowNodeHelper.js +3 -3
  216. package/examples/jsm/tsl/utils/GroundedSkybox.js +62 -0
  217. package/examples/jsm/tsl/utils/RNoise.js +51 -0
  218. package/examples/jsm/tsl/utils/SpecularHelpers.js +325 -0
  219. package/examples/jsm/utils/BufferGeometryUtils.js +128 -62
  220. package/examples/jsm/utils/ColorUtils.js +76 -0
  221. package/examples/jsm/utils/GeometryCompressionUtils.js +1 -1
  222. package/examples/jsm/utils/LDrawUtils.js +1 -1
  223. package/examples/jsm/utils/SceneOptimizer.js +1 -1
  224. package/examples/jsm/utils/ShadowMapViewer.js +24 -10
  225. package/examples/jsm/utils/ShadowMapViewerGPU.js +1 -1
  226. package/examples/jsm/utils/SkeletonUtils.js +14 -8
  227. package/examples/jsm/utils/WebGPUTextureUtils.js +1 -1
  228. package/examples/jsm/webxr/ARButton.js +1 -1
  229. package/examples/jsm/webxr/WebGLXRFallback.js +89 -0
  230. package/examples/jsm/webxr/XRControllerModelFactory.js +2 -2
  231. package/examples/jsm/webxr/XRHandMeshModel.js +36 -10
  232. package/examples/jsm/webxr/XRHandModelFactory.js +2 -1
  233. package/package.json +28 -34
  234. package/src/Three.Core.js +3 -1
  235. package/src/Three.TSL.js +35 -14
  236. package/src/Three.WebGPU.Nodes.js +5 -0
  237. package/src/Three.WebGPU.js +13 -0
  238. package/src/Three.js +1 -0
  239. package/src/animation/AnimationAction.js +18 -4
  240. package/src/animation/AnimationClip.js +0 -139
  241. package/src/animation/AnimationMixer.js +6 -0
  242. package/src/animation/AnimationUtils.js +1 -12
  243. package/src/animation/KeyframeTrack.js +47 -8
  244. package/src/animation/PropertyBinding.js +2 -2
  245. package/src/animation/PropertyMixer.js +4 -4
  246. package/src/animation/tracks/BooleanKeyframeTrack.js +1 -1
  247. package/src/animation/tracks/ColorKeyframeTrack.js +1 -1
  248. package/src/animation/tracks/NumberKeyframeTrack.js +1 -1
  249. package/src/animation/tracks/QuaternionKeyframeTrack.js +1 -1
  250. package/src/animation/tracks/StringKeyframeTrack.js +1 -1
  251. package/src/animation/tracks/VectorKeyframeTrack.js +1 -1
  252. package/src/audio/Audio.js +1 -1
  253. package/src/audio/AudioContext.js +2 -2
  254. package/src/audio/AudioListener.js +5 -3
  255. package/src/cameras/Camera.js +34 -4
  256. package/src/cameras/CubeCamera.js +20 -0
  257. package/src/cameras/StereoCamera.js +5 -2
  258. package/src/constants.js +90 -5
  259. package/src/core/BufferAttribute.js +13 -1
  260. package/src/core/BufferGeometry.js +43 -9
  261. package/src/core/Clock.js +7 -0
  262. package/src/core/Object3D.js +69 -11
  263. package/src/core/Raycaster.js +3 -3
  264. package/src/core/RenderTarget.js +19 -6
  265. package/src/extras/PMREMGenerator.js +13 -22
  266. package/src/extras/TextureUtils.js +6 -2
  267. package/src/extras/core/ShapePath.js +149 -160
  268. package/src/extras/curves/CatmullRomCurve3.js +3 -2
  269. package/src/geometries/ExtrudeGeometry.js +2 -2
  270. package/src/geometries/PolyhedronGeometry.js +1 -1
  271. package/src/geometries/SphereGeometry.js +8 -3
  272. package/src/geometries/TorusGeometry.js +8 -3
  273. package/src/helpers/CameraHelper.js +3 -0
  274. package/src/helpers/DirectionalLightHelper.js +6 -1
  275. package/src/helpers/HemisphereLightHelper.js +5 -0
  276. package/src/helpers/PointLightHelper.js +3 -25
  277. package/src/helpers/SpotLightHelper.js +4 -1
  278. package/src/lights/DirectionalLight.js +13 -0
  279. package/src/lights/HemisphereLight.js +10 -0
  280. package/src/lights/Light.js +1 -11
  281. package/src/lights/LightProbe.js +0 -15
  282. package/src/lights/LightShadow.js +15 -6
  283. package/src/lights/PointLight.js +15 -0
  284. package/src/lights/PointLightShadow.js +0 -86
  285. package/src/lights/SpotLight.js +22 -1
  286. package/src/lights/webgpu/IESSpotLight.js +2 -1
  287. package/src/loaders/AudioLoader.js +11 -1
  288. package/src/loaders/Cache.js +28 -0
  289. package/src/loaders/DataTextureLoader.js +75 -42
  290. package/src/loaders/FileLoader.js +2 -3
  291. package/src/loaders/ImageBitmapLoader.js +12 -9
  292. package/src/loaders/Loader.js +6 -0
  293. package/src/loaders/LoadingManager.js +5 -0
  294. package/src/loaders/MaterialLoader.js +37 -266
  295. package/src/loaders/ObjectLoader.js +48 -6
  296. package/src/loaders/nodes/NodeLoader.js +2 -2
  297. package/src/loaders/nodes/NodeObjectLoader.js +18 -0
  298. package/src/materials/LineBasicMaterial.js +4 -0
  299. package/src/materials/Material.js +198 -1
  300. package/src/materials/MeshBasicMaterial.js +24 -0
  301. package/src/materials/MeshDepthMaterial.js +10 -0
  302. package/src/materials/MeshDistanceMaterial.js +10 -0
  303. package/src/materials/MeshLambertMaterial.js +49 -1
  304. package/src/materials/MeshMatcapMaterial.js +25 -1
  305. package/src/materials/MeshNormalMaterial.js +9 -1
  306. package/src/materials/MeshPhongMaterial.js +49 -1
  307. package/src/materials/MeshPhysicalMaterial.js +38 -0
  308. package/src/materials/MeshStandardMaterial.js +42 -1
  309. package/src/materials/MeshToonMaterial.js +35 -2
  310. package/src/materials/PointsMaterial.js +7 -0
  311. package/src/materials/ShaderMaterial.js +106 -1
  312. package/src/materials/SpriteMaterial.js +7 -0
  313. package/src/materials/nodes/Line2NodeMaterial.js +380 -297
  314. package/src/materials/nodes/MeshBasicNodeMaterial.js +2 -2
  315. package/src/materials/nodes/MeshNormalNodeMaterial.js +2 -2
  316. package/src/materials/nodes/MeshPhongNodeMaterial.js +0 -9
  317. package/src/materials/nodes/MeshPhysicalNodeMaterial.js +3 -30
  318. package/src/materials/nodes/MeshSSSNodeMaterial.js +0 -13
  319. package/src/materials/nodes/MeshStandardNodeMaterial.js +5 -15
  320. package/src/materials/nodes/NodeMaterial.js +153 -89
  321. package/src/materials/nodes/SpriteNodeMaterial.js +0 -10
  322. package/src/materials/nodes/manager/NodeMaterialObserver.js +234 -100
  323. package/src/math/Box3.js +5 -0
  324. package/src/math/FrustumArray.js +103 -133
  325. package/src/math/Interpolant.js +1 -1
  326. package/src/math/Line3.js +6 -5
  327. package/src/math/MathUtils.js +12 -12
  328. package/src/math/Matrix2.js +13 -9
  329. package/src/math/Matrix3.js +24 -9
  330. package/src/math/Matrix4.js +112 -74
  331. package/src/math/Plane.js +4 -3
  332. package/src/math/Quaternion.js +3 -29
  333. package/src/math/Sphere.js +1 -1
  334. package/src/math/Triangle.js +1 -1
  335. package/src/math/Vector2.js +13 -9
  336. package/src/math/Vector3.js +17 -15
  337. package/src/math/Vector4.js +15 -11
  338. package/src/math/interpolants/BezierInterpolant.js +106 -0
  339. package/src/nodes/Nodes.js +86 -71
  340. package/src/nodes/TSL.js +15 -13
  341. package/src/nodes/accessors/Arrays.js +5 -5
  342. package/src/nodes/accessors/Batch.js +108 -0
  343. package/src/nodes/accessors/Bitangent.js +7 -7
  344. package/src/nodes/accessors/BufferAttributeNode.js +120 -17
  345. package/src/nodes/accessors/BufferNode.js +29 -2
  346. package/src/nodes/accessors/Camera.js +149 -28
  347. package/src/nodes/accessors/ClippingNode.js +5 -5
  348. package/src/nodes/accessors/CubeTextureNode.js +27 -2
  349. package/src/nodes/accessors/Instance.js +264 -0
  350. package/src/nodes/accessors/MaterialNode.js +27 -3
  351. package/src/nodes/accessors/MaterialProperties.js +6 -8
  352. package/src/nodes/accessors/MaterialReferenceNode.js +1 -2
  353. package/src/nodes/accessors/ModelNode.js +1 -1
  354. package/src/nodes/accessors/{MorphNode.js → Morph.js} +99 -112
  355. package/src/nodes/accessors/Normal.js +24 -24
  356. package/src/nodes/accessors/Object3DNode.js +1 -1
  357. package/src/nodes/accessors/Position.js +39 -3
  358. package/src/nodes/accessors/ReferenceBaseNode.js +6 -6
  359. package/src/nodes/accessors/ReferenceNode.js +9 -8
  360. package/src/nodes/accessors/ReflectVector.js +3 -3
  361. package/src/nodes/accessors/RendererReferenceNode.js +1 -2
  362. package/src/nodes/accessors/SceneProperties.js +47 -0
  363. package/src/nodes/accessors/Skinning.js +263 -0
  364. package/src/nodes/accessors/StorageBufferNode.js +16 -27
  365. package/src/nodes/accessors/StorageTexture3DNode.js +100 -0
  366. package/src/nodes/accessors/StorageTextureNode.js +74 -11
  367. package/src/nodes/accessors/Tangent.js +8 -18
  368. package/src/nodes/accessors/Texture3DNode.js +32 -35
  369. package/src/nodes/accessors/TextureNode.js +124 -25
  370. package/src/nodes/accessors/UniformArrayNode.js +6 -4
  371. package/src/nodes/accessors/UserDataNode.js +1 -2
  372. package/src/nodes/accessors/VertexColorNode.js +1 -2
  373. package/src/nodes/code/FunctionCallNode.js +1 -1
  374. package/src/nodes/code/FunctionNode.js +4 -19
  375. package/src/nodes/core/ArrayNode.js +21 -2
  376. package/src/nodes/core/AssignNode.js +3 -3
  377. package/src/nodes/core/AttributeNode.js +3 -3
  378. package/src/nodes/core/BypassNode.js +1 -1
  379. package/src/nodes/core/ContextNode.js +104 -5
  380. package/src/nodes/core/IndexNode.js +2 -1
  381. package/src/nodes/core/InputNode.js +1 -1
  382. package/src/nodes/core/InspectorNode.js +1 -1
  383. package/src/nodes/core/IsolateNode.js +1 -1
  384. package/src/nodes/core/MRTNode.js +55 -4
  385. package/src/nodes/core/Node.js +151 -17
  386. package/src/nodes/core/NodeBuilder.js +479 -101
  387. package/src/nodes/core/NodeError.js +28 -0
  388. package/src/nodes/core/NodeFrame.js +12 -4
  389. package/src/nodes/core/NodeUtils.js +20 -18
  390. package/src/nodes/core/OutputStructNode.js +13 -11
  391. package/src/nodes/core/OverrideContextNode.js +153 -0
  392. package/src/nodes/core/ParameterNode.js +4 -4
  393. package/src/nodes/core/PropertyNode.js +66 -4
  394. package/src/nodes/core/StackNode.js +91 -20
  395. package/src/nodes/core/StackTrace.js +139 -0
  396. package/src/nodes/core/StructNode.js +20 -8
  397. package/src/nodes/core/StructTypeNode.js +17 -23
  398. package/src/nodes/core/SubBuildNode.js +2 -2
  399. package/src/nodes/core/UniformGroupNode.js +36 -6
  400. package/src/nodes/core/UniformNode.js +21 -5
  401. package/src/nodes/core/VarNode.js +56 -23
  402. package/src/nodes/core/VaryingNode.js +14 -21
  403. package/src/nodes/display/BlendModes.js +1 -105
  404. package/src/nodes/display/BumpMapNode.js +6 -1
  405. package/src/nodes/display/ColorAdjustment.js +27 -9
  406. package/src/nodes/display/ColorSpaceNode.js +3 -3
  407. package/src/nodes/display/FrontFacingNode.js +38 -9
  408. package/src/nodes/display/NormalMapNode.js +41 -6
  409. package/src/nodes/display/PassNode.js +157 -56
  410. package/src/nodes/display/PremultiplyAlphaFunctions.js +39 -0
  411. package/src/nodes/display/RenderOutputNode.js +19 -10
  412. package/src/nodes/display/ScreenNode.js +4 -2
  413. package/src/nodes/display/ToneMappingNode.js +1 -1
  414. package/src/nodes/display/ToonOutlinePassNode.js +2 -2
  415. package/src/nodes/display/ViewportDepthNode.js +52 -4
  416. package/src/nodes/display/ViewportDepthTextureNode.js +11 -15
  417. package/src/nodes/display/ViewportTextureNode.js +39 -11
  418. package/src/nodes/fog/Fog.js +18 -35
  419. package/src/nodes/functions/BSDF/BRDF_GGX_Multiscatter.js +3 -3
  420. package/src/nodes/functions/BSDF/DFGLUT.js +56 -0
  421. package/src/nodes/functions/BSDF/EnvironmentBRDF.js +2 -2
  422. package/src/nodes/functions/BSDF/V_GGX_SmithCorrelated_Anisotropic.js +2 -2
  423. package/src/nodes/functions/PhysicalLightingModel.js +126 -45
  424. package/src/nodes/functions/VolumetricLightingModel.js +40 -7
  425. package/src/nodes/geometry/RangeNode.js +5 -3
  426. package/src/nodes/gpgpu/AtomicFunctionNode.js +1 -1
  427. package/src/nodes/gpgpu/BarrierNode.js +9 -0
  428. package/src/nodes/gpgpu/ComputeBuiltinNode.js +2 -3
  429. package/src/nodes/gpgpu/ComputeNode.js +74 -48
  430. package/src/nodes/gpgpu/SubgroupFunctionNode.js +2 -2
  431. package/src/nodes/gpgpu/WorkgroupInfoNode.js +5 -5
  432. package/src/nodes/lighting/AnalyticLightNode.js +53 -0
  433. package/src/nodes/lighting/EnvironmentNode.js +30 -5
  434. package/src/nodes/lighting/IESSpotLightNode.js +40 -2
  435. package/src/nodes/lighting/LightingContextNode.js +10 -2
  436. package/src/nodes/lighting/LightsNode.js +97 -65
  437. package/src/nodes/lighting/PointShadowNode.js +166 -148
  438. package/src/nodes/lighting/ShadowFilterNode.js +74 -117
  439. package/src/nodes/lighting/ShadowNode.js +149 -51
  440. package/src/nodes/materialx/lib/mx_noise.js +2 -2
  441. package/src/nodes/math/BitcastNode.js +1 -1
  442. package/src/nodes/math/BitcountNode.js +433 -0
  443. package/src/nodes/math/ConditionalNode.js +6 -6
  444. package/src/nodes/math/MathNode.js +115 -52
  445. package/src/nodes/math/OperatorNode.js +24 -23
  446. package/src/nodes/math/PackFloatNode.js +98 -0
  447. package/src/nodes/math/UnpackFloatNode.js +96 -0
  448. package/src/nodes/parsers/GLSLNodeFunction.js +1 -1
  449. package/src/nodes/pmrem/PMREMNode.js +34 -3
  450. package/src/nodes/pmrem/PMREMUtils.js +9 -15
  451. package/src/nodes/tsl/TSLBase.js +1 -1
  452. package/src/nodes/tsl/TSLCore.js +99 -33
  453. package/src/nodes/utils/ArrayElementNode.js +14 -1
  454. package/src/nodes/utils/ConvertNode.js +1 -1
  455. package/src/nodes/utils/DebugNode.js +12 -12
  456. package/src/nodes/utils/EquirectUV.js +30 -5
  457. package/src/nodes/utils/EventNode.js +31 -2
  458. package/src/nodes/utils/FlipNode.js +1 -1
  459. package/src/nodes/utils/FunctionOverloadingNode.js +2 -2
  460. package/src/nodes/utils/JoinNode.js +3 -3
  461. package/src/nodes/utils/LoopNode.js +1 -1
  462. package/src/nodes/utils/MemberNode.js +2 -2
  463. package/src/nodes/utils/Packing.js +48 -5
  464. package/src/nodes/utils/PostProcessingUtils.js +33 -1
  465. package/src/nodes/utils/RTTNode.js +41 -25
  466. package/src/nodes/utils/ReflectorNode.js +4 -4
  467. package/src/nodes/utils/Remap.js +48 -0
  468. package/src/nodes/utils/RotateNode.js +1 -1
  469. package/src/nodes/utils/SampleNode.js +1 -1
  470. package/src/nodes/utils/SetNode.js +1 -1
  471. package/src/nodes/utils/SplitNode.js +1 -1
  472. package/src/nodes/utils/SpriteSheetUV.js +35 -0
  473. package/src/nodes/utils/StorageArrayElementNode.js +1 -1
  474. package/src/nodes/utils/UVUtils.js +28 -0
  475. package/src/objects/BatchedMesh.js +66 -30
  476. package/src/objects/InstancedMesh.js +19 -3
  477. package/src/objects/Line.js +1 -1
  478. package/src/objects/Mesh.js +1 -1
  479. package/src/objects/Points.js +1 -1
  480. package/src/objects/SkinnedMesh.js +26 -9
  481. package/src/renderers/WebGLRenderer.js +344 -156
  482. package/src/renderers/common/Animation.js +3 -3
  483. package/src/renderers/common/Attributes.js +15 -1
  484. package/src/renderers/common/Backend.js +78 -13
  485. package/src/renderers/common/Background.js +26 -13
  486. package/src/renderers/common/BindGroup.js +1 -16
  487. package/src/renderers/common/Binding.js +11 -0
  488. package/src/renderers/common/Bindings.js +141 -57
  489. package/src/renderers/common/BlendMode.js +143 -0
  490. package/src/renderers/common/Buffer.js +49 -0
  491. package/src/renderers/common/BundleGroup.js +1 -1
  492. package/src/renderers/common/ChainMap.js +30 -6
  493. package/src/renderers/common/ClippingContext.js +10 -2
  494. package/src/renderers/common/ComputePipeline.js +1 -1
  495. package/src/renderers/common/CubeRenderTarget.js +51 -7
  496. package/src/renderers/common/Geometries.js +29 -3
  497. package/src/renderers/common/Info.js +374 -4
  498. package/src/renderers/common/InspectorBase.js +6 -1
  499. package/src/renderers/common/Lighting.js +51 -19
  500. package/src/renderers/common/Pipelines.js +40 -7
  501. package/src/renderers/common/PostProcessing.js +8 -206
  502. package/src/renderers/common/ReadbackBuffer.js +78 -0
  503. package/src/renderers/common/RenderBundle.js +3 -1
  504. package/src/renderers/common/RenderBundles.js +7 -3
  505. package/src/renderers/common/RenderContext.js +16 -0
  506. package/src/renderers/common/RenderContexts.js +33 -49
  507. package/src/renderers/common/RenderList.js +41 -4
  508. package/src/renderers/common/RenderLists.js +2 -1
  509. package/src/renderers/common/RenderObject.js +88 -16
  510. package/src/renderers/common/RenderObjectPipeline.js +40 -0
  511. package/src/renderers/common/RenderObjects.js +21 -5
  512. package/src/renderers/common/RenderPipeline.js +232 -17
  513. package/src/renderers/common/Renderer.js +700 -163
  514. package/src/renderers/common/Sampler.js +16 -48
  515. package/src/renderers/common/StorageBuffer.js +13 -1
  516. package/src/renderers/common/Textures.js +121 -9
  517. package/src/renderers/common/TimestampQueryPool.js +6 -4
  518. package/src/renderers/common/Uniform.js +8 -0
  519. package/src/renderers/common/UniformsGroup.js +84 -1
  520. package/src/renderers/common/XRManager.js +377 -51
  521. package/src/renderers/common/extras/PMREMGenerator.js +40 -61
  522. package/src/renderers/common/nodes/NodeBuilderState.js +10 -2
  523. package/src/renderers/common/nodes/NodeLibrary.js +4 -4
  524. package/src/renderers/common/nodes/{Nodes.js → NodeManager.js} +298 -106
  525. package/src/renderers/common/nodes/NodeStorageBuffer.js +13 -2
  526. package/src/renderers/common/nodes/NodeUniformBuffer.js +65 -0
  527. package/src/renderers/shaders/DFGLUTData.js +19 -34
  528. package/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js +2 -2
  529. package/src/renderers/shaders/ShaderChunk/color_fragment.glsl.js +1 -5
  530. package/src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl.js +1 -5
  531. package/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js +1 -5
  532. package/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js +8 -10
  533. package/src/renderers/shaders/ShaderChunk/common.glsl.js +13 -4
  534. package/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js +0 -6
  535. package/src/renderers/shaders/ShaderChunk/envmap_common_pars_fragment.glsl.js +0 -1
  536. package/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +9 -13
  537. package/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js +2 -2
  538. package/src/renderers/shaders/ShaderChunk/envmap_vertex.glsl.js +1 -1
  539. package/src/renderers/shaders/ShaderChunk/lightprobes_pars_fragment.glsl.js +80 -0
  540. package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +13 -2
  541. package/src/renderers/shaders/ShaderChunk/lights_fragment_end.glsl.js +6 -0
  542. package/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js +6 -2
  543. package/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js +3 -1
  544. package/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +8 -4
  545. package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +112 -53
  546. package/src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js +2 -2
  547. package/src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl.js +7 -0
  548. package/src/renderers/shaders/ShaderChunk/normal_vertex.glsl.js +6 -0
  549. package/src/renderers/shaders/ShaderChunk/packing.glsl.js +20 -4
  550. package/src/renderers/shaders/ShaderChunk/premultiplied_alpha_fragment.glsl.js +0 -1
  551. package/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js +225 -186
  552. package/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js +12 -2
  553. package/src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl.js +1 -1
  554. package/src/renderers/shaders/ShaderChunk/transmission_fragment.glsl.js +2 -2
  555. package/src/renderers/shaders/ShaderChunk.js +5 -3
  556. package/src/renderers/shaders/ShaderLib/backgroundCube.glsl.js +1 -2
  557. package/src/renderers/shaders/ShaderLib/depth.glsl.js +3 -0
  558. package/src/renderers/shaders/ShaderLib/{distanceRGBA.glsl.js → distance.glsl.js} +2 -3
  559. package/src/renderers/shaders/ShaderLib/meshlambert.glsl.js +2 -1
  560. package/src/renderers/shaders/ShaderLib/meshnormal.glsl.js +1 -2
  561. package/src/renderers/shaders/ShaderLib/meshphong.glsl.js +2 -1
  562. package/src/renderers/shaders/ShaderLib/meshphysical.glsl.js +4 -9
  563. package/src/renderers/shaders/ShaderLib/meshtoon.glsl.js +0 -1
  564. package/src/renderers/shaders/ShaderLib/shadow.glsl.js +1 -1
  565. package/src/renderers/shaders/ShaderLib/vsm.glsl.js +4 -6
  566. package/src/renderers/shaders/ShaderLib.js +7 -6
  567. package/src/renderers/shaders/UniformsLib.js +7 -5
  568. package/src/renderers/shaders/UniformsUtils.js +27 -5
  569. package/src/renderers/webgl/WebGLAnimation.js +2 -1
  570. package/src/renderers/webgl/WebGLBackground.js +15 -15
  571. package/src/renderers/webgl/WebGLBindingStates.js +99 -27
  572. package/src/renderers/webgl/WebGLBufferRenderer.js +0 -32
  573. package/src/renderers/webgl/WebGLCapabilities.js +9 -4
  574. package/src/renderers/webgl/WebGLEnvironments.js +228 -0
  575. package/src/renderers/webgl/WebGLGeometries.js +10 -7
  576. package/src/renderers/webgl/WebGLIndexedBufferRenderer.js +0 -32
  577. package/src/renderers/webgl/WebGLLights.js +18 -1
  578. package/src/renderers/webgl/WebGLMaterials.js +24 -13
  579. package/src/renderers/webgl/WebGLObjects.js +3 -1
  580. package/src/renderers/webgl/WebGLOutput.js +271 -0
  581. package/src/renderers/webgl/WebGLProgram.js +54 -113
  582. package/src/renderers/webgl/WebGLPrograms.js +74 -55
  583. package/src/renderers/webgl/WebGLRenderLists.js +24 -1
  584. package/src/renderers/webgl/WebGLRenderStates.js +13 -2
  585. package/src/renderers/webgl/WebGLShaderCache.js +5 -11
  586. package/src/renderers/webgl/WebGLShadowMap.js +188 -24
  587. package/src/renderers/webgl/WebGLState.js +75 -37
  588. package/src/renderers/webgl/WebGLTextures.js +230 -56
  589. package/src/renderers/webgl/WebGLUniforms.js +40 -3
  590. package/src/renderers/webgl/WebGLUniformsGroups.js +82 -35
  591. package/src/renderers/webgl/WebGLUtils.js +6 -2
  592. package/src/renderers/webgl-fallback/WebGLBackend.js +322 -92
  593. package/src/renderers/webgl-fallback/WebGLBufferRenderer.js +0 -41
  594. package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +295 -66
  595. package/src/renderers/webgl-fallback/utils/WebGLAttributeUtils.js +53 -19
  596. package/src/renderers/webgl-fallback/utils/WebGLCapabilities.js +25 -0
  597. package/src/renderers/webgl-fallback/utils/WebGLState.js +223 -6
  598. package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +118 -73
  599. package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +10 -10
  600. package/src/renderers/webgl-fallback/utils/WebGLUtils.js +9 -3
  601. package/src/renderers/webgpu/WebGPUBackend.js +733 -302
  602. package/src/renderers/webgpu/WebGPURenderer.js +2 -1
  603. package/src/renderers/webgpu/descriptors/GPUBindGroupDescriptor.js +48 -0
  604. package/src/renderers/webgpu/descriptors/GPUBufferDescriptor.js +57 -0
  605. package/src/renderers/webgpu/descriptors/GPUCommandEncoderDescriptor.js +30 -0
  606. package/src/renderers/webgpu/descriptors/GPUComputePassDescriptor.js +38 -0
  607. package/src/renderers/webgpu/descriptors/GPUComputePipelineDescriptor.js +48 -0
  608. package/src/renderers/webgpu/descriptors/GPUCopyExternalImageDestInfo.js +47 -0
  609. package/src/renderers/webgpu/descriptors/GPUCopyExternalImageSourceInfo.js +50 -0
  610. package/src/renderers/webgpu/descriptors/GPUExtent3D.js +51 -0
  611. package/src/renderers/webgpu/descriptors/GPUPipelineLayoutDescriptor.js +39 -0
  612. package/src/renderers/webgpu/descriptors/GPUQuerySetDescriptor.js +47 -0
  613. package/src/renderers/webgpu/descriptors/GPURenderBundleEncoderDescriptor.js +74 -0
  614. package/src/renderers/webgpu/descriptors/GPURenderPassColorAttachment.js +72 -0
  615. package/src/renderers/webgpu/descriptors/GPURenderPassDepthStencilAttachment.js +99 -0
  616. package/src/renderers/webgpu/descriptors/GPURenderPassDescriptor.js +72 -0
  617. package/src/renderers/webgpu/descriptors/GPURenderPassTimestampWrites.js +49 -0
  618. package/src/renderers/webgpu/descriptors/GPURenderPipelineDescriptor.js +130 -0
  619. package/src/renderers/webgpu/descriptors/GPUSamplerDescriptor.js +119 -0
  620. package/src/renderers/webgpu/descriptors/GPUShaderModuleDescriptor.js +46 -0
  621. package/src/renderers/webgpu/descriptors/GPUTexelCopyBufferInfo.js +57 -0
  622. package/src/renderers/webgpu/descriptors/GPUTexelCopyBufferLayout.js +48 -0
  623. package/src/renderers/webgpu/descriptors/GPUTexelCopyTextureInfo.js +61 -0
  624. package/src/renderers/webgpu/descriptors/GPUTextureDescriptor.js +99 -0
  625. package/src/renderers/webgpu/descriptors/GPUTextureViewDescriptor.js +108 -0
  626. package/src/renderers/webgpu/nodes/StandardNodeLibrary.js +0 -1
  627. package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +526 -93
  628. package/src/renderers/webgpu/nodes/WGSLNodeFunction.js +1 -1
  629. package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +194 -61
  630. package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +395 -234
  631. package/src/renderers/webgpu/utils/WebGPUCapabilities.js +48 -0
  632. package/src/renderers/webgpu/utils/WebGPUConstants.js +10 -0
  633. package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +224 -91
  634. package/src/renderers/webgpu/utils/WebGPUTexturePassUtils.js +228 -213
  635. package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +428 -175
  636. package/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +40 -25
  637. package/src/renderers/webgpu/utils/WebGPUUtils.js +70 -13
  638. package/src/renderers/webxr/WebXRController.js +12 -0
  639. package/src/renderers/webxr/WebXRManager.js +4 -2
  640. package/src/textures/CubeDepthTexture.js +76 -0
  641. package/src/textures/DepthTexture.js +1 -1
  642. package/src/textures/ExternalTexture.js +0 -3
  643. package/src/textures/HTMLTexture.js +74 -0
  644. package/src/textures/Source.js +2 -2
  645. package/src/textures/Texture.js +16 -5
  646. package/src/utils.js +280 -3
  647. package/examples/fonts/LICENSE +0 -13
  648. package/examples/fonts/MPLUSRounded1c/MPLUSRounded1c-Regular.typeface.json.zip +0 -0
  649. package/examples/fonts/MPLUSRounded1c/OFL.txt +0 -91
  650. package/examples/fonts/README.md +0 -11
  651. package/examples/fonts/droid/NOTICE +0 -190
  652. package/examples/fonts/droid/README.txt +0 -18
  653. package/examples/fonts/droid/droid_sans_bold.typeface.json +0 -1
  654. package/examples/fonts/droid/droid_sans_mono_regular.typeface.json +0 -1
  655. package/examples/fonts/droid/droid_sans_regular.typeface.json +0 -1
  656. package/examples/fonts/droid/droid_serif_bold.typeface.json +0 -1
  657. package/examples/fonts/droid/droid_serif_regular.typeface.json +0 -1
  658. package/examples/fonts/gentilis_bold.typeface.json +0 -1
  659. package/examples/fonts/gentilis_regular.typeface.json +0 -1
  660. package/examples/fonts/helvetiker_bold.typeface.json +0 -1
  661. package/examples/fonts/helvetiker_regular.typeface.json +0 -1
  662. package/examples/fonts/optimer_bold.typeface.json +0 -1
  663. package/examples/fonts/optimer_regular.typeface.json +0 -1
  664. package/examples/fonts/ttf/README.md +0 -9
  665. package/examples/fonts/ttf/kenpixel.ttf +0 -0
  666. package/examples/jsm/libs/ammo.wasm.js +0 -822
  667. package/examples/jsm/libs/ammo.wasm.wasm +0 -0
  668. package/examples/jsm/libs/draco/draco_encoder.js +0 -33
  669. package/examples/jsm/libs/draco/gltf/draco_encoder.js +0 -33
  670. package/examples/jsm/libs/lottie_canvas.module.js +0 -14849
  671. package/examples/jsm/libs/opentype.module.js +0 -14506
  672. package/examples/jsm/libs/rhino3dm/rhino3dm.js +0 -21
  673. package/examples/jsm/libs/rhino3dm/rhino3dm.module.js +0 -16
  674. package/examples/jsm/libs/rhino3dm/rhino3dm.wasm +0 -0
  675. package/examples/jsm/lighting/TiledLighting.js +0 -42
  676. package/examples/jsm/materials/MeshGouraudMaterial.js +0 -434
  677. package/examples/jsm/materials/MeshPostProcessingMaterial.js +0 -167
  678. package/examples/jsm/shaders/GodRaysShader.js +0 -333
  679. package/examples/jsm/tsl/display/AnamorphicNode.js +0 -282
  680. package/examples/jsm/tsl/lighting/TiledLightsNode.js +0 -442
  681. package/src/nodes/accessors/BatchNode.js +0 -163
  682. package/src/nodes/accessors/InstanceNode.js +0 -244
  683. package/src/nodes/accessors/InstancedMeshNode.js +0 -50
  684. package/src/nodes/accessors/SceneNode.js +0 -145
  685. package/src/nodes/accessors/SkinningNode.js +0 -327
  686. package/src/nodes/code/ScriptableNode.js +0 -726
  687. package/src/nodes/code/ScriptableValueNode.js +0 -253
  688. package/src/nodes/display/PosterizeNode.js +0 -65
  689. package/src/nodes/functions/BSDF/DFGApprox.js +0 -71
  690. package/src/nodes/utils/RemapNode.js +0 -125
  691. package/src/nodes/utils/SpriteSheetUVNode.js +0 -90
  692. package/src/renderers/webgl/WebGLCubeMaps.js +0 -99
  693. package/src/renderers/webgl/WebGLCubeUVMaps.js +0 -134
@@ -0,0 +1,1357 @@
1
+ import {
2
+ BoxGeometry,
3
+ BufferAttribute,
4
+ BufferGeometry,
5
+ ExtrudeGeometry,
6
+ InterpolationSamplingMode,
7
+ InterpolationSamplingType,
8
+ LatheGeometry,
9
+ Matrix3,
10
+ Matrix4,
11
+ Mesh,
12
+ MeshStandardMaterial,
13
+ Path,
14
+ PlaneGeometry,
15
+ ShapeGeometry,
16
+ Shape,
17
+ Sphere,
18
+ Vector2,
19
+ Vector3
20
+ } from 'three';
21
+
22
+ import { MeshStandardNodeMaterial } from '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 'three/tsl';
24
+
25
+ import { mergeGeometries } from '../../utils/BufferGeometryUtils.js';
26
+
27
+ const _scale = /*@__PURE__*/ new Vector3();
28
+ const _point = /*@__PURE__*/ new Vector3();
29
+ const _normalMatrix = /*@__PURE__*/ new Matrix3();
30
+ const _identity = /*@__PURE__*/ new Matrix4();
31
+
32
+ // material-zone codes baked per vertex into the merged geometry, so one material can
33
+ // 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;
36
+
37
+ // fraction of a floor's height taken by the glazed opening; the remainder is
38
+ // the spandrel band. shared by the window module and the spandrels so they tile.
39
+ const WINDOW_HEIGHT_RATIO = 0.62;
40
+
41
+ // width of the flat window-frame band around the glazing; shared by the frame module
42
+ // and the glass pane so the pane always tucks inside the frame
43
+ const WINDOW_BORDER = 0.1;
44
+
45
+ // the masonry course module ( brick height × length ). the generator snaps floor and
46
+ // bay dimensions to it, and the material's coursing reads the same values, so the
47
+ // procedural brickwork lines up with the geometry
48
+ const BRICK = { height: 0.3, length: 0.6 };
49
+
50
+ // merging requires all-indexed or all-non-indexed inputs; extrusions are
51
+ // non-indexed while boxes/planes are indexed, so normalize before merging
52
+
53
+ function merge( geometries ) {
54
+
55
+ return mergeGeometries( geometries.map( ( g ) => g.index ? g.toNonIndexed() : g ) );
56
+
57
+ }
58
+
59
+ function nonIndexed( geometry ) {
60
+
61
+ return geometry.index ? geometry.toNonIndexed() : geometry;
62
+
63
+ }
64
+
65
+ // the unit box is identical for every building's shell boxes — build it once
66
+ const _unitBox = /*@__PURE__*/ nonIndexed( new BoxGeometry( 1, 1, 1 ) );
67
+
68
+ /**
69
+ * Bakes a list of instance groups into one non-indexed BufferGeometry. Each group is a
70
+ * base geometry ( position + normal + uv ), an array of Matrix4 placements and a `partId`
71
+ * written to a per-vertex attribute. Transforming straight into preallocated typed arrays
72
+ * avoids mergeGeometries' per-instance allocations; the result is one geometry, ready for
73
+ * a single draw call and the compute rasterizer.
74
+ */
75
+ function bakeGroups( groups ) {
76
+
77
+ let total = 0;
78
+ for ( const group of groups ) total += group.geometry.attributes.position.count * group.matrices.length;
79
+
80
+ const position = new Float32Array( total * 3 );
81
+ const normal = new Float32Array( total * 3 );
82
+ const uv = new Float32Array( total * 2 );
83
+ const partId = new Float32Array( total );
84
+ // per-window interior-mapping room ( centre + size ) the glass pane looks into; only
85
+ // the glass group writes it, every other vertex stays zero. baked per vertex so the
86
+ // material reads each building's own room sizes without a global uniform.
87
+ const roomCenter = new Float32Array( total * 3 );
88
+ const roomSize = new Float32Array( total * 2 );
89
+
90
+ let w = 0;
91
+
92
+ // the bounding sphere falls out of the AABB gathered while transforming, sparing a
93
+ // second full pass over the positions ( computeBoundingSphere )
94
+ let minX = Infinity, minY = Infinity, minZ = Infinity;
95
+ let maxX = - Infinity, maxY = - Infinity, maxZ = - Infinity;
96
+
97
+ for ( const group of groups ) {
98
+
99
+ const geometry = group.geometry;
100
+ const P = geometry.attributes.position.array;
101
+ const N = geometry.attributes.normal.array;
102
+ const U = geometry.attributes.uv.array;
103
+ const count = geometry.attributes.position.count;
104
+ const id = group.partId;
105
+ const rooms = group.rooms; // per-instance { center, size }, glass only
106
+ const rigid = group.rigid === true; // pure rotation ( + translation ): the normal matrix is the rotation itself
107
+
108
+ for ( let i = 0; i < group.matrices.length; i ++ ) {
109
+
110
+ const room = rooms ? rooms[ i ] : null;
111
+
112
+ const matrix = group.matrices[ i ];
113
+ const e = matrix.elements;
114
+ const e0 = e[ 0 ], e1 = e[ 1 ], e2 = e[ 2 ], e4 = e[ 4 ], e5 = e[ 5 ], e6 = e[ 6 ], e8 = e[ 8 ], e9 = e[ 9 ], e10 = e[ 10 ], e12 = e[ 12 ], e13 = e[ 13 ], e14 = e[ 14 ];
115
+
116
+ // for a rigid frame the inverse-transpose equals the rotation, so its columns
117
+ // are read straight from the matrix and the per-instance 3×3 inverse is skipped
118
+ let n0, n1, n2, n3, n4, n5, n6, n7, n8;
119
+
120
+ if ( rigid ) {
121
+
122
+ n0 = e0; n1 = e1; n2 = e2; n3 = e4; n4 = e5; n5 = e6; n6 = e8; n7 = e9; n8 = e10;
123
+
124
+ } else {
125
+
126
+ const ne = _normalMatrix.getNormalMatrix( matrix ).elements;
127
+ n0 = ne[ 0 ]; n1 = ne[ 1 ]; n2 = ne[ 2 ]; n3 = ne[ 3 ]; n4 = ne[ 4 ]; n5 = ne[ 5 ]; n6 = ne[ 6 ]; n7 = ne[ 7 ]; n8 = ne[ 8 ];
128
+
129
+ }
130
+
131
+ for ( let v = 0; v < count; v ++ ) {
132
+
133
+ const v3 = v * 3, w3 = w * 3;
134
+ const x = P[ v3 ], y = P[ v3 + 1 ], z = P[ v3 + 2 ];
135
+ const wx = e0 * x + e4 * y + e8 * z + e12;
136
+ const wy = e1 * x + e5 * y + e9 * z + e13;
137
+ const wz = e2 * x + e6 * y + e10 * z + e14;
138
+ position[ w3 ] = wx; position[ w3 + 1 ] = wy; position[ w3 + 2 ] = wz;
139
+ if ( wx < minX ) minX = wx; if ( wx > maxX ) maxX = wx;
140
+ if ( wy < minY ) minY = wy; if ( wy > maxY ) maxY = wy;
141
+ if ( wz < minZ ) minZ = wz; if ( wz > maxZ ) maxZ = wz;
142
+
143
+ const nx = N[ v3 ], ny = N[ v3 + 1 ], nz = N[ v3 + 2 ];
144
+ const tx = n0 * nx + n3 * ny + n6 * nz, ty = n1 * nx + n4 * ny + n7 * nz, tz = n2 * nx + n5 * ny + n8 * nz;
145
+ const inv = 1 / ( Math.sqrt( tx * tx + ty * ty + tz * tz ) || 1 );
146
+ normal[ w3 ] = tx * inv; normal[ w3 + 1 ] = ty * inv; normal[ w3 + 2 ] = tz * inv;
147
+
148
+ uv[ w * 2 ] = U[ v * 2 ]; uv[ w * 2 + 1 ] = U[ v * 2 + 1 ];
149
+ partId[ w ] = id;
150
+
151
+ if ( room !== null ) {
152
+
153
+ roomCenter[ w3 ] = room.center.x; roomCenter[ w3 + 1 ] = room.center.y; roomCenter[ w3 + 2 ] = room.center.z;
154
+ roomSize[ w * 2 ] = room.size.x; roomSize[ w * 2 + 1 ] = room.size.y;
155
+
156
+ }
157
+
158
+ w ++;
159
+
160
+ }
161
+
162
+ }
163
+
164
+ }
165
+
166
+ const geometry = new BufferGeometry();
167
+ geometry.setAttribute( 'position', new BufferAttribute( position, 3 ) );
168
+ geometry.setAttribute( 'normal', new BufferAttribute( normal, 3 ) );
169
+ geometry.setAttribute( 'uv', new BufferAttribute( uv, 2 ) );
170
+ geometry.setAttribute( 'partId', new BufferAttribute( partId, 1 ) );
171
+ geometry.setAttribute( 'roomCenter', new BufferAttribute( roomCenter, 3 ) );
172
+ geometry.setAttribute( 'roomSize', new BufferAttribute( roomSize, 2 ) );
173
+
174
+ geometry.boundingSphere = new Sphere(
175
+ new Vector3( ( minX + maxX ) / 2, ( minY + maxY ) / 2, ( minZ + maxZ ) / 2 ),
176
+ Math.hypot( maxX - minX, maxY - minY, maxZ - minZ ) / 2
177
+ );
178
+
179
+ return geometry;
180
+
181
+ }
182
+
183
+ // deterministic PRNG (mulberry32) so a given seed always yields the same tower
184
+
185
+ function createRandom( seed ) {
186
+
187
+ let s = ( seed >>> 0 ) || 1;
188
+
189
+ return function () {
190
+
191
+ s = ( s + 0x6D2B79F5 ) | 0;
192
+ let t = Math.imul( s ^ ( s >>> 15 ), 1 | s );
193
+ t = ( t + Math.imul( t ^ ( t >>> 7 ), 61 | t ) ) ^ t;
194
+ return ( ( t ^ ( t >>> 14 ) ) >>> 0 ) / 4294967296;
195
+
196
+ };
197
+
198
+ }
199
+
200
+ // a stable per-floor hash ( from the floor index and the face origin ) used to pick the
201
+ // interior-mapping room module per floor without allocating a closure each floor
202
+ function floorHash( f, frame, k ) {
203
+
204
+ const s = Math.sin( f * 12.9898 + frame.origin.x * 0.07 + frame.origin.z * 0.131 + k ) * 43758.5453;
205
+ return s - Math.floor( s );
206
+
207
+ }
208
+
209
+ // the seed-driven "style" of a tower: footprint proportions, tier split and the
210
+ // shaping of piers and base arches. these sit between the fixed defaults and the
211
+ // caller's parameters, so any parameter passed in still overrides its seeded value.
212
+
213
+ function randomStyle( random ) {
214
+
215
+ const base = 0.10 + random() * 0.07;
216
+ const crown = 0.08 + random() * 0.08;
217
+
218
+ return {
219
+ footprint: { width: 26 + random() * 18, depth: 20 + random() * 14 },
220
+ tierFractions: { base, crown },
221
+ pierWidth: 0.4 + random() * 0.4,
222
+ pierDepth: 0.3 + random() * 0.3,
223
+ windowReveal: 0.12 + random() * 0.1,
224
+ stringCourseHeight: 0.5 + random() * 0.5,
225
+ archBayWidthRatio: Math.round( 1.5 + random() * 1.5 ),
226
+ archRise: 0.4 + random() * 0.5
227
+ };
228
+
229
+ }
230
+
231
+ /**
232
+ * Generates intricate, tripartite "Beaux-Arts / Neo-Gothic" terracotta
233
+ * skyscrapers from a small set of parameters.
234
+ *
235
+ * The mass is read as a footprint polygon (a rectangle with one chamfered
236
+ * corner) split into vertical faces, each split into three tiers — a tall
237
+ * arcaded base, a repeating shaft and an ornate crown — then into floors and
238
+ * bays. A handful of authored pieces (a pier, a window, a cornice profile, a
239
+ * gothic arch) are instanced across the whole tower, then baked — together with
240
+ * the bespoke base arcade — into a single non-indexed BufferGeometry tagged with
241
+ * a per-vertex `partId` ({@link PartId}) so one material can shade every zone.
242
+ *
243
+ * The generator is material agnostic — it only produces geometry. Pass a single
244
+ * material (e.g. a TSL node material that branches on `partId`) to dress it.
245
+ *
246
+ * ```js
247
+ * const generator = new SkyscraperGenerator( { seed: 35, totalHeight: 140 }, material );
248
+ * scene.add( generator.build() ); // a single Mesh
249
+ * ```
250
+ */
251
+ class SkyscraperGenerator {
252
+
253
+ constructor( parameters = {}, material = null ) {
254
+
255
+ this.parameters = parameters; // caller overrides; defaults + seed fill the rest at build time
256
+ this.material = material; // a single material; the look is driven by the baked `partId` attribute
257
+
258
+ this.mesh = null;
259
+
260
+ }
261
+
262
+ setParameters( parameters ) {
263
+
264
+ Object.assign( this.parameters, parameters );
265
+
266
+ return this;
267
+
268
+ }
269
+
270
+ build() {
271
+
272
+ const random = createRandom( this.parameters.seed ?? SkyscraperGenerator.defaults.seed );
273
+
274
+ // precedence: fixed defaults < seed-driven style < caller parameters
275
+
276
+ const p = Object.assign( {}, SkyscraperGenerator.defaults, randomStyle( random ), this.parameters );
277
+
278
+ // snap the masonry-driving dimensions to the brick module so the procedural
279
+ // brickwork ( courses up local Y, columns along each face ) lines up with the
280
+ // geometry: a whole number of courses per floor and bricks per bay
281
+ const vModule = BRICK.height * 2; // a course pair, so floor / window halves still land on a joint
282
+ p.floorHeight = Math.max( vModule * 3, Math.round( p.floorHeight / vModule ) * vModule );
283
+ p.windowHeight = Math.round( p.floorHeight * WINDOW_HEIGHT_RATIO / vModule ) * vModule;
284
+ p.bayWidth = Math.max( BRICK.length * 3, Math.round( p.bayWidth / BRICK.length ) * BRICK.length );
285
+ p.pierWidth = Math.max( BRICK.length, Math.round( p.pierWidth / BRICK.length ) * BRICK.length );
286
+
287
+ // vertical layout: base / shaft / crown as whole floor counts, so every floor
288
+ // line sits on a course ( the requested total height is rounded to suit )
289
+ const floors = Math.max( 3, Math.round( p.totalHeight / p.floorHeight ) );
290
+ const baseFloors = Math.max( 1, Math.round( floors * p.tierFractions.base ) );
291
+ const crownFloors = Math.max( 1, Math.round( floors * p.tierFractions.crown ) );
292
+ const shaftFloors = Math.max( 1, floors - baseFloors - crownFloors );
293
+
294
+ const baseHeight = baseFloors * p.floorHeight;
295
+ const crownHeight = crownFloors * p.floorHeight;
296
+ const shaftHeight = shaftFloors * p.floorHeight;
297
+ p.totalHeight = baseHeight + shaftHeight + crownHeight;
298
+
299
+ const baseTop = baseHeight;
300
+ const shaftTop = baseHeight + shaftHeight;
301
+
302
+ // one accumulator per kind of part, mostly instance matrices. kept separate so the
303
+ // bake below can order them by draw order ( which controls overdraw ), not build order.
304
+
305
+ const windows = [];
306
+ const glass = [];
307
+ const glassRooms = []; // per-glass interior-mapping room ( centre + size ), aligned with `glass`
308
+ const backWalls = []; // the thin wall closing the volume behind the glass
309
+ const bands = []; // spandrel bands, one at each floor line
310
+ const piers = new Map(); // pier height -> matrices, so each tier's continuous piers share one geometry
311
+ const trim = []; // cornices and parapets ( axis-aligned unit boxes )
312
+ const acUnits = []; // window air-conditioner boxes on a random subset of shaft windows
313
+ const finials = []; // pinnacles along the crown
314
+ const extras = []; // bespoke geometry: the base arcade and the setback / roof slabs
315
+
316
+ const addPier = ( frame, u, vBottom, height ) => {
317
+
318
+ const key = Math.round( height * 1000 ); // bucket equal pier heights ( a number key, no string )
319
+ if ( piers.has( key ) === false ) piers.set( key, [] );
320
+ piers.get( key ).push( frame.matrix( u, vBottom, 0 ) );
321
+
322
+ };
323
+
324
+ // footprints: full mass, and the inset crown after the setback
325
+
326
+ const footprint = buildFootprint( p.footprint.width, p.footprint.depth, p.chamferWidth, p.chamferCornerX, p.chamferCornerZ );
327
+ const faces = buildFaces( footprint );
328
+
329
+ const inset = p.setbackDepth * p.bayWidth;
330
+ const crownFootprint = buildFootprint(
331
+ Math.max( p.bayWidth * 2, p.footprint.width - inset * 2 ),
332
+ Math.max( p.bayWidth * 2, p.footprint.depth - inset * 2 ),
333
+ Math.max( 0, p.chamferWidth - inset ),
334
+ p.chamferCornerX,
335
+ p.chamferCornerZ
336
+ );
337
+ const crownFaces = buildFaces( crownFootprint );
338
+
339
+ // --- generate the parts -----------------------------------------------
340
+
341
+ const crownCornice = p.stringCourseHeight * 1.6; // the crown's heavy cap; its piers stop below it
342
+
343
+ // shaft and crown are the same facade over different faces, spans and pier heights
344
+ const tiers = [
345
+ { faces, bottom: baseTop, height: shaftHeight, pierHeight: shaftHeight, ac: acUnits },
346
+ { faces: crownFaces, bottom: shaftTop, height: crownHeight, pierHeight: crownHeight - crownCornice, ac: null }
347
+ ];
348
+
349
+ for ( const t of tiers ) {
350
+
351
+ for ( const frame of t.faces ) {
352
+
353
+ addWindows( frame, windows, glass, glassRooms, t.ac, t.bottom, t.height, p );
354
+ addWall( backWalls, frame, t.bottom, t.bottom + t.height, 0.8, - 0.6 );
355
+ addSpandrelBands( bands, frame, t.bottom, t.height, p );
356
+ addPiers( frame, t.bottom, t.pierHeight, p, addPier );
357
+
358
+ }
359
+
360
+ }
361
+
362
+ // the base: a gothic arcade, capped by a string course
363
+ for ( const frame of faces ) {
364
+
365
+ addArcade( extras, frame, baseHeight, p );
366
+ addCornice( trim, frame, baseTop - p.stringCourseHeight, p.stringCourseHeight, 0.5 );
367
+
368
+ }
369
+
370
+ // periodic string courses banding the shaft
371
+ if ( p.stringCourseEvery > 0 ) {
372
+
373
+ for ( let f = p.stringCourseEvery; f < shaftFloors; f += p.stringCourseEvery ) {
374
+
375
+ for ( const frame of faces ) addCornice( trim, frame, baseTop + f * p.floorHeight - p.stringCourseHeight * 0.5, p.stringCourseHeight, 0.3 );
376
+
377
+ }
378
+
379
+ }
380
+
381
+ // the crown's heavy cornice, its parapet and the finials along the top
382
+ for ( const frame of crownFaces ) {
383
+
384
+ addCornice( trim, frame, p.totalHeight - crownCornice, crownCornice, 0.9 );
385
+ addParapet( trim, frame, p.totalHeight, p );
386
+ addFinials( frame, finials, shaftTop, crownHeight, p );
387
+
388
+ }
389
+
390
+ // thin slabs capping the setback ledge and the roof
391
+ extras.push( slab( footprint, shaftTop, 0.6 ) );
392
+ extras.push( slab( crownFootprint, p.totalHeight, 0.6 ) );
393
+
394
+ // --- bake every part into one geometry ---------------------------------
395
+
396
+ // one mesh = one draw the renderer can't sort, so bake order is draw order: the
397
+ // facade front-to-back, the backing wall last so its hidden fragments never shade.
398
+
399
+ const groups = [
400
+ { geometry: buildWindowGeometry( p ), matrices: windows, partId: FRAME, rigid: true },
401
+ { geometry: nonIndexed( buildGlassGeometry( p ) ), matrices: glass, partId: GLASS, rooms: glassRooms, rigid: true },
402
+ { geometry: _unitBox, matrices: bands, partId: WALL }
403
+ ];
404
+
405
+ for ( const [ key, matrices ] of piers ) groups.push( { geometry: buildPierGeometry( p, key / 1000 ), matrices, partId: PIER, rigid: true } );
406
+
407
+ groups.push( { geometry: _unitBox, matrices: trim, partId: WALL } ); // cornices, parapets
408
+ groups.push( { geometry: _unitBox, matrices: acUnits, partId: AC } );
409
+ groups.push( { geometry: nonIndexed( buildFinialGeometry( p ) ), matrices: finials, partId: ORNAMENT, rigid: true } );
410
+
411
+ for ( const geometry of extras ) groups.push( { geometry: nonIndexed( geometry ), matrices: [ _identity ], partId: WALL, rigid: true } ); // base arcade + slabs, in building-local space
412
+
413
+ groups.push( { geometry: _unitBox, matrices: backWalls, partId: WALL } ); // last — hidden behind the facade
414
+
415
+ const geometry = bakeGroups( groups );
416
+
417
+ const mesh = new Mesh( geometry, this.material || new MeshStandardMaterial( { color: 0xddccaa, roughness: 0.9 } ) );
418
+ mesh.name = 'Skyscraper';
419
+
420
+ this.dispose();
421
+ this.mesh = mesh;
422
+
423
+ return mesh;
424
+
425
+ }
426
+
427
+ rebuild() {
428
+
429
+ return this.build();
430
+
431
+ }
432
+
433
+ dispose() {
434
+
435
+ if ( this.mesh === null ) return;
436
+
437
+ this.mesh.geometry.dispose();
438
+ this.mesh = null;
439
+
440
+ }
441
+
442
+ }
443
+
444
+ // fixed baseline. the remaining parameters (footprint, tierFractions, pierWidth,
445
+ // pierDepth, windowReveal, stringCourseHeight, archBayWidthRatio, archRise) are
446
+ // derived from the seed by randomStyle() unless the caller provides them.
447
+ SkyscraperGenerator.defaults = {
448
+ seed: 35,
449
+ totalHeight: 140,
450
+ floorHeight: 4,
451
+ bayWidth: 2.6,
452
+ stringCourseEvery: 6,
453
+ chamferWidth: 4,
454
+ chamferCornerX: 1,
455
+ chamferCornerZ: 1,
456
+ setbackDepth: 1.5,
457
+ acChance: 0.12
458
+ };
459
+
460
+ // --- footprint & faces ---------------------------------------------------
461
+
462
+ /**
463
+ * A rectangle (centred at the origin in the XZ plane) with one corner cut at
464
+ * 45 degrees, returned as an ordered list of `Vector2( x, z )`. `cornerX` /
465
+ * `cornerZ` ( each ±1 ) pick which corner is cut, so the chamfer can be aimed
466
+ * outward to a block corner.
467
+ */
468
+ function buildFootprint( width, depth, chamfer, cornerX = 1, cornerZ = 1 ) {
469
+
470
+ const hw = width / 2;
471
+ const hd = depth / 2;
472
+ const c = Math.min( chamfer, hw, hd );
473
+
474
+ // the four corners, counter-clockwise
475
+ const corners = [
476
+ new Vector2( hw, hd ),
477
+ new Vector2( - hw, hd ),
478
+ new Vector2( - hw, - hd ),
479
+ new Vector2( hw, - hd )
480
+ ];
481
+
482
+ const points = [];
483
+
484
+ for ( let i = 0; i < corners.length; i ++ ) {
485
+
486
+ const corner = corners[ i ];
487
+
488
+ // cut the requested corner: replace it with two points pulled back along
489
+ // each adjacent edge, leaving a 45° face that points out to that corner
490
+ if ( c > 0 && Math.sign( corner.x ) === cornerX && Math.sign( corner.y ) === cornerZ ) {
491
+
492
+ const prev = corners[ ( i + 3 ) % 4 ];
493
+ const next = corners[ ( i + 1 ) % 4 ];
494
+ points.push( corner.clone().lerp( prev, c / corner.distanceTo( prev ) ) );
495
+ points.push( corner.clone().lerp( next, c / corner.distanceTo( next ) ) );
496
+
497
+ } else {
498
+
499
+ points.push( corner.clone() );
500
+
501
+ }
502
+
503
+ }
504
+
505
+ return points;
506
+
507
+ }
508
+
509
+ /**
510
+ * Builds a face frame per footprint edge. Each frame is an orthonormal basis
511
+ * ( u along the edge, v up, n outward ) plus an origin and length, so all
512
+ * facade layout can happen in flat ( u, v ) space and bake to world with one
513
+ * matrix — the same authored piece then instances onto every face, including
514
+ * the diagonal chamfer.
515
+ */
516
+ function buildFaces( points ) {
517
+
518
+ const faces = [];
519
+ const up = new Vector3( 0, 1, 0 );
520
+
521
+ for ( let i = 0; i < points.length; i ++ ) {
522
+
523
+ const a = points[ i ];
524
+ const b = points[ ( i + 1 ) % points.length ];
525
+
526
+ // outward normal: perpendicular to the edge, pointing away from the
527
+ // origin (the footprint is centred there)
528
+
529
+ const n = new Vector3( b.y - a.y, 0, - ( b.x - a.x ) ).normalize();
530
+ const mid = new Vector3( ( a.x + b.x ) / 2, 0, ( a.y + b.y ) / 2 );
531
+ if ( n.dot( mid ) < 0 ) n.negate();
532
+
533
+ // right-handed basis: u = v × n, so makeBasis( u, v, n ) is a pure rotation
534
+
535
+ const u = new Vector3().crossVectors( up, n ).normalize();
536
+
537
+ const pa = new Vector3( a.x, 0, a.y );
538
+ const pb = new Vector3( b.x, 0, b.y );
539
+ const length = pa.distanceTo( pb );
540
+
541
+ // the edge end that u points away from becomes the origin
542
+
543
+ const origin = pb.clone().sub( pa ).dot( u ) > 0 ? pa : pb;
544
+
545
+ faces.push( new FaceFrame( origin, u, up.clone(), n, length ) );
546
+
547
+ }
548
+
549
+ return faces;
550
+
551
+ }
552
+
553
+ /** A face's local ( u along edge, v up, n outward ) frame in world space. */
554
+ class FaceFrame {
555
+
556
+ constructor( origin, u, v, n, length ) {
557
+
558
+ this.origin = origin;
559
+ this.u = u;
560
+ this.v = v;
561
+ this.n = n;
562
+ this.length = length;
563
+
564
+ }
565
+
566
+ point( u, v, w, target = new Vector3() ) {
567
+
568
+ return target
569
+ .copy( this.origin )
570
+ .addScaledVector( this.u, u )
571
+ .addScaledVector( this.v, v )
572
+ .addScaledVector( this.n, w );
573
+
574
+ }
575
+
576
+ /** Places a piece authored in the canonical local frame ( x across, y up, z outward ). */
577
+ matrix( u, v, w ) {
578
+
579
+ return new Matrix4()
580
+ .makeBasis( this.u, this.v, this.n )
581
+ .setPosition( this.point( u, v, w, _point ) );
582
+
583
+ }
584
+
585
+ /** How many bays of `bayWidth` fit, with the remainder split into end margins. */
586
+ bays( bayWidth ) {
587
+
588
+ const count = Math.max( 1, Math.floor( this.length / bayWidth ) );
589
+ const margin = ( this.length - count * bayWidth ) / 2;
590
+
591
+ return { count, margin, width: bayWidth };
592
+
593
+ }
594
+
595
+ }
596
+
597
+ // --- shell pieces --------------------------------------------------------
598
+
599
+ // a Matrix4 mapping the shared unit box ( 1×1×1, centred ) onto a face-aligned
600
+ // box of the given size, centred at the given face-local point. these matrices
601
+ // are what the shell InstancedMesh is built from.
602
+ function boxMatrix( frame, u, v, w, sizeU, sizeV, sizeN ) {
603
+
604
+ return new Matrix4()
605
+ .makeBasis( frame.u, frame.v, frame.n )
606
+ .scale( _scale.set( sizeU, sizeV, sizeN ) )
607
+ .setPosition( frame.point( u, v, w, _point ) );
608
+
609
+ }
610
+
611
+ function addWall( target, frame, vBottom, vTop, thickness = 0.8, front = 0 ) {
612
+
613
+ const h = vTop - vBottom;
614
+ target.push( boxMatrix( frame, frame.length / 2, vBottom + h / 2, front - thickness / 2, frame.length + thickness * 2, h, thickness ) );
615
+
616
+ }
617
+
618
+ /**
619
+ * Horizontal terracotta bands at every floor line. Together with the projecting
620
+ * piers they form the facade grid; the gaps between them are the window
621
+ * openings, with glass set behind.
622
+ */
623
+ function addSpandrelBands( target, frame, vBottom, height, p ) {
624
+
625
+ const floors = Math.max( 1, Math.round( height / p.floorHeight ) );
626
+ const fh = height / floors;
627
+ const bandHeight = p.floorHeight - p.windowHeight; // whole courses: floor minus the glazed opening
628
+
629
+ // pull the ends in by the band depth so a band doesn't poke its end-cap
630
+ // into the plane of the perpendicular face at the corners ( overdraw )
631
+ const bandLength = Math.max( 0.2, frame.length - 0.6 );
632
+
633
+ for ( let f = 0; f <= floors; f ++ ) {
634
+
635
+ // 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 ) );
637
+
638
+ }
639
+
640
+ }
641
+
642
+ /**
643
+ * A thin horizontal cap over a footprint's bounding box at height `y`. Its
644
+ * sides are pulled in behind the facade plane ( into the backing-wall shell )
645
+ * so they never sit coplanar with the walls, spandrels or piers and z-fight.
646
+ */
647
+ function slab( footprint, y, thickness ) {
648
+
649
+ // a thin cap following the footprint OUTLINE ( so the chamfered corner is cut, not
650
+ // left overhanging as a rectangular box ), inset a little so its edge tucks just
651
+ // behind the facade and the wall top reads as a lip around it
652
+
653
+ const inset = 0.8;
654
+ let cx = 0, cz = 0;
655
+ for ( const p of footprint ) {
656
+
657
+ cx += p.x; cz += p.y;
658
+
659
+ }
660
+
661
+ cx /= footprint.length; cz /= footprint.length;
662
+
663
+ // consistent ( CCW ) winding so the extrude caps face up / down correctly
664
+ let area = 0;
665
+ for ( let i = 0; i < footprint.length; i ++ ) {
666
+
667
+ const a = footprint[ i ], b = footprint[ ( i + 1 ) % footprint.length ];
668
+ area += a.x * b.y - b.x * a.y;
669
+
670
+ }
671
+
672
+ const pts = area < 0 ? footprint.slice().reverse() : footprint;
673
+
674
+ const shape = new Shape();
675
+ pts.forEach( ( p, i ) => {
676
+
677
+ const dx = cx - p.x, dz = cz - p.y;
678
+ const d = Math.hypot( dx, dz ) || 1;
679
+ const x = p.x + dx / d * inset;
680
+ const z = p.y + dz / d * inset;
681
+ if ( i === 0 ) shape.moveTo( x, z ); else shape.lineTo( x, z );
682
+
683
+ } );
684
+
685
+ // extrude the XZ outline downward by the thickness, the top dropped just below height y:
686
+ // the inset cap would otherwise sit coplanar with the surrounding wall top faces and
687
+ // z-fight, and the parapet / spandrel bands around the edge hide the shallow recess
688
+ const drop = 0.2;
689
+ const geometry = new ExtrudeGeometry( shape, { depth: thickness, bevelEnabled: false } );
690
+ geometry.rotateX( Math.PI / 2 );
691
+ geometry.translate( 0, y - drop, 0 );
692
+ return geometry;
693
+
694
+ }
695
+
696
+ /** A two-step projecting cornice / string-course band wrapping a face. */
697
+ function addCornice( target, frame, vBottom, height, depth ) {
698
+
699
+ target.push( boxMatrix( frame, frame.length / 2, vBottom + height * 0.275, depth / 2, frame.length, height * 0.55, depth ) );
700
+ target.push( boxMatrix( frame, frame.length / 2, vBottom + height * 0.775, depth * 0.85, frame.length, height * 0.45, depth * 1.7 ) );
701
+
702
+ }
703
+
704
+ /** A low parapet wall capping the crown. */
705
+ function addParapet( target, frame, vTop, p ) {
706
+
707
+ const height = 1.4;
708
+ target.push( boxMatrix( frame, frame.length / 2, vTop + height / 2, p.pierDepth * 0.4, frame.length, height, p.pierDepth * 0.8 ) );
709
+
710
+ }
711
+
712
+ /**
713
+ * The base storey: a wall pierced by tall pointed-arch openings, extruded with
714
+ * thickness so the openings read as deep recesses.
715
+ */
716
+ function addArcade( target, frame, height, p ) {
717
+
718
+ const archWidth = p.bayWidth * p.archBayWidthRatio;
719
+ const { count, margin } = frame.bays( archWidth );
720
+
721
+ const sill = height * 0.04;
722
+ const spring = height * 0.55;
723
+ const apex = Math.min( height * 0.96, spring + ( archWidth / 2 ) * ( 0.8 + p.archRise ) );
724
+
725
+ const shape = new Shape();
726
+ shape.moveTo( 0, 0 );
727
+ shape.lineTo( frame.length, 0 );
728
+ shape.lineTo( frame.length, height );
729
+ shape.lineTo( 0, height );
730
+ shape.lineTo( 0, 0 );
731
+
732
+ for ( let i = 0; i < count; i ++ ) {
733
+
734
+ const cx = margin + ( i + 0.5 ) * archWidth;
735
+ const hw = archWidth * 0.34;
736
+
737
+ const hole = new Path();
738
+ hole.moveTo( cx - hw, sill );
739
+ hole.lineTo( cx - hw, spring );
740
+ hole.quadraticCurveTo( cx - hw, apex, cx, apex );
741
+ hole.quadraticCurveTo( cx + hw, apex, cx + hw, spring );
742
+ hole.lineTo( cx + hw, sill );
743
+ hole.lineTo( cx - hw, sill );
744
+ shape.holes.push( hole );
745
+
746
+ }
747
+
748
+ const thickness = 1.1;
749
+ const geometry = new ExtrudeGeometry( shape, { depth: thickness, bevelEnabled: false, curveSegments: 8 } );
750
+ geometry.translate( 0, 0, - thickness );
751
+ geometry.applyMatrix4( frame.matrix( 0, 0, 0 ) );
752
+
753
+ target.push( geometry );
754
+
755
+ // a dark plane set behind the openings so the recesses read
756
+
757
+ const back = new PlaneGeometry( frame.length, height );
758
+ back.applyMatrix4( frame.matrix( frame.length / 2, height / 2, - thickness - 0.4 ) );
759
+ target.push( back );
760
+
761
+ }
762
+
763
+ // --- repeating field -----------------------------------------------------
764
+
765
+ function addPiers( frame, vBottom, height, p, addPier ) {
766
+
767
+ const { count, margin, width } = frame.bays( p.bayWidth );
768
+
769
+ // a pier on every bay edge except the far end: that corner is shared with
770
+ // the next face, which places its own pier there, so emitting both would
771
+ // stack two piers at each corner
772
+
773
+ for ( let i = 0; i < count; i ++ ) {
774
+
775
+ addPier( frame, margin + i * width, vBottom, height );
776
+
777
+ }
778
+
779
+ }
780
+
781
+ function addWindows( frame, windows, glass, glassRooms, acUnits, vBottom, height, p ) {
782
+
783
+ const { count, margin, width } = frame.bays( p.bayWidth );
784
+ const floors = Math.max( 1, Math.round( height / p.floorHeight ) );
785
+ const fh = height / floors;
786
+
787
+ // a window AC unit sitting on the sill, protruding from the facade. about half the window
788
+ // width, capped at a real unit's size ( ~0.66 m ) and kept wider than tall, sticking out
789
+ // about half its width
790
+ const acW = Math.min( ( p.bayWidth - p.pierWidth ) * 0.55, 0.66 );
791
+ const acH = acW * 0.6;
792
+ const acD = acW * 0.5;
793
+ const acV = - p.windowHeight / 2 + acH / 2 + WINDOW_BORDER; // bottom rests on the sill ( the top of the window's bottom frame rail )
794
+
795
+ // a real ~0.66 m unit looks lost in a wide opening, so only fit ACs where it still spans a
796
+ // fair share of the window — in practice, the narrower ( older-style ) windows
797
+ const acFits = acW >= ( width - p.pierWidth ) * 0.34;
798
+
799
+ for ( let f = 0; f < floors; f ++ ) {
800
+
801
+ const cy = vBottom + ( f + 0.5 ) * fh;
802
+
803
+ // the interior-mapping room module: one floor tall, a run of two or three bays
804
+ // wide, chosen per floor so neighbouring windows share an interior. the choice
805
+ // is deterministic ( seeded by the floor and the face ) so it is stable, and the
806
+ // run is recorded per window so the material can ray-march the right box.
807
+ const roomBays = floorHash( f, frame, 0 ) > 0.5 ? 3 : 2;
808
+ const roomPhase = Math.floor( floorHash( f, frame, 1 ) * roomBays );
809
+
810
+ for ( let b = 0; b < count; b ++ ) {
811
+
812
+ const cx = margin + ( b + 0.5 ) * width;
813
+
814
+ windows.push( frame.matrix( cx, cy, 0 ) );
815
+ glass.push( frame.matrix( cx, cy, - p.windowReveal ) );
816
+
817
+ // the run of bays this window's room spans, clamped at the face ends, recorded
818
+ // as the room's centre on the facade and its width × height in metres
819
+ const room = Math.floor( ( b + roomPhase ) / roomBays );
820
+ const bStart = Math.max( 0, room * roomBays - roomPhase );
821
+ const bEnd = Math.min( count, ( room + 1 ) * roomBays - roomPhase );
822
+ const span = bEnd - bStart;
823
+ glassRooms.push( { center: frame.point( margin + ( bStart + span / 2 ) * width, cy, - p.windowReveal ), size: new Vector2( span * width, fh - 1 ) } ); // centred on the glass plane, so the interior is anchored to the pane it is drawn on
824
+
825
+ if ( acUnits && acFits ) {
826
+
827
+ // deterministic per-window hash ( varies per face via the frame origin )
828
+ const r = Math.sin( f * 41.3 + b * 12.7 + frame.origin.x * 0.13 + frame.origin.z * 0.31 ) * 43758.5453;
829
+ // the back tucks into the window reveal ( just in front of the glass ) so the unit sits
830
+ // in the opening instead of floating on the facade
831
+ const acW0 = acD / 2 - p.windowReveal + 0.04;
832
+ if ( r - Math.floor( r ) < p.acChance ) acUnits.push( boxMatrix( frame, cx, cy + acV, acW0, acW, acH, acD ) );
833
+
834
+ }
835
+
836
+ }
837
+
838
+ }
839
+
840
+ }
841
+
842
+ function addFinials( frame, finials, vBottom, height, p ) {
843
+
844
+ const { count, margin, width } = frame.bays( p.bayWidth );
845
+ const top = vBottom + height;
846
+
847
+ // skip the far-end bay edge: it is the shared corner the next face also
848
+ // caps, so emitting both would stack two finials at each corner
849
+
850
+ for ( let i = 0; i < count; i ++ ) {
851
+
852
+ finials.push( new Matrix4().setPosition( frame.point( margin + i * width, top, p.pierDepth * 0.5, _point ) ) );
853
+
854
+ }
855
+
856
+ }
857
+
858
+ // --- authored modules ----------------------------------------------------
859
+
860
+ function buildPierGeometry( p, height ) {
861
+
862
+ // a wide pier with a slimmer pilaster raised on its face, giving the
863
+ // continuous vertical rib a stepped, terracotta profile
864
+
865
+ const back = new BoxGeometry( p.pierWidth, height, p.pierDepth * 0.6 );
866
+ back.translate( 0, height / 2, p.pierDepth * 0.3 );
867
+
868
+ // the pilaster stops just short of the pier top so that where a pier is left
869
+ // exposed ( at a setback ) the cap reads as one clean block rather than the
870
+ // back box and the pilaster stacked into a T
871
+ const pilasterHeight = Math.max( 1, height - 0.6 );
872
+ const front = new BoxGeometry( p.pierWidth * 0.55, pilasterHeight, p.pierDepth * 0.45 );
873
+ front.translate( 0, pilasterHeight / 2, p.pierDepth * 0.6 + p.pierDepth * 0.225 );
874
+
875
+ return merge( [ back, front ] );
876
+
877
+ }
878
+
879
+ function buildWindowGeometry( p ) {
880
+
881
+ // the flat frame face ( a rectangle with the glazing hole ), the four reveal walls
882
+ // of the opening and the glazing bars, merged into one instanced module. a full
883
+ // extrusion would also emit a hidden back cap and outer side walls; windows are by
884
+ // far the heaviest part of a building, so those are skipped.
885
+
886
+ const w = p.bayWidth - p.pierWidth;
887
+ const h = p.windowHeight;
888
+ const border = WINDOW_BORDER;
889
+ const depth = p.windowReveal; // reveal walls run all the way back to the glass ( placed at -windowReveal ), so no gap opens between them and the pane
890
+ const iw = w / 2 - border;
891
+ const ih = h / 2 - border;
892
+
893
+ const shape = new Shape();
894
+ shape.moveTo( - w / 2, - h / 2 );
895
+ shape.lineTo( w / 2, - h / 2 );
896
+ shape.lineTo( w / 2, h / 2 );
897
+ shape.lineTo( - w / 2, h / 2 );
898
+ shape.lineTo( - w / 2, - h / 2 );
899
+
900
+ const hole = new Path();
901
+ hole.moveTo( - iw, - ih );
902
+ hole.lineTo( - iw, ih );
903
+ hole.lineTo( iw, ih );
904
+ hole.lineTo( iw, - ih );
905
+ hole.lineTo( - iw, - ih );
906
+ shape.holes.push( hole );
907
+
908
+ const front = new ShapeGeometry( shape ); // visible frame face, flush with the facade
909
+
910
+ // the four reveal walls of the opening, set back to the glazing
911
+ const wall = ( x, y, rx, ry, sw, sh ) => {
912
+
913
+ const pl = new PlaneGeometry( sw, sh );
914
+ pl.rotateX( rx );
915
+ pl.rotateY( ry );
916
+ pl.translate( x, y, - depth / 2 );
917
+ return pl;
918
+
919
+ };
920
+
921
+ const left = wall( - iw, 0, 0, Math.PI / 2, depth, ih * 2 );
922
+ const right = wall( iw, 0, 0, - Math.PI / 2, depth, ih * 2 );
923
+ const sill = wall( 0, - ih, - Math.PI / 2, 0, iw * 2, depth );
924
+ const head = wall( 0, ih, Math.PI / 2, 0, iw * 2, depth );
925
+
926
+ // a single horizontal glazing bar ( transom ), flat, just in front of the glass —
927
+ // a thin box would triple the window's triangle count for sub-pixel thickness
928
+ const transom = new PlaneGeometry( iw * 2, 0.05 );
929
+ transom.translate( 0, h * 0.04, - depth + 0.02 ); // meeting rail, just above centre
930
+
931
+ return merge( [ front, left, right, sill, head, transom ] );
932
+
933
+ }
934
+
935
+ function buildGlassGeometry( p ) {
936
+
937
+ const w = p.bayWidth - p.pierWidth - WINDOW_BORDER * 2;
938
+ const h = p.windowHeight - WINDOW_BORDER * 2;
939
+
940
+ return new PlaneGeometry( w, h );
941
+
942
+ }
943
+
944
+ function buildFinialGeometry( p ) {
945
+
946
+ // a tapering pinnacle revolved around its axis
947
+
948
+ const s = p.pierWidth;
949
+ const profile = [
950
+ new Vector2( 0.0, 0 ),
951
+ new Vector2( s * 0.9, 0 ),
952
+ new Vector2( s * 0.9, s * 0.4 ),
953
+ new Vector2( s * 0.55, s * 1.0 ),
954
+ new Vector2( 0.0, s * 3.2 )
955
+ ];
956
+
957
+ return new LatheGeometry( profile, 8 ); // round enough to read as a smooth pinnacle, still light
958
+
959
+ }
960
+
961
+ // --- material ------------------------------------------------------------
962
+
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
+ // interior mapping: fakes a furnished room behind each glass pane in the fragment
981
+ // shader — no geometry, no texture. every pane carries the room it looks into ( centre +
982
+ // size, baked per window by addWindows ), so neighbouring panes share one interior. the
983
+ // view ray is cast into that box and the walls, floor, ceiling and a few furniture pieces
984
+ // it meets are shaded procedurally, keyed off a per-room hash. returns vec4( colour, lit ).
985
+ const interior = /*@__PURE__*/ Fn( () => {
986
+
987
+ // flat so floor() below can't split one pane across two cell ids ( centre is per-room )
988
+ const roomCenter = varying( attribute( 'roomCenter', 'vec3' ) ).setInterpolation( InterpolationSamplingType.FLAT, InterpolationSamplingMode.EITHER );
989
+ const roomSize = attribute( 'roomSize', 'vec2' );
990
+
991
+ // a per-face frame from the geometry normal ( holds on every facade, including the
992
+ // 45° chamfer ): u runs across the face, v is up, n points outward
993
+ const n = normalLocal;
994
+ const up = vec3( 0, 1, 0 );
995
+ const uAxis = cross( up, n ).normalize();
996
+
997
+ // this pixel and the view ray, in the room's ( across, up, depth ) frame; depth
998
+ // runs into the wall, so the ray's depth component is positive
999
+ const d = positionLocal.sub( roomCenter );
1000
+ const camLocal = modelWorldMatrixInverse.mul( vec4( cameraPosition, 1 ) ).xyz;
1001
+ const rayLocal = positionLocal.sub( camLocal ).normalize();
1002
+ const origin = vec3( dot( d, uAxis ), d.y, 0 );
1003
+ const dir = vec3( dot( rayLocal, uAxis ), rayLocal.y, dot( rayLocal, n ).negate() );
1004
+
1005
+ // the room box: the pane-wide × ceiling-height front rectangle ( centred on the pane ),
1006
+ // set back behind the glass and run a little deeper than it is tall. shade the far
1007
+ // side the ray exits ( slab method: nearest of the three far-plane crossings;
1008
+ // dividing by a near-zero direction gives ±inf, which min() harmlessly drops ).
1009
+ const setback = float( 0.1 ); // the room starts just behind the glass, so it sits flush in the frame opening
1010
+ const boxMax = vec3( roomSize.x.mul( 0.5 ), roomSize.y.mul( 0.5 ), setback.add( roomSize.y.mul( 1.55 ) ) );
1011
+ const boxMin = vec3( boxMax.x.negate(), boxMax.y.negate(), setback );
1012
+ const tFar = boxMin.sub( origin ).div( dir ).max( boxMax.sub( origin ).div( dir ) );
1013
+ const t = tFar.x.min( tFar.y ).min( tFar.z );
1014
+ const hit = origin.add( dir.mul( t ) );
1015
+ const q = hit.sub( boxMin ).div( boxMax.sub( boxMin ) ); // 0..1 inside the room
1016
+
1017
+ const onBack = q.z.greaterThan( 0.998 );
1018
+ const onCeil = q.y.greaterThan( 0.998 );
1019
+ const onFloor = q.y.lessThan( 0.002 );
1020
+
1021
+ // per-room key for a portable integer hash — fract( sin() ) isn't bit-exact across drivers
1022
+ const cell = floor( roomCenter.mul( 2.0 ) ); // + offset before the u32 cast keeps it non-negative
1023
+ const ckey = uint( cell.x.add( 1 << 21 ) ).mul( uint( 73856093 ) )
1024
+ .bitXor( uint( cell.y.add( 1 << 21 ) ).mul( uint( 19349663 ) ) )
1025
+ .bitXor( uint( cell.z.add( 1 << 21 ) ).mul( uint( 83492791 ) ) ).toVar();
1026
+ const hash = ( kx, ky, kz ) => ihash( ckey.add( uint( Math.round( ( kx + ky * 7 + kz * 13 ) * 100 ) ) ) );
1027
+ const seed = hash( 12.9898, 78.233, 37.719 );
1028
+ const seed2 = hash( 39.346, 11.135, 83.155 );
1029
+ const lit = step( 0.8, hash( 63.21, 9.17, 51.43 ) ); // ~20% of rooms have the lights on; the rest sit dark
1030
+
1031
+ // each room's bulb colour. most run warm, drifting from a dim amber ( ~2400K ) up to a
1032
+ // warm white ( ~3200K ); a minority run cool, from a fluorescent / LED daylight to a TV's
1033
+ // bluer glow — so a lit facade reads as a spread of bulb temperatures, not one flat tint
1034
+ const warmLight = mix( color( 0xffb845 ), color( 0xffe49c ), hash( 27.1, 4.9, 61.7 ) );
1035
+ const coolLight = mix( color( 0xdfe8ff ), color( 0x9fb6ff ), hash( 8.3, 51.2, 17.6 ) );
1036
+ const lightCol = select( hash( 44.7, 19.3, 6.1 ).greaterThan( 0.88 ), coolLight, warmLight ); // ~12% of lit rooms run cool
1037
+
1038
+ // depth falloff ( darker toward the back ), and a panel mask on a face given its
1039
+ // two 0..1 coordinates — used for the flat fittings below
1040
+ const depth = roomSize.y.mul( 1.55 );
1041
+ const falloffAt = ( z ) => mix( float( 1.0 ), float( 0.42 ), z.sub( setback ).div( depth ).clamp( 0, 1 ) );
1042
+ 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() ) );
1043
+
1044
+ // --- the room shell: walls, floor, ceiling, back wall, with flat fittings ----
1045
+
1046
+ // muted plaster, picked per room, with a darker skirting board along the wall foot
1047
+ let wall = mix( color( 0x9a8b73 ), color( 0x6f7a82 ), seed );
1048
+ wall = mix( wall, color( 0xb9ad97 ), seed2.mul( 0.6 ) );
1049
+ const wallCol = mix( wall, wall.mul( 0.5 ), smoothstep( 0.05, 0.04, q.y ) );
1050
+
1051
+ // floorboards with a thin seam every few, and a centred rug
1052
+ const seam = step( 0.94, fract( q.x.mul( 6 ) ) );
1053
+ const boards = mix( color( 0x4a3320 ), color( 0x6a4c30 ), seed ).mul( seam.mul( 0.3 ).oneMinus() );
1054
+ const rug = mix( color( 0x7a3b32 ), color( 0x3a5760 ), seed2 );
1055
+ const floorCol = mix( boards, rug, rect( q.x, q.z, 0.5, 0.62, 0.3, 0.26 ).mul( 0.9 ) );
1056
+
1057
+ // ceiling, lighter than the walls, with a round overhead light in the middle; in a
1058
+ // lit room the fixture reads bright and glows ( the material's emissive = colour × lit )
1059
+ const lamp = smoothstep( 0.16, 0.13, vec2( q.x.sub( 0.5 ), q.z.sub( 0.5 ) ).length() );
1060
+ const ceilCol = mix( mix( wall, color( 0xffffff ), 0.5 ), lightCol.mul( mix( float( 1.0 ), float( 4.5 ), lit ) ), lamp );
1061
+
1062
+ // back wall: a panelled door to one side, and a framed picture kept on the
1063
+ // opposite half of the wall so it never lands on the door
1064
+ const doorX = mix( float( 0.22 ), float( 0.78 ), seed );
1065
+ const door = mix( color( 0x5a4631 ), color( 0x39383c ), step( 0.5, seed2 ) );
1066
+ const picX = select( doorX.lessThan( 0.5 ), mix( float( 0.68 ), float( 0.82 ), seed2 ), mix( float( 0.18 ), float( 0.32 ), seed2 ) );
1067
+ const picCol = mix( color( 0x2c3a4a ), color( 0x7a5a3a ), hash( 5.1, 9.2, 3.3 ) );
1068
+ let backCol = mix( wallCol, door, rect( q.x, q.y, doorX, 0.33, 0.085, 0.35 ) );
1069
+ backCol = mix( backCol, color( 0x141210 ), rect( q.x, q.y, picX, 0.56, 0.075, 0.085 ) ); // dark frame
1070
+ backCol = mix( backCol, picCol, rect( q.x, q.y, picX, 0.56, 0.055, 0.065 ) ); // the picture
1071
+
1072
+ const shellCol = select( onBack, backCol, select( onCeil, ceilCol, select( onFloor, floorCol, wallCol ) ) );
1073
+
1074
+ // fake ambient occlusion: darken the hit toward the room's edges ( where two surfaces
1075
+ // meet ), so the box reads with soft corner shading instead of flat-lit walls. the two
1076
+ // in-plane axes depend on which face the ray exits through ( q is 0..1 inside the room ).
1077
+ const aoBand = 0.15;
1078
+ const aoEdge = ( a ) => smoothstep( 0, aoBand, a ).mul( smoothstep( 0, aoBand, a.oneMinus() ) );
1079
+ 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 ) ) ) );
1080
+ const shellAO = mix( float( 0.72 ), float( 1.0 ), edgeAO );
1081
+
1082
+ // --- nearest surface: the shell, then any furniture block that lies closer ----
1083
+ // each block is a solid axis-aligned box in room space; boxHit returns its near
1084
+ // face. consider() keeps whichever surface the ray meets first.
1085
+ let bestT = t;
1086
+ let bestCol = shellCol.mul( shellAO ).mul( falloffAt( hit.z ) );
1087
+ let bestEmit = float( 1 ); // per-hit emissive weight: shell and fittings emit fully, curtains far less
1088
+
1089
+ const boxHit = ( bMin, bMax ) => {
1090
+
1091
+ const ta = bMin.sub( origin ).div( dir );
1092
+ const tb = bMax.sub( origin ).div( dir );
1093
+ const lo = ta.min( tb ), hi = ta.max( tb );
1094
+ const tN = lo.x.max( lo.y ).max( lo.z );
1095
+ const p = origin.add( dir.mul( tN ) );
1096
+ 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 ) ) };
1097
+
1098
+ };
1099
+
1100
+ const consider = ( h, tN, c, emit = 1 ) => {
1101
+
1102
+ const near = h.and( tN.lessThan( bestT ) ); bestCol = select( near, c, bestCol ); bestEmit = select( near, float( emit ), bestEmit ); bestT = select( near, tN, bestT );
1103
+
1104
+ };
1105
+
1106
+ const halfU = boxMax.x, floorY = boxMin.y, ceilY = boxMax.y, backZ = boxMax.z;
1107
+ const midZ = setback.add( depth.mul( 0.5 ) ); // room centre, in depth
1108
+
1109
+ // a low table near the middle of the room ( its top catches the light )
1110
+ const tCx = mix( float( - 0.6 ), float( 0.6 ), seed );
1111
+ const tCz = midZ.add( mix( float( - 0.4 ), float( 0.5 ), seed2 ) );
1112
+ const tbl = boxHit( vec3( tCx.sub( 0.6 ), floorY, tCz.sub( 0.35 ) ), vec3( tCx.add( 0.6 ), floorY.add( 0.42 ), tCz.add( 0.35 ) ) );
1113
+ const tblCol = mix( color( 0x4a3526 ), color( 0x6b4a30 ), seed2 ).mul( select( tbl.qb.y.greaterThan( 0.94 ), float( 1.25 ), float( 0.8 ) ) );
1114
+ consider( tbl.hit, tbl.tN, tblCol.mul( falloffAt( tbl.p.z ) ) );
1115
+
1116
+ // a wide low sofa against the back wall, facing the window
1117
+ const sofaCx = mix( halfU.mul( - 0.3 ), halfU.mul( 0.3 ), seed2 );
1118
+ const sofa = boxHit( vec3( sofaCx.sub( 1.1 ), floorY, backZ.sub( 0.95 ) ), vec3( sofaCx.add( 1.1 ), floorY.add( mix( float( 0.8 ), float( 0.9 ), seed ) ), backZ.sub( 0.1 ) ) );
1119
+ const sofaCol = mix( color( 0x5a4a3a ), color( 0x42566a ), seed ).mul( select( sofa.qb.y.greaterThan( 0.9 ), float( 1.12 ), float( 0.85 ) ) );
1120
+ consider( sofa.hit, sofa.tN, sofaCol.mul( falloffAt( sofa.p.z ) ) );
1121
+
1122
+ // tall wardrobes in the back corners — each side stands in some rooms
1123
+ const wardrobe = ( cx, gate, h ) => {
1124
+
1125
+ const w = boxHit( vec3( cx.sub( 0.5 ), floorY, backZ.sub( 0.7 ) ), vec3( cx.add( 0.5 ), floorY.add( h ), backZ.sub( 0.1 ) ) );
1126
+ const c = mix( color( 0x3a2c22 ), color( 0x55473a ), seed ).mul( select( w.qb.y.greaterThan( 0.94 ), float( 1.2 ), float( 0.82 ) ) );
1127
+ consider( w.hit.and( gate ), w.tN, c.mul( falloffAt( w.p.z ) ) );
1128
+
1129
+ };
1130
+
1131
+ wardrobe( halfU.mul( - 0.82 ), hash( 7.3, 2.1, 9.9 ).greaterThan( 0.4 ), mix( float( 1.7 ), float( 2.3 ), seed ) );
1132
+ wardrobe( halfU.mul( 0.82 ), hash( 3.7, 8.4, 1.5 ).greaterThan( 0.4 ), mix( float( 1.7 ), float( 2.3 ), seed2 ) );
1133
+
1134
+ // curtains hung just inside the glass: drapes drawn part-way in from each side,
1135
+ // so some windows read open and others half-covered
1136
+
1137
+ // curtain fabric colour, picked per room from a muted domestic palette — creams and
1138
+ // taupes through warm grey, dusty blue, sage and faded terracotta — with a small
1139
+ // in-family drift so drawn drapes vary window to window instead of all reading beige
1140
+ const swatch = ( a, b ) => mix( color( a ), color( b ), seed2 );
1141
+ const pick = hash( 22.4, 6.7, 91.2 ).mul( 6 ); // 0..6, one bucket per family
1142
+ let fabric = swatch( 0xcabfa6, 0xd8cdb8 ); // cream
1143
+ fabric = select( pick.greaterThan( 1 ), swatch( 0x8a7a64, 0x9b8c72 ), fabric ); // beige / taupe
1144
+ fabric = select( pick.greaterThan( 2 ), swatch( 0x706a64, 0x837d76 ), fabric ); // warm grey
1145
+ fabric = select( pick.greaterThan( 3 ), swatch( 0x5f7079, 0x6f818b ), fabric ); // dusty blue
1146
+ fabric = select( pick.greaterThan( 4 ), swatch( 0x6c7558, 0x79835f ), fabric ); // sage green
1147
+ fabric = select( pick.greaterThan( 5 ), swatch( 0x8c5a44, 0x9a6a52 ), fabric ); // faded terracotta
1148
+ const drape = ( bMin, bMax, gate ) => {
1149
+
1150
+ const h = boxHit( bMin, bMax );
1151
+ const pleat = fabric.mul( mix( float( 0.78 ), float( 1.12 ), fract( h.p.x.mul( 2.5 ) ) ) ); // soft vertical pleats
1152
+ consider( h.hit.and( gate ), h.tN, pleat.mul( falloffAt( h.p.z ) ), 0.2 ); // a drape only transmits a little of the room's glow, never out-glowing the interior
1153
+
1154
+ };
1155
+
1156
+ const cz0 = setback, cz1 = setback.add( 0.12 );
1157
+ // drape widths, biased narrow ( squared ) and each capped at half the room width, so
1158
+ // the two sides only meet — fully curtaining the window — in the rare room where both
1159
+ // are nearly closed; most rooms read partly open
1160
+ const sL = smoothstep( 0.3, 1.0, seed ), sR = smoothstep( 0.3, 1.0, seed2 );
1161
+ const lw = halfU.mul( sL.mul( sL ) ); // left drape width ( 0 below seed 0.3 )
1162
+ const rw = halfU.mul( sR.mul( sR ) ); // right drape width
1163
+ drape( vec3( halfU.negate(), floorY, cz0 ), vec3( halfU.negate().add( lw ), ceilY, cz1 ), lw.greaterThan( 0.05 ) );
1164
+ drape( vec3( halfU.sub( rw ), floorY, cz0 ), vec3( halfU, ceilY, cz1 ), rw.greaterThan( 0.05 ) );
1165
+
1166
+ // lit rooms read brighter and take on their bulb's colour ( the lights are on )
1167
+ const warmth = mix( vec3( 1.0, 1.0, 1.0 ), lightCol, lit.mul( 0.85 ) );
1168
+ return vec4( bestCol.mul( warmth ).mul( mix( float( 1.0 ), float( 1.3 ), lit ) ), lit.mul( bestEmit ) );
1169
+
1170
+ } );
1171
+
1172
+ /**
1173
+ * The NYC masonry palette every tower is dressed from ( hex colours ): limestone-dominant
1174
+ * with terracotta accents. Shared by the single-tower example and {@link CityGenerator}'s
1175
+ * building material so both stay in sync.
1176
+ */
1177
+ const buildingPalette = [
1178
+ 0xa8553c, 0x9c4a34, // terracotta & red brick ( occasional accent )
1179
+ 0x8a6a52, 0x7d6450, // warm brick / brownstone ( muted )
1180
+ 0xc4a370, 0xb89a6f, 0xc2b183, // buff / tan
1181
+ 0xc6c0b2, 0xc6c0b2, 0xbdb7a8, 0xd1ccbe, 0xb4afa1, // limestone / pale dressed stone — the common default
1182
+ 0x9a988f, 0x8b8983, 0xa5a39a, // grey granite / concrete
1183
+ 0xdbd6cb, // pale glazed ( accent )
1184
+ 0x7c868d // steel / glass ( cool accent )
1185
+ ];
1186
+
1187
+ /** Picks one {@link buildingPalette} colour ( a hex number ) for a tower from its seed. */
1188
+ function pickBuildingColor( seed ) {
1189
+
1190
+ const h = Math.abs( Math.sin( seed * 12.9898 ) * 43758.5453 );
1191
+ return buildingPalette[ Math.floor( ( h - Math.floor( h ) ) * buildingPalette.length ) ];
1192
+
1193
+ }
1194
+
1195
+ /**
1196
+ * The facade material: a single MeshStandardNodeMaterial that reads the baked
1197
+ * per-vertex `partId` and reproduces every zone — procedural terracotta brickwork
1198
+ * on the walls and piers, smooth dressed stone on the window frames and ornament,
1199
+ * dark glazing, and grey AC units — all dressed with world-space
1200
+ * weathering. One material covers the whole building ( and a whole city ), which is
1201
+ * what makes it compute-rasterizer friendly. `buildingBase` is the tower's flat
1202
+ * masonry colour as a TSL node: pass a `uniform( Color )` for a single tower, or a
1203
+ * per-fragment palette pick for a city, so the same material dresses both.
1204
+ */
1205
+ function createSkyscraperMaterial( buildingBase = color( 0xc6c0b2 ) ) {
1206
+
1207
+ const soot = color( 0x4a4236 );
1208
+
1209
+ // broad weathering, all driven from world position so it reads consistently
1210
+ // across instanced and merged meshes: a slow tonal drift, a fine clay mottle,
1211
+ // and sooty vertical streaks that pool low down
1212
+
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 );
1215
+ const streak = mx_fractal_noise_float( vec3( positionWorld.x.mul( 1.5 ), positionWorld.y.mul( 0.04 ), positionWorld.z.mul( 1.5 ) ), 2 );
1216
+ const dirt = smoothstep( - 0.1, 0.45, streak ).mul( smoothstep( 210, 0, positionWorld.y ) ).mul( 0.6 );
1217
+
1218
+ // procedural terracotta brickwork in running bond, keyed off the BUILDING-LOCAL position
1219
+ // so the coursing anchors to each tower ( courses from its base, columns at its faces )
1220
+ // and lines up with the brick-snapped floor / bay dimensions. courses run up local Y;
1221
+ // the across-face axis is world XZ projected onto the face tangent, so brick width stays
1222
+ // constant on every face including the 45° chamfer. the geometry ( pre-bump ) normal is
1223
+ // used for the bond axis — otherwise colorNode pulls normal computation into its partId
1224
+ // branch and glass loses its env reflection.
1225
+
1226
+ const brickH = BRICK.height;
1227
+ const brickL = BRICK.length;
1228
+ const mortar = 0.025; // joint width, in metres
1229
+
1230
+ const nrm = normalWorldGeometry.abs();
1231
+ const across = positionLocal.x.mul( normalWorldGeometry.z ).sub( positionLocal.z.mul( normalWorldGeometry.x ) );
1232
+ const rowCoord = positionLocal.y.div( brickH );
1233
+ const courseRow = floor( rowCoord );
1234
+ const colCoord = across.div( brickL ).add( mod( courseRow, 2 ).mul( 0.5 ) ); // half-brick stagger per row
1235
+
1236
+ // anti-aliased mortar ( the "pristine grid" trick ): the drawn joint never falls below
1237
+ // the pixel footprint and its opacity fades to keep energy constant, so lines stay crisp
1238
+ // up close and dissolve far away instead of shimmering. the horizontal derivative comes
1239
+ // from continuous world X / Z ( weighted by the normal ), not fwidth( across ) which
1240
+ // would spike where the normal flips at pier edges.
1241
+ const mU = mortar / ( 2 * brickL );
1242
+ const mV = mortar / ( 2 * brickH );
1243
+ const ddU = nrm.z.mul( fwidth( positionWorld.x ) ).add( nrm.x.mul( fwidth( positionWorld.z ) ) ).div( brickL ).clamp( 1e-6, 0.5 );
1244
+ const ddV = fwidth( rowCoord ).clamp( 1e-6, 0.5 );
1245
+ const distU = float( 0.5 ).sub( fract( colCoord ).sub( 0.5 ).abs() );
1246
+ const distV = float( 0.5 ).sub( fract( rowCoord ).sub( 0.5 ).abs() );
1247
+ const drawU = ddU.max( mU );
1248
+ const drawV = ddV.max( mV );
1249
+ const lineU = smoothstep( drawU.add( ddU ), drawU.sub( ddU ), distU ).mul( float( mU ).div( drawU ).min( 1 ) );
1250
+ const lineV = smoothstep( drawV.add( ddV ), drawV.sub( ddV ), distV ).mul( float( mV ).div( drawV ).min( 1 ) );
1251
+ const wallFacing = smoothstep( 0.7, 0.45, nrm.y ); // brick only on vertical walls — not roofs, ledges, cornice tops
1252
+ const joint = lineU.max( lineV ).mul( wallFacing );
1253
+
1254
+ const brickKey = uint( courseRow.add( 1 << 16 ) ).mul( uint( 73856093 ) ).bitXor( uint( floor( colCoord ).add( 1 << 16 ) ).mul( uint( 19349663 ) ) ).toVar();
1255
+ const brickRnd = ihash( brickKey );
1256
+ const brickRnd2 = ihash( brickKey.add( uint( 1 ) ) ); // independent per-brick hash for hue
1257
+
1258
+ // soft brick relief for the bump: each brick is a gently domed mound falling to the
1259
+ // recessed mortar over a bevel ( distU / distV are the distance to the nearest column /
1260
+ // course line, 0 at the joint, 0.5 at the centre ), so bricks read rounded rather than
1261
+ // scratched. the bevel is widened to at least a screen pixel ( from the world-position
1262
+ // derivative, our stand-in for a mip LOD ) so the edge never goes sub-pixel and shimmers.
1263
+ const bevel = 0.02;
1264
+ const texel = fwidth( positionWorld ).length(); // on-screen size of a surface pixel — our hand-rolled LOD
1265
+ const lodBevel = texel.mul( 1.5 ).max( bevel );
1266
+ 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 ) );
1269
+
1270
+ // the merged geometry carries a per-vertex partId; this material reads it and
1271
+ // branches to reproduce each zone — no per-part materials, compute-raster friendly
1272
+
1273
+ const partId = varying( attribute( 'partId', 'float' ) ).setInterpolation( InterpolationSamplingType.FLAT, InterpolationSamplingMode.EITHER ); // flat: a per-face id must not interpolate, or equal() below misses on the rounding
1274
+ const isGlass = partId.equal( GLASS );
1275
+ const isFrame = partId.equal( FRAME );
1276
+ const isOrnament = partId.equal( ORNAMENT );
1277
+ const isAC = partId.equal( AC );
1278
+
1279
+ // stone zones: brick + weathering on the building's colour, lightened for
1280
+ // piers / ornament and darkened for window frames
1281
+ const lighten = select( partId.equal( PIER ), float( 0.12 ), select( isOrnament, float( 0.2 ), float( 0 ) ) );
1282
+ const perBrick = float( 1 ).add( tone ).add( mottle ).add( brickRnd.sub( 0.5 ).mul( 0.14 ) );
1283
+ // per-brick warm/cool shift ( red up / blue down, or vice-versa ) so individual
1284
+ // bricks read as slightly different fired tones, relative to the building's colour
1285
+ const warmCool = brickRnd2.sub( 0.5 ).mul( 0.14 );
1286
+ const brickShift = vec3( float( 1 ).add( warmCool ), float( 1 ), float( 1 ).sub( warmCool ) );
1287
+ const tint = mix( buildingBase, color( 0xffffff ), lighten ).mul( perBrick ).mul( brickShift );
1288
+ const masonry = mix( tint, tint.mul( 0.6 ), joint ); // recessed joints read darker
1289
+ // roofs / ledges show every blotch ( flat & light ), so horizontal surfaces get a gentler,
1290
+ // larger-scale grime instead of the wall's streaky soot — confined to those surfaces by a
1291
+ // branch ( roofMask > 0 ), so the fractal never runs on the vertical facade
1292
+ 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 ) );
1294
+ const stoneColor = mix( masonry, soot, mix( dirt, roofGrime, roofMask ) );
1295
+
1296
+ // glass: the interior-mapped room is the base colour; the smooth, low-roughness
1297
+ // 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();
1300
+
1301
+ // grimy glazing: the room shows through, but muted by a dusty film and dirt pooled
1302
+ // along the bottom of each pane, plus a baseline haze, so the panes read as old
1303
+ // glass rather than open holes. the streaks run down the facade ( world Y barely
1304
+ // scaled ); the pooled dirt uses the pane's own UV ( y = 0 at the sill ).
1305
+ const filmNoise = mx_fractal_noise_float( vec3( positionWorld.x.mul( 1.3 ), positionWorld.y.mul( 0.06 ), positionWorld.z.mul( 1.3 ) ), 2 );
1306
+ const dustStreak = smoothstep( - 0.15, 0.5, filmNoise ).mul( 0.45 );
1307
+ const pooled = smoothstep( 0.32, 0.0, uv().y ).mul( 0.4 );
1308
+ 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
1311
+
1312
+ // window frames are smooth dressed stone, not brick
1313
+ const frameColor = buildingBase.mul( 0.55 );
1314
+
1315
+ // finials / ornament: smooth dressed stone ( lightened ), not brick
1316
+ const ornamentColor = mix( buildingBase, color( 0xffffff ), 0.22 ).mul( float( 1 ).add( tone ) );
1317
+ // window AC units: a louvered white-plastic box, grimier toward the base where it drips.
1318
+ // keyed off the box's own UVs ( acUv.y runs 0 → 1 up each vented side )
1319
+ const acUv = uv();
1320
+ const acVent = smoothstep( 0.65, 0.4, normalWorldGeometry.y.abs() ); // 1 on the vertical vented sides, 0 on the flat top
1321
+ const acDetail = smoothstep( 0.08, 0.015, texel ); // louvers fade out before a slat nears a pixel
1322
+ const acLouver = acVent.mul( acDetail );
1323
+
1324
+ // 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
1326
+ 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 ) );
1328
+
1329
+ // a darker recessed grille panel inset into the lighter cabinet, with horizontal louvers
1330
+ // inside it ( the front vents ) — the white plastic reads as a thin border frame
1331
+ const acGrille = smoothstep( 0.06, 0.14, acUv.x ).mul( smoothstep( 0.94, 0.86, acUv.x ) )
1332
+ .mul( smoothstep( 0.12, 0.2, acUv.y ) ).mul( smoothstep( 0.96, 0.88, acUv.y ) ).mul( acLouver );
1333
+ const acSlats = fract( acUv.y.mul( 6 ) ); // bold louvers — reads at the unit's small on-screen size
1334
+ const acFin = mix( float( 0.82 ), float( 1.04 ), acSlats );
1335
+ const acBody = acPaint.mul( mix( float( 1 ), acFin.mul( 0.42 ), acGrille ) ); // cabinet stays light; recessed grille goes dark grey
1336
+
1337
+ // 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 );
1339
+ const acGrime = smoothstep( 0.4, 0.0, acUv.y ).mul( acStreak ).mul( acDinge.add( 0.3 ) );
1340
+ const acColor = mix( acBody, color( 0x6f685a ), acGrime.mul( 0.5 ) );
1341
+
1342
+ // recessed grille ( louver fins ) relief and a slightly rougher base
1343
+ const acRelief = acGrille.mul( acSlats.mul( 0.012 ).sub( 0.01 ) );
1344
+ const acRough = float( 0.52 ).add( acGrille.mul( 0.08 ) );
1345
+
1346
+ 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
1352
+
1353
+ return material;
1354
+
1355
+ }
1356
+
1357
+ export { SkyscraperGenerator, createSkyscraperMaterial, buildingPalette, pickBuildingColor };