From 46f1461de6f3481312bbc6ee692c9065659702d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Wed, 4 Sep 2024 22:15:19 -0500 Subject: [PATCH] feat: i tried my best to implement dynamic armor :( --- .../[id]/components/Model/TankSandbox.tsx | 3 +- .../components/Model/components/TankModel.tsx | 78 ++++++++++++++++--- .../PrimaryArmorSceneComponent/index.tsx | 8 +- .../SpacedArmorSubExternal/index.tsx | 34 ++++---- .../components/SpacedArmorSubSpaced/index.tsx | 22 +++--- .../SpacedArmorSceneComponent/index.tsx | 30 +++---- .../components/StaticArmorSceneComponent.tsx | 74 +++++++++--------- src/core/blitzkit/jsxTree.tsx | 22 +++++- src/hooks/useArmor.ts | 3 - 9 files changed, 182 insertions(+), 92 deletions(-) diff --git a/src/app/tools/tankopedia/[id]/components/Model/TankSandbox.tsx b/src/app/tools/tankopedia/[id]/components/Model/TankSandbox.tsx index 9883c309b..81aadabdc 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/TankSandbox.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/TankSandbox.tsx @@ -6,7 +6,6 @@ import { useImperativeHandle, useRef, } from 'react'; -import { Armor } from '../../../../../../components/Armor'; import { ArmorPlateDisplay } from '../../../../../../components/Armor/components/ArmorPlateDisplay'; import { ShotDisplay } from '../../../../../../components/Armor/components/ShotDisplay'; import { @@ -185,7 +184,7 @@ export const TankSandbox = forwardRef( {/* idk why the shot display doesn't work without suspense here lol */} }> - {display === TankopediaDisplay.DynamicArmor && } + {/* {display === TankopediaDisplay.DynamicArmor && } */} {display === TankopediaDisplay.StaticArmor && ( )} 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 583852a1f..8d2ee93a6 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx @@ -31,6 +31,9 @@ export const TankModel = memo(() => { const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id]; const { gltf } = useModel(protagonist.tank.id); const nodes = Object.values(gltf.nodes); + const isDynamicArmorActive = Duel.use((state) => + state.protagonist.consumables.includes(73), + ); useTankTransform(protagonist, turretContainer, gunContainer); @@ -100,9 +103,28 @@ export const TankModel = memo(() => { return jsxTree( node, { - castShadow: true, - receiveShadow: true, - onPointerDown, + mesh: { + castShadow: true, + receiveShadow: true, + onPointerDown, + }, + // group(group) { + // console.log(group.name); + // return { + // visible: + // group.name !== 'ST_base' && + // group.name !== 'MR_Base2' && + // (!group.name.includes('state_entity') || + // (isDynamicArmorActive && + // group.name.includes('state_entity_01')) || + // (!isDynamicArmorActive && + // group.name.includes('state_entity_00'))) && + // (!group.name.includes('shields') || + // (isDynamicArmorActive && + // group.name.includes('shields_rev')) || + // (!isDynamicArmorActive && group.name.endsWith('shields'))), + // }; + // }, }, node.uuid, ); @@ -178,9 +200,28 @@ export const TankModel = memo(() => { return jsxTree( node, { - castShadow: true, - receiveShadow: true, - onPointerDown, + mesh: { + castShadow: true, + receiveShadow: true, + onPointerDown, + }, + // group(group) { + // return { + // visible: + // group.name !== 'ST_base' && + // group.name !== 'MR_Base2' && + // (!group.name.includes('state_entity') || + // (isDynamicArmorActive && + // group.name.includes('state_entity_01')) || + // (!isDynamicArmorActive && + // group.name.includes('state_entity_00'))) && + // (!group.name.includes('shields') || + // (isDynamicArmorActive && + // group.name.includes('shields_rev')) || + // (!isDynamicArmorActive && + // group.name.endsWith('shields'))), + // }; + // }, }, node.uuid, ); @@ -258,9 +299,28 @@ export const TankModel = memo(() => { return jsxTree( node, { - castShadow: true, - receiveShadow: true, - onPointerDown, + mesh: { + castShadow: true, + receiveShadow: true, + onPointerDown, + }, + // group(group) { + // return { + // visible: + // group.name !== 'ST_base' && + // group.name !== 'MR_Base2' && + // (!group.name.includes('state_entity') || + // (isDynamicArmorActive && + // group.name.includes('state_entity_01')) || + // (!isDynamicArmorActive && + // group.name.includes('state_entity_00'))) && + // (!group.name.includes('shields') || + // (isDynamicArmorActive && + // group.name.includes('shields_rev')) || + // (!isDynamicArmorActive && + // group.name.endsWith('shields'))), + // }; + // }, }, node.uuid, ); diff --git a/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx b/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx index f33918b41..8f7e41741 100644 --- a/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx +++ b/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx @@ -156,10 +156,14 @@ export function PrimaryArmorSceneComponent({ <> {jsxTree( node, - { renderOrder: 0, material: excludeMaterial }, + { mesh: { renderOrder: 0, material: excludeMaterial } }, `${node.uuid}-exclude`, )} - {jsxTree(node, { renderOrder: 1, material }, `${node.uuid}-include`)} + {jsxTree( + node, + { mesh: { renderOrder: 1, material } }, + `${node.uuid}-include`, + )} ); } diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx index 80016de06..56e780db3 100644 --- a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx +++ b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx @@ -113,24 +113,30 @@ export function SpacedArmorSubExternal({ {jsxTree( node, { - renderOrder: 3, - material: new MeshBasicMaterial({ - colorWrite: false, - depthTest: true, - depthWrite: true, - ...(clip ? { clippingPlanes: [clip] } : {}), - }), - onClick() {}, - userData: { - type: ArmorType.External, - thickness, - variant, - } satisfies ArmorUserData, + mesh: { + renderOrder: 3, + material: new MeshBasicMaterial({ + colorWrite: false, + depthTest: true, + depthWrite: true, + ...(clip ? { clippingPlanes: [clip] } : {}), + }), + onClick() {}, + userData: { + type: ArmorType.External, + thickness, + variant, + } satisfies ArmorUserData, + }, }, `${node.uuid}-exclude`, )} - {jsxTree(node, { renderOrder: 4, material }, `${node.uuid}-include`)} + {jsxTree( + node, + { mesh: { renderOrder: 4, material } }, + `${node.uuid}-include`, + )} ); } diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx index 9ca10428c..ed37c98b7 100644 --- a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx +++ b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx @@ -123,13 +123,15 @@ export function SpacedArmorSubSpaced({ {jsxTree( node, { - renderOrder: 2, - material, - onClick() {}, - userData: { - type: ArmorType.Spaced, - thickness, - } satisfies ArmorUserData, + mesh: { + renderOrder: 2, + material, + onClick() {}, + userData: { + type: ArmorType.Spaced, + thickness, + } satisfies ArmorUserData, + }, }, `${node.uuid}-exclude`, )} @@ -137,8 +139,10 @@ export function SpacedArmorSubSpaced({ {jsxTree( node, { - renderOrder: 5, - material: depthWriteMaterial, + mesh: { + renderOrder: 5, + material: depthWriteMaterial, + }, }, `${node.uuid}-include`, )} diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx index 0b5076ee5..29ad3d48d 100644 --- a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx +++ b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx @@ -371,20 +371,22 @@ export function SpacedArmorSceneComponent({ jsxTree( node, { - renderOrder: 0, - material: omitMaterial, - userData: { - type: ArmorType.Primary, - thickness, - } satisfies ArmorUserData, - async onClick(event) { - event.stopPropagation(); - const shot = (await shoot( - event.point, - event.intersections, - true, - ))!; - tankopediaEphemeralStore.setState({ shot }); + mesh: { + renderOrder: 0, + material: omitMaterial, + userData: { + type: ArmorType.Primary, + thickness, + } satisfies ArmorUserData, + async onClick(event) { + event.stopPropagation(); + const shot = (await shoot( + event.point, + event.intersections, + true, + ))!; + tankopediaEphemeralStore.setState({ shot }); + }, }, }, node.uuid, diff --git a/src/components/Armor/components/StaticArmorSceneComponent.tsx b/src/components/Armor/components/StaticArmorSceneComponent.tsx index ff60bfc44..6761a3c53 100644 --- a/src/components/Armor/components/StaticArmorSceneComponent.tsx +++ b/src/components/Armor/components/StaticArmorSceneComponent.tsx @@ -215,46 +215,50 @@ export function StaticArmorSceneComponent({ return ( <> {jsxTree(node, { - material: surfaceMaterial, - onPointerDown, - userData: { - type: props.type, - variant: props.type === ArmorType.External ? props.variant : 'gun', - thickness, - } satisfies ArmorUserData, + mesh: { + material: surfaceMaterial, + onPointerDown, + userData: { + type: props.type, + variant: props.type === ArmorType.External ? props.variant : 'gun', + thickness, + } satisfies ArmorUserData, - onClick(event) { - if (discardClippingPlane(event.object, event.point)) return; + onClick(event) { + if (discardClippingPlane(event.object, event.point)) return; - event.stopPropagation(); + event.stopPropagation(); - const { editStatic } = tankopediaEphemeralStore.getState(); + const { editStatic } = tankopediaEphemeralStore.getState(); - const bounds = new Box3().setFromObject(event.object); - const point = bounds.min - .clone() - .add(bounds.max) - .divideScalar(2) - .setY(bounds.max.y); - const cameraNormal = camera.position.clone().sub(point).normalize(); - const surfaceNormal = event - .normal!.clone() - .applyQuaternion(event.object.getWorldQuaternion(new Quaternion())); - const angle = surfaceNormal.angleTo(cameraNormal); - const thicknessAngled = thickness / Math.sin(Math.PI / 2 - angle); + const bounds = new Box3().setFromObject(event.object); + const point = bounds.min + .clone() + .add(bounds.max) + .divideScalar(2) + .setY(bounds.max.y); + const cameraNormal = camera.position.clone().sub(point).normalize(); + const surfaceNormal = event + .normal!.clone() + .applyQuaternion( + event.object.getWorldQuaternion(new Quaternion()), + ); + const angle = surfaceNormal.angleTo(cameraNormal); + const thicknessAngled = thickness / Math.sin(Math.PI / 2 - angle); - mutateTankopediaEphemeralStore((draft) => { - draft.highlightArmor = { - type: props.type, - name, - point, - thickness, - thicknessAngled, - angle, - color: `#${color.getHexString()}`, - editingPlate: editStatic, - }; - }); + mutateTankopediaEphemeralStore((draft) => { + draft.highlightArmor = { + type: props.type, + name, + point, + thickness, + thicknessAngled, + angle, + color: `#${color.getHexString()}`, + editingPlate: editStatic, + }; + }); + }, }, })} diff --git a/src/core/blitzkit/jsxTree.tsx b/src/core/blitzkit/jsxTree.tsx index 044c4a771..1b05e4c4c 100644 --- a/src/core/blitzkit/jsxTree.tsx +++ b/src/core/blitzkit/jsxTree.tsx @@ -1,7 +1,14 @@ -import { MeshProps } from '@react-three/fiber'; +import { GroupProps, MeshProps } from '@react-three/fiber'; import { Mesh, Object3D } from 'three'; -export function jsxTree(node: Object3D, mesh?: MeshProps, key?: string) { +export function jsxTree( + node: Object3D, + mergers?: { + mesh?: MeshProps | ((mesh: Mesh) => MeshProps); + group?: GroupProps | ((object3d: Object3D) => GroupProps); + }, + key?: string, +) { if (node instanceof Mesh) { return ( ); } else if (node instanceof Object3D) { @@ -21,8 +31,12 @@ export function jsxTree(node: Object3D, mesh?: MeshProps, key?: string) { position={node.position} rotation={node.rotation} scale={node.scale} + {...(mergers?.group && + (typeof mergers.group === 'function' + ? mergers.group(node) + : mergers.group))} > - {node.children.map((child) => jsxTree(child, mesh, child.uuid))} + {node.children.map((child) => jsxTree(child, mergers, child.uuid))} ); } diff --git a/src/hooks/useArmor.ts b/src/hooks/useArmor.ts index da4ccc870..31bc8e6ba 100644 --- a/src/hooks/useArmor.ts +++ b/src/hooks/useArmor.ts @@ -9,9 +9,6 @@ const cache: Record< export function useArmor(id: number) { const gltf = useLoader(GLTFLoader, asset(`3d/tanks/armor/${id}.glb`)); - - console.log(Object.values(gltf.nodes).map((node) => node.name)); - if (!cache[id]) { cache[id] = { gltf,