feat: opaque amror

This commit is contained in:
TrèsAbhi committed 2024-01-10 17:29:49 -06:00
1 parent f6b4bb3923
commit f9d0659bd4
8 files changed
+119 -28

No files matched your search

+1
View File
@@ -37,6 +37,7 @@
"fullstats",
"fuzzysort",
"gamemode",
"glsl",
"GLTF",
"immer",
"JOINTINDEX",
@@ -1,9 +1,16 @@
import {
EnterFullScreenIcon,
ExitFullScreenIcon,
FrameIcon,
GearIcon,
} from '@radix-ui/react-icons';
import { Button, Card, Flex, Tabs, Theme } from '@radix-ui/themes';
import {
Button,
Card,
DropdownMenu,
Flex,
Tabs,
Theme,
} from '@radix-ui/themes';
import { Html } from '@react-three/drei';
import { Canvas } from '@react-three/fiber';
import { Suspense, use, useEffect, useRef, useState } from 'react';
@@ -29,6 +36,7 @@ export function TankDisplay() {
const [isFullScreen, setIsFullScreen] = useState(false);
const mode = useTankopedia((state) => state.mode);
const showGrid = useTankopedia((state) => state.model.showGrid);
const opaqueArmor = useTankopedia((state) => state.model.opaqueArmor);
const protagonist = useTankopedia((state) => {
if (!state.areTanksAssigned) return;
return state.protagonist;
@@ -147,17 +155,37 @@ export function TankDisplay() {
right: 18,
}}
>
<Button
color={showGrid ? 'purple' : 'gray'}
variant="ghost"
onClick={() => {
mutateTankopedia((draft) => {
draft.model.showGrid = !draft.model.showGrid;
});
}}
>
<FrameIcon />
</Button>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button color={showGrid ? 'purple' : 'gray'} variant="ghost">
<GearIcon />
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.CheckboxItem
checked={showGrid}
onCheckedChange={(checked) => {
mutateTankopedia((draft) => {
draft.model.showGrid = checked;
});
}}
>
Show grid
</DropdownMenu.CheckboxItem>
<DropdownMenu.CheckboxItem
checked={opaqueArmor}
onCheckedChange={(checked) => {
mutateTankopedia((draft) => {
draft.model.opaqueArmor = checked;
});
}}
>
Opaque armor
</DropdownMenu.CheckboxItem>
</DropdownMenu.Content>
</DropdownMenu.Root>
<Button
variant="ghost"
@@ -11,6 +11,7 @@ import {
modelDefinitions,
} from '../../../../../../../core/blitzkrieg/modelDefinitions';
import { useTankopedia } from '../../../../../../../stores/tankopedia';
import { Lighting } from '../../Lighting';
interface TankArmorProps extends GroupProps {}
@@ -81,6 +82,8 @@ export function TankArmor(props: TankArmorProps) {
return (
<HeadsUpDisplay>
<Lighting />
<group {...props} rotation={[-Math.PI / 2, 0, model.hullYaw]}>
{nodes.map((node) => {
const isHull = node.name.startsWith('hull_');
+9 -4
View File
@@ -1,7 +1,9 @@
import { MeshProps } from '@react-three/fiber';
import { MeshBasicMaterial } from 'three';
import { MeshStandardMaterial } from 'three';
import CustomShaderMaterial from 'three-custom-shader-material';
import { useTankopedia } from '../../stores/tankopedia';
import fragment from './shaders/fragment.glsl';
import fragmentOpaque from './shaders/fragmentOpaque.glsl';
import vertex from './shaders/vertex.glsl';
interface ArmorMeshProps extends MeshProps {
@@ -20,6 +22,8 @@ export function ArmorMesh({
normalization,
...props
}: ArmorMeshProps) {
const opaqueArmor = useTankopedia((state) => state.model.opaqueArmor);
return (
<>
<mesh {...props}>
@@ -28,10 +32,10 @@ export function ArmorMesh({
<mesh {...props} renderOrder={1}>
<CustomShaderMaterial
baseMaterial={MeshBasicMaterial}
baseMaterial={MeshStandardMaterial}
transparent
silent
depthWrite={false}
// depthWrite={false}
uniforms={{
thickness: { value: thickness },
penetration: { value: penetration },
@@ -40,7 +44,8 @@ export function ArmorMesh({
normalization: { value: normalization * (Math.PI / 180) },
}}
vertexShader={vertex}
fragmentShader={fragment}
// split shaders due to https://github.com/FarazzShaikh/THREE-CustomShaderMaterial/issues/48
fragmentShader={opaqueArmor ? fragmentOpaque : fragment}
/>
</mesh>
</>
@@ -1,4 +1,6 @@
varying vec3 vViewPosition;
// split shaders due to https://github.com/FarazzShaikh/THREE-CustomShaderMaterial/issues/48
varying vec3 vCSMViewPosition;
uniform float thickness;
uniform float penetration;
uniform float ricochet;
@@ -7,7 +9,7 @@ uniform float normalization;
void main() {
vec3 normalizedNormal = normalize(vNormal);
vec3 normalizedViewPosition = normalize(vViewPosition);
vec3 normalizedViewPosition = normalize(vCSMViewPosition);
float cosAngle = dot(normalizedNormal, -normalizedViewPosition);
float angle = acos(cosAngle);
@@ -18,18 +20,18 @@ void main() {
float finalPenetration = penetration;
float effectiveArmor = thickness / cos(angle - finalNormalization);
if(angle < ricochet) {
if (angle < ricochet) {
// no auto-ricochet, resume checks
if(threeCaliberRule) {
if (threeCaliberRule) {
penetrationChance = 1.0;
} else {
float maxRandomPenetrationBuff = finalPenetration * 0.05;
float penetrationDifference = effectiveArmor - finalPenetration;
if(penetrationDifference > maxRandomPenetrationBuff) {
if (penetrationDifference > maxRandomPenetrationBuff) {
// even with a +5% buff, can't penetrate
penetrationChance = 0.0;
} else if(penetrationDifference < -maxRandomPenetrationBuff) {
} else if (penetrationDifference < -maxRandomPenetrationBuff) {
// event with a -5% nerf, can penetrate
penetrationChance = 1.0;
} else {
@@ -39,5 +41,5 @@ void main() {
}
}
csm_FragColor *= vec4(1.0, 0.0, 0.0, (1.0 - penetrationChance) * 0.5);
csm_FragColor = vec4(1.0, 0.0, 0.0, (1.0 - penetrationChance) * 0.5);
}
@@ -0,0 +1,52 @@
// split shaders due to https://github.com/FarazzShaikh/THREE-CustomShaderMaterial/issues/48
varying vec3 vCSMViewPosition;
uniform float thickness;
uniform float penetration;
uniform float ricochet;
uniform float caliber;
uniform float normalization;
void main() {
vec3 normalizedNormal = normalize(vNormal);
vec3 normalizedViewPosition = normalize(vCSMViewPosition);
float cosAngle = dot(normalizedNormal, -normalizedViewPosition);
float angle = acos(cosAngle);
float penetrationChance = 0.0;
bool twoCaliberRule = caliber > 2.0 * thickness;
bool threeCaliberRule = caliber > 3.0 * thickness;
float finalNormalization = twoCaliberRule ? (normalization * 1.4 * caliber) / (2.0 * thickness) : normalization;
float finalPenetration = penetration;
float effectiveArmor = thickness / cos(angle - finalNormalization);
if (angle < ricochet) {
// no auto-ricochet, resume checks
if (threeCaliberRule) {
penetrationChance = 1.0;
} else {
float maxRandomPenetrationBuff = finalPenetration * 0.05;
float penetrationDifference = effectiveArmor - finalPenetration;
if (penetrationDifference > maxRandomPenetrationBuff) {
// even with a +5% buff, can't penetrate
penetrationChance = 0.0;
} else if (penetrationDifference < -maxRandomPenetrationBuff) {
// event with a -5% nerf, can penetrate
penetrationChance = 1.0;
} else {
// penetration gradiant
penetrationChance = 1.0 - (penetrationDifference + maxRandomPenetrationBuff) / (2.0 * maxRandomPenetrationBuff);
}
}
}
float h = penetrationChance / PI;
float s = 1.0;
float v = 1.0;
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(h + K.xyz) * 6.0 - K.www);
vec3 rgb = v * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), s);
csm_DiffuseColor = vec4(rgb, 1.0);
}
+2 -4
View File
@@ -1,8 +1,6 @@
varying vec3 vNormal;
varying vec3 vViewPosition;
varying vec3 vCSMViewPosition;
void main() {
vec4 vViewPosition4 = modelViewMatrix * vec4(position, 1.0);
vViewPosition = vViewPosition4.xyz;
vNormal = normalMatrix * normal;
vCSMViewPosition = vViewPosition4.xyz;
}
+2
View File
@@ -32,6 +32,7 @@ type Tankopedia = {
gunPitch: number;
controlsEnabled: boolean;
showGrid: boolean;
opaqueArmor: boolean;
};
mode: TankopediaMode;
} & (
@@ -70,6 +71,7 @@ export const useTankopedia = create<Tankopedia>()(() => ({
turretYaw: 0,
controlsEnabled: true,
showGrid: true,
opaqueArmor: false,
},
areTanksAssigned: false,
mode: 'model',