Skip to content

Commit

Permalink
Implement collision preview (Astrabit-ST#55)
Browse files Browse the repository at this point in the history
* Add collision shader

This adds a new shader for collision that renders over the fog. It
doesn't do anything yet because the collision is not being calculated
anywhere yet.

(cherry picked from commit eeecc6c)

* Fix loop dimensions in collision shader `calculate_vertices`

(cherry picked from commit 93bc2c9)

* Render fog and collision on top of event graphics

I also made the event border rectangles render on top of the fog and
collision because otherwise they may be hard to see through those
layers.

(cherry picked from commit ade0fa2)

* Start implementing collision calculation

(cherry picked from commit 7938c22)

* Replace image cache with dummy bind group in collision shader

(cherry picked from commit cbbf696)

* Update collision preview when map changes

(cherry picked from commit 2ad9e87)

* Fix fog and collision not rendering when events are disabled

(cherry picked from commit b47e70b)

* Add collision preview to tilepicker as well

(cherry picked from commit c5dd6d0)

* Collision preview now ignores disabled map layers

(cherry picked from commit 28e17bf)

* Fix labels in layer picker not aligning with checkboxes

(cherry picked from commit e777057)

* Bind viewport to group 0 instead of using dummy layout

(cherry picked from commit 74874f2)

* Fix crash when map has tile IDs larger than tileset size

* Fix crash when tileset `passages` is smaller than the tileset

* Use `copy_from_slice` to calculate tilepicker passages

* Remove extra vertices from collision shader

* Collision shader `calculate_vertices` now returns an array instead of a vector

* Fix collision calculation edge cases

* Simplify `calculate_passage`
  • Loading branch information
white-axe authored Nov 24, 2023
1 parent c57dffd commit 6fd75f6
Show file tree
Hide file tree
Showing 14 changed files with 834 additions and 52 deletions.
49 changes: 38 additions & 11 deletions crates/components/src/map_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ pub struct MapView {

pub scale: f32,
pub previous_scale: f32,

/// Used to store the bounding boxes of event graphics in order to render them on top of the
/// fog and collision layers
pub event_rects: Vec<egui::Rect>,
}

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Default)]
Expand All @@ -70,6 +74,16 @@ impl MapView {
let tilesets = update_state.data.tilesets();
let tileset = &tilesets[map.tileset_id];

let mut passages = luminol_data::Table2::new(map.data.xsize(), map.data.ysize());
luminol_graphics::collision::calculate_passages(
&tileset.passages,
&tileset.priorities,
&map.data,
Some(&map.events),
(0..map.data.zsize()).rev(),
|x, y, passage| passages[(x, y)] = passage,
);

let atlas = update_state.graphics.atlas_cache.load_atlas(
&update_state.graphics,
update_state.filesystem,
Expand Down Expand Up @@ -112,6 +126,7 @@ impl MapView {
update_state.filesystem,
&map,
tileset,
&passages,
update_state.graphics.push_constants_supported(),
)?;

Expand Down Expand Up @@ -139,6 +154,8 @@ impl MapView {

scale: 100.,
previous_scale: 100.,

event_rects: Vec::new(),
})
}

Expand Down Expand Up @@ -335,7 +352,7 @@ impl MapView {

if self.event_enabled {
let mut selected_event = None;
let mut selected_event_rects = None;
let mut selected_event_rect = None;

for (_, event) in map.events.iter() {
let sprites = self.events.get(event.id);
Expand Down Expand Up @@ -389,11 +406,7 @@ impl MapView {
if matches!(self.selected_layer, SelectedLayer::Events)
&& ui.input(|i| !i.modifiers.shift)
{
ui.painter().rect_stroke(
box_rect,
5.,
egui::Stroke::new(1., egui::Color32::WHITE),
);
self.event_rects.push(box_rect);

// If the mouse is not hovering over an event, then we will handle the selected
// tile based on where the map cursor is
Expand Down Expand Up @@ -421,7 +434,7 @@ impl MapView {
};
if let Some(e) = selected_event {
if e.id == event.id {
selected_event_rects = Some(box_rect);
selected_event_rect = Some(box_rect);
}
}
}
Expand Down Expand Up @@ -503,7 +516,7 @@ impl MapView {
if let Some(e) = selected_event {
if e.id == event.id {
self.selected_event_is_hovered = true;
selected_event_rects = Some(box_rect);
selected_event_rect = Some(box_rect);
}
}
}
Expand All @@ -514,7 +527,7 @@ impl MapView {
if let Some(id) = self.selected_event_id {
if dragging_event && id == event.id {
selected_event = Some(event);
selected_event_rects = Some(box_rect);
selected_event_rect = Some(box_rect);
}
}
} else {
Expand All @@ -537,20 +550,34 @@ impl MapView {

self.selected_event_id = selected_event.map(|e| e.id);

// Draw the fog and collision layers
self.map
.paint_overlay(graphics_state.clone(), ui.painter(), canvas_rect);

// Draw white rectangles on the border of all events
while let Some(rect) = self.event_rects.pop() {
ui.painter()
.rect_stroke(rect, 5., egui::Stroke::new(1., egui::Color32::WHITE));
}

// Draw a yellow rectangle on the border of the selected event's graphic
if let Some(selected_event) = selected_event {
// Make sure the event editor isn't open so we don't draw over the
// magenta rectangle
if !selected_event.extra_data.is_editor_open {
if let Some(box_rect) = selected_event_rects {
if let Some(rect) = selected_event_rect {
ui.painter().rect_stroke(
box_rect,
rect,
5.,
egui::Stroke::new(3., egui::Color32::YELLOW),
);
}
}
}
} else {
// Draw the fog and collision layers
self.map
.paint_overlay(graphics_state.clone(), ui.painter(), canvas_rect);
}

// Do we display the visible region?
Expand Down
48 changes: 44 additions & 4 deletions crates/components/src/tilepicker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@ pub struct Tilepicker {
#[derive(Debug)]
struct Resources {
tiles: luminol_graphics::tiles::Tiles,
collision: luminol_graphics::collision::Collision,
viewport: luminol_graphics::viewport::Viewport,
}

struct Callback {
resources: Arc<Resources>,
graphics_state: Arc<luminol_graphics::GraphicsState>,

coll_enabled: bool,
}

// FIXME
Expand All @@ -51,18 +54,27 @@ unsafe impl Sync for Callback {}
impl egui_wgpu::CallbackTrait for Callback {
fn paint<'a>(
&'a self,
info: egui::PaintCallbackInfo,
_info: egui::PaintCallbackInfo,
render_pass: &mut wgpu::RenderPass<'a>,
callback_resources: &'a egui_wgpu::CallbackResources,
_callback_resources: &'a egui_wgpu::CallbackResources,
) {
self.resources.viewport.bind(render_pass);
self.resources.viewport.bind(1, render_pass);
self.resources.tiles.draw(
&self.graphics_state,
&self.resources.viewport,
&[true],
None,
render_pass,
);

if self.coll_enabled {
self.resources.viewport.bind(0, render_pass);
self.resources.collision.draw(
&self.graphics_state,
&self.resources.viewport,
render_pass,
);
}
}
}

Expand Down Expand Up @@ -145,8 +157,34 @@ impl Tilepicker {
update_state.graphics.push_constants_supported(),
);

let mut passages =
luminol_data::Table2::new(tilepicker_data.xsize(), tilepicker_data.ysize());
for x in 0..8 {
passages[(x, 0)] = {
let tile_id = tilepicker_data[(x, 0, 0)].try_into().unwrap_or_default();
if tile_id >= tileset.passages.len() {
0
} else {
tileset.passages[tile_id]
}
};
}
let length =
(passages.len().saturating_sub(8)).min(tileset.passages.len().saturating_sub(384));
passages.as_mut_slice()[8..8 + length]
.copy_from_slice(&tileset.passages.as_slice()[384..384 + length]);
let collision = luminol_graphics::collision::Collision::new(
&update_state.graphics,
&passages,
update_state.graphics.push_constants_supported(),
);

Ok(Self {
resources: Arc::new(Resources { tiles, viewport }),
resources: Arc::new(Resources {
tiles,
collision,
viewport,
}),
ani_time: None,
selected_tiles_left: 0,
selected_tiles_top: 0,
Expand All @@ -172,6 +210,7 @@ impl Tilepicker {
update_state: &luminol_core::UpdateState<'_>,
ui: &mut egui::Ui,
scroll_rect: egui::Rect,
coll_enabled: bool,
) -> egui::Response {
let time = ui.ctx().input(|i| i.time);
let graphics_state = update_state.graphics.clone();
Expand Down Expand Up @@ -219,6 +258,7 @@ impl Tilepicker {
Callback {
resources: self.resources.clone(),
graphics_state: graphics_state.clone(),
coll_enabled,
},
));

Expand Down
50 changes: 50 additions & 0 deletions crates/graphics/src/collision/collision.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) direction: u32,
}

struct InstanceInput {
@location(2) tile_position: vec3<f32>,
@location(3) passage: u32,
}

struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
}

struct Viewport {
proj: mat4x4<f32>,
}

#if USE_PUSH_CONSTANTS == true
struct PushConstants {
viewport: Viewport,
}
var<push_constant> push_constants: PushConstants;
#else
@group(0) @binding(0)
var<uniform> viewport: Viewport;
#endif

@vertex
fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput {
var out: VertexOutput;

#if USE_PUSH_CONSTANTS == true
let viewport = push_constants.viewport;
#endif

if (instance.passage & vertex.direction) == 0u {
return out;
}

let position = viewport.proj * vec4<f32>(vertex.position.xy + (instance.tile_position.xy * 32.), 0.0, 1.0);
out.clip_position = vec4<f32>(position.xy, instance.tile_position.z, 1.0);

return out;
}

@fragment
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(1., 0., 0., 0.4);
}
Loading

0 comments on commit 6fd75f6

Please sign in to comment.