mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 12:09:51 +02:00
feat: spaced armor in space caught shell
This commit is contained in:
1 parent
fe0fdc0964
commit
647c9240a5
4 files changed
+103
-7
No files matched your search
@@ -7,6 +7,7 @@ import { HeadsUpDisplay } from '../../../../../../../components/HeadsUpDisplay';
|
|||||||
import { X_AXIS } from '../../../../../../../constants/axis';
|
import { X_AXIS } from '../../../../../../../constants/axis';
|
||||||
import { canRicochet } from '../../../../../../../core/blitz/canRicochet';
|
import { canRicochet } from '../../../../../../../core/blitz/canRicochet';
|
||||||
import { canSplash } from '../../../../../../../core/blitz/canSplash';
|
import { canSplash } from '../../../../../../../core/blitz/canSplash';
|
||||||
|
import { isAffectedBySpaced } from '../../../../../../../core/blitz/isAffectedBySpaced';
|
||||||
import { numericPenetration } from '../../../../../../../core/blitz/numericPenetration';
|
import { numericPenetration } from '../../../../../../../core/blitz/numericPenetration';
|
||||||
import { asset } from '../../../../../../../core/blitzkrieg/asset';
|
import { asset } from '../../../../../../../core/blitzkrieg/asset';
|
||||||
import {
|
import {
|
||||||
@@ -36,10 +37,14 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
|||||||
|
|
||||||
if (!protagonist || !antagonist) return null;
|
if (!protagonist || !antagonist) return null;
|
||||||
|
|
||||||
const gltf = useLoader(
|
const armorGltf = useLoader(
|
||||||
GLTFLoader,
|
GLTFLoader,
|
||||||
asset(`3d/tanks/armor/${protagonist.tank.id}.glb`),
|
asset(`3d/tanks/armor/${protagonist.tank.id}.glb`),
|
||||||
);
|
);
|
||||||
|
const modelGltf = useLoader(
|
||||||
|
GLTFLoader,
|
||||||
|
asset(`3d/tanks/models/${protagonist.tank.id}.glb`),
|
||||||
|
);
|
||||||
const model = useTankopedia((state) => state.model);
|
const model = useTankopedia((state) => state.model);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -50,7 +55,8 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
|||||||
|
|
||||||
if (!awaitedModelDefinitions) return null;
|
if (!awaitedModelDefinitions) return null;
|
||||||
|
|
||||||
const nodes = Object.values(gltf.nodes);
|
const armorNodes = Object.values(armorGltf.nodes);
|
||||||
|
const modelNodes = Object.values(modelGltf.nodes);
|
||||||
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
|
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
|
||||||
const turretModelDefinition =
|
const turretModelDefinition =
|
||||||
tankModelDefinition.turrets[protagonist.turret.id];
|
tankModelDefinition.turrets[protagonist.turret.id];
|
||||||
@@ -71,6 +77,7 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
|||||||
const turretRotation = new Euler(0, 0, model.turretYaw);
|
const turretRotation = new Euler(0, 0, model.turretYaw);
|
||||||
const ricochetCapable = canRicochet(antagonist.shell.type);
|
const ricochetCapable = canRicochet(antagonist.shell.type);
|
||||||
const splashCapable = canSplash(antagonist.shell.type);
|
const splashCapable = canSplash(antagonist.shell.type);
|
||||||
|
const affectedBySpacedCapable = isAffectedBySpaced(antagonist.shell.type);
|
||||||
|
|
||||||
if (tankModelDefinition.turretRotation) {
|
if (tankModelDefinition.turretRotation) {
|
||||||
const pitch = -tankModelDefinition.turretRotation.pitch * (Math.PI / 180);
|
const pitch = -tankModelDefinition.turretRotation.pitch * (Math.PI / 180);
|
||||||
@@ -97,7 +104,7 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
|||||||
rotation={[-Math.PI / 2, 0, model.hullYaw]}
|
rotation={[-Math.PI / 2, 0, model.hullYaw]}
|
||||||
visible={controlsEnabled}
|
visible={controlsEnabled}
|
||||||
>
|
>
|
||||||
{nodes.map((node) => {
|
{armorNodes.map((node) => {
|
||||||
const isHull = node.name.startsWith('hull_');
|
const isHull = node.name.startsWith('hull_');
|
||||||
const armorId = parseInt(node.name.match(/.+_armor_(\d+)/)![1]);
|
const armorId = parseInt(node.name.match(/.+_armor_(\d+)/)![1]);
|
||||||
const isVisible = isHull;
|
const isVisible = isHull;
|
||||||
@@ -113,6 +120,7 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ArmorMesh
|
<ArmorMesh
|
||||||
|
isAffectedBySpaced={affectedBySpacedCapable}
|
||||||
canSplash={splashCapable}
|
canSplash={splashCapable}
|
||||||
spaced={isSpaced}
|
spaced={isSpaced}
|
||||||
key={node.uuid}
|
key={node.uuid}
|
||||||
@@ -135,8 +143,41 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{modelNodes.map((node) => {
|
||||||
|
const isWheel = node.name.startsWith('chassis_wheel_');
|
||||||
|
const isTrack = node.name.startsWith('chassis_track_');
|
||||||
|
const isVisible = isWheel || isTrack;
|
||||||
|
const thickness = 50;
|
||||||
|
|
||||||
|
if (!isVisible || !showSpacedArmor) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ArmorMesh
|
||||||
|
isAffectedBySpaced={affectedBySpacedCapable}
|
||||||
|
canSplash={splashCapable}
|
||||||
|
spaced
|
||||||
|
key={node.uuid}
|
||||||
|
geometry={(node as Mesh).geometry}
|
||||||
|
thickness={thickness}
|
||||||
|
penetration={numericPenetration(antagonist.shell.penetration)}
|
||||||
|
canRicochet={ricochetCapable}
|
||||||
|
ricochet={
|
||||||
|
ricochetCapable
|
||||||
|
? (antagonist.shell as RicochetCapableShell).ricochet
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
normalization={
|
||||||
|
ricochetCapable
|
||||||
|
? (antagonist.shell as RicochetCapableShell).normalization
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
caliber={antagonist.shell.caliber}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
<group position={turretPosition} rotation={turretRotation}>
|
<group position={turretPosition} rotation={turretRotation}>
|
||||||
{nodes.map((node) => {
|
{armorNodes.map((node) => {
|
||||||
const isCurrentTurret = node.name.startsWith(
|
const isCurrentTurret = node.name.startsWith(
|
||||||
`turret_${turretModelDefinition.model
|
`turret_${turretModelDefinition.model
|
||||||
.toString()
|
.toString()
|
||||||
@@ -157,6 +198,7 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ArmorMesh
|
<ArmorMesh
|
||||||
|
isAffectedBySpaced={affectedBySpacedCapable}
|
||||||
canSplash={splashCapable}
|
canSplash={splashCapable}
|
||||||
spaced={isSpaced}
|
spaced={isSpaced}
|
||||||
key={node.uuid}
|
key={node.uuid}
|
||||||
@@ -189,7 +231,7 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
|||||||
.add(gunOrigin)}
|
.add(gunOrigin)}
|
||||||
rotation={[model.gunPitch, 0, 0]}
|
rotation={[model.gunPitch, 0, 0]}
|
||||||
>
|
>
|
||||||
{nodes.map((node) => {
|
{armorNodes.map((node) => {
|
||||||
const isCurrentGun = node.name.startsWith(
|
const isCurrentGun = node.name.startsWith(
|
||||||
`gun_${gunModelDefinition.model.toString().padStart(2, '0')}`,
|
`gun_${gunModelDefinition.model.toString().padStart(2, '0')}`,
|
||||||
);
|
);
|
||||||
@@ -207,6 +249,7 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
|||||||
return null;
|
return null;
|
||||||
return (
|
return (
|
||||||
<ArmorMesh
|
<ArmorMesh
|
||||||
|
isAffectedBySpaced={affectedBySpacedCapable}
|
||||||
canSplash={splashCapable}
|
canSplash={splashCapable}
|
||||||
spaced={isSpaced}
|
spaced={isSpaced}
|
||||||
key={node.uuid}
|
key={node.uuid}
|
||||||
@@ -229,6 +272,40 @@ export function TankArmor({ ...props }: TankArmorProps) {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{modelNodes.map((node) => {
|
||||||
|
const isCurrentGun =
|
||||||
|
node.name ===
|
||||||
|
`gun_${gunModelDefinition.model.toString().padStart(2, '0')}`;
|
||||||
|
const isVisible = isCurrentGun;
|
||||||
|
const thickness = 50;
|
||||||
|
|
||||||
|
if (!isVisible || !showSpacedArmor) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ArmorMesh
|
||||||
|
isAffectedBySpaced={affectedBySpacedCapable}
|
||||||
|
canSplash={splashCapable}
|
||||||
|
spaced
|
||||||
|
key={node.uuid}
|
||||||
|
geometry={(node as Mesh).geometry}
|
||||||
|
thickness={thickness}
|
||||||
|
penetration={numericPenetration(antagonist.shell.penetration)}
|
||||||
|
canRicochet={ricochetCapable}
|
||||||
|
ricochet={
|
||||||
|
ricochetCapable
|
||||||
|
? (antagonist.shell as RicochetCapableShell).ricochet
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
normalization={
|
||||||
|
ricochetCapable
|
||||||
|
? (antagonist.shell as RicochetCapableShell).normalization
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
caliber={antagonist.shell.caliber}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ interface ArmorMeshProps extends MeshProps {
|
|||||||
canSplash: boolean;
|
canSplash: boolean;
|
||||||
ricochet?: number;
|
ricochet?: number;
|
||||||
normalization?: number;
|
normalization?: number;
|
||||||
|
isAffectedBySpaced: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// shader split: https://github.com/FarazzShaikh/THREE-CustomShaderMaterial/issues/48
|
// shader split: https://github.com/FarazzShaikh/THREE-CustomShaderMaterial/issues/48
|
||||||
@@ -26,6 +27,7 @@ export function ArmorMesh({
|
|||||||
canRicochet,
|
canRicochet,
|
||||||
ricochet,
|
ricochet,
|
||||||
canSplash,
|
canSplash,
|
||||||
|
isAffectedBySpaced,
|
||||||
normalization,
|
normalization,
|
||||||
spaced = false,
|
spaced = false,
|
||||||
...props
|
...props
|
||||||
@@ -62,6 +64,7 @@ export function ArmorMesh({
|
|||||||
spaced: { value: spaced ?? false },
|
spaced: { value: spaced ?? false },
|
||||||
canRicochet: { value: canRicochet },
|
canRicochet: { value: canRicochet },
|
||||||
canSplash: { value: canSplash },
|
canSplash: { value: canSplash },
|
||||||
|
isAffectedBySpaced: { value: isAffectedBySpaced },
|
||||||
}}
|
}}
|
||||||
vertexShader={vertex}
|
vertexShader={vertex}
|
||||||
fragmentShader={opaqueArmor && !spaced ? fragmentOpaque : fragment}
|
fragmentShader={opaqueArmor && !spaced ? fragmentOpaque : fragment}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ uniform float normalization;
|
|||||||
uniform bool spaced;
|
uniform bool spaced;
|
||||||
uniform bool canRichochet;
|
uniform bool canRichochet;
|
||||||
uniform bool canSplash;
|
uniform bool canSplash;
|
||||||
|
uniform bool isAffectedBySpaced;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 normalizedNormal = normalize(vNormal);
|
vec3 normalizedNormal = normalize(vNormal);
|
||||||
@@ -45,14 +46,22 @@ void main() {
|
|||||||
|
|
||||||
if (canSplash) {
|
if (canSplash) {
|
||||||
if (spaced) {
|
if (spaced) {
|
||||||
csm_FragColor = vec4(1.0, 0.0, 0.0, alpha);
|
if (isAffectedBySpaced) {
|
||||||
|
csm_FragColor = vec4(1.0, 0.0, 0.0, 0.5);
|
||||||
|
} else {
|
||||||
|
csm_FragColor = vec4(1.0, 0.0, 0.0, alpha);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// color taken directly from the game
|
// color taken directly from the game
|
||||||
csm_FragColor = vec4(1.0, 0.392, 0.0, alpha);
|
csm_FragColor = vec4(1.0, 0.392, 0.0, alpha);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (spaced) {
|
if (spaced) {
|
||||||
csm_FragColor = vec4(0.0, 0.0, 1.0, alpha);
|
if (isAffectedBySpaced) {
|
||||||
|
csm_FragColor = vec4(0.0, 0.0, 1.0, 0.5);
|
||||||
|
} else {
|
||||||
|
csm_FragColor = vec4(0.0, 0.0, 1.0, alpha);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
csm_FragColor = vec4(1.0, 0.0, 0.0, alpha);
|
csm_FragColor = vec4(1.0, 0.0, 0.0, alpha);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { ShellDefinition } from '../blitzkrieg/tankDefinitions';
|
||||||
|
|
||||||
|
export function isAffectedBySpaced(
|
||||||
|
type: ShellDefinition['type'],
|
||||||
|
): type is 'he' | 'hc' {
|
||||||
|
return type === 'he' || type === 'hc';
|
||||||
|
}
|
||||||
Reference in new issue
Block a user