diff --git a/src/app/tools/tankopedia/[id]/components/Model/components/TankArmor/components/ExternalModuleMask.tsx b/src/app/tools/tankopedia/[id]/components/Model/components/TankArmor/components/ExternalModuleMask.tsx index 222b5bdc7..6c1f5a0a8 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/components/TankArmor/components/ExternalModuleMask.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/components/TankArmor/components/ExternalModuleMask.tsx @@ -1,5 +1,5 @@ import { memo, useEffect, useRef } from 'react'; -import { Euler, Group, Mesh, Vector3 } from 'three'; +import { Euler, Group, Vector3 } from 'three'; import { degToRad } from 'three/src/math/MathUtils'; import { ArmorMeshExternalModuleMask } from '../../../../../../../../../components/ArmorMesh'; import { I_HAT, J_HAT, K_HAT } from '../../../../../../../../../constants/axis'; @@ -181,7 +181,7 @@ export const ExternalModuleMask = memo( ornamental={ornamental} exclude key={node.uuid} - geometry={(node as Mesh).geometry} + node={node} /> ); })} @@ -200,7 +200,7 @@ export const ExternalModuleMask = memo( maxThickness={maxThickness} thickness={trackModelDefinition.thickness} key={node.uuid} - geometry={(node as Mesh).geometry} + node={node} /> ); })} @@ -221,13 +221,14 @@ export const ExternalModuleMask = memo( if (!isVisible || thickness === undefined || spaced) return null; return ( - + + + ); })} @@ -247,13 +248,14 @@ export const ExternalModuleMask = memo( if (!isVisible || thickness === undefined || spaced) return null; return ( - + + + ); })} @@ -271,7 +273,7 @@ export const ExternalModuleMask = memo( maxThickness={maxThickness} thickness={gunModelDefinition.thickness} key={node.uuid} - geometry={(node as Mesh).geometry} + node={node} /> ); })} diff --git a/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx b/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx index 0bd493e9f..ef8f4ee8e 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx @@ -1,16 +1,10 @@ import { ThreeEvent, useThree } from '@react-three/fiber'; import { memo, useEffect, useRef, useState } from 'react'; -import { - Euler, - Group, - Mesh, - MeshStandardMaterial, - Vector2, - Vector3, -} from 'three'; +import { Euler, Group, Vector3 } from 'three'; import { degToRad } from 'three/src/math/MathUtils'; import { I_HAT, J_HAT, K_HAT } from '../../../../../../../constants/axis'; import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits'; +import { jsxTree } from '../../../../../../../core/blitzkrieg/jsxTree'; import { ModelDefinitions, modelDefinitions, @@ -19,7 +13,6 @@ import { ModelTransformEventData, modelTransformEvent, } from '../../../../../../../core/blitzkrieg/modelTransform'; -import { resolveJsxTree } from '../../../../../../../core/blitzkrieg/resolveJsxTree'; import { normalizeAnglePI } from '../../../../../../../core/math/normalizeAngle180'; import { useModel } from '../../../../../../../hooks/useModel'; import { useDuel } from '../../../../../../../stores/duel'; @@ -138,58 +131,12 @@ export const TankModel = memo(() => { const isTrack = node.name.startsWith('chassis_track_'); const isVisible = isHull || isWheel || isTrack; - function handlePointerDown(event: ThreeEvent) { - event.stopPropagation(); - - if (isTrack) { - mutateTankopediaPersistent((draft) => { - draft.model.visual.controlsEnabled = false; - }); - mutateTankopediaTemporary((draft) => { - draft.shot = undefined; - }); - - window.addEventListener('pointermove', handlePointerMove); - window.addEventListener('pointerup', handlePointerUp); - } - } - function handlePointerMove(event: PointerEvent) { - const mesh = node as Mesh; - const material = mesh.material as MeshStandardMaterial; - const offset = new Vector2( - 0, - -event.movementY * (6 / canvas.height) - - event.movementX * (6 / canvas.width), - ); - - material.map?.offset.add(offset); - material.roughnessMap?.offset.add(offset); - material.normalMap?.offset.add(offset); - } - function handlePointerUp() { - mutateTankopediaPersistent((draft) => { - draft.model.visual.controlsEnabled = true; - }); - window.removeEventListener('pointermove', handlePointerMove); - window.removeEventListener('pointerup', handlePointerUp); - } - if (!isVisible) return null; - return ( - - ); + return jsxTree(node, { + castShadow: true, + receiveShadow: true, + }); })} @@ -199,10 +146,13 @@ export const TankModel = memo(() => { node.name === `turret_${turretModelDefinition.model.toString().padStart(2, '0')}`; const isVisible = isCurrentTurret; + + if (!isVisible) return null; + let pitch = 0; let yaw = 0; - function handlePointerDown(event: ThreeEvent) { + function onPointerDown(event: ThreeEvent) { event.stopPropagation(); if (isTurret) { @@ -240,23 +190,11 @@ export const TankModel = memo(() => { window.removeEventListener('pointerup', handlePointerUp); } - if (!isVisible) return null; - - return ( - - ); + return jsxTree(node, { + castShadow: true, + receiveShadow: true, + onPointerDown, + }); })} @@ -270,10 +208,13 @@ export const TankModel = memo(() => { node.name === `gun_${gunModelDefinition.model.toString().padStart(2, '0')}`; const isVisible = isCurrentGun || isCurrentMantlet; + + if (!isVisible) return null; + let pitch = 0; let yaw = 0; - function handlePointerDown(event: ThreeEvent) { + function onPointerDown(event: ThreeEvent) { event.stopPropagation(); mutateTankopediaPersistent((draft) => { @@ -308,23 +249,11 @@ export const TankModel = memo(() => { window.removeEventListener('pointerup', handlePointerUp); } - if (!isVisible) return null; - - return ( - - ); + return jsxTree(node, { + castShadow: true, + receiveShadow: true, + onPointerDown, + }); })} diff --git a/src/components/ArmorMesh/components/ExternalModuleMask.tsx b/src/components/ArmorMesh/components/ExternalModuleMask.tsx index 02cba6c83..1d5aed6bb 100644 --- a/src/components/ArmorMesh/components/ExternalModuleMask.tsx +++ b/src/components/ArmorMesh/components/ExternalModuleMask.tsx @@ -1,53 +1,52 @@ -import { MeshProps } from '@react-three/fiber'; -import { Color, WebGLRenderTarget } from 'three'; +import { Color, MeshBasicMaterial, Object3D, WebGLRenderTarget } from 'three'; +import { jsxTree } from '../../../core/blitzkrieg/jsxTree'; import { ArmorMeshUserData } from './Armor'; export const externalModuleMaskRenderTarget = new WebGLRenderTarget(); -type ArmorMeshExternalModuleMaskProps = MeshProps & { +type ArmorMeshExternalModuleMaskProps = { ornamental?: boolean; + node: Object3D; } & ( - | { - exclude?: false; - maxThickness: number; - thickness: number; - } - | { - exclude: true; - } - ); + | { + exclude?: false; + maxThickness: number; + thickness: number; + } + | { + exclude: true; + } +); + +const excludeMaterial = new MeshBasicMaterial({ + colorWrite: false, +}); export function ArmorMeshExternalModuleMask({ ornamental = false, + node, ...props }: ArmorMeshExternalModuleMaskProps) { return ( <> - {props.exclude && !ornamental && ( - - - - )} + {props.exclude && + !ornamental && + jsxTree(node, { renderOrder: 0, material: excludeMaterial })} - {!props.exclude && ( - {} : undefined} - userData={ - { - type: 'externalModule', - thickness: props.thickness, - } satisfies ArmorMeshUserData - } - > - - - )} + {!props.exclude && + jsxTree(node, { + renderOrder: 1, + onClick: ornamental ? () => {} : undefined, + userData: { + type: 'externalModule', + thickness: props.thickness, + } satisfies ArmorMeshUserData, + material: new MeshBasicMaterial({ + color: new Color(1, props.thickness / props.maxThickness, 0), + depthWrite: false, + colorWrite: !ornamental, + }), + })} ); } diff --git a/src/core/blitzkrieg/jsxTree.tsx b/src/core/blitzkrieg/jsxTree.tsx new file mode 100644 index 000000000..ac2bf4364 --- /dev/null +++ b/src/core/blitzkrieg/jsxTree.tsx @@ -0,0 +1,29 @@ +import { MeshProps } from '@react-three/fiber'; +import { Mesh, Object3D } from 'three'; + +export function jsxTree(node: Object3D, mesh?: MeshProps) { + if (node instanceof Mesh) { + return ( + + ); + } else if (node instanceof Object3D) { + return ( + + {node.children.map((child) => jsxTree(child, mesh))} + + ); + } + + return null; +} diff --git a/src/core/blitzkrieg/resolveJsxTree.tsx b/src/core/blitzkrieg/resolveJsxTree.tsx deleted file mode 100644 index c4800d64d..000000000 --- a/src/core/blitzkrieg/resolveJsxTree.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Mesh, Object3D } from 'three'; - -export function resolveJsxTree(parent: Object3D) { - return parent.children.map((child) => { - if (child.type === 'Mesh') { - return ( - - ); - } - if (child.type === 'Object3D') { - return ( - - ); - } - - throw new TypeError( - `Unhandled three jsx type ${child.type} (${child.name})`, - ); - }); -}