mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: wireframe
This commit is contained in:
1 parent
8ba56069c4
commit
15cf4ec0f1
4 files changed
+91
-98
No files matched your search
@@ -4,7 +4,7 @@ import Link from 'next/link';
|
|||||||
import { Suspense, use, useEffect, useMemo, useState } from 'react';
|
import { Suspense, use, useEffect, useMemo, useState } from 'react';
|
||||||
import { classIcons } from '../../../../../../components/ClassIcon';
|
import { classIcons } from '../../../../../../components/ClassIcon';
|
||||||
import { ThicknessRange } from '../../../../../../components/StaticArmor/components/StaticArmorScene';
|
import { ThicknessRange } from '../../../../../../components/StaticArmor/components/StaticArmorScene';
|
||||||
import { modelDefinitions } from '../../../../../../core/blitzkit/modelDefinitions';
|
import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration';
|
||||||
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';
|
||||||
import { useFullScreen } from '../../../../../../hooks/useFullScreen';
|
import { useFullScreen } from '../../../../../../hooks/useFullScreen';
|
||||||
@@ -35,48 +35,22 @@ export function HeroSection({ id }: HeroSectionProps) {
|
|||||||
? 'amber'
|
? 'amber'
|
||||||
: undefined;
|
: undefined;
|
||||||
const awaitedTankDefinitions = use(tankDefinitions);
|
const awaitedTankDefinitions = use(tankDefinitions);
|
||||||
const awaitedModelDefinitions = use(modelDefinitions);
|
|
||||||
const tank = awaitedTankDefinitions[id];
|
const tank = awaitedTankDefinitions[id];
|
||||||
const thicknessRange = useMemo(() => {
|
const thicknessRange = useMemo(() => {
|
||||||
const thicknesses = Object.values(awaitedTankDefinitions)
|
const entries = Object.values(awaitedTankDefinitions);
|
||||||
.filter((thisTank) => thisTank.tier === tank.tier)
|
const filtered = entries.filter((thisTank) => thisTank.tier === tank.tier);
|
||||||
.map((tank) => ({
|
const value =
|
||||||
model: awaitedModelDefinitions[tank.id],
|
filtered.reduce((accumulator, thisTank) => {
|
||||||
id: tank.id,
|
return (
|
||||||
}))
|
accumulator +
|
||||||
.filter(Boolean)
|
resolveNearPenetration(
|
||||||
.map(({ model, id }) => {
|
thisTank.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration,
|
||||||
const tankThicknesses = Object.values(model.armor.thickness);
|
)
|
||||||
const turretThicknesses = Object.values(model.turrets)
|
);
|
||||||
.map((turret) => {
|
}, 0) /
|
||||||
const turretThicknesses = Object.values(turret.armor.thickness);
|
((4 / 3) * filtered.length);
|
||||||
const gunThicknesses = Object.values(turret.guns)
|
|
||||||
.map((gun) => Object.values(gun.armor.thickness))
|
|
||||||
.flat();
|
|
||||||
|
|
||||||
return [...turretThicknesses, ...gunThicknesses];
|
return { value } satisfies ThicknessRange;
|
||||||
})
|
|
||||||
.flat();
|
|
||||||
const totalThicknesses = [...tankThicknesses, ...turretThicknesses];
|
|
||||||
|
|
||||||
return totalThicknesses;
|
|
||||||
})
|
|
||||||
.flat();
|
|
||||||
|
|
||||||
const sortedThicknesses = thicknesses.sort((a, b) => a - b);
|
|
||||||
const quartileIndex = (1 - 2 ** -6) * (sortedThicknesses.length + 1) - 1;
|
|
||||||
let quartile: number;
|
|
||||||
|
|
||||||
if (Number.isInteger(quartileIndex)) {
|
|
||||||
quartile = sortedThicknesses[quartileIndex];
|
|
||||||
} else {
|
|
||||||
quartile =
|
|
||||||
(sortedThicknesses[Math.floor(quartileIndex)] +
|
|
||||||
sortedThicknesses[Math.ceil(quartileIndex)]) /
|
|
||||||
2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { quartile } satisfies ThicknessRange;
|
|
||||||
}, [tank.tier]);
|
}, [tank.tier]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
|
|||||||
>
|
>
|
||||||
<Flex direction="column" align="end" justify="between">
|
<Flex direction="column" align="end" justify="between">
|
||||||
<Text color="gray" size="1">
|
<Text color="gray" size="1">
|
||||||
{thicknessRange.quartile.toFixed(0)}
|
{thicknessRange.value.toFixed(0)}
|
||||||
</Text>
|
</Text>
|
||||||
<Flex gap="1">
|
<Flex gap="1">
|
||||||
{showPrimaryArmor ? <EyeOpenIcon /> : <EyeClosedIcon />}
|
{showPrimaryArmor ? <EyeOpenIcon /> : <EyeClosedIcon />}
|
||||||
@@ -85,7 +85,7 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
|
|||||||
>
|
>
|
||||||
<Flex direction="column" align="end" justify="between">
|
<Flex direction="column" align="end" justify="between">
|
||||||
<Text color="gray" size="1">
|
<Text color="gray" size="1">
|
||||||
{thicknessRange.quartile.toFixed(0)}
|
{thicknessRange.value.toFixed(0)}
|
||||||
</Text>
|
</Text>
|
||||||
<Flex gap="1">
|
<Flex gap="1">
|
||||||
{showSpacedArmor ? <EyeOpenIcon /> : <EyeClosedIcon />}
|
{showSpacedArmor ? <EyeOpenIcon /> : <EyeClosedIcon />}
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ import {
|
|||||||
DoubleSide,
|
DoubleSide,
|
||||||
FrontSide,
|
FrontSide,
|
||||||
Intersection,
|
Intersection,
|
||||||
|
LineBasicMaterial,
|
||||||
|
Mesh,
|
||||||
MeshBasicMaterial,
|
MeshBasicMaterial,
|
||||||
|
MeshStandardMaterial,
|
||||||
Object3D,
|
Object3D,
|
||||||
Plane,
|
Plane,
|
||||||
Quaternion,
|
Quaternion,
|
||||||
@@ -97,7 +100,7 @@ export function SpacedArmorSceneComponent({
|
|||||||
const camera = useThree((state) => state.camera);
|
const camera = useThree((state) => state.camera);
|
||||||
|
|
||||||
if (props.static) {
|
if (props.static) {
|
||||||
const x = clamp(thickness / props.thicknessRange.quartile, 0, 1);
|
const x = clamp(thickness / props.thicknessRange.value, 0, 1);
|
||||||
|
|
||||||
let color: Color;
|
let color: Color;
|
||||||
let opacity: number;
|
let opacity: number;
|
||||||
@@ -125,9 +128,9 @@ export function SpacedArmorSceneComponent({
|
|||||||
|
|
||||||
opacity = clamp(opacity, 0, 1);
|
opacity = clamp(opacity, 0, 1);
|
||||||
|
|
||||||
const material = useMemo(
|
const surfaceMaterial = useMemo(
|
||||||
() =>
|
() =>
|
||||||
new MeshBasicMaterial({
|
new MeshStandardMaterial({
|
||||||
color,
|
color,
|
||||||
opacity,
|
opacity,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
@@ -136,6 +139,13 @@ export function SpacedArmorSceneComponent({
|
|||||||
}),
|
}),
|
||||||
[thickness],
|
[thickness],
|
||||||
);
|
);
|
||||||
|
const outlineMaterial = useMemo(
|
||||||
|
() =>
|
||||||
|
new LineBasicMaterial({
|
||||||
|
color: color.clone().multiplyScalar(1 - 2 ** -3),
|
||||||
|
}),
|
||||||
|
[thickness],
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hook inside an if statement?? don't panic! I assure you the static prop
|
* hook inside an if statement?? don't panic! I assure you the static prop
|
||||||
@@ -147,10 +157,11 @@ export function SpacedArmorSceneComponent({
|
|||||||
|
|
||||||
if (selectedName === undefined) {
|
if (selectedName === undefined) {
|
||||||
// nothing selected, go back to defaults
|
// nothing selected, go back to defaults
|
||||||
material.opacity = opacity;
|
surfaceMaterial.opacity = opacity;
|
||||||
material.color = color;
|
surfaceMaterial.color = color;
|
||||||
material.depthWrite = props.type !== ArmorType.External;
|
surfaceMaterial.depthWrite = props.type !== ArmorType.External;
|
||||||
material.side = FrontSide;
|
surfaceMaterial.side = FrontSide;
|
||||||
|
outlineMaterial.visible = true;
|
||||||
} else if (
|
} else if (
|
||||||
selectedName === props.name ||
|
selectedName === props.name ||
|
||||||
(props.name.startsWith('chassis_') &&
|
(props.name.startsWith('chassis_') &&
|
||||||
@@ -161,19 +172,21 @@ export function SpacedArmorSceneComponent({
|
|||||||
!selectedName.includes('_armor_'))
|
!selectedName.includes('_armor_'))
|
||||||
) {
|
) {
|
||||||
// this selected, stand out!
|
// this selected, stand out!
|
||||||
material.opacity = 1;
|
surfaceMaterial.opacity = 1;
|
||||||
material.color = color;
|
surfaceMaterial.color = color;
|
||||||
material.depthWrite = true;
|
surfaceMaterial.depthWrite = true;
|
||||||
material.side = DoubleSide;
|
surfaceMaterial.side = DoubleSide;
|
||||||
|
outlineMaterial.visible = true;
|
||||||
} else {
|
} else {
|
||||||
// something else selected, become background
|
// something else selected, become background
|
||||||
material.opacity = 1 / 4;
|
surfaceMaterial.opacity = 1 / 4;
|
||||||
material.color = unselectedColor;
|
surfaceMaterial.color = unselectedColor;
|
||||||
material.depthWrite = props.type !== ArmorType.External;
|
surfaceMaterial.depthWrite = props.type !== ArmorType.External;
|
||||||
material.side = FrontSide;
|
surfaceMaterial.side = FrontSide;
|
||||||
|
outlineMaterial.visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
material.transparent = material.opacity < 1;
|
surfaceMaterial.transparent = surfaceMaterial.opacity < 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsubscribe = tankopediaEphemeralStore.subscribe(
|
const unsubscribe = tankopediaEphemeralStore.subscribe(
|
||||||
@@ -184,44 +197,53 @@ export function SpacedArmorSceneComponent({
|
|||||||
return unsubscribe;
|
return unsubscribe;
|
||||||
}, [thickness]);
|
}, [thickness]);
|
||||||
|
|
||||||
return jsxTree(node, {
|
return (
|
||||||
material,
|
<>
|
||||||
// renderOrder,
|
{jsxTree(node, {
|
||||||
userData: {
|
material: surfaceMaterial,
|
||||||
type: props.type,
|
userData: {
|
||||||
variant: props.type === ArmorType.External ? props.variant : 'gun',
|
|
||||||
thickness,
|
|
||||||
} satisfies ArmorUserData,
|
|
||||||
|
|
||||||
onClick(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
const bounds = new Box3().setFromObject(event.object);
|
|
||||||
const point = bounds.min
|
|
||||||
.clone()
|
|
||||||
.add(bounds.max)
|
|
||||||
.divideScalar(2)
|
|
||||||
.setY(bounds.max.y);
|
|
||||||
const cameraNormal = camera.position.clone().sub(point).normalize();
|
|
||||||
const surfaceNormal = event
|
|
||||||
.normal!.clone()
|
|
||||||
.applyQuaternion(event.object.getWorldQuaternion(new Quaternion()));
|
|
||||||
const angle = surfaceNormal.angleTo(cameraNormal);
|
|
||||||
const thicknessAngled = thickness / Math.sin(Math.PI / 2 - angle);
|
|
||||||
|
|
||||||
mutateTankopediaEphemeralStore((draft) => {
|
|
||||||
draft.highlightArmor = {
|
|
||||||
type: props.type,
|
type: props.type,
|
||||||
name: props.name,
|
variant: props.type === ArmorType.External ? props.variant : 'gun',
|
||||||
point,
|
|
||||||
thickness,
|
thickness,
|
||||||
thicknessAngled,
|
} satisfies ArmorUserData,
|
||||||
angle,
|
|
||||||
color: `#${color.getHexString()}`,
|
onClick(event) {
|
||||||
};
|
event.stopPropagation();
|
||||||
});
|
|
||||||
},
|
const bounds = new Box3().setFromObject(event.object);
|
||||||
});
|
const point = bounds.min
|
||||||
|
.clone()
|
||||||
|
.add(bounds.max)
|
||||||
|
.divideScalar(2)
|
||||||
|
.setY(bounds.max.y);
|
||||||
|
const cameraNormal = camera.position.clone().sub(point).normalize();
|
||||||
|
const surfaceNormal = event
|
||||||
|
.normal!.clone()
|
||||||
|
.applyQuaternion(
|
||||||
|
event.object.getWorldQuaternion(new Quaternion()),
|
||||||
|
);
|
||||||
|
const angle = surfaceNormal.angleTo(cameraNormal);
|
||||||
|
const thicknessAngled = thickness / Math.sin(Math.PI / 2 - angle);
|
||||||
|
|
||||||
|
mutateTankopediaEphemeralStore((draft) => {
|
||||||
|
draft.highlightArmor = {
|
||||||
|
type: props.type,
|
||||||
|
name: props.name,
|
||||||
|
point,
|
||||||
|
thickness,
|
||||||
|
thicknessAngled,
|
||||||
|
angle,
|
||||||
|
color: `#${color.getHexString()}`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
|
||||||
|
<lineSegments material={outlineMaterial}>
|
||||||
|
<edgesGeometry args={[(node as Mesh).geometry]} />
|
||||||
|
</lineSegments>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const shoot = useCallback(
|
const shoot = useCallback(
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ interface StaticArmorSceneProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ThicknessRange {
|
export interface ThicknessRange {
|
||||||
// max: number;
|
value: number;
|
||||||
quartile: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StaticArmorScene = memo<StaticArmorSceneProps>(
|
export const StaticArmorScene = memo<StaticArmorSceneProps>(
|
||||||
@@ -57,8 +56,6 @@ 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 (
|
||||||
|
|||||||
Reference in new issue
Block a user