mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +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: {
|
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;
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
+20
-62
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
+50
-61
@@ -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 ===
|
||||||
|
|||||||
+53
-70
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -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
@@ -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],
|
||||||
|
|||||||
Reference in new issue
Block a user