diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json
index 6b4d23528..6dfbe5b1c 100644
--- a/packages/i18n/strings/en.json
+++ b/packages/i18n/strings/en.json
@@ -855,6 +855,10 @@
"tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.",
"sandbox": {
+ "dissector": {
+ "name": "Dissector"
+ },
+
"poses": {
"hull_down": "Hull down",
"face_hug": "Face hug",
diff --git a/packages/website/src/components/Armor/components/Dissector.tsx b/packages/website/src/components/Armor/components/Dissector.tsx
new file mode 100644
index 000000000..f7a49a326
--- /dev/null
+++ b/packages/website/src/components/Armor/components/Dissector.tsx
@@ -0,0 +1,17 @@
+import { memo } from 'react';
+import { DissectorSceneComponent } from './DissectorSceneComponent';
+
+export const Dissector = memo(() => {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+});
diff --git a/packages/website/src/components/Armor/components/DissectorSceneComponent.tsx b/packages/website/src/components/Armor/components/DissectorSceneComponent.tsx
new file mode 100644
index 000000000..c55386ca2
--- /dev/null
+++ b/packages/website/src/components/Armor/components/DissectorSceneComponent.tsx
@@ -0,0 +1,89 @@
+import { asset } from '@blitzkit/core';
+import { useGLTF } from '@react-three/drei';
+import {
+ Color,
+ DoubleSide,
+ LineBasicMaterial,
+ Material,
+ MeshStandardMaterial,
+} from 'three';
+import { jsxTree } from '../../../core/blitzkit/jsxTree';
+import { Duel } from '../../../stores/duel';
+import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
+import { groupNameRegex } from './StaticArmorSceneComponent';
+
+interface DissectorSceneComponentProps {
+ group: string;
+}
+
+const armorColor = new Color().multiplyScalar(1 / 16);
+
+const moduleColor: Record = {
+ Armor: armorColor,
+ ArmorScreen: armorColor,
+
+ Commander: new Color('white'),
+ Driver: new Color('white'),
+ Gunner1: new Color('white'),
+ Loader1: new Color('white'),
+
+ SurveyingDevice: new Color('purple'),
+ Radio: new Color('indigo'),
+ Gun: new Color('pink'),
+ FuelTank: new Color('yellow'),
+ Engine: new Color('orange'),
+ AmmoBay: new Color('gold'),
+ LeftTrack: new Color('green'),
+ RightTrack: new Color('green'),
+ TurretRotator: new Color('blue'),
+ Transmission: new Color('cyan'),
+};
+const moduleOutlineColor: Record = {};
+
+for (const key in moduleColor) {
+ moduleOutlineColor[key] = moduleColor[key].clone().multiplyScalar(4);
+}
+
+export function DissectorSceneComponent({
+ group,
+}: DissectorSceneComponentProps) {
+ const tank = Duel.use((state) => state.protagonist.tank);
+ const gltf = useGLTF(asset(`tanks/${tank.id}/collision/${group}.glb`));
+ const armor = TankopediaEphemeral.use((state) => state.armor);
+ const groupName = group.match(groupNameRegex)![1];
+ const groupArmor = armor.groups[groupName];
+
+ return jsxTree(gltf.scene, {
+ mesh(mesh, props) {
+ if (!(mesh.material instanceof Material)) {
+ return null;
+ }
+
+ const armorName = mesh.material.name;
+ const armorPlate = groupArmor.armors[armorName];
+
+ if (!(armorPlate.type in moduleColor)) {
+ throw new TypeError(`Unhandled armor type: ${armorPlate.type}`);
+ }
+
+ const material = new MeshStandardMaterial({
+ side: DoubleSide,
+ color: moduleColor[armorPlate.type],
+ });
+ const outlineMaterial = new LineBasicMaterial({
+ side: DoubleSide,
+ color: moduleOutlineColor[armorPlate.type],
+ });
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+ },
+ });
+}
diff --git a/packages/website/src/components/Armor/components/StaticArmor.tsx b/packages/website/src/components/Armor/components/StaticArmor.tsx
index 3191e7023..a31ba44fd 100644
--- a/packages/website/src/components/Armor/components/StaticArmor.tsx
+++ b/packages/website/src/components/Armor/components/StaticArmor.tsx
@@ -1,62 +1,19 @@
import { memo } from 'react';
-import { StaticArmorSceneComponents } from './StaticArmorSceneComponents';
+import { StaticArmorSceneComponent } from './StaticArmorSceneComponent';
export interface ThicknessRange {
value: number;
}
export const StaticArmor = memo(() => {
- // const wrapper = useRef(null);
- // const turretContainer = useRef(null);
- // const gunContainer = useRef(null);
- // const protagonist = Duel.use((draft) => draft.protagonist);
- // const tank = Duel.use((state) => state.protagonist.tank);
- // const track = Duel.use((state) => state.protagonist.track);
- // const turret = Duel.use((state) => state.protagonist.turret);
- // const gun = Duel.use((state) => state.protagonist.gun);
- // const { gltf: armorGltf } = useArmor(tank.id);
- // const { gltf: modelGltf } = useModel(tank.id);
- // const armorNodes = Object.values(armorGltf.nodes);
- // const modelNodes = Object.values(modelGltf.nodes);
- // const tankModelDefinition = useTankModelDefinition();
- // const trackModelDefinition = tankModelDefinition.tracks[track.id];
- // const turretModelDefinition = tankModelDefinition.turrets[turret.id];
- // const gunModelDefinition =
- // turretModelDefinition.guns[gun.gun_type!.value.base.id];
- // const hullOrigin = correctZYTuple(trackModelDefinition.origin);
- // const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
- // const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
- // const mutateDuel = Duel.useMutation();
- // const canvas = useThree((state) => state.gl.domElement);
- // const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
- // const duelStore = Duel.useStore();
- // const maskOrigin =
- // gunModelDefinition.mask === undefined
- // ? undefined
- // : gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y;
- // const showPrimaryArmor = TankopediaPersistent.use(
- // (state) => state.showPrimaryArmor,
- // );
- // const showSpacedArmor = TankopediaPersistent.use(
- // (state) => state.showSpacedArmor,
- // );
- // const showExternalModules = TankopediaPersistent.use(
- // (state) => state.showExternalModules,
- // );
- // const isDynamicArmorActive = Duel.use((state) =>
- // state.protagonist.consumables.includes(73),
- // );
-
- // useTankTransform(track, turret, turretContainer, gunContainer);
-
return (
-
-
+
+
-
+
-
+
diff --git a/packages/website/src/components/Armor/components/StaticArmorSceneComponents.tsx b/packages/website/src/components/Armor/components/StaticArmorSceneComponent.tsx
similarity index 94%
rename from packages/website/src/components/Armor/components/StaticArmorSceneComponents.tsx
rename to packages/website/src/components/Armor/components/StaticArmorSceneComponent.tsx
index 801880fed..69c2857b3 100644
--- a/packages/website/src/components/Armor/components/StaticArmorSceneComponents.tsx
+++ b/packages/website/src/components/Armor/components/StaticArmorSceneComponent.tsx
@@ -12,16 +12,16 @@ import { Duel } from '../../../stores/duel';
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
import { TankopediaPersistent } from '../../../stores/tankopediaPersistent';
-interface StaticArmorSceneComponentsProps {
+interface StaticArmorSceneComponentsProp {
group: string;
}
const moduleColor = new Color(192 / 255, 192 / 255, 192 / 255);
-const groupNameRegex = /([a-z]+)(_\d+)?/;
+export const groupNameRegex = /([a-z]+)(_\d+)?/;
-export function StaticArmorSceneComponents({
+export function StaticArmorSceneComponent({
group,
-}: StaticArmorSceneComponentsProps) {
+}: StaticArmorSceneComponentsProp) {
const tank = Duel.use((state) => state.protagonist.tank);
const showModules = TankopediaPersistent.use((state) => state.showModules);
const showArmorScreen = TankopediaPersistent.use(
diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/Options/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/Options/index.tsx
index 191fe44e7..fef2ad1ef 100644
--- a/packages/website/src/components/Tankopedia/HeroSection/components/Options/index.tsx
+++ b/packages/website/src/components/Tankopedia/HeroSection/components/Options/index.tsx
@@ -395,6 +395,15 @@ export function Options({ canvas }: OptionsProps) {
+
+
+
+
+
+
+
{/*
diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Lighting.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Lighting.tsx
index a5bf78aa3..3cf039cf8 100644
--- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Lighting.tsx
+++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Lighting.tsx
@@ -20,7 +20,8 @@ export function Lighting() {
environmentRotation={new Euler(0, degToRad(180), 0)}
/>
)}
- {display === TankopediaDisplay.StaticArmor && (
+ {(display === TankopediaDisplay.StaticArmor ||
+ display === TankopediaDisplay.Dissector) && (
)}
{display === TankopediaDisplay.ShootingRange && (
diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx
index e411c98a0..ebfc6f23e 100644
--- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx
+++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx
@@ -1,12 +1,14 @@
import { BLITZKIT_TANK_ICON_SIZE } from '@blitzkit/core';
import { Canvas } from '@react-three/fiber';
import { forwardRef, Suspense, useImperativeHandle, useRef } from 'react';
+import { Plane, Vector3 } from 'three';
import { useOnScreen } from '../../../../../hooks/useOnScreen';
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
import { TankopediaPersistent } from '../../../../../stores/tankopediaPersistent';
import { TankopediaDisplay } from '../../../../../stores/tankopediaPersistent/constants';
import { Armor } from '../../../../Armor';
import { ArmorPlateDisplay } from '../../../../Armor/components/ArmorPlateDisplay';
+import { Dissector } from '../../../../Armor/components/Dissector';
import { ShotDisplay } from '../../../../Armor/components/ShotDisplay';
import { StaticArmor } from '../../../../Armor/components/StaticArmor';
import { AutoClear } from './components/AutoClear';
@@ -158,7 +160,7 @@ export const TankSandbox = forwardRef(
: 'never'
}
gl={{
- clippingPlanes: [],
+ clippingPlanes: [new Plane(new Vector3(0, 0, 1))],
localClippingEnabled: true,
preserveDrawingBuffer: true,
}}
@@ -190,6 +192,7 @@ export const TankSandbox = forwardRef(
}>
{display === TankopediaDisplay.DynamicArmor && }
{display === TankopediaDisplay.StaticArmor && }
+ {display === TankopediaDisplay.Dissector && }
diff --git a/packages/website/src/stores/tankopediaEphemeral.ts b/packages/website/src/stores/tankopediaEphemeral.ts
index 671301f0e..2e9af2b75 100644
--- a/packages/website/src/stores/tankopediaEphemeral.ts
+++ b/packages/website/src/stores/tankopediaEphemeral.ts
@@ -137,7 +137,7 @@ export const TankopediaEphemeral = createContextualStore(
armor,
controlsEnabled: true,
xpMultiplier: 1,
- display: TankopediaDisplay.StaticArmor,
+ display: TankopediaDisplay.Dissector,
})),
);
},
diff --git a/packages/website/src/stores/tankopediaPersistent/constants.ts b/packages/website/src/stores/tankopediaPersistent/constants.ts
index 49bc4bda1..ba770de86 100644
--- a/packages/website/src/stores/tankopediaPersistent/constants.ts
+++ b/packages/website/src/stores/tankopediaPersistent/constants.ts
@@ -37,5 +37,6 @@ export enum TankopediaDisplay {
Model,
DynamicArmor,
StaticArmor,
+ Dissector,
ShootingRange,
}