From abb72fb674386c42d2050a2f9de6ea19f8df69eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Sun, 21 Apr 2024 11:09:24 -0500 Subject: [PATCH] fix: jsx tree node uuids --- .../components/Model/components/TankModel.tsx | 42 +- .../CoreArmorSceneComponent/index.tsx | 24 +- .../SpacedArmorSubExternal/index.tsx | 46 +- .../components/SpacedArmorSubSpaced/index.tsx | 34 +- .../SpacedArmorSceneComponent/index.tsx | 428 +++++++++--------- 5 files changed, 312 insertions(+), 262 deletions(-) diff --git a/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx b/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx index 32c0fde34..0d165b513 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx @@ -124,11 +124,15 @@ export const TankModel = memo(() => { event.stopPropagation(); } - return jsxTree(node, { - castShadow: true, - receiveShadow: true, - onPointerDown, - }); + return jsxTree( + node, + { + castShadow: true, + receiveShadow: true, + onPointerDown, + }, + node.uuid, + ); })} @@ -189,11 +193,15 @@ export const TankModel = memo(() => { window.removeEventListener('pointerup', handlePointerUp); } - return jsxTree(node, { - castShadow: true, - receiveShadow: true, - onPointerDown, - }); + return jsxTree( + node, + { + castShadow: true, + receiveShadow: true, + onPointerDown, + }, + node.uuid, + ); })} @@ -255,11 +263,15 @@ export const TankModel = memo(() => { window.removeEventListener('pointerup', handlePointerUp); } - return jsxTree(node, { - castShadow: true, - receiveShadow: true, - onPointerDown, - }); + return jsxTree( + node, + { + castShadow: true, + receiveShadow: true, + onPointerDown, + }, + node.uuid, + ); })} diff --git a/src/components/Armor/components/CoreArmorSceneComponent/index.tsx b/src/components/Armor/components/CoreArmorSceneComponent/index.tsx index 079f04dca..3a67eaef1 100644 --- a/src/components/Armor/components/CoreArmorSceneComponent/index.tsx +++ b/src/components/Armor/components/CoreArmorSceneComponent/index.tsx @@ -153,14 +153,22 @@ export function CoreArmorSceneComponent({ return ( <> - {jsxTree(node, { - renderOrder: 0, - material: excludeMaterial, - })} - {jsxTree(node, { - renderOrder: 1, - material, - })} + {jsxTree( + node, + { + renderOrder: 0, + material: excludeMaterial, + }, + node.uuid, + )} + {jsxTree( + node, + { + renderOrder: 1, + material, + }, + node.uuid, + )} ); } diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx index 8f3b12d16..dd11dfd41 100644 --- a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx +++ b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubExternal/index.tsx @@ -107,26 +107,34 @@ export function SpacedArmorSubExternal({ return ( <> - {jsxTree(node, { - 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, + { + renderOrder: 3, + material: new MeshBasicMaterial({ + colorWrite: false, + depthTest: true, + depthWrite: true, + ...(clip ? { clippingPlanes: [clip] } : {}), + }), + onClick() {}, + userData: { + type: ArmorType.External, + thickness, + variant, + } satisfies ArmorUserData, + }, + node.uuid, + )} - {jsxTree(node, { - renderOrder: 4, - material, - })} + {jsxTree( + node, + { + renderOrder: 4, + material, + }, + node.uuid, + )} ); } diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx index c283f3ce2..fe181ff15 100644 --- a/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx +++ b/src/components/Armor/components/SpacedArmorSceneComponent/components/SpacedArmorSubSpaced/index.tsx @@ -113,20 +113,28 @@ export function SpacedArmorSubSpaced({ return ( <> - {jsxTree(node, { - renderOrder: 2, - material, - onClick() {}, - userData: { - type: ArmorType.Spaced, - thickness, - } satisfies ArmorUserData, - })} + {jsxTree( + node, + { + renderOrder: 2, + material, + onClick() {}, + userData: { + type: ArmorType.Spaced, + thickness, + } satisfies ArmorUserData, + }, + node.uuid, + )} - {jsxTree(node, { - renderOrder: 5, - material: depthWriteMaterial, - })} + {jsxTree( + node, + { + renderOrder: 5, + material: depthWriteMaterial, + }, + node.uuid, + )} ); } diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx index 607be66ec..338ce2ee0 100644 --- a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx +++ b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx @@ -81,251 +81,265 @@ export function SpacedArmorSceneComponent({ return ( <> {props.type === ArmorType.Core && - jsxTree(node, { - renderOrder: 0, - material: omitMaterial, - userData: { - type: ArmorType.Core, - thickness, - } satisfies ArmorUserData, + jsxTree( + node, + { + renderOrder: 0, + material: omitMaterial, + userData: { + type: ArmorType.Core, + thickness, + } satisfies ArmorUserData, - async onClick(event) { - event.stopPropagation(); + async onClick(event) { + event.stopPropagation(); - const duel = useDuel.getState(); - const hasCalibratedShells = await hasEquipment( - 103, - duel.antagonist!.tank.equipment, - 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 duel = useDuel.getState(); + const hasCalibratedShells = await hasEquipment( + 103, + duel.antagonist!.tank.equipment, + duel.antagonist!.equipmentMatrix, ); - 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, - 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.userData.type === ArmorType.External && - object.userData.variant === 'gun', + showSpacedArmor + ? true + : object.userData.type === ArmorType.Core, ); - const firstTrackIntersectionIndex = - intersectionsWithDuplicates.findIndex( - ({ object }) => - object.userData.type === ArmorType.External && - object.userData.variant === 'track', + const intersectionsWithDuplicates = intersectionsFiltered.slice( + 0, + intersectionsFiltered.findIndex( + ({ object }) => object.userData.type === ArmorType.Core, + ) + 1, ); - let intersections = intersectionsWithDuplicates; - - if (firstGunIntersectionIndex !== -1) { - intersections = intersections.filter(({ object }, index) => { - return object.userData.type === ArmorType.External && - 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 firstGunIntersectionIndex = + intersectionsWithDuplicates.findIndex( + ({ object }) => + object.userData.type === ArmorType.External && + object.userData.variant === 'gun', ); - const thickness = - thicknessCoefficient * intersection.object.userData.thickness; + const firstTrackIntersectionIndex = + intersectionsWithDuplicates.findIndex( + ({ object }) => + object.userData.type === ArmorType.External && + object.userData.variant === 'track', + ); + let intersections = intersectionsWithDuplicates; - if (layerIndex !== 0 && startingIndex === 0) { - const previousIntersection = intersections[layerIndex - 1]; - const distance = - intersection.distance - previousIntersection.distance; + if (firstGunIntersectionIndex !== -1) { + intersections = intersections.filter(({ object }, index) => { + return object.userData.type === ArmorType.External && + 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 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; shot.layers.push({ - type: ArmorType.External, - index: layerIndex, - shellNormal, - surfaceNormal, - point: intersection.point, - thickness: intersection.object.userData.thickness, + type: null, status: blocked ? 'blocked' : 'penetration', + distance, }); - 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 (blocked) return; + } + + for (const intersection of intersections) { + const surfaceNormal = intersection + .normal!.clone() + .applyQuaternion( + event.object.getWorldQuaternion(new Quaternion()), + ); + const thickness = + thicknessCoefficient * + intersection.object.userData.thickness; + + if (layerIndex !== 0 && startingIndex === 0) { + const previousIntersection = intersections[layerIndex - 1]; + const distance = + intersection.distance - previousIntersection.distance; + if (explosive && !splashing) + remainingPenetration -= + 0.5 * remainingPenetration * distance; + const wasted = remainingPenetration < 0; - if (!threeCalibersRule && angle >= ricochet) { shot.layers.push({ - type, - index: layerIndex, - shellNormal, - surfaceNormal, - point: intersection.point, - thickness, - thicknessAngled: finalThickness, - angle, - status: 'ricochet', + type: null, + status: wasted ? 'blocked' : 'penetration', + distance, }); - remainingPenetration *= 0.75; + if (wasted && !splashing) return; + } - 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; + if ( + intersection.object.userData.type === ArmorType.External + ) { + remainingPenetration -= thickness; const blocked = remainingPenetration < 0; shot.layers.push({ - type, + type: ArmorType.External, index: layerIndex, shellNormal, surfaceNormal, point: intersection.point, - thickness, - thicknessAngled: finalThickness, - angle, + thickness: intersection.object.userData.thickness, 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 && (