From d9fb7fb61cc01b79f226f1af328187a2f73ec46e Mon Sep 17 00:00:00 2001 From: Froggy618157725 Date: Tue, 9 Jul 2024 23:53:13 -0700 Subject: [PATCH] WIP Ghost Mode --- .../assets/shaders/gui/outline.hlsl | 34 +++++++++++----- crates/alkahest-renderer/src/ecs/common.rs | 1 + crates/alkahest-renderer/src/ecs/tags.rs | 2 + crates/alkahest-renderer/src/renderer/mod.rs | 20 +++++++++- .../src/renderer/pickbuffer.rs | 40 +++++++++++++------ crates/alkahest/src/gui/inspector/mod.rs | 29 ++++++++++++-- 6 files changed, 99 insertions(+), 27 deletions(-) diff --git a/crates/alkahest-renderer/assets/shaders/gui/outline.hlsl b/crates/alkahest-renderer/assets/shaders/gui/outline.hlsl index 32c0652e..1ebc2319 100644 --- a/crates/alkahest-renderer/assets/shaders/gui/outline.hlsl +++ b/crates/alkahest-renderer/assets/shaders/gui/outline.hlsl @@ -4,10 +4,11 @@ cbuffer cb_outline : register(b0) { float time_since_selection; + bool ghost; }; -#define OUTLINE_COLOR float3(1.0, 0.6, 0.2) * 0.6 -#define OUTLINE_COLOR_BACK (OUTLINE_COLOR * 0.6) +#define OUTLINE_COLOR float3(1.0, 0.6, 0.2) +#define GHOST_COLOR float3(0.2, 0.6, 1.0) #define OUTLINE_WIDTH 2 Texture2D DepthTargetOutline : register(t0); @@ -24,6 +25,13 @@ float2 QueryTexelSize(Texture2D t) { // Pixel Shader float4 PSMain(VSOutput input) : SV_Target { float depth = DepthTargetOutline.Sample(SampleType, input.uv).r; + float3 outline_color; + if (ghost) { + outline_color = GHOST_COLOR; + } else { + outline_color = OUTLINE_COLOR; + } + float3 outline_color_back = outline_color * 0.6; // if the pixel isn't 0 (we are on the depth silhouette) if (depth != 0) @@ -47,9 +55,9 @@ float4 PSMain(VSOutput input) : SV_Target { { float depthScene = DepthTargetScene.Sample(SampleType, input.uv).r; if(depthScene > depth) // Behind scene - return float4(OUTLINE_COLOR, 0.65); + return float4(outline_color, 0.65); else // In front of scene - return float4(OUTLINE_COLOR, 1); + return float4(outline_color, 1); } } } @@ -57,13 +65,21 @@ float4 PSMain(VSOutput input) : SV_Target { // if we are on the silhouette but not on the border float depthScene = DepthTargetScene.Sample(SampleType, input.uv).r; float fillFlash = (1.0 - timeNormMul) * 0.20; - if(depthScene > depth) { // Behind scene - dither_discard(input.screen_pos, 0.15); -// return float4(OUTLINE_COLOR, 0.16 + fillFlash); - return float4(lerp(OUTLINE_COLOR, OUTLINE_COLOR_BACK, timeNormMul), 0.75 + fillFlash); + if (!ghost) { + if(depthScene > depth) { // Behind scene + dither_discard(input.screen_pos, 0.15); +// return float4(outline_color, 0.16 + fillFlash); + return float4(lerp(outline_color, outline_color_back, timeNormMul), 0.75 + fillFlash); + } + } else { + if(depthScene <= depth) { + dither_discard(input.screen_pos, 0.50); + return float4(outline_color, 0.75 + fillFlash); + } } + // else // In front of scene -// return float4(OUTLINE_COLOR, 0.015 + fillFlash); +// return float4(outline_color, 0.015 + fillFlash); } discard; diff --git a/crates/alkahest-renderer/src/ecs/common.rs b/crates/alkahest-renderer/src/ecs/common.rs index a60d59a8..d333521e 100644 --- a/crates/alkahest-renderer/src/ecs/common.rs +++ b/crates/alkahest-renderer/src/ecs/common.rs @@ -97,6 +97,7 @@ impl AsRef for Label { pub struct Hidden; pub struct Global; +pub struct Ghost; /// Marker component to indicate that the entity is allowed to be modified in /// potentially destructive ways (e.g. deleting it, changing it's name, etc.) diff --git a/crates/alkahest-renderer/src/ecs/tags.rs b/crates/alkahest-renderer/src/ecs/tags.rs index 18306012..a54f2a8f 100644 --- a/crates/alkahest-renderer/src/ecs/tags.rs +++ b/crates/alkahest-renderer/src/ecs/tags.rs @@ -80,6 +80,7 @@ pub enum EntityTag { Activity, Ambient, Global, + Ghost, Havok, Utility, User, @@ -116,6 +117,7 @@ impl Display for EntityTag { EntityTag::Activity => write!(f, "Activity"), EntityTag::Ambient => write!(f, "Ambient"), EntityTag::Global => write!(f, "Global"), + EntityTag::Ghost => write!(f, "Ghost"), EntityTag::Havok => write!(f, "Havok"), EntityTag::Utility => write!(f, "Utility"), EntityTag::User => write!(f, "User"), diff --git a/crates/alkahest-renderer/src/renderer/mod.rs b/crates/alkahest-renderer/src/renderer/mod.rs index d0264568..b4ca625d 100644 --- a/crates/alkahest-renderer/src/renderer/mod.rs +++ b/crates/alkahest-renderer/src/renderer/mod.rs @@ -18,6 +18,8 @@ use alkahest_data::{ }; use anyhow::Context; use bitflags::bitflags; +use glam::Vec3; +use hecs::Entity; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use strum::{EnumCount, EnumIter}; @@ -25,7 +27,7 @@ use windows::Win32::Graphics::Direct3D11::D3D11_VIEWPORT; use crate::{ ecs::{ - common::Hidden, + common::{Ghost, Hidden}, render::{ dynamic_geometry::update_dynamic_model_system, static_geometry::update_static_instances_system, @@ -161,15 +163,29 @@ impl Renderer { if !scene.entity(selected).map_or(true, |v| v.has::()) { self.draw_outline( scene, - selected, + Vec::from([selected]), resources .get::() .time_selected .elapsed() .as_secs_f32(), + false ); } } + let ghosts : Vec = scene.query::<&Ghost>().iter().map(|(e, _ )| e).collect(); + if ghosts.len() > 0 { + self.draw_outline( + scene, + ghosts, + resources + .get::() + .time_selected + .elapsed() + .as_secs_f32(), + true + ); + } } unsafe { diff --git a/crates/alkahest-renderer/src/renderer/pickbuffer.rs b/crates/alkahest-renderer/src/renderer/pickbuffer.rs index fddebd03..2436abc8 100644 --- a/crates/alkahest-renderer/src/renderer/pickbuffer.rs +++ b/crates/alkahest-renderer/src/renderer/pickbuffer.rs @@ -30,6 +30,11 @@ use crate::{ util::Hocus, }; +pub struct OutlineParams { + time_since_select: f32, + ghost: bool, +} + impl Renderer { pub(super) fn draw_pickbuffer(&self, scene: &Scene, selected: Option) { gpu_event!(self.gpu, "pickbuffer"); @@ -50,7 +55,7 @@ impl Renderer { } // TODO(cohae): move rendering logic to Pickbuffer (where possible) - pub(super) fn draw_outline(&self, scene: &Scene, selected: Entity, time_since_select: f32) { + pub(super) fn draw_outline(&self, scene: &Scene, selected: Vec, time_since_select: f32, ghost: bool) { gpu_event!(self.gpu, "selection_outline"); self.pickbuffer.outline_depth.clear(0.0, 0); @@ -69,13 +74,15 @@ impl Renderer { self.gpu .context() .OMSetDepthStencilState(Some(&self.pickbuffer.outline_depth.state), 0); - draw_entity( - scene, - selected, - self, - Some(&self.pickbuffer.static_instance_cb), - TfxRenderStage::GenerateGbuffer, - ); + for e in selected { + draw_entity( + scene, + e, + self, + Some(&self.pickbuffer.static_instance_cb), + TfxRenderStage::GenerateGbuffer, + ); + } // Draw the outline itself self.gpu @@ -90,10 +97,10 @@ impl Renderer { self.gpu.set_input_topology(EPrimitiveType::Triangles); self.gpu .context() - .VSSetShader(&self.pickbuffer.outline_vs, None); + .VSSetShader(&self.pickbuffer.outline_vs.clone(), None); self.gpu .context() - .PSSetShader(&self.pickbuffer.outline_ps, None); + .PSSetShader(&self.pickbuffer.outline_ps.clone(), None); self.gpu.context().PSSetShaderResources( 0, Some(&[ @@ -101,8 +108,13 @@ impl Renderer { Some(self.data.lock().gbuffers.depth.texture_view.clone()), ]), ); - self.pickbuffer.outline_cb.write(&time_since_select).ok(); - self.pickbuffer.outline_cb.bind(0, TfxShaderStage::Pixel); + if ghost { + self.pickbuffer.ghost_cb.write(&OutlineParams {time_since_select, ghost}).ok(); + self.pickbuffer.ghost_cb.bind(0, TfxShaderStage::Pixel); + } else { + self.pickbuffer.outline_cb.write(&OutlineParams {time_since_select, ghost}).ok(); + self.pickbuffer.outline_cb.bind(0, TfxShaderStage::Pixel); + } self.gpu.context().Draw(3, 0); } @@ -121,7 +133,8 @@ pub struct Pickbuffer { pub(super) outline_vs: ID3D11VertexShader, pub(super) outline_ps: ID3D11PixelShader, - pub(super) outline_cb: ConstantBuffer, + pub(super) outline_cb: ConstantBuffer, + pub(super) ghost_cb: ConstantBuffer, clear_vs: ID3D11VertexShader, clear_ps: ID3D11PixelShader, @@ -176,6 +189,7 @@ impl Pickbuffer { outline_vs, outline_ps, outline_cb: ConstantBuffer::create(gctx.clone(), None)?, + ghost_cb: ConstantBuffer::create(gctx.clone(), None)?, clear_vs, clear_ps, pick_ps, diff --git a/crates/alkahest/src/gui/inspector/mod.rs b/crates/alkahest/src/gui/inspector/mod.rs index d06b2206..ed8c0efd 100644 --- a/crates/alkahest/src/gui/inspector/mod.rs +++ b/crates/alkahest/src/gui/inspector/mod.rs @@ -6,7 +6,7 @@ use alkahest_data::map::{SLightCollection, SRespawnPoint}; use alkahest_renderer::{ camera::Camera, ecs::{ - common::{EntityWorldId, Global, Hidden, Label, Mutable}, + common::{EntityWorldId, Ghost, Global, Hidden, Label, Mutable}, map::{CubemapVolume, NodeMetadata}, render::{ decorators::DecoratorRenderer, dynamic_geometry::DynamicModelComponent, @@ -183,13 +183,30 @@ pub fn show_inspector_panel( let mut global = e.has::(); let mut global_changed = false; - if e.has::() && !e.has::() { - if ui.checkbox(&mut global, "Show in all Maps").changed() { + let mut mutable = e.has::(); + if !e.has::() { + if ui + .checkbox( + &mut global, + if mutable { + "Show in all Maps" + } else { + "Show ghost in all Maps" + }, + ) + .changed() + { global_changed = true; if global { cmd.insert_one(ent, Global); + if (!mutable) { + cmd.insert_one(ent, Ghost); + } } else { cmd.remove_one::(ent); + if (!mutable) { + cmd.remove_one::(ent); + } } }; ui.separator(); @@ -199,8 +216,14 @@ pub fn show_inspector_panel( if global_changed { if global { insert_tag(scene, ent, EntityTag::Global); + if (!mutable) { + insert_tag(scene, ent, EntityTag::Ghost); + } } else { remove_tag(scene, ent, EntityTag::Global); + if (!mutable) { + insert_tag(scene, ent, EntityTag::Ghost); + } } } }