diff --git a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx
index 69830885a..dae2f4d05 100644
--- a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx
+++ b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx
@@ -211,16 +211,25 @@ export function PrimaryArmorSceneComponent({
return (
<>
- {jsxTree(
- node,
- { mesh: { renderOrder: 0, material: excludeMaterial } },
- `${node.uuid}-exclude`,
- )}
- {jsxTree(
- node,
- { mesh: { renderOrder: 1, material } },
- `${node.uuid}-include`,
- )}
+ {jsxTree(node, {
+ mesh(_, props, key) {
+ return (
+
+ );
+ },
+ })}
+ {jsxTree(node, {
+ mesh(_, props, key) {
+ return (
+
+ );
+ },
+ })}
>
);
}
diff --git a/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx b/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx
index 983692e2a..6974393ce 100644
--- a/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx
+++ b/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx
@@ -114,33 +114,40 @@ export function SpacedArmorSubExternal({
return (
<>
- {jsxTree(
- node,
- {
- mesh: {
- renderOrder: 3,
- material: new MeshBasicMaterial({
- colorWrite: false,
- depthTest: true,
- depthWrite: true,
- ...(clip ? { clippingPlanes: [clip] } : {}),
- }),
- onClick() {},
- userData: {
- type: ArmorType.External,
- thickness,
- variant,
- } satisfies ArmorUserData,
- },
+ {jsxTree(node, {
+ mesh(_, props, key) {
+ return (
+
+ );
},
- `${node.uuid}-exclude`,
- )}
+ })}
- {jsxTree(
- node,
- { mesh: { renderOrder: 4, material } },
- `${node.uuid}-include`,
- )}
+ {jsxTree(node, {
+ mesh(_, props, key) {
+ return (
+
+ );
+ },
+ })}
>
);
}
diff --git a/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx b/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx
index 3c3904e71..3420523ce 100644
--- a/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx
+++ b/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx
@@ -124,32 +124,38 @@ export function SpacedArmorSubSpaced({
return (
<>
- {jsxTree(
- node,
- {
- mesh: {
- renderOrder: 2,
- material,
- onClick() {},
- userData: {
- type: ArmorType.Spaced,
- thickness,
- } satisfies ArmorUserData,
- },
+ {jsxTree(node, {
+ mesh(_, props, key) {
+ return (
+ {}}
+ userData={
+ {
+ type: ArmorType.Spaced,
+ thickness,
+ } satisfies ArmorUserData
+ }
+ />
+ );
},
- `${node.uuid}-exclude`,
- )}
+ })}
- {jsxTree(
- node,
- {
- mesh: {
- renderOrder: 5,
- material: depthWriteMaterial,
- },
+ {jsxTree(node, {
+ mesh(_, props, key) {
+ return (
+
+ );
},
- `${node.uuid}-include`,
- )}
+ })}
>
);
}
diff --git a/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx b/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx
index 2d8666347..c04024ec0 100644
--- a/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx
+++ b/packages/website/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx
@@ -372,29 +372,33 @@ export function SpacedArmorSceneComponent({
return (
<>
{props.type === ArmorType.Primary &&
- jsxTree(
- node,
- {
- mesh: {
- renderOrder: 0,
- material: omitMaterial,
- userData: {
- type: ArmorType.Primary,
- thickness,
- } satisfies ArmorUserData,
- async onClick(event) {
- event.stopPropagation();
- const shot = (await shoot(
- event.point,
- event.intersections,
- true,
- ))!;
- tankopediaEphemeralStore.setState({ shot });
- },
- },
+ jsxTree(node, {
+ mesh(mesh, props, key) {
+ return (
+ {
+ event.stopPropagation();
+ const shot = (await shoot(
+ event.point,
+ event.intersections,
+ true,
+ ))!;
+ tankopediaEphemeralStore.setState({ shot });
+ }}
+ />
+ );
},
- node.uuid,
- )}
+ })}
{props.type === ArmorType.Spaced && (
diff --git a/packages/website/src/components/Armor/components/StaticArmorSceneComponent.tsx b/packages/website/src/components/Armor/components/StaticArmorSceneComponent.tsx
index cc32af091..e1e74654f 100644
--- a/packages/website/src/components/Armor/components/StaticArmorSceneComponent.tsx
+++ b/packages/website/src/components/Armor/components/StaticArmorSceneComponent.tsx
@@ -215,50 +215,61 @@ export function StaticArmorSceneComponent({
return (
<>
{jsxTree(node, {
- mesh: {
- material: surfaceMaterial,
- onPointerDown,
- userData: {
- type: props.type,
- variant: props.type === ArmorType.External ? props.variant : 'gun',
- thickness,
- } satisfies ArmorUserData,
+ mesh(_, meshProps, key) {
+ return (
+ {
+ if (discardClippingPlane(event.object, event.point)) return;
- onClick(event) {
- if (discardClippingPlane(event.object, event.point)) return;
+ event.stopPropagation();
- event.stopPropagation();
+ const { editStatic } = tankopediaEphemeralStore.getState();
- const { editStatic } = tankopediaEphemeralStore.getState();
+ 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);
- 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,
- point,
- thickness,
- thicknessAngled,
- angle,
- color: `#${color.getHexString()}`,
- editingPlate: editStatic,
- };
- });
- },
+ mutateTankopediaEphemeralStore((draft) => {
+ draft.highlightArmor = {
+ type: props.type,
+ name,
+ point,
+ thickness,
+ thicknessAngled,
+ angle,
+ color: `#${color.getHexString()}`,
+ editingPlate: editStatic,
+ };
+ });
+ }}
+ />
+ );
},
})}
diff --git a/packages/website/src/components/Compare/Controls.tsx b/packages/website/src/components/Compare/Controls.tsx
index b17f8745e..2a7c984a1 100644
--- a/packages/website/src/components/Compare/Controls.tsx
+++ b/packages/website/src/components/Compare/Controls.tsx
@@ -1,5 +1,6 @@
import { PlusIcon, TrashIcon } from '@radix-ui/react-icons';
import { Button, Dialog, Flex, SegmentedControl } from '@radix-ui/themes';
+import { awaitableModelDefinitions } from '../../core/awaitables/modelDefinitions';
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
import { tankToCompareMember } from '../../core/blitzkit/tankToCompareMember';
import { useLocale } from '../../hooks/useLocale';
@@ -15,7 +16,10 @@ interface ControlsProps {
onAddTankDialogOpenChange: (open: boolean) => void;
}
-const provisionDefinitions = await awaitableProvisionDefinitions;
+const [provisionDefinitions, modelDefinitions] = await Promise.all([
+ awaitableProvisionDefinitions,
+ awaitableModelDefinitions,
+]);
export function Controls({
addTankDialogOpen,
@@ -56,9 +60,11 @@ export function Controls({
{
+ const model = modelDefinitions.models[tank.id];
+
mutateCompareEphemeral((draft) => {
draft.members.push(
- tankToCompareMember(tank, provisionDefinitions),
+ tankToCompareMember(tank, model, provisionDefinitions),
);
draft.sorting = undefined;
});
@@ -67,9 +73,15 @@ export function Controls({
onSelectAll={(tanks) => {
mutateCompareEphemeral((draft) => {
draft.members.push(
- ...tanks.map((tank) =>
- tankToCompareMember(tank, provisionDefinitions),
- ),
+ ...tanks.map((tank) => {
+ const model = modelDefinitions.models[tank.id];
+
+ return tankToCompareMember(
+ tank,
+ model,
+ provisionDefinitions,
+ );
+ }),
);
draft.sorting = undefined;
});
diff --git a/packages/website/src/components/Compare/TankControl.tsx b/packages/website/src/components/Compare/TankControl.tsx
index 96aa5840f..310fc4426 100644
--- a/packages/website/src/components/Compare/TankControl.tsx
+++ b/packages/website/src/components/Compare/TankControl.tsx
@@ -1,6 +1,7 @@
import { ExternalLinkIcon, LoopIcon, TrashIcon } from '@radix-ui/react-icons';
import { Dialog, Flex, IconButton } from '@radix-ui/themes';
import { useState } from 'react';
+import { awaitableModelDefinitions } from '../../core/awaitables/modelDefinitions';
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
import { tankToCompareMember } from '../../core/blitzkit/tankToCompareMember';
import { useLocale } from '../../hooks/useLocale';
@@ -13,7 +14,10 @@ interface TankControlProps {
id: number;
}
-const provisionDefinitions = await awaitableProvisionDefinitions;
+const [provisionDefinitions, modelDefinitions] = await Promise.all([
+ awaitableProvisionDefinitions,
+ awaitableModelDefinitions,
+]);
export function TankControl({ index, id }: TankControlProps) {
const { locale } = useLocale();
@@ -57,9 +61,12 @@ export function TankControl({ index, id }: TankControlProps) {
{
+ const model = modelDefinitions.models[tank.id];
+
mutateCompareEphemeral((draft) => {
draft.members[index] = tankToCompareMember(
tank,
+ model,
provisionDefinitions,
);
draft.sorting = undefined;
diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TankModel.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TankModel.tsx
index b91ee66b6..f8df92760 100644
--- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TankModel.tsx
+++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TankModel.tsx
@@ -114,29 +114,35 @@ export function TankModel() {
window.removeEventListener('pointerup', handlePointerUp);
}
- return jsxTree(
- node,
- {
- mesh: { onPointerDown, castShadow: true },
- // group(group) {
- // return {
- // visible:
- // group.name !== 'ST_base' &&
- // group.name !== 'MR_Base2' &&
- // (!group.name.includes('state_entity') ||
- // (isDynamicArmorActive &&
- // group.name.includes('state_entity_01')) ||
- // (!isDynamicArmorActive &&
- // group.name.includes('state_entity_00'))) &&
- // (!group.name.includes('shields') ||
- // (isDynamicArmorActive &&
- // group.name.includes('shields_rev')) ||
- // (!isDynamicArmorActive && group.name.endsWith('shields'))),
- // };
- // },
+ return jsxTree(node, {
+ mesh(_, props, key) {
+ return (
+
+ );
},
- node.uuid,
- );
+
+ // group(group) {
+ // return {
+ // visible:
+ // group.name !== 'ST_base' &&
+ // group.name !== 'MR_Base2' &&
+ // (!group.name.includes('state_entity') ||
+ // (isDynamicArmorActive &&
+ // group.name.includes('state_entity_01')) ||
+ // (!isDynamicArmorActive &&
+ // group.name.includes('state_entity_00'))) &&
+ // (!group.name.includes('shields') ||
+ // (isDynamicArmorActive &&
+ // group.name.includes('shields_rev')) ||
+ // (!isDynamicArmorActive && group.name.endsWith('shields'))),
+ // };
+ // },
+ });
})}
@@ -217,30 +223,36 @@ export function TankModel() {
window.removeEventListener('pointerup', handlePointerUp);
}
- return jsxTree(
- node,
- {
- mesh: { onPointerDown, castShadow: true },
- // group(group) {
- // return {
- // visible:
- // group.name !== 'ST_base' &&
- // group.name !== 'MR_Base2' &&
- // (!group.name.includes('state_entity') ||
- // (isDynamicArmorActive &&
- // group.name.includes('state_entity_01')) ||
- // (!isDynamicArmorActive &&
- // group.name.includes('state_entity_00'))) &&
- // (!group.name.includes('shields') ||
- // (isDynamicArmorActive &&
- // group.name.includes('shields_rev')) ||
- // (!isDynamicArmorActive &&
- // group.name.endsWith('shields'))),
- // };
- // },
+ return jsxTree(node, {
+ mesh(_, props, key) {
+ return (
+
+ );
},
- node.uuid,
- );
+
+ // group(group) {
+ // return {
+ // visible:
+ // group.name !== 'ST_base' &&
+ // group.name !== 'MR_Base2' &&
+ // (!group.name.includes('state_entity') ||
+ // (isDynamicArmorActive &&
+ // group.name.includes('state_entity_01')) ||
+ // (!isDynamicArmorActive &&
+ // group.name.includes('state_entity_00'))) &&
+ // (!group.name.includes('shields') ||
+ // (isDynamicArmorActive &&
+ // group.name.includes('shields_rev')) ||
+ // (!isDynamicArmorActive &&
+ // group.name.endsWith('shields'))),
+ // };
+ // },
+ });
})}
@@ -324,30 +336,36 @@ export function TankModel() {
window.removeEventListener('pointerup', handlePointerUp);
}
- return jsxTree(
- node,
- {
- mesh: { onPointerDown, castShadow: true },
- // group(group) {
- // return {
- // visible:
- // group.name !== 'ST_base' &&
- // group.name !== 'MR_Base2' &&
- // (!group.name.includes('state_entity') ||
- // (isDynamicArmorActive &&
- // group.name.includes('state_entity_01')) ||
- // (!isDynamicArmorActive &&
- // group.name.includes('state_entity_00'))) &&
- // (!group.name.includes('shields') ||
- // (isDynamicArmorActive &&
- // group.name.includes('shields_rev')) ||
- // (!isDynamicArmorActive &&
- // group.name.endsWith('shields'))),
- // };
- // },
+ return jsxTree(node, {
+ mesh(_, props, key) {
+ return (
+
+ );
},
- node.uuid,
- );
+
+ // group(group) {
+ // return {
+ // visible:
+ // group.name !== 'ST_base' &&
+ // group.name !== 'MR_Base2' &&
+ // (!group.name.includes('state_entity') ||
+ // (isDynamicArmorActive &&
+ // group.name.includes('state_entity_01')) ||
+ // (!isDynamicArmorActive &&
+ // group.name.includes('state_entity_00'))) &&
+ // (!group.name.includes('shields') ||
+ // (isDynamicArmorActive &&
+ // group.name.includes('shields_rev')) ||
+ // (!isDynamicArmorActive &&
+ // group.name.endsWith('shields'))),
+ // };
+ // },
+ });
})}
diff --git a/packages/website/src/core/blitzkit/jsxTree.tsx b/packages/website/src/core/blitzkit/jsxTree.tsx
index 4ae843b6c..253527522 100644
--- a/packages/website/src/core/blitzkit/jsxTree.tsx
+++ b/packages/website/src/core/blitzkit/jsxTree.tsx
@@ -1,43 +1,44 @@
-import type { GroupProps, MeshProps } from '@react-three/fiber';
+import type { MeshProps, Object3DProps } from '@react-three/fiber';
+import type { ReactNode } from 'react';
import { Mesh, Object3D } from 'three';
export function jsxTree(
node: Object3D,
mergers?: {
- mesh?: MeshProps | ((mesh: Mesh) => MeshProps);
- group?: GroupProps | ((object3d: Object3D) => GroupProps);
+ mesh?: (mesh: Mesh, props: MeshProps, key: string) => ReactNode;
+ group?: (
+ object3d: Object3D,
+ props: Object3DProps,
+ key: string,
+ ) => ReactNode;
},
- key?: string,
-) {
+): ReactNode {
if (node instanceof Mesh) {
- return (
-
+ const props = {
+ geometry: node.geometry,
+ material: node.material,
+ position: node.position,
+ rotation: node.rotation,
+ scale: node.scale,
+ };
+
+ return mergers?.mesh ? (
+ mergers.mesh(node, props, node.uuid)
+ ) : (
+
);
} else if (node instanceof Object3D) {
- return (
-
- {node.children.map((child) => jsxTree(child, mergers, child.uuid))}
-
+ const props = {
+ position: node.position,
+ rotation: node.rotation,
+ scale: node.scale,
+ children: node.children.map((child) => jsxTree(child, mergers)),
+ };
+
+ return mergers?.group ? (
+ mergers.group(node, props, node.uuid)
+ ) : (
+
);
}
diff --git a/packages/website/src/pages/[...locale]/tools/compare/_index.tsx b/packages/website/src/pages/[...locale]/tools/compare/_index.tsx
index 59d3106a8..7361e0211 100644
--- a/packages/website/src/pages/[...locale]/tools/compare/_index.tsx
+++ b/packages/website/src/pages/[...locale]/tools/compare/_index.tsx
@@ -113,7 +113,11 @@ function Content() {
if (tank === undefined) return;
- draft.members.push(tankToCompareMember(tank, provisionDefinitions));
+ const model = modelDefinitions.models[tank.id];
+
+ draft.members.push(
+ tankToCompareMember(tank, model, provisionDefinitions),
+ );
});
});
}, []);