diff --git a/assets/models/jupiter.glb b/assets/textures/jupiter.jpg similarity index 81% rename from assets/models/jupiter.glb rename to assets/textures/jupiter.jpg index d16eb84..102b402 100644 Binary files a/assets/models/jupiter.glb and b/assets/textures/jupiter.jpg differ diff --git a/src/defs.txt b/src/defs.txt index 0ec84fc..392ba72 100644 --- a/src/defs.txt +++ b/src/defs.txt @@ -11,10 +11,12 @@ actor 0 0 0 suit engine monopropellant actor 300000 0 500000 jupiter - scale 200000 + scale 400000 + sphere yes physics off - rotationy -1.40 - angularmomentum 0 0.0001 0 + rotationx -0.50 + rotationz -0.28 + angularmomentum 30 30 30 actor 3000 0 0 moonlet scale 500 diff --git a/src/world.rs b/src/world.rs index de2b716..a4c8dfe 100644 --- a/src/world.rs +++ b/src/world.rs @@ -22,7 +22,6 @@ const ASSET_ASTEROID2: &str = "models/asteroid2.glb#Scene0"; fn asset_name_to_path(name: &str) -> &'static str { match name { "suit" => "models/suit.glb#Scene0", - "jupiter" => "models/jupiter.glb#Scene0", "asteroid1" => ASSET_ASTEROID1, "asteroid2" => ASSET_ASTEROID2, "moonlet" => "models/moonlet.glb#Scene0", @@ -38,15 +37,19 @@ pub struct WorldPlugin; impl Plugin for WorldPlugin { fn build(&self, app: &mut App) { app.add_systems(Startup, (setup, load_defs)); + app.add_systems(Update, spawn_entities); //app.add_systems(Update, asset_loaded.after(load_cubemap_asset)); //app.add_systems(Update, swap_world_on_ar_toggle); app.add_plugins(PhysicsPlugins::default()); //app.add_plugins(PhysicsDebugPlugin::default()); app.insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))); app.insert_resource(Gravity(Vec3::splat(0.0))); + app.add_event::(); } } +#[derive(Event)] pub struct SpawnEvent(ParserState); + #[derive(Component)] pub struct Star; @@ -183,12 +186,14 @@ pub fn setup( }); } +#[derive(PartialEq, Clone)] enum DefClass { Actor, Chat, None, } +#[derive(Clone)] struct ParserState { class: DefClass, @@ -204,6 +209,7 @@ struct ParserState { rotation: Quat, angular_momentum: Vec3, pronoun: String, + is_sphere: bool, is_player: bool, is_lifeform: bool, is_alive: bool, @@ -250,6 +256,7 @@ impl Default for ParserState { rotation: Quat::IDENTITY, angular_momentum: Vec3::new(0.03, 0.3, 0.09), pronoun: "they/them".to_string(), + is_sphere: false, is_player: false, is_lifeform: false, is_alive: false, @@ -291,14 +298,6 @@ impl ParserState { self.text = default.text; self.is_choice = default.is_choice; } - fn reset_chat(&mut self) { - let default = ParserState::default(); - self.reset_message(); - self.stores_item = default.stores_item; - } - fn reset(&mut self) { - *self = Self::default(); - } fn as_chatbranch(&self) -> actor::ChatBranch { return actor::ChatBranch { id: self.chat.clone(), @@ -315,91 +314,10 @@ impl ParserState { script_parameter2: self.script_parameter2.clone(), } } - fn spawn_chat(&mut self, commands: &mut Commands) { - if self.stores_item { - debug!("{:#?}", self.as_chatbranch()); - commands.spawn(self.as_chatbranch()); - } - self.reset_message(); - } - fn spawn_actor(&mut self, commands: &mut Commands, asset_server: &Res) { - let mut actor = commands.spawn_empty(); - actor.insert(actor::Actor { - id: self.id.clone(), - camdistance: self.camdistance, - ..default() - }); - actor.insert(SceneBundle { - transform: Transform { - translation: self.pos, - scale: Vec3::splat(self.model_scale), - rotation: self.rotation, - }, - scene: asset_server.load(asset_name_to_path(self.model.as_str())), - ..default() - }); - - // Physics Parameters - if self.has_physics { - let fix_scale = 1.0 / self.model_scale.powf(3.0); - actor.insert(RigidBody::Dynamic); - actor.insert(AngularVelocity(self.angular_momentum)); - actor.insert(self.collider.clone()); - actor.insert(ColliderDensity(self.mass * fix_scale)); - } - // TODO: angular velocity for objects without collisions, static objects - - // Optional Components - if self.is_player { - actor.insert(actor::Player); - actor.insert(actor::PlayerCamera); - } - if self.is_lifeform { - actor.insert(actor::LifeForm::default()); - actor.insert(actor::Suit { - oxygen: self.oxygen, - oxygen_max: nature::OXY_D, - integrity: self.suit_integrity, - ..default() - }); - } - if !self.chat.is_empty() { - actor.insert(actor::Talker { - conv_id: self.chat.clone(), - ..default() - }); - } - if self.is_vehicle { - actor.insert(actor::Vehicle::default()); - } - if self.is_vehicle || self.is_suited { - actor.insert(actor::Engine { - thrust_forward: self.thrust_forward, - thrust_back: self.thrust_back, - thrust_sideways: self.thrust_sideways, - reaction_wheels: self.reaction_wheels, - warmup_seconds: self.warmup_seconds, - engine_type: self.engine_type, - ..default() - }); - } - - //info!("Spawning actor {} with model {} at {}/{}/{}", - // self.name, self.model, self.pos.x, self.pos.y, self.pos.z); - self.reset(); - } - fn spawn_entities(&mut self, commands: &mut Commands, asset_server: &Res) { - match self.class { - DefClass::Actor => { self.spawn_actor(commands, asset_server); } - DefClass::Chat => { self.spawn_chat(commands); } - DefClass::None => {} - } - } } pub fn load_defs( - mut commands: Commands, - asset_server: Res, + mut ew_spawn: EventWriter, ) { let re1 = Regex::new(r"^\s*([a-z_-]+)\s+(.*)$").unwrap(); let re2 = Regex::new("\"([^\"]*)\"|(-?[0-9]+(?:\\.[0-9]+)?)|([a-zA-Z_-][a-zA-Z0-9_-]*)").unwrap(); @@ -445,8 +363,8 @@ pub fn load_defs( match parts.as_slice() { // Parsing actors ["actor", x, y, z, model] => { - state.spawn_entities(&mut commands, &asset_server); - state.reset(); + ew_spawn.send(SpawnEvent(state)); + state = ParserState::default(); state.class = DefClass::Actor; state.model = model.to_string(); if let (Ok(x_float), Ok(y_float), Ok(z_float)) = @@ -455,10 +373,13 @@ pub fn load_defs( } else { error!("Can't parse coordinates as floats in def: {line}"); - state.reset(); + state = ParserState::default(); continue; } } + ["sphere", "yes"] => { + state.is_sphere = true; + } ["id", id] => { state.id = id.to_string(); } @@ -498,7 +419,7 @@ pub fn load_defs( } ["rotationx", rotation_x] => { if let Ok(rotation_x_float) = rotation_x.parse::() { - state.rotation = Quat::from_rotation_x(PI * rotation_x_float); + state.rotation *= Quat::from_rotation_x(PI * rotation_x_float); } else { error!("Can't parse float: {line}"); @@ -507,7 +428,16 @@ pub fn load_defs( } ["rotationy", rotation_y] => { if let Ok(rotation_y_float) = rotation_y.parse::() { - state.rotation = Quat::from_rotation_y(PI * rotation_y_float); + state.rotation *= Quat::from_rotation_y(PI * rotation_y_float); + } + else { + error!("Can't parse float: {line}"); + continue; + } + } + ["rotationz", rotation_z] => { + if let Ok(rotation_z_float) = rotation_z.parse::() { + state.rotation *= Quat::from_rotation_z(PI * rotation_z_float); } else { error!("Can't parse float: {line}"); @@ -598,8 +528,8 @@ pub fn load_defs( // Parsing chats ["chat", chat_name] => { debug!("Registering chat: {}", chat_name); - state.spawn_entities(&mut commands, &asset_server); - state.reset_chat(); + ew_spawn.send(SpawnEvent(state)); + state = ParserState::default(); state.class = DefClass::Chat; state.chat = chat_name.to_string(); } @@ -609,7 +539,7 @@ pub fn load_defs( } ["msg", sleep, text] => { debug!("Registering message (sleep={}): {}", sleep, text); - state.spawn_entities(&mut commands, &asset_server); + ew_spawn.send(SpawnEvent(state.clone())); if let Ok(sleep_float) = sleep.parse::() { state.delay = sleep_float; state.text = text.to_string(); @@ -622,7 +552,8 @@ pub fn load_defs( } ["msg", sleep, label, goto, text] => { debug!("Registering message (sleep={}): {}", sleep, text); - state.spawn_entities(&mut commands, &asset_server); + ew_spawn.send(SpawnEvent(state.clone())); + state.reset_message(); if let Ok(sleep_float) = sleep.parse::() { state.delay = sleep_float; state.text = text.to_string(); @@ -637,7 +568,8 @@ pub fn load_defs( } ["choice", sleep, text] => { debug!("Registering choice (sleep={}): {}", sleep, text); - state.spawn_entities(&mut commands, &asset_server); + ew_spawn.send(SpawnEvent(state.clone())); + state.reset_message(); if let Ok(sleep_float) = sleep.parse::() { state.delay = sleep_float; state.text = text.to_string(); @@ -650,7 +582,8 @@ pub fn load_defs( } ["choice", sleep, label, goto, text] => { debug!("Registering choice (sleep={}): {}", sleep, text); - state.spawn_entities(&mut commands, &asset_server); + ew_spawn.send(SpawnEvent(state.clone())); + state.reset_message(); if let Ok(sleep_float) = sleep.parse::() { state.delay = sleep_float; state.text = text.to_string(); @@ -690,7 +623,107 @@ pub fn load_defs( } } } - state.spawn_entities(&mut commands, &asset_server); + ew_spawn.send(SpawnEvent(state)); +} + +fn spawn_entities( + mut er_spawn: EventReader, + mut commands: Commands, + asset_server: Res, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + for state_wrapper in er_spawn.read() { + let state = &state_wrapper.0; + if state.class == DefClass::Chat { + if state.stores_item { + commands.spawn(state.as_chatbranch()); + } + } + else if state.class == DefClass::Actor { + let mut actor = commands.spawn_empty(); + actor.insert(actor::Actor { + id: state.id.clone(), + camdistance: state.camdistance, + ..default() + }); + if state.is_sphere { + let sphere_texture_handle: Handle = asset_server.load(format!("textures/{}.jpg", state.model)); + let sphere_handle = meshes.add(Sphere::default().mesh().uv(128, 128)); + let sphere_material_handle = materials.add(StandardMaterial { + base_color_texture: Some(sphere_texture_handle.clone()), + perceptual_roughness: 1.0, + metallic: 0.0, + ..default() + }); + actor.insert(PbrBundle { + mesh: sphere_handle, + material: sphere_material_handle, + transform: Transform { + translation: state.pos, + scale: Vec3::splat(state.model_scale), + rotation: state.rotation, + }, + ..default() + }); + } else { + actor.insert(SceneBundle { + transform: Transform { + translation: state.pos, + scale: Vec3::splat(state.model_scale), + rotation: state.rotation, + }, + scene: asset_server.load(asset_name_to_path(state.model.as_str())), + ..default() + }); + } + + // Physics Parameters + if state.has_physics { + let fix_scale = 1.0 / state.model_scale.powf(3.0); + actor.insert(RigidBody::Dynamic); + actor.insert(AngularVelocity(state.angular_momentum)); + actor.insert(state.collider.clone()); + actor.insert(ColliderDensity(state.mass * fix_scale)); + } + // TODO: angular velocity for objects without collisions, static objects + + // Optional Components + if state.is_player { + actor.insert(actor::Player); + actor.insert(actor::PlayerCamera); + } + if state.is_lifeform { + actor.insert(actor::LifeForm::default()); + actor.insert(actor::Suit { + oxygen: state.oxygen, + oxygen_max: nature::OXY_D, + integrity: state.suit_integrity, + ..default() + }); + } + if !state.chat.is_empty() { + actor.insert(actor::Talker { + conv_id: state.chat.clone(), + ..default() + }); + } + if state.is_vehicle { + actor.insert(actor::Vehicle::default()); + } + if state.is_vehicle || state.is_suited { + actor.insert(actor::Engine { + thrust_forward: state.thrust_forward, + thrust_back: state.thrust_back, + thrust_sideways: state.thrust_sideways, + reaction_wheels: state.reaction_wheels, + warmup_seconds: state.warmup_seconds, + engine_type: state.engine_type, + ..default() + }); + } + } + } } //pub fn swap_world_on_ar_toggle(