├── .clog.toml ├── .gitattributes ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE.md ├── README.md ├── benches ├── collision.rs └── grid_collision.rs ├── examples ├── benchmarks │ ├── circle_collision.rs │ ├── circle_movement.rs │ └── create_destroy.rs ├── empty.rs ├── experiment.rs ├── lots_of_cubes.rs └── meshes │ ├── bullet_small.dae │ ├── cube.dae │ ├── cube_blender.dae │ ├── gun_small.dae │ └── sphere.dae ├── lib ├── bootstrap-gl │ ├── Cargo.toml │ ├── examples │ │ ├── empty.rs │ │ └── hello_triangle.rs │ └── src │ │ ├── lib.rs │ │ ├── linux.rs │ │ ├── macos.rs │ │ ├── macros.rs │ │ ├── types.rs │ │ └── windows.rs ├── bootstrap_audio │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── linux.rs │ │ └── windows.rs ├── bootstrap_rs │ ├── Cargo.toml │ ├── build.rs │ ├── examples │ │ └── window.rs │ └── src │ │ ├── input.rs │ │ ├── lib.rs │ │ ├── linux │ │ ├── file.rs │ │ ├── mod.rs │ │ └── window.rs │ │ ├── macos │ │ ├── input.rs │ │ ├── mod.rs │ │ └── window.rs │ │ ├── window.rs │ │ └── windows │ │ ├── file.rs │ │ ├── input.rs │ │ ├── mod.rs │ │ └── window.rs ├── fiber │ ├── Cargo.toml │ ├── src │ │ ├── lib.rs │ │ └── platform │ │ │ └── windows.rs │ └── tests │ │ └── lib.rs ├── gl-util │ ├── Cargo.toml │ ├── examples │ │ ├── epps_head.obj │ │ ├── hello_triangle.rs │ │ ├── shader.rs │ │ ├── structured.bmp │ │ ├── texture.rs │ │ └── wireframe.rs │ └── src │ │ ├── context.rs │ │ ├── lib.rs │ │ ├── shader.rs │ │ ├── texture.rs │ │ └── windows │ │ └── mod.rs ├── hash │ ├── Cargo.toml │ └── src │ │ ├── fnv.rs │ │ └── lib.rs ├── loader │ ├── Cargo.toml │ └── src │ │ └── loader.rs ├── parse-bmp │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── parse-obj │ ├── Cargo.toml │ ├── examples │ │ ├── epps_head.obj │ │ ├── from_file.rs │ │ └── triangle.rs │ ├── src │ │ └── lib.rs │ └── tests │ │ └── lib.rs ├── polygon-material │ ├── Cargo.toml │ ├── src │ │ ├── lexer.rs │ │ ├── lib.rs │ │ ├── material_source.rs │ │ ├── parser.rs │ │ └── token.rs │ └── tests │ │ └── lib.rs ├── polygon_math │ ├── Cargo.toml │ └── src │ │ ├── color.rs │ │ ├── lib.rs │ │ ├── matrix.rs │ │ ├── orientation.rs │ │ ├── point.rs │ │ ├── quaternion.rs │ │ ├── test │ │ ├── matrix_test.rs │ │ ├── mod.rs │ │ └── quaternion_test.rs │ │ └── vector.rs ├── polygon_rs │ ├── Cargo.toml │ ├── examples │ │ ├── directional_light.rs │ │ ├── empty.rs │ │ ├── hello_triangle.rs │ │ ├── materials.rs │ │ ├── multiple_lights.rs │ │ ├── no_light.rs │ │ ├── point_light.rs │ │ ├── textures.rs │ │ └── utils.rs │ ├── resources │ │ ├── materials │ │ │ ├── diffuse_flat.material │ │ │ ├── diffuse_lit.material │ │ │ └── texture_diffuse_lit.material │ │ ├── meshes │ │ │ └── epps_head.obj │ │ └── textures │ │ │ └── structured.bmp │ └── src │ │ ├── anchor.rs │ │ ├── camera.rs │ │ ├── geometry │ │ ├── mesh.rs │ │ └── mod.rs │ │ ├── gl │ │ └── mod.rs │ │ ├── lib.rs │ │ ├── light.rs │ │ ├── macros.rs │ │ ├── material.rs │ │ ├── mesh_instance.rs │ │ ├── shader.rs │ │ └── texture.rs └── stopwatch │ ├── Cargo.toml │ └── src │ ├── lib.rs │ ├── stats.rs │ └── windows.rs └── src ├── camera.rs ├── collections ├── array.rs ├── atomic_array.rs └── mod.rs ├── component ├── alarm.rs ├── audio.rs ├── camera.rs ├── collider │ ├── bounding_volume.rs │ ├── grid_collision.rs │ └── mod.rs ├── light.rs ├── mesh.rs ├── mod.rs ├── singleton_component_manager.rs ├── struct_component_manager.rs └── transform.rs ├── engine.rs ├── input.rs ├── lib.rs ├── light.rs ├── macros.rs ├── mesh_renderer.rs ├── mod.rs ├── old ├── callback.rs ├── debug_draw.rs ├── ecs.rs ├── engine.rs ├── resource │ ├── collada.rs │ └── mod.rs ├── scene.rs ├── singleton.rs └── wav.rs ├── prelude.rs ├── resource ├── collada.rs └── mod.rs ├── scheduler.rs ├── time.rs └── transform.rs /.clog.toml: -------------------------------------------------------------------------------- 1 | [clog] 2 | repository = "https://github.com/excaliburHisSheath/gunship-rs" 3 | outfile = "CHANGELOG.md" 4 | 5 | [sections] 6 | "Features" = ["feature", "feat"] 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | /target/ 12 | lib/*/target/ 13 | /bin/ 14 | .cargo/ 15 | Cargo.lock 16 | lib/bootstrap-gl/gl_src/ 17 | 18 | # Visual Studio 19 | *.sln 20 | .vs/ 21 | 22 | *.amplxeproj 23 | *.cfg 24 | My Amplifier XE Results - create_destroy 25 | My Amplifier XE Results - circle_movement 26 | 27 | # Gunship Generated Files 28 | stopwatch.csv 29 | builds/ 30 | My Amplifier XE Results - * 31 | 32 | # Gunship reference files 33 | lib/bootstrap-gl/reference/gl.h 34 | lib/bootstrap-gl/reference/glext.h 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | os: osx 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | script: 8 | - (cd lib/bootstrap_rs && cargo build) 9 | - (cd lib/bootstrap_rs && cargo build --example window) 10 | matrix: 11 | allow_failures: 12 | - rust: stable 13 | - rust: beta 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be kept within this file. This project 4 | adheres (as closely as it can) to [Semantic Versioning](http://semver.org/). 5 | 6 | ## [Unreleased] 7 | ### [Added] 8 | - Texture support! 9 | - Construct entities with `Entity::new()`. 10 | - Support shaders that don't use lighting. 11 | 12 | ### [Changed] 13 | - `ResourceManager::add_mesh()` can now accept more than just `String` for the 14 | URI, including `&str`. 15 | 16 | ### [Fixed] 17 | - Fix meshes that don't have normal data. 18 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Gunship 2 | 3 | Hello fellow Rustacean and thank you for showing interest in Gunship! Before you go any further there's a few things you should know: 4 | 5 | - Gunship is in a *very* early stage of development. As such your contributions add a lot but it's also going to be more difficult to make those contributions due to the incomplete state of much of the code. 6 | - Gunship is meant to be an experimental engine used as a learning excercise for those who develop it. Of course the best way to learn about game engines is to make one that is high enough quality to be used in the wild so my goal in developing Gunship is to do just that. But being an learning project means that I often make impractical choices for Gunship's development in the name of gaining more experience rather than quickly building a production-ready product. For the forseeable future this will be the case so if you're looking to add new functionality be prepared to have to build a lot of it from scratch. 7 | 8 | ## Getting Started 9 | 10 | The best way to get started is with one of the [`difficulty: easy (hours)`](https://github.com/excaliburHisSheath/gunship-rs/issues?q=is%3Aissue+is%3Aopen+label%3A%22difficulty%3A+easy+%28hours%29%22). All Gunship tasks are mentored but those are especially quick to get up-and-running with and require the least knowlege of Rust or the engine. Easy tasks are also (usually) well defined and require little investigation (beyond familiarizing oneself with Rust/Gunship). 11 | 12 | ## Contributions 13 | 14 | Gunship welcomes contributions from everyone. All contributions should be made in the form of a GitHub pull request. Pull requests will be reviewed by core members and merged once they have been approved. 15 | 16 | ## Conduct 17 | 18 | We follow the [Rust Code of Conduct](https://www.rust-lang.org/conduct.html) and the [Contributor Covenant](http://contributor-covenant.org/version/1/4/) 19 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "gunship" 4 | version = "0.1.0" 5 | authors = ["David LeGare "] 6 | 7 | [features] 8 | timing = [] 9 | no-draw = [] 10 | 11 | [dependencies] 12 | bootstrap_rs = { path = "lib/bootstrap_rs" } 13 | bootstrap_audio = { path = "lib/bootstrap_audio" } 14 | cell-extras = { git = "https://github.com/excaliburHisSheath/cell-extras" } 15 | fiber = { version = "0.1", path = "lib/fiber" } 16 | hash = { path = "lib/hash" } 17 | lazy_static = "0.2.1" 18 | parse_collada = { path = "lib/parse_collada" } 19 | parse-obj = { version = "0.1", path = "lib/parse-obj" } 20 | polygon = { path = "lib/polygon_rs" } 21 | polygon_math = { path = "lib/polygon_math" } 22 | stopwatch = { path = "lib/stopwatch" } 23 | 24 | [dev_dependencies] 25 | rand = "*" 26 | 27 | [profile.dev] 28 | panic = "abort" 29 | 30 | [profile.release] 31 | panic = "abort" 32 | 33 | [profile.test ] 34 | panic = "abort" 35 | 36 | [profile.bench] 37 | panic = "abort" 38 | 39 | [profile.doc] 40 | panic = "abort" 41 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2016 David LeGare (excaliburhissheath@gmail.com) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Gunship 2 | ======= 3 | 4 | "The game engine for people who don't like making games." 5 | 6 | Gunship is an experimental game engine being developed as an effort to learn 7 | more about the inner-workings of a modern game engine. It is being written in 8 | [Rust](http://rust-lang.org/) in an attempt to break away from the game 9 | industry's unhealthy codependence on C++. 10 | 11 | Design Goals 12 | ------------ 13 | 14 | At this point Gunship is meant to be more of a learning project than a 15 | production-ready game engine. The idea is to build as much as possible from 16 | scratch in order to develop myself as a programmer and learn all the parts 17 | of a modern game engine. So far I've allowed myself to use the standard library 18 | and libraries for binding to platform-specific native libraries, but the 19 | intent is to keep dependencies to a minimum, even if there are relevant crates 20 | in the Rust ecosystem. 21 | 22 | As for the engine itself high-level design is pretty nonexistent. I'm keeping 23 | development directed by building a game along with the engine, so for the time 24 | being the design motivations for Gunship are "whatever work best for making a 25 | game". At present the design is roughly as follows: 26 | 27 | - The renderer is a separate crate from the engine core. It's potentially going 28 | to be usable on its own, though right now that's not a primary goal. Mostly 29 | the renderer's goal is to provide a high level, backend agnostic system for 30 | rendering that can be tested idependently but easily be plugged into the 31 | engine core. 32 | - The engine core provides the scheduler, which makes it easy to write highly 33 | parallel, asynchronous gameplay and engine code. 34 | - The engine core only provides the primitives for gameplay development (e.g. 35 | transforms, lights, meshes, etc.), but provides little-to-no framework for 36 | structuring gameplay code. Such frameworks will likely be provided 37 | as a layer on top of the engine core. 38 | 39 | Current Features 40 | ---------------- 41 | 42 | The engine is very early in its development and so doesn't have much to show off 43 | as of yet. That said, it does have a few working features at this point: 44 | 45 | - Multi-threaded, async engine and gameplay code makes it easy to write games 46 | while taking full advantage of multiple cores in modern CPUs. 47 | - Basic (*very* basic) 3D mesh rendering support, just basic meshes and limited 48 | support for custom shading. No shadows, highly inefficient. 49 | 50 | Broken Features 51 | --------------- 52 | 53 | The following features have been implemented in the past but were broken at some 54 | point: 55 | 56 | - 3D collision processing with sphere and box colliders. Actually pretty fast 57 | compared to the other collision systems, though very much lacking in 58 | functionality. 59 | - Code hotloading, which allows you to recompile code without stopping and 60 | starting the game. Currently very hacky in its implementation, but very much 61 | functional. 62 | - Super basic audio -- only one audio source at a time and pretty much 63 | hard-coded to work on my machine. 64 | 65 | Planned Features 66 | ---------------- 67 | 68 | Beyond the obvious (making the existing systems more robust and actually 69 | *usable*) there are a few features that I plan to work on in the near future: 70 | 71 | - Proper rendering system, including more efficient rendering through batching, 72 | skinned meshes, and particles. 73 | - [Cross-platform support](https://github.com/excaliburHisSheath/gunship-rs/milestones/Basic%20Cross-Platform%20Support) 74 | for Linux and OSX at least, eventually I'd like to get to mobile support and 75 | consoles, though who knows how long that could take. 76 | - [Proper logging system](https://github.com/excaliburHisSheath/gunship-rs/issues/21). 77 | 78 | Contributing 79 | ------------ 80 | 81 | At this point the engine's not stable enough for anyone but me to attempt to use 82 | it to make a game -- if you want to use Rust to make a game then check out 83 | [Piston](http://www.piston.rs/). On the other hand if what you want is to build 84 | some engine subsystem from scratch but don't want to have to put together all 85 | of the other pieces, feel free to use Gunship as your baseline. Even then I 86 | don't recommend it, but I'm happy to help however I can (just send an email or 87 | open an issue to ask questions). 88 | -------------------------------------------------------------------------------- /benches/collision.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate gunship; 4 | extern crate rand; 5 | extern crate test; 6 | 7 | use gunship::*; 8 | use gunship::component::collider::{Sphere, OBB}; 9 | use gunship::component::collider::bounding_volume::AABB; 10 | use std::f32::consts::PI; 11 | use test::Bencher; 12 | 13 | #[bench] 14 | fn sphere_sphere_x100(bencher: &mut Bencher) { 15 | let mut spheres = Vec::new(); 16 | for _ in 0..10 { 17 | spheres.push(Sphere { 18 | center: Point::new( 19 | random_range(-5.0, 5.0), 20 | random_range(-5.0, 5.0), 21 | random_range(-5.0, 5.0), 22 | ), 23 | radius: random_range(0.5, 2.0), 24 | }); 25 | } 26 | 27 | bencher.iter(|| { 28 | for lhs in &spheres { 29 | for rhs in &spheres { 30 | test::black_box(lhs.test_sphere(rhs)); 31 | } 32 | } 33 | }); 34 | } 35 | 36 | #[bench] 37 | fn obb_obb_x100(bencher: &mut Bencher) { 38 | let mut obbs = Vec::new(); 39 | for _ in 0..10 { 40 | obbs.push(OBB { 41 | center: Point::new( 42 | random_range(-5.0, 5.0), 43 | random_range(-5.0, 5.0), 44 | random_range(-5.0, 5.0), 45 | ), 46 | orientation: Matrix3::rotation( 47 | random_range(0.0, PI), 48 | random_range(0.0, PI), 49 | random_range(0.0, PI), 50 | ), 51 | half_widths: Vector3::new( 52 | random_range(0.5, 2.0), 53 | random_range(0.5, 2.0), 54 | random_range(0.5, 2.0), 55 | ), 56 | }); 57 | } 58 | 59 | bencher.iter(|| { 60 | for lhs in &obbs { 61 | for rhs in &obbs { 62 | test::black_box(lhs.test_obb(rhs)); 63 | } 64 | } 65 | }); 66 | } 67 | 68 | #[bench] 69 | fn sphere_obb_x100(bencher: &mut Bencher) { 70 | let mut spheres = Vec::new(); 71 | for _ in 0..10 { 72 | spheres.push(Sphere { 73 | center: Point::new( 74 | random_range(-5.0, 5.0), 75 | random_range(-5.0, 5.0), 76 | random_range(-5.0, 5.0), 77 | ), 78 | radius: random_range(0.5, 2.0), 79 | }); 80 | } 81 | 82 | let mut obbs = Vec::new(); 83 | for _ in 0..10 { 84 | obbs.push(OBB { 85 | center: Point::new( 86 | random_range(-5.0, 5.0), 87 | random_range(-5.0, 5.0), 88 | random_range(-5.0, 5.0), 89 | ), 90 | orientation: Matrix3::rotation( 91 | random_range(0.0, PI), 92 | random_range(0.0, PI), 93 | random_range(0.0, PI), 94 | ), 95 | half_widths: Vector3::new( 96 | random_range(0.5, 2.0), 97 | random_range(0.5, 2.0), 98 | random_range(0.5, 2.0), 99 | ), 100 | }); 101 | } 102 | 103 | bencher.iter(|| { 104 | for lhs in &spheres { 105 | for rhs in &obbs { 106 | test::black_box(lhs.test_obb(rhs)); 107 | } 108 | } 109 | }); 110 | } 111 | 112 | #[bench] 113 | fn abb_abb_x100(bencher: &mut Bencher) { 114 | let mut aabbs = Vec::new(); 115 | for _ in 0..10 { 116 | let min = Point::new( 117 | random_range(-5.0, 5.0), 118 | random_range(-5.0, 5.0), 119 | random_range(-5.0, 5.0), 120 | ); 121 | let offset = Vector3::new( 122 | random_range(0.1, 2.0), 123 | random_range(0.1, 2.0), 124 | random_range(0.1, 2.0), 125 | ); 126 | aabbs.push(AABB { 127 | min: min, 128 | max: min + offset, 129 | }); 130 | } 131 | 132 | bencher.iter(|| { 133 | for lhs in &aabbs { 134 | for rhs in &aabbs { 135 | test::black_box(lhs.test_aabb(rhs)); 136 | } 137 | } 138 | }); 139 | } 140 | 141 | fn random_range(min: f32, max: f32) -> f32 { 142 | let range = max - min; 143 | rand::random::() * range + min 144 | } 145 | -------------------------------------------------------------------------------- /benches/grid_collision.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate gunship; 4 | extern crate hash; 5 | extern crate rand; 6 | extern crate test; 7 | 8 | use gunship::*; 9 | use gunship::component::collider::grid_collision::{CollisionGrid, GridCell}; 10 | use hash::fnv::FnvHasher; 11 | use self::test::Bencher; 12 | use std::hash::Hash; 13 | 14 | macro_rules! random_range { 15 | ($ty:ty, $min:expr, $max:expr) => {{ 16 | let range = $max - $min; 17 | rand::random::<$ty>() * range + $min 18 | }} 19 | } 20 | 21 | #[bench] 22 | fn hash_grid_cell_x1000(bencher: &mut Bencher) { 23 | let mut hasher = FnvHasher::default(); 24 | 25 | bencher.iter(|| { 26 | for x in 0..10 { 27 | for y in 0..10 { 28 | for z in 0..10 { 29 | let grid_cell = test::black_box(GridCell::new(x, y, z)); 30 | test::black_box(grid_cell.hash(&mut hasher)); 31 | } 32 | } 33 | } 34 | }); 35 | } 36 | 37 | #[bench] 38 | fn grid_lookup_x1000(bencher: &mut Bencher) { 39 | // Fill up the grid in advance 40 | let mut collision_grid = CollisionGrid::default(); 41 | for grid_cell in GridCell::new(-50, -50, -50).iter_to(GridCell::new(50, 50, 50)) { 42 | collision_grid.insert(grid_cell, Vec::new()); 43 | } 44 | 45 | bencher.iter(|| { 46 | for x in 0..10 { 47 | for y in 0..10 { 48 | for z in 0..10 { 49 | test::black_box(collision_grid.get(&GridCell::new(x, y, z))); 50 | } 51 | } 52 | } 53 | }); 54 | } 55 | 56 | fn collision_bench(bencher: &mut Bencher) { 57 | let mut engine = Engine::new(); 58 | 59 | { 60 | let scene = engine.scene_mut(); 61 | 62 | scene.resource_manager().set_resource_path("examples/"); 63 | scene.resource_manager().load_model("meshes/cube.dae").unwrap(); 64 | 65 | let mut transform_manager = scene.get_manager_mut::(); 66 | let mut collider_manager = scene.get_manager_mut::(); 67 | 68 | // Create some amount of cubes. 69 | for _ in 0..1_000 { 70 | let entity = scene.create_entity(); 71 | let mut transform = transform_manager.assign(entity); 72 | transform.set_position(Point::new( 73 | rand::random::() * 10.0 - 5.0, 74 | rand::random::() * 10.0 - 5.0, 75 | 0.0)); 76 | collider_manager.assign(entity, Collider::Sphere { 77 | offset: Vector3::zero(), 78 | radius: 0.5, 79 | }); 80 | collider_manager.assign_callback(entity, callback); 81 | } 82 | } 83 | 84 | bencher.iter(|| { 85 | ::gunship::engine::do_collision_update(&mut engine); 86 | }); 87 | } 88 | 89 | fn callback(_scene: &Scene, _entity: Entity, _other: Entity) {} 90 | -------------------------------------------------------------------------------- /examples/benchmarks/circle_movement.rs: -------------------------------------------------------------------------------- 1 | extern crate gunship; 2 | extern crate rand; 3 | 4 | use std::f32::consts::PI; 5 | 6 | use gunship::*; 7 | 8 | const TOTAL_CUBES: usize = 1000; 9 | 10 | fn main() { 11 | let mut engine = Engine::new(); 12 | 13 | engine.register_system(CircleMovementSystem); 14 | setup_scene(engine.scene_mut()); 15 | 16 | engine.main_loop(); 17 | } 18 | 19 | fn setup_scene(scene: &mut Scene) { 20 | scene.register_manager(CircleMovementManager::new()); 21 | 22 | scene.resource_manager().set_resource_path("examples/"); 23 | scene.resource_manager().load_model("meshes/cube.dae").unwrap(); 24 | 25 | let mut transform_manager = scene.get_manager_mut::(); 26 | let mut camera_manager = scene.get_manager_mut::(); 27 | let mut light_manager = scene.get_manager_mut::(); 28 | let mut circle_movement_manager = scene.get_manager_mut::(); 29 | let mut mesh_manager = scene.get_manager_mut::(); 30 | 31 | // Create camera. 32 | { 33 | let camera = scene.create_entity(); 34 | let mut camera_transform = transform_manager.assign(camera); 35 | camera_transform.set_position(Point::new(0.0, 0.0, 30.0)); 36 | camera_transform.look_at(Point::origin(), Vector3::new(0.0, 0.0, -1.0)); 37 | camera_manager.assign( 38 | camera, 39 | Camera::new( 40 | PI / 3.0, 41 | 1.0, 42 | 0.001, 43 | 100.0)); 44 | } 45 | 46 | // Create light. 47 | { 48 | let light = scene.create_entity(); 49 | transform_manager.assign(light); 50 | light_manager.assign( 51 | light, 52 | Light::Point(PointLight { 53 | position: Point::origin() 54 | })); 55 | } 56 | 57 | // Create some amount of cubes. 58 | for _ in 0..TOTAL_CUBES { 59 | let entity = scene.create_entity(); 60 | transform_manager.assign(entity); 61 | circle_movement_manager.assign(entity, CircleMovement::new()); 62 | mesh_manager.assign(entity, "cube.pCube1"); 63 | } 64 | } 65 | 66 | #[derive(Debug, Clone)] 67 | struct CircleMovement { 68 | center: Point, 69 | radius: f32, 70 | period: f32, 71 | offset: f32, 72 | } 73 | 74 | impl CircleMovement { 75 | fn new() -> CircleMovement { 76 | CircleMovement { 77 | center: Point::new(rand::random::() * 30.0 - 15.0, rand::random::() * 30.0 - 15.0, 0.0), 78 | radius: rand::random::() * 4.0 + 1.0, 79 | period: rand::random::() * 4.0 + 1.0, 80 | offset: 0.0, 81 | } 82 | } 83 | } 84 | 85 | type CircleMovementManager = StructComponentManager; 86 | 87 | #[derive(Debug, Clone)] 88 | struct CircleMovementSystem; 89 | 90 | impl System for CircleMovementSystem { 91 | fn update(&mut self, scene: &Scene, delta: f32) { 92 | let circle_movement_manager = scene.get_manager::(); 93 | let transform_manager = scene.get_manager::(); 94 | 95 | for (mut circle_movement, entity) in circle_movement_manager.iter_mut() { 96 | let mut transform = transform_manager.get_mut(entity); 97 | 98 | // Calculate the position of the cube. 99 | circle_movement.offset += delta; 100 | let t = circle_movement.offset * PI * 2.0 / circle_movement.period; 101 | let x_offset = t.cos() * circle_movement.radius; 102 | let y_offset = t.sin() * circle_movement.radius; 103 | 104 | // Move the cube. 105 | transform.set_position( circle_movement.center + Vector3::new( x_offset, y_offset, 0.0 ) ); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /examples/benchmarks/create_destroy.rs: -------------------------------------------------------------------------------- 1 | extern crate gunship; 2 | extern crate rand; 3 | 4 | use std::collections::VecDeque; 5 | use std::f32::consts::PI; 6 | 7 | use gunship::*; 8 | 9 | const ENTITIES_TO_CREATE: usize = 10_000; 10 | const ENTITIES_TO_DESTROY: usize = 1_000; 11 | 12 | fn main() { 13 | let mut engine = Engine::new(); 14 | 15 | engine.register_system(CreateDestroySystem { 16 | entities: VecDeque::new(), 17 | }); 18 | setup_scene(engine.scene_mut()); 19 | 20 | engine.main_loop(); 21 | } 22 | 23 | fn setup_scene(scene: &mut Scene) { 24 | scene.resource_manager().set_resource_path("examples/"); 25 | scene.resource_manager().load_model("meshes/cube.dae").unwrap(); 26 | 27 | let mut transform_manager = scene.get_manager_mut::(); 28 | let mut camera_manager = scene.get_manager_mut::(); 29 | let mut light_manager = scene.get_manager_mut::(); 30 | 31 | // Create camera. 32 | { 33 | let camera = scene.create_entity(); 34 | let mut camera_transform = transform_manager.assign(camera); 35 | camera_transform.set_position(Point::new(0.0, 0.0, 30.0)); 36 | camera_transform.look_at(Point::origin(), Vector3::new(0.0, 0.0, -1.0)); 37 | camera_manager.assign( 38 | camera, 39 | Camera::new( 40 | PI / 3.0, 41 | 1.0, 42 | 0.001, 43 | 100.0)); 44 | } 45 | 46 | // Create light. 47 | { 48 | let light = scene.create_entity(); 49 | transform_manager.assign(light); 50 | light_manager.assign( 51 | light, 52 | Light::Point(PointLight { 53 | position: Point::origin() 54 | })); 55 | } 56 | } 57 | 58 | #[derive(Debug, Clone)] 59 | struct CreateDestroySystem { 60 | entities: VecDeque, 61 | } 62 | 63 | impl System for CreateDestroySystem { 64 | fn update(&mut self, scene: &Scene, _delta: f32) { 65 | let mut transform_manager = scene.get_manager_mut::(); 66 | let mut mesh_manager = scene.get_manager_mut::(); 67 | 68 | while self.entities.len() < ENTITIES_TO_CREATE { 69 | let entity = scene.create_entity(); 70 | 71 | let mut transform = transform_manager.assign(entity); 72 | transform.set_position(Point::new(rand::random::() * 30.0 - 15.0, rand::random::() * 30.0 - 15.0, 0.0)); 73 | mesh_manager.assign(entity, "cube.pCube1"); 74 | 75 | self.entities.push_back(entity); 76 | } 77 | 78 | for _ in 0..ENTITIES_TO_DESTROY { 79 | let entity = self.entities.pop_front().unwrap(); 80 | // scene.destroy_entity(entity); // SOON... 81 | 82 | mesh_manager.destroy_immediate(entity); 83 | transform_manager.destroy_immediate(entity); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /examples/empty.rs: -------------------------------------------------------------------------------- 1 | extern crate gunship; 2 | 3 | use gunship::engine::EngineBuilder; 4 | 5 | fn main() { 6 | let mut builder = EngineBuilder::new(); 7 | builder.max_workers(1); 8 | builder.build(|| {}); 9 | } 10 | -------------------------------------------------------------------------------- /examples/experiment.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate gunship; 3 | 4 | use gunship::*; 5 | use gunship::camera::Camera; 6 | use gunship::engine::EngineBuilder; 7 | use gunship::light::DirectionalLight; 8 | use gunship::mesh_renderer::MeshRenderer; 9 | use gunship::stopwatch::Stopwatch; 10 | use gunship::transform::Transform; 11 | use gunship::math::*; 12 | 13 | fn main() { 14 | let mut builder = EngineBuilder::new(); 15 | builder.max_workers(8); 16 | builder.build(|| { 17 | setup_scene(); 18 | }); 19 | 20 | // ENGINE HAS BEEN SHUT DOWN! 21 | } 22 | 23 | /// Things to do: 24 | /// 25 | /// 1. Load and create mesh resource. 26 | /// 2. Load and create material resource. 27 | /// 3. Create transform in scene and assign it a mesh and material. 28 | /// 4. Create transform in scene and assign it the camera. 29 | fn setup_scene() { 30 | // Start both async operations but don't await either, allowing both to run concurrently. 31 | let async_mesh = resource::load_mesh("lib/polygon_rs/resources/meshes/epps_head.obj"); 32 | let async_material = resource::load_material("lib/polygon_rs/resources/materials/diffuse_flat.material"); 33 | 34 | // Await the operations, suspending this fiber until they complete. 35 | let mesh = async_mesh.await().unwrap(); 36 | let _material = async_material.await().unwrap(); 37 | 38 | let mut mesh_transform = Transform::new(); 39 | let _mesh_renderer = MeshRenderer::new(&mesh, &mesh_transform); 40 | 41 | let mut camera_transform = Transform::new(); 42 | camera_transform.set_position(Point::new(0.0, 0.0, 10.0)); 43 | let camera = Camera::new(&camera_transform); // TODO: Don't drop the camera, it needs to stay in scope. 44 | 45 | let light = DirectionalLight::new(Vector3::new(1.0, -1.0, -1.0), Color::rgb(1.0, 1.0, 1.0), 0.25); 46 | 47 | let mut time: f32 = 0.0; 48 | engine::run_each_frame(move || { 49 | let _s = Stopwatch::new("Move mesh"); 50 | 51 | time += time::delta_f32() * TAU / 10.0; 52 | let new_pos = Point::new( 53 | time.cos() * 3.0, 54 | time.sin() * 3.0, 55 | 0.0, 56 | ); 57 | mesh_transform.set_position(new_pos); 58 | }); 59 | 60 | engine::run_each_frame(move || { 61 | let _s = Stopwatch::new("Move camera"); 62 | 63 | time += time::delta_f32() * TAU / 3.0; 64 | let new_pos = Point::new( 65 | 0.0, 66 | 0.0, 67 | 10.0 + time.cos() * 2.0, 68 | ); 69 | camera_transform.set_position(new_pos); 70 | }); 71 | } 72 | -------------------------------------------------------------------------------- /examples/lots_of_cubes.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate gunship; 3 | 4 | use gunship::*; 5 | use gunship::camera::Camera; 6 | use gunship::engine::EngineBuilder; 7 | use gunship::light::DirectionalLight; 8 | use gunship::mesh_renderer::MeshRenderer; 9 | use gunship::transform::Transform; 10 | use gunship::math::*; 11 | 12 | fn main() { 13 | let mut builder = EngineBuilder::new(); 14 | builder.max_workers(8); 15 | builder.build(|| { 16 | setup_scene(); 17 | }); 18 | 19 | // ENGINE HAS BEEN SHUT DOWN! 20 | } 21 | 22 | /// Things to do: 23 | /// 24 | /// 1. Load and create mesh resource. 25 | /// 2. Load and create material resource. 26 | /// 3. Create transform in scene and assign it a mesh and material. 27 | /// 4. Create transform in scene and assign it the camera. 28 | fn setup_scene() { 29 | // Start both async operations but don't await either, allowing both to run concurrently. 30 | let mesh = resource::load_mesh("lib/polygon_rs/resources/meshes/epps_head.obj").await().unwrap(); 31 | 32 | DirectionalLight::new(Vector3::new(1.0, -1.0, -1.0), Color::rgb(1.0, 1.0, 1.0), 0.25).forget(); 33 | 34 | let mut camera_transform = Transform::new(); 35 | camera_transform.set_position(Point::new(0.0, 0.0, 35.0)); 36 | Camera::new(&camera_transform).forget(); // TODO: Don't drop the camera, it needs to stay in scope. 37 | camera_transform.forget(); 38 | 39 | // -10 --- 10 40 | // X X X X -10 41 | // X X X X | 42 | // X X X X | 43 | // X X X X 10 44 | const NUM_ROWS: usize = 4; 45 | const SQUARE_SIZE: f32 = 20.0; 46 | 47 | fn coord(pos: usize) -> f32 { 48 | -(SQUARE_SIZE / 2.0) + (SQUARE_SIZE / NUM_ROWS as f32) * pos as f32 49 | } 50 | 51 | for row in 0..NUM_ROWS { 52 | for col in 0..NUM_ROWS { 53 | let mut mesh_transform = Transform::new(); 54 | mesh_transform.set_position(Point::new( 55 | coord(col), 56 | coord(row), 57 | 0.0, 58 | )); 59 | MeshRenderer::new(&mesh, &mesh_transform).forget(); 60 | mesh_transform.forget(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/bootstrap-gl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "bootstrap-gl" 4 | version = "0.1.0" 5 | authors = ["David LeGare "] 6 | 7 | [dependencies] 8 | kernel32-sys = "0.2" 9 | gdi32-sys = "0.2" 10 | opengl32-sys = "0.1" 11 | user32-sys = "0.1" 12 | winapi = "0.2" 13 | 14 | [dev_dependencies] 15 | bootstrap_rs = { path = "../bootstrap_rs" } 16 | stopwatch = { path = "../stopwatch" } 17 | -------------------------------------------------------------------------------- /lib/bootstrap-gl/examples/empty.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate bootstrap_gl as gl; 3 | extern crate stopwatch; 4 | 5 | use bootstrap::window::*; 6 | use gl::*; 7 | use std::fs::File; 8 | use std::io::Write; 9 | use std::thread; 10 | use std::time::*; 11 | use stopwatch::*; 12 | 13 | fn main() { 14 | let mut window = Window::new("OpenGL performance test").unwrap(); 15 | 16 | let mut times = Vec::with_capacity(10_000); 17 | 18 | let device_context = window.platform().device_context(); 19 | let context = unsafe { gl::create_context(device_context).unwrap() }; 20 | unsafe { gl::make_current(context); } 21 | 22 | let target_frame_time = Duration::new(0, 1_000_000_000 / 60); 23 | let mut frame_start = Instant::now(); 24 | let start_time = frame_start; 25 | 26 | 'outer: loop { 27 | { 28 | let _s = Stopwatch::new("Loop"); 29 | 30 | { 31 | let _s = Stopwatch::new("Window messages"); 32 | while let Some(message) = window.next_message() { 33 | if let Message::Close = message { break 'outer; } 34 | } 35 | } 36 | 37 | { 38 | let _s = Stopwatch::new("Clear buffer"); 39 | unsafe { gl::clear(ClearBufferMask::Color | ClearBufferMask::Depth); } 40 | } 41 | 42 | { 43 | let _s = Stopwatch::new("Swap buffers"); 44 | unsafe { gl::platform::swap_buffers(context); } 45 | } 46 | 47 | times.push(frame_start.elapsed()); 48 | } 49 | 50 | // Determine the next frame's start time, dropping frames if we missed the frame time. 51 | while frame_start < Instant::now() { 52 | frame_start += target_frame_time; 53 | } 54 | 55 | // Now wait until we've returned to the frame cadence before beginning the next frame. 56 | while Instant::now() < frame_start { 57 | thread::sleep(Duration::new(0, 0)); 58 | } 59 | } 60 | 61 | let run_duration = start_time.elapsed(); 62 | let stats = stats::analyze(&*times, target_frame_time); 63 | 64 | println!("Performance statistics:"); 65 | println!(" Duration: {} ({} frames)", PrettyDuration(run_duration), times.len()); 66 | println!(" Min: {}", PrettyDuration(stats.min)); 67 | println!(" Max: {}", PrettyDuration(stats.max)); 68 | println!(" Mean: {}", PrettyDuration(stats.mean)); 69 | println!(" Std: {}", PrettyDuration(stats.std)); 70 | println!(" Long frames: {} ({:.2}%)", stats.long_frames, stats.long_frame_ratio * 100.0); 71 | 72 | let events_string = stopwatch::write_events_to_string(); 73 | let mut out_file = File::create("bootstrap_gl_empty.json").unwrap(); 74 | out_file.write_all(events_string.as_bytes()).unwrap(); 75 | } 76 | -------------------------------------------------------------------------------- /lib/bootstrap-gl/examples/hello_triangle.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate bootstrap_gl as gl; 3 | extern crate stopwatch; 4 | 5 | use bootstrap::window::*; 6 | use gl::types::*; 7 | use std::time::{Duration, Instant}; 8 | use stopwatch::PrettyDuration; 9 | 10 | static VERTEX_POSITIONS: &'static [f32] = &[ 11 | -1.0, -1.0, 0.0, 12 | 1.0, -1.0, 0.0, 13 | 0.0, 1.0, 0.0, 14 | ]; 15 | 16 | fn main() { 17 | // Open a window to be used as a target for rendering. 18 | let mut window = Window::new("Hello, Triangle!").unwrap(); 19 | 20 | // Create an OpenGL context for the window. 21 | let device_context = window.platform().device_context(); 22 | let context = unsafe { 23 | let context = gl::create_context(device_context).unwrap(); 24 | gl::make_current(context); 25 | context 26 | }; 27 | 28 | // Create a vertex buffer to store the vertices of the triangle. We provide it with data and 29 | // specify the layout of that data. 30 | let buffer_name = gl::gen_buffer().unwrap(); 31 | unsafe { 32 | gl::bind_buffer(BufferTarget::Array, buffer_name); 33 | gl::buffer_data(BufferTarget::Array, VERTEX_POSITIONS, BufferUsage::StaticDraw); 34 | } 35 | 36 | // Create the vertex array object to hold the state needed to draw. 37 | let vertex_array_name = gl::gen_vertex_array().unwrap(); 38 | unsafe { 39 | gl::bind_vertex_array(vertex_array_name); 40 | gl::enable_vertex_attrib_array(AttributeLocation::from_index(0)); 41 | gl::vertex_attrib_pointer( 42 | AttributeLocation::from_index(0), 43 | 3, // components per vertex 44 | GlType::Float, 45 | False, 46 | 0, // stride (bytes) 47 | 0, // offset (bytes) 48 | ); 49 | } 50 | 51 | unsafe { gl::platform::set_swap_interval(0); } 52 | 53 | let mut last_frame_time = Instant::now(); 54 | 55 | 'outer: loop { 56 | while let Some(message) = window.next_message() { 57 | match message { 58 | Message::Close => break 'outer, 59 | _ => {}, 60 | } 61 | } 62 | 63 | unsafe { 64 | gl::clear(ClearBufferMask::Color | ClearBufferMask::Depth); 65 | 66 | gl::draw_arrays( 67 | DrawMode::Triangles, 68 | 0, // offset 69 | 3, // elements to draw 70 | ); 71 | } 72 | 73 | let swap_time = unsafe { 74 | let timer = Instant::now(); 75 | 76 | gl::swap_buffers(context); 77 | 78 | timer.elapsed() 79 | }; 80 | 81 | if last_frame_time.elapsed() > Duration::new(0, 17_000_000) { 82 | println!("!!! loop time exceeded: {:?}, swap time: {:?}", PrettyDuration(last_frame_time.elapsed()), PrettyDuration(swap_time)); 83 | } else { 84 | // println!("loop time: {:?}, swap time: {:?}", PrettyDuration(last_frame_time.elapsed()), PrettyDuration(swap_time)); 85 | } 86 | 87 | // while last_frame_time.elapsed() < Duration::new(0, 16_666_666) {} 88 | 89 | last_frame_time = Instant::now(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/bootstrap-gl/src/linux.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CString; 2 | use std::mem; 3 | use std::ptr; 4 | 5 | use gl; 6 | use super::x11::glx; 7 | // use super::x11::xlib; 8 | 9 | use window::Window; 10 | 11 | pub type Context = glx::GLXContext; 12 | 13 | pub unsafe fn init(_window: &Window) { 14 | println!("gl::init() is not implemented on linux"); 15 | } 16 | 17 | pub unsafe fn create_context(window: &Window) -> GLContext { 18 | let context = unsafe { 19 | let context = glx::glXCreateContext(window.display, window.visual_info, ptr::null_mut(), 1); 20 | glx::glXMakeCurrent(window.display, window.window, context); 21 | context 22 | }; 23 | 24 | set_proc_loader(); 25 | 26 | context 27 | } 28 | 29 | pub unsafe fn set_proc_loader() { 30 | // provide method for loading functions 31 | gl::load_with(|s| { 32 | let string = CString::new(s); 33 | mem::transmute(glx::glXGetProcAddress(mem::transmute(string.unwrap().as_ptr()))) 34 | }); 35 | } 36 | 37 | pub unsafe fn swap_buffers(window: &Window) { 38 | glx::glXSwapBuffers(window.display, window.window); 39 | } 40 | -------------------------------------------------------------------------------- /lib/bootstrap-gl/src/macos.rs: -------------------------------------------------------------------------------- 1 | use bootstrap::window::Window; 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | pub struct Context; 5 | 6 | pub fn init(_window: &Window) { 7 | } 8 | 9 | pub fn create_context(_window: &Window) -> Context { 10 | Context 11 | } 12 | 13 | pub fn destroy_context(_context: Context) { 14 | } 15 | 16 | pub fn proc_loader(_proc_name: &str) -> Option { 17 | None 18 | } 19 | 20 | pub fn swap_buffers(_window: &Window) { 21 | } 22 | -------------------------------------------------------------------------------- /lib/bootstrap-gl/src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Macro used for generating bindings to OpenGL procs. 2 | /// 3 | /// The OpenGL implementation for a computer actually lives in its graphics card. In order to call 4 | /// the various functions that are part of the OpenGL API we must load pointers to those functions. 5 | /// This macro generates the necessary boilerplate for loading and stashing those pointers, as well 6 | /// as handling failure when those pointers fail to load (i.e. panicking). 7 | /// 8 | /// TODO: Add a variant where the same gl proc can be mapped to multiple rust functions to improve 9 | /// type safety specification. 10 | #[macro_export] 11 | macro_rules! gl_proc { 12 | ( $proc_name:ident: 13 | $( #[$attr:meta] )* fn $fn_name:ident( $( $arg:ident : $arg_ty:ty ),* ) $( -> $result:ty )* ) => { 14 | $( #[$attr] )* 15 | pub unsafe fn $fn_name( $( $arg: $arg_ty, )* ) $( -> $result )* { 16 | match $fn_name::load() { 17 | Some(gl_proc) => gl_proc( $( $arg ),* ), 18 | None => panic!("Failed to load gl proc for {}", stringify!( $proc_name )), 19 | } 20 | } 21 | 22 | pub mod $fn_name { 23 | #[allow(unused_imports)] 24 | use types::*; 25 | 26 | static mut PROC_PTR: Option = None; 27 | 28 | pub type ProcType = extern "system" fn( $( $arg_ty, )* ) $( -> $result )*; 29 | 30 | pub unsafe fn load() -> Option { 31 | if let None = PROC_PTR { 32 | let null_terminated_name = concat!(stringify!($proc_name), "\0"); 33 | PROC_PTR = 34 | $crate::platform::load_proc(null_terminated_name) 35 | .map(|ptr| ::std::mem::transmute(ptr)); 36 | } 37 | 38 | PROC_PTR 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/bootstrap-gl/src/windows.rs: -------------------------------------------------------------------------------- 1 | extern crate winapi; 2 | extern crate opengl32; 3 | extern crate gdi32; 4 | extern crate user32; 5 | extern crate kernel32; 6 | 7 | use std::{mem, ptr}; 8 | 9 | use self::winapi::*; 10 | 11 | pub type DeviceContext = HDC; 12 | pub type Context = (HDC, HGLRC); 13 | 14 | pub unsafe fn create_context(device_context: DeviceContext) -> Option { 15 | let tmp_context = opengl32::wglCreateContext(device_context); 16 | if tmp_context.is_null() { 17 | return None; 18 | } 19 | 20 | make_current((device_context, tmp_context)); 21 | 22 | let render_context = create_context_attribs(device_context, ptr::null_mut(), ptr::null()); 23 | 24 | clear_current(); 25 | opengl32::wglDeleteContext(tmp_context); 26 | 27 | if render_context.is_null() { 28 | let error = kernel32::GetLastError(); 29 | println!("WARNING: Failed to created OpenGL context, last error: {:#x}", error); 30 | None 31 | } else { 32 | make_current((device_context, render_context)); 33 | 34 | // TODO: Don't do this in context creation. 35 | if set_swap_interval(0) != ::types::Boolean::True { 36 | println!("WARNING: Failed to set swap interval of setting swap interval"); 37 | } 38 | 39 | clear_current(); 40 | 41 | Some((device_context, render_context)) 42 | } 43 | } 44 | 45 | pub unsafe fn destroy_context(context: Context) { 46 | let (_, render_context) = context; 47 | clear_current(); 48 | 49 | let result = opengl32::wglDeleteContext(render_context); 50 | 51 | assert!(result == 1, "Failed to delete context: {:?}", render_context); 52 | } 53 | 54 | pub unsafe fn load_proc(proc_name: &str) -> Option { 55 | let string = proc_name.as_bytes(); 56 | debug_assert!( 57 | string[string.len() - 1] == 0, 58 | "Proc name \"{}\" is not null terminated", 59 | proc_name, 60 | ); 61 | 62 | let mut ptr = opengl32::wglGetProcAddress(string.as_ptr() as *const _); 63 | 64 | if ptr.is_null() { 65 | let module = kernel32::LoadLibraryA(b"opengl32.dll\0".as_ptr() as *const _); 66 | 67 | // TODO: What do we want to do in this case? Probably just return `None`, right? 68 | assert!(!module.is_null(), "Failed to load opengl32.dll"); 69 | 70 | ptr = kernel32::GetProcAddress(module, string.as_ptr() as *const _); 71 | } 72 | 73 | if ptr.is_null() { 74 | let actual_dc = opengl32::wglGetCurrentDC(); 75 | let actual_context = opengl32::wglGetCurrentContext(); 76 | println!( 77 | "pointer for {} was null, last error: 0x{:X}, active dc: {:?}, active context: {:?}", 78 | proc_name, 79 | kernel32::GetLastError(), 80 | actual_dc, 81 | actual_context, 82 | ); 83 | 84 | return None; 85 | } 86 | 87 | Some(mem::transmute(ptr)) 88 | } 89 | 90 | pub unsafe fn swap_buffers(context: Context) { 91 | let (device_context, _) = context; 92 | if gdi32::SwapBuffers(device_context) != TRUE { 93 | let (device_context, render_context) = context; 94 | let hwnd = user32::GetActiveWindow(); 95 | panic!( 96 | "Swap buffers failed, dc: {:?}, context: {:?} last error: 0x:{:X}, hwnd: {:?}", 97 | device_context, 98 | render_context, 99 | kernel32::GetLastError(), 100 | hwnd, 101 | ); 102 | } 103 | } 104 | 105 | pub unsafe fn make_current(context: Context) -> Context { 106 | let old_device_context = opengl32::wglGetCurrentDC(); 107 | let old_render_context = opengl32::wglGetCurrentContext(); 108 | 109 | let (device_context, render_context) = context; 110 | let result = opengl32::wglMakeCurrent(device_context, render_context); 111 | if result != TRUE { 112 | let hwnd = user32::GetActiveWindow(); 113 | panic!( 114 | "Failed to make context current, dc: {:?}, context: {:?} last error: 0x:{:X}, actual dc and context: {:?} and {:?}, hwnd: {:?}", 115 | device_context, 116 | render_context, 117 | kernel32::GetLastError(), 118 | old_device_context, 119 | old_render_context, 120 | hwnd, 121 | ); 122 | } 123 | 124 | (old_device_context, old_render_context) 125 | } 126 | 127 | pub unsafe fn clear_current() { 128 | make_current((ptr::null_mut(), ptr::null_mut())); 129 | } 130 | 131 | gl_proc!(wglGetExtensionsStringARB: 132 | fn get_extension_string(hdc: ::platform::winapi::HDC) -> *const u8); 133 | 134 | gl_proc!(wglCreateContextAttribsARB: 135 | fn create_context_attribs( 136 | hdc: ::platform::winapi::HDC, 137 | share_context: ::platform::winapi::HGLRC, 138 | attrib_list: *const i32 139 | ) -> ::platform::winapi::HGLRC); 140 | 141 | gl_proc!(wglGetSwapIntervalEXT: 142 | fn get_swap_interval() -> i32); 143 | 144 | gl_proc!(wglSwapIntervalEXT: 145 | fn set_swap_interval(interval: i32) -> ::types::Boolean); 146 | -------------------------------------------------------------------------------- /lib/bootstrap_audio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bootstrap_audio" 3 | version = "0.0.1" 4 | authors = ["David LeGare "] 5 | 6 | [dependencies] 7 | libc = "*" 8 | winapi = "*" 9 | ole32-sys = "*" 10 | -------------------------------------------------------------------------------- /lib/bootstrap_audio/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(windows)] 2 | #[path="windows.rs"] 3 | mod audio_impl; 4 | 5 | #[cfg(unix)] 6 | #[path="linux.rs"] 7 | mod audio_impl; 8 | 9 | pub use audio_impl::{AudioSource, init}; 10 | -------------------------------------------------------------------------------- /lib/bootstrap_audio/src/linux.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone)] 2 | pub struct AudioSource; 3 | 4 | impl AudioSource { 5 | pub fn stream>(&self, _data_source: &mut T, _max_time: f32) -> usize { 6 | 0 7 | } 8 | } 9 | 10 | pub fn init() -> Result { 11 | println!("bootstrap_audio::init() has not been implemented yet for linux"); 12 | Ok(AudioSource) 13 | } 14 | -------------------------------------------------------------------------------- /lib/bootstrap_rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bootstrap_rs" 3 | version = "0.0.0" 4 | authors = ["David LeGare "] 5 | build = "build.rs" 6 | 7 | [dependencies] 8 | cell-extras = { git = "https://github.com/excaliburHisSheath/cell-extras" } 9 | 10 | [target.'cfg(target_os = "windows")'.dependencies] 11 | winapi = "*" 12 | user32-sys = "*" 13 | kernel32-sys = "*" 14 | winmm-sys = "*" 15 | gdi32-sys = "0.2" 16 | 17 | [target.'cfg(target_os = "macos")'.dependencies] 18 | objc = "0.2" 19 | cocoa = { git = "https://github.com/excaliburHisSheath/cocoa-rs" } 20 | core-graphics = "0.3" 21 | core-foundation = "0.2" 22 | 23 | # [dependencies.x11] 24 | # version = "*" 25 | # features = ["glx", "xlib"] 26 | -------------------------------------------------------------------------------- /lib/bootstrap_rs/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | if cfg!(target_os = "linux") { 3 | println!("cargo:rustc-link-lib=dylib=Xi"); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/bootstrap_rs/examples/window.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | 3 | use bootstrap::window::*; 4 | 5 | fn main() { 6 | let mut window = Window::new("Bootstrap Window").unwrap(); 7 | 8 | for message in window.message_pump() { 9 | println!("message: {:?}", message); 10 | if let Message::Close = message { 11 | break; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/bootstrap_rs/src/input.rs: -------------------------------------------------------------------------------- 1 | pub use platform::input::{set_cursor_visibility, set_cursor_bounds, clear_cursor_bounds}; 2 | 3 | #[repr(u32)] 4 | #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] 5 | pub enum ScanCode { 6 | A = 'A' as u32, 7 | B = 'B' as u32, 8 | C = 'C' as u32, 9 | D = 'D' as u32, 10 | E = 'E' as u32, 11 | F = 'F' as u32, 12 | G = 'G' as u32, 13 | H = 'H' as u32, 14 | I = 'I' as u32, 15 | J = 'J' as u32, 16 | K = 'K' as u32, 17 | L = 'L' as u32, 18 | M = 'M' as u32, 19 | N = 'N' as u32, 20 | O = 'O' as u32, 21 | P = 'P' as u32, 22 | Q = 'Q' as u32, 23 | R = 'R' as u32, 24 | S = 'S' as u32, 25 | T = 'T' as u32, 26 | U = 'U' as u32, 27 | V = 'V' as u32, 28 | W = 'W' as u32, 29 | X = 'X' as u32, 30 | Y = 'Y' as u32, 31 | Z = 'Z' as u32, 32 | Key0 = '0' as u32, 33 | Key1 = '1' as u32, 34 | Key2 = '2' as u32, 35 | Key3 = '3' as u32, 36 | Key4 = '4' as u32, 37 | Key5 = '5' as u32, 38 | Key6 = '6' as u32, 39 | Key7 = '7' as u32, 40 | Key8 = '8' as u32, 41 | Key9 = '9' as u32, 42 | 43 | // TODO: Are these reasonable values for these codes? 44 | // These values were taken from the values observed from keypresses on my windows keyboard. 45 | // They're convenient for now because it means I can just reinterpret the scancodes I get from 46 | // Windows, but I don't know if these values make sense in a cross-platform context. 47 | Space = 32 as u32, 48 | F9 = 120 as u32, 49 | F10 = 121 as u32, 50 | F11 = 122 as u32, 51 | BackTick = 192 as u32, 52 | 53 | Unsupported, 54 | } 55 | -------------------------------------------------------------------------------- /lib/bootstrap_rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Almost certainly going to be stabilized as-is, unlikely to break anything. 2 | #![feature(const_fn)] 3 | 4 | // The scheduler puts a `Condvar` and `Mutex` into some statics. 5 | #![feature(drop_types_in_const)] 6 | 7 | extern crate cell_extras; 8 | 9 | // This `extern_crate` should be within the macos platform module, but `macro_use` only works at 10 | // the root of the crate. 11 | #[cfg(target_os = "macos")] 12 | #[macro_use] 13 | extern crate objc; 14 | 15 | #[cfg(target_os = "windows")] 16 | #[path="windows/mod.rs"] 17 | pub mod platform; 18 | 19 | #[cfg(target_os = "linux")] 20 | #[path="linux/mod.rs"] 21 | pub mod platform; 22 | 23 | #[cfg(target_os = "macos")] 24 | #[path="macos/mod.rs"] 25 | pub mod platform; 26 | 27 | pub mod window; 28 | pub mod input; 29 | -------------------------------------------------------------------------------- /lib/bootstrap_rs/src/linux/file.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/randomPoison/gunship-rs/5a7acd32d96370990252b75d4065aa4ffb9bc379/lib/bootstrap_rs/src/linux/file.rs -------------------------------------------------------------------------------- /lib/bootstrap_rs/src/linux/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate x11; 2 | 3 | pub mod file; 4 | pub mod window; 5 | -------------------------------------------------------------------------------- /lib/bootstrap_rs/src/macos/input.rs: -------------------------------------------------------------------------------- 1 | pub fn set_cursor_visibility(_visible: bool) {} 2 | 3 | pub fn set_cursor_bounds(_top: i32, _left: i32, _bottom: i32, _right: i32) {} 4 | 5 | pub fn clear_cursor_bounds() { 6 | } 7 | -------------------------------------------------------------------------------- /lib/bootstrap_rs/src/macos/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate cocoa; 2 | extern crate core_graphics; 3 | extern crate core_foundation; 4 | 5 | pub mod input; 6 | pub mod window; 7 | -------------------------------------------------------------------------------- /lib/bootstrap_rs/src/window.rs: -------------------------------------------------------------------------------- 1 | use input::ScanCode; 2 | use platform; 3 | 4 | /// Represents an open window on the host machine. 5 | #[derive(Debug)] 6 | pub struct Window(platform::window::Window); 7 | 8 | impl Window { 9 | /// Creates a new window named `name`. 10 | pub fn new(name: &str) -> Result { 11 | Ok(Window(platform::window::Window::new(name))) 12 | } 13 | 14 | /// Removes and returns the next pending message from the message queue. 15 | /// 16 | /// If no messages are pending returns `None` and does not block. 17 | pub fn next_message(&mut self) -> Option { 18 | self.0.next_message() 19 | } 20 | 21 | /// Removes and returns the next pending message, blocking until one is available. 22 | /// 23 | /// If there are no pending messages in the message queue this method blocks until 24 | /// the next one is available. This method may still return `None` if it is not 25 | /// possible for the window to yield more messages (e.g. if the window is closed). 26 | pub fn wait_message(&mut self) -> Option { 27 | self.0.wait_message() 28 | } 29 | 30 | /// Gets the bounds describing the position/size of the window. 31 | // TODO: Return more structured, less platform-specific data. 32 | pub fn get_rect(&self) -> (i32, i32, i32, i32) { 33 | self.0.get_rect() 34 | } 35 | 36 | /// Creates a message pump for the window. 37 | /// 38 | /// A message pump allows message processing for a window to be offloaded to a worker thread 39 | /// without having to give away ownership of the window. 40 | pub fn message_pump(&mut self) -> MessagePump { 41 | MessagePump(self.0.inner()) 42 | } 43 | 44 | /// Gets a reference to the platform-specific implementation of the window. 45 | pub fn platform(&self) -> &platform::window::Window { 46 | &self.0 47 | } 48 | } 49 | 50 | impl<'a> Iterator for &'a mut Window { 51 | type Item = Message; 52 | 53 | fn next(&mut self) -> Option { 54 | self.next_message() 55 | } 56 | } 57 | 58 | #[derive(Debug)] 59 | pub enum CreateWindowError { 60 | } 61 | 62 | /// An iterator that does message processing for `Window`. 63 | pub struct MessagePump(platform::window::WindowInner); 64 | 65 | impl MessagePump { 66 | /// Pumps all messages for the window, blocking until there are no more messages to be pumped. 67 | pub fn run(&mut self) { 68 | self.0.pump_forever(); 69 | } 70 | } 71 | 72 | impl Iterator for MessagePump { 73 | type Item = Message; 74 | 75 | fn next(&mut self) -> Option { 76 | self.0.wait_message() 77 | } 78 | } 79 | 80 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 81 | pub enum Message { 82 | Activate, 83 | Close, 84 | Destroy, 85 | Paint, 86 | KeyUp(ScanCode), 87 | KeyDown(ScanCode), 88 | 89 | /// The x movement and y movement in pixels. 90 | MouseMove(i32, i32), 91 | 92 | /// The x and y coordinates in pixels. 93 | /// 94 | /// These coordinates are relative to the window, with the upper-left corner 95 | /// of the window being (0, 0). 96 | // TODO: Change the origin to be the lower-left corner instead. 97 | MousePos(i32, i32), 98 | 99 | /// Message signaling a mouse button has been pressed. 100 | /// 101 | /// This message is sent any time a mouse button has been pressed. The wrapped 102 | /// value is the index of button on the mouse: 0 is LMB, 1 is RMB, 2 is LMB, with 103 | /// other button assignments being driver dependent (I assume). 104 | MouseButtonPressed(u8), 105 | 106 | /// Message signaling a mouse button has been released. 107 | /// 108 | /// This message is sent any time a mouse button has been released. The wrapped 109 | /// value is the index of button on the mouse: 0 is LMB, 1 is RMB, 2 is LMB, with 110 | /// other button assignments being driver dependent (I assume). 111 | MouseButtonReleased(u8), 112 | 113 | /// Message signalling how much the mouse wheel has been scrolled. 114 | /// 115 | /// This message is sent any time the mouse wheel is scrolled. The wrapped value 116 | /// is the amount the mouse wheel was scrolled, though the scale of this value 117 | /// is platform/driver dependent (I assume). 118 | MouseWheel(i32), 119 | } 120 | -------------------------------------------------------------------------------- /lib/bootstrap_rs/src/windows/file.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CString; 2 | use std::mem; 3 | use std::ptr; 4 | 5 | use super::winapi::*; 6 | use super::kernel32; 7 | 8 | pub fn file_modified(path: &str) -> Result { 9 | let cstring = CString::new(path).unwrap(); 10 | 11 | let handle = unsafe { 12 | kernel32::CreateFileA( 13 | cstring.as_ptr(), 14 | GENERIC_READ, 15 | FILE_SHARE_WRITE, 16 | ptr::null_mut(), 17 | OPEN_EXISTING, 18 | FILE_ATTRIBUTE_NORMAL, 19 | ptr::null_mut()) 20 | }; 21 | 22 | if handle == INVALID_HANDLE_VALUE { 23 | return Err(format!("Could not open file {}", path)); 24 | } 25 | 26 | let mut file_time = FILETIME { 27 | dwLowDateTime: 0, 28 | dwHighDateTime: 0, 29 | }; 30 | 31 | let result = unsafe { kernel32::GetFileTime(handle, ptr::null_mut(), ptr::null_mut(), &mut file_time) }; 32 | if result == 0 { 33 | return Err(format!("Unable to get modified time for the file {}", path)); 34 | } 35 | 36 | let result = unsafe { kernel32::CloseHandle(handle) }; 37 | if result == 0 { 38 | return Err(format!("Error while closing file handle for {}", path)); 39 | } 40 | 41 | Ok(unsafe { mem::transmute(file_time) }) 42 | } 43 | -------------------------------------------------------------------------------- /lib/bootstrap_rs/src/windows/input.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::mem::{self, size_of}; 3 | use std::ptr; 4 | use window::Message::*; 5 | use window::*; 6 | use super::winapi::*; 7 | use super::user32; 8 | 9 | // use windows::winapi::winuser::RAWINPUTDEVICE; 10 | // use windows::xinput::*; 11 | // 12 | // pub fn get_state(controller_index: DWORD) { 13 | // let mut state = XINPUT_STATE { 14 | // dwPacketNumber: 0, 15 | // Gamepad: XINPUT_GAMEPAD { 16 | // wButtons: 0, 17 | // bLeftTrigger: 0, 18 | // bRightTrigger: 0, 19 | // sThumbLX: 0, 20 | // sThumbLY: 0, 21 | // sThumbRX: 0, 22 | // sThumbRY: 0 23 | // } 24 | // }; 25 | // 26 | // // Simply get the state of the controller from XInput. 27 | // let result = unsafe { XInputGetState(controller_index, &mut state) }; 28 | // 29 | // println!("result: {}", result); 30 | // 31 | // if (result == ERROR_SUCCESS) { 32 | // // Controller is connected 33 | // } else { 34 | // // Controller is not connected 35 | // } 36 | // } 37 | 38 | pub fn set_cursor_visibility(visible: bool) { 39 | unsafe { user32::ShowCursor(visible as i32); } 40 | } 41 | 42 | pub fn set_cursor_bounds(top: i32, left: i32, bottom: i32, right: i32) { 43 | let rect = RECT { 44 | top: top, 45 | left: left, 46 | bottom: bottom, 47 | right: right, 48 | }; 49 | 50 | unsafe { 51 | user32::ClipCursor(&rect); 52 | } 53 | } 54 | 55 | pub fn clear_cursor_bounds() { 56 | unsafe { 57 | user32::ClipCursor(ptr::null()); 58 | } 59 | } 60 | 61 | pub fn register_raw_input(hwnd: HWND) { 62 | let devices = RAWINPUTDEVICE { 63 | usUsagePage: 0x01, 64 | usUsage: 0x02, 65 | dwFlags: 0, //RIDEV_NOLEGACY, // Adds HID mouse and also ignores legacy mouse messages. 66 | hwndTarget: hwnd 67 | }; 68 | 69 | if unsafe { user32::RegisterRawInputDevices(&devices, 1, size_of::() as u32) } == FALSE { 70 | // Registration failed. Call GetLastError for the cause of the error. 71 | println!("WARNING: Raw input registration failed because reasons."); 72 | } 73 | } 74 | 75 | pub fn handle_raw_input(messages: &mut VecDeque, lParam: LPARAM) { 76 | // Call GetRawInputData once to get the size of the data. 77 | let mut size: UINT = 0; 78 | unsafe { 79 | user32::GetRawInputData( 80 | lParam as HRAWINPUT, 81 | RID_INPUT, 82 | ptr::null_mut(), 83 | &mut size, 84 | size_of::() as u32); 85 | } 86 | 87 | let raw = unsafe { 88 | let mut raw = mem::uninitialized::(); 89 | assert!( 90 | user32::GetRawInputData( 91 | lParam as HRAWINPUT, 92 | RID_INPUT, 93 | ((&mut raw) as *mut RAWINPUT) as LPVOID, 94 | &mut size, 95 | size_of::() as u32) 96 | == size); 97 | raw 98 | }; 99 | 100 | let raw_mouse = unsafe { raw.mouse() }; 101 | 102 | assert!(raw.header.dwType == RIM_TYPEMOUSE); 103 | assert!(raw_mouse.usFlags == MOUSE_MOVE_RELATIVE); 104 | 105 | messages.push_back(MouseMove(raw_mouse.lLastX, raw_mouse.lLastY)); 106 | 107 | 108 | if raw_mouse.usButtonFlags != 0 { 109 | let button_flags = raw_mouse.usButtonFlags; 110 | if button_flags & RI_MOUSE_LEFT_BUTTON_DOWN != 0 { 111 | messages.push_back(MouseButtonPressed(0)); 112 | } 113 | if button_flags & RI_MOUSE_LEFT_BUTTON_UP != 0 { 114 | messages.push_back(MouseButtonReleased(0)); 115 | } 116 | if button_flags & RI_MOUSE_RIGHT_BUTTON_DOWN != 0 { 117 | messages.push_back(MouseButtonPressed(1)); 118 | } 119 | if button_flags & RI_MOUSE_RIGHT_BUTTON_UP != 0 { 120 | messages.push_back(MouseButtonReleased(1)); 121 | } 122 | if button_flags & RI_MOUSE_MIDDLE_BUTTON_DOWN != 0 { 123 | messages.push_back(MouseButtonPressed(2)); 124 | } 125 | if button_flags & RI_MOUSE_MIDDLE_BUTTON_UP != 0 { 126 | messages.push_back(MouseButtonReleased(2)); 127 | } 128 | if button_flags & RI_MOUSE_BUTTON_4_DOWN != 0 { 129 | messages.push_back(MouseButtonPressed(3)); 130 | } 131 | if button_flags & RI_MOUSE_BUTTON_4_UP != 0 { 132 | messages.push_back(MouseButtonReleased(3)); 133 | } 134 | if button_flags & RI_MOUSE_BUTTON_5_DOWN != 0 { 135 | messages.push_back(MouseButtonPressed(4)); 136 | } 137 | if button_flags & RI_MOUSE_BUTTON_5_UP != 0 { 138 | messages.push_back(MouseButtonReleased(4)); 139 | } 140 | if button_flags & RI_MOUSE_WHEEL != 0 { 141 | // NOTE: Mouse wheel handling is a bit of a nightmare. The raw input docs don't 142 | // specify anything meaningful about the data in `usButtonData`, but in practice it 143 | // seems to behave the same as the data for `WM_MOUSEWHEEL`, so that's how we interpret 144 | // it. The relevant docs are here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617.aspx 145 | 146 | // `usButtonData` is a u16, but if it represents a mouse wheel movement it's *actually* 147 | // signed, so we need to transmute it to treat it as signed. 148 | let scroll: i16 = unsafe { mem::transmute(raw_mouse.usButtonData) }; 149 | 150 | // The high order 16 bits provides the distance the wheel was rotated in multiples of 151 | // `WHEEL_DELTA`, so we divide by `WHEEL_DELTA` to get the value we want. 152 | let scroll = scroll as i32 / WHEEL_DELTA as i32; 153 | 154 | messages.push_back(MouseWheel(scroll)) 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /lib/bootstrap_rs/src/windows/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(bad_style)] 2 | 3 | extern crate gdi32; 4 | extern crate winapi; 5 | extern crate user32; 6 | extern crate kernel32; 7 | extern crate winmm; 8 | 9 | pub mod window; 10 | pub mod input; 11 | pub mod file; 12 | 13 | pub trait ToCU16Str { 14 | fn to_c_u16(&self) -> Vec; 15 | } 16 | 17 | impl<'a> ToCU16Str for &'a str { 18 | fn to_c_u16(&self) -> Vec { 19 | self.encode_utf16().chain(Some(0).into_iter()).collect() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/fiber/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fiber" 3 | version = "0.1.0" 4 | authors = ["David LeGare "] 5 | 6 | [target.'cfg(target_os="windows")'.dependencies] 7 | winapi = "0.2.8" 8 | kernel32-sys = "0.2.2" 9 | -------------------------------------------------------------------------------- /lib/fiber/src/platform/windows.rs: -------------------------------------------------------------------------------- 1 | extern crate kernel32; 2 | extern crate winapi; 3 | 4 | use ::{Fiber, PREV}; 5 | use std::mem; 6 | use std::ptr; 7 | use self::winapi::*; 8 | 9 | pub type PlatformId = LPVOID; 10 | 11 | pub fn init() -> PlatformId { 12 | let fiber = unsafe { kernel32::ConvertThreadToFiber(ptr::null_mut()) }; 13 | 14 | if fiber.is_null() { 15 | println!("ERROR: Failed to convert main thread to a fiber"); 16 | } 17 | 18 | fiber 19 | } 20 | 21 | pub fn create_fiber(stack_size: usize, func: fn(Fiber) -> !) -> PlatformId 22 | { 23 | let fiber = unsafe { 24 | kernel32::CreateFiber( 25 | stack_size as u32, 26 | Some(fiber_proc), 27 | func as LPVOID, 28 | ) 29 | }; 30 | 31 | // TODO: Return an error result, rather than just logging a warning. 32 | if fiber.is_null() { 33 | panic!("ERROR: Failed to create fiber"); 34 | } 35 | 36 | fiber 37 | } 38 | 39 | /// Makes `fiber` active, then returns the handle of the fiber that resumed the current one. 40 | pub unsafe fn resume(fiber: PlatformId) { 41 | kernel32::SwitchToFiber(fiber); 42 | } 43 | 44 | /// `data` is secretly a pointer to a `Box>`. 45 | unsafe extern "system" fn fiber_proc(data: LPVOID) { 46 | let func: fn(Fiber) -> ! = mem::transmute(data); 47 | let prev_fiber = PREV.with(|prev| prev.get().expect("PREV was None in fiber_proc()")); 48 | 49 | func(Fiber(prev_fiber)); 50 | } 51 | -------------------------------------------------------------------------------- /lib/fiber/tests/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate fiber; 2 | 3 | use fiber::Fiber; 4 | 5 | #[test] 6 | fn basic_usage() { 7 | fn fiber_proc(suspended: Fiber) -> ! { 8 | println!("Suspended fiber: {:?}", suspended); 9 | unsafe { suspended.resume(); } 10 | 11 | panic!("Uh-oh, shouldn't have resumed this fiber again"); 12 | } 13 | 14 | let fiber = Fiber::new(1024, fiber_proc); 15 | let fiber_id = fiber.id(); 16 | 17 | let prev = unsafe { fiber.resume() }; 18 | assert_eq!(fiber_id, prev.id()); 19 | } 20 | -------------------------------------------------------------------------------- /lib/gl-util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "gl-util" 4 | version = "0.1.0" 5 | authors = ["David LeGare "] 6 | 7 | [dependencies] 8 | bootstrap-gl = { version = "0.1", path = "../bootstrap-gl" } 9 | bootstrap_rs = { version = "0.0", path = "../bootstrap_rs" } 10 | 11 | [target.'cfg(target_os = "windows")'.dependencies] 12 | user32-sys = "0.1" 13 | winapi = "0.2.8" 14 | 15 | [dev_dependencies] 16 | parse-obj = { version = "0.1", path = "../parse-obj" } 17 | parse-bmp = { version = "0.1", path = "../parse-bmp" } 18 | -------------------------------------------------------------------------------- /lib/gl-util/examples/hello_triangle.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate gl_util as gl; 3 | 4 | use bootstrap::window::*; 5 | use gl::*; 6 | use gl::context::Context; 7 | 8 | static VERTEX_POSITIONS: [f32; 9] = [ 9 | -1.0, -1.0, 0.0, 10 | 1.0, -1.0, 0.0, 11 | 0.0, 1.0, 0.0, 12 | ]; 13 | 14 | fn main() { 15 | // Open a window to be used as a target for rendering. 16 | let mut window = Window::new("Hello, Triangle!").unwrap(); 17 | 18 | // Create the OpenGL context. `Context::new()` will attempt to find a default render target, 19 | // in this case it will use the window we just opened. 20 | let mut context = Context::from_window(&window).unwrap(); 21 | 22 | // Create the vertex array object, which groups all buffers for a mesh into a single object. 23 | let mut vertex_array = VertexArray::new(&context, &VERTEX_POSITIONS[..]); 24 | vertex_array.set_attrib( 25 | AttributeLocation::from_index(0), 26 | AttribLayout { elements: 3, offset: 0, stride: 0 }, 27 | ); 28 | 29 | // `DrawBuilder` is used to specify all of the various configuration options when drawing. In 30 | // this case we're using `vertex_buffer` in triangles mode, and we're sending its "position" 31 | // attribute to attribute location 0, which is the default for `glPosition`. 32 | let mut draw_builder = DrawBuilder::new(&mut context, &vertex_array, DrawMode::Triangles); 33 | 34 | 'outer: loop { 35 | while let Some(message) = window.next_message() { 36 | match message { 37 | Message::Close => break 'outer, 38 | _ => {}, 39 | } 40 | } 41 | 42 | // We use the context to clear the render target and swap buffers. `DrawBuilder` can be 43 | // used multiple to avoid having to re-configure the build for each draw. 44 | context.clear(); 45 | draw_builder.draw(); 46 | context.swap_buffers(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/gl-util/examples/shader.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate gl_util as gl; 3 | extern crate parse_obj; 4 | 5 | use bootstrap::window::*; 6 | use gl::*; 7 | use gl::context::Context; 8 | use gl::shader::*; 9 | use parse_obj::Obj; 10 | 11 | static VERT_SOURCE: &'static str = r#" 12 | #version 330 core 13 | 14 | uniform mat4 model_transform; 15 | 16 | layout(location = 0) in vec4 position; 17 | layout(location = 1) in vec3 normal_in; 18 | 19 | out vec3 normal; 20 | 21 | void main() { 22 | normal = normal_in; 23 | gl_Position = model_transform * position; 24 | } 25 | "#; 26 | 27 | static FRAG_SOURCE: &'static str = r#" 28 | #version 330 core 29 | 30 | uniform vec4 surface_color; 31 | 32 | in vec3 normal; 33 | out vec4 fragment_color; 34 | 35 | void main() { 36 | fragment_color = surface_color * vec4(normal, 1); 37 | } 38 | "#; 39 | 40 | static MODEL_TRANSFORM: [f32; 16] = [ 41 | 0.0, 0.0, -1.0, 0.0, 42 | 0.0, 1.0, 0.0, 0.0, 43 | 1.0, 0.0, 0.0, 0.0, 44 | 0.0, 0.0, 0.0, 1.0]; 45 | 46 | fn main() { 47 | // Load mesh file and normalize indices for OpenGL. 48 | let obj = Obj::from_file("examples/epps_head.obj").unwrap(); 49 | 50 | // Gather vertex data so that OpenGL can use them. 51 | let mut vertex_data = Vec::new(); 52 | 53 | // Iterate over each of the faces in the mesh. 54 | for (positions, normals) in obj.position_indices().iter().zip(obj.normal_indices().iter()) { 55 | // Iterate over each of the vertices in the face to combine the position and normal into 56 | // a single vertex. 57 | for (position_index, normal_index) in positions.iter().zip(normals.iter()) { 58 | let position = obj.positions()[*position_index]; 59 | let normal = obj.normals()[*normal_index]; 60 | 61 | vertex_data.extend(&[position.0, position.1, position.2, position.3]); 62 | vertex_data.extend(&[normal.0, normal.1, normal.2]); 63 | } 64 | } 65 | 66 | // Create indices list. 67 | let indices: Vec = (0..(obj.position_indices().len() * 3) as u32).collect(); 68 | 69 | // Create window and initialize OpenGL. 70 | let mut window = Window::new("gl-util - wireframe example").unwrap(); 71 | 72 | let context = Context::from_window(&window).unwrap(); 73 | 74 | // Compile and link shaders into a shader program. 75 | let vert_shader = Shader::new(&context, VERT_SOURCE, ShaderType::Vertex).unwrap(); 76 | let frag_shader = Shader::new(&context, FRAG_SOURCE, ShaderType::Fragment).unwrap(); 77 | let program = Program::new(&context, &[vert_shader, frag_shader]).unwrap(); 78 | 79 | let mut vertex_array = VertexArray::with_index_buffer(&context, &*vertex_data, &*indices); 80 | vertex_array.set_attrib( 81 | AttributeLocation::from_index(0), 82 | AttribLayout { elements: 4, stride: 7, offset: 0 }, 83 | ); 84 | vertex_array.set_attrib( 85 | AttributeLocation::from_index(1), 86 | AttribLayout { elements: 3, stride: 7, offset: 4 }, 87 | ); 88 | 89 | let mut draw_builder = DrawBuilder::new(&context, &vertex_array, DrawMode::Triangles); 90 | draw_builder 91 | .program(&program) 92 | .uniform("model_transform", GlMatrix { 93 | data: &MODEL_TRANSFORM, 94 | transpose: false, 95 | }) 96 | .uniform("surface_color", (1.0, 0.0, 0.0, 1.0)) 97 | .depth_test(Comparison::Less) 98 | .cull(Face::Back) 99 | .winding(WindingOrder::Clockwise); 100 | 101 | 'outer: loop { 102 | while let Some(message) = window.next_message() { 103 | match message { 104 | Message::Close => break 'outer, 105 | _ => {}, 106 | } 107 | } 108 | 109 | context.clear(); 110 | draw_builder.draw(); 111 | context.swap_buffers(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /lib/gl-util/examples/structured.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/randomPoison/gunship-rs/5a7acd32d96370990252b75d4065aa4ffb9bc379/lib/gl-util/examples/structured.bmp -------------------------------------------------------------------------------- /lib/gl-util/examples/texture.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate gl_util as gl; 3 | extern crate parse_bmp; 4 | 5 | use bootstrap::window::*; 6 | use gl::*; 7 | use gl::context::Context; 8 | use gl::shader::*; 9 | use gl::texture::*; 10 | use parse_bmp::{ 11 | Bitmap, 12 | BitmapData, 13 | }; 14 | 15 | static VERT_SOURCE: &'static str = r#" 16 | #version 330 core 17 | 18 | layout(location = 0) in vec4 position; 19 | layout(location = 1) in vec2 uv; 20 | 21 | out vec2 frag_uv; 22 | 23 | void main() { 24 | frag_uv = uv; 25 | gl_Position = position; 26 | } 27 | "#; 28 | 29 | static FRAG_SOURCE: &'static str = r#" 30 | #version 330 core 31 | 32 | uniform sampler2D sampler; 33 | 34 | in vec2 frag_uv; 35 | 36 | out vec4 fragment_color; 37 | 38 | void main() { 39 | fragment_color = texture(sampler, frag_uv) + vec4(frag_uv, 0.0, 1.0) * vec4(0.01, 0.01, 0.01, 1.0); 40 | } 41 | "#; 42 | 43 | static VERTEX_DATA: &'static [f32] = &[ 44 | -1.0, 3.0, 0.0, // Position 45 | 0.0, 2.0, // Uv 46 | 47 | 3.0, -1.0, 0.0, // Position 48 | 2.0, 0.0, // Uv 49 | 50 | -1.0, -1.0, 0.0, // Position 51 | 0.0, 0.0, // Uv 52 | ]; 53 | 54 | static TEXTURE_DATA: &'static [u8] = include_bytes!("./structured.bmp"); 55 | 56 | fn main() { 57 | // Create window and initialize OpenGL. 58 | let mut window = Window::new("gl-util - texture example").unwrap(); 59 | let context = Context::from_window(&window).unwrap(); 60 | 61 | // Compile and link shaders into a shader program. 62 | let vert_shader = Shader::new(&context, VERT_SOURCE, ShaderType::Vertex).unwrap(); 63 | let frag_shader = Shader::new(&context, FRAG_SOURCE, ShaderType::Fragment).unwrap(); 64 | let program = Program::new(&context, &[vert_shader, frag_shader]).unwrap(); 65 | 66 | let mut vertex_array = VertexArray::new(&context, &VERTEX_DATA[..]); 67 | vertex_array.set_attrib( 68 | AttributeLocation::from_index(0), 69 | AttribLayout { elements: 3, offset: 0, stride: 5 }, 70 | ); 71 | vertex_array.set_attrib( 72 | AttributeLocation::from_index(1), 73 | AttribLayout { elements: 2, offset: 3, stride: 5 }, 74 | ); 75 | 76 | // Parse the bitmap and setup the texture. 77 | let bitmap = Bitmap::from_bytes(TEXTURE_DATA).unwrap(); 78 | let data = match bitmap.data() { 79 | &BitmapData::Bgr(ref data) => &**data, 80 | _ => panic!("Nuh uh, that file is a bitmap"), 81 | }; 82 | 83 | let texture = Texture2d::new( 84 | &context, 85 | TextureFormat::Bgr, 86 | TextureInternalFormat::Rgb, 87 | bitmap.width(), 88 | bitmap.height(), 89 | data) 90 | .unwrap(); 91 | 92 | let mut draw_builder = DrawBuilder::new(&context, &vertex_array, DrawMode::Triangles); 93 | draw_builder 94 | .program(&program) 95 | .uniform("sampler", &texture) 96 | .winding(WindingOrder::Clockwise); 97 | 98 | 'outer: loop { 99 | while let Some(message) = window.next_message() { 100 | match message { 101 | Message::Close => break 'outer, 102 | _ => {}, 103 | } 104 | } 105 | 106 | context.clear(); 107 | draw_builder.draw(); 108 | context.swap_buffers(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /lib/gl-util/examples/wireframe.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate gl_util as gl; 3 | extern crate parse_obj; 4 | 5 | use bootstrap::window::*; 6 | use gl::*; 7 | use gl::context::Context; 8 | use parse_obj::Obj; 9 | 10 | fn main() { 11 | // Load mesh file and normalize indices for OpenGL. 12 | let obj = Obj::from_file("examples/epps_head.obj").unwrap(); 13 | let mut raw_indices = Vec::new(); 14 | for face in obj.position_indices() { 15 | for index in face { 16 | raw_indices.push(*index as u32); 17 | } 18 | } 19 | 20 | let mut window = Window::new("gl-util - wireframe example").unwrap(); 21 | let context = Context::from_window(&window).unwrap(); 22 | 23 | let mut vertex_array = VertexArray::with_index_buffer(&context, obj.raw_positions(), &*raw_indices); 24 | vertex_array.set_attrib( 25 | AttributeLocation::from_index(0), 26 | AttribLayout { elements: 4, offset: 0, stride: 0 }, 27 | ); 28 | 29 | 'outer: loop { 30 | while let Some(message) = window.next_message() { 31 | match message { 32 | Message::Close => break 'outer, 33 | _ => {}, 34 | } 35 | } 36 | 37 | context.clear(); 38 | DrawBuilder::new(&context, &vertex_array, DrawMode::Triangles) 39 | .polygon_mode(PolygonMode::Line) 40 | .draw(); 41 | context.swap_buffers(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/gl-util/src/texture.rs: -------------------------------------------------------------------------------- 1 | use context::Context; 2 | use gl; 3 | 4 | pub use gl::{ 5 | TextureObject, TextureFilterFunction, TextureFormat, TextureBindTarget, Texture2dTarget, 6 | TextureInternalFormat, TextureDataType, TextureParameterName, TextureParameterTarget}; 7 | 8 | #[derive(Debug)] 9 | pub struct Texture2d { 10 | texture_object: TextureObject, 11 | 12 | context: ::gl::Context, 13 | } 14 | 15 | impl Texture2d { 16 | /// Constructs a new `Texture2d` from the specified data. 17 | /// 18 | /// # Panics 19 | /// 20 | /// - If `width * height != data.len()`. 21 | pub fn new( 22 | context: &Context, 23 | data_format: TextureFormat, 24 | internal_format: TextureInternalFormat, 25 | width: usize, 26 | height: usize, 27 | data: &[T], 28 | ) -> Result { 29 | let context = context.raw(); 30 | let _guard = ::context::ContextGuard::new(context); 31 | 32 | let expected_pixels = width * height * data_format.elements() / T::ELEMENTS; 33 | assert!( 34 | expected_pixels == data.len(), 35 | "Wrong number of pixels in texture, width: {}, height: {}, expected pixels: {}, actual pixels: {}", 36 | width, 37 | height, 38 | expected_pixels, 39 | data.len()); 40 | 41 | let mut texture_object = TextureObject::null(); 42 | unsafe { gl::gen_textures(1, &mut texture_object); } 43 | 44 | // Check if the texture object was successfully created. 45 | if texture_object.is_null() { 46 | return Err(Error::FailedToGenerateTexture); 47 | } 48 | 49 | unsafe { 50 | gl::bind_texture(TextureBindTarget::Texture2d, texture_object); 51 | gl::texture_image_2d( 52 | Texture2dTarget::Texture2d, 53 | 0, 54 | internal_format, 55 | width as i32, 56 | height as i32, 57 | 0, 58 | data_format, 59 | T::DATA_TYPE, 60 | data.as_ptr() as *const ()); 61 | 62 | gl::texture_parameter_i32( 63 | TextureParameterTarget::Texture2d, 64 | TextureParameterName::MinFilter, 65 | TextureFilterFunction::Nearest.into()); 66 | gl::texture_parameter_i32( 67 | TextureParameterTarget::Texture2d, 68 | TextureParameterName::MagFilter, 69 | TextureFilterFunction::Nearest.into()); 70 | gl::bind_texture(TextureBindTarget::Texture2d, TextureObject::null()); 71 | } 72 | 73 | Ok(Texture2d { 74 | texture_object: texture_object, 75 | 76 | context: context, 77 | }) 78 | } 79 | 80 | pub fn empty(context: &Context) -> Texture2d { 81 | Texture2d { 82 | texture_object: TextureObject::null(), 83 | 84 | context: context.raw(), 85 | } 86 | } 87 | 88 | /// Returns the OpenGL primitive managed by this object. 89 | pub(crate) fn inner(&self) -> TextureObject { 90 | self.texture_object 91 | } 92 | } 93 | 94 | impl Drop for Texture2d { 95 | fn drop(&mut self) { 96 | let _guard = ::context::ContextGuard::new(self.context); 97 | unsafe { gl::delete_textures(1, &mut self.inner()); } 98 | } 99 | } 100 | 101 | pub trait TextureData { 102 | const DATA_TYPE: TextureDataType; 103 | const ELEMENTS: usize; 104 | } 105 | 106 | impl TextureData for f32 { 107 | const DATA_TYPE: TextureDataType = TextureDataType::f32; 108 | const ELEMENTS: usize = 1; 109 | } 110 | 111 | impl TextureData for u8 { 112 | const DATA_TYPE: TextureDataType = TextureDataType::u8; 113 | const ELEMENTS: usize = 1; 114 | } 115 | 116 | impl TextureData for (u8, u8, u8) { 117 | const DATA_TYPE: TextureDataType = TextureDataType::u8; 118 | const ELEMENTS: usize = 3; 119 | } 120 | 121 | impl TextureData for (u8, u8, u8, u8) { 122 | const DATA_TYPE: TextureDataType = TextureDataType::u8; 123 | const ELEMENTS: usize = 4; 124 | } 125 | 126 | #[derive(Debug)] 127 | pub enum Error { 128 | FailedToGenerateTexture, 129 | } 130 | 131 | pub unsafe fn set_active_texture(index: u32) { 132 | const TEXTURE_ID_BASE: u32 = 0x84C0; 133 | 134 | // TODO: Check that texture index is supported. 135 | 136 | let texture_id = TEXTURE_ID_BASE + index; 137 | gl::active_texture(texture_id); 138 | } 139 | -------------------------------------------------------------------------------- /lib/gl-util/src/windows/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate user32; 2 | extern crate winapi; 3 | 4 | use self::winapi::*; 5 | 6 | use gl; 7 | 8 | pub fn find_device_context() -> Option { 9 | let hwnd = unsafe { user32::GetActiveWindow() }; 10 | 11 | if hwnd.is_null() { 12 | return None; 13 | } 14 | 15 | device_context_from_window_handle(hwnd) 16 | } 17 | 18 | pub fn device_context_from_window_handle(window_handle: HWND) -> Option { 19 | let device_context = unsafe { user32::GetDC(window_handle) }; 20 | if device_context.is_null() { 21 | None 22 | } else { 23 | Some(device_context) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/hash/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "hash" 4 | version = "0.0.1" 5 | authors = [] 6 | -------------------------------------------------------------------------------- /lib/hash/src/fnv.rs: -------------------------------------------------------------------------------- 1 | use std::hash::{BuildHasher, Hasher}; 2 | 3 | ///! This was taken directly from https://github.com/servo/rust-fnv. I needed to make the hasher 4 | ///! clonable, and this was faster than actually cloning the repo and making PR. At some point I 5 | ///! should probably request the change be added to the main repo. 6 | 7 | #[derive(Debug, Clone, Copy, Default)] 8 | pub struct FnvHashState; 9 | 10 | impl BuildHasher for FnvHashState { 11 | type Hasher = FnvHasher; 12 | 13 | fn build_hasher(&self) -> FnvHasher { 14 | FnvHasher::default() 15 | } 16 | } 17 | 18 | #[derive(Debug, Copy, Clone)] 19 | pub struct FnvHasher(u64); 20 | 21 | impl Default for FnvHasher { 22 | #[inline] 23 | fn default() -> FnvHasher { FnvHasher(0xcbf29ce484222325) } 24 | } 25 | 26 | impl Hasher for FnvHasher { 27 | #[inline] 28 | fn finish(&self) -> u64 { self.0 } 29 | 30 | #[inline] 31 | fn write(&mut self, bytes: &[u8]) { 32 | let FnvHasher(mut hash) = *self; 33 | for byte in bytes.iter() { 34 | hash = hash ^ (*byte as u64); 35 | hash = hash.wrapping_mul(0x100000001b3); 36 | } 37 | *self = FnvHasher(hash); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/hash/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod fnv; 2 | 3 | pub use fnv::{FnvHashState, FnvHasher}; 4 | -------------------------------------------------------------------------------- /lib/loader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "loader" 3 | version = "0.0.0" 4 | authors = ["David LeGare "] 5 | 6 | [lib] 7 | name = "loader" 8 | 9 | [dependencies.bootstrap_rs] 10 | version = "*" 11 | path = "../bootstrap_rs" 12 | 13 | [dependencies] 14 | winapi = "*" 15 | kernel32-sys = "*" 16 | -------------------------------------------------------------------------------- /lib/parse-bmp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parse-bmp" 3 | version = "0.1.0" 4 | authors = ["David LeGare "] 5 | -------------------------------------------------------------------------------- /lib/parse-obj/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parse-obj" 3 | version = "0.1.0" 4 | authors = ["David LeGare "] 5 | -------------------------------------------------------------------------------- /lib/parse-obj/examples/from_file.rs: -------------------------------------------------------------------------------- 1 | extern crate parse_obj; 2 | 3 | use parse_obj::*; 4 | 5 | fn main() { 6 | let obj = Obj::from_file("examples/epps_head.obj"); 7 | println!("{:?}", obj); 8 | } 9 | -------------------------------------------------------------------------------- /lib/parse-obj/examples/triangle.rs: -------------------------------------------------------------------------------- 1 | extern crate parse_obj; 2 | 3 | use parse_obj::*; 4 | 5 | static TRIANGLE_OBJ: &'static str = r#" 6 | v -1.0 -1.0 0.0 7 | v 1.0 -1.0 0.0 8 | v 0.0 1.0 0.0 9 | 10 | f 1// 2// 3// 11 | "#; 12 | 13 | fn main() { 14 | let obj = Obj::from_str(TRIANGLE_OBJ); 15 | println!("{:?}", obj); 16 | } 17 | -------------------------------------------------------------------------------- /lib/parse-obj/tests/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate parse_obj; 2 | 3 | use parse_obj::*; 4 | 5 | static TRIANGLE_OBJ: &'static str = r#" 6 | v -1.0 -1.0 0.0 7 | v 1.0 -1.0 0.0 8 | v 0.0 1.0 0.0 9 | 10 | f 1// 2// 3// 11 | "#; 12 | 13 | static TRIANGLE_WITH_NORM: &'static str = r#" 14 | v -1.0 -1.0 0.0 15 | v 1.0 -1.0 0.0 16 | v 0.0 1.0 0.0 17 | 18 | vt 1.0 1.0 1.0 19 | 20 | vn 1.0 0.0 0.0 21 | 22 | f 1/1/1 2/1/1 3/1/1 23 | "#; 24 | 25 | #[test] 26 | fn test_iterator() { 27 | { 28 | let obj = Obj::from_str(TRIANGLE_OBJ).unwrap(); 29 | 30 | let mut face_iter = obj.faces(); 31 | let mut face = face_iter.next().unwrap(); 32 | assert!(face_iter.next().is_none()); 33 | 34 | assert_eq!(Some(((-1.0, -1.0, 0.0, 1.0), None, None)), face.next()); 35 | assert_eq!(Some(((1.0, -1.0, 0.0, 1.0), None, None)), face.next()); 36 | assert_eq!(Some(((0.0, 1.0, 0.0, 1.0), None, None)), face.next()); 37 | } 38 | 39 | { 40 | let obj = Obj::from_str(TRIANGLE_WITH_NORM).unwrap(); 41 | 42 | let mut face_iter = obj.faces(); 43 | let mut face = face_iter.next().unwrap(); 44 | assert!(face_iter.next().is_none()); 45 | 46 | assert_eq!(Some(((-1.0, -1.0, 0.0, 1.0), Some((1.0, 1.0, 1.0)), Some((1.0, 0.0, 0.0)))), face.next()); 47 | assert_eq!(Some(((1.0, -1.0, 0.0, 1.0), Some((1.0, 1.0, 1.0)), Some((1.0, 0.0, 0.0)))), face.next()); 48 | assert_eq!(Some(((0.0, 1.0, 0.0, 1.0), Some((1.0, 1.0, 1.0)), Some((1.0, 0.0, 0.0)))), face.next()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/polygon-material/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "polygon_material" 4 | version = "0.1.0" 5 | authors = ["David LeGare "] 6 | 7 | [dependencies] 8 | polygon_math = { version = "0.1", path = "../polygon_math" } 9 | -------------------------------------------------------------------------------- /lib/polygon-material/src/lexer.rs: -------------------------------------------------------------------------------- 1 | use std::iter::Peekable; 2 | use std::str::*; 3 | use super::token::*; 4 | 5 | #[derive(Debug)] 6 | pub struct Lexer<'a> { 7 | source: &'a str, 8 | chars: Peekable>, 9 | is_done: bool, 10 | } 11 | 12 | pub type Result = ::std::result::Result; 13 | 14 | impl<'a> Lexer<'a> { 15 | pub fn new(source: &str) -> Lexer { 16 | Lexer { 17 | source: source, 18 | chars: source.char_indices().peekable(), 19 | is_done: false, 20 | } 21 | } 22 | 23 | pub fn next(&mut self) -> Result<(Token, Span)> { 24 | // Start by eating all whitespace before the next valid token. 25 | let (start_index, character) = { 26 | let mut start = None; 27 | while let Some((index, character)) = self.chars.next() { 28 | if !character.is_whitespace() { 29 | // This is the first character of the current token. Mark the starting index and 30 | // break the loop so we can begin reading the token. 31 | start = Some((index, character)); 32 | break; 33 | } 34 | } 35 | 36 | match start { 37 | Some(index_char) => index_char, 38 | None => { 39 | self.is_done = true; 40 | 41 | // Never found a non-whitespace character, so we're at the end of the file. 42 | let span = Span::new(self.source.len(), self.source.len()); 43 | return Ok((Token::EndOfFile, span)) 44 | }, 45 | } 46 | }; 47 | 48 | // See if token is an identifier. 49 | if character.is_ident() { 50 | return self.parse_ident(start_index) 51 | } 52 | 53 | // See if token is a number literal. 54 | if character.is_numeric_part() { 55 | return unimplemented!(); 56 | } 57 | 58 | // See if character is string literal. 59 | if character == '"' { 60 | return unimplemented!(); 61 | } 62 | 63 | // See if token is program literal. 64 | if character == '{' { 65 | return self.parse_program_literal(start_index); 66 | } 67 | 68 | // Single-character symbols. 69 | let token = match character { 70 | ';' => Token::SemiColon, 71 | '=' => Token::Eq, 72 | ':' => Token::Colon, 73 | 74 | _ => { 75 | self.is_done = true; 76 | 77 | return Err(Error { 78 | span: Span::new(start_index, start_index + 1), 79 | data: ErrorData::IllegalSymbol(character), 80 | }); 81 | }, 82 | }; 83 | 84 | Ok((token, Span::new(start_index, start_index + 1))) 85 | } 86 | 87 | /// Checks if the lexer is done. 88 | /// 89 | /// Returns `true` if the lexer reached the end of the file or found a token error. 90 | pub fn is_done(&self) -> bool { 91 | self.is_done 92 | } 93 | 94 | fn parse_ident(&mut self, start_index: usize) -> Result<(Token, Span)> { 95 | while let Some(&(end_index, character)) = self.chars.peek() { 96 | if !character.is_ident() { 97 | let word = &self.source[start_index .. end_index]; 98 | let span = Span::new(start_index, end_index); 99 | 100 | let token = match word { 101 | "property" => Token::Property, 102 | "program" => Token::Program, 103 | _ => Token::Identifier, 104 | }; 105 | 106 | return Ok((token, span)) 107 | } 108 | 109 | // Consume the item we peeked at. 110 | self.chars.next(); 111 | } 112 | 113 | Ok((Token::Identifier, Span::new(start_index, self.source.len()))) 114 | } 115 | 116 | fn parse_program_literal(&mut self, start_index: usize) -> Result<(Token, Span)> { 117 | // Start at depth 1 because we've already removed the opening '{'. 118 | let mut depth = 1; 119 | 120 | // Walk through the source string 121 | while let Some((end_index, character)) = self.chars.next() { 122 | match character { 123 | '{' => depth += 1, 124 | '}' => { 125 | depth -= 1; 126 | if depth == 0 { 127 | // We're at the end. 128 | return Ok((Token::ProgramLiteral, Span::new(start_index + 1, end_index))); 129 | } 130 | }, 131 | _ => {} 132 | } 133 | } 134 | 135 | // Uh-oh, we got to the end and never closed the braces. 136 | self.is_done = true; 137 | Err(Error { 138 | span: Span::new(start_index, self.source.len()), 139 | data: ErrorData::UnclosedProgramLiteral, 140 | }) 141 | } 142 | } 143 | 144 | /// Represents a lex error. 145 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 146 | pub struct Error { 147 | pub span: Span, 148 | pub data: ErrorData, 149 | } 150 | 151 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 152 | pub enum ErrorData { 153 | IllegalSymbol(char), 154 | UnclosedProgramLiteral, 155 | } 156 | 157 | trait CharacterParseExt { 158 | fn is_ident_start(self) -> bool; 159 | fn is_ident(self) -> bool; 160 | fn is_numeric_part(self) -> bool; 161 | } 162 | 163 | impl CharacterParseExt for char { 164 | fn is_ident_start(self) -> bool { 165 | self.is_alphabetic() || self == '_' 166 | } 167 | 168 | fn is_ident(self) -> bool { 169 | self.is_alphanumeric() || self == '_' 170 | } 171 | 172 | fn is_numeric_part(self) -> bool { 173 | self.is_numeric() || self == '-' 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /lib/polygon-material/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(const_fn)] 2 | 3 | extern crate polygon_math as math; 4 | 5 | pub mod lexer; 6 | pub mod material_source; 7 | pub mod parser; 8 | pub mod token; 9 | -------------------------------------------------------------------------------- /lib/polygon-material/src/material_source.rs: -------------------------------------------------------------------------------- 1 | use parser::{Parser, Error as ParseError}; 2 | use std::fs::File; 3 | use std::io::Error as IoError; 4 | use std::io::prelude::*; 5 | use std::path::Path; 6 | 7 | /// Represents the contents of a material file that has been loaded into memory but has not been 8 | /// sent to the renderer. 9 | #[derive(Debug, PartialEq, Eq)] 10 | pub struct MaterialSource { 11 | pub properties: Vec, 12 | pub programs: Vec, 13 | } 14 | 15 | impl MaterialSource { 16 | pub fn from_file>(path: P) -> Result { 17 | let mut file = File::open(&path)?; 18 | let mut contents = String::new(); 19 | file.read_to_string(&mut contents)?; 20 | 21 | MaterialSource::from_str(&*contents) 22 | } 23 | 24 | pub fn from_str>(source: T) -> Result { 25 | let mut parser = Parser::new(source.as_ref()); 26 | parser.parse().map_err(|error| error.into()) 27 | } 28 | } 29 | 30 | /// Represents a program item parsed from a material file. 31 | /// 32 | /// TODO: Document the different variants. 33 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 34 | pub enum ProgramSource { 35 | Vertex(String), 36 | Fragment(String), 37 | } 38 | 39 | impl ProgramSource { 40 | /// Checks if the program source is a vertex shader. 41 | pub fn is_vertex(&self) -> bool { 42 | match *self { 43 | ProgramSource::Vertex(_) => true, 44 | _ => false, 45 | } 46 | } 47 | 48 | /// Checks if the programs source is fragment shader. 49 | pub fn is_fragment(&self) -> bool { 50 | match *self { 51 | ProgramSource::Fragment(_) => true, 52 | _ => false, 53 | } 54 | } 55 | 56 | pub fn source(&self) -> &str { 57 | match *self { 58 | ProgramSource::Vertex(ref source) => &*source, 59 | ProgramSource::Fragment(ref source) => &*source, 60 | } 61 | } 62 | } 63 | 64 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 65 | pub struct PropertySource { 66 | pub name: String, 67 | pub property_type: PropertyType, 68 | } 69 | 70 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 71 | #[allow(bad_style)] 72 | pub enum PropertyType { 73 | Color, 74 | Texture2d, 75 | f32, 76 | Vector3, 77 | } 78 | 79 | /// Represents an error in parsing a material source file. 80 | #[derive(Debug)] 81 | pub enum Error { 82 | IoError(IoError), 83 | ParseError(ParseError), 84 | } 85 | 86 | impl PartialEq for Error { 87 | fn eq(&self, other: &Error) -> bool { 88 | match *self { 89 | Error::IoError(_) => false, 90 | Error::ParseError(parse_error) => match *other { 91 | Error::IoError(_) => false, 92 | Error::ParseError(other_parse_error) => parse_error == other_parse_error 93 | } 94 | } 95 | } 96 | } 97 | 98 | impl From for Error { 99 | fn from(error: ParseError) -> Error { 100 | Error::ParseError(error) 101 | } 102 | } 103 | 104 | impl From for Error { 105 | fn from(error: IoError) -> Error { 106 | Error::IoError(error) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/polygon-material/src/parser.rs: -------------------------------------------------------------------------------- 1 | use lexer::{Lexer, Error as TokenError}; 2 | use material_source::{MaterialSource, ProgramSource, PropertySource, PropertyType}; 3 | use token::*; 4 | 5 | #[derive(Debug)] 6 | pub struct Parser<'a> { 7 | source: &'a str, 8 | lexer: Lexer<'a>, 9 | } 10 | 11 | impl<'a> Parser<'a> { 12 | pub fn new(source: &str) -> Parser { 13 | Parser { 14 | source: source, 15 | lexer: Lexer::new(source), 16 | } 17 | } 18 | 19 | pub fn parse(&mut self) -> Result { 20 | let mut properties = Vec::new(); 21 | let mut programs = Vec::new(); 22 | 23 | loop { 24 | let (token, span) = self.lexer.next()?; 25 | match token { 26 | Token::Program => programs.push(self.parse_program(span)?), 27 | Token::Property => properties.push(self.parse_property(span)?), 28 | 29 | Token::EndOfFile => break, 30 | 31 | _ => return Err(Error::ExpectedItem(span)), 32 | } 33 | } 34 | 35 | Ok(MaterialSource { 36 | properties: properties, 37 | programs: programs, 38 | }) 39 | } 40 | 41 | /// Parses a property item. 42 | /// 43 | /// # Preconditions 44 | /// 45 | /// - The "property" keyword was already pulled from the lexer. 46 | fn parse_property(&mut self, _start_span: Span) -> Result { 47 | let (token, span) = self.lexer.next()?; 48 | let ident = match token { 49 | Token::Identifier => self.source[span].into(), 50 | _ => return Err(Error::ExpectedIdent(span)), 51 | }; 52 | 53 | let (token, span) = self.lexer.next()?; 54 | match token { 55 | Token::Colon => {}, 56 | _ => return Err(Error::ExpectedColon(span)), 57 | } 58 | 59 | let (token, span) = self.lexer.next()?; 60 | let property_type = match token { 61 | Token::Identifier => match &self.source[span] { 62 | "Color" => PropertyType::Color, 63 | "Texture2d" => PropertyType::Texture2d, 64 | "f32" => PropertyType::f32, 65 | "Vector3" => PropertyType::Vector3, 66 | _ => return Err(Error::BadPropertyType(span)), 67 | }, 68 | _ => return Err(Error::ExpectedIdent(span)), 69 | }; 70 | 71 | let (token, span) = self.lexer.next()?; 72 | match token { 73 | Token::SemiColon => {}, 74 | _ => return Err(Error::ExpectedSemiColon(span)), 75 | } 76 | 77 | Ok(PropertySource { 78 | name: ident, 79 | property_type: property_type, 80 | }) 81 | } 82 | 83 | /// Parses a program item. 84 | /// 85 | /// # Preconditions 86 | /// 87 | /// - The "program" keyword was already pulled from the lexer. 88 | fn parse_program(&mut self, _start_span: Span) -> Result { 89 | let (first_token, first_span) = self.lexer.next()?; 90 | if first_token != Token::Identifier { 91 | return Err(Error::ExpectedIdent(first_span)); 92 | } 93 | 94 | let (second_token, second_span) = self.lexer.next()?; 95 | if second_token != Token::ProgramLiteral { 96 | return Err(Error::ExpectedProgramLiteral(second_span)) 97 | } 98 | 99 | let program_literal = &self.source[second_span]; 100 | let program_source = match &self.source[first_span] { 101 | "vert" => ProgramSource::Vertex(program_literal.into()), 102 | "frag" => ProgramSource::Fragment(program_literal.into()), 103 | _ => return Err(Error::BadProgramType(second_span)), 104 | }; 105 | 106 | Ok(program_source) 107 | } 108 | } 109 | 110 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 111 | pub enum Error { 112 | TokenError(TokenError), 113 | ExpectedItem(Span), 114 | ExpectedIdent(Span), 115 | ExpectedColon(Span), 116 | ExpectedProgramLiteral(Span), 117 | ExpectedSemiColon(Span), 118 | BadPropertyType(Span), 119 | BadProgramType(Span), 120 | } 121 | 122 | impl From for Error { 123 | fn from(from: TokenError) -> Error { 124 | Error::TokenError(from) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/polygon-material/src/token.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Index; 2 | 3 | /// Represents a token in a material source file. 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 5 | pub enum Token { 6 | /* Keywords */ 7 | Program, 8 | Property, 9 | 10 | /* Operator symbols */ 11 | Eq, 12 | 13 | /* Structural symbols */ 14 | Colon, 15 | SemiColon, 16 | OpenCurly, 17 | CloseCurly, 18 | 19 | /* Literal */ 20 | ProgramLiteral, 21 | 22 | /* Name components */ 23 | Identifier, 24 | 25 | /* Other */ 26 | EndOfFile, 27 | } 28 | 29 | /// Represents a span covering a chunk of source material. 30 | /// 31 | /// Used to reconstruct line numbers for errors. The indices are the byte indices in the source 32 | /// document. 33 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 34 | pub struct Span { 35 | pub begin: usize, 36 | pub end: usize, 37 | } 38 | 39 | impl Span { 40 | pub const fn new(begin: usize, end: usize) -> Span { 41 | Span { 42 | begin: begin, 43 | end: end, 44 | } 45 | } 46 | } 47 | 48 | impl Index for str { 49 | type Output = str; 50 | 51 | fn index(&self, index: Span) -> &str { 52 | &self[index.begin..index.end] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/polygon_math/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "polygon_math" 3 | version = "0.1.0" 4 | authors = ["David LeGare "] 5 | -------------------------------------------------------------------------------- /lib/polygon_math/src/color.rs: -------------------------------------------------------------------------------- 1 | use std::slice; 2 | 3 | pub const RED: Color = Color { r: 1.0, b: 0.0, g: 0.0, a: 1.0 }; 4 | pub const WHITE: Color = Color { r: 1.0, b: 1.0, g: 1.0, a: 1.0 }; 5 | pub const BLUE: Color = Color { r: 0.0, b: 1.0, g: 0.0, a: 1.0 }; 6 | 7 | /// A struct representing a color. 8 | /// 9 | /// Colors have a red, green, blue, and alpha component. If alpha is not needed used 10 | /// `Color::rgb()` and the alpha component will default to `1.0`, effectively behaving as if there 11 | /// were no alpha. Color components are represented in linear color space. If non-linear color 12 | /// computations are needed use one of the other color types. 13 | /// 14 | /// TODO: Add other color types -___-; 15 | #[derive(Debug, Clone, Copy, PartialEq)] 16 | #[repr(C)] 17 | pub struct Color { 18 | pub r: f32, 19 | pub g: f32, 20 | pub b: f32, 21 | pub a: f32, 22 | } 23 | 24 | impl Color { 25 | /// Constructs a new `Color` from a red, green, blue, and alpha component. 26 | /// 27 | /// If alpha isn't needed use `Color::rbg()` to construct a `Color` object with the default 28 | /// alpha of `1.0`. 29 | pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color { 30 | Color { 31 | r: r, 32 | g: g, 33 | b: b, 34 | a: a, 35 | } 36 | } 37 | 38 | /// Constructs a new `Color` from a red, green, and blue component. 39 | /// 40 | /// If alpha is needed use `Color::new()` to specify an alpha value. 41 | pub fn rgb(r: f32, g: f32, b: f32) -> Color { 42 | Color { 43 | r: r, 44 | g: g, 45 | b: b, 46 | a: 1.0, 47 | } 48 | } 49 | 50 | pub fn as_slice_of_arrays(colors: &[Color]) -> &[[f32; 4]] { 51 | let ptr = colors.as_ptr() as *const _; 52 | unsafe { slice::from_raw_parts(ptr, colors.len()) } 53 | } 54 | } 55 | 56 | impl Default for Color { 57 | fn default() -> Color { 58 | Color { 59 | r: 0.0, 60 | g: 0.0, 61 | b: 0.0, 62 | a: 1.0, 63 | } 64 | } 65 | } 66 | 67 | impl From<[f32; 3]> for Color { 68 | fn from(from: [f32; 3]) -> Color { 69 | let [r, g, b] = from; 70 | Color { 71 | r: r, 72 | g: g, 73 | b: b, 74 | a: 1.0, 75 | } 76 | } 77 | } 78 | 79 | impl From<[f32; 4]> for Color { 80 | fn from(from: [f32; 4]) -> Color { 81 | let [r, g, b, a] = from; 82 | Color { 83 | r: r, 84 | g: g, 85 | b: b, 86 | a: a, 87 | } 88 | } 89 | } 90 | 91 | impl From<(f32, f32, f32)> for Color { 92 | fn from(from: (f32, f32, f32)) -> Color { 93 | let (r, g, b) = from; 94 | Color { 95 | r: r, 96 | g: g, 97 | b: b, 98 | a: 1.0, 99 | } 100 | } 101 | } 102 | 103 | impl From<(f32, f32, f32, f32)> for Color { 104 | fn from(from: (f32, f32, f32, f32)) -> Color { 105 | let (r, g, b, a) = from; 106 | Color { 107 | r: r, 108 | g: g, 109 | b: b, 110 | a: a, 111 | } 112 | } 113 | } 114 | 115 | impl From for [f32; 4] { 116 | fn from(from: Color) -> [f32; 4] { 117 | let Color { r, g, b, a } = from; 118 | [r, g, b, a] 119 | } 120 | } 121 | 122 | impl<'a> From<&'a Color> for [f32; 4] { 123 | fn from(from: &Color) -> [f32; 4] { 124 | let &Color { r, g, b, a } = from; 125 | [r, g, b, a] 126 | } 127 | } 128 | 129 | impl From for (f32, f32, f32, f32) { 130 | fn from(from: Color) -> (f32, f32, f32, f32) { 131 | let Color { r, g, b, a } = from; 132 | (r, g, b, a) 133 | } 134 | } 135 | 136 | impl<'a> From<&'a Color> for (f32, f32, f32, f32) { 137 | fn from(from: &Color) -> (f32, f32, f32, f32) { 138 | let &Color { r, g, b, a } = from; 139 | (r, g, b, a) 140 | } 141 | } 142 | 143 | impl AsRef<[f32]> for Color { 144 | fn as_ref(&self) -> &[f32] { 145 | let ptr = self as *const Color as *const f32; 146 | unsafe { slice::from_raw_parts(ptr, 4) } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /lib/polygon_math/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(const_fn)] 2 | #![feature(slice_patterns)] 3 | #![cfg_attr(test, feature(test))] 4 | 5 | pub mod color; 6 | pub mod matrix; 7 | pub mod orientation; 8 | pub mod point; 9 | pub mod quaternion; 10 | pub mod vector; 11 | 12 | #[cfg(test)] 13 | mod test; 14 | 15 | pub use color::Color; 16 | pub use matrix::{Matrix3, Matrix4}; 17 | pub use orientation::Orientation; 18 | pub use point::Point; 19 | pub use std::f32::consts::PI; 20 | pub use vector::{Vector2, Vector3}; 21 | 22 | use std::ops::{Rem, Add}; 23 | 24 | pub const EPSILON: f32 = 1e-6; 25 | pub const TAU: f32 = 2.0 * PI; 26 | 27 | pub trait IsZero { 28 | fn is_zero(self) -> bool; 29 | } 30 | 31 | impl IsZero for f32 { 32 | fn is_zero(self) -> bool { 33 | self.abs() < EPSILON 34 | } 35 | } 36 | 37 | pub trait Clamp { 38 | fn clamp(self, min: Self, max: Self) -> Self; 39 | } 40 | 41 | impl Clamp for f32 { 42 | fn clamp(self, min: f32, max: f32) -> f32 { 43 | f32::min(f32::max(self, min), max) 44 | } 45 | } 46 | 47 | pub trait Dot { 48 | type Output; 49 | 50 | fn dot(self, rhs: Other) -> Self::Output; 51 | } 52 | 53 | impl Dot for [f32; 3] { 54 | type Output = f32; 55 | 56 | fn dot(self, rhs: [f32; 3]) -> f32 { 57 | self[0] * rhs[0] 58 | + self[1] * rhs[1] 59 | + self[2] * rhs[2] 60 | } 61 | } 62 | 63 | // Doesn't cause ICE. 64 | impl<'a, T> Dot<&'a T> for T where T: Dot + Copy { 65 | type Output = T::Output; 66 | 67 | fn dot(self, rhs: &T) -> Self::Output { 68 | self.dot(*rhs) 69 | } 70 | } 71 | 72 | // // Causes ICE. 73 | // impl<'a, T, U> Dot<&'a U> for T where T: Dot, U: Copy { 74 | // type Output = T::Output; 75 | // 76 | // fn dot(self, rhs: &U) -> Self::Output { 77 | // self.dot(*rhs) 78 | // } 79 | // } 80 | 81 | pub trait Lerp { 82 | fn lerp(t: f32, from: Self, to: Self) -> Self; 83 | } 84 | 85 | impl Lerp for f32 { 86 | fn lerp(t: f32, from: f32, to: f32) -> f32 { 87 | from + (to - from) * t 88 | } 89 | } 90 | 91 | pub trait Modulo { 92 | type Output; 93 | 94 | fn modulo(self, divisor: Rhs) -> Self::Output; 95 | } 96 | 97 | impl Modulo for T 98 | where 99 | T: Copy, 100 | T: Rem, 101 | T: Add, 102 | { 103 | type Output = T; 104 | 105 | fn modulo(self, divisor: T) -> T { 106 | ((self % divisor) + divisor) % divisor 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/polygon_math/src/test/matrix_test.rs: -------------------------------------------------------------------------------- 1 | use matrix::Matrix4; 2 | use super::test::{Bencher, black_box}; 3 | 4 | #[test] 5 | fn matrix_equality() 6 | { 7 | let identity_1 = Matrix4::identity(); 8 | let mut identity_2 = Matrix4::identity(); 9 | 10 | assert!(identity_1 == identity_1); // self equality 11 | assert!(identity_1 == identity_2); // two identity matrices 12 | 13 | identity_2[0][0] = 5.0; 14 | assert!(identity_1 != identity_2); 15 | } 16 | 17 | #[test] 18 | #[should_panic(expected = "assertion failed")] 19 | fn matrix_index_bounds() { 20 | let matrix = Matrix4::identity(); 21 | matrix[4][4]; 22 | } 23 | 24 | #[test] 25 | #[should_panic(expected = "assertion failed")] 26 | fn matrix_mut_index_bounds() { 27 | let mut _matrix = Matrix4::identity(); 28 | _matrix[4][4]; 29 | } 30 | 31 | #[test] 32 | fn matrix_identity() { 33 | let identity = Matrix4::identity(); 34 | 35 | assert!(identity[0][0] == 1.0); 36 | assert!(identity[1][1] == 1.0); 37 | assert!(identity[2][2] == 1.0); 38 | assert!(identity[3][3] == 1.0); 39 | } 40 | 41 | #[test] 42 | fn matrix_translation() 43 | { 44 | let identity = Matrix4::identity(); 45 | 46 | let translation_1 = Matrix4::translation(0.0, 0.0, 0.0); 47 | let translation_2 = Matrix4::translation(1.0, 2.0, 3.0); 48 | let translation_3 = Matrix4::translation(1.0, 2.0, 3.0); 49 | 50 | assert!(identity == translation_1); // no translation equals identity 51 | assert!(identity != translation_2); // translation not equals identity 52 | assert!(translation_2 == translation_3); // same translations are equal 53 | 54 | // check values directly 55 | assert!(translation_2[0][3] == 1.0); 56 | assert!(translation_2[1][3] == 2.0); 57 | assert!(translation_2[2][3] == 3.0); 58 | assert!(translation_2[3][3] == 1.0); 59 | } 60 | 61 | #[bench] 62 | fn bench_multiply(bencher: &mut Bencher) { 63 | let first = Matrix4::identity(); 64 | let second = Matrix4::identity(); 65 | 66 | bencher.iter(|| { 67 | black_box(first * second); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /lib/polygon_math/src/test/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate test; 2 | 3 | mod matrix_test; 4 | mod quaternion_test; 5 | -------------------------------------------------------------------------------- /lib/polygon_math/src/test/quaternion_test.rs: -------------------------------------------------------------------------------- 1 | use std::f32::consts::PI; 2 | 3 | use quaternion::Quaternion; 4 | use vector::Vector3; 5 | use matrix::Matrix4; 6 | 7 | #[test] 8 | fn multiplication() { 9 | // Test that multiplication against the identity quaternion does yields the correct result. 10 | let identity = Quaternion::identity(); 11 | assert_eq!(identity * identity, identity); 12 | 13 | let quat = Quaternion::axis_angle(Vector3::new(1.0, 0.0, 0.0), PI); 14 | assert_eq!(identity * quat, quat); 15 | assert_eq!(quat * identity, quat); 16 | } 17 | 18 | #[test] 19 | fn as_matrix() { 20 | assert_eq!(Quaternion::identity().as_matrix(), Matrix4::identity()); 21 | 22 | assert_eq!(Quaternion::axis_angle(Vector3::new(1.0, 0.0, 0.0), PI).as_matrix(), Matrix4::rotation(PI, 0.0, 0.0)); 23 | assert_eq!(Quaternion::axis_angle(Vector3::new(0.0, 1.0, 0.0), PI).as_matrix(), Matrix4::rotation(0.0, PI, 0.0)); 24 | assert_eq!(Quaternion::axis_angle(Vector3::new(0.0, 0.0, 1.0), PI).as_matrix(), Matrix4::rotation(0.0, 0.0, PI)); 25 | 26 | assert_eq!(Quaternion::axis_angle(Vector3::new(1.0, 0.0, 0.0), PI * 0.5).as_matrix(), Matrix4::rotation(PI * 0.5, 0.0, 0.0)); 27 | assert_eq!(Quaternion::axis_angle(Vector3::new(0.0, 1.0, 0.0), PI * 0.5).as_matrix(), Matrix4::rotation(0.0, PI * 0.5, 0.0)); 28 | assert_eq!(Quaternion::axis_angle(Vector3::new(0.0, 0.0, 1.0), PI * 0.5).as_matrix(), Matrix4::rotation(0.0, 0.0, PI * 0.5)); 29 | 30 | assert_eq!(Quaternion::axis_angle(Vector3::new(1.0, 0.0, 0.0), 0.5).as_matrix(), Matrix4::rotation(0.5, 0.0, 0.0)); 31 | assert_eq!(Quaternion::axis_angle(Vector3::new(0.0, 1.0, 0.0), 0.5).as_matrix(), Matrix4::rotation(0.0, 0.5, 0.0)); 32 | assert_eq!(Quaternion::axis_angle(Vector3::new(0.0, 0.0, 1.0), 0.5).as_matrix(), Matrix4::rotation(0.0, 0.0, 0.5)); 33 | } 34 | -------------------------------------------------------------------------------- /lib/polygon_rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "polygon" 4 | version = "0.0.2" 5 | authors = ["David LeGare "] 6 | 7 | [dependencies] 8 | bootstrap_rs = { version = "0.0", path = "../bootstrap_rs" } 9 | gl-util = { version = "0.1", path = "../gl-util" } 10 | parse-bmp = { version = "0.1", path = "../parse-bmp" } 11 | polygon_math = { version = "0.1", path = "../polygon_math" } 12 | polygon_material = { version = "0.1", path = "../polygon-material" } 13 | stopwatch = { path = "../stopwatch" } 14 | 15 | [dev_dependencies] 16 | parse-obj = { version = "0.1", path = "../parse-obj" } 17 | -------------------------------------------------------------------------------- /lib/polygon_rs/examples/directional_light.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate polygon; 3 | 4 | use bootstrap::window::*; 5 | use polygon::*; 6 | use polygon::anchor::*; 7 | use polygon::camera::*; 8 | use polygon::light::*; 9 | use polygon::math::*; 10 | use polygon::mesh_instance::*; 11 | 12 | pub mod utils; 13 | 14 | fn main() { 15 | // Open a window and create the renderer instance. 16 | let mut window = Window::new("Hello, Triangle!").unwrap(); 17 | let mut renderer = RendererBuilder::new(&window).build(); 18 | 19 | // Build a triangle mesh. 20 | let mesh = utils::load_mesh("resources/meshes/epps_head.obj").unwrap(); 21 | 22 | // Send the mesh to the GPU. 23 | let gpu_mesh = renderer.register_mesh(&mesh); 24 | 25 | // Create an anchor and register it with the renderer. 26 | let mut anchor = Anchor::new(); 27 | anchor.set_position(Point::new(0.0, 0.0, 0.0)); 28 | let mesh_anchor_id = renderer.register_anchor(anchor); 29 | 30 | let mut material = renderer.default_material(); 31 | material.set_color("surface_color", Color::rgb(1.0, 0.0, 0.0)); 32 | material.set_color("surface_specular", Color::rgb(1.0, 1.0, 1.0)); 33 | material.set_f32("surface_shininess", 4.0); 34 | 35 | // Create a mesh instance, attach it to the anchor, and register it with the renderer. 36 | let mut mesh_instance = MeshInstance::with_owned_material(gpu_mesh, material); 37 | mesh_instance.set_anchor(mesh_anchor_id); 38 | renderer.register_mesh_instance(mesh_instance); 39 | 40 | // Create a camera and an anchor for it. 41 | let mut camera_anchor = Anchor::new(); 42 | camera_anchor.set_position(Point::new(0.0, 0.0, 2.0)); 43 | let camera_anchor_id = renderer.register_anchor(camera_anchor); 44 | 45 | // Create the light and an anchor for it. 46 | let light = Light::directional(Vector3::new(1.0, -1.0, -1.0), 0.25, Color::rgb(1.0, 1.0, 1.0)); 47 | renderer.register_light(light); 48 | 49 | let mut camera = Camera::default(); 50 | camera.set_anchor(camera_anchor_id); 51 | renderer.register_camera(camera); 52 | 53 | 'outer: loop { 54 | while let Some(message) = window.next_message() { 55 | if let Message::Close = message { break 'outer; } 56 | } 57 | 58 | // Render the mesh. 59 | renderer.draw(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/polygon_rs/examples/empty.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate polygon; 3 | 4 | use bootstrap::window::*; 5 | use polygon::*; 6 | 7 | fn main() { 8 | // Open a window and create the renderer instance. 9 | let mut window = Window::new("Hello, Triangle!").unwrap(); 10 | let mut renderer = RendererBuilder::new(&window).build(); 11 | 12 | 'outer: loop { 13 | while let Some(message) = window.next_message() { 14 | if let Message::Close = message { 15 | break 'outer; 16 | } 17 | } 18 | 19 | // Render our empty scene. 20 | renderer.draw(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/polygon_rs/examples/hello_triangle.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate polygon; 3 | 4 | use bootstrap::window::*; 5 | use polygon::*; 6 | use polygon::anchor::*; 7 | use polygon::camera::*; 8 | use polygon::math::*; 9 | use polygon::mesh_instance::*; 10 | use polygon::geometry::mesh::*; 11 | 12 | static VERTEX_POSITIONS: [f32; 12] = [ 13 | -1.0, -1.0, 0.0, 1.0, 14 | 1.0, -1.0, 0.0, 1.0, 15 | 0.0, 1.0, 0.0, 1.0, 16 | ]; 17 | 18 | static INDICES: [u32; 3] = [0, 1, 2]; 19 | 20 | fn main() { 21 | // Open a window and create the renderer instance. 22 | let mut window = Window::new("Hello, Triangle!").unwrap(); 23 | let mut renderer = RendererBuilder::new(&window).build(); 24 | 25 | // Build a triangle mesh. 26 | let mesh = MeshBuilder::new() 27 | .set_position_data(Point::slice_from_f32_slice(&VERTEX_POSITIONS)) 28 | .set_indices(&INDICES) 29 | .build() 30 | .unwrap(); 31 | 32 | // Send the mesh to the GPU. 33 | let gpu_mesh = renderer.register_mesh(&mesh); 34 | 35 | // Create an anchor and register it with the renderer. 36 | let anchor = Anchor::new(); 37 | let anchor_id = renderer.register_anchor(anchor); 38 | 39 | // Setup the material for the mesh. 40 | let mut material = renderer.default_material(); 41 | material.set_color("surface_color", Color::rgb(1.0, 0.0, 0.0)); 42 | 43 | // Create a mesh instance, attach it to the anchor, and register it. 44 | let mut mesh_instance = MeshInstance::with_owned_material(gpu_mesh, material); 45 | mesh_instance.set_anchor(anchor_id); 46 | renderer.register_mesh_instance(mesh_instance); 47 | 48 | // Create a camera and an anchor for it. 49 | let mut camera_anchor = Anchor::new(); 50 | camera_anchor.set_position(Point::new(0.0, 0.0, 10.0)); 51 | let camera_anchor_id = renderer.register_anchor(camera_anchor); 52 | 53 | let mut camera = Camera::default(); 54 | camera.set_anchor(camera_anchor_id); 55 | renderer.register_camera(camera); 56 | 57 | // Set ambient color to pure white so we don't need to worry about lighting. 58 | renderer.set_ambient_light(Color::rgb(1.0, 1.0, 1.0)); 59 | 60 | 'outer: loop { 61 | while let Some(message) = window.next_message() { 62 | match message { 63 | Message::Close => break 'outer, 64 | _ => {}, 65 | } 66 | } 67 | 68 | // Rotate the triangle slightly. 69 | { 70 | let anchor = renderer.get_anchor_mut(anchor_id).unwrap(); 71 | let orientation = anchor.orientation(); 72 | anchor.set_orientation(orientation + Orientation::from_eulers(0.0, 0.0, 0.0005)); 73 | } 74 | 75 | // Render the mesh. 76 | renderer.draw(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/polygon_rs/examples/materials.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate polygon; 3 | 4 | use bootstrap::window::*; 5 | use polygon::*; 6 | use polygon::anchor::*; 7 | use polygon::camera::*; 8 | use polygon::light::*; 9 | use polygon::material::*; 10 | use polygon::math::*; 11 | use polygon::mesh_instance::*; 12 | 13 | mod utils; 14 | 15 | fn main() { 16 | // Open a window and create the renderer instance. 17 | let mut window = Window::new("Hello, Triangle!").unwrap(); 18 | let mut renderer = RendererBuilder::new(&window).build(); 19 | 20 | // Load mesh data from an OBJ file and send it to the GPU. 21 | let mesh = utils::load_mesh("resources/meshes/epps_head.obj").unwrap(); 22 | let gpu_mesh = renderer.register_mesh(&mesh); 23 | 24 | // Load texture data from a BMP file and send it to the GPU. 25 | let texture = utils::load_texture("resources/textures/structured.bmp"); 26 | let gpu_texture = renderer.register_texture(&texture); 27 | 28 | // Create an anchor for each of the meshes. 29 | let mut left_anchor = Anchor::new(); 30 | left_anchor.set_position(Point::new(-1.5, 0.0, 0.0)); 31 | let left_anchor_id = renderer.register_anchor(left_anchor); 32 | 33 | let mut middle_anchor = Anchor::new(); 34 | middle_anchor.set_position(Point::new(0.0, 0.0, 0.0)); 35 | let middle_anchor_id = renderer.register_anchor(middle_anchor); 36 | 37 | let mut right_anchor = Anchor::new(); 38 | right_anchor.set_position(Point::new(1.5, 0.0, 0.0)); 39 | let right_anchor_id = renderer.register_anchor(right_anchor); 40 | 41 | // Load the material for each of the meshes. 42 | let left_material_source = 43 | MaterialSource::from_file("resources/materials/diffuse_flat.material").unwrap(); 44 | let mut left_material = renderer.build_material(left_material_source).unwrap(); 45 | left_material.set_color("surface_color", Color::rgb(1.0, 1.0, 0.0)); 46 | 47 | let middle_material_source = 48 | MaterialSource::from_file("resources/materials/diffuse_lit.material").unwrap(); 49 | let mut middle_material = renderer.build_material(middle_material_source).unwrap(); 50 | middle_material.set_color("surface_color", Color::rgb(0.0, 1.0, 1.0)); 51 | middle_material.set_color("specular_color", Color::rgb(1.0, 1.0, 1.0)); 52 | middle_material.set_f32("surface_shininess", 4.0); 53 | 54 | let right_material_source = 55 | MaterialSource::from_file("resources/materials/texture_diffuse_lit.material").unwrap(); 56 | let mut right_material = renderer.build_material(right_material_source).unwrap(); 57 | right_material.set_texture("surface_diffuse", gpu_texture); 58 | right_material.set_color("surface_color", Color::rgb(1.0, 1.0, 1.0)); 59 | right_material.set_color("specular_color", Color::rgb(0.2, 0.2, 0.2)); 60 | right_material.set_f32("surface_shininess", 3.0); 61 | 62 | // Create a mesh instance for each of the meshes, attach it to the anchor, and register it 63 | // with the renderer. 64 | let mut left_mesh_instance = MeshInstance::with_owned_material(gpu_mesh, left_material); 65 | left_mesh_instance.set_anchor(left_anchor_id); 66 | let left_instance_id = renderer.register_mesh_instance(left_mesh_instance); 67 | 68 | let mut middle_mesh_instance = MeshInstance::with_owned_material(gpu_mesh, middle_material); 69 | middle_mesh_instance.set_anchor(middle_anchor_id); 70 | renderer.register_mesh_instance(middle_mesh_instance); 71 | 72 | let mut right_mesh_instance = MeshInstance::with_owned_material(gpu_mesh, right_material); 73 | right_mesh_instance.set_anchor(right_anchor_id); 74 | renderer.register_mesh_instance(right_mesh_instance); 75 | 76 | // Create a camera and an anchor for it. 77 | let mut camera_anchor = Anchor::new(); 78 | camera_anchor.set_position(Point::new(0.0, 0.0, 4.0)); 79 | let camera_anchor_id = renderer.register_anchor(camera_anchor); 80 | let mut camera = Camera::default(); 81 | camera.set_anchor(camera_anchor_id); 82 | renderer.register_camera(camera); 83 | 84 | // Create the light and an anchor for it. 85 | let light_anchor_id = renderer.register_anchor(Anchor::new()); 86 | let mut light = Light::point(5.0, 1.0, Color::new(1.0, 1.0, 1.0, 1.0)); 87 | light.set_anchor(light_anchor_id); 88 | renderer.register_light(light); 89 | 90 | let mut t: f32 = 0.0; 91 | 'outer: loop { 92 | while let Some(message) = window.next_message() { 93 | match message { 94 | Message::Close => break 'outer, 95 | _ => {}, 96 | } 97 | } 98 | 99 | // Change the surface color of the left mesh. 100 | { 101 | let color = Color::new( 102 | t.cos() * 0.5 + 0.5, 103 | t.sin() * 0.5 + 0.5, 104 | (t * 2.0).cos() * 0.5 + 0.5, 105 | 1.0); 106 | 107 | let mesh_instance = renderer.get_mesh_instance_mut(left_instance_id).unwrap(); 108 | mesh_instance 109 | .material_mut() 110 | .unwrap() 111 | .set_color("surface_color", color); 112 | } 113 | 114 | // Move the light back and forth between the middle and right mesh. 115 | { 116 | let anchor = renderer.get_anchor_mut(light_anchor_id).unwrap(); 117 | anchor.set_position(Point::new( 118 | t.cos() * 2.0 + 0.75, 119 | 0.0, 120 | 2.0, 121 | )); 122 | } 123 | 124 | // Render the meshes. 125 | renderer.draw(); 126 | 127 | t += 0.005; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /lib/polygon_rs/examples/multiple_lights.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate polygon; 3 | 4 | use bootstrap::window::*; 5 | use polygon::*; 6 | use polygon::anchor::*; 7 | use polygon::camera::*; 8 | use polygon::light::*; 9 | use polygon::math::*; 10 | use polygon::material::*; 11 | use polygon::mesh_instance::*; 12 | 13 | pub mod utils; 14 | 15 | struct OrbitingLight { 16 | anchor_id: AnchorId, 17 | time_offset: f32, 18 | scale: Vector3, 19 | radius: Vector3, 20 | } 21 | 22 | fn main() { 23 | // Open a window and create the renderer instance. 24 | let mut window = Window::new("Hello, Triangle!").unwrap(); 25 | let mut renderer = RendererBuilder::new(&window).build(); 26 | 27 | // Build a triangle mesh. 28 | let mesh = utils::load_mesh("resources/meshes/epps_head.obj").unwrap(); 29 | 30 | // Send the mesh to the GPU. 31 | let gpu_mesh = renderer.register_mesh(&mesh); 32 | 33 | // Create an anchor and register it with the renderer. 34 | let mut anchor = Anchor::new(); 35 | anchor.set_position(Point::new(0.0, 0.0, 0.0)); 36 | let mesh_anchor_id = renderer.register_anchor(anchor); 37 | 38 | let material_source = MaterialSource::from_file("resources/materials/diffuse_lit.material").unwrap(); 39 | let mut material = renderer.build_material(material_source).unwrap(); 40 | material.set_color("surface_color", Color::rgb(1.0, 1.0, 1.0)); 41 | material.set_color("surface_specular", Color::rgb(1.0, 1.0, 1.0)); 42 | material.set_f32("surface_shininess", 4.0); 43 | 44 | // Create a mesh instance, attach it to the anchor, and register it with the renderer. 45 | let mut mesh_instance = MeshInstance::with_owned_material(gpu_mesh, material); 46 | mesh_instance.set_anchor(mesh_anchor_id); 47 | renderer.register_mesh_instance(mesh_instance); 48 | 49 | // Create a camera and an anchor for it. 50 | let mut camera_anchor = Anchor::new(); 51 | camera_anchor.set_position(Point::new(0.0, 0.0, 2.0)); 52 | let camera_anchor_id = renderer.register_anchor(camera_anchor); 53 | 54 | let mut point_lights = Vec::new(); 55 | 56 | // Create the point lights. 57 | { 58 | let light_anchor_id = renderer.register_anchor(Anchor::new()); 59 | let mut light = Light::point(2.0, 1.0, Color::rgb(1.0, 0.0, 0.0)); 60 | light.set_anchor(light_anchor_id); 61 | renderer.register_light(light); 62 | 63 | point_lights.push(OrbitingLight { 64 | anchor_id: light_anchor_id, 65 | time_offset: 2.3, 66 | scale: Vector3::new(1.0, 1.0, 1.0), 67 | radius: Vector3::new(1.0, 1.0, 1.0), 68 | }); 69 | } 70 | 71 | { 72 | let light_anchor_id = renderer.register_anchor(Anchor::new()); 73 | let mut light = Light::point(2.0, 1.0, Color::rgb(0.0, 1.0, 0.0)); 74 | light.set_anchor(light_anchor_id); 75 | renderer.register_light(light); 76 | 77 | point_lights.push(OrbitingLight { 78 | anchor_id: light_anchor_id, 79 | time_offset: 0.0, 80 | scale: Vector3::new(2.0, 0.6, 1.0), 81 | radius: Vector3::new(1.0, 1.0, 1.0), 82 | }); 83 | } 84 | 85 | { 86 | let light_anchor_id = renderer.register_anchor(Anchor::new()); 87 | let mut light = Light::point(2.0, 1.0, Color::rgb(0.0, 0.0, 1.0)); 88 | light.set_anchor(light_anchor_id); 89 | renderer.register_light(light); 90 | 91 | point_lights.push(OrbitingLight { 92 | anchor_id: light_anchor_id, 93 | time_offset: 1.7, 94 | scale: Vector3::new(0.7, 1.7, 0.1), 95 | radius: Vector3::new(1.0, 1.0, 1.0), 96 | }); 97 | } 98 | 99 | let mut camera = Camera::default(); 100 | camera.set_anchor(camera_anchor_id); 101 | renderer.register_camera(camera); 102 | 103 | let mut t: f32 = 0.0; 104 | 'outer: loop { 105 | while let Some(message) = window.next_message() { 106 | match message { 107 | Message::Close => break 'outer, 108 | _ => {}, 109 | } 110 | } 111 | 112 | // Orbit the light around the mesh. 113 | for orbit in &point_lights { 114 | let anchor = renderer.get_anchor_mut(orbit.anchor_id).unwrap(); 115 | let position = Point::new( 116 | ((t + orbit.time_offset) * orbit.scale.x).sin() * orbit.radius.x, 117 | ((t + orbit.time_offset) * orbit.scale.y).cos() * orbit.radius.y, 118 | ((t + orbit.time_offset) * orbit.scale.z).sin() * orbit.radius.z, 119 | ); 120 | anchor.set_position(position); 121 | } 122 | 123 | // Render the mesh. 124 | renderer.draw(); 125 | 126 | t += 0.0015; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /lib/polygon_rs/examples/no_light.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate polygon; 3 | 4 | use bootstrap::window::*; 5 | use polygon::*; 6 | use polygon::anchor::*; 7 | use polygon::camera::*; 8 | use polygon::math::*; 9 | use polygon::material::*; 10 | use polygon::mesh_instance::*; 11 | 12 | pub mod utils; 13 | 14 | fn main() { 15 | // Open a window and create the renderer instance. 16 | let mut window = Window::new("Hello, Triangle!").unwrap(); 17 | let mut renderer = RendererBuilder::new(&window).build(); 18 | 19 | // Build a triangle mesh. 20 | let mesh = utils::load_mesh("resources/meshes/epps_head.obj").unwrap(); 21 | 22 | // Send the mesh to the GPU. 23 | let gpu_mesh = renderer.register_mesh(&mesh); 24 | 25 | // Create an anchor and register it with the renderer. 26 | let mut anchor = Anchor::new(); 27 | anchor.set_position(Point::new(0.0, 0.0, 0.0)); 28 | let mesh_anchor_id = renderer.register_anchor(anchor); 29 | 30 | let material_source = MaterialSource::from_file("resources/materials/diffuse_lit.material").unwrap(); 31 | let mut material = renderer.build_material(material_source).unwrap(); 32 | material.set_color("surface_color", Color::rgb(1.0, 1.0, 1.0)); 33 | material.set_color("surface_specular", Color::rgb(1.0, 1.0, 1.0)); 34 | material.set_f32("surface_shininess", 4.0); 35 | 36 | // Create a mesh instance, attach it to the anchor, and register it with the renderer. 37 | let mut mesh_instance = MeshInstance::with_owned_material(gpu_mesh, material); 38 | mesh_instance.set_anchor(mesh_anchor_id); 39 | renderer.register_mesh_instance(mesh_instance); 40 | 41 | // Create a camera and an anchor for it. 42 | let mut camera_anchor = Anchor::new(); 43 | camera_anchor.set_position(Point::new(0.0, 0.0, 2.0)); 44 | let camera_anchor_id = renderer.register_anchor(camera_anchor); 45 | 46 | let mut camera = Camera::default(); 47 | camera.set_anchor(camera_anchor_id); 48 | renderer.register_camera(camera); 49 | 50 | 'outer: loop { 51 | while let Some(message) = window.next_message() { 52 | match message { 53 | Message::Close => break 'outer, 54 | _ => {}, 55 | } 56 | } 57 | 58 | // Render the mesh. 59 | renderer.draw(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/polygon_rs/examples/point_light.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate polygon; 3 | 4 | use bootstrap::window::*; 5 | use polygon::*; 6 | use polygon::anchor::*; 7 | use polygon::camera::*; 8 | use polygon::light::*; 9 | use polygon::math::*; 10 | use polygon::material::*; 11 | use polygon::mesh_instance::*; 12 | 13 | pub mod utils; 14 | 15 | fn main() { 16 | // Open a window and create the renderer instance. 17 | let mut window = Window::new("Hello, Triangle!").unwrap(); 18 | let mut renderer = RendererBuilder::new(&window).build(); 19 | 20 | // Build a triangle mesh. 21 | let mesh = utils::load_mesh("resources/meshes/epps_head.obj").unwrap(); 22 | 23 | // Send the mesh to the GPU. 24 | let gpu_mesh = renderer.register_mesh(&mesh); 25 | 26 | // Create an anchor and register it with the renderer. 27 | let mut anchor = Anchor::new(); 28 | anchor.set_position(Point::new(0.0, 0.0, 0.0)); 29 | let mesh_anchor_id = renderer.register_anchor(anchor); 30 | 31 | let material_source = MaterialSource::from_file("resources/materials/diffuse_lit.material").unwrap(); 32 | let mut material = renderer.build_material(material_source).unwrap(); 33 | material.set_color("surface_color", Color::rgb(1.0, 0.0, 1.0)); 34 | material.set_color("surface_specular", Color::rgb(1.0, 1.0, 1.0)); 35 | material.set_f32("surface_shininess", 4.0); 36 | 37 | // Create a mesh instance, attach it to the anchor, and register it with the renderer. 38 | let mut mesh_instance = MeshInstance::with_owned_material(gpu_mesh, material); 39 | mesh_instance.set_anchor(mesh_anchor_id); 40 | renderer.register_mesh_instance(mesh_instance); 41 | 42 | // Create a camera and an anchor for it. 43 | let mut camera_anchor = Anchor::new(); 44 | camera_anchor.set_position(Point::new(0.0, 0.0, 2.0)); 45 | let camera_anchor_id = renderer.register_anchor(camera_anchor); 46 | 47 | // Create the light and an anchor for it. 48 | let mut light_anchor = Anchor::new(); 49 | light_anchor.set_position(Point::new(1.0, 1.0, 3.0)); 50 | light_anchor.set_scale(Vector3::new(0.01, 0.01, 0.01)); 51 | let light_anchor_id = renderer.register_anchor(light_anchor); 52 | let mut light = Light::point(1.0, 1.0, Color::rgb(1.0, 1.0, 1.0)); 53 | light.set_anchor(light_anchor_id); 54 | renderer.register_light(light); 55 | 56 | let mut camera = Camera::default(); 57 | camera.set_anchor(camera_anchor_id); 58 | renderer.register_camera(camera); 59 | 60 | const LIGHT_RADIUS: f32 = 0.5; 61 | 62 | let mut t: f32 = 0.0; 63 | 'outer: loop { 64 | while let Some(message) = window.next_message() { 65 | match message { 66 | Message::Close => break 'outer, 67 | _ => {}, 68 | } 69 | } 70 | 71 | // Orbit the light around the mesh. 72 | { 73 | let anchor = renderer.get_anchor_mut(light_anchor_id).unwrap(); 74 | let position = Point::new( 75 | t.sin() * LIGHT_RADIUS, 76 | t.cos() * LIGHT_RADIUS, 77 | 0.75, 78 | ); 79 | anchor.set_position(position); 80 | } 81 | 82 | // Render the mesh. 83 | renderer.draw(); 84 | 85 | t += 0.0005; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/polygon_rs/examples/textures.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate polygon; 3 | 4 | use bootstrap::window::*; 5 | use polygon::*; 6 | use polygon::anchor::*; 7 | use polygon::camera::*; 8 | use polygon::light::*; 9 | use polygon::material::*; 10 | use polygon::math::*; 11 | use polygon::mesh_instance::*; 12 | 13 | mod utils; 14 | 15 | fn main() { 16 | // Open a window and create the renderer instance. 17 | let mut window = Window::new("Hello, Triangle!").unwrap(); 18 | let mut renderer = RendererBuilder::new(&window).build(); 19 | 20 | // Build a triangle mesh. 21 | let mesh = utils::load_mesh("resources/meshes/epps_head.obj").unwrap(); 22 | let texture = utils::load_texture("resources/textures/structured.bmp"); 23 | 24 | // Send the mesh to the GPU. 25 | let gpu_mesh = renderer.register_mesh(&mesh); 26 | 27 | // Send the texture to the GPU. 28 | let gpu_texture = renderer.register_texture(&texture); 29 | 30 | // Create an anchor and register it with the renderer. 31 | let mut anchor = Anchor::new(); 32 | anchor.set_position(Point::new(0.0, 0.0, 0.0)); 33 | let mesh_anchor_id = renderer.register_anchor(anchor); 34 | 35 | let material_source = 36 | MaterialSource::from_file("resources/materials/texture_diffuse_lit.material").unwrap(); 37 | let mut material = renderer.build_material(material_source).unwrap(); 38 | material.set_color("surface_color", Color::rgb(1.0, 1.0, 1.0)); 39 | material.set_f32("surface_shininess", 4.0); 40 | material.set_texture("surface_diffuse", gpu_texture); 41 | 42 | // Create a mesh instance, attach it to the anchor, and register it with the renderer. 43 | let mut mesh_instance = MeshInstance::with_owned_material(gpu_mesh, material); 44 | mesh_instance.set_anchor(mesh_anchor_id); 45 | renderer.register_mesh_instance(mesh_instance); 46 | 47 | // Create a camera and an anchor for it. 48 | let mut camera_anchor = Anchor::new(); 49 | camera_anchor.set_position(Point::new(0.0, 0.0, 2.0)); 50 | let camera_anchor_id = renderer.register_anchor(camera_anchor); 51 | 52 | // Create the light and an anchor for it. 53 | let light_anchor_id = renderer.register_anchor(Anchor::new()); 54 | let mut light = Light::point(LIGHT_RADIUS, 1.0, Color::new(1.0, 1.0, 1.0, 1.0)); 55 | light.set_anchor(light_anchor_id); 56 | renderer.register_light(light); 57 | 58 | let mut camera = Camera::default(); 59 | camera.set_anchor(camera_anchor_id); 60 | renderer.register_camera(camera); 61 | 62 | const LIGHT_RADIUS: f32 = 2.0; 63 | 64 | let mut t: f32 = 0.0; 65 | 'outer: loop { 66 | while let Some(message) = window.next_message() { 67 | match message { 68 | Message::Close => break 'outer, 69 | _ => {}, 70 | } 71 | } 72 | 73 | // Rotate the mesh slightly. 74 | { 75 | let anchor = renderer.get_anchor_mut(mesh_anchor_id).unwrap(); 76 | anchor.set_orientation(Orientation::from_eulers(0.0, 2.0, 0.0) * (t / 2.0)); 77 | } 78 | 79 | // Orbit the light around the mesh. 80 | { 81 | let anchor = renderer.get_anchor_mut(light_anchor_id).unwrap(); 82 | anchor.set_position(Point::new( 83 | t.cos() * LIGHT_RADIUS * 0.5, 84 | t.sin() * LIGHT_RADIUS * 0.5, 85 | 0.75, 86 | )); 87 | } 88 | 89 | // Render the mesh. 90 | renderer.draw(); 91 | 92 | t += 0.0005; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/polygon_rs/examples/utils.rs: -------------------------------------------------------------------------------- 1 | extern crate parse_bmp; 2 | extern crate parse_obj; 3 | 4 | use polygon::geometry::mesh::*; 5 | use polygon::math::Vector2; 6 | use polygon::texture::Texture2d; 7 | use self::parse_bmp::Bitmap; 8 | use self::parse_obj::*; 9 | use std::path::Path; 10 | 11 | pub fn load_mesh>(path: P) -> Result { 12 | // Load mesh file and normalize indices for OpenGL. 13 | let obj = Obj::from_file(path).unwrap(); 14 | 15 | // Gather vertex data so that OpenGL can use them. 16 | let mut positions = Vec::new(); 17 | let mut normals = Vec::new(); 18 | let mut texcoords = Vec::new(); 19 | 20 | // Iterate over each of the faces in the mesh. 21 | for face in obj.faces() { 22 | // Iterate over each of the vertices in the face to combine the position and normal into 23 | // a single vertex. 24 | for (position, maybe_tex, maybe_normal) in face { 25 | positions.push(position.into()); 26 | 27 | // NOTE: The w texcoord is provided according to the bitmap spec but we don't need to 28 | // use it here, so we simply ignore it. 29 | if let Some((u, v, _w)) = maybe_tex { 30 | texcoords.push(Vector2::new(u, v)); 31 | } 32 | 33 | if let Some(normal) = maybe_normal { 34 | normals.push(normal.into()); 35 | } 36 | } 37 | } 38 | 39 | // Create indices list. 40 | let indices_count = obj.position_indices().len() as u32 * 3; 41 | let indices: Vec = (0..indices_count).collect(); 42 | 43 | MeshBuilder::new() 44 | .set_position_data(&*positions) 45 | .set_normal_data(&*normals) 46 | .set_texcoord_data(&*texcoords) 47 | .set_indices(&*indices) 48 | .build() 49 | } 50 | 51 | pub fn load_texture>(path: P) -> Texture2d { 52 | let bitmap = Bitmap::load(path).unwrap(); 53 | Texture2d::from_bitmap(bitmap) 54 | } 55 | -------------------------------------------------------------------------------- /lib/polygon_rs/resources/materials/diffuse_flat.material: -------------------------------------------------------------------------------- 1 | property surface_color: Color; 2 | 3 | program vert { 4 | @position = model_view_projection * vertex_position; 5 | } 6 | 7 | program frag { 8 | @color = surface_color; 9 | } 10 | -------------------------------------------------------------------------------- /lib/polygon_rs/resources/materials/diffuse_lit.material: -------------------------------------------------------------------------------- 1 | property surface_color: Color; 2 | property surface_specular: Color; 3 | property surface_shininess: f32; 4 | 5 | program frag { 6 | vec4 ambient = global_ambient * surface_color; 7 | vec4 diffuse = vec4(0, 0, 0, 1); 8 | vec4 specular = vec4(0, 0, 0, 1); 9 | 10 | // Vertex normal in view space. 11 | vec3 n = normalize(@vertex.view_normal); 12 | 13 | // Direction from vertex to camera in view space. 14 | vec3 v = normalize(-@vertex.view_position.xyz); 15 | 16 | for (int index = 0; index < 8; index += 1) { 17 | // Direction from vertex to light in view space. 18 | vec3 l; 19 | 20 | // Distance-based attenuation of the light. Doesn't apply for directional lights. 21 | float attenuation; 22 | 23 | // Handle calculation specific to the current light type. 24 | if (light_type[index] == 0) { 25 | // NOTE: We don't calculate diffuse or specular if there is not light. 26 | } else if (light_type[index] == 1) { 27 | // Point light. 28 | vec3 light_offset = (light_position_view[index] - @vertex.view_position).xyz; 29 | float dist = length(light_offset); 30 | l = normalize(light_offset); 31 | attenuation = pow(clamp(1.0 - (dist / light_radius[index]), 0, 1), 2.0); 32 | } else if (light_type[index] == 2) { 33 | // Directional light. 34 | l = -light_direction_view[index]; 35 | attenuation = 1; 36 | } 37 | 38 | if (light_type[index] != 0) { 39 | // Calculate diffuse color. 40 | float l_dot_n = dot(l, n); 41 | diffuse += max(l_dot_n, 0) * surface_color * light_color[index] * attenuation * light_strength[index]; 42 | 43 | // Calculate specular color. 44 | // Specular defaults to black for 45 | if (l_dot_n > 0) { 46 | vec3 r = normalize(reflect(-l, n)); 47 | float r_dot_v = clamp(dot(r, v), 0.0, 1.0); 48 | float shine = pow(r_dot_v, surface_shininess); 49 | specular += surface_specular * shine * attenuation * light_strength[index] * light_color[index]; 50 | } 51 | } 52 | } 53 | 54 | @color = ambient + diffuse + specular; 55 | } 56 | -------------------------------------------------------------------------------- /lib/polygon_rs/resources/materials/texture_diffuse_lit.material: -------------------------------------------------------------------------------- 1 | property surface_diffuse: Texture2d; 2 | property surface_color: Color; 3 | property surface_specular: Color; 4 | property surface_shininess: f32; 5 | 6 | program frag { 7 | vec4 surface_diffuse_sampled = texture(surface_diffuse, @vertex.uv0) * surface_color; 8 | 9 | vec4 ambient = global_ambient * surface_diffuse_sampled; 10 | vec4 diffuse = vec4(0, 0, 0, 1); 11 | vec4 specular = vec4(0, 0, 0, 1); 12 | 13 | // Vertex normal in view space. 14 | vec3 n = normalize(@vertex.view_normal); 15 | 16 | // Direction from vertex to camera in view space. 17 | vec3 v = normalize(-@vertex.view_position.xyz); 18 | 19 | for (int index = 0; index < 8; index += 1) { 20 | // Direction from vertex to light in view space. 21 | vec3 l; 22 | 23 | // Distance-based attenuation of the light. Doesn't apply for directional lights. 24 | float attenuation; 25 | 26 | // Handle calculation specific to the current light type. 27 | if (light_type[index] == 0) { 28 | // NOTE: We don't calculate diffuse or specular if there is not light. 29 | } else if (light_type[index] == 1) { 30 | // Point light. 31 | vec3 light_offset = (light_position_view[index] - @vertex.view_position).xyz; 32 | float dist = length(light_offset); 33 | l = normalize(light_offset); 34 | attenuation = pow(clamp(1.0 - (dist / light_radius[index]), 0, 1), 2.0); 35 | } else if (light_type[index] == 2) { 36 | // Directional light. 37 | l = -light_direction_view[index]; 38 | attenuation = 1; 39 | } 40 | 41 | if (light_type[index] != 0) { 42 | // Calculate diffuse color. 43 | float l_dot_n = dot(l, n); 44 | diffuse += max(l_dot_n, 0) * surface_diffuse_sampled * light_color[index] * attenuation * light_strength[index]; 45 | 46 | // Calculate specular color. 47 | // Specular defaults to black for 48 | if (l_dot_n > 0) { 49 | vec3 r = normalize(reflect(-l, n)); 50 | float r_dot_v = clamp(dot(r, v), 0.0, 1.0); 51 | float shine = pow(r_dot_v, surface_shininess); 52 | specular += surface_specular * shine * attenuation * light_strength[index] * light_color[index]; 53 | } 54 | } 55 | } 56 | 57 | @color = ambient + diffuse + specular; 58 | } 59 | -------------------------------------------------------------------------------- /lib/polygon_rs/resources/textures/structured.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/randomPoison/gunship-rs/5a7acd32d96370990252b75d4065aa4ffb9bc379/lib/polygon_rs/resources/textures/structured.bmp -------------------------------------------------------------------------------- /lib/polygon_rs/src/anchor.rs: -------------------------------------------------------------------------------- 1 | use math::*; 2 | 3 | #[derive(Debug)] 4 | pub struct Anchor { 5 | position: Point, 6 | orientation: Orientation, 7 | scale: Vector3, 8 | } 9 | 10 | impl Anchor { 11 | /// Creates a new anchor. 12 | pub fn new() -> Anchor { 13 | Anchor { 14 | position: Point::origin(), 15 | orientation: Orientation::new(), 16 | scale: Vector3::one(), 17 | } 18 | } 19 | 20 | /// Gets the current position of the anchor. 21 | pub fn position(&self) -> Point { 22 | self.position 23 | } 24 | 25 | /// Sets the position of the anchor. 26 | pub fn set_position(&mut self, position: Point) { 27 | self.position = position; 28 | } 29 | 30 | /// Gets the current orientation of the anchor. 31 | pub fn orientation(&self) -> Orientation { 32 | self.orientation 33 | } 34 | 35 | /// Sets the orientation of the anchor. 36 | pub fn set_orientation(&mut self, orientation: Orientation) { 37 | self.orientation = orientation; 38 | } 39 | 40 | /// Gets the current scale of the anchor. 41 | pub fn scale(&self) -> Vector3 { 42 | self.scale 43 | } 44 | 45 | /// Sets the scale of the anchor. 46 | pub fn set_scale(&mut self, scale: Vector3) { 47 | self.scale = scale; 48 | } 49 | 50 | /// Calculates the matrix to convert from object space to world space. 51 | pub fn matrix(&self) -> Matrix4 { 52 | let position = Matrix4::from_point(self.position); 53 | let orientation = Matrix4::from(self.orientation); 54 | let scale = Matrix4::from_scale_vector(self.scale); 55 | 56 | position * (orientation * scale) 57 | } 58 | 59 | /// Calculates the matrix used to convert normals from object space to world space. 60 | pub fn normal_matrix(&self) -> Matrix3 { 61 | let inv_scale = Matrix3::from_scale_vector(1.0 / self.scale); 62 | let orientation: Matrix3 = self.orientation.into(); 63 | let inv_rotation = orientation.transpose(); 64 | // let inv_translation = Matrix3::from_point(-self.position); 65 | 66 | let inverse = inv_scale * (inv_rotation);// * inv_translation); 67 | inverse.transpose() 68 | } 69 | 70 | /// Calculates the view transform for the camera. 71 | /// 72 | /// The view transform the matrix that converts from world coordinates to camera coordinates. 73 | pub fn view_matrix(&self) -> Matrix4 { 74 | let inv_orientation = Matrix4::from(self.orientation).transpose(); 75 | let inv_translation = Matrix4::translation( 76 | -self.position.x, 77 | -self.position.y, 78 | -self.position.z); 79 | inv_orientation * inv_translation 80 | } 81 | 82 | /// Calculates the inverse view matrix. 83 | pub fn inverse_view_matrix(&self) -> Matrix4 { 84 | Matrix4::from_point(self.position) * self.orientation.into() 85 | } 86 | } 87 | 88 | /// Identifies an achor that has been registered with the renderer. 89 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] 90 | pub struct AnchorId(usize); 91 | derive_Counter!(AnchorId); 92 | -------------------------------------------------------------------------------- /lib/polygon_rs/src/camera.rs: -------------------------------------------------------------------------------- 1 | use anchor::AnchorId; 2 | use math::*; 3 | 4 | /// A camera in the scene. 5 | #[derive(Debug, Clone)] 6 | pub struct Camera 7 | { 8 | fov: f32, 9 | aspect: f32, 10 | near: f32, 11 | far: f32, 12 | 13 | anchor: Option, 14 | } 15 | 16 | impl Camera 17 | { 18 | pub fn new(fov: f32, aspect: f32, near: f32, far: f32) -> Camera { 19 | Camera { 20 | fov: fov, 21 | aspect: aspect, 22 | near: near, 23 | far: far, 24 | 25 | anchor: None, 26 | } 27 | } 28 | 29 | /// Calculates the projection matrix for the camera. 30 | /// 31 | /// The projection matrix is the matrix that converts from camera space to 32 | /// clip space. This effectively converts the viewing frustrum into a unit cube. 33 | pub fn projection_matrix(&self) -> Matrix4 { 34 | let height = 2.0 * self.near * (self.fov * 0.5).tan(); 35 | let width = self.aspect * height; 36 | 37 | let mut projection = Matrix4::new(); 38 | projection[0][0] = 2.0 * self.near / width; 39 | projection[1][1] = 2.0 * self.near / height; 40 | projection[2][2] = -(self.far + self.near) / (self.far - self.near); 41 | projection[2][3] = -2.0 * self.far * self.near / (self.far - self.near); 42 | projection[3][2] = -1.0; 43 | projection 44 | } 45 | 46 | pub fn anchor(&self) -> Option { 47 | self.anchor 48 | } 49 | 50 | pub fn set_anchor(&mut self, anchor_id: AnchorId) { 51 | self.anchor = Some(anchor_id); 52 | } 53 | 54 | pub fn set_fov(&mut self, fov: f32) { 55 | debug_assert!(fov > 0.0, "Field of view must be non-negative: {}", fov); 56 | debug_assert!(fov < PI * 2.0, "Field of view must be less than 180 degrees: {}", fov); 57 | self.fov = fov; 58 | } 59 | 60 | pub fn set_aspect(&mut self, aspect: f32) { 61 | debug_assert!(aspect > 0.0, "Aspect ratio must be non-negative: {}", aspect); 62 | self.aspect = aspect; 63 | } 64 | 65 | pub fn set_near(&mut self, near: f32) { 66 | debug_assert!(near > 0.0, "Near plane distance must be non-negative: {}", near); 67 | debug_assert!(near < self.far, "Near plane distance must be less than far plane distance, near: {}, far: {}", near, self.far); 68 | self.near = near; 69 | } 70 | 71 | pub fn set_far(&mut self, far: f32) { 72 | debug_assert!(far > 0.0, "Far plane distance must be non-negative: {}", far); 73 | debug_assert!(far > self.near, "Far plane distance must be greater than near plane distance, near: {}, far: {}", self.near, far); 74 | self.far = far; 75 | } 76 | } 77 | 78 | impl Default for Camera { 79 | /// Creates a new 80 | fn default() -> Camera { 81 | Camera { 82 | fov: PI / 3.0, 83 | aspect: 1.0, 84 | near: 0.001, 85 | far: 1_000.0, 86 | 87 | anchor: None, 88 | } 89 | } 90 | } 91 | 92 | /// Identifies an achor that has been registered with the renderer. 93 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] 94 | pub struct CameraId(usize); 95 | derive_Counter!(CameraId); 96 | -------------------------------------------------------------------------------- /lib/polygon_rs/src/geometry/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mesh; 2 | -------------------------------------------------------------------------------- /lib/polygon_rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate bootstrap_rs as bootstrap; 2 | extern crate parse_bmp; 3 | extern crate polygon_material; 4 | extern crate stopwatch; 5 | 6 | pub extern crate polygon_math as math; 7 | 8 | // NOTE: This is a "standard" workaround for Rust's nasty macro visibility rules. Once the new 9 | // macro system arrives this can be removed. 10 | #[macro_use] 11 | mod macros; 12 | 13 | pub mod anchor; 14 | pub mod camera; 15 | pub mod geometry; 16 | pub mod gl; 17 | pub mod light; 18 | pub mod material; 19 | pub mod mesh_instance; 20 | pub mod shader; 21 | pub mod texture; 22 | 23 | use anchor::*; 24 | use bootstrap::window::Window; 25 | use camera::*; 26 | use geometry::mesh::Mesh; 27 | use light::*; 28 | use material::*; 29 | use math::Color; 30 | use mesh_instance::*; 31 | use texture::*; 32 | 33 | /// Identifies mesh data that has been sent to the GPU. 34 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] 35 | pub struct GpuMesh(usize); 36 | derive_Counter!(GpuMesh); 37 | 38 | /// The common interface that all rendering systems must provide. 39 | pub trait Renderer: 'static + Send { 40 | /// Renders one frame based on the renderer's current state to the current render target. 41 | fn draw(&mut self); 42 | 43 | /// Gets a copy of the default material for the renderer. 44 | fn default_material(&self) -> Material; 45 | 46 | /// Parses a material source file and generates a material from it. 47 | fn build_material(&mut self, source: MaterialSource) -> Result; 48 | 49 | /// Registers a material to be used as a shared material. 50 | fn register_shared_material(&mut self, material: Material) -> MaterialId; 51 | 52 | /// Gets a registered material. 53 | fn get_material(&self, material_id: MaterialId) -> Option<&Material>; 54 | 55 | /// Registers mesh data with the renderer, returning a unique id for the mesh. 56 | fn register_mesh(&mut self, mesh: &Mesh) -> GpuMesh; 57 | 58 | /// Registers texture data with the renderer, returning a unique id for the texture. 59 | fn register_texture(&mut self, texture: &Texture2d) -> GpuTexture; 60 | 61 | /// Registers a mesh instance with the renderer, returning a unique id for that mesh instance. 62 | fn register_mesh_instance(&mut self, mesh_instance: MeshInstance) -> MeshInstanceId; 63 | 64 | /// Gets a reference to a registered mesh instance. 65 | fn get_mesh_instance(&self, id: MeshInstanceId) -> Option<&MeshInstance>; 66 | 67 | /// Gets a mutable reference to a registered mesh instance. 68 | fn get_mesh_instance_mut(&mut self, id: MeshInstanceId) -> Option<&mut MeshInstance>; 69 | 70 | /// Registers an anchor with the renderer, returning a unique id for the anchor. 71 | fn register_anchor(&mut self, anchor: Anchor) -> AnchorId; 72 | 73 | /// Gets a reference to a registered anchor. 74 | fn get_anchor(&self, anchor_id: AnchorId) -> Option<&Anchor>; 75 | 76 | /// Gets a mutable reference to a registered anchor. 77 | fn get_anchor_mut(&mut self, anchor_id: AnchorId) -> Option<&mut Anchor>; 78 | 79 | /// Registers a camera with the renderer, returning a unique id for the camera. 80 | fn register_camera(&mut self, camera: Camera) -> CameraId; 81 | 82 | /// Gets a reference to a registered camera. 83 | fn get_camera(&self, camera_id: CameraId) -> Option<&Camera>; 84 | 85 | /// Gets a mutable reference to a registered camera. 86 | fn get_camera_mut(&mut self, camera_id: CameraId) -> Option<&mut Camera>; 87 | 88 | /// Registers a light with the renderer, returning a unique id for the light. 89 | fn register_light(&mut self, light: Light) -> LightId; 90 | 91 | /// Gets a reference to a registered light. 92 | fn get_light(&self, light_id: LightId) -> Option<&Light>; 93 | 94 | /// Gets a mutable reference to a registered light. 95 | fn get_light_mut(&mut self, light_id: LightId) -> Option<&mut Light>; 96 | 97 | fn set_ambient_light(&mut self, color: Color); 98 | } 99 | 100 | /// A helper struct for selecting and initializing the most suitable renderer for the client's 101 | /// needs. 102 | pub struct RendererBuilder<'a> { 103 | window: &'a Window, 104 | } 105 | 106 | impl<'a> RendererBuilder<'a> { 107 | /// Creates a new builder object. 108 | pub fn new(window: &Window) -> RendererBuilder { 109 | RendererBuilder { 110 | window: window, 111 | } 112 | } 113 | 114 | /// Constructs a new renderer using the options set in the builder. 115 | pub fn build(&mut self) -> Box { 116 | let renderer = gl::GlRender::new(self.window).unwrap(); 117 | Box::new(renderer) as Box 118 | } 119 | } 120 | 121 | /// Extra special secret trait for keep counter functionality local to this crate. 122 | /// 123 | /// All resources managed by a renderer have an associated ID type used to reference the data 124 | /// owned by the renderer. The renderers internally keep a counter to generate new ID values, but 125 | /// the functionality for creating new ID values needs to be kept private to polygon. In order to 126 | /// avoid having to define all ID types at the root of the crate (which would give the renderers 127 | /// access to the private methods to create new ID values) we define the private `Counter` trait 128 | /// and implement it for all ID types using the `derive_Counter!` macro. This allows us to define 129 | /// various ID types in the most appropriate module while still giving all renderers the ability 130 | /// to create new ID values. 131 | trait Counter { 132 | /// Creates a new counter with the initial value. 133 | fn initial() -> Self; 134 | 135 | /// Returns the next valid ID value, updating the internal counter in the process. 136 | fn next(&mut self) -> Self; 137 | } 138 | 139 | #[derive(Debug)] 140 | pub struct BuildMaterialError; 141 | -------------------------------------------------------------------------------- /lib/polygon_rs/src/light.rs: -------------------------------------------------------------------------------- 1 | use anchor::AnchorId; 2 | use math::{Color, Vector3}; 3 | 4 | #[derive(Clone, Copy, Debug)] 5 | pub struct Light { 6 | pub data: LightData, 7 | pub color: Color, 8 | pub strength: f32, 9 | anchor: Option, 10 | } 11 | 12 | impl Light { 13 | pub fn point(radius: f32, strength: f32, color: Color) -> Light { 14 | Light { 15 | data: LightData::Point { radius: radius }, 16 | color: color, 17 | strength: strength, 18 | anchor: None, 19 | } 20 | } 21 | 22 | pub fn directional(direction: Vector3, strength: f32, color: Color) -> Light { 23 | Light { 24 | data: LightData::Directional { direction: direction.normalized() }, 25 | color: color, 26 | strength: strength, 27 | anchor: None, 28 | } 29 | } 30 | 31 | pub fn anchor(&self) -> Option<&AnchorId> { 32 | self.anchor.as_ref() 33 | } 34 | 35 | pub fn set_anchor(&mut self, anchor_id: AnchorId) { 36 | self.anchor = Some(anchor_id); 37 | } 38 | } 39 | 40 | #[derive(Clone, Copy, Debug)] 41 | pub enum LightData { 42 | Point { radius: f32 }, 43 | Directional { direction: Vector3 }, 44 | } 45 | 46 | /// Identifies a light that has been registered with the renderer. 47 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] 48 | pub struct LightId(usize); 49 | derive_Counter!(LightId); 50 | -------------------------------------------------------------------------------- /lib/polygon_rs/src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Derives the `Counter` trait for any tuple-like struct declared as `SomeStruct(usize)`. 2 | /// 3 | /// Note that the inner value doesn't have to be `usize`, it only needs to be an integer type. 4 | macro_rules! derive_Counter { 5 | ($type_name: ident) => { 6 | impl $crate::Counter for $type_name { 7 | fn initial() -> Self { 8 | $type_name(0) 9 | } 10 | 11 | fn next(&mut self) -> Self { 12 | let next = *self; 13 | self.0 += 1; 14 | next 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/polygon_rs/src/mesh_instance.rs: -------------------------------------------------------------------------------- 1 | //! Mesh instances. 2 | //! 3 | //! When mesh data is sent to the GPU a `GpuMesh` is created to allow that mesh data to be 4 | //! referenced within polygon. In order to display a mesh in the scene we use mesh instances, 5 | //! represented by the `MeshInstance` type. Mesh instances serve two purposes: 6 | //! 7 | //! * Allowing meshes to be displayed numerous times in the same scene. 8 | //! * Associating materials with meshes in the scene. 9 | 10 | use {GpuMesh}; 11 | use anchor::AnchorId; 12 | use material::*; 13 | 14 | /// Represents an instance of a mesh in the scene. 15 | /// 16 | /// By default a mesh instance will not be attached to an anchor, and will not be rendered in 17 | /// the scene until one is set with `set_anchor()` and the mesh instance is registered with 18 | /// the renderer using `Renderer::register_mesh_instance()`. 19 | #[derive(Debug)] 20 | pub struct MeshInstance { 21 | mesh: GpuMesh, 22 | material: MaterialType, 23 | anchor: Option 24 | } 25 | 26 | impl MeshInstance { 27 | /// Creates a new mesh instance sharing the specified material. 28 | pub fn with_shared_material(mesh: GpuMesh, material: MaterialId) -> MeshInstance { 29 | MeshInstance { 30 | mesh: mesh, 31 | material: MaterialType::Shared(material), 32 | anchor: None, 33 | } 34 | } 35 | 36 | /// Creates a new mesh instance with its own material. 37 | pub fn with_owned_material(mesh: GpuMesh, material: Material) -> MeshInstance { 38 | MeshInstance { 39 | mesh: mesh, 40 | material: MaterialType::Owned(material), 41 | anchor: None, 42 | } 43 | } 44 | 45 | /// Sets the mesh referenced by the mesh instance. 46 | pub fn set_mesh(&mut self, mesh: GpuMesh) { 47 | self.mesh = mesh; 48 | } 49 | 50 | /// Gets a reference to the mesh referenced by the mesh instance. 51 | pub fn mesh(&self) -> &GpuMesh { 52 | &self.mesh 53 | } 54 | 55 | /// Gets a reference to either the shared material ID or the owned material. 56 | pub fn material_type(&self) -> &MaterialType { 57 | &self.material 58 | } 59 | 60 | /// Gets the shared material ID if the mesh instance is using a shared material. 61 | pub fn shared_material(&self) -> Option { 62 | match self.material { 63 | MaterialType::Shared(id) => Some(id), 64 | _ => None, 65 | } 66 | } 67 | 68 | /// Gets a reference to the material used by the mesh instance if it owns its material. 69 | pub fn material(&self) -> Option<&Material> { 70 | match self.material { 71 | MaterialType::Owned(ref material) => Some(material), 72 | _ => None, 73 | } 74 | } 75 | 76 | /// Gets a mutable reference to the material used by the mesh instance if it owns its material. 77 | pub fn material_mut(&mut self) -> Option<&mut Material> { 78 | match self.material { 79 | MaterialType::Owned(ref mut material) => Some(material), 80 | _ => None, 81 | } 82 | } 83 | 84 | /// Attaches the mesh instance to the specified anchor. 85 | pub fn set_anchor(&mut self, anchor_id: AnchorId) { 86 | self.anchor = Some(anchor_id); 87 | } 88 | 89 | /// Gets a reference to the anchor this instance is attached to. 90 | pub fn anchor(&self) -> Option { 91 | self.anchor 92 | } 93 | } 94 | 95 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] 96 | pub struct MeshInstanceId(usize); 97 | derive_Counter!(MeshInstanceId); 98 | -------------------------------------------------------------------------------- /lib/polygon_rs/src/shader.rs: -------------------------------------------------------------------------------- 1 | /// Identifies a shader that has been compiled and linked on the GPU. 2 | /// 3 | /// Shaders are created by the renderer by compiling shader source code. 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] 5 | pub struct Shader(usize); 6 | derive_Counter!(Shader); 7 | -------------------------------------------------------------------------------- /lib/polygon_rs/src/texture.rs: -------------------------------------------------------------------------------- 1 | use parse_bmp::{ 2 | Bitmap, 3 | BitmapData, 4 | }; 5 | 6 | /// Represents texture data that has been sent to the GPU. 7 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] 8 | pub struct GpuTexture(usize); 9 | derive_Counter!(GpuTexture); 10 | 11 | /// Represents a texture loaded into memory and ready to be sent to the GPU. 12 | /// 13 | /// `Texture2d` defines a backend-agnostic in-memory representation of texture data that can be 14 | /// used by any of the rendering backends to send texture data to the GPU. It encapsulates all 15 | /// relevant information about the texture, including the raw bytes of the texture and information 16 | /// describing the in-memory layout of that data. It also provides functionality for safely 17 | /// loading textures from common formats (NOTE: Only bitmap is supported currently). 18 | #[derive(Debug)] 19 | pub struct Texture2d { 20 | width: usize, 21 | height: usize, 22 | format: DataFormat, 23 | data: TextureData, 24 | } 25 | 26 | impl Texture2d { 27 | /// Loads a new `Texture` from a bitmap file. 28 | pub fn from_bitmap(bitmap: Bitmap) -> Texture2d { 29 | let texture = match bitmap.data() { 30 | &BitmapData::Bgr(ref data) => { 31 | Texture2d { 32 | width: bitmap.width(), 33 | height: bitmap.height(), 34 | format: DataFormat::Bgr, 35 | data: TextureData::u8x3(data.clone()), // TODO: Don't clone the data. 36 | } 37 | }, 38 | &BitmapData::Bgra(ref data) => { 39 | Texture2d { 40 | width: bitmap.width(), 41 | height: bitmap.height(), 42 | format: DataFormat::Bgra, 43 | data: TextureData::u8x4(data.clone()), // TODO: Don't clone the data. 44 | } 45 | }, 46 | }; 47 | 48 | texture 49 | } 50 | 51 | /// Returns the width of the texture. 52 | pub fn width(&self) -> usize { 53 | self.width 54 | } 55 | 56 | /// Returns the height of the texture. 57 | pub fn height(&self) -> usize() { 58 | self.height 59 | } 60 | 61 | /// Gets the data format for the texture. 62 | pub fn format(&self) -> DataFormat { 63 | self.format 64 | } 65 | 66 | /// Gets the data for the texture. 67 | pub fn data(&self) -> &TextureData { 68 | &self.data 69 | } 70 | } 71 | 72 | /// An enum representing the supported data formats for a texture. 73 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 74 | pub enum DataFormat { 75 | Rgb, 76 | Rgba, 77 | Bgr, 78 | Bgra, 79 | } 80 | 81 | /// An enum representing the possible data types for a texture. 82 | /// 83 | /// `TextureData` also owns the texture raw data buffer in order to maintain type safety. 84 | /// 85 | /// TODO: Should each element represent a single pixel, or is it okay to have multiple array 86 | /// elements combine to make a single pixel? I.e. for an RGB texture should it be required to use 87 | /// `u8x3` or should it be okay to use `u8` 3 elements at a time? Since the two have the same 88 | /// in-memory representation we can always transmute between the two so it's not a performance 89 | /// issue. 90 | #[allow(bad_style)] 91 | #[derive(Debug, Clone, PartialEq)] 92 | pub enum TextureData { 93 | f32(Vec), 94 | u8(Vec), 95 | u8x3(Vec<(u8, u8, u8)>), 96 | u8x4(Vec<(u8, u8, u8, u8)>), 97 | } 98 | -------------------------------------------------------------------------------- /lib/stopwatch/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "stopwatch" 4 | version = "0.0.0" 5 | authors = ["David LeGare "] 6 | 7 | [dependencies] 8 | bootstrap_rs = { path = "../bootstrap_rs" } 9 | fiber = { path = "../fiber" } 10 | lazy_static = "0.2" 11 | serde = "0.8" 12 | serde_derive = "0.8" 13 | serde_json = "0.8" 14 | 15 | [target.'cfg(target_os = "windows")'.dependencies] 16 | kernel32-sys = "0.2.2" 17 | -------------------------------------------------------------------------------- /lib/stopwatch/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(const_fn)] 2 | #![feature(drop_types_in_const)] 3 | #![feature(proc_macro)] 4 | 5 | #[macro_use] 6 | extern crate lazy_static; 7 | #[macro_use] 8 | extern crate serde_derive; 9 | extern crate serde_json; 10 | 11 | extern crate bootstrap_rs as bootstrap; 12 | extern crate fiber; 13 | 14 | use fiber::FiberId; 15 | use std::cell::RefCell; 16 | use std::collections::HashMap; 17 | use std::fmt::{self, Display, Formatter}; 18 | use std::mem; 19 | use std::sync::Mutex; 20 | use std::time::Duration; 21 | 22 | #[cfg(target_os="windows")] 23 | #[path="windows.rs"] 24 | pub mod platform; 25 | pub mod stats; 26 | 27 | thread_local! { 28 | static CONTEXT: RefCell = RefCell::new(Context::new()); 29 | } 30 | 31 | lazy_static! { 32 | static ref CONTEXT_MAP: Mutex> = Mutex::new(HashMap::with_capacity(1024)); 33 | static ref EVENTS: Mutex> = Mutex::new(Vec::new()); 34 | } 35 | 36 | /// Swaps the currently tracked execution context with the specified context. 37 | pub fn switch_context(old: FiberId, new: FiberId) { 38 | with_context(|stack| { 39 | let timestamp = platform::timestamp(); 40 | 41 | // Push an end event for each of the time slices. 42 | for stopwatch in stack.iter().rev() { 43 | push_event(Event { 44 | name: stopwatch.name, 45 | cat: String::new(), 46 | ph: "E", 47 | ts: timestamp, 48 | tid: platform::thread_id(), 49 | pid: 0, 50 | }); 51 | } 52 | }); 53 | 54 | let mut context_map = CONTEXT_MAP.lock().expect("Unable to acquire lock on context map"); 55 | 56 | let new_context = context_map.remove(&new).unwrap_or(Context::new()); 57 | let old_context = with_context(move |context| { 58 | let mut new_context = new_context; 59 | mem::swap(context, &mut new_context); 60 | new_context 61 | }); 62 | 63 | context_map.insert(old, old_context); 64 | 65 | with_context(|stack| { 66 | let timestamp = platform::timestamp(); 67 | 68 | // Push an end event for each of the time slices. 69 | for stopwatch in stack.iter() { 70 | push_event(Event { 71 | name: stopwatch.name, 72 | cat: String::new(), 73 | ph: "B", 74 | ts: timestamp, 75 | tid: platform::thread_id(), 76 | pid: 0, 77 | }); 78 | } 79 | }); 80 | } 81 | 82 | /// Writes the events history to a string. 83 | pub fn write_events_to_string() -> String { 84 | let events = EVENTS.lock().expect("Events mutex got poisoned"); 85 | serde_json::to_string(&*events).unwrap() 86 | } 87 | 88 | pub struct Stopwatch { 89 | name: &'static str, 90 | } 91 | 92 | impl Stopwatch { 93 | pub fn new(name: &'static str) -> Stopwatch { 94 | push_event(Event { 95 | name: name, 96 | cat: String::new(), 97 | ph: "B", 98 | ts: platform::timestamp(), 99 | tid: platform::thread_id(), 100 | pid: 0, // TODO: Do we care about tracking process ID? 101 | }); 102 | 103 | with_context(|stack| { 104 | stack.push(StopwatchData { name: name }); 105 | }); 106 | 107 | Stopwatch { 108 | name: name, 109 | } 110 | } 111 | 112 | pub fn with_budget(name: &'static str, _budget: Duration) -> Stopwatch { 113 | // TODO: We should actually do something with the budget, right? 114 | Stopwatch::new(name) 115 | } 116 | } 117 | 118 | impl Drop for Stopwatch { 119 | fn drop(&mut self) { 120 | with_context(|stack| { 121 | let stopwatch = stack.pop().expect("No stopwatch popped, stack is corrupted"); 122 | assert_eq!(self.name, stopwatch.name, "Stack got corrupted I guess"); 123 | }); 124 | 125 | push_event(Event { 126 | name: self.name, 127 | cat: String::new(), 128 | ph: "E", 129 | ts: platform::timestamp(), 130 | tid: platform::thread_id(), 131 | pid: 0, // TODO: Do we care about tracking process ID? 132 | }); 133 | } 134 | } 135 | 136 | #[derive(Debug, Serialize)] 137 | struct Event { 138 | /// Human-readable name for the event. 139 | name: &'static str, 140 | 141 | /// Event category. 142 | cat: String, 143 | 144 | /// Event phase (i.e. the event type). 145 | ph: &'static str, 146 | 147 | /// Timestamp in microseconds. 148 | ts: i64, 149 | 150 | /// Process ID for the event. 151 | pid: usize, 152 | 153 | /// Thread ID for the event. 154 | tid: usize, 155 | } 156 | 157 | fn push_event(event: Event) { 158 | let mut events = EVENTS.lock().expect("Events mutex got poisoned"); 159 | events.push(event); 160 | } 161 | 162 | #[derive(Debug, Clone, Copy)] 163 | struct StopwatchData { 164 | name: &'static str, 165 | } 166 | 167 | type Context = Vec; 168 | 169 | fn with_context(func: F) -> T 170 | where F: FnOnce(&mut Context) -> T 171 | { 172 | CONTEXT.with(move |context_cell| { 173 | let mut context = context_cell.borrow_mut(); 174 | func(&mut *context) 175 | }) 176 | } 177 | 178 | pub struct PrettyDuration(pub Duration); 179 | 180 | impl Display for PrettyDuration { 181 | fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> { 182 | let mins = self.0.as_secs() / 60; 183 | let secs = self.0.as_secs() % 60; 184 | let millis = self.0.subsec_nanos() as u64 / 1_000_000; 185 | let micros = (self.0.subsec_nanos() / 1_000) % 1_000; 186 | 187 | if mins > 0 { 188 | write!(formatter, "{}m {}s {}.{}ms", mins, secs, millis, micros) 189 | } else if secs > 0 { 190 | write!(formatter, "{}s {}.{}ms", secs, millis, micros) 191 | } else { 192 | write!(formatter, "{}.{}ms", millis, micros) 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /lib/stopwatch/src/stats.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | // Calculate performance statistics. 4 | // ============================================================================================ 5 | fn as_nanos(duration: Duration) -> u64 { 6 | duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64 7 | } 8 | 9 | fn from_nanos(nanos: u64) -> Duration { 10 | let secs = nanos / 1_000_000_000; 11 | let subsec_nanos = nanos % 1_000_000_000; 12 | Duration::new(secs, subsec_nanos as u32) 13 | } 14 | 15 | #[derive(Debug, Clone, Copy)] 16 | pub struct Statistics { 17 | pub min: Duration, 18 | pub max: Duration, 19 | pub mean: Duration, 20 | pub std: Duration, 21 | pub long_frames: usize, 22 | pub long_frame_ratio: f64, 23 | } 24 | 25 | pub fn analyze(frame_times: &[Duration], target_frame_time: Duration) -> Statistics { 26 | let mut min = frame_times[0]; 27 | let mut max = frame_times[0]; 28 | let mut total = Duration::new(0, 0); 29 | let mut long_frames = 0; 30 | 31 | for time in frame_times.iter().cloned() { 32 | total += time; 33 | if time < min { min = time; } 34 | if time > max { max = time; } 35 | if time > target_frame_time { long_frames += 1; } 36 | } 37 | 38 | let mean = total / frame_times.len() as u32; 39 | let total_sqr_deviation = frame_times.iter().cloned().fold(0, |total, time| { 40 | let diff = if time < mean { mean - time } else { time - mean }; 41 | 42 | // Convert to nanos so that we can square and hope we don't overflow ¯\_(ツ)_/¯. 43 | let nanos = as_nanos(diff); 44 | let diff_sqr = nanos * nanos; 45 | 46 | total + diff_sqr 47 | }); 48 | 49 | let std_dev = from_nanos(f64::sqrt(total_sqr_deviation as f64 / frame_times.len() as f64) as u64); 50 | let long_frame_ratio = long_frames as f64 / frame_times.len() as f64; 51 | 52 | Statistics { 53 | min: min, 54 | max: max, 55 | mean: mean, 56 | std: std_dev, 57 | long_frames: long_frames, 58 | long_frame_ratio: long_frame_ratio, 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/stopwatch/src/windows.rs: -------------------------------------------------------------------------------- 1 | extern crate kernel32; 2 | 3 | /// Gets the current timestamp in microseconds. 4 | pub fn timestamp() -> i64 { 5 | let mut frequency = 0; 6 | if unsafe { kernel32::QueryPerformanceFrequency(&mut frequency) } == 0 { 7 | panic!("Failed to query performance frequency"); 8 | } 9 | 10 | let mut counter = 0; 11 | if unsafe { kernel32::QueryPerformanceCounter(&mut counter) } == 0 { 12 | panic!("Failed to query performance counter"); 13 | } 14 | 15 | counter * 1_000_000 / frequency 16 | } 17 | 18 | pub fn thread_id() -> usize { 19 | unsafe { kernel32::GetCurrentThreadId() as usize } 20 | } 21 | -------------------------------------------------------------------------------- /src/camera.rs: -------------------------------------------------------------------------------- 1 | use engine::{self, EngineMessage}; 2 | use transform::Transform; 3 | use std::f32::consts::PI; 4 | use std::fmt::{self, Debug, Formatter}; 5 | use std::marker::PhantomData; 6 | use std::mem; 7 | use std::ops::{Deref, DerefMut}; 8 | use std::ptr::Unique; 9 | 10 | pub struct Camera { 11 | data: Unique, 12 | 13 | // Pretend `Camera` owns a raw pointer to default implementation for `Sync`. 14 | // TODO: Remove this once negative trait bounds are stabilized. 15 | _phantom: PhantomData<*mut ()>, 16 | } 17 | 18 | impl Camera { 19 | pub fn new(transform: &Transform) -> Camera { 20 | let mut camera_data = Box::new(CameraData::default()); 21 | 22 | let ptr = &mut *camera_data as *mut _; 23 | 24 | engine::send_message(EngineMessage::Camera(camera_data, transform.inner())); 25 | 26 | Camera { 27 | data: unsafe { Unique::new(ptr) }, 28 | _phantom: PhantomData, 29 | } 30 | } 31 | 32 | pub fn forget(self) { 33 | mem::forget(self); 34 | } 35 | } 36 | 37 | unsafe impl Send for Camera {} 38 | 39 | impl Debug for Camera { 40 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { 41 | let data = unsafe { self.data.get() }; 42 | 43 | fmt.debug_struct("Camera") 44 | .field("fov", &data.fov) 45 | .field("aspect", &data.aspect) 46 | .field("near", &data.near) 47 | .field("far", &data.far) 48 | .finish() 49 | } 50 | } 51 | 52 | impl Deref for Camera { 53 | type Target = CameraData; 54 | 55 | fn deref(&self) -> &CameraData { unsafe { self.data.get() } } 56 | } 57 | 58 | impl DerefMut for Camera { 59 | fn deref_mut(&mut self) -> &mut CameraData { unsafe { self.data.get_mut() } } 60 | } 61 | 62 | #[derive(Debug)] 63 | pub struct CameraData { 64 | fov: f32, 65 | aspect: f32, 66 | near: f32, 67 | far: f32, 68 | } 69 | 70 | impl CameraData { 71 | pub fn fov(&self) -> f32 { self.fov } 72 | 73 | pub fn aspect(&self) -> f32 { self.aspect } 74 | 75 | pub fn near(&self) -> f32 { self.near } 76 | 77 | pub fn far(&self) -> f32 { self.far } 78 | } 79 | 80 | impl Default for CameraData { 81 | fn default() -> CameraData { 82 | CameraData { 83 | fov: PI / 3.0, 84 | aspect: 1.0, 85 | near: 0.001, 86 | far: 1_000.0, 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/collections/array.rs: -------------------------------------------------------------------------------- 1 | /// A non-growable counterpart to `Vec`. 2 | /// 3 | /// Arrays behave exactly like vectors (currently they use `Vec` internally) but trade being able 4 | /// to reallocate to support more elements for being able to add elements through a shared reference. 5 | 6 | use std::ops::{Deref, DerefMut}; 7 | use std::fmt::{self, Debug, Formatter}; 8 | 9 | /// A non-growable array type supporting stack operations. 10 | pub struct Array(Vec); 11 | 12 | impl Array { 13 | pub fn new(capacity: usize) -> Array { 14 | Array(Vec::with_capacity(capacity)) 15 | } 16 | 17 | pub fn push(&self, element: T) { 18 | assert!(self.len() < self.capacity(), "Cannot add element when array is at capacity"); 19 | self.inner().push(element); 20 | } 21 | 22 | fn inner(&self) -> &mut Vec { 23 | let ptr = &self.0 as *const Vec as *mut Vec; 24 | unsafe { 25 | &mut *ptr 26 | } 27 | } 28 | } 29 | 30 | impl Clone for Array 31 | where T: Clone 32 | { 33 | fn clone(&self) -> Array { 34 | Array(self.0.clone()) 35 | } 36 | } 37 | 38 | impl Debug for Array 39 | where T: Debug 40 | { 41 | fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 42 | write!(f, "Array({:?})", &self.0) 43 | } 44 | } 45 | 46 | impl Deref for Array { 47 | type Target = Vec; 48 | 49 | fn deref(&self) -> &Vec { 50 | &self.0 51 | } 52 | } 53 | 54 | impl DerefMut for Array { 55 | fn deref_mut(&mut self) -> &mut Vec { 56 | &mut self.0 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/collections/atomic_array.rs: -------------------------------------------------------------------------------- 1 | use collections::alloc::raw_vec::RawVec; 2 | use std::cell::UnsafeCell; 3 | use std::ops::{Deref, Index}; 4 | use std::ptr; 5 | use std::slice::{self, Iter, IterMut}; 6 | use std::sync::atomic::{ AtomicBool, AtomicUsize, Ordering }; 7 | 8 | /// A dynamically allocated, fixed-size array container. 9 | /// 10 | /// This type provides a more thread-safe (though not completely thread-safe) alternative to 11 | /// [`Vec`][vec]. Thread safety is achieved as follows: 12 | /// 13 | /// - `AtomicArray` does not reallocate, meaning pushing and popping will not invalidate 14 | /// references to other elements in the array. This means it's always safe to concurrently push 15 | /// new elements while accessing existing elements, and it is possible to safely access existing 16 | /// elements while popping, though consumers of this type must manually take extra precautions. 17 | /// - All mutations to the container are done atomically, making it safe for multiple threads to 18 | /// concurrently push and pop elements. 19 | /// - Only push, pop, and swap-remove mutations are supported. While this is only a small subset 20 | /// of the mutations available for [`Vec`][vec], they are sufficient for many use cases and can 21 | /// be safely done concurrently without risk of deadlock. 22 | /// 23 | /// [vec]: https://doc.rust-lang.org/std/vec/struct.Vec.html 24 | // TODO: impl Debug for AtomicArray. 25 | pub struct AtomicArray { 26 | buffer: UnsafeCell>, 27 | len: AtomicUsize, 28 | write_lock: AtomicBool, 29 | } 30 | 31 | impl AtomicArray { 32 | pub fn new(capacity: usize) -> AtomicArray { 33 | AtomicArray { 34 | buffer: UnsafeCell::new(RawVec::with_capacity(capacity)), 35 | len: AtomicUsize::new(0), 36 | write_lock: AtomicBool::new(false), 37 | } 38 | } 39 | 40 | pub fn push(&self, element: T) { 41 | // Acquire the write lock by attempting to switch the flag from `false` to `true`. If it 42 | // returns `false` then we've acquired the lock. We use sequentially consistent ordering 43 | // for now to guarantee correctness at the cost of some performance. 44 | while !self.write_lock.compare_and_swap(false, true, Ordering::SeqCst) {} 45 | 46 | // Write the element into the buffer at the new location, making sure we don't drop 47 | // `element` or the object that previously occupied that slot in the bucket. 48 | let old_len = self.len.load(Ordering::SeqCst); 49 | unsafe { 50 | let dest = (&*self.buffer.get()).ptr().offset(old_len as isize); 51 | ptr::write(dest, element); 52 | } 53 | 54 | // Once the write completes it's safe to increment len since any subsequent reads of len 55 | // will not allow another thread to observe the element in an uninitialized state. 56 | self.len.fetch_add(1, Ordering::SeqCst); // TODO: Is `fetch_add()` the write operation? Should we be asserting on the old len or something? 57 | 58 | // Once that's done we can release the lock. 59 | self.write_lock.store(false, Ordering::SeqCst); 60 | } 61 | 62 | pub fn pop(&mut self) -> Option { 63 | unimplemented!(); 64 | } 65 | 66 | pub fn swap_remove(&mut self, _index: usize) -> Option { 67 | unimplemented!(); 68 | } 69 | 70 | pub fn last_mut(&mut self) -> Option<&mut T> { 71 | let len = self.len.load(Ordering::SeqCst); 72 | if len > 0 { 73 | unsafe { Some(&mut *(&*self.buffer.get()).ptr().offset((len - 1) as isize)) } 74 | } else { 75 | None 76 | } 77 | } 78 | 79 | pub fn len(&self) -> usize { 80 | self.len.load(Ordering::SeqCst) 81 | } 82 | 83 | pub fn capacity(&self) -> usize { 84 | unsafe { &*self.buffer.get() }.cap() 85 | } 86 | 87 | pub fn as_slice(&self) -> &[T] { 88 | unsafe { 89 | slice::from_raw_parts( 90 | self.buffer().ptr(), 91 | self.len.load(Ordering::SeqCst), 92 | ) 93 | } 94 | } 95 | 96 | pub fn as_slice_mut(&mut self) -> &mut [T] { 97 | unsafe { 98 | slice::from_raw_parts_mut( 99 | self.buffer().ptr(), 100 | self.len.load(Ordering::SeqCst), 101 | ) 102 | } 103 | } 104 | 105 | fn buffer(&self) -> &RawVec { 106 | unsafe { &*self.buffer.get() } 107 | } 108 | } 109 | 110 | impl Deref for AtomicArray { 111 | type Target = [T]; 112 | 113 | fn deref(&self) -> &[T] { 114 | self.as_slice() 115 | } 116 | } 117 | 118 | impl Index for AtomicArray { 119 | type Output = T; 120 | 121 | fn index(&self, index: usize) -> &T { 122 | let len = self.len.load(Ordering::SeqCst); 123 | assert!(index < len, "Index out of bounds, length is {} but index was {}", len, index); 124 | 125 | unsafe { &*(&*self.buffer.get()).ptr().offset(index as isize) } 126 | } 127 | } 128 | 129 | impl<'a, T> IntoIterator for &'a AtomicArray { 130 | type Item = &'a T; 131 | type IntoIter = Iter<'a, T>; 132 | 133 | fn into_iter(self) -> Iter<'a, T> { 134 | self.as_slice().into_iter() 135 | } 136 | } 137 | 138 | impl<'a, T> IntoIterator for &'a mut AtomicArray { 139 | type Item = &'a mut T; 140 | type IntoIter = IterMut<'a, T>; 141 | 142 | fn into_iter(self) -> IterMut<'a, T> { 143 | self.as_slice_mut().into_iter() 144 | } 145 | } 146 | 147 | unsafe impl Sync for AtomicArray where T: Sync {} 148 | -------------------------------------------------------------------------------- /src/collections/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate alloc; 2 | 3 | pub use self::array::Array; 4 | 5 | pub mod array; 6 | pub mod atomic_array; 7 | -------------------------------------------------------------------------------- /src/component/audio.rs: -------------------------------------------------------------------------------- 1 | use ecs::*; 2 | use engine::*; 3 | use scene::Scene; 4 | use std::rc::Rc; 5 | use super::DefaultMessage; 6 | use super::struct_component_manager::{Iter, IterMut, StructComponentManager}; 7 | use wav::Wave; 8 | 9 | #[derive(Debug, Clone)] 10 | pub struct AudioSource { 11 | audio_clip: Rc, 12 | offset: usize, 13 | is_playing: bool, 14 | looping: bool, 15 | } 16 | 17 | impl AudioSource { 18 | /// Start playing the current audio clip from where it left off. 19 | pub fn play(&mut self) { 20 | self.is_playing = true; 21 | } 22 | 23 | /// Pause the clip without resetting it to the beginning. 24 | pub fn pause(&mut self) { 25 | self.is_playing = false; 26 | } 27 | 28 | /// Stop the current audio clip and reset it to the beginning. 29 | pub fn stop(&mut self) { 30 | self.is_playing = false; 31 | self.offset = 0; 32 | } 33 | 34 | /// Reset the audio clip the start without stoping it. 35 | pub fn reset(&mut self) { 36 | self.offset = 0; 37 | } 38 | 39 | /// Retrieve whether the audio clip is currently playing. 40 | pub fn is_playing(&self) -> bool { 41 | self.is_playing 42 | } 43 | } 44 | 45 | impl Component for AudioSource { 46 | type Manager = AudioSourceManager; 47 | type Message = DefaultMessage; 48 | } 49 | 50 | #[derive(Debug, Clone)] 51 | pub struct AudioSourceManager(StructComponentManager); 52 | 53 | impl AudioSourceManager { 54 | pub fn new() -> AudioSourceManager { 55 | AudioSourceManager(StructComponentManager::new()) 56 | } 57 | 58 | pub fn assign(&mut self, entity: Entity, clip_name: &str) -> &AudioSource { 59 | let audio_clip = Engine::resource_manager().get_audio_clip(clip_name); 60 | self.0.assign(entity, AudioSource { 61 | audio_clip: audio_clip, 62 | offset: 0, 63 | is_playing: false, 64 | looping: false, 65 | }) 66 | } 67 | 68 | pub fn get(&self, entity: Entity) -> Option<&AudioSource> { 69 | self.0.get(entity) 70 | } 71 | 72 | pub fn iter(&self) -> Iter { 73 | self.0.iter() 74 | } 75 | 76 | pub fn iter_mut(&mut self) -> IterMut { 77 | self.0.iter_mut() 78 | } 79 | } 80 | 81 | impl ComponentManagerBase for AudioSourceManager {} 82 | 83 | impl ComponentManager for AudioSourceManager { 84 | type Component = AudioSource; 85 | 86 | fn register(builder: &mut EngineBuilder) { 87 | let audio_manager = AudioSourceManager::new(); 88 | builder.register_manager(audio_manager); 89 | } 90 | 91 | fn get(&self, entity: Entity) -> Option<&Self::Component> { 92 | self.0.get(entity) 93 | } 94 | 95 | fn destroy(&self, entity: Entity) { 96 | self.0.destroy(entity); 97 | } 98 | } 99 | 100 | derive_Singleton!(AudioSourceManager); 101 | 102 | pub struct AudioSystem; 103 | 104 | impl System for AudioSystem { 105 | fn update(&mut self, scene: &Scene, delta: f32) { 106 | let mut audio_source_manager = unsafe { scene.get_manager_mut::() }; // FIXME: Very bad, use new system. 107 | 108 | // TODO: Use a better method to filter out audio sources that aren't playing. 109 | for mut audio_source in audio_source_manager.iter_mut() 110 | .map(|(audio_source, _)| audio_source) 111 | .filter(|audio_source| audio_source.is_playing) { 112 | // Create an iterator over the samples using the data from the audio clip. 113 | let total_samples = { 114 | let mut stream = audio_source.audio_clip.data.samples[audio_source.offset..].iter() 115 | .map(|sample| *sample); 116 | 117 | // Sream the samples to the audio card. 118 | let samples_written = scene.audio_source.stream(&mut stream, delta); 119 | 120 | // Determine if we're done playing the clip yet. 121 | audio_source.offset + samples_written 122 | }; 123 | if total_samples >= audio_source.audio_clip.data.samples.len() { 124 | audio_source.offset = 0; 125 | 126 | if !audio_source.looping { 127 | audio_source.stop(); 128 | } 129 | } else { 130 | audio_source.offset = total_samples; 131 | } 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/component/camera.rs: -------------------------------------------------------------------------------- 1 | use component::DefaultManager; 2 | use math::*; 3 | 4 | pub type CameraManager = DefaultManager; 5 | 6 | derive_Component!(Camera); 7 | #[derive(Debug, Clone)] 8 | pub struct Camera 9 | { 10 | pub fov: f32, 11 | pub aspect: f32, 12 | pub near: f32, 13 | pub far: f32, 14 | } 15 | 16 | impl Default for Camera { 17 | fn default() -> Camera { 18 | Camera { 19 | fov: PI / 3.0, 20 | aspect: 1.0, 21 | near: 0.001, 22 | far: 100.0, 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/component/light.rs: -------------------------------------------------------------------------------- 1 | use component::DefaultManager; 2 | 3 | // TODO: Don't expose `polygon::light::Light` directly since it contains irrelevant data (e.g. the 4 | // anchor, which is polygon-specific and controlled by the transform in Gunship). 5 | pub use polygon::light::Light; 6 | pub use polygon::light::PointLight; 7 | 8 | derive_Component!(Light); 9 | 10 | pub type LightManager = DefaultManager; 11 | -------------------------------------------------------------------------------- /src/component/mesh.rs: -------------------------------------------------------------------------------- 1 | use ecs::*; 2 | use engine::*; 3 | use polygon::GpuMesh; 4 | use polygon::material::Material; 5 | use super::struct_component_manager::*; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct Mesh { 9 | entity: Entity, 10 | gl_mesh: GpuMesh, 11 | } 12 | 13 | impl Mesh { 14 | pub fn gl_mesh(&self) -> &GpuMesh { 15 | &self.gl_mesh 16 | } 17 | 18 | pub fn set_material(&self, uri: &'static str) { 19 | let material = 20 | Engine::resource_manager() 21 | .get_material(uri) 22 | .unwrap(); // TODO: Provide better panic message (or maybe DON'T PANIC!?). 23 | 24 | Engine::scene() 25 | .manager_for::() 26 | .send_message(self.entity, MeshMessage::SetMaterial(material.clone())); // TODO: Don't clone material? 27 | } 28 | 29 | pub fn material(&self) -> &Material { 30 | unimplemented!() 31 | } 32 | } 33 | 34 | impl Component for Mesh { 35 | type Manager = MeshManager; 36 | type Message = MeshMessage; 37 | } 38 | 39 | #[derive(Debug, Clone)] 40 | pub struct MeshManager(StructComponentManager); 41 | 42 | impl MeshManager { 43 | pub fn assign(&self, entity: Entity, path_text: &str) -> &Mesh { 44 | let mesh = 45 | Engine::resource_manager() 46 | .get_gpu_mesh(path_text) 47 | .ok_or_else(|| format!("ERROR: Unable to assign mesh with uri {}", path_text)) 48 | .unwrap(); // TODO: Provide better panic message (it's okay to panic here though, indicates a bug in game code). 49 | self.give_mesh(entity, mesh) 50 | } 51 | 52 | pub fn give_mesh(&self, entity: Entity, mesh: GpuMesh) -> &Mesh { 53 | self.0.assign(entity, Mesh { 54 | entity: entity, 55 | gl_mesh: mesh, 56 | }) 57 | } 58 | 59 | pub fn iter(&self) -> Iter { 60 | self.0.iter() 61 | } 62 | 63 | fn send_message(&self, entity: Entity, message: MeshMessage) { 64 | self.0.send_message(entity, message); 65 | } 66 | } 67 | 68 | impl ComponentManagerBase for MeshManager { 69 | fn update(&mut self) { 70 | self.0.process_messages(); 71 | } 72 | } 73 | 74 | impl ComponentManager for MeshManager { 75 | type Component = Mesh; 76 | 77 | fn register(builder: &mut EngineBuilder) { 78 | let mesh_manager = 79 | MeshManager(StructComponentManager::new()); 80 | builder.register_manager(mesh_manager); 81 | } 82 | 83 | fn get(&self, entity: Entity) -> Option<&Self::Component> { 84 | self.0.get(entity) 85 | } 86 | 87 | fn destroy(&self, entity: Entity) { 88 | self.0.destroy(entity); 89 | } 90 | } 91 | 92 | derive_Singleton!(MeshManager); 93 | 94 | #[derive(Debug, Clone)] 95 | pub enum MeshMessage { 96 | SetMaterial(Material), 97 | } 98 | 99 | impl Message for MeshMessage { 100 | type Target = Mesh; 101 | 102 | fn apply(self, _target: &mut Mesh) { 103 | match self { 104 | MeshMessage::SetMaterial(_) => { 105 | // target.material = RefCell::new(material); 106 | unimplemented!(); 107 | }, 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/component/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod transform; 2 | pub mod camera; 3 | pub mod mesh; 4 | pub mod light; 5 | pub mod audio; 6 | pub mod alarm; 7 | pub mod singleton_component_manager; 8 | pub mod struct_component_manager; 9 | pub mod collider; 10 | 11 | use ecs::*; 12 | use engine::*; 13 | use self::struct_component_manager::StructComponentManager; 14 | use std::boxed::FnBox; 15 | use std::fmt::Debug; 16 | use std::ops::{Deref, DerefMut}; 17 | 18 | pub use self::singleton_component_manager::SingletonComponentManager; 19 | pub use self::transform::{Transform, TransformManager}; 20 | pub use self::camera::{Camera, CameraManager}; 21 | pub use self::mesh::{Mesh, MeshManager}; 22 | pub use self::light::{Light, LightManager}; 23 | pub use self::audio::{AudioSource, AudioSourceManager, AudioSystem}; 24 | pub use self::alarm::{AlarmId, AlarmManager, alarm_update}; 25 | pub use self::collider::{Collider, ColliderManager, CollisionSystem, bounding_volume, grid_collision}; 26 | 27 | #[derive(Debug, Clone)] 28 | pub struct DefaultManager(StructComponentManager) 29 | where T: Component + Clone + Debug, 30 | T::Message: Message; 31 | 32 | impl DefaultManager 33 | where T: Component> + Clone + Debug, 34 | T::Message: Message, 35 | { 36 | pub fn new() -> DefaultManager { 37 | DefaultManager(StructComponentManager::new()) 38 | } 39 | } 40 | 41 | impl ComponentManagerBase for DefaultManager 42 | where T: Component> + Clone + Debug, 43 | T::Message: Message, 44 | { 45 | fn update(&mut self) { 46 | self.0.process_messages(); 47 | } 48 | } 49 | 50 | impl ComponentManager for DefaultManager 51 | where T: Component> + Clone + Debug, 52 | T::Message: Message, 53 | { 54 | type Component = T; 55 | 56 | fn register(builder: &mut EngineBuilder) { 57 | builder.register_manager(Self::new()); 58 | } 59 | 60 | fn get(&self, entity: Entity) -> Option<&Self::Component> { 61 | self.0.get(entity) 62 | } 63 | 64 | fn destroy(&self, entity: Entity) { 65 | self.0.destroy(entity); 66 | } 67 | } 68 | 69 | impl Deref for DefaultManager 70 | where T: Component> + Clone + Debug, 71 | T::Message: Message, 72 | { 73 | type Target = StructComponentManager; 74 | 75 | fn deref(&self) -> &StructComponentManager { 76 | &self.0 77 | } 78 | } 79 | 80 | impl DerefMut for DefaultManager 81 | where T: Component> + Clone + Debug, 82 | T::Message: Message, 83 | { 84 | fn deref_mut(&mut self) -> &mut StructComponentManager { 85 | &mut self.0 86 | } 87 | } 88 | 89 | pub struct DefaultMessage(Box) 90 | where T: Component; 91 | 92 | impl>> Message for DefaultMessage { 93 | type Target = T; 94 | 95 | fn apply(self, component: &mut T) { 96 | let inner = self.0; 97 | inner.call_once((component,)); 98 | } 99 | } 100 | 101 | impl From for DefaultMessage 102 | where T: Component, 103 | U: 'static + FnOnce(&mut T), 104 | { 105 | fn from(callback: U) -> DefaultMessage { 106 | DefaultMessage(Box::new(callback)) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/component/singleton_component_manager.rs: -------------------------------------------------------------------------------- 1 | use ecs::*; 2 | use engine::*; 3 | use std::cell::RefCell; 4 | use std::fmt::Debug; 5 | use std::ops::{Deref, DerefMut}; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct SingletonComponentManager 9 | where T: Component> + Debug + Clone + Default, 10 | T::Message: Message, 11 | { 12 | data: T, 13 | messages: RefCell>, 14 | } 15 | 16 | impl SingletonComponentManager 17 | where T: Component> + Debug + Clone + Default, 18 | T::Message: Message, 19 | { 20 | pub fn new(data: T) -> SingletonComponentManager { 21 | SingletonComponentManager { 22 | data: data, 23 | messages: RefCell::new(Vec::new()), 24 | } 25 | } 26 | 27 | pub fn send_message>(&self, message: U) { 28 | self.messages.borrow_mut().push(message.into()); 29 | } 30 | } 31 | 32 | impl Deref for SingletonComponentManager 33 | where T: Component> + Debug + Clone + Default, 34 | T::Message: Message, 35 | { 36 | type Target = T; 37 | 38 | fn deref(&self) -> &T { 39 | &self.data 40 | } 41 | } 42 | 43 | impl DerefMut for SingletonComponentManager 44 | where T: Component> + Debug + Clone + Default, 45 | T::Message: Message, 46 | { 47 | fn deref_mut(&mut self) -> &mut T { 48 | &mut self.data 49 | } 50 | } 51 | 52 | impl ComponentManagerBase for SingletonComponentManager 53 | where T: Component, Message=U> + Debug + Clone + Default, 54 | U: Message, 55 | { 56 | fn update(&mut self) { 57 | let mut messages = self.messages.borrow_mut(); 58 | for message in messages.drain(..) { 59 | message.apply(&mut self.data); 60 | } 61 | } 62 | } 63 | 64 | impl ComponentManager for SingletonComponentManager 65 | where T: Component, Message=U> + Debug + Clone + Default, 66 | U: Message, 67 | { 68 | type Component = T; 69 | 70 | fn register(builder: &mut EngineBuilder) { 71 | builder.register_manager(SingletonComponentManager::new(T::default())); 72 | } 73 | 74 | fn get(&self, _entity: Entity) -> Option<&Self::Component> { 75 | panic!("Singleton components do not need to be retreived, they can be derefenced from the manager"); 76 | } 77 | 78 | fn destroy(&self, _: Entity) {} 79 | } 80 | 81 | // ======================================= 82 | // SINGLETON COMPONENT MANAGER WITH UPDATE 83 | // ======================================= 84 | 85 | // TODO: Having a separate type for this won't be necessary once specialization is implemented. 86 | 87 | #[derive(Debug, Clone)] 88 | pub struct SingletonComponentUpdateManager 89 | where T: Component> + ComponentUpdate + Debug + Clone + Default, 90 | T::Message: Message, 91 | { 92 | data: T, 93 | messages: RefCell>, 94 | } 95 | 96 | impl SingletonComponentUpdateManager 97 | where T: Component> + ComponentUpdate + Debug + Clone + Default, 98 | T::Message: Message, 99 | { 100 | pub fn new(data: T) -> SingletonComponentUpdateManager { 101 | SingletonComponentUpdateManager { 102 | data: data, 103 | messages: RefCell::new(Vec::new()), 104 | } 105 | } 106 | 107 | pub fn send_message>(&self, message: U) { 108 | self.messages.borrow_mut().push(message.into()); 109 | } 110 | } 111 | 112 | impl Deref for SingletonComponentUpdateManager 113 | where T: Component> + ComponentUpdate + Debug + Clone + Default, 114 | T::Message: Message, 115 | { 116 | type Target = T; 117 | 118 | fn deref(&self) -> &T { 119 | &self.data 120 | } 121 | } 122 | 123 | impl DerefMut for SingletonComponentUpdateManager 124 | where T: Component> + ComponentUpdate + Debug + Clone + Default, 125 | T::Message: Message, 126 | { 127 | fn deref_mut(&mut self) -> &mut T { 128 | &mut self.data 129 | } 130 | } 131 | 132 | impl ComponentManagerBase for SingletonComponentUpdateManager 133 | where T: Component, Message=U> + ComponentUpdate + Debug + Clone + Default, 134 | U: Message, 135 | { 136 | fn update(&mut self) { 137 | let mut messages = self.messages.borrow_mut(); 138 | for message in messages.drain(..) { 139 | message.apply(&mut self.data); 140 | } 141 | self.data.update(); 142 | } 143 | } 144 | 145 | impl ComponentManager for SingletonComponentUpdateManager 146 | where T: Component, Message=U> + ComponentUpdate + Debug + Clone + Default, 147 | U: Message, 148 | { 149 | type Component = T; 150 | 151 | fn register(builder: &mut EngineBuilder) { 152 | builder.register_manager(SingletonComponentUpdateManager::new(T::default())); 153 | } 154 | 155 | fn get(&self, _entity: Entity) -> Option<&Self::Component> { 156 | panic!("Singleton components do not need to be retreived, they can be derefenced from the manager"); 157 | } 158 | 159 | fn destroy(&self, _: Entity) {} 160 | } 161 | -------------------------------------------------------------------------------- /src/input.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use bootstrap; 4 | use bootstrap::window::Message; 5 | use bootstrap::window::Message::*; 6 | use engine; 7 | 8 | pub use bootstrap::input::ScanCode; 9 | 10 | pub const MAX_SUPPORTED_MOUSE_BUTTONS: usize = 5; 11 | 12 | pub fn set_cursor(visible: bool) { 13 | bootstrap::input::set_cursor_visibility(visible); 14 | } 15 | 16 | pub fn set_capture(capture: bool) { 17 | if capture { 18 | let (top, left, bottom, right) = engine::window(|window| window.get_rect()); 19 | bootstrap::input::set_cursor_bounds(top, left, bottom, right); 20 | } else { 21 | bootstrap::input::clear_cursor_bounds(); 22 | } 23 | } 24 | 25 | #[derive(Debug, Clone)] 26 | pub struct Input { 27 | keys_pressed: HashSet, 28 | keys_released: HashSet, 29 | keys_down: HashSet, 30 | mouse_pos: (i32, i32), 31 | mouse_delta: (i32, i32), 32 | mouse_down: [bool; MAX_SUPPORTED_MOUSE_BUTTONS], 33 | mouse_pressed: [bool; MAX_SUPPORTED_MOUSE_BUTTONS], 34 | mouse_released: [bool; MAX_SUPPORTED_MOUSE_BUTTONS], 35 | mouse_scroll: i32, 36 | } 37 | 38 | impl Input { 39 | pub fn new() -> Input { 40 | Input { 41 | keys_pressed: HashSet::new(), 42 | keys_released: HashSet::new(), 43 | keys_down: HashSet::new(), 44 | mouse_pos: (400, 400), // TODO: What's up with this hard-coded garbage??? 45 | mouse_delta: (0, 0), 46 | mouse_down: [false; MAX_SUPPORTED_MOUSE_BUTTONS], 47 | mouse_pressed: [false; MAX_SUPPORTED_MOUSE_BUTTONS], 48 | mouse_released: [false; MAX_SUPPORTED_MOUSE_BUTTONS], 49 | mouse_scroll: 0, 50 | } 51 | } 52 | 53 | pub fn clear(&mut self) { 54 | self.keys_pressed.clear(); 55 | self.keys_released.clear(); 56 | self.mouse_delta = (0, 0); 57 | self.mouse_pressed = [false; MAX_SUPPORTED_MOUSE_BUTTONS]; 58 | self.mouse_released = [false; MAX_SUPPORTED_MOUSE_BUTTONS]; 59 | self.mouse_scroll = 0; 60 | } 61 | 62 | pub fn push_input(&mut self, message: Message) { 63 | match message { 64 | KeyDown(key) => { 65 | if !self.key_down(key) { 66 | self.keys_pressed.insert(key); 67 | } 68 | self.keys_down.insert(key); 69 | }, 70 | KeyUp(key) => { 71 | self.keys_released.insert(key); 72 | self.keys_down.remove(&key); 73 | }, 74 | MouseMove(x_delta, y_delta) => { 75 | self.mouse_delta = (x_delta, y_delta); 76 | }, 77 | MousePos(x_pos, y_pos) => { 78 | self.mouse_pos = (x_pos, y_pos); 79 | }, 80 | MouseButtonPressed(button) => { 81 | let index = button as usize; 82 | assert!(index < MAX_SUPPORTED_MOUSE_BUTTONS); 83 | 84 | self.mouse_down[index] = false; 85 | self.mouse_released[index] = true; 86 | }, 87 | MouseButtonReleased(button) => { 88 | let index = button as usize; 89 | assert!(index < MAX_SUPPORTED_MOUSE_BUTTONS); 90 | 91 | self.mouse_pressed[index] = true ^ self.mouse_down[index]; 92 | self.mouse_down[index] = true; 93 | }, 94 | MouseWheel(scroll_amount) => { 95 | self.mouse_scroll = scroll_amount; 96 | } 97 | _ => panic!("Unhandled message {:?} passed to Input::push_input()", message) // TODO: Don't panic? Should be unreachable in release. 98 | } 99 | } 100 | 101 | fn key_down(&self, key: ScanCode) -> bool { 102 | self.keys_down.contains(&key) 103 | } 104 | } 105 | 106 | pub fn key_down(key: ScanCode) -> bool { 107 | engine::input(|input| input.keys_down.contains(&key)) 108 | } 109 | 110 | pub fn key_pressed(key: ScanCode) -> bool { 111 | engine::input(|input| input.keys_pressed.contains(&key)) 112 | } 113 | 114 | pub fn key_released(key: ScanCode) -> bool { 115 | engine::input(|input| input.keys_released.contains(&key)) 116 | } 117 | 118 | pub fn mouse_pos() -> (i32, i32) { 119 | engine::input(|input| input.mouse_pos) 120 | } 121 | 122 | pub fn mouse_delta() -> (i32, i32) { 123 | engine::input(|input| input.mouse_delta) 124 | } 125 | 126 | pub fn mouse_button_down(button: usize) -> bool { 127 | assert!(button < MAX_SUPPORTED_MOUSE_BUTTONS); 128 | 129 | engine::input(|input| input.mouse_down[button]) 130 | } 131 | 132 | pub fn mouse_button_pressed(button: usize) -> bool { 133 | assert!(button < MAX_SUPPORTED_MOUSE_BUTTONS); 134 | 135 | engine::input(|input| input.mouse_pressed[button]) 136 | } 137 | 138 | pub fn mouse_button_released(button: usize) -> bool { 139 | assert!(button < MAX_SUPPORTED_MOUSE_BUTTONS); 140 | 141 | engine::input(|input| input.mouse_released[button]) 142 | } 143 | 144 | pub fn mouse_scroll() -> i32 { 145 | engine::input(|input| input.mouse_scroll) 146 | } 147 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Using `RawVec`, could be replaced. 2 | #![feature(alloc)] 3 | 4 | // Almost certainly going to be stabilized as-is, unlikely to break anything. 5 | #![feature(const_fn)] 6 | 7 | // The scheduler puts a `Condvar` and `Mutex` into some statics. 8 | #![feature(drop_types_in_const)] 9 | 10 | // Used by the scheduler for handling work. We might be able to remove that with some unsafe magic, 11 | // but even then being able to box a `FnOnce()` is valuable, so this is unlikely to go away. 12 | #![feature(fnbox)] 13 | 14 | // Useful when sending raw pointers between threads, could be replaced. 15 | #![feature(unique)] 16 | 17 | extern crate bootstrap_rs as bootstrap; 18 | extern crate bootstrap_audio as bs_audio; 19 | extern crate cell_extras; 20 | extern crate fiber; 21 | extern crate hash; 22 | #[macro_use] 23 | extern crate lazy_static; 24 | extern crate parse_obj as obj; 25 | extern crate polygon; 26 | 27 | pub extern crate polygon_math as math; 28 | pub extern crate stopwatch; 29 | 30 | #[macro_use] 31 | pub mod macros; 32 | 33 | pub mod camera; 34 | pub mod collections; 35 | pub mod engine; 36 | pub mod input; 37 | pub mod light; 38 | pub mod mesh_renderer; 39 | pub mod prelude; 40 | pub mod resource; 41 | pub mod scheduler; 42 | pub mod time; 43 | pub mod transform; 44 | -------------------------------------------------------------------------------- /src/light.rs: -------------------------------------------------------------------------------- 1 | use cell_extras::atomic_init_cell::AtomicInitCell; 2 | use cell_extras::atomic_ref_cell::AtomicRefCell; 3 | use engine::{self, EngineMessage}; 4 | use math::*; 5 | use polygon::light::*; 6 | use std::mem; 7 | use std::sync::Arc; 8 | 9 | // TODO: This shouldn't be fully public, only public within the crate. 10 | pub type LightInner = Arc<(AtomicInitCell, AtomicRefCell)>; 11 | 12 | #[derive(Debug)] 13 | pub struct DirectionalLight { 14 | data: LightInner, 15 | } 16 | 17 | impl DirectionalLight { 18 | pub fn new(direction: Vector3, color: Color, strength: f32) -> DirectionalLight { 19 | let light = Light::directional(direction, strength, color); 20 | let data = Arc::new((AtomicInitCell::new(), AtomicRefCell::new(light))); 21 | engine::send_message(EngineMessage::Light(data.clone())); 22 | DirectionalLight { 23 | data: data, 24 | } 25 | } 26 | 27 | pub fn forget(self) { 28 | mem::forget(self); 29 | } 30 | } 31 | 32 | #[derive(Debug)] 33 | pub struct PointLight { 34 | data: LightInner, 35 | } 36 | 37 | impl PointLight { 38 | pub fn new(radius: f32, color: Color, strength: f32) -> PointLight { 39 | let light = Light::point(radius, strength, color); 40 | let data = Arc::new((AtomicInitCell::new(), AtomicRefCell::new(light))); 41 | engine::send_message(EngineMessage::Light(data.clone())); 42 | PointLight { 43 | data: data, 44 | } 45 | } 46 | 47 | pub fn forget(self) { 48 | mem::forget(self); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! derive_Component { 3 | ($type_name: ty) => { 4 | impl $crate::ecs::Component for $type_name { 5 | type Manager = $crate::component::DefaultManager<$type_name>; 6 | type Message = $crate::component::DefaultMessage<$type_name>; 7 | } 8 | } 9 | } 10 | 11 | #[macro_export] 12 | macro_rules! derive_Singleton { 13 | ($type_name: ident) => { 14 | static mut INSTANCE: Option<*mut $type_name> = None; 15 | 16 | unsafe impl $crate::singleton::Singleton for $type_name { 17 | fn set_instance(instance: Self) { 18 | println!("setting instance"); 19 | if unsafe { INSTANCE.is_some() } { 20 | panic!("Cannot create singleton instance"); 21 | } 22 | 23 | let instance = Box::new(instance); 24 | unsafe { 25 | INSTANCE = Some(Box::into_raw(instance)); 26 | } 27 | println!("done setting instance"); 28 | } 29 | 30 | fn instance() -> &'static Self { 31 | unsafe { 32 | match INSTANCE { 33 | Some(instance) => &*instance, 34 | None => panic!("No instance found"), 35 | } 36 | } 37 | } 38 | 39 | unsafe fn destroy_instance() { 40 | if let Some(instance) = INSTANCE { 41 | Box::from_raw(instance); 42 | INSTANCE = None; 43 | } 44 | } 45 | } 46 | } 47 | } 48 | 49 | // TODO: Make this threadsafe by useing `std::sync::Once`. 50 | #[macro_export] 51 | macro_rules! warn_once { 52 | ($message: expr) => { 53 | static mut HAS_WARNED: bool = false; 54 | 55 | unsafe { 56 | if !HAS_WARNED { 57 | HAS_WARNED = true; 58 | println!($message); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/mesh_renderer.rs: -------------------------------------------------------------------------------- 1 | use engine::{self, EngineMessage}; 2 | use resource::{Mesh, MeshId}; 3 | use transform::Transform; 4 | use std::marker::PhantomData; 5 | use std::mem; 6 | 7 | #[derive(Debug)] 8 | pub struct MeshRenderer { 9 | data: *mut MeshRendererData, 10 | _phantom: PhantomData, 11 | } 12 | 13 | impl MeshRenderer { 14 | pub fn new(mesh: &Mesh, transform: &Transform) -> MeshRenderer { 15 | let mut data = Box::new(MeshRendererData { 16 | mesh_id: mesh.id(), 17 | }); 18 | 19 | let ptr = &mut *data as *mut _; 20 | 21 | engine::send_message(EngineMessage::MeshInstance(data, transform.inner())); 22 | 23 | MeshRenderer { 24 | data: ptr, 25 | _phantom: PhantomData, 26 | } 27 | } 28 | 29 | pub fn forget(self) { 30 | mem::forget(self); 31 | } 32 | } 33 | 34 | unsafe impl Send for MeshRenderer {} 35 | 36 | #[derive(Debug)] 37 | pub struct MeshRendererData { 38 | mesh_id: MeshId 39 | } 40 | 41 | impl MeshRendererData { 42 | pub fn mesh_id(&self) -> MeshId { self.mesh_id } 43 | } 44 | -------------------------------------------------------------------------------- /src/mod.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/randomPoison/gunship-rs/5a7acd32d96370990252b75d4065aa4ffb9bc379/src/mod.rs -------------------------------------------------------------------------------- /src/old/callback.rs: -------------------------------------------------------------------------------- 1 | //! Utility functionality for handling callbacks in a hotloading-compatible way. 2 | //! 3 | //! When hotloading occurs all function pointers are invalidated because all code from the old 4 | //! version of the game/engine is out of date, which means all callbacks are lost. To handle this 5 | //! we can provide a compilation-stable id to each callback and use that identify callbacks in a 6 | //! stable way. The `CallbackId` type provides this functionality, while `CallbackManager` provides 7 | //! a simple utility for associating callback ids with their concrete callback. 8 | 9 | use hash::*; 10 | use std::collections::HashMap; 11 | use std::fmt::{self, Debug}; 12 | 13 | #[cfg(not(feature="hotloading"))] 14 | #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] 15 | pub struct CallbackId(u64); 16 | 17 | #[cfg(feature="hotloading")] 18 | #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] 19 | pub struct CallbackId(String); 20 | 21 | impl CallbackId { 22 | #[cfg(not(feature="hotloading"))] 23 | pub fn of() -> CallbackId { 24 | CallbackId(unsafe { ::std::intrinsics::type_id::() }) 25 | } 26 | 27 | #[cfg(feature="hotloading")] 28 | pub fn of() -> CallbackId { 29 | CallbackId(String::from(unsafe { ::std::intrinsics::type_name::() })) 30 | } 31 | } 32 | 33 | /// Utility manager for handling callbacks in a hotloading-compatible way. 34 | pub struct CallbackManager { 35 | callbacks: HashMap, FnvHashState>, 36 | } 37 | 38 | impl CallbackManager { 39 | pub fn new() -> CallbackManager { 40 | CallbackManager { 41 | callbacks: HashMap::default(), 42 | } 43 | } 44 | 45 | pub fn register(&mut self, callback_id: CallbackId, callback: Box) { 46 | self.callbacks.insert(callback_id.clone(), callback); 47 | } 48 | 49 | pub fn get(&self, callback_id: &CallbackId) -> Option<&T> { 50 | self.callbacks 51 | .get(callback_id) 52 | .map(|box_callback| &**box_callback) // Deref from `&Box` to `&Callback`. 53 | } 54 | 55 | pub fn get_mut(&mut self, callback_id: &CallbackId) -> Option<&mut T> { 56 | self.callbacks 57 | .get_mut(callback_id) 58 | .map(|box_callback| &mut **box_callback) // Deref from `&Box` to `&Callback`. 59 | } 60 | } 61 | 62 | impl Clone for CallbackManager { 63 | fn clone(&self) -> CallbackManager { 64 | CallbackManager::new() 65 | } 66 | } 67 | 68 | impl Debug for CallbackManager { 69 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 70 | try!(write!(f, "CallbackManager {{ ")); 71 | for key in self.callbacks.keys() { 72 | try!(write!(f, "{:?} ", key)); 73 | } 74 | write!(f, "}}") 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/old/singleton.rs: -------------------------------------------------------------------------------- 1 | /// A trait defining a common interface for singleton objects. 2 | /// 3 | /// While it is rarely useful to be able to act generically over any singleton type (and it's not 4 | /// possible to create a `Singleton` trait object) the process for implementing a singleton is the 5 | /// same for all types. As such this type is useful in combination with `#[singleton]` 6 | /// which allows for a type to easily be made into a singleton. 7 | /// 8 | /// NOTE: `#[singleton]` has not yet been implemented, so for now you'll have to do it manually :^( 9 | pub unsafe trait Singleton: 'static + Sized { 10 | /// Creates the instance of the singleton. 11 | fn set_instance(instance: Self); 12 | 13 | /// Retrieves an immutable reference to the singleton instance. 14 | /// 15 | /// Only shared references to the instance can be safely retrieved. Allowing retrieval of 16 | /// mutable references would be unsafe because there's no way for Rust to statically avoid 17 | /// shared mutability. If no instance exists when this function is called the implementation 18 | /// must panic. 19 | fn instance() -> &'static Self; 20 | 21 | /// Destroys the instance of the singleton. 22 | /// 23 | /// This function is unsafe because it is not possible to statically know that there are no 24 | /// existing references to the instance. If there is the references will be dangling and will 25 | /// lead to a memory corruption. 26 | unsafe fn destroy_instance(); 27 | } 28 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use engine::EngineBuilder; 2 | -------------------------------------------------------------------------------- /src/time.rs: -------------------------------------------------------------------------------- 1 | //! Information about frame timing. 2 | //! 3 | //! By default the engine will run at 60 fps (giving a delta of 16.667 ms), but it will change 4 | //! its fixed framerate if necessary. For example, if the game fails to meet 60 fps, the engine 5 | //! will throttle down to 30 fps (with a delta of 33.333 ms) until it can return to 60 fps. The 6 | //! time delta doesn't represent the exact amount of time it took to complete the last frame, 7 | //! rather it gives the current locked framerate for the game. Therefore, game code can be 8 | //! written with the assumption of a fixed time step (i.e. the delta will be the same 9 | //! frame-to-frame) even if the exact time step may occaisonally change in practice. 10 | 11 | use std::time::Duration; 12 | 13 | /// Returns the exact time between frames. 14 | /// 15 | /// See module documentation for more information about frame timing. 16 | pub fn delta() -> Duration { 17 | Duration::new(1, 0) / 60 18 | } 19 | 20 | /// Returns the current time between frames in seconds. 21 | /// 22 | /// See module documentation for more information about frame timing. 23 | pub fn delta_f32() -> f32 { 24 | 1.0 / 60.0 25 | } 26 | --------------------------------------------------------------------------------