gdritter repos outlander / 497535d
Lots more abstraction work; beginnings of some entity shit Getty Ritter 6 years ago
4 changed file(s) with 414 addition(s) and 71 deletion(s). Collapse all Expand all
1 // An `Entity` is an abstract 64-bit quantity. Nothing outside this
2 // module should care what an entity is or how it works.
3 #[derive(Debug, Copy, Clone)]
4 pub struct Entity {
5 idx: u64,
6 }
7
8 // But for here, I'm borrowing an idea from here:
9 // http://bitsquid.blogspot.com/2014/08/building-data-oriented-entity-system.html
10 // the tl;dr (and it's not explained super well there) is that we want
11 // an easy way of retiring an entity and also finding out whether an
12 // entity is retired. The way we do this involves splitting the entity
13 // into two quantities: the generation (which is just 8 bits) and the
14 // index (which is the rest.) The logic to do so is pretty simple, as
15 // below.
16 impl Entity {
17 fn new(generation: u8, index: u64) -> Entity {
18 Entity { idx: (index << 8) | generation as u64 }
19 }
20
21 fn generation(self) -> u8 {
22 (self.idx & 0xf) as u8
23 }
24
25 fn index(self) -> u64 {
26 self.idx >> 8
27 }
28 }
29
30
31 // An `EntityMgr` is the type that knows how to create and destroy
32 // entities. It ain't thread-safe now---I'll think about that
33 // later. It has two values: one of them is the `generation` vec,
34 // which will get extended as new _fresh_ values are created, and the
35 // other is the `free_indices` list, which will get extended as new
36 // values are destroyed.
37 pub struct EntityMgr {
38 generation: Vec<u8>,
39 free_indices: Vec<u64>,
40 }
41
42 impl EntityMgr {
43 // Both of these start empty, of course
44 pub fn new() -> EntityMgr {
45 EntityMgr {
46 generation: vec![],
47 free_indices: vec![],
48 }
49 }
50
51 // When we create a 'new' entity, we'll either build a fresh one,
52 // or we'll reuse an existing one. We'll only reuse an existing
53 // one if we have enough in the queue to start doing so.
54 pub fn new_entity(&mut self) -> Entity {
55 const MIN_FREE_INDICES: usize = 1024;
56 if self.free_indices.len() < MIN_FREE_INDICES {
57 // If we don't have much in the queue, then we'll start by
58 // creating a new one: its new generation value is 0, and
59 // its raw index is going to be whatever the length of the
60 // generation list is. As long as stuff gets added to the
61 // generation list each time (which it will) and nothing
62 // (or not enough) is getting freed, this will act like an
63 // auto-incrementing value. We can think of our Entity's
64 // index as (0, n).
65 self.generation.push(0);
66 Entity::new(0, (self.generation.len() - 1) as u64)
67 } else {
68 // However, if we've got a fair bit floating around the
69 // free list, then we'll want to start reusing them. We
70 // grab that index, and then _use it as an index into the
71 // generation table_ to get the generation. We've already
72 // incremented the generation table when we destroyed the
73 // previous entity, so this will also be fresh (unless it
74 // has wrapped around---in that case, the old one is
75 // definitely out of circulation because of our
76 // MIN_FREE_INDICES check!)
77 let new_idx = self.free_indices.pop().unwrap();
78 Entity::new(self.generation[new_idx as usize], new_idx)
79 }
80 }
81
82 pub fn destroy(&mut self, e: Entity) {
83 // When we destroy an Entity, we adds it index to the list of
84 // free indices, and then increment the associated generation
85 // field. This makes sure that when we check to find out
86 // whether an entity is alive, we can easily do so with an
87 // indexing and a comparison!
88 self.generation[e.index() as usize] += 1;
89 self.free_indices.push(e.index());
90 }
91
92 pub fn is_alive(&self, e: Entity) -> bool {
93 // And with that, our `is_alive` check is eays.
94 self.generation[e.index() as usize] == e.generation()
95 }
96 }
22 pub use gl::types::*;
33 pub use gl::*;
44 }
5 use glutin;
6 use std::convert;
5 use std::{convert, ptr};
6 use graphics;
77
8 // Vertex data
9 static VERTEX_DATA: [gl::GLfloat; 6] = [0.0, 0.5, 0.5, -0.5, -0.5, -0.5];
10
11 // Shader sources
12 static VS_SRC: &'static str = "
13 #version 150
14 in vec2 position;
15 void main() {
16 gl_Position = vec4(position, 0.0, 1.0);
17 }";
18
19 static FS_SRC: &'static str = "
20 #version 150
21 out vec4 out_color;
22 void main() {
23 out_color = vec4(1.0, 1.0, 1.0, 1.0);
24 }";
25
26 #[derive(Debug)]
827 pub enum Error {
9 GlutinCreation(glutin::CreationError),
10 GlutinContext(glutin::ContextError),
28 Graphics(graphics::GraphicsError),
1129 }
1230
13 impl convert::From<glutin::CreationError> for Error {
14 fn from(err: glutin::CreationError) -> Error {
15 Error::GlutinCreation(err)
16 }
17 }
18
19 impl convert::From<glutin::ContextError> for Error {
20 fn from(err: glutin::ContextError) -> Error {
21 Error::GlutinContext(err)
31 impl convert::From<graphics::GraphicsError> for Error {
32 fn from(err: graphics::GraphicsError) -> Error {
33 Error::Graphics(err)
2234 }
2335 }
2436
2537 pub fn main_loop() -> Result<(), Error> {
26 use glutin::GlContext;
38 let window = graphics::Window::create()?;
2739
28 let mut events_loop = glutin::EventsLoop::new();
29 let window = glutin::WindowBuilder::new();
30 let context = glutin::ContextBuilder::new();
31 let gl_window = glutin::GlWindow::new(window, context, &events_loop)?;
40 let vs = graphics::Shader::compile(graphics::ShaderType::Vertex, VS_SRC)?;
41 let fs = graphics::Shader::compile(graphics::ShaderType::Fragment, FS_SRC)?;
42 let program = graphics::Program::link(vec![vs, fs])?;
3243
33 unsafe { gl_window.make_current() }?;
44 let _vao = graphics::VertexArray::new();
45 let _vbo = graphics::VertexBuffer::new_array_buffer(&VERTEX_DATA);
3446
35 gl::load_with(|symbol| gl_window.get_proc_address(symbol) as *const _);
47 program.use_program();
48 program.bind_frag_data_location(0, "out_color")?;
49 let pos_attr = program.get_attrib_location("position")?;
3650
37 let mut vao = 0;
38 let mut vbo = 0;
3951 unsafe {
40 gl::GenVertexArrays(1, &mut vao);
52 gl::EnableVertexAttribArray(pos_attr as gl::GLuint);
53 gl::VertexAttribPointer(
54 pos_attr as gl::GLuint,
55 2,
56 gl::FLOAT,
57 gl::FALSE as gl::GLboolean,
58 0,
59 ptr::null(),
60 );
4161 }
62
63 window.run(|event| {
64 use glutin::{ControlFlow, Event, WindowEvent};
65
66 if let Event::WindowEvent { event, .. } = event {
67 if let WindowEvent::Closed = event {
68 return ControlFlow::Break;
69 }
70 }
71
72 unsafe {
73 gl::ClearColor(0.3, 0.3, 0.3, 1.0);
74 gl::Clear(gl::COLOR_BUFFER_BIT);
75
76 gl::DrawArrays(gl::TRIANGLES, 0, 3);
77 }
78
79 ControlFlow::Continue
80 })?;
4281
4382 Ok(())
4483 }
22 pub use gl::types::*;
33 pub use gl::*;
44 }
5 use std::{convert,ffi,ptr,string};
6
7 pub enum ShaderError {
5 use glutin;
6 use std::{convert,ffi,mem,ptr,string};
7
8 #[derive(Debug)]
9 pub enum GraphicsError {
810 BadCString,
911 InvalidShaderLog,
1012 CompileError(String),
11 }
12
13 impl convert::From<ffi::NulError> for ShaderError {
14 fn from(_: ffi::NulError) -> ShaderError {
15 ShaderError::BadCString
16 }
17 }
18
19 impl convert::From<string::FromUtf8Error> for ShaderError {
20 fn from(_: string::FromUtf8Error) -> ShaderError {
21 ShaderError::InvalidShaderLog
13 LinkError(String),
14 GlutinCreation(glutin::CreationError),
15 GlutinContext(glutin::ContextError),
16 }
17
18 impl convert::From<glutin::CreationError> for GraphicsError {
19 fn from(err: glutin::CreationError) -> GraphicsError {
20 GraphicsError::GlutinCreation(err)
21 }
22 }
23
24 impl convert::From<glutin::ContextError> for GraphicsError {
25 fn from(err: glutin::ContextError) -> GraphicsError {
26 GraphicsError::GlutinContext(err)
27 }
28 }
29
30 impl convert::From<ffi::NulError> for GraphicsError {
31 fn from(_: ffi::NulError) -> GraphicsError {
32 GraphicsError::BadCString
33 }
34 }
35
36 impl convert::From<string::FromUtf8Error> for GraphicsError {
37 fn from(_: string::FromUtf8Error) -> GraphicsError {
38 GraphicsError::InvalidShaderLog
39 }
40 }
41
42 //
43
44 pub struct Window {
45 events: glutin::EventsLoop,
46 gl_window: glutin::GlWindow,
47 }
48
49 impl Window {
50 pub fn create() -> Result<Window, GraphicsError> {
51 use glutin::GlContext;
52 let events = glutin::EventsLoop::new();
53 let window = glutin::WindowBuilder::new();
54 let context = glutin::ContextBuilder::new();
55 let gl_window = glutin::GlWindow::new(window, context, &events)?;
56
57 unsafe { gl_window.make_current() }?;
58
59 gl::load_with(|symbol| gl_window.get_proc_address(symbol) as *const _);
60
61 Ok(Window { events, gl_window })
62 }
63
64 pub fn run<F>(self, mut cb: F) -> Result<(), GraphicsError>
65 where F: FnMut(glutin::Event) -> glutin::ControlFlow
66 {
67 use glutin::GlContext;
68 let Window { mut events, gl_window } = self;
69 let mut result = Ok(());
70
71 events.run_forever(|ev| {
72 let r = cb(ev);
73
74 match gl_window.swap_buffers() {
75 Ok(()) => r,
76 Err(ctx) => {
77 result = Err(GraphicsError::GlutinContext(ctx));
78 glutin::ControlFlow::Break
79 }
80 }
81 });
82
83 result
2284 }
2385 }
2486
2890
2991 impl Drop for Shader {
3092 fn drop(&mut self) {
31 unsafe {
32 gl::DeleteShader(self.n);
33 }
34 }
35 }
36
37 pub fn compile_shader(src: &str, ty: gl::GLenum) -> Result<Shader, ShaderError> {
38 let shader;
39 unsafe {
40 shader = gl::CreateShader(ty);
41 let c_str = ffi::CString::new(src.as_bytes())?;
42 gl::ShaderSource(shader, 1, &c_str.as_ptr(), ptr::null());
43 gl::CompileShader(shader);
44
45 let mut status = gl::FALSE as gl::GLint;
46 gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
47
48 if status != (gl::TRUE as gl::GLint) {
49 let mut len = 0;
50 gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
51 let mut buf = Vec::with_capacity(len as usize);
52 buf.set_len((len as usize) - 1);
53 gl::GetShaderInfoLog(
54 shader,
55 len,
56 ptr::null_mut(),
57 buf.as_mut_ptr() as *mut gl::GLchar
93 unsafe { gl::DeleteShader(self.n); }
94 }
95 }
96
97 #[derive(PartialEq, Eq, Debug, Copy, Clone)]
98 pub enum ShaderType {
99 Fragment,
100 Vertex,
101 }
102
103 impl ShaderType {
104 fn to_glenum(&self) -> gl::GLenum {
105 match *self {
106 ShaderType::Fragment => gl::FRAGMENT_SHADER,
107 ShaderType::Vertex => gl::VERTEX_SHADER,
108 }
109 }
110 }
111
112 impl Shader {
113 pub fn compile(ty: ShaderType, src: &str) -> Result<Shader, GraphicsError> {
114 let shader;
115 unsafe {
116 shader = gl::CreateShader(ty.to_glenum());
117 let c_str = ffi::CString::new(src.as_bytes())?;
118 gl::ShaderSource(shader, 1, &c_str.as_ptr(), ptr::null());
119 gl::CompileShader(shader);
120
121 let mut status = gl::FALSE as gl::GLint;
122 gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
123
124 if status != (gl::TRUE as gl::GLint) {
125 let mut len = 0;
126 gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
127 let mut buf = Vec::with_capacity(len as usize);
128 buf.set_len((len as usize) - 1);
129 gl::GetShaderInfoLog(
130 shader,
131 len,
132 ptr::null_mut(),
133 buf.as_mut_ptr() as *mut gl::GLchar
134 );
135 return Err(GraphicsError::CompileError(String::from_utf8(buf)?));
136 }
137 }
138 Ok(Shader { n: shader })
139 }
140 }
141
142
143 //
144
145 pub struct Program {
146 pub p: gl::GLuint,
147 pub shaders: Vec<Shader>,
148 }
149
150 impl Drop for Program {
151 fn drop(&mut self) {
152 self.shaders = vec![];
153 unsafe {
154 gl::DeleteProgram(self.p)
155 }
156 }
157 }
158
159 impl Program {
160 pub fn link(shaders: Vec<Shader>) -> Result<Program, GraphicsError> {
161 unsafe {
162 let program = gl::CreateProgram();
163 for s in shaders.iter() {
164 gl::AttachShader(program, s.n);
165 }
166 gl::LinkProgram(program);
167
168 let mut status = gl::FALSE as gl::GLint;
169 gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);
170
171 // Fail on error
172 if status != (gl::TRUE as gl::GLint) {
173 let mut len: gl::GLint = 0;
174 gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
175 let mut buf = Vec::with_capacity(len as usize);
176 buf.set_len((len as usize) - 1);
177 gl::GetProgramInfoLog(
178 program,
179 len,
180 ptr::null_mut(),
181 buf.as_mut_ptr() as *mut gl::GLchar,
182 );
183 return Err(GraphicsError::LinkError(String::from_utf8(buf)?));
184 }
185
186 Ok(Program {
187 p: program,
188 shaders: shaders,
189 })
190 }
191 }
192
193 pub fn use_program(&self) {
194 unsafe { gl::UseProgram(self.p); }
195 }
196
197 pub fn bind_frag_data_location(&self, idx: u32, s: &str) -> Result<(), GraphicsError> {
198 unsafe {
199 gl::BindFragDataLocation(
200 self.p,
201 idx,
202 ffi::CString::new(s)?.as_ptr()
58203 );
59 return Err(ShaderError::CompileError(String::from_utf8(buf)?));
60 }
61 }
62 Ok(Shader { n: shader })
63 }
204 }
205 Ok(())
206 }
207
208 pub fn get_attrib_location(&self, s: &str) -> Result<i32, GraphicsError> {
209 Ok(unsafe {
210 gl::GetAttribLocation(
211 self.p,
212 ffi::CString::new(s)?.as_ptr()
213 )
214 })
215 }
216 }
217
218 //
219
220 pub struct VertexArray {
221 idx: u32,
222 }
223
224 impl Drop for VertexArray {
225 fn drop(&mut self) {
226 unsafe { gl::DeleteVertexArrays(1, &self.idx); }
227 }
228 }
229
230 pub struct VertexBuffer {
231 idx: u32,
232 }
233
234 impl Drop for VertexBuffer {
235 fn drop(&mut self) {
236 unsafe { gl::DeleteBuffers(1, &self.idx); }
237 }
238 }
239
240 impl VertexArray {
241 pub fn new() -> VertexArray {
242 let mut idx = 0;
243 unsafe {
244 gl::GenVertexArrays(1, &mut idx);
245 gl::BindVertexArray(idx);
246 }
247
248 VertexArray { idx }
249 }
250 }
251
252 impl VertexBuffer {
253 pub fn new_array_buffer(vertex_data: &[gl::GLfloat]) -> VertexBuffer {
254 let mut idx = 0;
255 unsafe {
256 gl::GenBuffers(1, &mut idx);
257 gl::BindBuffer(gl::ARRAY_BUFFER, idx);
258 gl::BufferData(
259 gl::ARRAY_BUFFER,
260 (vertex_data.len() * mem::size_of::<gl::GLfloat>()) as gl::GLsizeiptr,
261 mem::transmute(&vertex_data[0]),
262 gl::STATIC_DRAW,
263 );
264 }
265
266 VertexBuffer { idx }
267 }
268 }
11 extern crate gl;
22 extern crate glutin;
33
4 pub mod cso;
45 pub mod game;
56 pub mod graphics;
67
78 fn main() {
8 game::mainloop::main_loop();
9 println!("Hello, world!");
9 match game::mainloop::main_loop() {
10 Ok(()) => (),
11 Err(err) => println!("{:?}", err),
12 }
1013 }