mirror of
https://github.com/PrimeDecomp/retrotool.git
synced 2026-07-12 18:18:58 -07:00
Rework tab system & improve texture load
This commit is contained in:
@@ -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
@@ -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::*;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user