mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
feat: advanced jsxTree
This commit is contained in:
1 parent
2bef96475c
commit
0e6aa734cb
10 files changed
+305
-226
No files matched your search
+19
-10
@@ -211,16 +211,25 @@ export function PrimaryArmorSceneComponent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{jsxTree(
|
{jsxTree(node, {
|
||||||
node,
|
mesh(_, props, key) {
|
||||||
{ mesh: { renderOrder: 0, material: excludeMaterial } },
|
return (
|
||||||
`${node.uuid}-exclude`,
|
<mesh
|
||||||
)}
|
{...props}
|
||||||
{jsxTree(
|
key={key}
|
||||||
node,
|
renderOrder={0}
|
||||||
{ mesh: { renderOrder: 1, material } },
|
material={excludeMaterial}
|
||||||
`${node.uuid}-include`,
|
/>
|
||||||
)}
|
);
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
{jsxTree(node, {
|
||||||
|
mesh(_, props, key) {
|
||||||
|
return (
|
||||||
|
<mesh {...props} key={key} renderOrder={1} material={material} />
|
||||||
|
);
|
||||||
|
},
|
||||||
|
})}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+32
-25
@@ -114,33 +114,40 @@ export function SpacedArmorSubExternal({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{jsxTree(
|
{jsxTree(node, {
|
||||||
node,
|
mesh(_, props, key) {
|
||||||
{
|
return (
|
||||||
mesh: {
|
<mesh
|
||||||
renderOrder: 3,
|
{...props}
|
||||||
material: new MeshBasicMaterial({
|
key={key}
|
||||||
colorWrite: false,
|
renderOrder={3}
|
||||||
depthTest: true,
|
material={
|
||||||
depthWrite: true,
|
new MeshBasicMaterial({
|
||||||
...(clip ? { clippingPlanes: [clip] } : {}),
|
colorWrite: false,
|
||||||
}),
|
depthTest: true,
|
||||||
onClick() {},
|
depthWrite: true,
|
||||||
userData: {
|
...(clip ? { clippingPlanes: [clip] } : {}),
|
||||||
type: ArmorType.External,
|
})
|
||||||
thickness,
|
}
|
||||||
variant,
|
userData={
|
||||||
} satisfies ArmorUserData,
|
{
|
||||||
},
|
type: ArmorType.External,
|
||||||
|
thickness,
|
||||||
|
variant,
|
||||||
|
} satisfies ArmorUserData
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
`${node.uuid}-exclude`,
|
})}
|
||||||
)}
|
|
||||||
|
|
||||||
{jsxTree(
|
{jsxTree(node, {
|
||||||
node,
|
mesh(_, props, key) {
|
||||||
{ mesh: { renderOrder: 4, material } },
|
return (
|
||||||
`${node.uuid}-include`,
|
<mesh {...props} key={key} renderOrder={4} material={material} />
|
||||||
)}
|
);
|
||||||
|
},
|
||||||
|
})}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+29
-23
@@ -124,32 +124,38 @@ export function SpacedArmorSubSpaced({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{jsxTree(
|
{jsxTree(node, {
|
||||||
node,
|
mesh(_, props, key) {
|
||||||
{
|
return (
|
||||||
mesh: {
|
<mesh
|
||||||
renderOrder: 2,
|
{...props}
|
||||||
material,
|
key={key}
|
||||||
onClick() {},
|
renderOrder={2}
|
||||||
userData: {
|
material={material}
|
||||||
type: ArmorType.Spaced,
|
onClick={() => {}}
|
||||||
thickness,
|
userData={
|
||||||
} satisfies ArmorUserData,
|
{
|
||||||
},
|
type: ArmorType.Spaced,
|
||||||
|
thickness,
|
||||||
|
} satisfies ArmorUserData
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
`${node.uuid}-exclude`,
|
})}
|
||||||
)}
|
|
||||||
|
|
||||||
{jsxTree(
|
{jsxTree(node, {
|
||||||
node,
|
mesh(_, props, key) {
|
||||||
{
|
return (
|
||||||
mesh: {
|
<mesh
|
||||||
renderOrder: 5,
|
{...props}
|
||||||
material: depthWriteMaterial,
|
key={key}
|
||||||
},
|
renderOrder={5}
|
||||||
|
material={depthWriteMaterial}
|
||||||
|
/>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
`${node.uuid}-include`,
|
})}
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+26
-22
@@ -372,29 +372,33 @@ export function SpacedArmorSceneComponent({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{props.type === ArmorType.Primary &&
|
{props.type === ArmorType.Primary &&
|
||||||
jsxTree(
|
jsxTree(node, {
|
||||||
node,
|
mesh(mesh, props, key) {
|
||||||
{
|
return (
|
||||||
mesh: {
|
<mesh
|
||||||
renderOrder: 0,
|
{...props}
|
||||||
material: omitMaterial,
|
key={key}
|
||||||
userData: {
|
renderOrder={0}
|
||||||
type: ArmorType.Primary,
|
material={omitMaterial}
|
||||||
thickness,
|
userData={
|
||||||
} satisfies ArmorUserData,
|
{
|
||||||
async onClick(event) {
|
type: ArmorType.Primary,
|
||||||
event.stopPropagation();
|
thickness,
|
||||||
const shot = (await shoot(
|
} satisfies ArmorUserData
|
||||||
event.point,
|
}
|
||||||
event.intersections,
|
onClick={async (event) => {
|
||||||
true,
|
event.stopPropagation();
|
||||||
))!;
|
const shot = (await shoot(
|
||||||
tankopediaEphemeralStore.setState({ shot });
|
event.point,
|
||||||
},
|
event.intersections,
|
||||||
},
|
true,
|
||||||
|
))!;
|
||||||
|
tankopediaEphemeralStore.setState({ shot });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
node.uuid,
|
})}
|
||||||
)}
|
|
||||||
|
|
||||||
{props.type === ArmorType.Spaced && (
|
{props.type === ArmorType.Spaced && (
|
||||||
<SpacedArmorSubSpaced node={node} thickness={thickness} />
|
<SpacedArmorSubSpaced node={node} thickness={thickness} />
|
||||||
|
|||||||
@@ -215,50 +215,61 @@ export function StaticArmorSceneComponent({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{jsxTree(node, {
|
{jsxTree(node, {
|
||||||
mesh: {
|
mesh(_, meshProps, key) {
|
||||||
material: surfaceMaterial,
|
return (
|
||||||
onPointerDown,
|
<mesh
|
||||||
userData: {
|
{...meshProps}
|
||||||
type: props.type,
|
key={key}
|
||||||
variant: props.type === ArmorType.External ? props.variant : 'gun',
|
material={surfaceMaterial}
|
||||||
thickness,
|
userData={
|
||||||
} satisfies ArmorUserData,
|
{
|
||||||
|
type: props.type,
|
||||||
|
variant:
|
||||||
|
props.type === ArmorType.External ? props.variant : 'gun',
|
||||||
|
thickness,
|
||||||
|
} satisfies ArmorUserData
|
||||||
|
}
|
||||||
|
onClick={(event) => {
|
||||||
|
if (discardClippingPlane(event.object, event.point)) return;
|
||||||
|
|
||||||
onClick(event) {
|
event.stopPropagation();
|
||||||
if (discardClippingPlane(event.object, event.point)) return;
|
|
||||||
|
|
||||||
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);
|
mutateTankopediaEphemeralStore((draft) => {
|
||||||
const point = bounds.min
|
draft.highlightArmor = {
|
||||||
.clone()
|
type: props.type,
|
||||||
.add(bounds.max)
|
name,
|
||||||
.divideScalar(2)
|
point,
|
||||||
.setY(bounds.max.y);
|
thickness,
|
||||||
const cameraNormal = camera.position.clone().sub(point).normalize();
|
thicknessAngled,
|
||||||
const surfaceNormal = event
|
angle,
|
||||||
.normal!.clone()
|
color: `#${color.getHexString()}`,
|
||||||
.applyQuaternion(
|
editingPlate: editStatic,
|
||||||
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,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { PlusIcon, TrashIcon } from '@radix-ui/react-icons';
|
import { PlusIcon, TrashIcon } from '@radix-ui/react-icons';
|
||||||
import { Button, Dialog, Flex, SegmentedControl } from '@radix-ui/themes';
|
import { Button, Dialog, Flex, SegmentedControl } from '@radix-ui/themes';
|
||||||
|
import { awaitableModelDefinitions } from '../../core/awaitables/modelDefinitions';
|
||||||
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
|
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
|
||||||
import { tankToCompareMember } from '../../core/blitzkit/tankToCompareMember';
|
import { tankToCompareMember } from '../../core/blitzkit/tankToCompareMember';
|
||||||
import { useLocale } from '../../hooks/useLocale';
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
@@ -15,7 +16,10 @@ interface ControlsProps {
|
|||||||
onAddTankDialogOpenChange: (open: boolean) => void;
|
onAddTankDialogOpenChange: (open: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const provisionDefinitions = await awaitableProvisionDefinitions;
|
const [provisionDefinitions, modelDefinitions] = await Promise.all([
|
||||||
|
awaitableProvisionDefinitions,
|
||||||
|
awaitableModelDefinitions,
|
||||||
|
]);
|
||||||
|
|
||||||
export function Controls({
|
export function Controls({
|
||||||
addTankDialogOpen,
|
addTankDialogOpen,
|
||||||
@@ -56,9 +60,11 @@ export function Controls({
|
|||||||
<TankSearch
|
<TankSearch
|
||||||
compact
|
compact
|
||||||
onSelect={(tank) => {
|
onSelect={(tank) => {
|
||||||
|
const model = modelDefinitions.models[tank.id];
|
||||||
|
|
||||||
mutateCompareEphemeral((draft) => {
|
mutateCompareEphemeral((draft) => {
|
||||||
draft.members.push(
|
draft.members.push(
|
||||||
tankToCompareMember(tank, provisionDefinitions),
|
tankToCompareMember(tank, model, provisionDefinitions),
|
||||||
);
|
);
|
||||||
draft.sorting = undefined;
|
draft.sorting = undefined;
|
||||||
});
|
});
|
||||||
@@ -67,9 +73,15 @@ export function Controls({
|
|||||||
onSelectAll={(tanks) => {
|
onSelectAll={(tanks) => {
|
||||||
mutateCompareEphemeral((draft) => {
|
mutateCompareEphemeral((draft) => {
|
||||||
draft.members.push(
|
draft.members.push(
|
||||||
...tanks.map((tank) =>
|
...tanks.map((tank) => {
|
||||||
tankToCompareMember(tank, provisionDefinitions),
|
const model = modelDefinitions.models[tank.id];
|
||||||
),
|
|
||||||
|
return tankToCompareMember(
|
||||||
|
tank,
|
||||||
|
model,
|
||||||
|
provisionDefinitions,
|
||||||
|
);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
draft.sorting = undefined;
|
draft.sorting = undefined;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ExternalLinkIcon, LoopIcon, TrashIcon } from '@radix-ui/react-icons';
|
import { ExternalLinkIcon, LoopIcon, TrashIcon } from '@radix-ui/react-icons';
|
||||||
import { Dialog, Flex, IconButton } from '@radix-ui/themes';
|
import { Dialog, Flex, IconButton } from '@radix-ui/themes';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { awaitableModelDefinitions } from '../../core/awaitables/modelDefinitions';
|
||||||
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
|
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
|
||||||
import { tankToCompareMember } from '../../core/blitzkit/tankToCompareMember';
|
import { tankToCompareMember } from '../../core/blitzkit/tankToCompareMember';
|
||||||
import { useLocale } from '../../hooks/useLocale';
|
import { useLocale } from '../../hooks/useLocale';
|
||||||
@@ -13,7 +14,10 @@ interface TankControlProps {
|
|||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const provisionDefinitions = await awaitableProvisionDefinitions;
|
const [provisionDefinitions, modelDefinitions] = await Promise.all([
|
||||||
|
awaitableProvisionDefinitions,
|
||||||
|
awaitableModelDefinitions,
|
||||||
|
]);
|
||||||
|
|
||||||
export function TankControl({ index, id }: TankControlProps) {
|
export function TankControl({ index, id }: TankControlProps) {
|
||||||
const { locale } = useLocale();
|
const { locale } = useLocale();
|
||||||
@@ -57,9 +61,12 @@ export function TankControl({ index, id }: TankControlProps) {
|
|||||||
<TankSearch
|
<TankSearch
|
||||||
compact
|
compact
|
||||||
onSelect={(tank) => {
|
onSelect={(tank) => {
|
||||||
|
const model = modelDefinitions.models[tank.id];
|
||||||
|
|
||||||
mutateCompareEphemeral((draft) => {
|
mutateCompareEphemeral((draft) => {
|
||||||
draft.members[index] = tankToCompareMember(
|
draft.members[index] = tankToCompareMember(
|
||||||
tank,
|
tank,
|
||||||
|
model,
|
||||||
provisionDefinitions,
|
provisionDefinitions,
|
||||||
);
|
);
|
||||||
draft.sorting = undefined;
|
draft.sorting = undefined;
|
||||||
|
|||||||
+86
-68
@@ -114,29 +114,35 @@ export function TankModel() {
|
|||||||
window.removeEventListener('pointerup', handlePointerUp);
|
window.removeEventListener('pointerup', handlePointerUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsxTree(
|
return jsxTree(node, {
|
||||||
node,
|
mesh(_, props, key) {
|
||||||
{
|
return (
|
||||||
mesh: { onPointerDown, castShadow: true },
|
<mesh
|
||||||
// group(group) {
|
{...props}
|
||||||
// return {
|
key={key}
|
||||||
// visible:
|
onPointerDown={onPointerDown}
|
||||||
// group.name !== 'ST_base' &&
|
castShadow
|
||||||
// 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'))),
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
},
|
},
|
||||||
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'))),
|
||||||
|
// };
|
||||||
|
// },
|
||||||
|
});
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<group ref={turretContainer}>
|
<group ref={turretContainer}>
|
||||||
@@ -217,30 +223,36 @@ export function TankModel() {
|
|||||||
window.removeEventListener('pointerup', handlePointerUp);
|
window.removeEventListener('pointerup', handlePointerUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsxTree(
|
return jsxTree(node, {
|
||||||
node,
|
mesh(_, props, key) {
|
||||||
{
|
return (
|
||||||
mesh: { onPointerDown, castShadow: true },
|
<mesh
|
||||||
// group(group) {
|
{...props}
|
||||||
// return {
|
key={key}
|
||||||
// visible:
|
onPointerDown={onPointerDown}
|
||||||
// group.name !== 'ST_base' &&
|
castShadow
|
||||||
// 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'))),
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
},
|
},
|
||||||
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'))),
|
||||||
|
// };
|
||||||
|
// },
|
||||||
|
});
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<group ref={gunContainer}>
|
<group ref={gunContainer}>
|
||||||
@@ -324,30 +336,36 @@ export function TankModel() {
|
|||||||
window.removeEventListener('pointerup', handlePointerUp);
|
window.removeEventListener('pointerup', handlePointerUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsxTree(
|
return jsxTree(node, {
|
||||||
node,
|
mesh(_, props, key) {
|
||||||
{
|
return (
|
||||||
mesh: { onPointerDown, castShadow: true },
|
<mesh
|
||||||
// group(group) {
|
{...props}
|
||||||
// return {
|
key={key}
|
||||||
// visible:
|
onPointerDown={onPointerDown}
|
||||||
// group.name !== 'ST_base' &&
|
castShadow
|
||||||
// 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'))),
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
},
|
},
|
||||||
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'))),
|
||||||
|
// };
|
||||||
|
// },
|
||||||
|
});
|
||||||
})}
|
})}
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
|||||||
@@ -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';
|
import { Mesh, Object3D } from 'three';
|
||||||
|
|
||||||
export function jsxTree(
|
export function jsxTree(
|
||||||
node: Object3D,
|
node: Object3D,
|
||||||
mergers?: {
|
mergers?: {
|
||||||
mesh?: MeshProps | ((mesh: Mesh) => MeshProps);
|
mesh?: (mesh: Mesh, props: MeshProps, key: string) => ReactNode;
|
||||||
group?: GroupProps | ((object3d: Object3D) => GroupProps);
|
group?: (
|
||||||
|
object3d: Object3D,
|
||||||
|
props: Object3DProps,
|
||||||
|
key: string,
|
||||||
|
) => ReactNode;
|
||||||
},
|
},
|
||||||
key?: string,
|
): ReactNode {
|
||||||
) {
|
|
||||||
if (node instanceof Mesh) {
|
if (node instanceof Mesh) {
|
||||||
return (
|
const props = {
|
||||||
<mesh
|
geometry: node.geometry,
|
||||||
key={key}
|
material: node.material,
|
||||||
geometry={node.geometry}
|
position: node.position,
|
||||||
material={node.material}
|
rotation: node.rotation,
|
||||||
position={node.position}
|
scale: node.scale,
|
||||||
rotation={node.rotation}
|
};
|
||||||
scale={node.scale}
|
|
||||||
{...(mergers?.mesh &&
|
return mergers?.mesh ? (
|
||||||
(typeof mergers.mesh === 'function'
|
mergers.mesh(node, props, node.uuid)
|
||||||
? mergers.mesh(node)
|
) : (
|
||||||
: mergers.mesh))}
|
<mesh key={node.uuid} {...props} />
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
} else if (node instanceof Object3D) {
|
} else if (node instanceof Object3D) {
|
||||||
return (
|
const props = {
|
||||||
<group
|
position: node.position,
|
||||||
key={key}
|
rotation: node.rotation,
|
||||||
position={node.position}
|
scale: node.scale,
|
||||||
rotation={node.rotation}
|
children: node.children.map((child) => jsxTree(child, mergers)),
|
||||||
scale={node.scale}
|
};
|
||||||
{...(mergers?.group &&
|
|
||||||
(typeof mergers.group === 'function'
|
return mergers?.group ? (
|
||||||
? mergers.group(node)
|
mergers.group(node, props, node.uuid)
|
||||||
: mergers.group))}
|
) : (
|
||||||
>
|
<group key={node.uuid} {...props} />
|
||||||
{node.children.map((child) => jsxTree(child, mergers, child.uuid))}
|
|
||||||
</group>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,11 @@ function Content() {
|
|||||||
|
|
||||||
if (tank === undefined) return;
|
if (tank === undefined) return;
|
||||||
|
|
||||||
draft.members.push(tankToCompareMember(tank, provisionDefinitions));
|
const model = modelDefinitions.models[tank.id];
|
||||||
|
|
||||||
|
draft.members.push(
|
||||||
|
tankToCompareMember(tank, model, provisionDefinitions),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
Reference in new issue
Block a user