1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! This is a prototype game engine, focussed on abstracting away all rendering logic and focussing purely on the game logic.
//!
//! # Example
//!
//! ```no_run
//! use cgmath::{Matrix4, Point3, Rad, Vector3};
//! use crystal_engine::{GameState, ModelHandle, Window, event::VirtualKeyCode};
//!
//! fn main() {
//!     // Create a new instance of your game and run it
//!     let window = Window::<Game>::new(800., 600.);
//!     window.run();
//! }
//!
//! pub struct Game {
//!     // Your game state is stored here
//!     model: ModelHandle,
//! }
//!
//! impl crystal_engine::Game for Game {
//!     fn init(state: &mut GameState) -> Self {
//!         // Load an object. This will automatically be rendered every frame
//!         // as long as the returned ModelHandle is not dropped.
//!         
//!         // Note that "new_obj_model" is only available when using the "format-obj" feature
//!         // for more information and different model formats, see the documentation of "GameState"
//!#        #[cfg(feature = "format-obj")]
//!         let model = state.new_obj_model("assets/some_object.obj")
//!             .with_position((0.0, -3.0, 0.0))
//!             .with_scale(0.3)
//!             .build();
//!
//!#        #[cfg(not(feature = "format-obj"))]
//!#        let model: ModelHandle = unsafe { std::mem::zeroed() };
//!
//!         // Update the camera by manipulating the state's field
//!         state.camera = Matrix4::look_at(
//!             Point3::new(0.3, 0.3, 1.0),
//!             Point3::new(0.0, 0.0, 0.0),
//!             Vector3::new(0.0, -1.0, 0.0),
//!         );
//!
//!         Self { model }
//!     }
//!
//!     fn keydown(&mut self, state: &mut GameState, key: VirtualKeyCode) {
//!         // Exit the game when the user hits escape
//!         if key == VirtualKeyCode::Escape {
//!             state.terminate_game();
//!         }
//!     }
//!
//!     fn update(&mut self, state: &mut GameState) {
//!         self.model.modify(|data| {
//!             // Rotate either left or right, based on what the user has pressed
//!             if state.keyboard.is_pressed(VirtualKeyCode::A) {
//!                 data.rotation.y -= Rad(0.05);
//!             }
//!             if state.keyboard.is_pressed(VirtualKeyCode::D) {
//!                 data.rotation.y += Rad(0.05);
//!             }
//!         });
//!     }
//! }
//! ```

#![warn(missing_docs)]

mod game_state;
mod model;
mod render;

pub use self::{
    game_state::GameState,
    model::{ModelData, ModelHandle},
    render::{DirectionalLight, LightColor, PointLight, PointLightAttenuation, Window},
};

/// Contains the states that are used in [GameState]. These are in a seperate module so we don't pollute the base module documentation.
pub mod state {
    pub use crate::{
        game_state::KeyboardState,
        render::{FixedVec, LightState},
    };
}

/// Re-exported module of `winit`, with some additional structs that are useful
pub mod event {
    pub use winit::{dpi::PhysicalPosition, event::*};
}

/// The entry point of the game implementation.
///
/// In your game you will have to implement this trait for your own Game object. See the main module documentation for an example.
pub trait Game {
    /// Create a new instance of the game. This will be called exactly once, whenever the game window is created.
    fn init(state: &mut GameState) -> Self;
    /// Update the game. This will be called every frame. Use this to implement your game logic.
    fn update(&mut self, state: &mut GameState);
    /// Checks if the game can shut down. This is called when a player tries to close the window by clicking X or pressing alt+f4
    fn can_shutdown(&mut self, _state: &mut GameState) -> bool {
        true
    }
    /// Triggered when a winit event is received.
    fn event(&mut self, _state: &mut GameState, _event: &event::WindowEvent) {}
    /// Triggered when a key is pressed.
    ///
    /// Note that the [GameState.keyboard](struct.GameState.html#structfield.keyboard) is updated *before* this method is called.
    /// This means that `state.keyboard.is_pressed(key)` will always return `true`.
    fn keydown(&mut self, _state: &mut GameState, _key: event::VirtualKeyCode) {}
    /// Triggered when a key is released.
    ///
    /// Note that the [GameState.keyboard](struct.GameState.html#structfield.keyboard) is updated *before* this method is called.
    /// This means that `state.keyboard.is_pressed(key)` will always return `false`.
    fn keyup(&mut self, _state: &mut GameState, _key: event::VirtualKeyCode) {}
}