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