mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 03:59:50 +02:00
fix: use new model format
This commit is contained in:
1 parent
1c3e98678f
commit
c68df74943
5 files changed
+108
-185
No files matched your search
+20
-18
@@ -1,5 +1,5 @@
|
|||||||
import { memo, useEffect, useRef } from 'react';
|
import { memo, useEffect, useRef } from 'react';
|
||||||
import { Euler, Group, Mesh, Vector3 } from 'three';
|
import { Euler, Group, Vector3 } from 'three';
|
||||||
import { degToRad } from 'three/src/math/MathUtils';
|
import { degToRad } from 'three/src/math/MathUtils';
|
||||||
import { ArmorMeshExternalModuleMask } from '../../../../../../../../../components/ArmorMesh';
|
import { ArmorMeshExternalModuleMask } from '../../../../../../../../../components/ArmorMesh';
|
||||||
import { I_HAT, J_HAT, K_HAT } from '../../../../../../../../../constants/axis';
|
import { I_HAT, J_HAT, K_HAT } from '../../../../../../../../../constants/axis';
|
||||||
@@ -181,7 +181,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
|
|||||||
ornamental={ornamental}
|
ornamental={ornamental}
|
||||||
exclude
|
exclude
|
||||||
key={node.uuid}
|
key={node.uuid}
|
||||||
geometry={(node as Mesh).geometry}
|
node={node}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -200,7 +200,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
|
|||||||
maxThickness={maxThickness}
|
maxThickness={maxThickness}
|
||||||
thickness={trackModelDefinition.thickness}
|
thickness={trackModelDefinition.thickness}
|
||||||
key={node.uuid}
|
key={node.uuid}
|
||||||
geometry={(node as Mesh).geometry}
|
node={node}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -221,13 +221,14 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
|
|||||||
if (!isVisible || thickness === undefined || spaced) return null;
|
if (!isVisible || thickness === undefined || spaced) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ArmorMeshExternalModuleMask
|
<group position={turretOrigin}>
|
||||||
ornamental={ornamental}
|
<ArmorMeshExternalModuleMask
|
||||||
exclude
|
ornamental={ornamental}
|
||||||
key={node.uuid}
|
exclude
|
||||||
geometry={(node as Mesh).geometry}
|
key={node.uuid}
|
||||||
position={turretOrigin}
|
node={node}
|
||||||
/>
|
/>
|
||||||
|
</group>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</group>
|
</group>
|
||||||
@@ -247,13 +248,14 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
|
|||||||
if (!isVisible || thickness === undefined || spaced) return null;
|
if (!isVisible || thickness === undefined || spaced) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ArmorMeshExternalModuleMask
|
<group position={turretOrigin.clone().add(gunOrigin)}>
|
||||||
ornamental={ornamental}
|
<ArmorMeshExternalModuleMask
|
||||||
exclude
|
ornamental={ornamental}
|
||||||
key={node.uuid}
|
exclude
|
||||||
geometry={(node as Mesh).geometry}
|
key={node.uuid}
|
||||||
position={turretOrigin.clone().add(gunOrigin)}
|
node={node}
|
||||||
/>
|
/>
|
||||||
|
</group>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
@@ -271,7 +273,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
|
|||||||
maxThickness={maxThickness}
|
maxThickness={maxThickness}
|
||||||
thickness={gunModelDefinition.thickness}
|
thickness={gunModelDefinition.thickness}
|
||||||
key={node.uuid}
|
key={node.uuid}
|
||||||
geometry={(node as Mesh).geometry}
|
node={node}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
import { ThreeEvent, useThree } from '@react-three/fiber';
|
import { ThreeEvent, useThree } from '@react-three/fiber';
|
||||||
import { memo, useEffect, useRef, useState } from 'react';
|
import { memo, useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import { Euler, Group, Vector3 } from 'three';
|
||||||
Euler,
|
|
||||||
Group,
|
|
||||||
Mesh,
|
|
||||||
MeshStandardMaterial,
|
|
||||||
Vector2,
|
|
||||||
Vector3,
|
|
||||||
} from 'three';
|
|
||||||
import { degToRad } from 'three/src/math/MathUtils';
|
import { degToRad } from 'three/src/math/MathUtils';
|
||||||
import { I_HAT, J_HAT, K_HAT } from '../../../../../../../constants/axis';
|
import { I_HAT, J_HAT, K_HAT } from '../../../../../../../constants/axis';
|
||||||
import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits';
|
||||||
|
import { jsxTree } from '../../../../../../../core/blitzkrieg/jsxTree';
|
||||||
import {
|
import {
|
||||||
ModelDefinitions,
|
ModelDefinitions,
|
||||||
modelDefinitions,
|
modelDefinitions,
|
||||||
@@ -19,7 +13,6 @@ import {
|
|||||||
ModelTransformEventData,
|
ModelTransformEventData,
|
||||||
modelTransformEvent,
|
modelTransformEvent,
|
||||||
} from '../../../../../../../core/blitzkrieg/modelTransform';
|
} from '../../../../../../../core/blitzkrieg/modelTransform';
|
||||||
import { resolveJsxTree } from '../../../../../../../core/blitzkrieg/resolveJsxTree';
|
|
||||||
import { normalizeAnglePI } from '../../../../../../../core/math/normalizeAngle180';
|
import { normalizeAnglePI } from '../../../../../../../core/math/normalizeAngle180';
|
||||||
import { useModel } from '../../../../../../../hooks/useModel';
|
import { useModel } from '../../../../../../../hooks/useModel';
|
||||||
import { useDuel } from '../../../../../../../stores/duel';
|
import { useDuel } from '../../../../../../../stores/duel';
|
||||||
@@ -138,58 +131,12 @@ export const TankModel = memo(() => {
|
|||||||
const isTrack = node.name.startsWith('chassis_track_');
|
const isTrack = node.name.startsWith('chassis_track_');
|
||||||
const isVisible = isHull || isWheel || isTrack;
|
const isVisible = isHull || isWheel || isTrack;
|
||||||
|
|
||||||
function handlePointerDown(event: ThreeEvent<PointerEvent>) {
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
if (isTrack) {
|
|
||||||
mutateTankopediaPersistent((draft) => {
|
|
||||||
draft.model.visual.controlsEnabled = false;
|
|
||||||
});
|
|
||||||
mutateTankopediaTemporary((draft) => {
|
|
||||||
draft.shot = undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('pointermove', handlePointerMove);
|
|
||||||
window.addEventListener('pointerup', handlePointerUp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function handlePointerMove(event: PointerEvent) {
|
|
||||||
const mesh = node as Mesh;
|
|
||||||
const material = mesh.material as MeshStandardMaterial;
|
|
||||||
const offset = new Vector2(
|
|
||||||
0,
|
|
||||||
-event.movementY * (6 / canvas.height) -
|
|
||||||
event.movementX * (6 / canvas.width),
|
|
||||||
);
|
|
||||||
|
|
||||||
material.map?.offset.add(offset);
|
|
||||||
material.roughnessMap?.offset.add(offset);
|
|
||||||
material.normalMap?.offset.add(offset);
|
|
||||||
}
|
|
||||||
function handlePointerUp() {
|
|
||||||
mutateTankopediaPersistent((draft) => {
|
|
||||||
draft.model.visual.controlsEnabled = true;
|
|
||||||
});
|
|
||||||
window.removeEventListener('pointermove', handlePointerMove);
|
|
||||||
window.removeEventListener('pointerup', handlePointerUp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isVisible) return null;
|
if (!isVisible) return null;
|
||||||
|
|
||||||
return (
|
return jsxTree(node, {
|
||||||
<mesh
|
castShadow: true,
|
||||||
children={resolveJsxTree(node)}
|
receiveShadow: true,
|
||||||
key={node.uuid}
|
});
|
||||||
castShadow
|
|
||||||
receiveShadow
|
|
||||||
geometry={(node as Mesh).geometry}
|
|
||||||
material={(node as Mesh).material}
|
|
||||||
position={node.position}
|
|
||||||
rotation={node.rotation}
|
|
||||||
scale={node.scale}
|
|
||||||
onPointerDown={handlePointerDown}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<group ref={turretContainer}>
|
<group ref={turretContainer}>
|
||||||
@@ -199,10 +146,13 @@ export const TankModel = memo(() => {
|
|||||||
node.name ===
|
node.name ===
|
||||||
`turret_${turretModelDefinition.model.toString().padStart(2, '0')}`;
|
`turret_${turretModelDefinition.model.toString().padStart(2, '0')}`;
|
||||||
const isVisible = isCurrentTurret;
|
const isVisible = isCurrentTurret;
|
||||||
|
|
||||||
|
if (!isVisible) return null;
|
||||||
|
|
||||||
let pitch = 0;
|
let pitch = 0;
|
||||||
let yaw = 0;
|
let yaw = 0;
|
||||||
|
|
||||||
function handlePointerDown(event: ThreeEvent<PointerEvent>) {
|
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
if (isTurret) {
|
if (isTurret) {
|
||||||
@@ -240,23 +190,11 @@ export const TankModel = memo(() => {
|
|||||||
window.removeEventListener('pointerup', handlePointerUp);
|
window.removeEventListener('pointerup', handlePointerUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isVisible) return null;
|
return jsxTree(node, {
|
||||||
|
castShadow: true,
|
||||||
return (
|
receiveShadow: true,
|
||||||
<mesh
|
onPointerDown,
|
||||||
visible={isVisible}
|
});
|
||||||
onPointerDown={handlePointerDown}
|
|
||||||
children={resolveJsxTree(node)}
|
|
||||||
key={node.uuid}
|
|
||||||
castShadow
|
|
||||||
receiveShadow
|
|
||||||
geometry={(node as Mesh).geometry}
|
|
||||||
material={(node as Mesh).material}
|
|
||||||
position={node.position}
|
|
||||||
rotation={node.rotation}
|
|
||||||
scale={node.scale}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<group ref={gunContainer}>
|
<group ref={gunContainer}>
|
||||||
@@ -270,10 +208,13 @@ export const TankModel = memo(() => {
|
|||||||
node.name ===
|
node.name ===
|
||||||
`gun_${gunModelDefinition.model.toString().padStart(2, '0')}`;
|
`gun_${gunModelDefinition.model.toString().padStart(2, '0')}`;
|
||||||
const isVisible = isCurrentGun || isCurrentMantlet;
|
const isVisible = isCurrentGun || isCurrentMantlet;
|
||||||
|
|
||||||
|
if (!isVisible) return null;
|
||||||
|
|
||||||
let pitch = 0;
|
let pitch = 0;
|
||||||
let yaw = 0;
|
let yaw = 0;
|
||||||
|
|
||||||
function handlePointerDown(event: ThreeEvent<PointerEvent>) {
|
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
mutateTankopediaPersistent((draft) => {
|
mutateTankopediaPersistent((draft) => {
|
||||||
@@ -308,23 +249,11 @@ export const TankModel = memo(() => {
|
|||||||
window.removeEventListener('pointerup', handlePointerUp);
|
window.removeEventListener('pointerup', handlePointerUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isVisible) return null;
|
return jsxTree(node, {
|
||||||
|
castShadow: true,
|
||||||
return (
|
receiveShadow: true,
|
||||||
<mesh
|
onPointerDown,
|
||||||
visible={isVisible}
|
});
|
||||||
onPointerDown={handlePointerDown}
|
|
||||||
children={resolveJsxTree(node)}
|
|
||||||
key={node.uuid}
|
|
||||||
castShadow
|
|
||||||
receiveShadow
|
|
||||||
geometry={(node as Mesh).geometry}
|
|
||||||
material={(node as Mesh).material}
|
|
||||||
position={node.position}
|
|
||||||
rotation={node.rotation}
|
|
||||||
scale={node.scale}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
|||||||
@@ -1,53 +1,52 @@
|
|||||||
import { MeshProps } from '@react-three/fiber';
|
import { Color, MeshBasicMaterial, Object3D, WebGLRenderTarget } from 'three';
|
||||||
import { Color, WebGLRenderTarget } from 'three';
|
import { jsxTree } from '../../../core/blitzkrieg/jsxTree';
|
||||||
import { ArmorMeshUserData } from './Armor';
|
import { ArmorMeshUserData } from './Armor';
|
||||||
|
|
||||||
export const externalModuleMaskRenderTarget = new WebGLRenderTarget();
|
export const externalModuleMaskRenderTarget = new WebGLRenderTarget();
|
||||||
|
|
||||||
type ArmorMeshExternalModuleMaskProps = MeshProps & {
|
type ArmorMeshExternalModuleMaskProps = {
|
||||||
ornamental?: boolean;
|
ornamental?: boolean;
|
||||||
|
node: Object3D;
|
||||||
} & (
|
} & (
|
||||||
| {
|
| {
|
||||||
exclude?: false;
|
exclude?: false;
|
||||||
maxThickness: number;
|
maxThickness: number;
|
||||||
thickness: number;
|
thickness: number;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
exclude: true;
|
exclude: true;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const excludeMaterial = new MeshBasicMaterial({
|
||||||
|
colorWrite: false,
|
||||||
|
});
|
||||||
|
|
||||||
export function ArmorMeshExternalModuleMask({
|
export function ArmorMeshExternalModuleMask({
|
||||||
ornamental = false,
|
ornamental = false,
|
||||||
|
node,
|
||||||
...props
|
...props
|
||||||
}: ArmorMeshExternalModuleMaskProps) {
|
}: ArmorMeshExternalModuleMaskProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{props.exclude && !ornamental && (
|
{props.exclude &&
|
||||||
<mesh {...props} renderOrder={0}>
|
!ornamental &&
|
||||||
<meshBasicMaterial colorWrite={false} />
|
jsxTree(node, { renderOrder: 0, material: excludeMaterial })}
|
||||||
</mesh>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!props.exclude && (
|
{!props.exclude &&
|
||||||
<mesh
|
jsxTree(node, {
|
||||||
{...props}
|
renderOrder: 1,
|
||||||
renderOrder={1}
|
onClick: ornamental ? () => {} : undefined,
|
||||||
onClick={ornamental ? () => {} : undefined}
|
userData: {
|
||||||
userData={
|
type: 'externalModule',
|
||||||
{
|
thickness: props.thickness,
|
||||||
type: 'externalModule',
|
} satisfies ArmorMeshUserData,
|
||||||
thickness: props.thickness,
|
material: new MeshBasicMaterial({
|
||||||
} satisfies ArmorMeshUserData
|
color: new Color(1, props.thickness / props.maxThickness, 0),
|
||||||
}
|
depthWrite: false,
|
||||||
>
|
colorWrite: !ornamental,
|
||||||
<meshBasicMaterial
|
}),
|
||||||
color={new Color(1, props.thickness / props.maxThickness, 0)}
|
})}
|
||||||
depthWrite={false}
|
|
||||||
colorWrite={!ornamental}
|
|
||||||
/>
|
|
||||||
</mesh>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { MeshProps } from '@react-three/fiber';
|
||||||
|
import { Mesh, Object3D } from 'three';
|
||||||
|
|
||||||
|
export function jsxTree(node: Object3D, mesh?: MeshProps) {
|
||||||
|
if (node instanceof Mesh) {
|
||||||
|
return (
|
||||||
|
<mesh
|
||||||
|
geometry={node.geometry}
|
||||||
|
material={node.material}
|
||||||
|
position={node.position}
|
||||||
|
rotation={node.rotation}
|
||||||
|
scale={node.scale}
|
||||||
|
{...mesh}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (node instanceof Object3D) {
|
||||||
|
return (
|
||||||
|
<group
|
||||||
|
position={node.position}
|
||||||
|
rotation={node.rotation}
|
||||||
|
scale={node.scale}
|
||||||
|
>
|
||||||
|
{node.children.map((child) => jsxTree(child, mesh))}
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { Mesh, Object3D } from 'three';
|
|
||||||
|
|
||||||
export function resolveJsxTree(parent: Object3D) {
|
|
||||||
return parent.children.map((child) => {
|
|
||||||
if (child.type === 'Mesh') {
|
|
||||||
return (
|
|
||||||
<mesh
|
|
||||||
key={child.uuid}
|
|
||||||
castShadow
|
|
||||||
receiveShadow
|
|
||||||
geometry={(child as Mesh).geometry}
|
|
||||||
material={(child as Mesh).material}
|
|
||||||
position={child.position}
|
|
||||||
rotation={child.rotation}
|
|
||||||
scale={child.scale}
|
|
||||||
children={resolveJsxTree(child)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (child.type === 'Object3D') {
|
|
||||||
return (
|
|
||||||
<group
|
|
||||||
key={child.uuid}
|
|
||||||
position={child.position}
|
|
||||||
rotation={child.rotation}
|
|
||||||
scale={child.scale}
|
|
||||||
children={resolveJsxTree(child)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new TypeError(
|
|
||||||
`Unhandled three jsx type ${child.type} (${child.name})`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user