diff --git a/bun.lockb b/bun.lockb index 9c71f03bf..d7cbc20b9 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/website/package.json b/packages/website/package.json index 787777462..ac1066ee4 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -20,6 +20,7 @@ "@radix-ui/colors": "^3.0.0", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/themes": "^3.1.4", + "@react-three/csg": "^4.0.0", "@react-three/drei": "^9.115.0", "@react-three/fiber": "^8.17.10", "compare-versions": "^6.1.1", diff --git a/packages/website/src/components/Armor/components/DissectorSceneComponent.tsx b/packages/website/src/components/Armor/components/DissectorSceneComponent.tsx index 1662de2a0..07b56f250 100644 --- a/packages/website/src/components/Armor/components/DissectorSceneComponent.tsx +++ b/packages/website/src/components/Armor/components/DissectorSceneComponent.tsx @@ -1,12 +1,27 @@ import { asset } from '@blitzkit/core'; +import { + Base, + Geometry, + Subtraction, + type CSGGeometryRef, +} from '@react-three/csg'; import { useGLTF } from '@react-three/drei'; +import { useEffect, useRef } from 'react'; +import type { Mesh } from 'three'; import { Color, DoubleSide, + Group, LineBasicMaterial, Material, MeshStandardMaterial, + Vector3, } from 'three'; +import { + dissectEvent, + lastDissection, + type DissectEventData, +} from '../../../core/blitzkit/dissect'; import { jsxTree } from '../../../core/blitzkit/jsxTree'; import { Duel } from '../../../stores/duel'; import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral'; @@ -16,6 +31,8 @@ interface DissectorSceneComponentProps { group: string; } +const NEGATIVE_SIZE = 20; + const armorColor = new Color().multiplyScalar(2 ** -8); const armorOutlineColor = armorColor.clone().multiplyScalar(2 ** 4.5); @@ -60,6 +77,10 @@ export function DissectorSceneComponent({ return jsxTree(gltf.scene, { mesh(mesh, props) { + const csg = useRef(null); + const subtractionWrapper = useRef(null); + const baseWrapper = useRef(null); + if (!(mesh.material instanceof Material)) { return null; } @@ -67,6 +88,10 @@ export function DissectorSceneComponent({ const armorName = mesh.material.name; const armorPlate = groupArmor.armors[armorName]; + if (armorPlate.type === 'Armor' || armorPlate.type === 'ArmorScreen') { + return null; + } + if (!(armorPlate.type in moduleColor)) { throw new TypeError(`Unhandled armor type: ${armorPlate.type}`); } @@ -79,14 +104,55 @@ export function DissectorSceneComponent({ side: DoubleSide, color: moduleOutlineColor[armorPlate.type], }); + const worldPosition = new Vector3(); + + useEffect(() => { + function handleDissectEvent(event: DissectEventData) { + if ( + !subtractionWrapper.current || + !csg.current || + !baseWrapper.current + ) + return; + + baseWrapper.current.getWorldPosition(worldPosition); + + subtractionWrapper.current.position + .copy(worldPosition) + .multiplyScalar(-1); + subtractionWrapper.current.rotation.y = event.rotation; + csg.current.update(); + } + + dissectEvent.on(handleDissectEvent); + handleDissectEvent(lastDissection); + + return () => { + dissectEvent.off(handleDissectEvent); + }; + }, []); return ( <> - + {/* */} - + + + + + + + + + + + + + {/* - + */} ); }, diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx index afecca245..8d8d86edd 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx @@ -152,11 +152,12 @@ export const TankSandbox = forwardRef( return ( (); + +export const lastDissection: DissectEventData = { rotation: 0, offset: 0 }; + +dissectEvent.on((data) => Object.assign(lastDissection, data)); diff --git a/packages/website/src/stores/tankopediaEphemeral.ts b/packages/website/src/stores/tankopediaEphemeral.ts index 671301f0e..2e9af2b75 100644 --- a/packages/website/src/stores/tankopediaEphemeral.ts +++ b/packages/website/src/stores/tankopediaEphemeral.ts @@ -137,7 +137,7 @@ export const TankopediaEphemeral = createContextualStore( armor, controlsEnabled: true, xpMultiplier: 1, - display: TankopediaDisplay.StaticArmor, + display: TankopediaDisplay.Dissector, })), ); },