This commit is contained in:
Michael Zhang 2019-07-27 05:03:19 -05:00
commit 2d8a62bc86
No known key found for this signature in database
GPG key ID: 5BAEFE5D04F0CE6C
14 changed files with 1586 additions and 0 deletions

11
.editorconfig Normal file
View file

@ -0,0 +1,11 @@
root = true
[*]
end_of_file = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.py]
charset = utf-8
indent_style = space
indent_size = 4

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
**/*.rs.bk

1151
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

13
Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "wedge"
version = "0.1.0"
authors = ["Michael Zhang <iptq@protonmail.com>"]
edition = "2018"
[dependencies]
glium = "0.25"
serde = "1.0"
serde_derive = "1.0"
json5 = "0.2"
nalgebra = "0.18"
nalgebra-glm = "0.4"

25
levels/tutorial.json Normal file
View file

@ -0,0 +1,25 @@
{
"dimensions": [3, 7],
"player1": {
"position": [1, 6],
"color": [66, 134, 244],
},
"player2": {
"position": [1, 6],
"color": [244, 83, 65],
},
"goal1": [1, 0],
"goal2": [1, 0],
"blocks": [
{
"movable": true,
"push_dir": 0,
"position": [1, 3],
"color": [255, 10, 100],
"segments": [
[0, 0, 0, 0],
[1, 0, 4, 0],
],
}
]
}

7
shaders/cell.frag Normal file
View file

@ -0,0 +1,7 @@
#version 330
out vec4 color;
void main() {
color = vec4(1.0, 1.0, 1.0, 1.0);
}

10
shaders/cell.vert Normal file
View file

@ -0,0 +1,10 @@
#version 330
in vec2 point;
uniform mat4 target;
uniform mat4 projection;
void main() {
gl_Position = projection * target * vec4(point, 0.0, 1.0);
}

24
src/data.rs Normal file
View file

@ -0,0 +1,24 @@
#[derive(Debug, Deserialize)]
pub struct PlayerData {
pub position: [u32; 2],
pub color: [u32; 3],
}
#[derive(Debug, Deserialize)]
pub struct BlockData {
pub movable: bool,
pub push_dir: u32,
pub position: [u32; 2],
pub color: [u32; 3],
pub segments: Vec<[u32; 4]>,
}
#[derive(Debug, Deserialize)]
pub struct LevelData {
pub dimensions: [u32; 2],
pub player1: PlayerData,
pub player2: PlayerData,
pub goal1: [u32; 2],
pub goal2: [u32; 2],
pub blocks: Vec<BlockData>,
}

51
src/enums.rs Normal file
View file

@ -0,0 +1,51 @@
#[derive(Copy, Clone)]
pub enum Board {
Left = 0,
Right = 1,
}
impl From<u32> for Board {
fn from(n: u32) -> Self {
match n {
0 => Board::Left,
1 => Board::Right,
_ => panic!("expecting 0 or 1, got {}", n),
}
}
}
#[derive(Copy, Clone)]
pub enum MotionDir {
Horizontal,
Vertical,
}
#[derive(Copy, Clone)]
pub enum PushDir {
Up,
Down,
Left,
Right,
}
#[derive(Copy, Clone)]
pub enum Shape {
Full = 0,
TopRight = 1,
TopLeft = 2,
BottomLeft = 3,
BottomRight = 4,
}
impl From<u32> for Shape {
fn from(n: u32) -> Self {
match n {
0 => Shape::Full,
1 => Shape::TopRight,
2 => Shape::TopLeft,
3 => Shape::BottomLeft,
4 => Shape::BottomRight,
_ => panic!("expecting 0..4, got {}", n),
}
}
}

51
src/game.rs Normal file
View file

@ -0,0 +1,51 @@
use std::time::Duration;
use glium::{Display, Frame};
use crate::level::Level;
use crate::renderer::Renderer;
use crate::resources::Resources;
const CELL_VERT: &str = include_str!("../shaders/cell.vert");
const CELL_FRAG: &str = include_str!("../shaders/cell.frag");
const LEVEL_TUTORIAL: &str = include_str!("../levels/tutorial.json");
pub struct Game<'a> {
pub resources: Resources,
pub display: &'a Display,
levels: Vec<Level>,
current_level: usize,
}
impl<'a> Game<'a> {
pub fn new(display: &'a Display) -> Game {
let mut resources = Resources::default();
resources
.load_shader(display, "cell", &CELL_VERT, &CELL_FRAG)
.unwrap();
let levels = vec![Level::from_json(&LEVEL_TUTORIAL)];
Game {
resources,
display,
levels,
current_level: 0,
}
}
pub fn create_renderer<'b>(&self, target: &'b mut Frame) -> Renderer<'b, '_> {
Renderer::new(self, target)
}
pub fn get_current_level(&self) -> &Level {
self.levels.iter().nth(self.current_level).unwrap()
}
pub fn update(&mut self, delta: Duration) {}
pub fn render(&self, renderer: &mut Renderer) {
let level = self.get_current_level();
level.render(renderer);
}
}

75
src/level.rs Normal file
View file

@ -0,0 +1,75 @@
use std::collections::VecDeque;
use crate::data::LevelData;
use crate::enums::{Board, Shape};
use crate::renderer::Renderer;
use crate::{GAME_HEIGHT, GAME_WIDTH};
pub struct Level {
dimensions: (u32, u32),
move_stack: VecDeque<()>,
blocks: Vec<Block>,
}
#[derive(Clone)]
pub struct Block {
segments: Vec<Segment>,
}
#[derive(Copy, Clone)]
pub struct Segment(u32, u32, Shape, Board);
impl Level {
pub fn from_json(data: impl AsRef<str>) -> Level {
let data: LevelData = json5::from_str(data.as_ref()).unwrap();
println!("{:?}", data);
let blocks = data
.blocks
.iter()
.map(|block| {
let segments = block
.segments
.iter()
.map(|segment| {
Segment(segment[0], segment[1], segment[2].into(), segment[3].into())
})
.collect();
Block { segments }
})
.collect();
Level {
dimensions: (data.dimensions[0], data.dimensions[1]),
move_stack: VecDeque::new(),
blocks,
}
}
pub fn render(&self, renderer: &mut Renderer) {
let playfield_ratio = (2 * self.dimensions.0 + 6) as f64 / (self.dimensions.1 + 4) as f64;
let screen_ratio = GAME_WIDTH as f64 / GAME_HEIGHT as f64;
let (scale, xoff, yoff) = if playfield_ratio > screen_ratio {
let scale = GAME_WIDTH / (2 * self.dimensions.0 + 6);
let yoff = GAME_HEIGHT / 2 - (self.dimensions.1 + 4) * scale / 2;
(scale, 0, yoff)
} else {
let scale = GAME_HEIGHT / (self.dimensions.1 + 4);
let xoff = GAME_WIDTH / 2 - (2 * self.dimensions.0 + 6) * scale / 2;
(scale, xoff, 0)
};
self.render_boards(renderer, scale, (xoff, yoff));
}
fn render_boards(&self, renderer: &mut Renderer, scale: u32, offset: (u32, u32)) {
let left_off = (offset.0 + 2 * scale, offset.1 + 2 * scale);
let right_off = (
offset.0 + (4 + self.dimensions.0) * scale,
offset.1 + 2 * scale,
);
renderer.render_cell(left_off, 50, Shape::Full);
}
}

62
src/main.rs Normal file
View file

@ -0,0 +1,62 @@
#[macro_use]
extern crate glium;
extern crate nalgebra_glm as glm;
#[macro_use]
extern crate serde_derive;
mod data;
mod enums;
mod game;
mod level;
mod renderer;
mod resources;
use std::time::Instant;
use glium::glutin::{
dpi::PhysicalSize, ContextBuilder, Event, EventsLoop, WindowBuilder, WindowEvent,
};
use glium::{Display, Rect};
use crate::game::Game;
const GAME_WIDTH: u32 = 1024;
const GAME_HEIGHT: u32 = 768;
fn main() {
let mut events_loop = EventsLoop::new();
let primary_monitor = events_loop.get_primary_monitor();
let dpi_factor = primary_monitor.get_hidpi_factor();
let dimensions: PhysicalSize = (GAME_WIDTH, GAME_HEIGHT).into();
let wb = WindowBuilder::new()
.with_dimensions(dimensions.to_logical(dpi_factor))
.with_resizable(false)
.with_title("wedge");
let cb = ContextBuilder::new();
let display = Display::new(wb, cb, &events_loop).unwrap();
let game = Game::new(&display);
let mut closed = false;
let mut prev = Instant::now();
while !closed {
let now = Instant::now();
let delta = now - prev;
events_loop.poll_events(|event| match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => closed = true,
_ => (),
});
let mut target = display.draw();
let mut renderer = game.create_renderer(&mut target);
game.render(&mut renderer);
target.finish().unwrap();
prev = now;
}
}

76
src/renderer.rs Normal file
View file

@ -0,0 +1,76 @@
use glium::draw_parameters::{Blend, DrawParameters};
use glium::index::{NoIndices, PrimitiveType};
use glium::{Display, Frame, Program, Surface, VertexBuffer};
use nalgebra::Matrix4;
use crate::enums::Shape;
use crate::game::Game;
use crate::{GAME_HEIGHT, GAME_WIDTH};
pub struct Renderer<'a, 'b> {
target: &'a mut Frame,
display: &'b Display,
program: &'b Program,
}
impl<'a, 'b> Renderer<'a, 'b> {
pub fn new(game: &'b Game, target: &'a mut Frame) -> Self {
let program = game.resources.get_shader("cell").unwrap();
Renderer {
target,
display: &game.display,
program,
}
}
pub fn render_cell(&mut self, location: (u32, u32), scale: u32, shape: Shape) {
#[derive(Copy, Clone)]
struct Vertex {
point: [f32; 2],
}
implement_vertex!(Vertex, point);
let indices = NoIndices(PrimitiveType::TrianglesList);
let mut vertices = Vec::<Vertex>::new();
match shape {
Shape::Full => {
vertices.push(Vertex { point: [0.0, 0.0] });
vertices.push(Vertex { point: [1.0, 0.0] });
vertices.push(Vertex { point: [0.0, 1.0] });
vertices.push(Vertex { point: [1.0, 1.0] });
vertices.push(Vertex { point: [0.0, 1.0] });
vertices.push(Vertex { point: [1.0, 0.0] });
}
_ => {
vertices.push(Vertex { point: [0.0, 0.0] });
vertices.push(Vertex { point: [1.0, 0.0] });
vertices.push(Vertex { point: [0.0, 1.0] });
}
}
let vertex_buffer = VertexBuffer::new(self.display, &vertices).unwrap();
let projection =
glm::ortho::<f32>(0.0, GAME_WIDTH as f32, GAME_HEIGHT as f32, 0.0, -1.0, 1.0);
let mut matrix = Matrix4::<f32>::identity();
matrix = matrix.append_nonuniform_scaling(&[scale as f32, scale as f32, 1.0].into());
matrix = matrix.append_translation(&[location.0 as f32, location.1 as f32, 0.0].into());
let uniforms = uniform! {
target: *matrix.as_ref(),
projection: *projection.as_ref(),
};
self.target
.draw(
&vertex_buffer,
&indices,
&self.program,
&uniforms,
&DrawParameters {
blend: Blend::alpha_blending(),
..Default::default()
},
)
.unwrap();
}
}

28
src/resources.rs Normal file
View file

@ -0,0 +1,28 @@
use std::collections::HashMap;
use glium::{Display, Program, ProgramCreationError, Texture2d};
#[derive(Default)]
pub struct Resources {
textures: HashMap<String, Texture2d>,
shaders: HashMap<String, Program>,
}
impl Resources {
pub fn load_shader(
&mut self,
display: &Display,
name: impl AsRef<str>,
vertex: &str,
fragment: &str,
) -> Result<(), ProgramCreationError> {
let name = name.as_ref().to_owned();
let program = Program::from_source(display, vertex, fragment, None)?;
self.shaders.insert(name, program);
Ok(())
}
pub fn get_shader(&self, name: impl AsRef<str>) -> Option<&Program> {
self.shaders.get(name.as_ref())
}
}