Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Ghost Mode #32

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions crates/alkahest-renderer/assets/shaders/gui/outline.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ cbuffer cb_outline : register(b0) {
float time_since_selection;
};

#define OUTLINE_COLOR float3(1.0, 0.6, 0.2) * 0.6
#define OUTLINE_COLOR float3(1.0, 0.6, 0.2)
#define OUTLINE_COLOR_BACK (OUTLINE_COLOR * 0.6)
#define GHOST_COLOR float3(0.2, 0.6, 1.0)
#define OUTLINE_WIDTH 2

Texture2D DepthTargetOutline : register(t0);
Texture2D DepthTargetScene : register(t1);
Texture2D DepthTargetGhost : register(t2);

SamplerState SampleType : register(s1);

Expand All @@ -24,9 +26,10 @@ float2 QueryTexelSize(Texture2D t) {
// Pixel Shader
float4 PSMain(VSOutput input) : SV_Target {
float depth = DepthTargetOutline.Sample(SampleType, input.uv).r;
float g_depth = DepthTargetGhost.Sample(SampleType, input.uv).r;

// if the pixel isn't 0 (we are on the depth silhouette)
if (depth != 0)
if (depth != 0 || g_depth != 0)
{
float timeNormMul = clamp(time_since_selection * 4.0, 0.0, 1.0);
float2 size = QueryTexelSize(DepthTargetScene);
Expand All @@ -40,30 +43,47 @@ float4 PSMain(VSOutput input) : SV_Target {
continue;
}

float2 offset = float2(i, j) * size * (3 - timeNormMul * 2);
float2 g_offset = float2(i, j) * size;
float2 offset = g_offset * (3 - timeNormMul * 2);

// and if one of the pixel-neighbor is black (we are on the border)
if (DepthTargetOutline.Sample(SampleType, input.uv + offset).r == 0)
if (depth !=0 && DepthTargetOutline.Sample(SampleType, input.uv + offset).r == 0)
{
float depthScene = DepthTargetScene.Sample(SampleType, input.uv).r;
if(depthScene > depth) // Behind scene
return float4(OUTLINE_COLOR, 0.65);
else // In front of scene
return float4(OUTLINE_COLOR, 1);
} else if (g_depth != 0 && DepthTargetGhost.Sample(SampleType, input.uv + g_offset).r == 0)
{
float depthScene = DepthTargetScene.Sample(SampleType, input.uv).r;
if(depthScene > depth) // Behind scene
return float4(GHOST_COLOR, 0.65);
else // In front of scene
return float4(GHOST_COLOR, 1);
}
}
}

// 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 (depth != 0) {
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 (g_depth != 0) {
if(depthScene <= g_depth) {
dither_discard(input.screen_pos, 0.50);
return float4(GHOST_COLOR, 0.75);
}
}

// else // In front of scene
// return float4(OUTLINE_COLOR, 0.015 + fillFlash);
// return float4(outline_color, 0.015 + fillFlash);
}

discard;
Expand Down
8 changes: 8 additions & 0 deletions crates/alkahest-renderer/src/ecs/common.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::fmt::Display;

use destiny_pkg::TagHash;
use ecolor::Color32;
use hecs::Entity;

/// Tiger entity world ID
#[derive(Copy, Clone)]
Expand Down Expand Up @@ -97,6 +99,12 @@ impl AsRef<str> for Label {

pub struct Hidden;
pub struct Global;
#[derive(Clone)]
pub struct Ghost {
pub hash: TagHash,
pub entity: Entity,
pub map_name: Option<String>
}

/// 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.)
Expand Down
4 changes: 2 additions & 2 deletions crates/alkahest-renderer/src/ecs/tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ pub enum EntityTag {
Activity,
Ambient,
Global,
Ghost,
Havok,
Utility,
User,
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -145,8 +147,6 @@ pub fn insert_tag(scene: &mut Scene, ent: Entity, tag: EntityTag) {
e.insert(tag);
return;
}

scene.insert_one(ent, Tags::from_iter([tag])).ok();
}

pub fn remove_tag(scene: &mut Scene, ent: Entity, tag: EntityTag) {
Expand Down
45 changes: 32 additions & 13 deletions crates/alkahest-renderer/src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ use alkahest_data::{
};
use anyhow::Context;
use bitflags::bitflags;
use destiny_pkg::TagHash;
use glam::Vec3;
use hecs::Entity;
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use strum::{EnumCount, EnumIter};
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,
Expand Down Expand Up @@ -131,7 +135,13 @@ impl Renderer {
data.asset_manager.techniques.get_shared(handle)
}

pub fn render_world(&self, view: &impl View, scene: &Scene, resources: &Resources) {
pub fn render_world(
&self,
view: &impl View,
scene: &Scene,
all_scenes: FxHashMap<TagHash, &Scene>,
resources: &Resources,
) {
self.pocus().lastfilters = resources.get::<NodeFilterSet>().clone();

self.begin_world_frame(scene);
Expand All @@ -157,19 +167,28 @@ impl Renderer {
self.draw_pickbuffer(scene, resources.get::<SelectedEntity>().selected());
}

if let Some(selected) = resources.get::<SelectedEntity>().selected() {
if !scene.entity(selected).map_or(true, |v| v.has::<Hidden>()) {
self.draw_outline(
scene,
selected,
resources
.get::<SelectedEntity>()
.time_selected
.elapsed()
.as_secs_f32(),
);
let mut ghost_query = scene.query::<&Ghost>();
let ghosts: Vec<&Ghost> = ghost_query.iter().map(|(_, g)| g).collect();
let mut selected = resources.get::<SelectedEntity>().selected();
if let Some(sel) = selected {
if scene.entity(sel).map_or(true, |v| v.has::<Hidden>()) {
selected = None;
}
}

if ghosts.len() > 0 || selected.is_some() {
self.draw_outline(
scene,
selected,
all_scenes,
ghosts,
resources
.get::<SelectedEntity>()
.time_selected
.elapsed()
.as_secs_f32(),
);
}
}

unsafe {
Expand Down
98 changes: 81 additions & 17 deletions crates/alkahest-renderer/src/renderer/pickbuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use alkahest_data::{
};
use anyhow::Context;
use crossbeam::atomic::AtomicCell;
use destiny_pkg::TagHash;
use hecs::Entity;
use rustc_hash::FxHashMap;
use windows::Win32::{
Foundation::RECT,
Graphics::Direct3D11::{
Expand All @@ -18,8 +20,9 @@ use windows::Win32::{

use crate::{
ecs::{
common::Ghost,
render::{draw_entity, static_geometry::draw_static_instances_individual_system},
Scene,
Scene, SceneInfo,
},
gpu::{buffer::ConstantBuffer, util::DxDeviceExt, GpuContext, SharedGpuContext},
gpu_event, include_dxbc,
Expand Down Expand Up @@ -50,10 +53,18 @@ 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: Option<Entity>,
all_scenes: FxHashMap<TagHash, &Scene>,
ghosts: Vec<&Ghost>,
time_since_select: f32,
) {
gpu_event!(self.gpu, "selection_outline");

self.pickbuffer.outline_depth.clear(0.0, 0);
self.pickbuffer.ghost_depth.clear(0.0, 0);

unsafe {
const NO_RT: Option<ID3D11RenderTargetView> = None;
Expand All @@ -63,19 +74,68 @@ impl Renderer {
.OMGetRenderTargets(Some(&mut rt_backup), None);

// Draw the selected entity into the outline depth buffer
self.gpu
.context()
.OMSetRenderTargets(None, Some(&self.pickbuffer.outline_depth.view));
self.gpu
.context()
.OMSetDepthStencilState(Some(&self.pickbuffer.outline_depth.state), 0);
draw_entity(
scene,
selected,
self,
Some(&self.pickbuffer.static_instance_cb),
TfxRenderStage::GenerateGbuffer,
);
if let Some(sel) = selected {
self.gpu
.context()
.OMSetRenderTargets(None, Some(&self.pickbuffer.outline_depth.view));
self.gpu
.context()
.OMSetDepthStencilState(Some(&self.pickbuffer.outline_depth.state), 0);

let ghost = scene.entity(sel).unwrap().get::<&Ghost>();
if ghost
.as_ref()
.is_some_and(|g| scene.get_map_hash().is_some_and(|t| t != g.hash))
{
if let Some(g) = ghost {
match all_scenes.get(&g.hash) {
Some(&ghost_scene) => {
draw_entity(
ghost_scene,
g.entity,
self,
Some(&self.pickbuffer.static_instance_cb),
TfxRenderStage::GenerateGbuffer,
);
}
None => warn!("Ghost Entity had no home!"),
}
}
} else {
draw_entity(
scene,
sel,
self,
Some(&self.pickbuffer.static_instance_cb),
TfxRenderStage::GenerateGbuffer,
);
}
}

// Draw the ghost entities into the ghost depth buffer
if ghosts.len() > 0 {
self.gpu
.context()
.OMSetRenderTargets(None, Some(&self.pickbuffer.ghost_depth.view));
self.gpu
.context()
.OMSetDepthStencilState(Some(&self.pickbuffer.ghost_depth.state), 0);

for ghost in ghosts {
match all_scenes.get(&ghost.hash) {
Some(&ghost_scene) => {
draw_entity(
ghost_scene,
ghost.entity,
self,
Some(&self.pickbuffer.static_instance_cb),
TfxRenderStage::GenerateGbuffer,
);
}
None => warn!("Ghost Entity had no home!"),
}
}
}

// Draw the outline itself
self.gpu
Expand All @@ -90,15 +150,16 @@ 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(&[
Some(self.pickbuffer.outline_depth.texture_view.clone()),
Some(self.data.lock().gbuffers.depth.texture_view.clone()),
Some(self.pickbuffer.ghost_depth.texture_view.clone()),
]),
);
self.pickbuffer.outline_cb.write(&time_since_select).ok();
Expand All @@ -115,6 +176,7 @@ pub struct Pickbuffer {

pub(super) selection_request: AtomicCell<Option<(u32, u32)>>,
pub outline_depth: DepthState,
pub ghost_depth: DepthState,
pub pick_buffer: RenderTarget,
pub pick_buffer_staging: CpuStagingBuffer,
pub static_instance_cb: ConstantBuffer<u8>,
Expand Down Expand Up @@ -157,6 +219,7 @@ impl Pickbuffer {
selection_request: AtomicCell::new(None),
outline_depth: DepthState::create(gctx.clone(), window_size)
.context("Outline Depth")?,
ghost_depth: DepthState::create(gctx.clone(), window_size).context("Ghost Depth")?,
pick_buffer: RenderTarget::create(
window_size,
DxgiFormat::R32_UINT,
Expand Down Expand Up @@ -193,6 +256,7 @@ impl Pickbuffer {
self.outline_depth
.resize(new_size)
.context("Outline Depth")?;
self.ghost_depth.resize(new_size).context("Ghost Depth")?;
self.pick_buffer
.resize(new_size)
.context("Entity_Pickbuffer")?;
Expand Down
3 changes: 2 additions & 1 deletion crates/alkahest/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,9 @@ impl AlkahestApp {
maps.update_maps(resources);

let map = maps.current_map().map(|m| &m.scene).unwrap_or(scratch_map);
let all_maps = maps.scene_hashmap();

renderer.render_world(&*resources.get::<Camera>(), map, resources);
renderer.render_world(&*resources.get::<Camera>(), map, all_maps, resources);
}

unsafe {
Expand Down
Loading