feat: i tried my best to implement dynamic armor :(

This commit is contained in:
TrèsAbhi committed 2024-09-04 22:15:19 -05:00
1 parent 952523d3f9
commit 46f1461de6
9 files changed
+182 -92

No files matched your search

@@ -6,7 +6,6 @@ import {
useImperativeHandle, useImperativeHandle,
useRef, useRef,
} from 'react'; } from 'react';
import { Armor } from '../../../../../../components/Armor';
import { ArmorPlateDisplay } from '../../../../../../components/Armor/components/ArmorPlateDisplay'; import { ArmorPlateDisplay } from '../../../../../../components/Armor/components/ArmorPlateDisplay';
import { ShotDisplay } from '../../../../../../components/Armor/components/ShotDisplay'; import { ShotDisplay } from '../../../../../../components/Armor/components/ShotDisplay';
import { import {
@@ -185,7 +184,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
{/* idk why the shot display doesn't work without suspense here lol */} {/* idk why the shot display doesn't work without suspense here lol */}
<Suspense fallback={<ModelLoader />}> <Suspense fallback={<ModelLoader />}>
{display === TankopediaDisplay.DynamicArmor && <Armor />} {/* {display === TankopediaDisplay.DynamicArmor && <Armor />} */}
{display === TankopediaDisplay.StaticArmor && ( {display === TankopediaDisplay.StaticArmor && (
<StaticArmor thicknessRange={thicknessRange} /> <StaticArmor thicknessRange={thicknessRange} />
)} )}
@@ -31,6 +31,9 @@ export const TankModel = memo(() => {
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id]; const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
const { gltf } = useModel(protagonist.tank.id); const { gltf } = useModel(protagonist.tank.id);
const nodes = Object.values(gltf.nodes); const nodes = Object.values(gltf.nodes);
const isDynamicArmorActive = Duel.use((state) =>
state.protagonist.consumables.includes(73),
);
useTankTransform(protagonist, turretContainer, gunContainer); useTankTransform(protagonist, turretContainer, gunContainer);
@@ -100,9 +103,28 @@ export const TankModel = memo(() => {
return jsxTree( return jsxTree(
node, node,
{ {
castShadow: true, mesh: {
receiveShadow: true, castShadow: true,
onPointerDown, 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, node.uuid,
); );
@@ -178,9 +200,28 @@ export const TankModel = memo(() => {
return jsxTree( return jsxTree(
node, node,
{ {
castShadow: true, mesh: {
receiveShadow: true, castShadow: true,
onPointerDown, 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, node.uuid,
); );
@@ -258,9 +299,28 @@ export const TankModel = memo(() => {
return jsxTree( return jsxTree(
node, node,
{ {
castShadow: true, mesh: {
receiveShadow: true, castShadow: true,
onPointerDown, 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, node.uuid,
); );
@@ -156,10 +156,14 @@ export function PrimaryArmorSceneComponent({
<> <>
{jsxTree( {jsxTree(
node, node,
{ renderOrder: 0, material: excludeMaterial }, { mesh: { renderOrder: 0, material: excludeMaterial } },
`${node.uuid}-exclude`, `${node.uuid}-exclude`,
)} )}
{jsxTree(node, { renderOrder: 1, material }, `${node.uuid}-include`)} {jsxTree(
node,
{ mesh: { renderOrder: 1, material } },
`${node.uuid}-include`,
)}
</> </>
); );
} }
@@ -113,24 +113,30 @@ export function SpacedArmorSubExternal({
{jsxTree( {jsxTree(
node, node,
{ {
renderOrder: 3, mesh: {
material: new MeshBasicMaterial({ renderOrder: 3,
colorWrite: false, material: new MeshBasicMaterial({
depthTest: true, colorWrite: false,
depthWrite: true, depthTest: true,
...(clip ? { clippingPlanes: [clip] } : {}), depthWrite: true,
}), ...(clip ? { clippingPlanes: [clip] } : {}),
onClick() {}, }),
userData: { onClick() {},
type: ArmorType.External, userData: {
thickness, type: ArmorType.External,
variant, thickness,
} satisfies ArmorUserData, variant,
} satisfies ArmorUserData,
},
}, },
`${node.uuid}-exclude`, `${node.uuid}-exclude`,
)} )}
{jsxTree(node, { renderOrder: 4, material }, `${node.uuid}-include`)} {jsxTree(
node,
{ mesh: { renderOrder: 4, material } },
`${node.uuid}-include`,
)}
</> </>
); );
} }
@@ -123,13 +123,15 @@ export function SpacedArmorSubSpaced({
{jsxTree( {jsxTree(
node, node,
{ {
renderOrder: 2, mesh: {
material, renderOrder: 2,
onClick() {}, material,
userData: { onClick() {},
type: ArmorType.Spaced, userData: {
thickness, type: ArmorType.Spaced,
} satisfies ArmorUserData, thickness,
} satisfies ArmorUserData,
},
}, },
`${node.uuid}-exclude`, `${node.uuid}-exclude`,
)} )}
@@ -137,8 +139,10 @@ export function SpacedArmorSubSpaced({
{jsxTree( {jsxTree(
node, node,
{ {
renderOrder: 5, mesh: {
material: depthWriteMaterial, renderOrder: 5,
material: depthWriteMaterial,
},
}, },
`${node.uuid}-include`, `${node.uuid}-include`,
)} )}
@@ -371,20 +371,22 @@ export function SpacedArmorSceneComponent({
jsxTree( jsxTree(
node, node,
{ {
renderOrder: 0, mesh: {
material: omitMaterial, renderOrder: 0,
userData: { material: omitMaterial,
type: ArmorType.Primary, userData: {
thickness, type: ArmorType.Primary,
} satisfies ArmorUserData, thickness,
async onClick(event) { } satisfies ArmorUserData,
event.stopPropagation(); async onClick(event) {
const shot = (await shoot( event.stopPropagation();
event.point, const shot = (await shoot(
event.intersections, event.point,
true, event.intersections,
))!; true,
tankopediaEphemeralStore.setState({ shot }); ))!;
tankopediaEphemeralStore.setState({ shot });
},
}, },
}, },
node.uuid, node.uuid,
@@ -215,46 +215,50 @@ export function StaticArmorSceneComponent({
return ( return (
<> <>
{jsxTree(node, { {jsxTree(node, {
material: surfaceMaterial, mesh: {
onPointerDown, material: surfaceMaterial,
userData: { onPointerDown,
type: props.type, userData: {
variant: props.type === ArmorType.External ? props.variant : 'gun', type: props.type,
thickness, variant: props.type === ArmorType.External ? props.variant : 'gun',
} satisfies ArmorUserData, thickness,
} satisfies ArmorUserData,
onClick(event) { onClick(event) {
if (discardClippingPlane(event.object, event.point)) return; 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 bounds = new Box3().setFromObject(event.object);
const point = bounds.min const point = bounds.min
.clone() .clone()
.add(bounds.max) .add(bounds.max)
.divideScalar(2) .divideScalar(2)
.setY(bounds.max.y); .setY(bounds.max.y);
const cameraNormal = camera.position.clone().sub(point).normalize(); const cameraNormal = camera.position.clone().sub(point).normalize();
const surfaceNormal = event const surfaceNormal = event
.normal!.clone() .normal!.clone()
.applyQuaternion(event.object.getWorldQuaternion(new Quaternion())); .applyQuaternion(
const angle = surfaceNormal.angleTo(cameraNormal); event.object.getWorldQuaternion(new Quaternion()),
const thicknessAngled = thickness / Math.sin(Math.PI / 2 - angle); );
const angle = surfaceNormal.angleTo(cameraNormal);
const thicknessAngled = thickness / Math.sin(Math.PI / 2 - angle);
mutateTankopediaEphemeralStore((draft) => { mutateTankopediaEphemeralStore((draft) => {
draft.highlightArmor = { draft.highlightArmor = {
type: props.type, type: props.type,
name, name,
point, point,
thickness, thickness,
thicknessAngled, thicknessAngled,
angle, angle,
color: `#${color.getHexString()}`, color: `#${color.getHexString()}`,
editingPlate: editStatic, editingPlate: editStatic,
}; };
}); });
},
}, },
})} })}
+18 -4
View File
@@ -1,7 +1,14 @@
import { MeshProps } from '@react-three/fiber'; import { GroupProps, MeshProps } from '@react-three/fiber';
import { Mesh, Object3D } from 'three'; 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) { if (node instanceof Mesh) {
return ( return (
<mesh <mesh
@@ -11,7 +18,10 @@ export function jsxTree(node: Object3D, mesh?: MeshProps, key?: string) {
position={node.position} position={node.position}
rotation={node.rotation} rotation={node.rotation}
scale={node.scale} scale={node.scale}
{...mesh} {...(mergers?.mesh &&
(typeof mergers.mesh === 'function'
? mergers.mesh(node)
: mergers.mesh))}
/> />
); );
} else if (node instanceof Object3D) { } else if (node instanceof Object3D) {
@@ -21,8 +31,12 @@ export function jsxTree(node: Object3D, mesh?: MeshProps, key?: string) {
position={node.position} position={node.position}
rotation={node.rotation} rotation={node.rotation}
scale={node.scale} 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))}
</group> </group>
); );
} }
-3
View File
@@ -9,9 +9,6 @@ const cache: Record<
export function useArmor(id: number) { export function useArmor(id: number) {
const gltf = useLoader(GLTFLoader, asset(`3d/tanks/armor/${id}.glb`)); const gltf = useLoader(GLTFLoader, asset(`3d/tanks/armor/${id}.glb`));
console.log(Object.values(gltf.nodes).map((node) => node.name));
if (!cache[id]) { if (!cache[id]) {
cache[id] = { cache[id] = {
gltf, gltf,