fix: jsx tree node uuids

This commit is contained in:
TrèsAbhi committed 2024-04-21 11:09:24 -05:00
1 parent b805c2e3b4
commit abb72fb674
5 files changed
+312 -262

No files matched your search

@@ -124,11 +124,15 @@ export const TankModel = memo(() => {
event.stopPropagation(); event.stopPropagation();
} }
return jsxTree(node, { return jsxTree(
castShadow: true, node,
receiveShadow: true, {
onPointerDown, castShadow: true,
}); receiveShadow: true,
onPointerDown,
},
node.uuid,
);
})} })}
<group ref={turretContainer}> <group ref={turretContainer}>
@@ -189,11 +193,15 @@ export const TankModel = memo(() => {
window.removeEventListener('pointerup', handlePointerUp); window.removeEventListener('pointerup', handlePointerUp);
} }
return jsxTree(node, { return jsxTree(
castShadow: true, node,
receiveShadow: true, {
onPointerDown, castShadow: true,
}); receiveShadow: true,
onPointerDown,
},
node.uuid,
);
})} })}
<group ref={gunContainer}> <group ref={gunContainer}>
@@ -255,11 +263,15 @@ export const TankModel = memo(() => {
window.removeEventListener('pointerup', handlePointerUp); window.removeEventListener('pointerup', handlePointerUp);
} }
return jsxTree(node, { return jsxTree(
castShadow: true, node,
receiveShadow: true, {
onPointerDown, castShadow: true,
}); receiveShadow: true,
onPointerDown,
},
node.uuid,
);
})} })}
</group> </group>
</group> </group>
@@ -153,14 +153,22 @@ export function CoreArmorSceneComponent({
return ( return (
<> <>
{jsxTree(node, { {jsxTree(
renderOrder: 0, node,
material: excludeMaterial, {
})} renderOrder: 0,
{jsxTree(node, { material: excludeMaterial,
renderOrder: 1, },
material, node.uuid,
})} )}
{jsxTree(
node,
{
renderOrder: 1,
material,
},
node.uuid,
)}
</> </>
); );
} }
@@ -107,26 +107,34 @@ export function SpacedArmorSubExternal({
return ( return (
<> <>
{jsxTree(node, { {jsxTree(
renderOrder: 3, node,
material: new MeshBasicMaterial({ {
colorWrite: false, renderOrder: 3,
depthTest: true, material: new MeshBasicMaterial({
depthWrite: true, colorWrite: false,
...(clip ? { clippingPlanes: [clip] } : {}), depthTest: true,
}), depthWrite: true,
onClick() {}, ...(clip ? { clippingPlanes: [clip] } : {}),
userData: { }),
type: ArmorType.External, onClick() {},
thickness, userData: {
variant, type: ArmorType.External,
} satisfies ArmorUserData, thickness,
})} variant,
} satisfies ArmorUserData,
},
node.uuid,
)}
{jsxTree(node, { {jsxTree(
renderOrder: 4, node,
material, {
})} renderOrder: 4,
material,
},
node.uuid,
)}
</> </>
); );
} }
@@ -113,20 +113,28 @@ export function SpacedArmorSubSpaced({
return ( return (
<> <>
{jsxTree(node, { {jsxTree(
renderOrder: 2, node,
material, {
onClick() {}, renderOrder: 2,
userData: { material,
type: ArmorType.Spaced, onClick() {},
thickness, userData: {
} satisfies ArmorUserData, type: ArmorType.Spaced,
})} thickness,
} satisfies ArmorUserData,
},
node.uuid,
)}
{jsxTree(node, { {jsxTree(
renderOrder: 5, node,
material: depthWriteMaterial, {
})} renderOrder: 5,
material: depthWriteMaterial,
},
node.uuid,
)}
</> </>
); );
} }
@@ -81,251 +81,265 @@ export function SpacedArmorSceneComponent({
return ( return (
<> <>
{props.type === ArmorType.Core && {props.type === ArmorType.Core &&
jsxTree(node, { jsxTree(
renderOrder: 0, node,
material: omitMaterial, {
userData: { renderOrder: 0,
type: ArmorType.Core, material: omitMaterial,
thickness, userData: {
} satisfies ArmorUserData, type: ArmorType.Core,
thickness,
} satisfies ArmorUserData,
async onClick(event) { async onClick(event) {
event.stopPropagation(); event.stopPropagation();
const duel = useDuel.getState(); const duel = useDuel.getState();
const hasCalibratedShells = await hasEquipment( const hasCalibratedShells = await hasEquipment(
103, 103,
duel.antagonist!.tank.equipment, duel.antagonist!.tank.equipment,
duel.antagonist!.equipmentMatrix, duel.antagonist!.equipmentMatrix,
);
const hasEnhancedArmor = await hasEquipment(
110,
duel.protagonist!.tank.equipment,
duel.protagonist!.equipmentMatrix,
);
const thicknessCoefficient = hasEnhancedArmor ? 1.04 : 1;
const shell = useDuel.getState().antagonist!.shell;
const explosive = isExplosive(shell.type);
const splashing = canSplash(shell.type);
const penetration =
resolveNearPenetration(shell.penetration) *
(hasCalibratedShells
? isExplosive(shell.type)
? 1.1
: 1.05
: 1);
const shot: Shot = {
layers: [],
point: event.point,
}; // TODO: type this
let remainingPenetration = penetration;
const shellNormal = camera.position
.clone()
.sub(event.point)
.normalize();
pushLogs(shellNormal, filterIntersections(event.intersections), 0);
function filterIntersections(intersectionsRaw: Intersection[]) {
const showSpacedArmor =
useTankopediaPersistent.getState().model.visual.showSpacedArmor;
const intersectionsTyped = intersectionsRaw.filter(
({ object }) => object.userData.type !== undefined,
) as ArmorMeshIntersection[];
const intersectionsFiltered = intersectionsTyped.filter(
({ object }) =>
showSpacedArmor
? true
: object.userData.type === ArmorType.Core,
); );
const intersectionsWithDuplicates = intersectionsFiltered.slice( const hasEnhancedArmor = await hasEquipment(
110,
duel.protagonist!.tank.equipment,
duel.protagonist!.equipmentMatrix,
);
const thicknessCoefficient = hasEnhancedArmor ? 1.04 : 1;
const shell = useDuel.getState().antagonist!.shell;
const explosive = isExplosive(shell.type);
const splashing = canSplash(shell.type);
const penetration =
resolveNearPenetration(shell.penetration) *
(hasCalibratedShells
? isExplosive(shell.type)
? 1.1
: 1.05
: 1);
const shot: Shot = {
layers: [],
point: event.point,
}; // TODO: type this
let remainingPenetration = penetration;
const shellNormal = camera.position
.clone()
.sub(event.point)
.normalize();
pushLogs(
shellNormal,
filterIntersections(event.intersections),
0, 0,
intersectionsFiltered.findIndex(
({ object }) => object.userData.type === ArmorType.Core,
) + 1,
); );
const firstGunIntersectionIndex =
intersectionsWithDuplicates.findIndex( function filterIntersections(intersectionsRaw: Intersection[]) {
const showSpacedArmor =
useTankopediaPersistent.getState().model.visual
.showSpacedArmor;
const intersectionsTyped = intersectionsRaw.filter(
({ object }) => object.userData.type !== undefined,
) as ArmorMeshIntersection[];
const intersectionsFiltered = intersectionsTyped.filter(
({ object }) => ({ object }) =>
object.userData.type === ArmorType.External && showSpacedArmor
object.userData.variant === 'gun', ? true
: object.userData.type === ArmorType.Core,
); );
const firstTrackIntersectionIndex = const intersectionsWithDuplicates = intersectionsFiltered.slice(
intersectionsWithDuplicates.findIndex( 0,
({ object }) => intersectionsFiltered.findIndex(
object.userData.type === ArmorType.External && ({ object }) => object.userData.type === ArmorType.Core,
object.userData.variant === 'track', ) + 1,
); );
let intersections = intersectionsWithDuplicates; const firstGunIntersectionIndex =
intersectionsWithDuplicates.findIndex(
if (firstGunIntersectionIndex !== -1) { ({ object }) =>
intersections = intersections.filter(({ object }, index) => { object.userData.type === ArmorType.External &&
return object.userData.type === ArmorType.External && object.userData.variant === 'gun',
object.userData.variant === 'gun'
? index === firstGunIntersectionIndex
: true;
});
}
if (firstTrackIntersectionIndex !== -1) {
intersections = intersections.filter(({ object }, index) => {
return object.userData.type === ArmorType.External &&
object.userData.variant === 'track'
? index === firstTrackIntersectionIndex
: true;
});
}
return intersections;
}
function pushLogs(
shellNormal: Vector3,
intersections: ArmorMeshIntersection[],
startingIndex: number,
) {
let layerIndex = startingIndex;
let loopIndex = 0;
if (layerIndex !== 0 && intersections.length > 0) {
const distance = (
shot.layers.at(-1) as ShotLayerBase
).point.distanceTo(intersections[0].point);
if (explosive && !splashing)
remainingPenetration -= 0.5 * remainingPenetration * distance;
const blocked = remainingPenetration < 0;
shot.layers.push({
type: null,
status: blocked ? 'blocked' : 'penetration',
distance,
});
if (blocked) return;
}
for (const intersection of intersections) {
const surfaceNormal = intersection
.normal!.clone()
.applyQuaternion(
event.object.getWorldQuaternion(new Quaternion()),
); );
const thickness = const firstTrackIntersectionIndex =
thicknessCoefficient * intersection.object.userData.thickness; intersectionsWithDuplicates.findIndex(
({ object }) =>
object.userData.type === ArmorType.External &&
object.userData.variant === 'track',
);
let intersections = intersectionsWithDuplicates;
if (layerIndex !== 0 && startingIndex === 0) { if (firstGunIntersectionIndex !== -1) {
const previousIntersection = intersections[layerIndex - 1]; intersections = intersections.filter(({ object }, index) => {
const distance = return object.userData.type === ArmorType.External &&
intersection.distance - previousIntersection.distance; object.userData.variant === 'gun'
? index === firstGunIntersectionIndex
: true;
});
}
if (firstTrackIntersectionIndex !== -1) {
intersections = intersections.filter(({ object }, index) => {
return object.userData.type === ArmorType.External &&
object.userData.variant === 'track'
? index === firstTrackIntersectionIndex
: true;
});
}
return intersections;
}
function pushLogs(
shellNormal: Vector3,
intersections: ArmorMeshIntersection[],
startingIndex: number,
) {
let layerIndex = startingIndex;
let loopIndex = 0;
if (layerIndex !== 0 && intersections.length > 0) {
const distance = (
shot.layers.at(-1) as ShotLayerBase
).point.distanceTo(intersections[0].point);
if (explosive && !splashing) if (explosive && !splashing)
remainingPenetration -= remainingPenetration -=
0.5 * remainingPenetration * distance; 0.5 * remainingPenetration * distance;
const wasted = remainingPenetration < 0;
shot.layers.push({
type: null,
status: wasted ? 'blocked' : 'penetration',
distance,
});
if (wasted && !splashing) return;
}
if (intersection.object.userData.type === ArmorType.External) {
remainingPenetration -= thickness;
const blocked = remainingPenetration < 0; const blocked = remainingPenetration < 0;
shot.layers.push({ shot.layers.push({
type: ArmorType.External, type: null,
index: layerIndex,
shellNormal,
surfaceNormal,
point: intersection.point,
thickness: intersection.object.userData.thickness,
status: blocked ? 'blocked' : 'penetration', status: blocked ? 'blocked' : 'penetration',
distance,
}); });
if (splashing) { if (blocked) return;
remainingPenetration = Math.max(0, remainingPenetration); }
} else if (blocked) {
break; for (const intersection of intersections) {
} const surfaceNormal = intersection
} else { .normal!.clone()
const type = intersection.object.userData.type; .applyQuaternion(
const angle = surfaceNormal.angleTo(shellNormal); event.object.getWorldQuaternion(new Quaternion()),
const ricochet = degToRad(shell.ricochet ?? 90); );
const normalization = degToRad(shell.normalization ?? 0); const thickness =
const threeCalibersRule = thicknessCoefficient *
shell.caliber > thickness * 3 || layerIndex > 0; intersection.object.userData.thickness;
const twoCalibersRule = shell.caliber > thickness * 2;
const finalNormalization = twoCalibersRule if (layerIndex !== 0 && startingIndex === 0) {
? (1.4 * normalization * shell.caliber) / (2.0 * thickness) const previousIntersection = intersections[layerIndex - 1];
: normalization; const distance =
const finalThickness = intersection.distance - previousIntersection.distance;
thickness === 0 if (explosive && !splashing)
? 0 remainingPenetration -=
: thickness / 0.5 * remainingPenetration * distance;
Math.cos(Math.max(0, angle - finalNormalization)); const wasted = remainingPenetration < 0;
if (!threeCalibersRule && angle >= ricochet) {
shot.layers.push({ shot.layers.push({
type, type: null,
index: layerIndex, status: wasted ? 'blocked' : 'penetration',
shellNormal, distance,
surfaceNormal,
point: intersection.point,
thickness,
thicknessAngled: finalThickness,
angle,
status: 'ricochet',
}); });
remainingPenetration *= 0.75; if (wasted && !splashing) return;
}
const raycaster = new Raycaster(); if (
const newShellNormal = shellNormal intersection.object.userData.type === ArmorType.External
.clone() ) {
.reflect(surfaceNormal); remainingPenetration -= thickness;
const newCameraNormal = newShellNormal
.clone()
.multiplyScalar(-1);
raycaster.set(intersection.point, newCameraNormal);
pushLogs(
newShellNormal,
filterIntersections(
raycaster.intersectObjects(scene.children, true),
),
layerIndex + 1,
);
break;
} else {
remainingPenetration -= finalThickness;
const blocked = remainingPenetration < 0; const blocked = remainingPenetration < 0;
shot.layers.push({ shot.layers.push({
type, type: ArmorType.External,
index: layerIndex, index: layerIndex,
shellNormal, shellNormal,
surfaceNormal, surfaceNormal,
point: intersection.point, point: intersection.point,
thickness, thickness: intersection.object.userData.thickness,
thicknessAngled: finalThickness,
angle,
status: blocked ? 'blocked' : 'penetration', status: blocked ? 'blocked' : 'penetration',
}); });
if (splashing) {
remainingPenetration = Math.max(0, remainingPenetration);
} else if (blocked) {
break;
}
} else {
const type = intersection.object.userData.type;
const angle = surfaceNormal.angleTo(shellNormal);
const ricochet = degToRad(shell.ricochet ?? 90);
const normalization = degToRad(shell.normalization ?? 0);
const threeCalibersRule =
shell.caliber > thickness * 3 || layerIndex > 0;
const twoCalibersRule = shell.caliber > thickness * 2;
const finalNormalization = twoCalibersRule
? (1.4 * normalization * shell.caliber) /
(2.0 * thickness)
: normalization;
const finalThickness =
thickness === 0
? 0
: thickness /
Math.cos(Math.max(0, angle - finalNormalization));
if (!threeCalibersRule && angle >= ricochet) {
shot.layers.push({
type,
index: layerIndex,
shellNormal,
surfaceNormal,
point: intersection.point,
thickness,
thicknessAngled: finalThickness,
angle,
status: 'ricochet',
});
remainingPenetration *= 0.75;
const raycaster = new Raycaster();
const newShellNormal = shellNormal
.clone()
.reflect(surfaceNormal);
const newCameraNormal = newShellNormal
.clone()
.multiplyScalar(-1);
raycaster.set(intersection.point, newCameraNormal);
pushLogs(
newShellNormal,
filterIntersections(
raycaster.intersectObjects(scene.children, true),
),
layerIndex + 1,
);
break;
} else {
remainingPenetration -= finalThickness;
const blocked = remainingPenetration < 0;
shot.layers.push({
type,
index: layerIndex,
shellNormal,
surfaceNormal,
point: intersection.point,
thickness,
thicknessAngled: finalThickness,
angle,
status: blocked ? 'blocked' : 'penetration',
});
}
} }
layerIndex++;
loopIndex++;
} }
layerIndex++;
loopIndex++;
} }
}
useTankopediaTemporary.setState({ shot }); useTankopediaTemporary.setState({ shot });
},
}, },
})} node.uuid,
)}
{props.type === ArmorType.Spaced && ( {props.type === ArmorType.Spaced && (
<SpacedArmorSubSpaced node={node} thickness={thickness} /> <SpacedArmorSubSpaced node={node} thickness={thickness} />