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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! 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.).unwrap();
//!     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()
//!             .unwrap();
//!
//!#        #[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)]
#![allow(clippy::needless_doctest_main)]
#![allow(clippy::borrowed_box)] // There are two incorrect suggestions for this lint: https://github.com/rust-lang/rust-clippy/issues/3971

mod error;
mod game_state;
mod gui;
mod internal;
mod model;
mod render;

pub use self::{
    game_state::GameState,
    gui::GuiElement,
    model::{ModelBuilder, ModelData, ModelHandle},
    render::{
        lights::{DirectionalLight, LightColor, PointLight, PointLightAttenuation},
        window::Window,
    },
};
pub use rusttype::Font;

/// 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::{
        error::*,
        game_state::KeyboardState,
        gui::{
            GuiElementBuilder, GuiElementCanvasBuilder, GuiElementData, GuiElementTextureBuilder,
        },
        render::lights::{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) {}
}