From 6bcf5c25dc85fe2cbd798252271cf0602ac3cacc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:48:24 -0500 Subject: [PATCH] fix: directional clip --- .vscode/settings.json | 2 + .../components/StaticArmorSceneComponent.tsx | 94 ++++++++++++++----- src/components/StaticArmor/index.tsx | 65 ++++++++++++- src/core/blitz/correctZYTuple.ts | 7 +- src/core/blitz/rotateDavaVector.ts | 6 ++ src/core/blitz/unrotateDavaVector.ts | 6 ++ 6 files changed, 149 insertions(+), 31 deletions(-) create mode 100644 src/core/blitz/rotateDavaVector.ts create mode 100644 src/core/blitz/unrotateDavaVector.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index b663f9824..ad9ea3b0b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -41,6 +41,7 @@ "CUBETEXCOORD", "customperiod", "datasource", + "Dava", "dbid", "Ddpf", "Ddsd", @@ -169,6 +170,7 @@ "UNORM", "unpickled", "Unregistering", + "unrotate", "vickers", "vitepress", "Vovko", diff --git a/src/components/Armor/components/StaticArmorSceneComponent.tsx b/src/components/Armor/components/StaticArmorSceneComponent.tsx index 0dcec35e5..3eb8dbecc 100644 --- a/src/components/Armor/components/StaticArmorSceneComponent.tsx +++ b/src/components/Armor/components/StaticArmorSceneComponent.tsx @@ -1,4 +1,4 @@ -import { useThree } from '@react-three/fiber'; +import { MeshProps, useThree } from '@react-three/fiber'; import { clamp } from 'lodash'; import { useEffect, useMemo } from 'react'; import { @@ -12,8 +12,15 @@ import { Object3D, Plane, Quaternion, + Vector3, } from 'three'; +import { I_HAT, J_HAT } from '../../../constants/axis'; +import { unrotateDavaVector } from '../../../core/blitz/unrotateDavaVector'; import { jsxTree } from '../../../core/blitzkit/jsxTree'; +import { + modelTransformEvent, + ModelTransformEventData, +} from '../../../core/blitzkit/modelTransform'; import { discardClippingPlane } from '../../../core/three/discardClippingPlane'; import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral'; import { ThicknessRange } from '../../StaticArmor'; @@ -25,10 +32,10 @@ import { type StaticArmorSceneComponentProps = { name: string; - clip?: Plane; thickness: number; thicknessRange: ThicknessRange; node: Object3D; + onPointerDown?: MeshProps['onPointerDown']; } & ( | { type: Exclude; @@ -37,7 +44,18 @@ type StaticArmorSceneComponentProps = { type: ArmorType.External; variant: ExternalModuleVariant; } -); +) & + ( + | { + clip?: undefined; + } + | { + clip: Plane; + hullOrigin: Vector3; + turretOrigin: Vector3; + gunOrigin: Vector3; + } + ); const unselectedColor = new Color(0x404040); const externalModuleColor = new Color(0, 1 / 4, 1 / 2); @@ -47,13 +65,13 @@ export function StaticArmorSceneComponent({ thickness, thicknessRange, node, - clip, + onPointerDown, ...props }: StaticArmorSceneComponentProps) { const mutateTankopediaEphemeralStore = TankopediaEphemeral.useMutation(); const tankopediaEphemeralStore = TankopediaEphemeral.useStore(); const camera = useThree((state) => state.camera); - + const scene = useThree((state) => state.scene); const x = thickness / thicknessRange.value; const xClamped = clamp(x, 0, 1); let color: Color; @@ -95,7 +113,7 @@ export function StaticArmorSceneComponent({ opacity, transparent: opacity < 1, depthWrite, - ...(clip ? { clippingPlanes: [clip] } : {}), + ...(props.clip ? { clippingPlanes: [props.clip] } : {}), }), [thickness], ); @@ -109,19 +127,6 @@ export function StaticArmorSceneComponent({ [thickness], ); - // if (clip) { - // /** - // * hook inside an if statement?? don't panic! I assure you the clip prop - // * never mutates :) - // */ - // useFrame(() => { - // surfaceMaterial.clippingPlanes!.forEach((plane) => { - // // console.log(plane.normal); - // plane.applyMatrix4(node.matrix); - // }); - // }); - // } - useEffect(() => { function handleHighlightArmor(selectedName?: string) { if (selectedName === undefined) { @@ -160,18 +165,59 @@ export function StaticArmorSceneComponent({ surfaceMaterial.needsUpdate = true; } - const unsubscribe = tankopediaEphemeralStore.subscribe( - (state) => state.highlightArmor?.name, - handleHighlightArmor, - ); + const unsubscribes = [ + tankopediaEphemeralStore.subscribe( + (state) => state.highlightArmor?.name, + handleHighlightArmor, + ), + ]; - return unsubscribe; + if (props.clip) { + /** + * hook inside an if statement?? don't panic! I assure you the clip prop + * never mutates :) + */ + + const { clip } = props; + const gunOrigin = unrotateDavaVector(props.gunOrigin.clone()); + const neckOrigin = unrotateDavaVector(props.hullOrigin.clone()).add( + unrotateDavaVector(props.turretOrigin.clone()), + ); + const barrelOrigin = neckOrigin.clone().add(gunOrigin); + const distanceToBarrel = clip.distanceToPoint(barrelOrigin); + const point = new Vector3(); + + function handleModelTransform(data: ModelTransformEventData) { + clip.normal + .set(0, 0, -1) + .applyAxisAngle(I_HAT, data.pitch) + .applyAxisAngle(J_HAT, data.yaw ?? 0); + clip.constant = 0; + + point + .copy(gunOrigin) + .applyAxisAngle(J_HAT, data.yaw ?? 0) + .add(clip.normal.multiplyScalar(-distanceToBarrel)) + .add(neckOrigin); + clip.normal.normalize(); + clip.constant -= clip.distanceToPoint(point); + } + + modelTransformEvent.on(handleModelTransform); + + unsubscribes.push(() => modelTransformEvent.off(handleModelTransform)); + } + + return () => { + unsubscribes.forEach((unsubscribe) => unsubscribe()); + }; }, [thickness]); return ( <> {jsxTree(node, { material: surfaceMaterial, + onPointerDown, userData: { type: props.type, variant: props.type === ArmorType.External ? props.variant : 'gun', diff --git a/src/components/StaticArmor/index.tsx b/src/components/StaticArmor/index.tsx index 5261e3671..ed64e548a 100644 --- a/src/components/StaticArmor/index.tsx +++ b/src/components/StaticArmor/index.tsx @@ -197,7 +197,7 @@ export const StaticArmor = memo(({ thicknessRange }) => { } return ( - + (({ thicknessRange }) => { type={spaced ? ArmorType.Spaced : ArmorType.Primary} thickness={thickness} node={node} + onPointerDown={onPointerDown} /> @@ -289,7 +290,7 @@ export const StaticArmor = memo(({ thicknessRange }) => { } return ( - + (({ thicknessRange }) => { type={spaced ? ArmorType.Spaced : ArmorType.Primary} thickness={thickness} node={node} + onPointerDown={onPointerDown} /> @@ -315,6 +317,61 @@ export const StaticArmor = memo(({ thicknessRange }) => { if (!isVisible || !showExternalModules) return null; + const position = new Vector2(); + const delta = new Vector2(); + let pitch = 0; + let yaw = 0; + + function onPointerDown(event: ThreeEvent) { + event.stopPropagation(); + + mutateTankopediaEphemeral((draft) => { + draft.controlsEnabled = false; + }); + mutateTankopediaEphemeral((draft) => { + draft.shot = undefined; + draft.highlightArmor = undefined; + }); + + position.set(event.clientX, event.clientY); + pitch = protagonist.pitch; + yaw = protagonist.yaw; + + window.addEventListener('pointermove', handlePointerMove); + window.addEventListener('pointerup', handlePointerUp); + } + async function handlePointerMove(event: PointerEvent) { + const duel = duelStore.getState(); + const hasImprovedVerticalStabilizer = await hasEquipment( + 122, + duel.protagonist.tank.equipment, + duel.protagonist.equipmentMatrix, + ); + const boundingRect = canvas.getBoundingClientRect(); + delta.set(event.clientX, event.clientY).sub(position); + position.set(event.clientX, event.clientY); + + [pitch, yaw] = applyPitchYawLimits( + pitch - delta.y * (Math.PI / boundingRect.height), + yaw + delta.x * (Math.PI / boundingRect.width), + gunModelDefinition.pitch, + turretModelDefinition.yaw, + hasImprovedVerticalStabilizer, + ); + modelTransformEvent.emit({ pitch, yaw }); + } + function handlePointerUp() { + mutateTankopediaEphemeral((draft) => { + draft.controlsEnabled = true; + }); + mutateDuel((draft) => { + draft.protagonist.pitch = normalizeAngleRad(pitch); + draft.protagonist.yaw = normalizeAngleRad(yaw); + }); + window.removeEventListener('pointermove', handlePointerMove); + window.removeEventListener('pointerup', handlePointerUp); + } + return ( (({ thicknessRange }) => { ? undefined : new Plane(new Vector3(0, 0, -1), -maskOrigin) } + hullOrigin={hullOrigin} + turretOrigin={turretOrigin} + gunOrigin={gunOrigin} + onPointerDown={onPointerDown} /> ); })} diff --git a/src/core/blitz/correctZYTuple.ts b/src/core/blitz/correctZYTuple.ts index f0ebb92a4..7eeca33f6 100644 --- a/src/core/blitz/correctZYTuple.ts +++ b/src/core/blitz/correctZYTuple.ts @@ -1,9 +1,6 @@ import { Vector3, Vector3Tuple } from 'three'; -import { I_HAT } from '../../constants/axis'; +import { rotateDavaVector } from './rotateDavaVector'; export function correctZYTuple(tuple: Vector3Tuple) { - return new Vector3(tuple[0], tuple[1], -tuple[2]).applyAxisAngle( - I_HAT, - Math.PI / 2, - ); + return rotateDavaVector(new Vector3(tuple[0], tuple[1], -tuple[2])); } diff --git a/src/core/blitz/rotateDavaVector.ts b/src/core/blitz/rotateDavaVector.ts new file mode 100644 index 000000000..f3e76e9bd --- /dev/null +++ b/src/core/blitz/rotateDavaVector.ts @@ -0,0 +1,6 @@ +import { Vector3 } from 'three'; +import { I_HAT } from '../../constants/axis'; + +export function rotateDavaVector(vector: Vector3) { + return vector.applyAxisAngle(I_HAT, Math.PI / 2); +} diff --git a/src/core/blitz/unrotateDavaVector.ts b/src/core/blitz/unrotateDavaVector.ts new file mode 100644 index 000000000..a312cef3d --- /dev/null +++ b/src/core/blitz/unrotateDavaVector.ts @@ -0,0 +1,6 @@ +import { Vector3 } from 'three'; +import { I_HAT } from '../../constants/axis'; + +export function unrotateDavaVector(vector: Vector3) { + return vector.applyAxisAngle(I_HAT, -Math.PI / 2); +}