feat: advanced mode

This commit is contained in:
tresabhi committed 2024-12-15 18:13:23 -06:00
1 parent bc9c9ad4e7
commit 318a71bd32
5 files changed
+56 -10

No files matched your search

@@ -49,6 +49,7 @@ export function PrimaryArmorSceneComponent({
damage: { value: null }, damage: { value: null },
explosionRadius: { value: null }, explosionRadius: { value: null },
greenPenetration: { value: null }, greenPenetration: { value: null },
advancedHighlighting: { value: null },
opaque: { value: null }, opaque: { value: null },
inverseProjectionMatrix: { value: null }, inverseProjectionMatrix: { value: null },
@@ -90,6 +91,9 @@ export function PrimaryArmorSceneComponent({
function handleGreenPenetrationChange(greenPenetration: boolean) { function handleGreenPenetrationChange(greenPenetration: boolean) {
material.uniforms.greenPenetration.value = greenPenetration; material.uniforms.greenPenetration.value = greenPenetration;
} }
function handleAdvancedHighlightingChange(advancedHighlighting: boolean) {
material.uniforms.advancedHighlighting.value = advancedHighlighting;
}
function handleOpaqueChange(opaque: boolean) { function handleOpaqueChange(opaque: boolean) {
material.uniforms.opaque.value = opaque; material.uniforms.opaque.value = opaque;
} }
@@ -133,12 +137,13 @@ export function PrimaryArmorSceneComponent({
if (!noInvalidate) invalidate(); if (!noInvalidate) invalidate();
} }
const initialState = tankopediaPersistentStore.getState();
handleShellChange(); handleShellChange();
handleGreenPenetrationChange( handleGreenPenetrationChange(initialState.greenPenetration);
tankopediaPersistentStore.getState().greenPenetration, handleAdvancedHighlightingChange(initialState.advancedHighlighting);
); handleOpaqueChange(initialState.opaque);
handleOpaqueChange(tankopediaPersistentStore.getState().opaque); handleWireframeChange(initialState.wireframe);
handleWireframeChange(tankopediaPersistentStore.getState().wireframe);
handleProtagonistEquipmentChange( handleProtagonistEquipmentChange(
duelStore.getState().protagonist.equipmentMatrix, duelStore.getState().protagonist.equipmentMatrix,
); );
@@ -156,6 +161,10 @@ export function PrimaryArmorSceneComponent({
(state) => state.greenPenetration, (state) => state.greenPenetration,
handleGreenPenetrationChange, handleGreenPenetrationChange,
), ),
tankopediaPersistentStore.subscribe(
(state) => state.advancedHighlighting,
handleAdvancedHighlightingChange,
),
tankopediaPersistentStore.subscribe( tankopediaPersistentStore.subscribe(
(state) => state.opaque, (state) => state.opaque,
handleOpaqueChange, handleOpaqueChange,
@@ -12,6 +12,7 @@ uniform bool canSplash;
uniform float damage; uniform float damage;
uniform float explosionRadius; uniform float explosionRadius;
uniform bool greenPenetration; uniform bool greenPenetration;
uniform bool advancedHighlighting;
uniform bool opaque; uniform bool opaque;
uniform mat4 inverseProjectionMatrix; uniform mat4 inverseProjectionMatrix;
@@ -27,16 +28,18 @@ float depthToDistance(float depth) {
void main() { void main() {
float penetrationChance = -1.0; float penetrationChance = -1.0;
float splashChance = -1.0; float splashChance = -1.0;
bool didRicochet = false;
vec2 screenCoordinates = gl_FragCoord.xy / resolution; vec2 screenCoordinates = gl_FragCoord.xy / resolution;
float angle = acos(dot(vNormal, -vViewPosition) / length(vViewPosition)); float angle = acos(dot(vNormal, -vViewPosition) / length(vViewPosition));
vec4 spacedArmorBufferFragment = texture2D(spacedArmorBuffer, screenCoordinates); vec4 spacedArmorBufferFragment = texture2D(spacedArmorBuffer, screenCoordinates);
bool isUnderSpacedArmor = spacedArmorBufferFragment.a != 0.0; bool isUnderSpacedArmor = spacedArmorBufferFragment.a != 0.0;
bool threeCalibersRule = caliber > thickness * 3.0 || isUnderSpacedArmor; bool threeCalibersRule = caliber > thickness * 3.0 || isUnderSpacedArmor;
if (!threeCalibersRule && angle >= ricochet) { if (!threeCalibersRule && angle >= ricochet) {
penetrationChance = 0.0; penetrationChance = 0.0;
didRicochet = true;
} else { } else {
bool twoCalibersRule = caliber > thickness * 2.0 && thickness > 0.0; bool twoCalibersRule = caliber > thickness * 2.0 && thickness > 0.0;
float finalNormalization = twoCalibersRule ? ((1.4 * normalization * caliber) / (2.0 * thickness)) : normalization; float finalNormalization = twoCalibersRule ? ((1.4 * normalization * caliber) / (2.0 * thickness)) : normalization;
@@ -82,11 +85,22 @@ void main() {
float alpha = opaque ? 1.0 : 0.5; float alpha = opaque ? 1.0 : 0.5;
vec3 color = vec3(1.0, splashChance * 0.392, 0.0); vec3 color = vec3(1.0, splashChance * 0.392, 0.0);
if (greenPenetration) {
if (advancedHighlighting && didRicochet) {
color = vec3(1.0, color.g, 1.0);
}
if (greenPenetration || advancedHighlighting) {
// non-trigonometric red-green mixing: https://www.desmos.com/calculator/ceo1oq7l67 // non-trigonometric red-green mixing: https://www.desmos.com/calculator/ceo1oq7l67
float falling = -pow(penetrationChance, 2.0) + 1.0; float falling = -pow(penetrationChance, 2.0) + 1.0;
float gaining = -pow(penetrationChance - 1.0, 2.0) + 1.0; float gaining = -pow(penetrationChance - 1.0, 2.0) + 1.0;
gl_FragColor = vec4(falling * color + gaining * vec3(0.0, 1.0, 0.0), alpha); vec3 penetrationColor = vec3(0.0, 1.0, 0.0);
if (advancedHighlighting && threeCalibersRule) {
penetrationColor = vec3(0.0, 1.0, 1.0);
}
gl_FragColor = vec4(falling * color + gaining * penetrationColor, alpha);
} else { } else {
gl_FragColor = vec4(color, (1.0 - penetrationChance) * alpha); gl_FragColor = vec4(color, (1.0 - penetrationChance) * alpha);
} }
@@ -17,6 +17,7 @@ import {
import { import {
Box, Box,
Button, Button,
Checkbox,
Dialog, Dialog,
DropdownMenu, DropdownMenu,
Flex, Flex,
@@ -68,6 +69,9 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
const greenPenetration = TankopediaPersistent.use( const greenPenetration = TankopediaPersistent.use(
(state) => state.greenPenetration, (state) => state.greenPenetration,
); );
const advancedHighlighting = TankopediaPersistent.use(
(state) => state.advancedHighlighting,
);
const wireframe = TankopediaPersistent.use((state) => state.wireframe); const wireframe = TankopediaPersistent.use((state) => state.wireframe);
const opaque = TankopediaPersistent.use((state) => state.opaque); const opaque = TankopediaPersistent.use((state) => state.opaque);
const fullScreenAvailable = useFullscreenAvailability(); const fullScreenAvailable = useFullscreenAvailability();
@@ -325,6 +329,24 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
style={{ transform: 'translateX(-50%)' }} style={{ transform: 'translateX(-50%)' }}
> >
{display === TankopediaDisplay.DynamicArmor && ( {display === TankopediaDisplay.DynamicArmor && (
<>
<Flex
align="center"
gap="2"
mb="1"
style={{ cursor: 'pointer' }}
onClick={() => {
mutateTankopediaPersistent((draft) => {
draft.advancedHighlighting = !draft.advancedHighlighting;
});
}}
>
<Checkbox checked={advancedHighlighting} />
<Text color="gray" size="2">
Advanced highlighting
</Text>
</Flex>
<Flex align="center" gap="2"> <Flex align="center" gap="2">
<Text color="gray" size="2"> <Text color="gray" size="2">
Shooter: Shooter:
@@ -370,8 +392,8 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
</Dialog.Content> </Dialog.Content>
</Dialog.Root> </Dialog.Root>
</Flex> </Flex>
</>
)} )}
<Flex gap="3" align="center" mt="2"> <Flex gap="3" align="center" mt="2">
<SegmentedControl.Root <SegmentedControl.Root
value={`${display}`} value={`${display}`}
@@ -352,7 +352,6 @@ export function Controls({ naked }: ControlsProps) {
ref={orbitControls} ref={orbitControls}
enabled={tankopediaEphemeralStore.getState().controlsEnabled} enabled={tankopediaEphemeralStore.getState().controlsEnabled}
enableDamping={false} enableDamping={false}
maxPolarAngle={degToRad(100)}
autoRotate={autoRotate} autoRotate={autoRotate}
autoRotateSpeed={1 / 4} autoRotateSpeed={1 / 4}
/> />
@@ -12,6 +12,7 @@ export type TankopediaTestTankDisplay = 'include' | 'exclude' | 'only';
export interface TankopediaPersistentStore { export interface TankopediaPersistentStore {
wireframe: boolean; wireframe: boolean;
opaque: boolean; opaque: boolean;
advancedHighlighting: boolean;
environment: (typeof ENVIRONMENTS)[number]; environment: (typeof ENVIRONMENTS)[number];
showGrid: boolean; showGrid: boolean;
greenPenetration: boolean; greenPenetration: boolean;
@@ -33,6 +34,7 @@ export const TankopediaPersistent = createContextualStore(() =>
subscribeWithSelector<TankopediaPersistentStore>(() => ({ subscribeWithSelector<TankopediaPersistentStore>(() => ({
wireframe: false, wireframe: false,
opaque: false, opaque: false,
advancedHighlighting: false,
environment: ENVIRONMENTS[0], environment: ENVIRONMENTS[0],
showGrid: true, showGrid: true,
greenPenetration: false, greenPenetration: false,