feat: hull origins

This commit is contained in:
TrèsAbhi committed 2024-01-22 16:54:07 -06:00
1 parent 3e12d6bb9d
commit 5295aa37be
14 files changed
+225 -259

No files matched your search

+9 -2
View File
@@ -65,6 +65,7 @@ interface VehicleDefinitions {
}; };
chassis: { chassis: {
[key: string]: { [key: string]: {
hullPosition: string;
armor: { armor: {
leftTrack: leftTrack:
| number | number
@@ -294,14 +295,19 @@ export async function buildDefinitions(production: boolean) {
const tankDefinition = await readXMLDVPL<{ root: VehicleDefinitions }>( const tankDefinition = await readXMLDVPL<{ root: VehicleDefinitions }>(
`${DATA}/${POI.vehicleDefinitions}/${nation}/${tankKey}.xml.dvpl`, `${DATA}/${POI.vehicleDefinitions}/${nation}/${tankKey}.xml.dvpl`,
); );
const defaultChassis = Object.values(tankDefinition.root.chassis).at(
-1,
)!;
const turretOrigin = tankDefinition.root.hull.turretPositions.turret const turretOrigin = tankDefinition.root.hull.turretPositions.turret
.split(' ') .split(' ')
.map(Number) as Vector3Tuple; .map(Number) as Vector3Tuple;
const hullOrigin = defaultChassis.hullPosition
.split(' ')
.map(Number) as Vector3Tuple;
const tankId = toUniqueId(nation, tank.id); const tankId = toUniqueId(nation, tank.id);
const tankTags = tank.tags.split(' '); const tankTags = tank.tags.split(' ');
const hullArmor: ModelArmor = { thickness: {} }; const hullArmor: ModelArmor = { thickness: {} };
const trackArmorRaw = Object.values(tankDefinition.root.chassis).at(-1)! const trackArmorRaw = defaultChassis.armor.leftTrack;
.armor.leftTrack;
const equipment = tankDefinition.root.optDevicePreset; const equipment = tankDefinition.root.optDevicePreset;
tankStringIdMap[`${nation}:${tankKey}`] = tankId; tankStringIdMap[`${nation}:${tankKey}`] = tankId;
@@ -368,6 +374,7 @@ export async function buildDefinitions(production: boolean) {
? trackArmorRaw ? trackArmorRaw
: trackArmorRaw['#text'], : trackArmorRaw['#text'],
turretOrigin, turretOrigin,
hullOrigin,
turretRotation: tankDefinition.root.hull.turretInitialRotation turretRotation: tankDefinition.root.hull.turretInitialRotation
? { ? {
yaw: tankDefinition.root.hull.turretInitialRotation.yaw, yaw: tankDefinition.root.hull.turretInitialRotation.yaw,
@@ -7,7 +7,7 @@ import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimi
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions'; import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
import { Pose, poseEvent } from '../../../../../core/blitzkrieg/pose'; import { Pose, poseEvent } from '../../../../../core/blitzkrieg/pose';
import { useAwait } from '../../../../../hooks/useAwait'; import { useAwait } from '../../../../../hooks/useAwait';
import { useTankopediaTemporary } from '../../../../../stores/tankopedia'; import { useTankopediaPersistent } from '../../../../../stores/tankopedia';
import { Duel } from '../page'; import { Duel } from '../page';
const poseDistances: Record<Pose, number> = { const poseDistances: Record<Pose, number> = {
@@ -34,6 +34,11 @@ export function Controls({ duel }: ControlsProps) {
antagonistModelDefinition.turrets[duel.antagonist.turret.id]; antagonistModelDefinition.turrets[duel.antagonist.turret.id];
const protagonistGunModelDefinition = const protagonistGunModelDefinition =
protagonistTurretModelDefinition.guns[duel.protagonist.gun.id]; protagonistTurretModelDefinition.guns[duel.protagonist.gun.id];
const protagonistHullOrigin = new Vector3(
protagonistModelDefinition.hullOrigin[0],
protagonistModelDefinition.hullOrigin[1],
-protagonistModelDefinition.hullOrigin[2],
);
const protagonistTurretOrigin = new Vector3( const protagonistTurretOrigin = new Vector3(
protagonistModelDefinition.turretOrigin[0], protagonistModelDefinition.turretOrigin[0],
protagonistModelDefinition.turretOrigin[1], protagonistModelDefinition.turretOrigin[1],
@@ -45,6 +50,7 @@ export function Controls({ duel }: ControlsProps) {
-protagonistTurretModelDefinition.gunOrigin[2], -protagonistTurretModelDefinition.gunOrigin[2],
); );
const antagonistGunHeight = const antagonistGunHeight =
antagonistModelDefinition.hullOrigin[1] +
antagonistModelDefinition.turretOrigin[1] + antagonistModelDefinition.turretOrigin[1] +
antagonistTurretModelDefinition.gunOrigin[1]; antagonistTurretModelDefinition.gunOrigin[1];
@@ -52,7 +58,7 @@ export function Controls({ duel }: ControlsProps) {
camera.position.set(-4, 4, -16); camera.position.set(-4, 4, -16);
orbitControls.current?.target.set(0, antagonistGunHeight / 2, 0); orbitControls.current?.target.set(0, antagonistGunHeight / 2, 0);
const unsubscribeTankopedia = useTankopediaTemporary.subscribe( const unsubscribeTankopedia = useTankopediaPersistent.subscribe(
(state) => state.model.visual.controlsEnabled, (state) => state.model.visual.controlsEnabled,
(enabled) => { (enabled) => {
if (orbitControls.current) orbitControls.current.enabled = enabled; if (orbitControls.current) orbitControls.current.enabled = enabled;
@@ -71,6 +77,7 @@ export function Controls({ duel }: ControlsProps) {
camera.position camera.position
.set(0, 0, 0) .set(0, 0, 0)
.add(protagonistHullOrigin)
.add(protagonistTurretOrigin) .add(protagonistTurretOrigin)
.add(protagonistGunOrigin) .add(protagonistGunOrigin)
.add( .add(
@@ -81,7 +88,10 @@ export function Controls({ duel }: ControlsProps) {
), ),
); );
camera.lookAt( camera.lookAt(
protagonistTurretOrigin.clone().add(protagonistGunOrigin), protagonistHullOrigin
.clone()
.add(protagonistTurretOrigin)
.add(protagonistGunOrigin),
); );
orbitControls.current?.target.set(0, antagonistGunHeight, 0); orbitControls.current?.target.set(0, antagonistGunHeight, 0);
@@ -98,6 +108,7 @@ export function Controls({ duel }: ControlsProps) {
camera.position camera.position
.set(0, 0, 0) .set(0, 0, 0)
.add(protagonistHullOrigin)
.add(protagonistTurretOrigin) .add(protagonistTurretOrigin)
.add(protagonistGunOrigin) .add(protagonistGunOrigin)
.add( .add(
@@ -109,6 +120,7 @@ export function Controls({ duel }: ControlsProps) {
); );
orbitControls.current?.target orbitControls.current?.target
.set(0, 0, 0) .set(0, 0, 0)
.add(protagonistHullOrigin)
.add(protagonistTurretOrigin) .add(protagonistTurretOrigin)
.add(protagonistGunOrigin) .add(protagonistGunOrigin)
.add( .add(
@@ -141,7 +153,7 @@ export function Controls({ duel }: ControlsProps) {
return ( return (
<OrbitControls <OrbitControls
ref={orbitControls} ref={orbitControls}
enabled={useTankopediaTemporary.getState().model.visual.controlsEnabled} enabled={useTankopediaPersistent.getState().model.visual.controlsEnabled}
rotateSpeed={0.25} rotateSpeed={0.25}
enableDamping={false} enableDamping={false}
/> />
@@ -165,6 +165,7 @@ export function TankSandbox({ duel }: TankSandboxProps) {
<Lighting duel={duel} /> <Lighting duel={duel} />
<TankModel duel={duel} /> <TankModel duel={duel} />
<TankArmor duel={duel} /> <TankArmor duel={duel} />
{/* <SpacedArmorDepth duel={duel} /> */}
</Suspense> </Suspense>
</Canvas> </Canvas>
</div> </div>
@@ -7,9 +7,9 @@ import {
import { Button, DropdownMenu, Flex } from '@radix-ui/themes'; import { Button, DropdownMenu, Flex } from '@radix-ui/themes';
import { RefObject } from 'react'; import { RefObject } from 'react';
import { Pose, poseEvent } from '../../../../../../../core/blitzkrieg/pose'; import { Pose, poseEvent } from '../../../../../../../core/blitzkrieg/pose';
import { import mutateTankopediaPersistent, {
mutateTankopediaTemporary, mutateTankopediaTemporary,
useTankopediaTemporary, useTankopediaPersistent,
} from '../../../../../../../stores/tankopedia'; } from '../../../../../../../stores/tankopedia';
interface OptionsProps { interface OptionsProps {
@@ -18,10 +18,10 @@ interface OptionsProps {
} }
export function Options({ isFullScreen, canvas }: OptionsProps) { export function Options({ isFullScreen, canvas }: OptionsProps) {
const showGrid = useTankopediaTemporary( const showGrid = useTankopediaPersistent(
(state) => state.model.visual.showGrid, (state) => state.model.visual.showGrid,
); );
const greenPenetration = useTankopediaTemporary( const greenPenetration = useTankopediaPersistent(
(state) => state.model.visual.greenPenetration, (state) => state.model.visual.greenPenetration,
); );
const fullScreenAvailable = const fullScreenAvailable =
@@ -69,7 +69,7 @@ export function Options({ isFullScreen, canvas }: OptionsProps) {
<DropdownMenu.CheckboxItem <DropdownMenu.CheckboxItem
checked={showGrid} checked={showGrid}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
mutateTankopediaTemporary((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.visual.showGrid = checked; draft.model.visual.showGrid = checked;
}); });
}} }}
@@ -80,7 +80,7 @@ export function Options({ isFullScreen, canvas }: OptionsProps) {
<DropdownMenu.CheckboxItem <DropdownMenu.CheckboxItem
checked={greenPenetration} checked={greenPenetration}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
mutateTankopediaTemporary((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.visual.greenPenetration = checked; draft.model.visual.greenPenetration = checked;
}); });
}} }}
@@ -2,11 +2,7 @@ import { useEffect, useRef } from 'react';
import { Euler, Group, Mesh, Vector3 } from 'three'; import { Euler, Group, Mesh, Vector3 } from 'three';
import { degToRad } from 'three/src/math/MathUtils'; import { degToRad } from 'three/src/math/MathUtils';
import { ArmorMesh } from '../../../../../../../../../components/ArmorMesh'; import { ArmorMesh } from '../../../../../../../../../components/ArmorMesh';
import { import { I_HAT, J_HAT, K_HAT } from '../../../../../../../../../constants/axis';
X_AXIS,
Y_AXIS,
Z_AXIS,
} from '../../../../../../../../../constants/axis';
import { import {
ModelTransformEventData, ModelTransformEventData,
modelTransformEvent, modelTransformEvent,
@@ -26,41 +22,28 @@ export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
const wrapper = useRef<Group>(null); const wrapper = useRef<Group>(null);
const modelDefinitions = useModelDefinitions(); const modelDefinitions = useModelDefinitions();
const turretContainer = useRef<Group>(null); const turretContainer = useRef<Group>(null);
const gunContainer = useRef<Group>(null);
const initialTankopediaState = useTankopediaTemporary.getState(); const initialTankopediaState = useTankopediaTemporary.getState();
useEffect(() => { useEffect(() => {
const hullOrigin = new Vector3(
tankModelDefinition.hullOrigin[0],
tankModelDefinition.hullOrigin[1],
-tankModelDefinition.hullOrigin[2],
).applyAxisAngle(I_HAT, Math.PI / 2);
const turretOrigin = new Vector3( const turretOrigin = new Vector3(
tankModelDefinition.turretOrigin[0], tankModelDefinition.turretOrigin[0],
tankModelDefinition.turretOrigin[1], tankModelDefinition.turretOrigin[1],
-tankModelDefinition.turretOrigin[2], -tankModelDefinition.turretOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2); ).applyAxisAngle(I_HAT, 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(); const turretPosition = new Vector3();
const turretRotation = new Euler(); const turretRotation = new Euler();
const gunPosition = new Vector3();
const gunRotation = new Euler();
function handleModelTransform({ yaw, pitch }: ModelTransformEventData) {
gunPosition
.set(0, 0, 0)
.sub(turretOrigin)
.sub(gunOrigin)
.applyAxisAngle(X_AXIS, pitch)
.add(gunOrigin)
.add(turretOrigin);
gunRotation.set(pitch, 0, 0);
gunContainer.current?.position.copy(gunPosition);
gunContainer.current?.rotation.copy(gunRotation);
function handleModelTransform({ yaw }: ModelTransformEventData) {
if (yaw === undefined) return; if (yaw === undefined) return;
turretPosition turretPosition
.set(0, 0, 0) .set(0, 0, 0)
.sub(hullOrigin)
.sub(turretOrigin) .sub(turretOrigin)
.applyAxisAngle(new Vector3(0, 0, 1), yaw); .applyAxisAngle(new Vector3(0, 0, 1), yaw);
turretRotation.set(0, 0, yaw); turretRotation.set(0, 0, yaw);
@@ -73,15 +56,15 @@ export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
const initialRoll = -degToRad(tankModelDefinition.turretRotation.roll); const initialRoll = -degToRad(tankModelDefinition.turretRotation.roll);
turretPosition turretPosition
.applyAxisAngle(X_AXIS, initialPitch) .applyAxisAngle(I_HAT, initialPitch)
.applyAxisAngle(Y_AXIS, initialRoll) .applyAxisAngle(J_HAT, initialRoll)
.applyAxisAngle(Z_AXIS, initialYaw); .applyAxisAngle(K_HAT, initialYaw);
turretRotation.x += initialPitch; turretRotation.x += initialPitch;
turretRotation.y += initialRoll; turretRotation.y += initialRoll;
turretRotation.z += initialYaw; turretRotation.z += initialYaw;
} }
turretPosition.add(turretOrigin); turretPosition.add(turretOrigin).add(hullOrigin);
turretContainer.current?.position.copy(turretPosition); turretContainer.current?.position.copy(turretPosition);
turretContainer.current?.rotation.copy(turretRotation); turretContainer.current?.rotation.copy(turretRotation);
} }
@@ -112,16 +95,16 @@ export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
tankModelDefinition.turrets[duel.protagonist.turret.id]; tankModelDefinition.turrets[duel.protagonist.turret.id];
const gunModelDefinition = const gunModelDefinition =
turretModelDefinition.guns[duel.protagonist.gun.id]; turretModelDefinition.guns[duel.protagonist.gun.id];
const hullOrigin = new Vector3(
tankModelDefinition.hullOrigin[0],
tankModelDefinition.hullOrigin[1],
-tankModelDefinition.hullOrigin[2],
);
const turretOrigin = new Vector3( const turretOrigin = new Vector3(
tankModelDefinition.turretOrigin[0], tankModelDefinition.turretOrigin[0],
tankModelDefinition.turretOrigin[1], tankModelDefinition.turretOrigin[1],
-tankModelDefinition.turretOrigin[2], -tankModelDefinition.turretOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2); ).applyAxisAngle(I_HAT, Math.PI / 2);
const gunOrigin = new Vector3(
turretModelDefinition.gunOrigin[0],
turretModelDefinition.gunOrigin[1],
-turretModelDefinition.gunOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2);
const maxThickness = Math.max( const maxThickness = Math.max(
tankModelDefinition.trackThickness, tankModelDefinition.trackThickness,
gunModelDefinition.barrelThickness, gunModelDefinition.barrelThickness,
@@ -138,6 +121,7 @@ export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
ref={wrapper} ref={wrapper}
rotation={[-Math.PI / 2, 0, 0]} rotation={[-Math.PI / 2, 0, 0]}
visible={initialTankopediaState.mode === 'armor'} visible={initialTankopediaState.mode === 'armor'}
position={hullOrigin}
> >
{armorNodes.map((node) => { {armorNodes.map((node) => {
const isHull = node.name.startsWith('hull_'); const isHull = node.name.startsWith('hull_');
@@ -186,32 +170,6 @@ export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
/> />
); );
})} })}
<group ref={gunContainer}>
{armorNodes.map((node) => {
const isCurrentGun = node.name.startsWith(
`gun_${gunModelDefinition.model.toString().padStart(2, '0')}`,
);
const isVisible = isCurrentGun;
const armorId = nameToArmorId(node.name);
const { spaced, thickness } = resolveArmor(
gunModelDefinition.armor,
armorId,
);
if (!isVisible || thickness === undefined || spaced) return null;
return (
<ArmorMesh
maxThickness={maxThickness}
duel={duel}
key={node.uuid}
geometry={(node as Mesh).geometry}
position={turretOrigin.clone().add(gunOrigin)}
thickness={thickness}
/>
);
})}
</group>
</group> </group>
</group> </group>
); );
@@ -2,11 +2,7 @@ import { memo, useEffect, useRef } from 'react';
import { Euler, Group, Mesh, Vector3 } from 'three'; import { Euler, Group, Mesh, Vector3 } from 'three';
import { degToRad } from 'three/src/math/MathUtils'; import { degToRad } from 'three/src/math/MathUtils';
import { ArmorMeshExternalModuleMask } from '../../../../../../../../../components/ArmorMesh'; import { ArmorMeshExternalModuleMask } from '../../../../../../../../../components/ArmorMesh';
import { import { I_HAT, J_HAT, K_HAT } from '../../../../../../../../../constants/axis';
X_AXIS,
Y_AXIS,
Z_AXIS,
} from '../../../../../../../../../constants/axis';
import { import {
ModelTransformEventData, ModelTransformEventData,
modelTransformEvent, modelTransformEvent,
@@ -38,16 +34,21 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
const initialTankopediaState = useTankopediaTemporary.getState(); const initialTankopediaState = useTankopediaTemporary.getState();
useEffect(() => { useEffect(() => {
const hullOrigin = new Vector3(
tankModelDefinition.hullOrigin[0],
tankModelDefinition.hullOrigin[1],
-tankModelDefinition.hullOrigin[2],
).applyAxisAngle(I_HAT, Math.PI / 2);
const turretOrigin = new Vector3( const turretOrigin = new Vector3(
tankModelDefinition.turretOrigin[0], tankModelDefinition.turretOrigin[0],
tankModelDefinition.turretOrigin[1], tankModelDefinition.turretOrigin[1],
-tankModelDefinition.turretOrigin[2], -tankModelDefinition.turretOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2); ).applyAxisAngle(I_HAT, Math.PI / 2);
const gunOrigin = new Vector3( const gunOrigin = new Vector3(
turretModelDefinition.gunOrigin[0], turretModelDefinition.gunOrigin[0],
turretModelDefinition.gunOrigin[1], turretModelDefinition.gunOrigin[1],
-turretModelDefinition.gunOrigin[2], -turretModelDefinition.gunOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2); ).applyAxisAngle(I_HAT, Math.PI / 2);
const turretPosition = new Vector3(); const turretPosition = new Vector3();
const turretRotation = new Euler(); const turretRotation = new Euler();
const gunPosition = new Vector3(); const gunPosition = new Vector3();
@@ -56,11 +57,13 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
function handleModelTransform({ yaw, pitch }: ModelTransformEventData) { function handleModelTransform({ yaw, pitch }: ModelTransformEventData) {
gunPosition gunPosition
.set(0, 0, 0) .set(0, 0, 0)
.sub(hullOrigin)
.sub(turretOrigin) .sub(turretOrigin)
.sub(gunOrigin) .sub(gunOrigin)
.applyAxisAngle(X_AXIS, pitch) .applyAxisAngle(I_HAT, pitch)
.add(gunOrigin) .add(gunOrigin)
.add(turretOrigin); .add(turretOrigin)
.add(hullOrigin);
gunRotation.set(pitch, 0, 0); gunRotation.set(pitch, 0, 0);
gunContainer.current?.position.copy(gunPosition); gunContainer.current?.position.copy(gunPosition);
gunContainer.current?.rotation.copy(gunRotation); gunContainer.current?.rotation.copy(gunRotation);
@@ -70,6 +73,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
turretPosition turretPosition
.set(0, 0, 0) .set(0, 0, 0)
.sub(turretOrigin) .sub(turretOrigin)
.sub(hullOrigin)
.applyAxisAngle(new Vector3(0, 0, 1), yaw); .applyAxisAngle(new Vector3(0, 0, 1), yaw);
turretRotation.set(0, 0, yaw); turretRotation.set(0, 0, yaw);
@@ -81,15 +85,15 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
const initialRoll = -degToRad(tankModelDefinition.turretRotation.roll); const initialRoll = -degToRad(tankModelDefinition.turretRotation.roll);
turretPosition turretPosition
.applyAxisAngle(X_AXIS, initialPitch) .applyAxisAngle(I_HAT, initialPitch)
.applyAxisAngle(Y_AXIS, initialRoll) .applyAxisAngle(J_HAT, initialRoll)
.applyAxisAngle(Z_AXIS, initialYaw); .applyAxisAngle(K_HAT, initialYaw);
turretRotation.x += initialPitch; turretRotation.x += initialPitch;
turretRotation.y += initialRoll; turretRotation.y += initialRoll;
turretRotation.z += initialYaw; turretRotation.z += initialYaw;
} }
turretPosition.add(turretOrigin); turretPosition.add(turretOrigin).add(hullOrigin);
turretContainer.current?.position.copy(turretPosition); turretContainer.current?.position.copy(turretPosition);
turretContainer.current?.rotation.copy(turretRotation); turretContainer.current?.rotation.copy(turretRotation);
} }
@@ -133,16 +137,16 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
}) })
.filter(Boolean), .filter(Boolean),
); );
const hullOrigin = new Vector3(
tankModelDefinition.hullOrigin[0],
tankModelDefinition.hullOrigin[1],
-tankModelDefinition.hullOrigin[2],
).applyAxisAngle(I_HAT, Math.PI / 2);
const turretOrigin = new Vector3( const turretOrigin = new Vector3(
tankModelDefinition.turretOrigin[0], tankModelDefinition.turretOrigin[0],
tankModelDefinition.turretOrigin[1], tankModelDefinition.turretOrigin[1],
-tankModelDefinition.turretOrigin[2], -tankModelDefinition.turretOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2); ).applyAxisAngle(I_HAT, Math.PI / 2);
const gunOrigin = new Vector3(
turretModelDefinition.gunOrigin[0],
turretModelDefinition.gunOrigin[1],
-turretModelDefinition.gunOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2);
return ( return (
<group <group
@@ -150,22 +154,27 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
rotation={[-Math.PI / 2, 0, 0]} rotation={[-Math.PI / 2, 0, 0]}
visible={initialTankopediaState.mode === 'armor'} visible={initialTankopediaState.mode === 'armor'}
> >
{armorNodes.map((node) => { <group position={hullOrigin}>
const isHull = node.name.startsWith('hull_'); {armorNodes.map((node) => {
const isVisible = isHull; const isHull = node.name.startsWith('hull_');
const armorId = nameToArmorId(node.name); const isVisible = isHull;
const { thickness } = resolveArmor(tankModelDefinition.armor, armorId); const armorId = nameToArmorId(node.name);
const { thickness } = resolveArmor(
tankModelDefinition.armor,
armorId,
);
if (!isVisible || thickness === undefined) return null; if (!isVisible || thickness === undefined) return null;
return ( return (
<ArmorMeshExternalModuleMask <ArmorMeshExternalModuleMask
exclude exclude
key={node.uuid} key={node.uuid}
geometry={(node as Mesh).geometry} geometry={(node as Mesh).geometry}
/> />
); );
})} })}
</group>
{modelNodes.map((node) => { {modelNodes.map((node) => {
const isWheel = node.name.startsWith('chassis_wheel_'); const isWheel = node.name.startsWith('chassis_wheel_');
@@ -185,37 +194,15 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
})} })}
<group ref={turretContainer}> <group ref={turretContainer}>
{armorNodes.map((node) => { <group position={hullOrigin}>
const isCurrentTurret = node.name.startsWith(
`turret_${turretModelDefinition.model.toString().padStart(2, '0')}`,
);
const isVisible = isCurrentTurret;
const armorId = nameToArmorId(node.name);
const { thickness } = resolveArmor(
turretModelDefinition.armor,
armorId,
);
if (!isVisible || thickness === undefined) return null;
return (
<ArmorMeshExternalModuleMask
exclude
key={node.uuid}
geometry={(node as Mesh).geometry}
position={turretOrigin}
/>
);
})}
<group ref={gunContainer}>
{armorNodes.map((node) => { {armorNodes.map((node) => {
const isCurrentGun = node.name.startsWith( const isCurrentTurret = node.name.startsWith(
`gun_${gunModelDefinition.model.toString().padStart(2, '0')}`, `turret_${turretModelDefinition.model.toString().padStart(2, '0')}`,
); );
const isVisible = isCurrentGun; const isVisible = isCurrentTurret;
const armorId = nameToArmorId(node.name); const armorId = nameToArmorId(node.name);
const { thickness } = resolveArmor( const { thickness } = resolveArmor(
gunModelDefinition.armor, turretModelDefinition.armor,
armorId, armorId,
); );
@@ -226,11 +213,13 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
exclude exclude
key={node.uuid} key={node.uuid}
geometry={(node as Mesh).geometry} geometry={(node as Mesh).geometry}
position={turretOrigin.clone().add(gunOrigin)} position={turretOrigin}
/> />
); );
})} })}
</group>
<group ref={gunContainer}>
{modelNodes.map((node) => { {modelNodes.map((node) => {
const isCurrentGun = const isCurrentGun =
node.name === node.name ===
@@ -2,11 +2,7 @@ import { memo, useEffect, useRef } from 'react';
import { Euler, Group, Mesh, Vector3 } from 'three'; import { Euler, Group, Mesh, Vector3 } from 'three';
import { degToRad } from 'three/src/math/MathUtils'; import { degToRad } from 'three/src/math/MathUtils';
import { ArmorMeshSpacedArmorDepth } from '../../../../../../../../../components/ArmorMesh/components/SpacedArmorDepth'; import { ArmorMeshSpacedArmorDepth } from '../../../../../../../../../components/ArmorMesh/components/SpacedArmorDepth';
import { import { I_HAT, J_HAT, K_HAT } from '../../../../../../../../../constants/axis';
X_AXIS,
Y_AXIS,
Z_AXIS,
} from '../../../../../../../../../constants/axis';
import { import {
ModelTransformEventData, ModelTransformEventData,
modelTransformEvent, modelTransformEvent,
@@ -44,16 +40,21 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
useEffect(() => { useEffect(() => {
if (!modelDefinitions) return; if (!modelDefinitions) return;
const hullOrigin = new Vector3(
tankModelDefinition.hullOrigin[0],
tankModelDefinition.hullOrigin[1],
-tankModelDefinition.hullOrigin[2],
).applyAxisAngle(I_HAT, Math.PI / 2);
const turretOrigin = new Vector3( const turretOrigin = new Vector3(
tankModelDefinition.turretOrigin[0], tankModelDefinition.turretOrigin[0],
tankModelDefinition.turretOrigin[1], tankModelDefinition.turretOrigin[1],
-tankModelDefinition.turretOrigin[2], -tankModelDefinition.turretOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2); ).applyAxisAngle(I_HAT, Math.PI / 2);
const gunOrigin = new Vector3( const gunOrigin = new Vector3(
turretModelDefinition.gunOrigin[0], turretModelDefinition.gunOrigin[0],
turretModelDefinition.gunOrigin[1], turretModelDefinition.gunOrigin[1],
-turretModelDefinition.gunOrigin[2], -turretModelDefinition.gunOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2); ).applyAxisAngle(I_HAT, Math.PI / 2);
const turretPosition = new Vector3(); const turretPosition = new Vector3();
const turretRotation = new Euler(); const turretRotation = new Euler();
const gunPosition = new Vector3(); const gunPosition = new Vector3();
@@ -62,11 +63,13 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
function handleModelTransform({ yaw, pitch }: ModelTransformEventData) { function handleModelTransform({ yaw, pitch }: ModelTransformEventData) {
gunPosition gunPosition
.set(0, 0, 0) .set(0, 0, 0)
.sub(hullOrigin)
.sub(turretOrigin) .sub(turretOrigin)
.sub(gunOrigin) .sub(gunOrigin)
.applyAxisAngle(X_AXIS, pitch) .applyAxisAngle(I_HAT, pitch)
.add(gunOrigin) .add(gunOrigin)
.add(turretOrigin); .add(turretOrigin)
.add(hullOrigin);
gunRotation.set(pitch, 0, 0); gunRotation.set(pitch, 0, 0);
gunContainer.current?.position.copy(gunPosition); gunContainer.current?.position.copy(gunPosition);
gunContainer.current?.rotation.copy(gunRotation); gunContainer.current?.rotation.copy(gunRotation);
@@ -75,6 +78,7 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
turretPosition turretPosition
.set(0, 0, 0) .set(0, 0, 0)
.sub(hullOrigin)
.sub(turretOrigin) .sub(turretOrigin)
.applyAxisAngle(new Vector3(0, 0, 1), yaw); .applyAxisAngle(new Vector3(0, 0, 1), yaw);
turretRotation.set(0, 0, yaw); turretRotation.set(0, 0, yaw);
@@ -87,15 +91,15 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
const initialRoll = -degToRad(tankModelDefinition.turretRotation.roll); const initialRoll = -degToRad(tankModelDefinition.turretRotation.roll);
turretPosition turretPosition
.applyAxisAngle(X_AXIS, initialPitch) .applyAxisAngle(I_HAT, initialPitch)
.applyAxisAngle(Y_AXIS, initialRoll) .applyAxisAngle(J_HAT, initialRoll)
.applyAxisAngle(Z_AXIS, initialYaw); .applyAxisAngle(K_HAT, initialYaw);
turretRotation.x += initialPitch; turretRotation.x += initialPitch;
turretRotation.y += initialRoll; turretRotation.y += initialRoll;
turretRotation.z += initialYaw; turretRotation.z += initialYaw;
} }
turretPosition.add(turretOrigin); turretPosition.add(turretOrigin).add(hullOrigin);
turretContainer.current?.position.copy(turretPosition); turretContainer.current?.position.copy(turretPosition);
turretContainer.current?.rotation.copy(turretRotation); turretContainer.current?.rotation.copy(turretRotation);
} }
@@ -139,16 +143,16 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
}) })
.filter(Boolean), .filter(Boolean),
); );
const hullOrigin = new Vector3(
tankModelDefinition.hullOrigin[0],
tankModelDefinition.hullOrigin[1],
-tankModelDefinition.hullOrigin[2],
).applyAxisAngle(I_HAT, Math.PI / 2);
const turretOrigin = new Vector3( const turretOrigin = new Vector3(
tankModelDefinition.turretOrigin[0], tankModelDefinition.turretOrigin[0],
tankModelDefinition.turretOrigin[1], tankModelDefinition.turretOrigin[1],
-tankModelDefinition.turretOrigin[2], -tankModelDefinition.turretOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2); ).applyAxisAngle(I_HAT, Math.PI / 2);
const gunOrigin = new Vector3(
turretModelDefinition.gunOrigin[0],
turretModelDefinition.gunOrigin[1],
-turretModelDefinition.gunOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2);
return ( return (
<group <group
@@ -156,28 +160,30 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
rotation={[-Math.PI / 2, 0, 0]} rotation={[-Math.PI / 2, 0, 0]}
visible={initialTankopediaState.mode === 'armor'} visible={initialTankopediaState.mode === 'armor'}
> >
{armorNodes.map((node) => { <group position={hullOrigin}>
const isHull = node.name.startsWith('hull_'); {armorNodes.map((node) => {
const isVisible = isHull; const isHull = node.name.startsWith('hull_');
const armorId = nameToArmorId(node.name); const isVisible = isHull;
const { spaced, thickness } = resolveArmor( const armorId = nameToArmorId(node.name);
tankModelDefinition.armor, const { spaced, thickness } = resolveArmor(
armorId, tankModelDefinition.armor,
); armorId,
);
if (!isVisible || thickness === undefined) return null; if (!isVisible || thickness === undefined) return null;
return ( return (
<ArmorMeshSpacedArmorDepth <ArmorMeshSpacedArmorDepth
include={spaced} include={spaced}
isExternalModule={false} isExternalModule={false}
thickness={thickness} thickness={thickness}
maxThickness={maxThickness} maxThickness={maxThickness}
key={node.uuid} key={node.uuid}
geometry={(node as Mesh).geometry} geometry={(node as Mesh).geometry}
/> />
); );
})} })}
</group>
{modelNodes.map((node) => { {modelNodes.map((node) => {
const isWheel = node.name.startsWith('chassis_wheel_'); const isWheel = node.name.startsWith('chassis_wheel_');
const isTrack = node.name.startsWith('chassis_track_'); const isTrack = node.name.startsWith('chassis_track_');
@@ -195,40 +201,15 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
})} })}
<group ref={turretContainer}> <group ref={turretContainer}>
{armorNodes.map((node) => { <group position={hullOrigin}>
const isCurrentTurret = node.name.startsWith(
`turret_${turretModelDefinition.model.toString().padStart(2, '0')}`,
);
const isVisible = isCurrentTurret;
const armorId = nameToArmorId(node.name);
const { spaced, thickness } = resolveArmor(
turretModelDefinition.armor,
armorId,
);
if (!isVisible || thickness === undefined) return null;
return (
<ArmorMeshSpacedArmorDepth
include={spaced}
isExternalModule={false}
thickness={thickness}
maxThickness={maxThickness}
key={node.uuid}
geometry={(node as Mesh).geometry}
position={turretOrigin}
/>
);
})}
<group ref={gunContainer}>
{armorNodes.map((node) => { {armorNodes.map((node) => {
const isCurrentGun = node.name.startsWith( const isCurrentTurret = node.name.startsWith(
`gun_${gunModelDefinition.model.toString().padStart(2, '0')}`, `turret_${turretModelDefinition.model.toString().padStart(2, '0')}`,
); );
const isVisible = isCurrentGun; const isVisible = isCurrentTurret;
const armorId = nameToArmorId(node.name); const armorId = nameToArmorId(node.name);
const { spaced, thickness } = resolveArmor( const { spaced, thickness } = resolveArmor(
gunModelDefinition.armor, turretModelDefinition.armor,
armorId, armorId,
); );
@@ -242,10 +223,12 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
maxThickness={maxThickness} maxThickness={maxThickness}
key={node.uuid} key={node.uuid}
geometry={(node as Mesh).geometry} geometry={(node as Mesh).geometry}
position={turretOrigin.clone().add(gunOrigin)} position={turretOrigin}
/> />
); );
})} })}
</group>
<group ref={gunContainer}>
{modelNodes.map((node) => { {modelNodes.map((node) => {
const isCurrentGun = const isCurrentGun =
node.name === node.name ===
@@ -9,7 +9,7 @@ import {
Vector3, Vector3,
} from 'three'; } from 'three';
import { degToRad } from 'three/src/math/MathUtils'; import { degToRad } from 'three/src/math/MathUtils';
import { X_AXIS, Y_AXIS, Z_AXIS } from '../../../../../../../constants/axis'; import { I_HAT, J_HAT, K_HAT } from '../../../../../../../constants/axis';
import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits'; import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits';
import { import {
ModelDefinitions, ModelDefinitions,
@@ -22,7 +22,7 @@ import {
import { resolveJsxTree } from '../../../../../../../core/blitzkrieg/resolveJsxTree'; import { resolveJsxTree } from '../../../../../../../core/blitzkrieg/resolveJsxTree';
import { normalizeAnglePI } from '../../../../../../../core/math/normalizeAngle180'; import { normalizeAnglePI } from '../../../../../../../core/math/normalizeAngle180';
import { useModel } from '../../../../../../../hooks/useModel'; import { useModel } from '../../../../../../../hooks/useModel';
import { import mutateTankopediaPersistent, {
mutateTankopediaTemporary, mutateTankopediaTemporary,
useTankopediaTemporary, useTankopediaTemporary,
} from '../../../../../../../stores/tankopedia'; } from '../../../../../../../stores/tankopedia';
@@ -52,16 +52,21 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
useEffect(() => { useEffect(() => {
if (!awaitedModelDefinitions) return; if (!awaitedModelDefinitions) return;
const hullOrigin = new Vector3(
tankModelDefinition.hullOrigin[0],
tankModelDefinition.hullOrigin[1],
-tankModelDefinition.hullOrigin[2],
).applyAxisAngle(I_HAT, Math.PI / 2);
const turretOrigin = new Vector3( const turretOrigin = new Vector3(
tankModelDefinition.turretOrigin[0], tankModelDefinition.turretOrigin[0],
tankModelDefinition.turretOrigin[1], tankModelDefinition.turretOrigin[1],
-tankModelDefinition.turretOrigin[2], -tankModelDefinition.turretOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2); ).applyAxisAngle(I_HAT, Math.PI / 2);
const gunOrigin = new Vector3( const gunOrigin = new Vector3(
turretModelDefinition.gunOrigin[0], turretModelDefinition.gunOrigin[0],
turretModelDefinition.gunOrigin[1], turretModelDefinition.gunOrigin[1],
-turretModelDefinition.gunOrigin[2], -turretModelDefinition.gunOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2); ).applyAxisAngle(I_HAT, Math.PI / 2);
const turretPosition = new Vector3(); const turretPosition = new Vector3();
const turretRotation = new Euler(); const turretRotation = new Euler();
const gunPosition = new Vector3(); const gunPosition = new Vector3();
@@ -70,11 +75,13 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
function handleModelTransform({ yaw, pitch }: ModelTransformEventData) { function handleModelTransform({ yaw, pitch }: ModelTransformEventData) {
gunPosition gunPosition
.set(0, 0, 0) .set(0, 0, 0)
.sub(hullOrigin)
.sub(turretOrigin) .sub(turretOrigin)
.sub(gunOrigin) .sub(gunOrigin)
.applyAxisAngle(X_AXIS, pitch) .applyAxisAngle(I_HAT, pitch)
.add(gunOrigin) .add(gunOrigin)
.add(turretOrigin); .add(turretOrigin)
.add(hullOrigin);
gunRotation.set(pitch, 0, 0); gunRotation.set(pitch, 0, 0);
gunContainer.current?.position.copy(gunPosition); gunContainer.current?.position.copy(gunPosition);
gunContainer.current?.rotation.copy(gunRotation); gunContainer.current?.rotation.copy(gunRotation);
@@ -83,6 +90,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
turretPosition turretPosition
.set(0, 0, 0) .set(0, 0, 0)
.sub(hullOrigin)
.sub(turretOrigin) .sub(turretOrigin)
.applyAxisAngle(new Vector3(0, 0, 1), yaw); .applyAxisAngle(new Vector3(0, 0, 1), yaw);
turretRotation.set(0, 0, yaw); turretRotation.set(0, 0, yaw);
@@ -95,15 +103,15 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
const initialRoll = -degToRad(tankModelDefinition.turretRotation.roll); const initialRoll = -degToRad(tankModelDefinition.turretRotation.roll);
turretPosition turretPosition
.applyAxisAngle(X_AXIS, initialPitch) .applyAxisAngle(I_HAT, initialPitch)
.applyAxisAngle(Y_AXIS, initialRoll) .applyAxisAngle(J_HAT, initialRoll)
.applyAxisAngle(Z_AXIS, initialYaw); .applyAxisAngle(K_HAT, initialYaw);
turretRotation.x += initialPitch; turretRotation.x += initialPitch;
turretRotation.y += initialRoll; turretRotation.y += initialRoll;
turretRotation.z += initialYaw; turretRotation.z += initialYaw;
} }
turretPosition.add(turretOrigin); turretPosition.add(turretOrigin).add(hullOrigin);
turretContainer.current?.position.copy(turretPosition); turretContainer.current?.position.copy(turretPosition);
turretContainer.current?.rotation.copy(turretRotation); turretContainer.current?.rotation.copy(turretRotation);
} }
@@ -137,7 +145,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
event.stopPropagation(); event.stopPropagation();
if (isTrack) { if (isTrack) {
mutateTankopediaTemporary((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.visual.controlsEnabled = false; draft.model.visual.controlsEnabled = false;
}); });
@@ -159,7 +167,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
material.normalMap?.offset.add(offset); material.normalMap?.offset.add(offset);
} }
function handlePointerUp() { function handlePointerUp() {
mutateTankopediaTemporary((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.visual.controlsEnabled = true; draft.model.visual.controlsEnabled = true;
}); });
window.removeEventListener('pointermove', handlePointerMove); window.removeEventListener('pointermove', handlePointerMove);
@@ -201,7 +209,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
yaw = physical.yaw; yaw = physical.yaw;
pitch = physical.pitch; pitch = physical.pitch;
mutateTankopediaTemporary((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.visual.controlsEnabled = false; draft.model.visual.controlsEnabled = false;
}); });
window.addEventListener('pointermove', handlePointerMove); window.addEventListener('pointermove', handlePointerMove);
@@ -218,10 +226,12 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
modelTransformEvent.emit({ pitch, yaw }); modelTransformEvent.emit({ pitch, yaw });
} }
function handlePointerUp() { function handlePointerUp() {
mutateTankopediaTemporary((state) => { mutateTankopediaTemporary((draft) => {
state.model.visual.controlsEnabled = true; draft.model.pose.pitch = normalizeAnglePI(pitch);
state.model.pose.pitch = normalizeAnglePI(pitch); draft.model.pose.yaw = normalizeAnglePI(yaw);
state.model.pose.yaw = normalizeAnglePI(yaw); });
mutateTankopediaPersistent((draft) => {
draft.model.visual.controlsEnabled = true;
}); });
window.removeEventListener('pointermove', handlePointerMove); window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp); window.removeEventListener('pointerup', handlePointerUp);
@@ -263,7 +273,7 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
function handlePointerDown(event: ThreeEvent<PointerEvent>) { function handlePointerDown(event: ThreeEvent<PointerEvent>) {
event.stopPropagation(); event.stopPropagation();
mutateTankopediaTemporary((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.visual.controlsEnabled = false; draft.model.visual.controlsEnabled = false;
}); });
pitch = physical.pitch; pitch = physical.pitch;
@@ -281,10 +291,12 @@ export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
modelTransformEvent.emit({ pitch, yaw }); modelTransformEvent.emit({ pitch, yaw });
} }
function handlePointerUp() { function handlePointerUp() {
mutateTankopediaTemporary((state) => { mutateTankopediaPersistent((draft) => {
state.model.visual.controlsEnabled = true; draft.model.visual.controlsEnabled = true;
state.model.pose.pitch = normalizeAnglePI(pitch); });
state.model.pose.yaw = normalizeAnglePI(yaw); mutateTankopediaTemporary((draft) => {
draft.model.pose.pitch = normalizeAnglePI(pitch);
draft.model.pose.yaw = normalizeAnglePI(yaw);
}); });
window.removeEventListener('pointermove', handlePointerMove); window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp); window.removeEventListener('pointerup', handlePointerUp);
@@ -1,12 +1,11 @@
import { Flex } from '@radix-ui/themes'; import { Flex } from '@radix-ui/themes';
import { ModuleButton } from '../../../../../components/ModuleButton'; import { ModuleButton } from '../../../../../components/ModuleButton';
import { import mutateTankopediaPersistent, {
mutateTankopediaTemporary, useTankopediaPersistent,
useTankopediaTemporary,
} from '../../../../../stores/tankopedia'; } from '../../../../../stores/tankopedia';
export function QuickEquipments() { export function QuickEquipments() {
const equipment = useTankopediaTemporary((state) => state.model.equipment); const equipment = useTankopediaPersistent((state) => state.model.equipment);
return ( return (
<Flex <Flex
@@ -26,7 +25,7 @@ export function QuickEquipments() {
rowChild rowChild
equipment={103} equipment={103}
onClick={() => { onClick={() => {
mutateTankopediaTemporary((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.equipment.calibratedShells = draft.model.equipment.calibratedShells =
!draft.model.equipment.calibratedShells; !draft.model.equipment.calibratedShells;
}); });
@@ -39,7 +38,7 @@ export function QuickEquipments() {
rowChild rowChild
equipment={110} equipment={110}
onClick={() => { onClick={() => {
mutateTankopediaTemporary((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.equipment.enhancedArmor = draft.model.equipment.enhancedArmor =
!draft.model.equipment.enhancedArmor; !draft.model.equipment.enhancedArmor;
}); });
@@ -1,9 +1,9 @@
import { Color } from 'three'; import { Color } from 'three';
import InfiniteGridHelper from '../../../../../components/InfiniteGridHelper'; import InfiniteGridHelper from '../../../../../components/InfiniteGridHelper';
import { useTankopediaTemporary } from '../../../../../stores/tankopedia'; import { useTankopediaPersistent } from '../../../../../stores/tankopedia';
export function SceneProps() { export function SceneProps() {
const showGrid = useTankopediaTemporary( const showGrid = useTankopediaPersistent(
(state) => state.model.visual.showGrid, (state) => state.model.visual.showGrid,
); );
@@ -7,7 +7,7 @@ import { Duel } from '../../../../app/tools/tankopedia/[id]/page';
import { canSplash } from '../../../../core/blitz/canSplash'; import { canSplash } from '../../../../core/blitz/canSplash';
import { isExplosive } from '../../../../core/blitz/isExplosive'; import { isExplosive } from '../../../../core/blitz/isExplosive';
import { resolveNearPenetration } from '../../../../core/blitz/resolveNearPenetration'; import { resolveNearPenetration } from '../../../../core/blitz/resolveNearPenetration';
import { useTankopediaTemporary } from '../../../../stores/tankopedia'; import { useTankopediaPersistent } from '../../../../stores/tankopedia';
import { externalModuleMaskRenderTarget } from '../ExternalModuleMask'; import { externalModuleMaskRenderTarget } from '../ExternalModuleMask';
import { spacedArmorDepthRenderTarget } from '../SpacedArmorDepth'; import { spacedArmorDepthRenderTarget } from '../SpacedArmorDepth';
import fragmentShader from './shaders/fragment.glsl'; import fragmentShader from './shaders/fragment.glsl';
@@ -25,10 +25,10 @@ export function ArmorMesh({
duel, duel,
...props ...props
}: ArmorMeshProps) { }: ArmorMeshProps) {
const equipment = useTankopediaTemporary((draft) => draft.model.equipment); const equipment = useTankopediaPersistent((draft) => draft.model.equipment);
const camera = useThree((state) => state.camera); const camera = useThree((state) => state.camera);
const { shell } = duel.antagonist; const { shell } = duel.antagonist;
const greenPenetration = useTankopediaTemporary( const greenPenetration = useTankopediaPersistent(
(state) => state.model.visual.greenPenetration, (state) => state.model.visual.greenPenetration,
); );
const material = useRef<ShaderMaterial>(null); const material = useRef<ShaderMaterial>(null);
+3 -3
View File
@@ -1,5 +1,5 @@
import { Vector3 } from 'three'; import { Vector3 } from 'three';
export const X_AXIS = new Vector3(1, 0, 0); export const I_HAT = new Vector3(1, 0, 0);
export const Y_AXIS = new Vector3(0, 1, 0); export const J_HAT = new Vector3(0, 1, 0);
export const Z_AXIS = new Vector3(0, 0, 1); export const K_HAT = new Vector3(0, 0, 1);
+1
View File
@@ -12,6 +12,7 @@ interface ModelDefinition {
armor: ModelArmor; armor: ModelArmor;
trackThickness: number; trackThickness: number;
turretOrigin: Vector3Tuple; turretOrigin: Vector3Tuple;
hullOrigin: Vector3Tuple;
turretRotation?: InitialTurretRotation; turretRotation?: InitialTurretRotation;
turrets: Record<number, TurretModelDefinition>; turrets: Record<number, TurretModelDefinition>;
} }
+22 -18
View File
@@ -21,6 +21,17 @@ export interface DuelMember {
shell: ShellDefinition; shell: ShellDefinition;
} }
interface TankopediaPersistent { interface TankopediaPersistent {
model: {
equipment: {
enhancedArmor: boolean;
calibratedShells: boolean;
};
visual: {
controlsEnabled: boolean;
showGrid: boolean;
greenPenetration: boolean;
};
};
sort: { sort: {
by: TankopediaSortBy; by: TankopediaSortBy;
direction: TankopediaSortDirection; direction: TankopediaSortDirection;
@@ -43,15 +54,6 @@ interface TankopediaTemporary {
yaw: number; yaw: number;
pitch: number; pitch: number;
}; };
equipment: {
enhancedArmor: boolean;
calibratedShells: boolean;
};
visual: {
controlsEnabled: boolean;
showGrid: boolean;
greenPenetration: boolean;
};
}; };
equipmentMatrix: EquipmentMatrix; equipmentMatrix: EquipmentMatrix;
mode: TankopediaMode; mode: TankopediaMode;
@@ -60,6 +62,17 @@ interface TankopediaTemporary {
export const useTankopediaPersistent = create<TankopediaPersistent>()( export const useTankopediaPersistent = create<TankopediaPersistent>()(
persist( persist(
subscribeWithSelector<TankopediaPersistent>(() => ({ subscribeWithSelector<TankopediaPersistent>(() => ({
model: {
equipment: {
enhancedArmor: false,
calibratedShells: false,
},
visual: {
controlsEnabled: true,
showGrid: true,
greenPenetration: false,
},
},
sort: { sort: {
by: 'tier', by: 'tier',
direction: 'descending', direction: 'descending',
@@ -84,15 +97,6 @@ export const useTankopediaTemporary = create<TankopediaTemporary>()(
yaw: 0, yaw: 0,
pitch: 0, pitch: 0,
}, },
equipment: {
enhancedArmor: false,
calibratedShells: false,
},
visual: {
controlsEnabled: true,
showGrid: true,
greenPenetration: false,
},
}, },
equipmentMatrix: [ equipmentMatrix: [
[1, 0, 0], [1, 0, 0],