mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: tank armor position stateful
This commit is contained in:
1 parent
9e0c4ddb9a
commit
6239a72bce
7 files changed
+140
-77
No files matched your search
@@ -28,8 +28,8 @@ export async function buildTankArmors() {
|
||||
const tank = tanks.root[tankKey];
|
||||
const id = toUniqueId(nation, tank.id);
|
||||
|
||||
if (id !== 7297) continue; // 60tp
|
||||
// if (id !== 5137) continue; // tiger ii
|
||||
// if (id !== 7297) continue; // 60tp
|
||||
if (id !== 5137) continue; // tiger ii
|
||||
// if (id !== 24609) continue; // concept 1b
|
||||
|
||||
console.log(`Building armor ${id} @ ${nation}/${tankKey}`);
|
||||
|
||||
@@ -49,10 +49,10 @@ export async function buildTankModels() {
|
||||
// if (id !== 7425) continue; // isu 152
|
||||
// if (id !== 10369) continue; // mino
|
||||
// if (id !== 4417) continue; // amx m4 mle
|
||||
if (id !== 7297) continue; // 60tp
|
||||
// if (id !== 7297) continue; // 60tp
|
||||
// if (id !== 1) continue; // t-34
|
||||
// if (id !== 6753) continue; // type 71
|
||||
// if (id !== 5137) continue; // tiger ii
|
||||
if (id !== 5137) continue; // tiger ii
|
||||
// if (id !== 11633) continue; // forest witch
|
||||
// if (id !== 6225) continue; // fv215b
|
||||
// if (id !== 4481) continue; // kran
|
||||
|
||||
@@ -7,7 +7,6 @@ import { Button, Card, Flex, Tabs, Theme } from '@radix-ui/themes';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { Canvas } from '@react-three/fiber';
|
||||
import { Suspense, use, useEffect, useRef, useState } from 'react';
|
||||
import { Group } from 'three';
|
||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions';
|
||||
import { theme } from '../../../../../../stitches.config';
|
||||
@@ -29,7 +28,6 @@ export function TankDisplay() {
|
||||
const canvasWrapper = useRef<HTMLDivElement>(null);
|
||||
const [isFullScreen, setIsFullScreen] = useState(false);
|
||||
const mode = useTankopedia((state) => state.mode);
|
||||
const hullContainer = useRef<Group>(null);
|
||||
const showGrid = useTankopedia((state) => state.model.showGrid);
|
||||
const protagonist = useTankopedia((state) => {
|
||||
if (!state.areTanksAssigned) return;
|
||||
@@ -134,8 +132,8 @@ export function TankDisplay() {
|
||||
</Html>
|
||||
}
|
||||
>
|
||||
<TankModel ref={hullContainer} />
|
||||
<TankArmor />
|
||||
<TankModel />
|
||||
{mode === 'armor' && <TankArmor />}
|
||||
</Suspense>
|
||||
</Canvas>
|
||||
</div>
|
||||
|
||||
@@ -1,65 +1,140 @@
|
||||
import { forwardRef } from 'react';
|
||||
import { Group } from 'three';
|
||||
import { useLoader } from '@react-three/fiber';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Euler, Mesh, Vector3 } from 'three';
|
||||
import { GLTFLoader } from 'three-stdlib';
|
||||
import { ArmorMesh } from '../../../../../../../components/ArmorMesh';
|
||||
import { HeadsUpDisplay } from '../../../../../../../components/HeadsUpDisplay';
|
||||
import { X_AXIS } from '../../../../../../../constants/axis';
|
||||
import { asset } from '../../../../../../../core/blitzkrieg/asset';
|
||||
import {
|
||||
ModelDefinitions,
|
||||
modelDefinitions,
|
||||
} from '../../../../../../../core/blitzkrieg/modelDefinitions';
|
||||
import { useTankopedia } from '../../../../../../../stores/tankopedia';
|
||||
|
||||
interface TankArmorProps {}
|
||||
|
||||
export const TankArmor = forwardRef<Group, TankArmorProps>((_props, ref) => {
|
||||
export function TankArmor() {
|
||||
const [awaitedModelDefinitions, setAwaitedModelDefinitions] = useState<
|
||||
ModelDefinitions | undefined
|
||||
>(undefined);
|
||||
const protagonist = useTankopedia((state) => {
|
||||
if (!state.areTanksAssigned) return;
|
||||
return state.protagonist;
|
||||
});
|
||||
|
||||
if (!protagonist) return null;
|
||||
|
||||
const gltf = useLoader(
|
||||
GLTFLoader,
|
||||
asset(`3d/tanks/armor/${protagonist.tank.id}.glb`),
|
||||
);
|
||||
const model = useTankopedia((state) => state.model);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setAwaitedModelDefinitions(await modelDefinitions);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
if (!awaitedModelDefinitions) return null;
|
||||
|
||||
const nodes = Object.values(gltf.nodes);
|
||||
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
|
||||
const turretModelDefinition =
|
||||
tankModelDefinition.turrets[protagonist.turret.id];
|
||||
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||
const turretOrigin = new Vector3(
|
||||
tankModelDefinition.turretOrigin[0],
|
||||
tankModelDefinition.turretOrigin[1],
|
||||
-tankModelDefinition.turretOrigin[2],
|
||||
).applyAxisAngle(X_AXIS, Math.PI / 2);
|
||||
const gunOrigin = new Vector3(
|
||||
turretModelDefinition.gunOrigin[0],
|
||||
turretModelDefinition.gunOrigin[1],
|
||||
-turretModelDefinition.gunOrigin[2],
|
||||
).applyAxisAngle(X_AXIS, Math.PI / 2);
|
||||
const turretPosition = new Vector3()
|
||||
.sub(turretOrigin)
|
||||
.applyAxisAngle(new Vector3(0, 0, 1), model.turretYaw);
|
||||
const turretRotation = new Euler(0, 0, model.turretYaw);
|
||||
|
||||
if (tankModelDefinition.turretRotation) {
|
||||
const pitch = -tankModelDefinition.turretRotation.pitch * (Math.PI / 180);
|
||||
const yaw = -tankModelDefinition.turretRotation.yaw * (Math.PI / 180);
|
||||
const roll = -tankModelDefinition.turretRotation.roll * (Math.PI / 180);
|
||||
|
||||
turretPosition
|
||||
.applyAxisAngle(new Vector3(1, 0, 0), pitch)
|
||||
.applyAxisAngle(new Vector3(0, 1, 0), roll)
|
||||
.applyAxisAngle(new Vector3(0, 0, 1), yaw);
|
||||
turretRotation.x += pitch;
|
||||
turretRotation.y += roll;
|
||||
turretRotation.z += yaw;
|
||||
}
|
||||
|
||||
turretPosition.add(turretOrigin);
|
||||
|
||||
return (
|
||||
<HeadsUpDisplay>
|
||||
<mesh>
|
||||
<boxGeometry />
|
||||
<meshBasicMaterial color="red" />
|
||||
</mesh>
|
||||
|
||||
{/* {armorObjects.map((object) => {
|
||||
const isHull = object.name.startsWith('hull_');
|
||||
const isVisible = isHull;
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<ArmorMesh key={object.uuid} geometry={(object as Mesh).geometry} />
|
||||
);
|
||||
})} */}
|
||||
|
||||
{/* {armorObjects.map((object) => {
|
||||
const isCurrentTurret = object.name.startsWith(
|
||||
`turret_${model.toString().padStart(2, '0')}`,
|
||||
);
|
||||
const isVisible = isCurrentTurret;
|
||||
<group rotation={[-Math.PI / 2, 0, model.hullYaw]}>
|
||||
{nodes.map((node) => {
|
||||
const isHull = node.name.startsWith('hull_');
|
||||
const isVisible = isHull;
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<mesh
|
||||
position={turretOrigin}
|
||||
key={object.uuid}
|
||||
geometry={(object as Mesh).geometry}
|
||||
>
|
||||
<ArmorMesh />
|
||||
</mesh>
|
||||
<ArmorMesh key={node.uuid} geometry={(node as Mesh).geometry} />
|
||||
);
|
||||
})} */}
|
||||
})}
|
||||
|
||||
{/* {armorObjects.map((object) => {
|
||||
const isCurrentGun = object.name.startsWith(
|
||||
`gun_${model.toString().padStart(2, '0')}`,
|
||||
);
|
||||
const isVisible = isCurrentGun;
|
||||
<group position={turretPosition} rotation={turretRotation}>
|
||||
{nodes.map((node) => {
|
||||
const isCurrentTurret = node.name.startsWith(
|
||||
`turret_${turretModelDefinition.model
|
||||
.toString()
|
||||
.padStart(2, '0')}`,
|
||||
);
|
||||
const isVisible = isCurrentTurret;
|
||||
|
||||
if (!isVisible) return null;
|
||||
if (!isVisible) return null;
|
||||
return (
|
||||
<ArmorMesh
|
||||
key={node.uuid}
|
||||
position={turretOrigin}
|
||||
geometry={(node as Mesh).geometry}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
return (
|
||||
<mesh
|
||||
position={turretOrigin.clone().add(gunOrigin)}
|
||||
key={object.uuid}
|
||||
geometry={(object as Mesh).geometry}
|
||||
>
|
||||
<ArmorHighlightMaterial />
|
||||
</mesh>
|
||||
);
|
||||
})} */}
|
||||
<group
|
||||
position={new Vector3()
|
||||
.sub(turretOrigin)
|
||||
.sub(gunOrigin)
|
||||
.applyAxisAngle(new Vector3(1, 0, 0), model.gunPitch)
|
||||
.add(turretOrigin)
|
||||
.add(gunOrigin)}
|
||||
rotation={[model.gunPitch, 0, 0]}
|
||||
>
|
||||
{nodes.map((node) => {
|
||||
const isCurrentGun = node.name.startsWith(
|
||||
`gun_${gunModelDefinition.model.toString().padStart(2, '0')}`,
|
||||
);
|
||||
const isVisible = isCurrentGun;
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<ArmorMesh
|
||||
key={node.uuid}
|
||||
position={turretOrigin.clone().add(gunOrigin)}
|
||||
geometry={(node as Mesh).geometry}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
</HeadsUpDisplay>
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1,11 +1,5 @@
|
||||
import { ThreeEvent, useLoader, useThree } from '@react-three/fiber';
|
||||
import {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Euler,
|
||||
Group,
|
||||
@@ -28,35 +22,30 @@ import mutateTankopedia, {
|
||||
useTankopedia,
|
||||
} from '../../../../../../../stores/tankopedia';
|
||||
|
||||
export const TankModel = forwardRef<Group>((_props, ref) => {
|
||||
export function TankModel() {
|
||||
// "TypeError: dispatcher.use is not a function"
|
||||
// const awaitedModelDefinitions = use(modelDefinitions);
|
||||
const [awaitedModelDefinitions, setAwaitedModelDefinitions] = useState<
|
||||
ModelDefinitions | undefined
|
||||
>(undefined);
|
||||
const canvas = useThree((state) => state.gl.domElement);
|
||||
const protagonist = useTankopedia((state) => {
|
||||
if (!state.areTanksAssigned) return;
|
||||
return state.protagonist;
|
||||
});
|
||||
const canvas = useThree((state) => state.gl.domElement);
|
||||
|
||||
if (!protagonist) return null;
|
||||
|
||||
const modelGltf = useLoader(
|
||||
const gltf = useLoader(
|
||||
GLTFLoader,
|
||||
asset(`3d/tanks/models/${protagonist.tank.id}.glb`),
|
||||
);
|
||||
const armorGltf = useLoader(
|
||||
GLTFLoader,
|
||||
asset(`3d/tanks/armor/${protagonist.tank.id}.glb`),
|
||||
);
|
||||
const model = useTankopedia((state) => state.model);
|
||||
const hullContainer = useRef<Group>(null);
|
||||
const turretContainer = useRef<Group>(null);
|
||||
const gunContainer = useRef<Group>(null);
|
||||
const nodes = Object.values(modelGltf.nodes);
|
||||
const nodes = Object.values(gltf.nodes);
|
||||
|
||||
useImperativeHandle(ref, () => hullContainer.current!);
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setAwaitedModelDefinitions(await modelDefinitions);
|
||||
@@ -411,4 +400,4 @@ export const TankModel = forwardRef<Group>((_props, ref) => {
|
||||
</group>
|
||||
</group>
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -24,7 +24,7 @@ export default function Page({ params }: { params: { id: string } }) {
|
||||
draft.model.hullYaw = 0;
|
||||
draft.model.turretYaw = 0;
|
||||
draft.model.gunPitch = 0;
|
||||
draft.mode = 'model';
|
||||
draft.mode = 'armor';
|
||||
draft.areTanksAssigned = true;
|
||||
|
||||
if (!draft.areTanksAssigned) return;
|
||||
|
||||
@@ -12,7 +12,8 @@ export function ArmorMesh(props: ArmorMeshProps) {
|
||||
<mesh {...props} renderOrder={1}>
|
||||
<meshBasicMaterial
|
||||
transparent
|
||||
opacity={Math.random() / 2}
|
||||
// opacity={Math.random() / 2}
|
||||
opacity={0.25}
|
||||
color="red"
|
||||
depthWrite={false}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user