Lots more abstraction work; beginnings of some entity shit
Getty Ritter
7 years ago
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 | } |
2 | 2 | pub use gl::types::*; |
3 | 3 | pub use gl::*; |
4 | 4 | } |
5 | use glutin; | |
6 | use std::convert; | |
5 | use std::{convert, ptr}; | |
6 | use graphics; | |
7 | 7 | |
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)] | |
8 | 27 | pub enum Error { |
9 | GlutinCreation(glutin::CreationError), | |
10 | GlutinContext(glutin::ContextError), | |
28 | Graphics(graphics::GraphicsError), | |
11 | 29 | } |
12 | 30 | |
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) | |
22 | 34 | } |
23 | 35 | } |
24 | 36 | |
25 | 37 | pub fn main_loop() -> Result<(), Error> { |
26 |
|
|
38 | let window = graphics::Window::create()?; | |
27 | 39 | |
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])?; | |
32 | 43 | |
33 |
|
|
44 | let _vao = graphics::VertexArray::new(); | |
45 | let _vbo = graphics::VertexBuffer::new_array_buffer(&VERTEX_DATA); | |
34 | 46 | |
35 |
|
|
47 | program.use_program(); | |
48 | program.bind_frag_data_location(0, "out_color")?; | |
49 | let pos_attr = program.get_attrib_location("position")?; | |
36 | 50 | |
37 | let mut vao = 0; | |
38 | let mut vbo = 0; | |
39 | 51 | unsafe { |
40 |
gl:: |
|
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 | ); | |
41 | 61 | } |
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 | })?; | |
42 | 81 | |
43 | 82 | Ok(()) |
44 | 83 | } |
2 | 2 | pub use gl::types::*; |
3 | 3 | pub use gl::*; |
4 | 4 | } |
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 { | |
8 | 10 | BadCString, |
9 | 11 | InvalidShaderLog, |
10 | 12 | 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 |
|
|
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 | |
22 | 84 | } |
23 | 85 | } |
24 | 86 | |
28 | 90 | |
29 | 91 | impl Drop for Shader { |
30 | 92 | 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 |
|
|
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() | |
58 | 203 | ); |
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 | } |