mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-11-14 13:16:11 +01:00
feat: isolated duel
This commit is contained in:
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>
|
||||
|
||||
+7
-13
@@ -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}
|
||||
|
||||
+8
-12
@@ -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,
|
||||
|
||||
+8
-12
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user