From 32ed510fcb8fb8be27dbd58daab6f6475616c66f Mon Sep 17 00:00:00 2001 From: Luke Street Date: Thu, 2 Mar 2023 00:38:21 -0500 Subject: [PATCH] WIP CMDL viewer & improvements --- Cargo.lock | 4 + lib/Cargo.toml | 1 + lib/src/format/cmdl.rs | 21 +- lib/src/format/mod.rs | 1 + lib/src/format/mtrl.rs | 43 ++ retrotool-gui/Cargo.toml | 3 + retrotool-gui/icon.ttf | Bin 0 -> 177264 bytes retrotool-gui/src/icon.rs | 668 ++++++++++++++++++++++++++ retrotool-gui/src/loaders.rs | 113 ++++- retrotool-gui/src/main.rs | 492 ++++---------------- retrotool-gui/src/tabs/mod.rs | 109 +++++ retrotool-gui/src/tabs/model.rs | 750 ++++++++++++++++++++++++++++++ retrotool-gui/src/tabs/project.rs | 98 ++++ retrotool-gui/src/tabs/texture.rs | 226 +++++++++ retrotool/Cargo.toml | 4 +- retrotool/src/cmd/cmdl.rs | 5 +- 16 files changed, 2129 insertions(+), 409 deletions(-) create mode 100644 lib/src/format/mtrl.rs create mode 100644 retrotool-gui/icon.ttf create mode 100644 retrotool-gui/src/icon.rs create mode 100644 retrotool-gui/src/tabs/mod.rs create mode 100644 retrotool-gui/src/tabs/model.rs create mode 100644 retrotool-gui/src/tabs/project.rs create mode 100644 retrotool-gui/src/tabs/texture.rs diff --git a/Cargo.lock b/Cargo.lock index 7e97207..cd9e5d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3055,6 +3055,7 @@ dependencies = [ "binrw", "binrw_derive", "ddsfile", + "flate2", "log", "memmap2", "tegra_swizzle", @@ -3090,10 +3091,13 @@ dependencies = [ "bevy", "bevy_egui", "binrw", + "bytemuck", "egui", "egui_dock", + "half", "image", "log", + "num-traits", "retrolib", "uuid", "walkdir", diff --git a/lib/Cargo.toml b/lib/Cargo.toml index e5c61e4..fbf0399 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -14,6 +14,7 @@ anyhow = "1.0.69" binrw = "0.11.1" binrw_derive = "0.11.1" ddsfile = { git = "https://github.com/encounter/ddsfile", rev = "880f04c1dffa680eab0e9e09cfa58591fe186a31" } +flate2 = "1.0.25" log = "0.4.17" memmap2 = "0.5.9" tegra_swizzle = "0.3.0" diff --git a/lib/src/format/cmdl.rs b/lib/src/format/cmdl.rs index 736e189..966ded3 100644 --- a/lib/src/format/cmdl.rs +++ b/lib/src/format/cmdl.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, io::Cursor}; +use std::io::Cursor; use anyhow::{bail, ensure, Result}; use binrw::{binrw, BinReaderExt, Endian}; @@ -745,11 +745,11 @@ pub enum EMaterialFlag { MFC4 = 26, } -fn decompress_gpu_buffers<'a>( - file_data: &'a [u8], +fn decompress_gpu_buffers( + file_data: &[u8], read_info: &[SModelReadBufferInfo], buffer_info: &[SModelBufferInfo], -) -> Result>> { +) -> Result>> { let mut out = Vec::with_capacity(buffer_info.len()); for info in buffer_info { let read_info = &read_info[info.read_index as usize]; @@ -757,23 +757,24 @@ fn decompress_gpu_buffers<'a>( &file_data[read_info.offset as usize..(read_info.offset + read_info.size) as usize]; let comp_buf = &read_buffer[info.offset as usize..(info.offset + info.size) as usize]; let (_, buf) = decompress_buffer(comp_buf, info.dest_size as u64)?; - out.push(buf); + out.push(buf.into_owned()); } Ok(out) } -pub struct ModelData<'a> { +#[derive(Debug, Clone)] +pub struct ModelData { pub head: SModelHeader, pub mtrl: SMaterialChunk, pub mesh: SMeshLoadInformation, pub vbuf: SVertexBufferInfoSection, pub ibuf: SIndexBufferInfoSection, - pub vtx_buffers: Vec>, - pub idx_buffers: Vec>, + pub vtx_buffers: Vec>, + pub idx_buffers: Vec>, } -impl ModelData<'_> { - pub fn slice<'a>(data: &'a [u8], meta: &[u8], e: Endian) -> Result> { +impl ModelData { + pub fn slice(data: &[u8], meta: &[u8], e: Endian) -> Result { let (cmdl_desc, mut cmdl_data, _) = FormDescriptor::slice(data, Endian::Little)?; ensure!(cmdl_desc.id == K_FORM_CMDL); ensure!(cmdl_desc.version_a == 114); diff --git a/lib/src/format/mod.rs b/lib/src/format/mod.rs index 663aab5..ec1eca6 100644 --- a/lib/src/format/mod.rs +++ b/lib/src/format/mod.rs @@ -1,6 +1,7 @@ pub mod chunk; pub mod cmdl; pub mod foot; +pub mod mtrl; pub mod pack; pub mod rfrm; pub mod txtr; diff --git a/lib/src/format/mtrl.rs b/lib/src/format/mtrl.rs new file mode 100644 index 0000000..b9cacf9 --- /dev/null +++ b/lib/src/format/mtrl.rs @@ -0,0 +1,43 @@ +use std::io::{Cursor, Read}; + +use anyhow::{ensure, Result}; +use binrw::{binrw, BinReaderExt, Endian}; +use flate2::bufread::ZlibDecoder; + +use crate::format::{rfrm::FormDescriptor, FourCC}; + +// Texture +pub const K_FORM_MTRL: FourCC = FourCC(*b"MTRL"); + +#[binrw] +#[derive(Clone, Debug)] +struct SMaterialMetaData { + unk1: u32, // count? + unk2: u32, // reader version? + compressed_size: u32, + decompressed_size: u32, + file_offset: u32, +} + +#[derive(Debug, Clone)] +pub struct MaterialData { + pub decompressed: Vec, +} + +impl MaterialData { + pub fn slice(data: &[u8], meta: &[u8], e: Endian) -> Result { + let (mtrl_desc, _, _) = FormDescriptor::slice(data, Endian::Little)?; + ensure!(mtrl_desc.id == K_FORM_MTRL); + ensure!(mtrl_desc.version_a == 168); + ensure!(mtrl_desc.version_b == 168); + + let meta: SMaterialMetaData = Cursor::new(meta).read_type(e)?; + let mut reader = ZlibDecoder::new( + &data[meta.file_offset as usize..(meta.file_offset + meta.compressed_size) as usize], + ); + let mut decompressed = vec![0u8; meta.decompressed_size as usize]; + reader.read_exact(&mut decompressed)?; + + Ok(MaterialData { decompressed }) + } +} diff --git a/retrotool-gui/Cargo.toml b/retrotool-gui/Cargo.toml index 959a3c3..22b1748 100644 --- a/retrotool-gui/Cargo.toml +++ b/retrotool-gui/Cargo.toml @@ -19,10 +19,13 @@ astc-decode = "0.3.1" bevy = "0.9.1" bevy_egui = { git = "https://github.com/mvlabat/bevy_egui", rev = "65f37508339e3a1abe54ae901dad7f49e820cd41" } binrw = "0.11.1" +bytemuck = "1.13.0" egui = "0.21.0" egui_dock = "0.4.0" +half = "2.2.1" image = "0.24.5" log = "0.4.17" +num-traits = "0.2.15" retrolib = { path = "../lib" } uuid = "1.3.0" walkdir = "2.3.2" diff --git a/retrotool-gui/icon.ttf b/retrotool-gui/icon.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a688d27dfeac8fdec56276ee7e29ba397a7eb31f GIT binary patch literal 177264 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 literal 0 HcmV?d00001 diff --git a/retrotool-gui/src/icon.rs b/retrotool-gui/src/icon.rs new file mode 100644 index 0000000..b29405d --- /dev/null +++ b/retrotool-gui/src/icon.rs @@ -0,0 +1,668 @@ +#![allow(dead_code)] + +pub const ACTION: char = '\u{e900}'; +pub const ACTION_TWEAK: char = '\u{e901}'; +pub const ADD: char = '\u{e902}'; +pub const ALIASED: char = '\u{e903}'; +pub const ALIGN_BOTTOM: char = '\u{e904}'; +pub const ALIGN_CENTER: char = '\u{e905}'; +pub const ALIGN_FLUSH: char = '\u{e906}'; +pub const ALIGN_JUSTIFY: char = '\u{e907}'; +pub const ALIGN_LEFT: char = '\u{e908}'; +pub const ALIGN_MIDDLE: char = '\u{e909}'; +pub const ALIGN_RIGHT: char = '\u{e90a}'; +pub const ALIGN_TOP: char = '\u{e90b}'; +pub const ANCHOR_BOTTOM: char = '\u{e90c}'; +pub const ANCHOR_CENTER: char = '\u{e90d}'; +pub const ANCHOR_LEFT: char = '\u{e90e}'; +pub const ANCHOR_RIGHT: char = '\u{e90f}'; +pub const ANCHOR_TOP: char = '\u{e910}'; +pub const ANIM: char = '\u{e911}'; +pub const ANIM_DATA: char = '\u{e912}'; +pub const ANTIALIASED: char = '\u{e913}'; +pub const APPEND_BLEND: char = '\u{e914}'; +pub const ARMATURE_DATA: char = '\u{e915}'; +pub const ARROW_LEFTRIGHT: char = '\u{e916}'; +pub const ASSET_MANAGER: char = '\u{e917}'; +pub const AUTO: char = '\u{e918}'; +pub const AUTOMERGE_OFF: char = '\u{e919}'; +pub const AUTOMERGE_ON: char = '\u{e91a}'; +pub const AXIS_FRONT: char = '\u{e91b}'; +pub const AXIS_SIDE: char = '\u{e91c}'; +pub const AXIS_TOP: char = '\u{e91d}'; +pub const BACK: char = '\u{e91e}'; +pub const BLENDER: char = '\u{e91f}'; +pub const BOIDS: char = '\u{e920}'; +pub const BOLD: char = '\u{e921}'; +pub const BONE_DATA: char = '\u{e922}'; +pub const BOOKMARKS: char = '\u{e923}'; +pub const BORDERMOVE: char = '\u{e924}'; +pub const BRUSH_DATA: char = '\u{e925}'; +pub const BRUSHES_ALL: char = '\u{e926}'; +pub const CAMERA_DATA: char = '\u{e927}'; +pub const CAMERA_STEREO: char = '\u{e928}'; +pub const CANCEL: char = '\u{e929}'; +pub const CENTER_ONLY: char = '\u{e92a}'; +pub const CHECKBOX_DEHLT: char = '\u{e92b}'; +pub const CHECKBOX_HLT: char = '\u{e92c}'; +pub const CHECKMARK: char = '\u{e92d}'; +pub const CLIP_UV_DEHLT: char = '\u{e92e}'; +pub const CLIP_UV_HLT: char = '\u{e92f}'; +pub const COLLAPSEMENU: char = '\u{e930}'; +pub const COLLECTION_NEW: char = '\u{e931}'; +pub const COLOR: char = '\u{e932}'; +pub const COLOR_ALPHA: char = '\u{e933}'; +pub const COLOR_BLUE: char = '\u{e934}'; +pub const COLOR_GREEN: char = '\u{e935}'; +pub const COLOR_RED: char = '\u{e936}'; +pub const COMMUNITY: char = '\u{e937}'; +pub const CON_ACTION: char = '\u{e938}'; +pub const CON_ARMATURE: char = '\u{e939}'; +pub const CON_CAMERASOLVER: char = '\u{e93a}'; +pub const CON_CHILDOF: char = '\u{e93b}'; +pub const CON_CLAMPTO: char = '\u{e93c}'; +pub const CON_DISTLIMIT: char = '\u{e93d}'; +pub const CONE: char = '\u{e93e}'; +pub const CON_FLOOR: char = '\u{e93f}'; +pub const CON_FOLLOWPATH: char = '\u{e940}'; +pub const CON_FOLLOWTRACK: char = '\u{e941}'; +pub const CON_KINEMATIC: char = '\u{e942}'; +pub const CON_LOCKTRACK: char = '\u{e943}'; +pub const CON_LOCLIKE: char = '\u{e944}'; +pub const CON_LOCLIMIT: char = '\u{e945}'; +pub const CON_OBJECTSOLVER: char = '\u{e946}'; +pub const CON_PIVOT: char = '\u{e947}'; +pub const CON_ROTLIKE: char = '\u{e948}'; +pub const CON_ROTLIMIT: char = '\u{e949}'; +pub const CON_SAMEVOL: char = '\u{e94a}'; +pub const CON_SHRINKWRAP: char = '\u{e94b}'; +pub const CON_SIZELIKE: char = '\u{e94c}'; +pub const CON_SIZELIMIT: char = '\u{e94d}'; +pub const CONSOLE: char = '\u{e94e}'; +pub const CON_SPLINEIK: char = '\u{e94f}'; +pub const CONSTRAINT: char = '\u{e950}'; +pub const CONSTRAINT_BONE: char = '\u{e951}'; +pub const CON_STRETCHTO: char = '\u{e952}'; +pub const CON_TRACKTO: char = '\u{e953}'; +pub const CON_TRANSFORM: char = '\u{e954}'; +pub const CON_TRANSFORM_CACHE: char = '\u{e955}'; +pub const CON_TRANSLIKE: char = '\u{e956}'; +pub const COPY_DOWN: char = '\u{e957}'; +pub const COPY_ID: char = '\u{e958}'; +pub const CUBE: char = '\u{e959}'; +pub const CURSOR: char = '\u{e95a}'; +pub const CURVE_BEZCIRCLE: char = '\u{e95b}'; +pub const CURVE_BEZCURVE: char = '\u{e95c}'; +pub const CURVE_DATA: char = '\u{e95d}'; +pub const CURVE_NCIRCLE: char = '\u{e95e}'; +pub const CURVE_NCURVE: char = '\u{e95f}'; +pub const CURVE_PATH: char = '\u{e960}'; +pub const DECORATE: char = '\u{e961}'; +pub const DECORATE_ANIMATE: char = '\u{e962}'; +pub const DECORATE_DRIVER: char = '\u{e963}'; +pub const DECORATE_KEYFRAME: char = '\u{e964}'; +pub const DECORATE_LIBRARY_OVERRIDE: char = '\u{e965}'; +pub const DECORATE_LINKED: char = '\u{e966}'; +pub const DECORATE_LOCKED: char = '\u{e967}'; +pub const DECORATE_OVERRIDE: char = '\u{e968}'; +pub const DECORATE_UNLOCKED: char = '\u{e969}'; +pub const DESKTOP: char = '\u{e96a}'; +pub const DISCLOSURE_TRI_DOWN: char = '\u{e96b}'; +pub const DISCLOSURE_TRI_RIGHT: char = '\u{e96c}'; +pub const DISK: char = '\u{e96d}'; +pub const DISK_DRIVE: char = '\u{e96e}'; +pub const DOCUMENTS: char = '\u{e96f}'; +pub const DOT: char = '\u{e970}'; +pub const DOWNARROW_HLT: char = '\u{e971}'; +pub const DRIVER: char = '\u{e972}'; +pub const DRIVER_DISTANCE: char = '\u{e973}'; +pub const DRIVER_ROTATIONAL_DIFFERENCE: char = '\u{e974}'; +pub const DRIVER_TRANSFORM: char = '\u{e975}'; +pub const DUPLICATE: char = '\u{e976}'; +pub const EDGE_SELECT: char = '\u{e977}'; +pub const EDITMODE_HLT: char = '\u{e978}'; +pub const EMPTY_ARROWS: char = '\u{e979}'; +pub const EMPTY_AXIS: char = '\u{e97a}'; +pub const EMPTY_DATA: char = '\u{e97b}'; +pub const EMPTY_SINGLE_ARROW: char = '\u{e97c}'; +pub const ERROR: char = '\u{e97d}'; +pub const EXPERIMENTAL: char = '\u{e97e}'; +pub const EXTERNAL_DRIVE: char = '\u{e97f}'; +pub const EYEDROPPER: char = '\u{e980}'; +pub const FACE_MAPS: char = '\u{e981}'; +pub const FACE_SELECT: char = '\u{e982}'; +pub const FAKE_USER_OFF: char = '\u{e983}'; +pub const FAKE_USER_ON: char = '\u{e984}'; +pub const FCURVE: char = '\u{e985}'; +pub const FCURVE_SNAPSHOT: char = '\u{e986}'; +pub const FF: char = '\u{e987}'; +pub const FILE: char = '\u{e988}'; +pub const FILE_3D: char = '\u{e989}'; +pub const FILE_ARCHIVE: char = '\u{e98a}'; +pub const FILE_BACKUP: char = '\u{e98b}'; +pub const FILE_BLANK: char = '\u{e98c}'; +pub const FILE_BLEND: char = '\u{e98d}'; +pub const FILEBROWSER: char = '\u{e98e}'; +pub const FILE_CACHE: char = '\u{e98f}'; +pub const FILE_FOLDER: char = '\u{e990}'; +pub const FILE_FONT: char = '\u{e991}'; +pub const FILE_HIDDEN: char = '\u{e992}'; +pub const FILE_IMAGE: char = '\u{e993}'; +pub const FILE_MOVIE: char = '\u{e994}'; +pub const FILE_NEW: char = '\u{e995}'; +pub const FILE_PARENT: char = '\u{e996}'; +pub const FILE_REFRESH: char = '\u{e997}'; +pub const FILE_SCRIPT: char = '\u{e998}'; +pub const FILE_SOUND: char = '\u{e999}'; +pub const FILE_TEXT: char = '\u{e99a}'; +pub const FILE_TICK: char = '\u{e99b}'; +pub const FILE_VOLUME: char = '\u{e99c}'; +pub const FILTER: char = '\u{e99d}'; +pub const FOLDER_REDIRECT: char = '\u{e99e}'; +pub const FONT_DATA: char = '\u{e99f}'; +pub const FONT_PREVIEW: char = '\u{e9a0}'; +pub const FORCE_BOID: char = '\u{e9a1}'; +pub const FORCE_CHARGE: char = '\u{e9a2}'; +pub const FORCE_CURVE: char = '\u{e9a3}'; +pub const FORCE_DRAG: char = '\u{e9a4}'; +pub const FORCE_FLUIDFLOW: char = '\u{e9a5}'; +pub const FORCE_FORCE: char = '\u{e9a6}'; +pub const FORCE_HARMONIC: char = '\u{e9a7}'; +pub const FORCE_LENNARDJONES: char = '\u{e9a8}'; +pub const FORCE_MAGNETIC: char = '\u{e9a9}'; +pub const FORCE_TEXTURE: char = '\u{e9aa}'; +pub const FORCE_TURBULENCE: char = '\u{e9ab}'; +pub const FORCE_VORTEX: char = '\u{e9ac}'; +pub const FORCE_WIND: char = '\u{e9ad}'; +pub const FORWARD: char = '\u{e9ae}'; +pub const FRAME_NEXT: char = '\u{e9af}'; +pub const FRAME_PREV: char = '\u{e9b0}'; +pub const FREEZE: char = '\u{e9b1}'; +pub const FULLSCREEN_ENTER: char = '\u{e9b2}'; +pub const FULLSCREEN_EXIT: char = '\u{e9b3}'; +pub const FUND: char = '\u{e9b4}'; +pub const GHOST_DISABLED: char = '\u{e9b5}'; +pub const GHOST_ENABLED: char = '\u{e9b6}'; +pub const GIZMO: char = '\u{e9b7}'; +pub const GP_MULTIFRAME_EDITING: char = '\u{e9b8}'; +pub const GP_ONLY_SELECTED: char = '\u{e9b9}'; +pub const GP_SELECT_BETWEEN_STROKES: char = '\u{e9ba}'; +pub const GP_SELECT_POINTS: char = '\u{e9bb}'; +pub const GP_SELECT_STROKES: char = '\u{e9bc}'; +pub const GRAPH: char = '\u{e9bd}'; +pub const GREASEPENCIL: char = '\u{e9be}'; +pub const GRID: char = '\u{e9bf}'; +pub const GRIP: char = '\u{e9c0}'; +pub const GROUP: char = '\u{e9c1}'; +pub const GROUP_BONE: char = '\u{e9c2}'; +pub const GROUP_UVS: char = '\u{e9c3}'; +pub const GROUP_VCOL: char = '\u{e9c4}'; +pub const GROUP_VERTEX: char = '\u{e9c5}'; +pub const HAIR: char = '\u{e9c6}'; +pub const HAIR_DATA: char = '\u{e9c7}'; +pub const HAND: char = '\u{e9c8}'; +pub const HANDLE_ALIGNED: char = '\u{e9c9}'; +pub const HANDLE_AUTO: char = '\u{e9ca}'; +pub const HANDLE_AUTOCLAMPED: char = '\u{e9cb}'; +pub const HANDLE_FREE: char = '\u{e9cc}'; +pub const HANDLE_VECTOR: char = '\u{e9cd}'; +pub const HEART: char = '\u{e9ce}'; +pub const HELP: char = '\u{e9cf}'; +pub const HIDE_OFF: char = '\u{e9d0}'; +pub const HIDE_ON: char = '\u{e9d1}'; +pub const HOLDOUT_OFF: char = '\u{e9d2}'; +pub const HOLDOUT_ON: char = '\u{e9d3}'; +pub const HOME: char = '\u{e9d4}'; +pub const HOOK: char = '\u{e9d5}'; +pub const IMAGE: char = '\u{e9d6}'; +pub const IMAGE_ALPHA: char = '\u{e9d7}'; +pub const IMAGE_BACKGROUND: char = '\u{e9d8}'; +pub const IMAGE_DATA: char = '\u{e9d9}'; +pub const IMAGE_PLANE: char = '\u{e9da}'; +pub const IMAGE_REFERENCE: char = '\u{e9db}'; +pub const IMAGE_RGB: char = '\u{e9dc}'; +pub const IMAGE_RGB_ALPHA: char = '\u{e9dd}'; +pub const IMAGE_ZDEPTH: char = '\u{e9de}'; +pub const IMGDISPLAY: char = '\u{e9df}'; +pub const IMPORT: char = '\u{e9e0}'; +pub const INDIRECT_ONLY_OFF: char = '\u{e9e1}'; +pub const INDIRECT_ONLY_ON: char = '\u{e9e2}'; +pub const INFO: char = '\u{e9e3}'; +pub const INTERPOLATE_BEZIER: char = '\u{e9e4}'; +pub const INTERPOLATE_CIRCLE: char = '\u{e9e5}'; +pub const INTERPOLATE_CONSTANT: char = '\u{e9e6}'; +pub const INTERPOLATE_CUBIC: char = '\u{e9e7}'; +pub const INTERPOLATE_EXPONENTIAL: char = '\u{e9e8}'; +pub const INTERPOLATE_LINEAR: char = '\u{e9e9}'; +pub const INTERPOLATE_QUAD: char = '\u{e9ea}'; +pub const INTERPOLATE_QUARTIC: char = '\u{e9eb}'; +pub const INTERPOLATE_QUINTIC: char = '\u{e9ec}'; +pub const INTERPOLATE_SINE: char = '\u{e9ed}'; +pub const INVERSE_SQUARE_CURVE: char = '\u{e9ee}'; +pub const IPO_BACK: char = '\u{e9ef}'; +pub const IPO_BOUNCE: char = '\u{e9f0}'; +pub const IPO_EASE_IN: char = '\u{e9f1}'; +pub const IPO_EASE_IN_OUT: char = '\u{e9f2}'; +pub const IPO_EASE_OUT: char = '\u{e9f3}'; +pub const IPO_ELASTIC: char = '\u{e9f4}'; +pub const ITALIC: char = '\u{e9f5}'; +pub const KEY_DEHLT: char = '\u{e9f6}'; +pub const KEYFRAME: char = '\u{e9f7}'; +pub const KEYFRAME_HLT: char = '\u{e9f8}'; +pub const KEY_HLT: char = '\u{e9f9}'; +pub const KEYINGSET: char = '\u{e9fa}'; +pub const LATTICE_DATA: char = '\u{e9fb}'; +pub const LAYER_ACTIVE: char = '\u{e9fc}'; +pub const LAYER_USED: char = '\u{e9fd}'; +pub const LIBRARY_DATA_BROKEN: char = '\u{e9fe}'; +pub const LIBRARY_DATA_DIRECT: char = '\u{e9ff}'; +pub const LIBRARY_DATA_INDIRECT: char = '\u{ea00}'; +pub const LIBRARY_DATA_OVERRIDE: char = '\u{ea01}'; +pub const LIGHT: char = '\u{ea02}'; +pub const LIGHT_AREA: char = '\u{ea03}'; +pub const LIGHT_DATA: char = '\u{ea04}'; +pub const LIGHT_HEMI: char = '\u{ea05}'; +pub const LIGHT_POINT: char = '\u{ea06}'; +pub const LIGHTPROBE_CUBEMAP: char = '\u{ea07}'; +pub const LIGHTPROBE_GRID: char = '\u{ea08}'; +pub const LIGHTPROBE_PLANAR: char = '\u{ea09}'; +pub const LIGHT_SPOT: char = '\u{ea0a}'; +pub const LIGHT_SUN: char = '\u{ea0b}'; +pub const LINEAR_CURVE: char = '\u{ea0c}'; +pub const LINE_DATA: char = '\u{ea0d}'; +pub const LINENUMBERS_OFF: char = '\u{ea0e}'; +pub const LINENUMBERS_ON: char = '\u{ea0f}'; +pub const LINK_BLEND: char = '\u{ea10}'; +pub const LINKED: char = '\u{ea11}'; +pub const LOCKED: char = '\u{ea12}'; +pub const LOCK_VIEW_OFF: char = '\u{ea13}'; +pub const LOCK_VIEW_ON: char = '\u{ea14}'; +pub const LONGDISPLAY: char = '\u{ea15}'; +pub const LOOP_BACK: char = '\u{ea16}'; +pub const LOOP_FORWARDS: char = '\u{ea17}'; +pub const MARKER: char = '\u{ea18}'; +pub const MARKER_HLT: char = '\u{ea19}'; +pub const MAT_CLOTH: char = '\u{ea1a}'; +pub const MAT_CUBE: char = '\u{ea1b}'; +pub const MATERIAL: char = '\u{ea1c}'; +pub const MATERIAL_DATA: char = '\u{ea1d}'; +pub const MAT_FLUID: char = '\u{ea1e}'; +pub const MAT_PLANE: char = '\u{ea1f}'; +pub const MAT_SHADERBALL: char = '\u{ea20}'; +pub const MAT_SPHERE: char = '\u{ea21}'; +pub const MAT_SPHERE_SKY: char = '\u{ea22}'; +pub const MEMORY: char = '\u{ea23}'; +pub const MENU_PANEL: char = '\u{ea24}'; +pub const MESH_CAPSULE: char = '\u{ea25}'; +pub const MESH_CIRCLE: char = '\u{ea26}'; +pub const MESH_CONE: char = '\u{ea27}'; +pub const MESH_CUBE: char = '\u{ea28}'; +pub const MESH_CYLINDER: char = '\u{ea29}'; +pub const MESH_DATA: char = '\u{ea2a}'; +pub const MESH_GRID: char = '\u{ea2b}'; +pub const MESH_ICOSPHERE: char = '\u{ea2c}'; +pub const MESH_MONKEY: char = '\u{ea2d}'; +pub const MESH_PLANE: char = '\u{ea2e}'; +pub const MESH_TORUS: char = '\u{ea2f}'; +pub const MESH_UVSPHERE: char = '\u{ea30}'; +pub const META_BALL: char = '\u{ea31}'; +pub const META_CAPSULE: char = '\u{ea32}'; +pub const META_CUBE: char = '\u{ea33}'; +pub const META_DATA: char = '\u{ea34}'; +pub const META_ELLIPSOID: char = '\u{ea35}'; +pub const META_PLANE: char = '\u{ea36}'; +pub const MOD_ARMATURE: char = '\u{ea37}'; +pub const MOD_ARRAY: char = '\u{ea38}'; +pub const MOD_BEVEL: char = '\u{ea39}'; +pub const MOD_BOOLEAN: char = '\u{ea3a}'; +pub const MOD_BUILD: char = '\u{ea3b}'; +pub const MOD_CAST: char = '\u{ea3c}'; +pub const MOD_CLOTH: char = '\u{ea3d}'; +pub const MOD_CURVE: char = '\u{ea3e}'; +pub const MOD_DATA_TRANSFER: char = '\u{ea3f}'; +pub const MOD_DECIM: char = '\u{ea40}'; +pub const MOD_DISPLACE: char = '\u{ea41}'; +pub const MOD_DYNAMICPAINT: char = '\u{ea42}'; +pub const MOD_EDGESPLIT: char = '\u{ea43}'; +pub const MOD_EXPLODE: char = '\u{ea44}'; +pub const MOD_FLUID: char = '\u{ea45}'; +pub const MOD_FLUIDSIM: char = '\u{ea46}'; +pub const MOD_HUE_SATURATION: char = '\u{ea47}'; +pub const MODIFIER: char = '\u{ea48}'; +pub const MODIFIER_DATA: char = '\u{ea49}'; +pub const MODIFIER_OFF: char = '\u{ea4a}'; +pub const MODIFIER_ON: char = '\u{ea4b}'; +pub const MOD_INSTANCE: char = '\u{ea4c}'; +pub const MOD_LATTICE: char = '\u{ea4d}'; +pub const MOD_MASK: char = '\u{ea4e}'; +pub const MOD_MESHDEFORM: char = '\u{ea4f}'; +pub const MOD_MIRROR: char = '\u{ea50}'; +pub const MOD_MULTIRES: char = '\u{ea51}'; +pub const MOD_NOISE: char = '\u{ea52}'; +pub const MOD_NORMALEDIT: char = '\u{ea53}'; +pub const MOD_OCEAN: char = '\u{ea54}'; +pub const MOD_OFFSET: char = '\u{ea55}'; +pub const MOD_OPACITY: char = '\u{ea56}'; +pub const MOD_PARTICLE_INSTANCE: char = '\u{ea57}'; +pub const MOD_PARTICLES: char = '\u{ea58}'; +pub const MOD_PHYSICS: char = '\u{ea59}'; +pub const MOD_REMESH: char = '\u{ea5a}'; +pub const MOD_SCREW: char = '\u{ea5b}'; +pub const MOD_SHRINKWRAP: char = '\u{ea5c}'; +pub const MOD_SIMPLEDEFORM: char = '\u{ea5d}'; +pub const MOD_SIMPLIFY: char = '\u{ea5e}'; +pub const MOD_SKIN: char = '\u{ea5f}'; +pub const MOD_SMOOTH: char = '\u{ea60}'; +pub const MOD_SOFT: char = '\u{ea61}'; +pub const MOD_SOLIDIFY: char = '\u{ea62}'; +pub const MOD_SUBSURF: char = '\u{ea63}'; +pub const MOD_THICKNESS: char = '\u{ea64}'; +pub const MOD_TIME: char = '\u{ea65}'; +pub const MOD_TINT: char = '\u{ea66}'; +pub const MOD_TRIANGULATE: char = '\u{ea67}'; +pub const MOD_UVPROJECT: char = '\u{ea68}'; +pub const MOD_VERTEX_WEIGHT: char = '\u{ea69}'; +pub const MOD_WARP: char = '\u{ea6a}'; +pub const MOD_WAVE: char = '\u{ea6b}'; +pub const MOD_WIREFRAME: char = '\u{ea6c}'; +pub const MONKEY: char = '\u{ea6d}'; +pub const MOUSE_LMB: char = '\u{ea6e}'; +pub const MOUSE_LMB_DRAG: char = '\u{ea6f}'; +pub const MOUSE_MMB: char = '\u{ea70}'; +pub const MOUSE_MMB_DRAG: char = '\u{ea71}'; +pub const MOUSE_MOVE: char = '\u{ea72}'; +pub const MOUSE_RMB: char = '\u{ea73}'; +pub const MOUSE_RMB_DRAG: char = '\u{ea74}'; +pub const MUTE_IPO_OFF: char = '\u{ea75}'; +pub const MUTE_IPO_ON: char = '\u{ea76}'; +pub const NETWORK_DRIVE: char = '\u{ea77}'; +pub const NEW_FOLDER: char = '\u{ea78}'; +pub const NEXT_KEYFRAME: char = '\u{ea79}'; +pub const NLA: char = '\u{ea7a}'; +pub const NLA_PUSHDOWN: char = '\u{ea7b}'; +pub const NO_CURVE: char = '\u{ea7c}'; +pub const NODE: char = '\u{ea7d}'; +pub const NODE_COMPOSITING: char = '\u{ea7e}'; +pub const NODE_CORNER: char = '\u{ea7f}'; +pub const NODE_INSERT_OFF: char = '\u{ea80}'; +pub const NODE_INSERT_ON: char = '\u{ea81}'; +pub const NODE_MATERIAL: char = '\u{ea82}'; +pub const NODE_SEL: char = '\u{ea83}'; +pub const NODE_SIDE: char = '\u{ea84}'; +pub const NODE_TEXTURE: char = '\u{ea85}'; +pub const NODE_TOP: char = '\u{ea86}'; +pub const NODETREE: char = '\u{ea87}'; +pub const NORMALIZE_FCURVES: char = '\u{ea88}'; +pub const NORMALS_FACE: char = '\u{ea89}'; +pub const NORMALS_VERTEX: char = '\u{ea8a}'; +pub const NORMALS_VERTEX_FACE: char = '\u{ea8b}'; +pub const OBJECT_DATA: char = '\u{ea8c}'; +pub const OBJECT_DATAMODE: char = '\u{ea8d}'; +pub const OBJECT_HIDDEN: char = '\u{ea8e}'; +pub const OBJECT_ORIGIN: char = '\u{ea8f}'; +pub const ONIONSKIN_OFF: char = '\u{ea90}'; +pub const ONIONSKIN_ON: char = '\u{ea91}'; +pub const OPTIONS: char = '\u{ea92}'; +pub const ORIENTATION_CURSOR: char = '\u{ea93}'; +pub const ORIENTATION_GIMBAL: char = '\u{ea94}'; +pub const ORIENTATION_GLOBAL: char = '\u{ea95}'; +pub const ORIENTATION_LOCAL: char = '\u{ea96}'; +pub const ORIENTATION_NORMAL: char = '\u{ea97}'; +pub const ORIENTATION_VIEW: char = '\u{ea98}'; +pub const ORPHAN_DATA: char = '\u{ea99}'; +pub const OUTLINER: char = '\u{ea9a}'; +pub const OUTLINER_COLLECTION: char = '\u{ea9b}'; +pub const OUTLINER_DATA_ARMATURE: char = '\u{ea9c}'; +pub const OUTLINER_DATA_CAMERA: char = '\u{ea9d}'; +pub const OUTLINER_DATA_CURVE: char = '\u{ea9e}'; +pub const OUTLINER_DATA_EMPTY: char = '\u{ea9f}'; +pub const OUTLINER_DATA_FONT: char = '\u{eaa0}'; +pub const OUTLINER_DATA_GP_LAYER: char = '\u{eaa1}'; +pub const OUTLINER_DATA_GREASEPENCIL: char = '\u{eaa2}'; +pub const OUTLINER_DATA_HAIR: char = '\u{eaa3}'; +pub const OUTLINER_DATA_LATTICE: char = '\u{eaa4}'; +pub const OUTLINER_DATA_LIGHT: char = '\u{eaa5}'; +pub const OUTLINER_DATA_LIGHTPROBE: char = '\u{eaa6}'; +pub const OUTLINER_DATA_MESH: char = '\u{eaa7}'; +pub const OUTLINER_DATA_META: char = '\u{eaa8}'; +pub const OUTLINER_DATA_POINTCLOUD: char = '\u{eaa9}'; +pub const OUTLINER_DATA_SPEAKER: char = '\u{eaaa}'; +pub const OUTLINER_DATA_SURFACE: char = '\u{eaab}'; +pub const OUTLINER_DATA_VOLUME: char = '\u{eaac}'; +pub const OUTLINER_OB_ARMATURE: char = '\u{eaad}'; +pub const OUTLINER_OB_CAMERA: char = '\u{eaae}'; +pub const OUTLINER_OB_CURVE: char = '\u{eaaf}'; +pub const OUTLINER_OB_EMPTY: char = '\u{eab0}'; +pub const OUTLINER_OB_FONT: char = '\u{eab1}'; +pub const OUTLINER_OB_FORCE_FIELD: char = '\u{eab2}'; +pub const OUTLINER_OB_GREASEPENCIL: char = '\u{eab3}'; +pub const OUTLINER_OB_GROUP_INSTANCE: char = '\u{eab4}'; +pub const OUTLINER_OB_HAIR: char = '\u{eab5}'; +pub const OUTLINER_OB_IMAGE: char = '\u{eab6}'; +pub const OUTLINER_OB_LATTICE: char = '\u{eab7}'; +pub const OUTLINER_OB_LIGHT: char = '\u{eab8}'; +pub const OUTLINER_OB_LIGHTPROBE: char = '\u{eab9}'; +pub const OUTLINER_OB_MESH: char = '\u{eaba}'; +pub const OUTLINER_OB_META: char = '\u{eabb}'; +pub const OUTLINER_OB_POINTCLOUD: char = '\u{eabc}'; +pub const OUTLINER_OB_SPEAKER: char = '\u{eabd}'; +pub const OUTLINER_OB_SURFACE: char = '\u{eabe}'; +pub const OUTLINER_OB_VOLUME: char = '\u{eabf}'; +pub const OUTPUT: char = '\u{eac0}'; +pub const OVERLAY: char = '\u{eac1}'; +pub const PACKAGE: char = '\u{eac2}'; +pub const PANEL_CLOSE: char = '\u{eac3}'; +pub const PARTICLE_DATA: char = '\u{eac4}'; +pub const PARTICLEMODE: char = '\u{eac5}'; +pub const PARTICLE_PATH: char = '\u{eac6}'; +pub const PARTICLE_POINT: char = '\u{eac7}'; +pub const PARTICLES: char = '\u{eac8}'; +pub const PARTICLE_TIP: char = '\u{eac9}'; +pub const PASTE_DOWN: char = '\u{eaca}'; +pub const PASTE_FLIP_DOWN: char = '\u{eacb}'; +pub const PASTE_FLIP_UP: char = '\u{eacc}'; +pub const PAUSE: char = '\u{eacd}'; +pub const PHYSICS: char = '\u{eace}'; +pub const PINNED: char = '\u{eacf}'; +pub const PIVOT_ACTIVE: char = '\u{ead0}'; +pub const PIVOT_CURSOR: char = '\u{ead1}'; +pub const PIVOT_INDIVIDUAL: char = '\u{ead2}'; +pub const PIVOT_MEDIAN: char = '\u{ead3}'; +pub const PIVOT_OBOUNDING_BOX: char = '\u{ead4}'; +pub const PLAY: char = '\u{ead5}'; +pub const PLAY_REVERSE: char = '\u{ead6}'; +pub const PLAY_SOUND: char = '\u{ead7}'; +pub const PLUGIN: char = '\u{ead8}'; +pub const PLUS: char = '\u{ead9}'; +pub const PMARKER: char = '\u{eada}'; +pub const PMARKER_ACT: char = '\u{eadb}'; +pub const PMARKER_SEL: char = '\u{eadc}'; +pub const POINTCLOUD_DATA: char = '\u{eadd}'; +pub const POSE_HLT: char = '\u{eade}'; +pub const PREFERENCES: char = '\u{eadf}'; +pub const PRESET: char = '\u{eae0}'; +pub const PRESET_NEW: char = '\u{eae1}'; +pub const PREVIEW_RANGE: char = '\u{eae2}'; +pub const PREV_KEYFRAME: char = '\u{eae3}'; +pub const PROPERTIES: char = '\u{eae4}'; +pub const PROPORTIONAL_CENTER_ON: char = '\u{eae5}'; +pub const PROPORTIONAL_OFF: char = '\u{eae6}'; +pub const PROPORTIONAL_ON: char = '\u{eae7}'; +pub const PROPORTIONAL_PROJECTED: char = '\u{eae8}'; +pub const QUESTION: char = '\u{eae9}'; +pub const QUIT: char = '\u{eaea}'; +pub const RADIOBUT_OFF: char = '\u{eaeb}'; +pub const RADIOBUT_ON: char = '\u{eaec}'; +pub const REC: char = '\u{eaed}'; +pub const RECOVER_LAST: char = '\u{eaee}'; +pub const REMOVE: char = '\u{eaef}'; +pub const RENDER_ANIMATION: char = '\u{eaf0}'; +pub const RENDERLAYERS: char = '\u{eaf1}'; +pub const RENDER_RESULT: char = '\u{eaf2}'; +pub const RENDER_STILL: char = '\u{eaf3}'; +pub const RESTRICT_COLOR_OFF: char = '\u{eaf4}'; +pub const RESTRICT_COLOR_ON: char = '\u{eaf5}'; +pub const RESTRICT_INSTANCED_OFF: char = '\u{eaf6}'; +pub const RESTRICT_INSTANCED_ON: char = '\u{eaf7}'; +pub const RESTRICT_RENDER_OFF: char = '\u{eaf8}'; +pub const RESTRICT_RENDER_ON: char = '\u{eaf9}'; +pub const RESTRICT_SELECT_OFF: char = '\u{eafa}'; +pub const RESTRICT_SELECT_ON: char = '\u{eafb}'; +pub const RESTRICT_VIEW_OFF: char = '\u{eafc}'; +pub const RESTRICT_VIEW_ON: char = '\u{eafd}'; +pub const REW: char = '\u{eafe}'; +pub const RIGHTARROW: char = '\u{eaff}'; +pub const RIGHTARROW_THIN: char = '\u{eb00}'; +pub const RIGID_BODY: char = '\u{eb01}'; +pub const RIGID_BODY_CONSTRAINT: char = '\u{eb02}'; +pub const RNA: char = '\u{eb03}'; +pub const RNA_ADD: char = '\u{eb04}'; +pub const ROOT_CURVE: char = '\u{eb05}'; +pub const ROUND_CURVE: char = '\u{eb06}'; +pub const SCENE: char = '\u{eb07}'; +pub const SCENE_DATA: char = '\u{eb08}'; +pub const SCREEN_BACK: char = '\u{eb09}'; +pub const SCRIPT: char = '\u{eb0a}'; +pub const SCRIPT_PLUGINS: char = '\u{eb0b}'; +pub const SCULPTMODE_HLT: char = '\u{eb0c}'; +pub const SELECT_DIFFERENCE: char = '\u{eb0d}'; +pub const SELECT_EXTEND: char = '\u{eb0e}'; +pub const SELECT_INTERSECT: char = '\u{eb0f}'; +pub const SELECT_SET: char = '\u{eb10}'; +pub const SELECT_SUBTRACT: char = '\u{eb11}'; +pub const SEQ_CHROMA_SCOPE: char = '\u{eb12}'; +pub const SEQ_HISTOGRAM: char = '\u{eb13}'; +pub const SEQ_LUMA_WAVEFORM: char = '\u{eb14}'; +pub const SEQ_PREVIEW: char = '\u{eb15}'; +pub const SEQ_SEQUENCER: char = '\u{eb16}'; +pub const SEQ_SPLITVIEW: char = '\u{eb17}'; +pub const SEQ_STRIP_DUPLICATE: char = '\u{eb18}'; +pub const SEQ_STRIP_META: char = '\u{eb19}'; +pub const SEQUENCE: char = '\u{eb1a}'; +pub const SETTINGS: char = '\u{eb1b}'; +pub const SHADERFX: char = '\u{eb1c}'; +pub const SHADING_BOUNDING_BOX: char = '\u{eb1d}'; +pub const SHADING_RENDERED: char = '\u{eb1e}'; +pub const SHADING_SOLID: char = '\u{eb1f}'; +pub const SHADING_TEXTURE: char = '\u{eb20}'; +pub const SHADING_WIRE: char = '\u{eb21}'; +pub const SHAPEKEY_DATA: char = '\u{eb22}'; +pub const SHARP_CURVE: char = '\u{eb23}'; +pub const SHORTDISPLAY: char = '\u{eb24}'; +pub const SMALL_CAPS: char = '\u{eb25}'; +pub const SMOOTH_CURVE: char = '\u{eb26}'; +pub const SNAP_EDGE: char = '\u{eb27}'; +pub const SNAP_FACE: char = '\u{eb28}'; +pub const SNAP_FACE_CENTER: char = '\u{eb29}'; +pub const SNAP_GRID: char = '\u{eb2a}'; +pub const SNAP_INCREMENT: char = '\u{eb2b}'; +pub const SNAP_MIDPOINT: char = '\u{eb2c}'; +pub const SNAP_NORMAL: char = '\u{eb2d}'; +pub const SNAP_OFF: char = '\u{eb2e}'; +pub const SNAP_ON: char = '\u{eb2f}'; +pub const SNAP_PEEL_OBJECT: char = '\u{eb30}'; +pub const SNAP_PERPENDICULAR: char = '\u{eb31}'; +pub const SNAP_VERTEX: char = '\u{eb32}'; +pub const SNAP_VOLUME: char = '\u{eb33}'; +pub const SOLO_OFF: char = '\u{eb34}'; +pub const SOLO_ON: char = '\u{eb35}'; +pub const SORT_ALPHA: char = '\u{eb36}'; +pub const SORT_ASC: char = '\u{eb37}'; +pub const SORT_BY_EXT: char = '\u{eb38}'; +pub const SORT_DESC: char = '\u{eb39}'; +pub const SORT_SIZE: char = '\u{eb3a}'; +pub const SORT_TIME: char = '\u{eb3b}'; +pub const SOUND: char = '\u{eb3c}'; +pub const SPEAKER: char = '\u{eb3d}'; +pub const SPHERE: char = '\u{eb3e}'; +pub const SPHERE_CURVE: char = '\u{eb3f}'; +pub const SPREADSHEET: char = '\u{eb40}'; +pub const STATUSBAR: char = '\u{eb41}'; +pub const STICKY_UVS_DISABLE: char = '\u{eb42}'; +pub const STICKY_UVS_LOC: char = '\u{eb43}'; +pub const STICKY_UVS_VERT: char = '\u{eb44}'; +pub const STRANDS: char = '\u{eb45}'; +pub const STROKE: char = '\u{eb46}'; +pub const STYLUS_PRESSURE: char = '\u{eb47}'; +pub const SURFACE_DATA: char = '\u{eb48}'; +pub const SURFACE_NCIRCLE: char = '\u{eb49}'; +pub const SURFACE_NCURVE: char = '\u{eb4a}'; +pub const SURFACE_NCYLINDER: char = '\u{eb4b}'; +pub const SURFACE_NSPHERE: char = '\u{eb4c}'; +pub const SURFACE_NSURFACE: char = '\u{eb4d}'; +pub const SURFACE_NTORUS: char = '\u{eb4e}'; +pub const SYNTAX_OFF: char = '\u{eb4f}'; +pub const SYNTAX_ON: char = '\u{eb50}'; +pub const SYSTEM: char = '\u{eb51}'; +pub const TEMP: char = '\u{eb52}'; +pub const TEXT: char = '\u{eb53}'; +pub const TEXTURE: char = '\u{eb54}'; +pub const TEXTURE_DATA: char = '\u{eb55}'; +pub const THREE_DOTS: char = '\u{eb56}'; +pub const TIME: char = '\u{eb57}'; +pub const TOOL_SETTINGS: char = '\u{eb58}'; +pub const TOPBAR: char = '\u{eb59}'; +pub const TPAINT_HLT: char = '\u{eb5a}'; +pub const TRACKER: char = '\u{eb5b}'; +pub const TRACKER_DATA: char = '\u{eb5c}'; +pub const TRACKING: char = '\u{eb5d}'; +pub const TRACKING_BACKWARDS: char = '\u{eb5e}'; +pub const TRACKING_BACKWARDS_SINGLE: char = '\u{eb5f}'; +pub const TRACKING_CLEAR_BACKWARDS: char = '\u{eb60}'; +pub const TRACKING_CLEAR_FORWARDS: char = '\u{eb61}'; +pub const TRACKING_FORWARDS: char = '\u{eb62}'; +pub const TRACKING_FORWARDS_SINGLE: char = '\u{eb63}'; +pub const TRACKING_REFINE_BACKWARDS: char = '\u{eb64}'; +pub const TRACKING_REFINE_FORWARDS: char = '\u{eb65}'; +pub const TRANSFORM_ORIGINS: char = '\u{eb66}'; +pub const TRASH: char = '\u{eb67}'; +pub const TRIA_DOWN: char = '\u{eb68}'; +pub const TRIA_DOWN_BAR: char = '\u{eb69}'; +pub const TRIA_LEFT: char = '\u{eb6a}'; +pub const TRIA_LEFT_BAR: char = '\u{eb6b}'; +pub const TRIA_RIGHT: char = '\u{eb6c}'; +pub const TRIA_RIGHT_BAR: char = '\u{eb6d}'; +pub const TRIA_UP: char = '\u{eb6e}'; +pub const TRIA_UP_BAR: char = '\u{eb6f}'; +pub const UGLYPACKAGE: char = '\u{eb70}'; +pub const UNDERLINE: char = '\u{eb71}'; +pub const UNLINKED: char = '\u{eb72}'; +pub const UNLOCKED: char = '\u{eb73}'; +pub const UNPINNED: char = '\u{eb74}'; +pub const URL: char = '\u{eb75}'; +pub const USER: char = '\u{eb76}'; +pub const UV: char = '\u{eb77}'; +pub const UV_DATA: char = '\u{eb78}'; +pub const UV_EDGE_SELECT: char = '\u{eb79}'; +pub const UV_FACE_SELECT: char = '\u{eb7a}'; +pub const UV_ISLANDS_SELECT: char = '\u{eb7b}'; +pub const UV_SYNC_SELECT: char = '\u{eb7c}'; +pub const UV_VERTEX_SELECT: char = '\u{eb7d}'; +pub const VERTEX_SELECT: char = '\u{eb7e}'; +pub const VIEW3D: char = '\u{eb7f}'; +pub const VIEW_CAMERA: char = '\u{eb80}'; +pub const VIEW_ORTHO: char = '\u{eb81}'; +pub const VIEW_PAN: char = '\u{eb82}'; +pub const VIEW_PERSPECTIVE: char = '\u{eb83}'; +pub const VIEW_ZOOM: char = '\u{eb84}'; +pub const VIEWZOOM: char = '\u{eb85}'; +pub const VISUAL_SELECTION_DISABLED: char = '\u{eb86}'; +pub const VISUAL_SELECTION_HIDDEN: char = '\u{eb87}'; +pub const VISUAL_SELECTION_SELECTABLE: char = '\u{eb88}'; +pub const VISUAL_SELECTION_UNSELECTABLE: char = '\u{eb89}'; +pub const VOLUME_DATA: char = '\u{eb8a}'; +pub const VPAINT_HLT: char = '\u{eb8b}'; +pub const WINDOW: char = '\u{eb8c}'; +pub const WORDWRAP_OFF: char = '\u{eb8d}'; +pub const WORDWRAP_ON: char = '\u{eb8e}'; +pub const WORKSPACE: char = '\u{eb8f}'; +pub const WORLD: char = '\u{eb90}'; +pub const WORLD_DATA: char = '\u{eb91}'; +pub const WPAINT_HLT: char = '\u{eb92}'; +pub const X: char = '\u{eb93}'; +pub const X_RAY: char = '\u{eb94}'; +pub const ZOOM_ALL: char = '\u{eb95}'; +pub const ZOOM_IN: char = '\u{eb96}'; +pub const ZOOM_OUT: char = '\u{eb97}'; +pub const ZOOM_PREVIOUS: char = '\u{eb98}'; +pub const ZOOM_SELECTED: char = '\u{eb99}'; diff --git a/retrotool-gui/src/loaders.rs b/retrotool-gui/src/loaders.rs index 65dc7a7..406113e 100644 --- a/retrotool-gui/src/loaders.rs +++ b/retrotool-gui/src/loaders.rs @@ -9,16 +9,19 @@ use astc_decode::{astc_decode, Footprint}; use bevy::{ app::{App, Plugin}, asset::{ - AddAsset, AssetIo, AssetIoError, AssetLoader, BoxedFuture, LoadContext, LoadedAsset, - Metadata, + AddAsset, AssetIo, AssetIoError, AssetLoader, AssetPath, BoxedFuture, LoadContext, + LoadedAsset, Metadata, }, prelude::*, + utils::HashMap, }; use binrw::Endian; use image::RgbaImage; use retrolib::{ format::{ + cmdl::{CMaterialDataInner, ModelData}, foot::locate_meta, + mtrl::MaterialData, pack::{Package, SparsePackageEntry}, txtr::TextureData, }, @@ -61,6 +64,7 @@ impl AssetIo for RetroAssetIo { if let Some(package) = packages.iter().find(|p| p.entries.iter().any(|e| e.id == id)) { + println!("Loading {} from {}", id, package.path.display()); package_path = Some(package.path.clone()); } } @@ -227,3 +231,108 @@ impl AssetLoader for TextureAssetLoader { fn extensions(&self) -> &[&str] { &["txtr"] } } + +#[derive(Debug, Clone, bevy::reflect::TypeUuid)] +#[uuid = "83269869-1209-408e-8835-bc6f2496e829"] +pub struct ModelAsset { + pub inner: ModelData, + pub textures: HashMap>, +} + +pub struct ModelAssetLoader; + +impl Plugin for ModelAssetLoader { + fn build(&self, app: &mut App) { + app.add_asset::().add_asset_loader(ModelAssetLoader); + } +} + +impl AssetLoader for ModelAssetLoader { + fn load<'a>( + &'a self, + bytes: &'a [u8], + load_context: &'a mut LoadContext, + ) -> BoxedFuture<'a, anyhow::Result<(), Error>> { + Box::pin(async move { + let meta = locate_meta(bytes, Endian::Little)?; + let data = ModelData::slice(bytes, meta, Endian::Little)?; + println!("Loaded model {:?}", data.head); + let mut dependencies = HashMap::::new(); + for mat in &data.mtrl.materials { + for data in &mat.data { + match &data.data { + CMaterialDataInner::Texture(texture) => { + dependencies.insert( + texture.id, + AssetPath::new(PathBuf::from(format!("{}.TXTR", texture.id)), None), + ); + } + CMaterialDataInner::LayeredTexture(texture) => { + for texture in &texture.textures { + if texture.id.is_nil() { + continue; + } + dependencies.insert( + texture.id, + AssetPath::new( + PathBuf::from(format!("{}.TXTR", texture.id)), + None, + ), + ); + } + } + _ => {} + } + } + } + let textures = dependencies + .iter() + .map(|(u, p)| (*u, load_context.get_handle(p.clone()))) + .collect(); + load_context.set_default_asset( + LoadedAsset::new(ModelAsset { inner: data, textures }) + .with_dependencies(dependencies.into_values().collect()), + ); + Ok(()) + }) + } + + fn extensions(&self) -> &[&str] { &["cmdl"] } +} + +#[derive(Debug, Clone, bevy::reflect::TypeUuid)] +#[uuid = "83269869-1209-408e-8835-bc6f2496e82a"] +pub struct MaterialAsset { + pub inner: ModelData, +} + +pub struct MaterialAssetLoader; + +impl Plugin for MaterialAssetLoader { + fn build(&self, app: &mut App) { + app.add_asset::().add_asset_loader(MaterialAssetLoader); + } +} + +impl AssetLoader for MaterialAssetLoader { + fn load<'a>( + &'a self, + bytes: &'a [u8], + _load_context: &'a mut LoadContext, + ) -> BoxedFuture<'a, anyhow::Result<(), Error>> { + Box::pin(async move { + let meta = locate_meta(bytes, Endian::Little)?; + // let (desc, data, remain) = FormDescriptor::slice(bytes, Endian::Little)?; + // println!("Loading material {:?}", desc); + let _mtrl = MaterialData::slice(bytes, meta, Endian::Little)?; + // fs::write("mtrl.out", &mtrl.decompressed)?; + // load_context.set_default_asset( + // LoadedAsset::new(ModelAsset { inner: data, textures }) + // .with_dependencies(dependencies.into_values().collect()), + // ); + Ok(()) + }) + } + + fn extensions(&self) -> &[&str] { &["mtrl"] } +} diff --git a/retrotool-gui/src/main.rs b/retrotool-gui/src/main.rs index 35c1681..5116ca6 100644 --- a/retrotool-gui/src/main.rs +++ b/retrotool-gui/src/main.rs @@ -1,34 +1,22 @@ +mod icon; mod loaders; +mod tabs; use std::path::PathBuf; -use bevy::{ - asset::LoadState, - ecs::system::{ - lifetimeless::{SRes, SResMut}, - SystemParam, SystemParamItem, SystemState, - }, - prelude::*, - render::render_resource::{ - Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, - }, -}; -use bevy_egui::{ - egui, - egui::{text::LayoutJob, Color32, FontId, TextFormat, Widget}, - EguiContext, EguiPlugin, -}; -use egui::{TextureId, Ui, WidgetText}; -use retrolib::format::{ - txtr::{ETextureFormat, ETextureType, K_FORM_TXTR}, - FourCC, -}; +use bevy::{app::AppExit, prelude::*}; +use bevy_egui::{egui, EguiContext, EguiPlugin}; +use egui::{FontFamily, FontId}; +use retrolib::format::FourCC; use uuid::Uuid; use walkdir::{DirEntry, WalkDir}; -use crate::loaders::{ - package_loader_system, PackageAssetLoader, PackageDirectory, RetroAssetIoPlugin, TextureAsset, - TextureAssetLoader, +use crate::{ + loaders::{ + package_loader_system, MaterialAssetLoader, ModelAssetLoader, PackageAssetLoader, + PackageDirectory, RetroAssetIoPlugin, TextureAssetLoader, + }, + tabs::{load_tab, model::TemporaryTag, project::ProjectTab, TabState, TabType, TabViewer}, }; #[derive(Default, Resource)] @@ -40,395 +28,65 @@ fn main() { file_open.0.push(arg.into()); } App::new() - .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) - .insert_resource(Msaa { samples: 1 }) + .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.05))) + .insert_resource(Msaa { samples: 4 }) .insert_resource(bevy::render::settings::WgpuSettings { features: bevy::render::settings::WgpuFeatures::TEXTURE_COMPRESSION_BC, - ..Default::default() + ..default() }) + .insert_resource(bevy::winit::WinitSettings::desktop_app()) .insert_resource(file_open) .init_resource::() .init_resource::() - .add_plugins(DefaultPlugins.build().add_before::(RetroAssetIoPlugin)) + .add_plugins( + DefaultPlugins + .build() + .set(WindowPlugin { + window: WindowDescriptor { + width: 1600.0, + height: 900.0, + position: WindowPosition::Centered, + title: "retrotool".to_string(), + ..default() + }, + ..default() + }) + .add_before::(RetroAssetIoPlugin), + ) .add_plugin(PackageAssetLoader) .add_plugin(TextureAssetLoader) + .add_plugin(ModelAssetLoader) + .add_plugin(MaterialAssetLoader) .add_plugin(EguiPlugin) - .add_system(file_drop) - .add_system(load_files) - .add_system(package_loader_system) - .add_system(ui_system) + .add_startup_system(setup_icon_font) + .add_system_to_stage(CoreStage::PreUpdate, file_drop) + .add_system_to_stage(CoreStage::PreUpdate, load_files) + .add_system_to_stage(CoreStage::PreUpdate, package_loader_system) + .add_system_to_stage(CoreStage::PreUpdate, ui_system.before_commands()) .run(); } #[derive(Debug, Clone, Default, Eq, PartialEq)] -struct AssetRef { +pub struct AssetRef { id: Uuid, kind: FourCC, } -enum TabType { - Project(ProjectTab), - Texture(TextureTab), - Empty, -} - #[derive(Resource)] struct UiState { tree: egui_dock::Tree, + ui_font: FontId, + code_font: FontId, } impl Default for UiState { fn default() -> Self { let mut tree = egui_dock::Tree::new(vec![TabType::Empty]); - tree.split_left(egui_dock::NodeIndex::root(), 0.3, vec![TabType::Project(ProjectTab)]); - Self { tree } - } -} - -struct TabViewer<'a> { - world: &'a mut World, - state: TabState, -} - -struct TabState { - open_assets: Vec, - open_tab: Option, -} - -trait SystemTab { - type LoadParam: SystemParam; - type UiParam: SystemParam; - - fn load(&mut self, _ctx: &mut EguiContext, _query: SystemParamItem<'_, '_, Self::LoadParam>) {} - - fn close(&mut self, _ctx: &mut EguiContext, _query: SystemParamItem<'_, '_, Self::LoadParam>) {} - - fn ui( - &mut self, - ui: &mut Ui, - query: SystemParamItem<'_, '_, Self::UiParam>, - state: &mut TabState, - ); - - fn title(&mut self) -> WidgetText; -} - -struct ProjectTab; - -impl SystemTab for ProjectTab { - type LoadParam = (); - type UiParam = (SRes, SRes>); - - fn ui( - &mut self, - ui: &mut Ui, - query: SystemParamItem<'_, '_, Self::UiParam>, - state: &mut TabState, - ) { - let (server, packages) = query; - let mut packages_sorted = - packages.iter().map(|(_, p)| p).collect::>(); - packages_sorted.sort_by_key(|p| &p.name); - for package in packages_sorted { - egui::CollapsingHeader::new(&package.name).show(ui, |ui| { - for entry in &package.entries { - let mut job = LayoutJob::simple( - format!("{} {}", entry.kind, entry.id), - FontId::monospace(12.0), - Color32::GRAY, - 0.0, - ); - if let Some(name) = &entry.name { - job.append( - &format!("\n{name}"), - 0.0, - TextFormat::simple(FontId::monospace(12.0), Color32::WHITE), - ); - } - let asset_ref = AssetRef { id: entry.id, kind: entry.kind }; - if egui::SelectableLabel::new(state.open_assets.contains(&asset_ref), job) - .ui(ui) - .clicked() - { - #[allow(clippy::single_match)] - match entry.kind { - K_FORM_TXTR => { - let handle = server.load::(format!( - "{}.{}", - entry.id, entry.kind - )); - state.open_tab = Some(TabType::Texture(TextureTab { - asset_ref: asset_ref.clone(), - handle, - loaded_texture: None, - })); - } - _ => {} - } - } - } - }); - } - } - - fn title(&mut self) -> WidgetText { "Browser".into() } -} - -struct LoadedTexture { - texture_ids: Vec, -} - -struct TextureTab { - asset_ref: AssetRef, - handle: Handle, - loaded_texture: Option, -} - -impl SystemTab for TextureTab { - type LoadParam = (SRes>, SResMut>); - type UiParam = (SRes, SRes>); - - fn load(&mut self, ctx: &mut EguiContext, query: SystemParamItem<'_, '_, Self::LoadParam>) { - if self.loaded_texture.is_some() { - return; - } - - let (textures, mut images) = query; - let Some(txtr) = textures.get(&self.handle) else { return; }; - let mut texture_ids = Vec::new(); - if let Some(rgba) = &txtr.rgba { - let image_handle = images.add(Image { - data: rgba.clone(), - texture_descriptor: TextureDescriptor { - label: None, - size: Extent3d { - width: txtr.inner.head.width, - height: txtr.inner.head.height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D2, - format: if txtr.inner.head.format.is_srgb() { - TextureFormat::Rgba8UnormSrgb - } else { - TextureFormat::Rgba8Unorm - }, - usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, - }, - sampler_descriptor: Default::default(), - texture_view_descriptor: None, - }); - texture_ids.push(ctx.add_image(image_handle)); - } else { - let array_stride: usize = - (txtr.inner.head.mip_sizes.iter().sum::() / txtr.inner.head.layers) as usize; - for layer in 0..txtr.inner.head.layers as usize { - let image_handle = images.add(Image { - data: txtr.inner.data - [layer * array_stride..(layer * array_stride) + array_stride] - .to_vec(), - texture_descriptor: TextureDescriptor { - label: None, - size: Extent3d { - width: txtr.inner.head.width, - height: txtr.inner.head.height, - depth_or_array_layers: 1, - }, - mip_level_count: txtr.inner.head.mip_sizes.len() as u32, - sample_count: 1, - dimension: match txtr.inner.head.kind { - ETextureType::_1D => TextureDimension::D1, - ETextureType::_2D => TextureDimension::D2, - ETextureType::_3D => TextureDimension::D3, - ETextureType::Cube => TextureDimension::D2, - ETextureType::_1DArray => TextureDimension::D1, - ETextureType::_2DArray => TextureDimension::D2, - ETextureType::_2DMultisample => TextureDimension::D2, - ETextureType::_2DMultisampleArray => TextureDimension::D2, - ETextureType::CubeArray => TextureDimension::D2, - }, - format: match txtr.inner.head.format { - ETextureFormat::R8Unorm => TextureFormat::Rgba8Unorm, - ETextureFormat::R8Snorm => TextureFormat::R8Snorm, - ETextureFormat::R8Uint => TextureFormat::R8Uint, - ETextureFormat::R8Sint => TextureFormat::R8Sint, - ETextureFormat::R16Unorm => TextureFormat::R16Unorm, - ETextureFormat::R16Snorm => TextureFormat::R16Snorm, - ETextureFormat::R16Uint => TextureFormat::R16Uint, - ETextureFormat::R16Sint => TextureFormat::R16Sint, - ETextureFormat::R16Float => TextureFormat::R16Float, - ETextureFormat::R32Uint => TextureFormat::R32Uint, - ETextureFormat::R32Sint => TextureFormat::R32Sint, - ETextureFormat::Rgba8Unorm => TextureFormat::Rgba8Unorm, - ETextureFormat::Rgba8Srgb => TextureFormat::Rgba8UnormSrgb, - ETextureFormat::Rgba16Float => TextureFormat::Rgba16Float, - ETextureFormat::Rgba32Float => TextureFormat::Rgba32Float, - ETextureFormat::Depth16Unorm => TextureFormat::Depth16Unorm, - ETextureFormat::Depth16Unorm2 => TextureFormat::Depth16Unorm, - ETextureFormat::Depth24S8Unorm => TextureFormat::Depth24PlusStencil8, - ETextureFormat::Depth32Float => TextureFormat::Depth32Float, - ETextureFormat::RgbaBc1Unorm => TextureFormat::Bc1RgbaUnorm, - ETextureFormat::RgbaBc1Srgb => TextureFormat::Bc1RgbaUnormSrgb, - ETextureFormat::RgbaBc2Unorm => TextureFormat::Bc2RgbaUnorm, - ETextureFormat::RgbaBc2Srgb => TextureFormat::Bc2RgbaUnormSrgb, - ETextureFormat::RgbaBc3Unorm => TextureFormat::Bc3RgbaUnorm, - ETextureFormat::RgbaBc3Srgb => TextureFormat::Bc3RgbaUnormSrgb, - ETextureFormat::RgbaBc4Unorm => TextureFormat::Bc4RUnorm, - ETextureFormat::RgbaBc4Snorm => TextureFormat::Bc4RSnorm, - ETextureFormat::RgbaBc5Unorm => TextureFormat::Bc5RgUnorm, - ETextureFormat::RgbaBc5Snorm => TextureFormat::Bc5RgSnorm, - ETextureFormat::Rg11B10Float => TextureFormat::Rg11b10Float, - ETextureFormat::R32Float => TextureFormat::R32Float, - ETextureFormat::Rg8Unorm => TextureFormat::Rg8Unorm, - ETextureFormat::Rg8Snorm => TextureFormat::Rg8Snorm, - ETextureFormat::Rg8Uint => TextureFormat::Rg8Uint, - ETextureFormat::Rg8Sint => TextureFormat::Rg8Sint, - ETextureFormat::Rg16Float => TextureFormat::Rg16Float, - ETextureFormat::Rg16Unorm => TextureFormat::Rg16Unorm, - ETextureFormat::Rg16Snorm => TextureFormat::Rg16Snorm, - ETextureFormat::Rg16Uint => TextureFormat::Rg16Uint, - ETextureFormat::Rg16Sint => TextureFormat::Rg16Sint, - ETextureFormat::Rgb10A2Unorm => TextureFormat::Rgb10a2Unorm, - ETextureFormat::Rg32Uint => TextureFormat::Rg32Uint, - ETextureFormat::Rg32Sint => TextureFormat::Rg32Sint, - ETextureFormat::Rg32Float => TextureFormat::Rg32Float, - ETextureFormat::Rgba16Unorm => TextureFormat::Rgba16Unorm, - ETextureFormat::Rgba16Snorm => TextureFormat::Rgba16Snorm, - ETextureFormat::Rgba16Uint => TextureFormat::Rgba16Uint, - ETextureFormat::Rgba16Sint => TextureFormat::Rgba16Sint, - ETextureFormat::Rgba32Uint => TextureFormat::Rgba32Uint, - ETextureFormat::Rgba32Sint => TextureFormat::Rgba32Sint, - ETextureFormat::BptcUfloat => TextureFormat::Bc6hRgbUfloat, - ETextureFormat::BptcSfloat => TextureFormat::Bc6hRgbSfloat, - ETextureFormat::BptcUnorm => TextureFormat::Bc7RgbaUnorm, - ETextureFormat::BptcUnormSrgb => TextureFormat::Bc7RgbaUnormSrgb, - _ => todo!(), - }, - usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, - }, - sampler_descriptor: Default::default(), - texture_view_descriptor: None, - }); - texture_ids.push(ctx.add_image(image_handle)); - } - }; - self.loaded_texture = Some(LoadedTexture { texture_ids }); - } - - fn ui( - &mut self, - ui: &mut Ui, - query: SystemParamItem<'_, '_, Self::UiParam>, - _state: &mut TabState, - ) { - let (server, textures) = query; - - ui.label(format!("{} {}", self.asset_ref.kind, self.asset_ref.id)); - - match server.get_load_state(&self.handle) { - LoadState::NotLoaded => { - return; - } - LoadState::Loading => { - ui.spinner(); - return; - } - LoadState::Loaded => {} - LoadState::Failed => { - ui.colored_label(Color32::RED, "Loading failed"); - return; - } - LoadState::Unloaded => { - return; - } - }; - - let loaded = self.loaded_texture.as_mut().unwrap(); - if let Some(txtr) = textures.get(&self.handle) { - ui.label(format!("Type: {:?}", txtr.inner.head.kind)); - ui.label(format!("Format: {:?}", txtr.inner.head.format)); - ui.label(format!( - "Dimensions: {}x{}x{} (mips: {})", - txtr.inner.head.width, - txtr.inner.head.height, - txtr.inner.head.layers, - txtr.inner.head.mip_sizes.len() - )); - if txtr.inner.head.kind == ETextureType::Cube && loaded.texture_ids.len() == 6 { - let width = txtr.inner.head.width; - let height = txtr.inner.head.height; - let (_, rect) = - ui.allocate_space(egui::Vec2 { x: (width * 4) as f32, y: (height * 3) as f32 }); - let size = egui::Vec2 { x: width as f32, y: height as f32 }; - let mut draw_image = |i: usize, x: u32, y: u32| { - let min = egui::Vec2 { x: (width * x) as f32, y: (height * y) as f32 }; - let max = - egui::Vec2 { x: (width * (x + 1)) as f32, y: (height * (y + 1)) as f32 }; - egui::widgets::Image::new(loaded.texture_ids[i], size) - .paint_at(ui, egui::Rect { min: rect.min + min, max: rect.min + max }); - }; - draw_image(2, 1, 0); - draw_image(1, 0, 1); - draw_image(4, 1, 1); - draw_image(0, 2, 1); - draw_image(5, 3, 1); - draw_image(3, 1, 2); - } else { - for image in &loaded.texture_ids { - ui.add(egui::widgets::Image::new(*image, egui::Vec2 { - x: txtr.inner.head.width as f32, - y: txtr.inner.head.height as f32, - })); - } - } - } - } - - fn title(&mut self) -> WidgetText { - format!("{} {}", self.asset_ref.kind, self.asset_ref.id).into() - } -} - -fn load_tab(world: &mut World, ctx: &mut EguiContext, tab: &mut T) { - let mut state: SystemState = SystemState::new(world); - tab.load(ctx, state.get_mut(world)); -} - -fn render_tab( - world: &mut World, - ui: &mut Ui, - tab: &mut T, - tab_state: &mut TabState, -) { - let mut state: SystemState = SystemState::new(world); - tab.ui(ui, state.get_mut(world), tab_state); -} - -impl egui_dock::TabViewer for TabViewer<'_> { - type Tab = TabType; - - fn ui(&mut self, ui: &mut Ui, tab: &mut Self::Tab) { - match tab { - TabType::Project(tab) => render_tab(self.world, ui, tab, &mut self.state), - TabType::Texture(tab) => render_tab(self.world, ui, tab, &mut self.state), - TabType::Empty => {} - } - } - - fn title(&mut self, tab: &mut Self::Tab) -> WidgetText { - match tab { - TabType::Project(tab) => tab.title(), - TabType::Texture(tab) => tab.title(), - TabType::Empty => "Placeholder".into(), - } - } - - fn on_close(&mut self, tab: &mut Self::Tab) -> bool { - match tab { - TabType::Project(_) => false, - TabType::Texture(_) => true, - TabType::Empty => false, + tree.split_left(egui_dock::NodeIndex::root(), 0.25, vec![TabType::Project(ProjectTab)]); + Self { + tree, + ui_font: FontId { size: 13.0, family: FontFamily::Proportional }, + code_font: FontId { size: 14.0, family: FontFamily::Monospace }, } } } @@ -476,7 +134,7 @@ fn ui_system(world: &mut World) { egui::menu::bar(ui, |ui| { egui::menu::menu_button(ui, "File", |ui| { if ui.button("Quit").clicked() { - std::process::exit(0); + world.send_event(AppExit); } }); }); @@ -495,14 +153,34 @@ fn ui_system(world: &mut World) { load_tab(world, egui_ctx.as_mut(), tab); tab_assets.push(tab.asset_ref.clone()); } + TabType::Model(tab) => { + load_tab(world, egui_ctx.as_mut(), tab); + tab_assets.push(tab.asset_ref.clone()); + } TabType::Empty => {} } } } } - let mut viewer = - TabViewer { world, state: TabState { open_assets: tab_assets, open_tab: None } }; + // Remove all temporary entities + let mut to_remove = vec![]; + for (entity, _) in world.query::<(Entity, With)>().iter(world) { + to_remove.push(entity); + } + for entity in to_remove { + world.despawn(entity); + } + + let mut viewer = TabViewer { + world, + state: TabState { + open_assets: tab_assets, + open_tab: None, + viewport: default(), + render_layer: 0, + }, + }; egui_dock::DockArea::new(&mut ui_state.tree) .style(egui_dock::Style::from_egui(egui_ctx.ctx_mut().style().as_ref())) .show(egui_ctx.ctx_mut(), &mut viewer); @@ -510,6 +188,38 @@ fn ui_system(world: &mut World) { if let Some(tab) = viewer.state.open_tab { ui_state.tree.push_to_first_leaf(tab); } + + if viewer.state.render_layer == 0 { + // Spawn a camera to just clear the screen + world.spawn((Camera3dBundle::default(), TemporaryTag)); + } }); }); } + +fn setup_icon_font(mut context: ResMut, state: ResMut) { + let font = egui::FontData::from_static(include_bytes!("../icon.ttf")); + let font_name = "blender".to_string(); + let mut fonts = egui::FontDefinitions::default(); + fonts.font_data.insert(font_name.clone(), font); + fonts + .families + .insert(FontFamily::Name(font_name.clone().into()), vec!["Hack".into(), font_name.clone()]); + fonts.families.get_mut(&FontFamily::Proportional).unwrap().push(font_name.clone()); + fonts.families.get_mut(&FontFamily::Monospace).unwrap().push(font_name); + context.ctx_mut().set_fonts(fonts); + + let mut style = (*context.ctx_mut().style()).clone(); + style.text_styles.insert(egui::TextStyle::Body, FontId { + size: (state.ui_font.size * 0.75).floor(), + family: state.ui_font.family.clone(), + }); + style.text_styles.insert(egui::TextStyle::Body, state.ui_font.clone()); + style.text_styles.insert(egui::TextStyle::Button, state.ui_font.clone()); + style.text_styles.insert(egui::TextStyle::Heading, FontId { + size: (state.ui_font.size * 1.5).floor(), + family: state.ui_font.family.clone(), + }); + style.text_styles.insert(egui::TextStyle::Monospace, state.code_font.clone()); + context.ctx_mut().set_style(style); +} diff --git a/retrotool-gui/src/tabs/mod.rs b/retrotool-gui/src/tabs/mod.rs new file mode 100644 index 0000000..c19a8a4 --- /dev/null +++ b/retrotool-gui/src/tabs/mod.rs @@ -0,0 +1,109 @@ +pub mod model; +pub mod project; +pub mod texture; + +use bevy::{ecs::system::*, prelude::*, render::camera::*}; +use bevy_egui::EguiContext; + +use crate::AssetRef; + +pub enum TabType { + Project(project::ProjectTab), + Texture(texture::TextureTab), + Model(model::ModelTab), + Empty, +} + +pub struct TabState { + pub open_assets: Vec, + pub open_tab: Option, + pub viewport: Viewport, + pub render_layer: u8, +} + +pub trait SystemTab { + type LoadParam: SystemParam; + type UiParam: SystemParam; + + fn load(&mut self, _ctx: &mut EguiContext, _query: SystemParamItem<'_, '_, Self::LoadParam>) {} + + fn close(&mut self, _query: SystemParamItem<'_, '_, Self::LoadParam>) {} // , _ctx: &mut EguiContext + + fn ui( + &mut self, + ui: &mut egui::Ui, + query: SystemParamItem<'_, '_, Self::UiParam>, + state: &mut TabState, + ); + + fn title(&mut self) -> egui::WidgetText; +} + +pub fn load_tab(world: &mut World, ctx: &mut EguiContext, tab: &mut T) { + let mut state: SystemState = SystemState::new(world); + tab.load(ctx, state.get_mut(world)); + state.apply(world); +} + +fn render_tab( + world: &mut World, + ui: &mut egui::Ui, + tab: &mut T, + tab_state: &mut TabState, +) { + let mut state: SystemState = SystemState::new(world); + tab.ui(ui, state.get_mut(world), tab_state); + state.apply(world); +} + +fn close_tab(world: &mut World, tab: &mut T) { + let mut state: SystemState = SystemState::new(world); + tab.close(state.get_mut(world)); + state.apply(world); +} + +pub struct TabViewer<'a> { + pub world: &'a mut World, + pub state: TabState, +} + +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, &mut self.state), + TabType::Texture(tab) => render_tab(self.world, ui, tab, &mut self.state), + TabType::Model(tab) => render_tab(self.world, ui, tab, &mut self.state), + TabType::Empty => {} + } + } + + 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::Empty => "".into(), + } + } + + fn on_close(&mut self, tab: &mut Self::Tab) -> bool { + match tab { + TabType::Project(_) => false, + TabType::Texture(tab) => { + close_tab(self.world, tab); + true + } + TabType::Model(tab) => { + close_tab(self.world, tab); + true + } + TabType::Empty => false, + } + } + + fn clear_background(&self, tab: &Self::Tab) -> bool { + !matches!(tab, TabType::Empty | TabType::Model(_)) + } +} diff --git a/retrotool-gui/src/tabs/model.rs b/retrotool-gui/src/tabs/model.rs new file mode 100644 index 0000000..3c7cf64 --- /dev/null +++ b/retrotool-gui/src/tabs/model.rs @@ -0,0 +1,750 @@ +use std::{borrow::Cow, ops::Range}; + +use bevy::{ + asset::LoadState, + core_pipeline::clear_color::ClearColorConfig, + ecs::system::{lifetimeless::*, *}, + prelude::*, + render::{ + camera::Viewport, + mesh::*, + primitives::Aabb, + render_resource::{ + AddressMode, Extent3d, FilterMode, SamplerDescriptor, TextureDescriptor, + TextureDimension, TextureFormat, TextureUsages, + }, + texture::ImageSampler, + view::RenderLayers, + }, + utils::HashMap, +}; +use bevy_egui::EguiContext; +use egui::{Id, PointerButton, Sense}; +use half::f16; +use retrolib::format::{ + cmdl::{ + CMaterialDataInner, EBufferType, EMaterialDataId, EVertexComponent, EVertexDataFormat, + ModelData, STextureUsageInfo, SVertexDataComponent, + }, + txtr::{ETextureFormat, ETextureType}, +}; +use uuid::Uuid; + +use crate::{ + icon, + loaders::{ModelAsset, TextureAsset}, + tabs::SystemTab, + AssetRef, TabState, +}; + +pub struct LoadedModel { + pub entities: Vec<(Entity, bool)>, + pub camera_xf: Transform, + pub upside_down: bool, + pub radius: f32, + pub origin: Vec3, + pub projection: Projection, +} + +pub struct ModelTab { + pub asset_ref: AssetRef, + pub handle: Handle, + pub loaded: Option, +} + +#[derive(Debug, Clone, Default)] +struct VertexBufferInfo { + // pub vertex_count: u32, + pub attributes: Vec<(MeshVertexAttribute, VertexAttributeValues)>, +} + +#[inline] +fn copy_direct(input: &[u8], component: &SVertexDataComponent) -> Vec +where T: bytemuck::AnyBitPattern { + let stride = component.stride as usize; + let mut out = bytemuck::zeroed_vec(input.len() / stride); + let mut offset = component.offset as usize; + for v in &mut out { + *v = *bytemuck::from_bytes(&input[offset..offset + std::mem::size_of::()]); + offset += stride; + } + out +} + +#[inline] +fn copy_converting(input: &[u8], component: &SVertexDataComponent, convert: C) -> Vec +where + T: bytemuck::AnyBitPattern, + R: bytemuck::AnyBitPattern, + C: Fn(T) -> R, +{ + let stride = component.stride as usize; + let mut out = bytemuck::zeroed_vec(input.len() / stride); + let mut offset = component.offset as usize; + for v in &mut out { + *v = convert(*bytemuck::from_bytes(&input[offset..offset + std::mem::size_of::()])); + offset += stride; + } + out +} + +fn convert_component( + input: &[u8], + component: &SVertexDataComponent, +) -> Option<(MeshVertexAttribute, VertexAttributeValues)> { + use EVertexComponent::*; + use EVertexDataFormat::*; + use VertexAttributeValues::*; + let attribute = match component.component { + Position => Mesh::ATTRIBUTE_POSITION, + Normal => Mesh::ATTRIBUTE_NORMAL, + Tangent0 => Mesh::ATTRIBUTE_TANGENT, + TexCoord0 => Mesh::ATTRIBUTE_UV_0, + // Color => Mesh::ATTRIBUTE_COLOR, + // BoneIndices => Mesh::ATTRIBUTE_JOINT_INDEX, + // BoneWeights => Mesh::ATTRIBUTE_JOINT_WEIGHT, + _ => return None, + }; + let values = match component.format { + Rg8Unorm => Unorm8x2(copy_direct(input, component)), + Rg8Uint => Uint8x2(copy_direct(input, component)), + Rg8Snorm => Snorm8x2(copy_direct(input, component)), + Rg8Sint => Sint8x2(copy_direct(input, component)), + R32Uint => Uint32(copy_direct(input, component)), + R32Sint => Sint32(copy_direct(input, component)), + R32Float => Float32(copy_direct(input, component)), + Rg16Unorm => Unorm16x2(copy_direct(input, component)), + Rg16Uint => Uint16x2(copy_direct(input, component)), + Rg16Snorm => Snorm16x2(copy_direct(input, component)), + Rg16Sint => Sint16x2(copy_direct(input, component)), + Rg16Float => Float32x2(copy_converting(input, component, |v: [u16; 2]| { + v.map(|u| f16::from_bits(u).to_f32()) + })), + Rgba8Unorm => match component.component { + Color => Float32x4(copy_converting(input, component, |v: [u8; 4]| { + v.map(|u| u as f32 * 255.0) + })), + _ => Unorm8x4(copy_direct(input, component)), + }, + Rgba8Uint => Uint8x4(copy_direct(input, component)), + Rgba8Snorm => Snorm8x4(copy_direct(input, component)), + Rgba8Sint => Sint8x4(copy_direct(input, component)), + Rg32Uint => Uint32x2(copy_direct(input, component)), + Rg32Sint => Sint32x2(copy_direct(input, component)), + Rg32Float => Float32x2(copy_direct(input, component)), + Rgba16Unorm => Unorm16x4(copy_direct(input, component)), + Rgba16Uint => Uint16x4(copy_direct(input, component)), + Rgba16Snorm => Snorm16x4(copy_direct(input, component)), + Rgba16Sint => Sint16x4(copy_direct(input, component)), + Rgba16Float => match component.component { + Position | Normal => Float32x3(copy_converting(input, component, |v: [u16; 3]| { + v.map(|u| f16::from_bits(u).to_f32()) + })), + TexCoord0 => Float32x2(copy_converting(input, component, |v: [u16; 2]| { + v.map(|u| f16::from_bits(u).to_f32()) + })), + _ => Float32x4(copy_converting(input, component, |v: [u16; 4]| { + v.map(|u| f16::from_bits(u).to_f32()) + })), + }, + Rgb32Uint => Uint32x3(copy_direct(input, component)), + Rgb32Sint => Sint32x3(copy_direct(input, component)), + Rgb32Float => Float32x3(copy_direct(input, component)), + Rgba32Uint => Uint32x4(copy_direct(input, component)), + Rgba32Sint => Sint32x4(copy_direct(input, component)), + Rgba32Float => match component.component { + Position | Normal => Float32x3(copy_direct(input, component)), + TexCoord0 => Float32x2(copy_direct(input, component)), + _ => Float32x4(copy_direct(input, component)), + }, + R16Uint => Uint32(copy_converting(input, component, |v: u16| v as u32)), + R16Sint => Sint32(copy_converting(input, component, |v: i16| v as i32)), + R16Float => Float32(copy_converting(input, component, |v: u16| f16::from_bits(v).to_f32())), + _ => todo!(), + }; + Some((attribute, values)) +} + +fn slice_vertices(values: &VertexAttributeValues, range: Range) -> VertexAttributeValues { + use VertexAttributeValues::*; + match values { + Float32(vec) => Float32(vec[range].to_vec()), + Sint32(vec) => Sint32(vec[range].to_vec()), + Uint32(vec) => Uint32(vec[range].to_vec()), + Float32x2(vec) => Float32x2(vec[range].to_vec()), + Sint32x2(vec) => Sint32x2(vec[range].to_vec()), + Uint32x2(vec) => Uint32x2(vec[range].to_vec()), + Float32x3(vec) => Float32x3(vec[range].to_vec()), + Sint32x3(vec) => Sint32x3(vec[range].to_vec()), + Uint32x3(vec) => Uint32x3(vec[range].to_vec()), + Float32x4(vec) => Float32x4(vec[range].to_vec()), + Sint32x4(vec) => Sint32x4(vec[range].to_vec()), + Uint32x4(vec) => Uint32x4(vec[range].to_vec()), + Sint16x2(vec) => Sint16x2(vec[range].to_vec()), + Snorm16x2(vec) => Snorm16x2(vec[range].to_vec()), + Uint16x2(vec) => Uint16x2(vec[range].to_vec()), + Unorm16x2(vec) => Unorm16x2(vec[range].to_vec()), + Sint16x4(vec) => Sint16x4(vec[range].to_vec()), + Snorm16x4(vec) => Snorm16x4(vec[range].to_vec()), + Uint16x4(vec) => Uint16x4(vec[range].to_vec()), + Unorm16x4(vec) => Unorm16x4(vec[range].to_vec()), + Sint8x2(vec) => Sint8x2(vec[range].to_vec()), + Snorm8x2(vec) => Snorm8x2(vec[range].to_vec()), + Uint8x2(vec) => Uint8x2(vec[range].to_vec()), + Unorm8x2(vec) => Unorm8x2(vec[range].to_vec()), + Sint8x4(vec) => Sint8x4(vec[range].to_vec()), + Snorm8x4(vec) => Snorm8x4(vec[range].to_vec()), + Uint8x4(vec) => Uint8x4(vec[range].to_vec()), + Unorm8x4(vec) => Unorm8x4(vec[range].to_vec()), + } +} + +#[inline] +fn slice_indices(input: &[T], start: u32, count: u32) -> (Vec, Range) +where T: num_traits::Num + num_traits::Bounded + Copy + PartialOrd + TryInto { + let slice = &input[start as usize..(start + count) as usize]; + let mut min = T::max_value(); + let mut max = T::min_value(); + for &v in slice { + if v < min { + min = v; + } + if v > max { + max = v; + } + } + let values = + if min == T::zero() { slice.to_vec() } else { slice.iter().map(|&v| v - min).collect() }; + (values, min.try_into().unwrap_or(usize::MAX)..max.try_into().unwrap_or(usize::MAX) + 1) +} + +#[derive(Clone)] +enum IndicesSlice<'a> { + U16(Cow<'a, [u16]>), + U32(Cow<'a, [u32]>), +} + +#[derive(Component)] +pub struct TemporaryTag; + +fn sampler_descriptor_from_usage<'a>(usage: &STextureUsageInfo) -> SamplerDescriptor<'a> { + SamplerDescriptor { + label: None, + address_mode_u: match usage.wrap_x { + 0 | u32::MAX => AddressMode::ClampToEdge, + 1 => AddressMode::Repeat, + 2 => AddressMode::MirrorRepeat, + 3 => todo!("Mirror clamp"), + 4 => AddressMode::ClampToBorder, + 5 => todo!("Clamp"), + n => todo!("wrap {n}"), + }, + address_mode_v: match usage.wrap_y { + 0 | u32::MAX => AddressMode::ClampToEdge, + 1 => AddressMode::Repeat, + 2 => AddressMode::MirrorRepeat, + 3 => todo!("Mirror clamp"), + 4 => AddressMode::ClampToBorder, + 5 => todo!("Clamp"), + n => todo!("wrap {n}"), + }, + address_mode_w: match usage.wrap_z { + 0 | u32::MAX => AddressMode::ClampToEdge, + 1 => AddressMode::Repeat, + 2 => AddressMode::MirrorRepeat, + 3 => todo!("Mirror clamp"), + 4 => AddressMode::ClampToBorder, + 5 => todo!("Clamp"), + n => todo!("wrap {n}"), + }, + mag_filter: match usage.filter { + 0 | u32::MAX => FilterMode::Nearest, + 1 => FilterMode::Linear, + n => todo!("Filter {n}"), + }, + min_filter: match usage.filter { + 0 | u32::MAX => FilterMode::Nearest, + 1 => FilterMode::Linear, + n => todo!("Filter {n}"), + }, + mipmap_filter: FilterMode::Linear, // TODO? + lod_min_clamp: 0.0, + lod_max_clamp: 0.0, + compare: None, + anisotropy_clamp: None, + border_color: None, + } +} + +impl SystemTab for ModelTab { + type LoadParam = ( + SCommands, + SResMut>, + SResMut>, + SResMut>, + SResMut>, + SResMut>, + SResMut, + ); + type UiParam = SCommands; + + fn load(&mut self, _ctx: &mut EguiContext, query: SystemParamItem<'_, '_, Self::LoadParam>) { + let ( + mut commands, + mut meshes, + mut materials, + mut models, + texture_assets, + mut images, + server, + ) = query; + if let Some(loaded) = &self.loaded { + for (entity, _) in &loaded.entities { + if let Some(mut commands) = commands.get_entity(*entity) { + commands.insert(Visibility::INVISIBLE); + } + } + return; + } + + let ModelAsset { + inner: ModelData { head, mtrl, mesh, vbuf, ibuf, vtx_buffers, idx_buffers }, + textures, + } = match models.get_mut(&self.handle) { + Some(v) => v, + None => return, + }; + // Ensure all dependencies loaded + match server.get_group_load_state(textures.iter().map(|(_, h)| h.id())) { + LoadState::Loaded => {} + _ => return, + } + + // Build sampler descriptors + let mut sampler_descriptors = HashMap::::new(); + for mat in &mtrl.materials { + for data in &mat.data { + match &data.data { + CMaterialDataInner::Texture(texture) => { + if let Some(usage) = &texture.usage { + sampler_descriptors + .insert(texture.id, sampler_descriptor_from_usage(usage)); + } + } + CMaterialDataInner::LayeredTexture(textures) => { + for texture in &textures.textures { + if let Some(usage) = &texture.usage { + sampler_descriptors + .insert(texture.id, sampler_descriptor_from_usage(usage)); + } + } + } + _ => continue, + } + } + } + + // Build texture images + let mut texture_handles = HashMap::>::new(); + for (id, handle) in textures { + let txtr = texture_assets.get(handle).unwrap(); + let image_handle = images.add(Image { + data: txtr.rgba.as_ref().unwrap_or(&txtr.inner.data).clone(), + texture_descriptor: TextureDescriptor { + label: None, + size: Extent3d { + width: txtr.inner.head.width, + height: txtr.inner.head.height, + depth_or_array_layers: if txtr.rgba.is_some() { + 1 // FIXME + } else { + txtr.inner.head.layers + }, + }, + mip_level_count: if txtr.rgba.is_some() { + 1 + } else { + txtr.inner.head.mip_sizes.len() as u32 + }, + sample_count: 1, + dimension: match txtr.inner.head.kind { + ETextureType::_1D => TextureDimension::D1, + ETextureType::_2D => TextureDimension::D2, + ETextureType::_3D => TextureDimension::D3, + ETextureType::Cube => TextureDimension::D2, + ETextureType::_1DArray => TextureDimension::D1, + ETextureType::_2DArray => TextureDimension::D2, + ETextureType::_2DMultisample => TextureDimension::D2, + ETextureType::_2DMultisampleArray => TextureDimension::D2, + ETextureType::CubeArray => TextureDimension::D2, + }, + format: if txtr.rgba.is_some() { + if txtr.inner.head.format.is_srgb() { + TextureFormat::Rgba8UnormSrgb + } else { + TextureFormat::Rgba8Unorm + } + } else { + match txtr.inner.head.format { + ETextureFormat::R8Unorm => TextureFormat::Rgba8Unorm, + ETextureFormat::R8Snorm => TextureFormat::R8Snorm, + ETextureFormat::R8Uint => TextureFormat::R8Uint, + ETextureFormat::R8Sint => TextureFormat::R8Sint, + ETextureFormat::R16Unorm => TextureFormat::R16Unorm, + ETextureFormat::R16Snorm => TextureFormat::R16Snorm, + ETextureFormat::R16Uint => TextureFormat::R16Uint, + ETextureFormat::R16Sint => TextureFormat::R16Sint, + ETextureFormat::R16Float => TextureFormat::R16Float, + ETextureFormat::R32Uint => TextureFormat::R32Uint, + ETextureFormat::R32Sint => TextureFormat::R32Sint, + ETextureFormat::Rgba8Unorm => TextureFormat::Rgba8Unorm, + ETextureFormat::Rgba8Srgb => TextureFormat::Rgba8UnormSrgb, + ETextureFormat::Rgba16Float => TextureFormat::Rgba16Float, + ETextureFormat::Rgba32Float => TextureFormat::Rgba32Float, + ETextureFormat::Depth16Unorm => TextureFormat::Depth16Unorm, + ETextureFormat::Depth16Unorm2 => TextureFormat::Depth16Unorm, + ETextureFormat::Depth24S8Unorm => TextureFormat::Depth24PlusStencil8, + ETextureFormat::Depth32Float => TextureFormat::Depth32Float, + ETextureFormat::RgbaBc1Unorm => TextureFormat::Bc1RgbaUnorm, + ETextureFormat::RgbaBc1Srgb => TextureFormat::Bc1RgbaUnormSrgb, + ETextureFormat::RgbaBc2Unorm => TextureFormat::Bc2RgbaUnorm, + ETextureFormat::RgbaBc2Srgb => TextureFormat::Bc2RgbaUnormSrgb, + ETextureFormat::RgbaBc3Unorm => TextureFormat::Bc3RgbaUnorm, + ETextureFormat::RgbaBc3Srgb => TextureFormat::Bc3RgbaUnormSrgb, + ETextureFormat::RgbaBc4Unorm => TextureFormat::Bc4RUnorm, + ETextureFormat::RgbaBc4Snorm => TextureFormat::Bc4RSnorm, + ETextureFormat::RgbaBc5Unorm => TextureFormat::Bc5RgUnorm, + ETextureFormat::RgbaBc5Snorm => TextureFormat::Bc5RgSnorm, + ETextureFormat::Rg11B10Float => TextureFormat::Rg11b10Float, + ETextureFormat::R32Float => TextureFormat::R32Float, + ETextureFormat::Rg8Unorm => TextureFormat::Rg8Unorm, + ETextureFormat::Rg8Snorm => TextureFormat::Rg8Snorm, + ETextureFormat::Rg8Uint => TextureFormat::Rg8Uint, + ETextureFormat::Rg8Sint => TextureFormat::Rg8Sint, + ETextureFormat::Rg16Float => TextureFormat::Rg16Float, + ETextureFormat::Rg16Unorm => TextureFormat::Rg16Unorm, + ETextureFormat::Rg16Snorm => TextureFormat::Rg16Snorm, + ETextureFormat::Rg16Uint => TextureFormat::Rg16Uint, + ETextureFormat::Rg16Sint => TextureFormat::Rg16Sint, + ETextureFormat::Rgb10A2Unorm => TextureFormat::Rgb10a2Unorm, + ETextureFormat::Rg32Uint => TextureFormat::Rg32Uint, + ETextureFormat::Rg32Sint => TextureFormat::Rg32Sint, + ETextureFormat::Rg32Float => TextureFormat::Rg32Float, + ETextureFormat::Rgba16Unorm => TextureFormat::Rgba16Unorm, + ETextureFormat::Rgba16Snorm => TextureFormat::Rgba16Snorm, + ETextureFormat::Rgba16Uint => TextureFormat::Rgba16Uint, + ETextureFormat::Rgba16Sint => TextureFormat::Rgba16Sint, + ETextureFormat::Rgba32Uint => TextureFormat::Rgba32Uint, + ETextureFormat::Rgba32Sint => TextureFormat::Rgba32Sint, + ETextureFormat::BptcUfloat => TextureFormat::Bc6hRgbUfloat, + ETextureFormat::BptcSfloat => TextureFormat::Bc6hRgbSfloat, + ETextureFormat::BptcUnorm => TextureFormat::Bc7RgbaUnorm, + ETextureFormat::BptcUnormSrgb => TextureFormat::Bc7RgbaUnormSrgb, + format => todo!("format {format:?}"), + } + }, + usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, + }, + sampler_descriptor: sampler_descriptors + .get(id) + .map(|desc| ImageSampler::Descriptor(desc.clone())) + .unwrap_or_default(), + texture_view_descriptor: None, + }); + texture_handles.insert(*id, image_handle); + } + + // Build vertex buffers + let mut buf_infos: Vec = Vec::with_capacity(vtx_buffers.len()); + let mut cur_buf = 0usize; + for info in &vbuf.info { + let num_buffers = info.unk as usize; // guess + let mut attributes = Vec::with_capacity(info.components.len()); + for component in &info.components { + let input = &*vtx_buffers[cur_buf + component.buffer_index as usize]; + if let Some((attribute, values)) = convert_component(input, component) { + attributes.push((attribute, values)); + } + } + buf_infos.push(VertexBufferInfo { + // vertex_count: info.vertex_count, + attributes, + }); + cur_buf += num_buffers; + } + + // Process index buffers + let mut index_buffers = Vec::::new(); + for (idx, &index_type) in ibuf.info.iter().enumerate() { + let in_buf = &*idx_buffers[idx]; + let out = match index_type { + EBufferType::U8 => { + IndicesSlice::U16(Cow::Owned(in_buf.iter().map(|&u| u as u16).collect())) + } + EBufferType::U16 => IndicesSlice::U16(Cow::Borrowed(bytemuck::cast_slice(in_buf))), + EBufferType::U32 => IndicesSlice::U32(Cow::Borrowed(bytemuck::cast_slice(in_buf))), + }; + index_buffers.push(out); + } + + // Build materials + let mut material_handles = Vec::with_capacity(mtrl.materials.len()); + for mat in &mtrl.materials { + let mut out_mat = StandardMaterial::default(); + println!("Shader {}, unk {}", mat.shader_id, mat.unk_guid); + let _ = server.load_untyped(format!("{}.MTRL", mat.shader_id)); + for data in &mat.data { + match data.data_id { + EMaterialDataId::DIFT | EMaterialDataId::BCLR => match &data.data { + CMaterialDataInner::Texture(texture) => { + out_mat.base_color_texture = texture_handles.get(&texture.id).cloned(); + } + _ => { + log::warn!( + "Unsupported material data type for DIFT {:?}", + data.data_type + ); + } + }, + EMaterialDataId::DIFC => match &data.data { + CMaterialDataInner::Color(color) => { + out_mat.base_color = Color::rgba(color.r, color.g, color.b, color.a); + } + _ => log::warn!( + "Unsupported material data type for DIFC {:?}", + data.data_type + ), + }, + EMaterialDataId::ICAN => match &data.data { + CMaterialDataInner::Texture(_texture) => { + // out_mat.emissive_texture = texture_handles.get(&texture.id).cloned(); + } + _ => log::warn!( + "Unsupported material data type for ICAN {:?}", + data.data_type + ), + }, + EMaterialDataId::ICNC => match &data.data { + CMaterialDataInner::Color(_color) => { + // out_mat.emissive = Color::rgba(color.r, color.g, color.b, color.a); + } + _ => log::warn!( + "Unsupported material data type for ICNC {:?}", + data.data_type + ), + }, + id => { + log::warn!("Unsupported material data ID {id:?}"); + } + } + } + material_handles.push(materials.add(out_mat)); + } + + // Process meshes + let aabb = Aabb::from_min_max( + Vec3::new(head.bounds.min.x, head.bounds.min.y, head.bounds.min.z), + Vec3::new(head.bounds.max.x, head.bounds.max.y, head.bounds.max.z), + ); + let mut entities = vec![]; + for (_idx, in_mesh) in mesh.meshes.iter().enumerate() { + let (indices, vert_range) = match &index_buffers[in_mesh.idx_buf_idx as usize] { + IndicesSlice::U16(indices) => { + let (values, range) = + slice_indices(indices, in_mesh.index_start, in_mesh.index_count); + (Indices::U16(values), range) + } + IndicesSlice::U32(indices) => { + let (values, range) = + slice_indices(indices, in_mesh.index_start, in_mesh.index_count); + (Indices::U32(values), range) + } + }; + let mut out_mesh = Mesh::new(PrimitiveTopology::TriangleList); + out_mesh.set_indices(Some(indices)); + for (component, values) in &buf_infos[in_mesh.vtx_buf_idx as usize].attributes { + out_mesh.insert_attribute( + component.clone(), + slice_vertices(values, vert_range.clone()), + ); + } + entities.push(( + commands + .spawn(PbrBundle { + mesh: meshes.add(out_mesh), + material: material_handles[in_mesh.material_idx as usize].clone(), + transform: Transform::from_translation((-aabb.center).into()), + ..default() + }) + .id(), + true, + )); + } + + let radius = (aabb.max() - aabb.min()).max_element() * 1.25; + let mut camera_xf = + Transform::from_xyz(-radius, 5.0, radius).looking_at(Vec3::ZERO, Vec3::Y); + let rot_matrix = Mat3::from_quat(camera_xf.rotation); + camera_xf.translation = rot_matrix.mul_vec3(Vec3::new(0.0, 0.0, radius)); + self.loaded = Some(LoadedModel { + entities, + camera_xf, + upside_down: false, + radius, + origin: Vec3::ZERO, + projection: Projection::Perspective(default()), + }); + } + + fn close(&mut self, query: SystemParamItem<'_, '_, Self::LoadParam>) { + let (mut commands, _, _, _, _, _, _) = query; + if let Some(loaded) = &self.loaded { + for (entity, _) in &loaded.entities { + if let Some(mut commands) = commands.get_entity(*entity) { + commands.despawn(); + } + } + } + } + + fn ui( + &mut self, + ui: &mut egui::Ui, + query: SystemParamItem<'_, '_, Self::UiParam>, + state: &mut TabState, + ) { + let rect = ui.available_rect_before_wrap(); + let left_top = rect.left_top(); + let size = rect.size(); + let viewport = Viewport { + physical_position: UVec2 { x: left_top.x as u32, y: left_top.y as u32 }, + physical_size: UVec2 { x: size.x as u32, y: size.y as u32 }, + depth: 0.0..1.0, + }; + + let response = ui.interact(rect, Id::new("background"), Sense::click_and_drag()); + + let mut commands = query; + if let Some(loaded) = &mut self.loaded { + let mut transform = &mut loaded.camera_xf; + let mut any = false; + let mut rotation_move = Vec2::ZERO; + let mut pan = Vec2::ZERO; + let scroll = { + if response.hovered() { + let delta = ui.input(|i| i.scroll_delta); + Vec2::new(delta.x, delta.y) + } else { + Vec2::ZERO + } + }; + if response.drag_started_by(PointerButton::Primary) + || response.drag_released_by(PointerButton::Primary) + { + // only check for upside down when orbiting started or ended this frame + // if the camera is "upside" down, panning horizontally would be inverted, so invert the input to make it correct + let up = transform.rotation * Vec3::Y; + loaded.upside_down = up.y <= 0.0; + } + if response.dragged_by(PointerButton::Primary) { + let delta = response.drag_delta(); + rotation_move = Vec2::new(delta.x, delta.y); + } else if response.dragged_by(PointerButton::Middle) { + let delta = response.drag_delta(); + pan = Vec2::new(delta.x, delta.y); + } + if rotation_move.length_squared() > 0.0 { + any = true; + let delta_x = { + let delta = rotation_move.x / rect.width() * std::f32::consts::PI * 2.0; + if loaded.upside_down { + -delta + } else { + delta + } + }; + let delta_y = rotation_move.y / rect.height() * std::f32::consts::PI; + let yaw = Quat::from_rotation_y(-delta_x); + let pitch = Quat::from_rotation_x(-delta_y); + transform.rotation = yaw * transform.rotation; // rotate around global y axis + transform.rotation *= pitch; // rotate around local x axis + } else if pan.length_squared() > 0.0 { + any = true; + if let Projection::Perspective(projection) = &loaded.projection { + pan *= Vec2::new(projection.fov * projection.aspect_ratio, projection.fov) + / Vec2::new(rect.width(), rect.height()); + } + // translate by local axes + let right = transform.rotation * Vec3::X * -pan.x; + let up = transform.rotation * Vec3::Y * pan.y; + // make panning proportional to distance away from focus point + let translation = (right + up) * loaded.radius; + loaded.origin += translation; + } else if scroll.y.abs() > 0.0 { + any = true; + loaded.radius -= (scroll.y / 50.0/* TODO ? */) * loaded.radius * 0.2; + // dont allow zoom to reach zero or you get stuck + loaded.radius = f32::max(loaded.radius, 0.05); + } + if any { + // emulating parent/child to make the yaw/y-axis rotation behave like a turntable + // parent = x and y rotation + // child = z-offset + let rot_matrix = Mat3::from_quat(transform.rotation); + transform.translation = + loaded.origin + rot_matrix.mul_vec3(Vec3::new(0.0, 0.0, loaded.radius)); + } + + commands.spawn(( + Camera3dBundle { + camera_3d: Camera3d { + clear_color: if state.render_layer == 0 { + ClearColorConfig::Default + } else { + ClearColorConfig::None + }, + ..default() + }, + camera: Camera { + viewport: Some(viewport), + priority: state.render_layer as isize, + ..default() + }, + transform: loaded.camera_xf, + ..default() + }, + RenderLayers::layer(state.render_layer), + TemporaryTag, + )); + commands.spawn(( + DirectionalLightBundle { + directional_light: DirectionalLight { ..default() }, + transform: Transform::from_xyz(-30.0, 5.0, 20.0) + .looking_at(Vec3::ZERO, Vec3::Y), + ..default() + }, + RenderLayers::layer(state.render_layer), + TemporaryTag, + )); + + egui::Frame::group(ui.style()).show(ui, |ui| { + egui::ScrollArea::vertical().max_height(rect.height() * 0.25).show(ui, |ui| { + for (idx, (entity, visible)) in loaded.entities.iter_mut().enumerate() { + ui.checkbox(visible, format!("Mesh {idx}")); + if let Some(mut commands) = commands.get_entity(*entity) { + commands.insert(( + if *visible { Visibility::VISIBLE } else { Visibility::INVISIBLE }, + RenderLayers::layer(state.render_layer), + )); + } + } + }); + }); + state.render_layer += 1; + } + } + + fn title(&mut self) -> egui::WidgetText { + format!("{} {} {}", icon::FILE_3D, self.asset_ref.kind, self.asset_ref.id).into() + } +} diff --git a/retrotool-gui/src/tabs/project.rs b/retrotool-gui/src/tabs/project.rs new file mode 100644 index 0000000..24fe95b --- /dev/null +++ b/retrotool-gui/src/tabs/project.rs @@ -0,0 +1,98 @@ +use bevy::{ + ecs::system::{lifetimeless::*, *}, + prelude::*, +}; +use egui::{text::LayoutJob, Color32, TextFormat, Widget}; +use retrolib::format::{cmdl::K_FORM_CMDL, txtr::K_FORM_TXTR, FourCC}; + +use crate::{ + icon, + loaders::{ModelAsset, PackageDirectory, TextureAsset}, + tabs::{model::ModelTab, texture::TextureTab, SystemTab, TabState, TabType}, + AssetRef, +}; + +pub const K_FORM_FMV0: FourCC = FourCC(*b"FMV0"); +pub const K_FORM_ROOM: FourCC = FourCC(*b"ROOM"); + +pub struct ProjectTab; + +impl SystemTab for ProjectTab { + type LoadParam = (); + type UiParam = (SRes, SRes>); + + fn ui( + &mut self, + ui: &mut egui::Ui, + query: SystemParamItem<'_, '_, Self::UiParam>, + state: &mut TabState, + ) { + let (server, packages) = query; + let mut packages_sorted = + packages.iter().map(|(_, p)| p).collect::>(); + packages_sorted.sort_by_key(|p| &p.name); + for package in packages_sorted { + egui::CollapsingHeader::new(&package.name).show(ui, |ui| { + for entry in &package.entries { + let monospace = + ui.style().text_styles.get(&egui::TextStyle::Monospace).unwrap().clone(); + let mut job = LayoutJob::simple( + format!( + "{} {} {}", + match entry.kind { + K_FORM_TXTR => icon::TEXTURE, + K_FORM_CMDL => icon::FILE_3D, + K_FORM_FMV0 => icon::FILE_MOVIE, + K_FORM_ROOM => icon::SCENE_DATA, + _ => icon::FILE, + }, + entry.kind, + entry.id + ), + monospace.clone(), + Color32::GRAY, + 0.0, + ); + if let Some(name) = &entry.name { + job.append( + &format!("\n{name}"), + 0.0, + TextFormat::simple(monospace, Color32::WHITE), + ); + } + let asset_ref = AssetRef { id: entry.id, kind: entry.kind }; + if egui::SelectableLabel::new(state.open_assets.contains(&asset_ref), job) + .ui(ui) + .clicked() + { + match entry.kind { + K_FORM_TXTR => { + let handle = server.load::(format!( + "{}.{}", + entry.id, entry.kind + )); + state.open_tab = Some(TabType::Texture(TextureTab { + asset_ref: asset_ref.clone(), + handle, + loaded_texture: None, + })); + } + K_FORM_CMDL => { + let handle = server + .load::(format!("{}.{}", entry.id, entry.kind)); + state.open_tab = Some(TabType::Model(ModelTab { + asset_ref: asset_ref.clone(), + handle, + loaded: None, + })); + } + _ => {} + } + } + } + }); + } + } + + fn title(&mut self) -> egui::WidgetText { format!("{} Browser", icon::FILEBROWSER).into() } +} diff --git a/retrotool-gui/src/tabs/texture.rs b/retrotool-gui/src/tabs/texture.rs new file mode 100644 index 0000000..120189b --- /dev/null +++ b/retrotool-gui/src/tabs/texture.rs @@ -0,0 +1,226 @@ +use bevy::{ + asset::LoadState, + ecs::system::{lifetimeless::*, *}, + prelude::*, + render::render_resource::*, +}; +use bevy_egui::EguiContext; +use retrolib::format::txtr::{ETextureFormat, ETextureType}; + +use crate::{loaders::TextureAsset, tabs::SystemTab, AssetRef, TabState, icon}; + +pub struct LoadedTexture { + pub texture_ids: Vec, +} + +pub struct TextureTab { + pub asset_ref: AssetRef, + pub handle: Handle, + pub loaded_texture: Option, +} + +impl SystemTab for TextureTab { + type LoadParam = (SRes>, SResMut>); + type UiParam = (SRes, SRes>); + + fn load(&mut self, ctx: &mut EguiContext, query: SystemParamItem<'_, '_, Self::LoadParam>) { + if self.loaded_texture.is_some() { + return; + } + + let (textures, mut images) = query; + let Some(txtr) = textures.get(&self.handle) else { return; }; + let mut texture_ids = Vec::new(); + if let Some(rgba) = &txtr.rgba { + let image_handle = images.add(Image { + data: rgba.clone(), + texture_descriptor: TextureDescriptor { + label: None, + size: Extent3d { + width: txtr.inner.head.width, + height: txtr.inner.head.height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: if txtr.inner.head.format.is_srgb() { + TextureFormat::Rgba8UnormSrgb + } else { + TextureFormat::Rgba8Unorm + }, + usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, + }, + sampler_descriptor: default(), + texture_view_descriptor: None, + }); + texture_ids.push(ctx.add_image(image_handle)); + } else { + let array_stride: usize = + (txtr.inner.head.mip_sizes.iter().sum::() / txtr.inner.head.layers) as usize; + for layer in 0..txtr.inner.head.layers as usize { + let image_handle = images.add(Image { + data: txtr.inner.data + [layer * array_stride..(layer * array_stride) + array_stride] + .to_vec(), + texture_descriptor: TextureDescriptor { + label: None, + size: Extent3d { + width: txtr.inner.head.width, + height: txtr.inner.head.height, + depth_or_array_layers: 1, + }, + mip_level_count: txtr.inner.head.mip_sizes.len() as u32, + sample_count: 1, + dimension: match txtr.inner.head.kind { + ETextureType::_1D => TextureDimension::D1, + ETextureType::_2D => TextureDimension::D2, + ETextureType::_3D => TextureDimension::D3, + ETextureType::Cube => TextureDimension::D2, + ETextureType::_1DArray => TextureDimension::D1, + ETextureType::_2DArray => TextureDimension::D2, + ETextureType::_2DMultisample => TextureDimension::D2, + ETextureType::_2DMultisampleArray => TextureDimension::D2, + ETextureType::CubeArray => TextureDimension::D2, + }, + format: match txtr.inner.head.format { + ETextureFormat::R8Unorm => TextureFormat::Rgba8Unorm, + ETextureFormat::R8Snorm => TextureFormat::R8Snorm, + ETextureFormat::R8Uint => TextureFormat::R8Uint, + ETextureFormat::R8Sint => TextureFormat::R8Sint, + ETextureFormat::R16Unorm => TextureFormat::R16Unorm, + ETextureFormat::R16Snorm => TextureFormat::R16Snorm, + ETextureFormat::R16Uint => TextureFormat::R16Uint, + ETextureFormat::R16Sint => TextureFormat::R16Sint, + ETextureFormat::R16Float => TextureFormat::R16Float, + ETextureFormat::R32Uint => TextureFormat::R32Uint, + ETextureFormat::R32Sint => TextureFormat::R32Sint, + ETextureFormat::Rgba8Unorm => TextureFormat::Rgba8Unorm, + ETextureFormat::Rgba8Srgb => TextureFormat::Rgba8UnormSrgb, + ETextureFormat::Rgba16Float => TextureFormat::Rgba16Float, + ETextureFormat::Rgba32Float => TextureFormat::Rgba32Float, + ETextureFormat::Depth16Unorm => TextureFormat::Depth16Unorm, + ETextureFormat::Depth16Unorm2 => TextureFormat::Depth16Unorm, + ETextureFormat::Depth24S8Unorm => TextureFormat::Depth24PlusStencil8, + ETextureFormat::Depth32Float => TextureFormat::Depth32Float, + ETextureFormat::RgbaBc1Unorm => TextureFormat::Bc1RgbaUnorm, + ETextureFormat::RgbaBc1Srgb => TextureFormat::Bc1RgbaUnormSrgb, + ETextureFormat::RgbaBc2Unorm => TextureFormat::Bc2RgbaUnorm, + ETextureFormat::RgbaBc2Srgb => TextureFormat::Bc2RgbaUnormSrgb, + ETextureFormat::RgbaBc3Unorm => TextureFormat::Bc3RgbaUnorm, + ETextureFormat::RgbaBc3Srgb => TextureFormat::Bc3RgbaUnormSrgb, + ETextureFormat::RgbaBc4Unorm => TextureFormat::Bc4RUnorm, + ETextureFormat::RgbaBc4Snorm => TextureFormat::Bc4RSnorm, + ETextureFormat::RgbaBc5Unorm => TextureFormat::Bc5RgUnorm, + ETextureFormat::RgbaBc5Snorm => TextureFormat::Bc5RgSnorm, + ETextureFormat::Rg11B10Float => TextureFormat::Rg11b10Float, + ETextureFormat::R32Float => TextureFormat::R32Float, + ETextureFormat::Rg8Unorm => TextureFormat::Rg8Unorm, + ETextureFormat::Rg8Snorm => TextureFormat::Rg8Snorm, + ETextureFormat::Rg8Uint => TextureFormat::Rg8Uint, + ETextureFormat::Rg8Sint => TextureFormat::Rg8Sint, + ETextureFormat::Rg16Float => TextureFormat::Rg16Float, + ETextureFormat::Rg16Unorm => TextureFormat::Rg16Unorm, + ETextureFormat::Rg16Snorm => TextureFormat::Rg16Snorm, + ETextureFormat::Rg16Uint => TextureFormat::Rg16Uint, + ETextureFormat::Rg16Sint => TextureFormat::Rg16Sint, + ETextureFormat::Rgb10A2Unorm => TextureFormat::Rgb10a2Unorm, + ETextureFormat::Rg32Uint => TextureFormat::Rg32Uint, + ETextureFormat::Rg32Sint => TextureFormat::Rg32Sint, + ETextureFormat::Rg32Float => TextureFormat::Rg32Float, + ETextureFormat::Rgba16Unorm => TextureFormat::Rgba16Unorm, + ETextureFormat::Rgba16Snorm => TextureFormat::Rgba16Snorm, + ETextureFormat::Rgba16Uint => TextureFormat::Rgba16Uint, + ETextureFormat::Rgba16Sint => TextureFormat::Rgba16Sint, + ETextureFormat::Rgba32Uint => TextureFormat::Rgba32Uint, + ETextureFormat::Rgba32Sint => TextureFormat::Rgba32Sint, + ETextureFormat::BptcUfloat => TextureFormat::Bc6hRgbUfloat, + ETextureFormat::BptcSfloat => TextureFormat::Bc6hRgbSfloat, + ETextureFormat::BptcUnorm => TextureFormat::Bc7RgbaUnorm, + ETextureFormat::BptcUnormSrgb => TextureFormat::Bc7RgbaUnormSrgb, + _ => todo!(), + }, + usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, + }, + sampler_descriptor: default(), + texture_view_descriptor: None, + }); + texture_ids.push(ctx.add_image(image_handle)); + } + }; + self.loaded_texture = Some(LoadedTexture { texture_ids }); + } + + fn ui( + &mut self, + ui: &mut egui::Ui, + query: SystemParamItem<'_, '_, Self::UiParam>, + _state: &mut TabState, + ) { + let (server, textures) = query; + + ui.label(format!("{} {}", self.asset_ref.kind, self.asset_ref.id)); + + match server.get_load_state(&self.handle) { + LoadState::NotLoaded => { + return; + } + LoadState::Loading => { + ui.spinner(); + return; + } + LoadState::Loaded => {} + LoadState::Failed => { + ui.colored_label(egui::Color32::RED, "Loading failed"); + return; + } + LoadState::Unloaded => { + return; + } + }; + + let loaded = self.loaded_texture.as_mut().unwrap(); + if let Some(txtr) = textures.get(&self.handle) { + ui.label(format!("Type: {:?}", txtr.inner.head.kind)); + ui.label(format!("Format: {:?}", txtr.inner.head.format)); + ui.label(format!( + "Dimensions: {}x{}x{} (mips: {})", + txtr.inner.head.width, + txtr.inner.head.height, + txtr.inner.head.layers, + txtr.inner.head.mip_sizes.len() + )); + if txtr.inner.head.kind == ETextureType::Cube && loaded.texture_ids.len() == 6 { + let width = txtr.inner.head.width; + let height = txtr.inner.head.height; + let (_, rect) = + ui.allocate_space(egui::Vec2 { x: (width * 4) as f32, y: (height * 3) as f32 }); + let size = egui::Vec2 { x: width as f32, y: height as f32 }; + let mut draw_image = |i: usize, x: u32, y: u32| { + let min = egui::Vec2 { x: (width * x) as f32, y: (height * y) as f32 }; + let max = + egui::Vec2 { x: (width * (x + 1)) as f32, y: (height * (y + 1)) as f32 }; + egui::widgets::Image::new(loaded.texture_ids[i], size) + .paint_at(ui, egui::Rect { min: rect.min + min, max: rect.min + max }); + }; + draw_image(2, 1, 0); + draw_image(1, 0, 1); + draw_image(4, 1, 1); + draw_image(0, 2, 1); + draw_image(5, 3, 1); + draw_image(3, 1, 2); + } else { + for image in &loaded.texture_ids { + ui.add(egui::widgets::Image::new(*image, egui::Vec2 { + x: txtr.inner.head.width as f32, + y: txtr.inner.head.height as f32, + })); + } + } + } + } + + fn title(&mut self) -> egui::WidgetText { + format!("{} {} {}", icon::TEXTURE, self.asset_ref.kind, self.asset_ref.id).into() + } +} diff --git a/retrotool/Cargo.toml b/retrotool/Cargo.toml index 867018c..d108a57 100644 --- a/retrotool/Cargo.toml +++ b/retrotool/Cargo.toml @@ -11,19 +11,17 @@ readme = "README.md" categories = ["command-line-utilities"] [dependencies] -retrolib = { path = "../lib" } anyhow = "1.0.69" argh = "0.1.10" -# astc-decode = "0.3.1" binrw = "0.11.1" binrw_derive = "0.11.1" ddsfile = { git = "https://github.com/encounter/ddsfile", rev = "880f04c1dffa680eab0e9e09cfa58591fe186a31" } env_logger = "0.10.0" gltf-json = { version = "1.1.0", features = ["names", "extras"] } half = "2.2.1" -# image = "0.24.5" log = "0.4.17" memmap2 = "0.5.8" +retrolib = { path = "../lib" } serde_json = "1.0.93" tegra_swizzle = "0.3.0" uuid = "1.3.0" diff --git a/retrotool/src/cmd/cmdl.rs b/retrotool/src/cmd/cmdl.rs index e161066..94ed7ef 100644 --- a/retrotool/src/cmd/cmdl.rs +++ b/retrotool/src/cmd/cmdl.rs @@ -1,5 +1,4 @@ use std::{ - borrow::Cow, collections::HashMap, fs, fs::DirBuilder, @@ -175,7 +174,7 @@ fn convert(args: ConvertArgs) -> Result<()> { continue; } - let mut reader = Cursor::new(buf.as_ref()); + let mut reader = Cursor::new(&*buf); let mut new_buf: Vec = Vec::with_capacity(info.vertex_count as usize * info.out_stride as usize); let mut tmp_buf = vec![0u8; 16]; // max size of attribute @@ -209,7 +208,7 @@ fn convert(args: ConvertArgs) -> Result<()> { } } } - *buf = Cow::Owned(new_buf); + *buf = new_buf; } DirBuilder::new().recursive(true).create(&args.out_dir)?;