mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: i tried my best to implement dynamic armor :(
This commit is contained in:
1 parent
952523d3f9
commit
46f1461de6
9 files changed
+182
-92
No files matched your search
@@ -6,7 +6,6 @@ import {
|
|||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
useRef,
|
useRef,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { Armor } from '../../../../../../components/Armor';
|
|
||||||
import { ArmorPlateDisplay } from '../../../../../../components/Armor/components/ArmorPlateDisplay';
|
import { ArmorPlateDisplay } from '../../../../../../components/Armor/components/ArmorPlateDisplay';
|
||||||
import { ShotDisplay } from '../../../../../../components/Armor/components/ShotDisplay';
|
import { ShotDisplay } from '../../../../../../components/Armor/components/ShotDisplay';
|
||||||
import {
|
import {
|
||||||
@@ -185,7 +184,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
|
|
||||||
{/* idk why the shot display doesn't work without suspense here lol */}
|
{/* idk why the shot display doesn't work without suspense here lol */}
|
||||||
<Suspense fallback={<ModelLoader />}>
|
<Suspense fallback={<ModelLoader />}>
|
||||||
{display === TankopediaDisplay.DynamicArmor && <Armor />}
|
{/* {display === TankopediaDisplay.DynamicArmor && <Armor />} */}
|
||||||
{display === TankopediaDisplay.StaticArmor && (
|
{display === TankopediaDisplay.StaticArmor && (
|
||||||
<StaticArmor thicknessRange={thicknessRange} />
|
<StaticArmor thicknessRange={thicknessRange} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ export const TankModel = memo(() => {
|
|||||||
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||||
const { gltf } = useModel(protagonist.tank.id);
|
const { gltf } = useModel(protagonist.tank.id);
|
||||||
const nodes = Object.values(gltf.nodes);
|
const nodes = Object.values(gltf.nodes);
|
||||||
|
const isDynamicArmorActive = Duel.use((state) =>
|
||||||
|
state.protagonist.consumables.includes(73),
|
||||||
|
);
|
||||||
|
|
||||||
useTankTransform(protagonist, turretContainer, gunContainer);
|
useTankTransform(protagonist, turretContainer, gunContainer);
|
||||||
|
|
||||||
@@ -100,9 +103,28 @@ export const TankModel = memo(() => {
|
|||||||
return jsxTree(
|
return jsxTree(
|
||||||
node,
|
node,
|
||||||
{
|
{
|
||||||
castShadow: true,
|
mesh: {
|
||||||
receiveShadow: true,
|
castShadow: true,
|
||||||
onPointerDown,
|
receiveShadow: true,
|
||||||
|
onPointerDown,
|
||||||
|
},
|
||||||
|
// group(group) {
|
||||||
|
// console.log(group.name);
|
||||||
|
// 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'))),
|
||||||
|
// };
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
node.uuid,
|
node.uuid,
|
||||||
);
|
);
|
||||||
@@ -178,9 +200,28 @@ export const TankModel = memo(() => {
|
|||||||
return jsxTree(
|
return jsxTree(
|
||||||
node,
|
node,
|
||||||
{
|
{
|
||||||
castShadow: true,
|
mesh: {
|
||||||
receiveShadow: true,
|
castShadow: true,
|
||||||
onPointerDown,
|
receiveShadow: true,
|
||||||
|
onPointerDown,
|
||||||
|
},
|
||||||
|
// 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'))),
|
||||||
|
// };
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
node.uuid,
|
node.uuid,
|
||||||
);
|
);
|
||||||
@@ -258,9 +299,28 @@ export const TankModel = memo(() => {
|
|||||||
return jsxTree(
|
return jsxTree(
|
||||||
node,
|
node,
|
||||||
{
|
{
|
||||||
castShadow: true,
|
mesh: {
|
||||||
receiveShadow: true,
|
castShadow: true,
|
||||||
onPointerDown,
|
receiveShadow: true,
|
||||||
|
onPointerDown,
|
||||||
|
},
|
||||||
|
// 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'))),
|
||||||
|
// };
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
node.uuid,
|
node.uuid,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -156,10 +156,14 @@ export function PrimaryArmorSceneComponent({
|
|||||||
<>
|
<>
|
||||||
{jsxTree(
|
{jsxTree(
|
||||||
node,
|
node,
|
||||||
{ renderOrder: 0, material: excludeMaterial },
|
{ mesh: { renderOrder: 0, material: excludeMaterial } },
|
||||||
`${node.uuid}-exclude`,
|
`${node.uuid}-exclude`,
|
||||||
)}
|
)}
|
||||||
{jsxTree(node, { renderOrder: 1, material }, `${node.uuid}-include`)}
|
{jsxTree(
|
||||||
|
node,
|
||||||
|
{ mesh: { renderOrder: 1, material } },
|
||||||
|
`${node.uuid}-include`,
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+20
-14
@@ -113,24 +113,30 @@ export function SpacedArmorSubExternal({
|
|||||||
{jsxTree(
|
{jsxTree(
|
||||||
node,
|
node,
|
||||||
{
|
{
|
||||||
renderOrder: 3,
|
mesh: {
|
||||||
material: new MeshBasicMaterial({
|
renderOrder: 3,
|
||||||
colorWrite: false,
|
material: new MeshBasicMaterial({
|
||||||
depthTest: true,
|
colorWrite: false,
|
||||||
depthWrite: true,
|
depthTest: true,
|
||||||
...(clip ? { clippingPlanes: [clip] } : {}),
|
depthWrite: true,
|
||||||
}),
|
...(clip ? { clippingPlanes: [clip] } : {}),
|
||||||
onClick() {},
|
}),
|
||||||
userData: {
|
onClick() {},
|
||||||
type: ArmorType.External,
|
userData: {
|
||||||
thickness,
|
type: ArmorType.External,
|
||||||
variant,
|
thickness,
|
||||||
} satisfies ArmorUserData,
|
variant,
|
||||||
|
} satisfies ArmorUserData,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
`${node.uuid}-exclude`,
|
`${node.uuid}-exclude`,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{jsxTree(node, { renderOrder: 4, material }, `${node.uuid}-include`)}
|
{jsxTree(
|
||||||
|
node,
|
||||||
|
{ mesh: { renderOrder: 4, material } },
|
||||||
|
`${node.uuid}-include`,
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+13
-9
@@ -123,13 +123,15 @@ export function SpacedArmorSubSpaced({
|
|||||||
{jsxTree(
|
{jsxTree(
|
||||||
node,
|
node,
|
||||||
{
|
{
|
||||||
renderOrder: 2,
|
mesh: {
|
||||||
material,
|
renderOrder: 2,
|
||||||
onClick() {},
|
material,
|
||||||
userData: {
|
onClick() {},
|
||||||
type: ArmorType.Spaced,
|
userData: {
|
||||||
thickness,
|
type: ArmorType.Spaced,
|
||||||
} satisfies ArmorUserData,
|
thickness,
|
||||||
|
} satisfies ArmorUserData,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
`${node.uuid}-exclude`,
|
`${node.uuid}-exclude`,
|
||||||
)}
|
)}
|
||||||
@@ -137,8 +139,10 @@ export function SpacedArmorSubSpaced({
|
|||||||
{jsxTree(
|
{jsxTree(
|
||||||
node,
|
node,
|
||||||
{
|
{
|
||||||
renderOrder: 5,
|
mesh: {
|
||||||
material: depthWriteMaterial,
|
renderOrder: 5,
|
||||||
|
material: depthWriteMaterial,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
`${node.uuid}-include`,
|
`${node.uuid}-include`,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -371,20 +371,22 @@ export function SpacedArmorSceneComponent({
|
|||||||
jsxTree(
|
jsxTree(
|
||||||
node,
|
node,
|
||||||
{
|
{
|
||||||
renderOrder: 0,
|
mesh: {
|
||||||
material: omitMaterial,
|
renderOrder: 0,
|
||||||
userData: {
|
material: omitMaterial,
|
||||||
type: ArmorType.Primary,
|
userData: {
|
||||||
thickness,
|
type: ArmorType.Primary,
|
||||||
} satisfies ArmorUserData,
|
thickness,
|
||||||
async onClick(event) {
|
} satisfies ArmorUserData,
|
||||||
event.stopPropagation();
|
async onClick(event) {
|
||||||
const shot = (await shoot(
|
event.stopPropagation();
|
||||||
event.point,
|
const shot = (await shoot(
|
||||||
event.intersections,
|
event.point,
|
||||||
true,
|
event.intersections,
|
||||||
))!;
|
true,
|
||||||
tankopediaEphemeralStore.setState({ shot });
|
))!;
|
||||||
|
tankopediaEphemeralStore.setState({ shot });
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
node.uuid,
|
node.uuid,
|
||||||
|
|||||||
@@ -215,46 +215,50 @@ export function StaticArmorSceneComponent({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{jsxTree(node, {
|
{jsxTree(node, {
|
||||||
material: surfaceMaterial,
|
mesh: {
|
||||||
onPointerDown,
|
material: surfaceMaterial,
|
||||||
userData: {
|
onPointerDown,
|
||||||
type: props.type,
|
userData: {
|
||||||
variant: props.type === ArmorType.External ? props.variant : 'gun',
|
type: props.type,
|
||||||
thickness,
|
variant: props.type === ArmorType.External ? props.variant : 'gun',
|
||||||
} satisfies ArmorUserData,
|
thickness,
|
||||||
|
} satisfies ArmorUserData,
|
||||||
|
|
||||||
onClick(event) {
|
onClick(event) {
|
||||||
if (discardClippingPlane(event.object, event.point)) return;
|
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 bounds = new Box3().setFromObject(event.object);
|
||||||
const point = bounds.min
|
const point = bounds.min
|
||||||
.clone()
|
.clone()
|
||||||
.add(bounds.max)
|
.add(bounds.max)
|
||||||
.divideScalar(2)
|
.divideScalar(2)
|
||||||
.setY(bounds.max.y);
|
.setY(bounds.max.y);
|
||||||
const cameraNormal = camera.position.clone().sub(point).normalize();
|
const cameraNormal = camera.position.clone().sub(point).normalize();
|
||||||
const surfaceNormal = event
|
const surfaceNormal = event
|
||||||
.normal!.clone()
|
.normal!.clone()
|
||||||
.applyQuaternion(event.object.getWorldQuaternion(new Quaternion()));
|
.applyQuaternion(
|
||||||
const angle = surfaceNormal.angleTo(cameraNormal);
|
event.object.getWorldQuaternion(new Quaternion()),
|
||||||
const thicknessAngled = thickness / Math.sin(Math.PI / 2 - angle);
|
);
|
||||||
|
const angle = surfaceNormal.angleTo(cameraNormal);
|
||||||
|
const thicknessAngled = thickness / Math.sin(Math.PI / 2 - angle);
|
||||||
|
|
||||||
mutateTankopediaEphemeralStore((draft) => {
|
mutateTankopediaEphemeralStore((draft) => {
|
||||||
draft.highlightArmor = {
|
draft.highlightArmor = {
|
||||||
type: props.type,
|
type: props.type,
|
||||||
name,
|
name,
|
||||||
point,
|
point,
|
||||||
thickness,
|
thickness,
|
||||||
thicknessAngled,
|
thicknessAngled,
|
||||||
angle,
|
angle,
|
||||||
color: `#${color.getHexString()}`,
|
color: `#${color.getHexString()}`,
|
||||||
editingPlate: editStatic,
|
editingPlate: editStatic,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
import { MeshProps } from '@react-three/fiber';
|
import { GroupProps, MeshProps } from '@react-three/fiber';
|
||||||
import { Mesh, Object3D } from 'three';
|
import { Mesh, Object3D } from 'three';
|
||||||
|
|
||||||
export function jsxTree(node: Object3D, mesh?: MeshProps, key?: string) {
|
export function jsxTree(
|
||||||
|
node: Object3D,
|
||||||
|
mergers?: {
|
||||||
|
mesh?: MeshProps | ((mesh: Mesh) => MeshProps);
|
||||||
|
group?: GroupProps | ((object3d: Object3D) => GroupProps);
|
||||||
|
},
|
||||||
|
key?: string,
|
||||||
|
) {
|
||||||
if (node instanceof Mesh) {
|
if (node instanceof Mesh) {
|
||||||
return (
|
return (
|
||||||
<mesh
|
<mesh
|
||||||
@@ -11,7 +18,10 @@ export function jsxTree(node: Object3D, mesh?: MeshProps, key?: string) {
|
|||||||
position={node.position}
|
position={node.position}
|
||||||
rotation={node.rotation}
|
rotation={node.rotation}
|
||||||
scale={node.scale}
|
scale={node.scale}
|
||||||
{...mesh}
|
{...(mergers?.mesh &&
|
||||||
|
(typeof mergers.mesh === 'function'
|
||||||
|
? mergers.mesh(node)
|
||||||
|
: mergers.mesh))}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (node instanceof Object3D) {
|
} else if (node instanceof Object3D) {
|
||||||
@@ -21,8 +31,12 @@ export function jsxTree(node: Object3D, mesh?: MeshProps, key?: string) {
|
|||||||
position={node.position}
|
position={node.position}
|
||||||
rotation={node.rotation}
|
rotation={node.rotation}
|
||||||
scale={node.scale}
|
scale={node.scale}
|
||||||
|
{...(mergers?.group &&
|
||||||
|
(typeof mergers.group === 'function'
|
||||||
|
? mergers.group(node)
|
||||||
|
: mergers.group))}
|
||||||
>
|
>
|
||||||
{node.children.map((child) => jsxTree(child, mesh, child.uuid))}
|
{node.children.map((child) => jsxTree(child, mergers, child.uuid))}
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ const cache: Record<
|
|||||||
|
|
||||||
export function useArmor(id: number) {
|
export function useArmor(id: number) {
|
||||||
const gltf = useLoader(GLTFLoader, asset(`3d/tanks/armor/${id}.glb`));
|
const gltf = useLoader(GLTFLoader, asset(`3d/tanks/armor/${id}.glb`));
|
||||||
|
|
||||||
console.log(Object.values(gltf.nodes).map((node) => node.name));
|
|
||||||
|
|
||||||
if (!cache[id]) {
|
if (!cache[id]) {
|
||||||
cache[id] = {
|
cache[id] = {
|
||||||
gltf,
|
gltf,
|
||||||
|
|||||||
Reference in new issue
Block a user