From 2090d313af93594ba1f3b7986060344395db898b Mon Sep 17 00:00:00 2001 From: Mark Lundin Date: Wed, 4 Dec 2024 14:50:07 +0000 Subject: [PATCH 01/12] Updated Picker to handle offset canvas and canvas size changes --- packages/lib/src/utils/picker.tsx | 47 ++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/packages/lib/src/utils/picker.tsx b/packages/lib/src/utils/picker.tsx index 214d27f..18f79fb 100644 --- a/packages/lib/src/utils/picker.tsx +++ b/packages/lib/src/utils/picker.tsx @@ -1,5 +1,5 @@ import { AppBase, CameraComponent, Entity, GraphNode, Picker } from "playcanvas" -import { useCallback, useLayoutEffect, useMemo, useRef } from "react" +import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from "react" import { SyntheticMouseEvent, SyntheticPointerEvent } from "./synthetic-event"; // Utility to propagate events up the entity hierarchy @@ -36,7 +36,7 @@ const getNearestCommonAncestor = (a: GraphNode | null, b: GraphNode | null): Gra }; -const getEntityAtPointerEvent = async (app : AppBase, picker: Picker, e : MouseEvent) : Promise => { +const getEntityAtPointerEvent = async (app : AppBase, picker: Picker, rect: DOMRect, e : MouseEvent) : Promise => { // Find the highest priority camera const [activeCamera] : CameraComponent[] = (app.root.findComponents('camera') as CameraComponent[]) .filter((camera: CameraComponent) => !camera.renderTarget) @@ -44,10 +44,23 @@ const getEntityAtPointerEvent = async (app : AppBase, picker: Picker, e : MouseE if (!activeCamera) return null; - // prepare the picker and perform picking - picker.prepare(activeCamera, app.scene); - const devicePixelRatio = window?.devicePixelRatio ?? 1; - const [meshInstance] = await picker.getSelectionAsync(e.clientX * devicePixelRatio , e.clientY * devicePixelRatio); + // Get canvas bounds + const canvas = app.graphicsDevice.canvas; + + // Calculate position relative to canvas + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + + // Account for canvas scaling + const scaleX = canvas.width / rect.width; + const scaleY = canvas.height / rect.height; + + // prepare the picker and perform picking + picker.prepare(activeCamera, app.scene); + const [meshInstance] = await picker.getSelectionAsync( + x * scaleX, + y * scaleY + ); if (!meshInstance) return null @@ -57,6 +70,18 @@ const getEntityAtPointerEvent = async (app : AppBase, picker: Picker, e : MouseE export const usePicker = (app: AppBase | null, el: HTMLElement | null) => { const activeEntity = useRef(null); const pointerDetails = useRef(null); + const canvasRectRef = useRef(app ? app.graphicsDevice.canvas.getBoundingClientRect() : null); + + // Watch for the canvas to resize. Neccesary for correct picking + useEffect(() => { + const resizeObserver = new ResizeObserver(() => { + canvasRectRef.current = app ? app.graphicsDevice.canvas.getBoundingClientRect() : null; + }); + + if(app) resizeObserver.observe(app.graphicsDevice.canvas); + return () => resizeObserver.disconnect(); + + }, [app]); // Construct a Global Picker const picker: Picker | null = useMemo((): Picker | null => { @@ -74,8 +99,10 @@ export const usePicker = (app: AppBase | null, el: HTMLElement | null) => { const e : PointerEvent | null = pointerDetails.current; if (!picker || !app || !e) return null; - const entity = await getEntityAtPointerEvent(app, picker, e); - if (!entity) return null; + if (!canvasRectRef.current) return null; + + const entity = await getEntityAtPointerEvent(app, picker, canvasRectRef.current, e); + // if (!entity) return null; const prevEntity = activeEntity.current; @@ -105,9 +132,9 @@ export const usePicker = (app: AppBase | null, el: HTMLElement | null) => { // Construct a generic handler for pointer events const onInteractionEvent = useCallback(async (e: MouseEvent) => { - if (!picker || !app) return; + if (!picker || !app || !canvasRectRef.current) return; - const entity = await getEntityAtPointerEvent(app, picker, e); + const entity = await getEntityAtPointerEvent(app, picker, canvasRectRef.current, e); if (!entity) return From 119673c0b88d87e6046eabc76bac74065069a105 Mon Sep 17 00:00:00 2001 From: Mark Lundin Date: Wed, 4 Dec 2024 14:50:37 +0000 Subject: [PATCH 02/12] Added OrbitCamera `__name` --- packages/lib/src/scripts/orbit-controls/orbit-camera.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/lib/src/scripts/orbit-controls/orbit-camera.js b/packages/lib/src/scripts/orbit-controls/orbit-camera.js index 5e6eca0..2ff3951 100644 --- a/packages/lib/src/scripts/orbit-controls/orbit-camera.js +++ b/packages/lib/src/scripts/orbit-controls/orbit-camera.js @@ -5,7 +5,7 @@ import { } from 'playcanvas'; export class OrbitCamera extends Script { - static name = 'OrbitCamera'; + static __name = 'orbitCamera'; /** * @attribute @@ -443,13 +443,13 @@ export class OrbitCamera extends Script { export class OrbitCameraInputMouse extends Script { - static name = 'OrbitCameraInputMouse'; + static __name = 'orbitCameraInputMouse'; /** * How fast the camera moves around the orbit. Higher is faster * * @attribute - * @type {number} + * @type {number.0} */ orbitSensitivity = 0.3; @@ -574,7 +574,7 @@ export class OrbitCameraInputMouse extends Script { } export class OrbitCameraInputTouch extends Script { - static name = 'OrbitCameraInputTouch'; + static __name = 'orbitCameraInputTouch'; /** * How fast the camera moves around the orbit. Higher is faster From 3b4dc5cd8b7966616f6627ceeed8d1ed83ac570f Mon Sep 17 00:00:00 2001 From: Mark Lundin Date: Wed, 4 Dec 2024 14:51:04 +0000 Subject: [PATCH 03/12] scripts use `__name` --- packages/lib/src/hooks/use-script.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/lib/src/hooks/use-script.tsx b/packages/lib/src/hooks/use-script.tsx index 1f03358..beb73e7 100644 --- a/packages/lib/src/hooks/use-script.tsx +++ b/packages/lib/src/hooks/use-script.tsx @@ -2,6 +2,7 @@ import { useEffect, useRef } from 'react'; import { useParent } from './use-parent'; import { useApp } from './use-app'; import { Application, Entity, Script, ScriptComponent } from 'playcanvas'; +import { ScriptConstructor } from '../components/Script'; const toLowerCamelCase = (str: string) : string => str[0].toLowerCase() + str.substring(1); @@ -9,10 +10,10 @@ interface Props { [key: string]: unknown; } -export const useScript = (ScriptConstructor: typeof Script, props: Props) : void => { +export const useScript = (scriptConstructor: ScriptConstructor, props: Props) : void => { const parent: Entity = useParent(); const app: Application = useApp(); - const scriptName: string = toLowerCamelCase(ScriptConstructor.name); + const scriptName: string = toLowerCamelCase(scriptConstructor.__name); const scriptRef = useRef