From 2be9fd1448c85c22f9c840f4728fe0ca4b09db33 Mon Sep 17 00:00:00 2001 From: Olivier NOUGUIER Date: Fri, 24 Apr 2026 22:12:05 +0200 Subject: [PATCH 1/3] feat: add HTMLTexture support and update bindings for three.js r184 --- .kilo/plans/1777059824803-jolly-circuit.md | 69 ++++++++ .../cheleb/scalajswebgl/app/HomePage.scala | 3 +- .../dev/cheleb/scalajswebgl/app/Router.scala | 3 + .../three/materials/HTMLTextureSample.scala | 154 ++++++++++++++++++ .../src/main/scala/THREE/core/Clock.scala | 4 +- .../src/main/scala/THREE/core/Timer.scala | 2 +- .../THREE/renderers/RenderPipeline.scala | 4 +- .../scala/THREE/textures/HTMLTexture.scala | 67 ++++++++ 8 files changed, 300 insertions(+), 6 deletions(-) create mode 100644 .kilo/plans/1777059824803-jolly-circuit.md create mode 100644 example/client/src/main/scala/dev/cheleb/scalajswebgl/samples/three/materials/HTMLTextureSample.scala create mode 100644 modules/three/src/main/scala/THREE/textures/HTMLTexture.scala diff --git a/.kilo/plans/1777059824803-jolly-circuit.md b/.kilo/plans/1777059824803-jolly-circuit.md new file mode 100644 index 0000000..a0bca8d --- /dev/null +++ b/.kilo/plans/1777059824803-jolly-circuit.md @@ -0,0 +1,69 @@ +# Plan to Update three.js Library from r183 to r184 + +## Overview +This plan outlines the steps to update the three.js library binding in the Scala.js project from version r183 to r184. The project contains manually written Scala.js facades in `modules/three/` that need to be updated to match the three.js r184 API. + +## Current State +- Submodules contain: + - `submodules/three.js-r183/` (current version) + - `submodules/three.js-r184/` (new version to adopt) +- Scala.js bindings are in `modules/three/src/main/scala/THREE/` +- Bindings appear to be manually written facades, not auto-generated +- Some references to r183 exist in code comments (RenderPipeline.scala, Timer.scala, Clock.scala) + +## Steps + +### 1. Analyze API Changes +- Compare three.js r183 vs r184 to identify breaking changes, new features, and deprecations +- Focus on areas where Scala.js bindings exist: + - Core (Object3D, Scene, Camera, etc.) + - Renderers (WebGLRenderer, etc.) + - Materials, Geometries, Textures + - Animation, Loaders, Controls + +### 2. Update Scala.js Facades +- Modify `modules/three/src/main/scala/THREE/` files to match r184 API: + - Add new methods/properties + - Remove deprecated ones + - Update method signatures if changed + - Update JSDoc annotations with correct @since tags + - Update deprecated annotations (change r183 to r184 where appropriate) + +### 3. Update Version References +- Change all "@since r183" to "@since r184" where appropriate +- Update deprecation messages that reference r183 +- Check for any hardcoded version references in comments or strings + +### 4. Verify Build +- Run `sbt compile` to ensure all bindings compile correctly +- Run tests if available to verify functionality + +### 5. Update Example Usage (if needed) +- Check if any example code in `example/client/` needs updates for API changes + +## Implementation Notes +- Since bindings are manually written, careful comparison of three.js source is required +- Pay special attention to: + - Changes in method signatures + - New/removed properties + - Changes in constant values + - New classes or removed classes +- The three.js-r184 submodule already contains the source, so we can reference it directly + +## Files to Examine +- `modules/three/src/main/scala/THREE/` (all Scala.js facade files) +- Key files likely to need updates based on r183 references: + - `modules/three/src/main/scala/THREE/renderers/RenderPipeline.scala` + - `modules/three/src/main/scala/THREE/core/Timer.scala` + - `modules/three/src/main/scala/THREE/core/Clock.scala` +- `submodules/three.js-r184/src/` for reference implementation + +## Risks +- Missing API changes could lead to runtime facades not matching actual library +- Incorrect facades could cause compilation errors or runtime JavaScript errors +- Need to ensure all three.js facade traits/classes properly extend js.Object and use @JSImport/@JSName correctly + +## Estimated Effort +- Analysis: 1-2 hours +- Facade updates: 2-4 hours (depending on number of changes) +- Testing: 30 minutes - 1 hour \ No newline at end of file diff --git a/example/client/src/main/scala/dev/cheleb/scalajswebgl/app/HomePage.scala b/example/client/src/main/scala/dev/cheleb/scalajswebgl/app/HomePage.scala index d6c6aa9..4dcb90d 100644 --- a/example/client/src/main/scala/dev/cheleb/scalajswebgl/app/HomePage.scala +++ b/example/client/src/main/scala/dev/cheleb/scalajswebgl/app/HomePage.scala @@ -62,7 +62,8 @@ object HomePage: demo("CompressedTexture", Router.uiRoute("demo", "three", "compressedtexture")), demo("DepthTexture", Router.uiRoute("demo", "three", "depthtexture")), demo("FramebufferTexture", Router.uiRoute("demo", "three", "framebuffertexture")), - demo("DDSLoader", Router.uiRoute("demo", "three", "ddsloader")) + demo("DDSLoader", Router.uiRoute("demo", "three", "ddsloader")), + demo("HTMLTexture", Router.uiRoute("demo", "three", "htmltexture")) ) ), div( diff --git a/example/client/src/main/scala/dev/cheleb/scalajswebgl/app/Router.scala b/example/client/src/main/scala/dev/cheleb/scalajswebgl/app/Router.scala index 2483a1d..040e674 100644 --- a/example/client/src/main/scala/dev/cheleb/scalajswebgl/app/Router.scala +++ b/example/client/src/main/scala/dev/cheleb/scalajswebgl/app/Router.scala @@ -191,6 +191,9 @@ object Router: path("framebuffertexture") { FramebufferTextureSample() }, + path("htmltexture") { + HTMLTextureSample() + }, path("ddsloader") { DDSLoaderSample() }, diff --git a/example/client/src/main/scala/dev/cheleb/scalajswebgl/samples/three/materials/HTMLTextureSample.scala b/example/client/src/main/scala/dev/cheleb/scalajswebgl/samples/three/materials/HTMLTextureSample.scala new file mode 100644 index 0000000..a1360c9 --- /dev/null +++ b/example/client/src/main/scala/dev/cheleb/scalajswebgl/samples/three/materials/HTMLTextureSample.scala @@ -0,0 +1,154 @@ +package dev.cheleb.scalajswebgl.samples.three.materials + +import com.raquo.laminar.api.L.* + +import THREE.* + +import org.scalajs.dom +import org.scalajs.dom.window +import scala.scalajs.js +import scala.math.sin + +object HTMLTextureSample { + + def apply() = + + // Create the HTML element that will be used as a texture source + val htmlSource = dom.document.createElement("div").asInstanceOf[dom.html.Div] + htmlSource.style.width = "256px" + htmlSource.style.height = "256px" + htmlSource.style.background = "linear-gradient(135deg, #667eea 0%, #764ba2 100%)" + htmlSource.style.color = "white" + htmlSource.style.fontFamily = "Arial, sans-serif" + htmlSource.style.fontSize = "18px" + htmlSource.style.display = "flex" + htmlSource.style.setProperty("flex-direction", "column") + htmlSource.style.setProperty("align-items", "center") + htmlSource.style.setProperty("justify-content", "center") + htmlSource.style.borderRadius = "8px" + htmlSource.style.padding = "16px" + htmlSource.style.boxSizing = "border-box" + htmlSource.innerHTML = + """
+ |

HTMLTexture

+ |

Time: 0.0s

+ |
+ |
+ |
+ |
""".stripMargin + + val htmlTextureDiv = div( + h1("HTMLTexture Demo"), + p( + "Demonstrating HTMLTexture: a live HTML element rendered as a texture on 3D objects. ", + "The HTML content updates in real-time and is reflected on the mesh surfaces." + ), + div( + cls := "canvas-container" + ) + ) + + // Create scene + val scene = Scene() + + // Create camera + val camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000) + camera.position.z = 6 + + // Create renderer + val renderer = WebGLRenderer(antialias = true) + renderer.setSize(window.innerWidth * 0.8, window.innerHeight * 0.8) + renderer.setClearColor("#1a1a2e", 1) + + // Create the HTMLTexture from the div element + val htmlTexture = new HTMLTexture(htmlSource) + + // --- Object 1: Rotating cube with HTMLTexture --- + val cubeGeometry = new BoxGeometry(2, 2, 2) + val cubeMaterial = MeshPhongMaterial( + color = 0xffffff, + shininess = 60, + map = htmlTexture + ) + val cube = new Mesh(cubeGeometry, cubeMaterial) + cube.position.x = -2.5 + scene.add(cube) + + // --- Object 2: Sphere with HTMLTexture --- + val sphereGeometry = new SphereGeometry(1.3, 32, 32) + val sphereMaterial = MeshPhongMaterial( + color = 0xffffff, + shininess = 80, + map = htmlTexture + ) + val sphere = new Mesh(sphereGeometry, sphereMaterial) + sphere.position.x = 2.5 + scene.add(sphere) + + // --- Object 3: Plane showing the texture flat --- + val planeGeometry = new PlaneGeometry(2.5, 2.5) + val planeMaterial = MeshBasicMaterial(map = htmlTexture) + val plane = new Mesh(planeGeometry, planeMaterial) + plane.position.set(0, -2.5, 0) + plane.rotation.x = -0.3 + scene.add(plane) + + // Add lighting + val directionalLight = DirectionalLight(0xffffff, 1.5) + directionalLight.position.set(5, 5, 5) + scene.add(directionalLight) + + val directionalLight2 = DirectionalLight(0xffffff, 0.8) + directionalLight2.position.set(-5, 3, -3) + scene.add(directionalLight2) + + val ambientLight = AmbientLight(0xffffff, 1.0) + scene.add(ambientLight) + + val pointLight = PointLight(0x4488ff, 1.2, 20) + pointLight.position.set(-3, 3, 3) + scene.add(pointLight) + + // Animation loop + val animate: () => Unit = () => { + val time = js.Date.now() * 0.001 + + // Rotate objects + cube.rotation.x = time * 0.3 + cube.rotation.y = time * 0.5 + + sphere.rotation.y = time * 0.4 + + // Update the HTML content dynamically + val timerEl = htmlSource.querySelector("#timer") + if (timerEl != null) { + val seconds = f"${time % 100}%.1f" + timerEl.textContent = s"Time: ${seconds}s" + } + val fillEl = htmlSource.querySelector("#fill").asInstanceOf[dom.html.Div] + if (fillEl != null) { + val pct = ((sin(time * 0.5) + 1) * 50).toInt + fillEl.style.width = s"$pct%" + } + + // The HTMLTexture listens for paint events automatically, + // but we can force an update if needed: + htmlTexture.needsUpdate = true + + renderer.render(scene, camera) + } + renderer.setAnimationLoop(animate) + + // Handle window resize + val onWindowResize: dom.Event => Unit = { _ => + camera.aspect = window.innerWidth / window.innerHeight + camera.updateProjectionMatrix() + renderer.setSize(window.innerWidth * 0.8, window.innerHeight * 0.8) + } + window.addEventListener("resize", onWindowResize) + + // Append renderer to the canvas container + htmlTextureDiv.ref.querySelector(".canvas-container").appendChild(renderer.domElement) + + htmlTextureDiv +} diff --git a/modules/three/src/main/scala/THREE/core/Clock.scala b/modules/three/src/main/scala/THREE/core/Clock.scala index aab346e..b82aede 100644 --- a/modules/three/src/main/scala/THREE/core/Clock.scala +++ b/modules/three/src/main/scala/THREE/core/Clock.scala @@ -8,13 +8,13 @@ import scala.annotation.nowarn * Class for keeping track of time. * * @deprecated - * since r183. Use [[Timer]] instead. + * since r184. Use [[Timer]] instead. * @see * [[Timer]] for the recommended replacement */ @js.native @JSImport("three", "Clock") -@deprecated("Use THREE.Timer instead. Clock will be removed in a future version.", "r183") +@deprecated("Use THREE.Timer instead. Clock will be removed in a future version.", "r184") class Clock(var autoStart: Boolean = true) extends js.Object { /** Holds the time at which the clock's start() method was last called. */ diff --git a/modules/three/src/main/scala/THREE/core/Timer.scala b/modules/three/src/main/scala/THREE/core/Timer.scala index 4eb7129..4f570c9 100644 --- a/modules/three/src/main/scala/THREE/core/Timer.scala +++ b/modules/three/src/main/scala/THREE/core/Timer.scala @@ -16,7 +16,7 @@ import org.scalajs.dom * delta values when the app is inactive (e.g. tab switched or browser * hidden). * - * @since r183 + * @since r184 */ @js.native @JSImport("three", "Timer") diff --git a/modules/three/src/main/scala/THREE/renderers/RenderPipeline.scala b/modules/three/src/main/scala/THREE/renderers/RenderPipeline.scala index 3363656..74f3da6 100644 --- a/modules/three/src/main/scala/THREE/renderers/RenderPipeline.scala +++ b/modules/three/src/main/scala/THREE/renderers/RenderPipeline.scala @@ -4,10 +4,10 @@ import scala.scalajs.js import scala.scalajs.js.annotation.* /** - * RenderPipeline is the new name for PostProcessing since r183. It manages a + * RenderPipeline is the new name for PostProcessing since r184. It manages a * series of post-processing passes to be applied to a scene. * - * @since r183 + * @since r184 */ @js.native @JSImport("three/webgpu", "RenderPipeline") diff --git a/modules/three/src/main/scala/THREE/textures/HTMLTexture.scala b/modules/three/src/main/scala/THREE/textures/HTMLTexture.scala new file mode 100644 index 0000000..42c61ef --- /dev/null +++ b/modules/three/src/main/scala/THREE/textures/HTMLTexture.scala @@ -0,0 +1,67 @@ +package THREE + +import scala.scalajs.js +import scala.scalajs.js.annotation.* + +/** + * Creates a texture from an HTML element. + * + * This is almost the same as the base texture class, except that it sets [[Texture#needsUpdate]] + * to `true` immediately and listens for the parent canvas's paint events to trigger updates. + * + * @augments Texture + */ +@js.native +@JSImport("three", "HTMLTexture") +class HTMLTexture( + element: js.UndefOr[js.Any] = js.undefined, + mapping: js.UndefOr[Int] = js.undefined, + wrapS: js.UndefOr[Int] = js.undefined, + wrapT: js.UndefOr[Int] = js.undefined, + magFilter: js.UndefOr[Int] = js.undefined, + minFilter: js.UndefOr[Int] = js.undefined, + format: js.UndefOr[Int] = js.undefined, + `type`: js.UndefOr[Int] = js.undefined, + anisotropy: js.UndefOr[Int] = js.undefined +) extends Texture( + element, + mapping, + wrapS, + wrapT, + magFilter, + minFilter, + format, + `type`, + anisotropy +) { + + /** + * This flag can be used for type testing. + * + * @type {boolean} + * @readonly + * @default true + */ + val isHTMLTexture: Boolean = js.native + + /** + * Releases the internal resources of the texture and removes the paint callback + * from the parent element. + */ + override def dispose(): Unit = js.native + +} + +object HTMLTexture { + def apply( + element: js.UndefOr[js.Any] = js.undefined, + mapping: js.UndefOr[Int] = js.undefined, + wrapS: js.UndefOr[Int] = js.undefined, + wrapT: js.UndefOr[Int] = js.undefined, + magFilter: js.UndefOr[Int] = js.undefined, + minFilter: js.UndefOr[Int] = js.undefined, + format: js.UndefOr[Int] = js.undefined, + `type`: js.UndefOr[Int] = js.undefined, + anisotropy: js.UndefOr[Int] = js.undefined + ): HTMLTexture = new HTMLTexture(element, mapping, wrapS, wrapT, magFilter, minFilter, format, `type`, anisotropy) +} From 5693c21132cba21c1b033cfd9d3457727c9555dd Mon Sep 17 00:00:00 2001 From: Olivier NOUGUIER Date: Fri, 24 Apr 2026 22:21:35 +0200 Subject: [PATCH 2/3] feat: integrate three-html-render polyfill and enhance HTMLTextureSample --- .gitignore | 2 +- example/client/package.json | 3 +- .../three/materials/HTMLTextureSample.scala | 132 ++++++++++-------- 3 files changed, 77 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index a852188..d7c3752 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,5 @@ dist/ scripts-managed/ externals/ -submodules/three.js/ +submodules/ diff --git a/example/client/package.json b/example/client/package.json index 7beec74..b0083d1 100644 --- a/example/client/package.json +++ b/example/client/package.json @@ -15,7 +15,8 @@ "@ui5/webcomponents-fiori": "2.21.1", "@ui5/webcomponents-icons": "2.21.1", "gl-matrix": "^3.4.3", - "three": "0.184.0" + "three": "0.184.0", + "three-html-render": "^0.1.2" }, "devDependencies": { "@scala-js/vite-plugin-scalajs": "^1.1.0", diff --git a/example/client/src/main/scala/dev/cheleb/scalajswebgl/samples/three/materials/HTMLTextureSample.scala b/example/client/src/main/scala/dev/cheleb/scalajswebgl/samples/three/materials/HTMLTextureSample.scala index a1360c9..7b5cdef 100644 --- a/example/client/src/main/scala/dev/cheleb/scalajswebgl/samples/three/materials/HTMLTextureSample.scala +++ b/example/client/src/main/scala/dev/cheleb/scalajswebgl/samples/three/materials/HTMLTextureSample.scala @@ -7,36 +7,19 @@ import THREE.* import org.scalajs.dom import org.scalajs.dom.window import scala.scalajs.js +import scala.scalajs.js.annotation.JSImport import scala.math.sin +@js.native +@JSImport("three-html-render/polyfill", JSImport.Namespace) +object HtmlInCanvasPolyfill extends js.Object { + def installHtmlInCanvasPolyfill(): Unit = js.native +} + object HTMLTextureSample { def apply() = - // Create the HTML element that will be used as a texture source - val htmlSource = dom.document.createElement("div").asInstanceOf[dom.html.Div] - htmlSource.style.width = "256px" - htmlSource.style.height = "256px" - htmlSource.style.background = "linear-gradient(135deg, #667eea 0%, #764ba2 100%)" - htmlSource.style.color = "white" - htmlSource.style.fontFamily = "Arial, sans-serif" - htmlSource.style.fontSize = "18px" - htmlSource.style.display = "flex" - htmlSource.style.setProperty("flex-direction", "column") - htmlSource.style.setProperty("align-items", "center") - htmlSource.style.setProperty("justify-content", "center") - htmlSource.style.borderRadius = "8px" - htmlSource.style.padding = "16px" - htmlSource.style.boxSizing = "border-box" - htmlSource.innerHTML = - """
- |

HTMLTexture

- |

Time: 0.0s

- |
- |
- |
- |
""".stripMargin - val htmlTextureDiv = div( h1("HTMLTexture Demo"), p( @@ -48,76 +31,106 @@ object HTMLTextureSample { ) ) + // Install the polyfill if native HTML-in-Canvas API is not available + if (!js.Dynamic.global.HTMLCanvasElement.prototype.hasOwnProperty("requestPaint").asInstanceOf[Boolean]) { + HtmlInCanvasPolyfill.installHtmlInCanvasPolyfill() + } + + // Create the HTML element that will be used as a texture source + val htmlSource = dom.document.createElement("div").asInstanceOf[dom.html.Div] + htmlSource.id = "draw_element" + htmlSource.style.width = "512px" + htmlSource.style.background = "linear-gradient(135deg, #667eea 0%, #764ba2 100%)" + htmlSource.style.color = "white" + htmlSource.style.fontFamily = "Arial, sans-serif" + htmlSource.style.fontSize = "24px" + htmlSource.style.setProperty("line-height", "1.5") + htmlSource.style.setProperty("text-align", "center") + htmlSource.style.padding = "30px" + htmlSource.innerHTML = + """
+ |

HTMLTexture

+ |

Time: 0.0s

+ |
+ |
+ |
+ |

Live HTML on 3D mesh!

+ |
""".stripMargin + // Create scene val scene = Scene() // Create camera - val camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000) - camera.position.z = 6 + val camera = new PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 2000) + camera.position.z = 8 // Create renderer val renderer = WebGLRenderer(antialias = true) renderer.setSize(window.innerWidth * 0.8, window.innerHeight * 0.8) - renderer.setClearColor("#1a1a2e", 1) + renderer.setClearColor("#aaaaaa", 1) + + // Pre-setup: attach the HTML element to the canvas and trigger an initial paint + // before the renderer tries to use the texture. This ensures the polyfill has + // completed at least one snapshot before texElementImage2D is called. + val canvas = renderer.domElement + canvas.asInstanceOf[js.Dynamic].setAttribute("layoutsubtree", "true") + canvas.appendChild(htmlSource) + canvas.asInstanceOf[js.Dynamic].requestPaint() // Create the HTMLTexture from the div element val htmlTexture = new HTMLTexture(htmlSource) // --- Object 1: Rotating cube with HTMLTexture --- - val cubeGeometry = new BoxGeometry(2, 2, 2) - val cubeMaterial = MeshPhongMaterial( - color = 0xffffff, - shininess = 60, - map = htmlTexture + val cubeGeometry = new BoxGeometry(2.5, 2.5, 2.5) + val cubeMaterial = MeshStandardMaterial( + roughness = 0.2, + metalness = 0.3 ) + cubeMaterial.map = htmlTexture val cube = new Mesh(cubeGeometry, cubeMaterial) - cube.position.x = -2.5 + cube.position.x = -3 scene.add(cube) // --- Object 2: Sphere with HTMLTexture --- - val sphereGeometry = new SphereGeometry(1.3, 32, 32) - val sphereMaterial = MeshPhongMaterial( - color = 0xffffff, - shininess = 80, - map = htmlTexture + val sphereGeometry = new SphereGeometry(1.5, 32, 32) + val sphereMaterial = MeshStandardMaterial( + roughness = 0.1, + metalness = 0.4 ) + sphereMaterial.map = htmlTexture val sphere = new Mesh(sphereGeometry, sphereMaterial) - sphere.position.x = 2.5 + sphere.position.x = 3 scene.add(sphere) // --- Object 3: Plane showing the texture flat --- - val planeGeometry = new PlaneGeometry(2.5, 2.5) + val planeGeometry = new PlaneGeometry(3, 3) val planeMaterial = MeshBasicMaterial(map = htmlTexture) val plane = new Mesh(planeGeometry, planeMaterial) - plane.position.set(0, -2.5, 0) - plane.rotation.x = -0.3 + plane.position.set(0, -3, 0) + plane.rotation.x = -0.4 scene.add(plane) // Add lighting - val directionalLight = DirectionalLight(0xffffff, 1.5) + val directionalLight = DirectionalLight(0xffffff, 2.0) directionalLight.position.set(5, 5, 5) scene.add(directionalLight) - val directionalLight2 = DirectionalLight(0xffffff, 0.8) + val directionalLight2 = DirectionalLight(0xffffff, 1.0) directionalLight2.position.set(-5, 3, -3) scene.add(directionalLight2) - val ambientLight = AmbientLight(0xffffff, 1.0) + val ambientLight = AmbientLight(0xffffff, 1.5) scene.add(ambientLight) - val pointLight = PointLight(0x4488ff, 1.2, 20) - pointLight.position.set(-3, 3, 3) - scene.add(pointLight) - // Animation loop val animate: () => Unit = () => { val time = js.Date.now() * 0.001 // Rotate objects - cube.rotation.x = time * 0.3 - cube.rotation.y = time * 0.5 + cube.rotation.x = sin(time * 0.5) * 0.5 + cube.rotation.y = time * 0.4 - sphere.rotation.y = time * 0.4 + sphere.rotation.y = time * 0.3 // Update the HTML content dynamically val timerEl = htmlSource.querySelector("#timer") @@ -131,13 +144,16 @@ object HTMLTextureSample { fillEl.style.width = s"$pct%" } - // The HTMLTexture listens for paint events automatically, - // but we can force an update if needed: - htmlTexture.needsUpdate = true - renderer.render(scene, camera) } - renderer.setAnimationLoop(animate) + + // Defer the animation loop start to allow the polyfill to complete + // its first requestPaint() + rAF snapshot cycle. + window.requestAnimationFrame { _ => + window.requestAnimationFrame { _ => + renderer.setAnimationLoop(animate) + } + } // Handle window resize val onWindowResize: dom.Event => Unit = { _ => @@ -148,7 +164,7 @@ object HTMLTextureSample { window.addEventListener("resize", onWindowResize) // Append renderer to the canvas container - htmlTextureDiv.ref.querySelector(".canvas-container").appendChild(renderer.domElement) + htmlTextureDiv.ref.querySelector(".canvas-container").appendChild(canvas) htmlTextureDiv } From fb58ac10e5a96db7e7f98a94f61235579303e821 Mon Sep 17 00:00:00 2001 From: Olivier NOUGUIER Date: Fri, 24 Apr 2026 22:29:48 +0200 Subject: [PATCH 3/3] feat: add three-html-render dependency to enhance rendering capabilities --- example/client/bun.lock | 3 +++ 1 file changed, 3 insertions(+) diff --git a/example/client/bun.lock b/example/client/bun.lock index 4763ab9..7523ef1 100644 --- a/example/client/bun.lock +++ b/example/client/bun.lock @@ -10,6 +10,7 @@ "@ui5/webcomponents-icons": "2.21.1", "gl-matrix": "^3.4.3", "three": "0.184.0", + "three-html-render": "^0.1.2", }, "devDependencies": { "@scala-js/vite-plugin-scalajs": "^1.1.0", @@ -357,6 +358,8 @@ "three": ["three@0.184.0", "", {}, "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg=="], + "three-html-render": ["three-html-render@0.1.2", "", { "peerDependencies": { "three": ">=0.150.0" }, "optionalPeers": ["three"] }, "sha512-JHx6x006LuLcHMRg+X3DyrrbPVsXklhFJGBtpTULFhbnpK7DidgGr6dJ6aTEaN9sc6uFgwI0VHExEYo4qwVSug=="], + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="],