|
1 use integral_geometry::{Point, Rect, Size}; |
|
2 use land2d::{Land2D}; |
|
3 use vec2d::{Vec2D}; |
|
4 |
|
5 use super::gl::{ |
|
6 Texture2D, |
|
7 Buffer, |
|
8 Shader, |
|
9 InputLayout, |
|
10 VariableBinding, |
|
11 InputElement, |
|
12 InputFormat, |
|
13 }; |
|
14 |
|
15 // TODO: temp |
|
16 const VERTEX_SHADER: &'static str = r#" |
|
17 #version 150 |
|
18 |
|
19 in vec2 Position; |
|
20 in vec3 Uv; |
|
21 |
|
22 out vec3 a_Uv; |
|
23 |
|
24 //uniform Common { |
|
25 uniform mat4 Projection; |
|
26 //}; |
|
27 |
|
28 void main() |
|
29 { |
|
30 a_Uv = Uv; |
|
31 gl_Position = Projection * vec4(Position, 0.0, 1.0); |
|
32 } |
|
33 "#; |
|
34 |
|
35 const PIXEL_SHADER: &'static str = r#" |
|
36 #version 150 |
|
37 |
|
38 in vec3 a_Uv; |
|
39 |
|
40 uniform sampler2D Texture; |
|
41 |
|
42 out vec4 Target; |
|
43 |
|
44 void main() |
|
45 { |
|
46 Target = texture2D(Texture, a_Uv.xy); |
|
47 } |
|
48 "#; |
|
49 |
|
50 pub struct MapTile { |
|
51 // either index into GL texture array or emulated [Texture; N] |
|
52 texture_index: u32, |
|
53 |
|
54 width: u32, |
|
55 height: u32, |
|
56 } |
|
57 |
|
58 #[repr(C)] |
|
59 #[derive(Copy, Clone)] |
|
60 pub struct TileVertex { |
|
61 pos: [f32; 2], |
|
62 // doesn't hurt to include another float, just in case.. |
|
63 uv: [f32; 3], |
|
64 } |
|
65 |
|
66 pub struct DrawTile { |
|
67 texture_index: u32, |
|
68 index_len: u32, |
|
69 } |
|
70 |
|
71 pub struct MapRenderer { |
|
72 tiles: Vec<MapTile>, |
|
73 textures: Vec<Texture2D>, |
|
74 |
|
75 tile_vertex_buffer: Buffer, |
|
76 tile_index_buffer: Buffer, |
|
77 tile_vertices: Vec<TileVertex>, |
|
78 tile_indices: Vec<u16>, |
|
79 tile_draw_calls: Vec<DrawTile>, |
|
80 index_offset: u16, |
|
81 tile_shader: Shader, |
|
82 tile_layout: InputLayout, |
|
83 |
|
84 tile_width: u32, |
|
85 tile_height: u32, |
|
86 num_tile_x: i32, |
|
87 } |
|
88 |
|
89 impl MapRenderer { |
|
90 pub fn new(tile_width: u32, tile_height: u32) -> Self { |
|
91 let tile_shader = Shader::new( |
|
92 VERTEX_SHADER, |
|
93 Some(PIXEL_SHADER), |
|
94 &[ |
|
95 VariableBinding::Attribute("Position", 0), |
|
96 VariableBinding::Attribute("Uv", 1), |
|
97 VariableBinding::Sampler("Texture", 0), |
|
98 ] |
|
99 ).unwrap(); |
|
100 |
|
101 let tile_layout = InputLayout::new(vec![ |
|
102 // position |
|
103 InputElement { |
|
104 shader_slot: 0, |
|
105 buffer_slot: 0, |
|
106 format: InputFormat::Float(gl::FLOAT, false), |
|
107 components: 2, |
|
108 stride: 20, |
|
109 offset: 0 |
|
110 }, |
|
111 // uv |
|
112 InputElement { |
|
113 shader_slot: 1, |
|
114 buffer_slot: 0, |
|
115 format: InputFormat::Float(gl::FLOAT, false), |
|
116 components: 3, |
|
117 stride: 20, |
|
118 offset: 8 |
|
119 }, |
|
120 ]); |
|
121 |
|
122 MapRenderer { |
|
123 tiles: Vec::new(), |
|
124 textures: Vec::new(), |
|
125 |
|
126 tile_vertex_buffer: Buffer::empty(gl::ARRAY_BUFFER, gl::DYNAMIC_DRAW), |
|
127 tile_index_buffer: Buffer::empty(gl::ELEMENT_ARRAY_BUFFER, gl::DYNAMIC_DRAW), |
|
128 tile_vertices: Vec::new(), |
|
129 tile_indices: Vec::new(), |
|
130 index_offset: 0, |
|
131 |
|
132 tile_draw_calls: Vec::new(), |
|
133 tile_shader, |
|
134 tile_layout, |
|
135 |
|
136 tile_width, |
|
137 tile_height, |
|
138 num_tile_x: 0, |
|
139 } |
|
140 } |
|
141 |
|
142 pub fn init(&mut self, land: &Vec2D<u32>) { |
|
143 // clear tiles, but keep our textures for potential re-use |
|
144 self.tiles.clear(); |
|
145 |
|
146 let tw = self.tile_width as usize; |
|
147 let th = self.tile_height as usize; |
|
148 let lw = land.width(); |
|
149 let lh = land.height(); |
|
150 let num_tile_x = lw / tw + if lw % tw != 0 { 1 } else { 0 }; |
|
151 let num_tile_y = lh / th + if lh % th != 0 { 1 } else { 0 }; |
|
152 |
|
153 self.num_tile_x = num_tile_x as i32; |
|
154 |
|
155 for y in 0..num_tile_y { |
|
156 for x in 0..num_tile_x { |
|
157 let idx = x + y * num_tile_x; |
|
158 |
|
159 let (data, stride) = { |
|
160 let bpp = 4; |
|
161 |
|
162 let offset = x * tw * bpp + y * th * lw * bpp; |
|
163 |
|
164 let data = unsafe { &land.as_bytes()[offset..] }; |
|
165 let stride = land.width(); |
|
166 |
|
167 (data, stride as u32) |
|
168 }; |
|
169 |
|
170 let texture_index = if idx >= self.textures.len() { |
|
171 let texture = Texture2D::with_data( |
|
172 data, |
|
173 stride, |
|
174 self.tile_width, |
|
175 self.tile_height, |
|
176 gl::RGBA8, |
|
177 gl::RGBA, |
|
178 gl::UNSIGNED_BYTE, |
|
179 gl::NEAREST |
|
180 ); |
|
181 |
|
182 let texture_index = self.textures.len(); |
|
183 self.textures.push(texture); |
|
184 |
|
185 texture_index |
|
186 } else { |
|
187 let texture_region = Rect::new( |
|
188 Point::new(0, 0), |
|
189 Point::new(self.tile_width as i32, self.tile_height as i32) |
|
190 ); |
|
191 |
|
192 self.textures[idx].update(texture_region, data, stride, gl::RGBA, gl::UNSIGNED_BYTE); |
|
193 idx |
|
194 }; |
|
195 |
|
196 let tile = MapTile { |
|
197 texture_index: texture_index as u32, |
|
198 |
|
199 // TODO: are there ever non-power of two textures? |
|
200 width: self.tile_width, |
|
201 height: self.tile_height, |
|
202 }; |
|
203 self.tiles.push(tile); |
|
204 } |
|
205 } |
|
206 } |
|
207 |
|
208 pub fn update(&mut self, land: &Land2D<u32>, region: Rect) { |
|
209 |
|
210 } |
|
211 |
|
212 pub fn render(&mut self, viewport: Rect) { |
|
213 self.tile_vertices.clear(); |
|
214 self.tile_indices.clear(); |
|
215 self.tile_draw_calls.clear(); |
|
216 self.index_offset = 0; |
|
217 |
|
218 for (idx, tile) in self.tiles.iter().enumerate() { |
|
219 let tile_x = idx as i32 % self.num_tile_x; |
|
220 let tile_y = idx as i32 / self.num_tile_x; |
|
221 let tile_w = self.tile_width as i32; |
|
222 let tile_h = self.tile_height as i32; |
|
223 |
|
224 let origin = Point::new(tile_x * tile_w, tile_y * tile_h); |
|
225 let tile_rect = Rect::new(origin, origin + Point::new(tile_w, tile_h)); |
|
226 |
|
227 if viewport.intersects(&tile_rect) { |
|
228 // lazy |
|
229 //dbg!(origin); |
|
230 let tile_x = origin.x as f32; |
|
231 let tile_y = origin.y as f32; |
|
232 let tile_w = tile_x + tile_w as f32; |
|
233 let tile_h = tile_y + tile_h as f32; |
|
234 let uv_depth = tile.texture_index as f32; |
|
235 |
|
236 //dbg!(tile_x); |
|
237 let tl = TileVertex { pos: [tile_x, tile_y], uv: [0f32, 0f32, uv_depth] }; |
|
238 let bl = TileVertex { pos: [tile_x, tile_h], uv: [0f32, 1f32, uv_depth] }; |
|
239 let br = TileVertex { pos: [tile_w, tile_h], uv: [1f32, 1f32, uv_depth] }; |
|
240 let tr = TileVertex { pos: [tile_w, tile_y], uv: [1f32, 0f32, uv_depth] }; |
|
241 |
|
242 self.tile_vertices.extend(&[tl, bl, br, tr]); |
|
243 |
|
244 let i = self.index_offset; |
|
245 self.tile_indices.extend(&[ |
|
246 i + 0, i + 1, i + 2, |
|
247 i + 2, i + 3, i + 0, |
|
248 ]); |
|
249 self.index_offset += 4; |
|
250 |
|
251 self.tile_draw_calls.push(DrawTile { |
|
252 texture_index: tile.texture_index, |
|
253 index_len: 6 |
|
254 }); |
|
255 } |
|
256 } |
|
257 |
|
258 self.tile_vertex_buffer.write_typed(&self.tile_vertices); |
|
259 self.tile_index_buffer.write_typed(&self.tile_indices); |
|
260 |
|
261 let _g = self.tile_layout.bind(&[ |
|
262 (0, &self.tile_vertex_buffer) |
|
263 ], Some(&self.tile_index_buffer)); |
|
264 |
|
265 let ortho = { |
|
266 let l = viewport.left() as f32; |
|
267 let r = viewport.right() as f32; |
|
268 let b = viewport.bottom() as f32; |
|
269 let t = viewport.top() as f32; |
|
270 |
|
271 [ |
|
272 2f32 / (r - l), 0f32, 0f32, 0f32, |
|
273 0f32, 2f32 / (t - b), 0f32, 0f32, |
|
274 0f32, 0f32, 0.5f32, 0f32, |
|
275 (r + l) / (l - r), (t + b) / (b - t), 0.5f32, 1f32, |
|
276 ] |
|
277 }; |
|
278 |
|
279 self.tile_shader.bind(); |
|
280 self.tile_shader.set_matrix("Projection", ortho.as_ptr()); |
|
281 |
|
282 let mut draw_offset = 0; |
|
283 for draw_call in &self.tile_draw_calls { |
|
284 unsafe { |
|
285 self.tile_shader.bind_texture_2d(0, &self.textures[draw_call.texture_index as usize]); |
|
286 |
|
287 gl::DrawElements( |
|
288 gl::TRIANGLES, |
|
289 draw_call.index_len as i32, |
|
290 gl::UNSIGNED_SHORT, |
|
291 draw_offset as *const _ |
|
292 ); |
|
293 } |
|
294 |
|
295 draw_offset += draw_call.index_len * 2; |
|
296 } |
|
297 } |
|
298 } |
|
299 |
|
300 |