feat: dynamic model defs

This commit is contained in:
TrèsAbhi committed 2024-07-30 13:24:55 -05:00
1 parent 3889c0f75d
commit 8ba56069c4
18 files changed
+127 -57

No files matched your search

@@ -28,6 +28,7 @@ import {
} from '../../../../../../core/blitzkit/tankDefinitions/constants'; } from '../../../../../../core/blitzkit/tankDefinitions/constants';
import { useAdExempt } from '../../../../../../hooks/useAdExempt'; import { useAdExempt } from '../../../../../../hooks/useAdExempt';
import { useEquipment } from '../../../../../../hooks/useEquipment'; import { useEquipment } from '../../../../../../hooks/useEquipment';
import { useTankModelDefinition } from '../../../../../../hooks/useTankModelDefinition';
import * as Duel from '../../../../../../stores/duel'; import * as Duel from '../../../../../../stores/duel';
import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral'; import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral';
import { Info } from './components/Info'; import { Info } from './components/Info';
@@ -37,7 +38,6 @@ export function Characteristics() {
const mutateDuel = Duel.useMutation(); const mutateDuel = Duel.useMutation();
const exempt = useAdExempt(); const exempt = useAdExempt();
const awaitedEquipmentDefinitions = use(equipmentDefinitions); const awaitedEquipmentDefinitions = use(equipmentDefinitions);
const awaitedModelDefinitions = use(modelDefinitions);
const awaitedProvisionDefinitions = use(provisionDefinitions); const awaitedProvisionDefinitions = use(provisionDefinitions);
const crewSkills = TankopediaEphemeral.use((state) => state.skills); const crewSkills = TankopediaEphemeral.use((state) => state.skills);
const penetrationDistanceInput = useRef<HTMLInputElement>(null); const penetrationDistanceInput = useRef<HTMLInputElement>(null);
@@ -68,7 +68,8 @@ export function Characteristics() {
const stockTrack = tank.tracks[0]; const stockTrack = tank.tracks[0];
const stockTurret = tank.turrets[0]; const stockTurret = tank.turrets[0];
const stockGun = stockTurret.guns[0]; const stockGun = stockTurret.guns[0];
const tankModelDefinition = awaitedModelDefinitions[tank.id]; const awaitedModelDefinitions = use(modelDefinitions);
const tankModelDefinition = useTankModelDefinition();
const turretModelDefinition = tankModelDefinition.turrets[turret.id]; const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id]; const gunModelDefinition = turretModelDefinition.guns[gun.id];
const stats = tankCharacteristics( const stats = tankCharacteristics(
@@ -94,8 +95,8 @@ export function Characteristics() {
applySpallLiner: false, applySpallLiner: false,
}, },
{ {
tankModelDefinition,
equipmentDefinitions: awaitedEquipmentDefinitions, equipmentDefinitions: awaitedEquipmentDefinitions,
modelDefinitions: awaitedModelDefinitions,
provisionDefinitions: awaitedProvisionDefinitions, provisionDefinitions: awaitedProvisionDefinitions,
}, },
); );
@@ -1,16 +1,15 @@
import { Canvas } from '@react-three/fiber'; import { Canvas } from '@react-three/fiber';
import { Suspense, use, useEffect, useRef } from 'react'; import { Suspense, useEffect, useRef } from 'react';
import { Armor } from '../../../../../../components/Armor'; import { Armor } from '../../../../../../components/Armor';
import { ArmorPlateDisplay } from '../../../../../../components/Armor/components/ArmorPlateDisplay'; import { ArmorPlateDisplay } from '../../../../../../components/Armor/components/ArmorPlateDisplay';
import { ShotDisplay } from '../../../../../../components/Armor/components/ShotDisplay'; import { ShotDisplay } from '../../../../../../components/Armor/components/ShotDisplay';
import { StaticArmor } from '../../../../../../components/StaticArmor'; import { StaticArmor } from '../../../../../../components/StaticArmor';
import { ThicknessRange } from '../../../../../../components/StaticArmor/components/StaticArmorScene'; import { ThicknessRange } from '../../../../../../components/StaticArmor/components/StaticArmorScene';
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits'; import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
import { modelDefinitions } from '../../../../../../core/blitzkit/modelDefinitions';
import { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransform'; import { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransform';
import { Pose, poseEvent } from '../../../../../../core/blitzkit/pose'; import { Pose, poseEvent } from '../../../../../../core/blitzkit/pose';
import { tankDefinitions } from '../../../../../../core/blitzkit/tankDefinitions';
import { useEquipment } from '../../../../../../hooks/useEquipment'; import { useEquipment } from '../../../../../../hooks/useEquipment';
import { useTankModelDefinition } from '../../../../../../hooks/useTankModelDefinition';
import * as Duel from '../../../../../../stores/duel'; import * as Duel from '../../../../../../stores/duel';
import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral'; import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral';
import * as TankopediaPersistent from '../../../../../../stores/tankopediaPersistent'; import * as TankopediaPersistent from '../../../../../../stores/tankopediaPersistent';
@@ -29,11 +28,9 @@ export function TankSandbox({ thicknessRange }: TankSandboxProps) {
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation(); const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
const canvas = useRef<HTMLCanvasElement>(null); const canvas = useRef<HTMLCanvasElement>(null);
const hasImprovedVerticalStabilizer = useEquipment(122); const hasImprovedVerticalStabilizer = useEquipment(122);
const awaitedModelDefinitions = use(modelDefinitions);
const awaitedTankDefinitions = use(tankDefinitions);
const protagonist = Duel.use((state) => state.protagonist); const protagonist = Duel.use((state) => state.protagonist);
const mutateDuel = Duel.useMutation(); const mutateDuel = Duel.useMutation();
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id]; const tankModelDefinition = useTankModelDefinition();
const turretModelDefinition = const turretModelDefinition =
tankModelDefinition.turrets[protagonist.turret.id]; tankModelDefinition.turrets[protagonist.turret.id];
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id]; const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
@@ -5,11 +5,10 @@ import { ModelTankWrapper } from '../../../../../../../components/Armor/componen
import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits'; import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits';
import { hasEquipment } from '../../../../../../../core/blitzkit/hasEquipment'; import { hasEquipment } from '../../../../../../../core/blitzkit/hasEquipment';
import { jsxTree } from '../../../../../../../core/blitzkit/jsxTree'; import { jsxTree } from '../../../../../../../core/blitzkit/jsxTree';
import { modelDefinitions } from '../../../../../../../core/blitzkit/modelDefinitions';
import { modelTransformEvent } from '../../../../../../../core/blitzkit/modelTransform'; import { modelTransformEvent } from '../../../../../../../core/blitzkit/modelTransform';
import { normalizeAngleRad } from '../../../../../../../core/math/normalizeAngleRad'; import { normalizeAngleRad } from '../../../../../../../core/math/normalizeAngleRad';
import { useAwait } from '../../../../../../../hooks/useAwait';
import { useModel } from '../../../../../../../hooks/useModel'; import { useModel } from '../../../../../../../hooks/useModel';
import { useTankModelDefinition } from '../../../../../../../hooks/useTankModelDefinition';
import { useTankTransform } from '../../../../../../../hooks/useTankTransform'; import { useTankTransform } from '../../../../../../../hooks/useTankTransform';
import * as Duel from '../../../../../../../stores/duel'; import * as Duel from '../../../../../../../stores/duel';
import * as TankopediaEphemeral from '../../../../../../../stores/tankopediaEphemeral'; import * as TankopediaEphemeral from '../../../../../../../stores/tankopediaEphemeral';
@@ -19,7 +18,6 @@ import { TankopediaDisplay } from '../../../../../../../stores/tankopediaPersist
export const TankModel = memo(() => { export const TankModel = memo(() => {
const mutateDuel = Duel.useMutation(); const mutateDuel = Duel.useMutation();
const duelStore = Duel.useStore(); const duelStore = Duel.useStore();
const awaitedModelDefinitions = useAwait(modelDefinitions);
const protagonist = Duel.use((draft) => draft.protagonist); const protagonist = Duel.use((draft) => draft.protagonist);
const tankopediaPersistentStore = TankopediaPersistent.useStore(); const tankopediaPersistentStore = TankopediaPersistent.useStore();
const canvas = useThree((state) => state.gl.domElement); const canvas = useThree((state) => state.gl.domElement);
@@ -27,18 +25,15 @@ export const TankModel = memo(() => {
const turretContainer = useRef<Group>(null); const turretContainer = useRef<Group>(null);
const gunContainer = useRef<Group>(null); const gunContainer = useRef<Group>(null);
const mutateTankopediaTemporary = TankopediaEphemeral.useMutation(); const mutateTankopediaTemporary = TankopediaEphemeral.useMutation();
const tankModelDefinition = useTankModelDefinition();
useTankTransform(protagonist, turretContainer, gunContainer);
if (!awaitedModelDefinitions) return;
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
const turretModelDefinition = const turretModelDefinition =
tankModelDefinition.turrets[protagonist.turret.id]; tankModelDefinition.turrets[protagonist.turret.id];
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id]; const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
const { gltf } = useModel(protagonist.tank.id); const { gltf } = useModel(protagonist.tank.id);
const nodes = Object.values(gltf.nodes); const nodes = Object.values(gltf.nodes);
useTankTransform(protagonist, turretContainer, gunContainer);
return ( return (
<ModelTankWrapper ref={hullContainer}> <ModelTankWrapper ref={hullContainer}>
{nodes.map((node) => { {nodes.map((node) => {
@@ -1,24 +1,23 @@
import { HeightIcon, WidthIcon } from '@radix-ui/react-icons'; import { HeightIcon, WidthIcon } from '@radix-ui/react-icons';
import { Flex, TextField } from '@radix-ui/themes'; import { Flex, TextField } from '@radix-ui/themes';
import { use, useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { degToRad, radToDeg } from 'three/src/math/MathUtils'; import { degToRad, radToDeg } from 'three/src/math/MathUtils';
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits'; import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
import { modelDefinitions } from '../../../../../../core/blitzkit/modelDefinitions';
import { import {
modelTransformEvent, modelTransformEvent,
ModelTransformEventData, ModelTransformEventData,
} from '../../../../../../core/blitzkit/modelTransform'; } from '../../../../../../core/blitzkit/modelTransform';
import { useEquipment } from '../../../../../../hooks/useEquipment'; import { useEquipment } from '../../../../../../hooks/useEquipment';
import { useTankModelDefinition } from '../../../../../../hooks/useTankModelDefinition';
import * as Duel from '../../../../../../stores/duel'; import * as Duel from '../../../../../../stores/duel';
import * as styles from './index.css'; import * as styles from './index.css';
export function QuickInputs() { export function QuickInputs() {
const mutateDuel = Duel.useMutation(); const mutateDuel = Duel.useMutation();
const protagonist = Duel.use((state) => state.protagonist); const protagonist = Duel.use((state) => state.protagonist);
const awaitedModelDefinitions = use(modelDefinitions);
const yawInput = useRef<HTMLInputElement>(null); const yawInput = useRef<HTMLInputElement>(null);
const pitchInput = useRef<HTMLInputElement>(null); const pitchInput = useRef<HTMLInputElement>(null);
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id]; const tankModelDefinition = useTankModelDefinition();
const turretModelDefinition = const turretModelDefinition =
tankModelDefinition.turrets[protagonist.turret.id]; tankModelDefinition.turrets[protagonist.turret.id];
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id]; const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
+4 -1
View File
@@ -1,4 +1,5 @@
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { modelDefinitions } from '../../../../core/blitzkit/modelDefinitions';
import { provisionDefinitions } from '../../../../core/blitzkit/provisionDefinitions'; import { provisionDefinitions } from '../../../../core/blitzkit/provisionDefinitions';
import { tankDefinitions } from '../../../../core/blitzkit/tankDefinitions'; import { tankDefinitions } from '../../../../core/blitzkit/tankDefinitions';
import { TIER_ROMAN_NUMERALS } from '../../../../core/blitzkit/tankDefinitions/constants'; import { TIER_ROMAN_NUMERALS } from '../../../../core/blitzkit/tankDefinitions/constants';
@@ -17,6 +18,8 @@ export default async function Layout({
}) { }) {
const id = Number(params.id); const id = Number(params.id);
const awaitedTankDefinitions = await tankDefinitions; const awaitedTankDefinitions = await tankDefinitions;
const awaitedModelDefinitions = await modelDefinitions;
const tankModelDefinition = awaitedModelDefinitions[id];
const awaitedProvisionDefinitions = await provisionDefinitions; const awaitedProvisionDefinitions = await provisionDefinitions;
const tank = awaitedTankDefinitions[id]; const tank = awaitedTankDefinitions[id];
const title = `${tank.name} - Tier ${TIER_ROMAN_NUMERALS[tank.tier]} ${ const title = `${tank.name} - Tier ${TIER_ROMAN_NUMERALS[tank.tier]} ${
@@ -25,7 +28,7 @@ export default async function Layout({
const description = `Statistics, armor, and equipment for ${tank.name}`; const description = `Statistics, armor, and equipment for ${tank.name}`;
return ( return (
<TankopediaEphemeral.Provider> <TankopediaEphemeral.Provider data={tankModelDefinition}>
<TankFilters.Provider> <TankFilters.Provider>
<Duel.Provider <Duel.Provider
data={{ data={{
+2 -1
View File
@@ -14,6 +14,7 @@ import { ShotDisplaySection } from './components/ShotDisplaySection';
import { VideoSection } from './components/VideoSection'; import { VideoSection } from './components/VideoSection';
export default function Page({ params }: { params: { id: string } }) { export default function Page({ params }: { params: { id: string } }) {
const id = Number(params.id);
const exempt = useAdExempt(); const exempt = useAdExempt();
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation(); const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
const mutateDuel = Duel.useMutation(); const mutateDuel = Duel.useMutation();
@@ -73,7 +74,7 @@ export default function Page({ params }: { params: { id: string } }) {
return ( return (
<PageWrapper p="0" noMaxWidth color="purple" size={1600}> <PageWrapper p="0" noMaxWidth color="purple" size={1600}>
<HeroSection id={Number(params.id)} /> <HeroSection id={id} />
<ShotDisplaySection /> <ShotDisplaySection />
{!exempt && <AdMidSectionResponsive />} {!exempt && <AdMidSectionResponsive />}
<CharacteristicsSection /> <CharacteristicsSection />
@@ -1,7 +1,11 @@
import { Card, Code, Flex, Text } from '@radix-ui/themes'; import { Card, Code, Flex, Text, TextField } from '@radix-ui/themes';
import { Html } from '@react-three/drei'; import { Html } from '@react-three/drei';
import { useEffect, useRef } from 'react';
import { radToDeg } from 'three/src/math/MathUtils'; import { radToDeg } from 'three/src/math/MathUtils';
import { ModelArmor } from '../../../core/blitzkit/modelDefinitions';
import { resolveArmorIndex } from '../../../core/blitzkit/resolveArmorIndex';
import * as App from '../../../stores/app'; import * as App from '../../../stores/app';
import * as Duel from '../../../stores/duel';
import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral'; import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral';
import { layerTypeNames } from './ShotDisplayCard'; import { layerTypeNames } from './ShotDisplayCard';
import { ArmorType } from './SpacedArmorScene'; import { ArmorType } from './SpacedArmorScene';
@@ -10,7 +14,14 @@ export function ArmorPlateDisplay() {
const highlightArmor = TankopediaEphemeral.use( const highlightArmor = TankopediaEphemeral.use(
(state) => state.highlightArmor, (state) => state.highlightArmor,
); );
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
const developerMode = App.use((state) => state.developerMode); const developerMode = App.use((state) => state.developerMode);
const devThicknessInput = useRef<HTMLInputElement>(null);
const duelStore = Duel.useStore();
useEffect(() => {
devThicknessInput.current?.focus();
});
if (highlightArmor === undefined) return null; if (highlightArmor === undefined) return null;
@@ -42,9 +53,60 @@ export function ArmorPlateDisplay() {
</Text> </Text>
)} )}
{developerMode && ( {developerMode && (
<Text mt="2" color="gray" size="2"> <>
<b>DEV:</b> <Code>{highlightArmor.name}</Code> <Text mt="2" color="gray" size="2">
</Text> <b>DEV:</b> <Code>{highlightArmor.name}</Code>
</Text>
<TextField.Root
type="number"
style={{ width: 96 }}
onKeyDown={(event) => {
if (event.key === 'Enter') {
devThicknessInput.current?.blur();
mutateTankopediaEphemeral((draft) => {
draft.highlightArmor = undefined;
const { protagonist } = duelStore.getState();
let armor: ModelArmor;
if (highlightArmor.name.startsWith('hull_')) {
armor = draft.model.armor;
} else if (highlightArmor.name.startsWith('turret_')) {
armor =
draft.model.turrets[protagonist.turret.id].armor;
} else if (highlightArmor.name.startsWith('gun_')) {
armor =
draft.model.turrets[protagonist.turret.id].guns[
protagonist.gun.id
].armor;
} else return;
const index = resolveArmorIndex(highlightArmor.name);
if (index === undefined) return;
const input = event.currentTarget.valueAsNumber;
if (!isNaN(input)) {
armor.thickness[index] = input;
}
});
} else if (event.key === 'Escape') {
devThicknessInput.current?.blur();
mutateTankopediaEphemeral((draft) => {
draft.highlightArmor = undefined;
});
}
}}
key={highlightArmor.name}
defaultValue={highlightArmor.thickness.toFixed(0)}
mt="1"
autoFocus
ref={devThicknessInput}
size="1"
>
<TextField.Slot side="right">mm</TextField.Slot>
</TextField.Root>
</>
)} )}
</Flex> </Flex>
</Card> </Card>
@@ -4,7 +4,7 @@ import { correctZYTuple } from '../../../core/blitz/correctZYTuple';
import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId'; import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId';
import { resolveArmor } from '../../../core/blitzkit/resolveThickness'; import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
import { useArmor } from '../../../hooks/useArmor'; import { useArmor } from '../../../hooks/useArmor';
import { useModelDefinitions } from '../../../hooks/useModelDefinitions'; import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
import { useTankTransform } from '../../../hooks/useTankTransform'; import { useTankTransform } from '../../../hooks/useTankTransform';
import * as Duel from '../../../stores/duel'; import * as Duel from '../../../stores/duel';
import * as TankopediaPersistent from '../../../stores/tankopediaPersistent'; import * as TankopediaPersistent from '../../../stores/tankopediaPersistent';
@@ -13,7 +13,6 @@ import { PrimaryArmorSceneComponent } from './PrimaryArmorSceneComponent';
export const PrimaryArmorScene = memo(() => { export const PrimaryArmorScene = memo(() => {
const wrapper = useRef<Group>(null); const wrapper = useRef<Group>(null);
const modelDefinitions = useModelDefinitions();
const turretContainer = useRef<Group>(null); const turretContainer = useRef<Group>(null);
const gunContainer = useRef<Group>(null); const gunContainer = useRef<Group>(null);
const protagonist = Duel.use((draft) => draft.protagonist); const protagonist = Duel.use((draft) => draft.protagonist);
@@ -23,7 +22,7 @@ export const PrimaryArmorScene = memo(() => {
const gun = Duel.use((state) => state.protagonist.gun); const gun = Duel.use((state) => state.protagonist.gun);
const armorGltf = useArmor(tank.id); const armorGltf = useArmor(tank.id);
const armorNodes = Object.values(armorGltf.nodes); const armorNodes = Object.values(armorGltf.nodes);
const tankModelDefinition = modelDefinitions[tank.id]; const tankModelDefinition = useTankModelDefinition();
const trackModelDefinition = tankModelDefinition.tracks[track.id]; const trackModelDefinition = tankModelDefinition.tracks[track.id];
const turretModelDefinition = tankModelDefinition.turrets[turret.id]; const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id]; const gunModelDefinition = turretModelDefinition.guns[gun.id];
@@ -32,6 +31,12 @@ export const PrimaryArmorScene = memo(() => {
const gunOrigin = correctZYTuple(turretModelDefinition.gunOrigin); const gunOrigin = correctZYTuple(turretModelDefinition.gunOrigin);
const tankopediaPersistentStore = TankopediaPersistent.useStore(); const tankopediaPersistentStore = TankopediaPersistent.useStore();
console.log(
tankModelDefinition.armor,
turretModelDefinition.armor,
gunModelDefinition.armor,
);
useTankTransform(protagonist, turretContainer, gunContainer); useTankTransform(protagonist, turretContainer, gunContainer);
return ( return (
@@ -5,7 +5,7 @@ import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId';
import { resolveArmor } from '../../../core/blitzkit/resolveThickness'; import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
import { useArmor } from '../../../hooks/useArmor'; import { useArmor } from '../../../hooks/useArmor';
import { useModel } from '../../../hooks/useModel'; import { useModel } from '../../../hooks/useModel';
import { useModelDefinitions } from '../../../hooks/useModelDefinitions'; import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
import { useTankTransform } from '../../../hooks/useTankTransform'; import { useTankTransform } from '../../../hooks/useTankTransform';
import * as Duel from '../../../stores/duel'; import * as Duel from '../../../stores/duel';
import { ModelTankWrapper } from './ModelTankWrapper'; import { ModelTankWrapper } from './ModelTankWrapper';
@@ -23,7 +23,6 @@ interface SpacedArmorSceneProps {
export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => { export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
const wrapper = useRef<Group>(null); const wrapper = useRef<Group>(null);
const modelDefinitions = useModelDefinitions();
const turretContainer = useRef<Group>(null); const turretContainer = useRef<Group>(null);
const gunContainer = useRef<Group>(null); const gunContainer = useRef<Group>(null);
const protagonist = Duel.use((draft) => draft.protagonist); const protagonist = Duel.use((draft) => draft.protagonist);
@@ -35,7 +34,7 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
const { gltf: modelGltf } = useModel(tank.id); const { gltf: modelGltf } = useModel(tank.id);
const armorNodes = Object.values(armorGltf.nodes); const armorNodes = Object.values(armorGltf.nodes);
const modelNodes = Object.values(modelGltf.nodes); const modelNodes = Object.values(modelGltf.nodes);
const tankModelDefinition = modelDefinitions[tank.id]; const tankModelDefinition = useTankModelDefinition();
const trackModelDefinition = tankModelDefinition.tracks[track.id]; const trackModelDefinition = tankModelDefinition.tracks[track.id];
const turretModelDefinition = tankModelDefinition.turrets[turret.id]; const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id]; const gunModelDefinition = turretModelDefinition.guns[gun.id];
@@ -92,9 +92,9 @@ export function SpacedArmorSceneComponent({
...props ...props
}: SpacedArmorSceneComponentProps) { }: SpacedArmorSceneComponentProps) {
const tankopediaEphemeralStore = TankopediaEphemeral.useStore(); const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
const duelStore = Duel.useStore();
const mutateTankopediaEphemeralStore = TankopediaEphemeral.useMutation(); const mutateTankopediaEphemeralStore = TankopediaEphemeral.useMutation();
const camera = useThree((state) => state.camera); const camera = useThree((state) => state.camera);
const duelStore = Duel.useStore();
if (props.static) { if (props.static) {
const x = clamp(thickness / props.thicknessRange.quartile, 0, 1); const x = clamp(thickness / props.thicknessRange.quartile, 0, 1);
@@ -134,7 +134,7 @@ export function SpacedArmorSceneComponent({
depthWrite, depthWrite,
...(clip ? { clippingPlanes: [clip] } : {}), ...(clip ? { clippingPlanes: [clip] } : {}),
}), }),
[], [thickness],
); );
/** /**
@@ -182,7 +182,7 @@ export function SpacedArmorSceneComponent({
); );
return unsubscribe; return unsubscribe;
}, []); }, [thickness]);
return jsxTree(node, { return jsxTree(node, {
material, material,
@@ -5,7 +5,7 @@ import { nameToArmorId } from '../../../core/blitzkit/nameToArmorId';
import { resolveArmor } from '../../../core/blitzkit/resolveThickness'; import { resolveArmor } from '../../../core/blitzkit/resolveThickness';
import { useArmor } from '../../../hooks/useArmor'; import { useArmor } from '../../../hooks/useArmor';
import { useModel } from '../../../hooks/useModel'; import { useModel } from '../../../hooks/useModel';
import { useModelDefinitions } from '../../../hooks/useModelDefinitions'; import { useTankModelDefinition } from '../../../hooks/useTankModelDefinition';
import { useTankTransform } from '../../../hooks/useTankTransform'; import { useTankTransform } from '../../../hooks/useTankTransform';
import * as Duel from '../../../stores/duel'; import * as Duel from '../../../stores/duel';
import * as TankopediaPersistent from '../../../stores/tankopediaPersistent'; import * as TankopediaPersistent from '../../../stores/tankopediaPersistent';
@@ -25,7 +25,6 @@ export interface ThicknessRange {
export const StaticArmorScene = memo<StaticArmorSceneProps>( export const StaticArmorScene = memo<StaticArmorSceneProps>(
({ thicknessRange }) => { ({ thicknessRange }) => {
const wrapper = useRef<Group>(null); const wrapper = useRef<Group>(null);
const awaitedTankModelDefinitions = useModelDefinitions();
const turretContainer = useRef<Group>(null); const turretContainer = useRef<Group>(null);
const gunContainer = useRef<Group>(null); const gunContainer = useRef<Group>(null);
const protagonist = Duel.use((draft) => draft.protagonist); const protagonist = Duel.use((draft) => draft.protagonist);
@@ -37,7 +36,7 @@ export const StaticArmorScene = memo<StaticArmorSceneProps>(
const { gltf: modelGltf } = useModel(tank.id); const { gltf: modelGltf } = useModel(tank.id);
const armorNodes = Object.values(armorGltf.nodes); const armorNodes = Object.values(armorGltf.nodes);
const modelNodes = Object.values(modelGltf.nodes); const modelNodes = Object.values(modelGltf.nodes);
const tankModelDefinition = awaitedTankModelDefinitions[tank.id]; const tankModelDefinition = useTankModelDefinition();
const trackModelDefinition = tankModelDefinition.tracks[track.id]; const trackModelDefinition = tankModelDefinition.tracks[track.id];
const turretModelDefinition = tankModelDefinition.turrets[turret.id]; const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id]; const gunModelDefinition = turretModelDefinition.guns[gun.id];
@@ -58,6 +57,8 @@ export const StaticArmorScene = memo<StaticArmorSceneProps>(
(state) => state.model.visual.showExternalModules, (state) => state.model.visual.showExternalModules,
); );
console.log('yeah');
useTankTransform(protagonist, turretContainer, gunContainer); useTankTransform(protagonist, turretContainer, gunContainer);
return ( return (
+1 -1
View File
@@ -13,7 +13,7 @@ export interface BoundingBox {
min: Vector3Tuple; min: Vector3Tuple;
max: Vector3Tuple; max: Vector3Tuple;
} }
interface ModelDefinition { export interface ModelDefinition {
armor: ModelArmor; armor: ModelArmor;
turretOrigin: Vector3Tuple; turretOrigin: Vector3Tuple;
turretRotation?: InitialTurretRotation; turretRotation?: InitialTurretRotation;
+6
View File
@@ -0,0 +1,6 @@
const pattern = /\d+$/;
export function resolveArmorIndex(name: string) {
const match = name.match(pattern)?.[0];
return match ? parseInt(match) : undefined;
}
+4 -5
View File
@@ -5,7 +5,7 @@ import { resolvePenetrationCoefficient } from '../blitz/resolvePenetrationCoeffi
import { coefficient } from './coefficient'; import { coefficient } from './coefficient';
import { degressiveStat } from './degressiveStat'; import { degressiveStat } from './degressiveStat';
import { EquipmentDefinitions } from './equipmentDefinitions'; import { EquipmentDefinitions } from './equipmentDefinitions';
import { ModelDefinitions } from './modelDefinitions'; import { ModelDefinition, ModelDefinitions } from './modelDefinitions';
import { normalizeBoundingBox } from './normalizeBoundingBox'; import { normalizeBoundingBox } from './normalizeBoundingBox';
import { progressiveStat } from './progressiveStat'; import { progressiveStat } from './progressiveStat';
import { ProvisionDefinitions } from './provisionDefinitions'; import { ProvisionDefinitions } from './provisionDefinitions';
@@ -66,17 +66,16 @@ export function tankCharacteristics(
applySpallLiner: boolean; applySpallLiner: boolean;
}, },
{ {
modelDefinitions, tankModelDefinition,
equipmentDefinitions, equipmentDefinitions,
provisionDefinitions, provisionDefinitions,
}: { }: {
modelDefinitions: ModelDefinitions; tankModelDefinition: ModelDefinition;
equipmentDefinitions: EquipmentDefinitions; equipmentDefinitions: EquipmentDefinitions;
provisionDefinitions: ProvisionDefinitions; provisionDefinitions: ProvisionDefinitions;
}, },
) { ) {
const presetRows = equipmentDefinitions.presets[tank.equipment]; const presetRows = equipmentDefinitions.presets[tank.equipment];
const tankModelDefinition = modelDefinitions[tank.id];
const turretModelDefinition = tankModelDefinition.turrets[turret.id]; const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id]; const gunModelDefinition = turretModelDefinition.guns[gun.id];
-6
View File
@@ -1,6 +0,0 @@
import { modelDefinitions } from '../core/blitzkit/modelDefinitions';
import { useAwait } from './useAwait';
export function useModelDefinitions() {
return useAwait(modelDefinitions);
}
+7
View File
@@ -0,0 +1,7 @@
'use client';
import * as TankopediaEphemeral from '../stores/tankopediaEphemeral';
export function useTankModelDefinition() {
return TankopediaEphemeral.use((state) => state.model);
}
+2 -4
View File
@@ -3,23 +3,21 @@ import { Euler, Group, Vector3 } from 'three';
import { degToRad } from 'three/src/math/MathUtils'; import { degToRad } from 'three/src/math/MathUtils';
import { I_HAT, J_HAT, K_HAT } from '../constants/axis'; import { I_HAT, J_HAT, K_HAT } from '../constants/axis';
import { correctZYTuple } from '../core/blitz/correctZYTuple'; import { correctZYTuple } from '../core/blitz/correctZYTuple';
import { modelDefinitions } from '../core/blitzkit/modelDefinitions';
import { import {
ModelTransformEventData, ModelTransformEventData,
modelTransformEvent, modelTransformEvent,
} from '../core/blitzkit/modelTransform'; } from '../core/blitzkit/modelTransform';
import { DuelMember } from '../stores/duel'; import { DuelMember } from '../stores/duel';
import { useAwait } from './useAwait'; import { useTankModelDefinition } from './useTankModelDefinition';
export function useTankTransform( export function useTankTransform(
member: DuelMember, member: DuelMember,
turretContainer: RefObject<Group>, turretContainer: RefObject<Group>,
gunContainer: RefObject<Group>, gunContainer: RefObject<Group>,
) { ) {
const awaitedModelDefinitions = useAwait(modelDefinitions); const tankModelDefinition = useTankModelDefinition();
useEffect(() => { useEffect(() => {
const tankModelDefinition = awaitedModelDefinitions[member.tank.id];
const trackModelDefinition = tankModelDefinition.tracks[member.track.id]; const trackModelDefinition = tankModelDefinition.tracks[member.track.id];
const turretModelDefinition = tankModelDefinition.turrets[member.turret.id]; const turretModelDefinition = tankModelDefinition.turrets[member.turret.id];
+4 -1
View File
@@ -5,12 +5,14 @@ import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware'; import { subscribeWithSelector } from 'zustand/middleware';
import { ArmorType } from '../components/Armor/components/SpacedArmorScene'; import { ArmorType } from '../components/Armor/components/SpacedArmorScene';
import { ExternalModuleVariant } from '../components/Armor/components/SpacedArmorSceneComponent'; import { ExternalModuleVariant } from '../components/Armor/components/SpacedArmorSceneComponent';
import { ModelDefinition } from '../core/blitzkit/modelDefinitions';
import { createNextSafeStore } from '../core/zustand/createNextSafeStore'; import { createNextSafeStore } from '../core/zustand/createNextSafeStore';
interface TankopediaEphemeral { interface TankopediaEphemeral {
shot?: Shot; shot?: Shot;
skills: Record<string, number>; skills: Record<string, number>;
controlsEnabled: boolean; controlsEnabled: boolean;
model: ModelDefinition;
highlightArmor?: { highlightArmor?: {
name: string; name: string;
point: Vector3; point: Vector3;
@@ -86,10 +88,11 @@ export type ArmorPiercingLayer =
}; };
export const { Provider, use, useMutation, useStore } = createNextSafeStore( export const { Provider, use, useMutation, useStore } = createNextSafeStore(
() => (model: ModelDefinition) =>
create<TankopediaEphemeral>()( create<TankopediaEphemeral>()(
subscribeWithSelector<TankopediaEphemeral>(() => ({ subscribeWithSelector<TankopediaEphemeral>(() => ({
skills: {}, skills: {},
model,
controlsEnabled: true, controlsEnabled: true,
})), })),
), ),