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

Nicer routes #36

Merged
merged 15 commits into from
Nov 10, 2024
Merged
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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
- Added console command `set_camera_from_cb12` to load view matrices from a raw view scope buffer
- Added commands `lock_time` and `unlock_time` to allow fixing the game time to a specific value (eg. for more deterministic image comparisons)
- Shadow quality option (replaces shadow checkbox)
- Added Arrow Hotkeys to navigate selected object heirarchies @Froggy618157725
in [#36](https://github.com/cohaereo/alkahest/pull/36)
- Added hotkeys to add to current route. <kbd>+</kbd> adds at the end of the route, or after current node, <kbd>-</kbd> adds before the selected node
- Content confidentiality dialog on first startup
- Log unimplemented TFX bytecode ops in the TFX debugger

### Changed

- Enable SSAO by default
- Abstract global/fullscreen pipelines
- Replaced `hecs` with `bevy-ecs`
- Use bevy-ecs change detection to update cbuffers
- Improved Route UI. Can now use translation gizmo for small adjustments
- Enabled fast math for `glam`
- Various atmosphere-related improvements

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

use bevy_ecs::{bundle::Bundle, component::Component};
use ecolor::Color32;
use glam::Vec3;

use super::visibility::VisibilityBundle;

Expand Down Expand Up @@ -57,6 +58,7 @@ impl Display for Icon {
pub struct Label {
pub label: String,
pub default: bool,
pub offset: Vec3,
}

impl Display for Label {
Expand All @@ -71,15 +73,21 @@ impl Label {
Self {
label: s.to_string(),
default: true,
offset: Vec3::new(0.0, 0.0, 0.0),
}
}
pub fn with_offset(mut self, x: f32, y: f32, z: f32) -> Self {
self.offset = Vec3::new(x, y, z);
self
}
}

impl From<&str> for Label {
fn from(s: &str) -> Self {
Self {
label: s.to_string(),
default: false,
offset: Vec3::new(0.0, 0.0, 0.0),
}
}
}
Expand All @@ -89,6 +97,7 @@ impl From<String> for Label {
Self {
label: s,
default: false,
offset: Vec3::new(0.0, 0.0, 0.0),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/alkahest-renderer/src/ecs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod hierarchy;
pub mod map;
pub mod render;
pub mod resources;
pub mod route;
pub mod tags;
pub mod transform;
pub mod utility;
Expand Down
225 changes: 225 additions & 0 deletions crates/alkahest-renderer/src/ecs/route.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
use std::fmt::Write;

use anyhow::Context;
use bevy_ecs::{bundle::Bundle, entity::Entity, prelude::Component, system::Commands};
use destiny_pkg::TagHash;
use glam::Vec3;

use super::{
common::{Global, Icon, Label, Mutable, RenderCommonBundle},
hierarchy::Parent,
tags::{EntityTag, NodeFilter, Tags},
transform::TransformFlags,
utility::{Utility, UtilityCommonBundle},
visibility::Visibility,
SceneInfo,
};
use crate::{
ecs::{hierarchy::Children, transform::Transform, Scene},
icons::{ICON_MAP_MARKER, ICON_MAP_MARKER_PATH},
util::color::Color,
};

pub struct RouteNodeData {
pub pos: Vec3,
pub map_hash: Option<TagHash>,
pub is_teleport: bool,
pub label: Option<String>,
}

impl Default for RouteNodeData {
fn default() -> Self {
Self {
pos: Vec3::ZERO,
map_hash: None,
is_teleport: false,
label: None,
}
}
}
pub struct RouteData {
pub path: Vec<RouteNodeData>,
pub color: Color,
pub rainbow: bool,
pub speed_multiplier: f32,
pub scale: f32,
pub marker_interval: f32,
pub show_all: bool,
pub activity_hash: Option<TagHash>,
}

impl Default for RouteData {
fn default() -> Self {
Self {
path: vec![],
color: Color::WHITE,
rainbow: false,
speed_multiplier: 1.0,
scale: 1.0,
marker_interval: 0.0,
show_all: false,
activity_hash: None,
}
}
}

#[derive(Component, Default)]
pub struct RouteNode {
pub map_hash: Option<TagHash>,
pub is_teleport: bool,
}

#[derive(Bundle)]
pub struct RouteNodeBundle {
pub parent: Parent,
pub transform: Transform,
pub node: RouteNode,
pub global: Global,
pub util_common: UtilityCommonBundle,
}

impl RouteNodeBundle {
pub fn new(parent: Entity, node: RouteNodeData) -> Self {
Self {
parent: Parent(parent),
transform: Transform {
translation: node.pos,
flags: TransformFlags::IGNORE_ROTATION | TransformFlags::IGNORE_SCALE,
..Default::default()
},
node: RouteNode {
map_hash: node.map_hash,
is_teleport: node.is_teleport,
},
global: Global,
util_common: UtilityCommonBundle {
label: if let Some(label) = node.label {
RouteNode::label(&label)
} else {
RouteNode::default_label()
},
icon: RouteNode::icon(),
filter: NodeFilter::Utility,
tags: Tags::from_iter([EntityTag::Utility, EntityTag::Global]),
mutable: Mutable,
render_common: RenderCommonBundle::default(),
},
}
}
}

#[derive(Component)]
pub struct Route {
pub color: Color,
pub rainbow: bool,
pub speed_multiplier: f32,
pub scale: f32,
pub marker_interval: f32,
pub show_all: bool,
pub activity_hash: Option<TagHash>,
}

impl Default for Route {
fn default() -> Self {
Self {
color: Color::WHITE,
rainbow: false,
speed_multiplier: 1.0,
scale: 1.0,
marker_interval: 0.0,
show_all: false,
activity_hash: None,
}
}
}

impl Route {
pub fn get_command(&self, scene: &Scene, entity: Entity) -> anyhow::Result<String> {
let mut command = String::from("route");
if let Some(hash) = self.activity_hash.as_ref() {
write!(&mut command, " hash {}", hash.0)?;
}
if let Some(children) = scene.entity(entity).get::<Children>() {
for child_ent in &children.0 {
let pos = scene
.entity(*child_ent)
.get::<Transform>()
.context("Missing Transform")?;
let node = scene
.entity(*child_ent)
.get::<RouteNode>()
.context("Missing Route Node")?;
let label = scene
.entity(*child_ent)
.get::<Label>()
.context("Missing Label")?;

write!(
&mut command,
" node {} {} {}{}{}{}",
pos.translation[0],
pos.translation[1],
pos.translation[2],
if node.is_teleport { " tp" } else { "" },
node.map_hash
.map_or(String::new(), |h| { format!(" hash {}", h.0) }),
if !label.default {
format!(
" label {}",
label.label.replace('\\', r"\\").replace(' ', r"\s")
)
} else {
String::new()
}
)?;
}
}
Ok(command)
}

pub fn fixup_visiblity(&self, scene: &Scene, cmd: &mut Commands, entity: Entity) {
let mut prev_visible = false;
if let Some(children) = scene.get::<Children>(entity) {
for (i, child_ent) in children.0.iter().enumerate() {
let ent = children.0.get(i + 1);
let next_node = ent.and_then(|e| scene.entity(*e).get::<RouteNode>());
if let Some(node) = scene.get::<RouteNode>(*child_ent) {
let current_visible = node.map_hash == scene.get_map_hash();
let next_visible =
next_node.map_or(false, |n| n.map_hash == scene.get_map_hash());
let e = scene.entity(*child_ent);
if self.show_all || prev_visible || current_visible || next_visible {
cmd.entity(e.id()).insert(Visibility::Visible);
} else {
cmd.entity(e.id()).insert(Visibility::Hidden);
}
prev_visible = current_visible;
}
}
}
}
}

impl Utility for Route {
fn icon() -> Icon {
Icon::Unicode(ICON_MAP_MARKER_PATH)
}

fn default_label() -> Label {
Label::new_default("Route")
}
}

impl Utility for RouteNode {
fn icon() -> Icon {
Icon::Unicode(ICON_MAP_MARKER)
}

fn label(str: &str) -> Label {
Label::from(str).with_offset(0.0, 0.0, 0.12)
}

fn default_label() -> Label {
Label::new_default("").with_offset(0.0, 0.0, 0.12)
}
}
Loading
Loading