From fb353d7ab401b420e1cd721ee530343251f3b37a Mon Sep 17 00:00:00 2001 From: Berkus Decker Date: Sun, 28 Jan 2024 22:59:35 +0200 Subject: [PATCH 001/120] Move conversion to png in a separate file --- src/convert_to_png.rs | 59 ++++++++++++++++++++++++++++++++++++++ src/main.rs | 66 ------------------------------------------- 2 files changed, 59 insertions(+), 66 deletions(-) create mode 100644 src/convert_to_png.rs diff --git a/src/convert_to_png.rs b/src/convert_to_png.rs new file mode 100644 index 0000000..99081a6 --- /dev/null +++ b/src/convert_to_png.rs @@ -0,0 +1,59 @@ +use { + crate::support::texture::PixelMap, + std::{fs::File, io::BufWriter, path::PathBuf}, +}; + +// /// Load palette once and then apply to a bunch of pixmap data +fn convert_all_pixmaps() -> Result<(), support::Error> { + let palette = + &PixelMap::load_from(String::from("DecodedData/DATA/REG/PALETTES/DRRENDER.PAL"))?[0]; + visit_dirs(Path::new("DecodedData"), &mut |dir_entry| { + if let Ok(file_type) = dir_entry.file_type() { + let fname = String::from(dir_entry.path().to_str().unwrap()); + if file_type.is_file() && fname.ends_with(".PIX") { + convert_pixmap(fname, palette).unwrap(); + } + } + }) +} + +fn convert_pixmap(fname: String, palette: &PixelMap) -> Result<(), support::Error> { + let pmap = PixelMap::load_from(fname.clone()) + .expect(format!("Couldnt open pix file {:?}", fname).as_ref()); + // let mut counter = 0; + for pix in pmap { + // counter += 1; + let mut pngname = PathBuf::from(&fname); + // let name = String::from(pngname.file_name().unwrap().to_str().unwrap()); + pngname.set_file_name(&pix.name); + pngname.set_extension("png"); + + info!("Creating file {:?}", pngname); + let file = File::create(&pngname) + .expect(format!("Couldnt create png file {:?}", pngname).as_ref()); + let w = &mut BufWriter::new(file); + + pix.write_png_remapped_via(palette, w) + .expect("Failed to write PNG"); + } + Ok(()) +} + +/// Uses different palette for race-selection part +fn convert_menu_pixmap(fname: String) -> Result<(), support::Error> { + let palette = + &PixelMap::load_from(String::from("DecodedData/DATA/REG/PALETTES/DRACEFLC.PAL"))?[0]; + convert_pixmap(fname, palette) +} + +fn convert_game_pixmap(fname: String) -> Result<(), support::Error> { + let palette = + &PixelMap::load_from(String::from("DecodedData/DATA/REG/PALETTES/DRRENDER.PAL"))?[0]; + convert_pixmap(fname, palette) +} + +fn main() { + convert_all_pixmaps().expect("Listing failed"); + convert_game_pixmap(String::from("DecodedData/DATA/PIXELMAP/EAGYELE.PIX")) + .expect("Conversion failed"); +} diff --git a/src/main.rs b/src/main.rs index 38ad975..5af324b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,10 +7,6 @@ // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) // pub mod support; - -#[cfg(feature = "convert")] -use crate::support::texture::PixelMap; - use { crate::support::{camera::CameraState, car::Car, render_manager::RenderManager}, cgmath::Vector3, @@ -55,8 +51,6 @@ fn setup_logging() -> Result<(), fern::InitError> { Ok(()) } -#[cfg(feature = "convert")] -use std::{fs::File, io::BufWriter, path::PathBuf}; use std::{ fs::{self, DirEntry}, path::Path, @@ -78,69 +72,9 @@ fn visit_dirs(dir: &Path, cb: &mut dyn for<'r> FnMut(&'r DirEntry)) -> Result<() Ok(()) } -#[cfg(feature = "convert")] -fn convert_pixmap(fname: String, palette: &PixelMap) -> Result<(), support::Error> { - let pmap = PixelMap::load_from(fname.clone()) - .expect(format!("Couldnt open pix file {:?}", fname).as_ref()); - // let mut counter = 0; - for pix in pmap { - // counter += 1; - let mut pngname = PathBuf::from(&fname); - // let name = String::from(pngname.file_name().unwrap().to_str().unwrap()); - pngname.set_file_name(&pix.name); - pngname.set_extension("png"); - - info!("Creating file {:?}", pngname); - let file = File::create(&pngname) - .expect(format!("Couldnt create png file {:?}", pngname).as_ref()); - let w = &mut BufWriter::new(file); - - pix.write_png_remapped_via(palette, w) - .expect("Failed to write PNG"); - } - Ok(()) -} - -/// Uses different palette for race-selection part -#[cfg(feature = "convert")] -fn convert_menu_pixmap(fname: String) -> Result<(), support::Error> { - let palette = - &PixelMap::load_from(String::from("DecodedData/DATA/REG/PALETTES/DRACEFLC.PAL"))?[0]; - convert_pixmap(fname, palette) -} - -#[cfg(feature = "convert")] -fn convert_game_pixmap(fname: String) -> Result<(), support::Error> { - let palette = - &PixelMap::load_from(String::from("DecodedData/DATA/REG/PALETTES/DRRENDER.PAL"))?[0]; - convert_pixmap(fname, palette) -} - -/// Load palette once and then apply to a bunch of pixmap data -#[cfg(feature = "convert")] -fn convert_all_pixmaps() -> Result<(), support::Error> { - let palette = - &PixelMap::load_from(String::from("DecodedData/DATA/REG/PALETTES/DRRENDER.PAL"))?[0]; - visit_dirs(Path::new("DecodedData"), &mut |dir_entry| { - if let Ok(file_type) = dir_entry.file_type() { - let fname = String::from(dir_entry.path().to_str().unwrap()); - if file_type.is_file() && fname.ends_with(".PIX") { - convert_pixmap(fname, palette).unwrap(); - } - } - }) -} - fn main() { setup_logging().expect("failed to initialize logging"); - #[cfg(feature = "convert")] - { - convert_all_pixmaps().expect("Listing failed"); - convert_game_pixmap(String::from("DecodedData/DATA/PIXELMAP/EAGYELE.PIX")) - .expect("Conversion failed"); - } - // Load all cars and arrange in a grid 6x7 (40 cars total) let mut cars = Vec::new(); From 14fe106a5899b9205d4767af02146c195b69bde5 Mon Sep 17 00:00:00 2001 From: Berkus Decker Date: Sun, 28 Jan 2024 23:01:44 +0200 Subject: [PATCH 002/120] Add bevy dependency --- Cargo.toml | 1 + src/main.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 548de26..5b503ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,4 +17,5 @@ log = "0.4" fern = "0.6" chrono = "0.4" png = "0.17" +bevy = "0.1" # todo: use failure diff --git a/src/main.rs b/src/main.rs index 5af324b..8295569 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,9 +6,9 @@ // Distributed under the Boost Software License, Version 1.0. // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) // -pub mod support; use { crate::support::{camera::CameraState, car::Car, render_manager::RenderManager}, + bevy::prelude::*, cgmath::Vector3, glium::{ glutin::{ @@ -18,8 +18,14 @@ use { Surface, }, log::info, + std::{ + fs::{self, DirEntry}, + path::Path, + }, }; +pub mod support; + fn setup_logging() -> Result<(), fern::InitError> { let base_config = fern::Dispatch::new().format(|out, message, record| { out.finish(format_args!( @@ -51,11 +57,6 @@ fn setup_logging() -> Result<(), fern::InitError> { Ok(()) } -use std::{ - fs::{self, DirEntry}, - path::Path, -}; - // one possible implementation of walking a directory only visiting files fn visit_dirs(dir: &Path, cb: &mut dyn for<'r> FnMut(&'r DirEntry)) -> Result<(), support::Error> { if dir.is_dir() { @@ -72,8 +73,23 @@ fn visit_dirs(dir: &Path, cb: &mut dyn for<'r> FnMut(&'r DirEntry)) -> Result<() Ok(()) } -fn main() { - setup_logging().expect("failed to initialize logging"); +fn setup_textures( + mut commands: Commands, + asset_server: Res, + mut textures: ResMut>, + mut texture_atlases: ResMut>, +) { + // bevy @todo: load all textures into the texture atlas + + let texture_handle = asset_server + .load_sync( + &mut textures, + "assets/textures/rpg/chars/gabe/gabe-idle-run.png", + ) + .unwrap(); + let texture = textures.get(&texture_handle).unwrap(); + let texture_atlas = TextureAtlas::from_grid(texture_handle, texture.size, 7, 1); + let texture_atlas_handle = texture_atlases.add(texture_atlas); // Load all cars and arrange in a grid 6x7 (40 cars total) @@ -99,7 +115,27 @@ fn main() { }) .unwrap(); - // Prepare window + commands + .spawn(Camera2dComponents::default()) + .spawn(SpriteSheetComponents { + texture_atlas: texture_atlas_handle, + scale: Scale(6.0), + ..Default::default() + }) + .with(Timer::from_seconds(0.1)); +} + +// @todo return result +fn main() { + setup_logging().expect("failed to initialize logging"); + + App::build() + .add_default_plugins() + .add_startup_system(setup_textures.system()) + .add_system(animate_camera.system()) + .run(); + + // Prepare window -- move to bevy init let mut events_loop = glium::glutin::event_loop::EventLoop::new(); let window = glium::glutin::window::WindowBuilder::new() From bdefdb13e3f361d470629b4602ed1d5c35417ff0 Mon Sep 17 00:00:00 2001 From: Berkus Decker Date: Sun, 28 Jan 2024 23:06:41 +0200 Subject: [PATCH 003/120] Convert parsing to remove unwraps() whenever possible --- Cargo.toml | 3 + src/main.rs | 102 +++++++----- src/support/car.rs | 361 ++++++++++++++++++++-------------------- src/support/material.rs | 2 +- src/support/mod.rs | 42 ++--- src/support/texture.rs | 42 +++-- 6 files changed, 283 insertions(+), 269 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5b503ba..61fc936 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,6 @@ chrono = "0.4" png = "0.17" bevy = "0.1" # todo: use failure +anyhow = "1.0" +thiserror = "1.0" +fehler = "1.0" diff --git a/src/main.rs b/src/main.rs index 8295569..e5794be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![feature(try_trait)] + // // Part of Roadkill Project. // @@ -8,8 +10,10 @@ // use { crate::support::{camera::CameraState, car::Car, render_manager::RenderManager}, + anyhow::{anyhow, Context, Error, Result}, bevy::prelude::*, cgmath::Vector3, + fehler::throws, glium::{ glutin::{ event::{Event, WindowEvent}, @@ -26,7 +30,8 @@ use { pub mod support; -fn setup_logging() -> Result<(), fern::InitError> { +#[throws(fern::InitError)] +fn setup_logging() { let base_config = fern::Dispatch::new().format(|out, message, record| { out.finish(format_args!( "{}[{}][{}] {}", @@ -53,53 +58,55 @@ fn setup_logging() -> Result<(), fern::InitError> { .chain(stdout_config) .chain(file_config) .apply()?; - - Ok(()) } // one possible implementation of walking a directory only visiting files -fn visit_dirs(dir: &Path, cb: &mut dyn for<'r> FnMut(&'r DirEntry)) -> Result<(), support::Error> { +#[throws] +fn visit_files(dir: &Path, cb: &mut dyn for<'r> FnMut(&'r DirEntry) -> Result<()>) { if dir.is_dir() { for entry in fs::read_dir(dir)? { let entry = entry?; let path = entry.path(); if path.is_dir() { - visit_dirs(&path, cb)?; + visit_files(&path, cb)?; } else { - cb(&entry); + cb(&entry)?; } } } - Ok(()) } -fn setup_textures( - mut commands: Commands, - asset_server: Res, - mut textures: ResMut>, - mut texture_atlases: ResMut>, -) { +fn setup_textures(// mut commands: Commands, + // asset_server: Res, + // mut textures: ResMut>, + // mut texture_atlases: ResMut>, +) -> Result> { // bevy @todo: load all textures into the texture atlas - - let texture_handle = asset_server - .load_sync( - &mut textures, - "assets/textures/rpg/chars/gabe/gabe-idle-run.png", - ) - .unwrap(); - let texture = textures.get(&texture_handle).unwrap(); - let texture_atlas = TextureAtlas::from_grid(texture_handle, texture.size, 7, 1); - let texture_atlas_handle = texture_atlases.add(texture_atlas); + // TextureAtlasBuilder + + // let texture_handle = asset_server + // .load_sync( + // &mut textures, + // "assets/textures/rpg/chars/gabe/gabe-idle-run.png", + // ) + // .unwrap(); + // let texture = textures.get(&texture_handle).unwrap(); + // let texture_atlas = TextureAtlas::from_grid(texture_handle, texture.size, 7, 1); + // let texture_atlas_handle = texture_atlases.add(texture_atlas); // Load all cars and arrange in a grid 6x7 (40 cars total) let mut cars = Vec::new(); let mut counter = 0; - visit_dirs(Path::new("DecodedData/DATA/CARS"), &mut |entry| { + visit_files(Path::new("DecodedData/DATA/CARS"), &mut |entry| { if let Ok(file_type) = entry.file_type() { - let fname = String::from(entry.path().to_str().unwrap()); + let fname = entry + .path() + .to_str() + .map(String::from) + .ok_or_else(|| anyhow!("Failed to make filename"))?; if file_type.is_file() && fname.ends_with(".ENC") { - let mut car = Car::load_from(fname).unwrap(); + let mut car = Car::load_from(fname)?; let z = 1.0f32 * f32::from(counter / 7); let x = 1.0f32 * f32::from(counter % 7 as u16); @@ -112,28 +119,31 @@ fn setup_textures( cars.push(car); } } - }) - .unwrap(); - - commands - .spawn(Camera2dComponents::default()) - .spawn(SpriteSheetComponents { - texture_atlas: texture_atlas_handle, - scale: Scale(6.0), - ..Default::default() - }) - .with(Timer::from_seconds(0.1)); + Ok(()) + })?; + + Ok(cars) + + // commands + // .spawn(Camera2dComponents::default()) + // .spawn(SpriteSheetComponents { + // texture_atlas: texture_atlas_handle, + // scale: Scale(6.0), + // ..Default::default() + // }) + // .with(Timer::from_seconds(0.1)); } -// @todo return result -fn main() { - setup_logging().expect("failed to initialize logging"); +fn main() -> Result<()> { + setup_logging().context("failed to initialize logging")?; + + // App::build() + // .add_default_plugins() + // .add_startup_system(setup_textures.system()) + // .add_system(animate_camera.system()) + // .run(); - App::build() - .add_default_plugins() - .add_startup_system(setup_textures.system()) - .add_system(animate_camera.system()) - .run(); + let cars = setup_textures()?; // Prepare window -- move to bevy init @@ -143,7 +153,7 @@ fn main() { .with_inner_size(glium::glutin::dpi::LogicalSize::new(800.0, 600.0)); let windowed_context = glium::glutin::ContextBuilder::new(); - let display = glium::Display::new(window, windowed_context, &events_loop).unwrap(); + let display = glium::Display::new(window, windowed_context, &events_loop)?; let mut render_manager = RenderManager::new(&display); for car in &cars { diff --git a/src/support/car.rs b/src/support/car.rs index 75ab917..097e44c 100644 --- a/src/support/car.rs +++ b/src/support/car.rs @@ -13,8 +13,8 @@ use { mesh::Mesh, path_subst, texture::PixelMap, - Error, }, + anyhow::{anyhow, Result}, cgmath::Vector3, log::*, std::{ @@ -37,64 +37,70 @@ pub struct Car { } /// Expect next line to match provided text exactly. -fn expect_match>(input: &mut Iter, text: &str) { +fn expect_match>(input: &mut Iter, text: &str) -> Result<()> { if let Some(line) = input.next() { if line == text { - return; + return Ok(()); } - panic!("Expected {:?} but got {:?}", text, line); + return Err(anyhow!("Expected {:?} but got {:?}", text, line)); } - panic!("Expected {:?} but got empty line", text); + Err(anyhow!("Expected {:?} but got empty line", text)) } /// Parse a three-component vector from a comma-separated string. -fn parse_vector(line: &String) -> Vector3 { +fn parse_vector(line: &String) -> Result> { let line: Vec = line.split(',').map(|i| i.trim().parse().unwrap()).collect(); - Vector3::from((line[0], line[1], line[2])) + Ok(Vector3::from((line[0], line[1], line[2]))) +} + +fn consume_line>(input: &mut Iter) -> Result { + input.next().ok_or(anyhow!("Bad input data")) } /// Read systems in a single damage spec clause. -fn read_systems>(input: &mut Iter) { +fn read_systems>(input: &mut Iter) -> Result<()> { // read condition flag for this clause /*let condition =*/ - input.next().unwrap(); + consume_line(input)?; // read systems count, read this many systems - let systems_count = input.next().unwrap().parse().unwrap(); + let systems_count = consume_line(input)?.parse()?; for _ in 0..systems_count { - input.next(); + consume_line(input)?; } + Ok(()) } /// Read all damage spec clauses. -fn read_clauses>(input: &mut Iter) { +fn read_clauses>(input: &mut Iter) -> Result<()> { // read clause count, read this many systems - let clause_count = input.next().unwrap().parse().unwrap(); + let clause_count = consume_line(input)?.parse()?; for _ in 0..clause_count { - read_systems(input); + read_systems(input)?; } + Ok(()) } /// Read a vector of strings. -fn read_vector>(input: &mut Iter) -> Vec { +fn read_vector>(input: &mut Iter) -> Result> { // read vector size, read this many strings - let size = input.next().unwrap().parse().unwrap(); + let size = consume_line(input)?.parse()?; let mut vec = Vec::::with_capacity(size); for _ in 0..size { - vec.push(input.next().unwrap()); + vec.push(consume_line(input)?); } - vec + Ok(vec) } -fn read_funk>(input: &mut Iter) { - expect_match(input, "START OF FUNK"); +fn read_funk>(input: &mut Iter) -> Result<()> { + expect_match(input, "START OF FUNK")?; // for now just ignore everything here, read until END OF FUNK loop { // @todo read funk loop with NEXT FUNK as trigger // read_funk(); // NEXT FUNK - let line = input.next().unwrap(); + let line = consume_line(input)?; if line == "END OF FUNK" { - return; + return Ok(()); } } } @@ -105,41 +111,42 @@ struct Groove {} // fn read_groove>(input: &mut Iter) -> Groove { // } -fn read_grooves>(input: &mut Iter) { - expect_match(input, "START OF GROOVE"); +fn read_grooves>(input: &mut Iter) -> Result<()> { + expect_match(input, "START OF GROOVE")?; // for now just ignore everything here, read until END OF GROOVE loop { // @todo read groove loop with NEXT GROOVE as trigger // read_groove(); // NEXT GROOVE - let line = input.next().unwrap(); + let line = consume_line(input)?; if line == "END OF GROOVE" { - return; + return Ok(()); } } } /// A bunch of some matrices and mappings or vertex-pairs, ignore for now. -fn read_some_metadata>(input: &mut Iter) { - input.next(); // 0.700000 - input.next(); // 0.050000,0.300000 - input.next(); // 0.050000 - input.next(); // 0.050000 - input.next(); // 0.000000 - input.next(); // 0.000000 - let size = input.next().unwrap().parse().unwrap(); +fn read_some_metadata>(input: &mut Iter) -> Result<()> { + consume_line(input)?; // 0.700000 + consume_line(input)?; // 0.050000,0.300000 + consume_line(input)?; // 0.050000 + consume_line(input)?; // 0.050000 + consume_line(input)?; // 0.000000 + consume_line(input)?; // 0.000000 + let size = consume_line(input)?.parse()?; for _ in 0..size { - input.next(); // 11 - input.next(); // -0.107444, -0.080211, 0.106640 - input.next(); // -0.057444, 0.054463, 0.206640 - input.next(); // 0.038245, 0.352418, 0.220975 - input.next(); // 0.111755, 0.051602, 0.079025 - let pair_count = input.next().unwrap().parse().unwrap(); + consume_line(input)?; // 11 + consume_line(input)?; // -0.107444, -0.080211, 0.106640 + consume_line(input)?; // -0.057444, 0.054463, 0.206640 + consume_line(input)?; // 0.038245, 0.352418, 0.220975 + consume_line(input)?; // 0.111755, 0.051602, 0.079025 + let pair_count = consume_line(input)?.parse()?; for _ in 0..pair_count { - input.next(); - input.next(); + consume_line(input)?; + consume_line(input)?; } } + Ok(()) } // @fixme used to patch actors now @@ -151,22 +158,20 @@ pub struct Mechanics { pub rfwheel_pos: Vector3, } -fn read_mechanics_block_v1_1>( - input: &mut Iter, -) -> Result { - let lrwheel_pos = parse_vector(&input.next().unwrap()); +fn read_mechanics_block_v1_1>(input: &mut Iter) -> Result { + let lrwheel_pos = parse_vector(&consume_line(input)?)?; trace!("Left rear wheel position: {:?}", lrwheel_pos); - let rrwheel_pos = parse_vector(&input.next().unwrap()); + let rrwheel_pos = parse_vector(&consume_line(input)?)?; trace!("Right rear wheel position: {:?}", rrwheel_pos); - let lfwheel_pos = parse_vector(&input.next().unwrap()); + let lfwheel_pos = parse_vector(&consume_line(input)?)?; trace!("Left front wheel position: {:?}", lfwheel_pos); - let rfwheel_pos = parse_vector(&input.next().unwrap()); + let rfwheel_pos = parse_vector(&consume_line(input)?)?; trace!("Right front wheel position: {:?}", rfwheel_pos); - let centre_of_mass_pos = parse_vector(&input.next().unwrap()); + let centre_of_mass_pos = parse_vector(&consume_line(input)?)?; trace!("Centre of mass position: {:?}", centre_of_mass_pos); Ok(Mechanics { @@ -177,87 +182,92 @@ fn read_mechanics_block_v1_1>( }) } -fn read_mechanics_block_v1_1_v3>(input: &mut Iter) { - let min_bb = parse_vector(&input.next().unwrap()); - let max_bb = parse_vector(&input.next().unwrap()); +fn read_mechanics_block_v1_1_v3>(input: &mut Iter) -> Result<()> { + let min_bb = parse_vector(&consume_line(input)?); + let max_bb = parse_vector(&consume_line(input)?); trace!("Bounding box: ({:?} - {:?})", min_bb, max_bb); + Ok(()) } // Version 2 contains count for bounding boxes (which is always 1, that's why it's removed in ver 3) -fn read_mechanics_block_v1_1_v2>(input: &mut Iter) { - expect_match(input, "1"); - read_mechanics_block_v1_1_v3(input); +fn read_mechanics_block_v1_1_v2>(input: &mut Iter) -> Result<()> { + expect_match(input, "1")?; + read_mechanics_block_v1_1_v3(input) } -fn read_mechanics_block_v1_2>(input: &mut Iter) { +fn read_mechanics_block_v1_2>(input: &mut Iter) -> Result<()> { // 0.5 // min turning circle radius - input.next(); + consume_line(input)?; // 0.025, 0.025 // suspension give (forward, back) - input.next(); + consume_line(input)?; // 0.090 // ride height (must be more than miny in bounding box ) - input.next(); + consume_line(input)?; // 0.5 // damping factor - input.next(); + consume_line(input)?; // 1.5 // mass in tonnes - input.next(); + consume_line(input)?; // 1 // fractional reduction in friction when slipping - input.next(); + consume_line(input)?; // 79, 80 // friction angle ( front and rear ) - input.next(); + consume_line(input)?; // 0.4, 0.2, 0.816 // width, height, length(0.816, 1.216) for angular momentum calculation - input.next(); + consume_line(input)?; + Ok(()) } -fn read_mechanics_block_v1_3>(input: &mut Iter) { +fn read_mechanics_block_v1_3>(input: &mut Iter) -> Result<()> { // 0.05, 0.05 // rolling resistance front and back - input.next(); + consume_line(input)?; // 6 // number of gears - input.next(); + consume_line(input)?; // 200 // speed at red line in highest gear - input.next(); + consume_line(input)?; // 4 // acceleration in highest gear m/s^2 (i.e. engine strength) - input.next(); + consume_line(input)?; + Ok(()) } -fn read_mechanics_block_v2>(input: &mut Iter) { +fn read_mechanics_block_v2>(input: &mut Iter) -> Result<()> { // 2.0 // traction fractional multiplier v. 2 - input.next(); + consume_line(input)?; // 50 // speed at which down force = weight v. 2 - input.next(); + consume_line(input)?; // 1.0 // brake multiplier, 1 = nomral brakes v. 2 - input.next(); + consume_line(input)?; // 1.0 // increase in brakes per second 1 = normal v. 2 - input.next(); + consume_line(input)?; + Ok(()) } -fn read_mechanics_block_v3>(input: &mut Iter) { +fn read_mechanics_block_v3>(input: &mut Iter) -> Result<()> { // 3 // 0,-0.18,-0.52 // extra point 1 v. 3 // -0.07,0.07,0.18 // extra point 2 v. 3 // 0.07,0.07,0.18 // extra point 3 v. 3 - read_vector(input); + read_vector(input)?; + Ok(()) } -fn read_mechanics_v2>(input: &mut Iter) -> Result { +fn read_mechanics_v2>(input: &mut Iter) -> Result { let mech = read_mechanics_block_v1_1(input)?; - read_mechanics_block_v1_1_v2(input); - read_mechanics_block_v1_2(input); - read_mechanics_block_v2(input); - read_mechanics_block_v1_3(input); + read_mechanics_block_v1_1_v2(input)?; + read_mechanics_block_v1_2(input)?; + read_mechanics_block_v2(input)?; + read_mechanics_block_v1_3(input)?; Ok(mech) } -fn read_mechanics_v3>(input: &mut Iter) -> Result { +fn read_mechanics_v3>(input: &mut Iter) -> Result { let mech = read_mechanics_block_v1_1(input)?; - read_mechanics_block_v1_1_v3(input); - read_mechanics_block_v3(input); - read_mechanics_block_v1_2(input); - read_mechanics_block_v2(input); - read_mechanics_block_v1_3(input); + read_mechanics_block_v1_1_v3(input)?; + read_mechanics_block_v3(input)?; + read_mechanics_block_v1_2(input)?; + read_mechanics_block_v2(input)?; + read_mechanics_block_v1_3(input)?; Ok(mech) } -fn read_mechanics_v4>(input: &mut Iter) -> Result { +fn read_mechanics_v4>(input: &mut Iter) -> Result { read_mechanics_v3(input) } @@ -265,7 +275,7 @@ fn read_meshes( fname: &String, load_models: &Vec, car_meshes: &mut HashMap, -) -> Result<(), Error> { +) -> Result<()> { let mut load_models = load_models.clone(); load_models.sort(); load_models.dedup(); @@ -279,15 +289,9 @@ fn read_meshes( &mesh_file_name, &Path::new("MODELS"), Some(String::from("DAT")), - ); - info!("### Opening mesh file {:?}", mesh_file_name); - let meshes = Mesh::load_from( - mesh_file_name - .clone() - .into_os_string() - .into_string() - .unwrap(), )?; + info!("### Opening mesh file {:?}", mesh_file_name); + let meshes = Mesh::load_from(mesh_file_name.clone().into_os_string().into_string()?)?; for mesh in meshes { car_meshes.insert(mesh.name.clone(), mesh); } @@ -295,25 +299,20 @@ fn read_meshes( Ok(()) } +// @todo this could be patched into the TextureBuilder fn read_materials( fname: &String, load_materials: &HashSet, car_materials: &mut HashMap, -) -> Result<(), Error> { +) -> Result<()> { for material in load_materials { let mut mat_file_name = PathBuf::from(&fname); mat_file_name.set_file_name(material); - let mat_file_name = path_subst(&mat_file_name, &Path::new("MATERIAL"), None); + let mat_file_name = path_subst(&mat_file_name, &Path::new("MATERIAL"), None)?; info!("### Opening material {:?}", mat_file_name); - let materials = Material::load_from( - mat_file_name - .clone() - .into_os_string() - .into_string() - .unwrap(), - )?; + let materials = Material::load_from(mat_file_name)?; for mat in materials { - car_materials.insert(mat.name.clone(), mat); + car_materials.insert(mat.name.clone(), mat); // @todo make this texture handles } } Ok(()) @@ -341,103 +340,111 @@ impl Car { } } - pub fn load_from(fname: String) -> Result { + pub fn load_from(fname: String) -> Result { // Load description file. let description_file_name = path_subst( &Path::new(fname.as_str()), &Path::new("CARS"), - Some(String::from("ENC")), - ); + Some("ENC".into()), + )?; info!("### Opening car {:?}", description_file_name); - let description_file = File::open(description_file_name)?; - let description_file = BufReader::new(description_file); + let description_file = BufReader::new(File::open(description_file_name)?); let mut input_lines = description_file .lines() - .map(|line| line.unwrap()) + .filter_map(|line| line.ok()) .filter(|line| !line.starts_with("//")) // Skip whole-line comments .filter(|line| !line.is_empty()) // Skip empty lines // Separate in-line comments from data - .map(|line| line.split("//").next().unwrap().trim().to_owned()); + .map(|line| { + line.split("//") + .next() + .map(|x| x.trim()) + .map(|x| x.to_owned()) + .unwrap_or("".into()) + }) + .filter(|line| !line.is_empty()); - let car_name = input_lines.next().unwrap(); + let car_name = consume_line(&mut input_lines)?; debug!("Car name {}", car_name); - expect_match(&mut input_lines, "START OF DRIVABLE STUFF"); + expect_match(&mut input_lines, "START OF DRIVABLE STUFF")?; - let driver_head_3d_offset = parse_vector(&input_lines.next().unwrap()); + let driver_head_3d_offset = parse_vector(&consume_line(&mut input_lines)?); trace!( "Offset of driver's head in 3D space {:?}", driver_head_3d_offset ); - let head_turn_angles = input_lines.next().unwrap(); + let head_turn_angles = consume_line(&mut input_lines)?; trace!( "Angles to turn to make head go left and right {}", head_turn_angles ); - let mirror_3d_offset_and_fov = input_lines.next().unwrap(); + let mirror_3d_offset_and_fov = consume_line(&mut input_lines)?; trace!( "Offset of 'mirror camera' in 3D space, viewing angle of mirror {}", mirror_3d_offset_and_fov ); - let pratcam_borders = input_lines.next().unwrap(); + let pratcam_borders = consume_line(&mut input_lines)?; trace!( "Pratcam border names (left, top, right, bottom) {}", pratcam_borders ); - expect_match(&mut input_lines, "END OF DRIVABLE STUFF"); + expect_match(&mut input_lines, "END OF DRIVABLE STUFF")?; - let engine_noise = input_lines.next().unwrap(); + let engine_noise = consume_line(&mut input_lines)?; trace!( "Engine noise (normal, enclosed space, underwater) {}", engine_noise ); - let stealworthy = input_lines.next().unwrap(); + let stealworthy = consume_line(&mut input_lines)?; trace!("Cannot be stolen (without cheat): {}", stealworthy); - read_clauses(&mut input_lines); - read_clauses(&mut input_lines); - read_clauses(&mut input_lines); - read_clauses(&mut input_lines); - read_clauses(&mut input_lines); - read_clauses(&mut input_lines); + read_clauses(&mut input_lines)?; + read_clauses(&mut input_lines)?; + read_clauses(&mut input_lines)?; + read_clauses(&mut input_lines)?; + read_clauses(&mut input_lines)?; + read_clauses(&mut input_lines)?; - let grid_image = input_lines.next().unwrap(); + let grid_image = consume_line(&mut input_lines)?; trace!("Grid image (opponent, frank, annie): {}", grid_image); - let mut load_pixmaps = read_vector(&mut input_lines); - load_pixmaps.append(&mut read_vector(&mut input_lines)); - load_pixmaps.append(&mut read_vector(&mut input_lines)); + let mut load_pixmaps = read_vector(&mut input_lines)?; + load_pixmaps.append(&mut read_vector(&mut input_lines)?); + load_pixmaps.append(&mut read_vector(&mut input_lines)?); - let load_shadetable = read_vector(&mut input_lines); + let load_shadetable = read_vector(&mut input_lines)?; debug!("Shadetable to load: {:?}", load_shadetable); - let mut load_materials = read_vector(&mut input_lines); - load_materials.append(&mut read_vector(&mut input_lines)); - load_materials.append(&mut read_vector(&mut input_lines)); + let mut load_materials = read_vector(&mut input_lines)?; + load_materials.append(&mut read_vector(&mut input_lines)?); + load_materials.append(&mut read_vector(&mut input_lines)?); - let mut load_models = read_vector(&mut input_lines); + let mut load_models = read_vector(&mut input_lines)?; - let load_actors = read_vector(&mut input_lines); - let load_actors: HashMap = load_actors + let load_actors: HashMap = read_vector(&mut input_lines)? .iter() .map(|act| act.split(",")) .map(|mut split| { ( - split.next().unwrap().parse().unwrap(), - String::from(split.next().unwrap()), + split.next().and_then(|id| id.parse().ok()).unwrap_or(0), + split + .next() + .map(|x| String::from(x)) + .unwrap_or_else(|| "".into()), ) }) .collect(); debug!("Actors to load: {:?}", load_actors); - let reflective_material = input_lines.next().unwrap(); + let reflective_material = consume_line(&mut input_lines)?; trace!( "Name of reflective screen material (or none if non-reflective): {}", reflective_material @@ -446,74 +453,73 @@ impl Car { // Number of steerable wheels // GroovyFunkRef of 1st steerable wheel -- this is index in the GROOVE array below // GroovyFunkRef of 2nd steerable wheel - let steerable_wheels = read_vector(&mut input_lines); + let steerable_wheels = read_vector(&mut input_lines)?; trace!("Steerable wheels GroovyFunkRefs: {:?}", steerable_wheels); - let lfsus_gfref = input_lines.next().unwrap(); + let lfsus_gfref = consume_line(&mut input_lines)?; trace!("Left-front suspension parts GroovyFunkRef: {}", lfsus_gfref); - let rfsus_gfref = input_lines.next().unwrap(); + let rfsus_gfref = consume_line(&mut input_lines)?; trace!( "Right-front suspension parts GroovyFunkRef: {}", rfsus_gfref ); - let lrsus_gfref = input_lines.next().unwrap(); + let lrsus_gfref = consume_line(&mut input_lines)?; trace!("Left-rear suspension parts GroovyFunkRef: {}", lrsus_gfref); - let rrsus_gfref = input_lines.next().unwrap(); + let rrsus_gfref = consume_line(&mut input_lines)?; trace!("Right-rear suspension parts GroovyFunkRef: {}", rrsus_gfref); - let driven_wheels_gfref = input_lines.next().unwrap(); + let driven_wheels_gfref = consume_line(&mut input_lines)?; trace!( "Driven wheels GroovyFunkRefs (for spinning) - MUST BE 4 ITEMS: {}", driven_wheels_gfref ); - let nondriven_wheels_gfref = input_lines.next().unwrap(); + let nondriven_wheels_gfref = consume_line(&mut input_lines)?; trace!( "Non-driven wheels GroovyFunkRefs (for spinning) - MUST BE 4 ITEMS: {}", nondriven_wheels_gfref ); - let driven_wheels_diameter = input_lines.next().unwrap(); + let driven_wheels_diameter = consume_line(&mut input_lines)?; trace!("Driven wheels diameter: {}", driven_wheels_diameter); - let nondriven_wheels_diameter = input_lines.next().unwrap(); + let nondriven_wheels_diameter = consume_line(&mut input_lines)?; trace!("Non-driven wheels diameter: {}", nondriven_wheels_diameter); - read_funk(&mut input_lines); - read_grooves(&mut input_lines); + read_funk(&mut input_lines)?; + read_grooves(&mut input_lines)?; - read_some_metadata(&mut input_lines); - read_some_metadata(&mut input_lines); - read_some_metadata(&mut input_lines); + read_some_metadata(&mut input_lines)?; + read_some_metadata(&mut input_lines)?; + read_some_metadata(&mut input_lines)?; - let mechanics = input_lines.next().unwrap(); + let mechanics = consume_line(&mut input_lines)?; if !mechanics.starts_with("START OF MECHANICS STUFF") { - panic!( + return Err(anyhow!( "Expected START OF MECHANICS STUFF, got {:?} instead", mechanics - ); + )); } let version = mechanics .split(" version ") .skip(1) .next() - .unwrap() - .parse() - .unwrap(); + .map(|x| x.parse()) + .ok_or(anyhow!("Bad input data"))??; let _mech = match version { 2 => read_mechanics_v2(&mut input_lines), 3 => read_mechanics_v3(&mut input_lines), 4 => read_mechanics_v4(&mut input_lines), - x => panic!("Unsupported mechanics version {}", x), + x => return Err(anyhow!("Unsupported mechanics version {}", x)), }?; - expect_match(&mut input_lines, "END OF MECHANICS STUFF"); + expect_match(&mut input_lines, "END OF MECHANICS STUFF")?; - let some_materials = read_vector(&mut input_lines); + let some_materials = read_vector(&mut input_lines)?; debug!("Some other materials to use: {:?}", some_materials); // @todo More post-mechanics stuff @@ -536,9 +542,9 @@ impl Car { &actor_file_name, &Path::new("ACTORS"), Some(String::from("ACT")), - ); + )?; info!("### Opening actor {:?}", actor_file_name); - let car_actors = Actor::load_from(actor_file_name.into_os_string().into_string().unwrap())?; + let car_actors = Actor::load_from(actor_file_name)?; // Read meshes referenced from actor file load_models.clear(); @@ -580,10 +586,9 @@ impl Car { // Load palette from PIX file. let mut pal_file_name = PathBuf::from(&fname); pal_file_name.set_file_name("DRRENDER.PAL"); - let pal_file_name = path_subst(&pal_file_name, &Path::new("REG/PALETTES"), None); + let pal_file_name = path_subst(&pal_file_name, &Path::new("REG/PALETTES"), None)?; info!("### Opening palette {:?}", pal_file_name); - let palette = - &PixelMap::load_from(pal_file_name.into_os_string().into_string().unwrap())?[0]; + let palette = &PixelMap::load_from(pal_file_name)?[0]; for x in 0..palette.units { trace!( @@ -599,17 +604,11 @@ impl Car { for pixmap in load_pixmaps { let mut pix_file_name = PathBuf::from(&fname); pix_file_name.set_file_name(pixmap); - let pix_file_name = path_subst(&pix_file_name, &Path::new("PIXELMAP"), None); + let pix_file_name = path_subst(&pix_file_name, &Path::new("PIXELMAP"), None)?; info!("### Opening pixelmap {:?}", pix_file_name); - let pix = PixelMap::load_from( - pix_file_name - .clone() - .into_os_string() - .into_string() - .unwrap(), - )?; + let pix = PixelMap::load_from(pix_file_name)?; for pmap in pix { - let pmap = pmap.remap_via(&palette)?; + let pmap = pmap.remap_via_palette(&palette)?; car_textures.insert(pmap.name.clone(), pmap); } } diff --git a/src/support/material.rs b/src/support/material.rs index a4edbe9..d2b6b2a 100644 --- a/src/support/material.rs +++ b/src/support/material.rs @@ -66,7 +66,7 @@ impl Material { /** * Load multiple materials from a file. */ - pub fn load_from(fname: String) -> Result, Error> { + pub fn load_from>(fname: P) -> Result, Error> { let file = File::open(fname)?; let mut file = BufReader::new(file); let mut materials = Vec::::new(); diff --git a/src/support/mod.rs b/src/support/mod.rs index c477382..52479e9 100644 --- a/src/support/mod.rs +++ b/src/support/mod.rs @@ -12,6 +12,7 @@ // extern crate obj; use { + anyhow::{anyhow, Result}, byteorder::{BigEndian, ReadBytesExt}, cgmath::Vector3, glium::implement_vertex, @@ -24,6 +25,7 @@ use { thread, time::{Duration, Instant}, }, + thiserror::Error as ThisError, }; pub mod actor; @@ -69,30 +71,14 @@ impl Sub for Vertex { } } -// @todo use failure -#[derive(Debug)] +#[derive(Debug, ThisError)] pub enum Error { - IO(std::io::Error), - Utf8(std::str::Utf8Error), - FromUtf8(std::string::FromUtf8Error), -} - -impl From for Error { - fn from(error: std::io::Error) -> Self { - Error::IO(error) - } -} - -impl From for Error { - fn from(error: std::str::Utf8Error) -> Self { - Error::Utf8(error) - } -} - -impl From for Error { - fn from(error: std::string::FromUtf8Error) -> Self { - Error::FromUtf8(error) - } + #[error("i/o error {0:?}")] + IO(#[from] std::io::Error), + #[error("utf-8 conversion error {0:?}")] + Utf8(#[from] std::str::Utf8Error), + #[error("from utf-8 conversion error {0:?}")] + FromUtf8(#[from] std::string::FromUtf8Error), } pub enum Action { @@ -139,11 +125,13 @@ pub fn read_c_string(reader: &mut R) -> Result { } /* - * Creates a pathname to filepath with the last directory replaced to newdir + * Creates a pathname to filepath with the last directory replaced by newdir * and optionally changing extension to newext. */ -pub fn path_subst(filepath: &Path, newdir: &Path, newext: Option) -> PathBuf { - let fname = filepath.file_name().unwrap(); +pub fn path_subst(filepath: &Path, newdir: &Path, newext: Option) -> Result { + let fname = filepath + .file_name() + .ok_or_else(|| anyhow!("Can't parse filename"))?; let mut dir = PathBuf::from(filepath); dir.pop(); // remove file name dir.pop(); // remove parent dir @@ -152,7 +140,7 @@ pub fn path_subst(filepath: &Path, newdir: &Path, newext: Option) -> Pat if let Some(ext) = newext { dir.set_extension(ext); } - return dir; + Ok(dir) } // Returns a vertex buffer that should be rendered as `TrianglesList`. diff --git a/src/support/texture.rs b/src/support/texture.rs index 57553db..bc6b04e 100644 --- a/src/support/texture.rs +++ b/src/support/texture.rs @@ -1,7 +1,7 @@ // // Part of Roadkill Project. // -// Copyright 2010, 2017, Stanislav Karchebnyy +// Copyright 2010, 2017, 2020 Berkus Karchebnyy // // Distributed under the Boost Software License, Version 1.0. // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,7 +10,6 @@ use { crate::support::{self, resource::Chunk, Error}, byteorder::ReadBytesExt, log::*, - png, std::{ fs::File, io::{BufRead, BufReader, Write}, @@ -41,10 +40,14 @@ impl std::fmt::Display for PixelMap { } } +// @todo impl From for bevy::Texture {} + /** - * Megatexture for storing all loaded textures. - * Usually 1024x1024 or 4096x4096 texture with multiple smaller textures inside. - */ +* Megatexture for storing all loaded textures. +* Usually 1024x1024 or 4096x4096 texture with multiple smaller textures inside. + +@todo drop local Texture and use bevy::TextureAtlas +*/ #[derive(Default)] pub struct Texture { pub w: u16, @@ -53,8 +56,11 @@ pub struct Texture { } /** - * Named reference into the megatexture. - */ +* Named reference into the megatexture. + +@todo use bevy Handle or sth like this to get the texture out of the atlas +We need only u and v for the texture because it's all already part of the megatexture. +*/ pub struct TextureReference { pub id: i32, pub x0: f32, @@ -66,7 +72,7 @@ pub struct TextureReference { impl PixelMap { /// Convert indexed-color image to RGBA using provided palette. - pub fn remap_via(&self, palette: &PixelMap) -> Result { + pub fn remap_via_palette(&self, palette: &PixelMap) -> Result { let mut pm = self.clone(); pm.data = Vec::::with_capacity(self.data.len() * 4); pm.unit_bytes = 4; @@ -101,6 +107,7 @@ impl PixelMap { Ok(pm) } + #[cfg(convert)] pub fn write_png_remapped_via( &self, palette: &PixelMap, @@ -159,13 +166,13 @@ impl PixelMap { Ok(()) } - pub fn load(rdr: &mut R) -> Result { + pub fn load(reader: &mut R) -> Result { let mut pm = PixelMap::default(); // Read chunks until last chunk is encountered. // Certain chunks initialize certain properties. loop { - let c = Chunk::load(rdr)?; + let c = Chunk::load(reader)?; match c { Chunk::PixelmapHeader { name, @@ -174,12 +181,15 @@ impl PixelMap { mipmap_w, mipmap_h, } => { - pm.name = name; + pm.name = name.clone(); pm.w = w; pm.h = h; pm.use_w = mipmap_w; pm.use_h = mipmap_h; - debug!("Pixelmap {}x{} use {}x{}", w, h, mipmap_w, mipmap_h); + debug!( + "Pixelmap {} ({}x{} use {}x{})", + name, w, h, mipmap_w, mipmap_h + ); } Chunk::PixelmapData { units, @@ -189,7 +199,10 @@ impl PixelMap { pm.units = units; pm.unit_bytes = unit_bytes; pm.data = data; - debug!("Pixelmap data {} units, {} bytes each", units, unit_bytes); + debug!( + "Pixelmap data in {} units, {} bytes each", + units, unit_bytes + ); } Chunk::Null() => break, Chunk::FileHeader { file_type } => { @@ -204,7 +217,8 @@ impl PixelMap { Ok(pm) } - pub fn load_from(fname: String) -> Result, Error> { + /// Load one or more named textures from a single file + pub fn load_from>(fname: P) -> Result, Error> { let file = File::open(fname)?; let mut file = BufReader::new(file); let mut pmaps = Vec::::new(); From 2aa7fdbd7b55e6ea2246c3f533de079cfaaedec2 Mon Sep 17 00:00:00 2001 From: Berkus Decker Date: Fri, 28 Aug 2020 02:24:35 +0300 Subject: [PATCH 004/120] Start of bevy loader for assets --- src/main.rs | 183 +++++++++++++++++++++++++++++----------------------- 1 file changed, 103 insertions(+), 80 deletions(-) diff --git a/src/main.rs b/src/main.rs index e5794be..18c929a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ // Distributed under the Boost Software License, Version 1.0. // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) // +use bevy::asset::{AssetLoadRequestHandler, LoadRequest}; use { crate::support::{camera::CameraState, car::Car, render_manager::RenderManager}, anyhow::{anyhow, Context, Error, Result}, @@ -76,14 +77,35 @@ fn visit_files(dir: &Path, cb: &mut dyn for<'r> FnMut(&'r DirEntry) -> Result<() } } -fn setup_textures(// mut commands: Commands, - // asset_server: Res, +struct CarHandler; + +impl AssetLoadRequestHandler for CarHandler { + fn handle_request(&self, load_request: &LoadRequest) { + let mut car = Car::load_from(load_request.path.clone()).unwrap(); + unimplemented!() + } + + fn extensions(&self) -> &[&str] { + &["ENC"] + } +} + +fn setup_textures( + mut commands: Commands, + mut asset_server: ResMut, // mut textures: ResMut>, // mut texture_atlases: ResMut>, -) -> Result> { +) /*-> Result>*/ +{ // bevy @todo: load all textures into the texture atlas // TextureAtlasBuilder + // add handler for ENC assets, then + asset_server.add_handler(CarHandler); + asset_server + .load_asset_folder("DecodedData/DATA/CARS") + .unwrap(); + // let texture_handle = asset_server // .load_sync( // &mut textures, @@ -96,33 +118,33 @@ fn setup_textures(// mut commands: Commands, // Load all cars and arrange in a grid 6x7 (40 cars total) - let mut cars = Vec::new(); - let mut counter = 0; - visit_files(Path::new("DecodedData/DATA/CARS"), &mut |entry| { - if let Ok(file_type) = entry.file_type() { - let fname = entry - .path() - .to_str() - .map(String::from) - .ok_or_else(|| anyhow!("Failed to make filename"))?; - if file_type.is_file() && fname.ends_with(".ENC") { - let mut car = Car::load_from(fname)?; - - let z = 1.0f32 * f32::from(counter / 7); - let x = 1.0f32 * f32::from(counter % 7 as u16); - counter += 1; - - info!("Moving car {} to {},0,{}", counter, x, -z); - - car.base_translation = Vector3::from([x, 0f32, -z]); - - cars.push(car); - } - } - Ok(()) - })?; - - Ok(cars) + // let mut cars = Vec::new(); + // let mut counter = 0; + // visit_files(Path::new("DecodedData/DATA/CARS"), &mut |entry| { + // if let Ok(file_type) = entry.file_type() { + // let fname = entry + // .path() + // .to_str() + // .map(String::from) + // .ok_or_else(|| anyhow!("Failed to make filename"))?; + // if file_type.is_file() && fname.ends_with(".ENC") { + // let mut car = Car::load_from(fname)?; + // + // let z = 1.0f32 * f32::from(counter / 7); + // let x = 1.0f32 * f32::from(counter % 7 as u16); + // counter += 1; + // + // info!("Moving car {} to {},0,{}", counter, x, -z); + // + // car.base_translation = Vector3::from([x, 0f32, -z]); + // + // cars.push(car); + // } + // } + // Ok(()) + // })?; + // + // Ok(cars) // commands // .spawn(Camera2dComponents::default()) @@ -134,59 +156,60 @@ fn setup_textures(// mut commands: Commands, // .with(Timer::from_seconds(0.1)); } -fn main() -> Result<()> { +#[throws] +fn main() { setup_logging().context("failed to initialize logging")?; - // App::build() - // .add_default_plugins() - // .add_startup_system(setup_textures.system()) - // .add_system(animate_camera.system()) - // .run(); - - let cars = setup_textures()?; + // let cars = setup_textures()?; // Prepare window -- move to bevy init - let mut events_loop = glium::glutin::event_loop::EventLoop::new(); - let window = glium::glutin::window::WindowBuilder::new() - .with_title("carma") - .with_inner_size(glium::glutin::dpi::LogicalSize::new(800.0, 600.0)); - let windowed_context = glium::glutin::ContextBuilder::new(); - - let display = glium::Display::new(window, windowed_context, &events_loop)?; - - let mut render_manager = RenderManager::new(&display); - for car in &cars { - render_manager.prepare_car(car, &display); - } - - let mut camera = CameraState::new(); - - events_loop.run(move |event, _, control_flow| { - println!("{:?}", event); - *control_flow = ControlFlow::Wait; - - camera.update(); - - match event { - Event::LoopDestroyed => return, - Event::WindowEvent { event, .. } => match event { - // WindowEvent::Resized(physical_size) => display.resize(physical_size), - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - _ => camera.process_input(&event), - }, - Event::RedrawRequested(_) => { - let mut frame = display.draw(); - frame.clear_color(0.4, 0.4, 0.4, 0.0); - frame.clear_depth(1.0); - - for car in &cars { - render_manager.draw_car(car, &mut frame, &camera); - } - frame.finish().unwrap(); - // windowed_context.swap_buffers().unwrap(); - } - _ => (), - } - }); + // let mut events_loop = glium::glutin::event_loop::EventLoop::new(); + // let window = glium::glutin::window::WindowBuilder::new() + // .with_title("carma") + // .with_inner_size(glium::glutin::dpi::LogicalSize::new(800.0, 600.0)); + // let windowed_context = glium::glutin::ContextBuilder::new(); + // + // let display = glium::Display::new(window, windowed_context, &events_loop)?; + // + // let mut render_manager = RenderManager::new(&display); + // for car in &cars { + // render_manager.prepare_car(car, &display); + // } + // + // let mut camera = CameraState::new(); + + App::build() + .add_default_plugins() + .add_startup_system(setup_textures.system()) + // .add_system(animate_camera.system()) + .run() + + // events_loop.run(move |event, _, control_flow| { + // println!("{:?}", event); + // *control_flow = ControlFlow::Wait; + // + // camera.update(); + // + // match event { + // Event::LoopDestroyed => return, + // Event::WindowEvent { event, .. } => match event { + // // WindowEvent::Resized(physical_size) => display.resize(physical_size), + // WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + // _ => camera.process_input(&event), + // }, + // Event::RedrawRequested(_) => { + // let mut frame = display.draw(); + // frame.clear_color(0.4, 0.4, 0.4, 0.0); + // frame.clear_depth(1.0); + // + // for car in &cars { + // render_manager.draw_car(car, &mut frame, &camera); + // } + // frame.finish().unwrap(); + // // windowed_context.swap_buffers().unwrap(); + // } + // _ => (), + // } + // }); } From 0a51f5e5e305911943a954b401d33c9acfb3ca66 Mon Sep 17 00:00:00 2001 From: Berkus Decker Date: Fri, 28 Aug 2020 02:25:12 +0300 Subject: [PATCH 005/120] Add todo --- src/support/render_manager.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/support/render_manager.rs b/src/support/render_manager.rs index d45288a..7df7bf4 100644 --- a/src/support/render_manager.rs +++ b/src/support/render_manager.rs @@ -28,6 +28,8 @@ pub struct RenderManager { bound_textures: HashMap>, // MaterialId -> texture program: Program, } +// @todo these become resources +// esp the shader program fn debug_tree(name: &String, actor_name: &String, stack: &Vec>) { debug!("{} for {}: stack depth {}", name, actor_name, stack.len()); From 562bb13046bbe9009b135694b0348882d7057fec Mon Sep 17 00:00:00 2001 From: Berkus Decker Date: Fri, 28 Aug 2020 02:25:29 +0300 Subject: [PATCH 006/120] Drop old unused code --- src/support/mod.rs | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/src/support/mod.rs b/src/support/mod.rs index 52479e9..7ed86e7 100644 --- a/src/support/mod.rs +++ b/src/support/mod.rs @@ -143,49 +143,6 @@ pub fn path_subst(filepath: &Path, newdir: &Path, newext: Option) -> Res Ok(dir) } -// Returns a vertex buffer that should be rendered as `TrianglesList`. -// pub fn load_wavefront(display: &Display, data: &[u8]) -> VertexBufferAny { -// #[derive(Copy, Clone)] -// struct Vertex { -// position: [f32; 3], -// normal: [f32; 3], -// texture: [f32; 2], -// } - -// implement_vertex!(Vertex, position, normal, texture); - -// let mut data = ::std::io::BufReader::new(data); -// let data = obj::Obj::load(&mut data); - -// let mut vertex_data = Vec::new(); - -// for object in data.object_iter() { -// for shape in object.group_iter().flat_map(|g| g.indices().iter()) { -// match shape { -// &genmesh::Polygon::PolyTri(genmesh::Triangle { x: v1, y: v2, z: v3 }) => { -// for v in [v1, v2, v3].iter() { -// let position = data.position()[v.0]; -// let texture = v.1.map(|index| data.texture()[index]); -// let normal = v.2.map(|index| data.normal()[index]); - -// let texture = texture.unwrap_or([0.0, 0.0]); -// let normal = normal.unwrap_or([0.0, 0.0, 0.0]); - -// vertex_data.push(Vertex { -// position: position, -// normal: normal, -// texture: texture, -// }) -// } -// }, -// _ => unimplemented!() -// } -// } -// } - -// glium::vertex::VertexBuffer::new(display, &vertex_data).unwrap().into_vertex_buffer_any() -// } - pub const NULL_CHUNK: u32 = 0x0; pub const PIXELMAP_HEADER_CHUNK: u32 = 0x3; pub const MATERIAL_DESC_CHUNK: u32 = 0x4; From e247275e9e1555405a434badc3bab6b73f42765b Mon Sep 17 00:00:00 2001 From: Berkus Decker Date: Sun, 28 Jan 2024 23:06:41 +0200 Subject: [PATCH 007/120] Refactor path representation to use AsRef bound --- src/support/actor.rs | 2 +- src/support/car.rs | 58 ++++++++++++++++++++------------------------ src/support/mod.rs | 20 +++++++++------ 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/support/actor.rs b/src/support/actor.rs index 0adf908..e8997a3 100644 --- a/src/support/actor.rs +++ b/src/support/actor.rs @@ -183,7 +183,7 @@ impl Actor { Ok(actor) } - pub fn load_from(fname: String) -> Result { + pub fn load_from>(fname: P) -> Result { let file = File::open(fname)?; let mut file = BufReader::new(file); let m = Actor::load(&mut file)?; diff --git a/src/support/car.rs b/src/support/car.rs index 097e44c..a97673e 100644 --- a/src/support/car.rs +++ b/src/support/car.rs @@ -271,8 +271,8 @@ fn read_mechanics_v4>(input: &mut Iter) -> Result< read_mechanics_v3(input) } -fn read_meshes( - fname: &String, +fn read_meshes>( + fname: P, load_models: &Vec, car_meshes: &mut HashMap, ) -> Result<()> { @@ -283,15 +283,17 @@ fn read_meshes( // Now iterate all meshes and load them. for mesh in load_models { - let mut mesh_file_name = PathBuf::from(&fname); + let mut mesh_file_name = fname.as_ref().to_path_buf(); mesh_file_name.set_file_name(mesh); - let mesh_file_name = path_subst( - &mesh_file_name, - &Path::new("MODELS"), - Some(String::from("DAT")), - )?; + let mesh_file_name = path_subst(mesh_file_name, "MODELS".into(), Some("DAT".into()))?; info!("### Opening mesh file {:?}", mesh_file_name); - let meshes = Mesh::load_from(mesh_file_name.clone().into_os_string().into_string()?)?; + let meshes = Mesh::load_from( + mesh_file_name + .clone() + .into_os_string() + .into_string() + .unwrap(), + )?; for mesh in meshes { car_meshes.insert(mesh.name.clone(), mesh); } @@ -300,15 +302,15 @@ fn read_meshes( } // @todo this could be patched into the TextureBuilder -fn read_materials( - fname: &String, +fn read_materials>( + fname: P, load_materials: &HashSet, car_materials: &mut HashMap, ) -> Result<()> { for material in load_materials { - let mut mat_file_name = PathBuf::from(&fname); + let mut mat_file_name = fname.as_ref().to_path_buf(); mat_file_name.set_file_name(material); - let mat_file_name = path_subst(&mat_file_name, &Path::new("MATERIAL"), None)?; + let mat_file_name = path_subst(mat_file_name, "MATERIAL".into(), None)?; info!("### Opening material {:?}", mat_file_name); let materials = Material::load_from(mat_file_name)?; for mat in materials { @@ -340,16 +342,12 @@ impl Car { } } - pub fn load_from(fname: String) -> Result { + pub fn load_from + std::fmt::Debug>(fname: P) -> Result { // Load description file. - let description_file_name = path_subst( - &Path::new(fname.as_str()), - &Path::new("CARS"), - Some("ENC".into()), - )?; - info!("### Opening car {:?}", description_file_name); + // let description_file_name = path_subst(fname, "CARS", Some("ENC".into()))?; + info!("### Opening car {:?}", fname); - let description_file = BufReader::new(File::open(description_file_name)?); + let description_file = BufReader::new(File::open(fname.as_ref())?); let mut input_lines = description_file .lines() @@ -532,17 +530,13 @@ impl Car { debug!("Meshes to load: {:?}", load_models); // Read meshes referenced from text description - read_meshes(&fname, &load_models, &mut car_meshes)?; + read_meshes(fname.as_ref(), &load_models, &mut car_meshes)?; // Load actor file. - let mut actor_file_name = PathBuf::from(&fname); + let mut actor_file_name = fname.as_ref().to_path_buf(); let idx: isize = 0; actor_file_name.set_file_name(&load_actors[&idx]); // Read mipmap 0 actor - let actor_file_name = path_subst( - &actor_file_name, - &Path::new("ACTORS"), - Some(String::from("ACT")), - )?; + let actor_file_name = path_subst(actor_file_name, "ACTORS".into(), Some("ACT".into()))?; info!("### Opening actor {:?}", actor_file_name); let car_actors = Actor::load_from(actor_file_name)?; @@ -584,9 +578,9 @@ impl Car { read_materials(&fname, &load_materials, &mut car_materials)?; // Load palette from PIX file. - let mut pal_file_name = PathBuf::from(&fname); + let mut pal_file_name = fname.as_ref().to_path_buf(); pal_file_name.set_file_name("DRRENDER.PAL"); - let pal_file_name = path_subst(&pal_file_name, &Path::new("REG/PALETTES"), None)?; + let pal_file_name = path_subst(pal_file_name, "REG/PALETTES".into(), None)?; info!("### Opening palette {:?}", pal_file_name); let palette = &PixelMap::load_from(pal_file_name)?[0]; @@ -602,9 +596,9 @@ impl Car { let mut car_textures = HashMap::::new(); for pixmap in load_pixmaps { - let mut pix_file_name = PathBuf::from(&fname); + let mut pix_file_name = fname.as_ref().to_path_buf(); pix_file_name.set_file_name(pixmap); - let pix_file_name = path_subst(&pix_file_name, &Path::new("PIXELMAP"), None)?; + let pix_file_name = path_subst(pix_file_name, "PIXELMAP".into(), None)?; info!("### Opening pixelmap {:?}", pix_file_name); let pix = PixelMap::load_from(pix_file_name)?; for pmap in pix { diff --git a/src/support/mod.rs b/src/support/mod.rs index 7ed86e7..38bcdb4 100644 --- a/src/support/mod.rs +++ b/src/support/mod.rs @@ -128,15 +128,21 @@ pub fn read_c_string(reader: &mut R) -> Result { * Creates a pathname to filepath with the last directory replaced by newdir * and optionally changing extension to newext. */ -pub fn path_subst(filepath: &Path, newdir: &Path, newext: Option) -> Result { - let fname = filepath - .file_name() - .ok_or_else(|| anyhow!("Can't parse filename"))?; - let mut dir = PathBuf::from(filepath); - dir.pop(); // remove file name +pub fn path_subst>( + filepath: P, + newdir: P, + newext: Option, +) -> Result { + let fname = filepath.as_ref().file_name(); + let mut dir = filepath.as_ref().to_path_buf(); + if let Some(_) = fname { + dir.pop(); // remove file name + } dir.pop(); // remove parent dir dir.push(newdir); // replace parent dir - dir.push(fname); // add back file name + if let Some(fname) = fname { + dir.push(fname); // add back file name + } if let Some(ext) = newext { dir.set_extension(ext); } From e4ffdcecd01258082f7cc4f6237e1bbc1ce2cd85 Mon Sep 17 00:00:00 2001 From: Berkus Decker Date: Mon, 31 Aug 2020 03:09:19 +0300 Subject: [PATCH 008/120] [wip] Load Cars via an AssetLoader --- src/main.rs | 40 +++++++++++++++++++++++----------------- src/support/car.rs | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/main.rs b/src/main.rs index 18c929a..91e4adc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![feature(try_trait)] +#![allow(unused_imports)] // // Part of Roadkill Project. @@ -8,9 +9,12 @@ // Distributed under the Boost Software License, Version 1.0. // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) // -use bevy::asset::{AssetLoadRequestHandler, LoadRequest}; use { - crate::support::{camera::CameraState, car::Car, render_manager::RenderManager}, + crate::support::{ + camera::CameraState, + car::{Car, CarLoader}, + render_manager::RenderManager, + }, anyhow::{anyhow, Context, Error, Result}, bevy::prelude::*, cgmath::Vector3, @@ -77,20 +81,14 @@ fn visit_files(dir: &Path, cb: &mut dyn for<'r> FnMut(&'r DirEntry) -> Result<() } } -struct CarHandler; - -impl AssetLoadRequestHandler for CarHandler { - fn handle_request(&self, load_request: &LoadRequest) { - let mut car = Car::load_from(load_request.path.clone()).unwrap(); - unimplemented!() - } +// @todo these are all resource types under support, just implement AssetLoadRequestHandler for them? +// ACT +// DAT +// MAT +// PAL +// PIX - fn extensions(&self) -> &[&str] { - &["ENC"] - } -} - -fn setup_textures( +fn setup_cars( mut commands: Commands, mut asset_server: ResMut, // mut textures: ResMut>, @@ -100,8 +98,14 @@ fn setup_textures( // bevy @todo: load all textures into the texture atlas // TextureAtlasBuilder + // I'd say use the bevy_gltf crate/plugin as a reference: https://github.com/bevyengine/bevy/tree/master/crates/bevy_gltf/src + // Specifically, you need to implement AssetLoader for MyAssetLoader and call: + // app.add_asset::() + // .add_asset_loader::(); + // add handler for ENC assets, then - asset_server.add_handler(CarHandler); + // asset_server.add_handler(crate::support::car::CarLoadRequestHandler); + // asset_server.add_loader(crate::support::car::CarLoader); asset_server .load_asset_folder("DecodedData/DATA/CARS") .unwrap(); @@ -181,7 +185,9 @@ fn main() { App::build() .add_default_plugins() - .add_startup_system(setup_textures.system()) + .add_asset::() + .add_asset_loader::() + .add_startup_system(setup_cars.system()) // .add_system(animate_camera.system()) .run() diff --git a/src/support/car.rs b/src/support/car.rs index a97673e..f3b832f 100644 --- a/src/support/car.rs +++ b/src/support/car.rs @@ -14,7 +14,8 @@ use { path_subst, texture::PixelMap, }, - anyhow::{anyhow, Result}, + anyhow::{anyhow, Error as AnyError, Result}, + bevy::asset::{/*AssetLoadRequestHandler,*/ AssetLoader, LoadRequest}, cgmath::Vector3, log::*, std::{ @@ -24,6 +25,7 @@ use { iter::Iterator, path::{Path, PathBuf}, }, + thiserror::Error as ThisError, }; // Car assembles the gameplay object (a car in this case) from various model and texture files. @@ -36,6 +38,35 @@ pub struct Car { pub base_translation: Vector3, } +// pub struct CarLoadRequestHandler; +// +// impl AssetLoadRequestHandler for CarLoadRequestHandler { +// fn handle_request(&self, load_request: &LoadRequest) { +// // @todo what to do here? +// // let car = Car::load_from(load_request.path.clone()).unwrap(); +// unimplemented!() +// } +// +// fn extensions(&self) -> &[&str] { +// &["ENC"] +// } +// } + +#[derive(Default)] +pub struct CarLoader; + +impl AssetLoader for CarLoader { + fn from_bytes(&self, asset_path: &Path, _bytes: Vec) -> Result { + info!("### Loading car {:?} via AssetLoader", asset_path); + Car::load_from(asset_path) + } + + fn extensions(&self) -> &[&str] { + static EXTENSIONS: &[&str] = &["ENC"]; + EXTENSIONS + } +} + /// Expect next line to match provided text exactly. fn expect_match>(input: &mut Iter, text: &str) -> Result<()> { if let Some(line) = input.next() { From 599dec9da0a7d1e1648dff5c89a07bae32654f84 Mon Sep 17 00:00:00 2001 From: Berkus Decker Date: Sun, 28 Jan 2024 23:13:32 +0200 Subject: [PATCH 009/120] [wip] cleanup loading code --- src/support/actor.rs | 25 +++++++++--------- src/support/camera.rs | 1 + src/support/material.rs | 49 ++++++++++++++++++++++++++--------- src/support/mesh.rs | 31 ++++++++++++---------- src/support/mod.rs | 11 +++----- src/support/render_manager.rs | 6 ++--- src/support/resource.rs | 6 +++-- src/support/texture.rs | 24 ++++++++--------- 8 files changed, 90 insertions(+), 63 deletions(-) diff --git a/src/support/actor.rs b/src/support/actor.rs index e8997a3..993aa34 100644 --- a/src/support/actor.rs +++ b/src/support/actor.rs @@ -8,6 +8,7 @@ // use { crate::support::{self, resource::Chunk, Error}, + anyhow::{anyhow, Result}, byteorder::ReadBytesExt, id_tree::*, log::*, @@ -107,7 +108,8 @@ impl Actor { } } - pub fn load(rdr: &mut R) -> Result { + //#[throws] + pub fn load(reader: &mut R) -> Result { use id_tree::InsertBehavior::*; let mut actor = Actor::new(TreeBuilder::new().with_node_capacity(5).build()); @@ -119,8 +121,12 @@ impl Actor { // Read chunks until last chunk is encountered. // Certain chunks initialize certain properties. loop { - let c = Chunk::load(rdr)?; - match c { + match Chunk::load(reader)? { + Chunk::FileHeader { file_type } => { + if file_type != support::ACTOR_FILE_TYPE { + return Err(anyhow!("Invalid model file type {}", file_type)); + } + } Chunk::ActorName { name, visible } => { trace!("Actor {} visible {}", name, visible); let child_id: NodeId = actor @@ -170,11 +176,6 @@ impl Actor { } } Chunk::Null() => break, - Chunk::FileHeader { file_type } => { - if file_type != support::ACTOR_FILE_TYPE { - panic!("Invalid model file type {}", file_type); - } - } _ => unimplemented!(), // unexpected type here } } @@ -183,10 +184,8 @@ impl Actor { Ok(actor) } - pub fn load_from>(fname: P) -> Result { - let file = File::open(fname)?; - let mut file = BufReader::new(file); - let m = Actor::load(&mut file)?; - Ok(m) + pub fn load_from>(filename: P) -> Result { + let mut file = BufReader::new(File::open(filename)?); + Ok(Actor::load(&mut file)?) } } diff --git a/src/support/camera.rs b/src/support/camera.rs index 1fba577..f4ab38e 100644 --- a/src/support/camera.rs +++ b/src/support/camera.rs @@ -8,6 +8,7 @@ // use {cgmath::*, glium::glutin}; +// @todo this converts to an entity with some components and a system to process movement pub struct CameraState { aspect_ratio: f32, fov: f32, diff --git a/src/support/material.rs b/src/support/material.rs index d2b6b2a..d7bbd7e 100644 --- a/src/support/material.rs +++ b/src/support/material.rs @@ -8,10 +8,14 @@ // use { crate::support::{self, resource::Chunk, Error}, + anyhow::{anyhow, Result}, + bevy::asset::AssetLoader, byteorder::ReadBytesExt, + log::*, std::{ fs::File, io::{BufRead, BufReader}, + path::Path, }, }; @@ -29,21 +33,40 @@ impl std::fmt::Display for Material { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, - "{} pixelmap {} rendertab {}", + "{}, pixelmap {}, rendertab {}", self.name, self.pixelmap_name, self.rendertab_name, ) } } +#[derive(Default)] +pub struct MaterialLoader; + +impl AssetLoader for MaterialLoader { + fn from_bytes(&self, asset_path: &Path, _bytes: Vec) -> Result { + info!("### Loading car {:?} via AssetLoader", asset_path); + Material::load_from(asset_path) + } + + fn extensions(&self) -> &[&str] { + static EXTENSIONS: &[&str] = &["ENC"]; + EXTENSIONS + } +} + impl Material { - pub fn load(rdr: &mut R) -> Result { + pub fn load(reader: &mut R) -> Result { let mut mat = Material::default(); // Read chunks until last chunk is encountered. // Certain chunks initialize certain properties. loop { - let c = Chunk::load(rdr)?; - match c { + match Chunk::load(reader)? { + Chunk::FileHeader { file_type } => { + if file_type != support::MATERIAL_FILE_TYPE { + return Err(anyhow!("Invalid material file type {}", file_type)); + } + } Chunk::MaterialDesc { name, params } => { mat.params = params; mat.name = name; @@ -51,11 +74,6 @@ impl Material { Chunk::PixelmapRef(name) => mat.pixelmap_name = name, Chunk::RenderTabRef(name) => mat.rendertab_name = name, Chunk::Null() => break, - Chunk::FileHeader { file_type } => { - if file_type != support::MATERIAL_FILE_TYPE { - panic!("Invalid material file type {}", file_type); - } - } _ => unimplemented!(), // unexpected type here } } @@ -65,10 +83,17 @@ impl Material { /** * Load multiple materials from a file. + // + // currently theres no "really nice" way to do that. currently it would be something like + // + // impl AssetLoader> for VecMeshLoader {} + // + // Then consume AssetEvent> in a system. When new Veces get loaded, insert each Mesh into Assets + // + // atelier-assets handles this scenario in a cleaner way (and we're considering a move to that) */ - pub fn load_from>(fname: P) -> Result, Error> { - let file = File::open(fname)?; - let mut file = BufReader::new(file); + pub fn load_from>(filename: P) -> Result> { + let mut file = BufReader::new(File::open(filename)?); let mut materials = Vec::::new(); loop { let mat = Material::load(&mut file); diff --git a/src/support/mesh.rs b/src/support/mesh.rs index 9b9cb0a..b560e4d 100644 --- a/src/support/mesh.rs +++ b/src/support/mesh.rs @@ -6,11 +6,11 @@ // Distributed under the Boost Software License, Version 1.0. // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) // -#[allow(unused_imports)] -use cgmath::{InnerSpace, Vector3, Zero}; use { crate::support::{self, resource::Chunk, Error, Vertex}, + anyhow::{anyhow, Result}, byteorder::{BigEndian, ReadBytesExt}, + cgmath::{InnerSpace, Vector3, Zero}, std::{ fs::File, io::{BufRead, BufReader}, @@ -24,7 +24,7 @@ pub struct UvCoord { } impl UvCoord { - pub fn load(rdr: &mut R) -> Result { + pub fn load(rdr: &mut R) -> Result { let mut uv = UvCoord::default(); uv.u = rdr.read_f32::()?; uv.v = rdr.read_f32::()?; @@ -42,7 +42,7 @@ pub struct Face { } impl Face { - pub fn load(rdr: &mut R) -> Result { + pub fn load(rdr: &mut R) -> Result { let mut s = Face::default(); s.v1 = rdr.read_u16::()?; s.v2 = rdr.read_u16::()?; @@ -53,6 +53,8 @@ impl Face { } } +// @todo use bevy_render::mesh::Mesh + #[derive(Default)] pub struct Mesh { pub name: String, @@ -62,7 +64,7 @@ pub struct Mesh { } impl Mesh { - pub fn load(rdr: &mut R) -> Result { + pub fn load(reader: &mut R) -> Result { let mut m = Mesh::default(); let mut fmlist = Vec::::new(); let mut uvcoords = Vec::::new(); @@ -70,11 +72,17 @@ impl Mesh { // Read chunks until last chunk is encountered. // Certain chunks initialize certain properties. loop { - let c = Chunk::load(rdr)?; - match c { + match Chunk::load(reader)? { + Chunk::FileHeader { file_type } => { + if file_type != support::MESH_FILE_TYPE { + return Err(anyhow!("Invalid mesh file type {}", file_type)); + } + } Chunk::FileName { name, subtype } => { m.name = name; - assert!(subtype == support::MODEL_FILE_SUBTYPE); + if subtype != support::MODEL_FILE_SUBTYPE { + return Err(anyhow!("Invalid mesh file subtype {}", subtype)); + } } Chunk::VertexList(r) => { m.vertices = r; @@ -92,11 +100,6 @@ impl Mesh { fmlist = r; } Chunk::Null() => break, - Chunk::FileHeader { file_type } => { - if file_type != support::MESH_FILE_TYPE { - panic!("Invalid mesh file type {}", file_type); - } - } _ => unimplemented!(), // unexpected type here } } @@ -117,7 +120,7 @@ impl Mesh { } // Single mesh file may contain multiple meshes - pub fn load_from(fname: String) -> Result, Error> { + pub fn load_from(fname: String) -> Result> { let file = File::open(fname)?; let mut file = BufReader::new(file); let mut meshes = Vec::::new(); diff --git a/src/support/mod.rs b/src/support/mod.rs index 38bcdb4..ab24037 100644 --- a/src/support/mod.rs +++ b/src/support/mod.rs @@ -8,9 +8,6 @@ // #![allow(dead_code)] -// extern crate genmesh; -// extern crate obj; - use { anyhow::{anyhow, Result}, byteorder::{BigEndian, ReadBytesExt}, @@ -48,11 +45,11 @@ pub struct Vertex { implement_vertex!(Vertex, position, normal, tex_coords); impl Vertex { - pub fn load(rdr: &mut R) -> Result { + pub fn load(reader: &mut R) -> Result { let mut vertex = Vertex::default(); - vertex.position[0] = rdr.read_f32::()?; - vertex.position[1] = rdr.read_f32::()?; - vertex.position[2] = rdr.read_f32::()?; + vertex.position[0] = reader.read_f32::()?; + vertex.position[1] = reader.read_f32::()?; + vertex.position[2] = reader.read_f32::()?; Ok(vertex) } } diff --git a/src/support/render_manager.rs b/src/support/render_manager.rs index 7df7bf4..b7d2b60 100644 --- a/src/support/render_manager.rs +++ b/src/support/render_manager.rs @@ -22,14 +22,13 @@ use { }; /// Provide storage for in-memory level-data - models, meshes, textures etc. +/// @todo use bevy AssetLoader and Asset<> resources (esp for the shader program) pub struct RenderManager { vertices: HashMap>, indices: HashMap>>, // MaterialId -> index buffer bound_textures: HashMap>, // MaterialId -> texture program: Program, } -// @todo these become resources -// esp the shader program fn debug_tree(name: &String, actor_name: &String, stack: &Vec>) { debug!("{} for {}: stack depth {}", name, actor_name, stack.len()); @@ -65,6 +64,7 @@ impl RenderManager { } } + // @todo just map uv of a default black in megatexture fn bind_default_texture( textures: &mut HashMap, mat: u16, @@ -76,7 +76,7 @@ impl RenderManager { textures.insert(mat, black_texture); } - // @todo Prepare megatexture from all these small textures and keep a map + // @todo Prepare megatexture from all these small textures and keep a bevy::TextureAtlas // of texture ID to the rect region, scale u,v appropriately in vertices. // In theory, whole of the game could fit in 4096x4096 megatex. fn bind_textures(&mut self, actor_name: &String, car: &Car, display: &Display) { diff --git a/src/support/resource.rs b/src/support/resource.rs index 6b0455a..e3c6022 100644 --- a/src/support/resource.rs +++ b/src/support/resource.rs @@ -12,6 +12,7 @@ use { mesh::{Face, UvCoord}, read_c_string, Error, Vertex, }, + anyhow::Result, byteorder::{BigEndian, ReadBytesExt}, log::*, std::io::BufRead, @@ -27,7 +28,7 @@ struct ChunkHeader { } impl ChunkHeader { - pub fn load(source: &mut R) -> Result { + pub fn load(source: &mut R) -> Result { let mut h = ChunkHeader::default(); h.chunk_type = source.read_u32::()?; h.size = source.read_u32::()?; @@ -82,7 +83,8 @@ pub enum Chunk { } impl Chunk { - pub fn load(source: &mut R) -> Result { + //#[throws] + pub fn load(source: &mut R) -> Result { let header = ChunkHeader::load(source)?; match header.chunk_type { support::NULL_CHUNK => Ok(Chunk::Null()), diff --git a/src/support/texture.rs b/src/support/texture.rs index bc6b04e..14ce1c3 100644 --- a/src/support/texture.rs +++ b/src/support/texture.rs @@ -8,6 +8,7 @@ // use { crate::support::{self, resource::Chunk, Error}, + anyhow::{anyhow, Result}, byteorder::ReadBytesExt, log::*, std::{ @@ -72,7 +73,7 @@ pub struct TextureReference { impl PixelMap { /// Convert indexed-color image to RGBA using provided palette. - pub fn remap_via_palette(&self, palette: &PixelMap) -> Result { + pub fn remap_via_palette(&self, palette: &PixelMap) -> Result { let mut pm = self.clone(); pm.data = Vec::::with_capacity(self.data.len() * 4); pm.unit_bytes = 4; @@ -166,14 +167,18 @@ impl PixelMap { Ok(()) } - pub fn load(reader: &mut R) -> Result { + pub fn load(reader: &mut R) -> Result { let mut pm = PixelMap::default(); // Read chunks until last chunk is encountered. // Certain chunks initialize certain properties. loop { - let c = Chunk::load(reader)?; - match c { + match Chunk::load(reader)? { + Chunk::FileHeader { file_type } => { + if file_type != support::PIXELMAP_FILE_TYPE { + return Err(anyhow!("Invalid pixelmap file type {}", file_type)); + } + } Chunk::PixelmapHeader { name, w, @@ -205,11 +210,6 @@ impl PixelMap { ); } Chunk::Null() => break, - Chunk::FileHeader { file_type } => { - if file_type != support::PIXELMAP_FILE_TYPE { - panic!("Invalid pixelmap file type {}", file_type); - } - } _ => unimplemented!(), // unexpected type here } } @@ -218,9 +218,8 @@ impl PixelMap { } /// Load one or more named textures from a single file - pub fn load_from>(fname: P) -> Result, Error> { - let file = File::open(fname)?; - let mut file = BufReader::new(file); + pub fn load_from>(fname: P) -> Result> { + let mut file = BufReader::new(File::open(fname)?); let mut pmaps = Vec::::new(); loop { let pmap = PixelMap::load(&mut file); @@ -240,6 +239,7 @@ impl PixelMap { } } +// @todo Use bevy::Texture directly? impl Texture { pub fn new() -> Texture { Texture { From fbdc31b5ba530c6b757dbddc7df5a01f0b743a6a Mon Sep 17 00:00:00 2001 From: Berkus Decker Date: Sun, 28 Jan 2024 23:15:59 +0200 Subject: [PATCH 010/120] wip: add separate bevy scene setup --- .idea/markdown.xml | 9 +++ .idea/vcs.xml | 6 ++ Cargo.toml | 11 +++- bevy-test/Cargo.toml | 10 ++++ bevy-test/src/main.rs | 128 ++++++++++++++++++++++++++++++++++++++++ src/assets/asset_io.rs | 8 +++ src/assets/car_asset.rs | 37 ++++++++++++ src/main.rs | 13 +++- src/support/actor.rs | 5 +- src/support/car.rs | 33 +---------- src/support/material.rs | 2 +- src/support/mesh.rs | 4 +- src/support/mod.rs | 29 +++++---- 13 files changed, 239 insertions(+), 56 deletions(-) create mode 100644 .idea/markdown.xml create mode 100644 bevy-test/Cargo.toml create mode 100644 bevy-test/src/main.rs create mode 100644 src/assets/asset_io.rs create mode 100644 src/assets/car_asset.rs diff --git a/.idea/markdown.xml b/.idea/markdown.xml new file mode 100644 index 0000000..4e28a22 --- /dev/null +++ b/.idea/markdown.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1dd..7ddfc9e 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,5 +1,11 @@ + + + + + + diff --git a/Cargo.toml b/Cargo.toml index 61fc936..b5fdf88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,23 +2,28 @@ authors = ["Berkus Decker "] edition = "2021" name = "carma" -version = "0.0.1" +version = "0.0.3" +resolver = "2" [dependencies] derive_builder = "0.13" glium = "0.32" image = "0.24" # then move on to vulkano = "0.19" + +bevy = "0.1" +bevy_asset_loader = "0.14.1" + byteorder = "1.3" cgmath = "0.18" -id_tree = "1.7" +id_tree = "1.8" num = "0.4" log = "0.4" fern = "0.6" chrono = "0.4" png = "0.17" -bevy = "0.1" # todo: use failure + anyhow = "1.0" thiserror = "1.0" fehler = "1.0" diff --git a/bevy-test/Cargo.toml b/bevy-test/Cargo.toml new file mode 100644 index 0000000..8e07f44 --- /dev/null +++ b/bevy-test/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "bevy-test" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bevy = "0.9.1" +smooth-bevy-cameras = "0.7.0" diff --git a/bevy-test/src/main.rs b/bevy-test/src/main.rs new file mode 100644 index 0000000..5dda09a --- /dev/null +++ b/bevy-test/src/main.rs @@ -0,0 +1,128 @@ +use { + bevy::{ + prelude::*, + render::render_resource::{Extent3d, TextureDimension, TextureFormat}, + }, + smooth_bevy_cameras::{ + controllers::unreal::{UnrealCameraBundle, UnrealCameraController, UnrealCameraPlugin}, + LookTransformPlugin, + }, + std::f32::consts::PI, +}; + +fn main() { + App::new() + .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) + .add_plugin(LookTransformPlugin) + .add_plugin(UnrealCameraPlugin::default()) + .add_startup_system(setup) + .add_system(rotate) + .run(); +} + +/// A marker component for our shapes so we can query them separately from the ground plane +#[derive(Component)] +struct Shape; + +const X_EXTENT: f32 = 14.; + +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut images: ResMut>, + mut materials: ResMut>, +) { + let debug_material = materials.add(StandardMaterial { + base_color_texture: Some(images.add(uv_debug_texture())), + ..default() + }); + + let shapes = [ + meshes.add(shape::Cube::default().into()), + meshes.add(shape::Box::default().into()), + meshes.add(shape::Capsule::default().into()), + meshes.add(shape::Torus::default().into()), + meshes.add(shape::Icosphere::default().into()), + meshes.add(shape::UVSphere::default().into()), + ]; + + let num_shapes = shapes.len(); + + for (i, shape) in shapes.into_iter().enumerate() { + commands.spawn(( + PbrBundle { + mesh: shape, + material: debug_material.clone(), + transform: Transform::from_xyz( + -X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * X_EXTENT, + 2.0, + 0.0, + ) + .with_rotation(Quat::from_rotation_x(-PI / 4.)), + ..default() + }, + Shape, + )); + } + + commands.spawn(PointLightBundle { + point_light: PointLight { + intensity: 9000.0, + range: 100., + shadows_enabled: true, + ..default() + }, + transform: Transform::from_xyz(8.0, 16.0, 8.0), + ..default() + }); + + // ground plane + commands.spawn(PbrBundle { + mesh: meshes.add(shape::Plane { size: 50. }.into()), + material: materials.add(Color::SILVER.into()), + ..default() + }); + + commands + .spawn(Camera3dBundle::default()) + .insert(UnrealCameraBundle::new( + UnrealCameraController::default(), + Vec3::new(0.0, 6.0, 12.0), + Vec3::new(0., 1., 0.), + Vec3::Y, + )); +} + +fn rotate(mut query: Query<&mut Transform, With>, time: Res