From 068eeb082d3436a74fccc351322da86ed79e94c5 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 18 Aug 2025 18:05:27 +0900 Subject: [PATCH 1/2] Update ImageOverlayPlugin.frame to transform tile-frame points to projection points. --- example/r3f/plugins/ImageOverlayPlugin.jsx | 15 ++++++++------- example/r3f/projection.jsx | 9 ++++++++- src/three/plugins/images/ImageOverlayPlugin.js | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/example/r3f/plugins/ImageOverlayPlugin.jsx b/example/r3f/plugins/ImageOverlayPlugin.jsx index 007a04da0..1e5f6d95b 100644 --- a/example/r3f/plugins/ImageOverlayPlugin.jsx +++ b/example/r3f/plugins/ImageOverlayPlugin.jsx @@ -2,6 +2,7 @@ import { useFrame, useThree } from '@react-three/fiber'; import { ImageOverlayPlugin as ImageOverlayPluginImpl } from '3d-tiles-renderer/plugins'; import { TilesPlugin, TilesPluginContext, TilesRendererContext } from '3d-tiles-renderer/r3f'; import { forwardRef, useContext, useEffect, useMemo, useRef } from 'react'; +import { Matrix4 } from 'three'; export const ImageOverlayPlugin = forwardRef( function ImageOverlayPlugin( { children, ...rest }, ref ) { @@ -21,7 +22,7 @@ export const ImageOverlay = forwardRef( function ImageOverlay( props, ref ) { order = null, opacity = 1, color = 0xffffff, - worldFrame = null, + worldToProjection = null, ...rest } = props; @@ -55,23 +56,23 @@ export const ImageOverlay = forwardRef( function ImageOverlay( props, ref ) { overlay.opacity = opacity; overlay.color.set( color ); - if ( worldFrame && ! overlay.frame ) { + if ( worldToProjection && ! overlay.frame ) { - overlay.frame = worldFrame.clone(); + overlay.frame = new Matrix4(); - } else if ( ! worldFrame && overlay.frame ) { + } else if ( ! worldToProjection && overlay.frame ) { overlay.frame = null; } - }, [ overlay, opacity, color, worldFrame ] ); + }, [ overlay, opacity, color, worldToProjection ] ); useFrame( () => { - if ( worldFrame && tiles ) { + if ( worldToProjection && tiles ) { - overlay.frame.copy( worldFrame ).premultiply( tiles.group.matrixWorldInverse ); + overlay.frame.copy( worldToProjection ).multiply( tiles.group.matrixWorld ); } diff --git a/example/r3f/projection.jsx b/example/r3f/projection.jsx index 427f69a16..b8d331b99 100644 --- a/example/r3f/projection.jsx +++ b/example/r3f/projection.jsx @@ -38,6 +38,12 @@ function Scene() { }, [] ); + const worldToProjectionMatrix = useMemo( () => { + + return new Matrix4(); + + }, [] ); + useEffect( () => { return () => { @@ -55,6 +61,7 @@ function Scene() { boxMesh.scale.x = overlay.aspectRatio; boxMesh.position.x = overlay.aspectRatio / 2; + worldToProjectionMatrix.copy( transformRoot.matrixWorld ).invert(); } @@ -73,7 +80,7 @@ function Scene() { type={ CesiumIonOverlay } assetId='3954' apiToken={ import.meta.env.VITE_ION_KEY } - worldFrame={ transformRoot ? transformRoot.matrixWorld : null } + worldToProjection={ worldToProjectionMatrix } ref={ setOverlay } /> diff --git a/src/three/plugins/images/ImageOverlayPlugin.js b/src/three/plugins/images/ImageOverlayPlugin.js index 52c81d0bd..7bfefcc19 100644 --- a/src/three/plugins/images/ImageOverlayPlugin.js +++ b/src/three/plugins/images/ImageOverlayPlugin.js @@ -1138,7 +1138,7 @@ export class ImageOverlayPlugin { // retrieve the uvs and range for all the meshes if ( overlay.isPlanarProjection ) { - _matrix.copy( overlay.frame ).invert(); + _matrix.copy( overlay.frame ); if ( scene.parent !== null ) { _matrix.multiply( tiles.group.matrixWorldInverse ); From 34a4b4040005ae7f6b0b7c0281faf69e60a9548a Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 18 Aug 2025 20:10:19 +0900 Subject: [PATCH 2/2] Add support for projection matrix --- example/r3f/projection.jsx | 66 ++++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/example/r3f/projection.jsx b/example/r3f/projection.jsx index b8d331b99..50dfc0f11 100644 --- a/example/r3f/projection.jsx +++ b/example/r3f/projection.jsx @@ -3,18 +3,21 @@ import { createRoot } from 'react-dom/client'; import { Canvas, useFrame } from '@react-three/fiber'; import { TilesPlugin, TilesRenderer, EnvironmentControls } from '3d-tiles-renderer/r3f'; import { TilesFadePlugin, CesiumIonOverlay, EnforceNonZeroErrorPlugin } from '3d-tiles-renderer/plugins'; -import { BoxGeometry, EdgesGeometry, Euler, Matrix4, Quaternion, Vector3 } from 'three'; +import { BoxGeometry, EdgesGeometry, Euler, Matrix4, Mesh, MeshNormalMaterial, MeshStandardMaterial, PerspectiveCamera, Quaternion, Vector3 } from 'three'; import { PivotControls } from '@react-three/drei'; import { ImageOverlay, ImageOverlayPlugin } from './plugins/ImageOverlayPlugin.jsx'; import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2.js'; import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry.js'; +import { useControls } from 'leva'; const tilesetUrl = 'https://raw.githubusercontent.com/NASA-AMMOS/3DTilesSampleData/master/msl-dingo-gap/0528_0260184_to_s64o256_colorize/0528_0260184_to_s64o256_colorize/0528_0260184_to_s64o256_colorize_tileset.json'; function Scene() { const [ transformRoot, setTransformRoot ] = useState( null ); + const [ projectionRoot, setProjectionRoot ] = useState( null ); const [ overlay, setOverlay ] = useState( null ); + const { perspective } = useControls( { perspective: true } ); const worldMatrix = useMemo( () => { @@ -29,11 +32,21 @@ function Scene() { const boxMesh = useMemo( () => { const boxGeometry = new BoxGeometry(); + boxGeometry.translate( 0.5, 0.5, 0.5 ); + + const mesh = new Mesh( boxGeometry, new MeshNormalMaterial() ); + mesh.material.opacity = 0.25; + mesh.material.transparent = true; + mesh.material.depthWrite = false; + return mesh; + + const edgesGeometry = new EdgesGeometry( boxGeometry ); const linesGeometry = new LineSegmentsGeometry().fromEdgesGeometry( edgesGeometry ); const lines = new LineSegments2( linesGeometry ); lines.material.color.set( 0xffff00 ); lines.material.linewidth = 2; + return lines; }, [] ); @@ -55,13 +68,30 @@ function Scene() { }, [ boxMesh ] ); + useEffect( () => { + + if ( ! projectionRoot ) { + + return; + + } + + projectionRoot.updateMatrix(); + projectionRoot.matrixAutoUpdate = false; + + const camera = new PerspectiveCamera(); + // projectionRoot.matrix.makeScale( 1, 1, 2 ).setPosition( - 0.5, - 0.5, - 0.5 );//.premultiply( camera.projectionMatrixInverse ); + projectionRoot.matrix.makeScale( 1, 2, 2 ).setPosition( - 1, - 1, - 1 ).premultiply( camera.projectionMatrixInverse ); + + + }, [ projectionRoot ] ); + useFrame( () => { if ( overlay && boxMesh ) { - boxMesh.scale.x = overlay.aspectRatio; - boxMesh.position.x = overlay.aspectRatio / 2; - worldToProjectionMatrix.copy( transformRoot.matrixWorld ).invert(); + // boxMesh.scale.x = overlay.aspectRatio; + worldToProjectionMatrix.copy( ( perspective ? projectionRoot : transformRoot ).matrixWorld ).invert(); } @@ -89,12 +119,28 @@ function Scene() { {/* Controls */} - - - - - - + + + { + ! perspective && + + + + + + } + + { + perspective && + + + + + + + + } + );