Rework tab system & improve texture load

This commit is contained in:
Luke Street
2023-03-27 22:30:13 -04:00
parent 56d3acc12d
commit 80412666e5
18 changed files with 493 additions and 462 deletions
+4 -4
View File
@@ -50,7 +50,7 @@ impl FourCC {
}
impl Display for FourCC {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
for c in self.0 {
f.write_char(c as char)?;
}
@@ -59,7 +59,7 @@ impl Display for FourCC {
}
impl Debug for FourCC {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_char('"')?;
for c in self.0 {
f.write_char(c as char)?;
@@ -530,9 +530,9 @@ impl From<CObjectId> for Uuid {
}
impl Display for CObjectId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) }
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "{}", self.0) }
}
impl Debug for CObjectId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.0) }
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "{:?}", self.0) }
}
+1 -24
View File
@@ -1,6 +1,5 @@
use std::{
fmt::{Debug, Display, Formatter},
fs,
io::{Cursor, Read, Seek},
};
@@ -13,7 +12,6 @@ use crate::{
rfrm::FormDescriptor, slice_chunks, CColor4f, CObjectId, CStringFixed, CVector3f,
CVector4f, FourCC, TaggedVec,
},
util::templates::{TemplateRoot, TypeTemplate},
};
// Room
@@ -225,7 +223,7 @@ pub struct GameObjectComponent {
}
impl Display for ComponentType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
ComponentType::Known(t) => write!(f, "{:?}", t),
ComponentType::Unknown(t) => write!(f, "{:#X}", t),
@@ -358,27 +356,6 @@ impl RoomData {
},
)?;
let root: TemplateRoot =
serde_json::from_str(include_str!("../../templates/mp1r/root.json"))?;
// println!("{:?}", root);
let object: TypeTemplate =
serde_json::from_str(include_str!("../../templates/mp1r/objects/Render.json"))?;
let enum_: TypeTemplate = serde_json::from_str(include_str!(
"../../templates/mp1r/enums/RenderTargetScene.json"
))?;
// println!("{:?}", object);
let struct_: TypeTemplate = serde_json::from_str(include_str!("/home/lstreet/Development/paktool-rs/lib/templates/mp1r/typedefs/RenderStaticModel.json")).unwrap();
println!("{}", serde_json::to_string_pretty(&root).unwrap());
println!("{}", serde_json::to_string_pretty(&object).unwrap());
println!("{}", serde_json::to_string_pretty(&enum_).unwrap());
println!("{}", serde_json::to_string_pretty(&struct_).unwrap());
// fs::write("/home/lstreet/Development/paktool-rs/lib/templates/mp1r/root.json", serde_json::to_string_pretty(&root).unwrap()).unwrap();
// fs::write("/home/lstreet/Development/paktool-rs/lib/templates/mp1r/objects/Render.json", serde_json::to_string_pretty(&object).unwrap()).unwrap();
// fs::write("/home/lstreet/Development/paktool-rs/lib/templates/mp1r/enums/RenderTargetScene.json", serde_json::to_string_pretty(&enum_).unwrap()).unwrap();
fs::write("/home/lstreet/Development/paktool-rs/lib/templates/mp1r/typedefs/RenderStaticModel.json", serde_json::to_string_pretty(&struct_).unwrap()).unwrap();
let mut constructed_properties = Vec::with_capacity(component_properties.len());
for props in &component_properties {
use EGOComponentType::*;
+7 -7
View File
@@ -50,7 +50,7 @@ pub enum ETextureType {
}
impl Display for ETextureType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_str(match self {
ETextureType::D1 => "1D",
ETextureType::D2 => "2D",
@@ -68,7 +68,7 @@ impl Display for ETextureType {
#[binrw]
#[repr(u8)]
#[brw(repr(u8))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum ETextureWrap {
ClampToEdge = 0,
Repeat = 1,
@@ -81,7 +81,7 @@ pub enum ETextureWrap {
#[binrw]
#[repr(u8)]
#[brw(repr(u8))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum ETextureFilter {
Nearest = 0,
Linear = 1,
@@ -90,7 +90,7 @@ pub enum ETextureFilter {
#[binrw]
#[repr(u8)]
#[brw(repr(u8))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum ETextureMipFilter {
Nearest = 0,
Linear = 1,
@@ -99,7 +99,7 @@ pub enum ETextureMipFilter {
#[binrw]
#[repr(u8)]
#[brw(repr(u8))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum ETextureAnisotropicRatio {
None = u8::MAX,
Ratio1 = 0,
@@ -127,7 +127,7 @@ pub struct STextureHeader {
}
#[binrw]
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct STextureSamplerData {
pub unk: u32,
pub filter: ETextureFilter,
@@ -268,7 +268,7 @@ pub enum ETextureFormat {
}
impl Display for ETextureFormat {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_str(match self {
ETextureFormat::R8Unorm => "R8 UNORM",
ETextureFormat::R8Snorm => "R8 SNORM",
+2 -2
View File
@@ -76,11 +76,11 @@ impl IntoRepr for TypeTemplateTypeDiscriminants {
pub struct HexU32(pub u32);
impl fmt::Display for HexU32 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:#010X}", self.0) }
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:#010X}", self.0) }
}
impl fmt::Debug for HexU32 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:#010X}", self.0) }
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:#010X}", self.0) }
}
impl ser::Serialize for HexU32 {
+26 -4
View File
@@ -7,10 +7,13 @@ use bevy::{
use binrw::Endian;
use retrolib::format::{
foot::{locate_asset_id, locate_meta},
ltpb::{LightProbeBundleHeader, LightProbeData, LightProbeExtra},
ltpb::{LightProbeBundleHeader, LightProbeData, LightProbeExtra, K_FORM_LTPB},
};
use crate::loaders::texture::{load_texture_asset, TextureAsset};
use crate::{
loaders::texture::{load_texture_asset, TextureAsset},
AssetRef,
};
#[derive(Debug, Clone, bevy::reflect::TypeUuid)]
#[uuid = "f5d65a8b-ffcc-47ea-8c9d-1ab30cca723c"]
@@ -47,8 +50,27 @@ impl AssetLoader for LightProbeAssetLoader {
info!("Loading light probe {} {:?}", id, data.head);
let mut textures = Vec::with_capacity(data.textures.len());
for texture_data in data.textures {
textures.push(load_texture_asset(id, texture_data, &self.supported_formats)?);
for (idx, texture_data) in data.textures.into_iter().enumerate() {
let result = load_texture_asset(texture_data, &self.supported_formats)?;
let image_handle = load_context
.set_labeled_asset(&format!("image_{idx}"), LoadedAsset::new(result.texture));
let mut slice_handles = Vec::with_capacity(result.slices.len());
for (mip, images) in result.slices.into_iter().enumerate() {
let mut handles = Vec::with_capacity(images.len());
for (layer, image) in images.into_iter().enumerate() {
handles.push(load_context.set_labeled_asset(
&format!("image_{idx}_mip_{mip}_layer_{layer}"),
LoadedAsset::new(image),
));
}
slice_handles.push(handles);
}
textures.push(TextureAsset {
asset_ref: AssetRef { id, kind: K_FORM_LTPB },
inner: result.inner,
texture: image_handle,
slices: slice_handles,
});
}
load_context.set_default_asset(LoadedAsset::new(LightProbeAsset {
head: data.head,
+36 -36
View File
@@ -4,7 +4,7 @@ use anyhow::{Error, Result};
use bevy::{
asset::{AssetLoader, AssetPath, BoxedFuture, LoadContext, LoadState, LoadedAsset},
prelude::*,
render::{render_resource::SamplerDescriptor, texture::ImageSampler},
render::{render_resource::SamplerDescriptor},
utils::{hashbrown::hash_map::Entry, HashMap},
};
use binrw::Endian;
@@ -118,6 +118,7 @@ impl ModelAsset {
server.get_group_load_state(self.textures.values().map(|h| h.id()))
}
#[allow(dead_code)]
pub fn sampler_data<'asset>(
&self,
texture_id: &Uuid,
@@ -131,47 +132,44 @@ impl ModelAsset {
pub fn build_texture_images(
&mut self,
texture_assets: &Assets<TextureAsset>,
images: &mut Assets<Image>,
texture_assets: &mut Assets<TextureAsset>,
_images: &mut Assets<Image>,
) {
// Build sampler descriptors
let mut sampler_descriptors = HashMap::<Uuid, SamplerDescriptor>::new();
for mat in &self.inner.mtrl.materials {
for data in &mat.data {
match &data.data {
CMaterialDataInner::Texture(texture) => {
if let Some(usage) = &texture.usage {
let sampler_data = self.sampler_data(&texture.id, texture_assets);
sampler_descriptors.insert(
texture.id,
sampler_descriptor_from_usage(usage, sampler_data),
);
}
}
CMaterialDataInner::LayeredTexture(layers) => {
for texture in &layers.textures {
if let Some(usage) = &texture.usage {
let sampler_data = self.sampler_data(&texture.id, texture_assets);
sampler_descriptors.insert(
texture.id,
sampler_descriptor_from_usage(usage, sampler_data),
);
}
}
}
_ => continue,
}
}
}
// let mut sampler_descriptors = HashMap::<Uuid, SamplerDescriptor>::new();
// for mat in &self.inner.mtrl.materials {
// for data in &mat.data {
// match &data.data {
// CMaterialDataInner::Texture(texture) => {
// if let Some(usage) = &texture.usage {
// let sampler_data = self.sampler_data(&texture.id, texture_assets);
// sampler_descriptors.insert(
// texture.id,
// sampler_descriptor_from_usage(usage, sampler_data),
// );
// }
// }
// CMaterialDataInner::LayeredTexture(layers) => {
// for texture in &layers.textures {
// if let Some(usage) = &texture.usage {
// let sampler_data = self.sampler_data(&texture.id, texture_assets);
// sampler_descriptors.insert(
// texture.id,
// sampler_descriptor_from_usage(usage, sampler_data),
// );
// }
// }
// }
// _ => continue,
// }
// }
// }
// Build texture images
for (id, handle) in &self.textures {
let asset = texture_assets.get(handle).unwrap();
let mut image = asset.texture.clone();
if let Some(desc) = sampler_descriptors.get(id) {
image.sampler_descriptor = ImageSampler::Descriptor(desc.clone());
}
self.texture_images.insert(*id, images.add(image));
// TODO: use sampler descriptors
self.texture_images.insert(*id, asset.texture.clone());
}
}
@@ -334,6 +332,7 @@ fn build_material(
Ok(out_mat)
}
#[allow(dead_code)]
fn texture_wrap(wrap: ETextureWrap) -> AddressMode {
match wrap {
ETextureWrap::ClampToEdge => AddressMode::ClampToEdge,
@@ -345,6 +344,7 @@ fn texture_wrap(wrap: ETextureWrap) -> AddressMode {
}
}
#[allow(dead_code)]
fn sampler_descriptor_from_usage<'desc>(
usage: &STextureUsageInfo,
data: Option<&STextureSamplerData>,
+53 -15
View File
@@ -1,3 +1,5 @@
use std::num::NonZeroU8;
use anyhow::{anyhow, Error, Result};
use bevy::{
asset::{AssetLoader, BoxedFuture, LoadContext, LoadedAsset},
@@ -7,7 +9,7 @@ use bevy::{
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
},
renderer::RenderDevice,
texture::CompressedImageFormats,
texture::{CompressedImageFormats, ImageSampler},
},
};
use binrw::Endian;
@@ -17,7 +19,8 @@ use retrolib::format::{
decompress_image, slice_texture, ETextureFormat, ETextureType, TextureData, K_FORM_TXTR,
},
};
use uuid::Uuid;
use wgpu::SamplerDescriptor;
use wgpu_types::{AddressMode, FilterMode};
use crate::AssetRef;
@@ -26,8 +29,8 @@ use crate::AssetRef;
pub struct TextureAsset {
pub asset_ref: AssetRef,
pub inner: TextureData,
pub texture: Image,
pub slices: Vec<Vec<Image>>, // [mip][layer]
pub texture: Handle<Image>,
pub slices: Vec<Vec<Handle<Image>>>, // [mip][layer]
}
pub struct TextureAssetLoader {
@@ -56,8 +59,26 @@ impl AssetLoader for TextureAssetLoader {
let data = TextureData::slice(bytes, meta, Endian::Little)?;
info!("Loading texture {} {:?}", id, data.head);
let asset = load_texture_asset(id, data, &self.supported_formats)?;
load_context.set_default_asset(LoadedAsset::new(asset));
let result = load_texture_asset(data, &self.supported_formats)?;
let image_handle =
load_context.set_labeled_asset("image", LoadedAsset::new(result.texture));
let mut slice_handles = Vec::with_capacity(result.slices.len());
for (mip, images) in result.slices.into_iter().enumerate() {
let mut handles = Vec::with_capacity(images.len());
for (layer, image) in images.into_iter().enumerate() {
handles.push(load_context.set_labeled_asset(
&format!("mip_{}_layer_{}", mip, layer),
LoadedAsset::new(image),
));
}
slice_handles.push(handles);
}
load_context.set_default_asset(LoadedAsset::new(TextureAsset {
asset_ref: AssetRef { id, kind: K_FORM_TXTR },
inner: result.inner,
texture: image_handle,
slices: slice_handles,
}));
Ok(())
})
}
@@ -65,11 +86,16 @@ impl AssetLoader for TextureAssetLoader {
fn extensions(&self) -> &[&str] { &["txtr"] }
}
pub struct LoadTextureResult {
pub inner: TextureData,
pub texture: Image,
pub slices: Vec<Vec<Image>>, // [mip][layer]
}
pub fn load_texture_asset(
id: Uuid,
data: TextureData,
supported_formats: &CompressedImageFormats,
) -> Result<TextureAsset> {
) -> Result<LoadTextureResult> {
let is_srgb = data.head.format.is_srgb();
let slices = slice_texture(&data)?;
let (bw, bh, _) = data.head.format.block_size();
@@ -110,12 +136,7 @@ pub fn load_texture_asset(
)
};
let texture = texture_to_image(&data, format, image_data)?;
Ok(TextureAsset {
asset_ref: AssetRef { id, kind: K_FORM_TXTR },
inner: data,
texture,
slices: images,
})
Ok(LoadTextureResult { inner: data, texture, slices: images })
}
/// Create an [Image] from a 2D texture slice.
@@ -145,6 +166,7 @@ fn texture_slice_to_image(
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
view_formats: &[],
},
sampler_descriptor: DEFAULT_SAMPLER,
..default()
}
}
@@ -155,10 +177,25 @@ fn texture_format_supported(
supported_formats: &CompressedImageFormats,
) -> bool {
supported_formats.supports(format)
// ASTC 3D textures are unsupported
// ASTC 3D textures are not supported by wgpu
&& !(kind == ETextureType::D3 && matches!(format, TextureFormat::Astc { .. }))
}
const DEFAULT_SAMPLER: ImageSampler = ImageSampler::Descriptor(SamplerDescriptor {
label: None,
address_mode_u: AddressMode::Repeat,
address_mode_v: AddressMode::Repeat,
address_mode_w: AddressMode::Repeat,
mag_filter: FilterMode::Linear,
min_filter: FilterMode::Linear,
mipmap_filter: FilterMode::Linear,
lod_min_clamp: 0.0,
lod_max_clamp: f32::MAX,
compare: None,
anisotropy_clamp: NonZeroU8::new(8),
border_color: None,
});
/// Creates an [Image] from a full texture.
fn texture_to_image(
data: &TextureData,
@@ -186,6 +223,7 @@ fn texture_to_image(
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
view_formats: &[],
},
sampler_descriptor: DEFAULT_SAMPLER,
..default()
})
}
+113 -93
View File
@@ -4,19 +4,22 @@ mod material;
mod render;
mod tabs;
use std::{path::PathBuf, time::Duration};
use std::{borrow::Cow, path::PathBuf, time::Duration};
use bevy::{
app::AppExit,
asset::diagnostic::AssetCountDiagnosticsPlugin,
diagnostic::{Diagnostics, EntityCountDiagnosticsPlugin, FrameTimeDiagnosticsPlugin},
diagnostic::{
Diagnostics, EntityCountDiagnosticsPlugin, FrameTimeDiagnosticsPlugin,
SystemInformationDiagnosticsPlugin,
},
prelude::*,
window::{PrimaryWindow, WindowResolution},
};
use bevy_egui::{egui, EguiContext, EguiContexts, EguiPlugin};
use bevy_mod_raycast::{DefaultPluginState, DefaultRaycastingPlugin};
use egui::{FontFamily, FontId, Frame, Rounding};
use egui_dock::{TabIndex, TabViewer as DockTabViewer};
use egui_dock::{NodeIndex, TabIndex, TabViewer as DockTabViewer};
use retrolib::format::FourCC;
use uuid::Uuid;
use walkdir::{DirEntry, WalkDir};
@@ -30,7 +33,10 @@ use crate::{
},
material::CustomMaterial,
render::{grid::GridPlugin, TemporaryLabel},
tabs::{load_tab, modcon::ModConRaycastSet, TabState, TabType, TabViewer},
tabs::{
modcon::ModConRaycastSet, project::ProjectTab, splash::SplashTab, EditorTab, TabState,
TabType, TabViewer,
},
};
#[derive(Default, Resource)]
@@ -87,11 +93,11 @@ fn main() {
.add_plugin(EntityCountDiagnosticsPlugin::default())
.add_plugin(AssetCountDiagnosticsPlugin::<TextureAsset>::default())
.add_plugin(AssetCountDiagnosticsPlugin::<ModelAsset>::default())
.add_plugin(SystemInformationDiagnosticsPlugin::default())
// Systems
.add_startup_system(setup_egui)
.add_system(file_drop.before(load_files))
.add_system(load_files)
.add_system(bottom_bar_system.before(ui_system))
.add_system(ui_system)
.run();
}
@@ -111,10 +117,8 @@ struct UiState {
impl Default for UiState {
fn default() -> Self {
let mut tree = egui_dock::Tree::new(vec![TabType::Splash(default())]);
tree.split_left(egui_dock::NodeIndex::root(), 0.25, vec![TabType::Project(default())]);
Self {
tree,
tree: default_tree(),
ui_font: FontId { size: 13.0, family: FontFamily::Proportional },
code_font: FontId { size: 14.0, family: FontFamily::Monospace },
}
@@ -158,95 +162,105 @@ fn load_files(
}
}
fn bottom_bar_system(mut egui_ctx: EguiContexts, diagnostics: Res<Diagnostics>) {
egui::TopBottomPanel::bottom("bottom_panel").show_separator_line(false).show(
egui_ctx.ctx_mut(),
|ui| {
ui.horizontal(|ui| {
ui.label(format!(
"[FPS {:.0}] [Loaded Textures: {} | Models: {} | Entities: {}]",
diagnostics
.get(FrameTimeDiagnosticsPlugin::FPS)
.and_then(|d| d.smoothed())
.unwrap_or_default(),
diagnostics
.get_measurement(
AssetCountDiagnosticsPlugin::<TextureAsset>::diagnostic_id()
)
.map(|d| d.value)
.unwrap_or_default(),
diagnostics
.get_measurement(AssetCountDiagnosticsPlugin::<ModelAsset>::diagnostic_id())
.map(|d| d.value)
.unwrap_or_default(),
diagnostics
.get_measurement(EntityCountDiagnosticsPlugin::ENTITY_COUNT)
.map(|d| d.value)
.unwrap_or_default(),
));
});
},
);
fn close_all_tabs(world: &mut World, ui_state: &mut UiState) {
for node in ui_state.tree.iter_mut() {
if let egui_dock::Node::Leaf { tabs, .. } = node {
tabs.retain_mut(|tab| !tab.close(world));
}
}
'outer: loop {
for (i, node) in ui_state.tree.iter().enumerate() {
if matches!(node, egui_dock::Node::Leaf { tabs, .. } if tabs.is_empty()) {
ui_state.tree.remove_leaf(NodeIndex(i));
continue 'outer;
}
}
break;
}
}
fn default_tree() -> egui_dock::Tree<TabType> {
let mut tree = egui_dock::Tree::<TabType>::new(vec![SplashTab::new()]);
tree.split_left(NodeIndex::root(), 0.25, vec![ProjectTab::new()]);
tree
}
fn ui_system(world: &mut World) {
let mut ctx = world
.query::<(&mut EguiContext, With<PrimaryWindow>)>()
.iter(world)
.next()
.query_filtered::<&EguiContext, With<PrimaryWindow>>()
.get_single(world)
.unwrap()
.0
.clone();
let style = ctx.get_mut().style();
egui::TopBottomPanel::top("top_panel")
.show_separator_line(false)
.frame(Frame::side_top_panel(style.as_ref()).fill(egui::Color32::BLACK))
.show(ctx.get_mut(), |ui| {
egui::menu::bar(ui, |ui| {
egui::menu::menu_button(ui, "File", |ui| {
if ui.button("Quit").clicked() {
world.send_event(AppExit);
}
world.resource_scope::<UiState, _>(|world, mut ui_state| {
let style = ctx.get_mut().style();
egui::TopBottomPanel::top("top_panel")
.show_separator_line(false)
.frame(Frame::side_top_panel(style.as_ref()).fill(egui::Color32::BLACK))
.show(ctx.get_mut(), |ui| {
egui::menu::bar(ui, |ui| {
egui::menu::menu_button(ui, "File", |ui| {
if ui.button("Quit").clicked() {
world.send_event(AppExit);
}
});
egui::menu::menu_button(ui, "View", |ui| {
if ui.button("Restore default layout").clicked() {
close_all_tabs(world, ui_state.as_mut());
if ui_state.tree.is_empty() {
ui_state.tree = default_tree();
}
ui.close_menu();
}
});
});
});
});
world.resource_scope::<UiState, _>(|world, mut ui_state| {
let diagnostics = world.resource::<Diagnostics>();
egui::TopBottomPanel::bottom("bottom_panel").show_separator_line(false).show(
ctx.get_mut(),
|ui| {
ui.horizontal(|ui| {
ui.label(format!(
"[FPS {:.0} | RAM {}] [Loaded Textures: {} | Models: {} | Entities: {}]",
diagnostics
.get(FrameTimeDiagnosticsPlugin::FPS)
.and_then(|d| d.smoothed())
.unwrap_or_default(),
diagnostics
.get(SystemInformationDiagnosticsPlugin::MEM_USAGE)
.and_then(|d| d
.measurement()
.map(|m| Cow::Owned(format!("{:.0}{}", m.value, d.suffix))))
.unwrap_or(Cow::Borrowed("?")),
diagnostics
.get_measurement(
AssetCountDiagnosticsPlugin::<TextureAsset>::diagnostic_id()
)
.map(|d| d.value)
.unwrap_or_default(),
diagnostics
.get_measurement(
AssetCountDiagnosticsPlugin::<ModelAsset>::diagnostic_id()
)
.map(|d| d.value)
.unwrap_or_default(),
diagnostics
.get_measurement(EntityCountDiagnosticsPlugin::ENTITY_COUNT)
.map(|d| d.value)
.unwrap_or_default(),
));
});
},
);
let mut tab_assets = vec![];
for node in ui_state.tree.iter_mut() {
if let egui_dock::Node::Leaf { tabs, .. } = node {
for tab in tabs {
match tab {
TabType::Project(tab) => {
load_tab(world, &mut ctx, tab.as_mut());
}
TabType::Texture(tab) => {
load_tab(world, &mut ctx, tab.as_mut());
tab_assets.push(tab.asset_ref);
}
TabType::Model(tab) => {
load_tab(world, &mut ctx, tab.as_mut());
tab_assets.push(tab.asset_ref);
}
TabType::ModCon(tab) => {
load_tab(world, &mut ctx, tab.as_mut());
tab_assets.push(tab.asset_ref);
}
TabType::LightProbe(tab) => {
load_tab(world, &mut ctx, tab.as_mut());
tab_assets.push(tab.asset_ref);
}
TabType::Room(tab) => {
load_tab(world, &mut ctx, tab.as_mut());
tab_assets.push(tab.asset_ref);
}
TabType::Templates(tab) => {
load_tab(world, &mut ctx, tab.as_mut());
}
TabType::Splash(tab) => {
load_tab(world, &mut ctx, tab.as_mut());
}
tab.load(world);
if let Some(asset) = tab.asset() {
tab_assets.push(asset);
}
}
}
@@ -273,6 +287,7 @@ fn ui_system(world: &mut World) {
},
};
// Setup and draw the dock area
let mut style = egui_dock::Style::from_egui(style.as_ref());
const MARGIN: f32 = 5.0;
style.border.color = egui::Color32::BLACK;
@@ -288,33 +303,38 @@ fn ui_system(world: &mut World) {
style.tabs.grabbed_color = style.tabs.bg_fill.gamma_multiply(0.8);
style.tabs.rounding = Rounding { nw: MARGIN, ne: MARGIN, sw: 0.0, se: 0.0 };
style.tabs.text_align = egui::Align2::CENTER_CENTER;
egui_dock::DockArea::new(&mut ui_state.tree).style(style).show_add_buttons(true).show_add_popup(true).show(ctx.get_mut(), &mut viewer);
egui_dock::DockArea::new(&mut ui_state.tree)
.style(style)
.show_add_buttons(true)
.show_add_popup(true)
.show(ctx.get_mut(), &mut viewer);
// Close all tabs in a group
if let Some(node) = viewer.state.close_all {
if let egui_dock::Node::Leaf { tabs, .. } = &mut ui_state.tree[node] {
for tab in tabs.iter_mut() {
viewer.on_close(tab);
tabs.retain_mut(|tab| !viewer.on_close(tab));
if tabs.is_empty() {
ui_state.tree.remove_leaf(node);
} else {
ui_state.tree.set_active_tab(node, TabIndex(0));
}
tabs.clear();
ui_state.tree.remove_leaf(node);
}
}
// Close other tabs in a group
if let Some((node, tab_index)) = viewer.state.close_others {
if let egui_dock::Node::Leaf { tabs, .. } = &mut ui_state.tree[node] {
let mut i = 0usize;
tabs.retain_mut(|tab| {
let keep = i == tab_index.0;
if !keep {
viewer.on_close(tab);
}
i += 1;
keep
keep || !viewer.on_close(tab)
});
ui_state.tree.set_active_tab(node, TabIndex(0));
}
}
// Open a new tab if requested
if let Some(open) = viewer.state.open_tab {
if let Some(node) = open.node {
ui_state.tree.set_focused_node(node);
@@ -324,8 +344,8 @@ fn ui_system(world: &mut World) {
}
}
// If we're not rendering any scenes, spawn a camera to just clear the screen
if viewer.state.render_layer == 0 {
// Spawn a camera to just clear the screen
world.spawn((Camera3dBundle::default(), TemporaryLabel));
}
});
+3 -3
View File
@@ -267,11 +267,11 @@ impl Default for GridSettings {
// noinspection RsSortImplTraitMembers
impl ExtractComponent for GridSettings {
type Filter = ();
type Filter = With<Camera>;
type Out = Self;
type Query = (Read<Self>, Read<Camera>);
type Query = Read<Self>;
fn extract_component((settings, _camera): QueryItem<'_, Self::Query>) -> Option<Self::Out> {
fn extract_component(settings: QueryItem<Self::Query>) -> Option<Self::Out> {
Some(settings.clone())
}
}
+23 -14
View File
@@ -3,13 +3,13 @@ use bevy::{
ecs::system::{lifetimeless::*, *},
prelude::*,
};
use bevy_egui::{EguiContext, EguiUserTextures};
use bevy_egui::EguiUserTextures;
use retrolib::format::txtr::ETextureType;
use crate::{
icon,
loaders::lightprobe::LightProbeAsset,
tabs::{texture::LoadedTexture, SystemTab, TabState},
tabs::{texture::LoadedTexture, EditorTabSystem, TabState},
AssetRef,
};
@@ -20,17 +20,23 @@ pub struct LightProbeTab {
pub loaded_textures: Vec<Vec<LoadedTexture>>,
}
impl SystemTab for LightProbeTab {
impl LightProbeTab {
pub fn new(asset_ref: AssetRef, handle: Handle<LightProbeAsset>) -> Box<Self> {
Box::new(Self { asset_ref, handle, ..default() })
}
}
impl EditorTabSystem for LightProbeTab {
type LoadParam =
(SRes<Assets<LightProbeAsset>>, SResMut<Assets<Image>>, SResMut<EguiUserTextures>);
type UiParam = (SRes<AssetServer>, SRes<Assets<LightProbeAsset>>);
fn load(&mut self, _ctx: &mut EguiContext, query: SystemParamItem<'_, '_, Self::LoadParam>) {
fn load(&mut self, query: SystemParamItem<Self::LoadParam>) {
if !self.loaded_textures.is_empty() {
return;
}
let (assets, mut images, mut egui_textures) = query;
let (assets, images, mut egui_textures) = query;
let Some(asset) = assets.get(&self.handle) else { return; };
self.loaded_textures.reserve_exact(asset.textures.len());
@@ -39,10 +45,13 @@ impl SystemTab for LightProbeTab {
for mip in &texture.slices {
let mut texture_ids = Vec::with_capacity(mip.len());
for image in mip {
let handle = images.add(image.clone());
texture_ids.push(egui_textures.add_image(handle));
texture_ids.push(egui_textures.add_image(image.clone_weak()));
}
let size = mip.first().map(|m| m.texture_descriptor.size).unwrap_or_default();
let size = mip
.first()
.and_then(|h| images.get(h))
.map(|m| m.texture_descriptor.size)
.unwrap_or_default();
slices.push(LoadedTexture { texture_ids, width: size.width, height: size.height });
}
self.loaded_textures.push(slices);
@@ -52,7 +61,7 @@ impl SystemTab for LightProbeTab {
fn ui(
&mut self,
ui: &mut egui::Ui,
query: SystemParamItem<'_, '_, Self::UiParam>,
query: SystemParamItem<Self::UiParam>,
_state: &mut TabState,
) {
let (server, assets) = query;
@@ -60,10 +69,7 @@ impl SystemTab for LightProbeTab {
ui.label(format!("{} {}", self.asset_ref.kind, self.asset_ref.id));
match server.get_load_state(&self.handle) {
LoadState::NotLoaded => {
return;
}
LoadState::Loading => {
LoadState::NotLoaded | LoadState::Loading => {
ui.spinner();
return;
}
@@ -73,6 +79,7 @@ impl SystemTab for LightProbeTab {
return;
}
LoadState::Unloaded => {
ui.colored_label(egui::Color32::RED, "Unloaded");
return;
}
};
@@ -128,9 +135,11 @@ impl SystemTab for LightProbeTab {
}
}
fn title(&mut self) -> egui::WidgetText {
fn title(&self) -> egui::WidgetText {
format!("{} {} {}", icon::LIGHTPROBE_CUBEMAP, self.asset_ref.kind, self.asset_ref.id).into()
}
fn id(&self) -> String { format!("{} {}", self.asset_ref.kind, self.asset_ref.id) }
fn asset(&self) -> Option<AssetRef> { Some(self.asset_ref) }
}
+68 -99
View File
@@ -8,28 +8,19 @@ pub mod templates;
pub mod texture;
use bevy::{ecs::system::*, prelude::*, render::camera::*};
use bevy_egui::EguiContext;
use egui::Widget;
use egui_dock::{NodeIndex, Style, TabIndex};
use crate::{icon, AssetRef};
pub enum TabType {
Project(Box<project::ProjectTab>),
Texture(Box<texture::TextureTab>),
Model(Box<model::ModelTab>),
ModCon(Box<modcon::ModConTab>),
LightProbe(Box<lightprobe::LightProbeTab>),
Room(Box<room::RoomTab>),
Templates(Box<templates::TemplatesTab>),
Splash(Box<splash::SplashTab>),
}
pub type TabType = Box<dyn EditorTab>;
pub struct OpenTab {
pub tab: TabType,
pub node: Option<NodeIndex>,
}
#[derive(Default)]
pub struct TabState {
pub open_assets: Vec<AssetRef>,
pub open_tab: Option<OpenTab>,
@@ -43,49 +34,80 @@ impl TabState {
fn open_tab(&mut self, tab: TabType) { self.open_tab = Some(OpenTab { tab, node: None }); }
}
pub trait SystemTab {
type LoadParam: SystemParam;
type UiParam: SystemParam;
pub trait EditorTab: Send + Sync {
fn new() -> Box<Self>
where Self: Default {
default()
}
fn load(&mut self, _ctx: &mut EguiContext, _query: SystemParamItem<'_, '_, Self::LoadParam>) {}
fn load(&mut self, world: &mut World);
fn close(&mut self, _query: SystemParamItem<'_, '_, Self::LoadParam>) {} // , _ctx: &mut EguiContext
fn ui(&mut self, world: &mut World, ui: &mut egui::Ui, tab_state: &mut TabState);
fn close(&mut self, world: &mut World) -> bool;
fn title(&self) -> egui::WidgetText;
fn id(&self) -> String;
fn clear_background(&self) -> bool { true }
fn asset(&self) -> Option<AssetRef> { None }
}
pub trait EditorTabSystem: Send + Sync {
type LoadParam: SystemParam + 'static;
type UiParam: SystemParam + 'static;
fn load(&mut self, _query: SystemParamItem<Self::LoadParam>) {}
fn close(&mut self, _query: SystemParamItem<Self::LoadParam>) -> bool { true }
fn ui(
&mut self,
ui: &mut egui::Ui,
query: SystemParamItem<'_, '_, Self::UiParam>,
query: SystemParamItem<Self::UiParam>,
state: &mut TabState,
);
fn title(&mut self) -> egui::WidgetText;
fn title(&self) -> egui::WidgetText;
fn id(&self) -> String;
fn clear_background(&self) -> bool { true }
fn asset(&self) -> Option<AssetRef> { None }
}
pub fn load_tab<T: SystemTab + 'static>(world: &mut World, ctx: &mut EguiContext, tab: &mut T) {
let mut state: SystemState<T::LoadParam> = SystemState::new(world);
tab.load(ctx, state.get_mut(world));
state.apply(world);
}
impl<T: EditorTabSystem> EditorTab for T {
fn load(&mut self, world: &mut World) {
let mut state: SystemState<T::LoadParam> = SystemState::new(world);
EditorTabSystem::load(self, state.get_mut(world));
state.apply(world);
}
fn render_tab<T: SystemTab + 'static>(
world: &mut World,
ui: &mut egui::Ui,
tab: &mut T,
tab_state: &mut TabState,
) {
let mut state: SystemState<T::UiParam> = SystemState::new(world);
ui.push_id(tab.id(), |ui| {
tab.ui(ui, state.get_mut(world), tab_state);
});
state.apply(world);
}
fn ui(&mut self, world: &mut World, ui: &mut egui::Ui, tab_state: &mut TabState) {
let mut state: SystemState<T::UiParam> = SystemState::new(world);
ui.push_id(self.id(), |ui| {
EditorTabSystem::ui(self, ui, state.get_mut(world), tab_state);
});
state.apply(world);
}
fn close_tab<T: SystemTab + 'static>(world: &mut World, tab: &mut T) {
let mut state: SystemState<T::LoadParam> = SystemState::new(world);
tab.close(state.get_mut(world));
state.apply(world);
fn close(&mut self, world: &mut World) -> bool {
let mut state: SystemState<T::LoadParam> = SystemState::new(world);
let result = EditorTabSystem::close(self, state.get_mut(world));
state.apply(world);
result
}
fn title(&self) -> egui::WidgetText { EditorTabSystem::title(self) }
fn id(&self) -> String { EditorTabSystem::id(self) }
fn clear_background(&self) -> bool { EditorTabSystem::clear_background(self) }
fn asset(&self) -> Option<AssetRef> { EditorTabSystem::asset(self) }
}
pub struct TabViewer<'a> {
@@ -97,16 +119,7 @@ impl egui_dock::TabViewer for TabViewer<'_> {
type Tab = TabType;
fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) {
match tab {
TabType::Project(tab) => render_tab(self.world, ui, tab.as_mut(), &mut self.state),
TabType::Texture(tab) => render_tab(self.world, ui, tab.as_mut(), &mut self.state),
TabType::Model(tab) => render_tab(self.world, ui, tab.as_mut(), &mut self.state),
TabType::ModCon(tab) => render_tab(self.world, ui, tab.as_mut(), &mut self.state),
TabType::LightProbe(tab) => render_tab(self.world, ui, tab.as_mut(), &mut self.state),
TabType::Room(tab) => render_tab(self.world, ui, tab.as_mut(), &mut self.state),
TabType::Templates(tab) => render_tab(self.world, ui, tab.as_mut(), &mut self.state),
TabType::Splash(tab) => render_tab(self.world, ui, tab.as_mut(), &mut self.state),
}
tab.ui(self.world, ui, &mut self.state);
}
fn context_menu(
@@ -126,49 +139,9 @@ impl egui_dock::TabViewer for TabViewer<'_> {
};
}
fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText {
match tab {
TabType::Project(tab) => tab.title(),
TabType::Texture(tab) => tab.title(),
TabType::Model(tab) => tab.title(),
TabType::ModCon(tab) => tab.title(),
TabType::LightProbe(tab) => tab.title(),
TabType::Room(tab) => tab.title(),
TabType::Templates(tab) => tab.title(),
TabType::Splash(tab) => tab.title(),
}
}
fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText { tab.title() }
fn on_close(&mut self, tab: &mut Self::Tab) -> bool {
match tab {
TabType::Project(_) => true,
TabType::Texture(tab) => {
close_tab(self.world, tab.as_mut());
true
}
TabType::Model(tab) => {
close_tab(self.world, tab.as_mut());
true
}
TabType::ModCon(tab) => {
close_tab(self.world, tab.as_mut());
true
}
TabType::LightProbe(tab) => {
close_tab(self.world, tab.as_mut());
true
}
TabType::Room(tab) => {
close_tab(self.world, tab.as_mut());
true
}
TabType::Templates(_) => true,
TabType::Splash(tab) => {
close_tab(self.world, tab.as_mut());
true
}
}
}
fn on_close(&mut self, tab: &mut Self::Tab) -> bool { tab.close(self.world) }
fn add_popup(&mut self, ui: &mut egui::Ui, node: NodeIndex) {
ui.set_min_width(100.0);
@@ -176,13 +149,11 @@ impl egui_dock::TabViewer for TabViewer<'_> {
if ui.button(format!("{} Browser", icon::FILEBROWSER)).clicked() {
self.state.open_tab =
Some(OpenTab { tab: TabType::Project(Box::default()), node: Some(node) });
Some(OpenTab { tab: project::ProjectTab::new(), node: Some(node) });
}
if ui.button(format!("{} Templates", icon::EDITMODE_HLT)).clicked() {
self.state.open_tab = Some(OpenTab {
tab: TabType::Templates(Box::new(templates::TemplatesTab::new())),
node: Some(node),
});
self.state.open_tab =
Some(OpenTab { tab: templates::TemplatesTab::new(), node: Some(node) });
}
}
@@ -194,9 +165,7 @@ impl egui_dock::TabViewer for TabViewer<'_> {
}
}
fn clear_background(&self, tab: &Self::Tab) -> bool {
!matches!(tab, TabType::Model(_) | TabType::ModCon(_) | TabType::Room(_))
}
fn clear_background(&self, tab: &Self::Tab) -> bool { tab.clear_background() }
}
pub fn property_with_value(ui: &mut egui::Ui, name: &str, value: String) {
+19 -15
View File
@@ -6,7 +6,6 @@ use bevy::{
prelude::*,
render::{camera::Viewport, primitives::Aabb, view::RenderLayers},
};
use bevy_egui::EguiContext;
use bevy_mod_raycast::{Intersection, RaycastMesh, RaycastSource};
use egui::{Sense, Widget};
use retrolib::format::SumBy;
@@ -24,7 +23,7 @@ use crate::{
camera::ModelCamera, convert_transform, grid::GridSettings, model::load_model,
TemporaryLabel,
},
tabs::{model::ModelTab, SystemTab, TabState, TabType},
tabs::{model::ModelTab, EditorTabSystem, TabState},
AssetRef,
};
@@ -69,6 +68,10 @@ impl Default for ModConTab {
}
impl ModConTab {
pub fn new(asset_ref: AssetRef, handle: Handle<ModConAsset>) -> Box<Self> {
Box::new(Self { asset_ref, handle, ..default() })
}
fn get_load_state(
&self,
server: &AssetServer,
@@ -107,7 +110,7 @@ pub struct ModelLabel {
pub tab_id: Uuid,
}
impl SystemTab for ModConTab {
impl EditorTabSystem for ModConTab {
type LoadParam = (
SCommands,
SResMut<Assets<Mesh>>,
@@ -127,13 +130,13 @@ impl SystemTab for ModConTab {
SQuery<(Read<ModelLabel>, Read<Children>)>,
);
fn load(&mut self, _ctx: &mut EguiContext, query: SystemParamItem<'_, '_, Self::LoadParam>) {
fn load(&mut self, query: SystemParamItem<Self::LoadParam>) {
let (
mut commands,
mut meshes,
mut materials,
mut models,
texture_assets,
mut texture_assets,
mut images,
server,
mod_con_assets,
@@ -183,7 +186,7 @@ impl SystemTab for ModConTab {
_ => continue,
}
asset.build_texture_images(&texture_assets, &mut images);
asset.build_texture_images(&mut texture_assets, &mut images);
let result = load_model(asset, &mut meshes);
let built = match result {
Ok(value) => value,
@@ -257,19 +260,20 @@ impl SystemTab for ModConTab {
}
}
fn close(&mut self, query: SystemParamItem<'_, '_, Self::LoadParam>) {
fn close(&mut self, query: SystemParamItem<Self::LoadParam>) -> bool {
let (mut commands, _, _, _, _, _, _, _) = query;
for model in self.models.iter().flat_map(|l| &l.loaded) {
if let Some(commands) = commands.get_entity(model.entity) {
commands.despawn_recursive();
}
}
true
}
fn ui(
&mut self,
ui: &mut egui::Ui,
query: SystemParamItem<'_, '_, Self::UiParam>,
query: SystemParamItem<Self::UiParam>,
state: &mut TabState,
) {
let scale = ui.ctx().pixels_per_point();
@@ -286,7 +290,7 @@ impl SystemTab for ModConTab {
self.camera.update(&rect, &response, ui.input(|i| i.scroll_delta));
let (mut commands, server, models, mod_con_assets, intersection_query, model_query) = query;
if !self.models.iter().all(|m| !m.loaded.is_empty()) {
if self.models.is_empty() || !self.models.iter().all(|m| !m.loaded.is_empty()) {
ui.centered_and_justified(|ui| {
match self.get_load_state(&server, &mod_con_assets, &models) {
LoadState::Failed => egui::Label::new(
@@ -321,11 +325,7 @@ impl SystemTab for ModConTab {
response = response.context_menu(|ui| {
if ui.button("Open in new tab").clicked() {
let handle = server.load(format!("{}.{}", selected.id, selected.kind));
state.open_tab(TabType::Model(Box::new(ModelTab {
asset_ref: *selected,
handle,
..default()
})));
state.open_tab(ModelTab::new(*selected, handle));
ui.close_menu();
}
if ui.button("Copy GUID").clicked() {
@@ -424,9 +424,13 @@ impl SystemTab for ModConTab {
state.render_layer += 1;
}
fn title(&mut self) -> egui::WidgetText {
fn title(&self) -> egui::WidgetText {
format!("{} {} {}", icon::SCENE_DATA, self.asset_ref.kind, self.asset_ref.id).into()
}
fn id(&self) -> String { format!("{} {}", self.asset_ref.kind, self.asset_ref.id) }
fn clear_background(&self) -> bool { false }
fn asset(&self) -> Option<AssetRef> { Some(self.asset_ref) }
}
+37 -22
View File
@@ -7,8 +7,8 @@ use bevy::{
prelude::*,
render::{camera::Viewport, view::RenderLayers},
};
use bevy_egui::{EguiContext, EguiUserTextures};
use egui::{Color32, Sense, Widget};
use bevy_egui::EguiUserTextures;
use egui::Widget;
use retrolib::format::{
cmdl::{CMaterialCache, CMaterialDataInner, CMaterialTextureTokenData},
txtr::K_FORM_TXTR,
@@ -32,7 +32,7 @@ use crate::{
tabs::{
property_with_value,
texture::{TextureTab, UiTexture},
SystemTab, TabType,
EditorTabSystem,
},
AssetRef, TabState,
};
@@ -65,6 +65,10 @@ pub struct ModelTab {
}
impl ModelTab {
pub fn new(asset_ref: AssetRef, handle: Handle<ModelAsset>) -> Box<Self> {
Box::new(Self { asset_ref, handle, ..default() })
}
fn get_load_state(&self, server: &AssetServer, models: &Assets<ModelAsset>) -> LoadState {
match server.get_load_state(&self.handle) {
LoadState::Loaded => {}
@@ -79,7 +83,7 @@ impl ModelTab {
}
}
impl SystemTab for ModelTab {
impl EditorTabSystem for ModelTab {
type LoadParam = (
SCommands,
SResMut<Assets<Mesh>>,
@@ -92,13 +96,13 @@ impl SystemTab for ModelTab {
);
type UiParam = (SCommands, SRes<AssetServer>, SRes<Assets<ModelAsset>>);
fn load(&mut self, _ctx: &mut EguiContext, query: SystemParamItem<'_, '_, Self::LoadParam>) {
fn load(&mut self, query: SystemParamItem<Self::LoadParam>) {
let (
mut commands,
mut meshes,
mut materials,
mut models,
texture_assets,
mut texture_assets,
mut images,
server,
mut egui_textures,
@@ -122,7 +126,7 @@ impl SystemTab for ModelTab {
_ => return,
}
asset.build_texture_images(&texture_assets, &mut images);
asset.build_texture_images(&mut texture_assets, &mut images);
let result = load_model(asset, &mut meshes);
let built = match result {
Ok(value) => value,
@@ -152,6 +156,7 @@ impl SystemTab for ModelTab {
mesh: mesh.mesh,
material,
// transform: Transform::from_translation((-built.aabb.center).into()),
visibility: Visibility::Hidden,
..default()
})
.id();
@@ -171,16 +176,16 @@ impl SystemTab for ModelTab {
// Build egui textures
for (texture_id, texture_handle) in &asset.textures {
let texture = texture_assets.get(texture_handle).unwrap();
let ui_texture = UiTexture::new(
let ui_texture = UiTexture::from_handle(
texture.slices[0][0].clone(),
images.as_mut(),
egui_textures.as_mut(),
);
).unwrap();
self.egui_textures.insert(*texture_id, ui_texture);
}
}
fn close(&mut self, query: SystemParamItem<'_, '_, Self::LoadParam>) {
fn close(&mut self, query: SystemParamItem<Self::LoadParam>) -> bool {
let (mut commands, _, _, _, _, _, _, _) = query;
if let Some(loaded) = &self.loaded {
for mesh in &loaded.meshes {
@@ -189,12 +194,13 @@ impl SystemTab for ModelTab {
}
}
}
true
}
fn ui(
&mut self,
ui: &mut egui::Ui,
query: SystemParamItem<'_, '_, Self::UiParam>,
query: SystemParamItem<Self::UiParam>,
state: &mut TabState,
) {
let scale = ui.ctx().pixels_per_point();
@@ -207,7 +213,7 @@ impl SystemTab for ModelTab {
depth: 0.0..1.0,
};
let response =
ui.interact(rect, ui.make_persistent_id("background"), Sense::click_and_drag());
ui.interact(rect, ui.make_persistent_id("background"), egui::Sense::click_and_drag());
self.camera.update(&rect, &response, ui.input(|i| i.scroll_delta));
let (mut commands, server, models) = query;
@@ -275,10 +281,16 @@ impl SystemTab for ModelTab {
),
);
if !matches!(mesh.unk_c, 0 | 1) {
ui.colored_label(Color32::RED, format!("(unk_c: {})", mesh.unk_c));
ui.colored_label(
egui::Color32::RED,
format!("(unk_c: {})", mesh.unk_c),
);
}
if mesh.unk_e != 64 {
ui.colored_label(Color32::RED, format!("(unk_e: {})", mesh.unk_e));
ui.colored_label(
egui::Color32::RED,
format!("(unk_e: {})", mesh.unk_e),
);
}
if ui
.small_button(format!("{}", icon::MATERIAL_DATA))
@@ -299,7 +311,7 @@ impl SystemTab for ModelTab {
});
if let Some(material_idx) = self.selected_material {
ui.push_id(format!("material_{}", material_idx), |ui| {
egui::Frame::group(ui.style()).fill(Color32::from_black_alpha(200)).show(
egui::Frame::group(ui.style()).fill(egui::Color32::from_black_alpha(200)).show(
ui,
|ui| {
egui::ScrollArea::vertical()
@@ -338,11 +350,15 @@ impl SystemTab for ModelTab {
}
}
fn title(&mut self) -> egui::WidgetText {
fn title(&self) -> egui::WidgetText {
format!("{} {} {}", icon::FILE_3D, self.asset_ref.kind, self.asset_ref.id).into()
}
fn id(&self) -> String { format!("{} {}", self.asset_ref.kind, self.asset_ref.id) }
fn clear_background(&self) -> bool { false }
fn asset(&self) -> Option<AssetRef> { Some(self.asset_ref) }
}
fn texture_ui(
@@ -356,16 +372,15 @@ fn texture_ui(
if let Some(ui_texture) = textures.get(&texture.id) {
if ui_texture
.image_scaled(200.0)
.sense(Sense::click())
.sense(egui::Sense::click())
.ui(ui)
.on_hover_cursor(egui::CursorIcon::PointingHand)
.clicked()
{
state.open_tab(TabType::Texture(Box::new(TextureTab {
asset_ref: AssetRef { id: texture.id, kind: K_FORM_TXTR },
handle: server.load(format!("{}.{}", texture.id, K_FORM_TXTR)),
..default()
})));
state.open_tab(TextureTab::new(
AssetRef { id: texture.id, kind: K_FORM_TXTR },
server.load(format!("{}.{}", texture.id, K_FORM_TXTR)),
));
}
}
if let Some(usage) = &texture.usage {
+17 -47
View File
@@ -1,10 +1,10 @@
use bevy::{
asset::LoadState,
asset::{AssetPath, LoadState},
ecs::system::{lifetimeless::*, *},
prelude::*,
render::render_resource::Extent3d,
};
use bevy_egui::{EguiContext, EguiUserTextures};
use bevy_egui::EguiUserTextures;
use egui::{text::LayoutJob, Color32, TextFormat, Widget};
use retrolib::format::{
cmdl::{K_FORM_CMDL, K_FORM_SMDL, K_FORM_WMDL},
@@ -16,10 +16,10 @@ use retrolib::format::{
use crate::{
icon,
loaders::{model::ModelAsset, package::PackageDirectory, texture::TextureAsset},
loaders::{package::PackageDirectory, texture::TextureAsset},
tabs::{
lightprobe::LightProbeTab, modcon::ModConTab, model::ModelTab, room::RoomTab,
texture::TextureTab, SystemTab, TabState, TabType,
texture::TextureTab, EditorTabSystem, TabState,
},
AssetRef,
};
@@ -91,17 +91,16 @@ impl ProjectTab {
}
}
impl SystemTab for ProjectTab {
impl EditorTabSystem for ProjectTab {
type LoadParam = (
SRes<AssetServer>,
SRes<Assets<TextureAsset>>,
SResMut<Assets<Image>>,
SResMut<EguiUserTextures>,
);
type UiParam = (SRes<AssetServer>, SRes<Assets<PackageDirectory>>);
fn load(&mut self, _ctx: &mut EguiContext, query: SystemParamItem<'_, '_, Self::LoadParam>) {
let (server, textures, mut images, mut egui_textures) = query;
fn load(&mut self, query: SystemParamItem<Self::LoadParam>) {
let (server, textures, mut egui_textures) = query;
if let HoverState::Loading { asset, handle } = &self.hover_state {
if asset.kind != K_FORM_TXTR {
return;
@@ -109,12 +108,11 @@ impl SystemTab for ProjectTab {
if server.get_load_state(handle) == LoadState::Loaded {
let texture_handle = handle.clone().typed::<TextureAsset>();
let asset = textures.get(&texture_handle).unwrap();
if let Some(image) = asset.slices.first().and_then(|v| v.first()) {
let image_handle = images.add(image.clone());
if let Some(image_handle) = asset.slices.first().and_then(|v| v.first()) {
let texture_id = egui_textures.add_image(image_handle.clone_weak());
self.hover_state = HoverState::Texture {
_handle: texture_handle,
_image: image_handle,
_image: image_handle.clone(),
size: Extent3d {
width: asset.inner.head.width,
height: asset.inner.head.height,
@@ -134,7 +132,7 @@ impl SystemTab for ProjectTab {
fn ui(
&mut self,
ui: &mut egui::Ui,
query: SystemParamItem<'_, '_, Self::UiParam>,
query: SystemParamItem<Self::UiParam>,
state: &mut TabState,
) {
let (server, packages) = query;
@@ -215,50 +213,22 @@ impl SystemTab for ProjectTab {
});
}
if response.clicked() {
let path: AssetPath = format!("{}.{}", entry.id, entry.kind).into();
match entry.kind {
K_FORM_TXTR => {
let handle = server.load::<TextureAsset, _>(format!(
"{}.{}",
entry.id, entry.kind
));
state.open_tab(TabType::Texture(Box::new(TextureTab {
asset_ref,
handle,
..default()
})));
state.open_tab(TextureTab::new(asset_ref, server.load(path)));
}
K_FORM_CMDL | K_FORM_SMDL | K_FORM_WMDL => {
let handle = server
.load::<ModelAsset, _>(format!("{}.{}", entry.id, entry.kind));
state.open_tab(TabType::Model(Box::new(ModelTab {
asset_ref,
handle,
..default()
})));
state.open_tab(ModelTab::new(asset_ref, server.load(path)));
}
K_FORM_MCON => {
let handle = server.load(format!("{}.{}", entry.id, entry.kind));
state.open_tab(TabType::ModCon(Box::new(ModConTab {
asset_ref,
handle,
..default()
})));
state.open_tab(ModConTab::new(asset_ref, server.load(path)));
}
K_FORM_LTPB => {
let handle = server.load(format!("{}.{}", entry.id, entry.kind));
state.open_tab(TabType::LightProbe(Box::new(LightProbeTab {
asset_ref,
handle,
..default()
})));
state.open_tab(LightProbeTab::new(asset_ref, server.load(path)));
}
K_FORM_ROOM => {
let handle = server.load(format!("{}.{}", entry.id, entry.kind));
state.open_tab(TabType::Room(Box::new(RoomTab {
asset_ref,
handle,
..default()
})));
state.open_tab(RoomTab::new(asset_ref, server.load(path)));
}
_ => {}
}
@@ -268,7 +238,7 @@ impl SystemTab for ProjectTab {
}
}
fn title(&mut self) -> egui::WidgetText { format!("{} Browser", icon::FILEBROWSER).into() }
fn title(&self) -> egui::WidgetText { format!("{} Browser", icon::FILEBROWSER).into() }
fn id(&self) -> String { "project".to_string() }
}
+45 -37
View File
@@ -4,7 +4,6 @@ use bevy::{
prelude::*,
render::{camera::Viewport, view::RenderLayers},
};
use bevy_egui::EguiContext;
use bevy_mod_raycast::{Intersection, RaycastSource};
use egui::Sense;
use retrolib::format::room::ConstructedPropertyValue;
@@ -14,7 +13,7 @@ use crate::{
loaders::{model::ModelAsset, room::RoomAsset, texture::TextureAsset},
material::CustomMaterial,
render::{camera::ModelCamera, grid::GridSettings, TemporaryLabel},
tabs::{modcon::ModelLabel, property_with_value, SystemTab, TabState},
tabs::{modcon::ModelLabel, property_with_value, EditorTabSystem, TabState},
AssetRef,
};
@@ -28,40 +27,44 @@ impl Default for RoomTab {
fn default() -> Self { Self { asset_ref: default(), handle: default(), camera: default() } }
}
// impl RoomTab {
// fn get_load_state(
// &self,
// server: &AssetServer,
// assets: &Assets<RoomAsset>,
// models: &Assets<ModelAsset>,
// ) -> LoadState {
// match server.get_load_state(&self.handle) {
// LoadState::Loaded => {}
// state => return state,
// };
// let asset = match assets.get(&self.handle) {
// Some(v) => v,
// None => return LoadState::Failed,
// };
// // Ensure all dependencies loaded
// match server.get_group_load_state(asset.models.iter().map(|h| h.id())) {
// LoadState::Loaded => {}
// state => return state,
// }
// for model in &asset.models {
// let model = models.get(model).unwrap();
// match model.get_load_state(server) {
// LoadState::Loaded => {}
// state => return state,
// }
// }
// LoadState::Loaded
// }
// }
impl RoomTab {
pub fn new(asset_ref: AssetRef, handle: Handle<RoomAsset>) -> Box<Self> {
Box::new(Self { asset_ref, handle, ..default() })
}
// fn get_load_state(
// &self,
// server: &AssetServer,
// assets: &Assets<RoomAsset>,
// models: &Assets<ModelAsset>,
// ) -> LoadState {
// match server.get_load_state(&self.handle) {
// LoadState::Loaded => {}
// state => return state,
// };
// let asset = match assets.get(&self.handle) {
// Some(v) => v,
// None => return LoadState::Failed,
// };
// // Ensure all dependencies loaded
// match server.get_group_load_state(asset.models.iter().map(|h| h.id())) {
// LoadState::Loaded => {}
// state => return state,
// }
// for model in &asset.models {
// let model = models.get(model).unwrap();
// match model.get_load_state(server) {
// LoadState::Loaded => {}
// state => return state,
// }
// }
// LoadState::Loaded
// }
}
pub struct RoomRaycastSet;
impl SystemTab for RoomTab {
impl EditorTabSystem for RoomTab {
type LoadParam = (
SCommands,
SResMut<Assets<Mesh>>,
@@ -81,7 +84,7 @@ impl SystemTab for RoomTab {
SQuery<Read<ModelLabel>>,
);
fn load(&mut self, _ctx: &mut EguiContext, query: SystemParamItem<'_, '_, Self::LoadParam>) {
fn load(&mut self, query: SystemParamItem<Self::LoadParam>) {
let (
_commands,
_meshes,
@@ -94,19 +97,20 @@ impl SystemTab for RoomTab {
) = query;
}
fn close(&mut self, query: SystemParamItem<'_, '_, Self::LoadParam>) {
fn close(&mut self, query: SystemParamItem<Self::LoadParam>) -> bool {
let (_commands, _, _, _, _, _, _, _) = query;
// for model in self.models.iter().flat_map(|l| &l.loaded) {
// if let Some(commands) = commands.get_entity(model.entity) {
// commands.despawn_recursive();
// }
// }
true
}
fn ui(
&mut self,
ui: &mut egui::Ui,
query: SystemParamItem<'_, '_, Self::UiParam>,
query: SystemParamItem<Self::UiParam>,
state: &mut TabState,
) {
let scale = ui.ctx().pixels_per_point();
@@ -313,11 +317,15 @@ impl SystemTab for RoomTab {
state.render_layer += 1;
}
fn title(&mut self) -> egui::WidgetText {
fn title(&self) -> egui::WidgetText {
format!("{} {} {}", icon::SCENE_DATA, self.asset_ref.kind, self.asset_ref.id).into()
}
fn id(&self) -> String { format!("{} {}", self.asset_ref.kind, self.asset_ref.id) }
fn clear_background(&self) -> bool { false }
fn asset(&self) -> Option<AssetRef> { Some(self.asset_ref) }
}
fn property_ui(ui: &mut egui::Ui, property: &ConstructedPropertyValue) {
+6 -6
View File
@@ -2,12 +2,12 @@ use bevy::{
ecs::system::{lifetimeless::*, SystemParamItem},
prelude::*,
};
use bevy_egui::{EguiContext, EguiUserTextures};
use bevy_egui::EguiUserTextures;
use egui::Widget;
use crate::{
icon,
tabs::{texture::UiTexture, SystemTab, TabState},
tabs::{texture::UiTexture, EditorTabSystem, TabState},
};
#[derive(Default)]
@@ -16,11 +16,11 @@ pub struct SplashTab {
pub icon_image: Option<Handle<Image>>,
}
impl SystemTab for SplashTab {
impl EditorTabSystem for SplashTab {
type LoadParam = (SRes<AssetServer>, SResMut<Assets<Image>>, SResMut<EguiUserTextures>);
type UiParam = ();
fn load(&mut self, _ctx: &mut EguiContext, query: SystemParamItem<'_, '_, Self::LoadParam>) {
fn load(&mut self, query: SystemParamItem<Self::LoadParam>) {
if self.icon.is_some() {
return;
}
@@ -41,7 +41,7 @@ impl SystemTab for SplashTab {
fn ui(
&mut self,
ui: &mut egui::Ui,
_query: SystemParamItem<'_, '_, Self::UiParam>,
_query: SystemParamItem<Self::UiParam>,
_state: &mut TabState,
) {
let icon = match &self.icon {
@@ -68,7 +68,7 @@ impl SystemTab for SplashTab {
});
}
fn title(&mut self) -> egui::WidgetText { format!("{} Splash", icon::HOME).into() }
fn title(&self) -> egui::WidgetText { format!("{} Splash", icon::HOME).into() }
fn id(&self) -> String { "splash".into() }
}
+7 -7
View File
@@ -15,7 +15,7 @@ use strum::{EnumMessage, IntoEnumIterator};
use crate::{
icon,
tabs::{SystemTab, TabState},
tabs::{EditorTabSystem, TabState},
};
pub struct TemplatesTab {
@@ -24,25 +24,25 @@ pub struct TemplatesTab {
}
impl TemplatesTab {
pub fn new() -> Self {
Self {
pub fn new() -> Box<Self> {
Box::new(Self {
current: load_type_template(include_str!(
"../../../lib/templates/mp1r/objects/Render.json"
))
.unwrap(),
editing_key: None,
}
})
}
}
impl SystemTab for TemplatesTab {
impl EditorTabSystem for TemplatesTab {
type LoadParam = ();
type UiParam = ();
fn ui(
&mut self,
ui: &mut egui::Ui,
_query: SystemParamItem<'_, '_, Self::UiParam>,
_query: SystemParamItem<Self::UiParam>,
_state: &mut TabState,
) {
egui::TextEdit::singleline(&mut self.current.name).hint_text("Name").ui(ui);
@@ -173,7 +173,7 @@ impl SystemTab for TemplatesTab {
}
}
fn title(&mut self) -> egui::WidgetText { format!("{} Templates", icon::EDITMODE_HLT).into() }
fn title(&self) -> egui::WidgetText { format!("{} Templates", icon::EDITMODE_HLT).into() }
fn id(&self) -> String { "Templates".into() }
}
+26 -27
View File
@@ -3,11 +3,11 @@ use bevy::{
ecs::system::{lifetimeless::*, *},
prelude::*,
};
use bevy_egui::{EguiContext, EguiUserTextures};
use bevy_egui::EguiUserTextures;
use egui::Widget;
use retrolib::format::txtr::ETextureType;
use crate::{icon, loaders::texture::TextureAsset, tabs::SystemTab, AssetRef, TabState};
use crate::{icon, loaders::texture::TextureAsset, tabs::EditorTabSystem, AssetRef, TabState};
pub struct LoadedTexture {
pub width: u32,
@@ -15,6 +15,7 @@ pub struct LoadedTexture {
pub texture_ids: Vec<egui::TextureId>,
}
#[derive(Default)]
pub struct TextureTab {
pub asset_ref: AssetRef,
pub handle: Handle<TextureAsset>,
@@ -23,15 +24,9 @@ pub struct TextureTab {
pub v_flip: bool,
}
impl Default for TextureTab {
fn default() -> Self {
Self {
asset_ref: default(),
handle: default(),
loaded_textures: default(),
selected_mip: 0,
v_flip: false,
}
impl TextureTab {
pub fn new(asset_ref: AssetRef, handle: Handle<TextureAsset>) -> Box<Self> {
Box::new(Self { asset_ref, handle, ..default() })
}
}
@@ -43,22 +38,23 @@ pub struct UiTexture {
}
impl UiTexture {
#[allow(dead_code)]
pub fn new(
image: Image,
images: &mut Assets<Image>,
egui_textures: &mut EguiUserTextures,
textures: &mut EguiUserTextures,
) -> Self {
let width = image.texture_descriptor.size.width;
let height = image.texture_descriptor.size.height;
let handle = images.add(image);
let weak_handle = handle.clone_weak();
Self { _image: handle, texture_id: egui_textures.add_image(weak_handle), width, height }
Self { _image: handle, texture_id: textures.add_image(weak_handle), width, height }
}
pub fn from_handle(
handle: Handle<Image>,
images: &mut Assets<Image>,
egui_textures: &mut EguiUserTextures,
textures: &mut EguiUserTextures,
) -> Option<Self> {
let Some(image) = images.get(&handle) else { return None; };
let width = image.texture_descriptor.size.width;
@@ -66,7 +62,7 @@ impl UiTexture {
let weak_handle = handle.clone_weak();
Some(Self {
_image: handle,
texture_id: egui_textures.add_image(weak_handle),
texture_id: textures.add_image(weak_handle),
width,
height,
})
@@ -89,26 +85,29 @@ impl UiTexture {
}
}
impl SystemTab for TextureTab {
impl EditorTabSystem for TextureTab {
type LoadParam =
(SRes<Assets<TextureAsset>>, SResMut<Assets<Image>>, SResMut<EguiUserTextures>);
type UiParam = (SRes<AssetServer>, SRes<Assets<TextureAsset>>);
fn load(&mut self, _ctx: &mut EguiContext, query: SystemParamItem<'_, '_, Self::LoadParam>) {
fn load(&mut self, query: SystemParamItem<Self::LoadParam>) {
if !self.loaded_textures.is_empty() {
return;
}
let (textures, mut images, mut egui_textures) = query;
let (textures, images, mut egui_textures) = query;
let Some(asset) = textures.get(&self.handle) else { return; };
self.loaded_textures.reserve_exact(asset.slices.len());
for mip in &asset.slices {
let mut texture_ids = Vec::with_capacity(mip.len());
for image in mip {
let handle = images.add(image.clone());
texture_ids.push(egui_textures.add_image(handle));
texture_ids.push(egui_textures.add_image(image.clone_weak()));
}
let size = mip.first().map(|m| m.texture_descriptor.size).unwrap_or_default();
let size = mip
.first()
.and_then(|h| images.get(h))
.map(|m| m.texture_descriptor.size)
.unwrap_or_default();
self.loaded_textures.push(LoadedTexture {
texture_ids,
width: size.width,
@@ -120,7 +119,7 @@ impl SystemTab for TextureTab {
fn ui(
&mut self,
ui: &mut egui::Ui,
query: SystemParamItem<'_, '_, Self::UiParam>,
query: SystemParamItem<Self::UiParam>,
_state: &mut TabState,
) {
let (server, textures) = query;
@@ -128,10 +127,7 @@ impl SystemTab for TextureTab {
ui.label(format!("{} {}", self.asset_ref.kind, self.asset_ref.id));
match server.get_load_state(&self.handle) {
LoadState::NotLoaded => {
return;
}
LoadState::Loading => {
LoadState::NotLoaded | LoadState::Loading => {
ui.spinner();
return;
}
@@ -141,6 +137,7 @@ impl SystemTab for TextureTab {
return;
}
LoadState::Unloaded => {
ui.colored_label(egui::Color32::RED, "Unloaded");
return;
}
};
@@ -198,9 +195,11 @@ impl SystemTab for TextureTab {
}
}
fn title(&mut self) -> egui::WidgetText {
fn title(&self) -> egui::WidgetText {
format!("{} {} {}", icon::TEXTURE, self.asset_ref.kind, self.asset_ref.id).into()
}
fn id(&self) -> String { format!("{} {}", self.asset_ref.kind, self.asset_ref.id) }
fn asset(&self) -> Option<AssetRef> { Some(self.asset_ref) }
}