diff --git a/src/viewport-three/viewport-host.js b/src/viewport-three/viewport-host.js index b8d52262..69c39a77 100644 --- a/src/viewport-three/viewport-host.js +++ b/src/viewport-three/viewport-host.js @@ -2198,6 +2198,111 @@ export class ViewportHost { metalness: 0.02 }); } + getWaterReflectionMode() { + if (this.currentWorld === null || + !this.currentWorld.advancedRendering.enabled || + this.currentWorld.advancedRendering.waterPath !== "quality" || + this.displayMode !== "normal" || + this.viewMode !== "perspective") { + return "none"; + } + return this.currentWorld.advancedRendering.waterReflectionMode; + } + createWaterReflectionRenderTarget() { + const canvasWidth = this.container?.clientWidth ?? this.renderer.domElement.width; + const canvasHeight = this.container?.clientHeight ?? this.renderer.domElement.height; + const width = Math.max(128, Math.round(Math.max(canvasWidth, 512) * 0.5)); + const height = Math.max(128, Math.round(Math.max(canvasHeight, 512) * 0.5)); + return new WebGLRenderTarget(width, height); + } + resizeWaterReflectionTargets() { + const canvasWidth = this.container?.clientWidth ?? this.renderer.domElement.width; + const canvasHeight = this.container?.clientHeight ?? this.renderer.domElement.height; + const width = Math.max(128, Math.round(Math.max(canvasWidth, 512) * 0.5)); + const height = Math.max(128, Math.round(Math.max(canvasHeight, 512) * 0.5)); + for (const binding of this.viewportWaterSurfaceBindings) { + binding.reflectionRenderTarget?.setSize(width, height); + } + } + updateViewportWaterReflections() { + const activeCamera = this.getActiveCamera(); + if (!(activeCamera instanceof PerspectiveCamera)) { + for (const binding of this.viewportWaterSurfaceBindings) { + if (binding.reflectionEnabledUniform !== null) { + binding.reflectionEnabledUniform.value = 0; + } + } + return; + } + const reflectionMode = this.getWaterReflectionMode(); + for (const binding of this.viewportWaterSurfaceBindings) { + if (reflectionMode === "none" || + binding.reflectionTextureUniform === null || + binding.reflectionMatrixUniform === null || + binding.reflectionEnabledUniform === null) { + if (binding.reflectionEnabledUniform !== null) { + binding.reflectionEnabledUniform.value = 0; + } + continue; + } + if (binding.reflectionRenderTarget === null) { + binding.reflectionRenderTarget = this.createWaterReflectionRenderTarget(); + } + const canRenderReflection = updatePlanarReflectionCamera(binding.brush, activeCamera, this.waterReflectionCamera, binding.reflectionMatrixUniform.value); + if (!canRenderReflection || binding.reflectionRenderTarget === null) { + binding.reflectionEnabledUniform.value = 0; + continue; + } + const hiddenObjects = []; + const hideObject = (object) => { + if (object === null || object === undefined) { + return; + } + hiddenObjects.push({ object, visible: object.visible }); + object.visible = false; + }; + for (const waterBinding of this.viewportWaterSurfaceBindings) { + const renderObjects = this.brushRenderObjects.get(waterBinding.brush.id); + if (renderObjects !== undefined) { + hideObject(renderObjects.mesh); + } + } + for (const renderObjects of this.brushRenderObjects.values()) { + hideObject(renderObjects.edges); + for (const edgeHelper of renderObjects.edgeHelpers) { + hideObject(edgeHelper.line); + } + for (const vertexHelper of renderObjects.vertexHelpers) { + hideObject(vertexHelper.mesh); + } + } + hideObject(this.axesHelper); + hideObject(this.gridHelpers.xz); + hideObject(this.gridHelpers.xy); + hideObject(this.gridHelpers.yz); + hideObject(this.entityGroup); + hideObject(this.transformGizmoGroup); + hideObject(this.boxCreatePreviewMesh); + hideObject(this.boxCreatePreviewEdges); + hideObject(this.creationPreviewObject); + if (reflectionMode === "world") { + hideObject(this.modelGroup); + } + const previousAutoClear = this.renderer.autoClear; + const previousRenderTarget = this.renderer.getRenderTarget(); + this.renderer.autoClear = true; + this.renderer.setRenderTarget(binding.reflectionRenderTarget); + this.renderer.clear(); + this.renderer.render(this.scene, this.waterReflectionCamera); + this.renderer.setRenderTarget(previousRenderTarget); + this.renderer.autoClear = previousAutoClear; + for (const hiddenObject of hiddenObjects) { + hiddenObject.object.visible = hiddenObject.visible; + } + binding.reflectionTextureUniform.value = binding.reflectionRenderTarget.texture; + binding.reflectionEnabledUniform.value = 0.36; + } + } getOrCreateTexture(material) { const signature = createStarterMaterialSignature(material); const cachedTexture = this.materialTextureCache.get(material.id); @@ -2377,6 +2482,10 @@ export class ViewportHost { } this.brushRenderObjects.clear(); this.volumeAnimatedUniforms.length = 0; + for (const binding of this.viewportWaterSurfaceBindings) { + binding.reflectionRenderTarget?.dispose(); + } + this.viewportWaterSurfaceBindings.length = 0; } clearEntityMarkers() { for (const renderObjects of this.entityRenderObjects.values()) {