feat: isolated duel

This commit is contained in:
TrèsAbhi committed 2024-01-22 18:04:13 -06:00
1 parent fce5f19100
commit 0b5c2e42fb
15 files changed
+218 -249

No files matched your search

@@ -5,12 +5,12 @@ import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawL
import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions';
import { modelTransformEvent } from '../../../../../../core/blitzkrieg/modelTransform';
import { Pose, poseEvent } from '../../../../../../core/blitzkrieg/pose';
import { useDuel } from '../../../../../../stores/duel';
import {
TankopediaMode,
mutateTankopediaTemporary,
useTankopediaTemporary,
} from '../../../../../../stores/tankopedia';
import { Duel } from '../../page';
import { Controls } from '../Control';
import { Lighting } from '../Lighting';
import { RotationInputs } from '../QuickInputs';
@@ -20,21 +20,17 @@ import { Options } from './components/Options';
import { TankArmor } from './components/TankArmor';
import { TankModel } from './components/TankModel';
interface TankSandboxProps {
duel: Duel;
}
export function TankSandbox({ duel }: TankSandboxProps) {
export function TankSandbox() {
const canvas = useRef<HTMLCanvasElement>(null);
const awaitedModelDefinitions = use(modelDefinitions);
const canvasWrapper = useRef<HTMLDivElement>(null);
const [isFullScreen, setIsFullScreen] = useState(false);
const mode = useTankopediaTemporary((state) => state.mode);
const tankModelDefinition = awaitedModelDefinitions[duel.protagonist.tank.id];
const protagonist = useDuel((state) => state.protagonist!);
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
const turretModelDefinition =
tankModelDefinition.turrets[duel.protagonist.turret.id];
const gunModelDefinition =
turretModelDefinition.guns[duel.protagonist.gun.id];
tankModelDefinition.turrets[protagonist.turret.id];
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
function handlePointerDown() {
window.addEventListener('pointermove', handlePointerMove);
@@ -119,8 +115,6 @@ export function TankSandbox({ duel }: TankSandboxProps) {
});
useEffect(() => {
if (!duel.protagonist) return void 0;
mutateTankopediaTemporary((draft) => {
[draft.model.pose.pitch, draft.model.pose.yaw] = applyPitchYawLimits(
draft.model.pose.pitch,
@@ -129,7 +123,7 @@ export function TankSandbox({ duel }: TankSandboxProps) {
turretModelDefinition.yaw,
);
});
}, [duel.protagonist.gun, duel.protagonist.turret]);
}, [protagonist.gun, protagonist.turret]);
return (
<Theme radius={isFullScreen ? 'none' : undefined}>
@@ -170,21 +164,20 @@ export function TankSandbox({ duel }: TankSandboxProps) {
camera={{ fov: 25 }}
onPointerDown={handlePointerDown}
>
<Controls duel={duel} />
<Controls />
<SceneProps />
<Suspense fallback={<ModelLoader />}>
<Lighting duel={duel} />
<TankModel duel={duel} />
<TankArmor duel={duel} />
{/* <SpacedArmorDepth duel={duel} /> */}
<Lighting />
<TankModel />
<TankArmor />
</Suspense>
</Canvas>
</div>
<Options canvas={canvasWrapper} isFullScreen={isFullScreen} />
<RotationInputs duel={duel} />
<RotationInputs />
</Flex>
</Theme>
</Card>
@@ -11,14 +11,11 @@ import { nameToArmorId } from '../../../../../../../../../core/blitzkrieg/nameTo
import { resolveArmor } from '../../../../../../../../../core/blitzkrieg/resolveThickness';
import { useArmor } from '../../../../../../../../../hooks/useArmor';
import { useModelDefinitions } from '../../../../../../../../../hooks/useModelDefinitions';
import { useDuel } from '../../../../../../../../../stores/duel';
import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia';
import { Duel } from '../../../../../page';
interface ArmorHighlightingProps {
duel: Duel;
}
export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
export function ArmorHighlighting() {
const protagonist = useDuel((state) => state.protagonist!);
const wrapper = useRef<Group>(null);
const modelDefinitions = useModelDefinitions();
const turretContainer = useRef<Group>(null);
@@ -88,13 +85,12 @@ export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
return unsubscribe;
});
const armorGltf = useArmor(duel.protagonist.tank.id);
const armorGltf = useArmor(protagonist.tank.id);
const armorNodes = Object.values(armorGltf.nodes);
const tankModelDefinition = modelDefinitions[duel.protagonist.tank.id];
const tankModelDefinition = modelDefinitions[protagonist.tank.id];
const turretModelDefinition =
tankModelDefinition.turrets[duel.protagonist.turret.id];
const gunModelDefinition =
turretModelDefinition.guns[duel.protagonist.gun.id];
tankModelDefinition.turrets[protagonist.turret.id];
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
const hullOrigin = new Vector3(
tankModelDefinition.hullOrigin[0],
tankModelDefinition.hullOrigin[1],
@@ -137,7 +133,6 @@ export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
return (
<ArmorMesh
maxThickness={maxThickness}
duel={duel}
key={node.uuid}
geometry={(node as Mesh).geometry}
thickness={thickness}
@@ -162,7 +157,6 @@ export function ArmorHighlighting({ duel }: ArmorHighlightingProps) {
return (
<ArmorMesh
maxThickness={maxThickness}
duel={duel}
key={node.uuid}
geometry={(node as Mesh).geometry}
position={turretOrigin}
@@ -12,12 +12,8 @@ import { resolveArmor } from '../../../../../../../../../core/blitzkrieg/resolve
import { useArmor } from '../../../../../../../../../hooks/useArmor';
import { useModel } from '../../../../../../../../../hooks/useModel';
import { useModelDefinitions } from '../../../../../../../../../hooks/useModelDefinitions';
import { useDuel } from '../../../../../../../../../stores/duel';
import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia';
import { Duel } from '../../../../../page';
interface ExternalModuleMaskProps {
duel: Duel;
}
/**
* When rendered, generates a mask and thickness buffer for external modules.
@@ -26,7 +22,8 @@ interface ExternalModuleMaskProps {
* - B: no data
* - A: 1 means is armor; 0 is background
*/
export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
export const ExternalModuleMask = memo(() => {
const protagonist = useDuel((state) => state.protagonist!);
const wrapper = useRef<Group>(null);
const modelDefinitions = useModelDefinitions();
const turretContainer = useRef<Group>(null);
@@ -117,16 +114,15 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(({ duel }) => {
return unsubscribe;
});
const armorGltf = useArmor(duel.protagonist.tank.id);
const { gltf: modelGltf } = useModel(duel.protagonist.tank.id);
const armorGltf = useArmor(protagonist.tank.id);
const { gltf: modelGltf } = useModel(protagonist.tank.id);
const armorNodes = Object.values(armorGltf.nodes);
const modelNodes = Object.values(modelGltf.nodes);
const tankModelDefinition = modelDefinitions[duel.protagonist.tank.id];
const tankModelDefinition = modelDefinitions[protagonist.tank.id];
const turretModelDefinition =
tankModelDefinition.turrets[duel.protagonist.turret.id];
const gunModelDefinition =
turretModelDefinition.guns[duel.protagonist.gun.id];
tankModelDefinition.turrets[protagonist.turret.id];
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
const maxThickness = Math.max(
tankModelDefinition.trackThickness,
gunModelDefinition.barrelThickness,
@@ -12,12 +12,8 @@ import { resolveArmor } from '../../../../../../../../../core/blitzkrieg/resolve
import { useArmor } from '../../../../../../../../../hooks/useArmor';
import { useModel } from '../../../../../../../../../hooks/useModel';
import { useModelDefinitions } from '../../../../../../../../../hooks/useModelDefinitions';
import { useDuel } from '../../../../../../../../../stores/duel';
import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia';
import { Duel } from '../../../../../page';
interface SpacedArmorDepthProps {
duel: Duel;
}
/**
* When rendered, this component provides the depth buffer for the armor which
@@ -30,7 +26,8 @@ interface SpacedArmorDepthProps {
* - B: no data
* - A: 1 means is armor; 0 is background
*/
export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
export const SpacedArmorDepth = memo(() => {
const protagonist = useDuel((state) => state.protagonist!);
const wrapper = useRef<Group>(null);
const modelDefinitions = useModelDefinitions();
const turretContainer = useRef<Group>(null);
@@ -123,16 +120,15 @@ export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(({ duel }) => {
return unsubscribe;
});
const armorGltf = useArmor(duel.protagonist.tank.id);
const { gltf: modelGltf } = useModel(duel.protagonist.tank.id);
const armorGltf = useArmor(protagonist.tank.id);
const { gltf: modelGltf } = useModel(protagonist.tank.id);
const armorNodes = Object.values(armorGltf.nodes);
const modelNodes = Object.values(modelGltf.nodes);
const tankModelDefinition = modelDefinitions[duel.protagonist.tank.id];
const tankModelDefinition = modelDefinitions[protagonist.tank.id];
const turretModelDefinition =
tankModelDefinition.turrets[duel.protagonist.turret.id];
const gunModelDefinition =
turretModelDefinition.guns[duel.protagonist.gun.id];
tankModelDefinition.turrets[protagonist.turret.id];
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
const maxThickness = Math.max(
tankModelDefinition.trackThickness,
gunModelDefinition.barrelThickness,
@@ -3,29 +3,24 @@ import { memo } from 'react';
import { DepthTexture, Scene } from 'three';
import { externalModuleMaskRenderTarget } from '../../../../../../../../components/ArmorMesh';
import { spacedArmorDepthRenderTarget } from '../../../../../../../../components/ArmorMesh/components/SpacedArmorDepth';
import { Duel } from '../../../../page';
import { ArmorHighlighting } from './components/ArmorHighlighting';
import { ExternalModuleMask } from './components/ExternalModuleMask';
import { SpacedArmorDepth } from './components/SpacedArmorDepth';
interface TankArmorProps {
duel: Duel;
}
export const TankArmor = memo<TankArmorProps>(({ duel }) => {
export const TankArmor = memo(() => {
const externalModuleMaskScene = new Scene();
const spacedArmorDepthScene = new Scene();
const armorHighlightingScene = new Scene();
const externalModuleMaskPortal = createPortal(
<ExternalModuleMask duel={duel} />,
<ExternalModuleMask />,
externalModuleMaskScene,
);
const spacedArmorDepthPortal = createPortal(
<SpacedArmorDepth duel={duel} />,
<SpacedArmorDepth />,
spacedArmorDepthScene,
);
const armorHighlightingPortal = createPortal(
<ArmorHighlighting duel={duel} />,
<ArmorHighlighting />,
armorHighlightingScene,
);
@@ -22,21 +22,17 @@ import {
import { resolveJsxTree } from '../../../../../../../core/blitzkrieg/resolveJsxTree';
import { normalizeAnglePI } from '../../../../../../../core/math/normalizeAngle180';
import { useModel } from '../../../../../../../hooks/useModel';
import { useDuel } from '../../../../../../../stores/duel';
import mutateTankopediaPersistent, {
mutateTankopediaTemporary,
useTankopediaTemporary,
} from '../../../../../../../stores/tankopedia';
import { Duel } from '../../../page';
interface TankModelProps {
duel: Duel;
}
export const TankModel = memo(({ duel: { protagonist } }: TankModelProps) => {
export const TankModel = memo(() => {
const [awaitedModelDefinitions, setAwaitedModelDefinitions] = useState<
ModelDefinitions | undefined
>(undefined);
const protagonist = useDuel((draft) => draft.protagonist!);
const canvas = useThree((state) => state.gl.domElement);
const physical = useTankopediaTemporary((state) => state.model.pose);
const hullContainer = useRef<Group>(null);