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