From 83a9cda64b827e236b4e30ddef851812ffbc6445 Mon Sep 17 00:00:00 2001 From: york Date: Mon, 17 Jun 2024 08:03:15 +0800 Subject: [PATCH] feat: webgl 3d put --- dev/webgl-3d-put.story.tsx | 101 +++++++++++++++++++++++++++ src/components/webgl-3d-renderer.tsx | 26 ++++--- 2 files changed, 118 insertions(+), 9 deletions(-) create mode 100644 dev/webgl-3d-put.story.tsx diff --git a/dev/webgl-3d-put.story.tsx b/dev/webgl-3d-put.story.tsx new file mode 100644 index 00000000..18f0b62b --- /dev/null +++ b/dev/webgl-3d-put.story.tsx @@ -0,0 +1,101 @@ +import * as React from "react" +import { createWebgl3DRenderer, Graphic3d, useWindowSize, angleToRadian, Vec3, useGlobalKeyDown, Nullable, useUndoRedo, metaKeyIfMacElseCtrlKey } from "../src" + +export default () => { + const ref = React.useRef(null) + const renderer = React.useRef>() + const size = useWindowSize() + const width = size.width / 2 + const height = size.height + const eye: Vec3 = [0, -90, 90] + const { state, undo, redo } = useUndoRedo([ + { + geometry: { + type: 'triangle strip', + points: [-50, -50, 0, -50, 50, 0, 50, -50, 0, 50, 50, 0], + }, + color: [0.6, 0.6, 0.6, 1], + position: [0, 0, 0], + }, + ]) + const [preview, setPreview] = React.useState() + + React.useEffect(() => { + if (!ref.current || renderer.current) { + return + } + renderer.current = createWebgl3DRenderer(ref.current) + }, [ref.current]) + + useGlobalKeyDown(e => { + if (metaKeyIfMacElseCtrlKey(e)) { + if (e.code === 'KeyZ') { + if (e.shiftKey) { + redo(e) + } else { + undo(e) + } + } + } + }) + const render = (g: Nullable[]) => { + renderer.current?.render?.( + g, + { + eye, + up: [0, 1, 0], + target: [0, 0, 0], + fov: angleToRadian(60), + near: 0.1, + far: 1000, + }, + { + position: [1000, -1000, 1000], + color: [1, 1, 1, 1], + specular: [1, 1, 1, 1], + shininess: 50, + specularFactor: 1, + }, + [1, 1, 1, 1], + ) + } + + React.useEffect(() => { + const graphics = [...state] + if (preview) { + graphics.push(preview) + } + render(graphics) + }, [state, preview]) + + return ( +
+ { + if (renderer.current) { + const info = renderer.current.pickingDrawObjectsInfo[0] + if (info) { + const target = renderer.current.getTarget(e.clientX, e.clientY, eye, 0, info.reversedProjection) + setPreview({ + geometry: { + type: 'cube', + size: 5, + }, + color: [1, 0, 0, 1], + position: [target[0], target[1], 5], + }) + } + } + }} + /> +
+ ) +} diff --git a/src/components/webgl-3d-renderer.tsx b/src/components/webgl-3d-renderer.tsx index b87d62e4..835645d0 100644 --- a/src/components/webgl-3d-renderer.tsx +++ b/src/components/webgl-3d-renderer.tsx @@ -337,21 +337,25 @@ export function createWebgl3DRenderer(canvas: HTMLCanvasElement) { return index === 0xffffff ? undefined : index } - const pickPoint = (inputX: number, inputY: number, eye: Vec3, z: number, filter: (graphic: Graphic3d, index: number) => boolean = () => true): Vec3 | undefined => { - const rect = canvas.getBoundingClientRect(); + const getTarget = (inputX: number, inputY: number, eye: Vec3, z: number, reversedProjection: m4.Mat4): Vec3 => { + const rect = canvas.getBoundingClientRect() const x = (inputX - rect.left) / canvas.clientWidth * 2 - 1 const y = -((inputY - rect.top) / canvas.clientHeight * 2 - 1) + const a = m4.transformPoint(reversedProjection, [x, y, 1]) + const b = (z - eye[2]) / (a[2] - eye[2]) + return [ + eye[0] + (a[0] - eye[0]) * b, + eye[1] + (a[1] - eye[1]) * b, + z, + ] + } + + const pickPoint = (inputX: number, inputY: number, eye: Vec3, z: number, filter: (graphic: Graphic3d, index: number) => boolean = () => true): Vec3 | undefined => { const index = pick(inputX, inputY, filter) if (index !== undefined) { const info = pickingDrawObjectsInfo.find(p => p && p.index === index) if (info && info.graphic.geometry.type === 'vertices') { - const a = m4.transformPoint(info.reversedProjection, [x, y, 1]) - const b = (z - eye[2]) / (a[2] - eye[2]) - const target: Vec3 = [ - eye[0] + (a[0] - eye[0]) * b, - eye[1] + (a[1] - eye[1]) * b, - z, - ] + const target = getTarget(inputX, inputY, eye, z, info.reversedProjection) const line = new verb.geom.Line(eye, target) const intersections = verb.geom.Intersect.curveAndSurface(line, info.graphic.geometry.nurbs, 1e-3) if (intersections.length > 0) { @@ -367,6 +371,10 @@ export function createWebgl3DRenderer(canvas: HTMLCanvasElement) { render, pick, pickPoint, + getTarget, + get pickingDrawObjectsInfo() { + return pickingDrawObjectsInfo + }, } }