--- a/rust/hwphysics/src/collision.rs Thu Mar 21 01:23:05 2019 +0300
+++ b/rust/hwphysics/src/collision.rs Fri Mar 22 18:01:08 2019 +0200
@@ -125,4 +125,4 @@
fn add(&mut self, gear_id: GearId, gear_data: CollisionData) {
self.grid.insert_static(gear_id, &gear_data.bounds);
}
-}
\ No newline at end of file
+}
--- a/rust/hwrunner/Cargo.toml Thu Mar 21 01:23:05 2019 +0300
+++ b/rust/hwrunner/Cargo.toml Fri Mar 22 18:01:08 2019 +0200
@@ -5,8 +5,9 @@
edition = "2018"
[dependencies]
-gfx = "0.17"
-glutin = "0.18"
-gfx_window_glutin = "0.26"
+#gfx = "0.17"
+glutin = "0.20"
+gl = "0.11"
+#gfx_window_glutin = "0.26"
lib-hedgewars-engine = { path = "../lib-hedgewars-engine" }
--- a/rust/hwrunner/src/main.rs Thu Mar 21 01:23:05 2019 +0300
+++ b/rust/hwrunner/src/main.rs Fri Mar 22 18:01:08 2019 +0200
@@ -1,24 +1,21 @@
use glutin::{
- dpi::LogicalSize,
+ dpi,
Event,
WindowEvent,
+ DeviceEvent,
+ ElementState,
+ MouseButton,
+ MouseScrollDelta,
EventsLoop,
- GlWindow,
- GlContext
+ WindowedContext,
+ GlRequest,
+ GlProfile,
+ ContextTrait,
};
-use gfx::{
- texture,
- format,
- Encoder,
- Device
-};
-
-use gfx_window_glutin::init_existing;
-
use hedgewars_engine::instance::EngineInstance;
-fn init(event_loop: &EventsLoop, size: LogicalSize) -> GlWindow {
+fn init(event_loop: &EventsLoop, size: dpi::LogicalSize) -> WindowedContext {
use glutin::{
ContextBuilder,
WindowBuilder
@@ -28,41 +25,96 @@
.with_title("hwengine")
.with_dimensions(size);
- let context = ContextBuilder::new();
- GlWindow::new(window, context, event_loop).unwrap()
+ let cxt = ContextBuilder::new()
+ .with_gl(GlRequest::Latest)
+ .with_gl_profile(GlProfile::Core)
+ .build_windowed(window, &event_loop).ok().unwrap();
+
+ unsafe {
+ cxt.make_current().unwrap();
+ gl::load_with(|ptr| cxt.get_proc_address(ptr) as *const _);
+
+ if let Some(sz) = cxt.get_inner_size() {
+ let phys = sz.to_physical(cxt.get_hidpi_factor());
+
+ gl::Viewport(0, 0, phys.width as i32, phys.height as i32);
+ }
+ }
+
+ cxt
}
fn main() {
let mut event_loop = EventsLoop::new();
- let window = init(&event_loop, LogicalSize::new(1024.0, 768.0));
+ let (w, h) = (1024.0, 768.0);
+ let window = init(&event_loop, dpi::LogicalSize::new(w, h));
- let (mut device, mut factory, color_view, depth_view) =
- init_existing::<format::Rgba8, format::Depth>(&window);
+ let mut engine = EngineInstance::new();
- let mut encoder: Encoder<_, _> = factory.create_command_buffer().into();
+ // dirty dirty code follows; DO NOT USE
+ let mut zoom = 1f32;
+ let mut dragging = false;
+ let mut x = 0f32;
+ let mut y = 0f32;
- let engine = EngineInstance::new();
+ use std::time::Instant;
+ let mut now = Instant::now();
+
let mut is_running = true;
while is_running {
+ let curr = Instant::now();
+ let delta = curr - now;
+ now = curr;
+ let ms = delta.as_secs() as f64 * 1000.0 + delta.subsec_millis() as f64;
+ window.set_title(&format!("hwengine {:.3}ms", ms));
+
event_loop.poll_events(|event| {
match event {
Event::WindowEvent { event, ..} => match event {
WindowEvent::CloseRequested => {
is_running = false;
},
+ WindowEvent::MouseInput { button, state, .. } => {
+ if let MouseButton::Right = button {
+ if let ElementState::Pressed = state {
+ dragging = true;
+ } else {
+ dragging = false;
+ }
+ }
+ }
+ WindowEvent::MouseWheel { delta, .. } => {
+ match delta {
+ MouseScrollDelta::LineDelta(x, y) => {
+ zoom += y as f32 * 0.1f32;
+ }
+ MouseScrollDelta::PixelDelta(delta) => {
+ let physical = delta.to_physical(window.get_hidpi_factor());
+ zoom += physical.y as f32 * 0.1f32;
+ }
+ }
+ }
_ => ()
},
+ Event::DeviceEvent { event, .. } => match event {
+ DeviceEvent::MouseMotion { delta } => {
+ if dragging {
+ x -= delta.0 as f32;
+ y -= delta.1 as f32;
+ }
+ }
+ _ => {}
+ }
_ => ()
}
});
- encoder.clear(&color_view, [0.5, 0.0, 0.0, 1.0]);
- engine.render(&mut encoder, &color_view);
+ unsafe { window.make_current().unwrap() };
- encoder.flush(&mut device);
+ // temporary params.. dont actually handle input here
+ engine.render(x, y, w as f32 * zoom, h as f32 * zoom);
window.swap_buffers().unwrap();
- device.cleanup();
}
}
--- a/rust/land2d/src/lib.rs Thu Mar 21 01:23:05 2019 +0300
+++ b/rust/land2d/src/lib.rs Fri Mar 22 18:01:08 2019 +0200
@@ -30,6 +30,12 @@
&self.pixels.as_slice()
}
+ pub fn raw_pixel_bytes(&self) -> &[u8] {
+ unsafe {
+ self.pixels.as_bytes()
+ }
+ }
+
#[inline]
pub fn width(&self) -> usize {
self.pixels.width()
--- a/rust/lib-hedgewars-engine/Cargo.toml Thu Mar 21 01:23:05 2019 +0300
+++ b/rust/lib-hedgewars-engine/Cargo.toml Fri Mar 22 18:01:08 2019 +0200
@@ -5,8 +5,9 @@
edition = "2018"
[dependencies]
-gfx = "0.17"
-gfx_device_gl = "0.15"
+gl = "0.11"
+#gfx = "0.17"
+#gfx_device_gl = "0.15"
netbuf = "0.4"
fpnum = { path = "../fpnum" }
@@ -16,6 +17,8 @@
landgen = { path = "../landgen" }
hedgewars-engine-messages = { path = "../hedgewars-engine-messages" }
hwphysics = { path = "../hwphysics" }
+mapgen = { path = "../mapgen" }
+vec2d = { path = "../vec2d" }
[lib]
name = "hedgewars_engine"
--- a/rust/lib-hedgewars-engine/src/instance.rs Thu Mar 21 01:23:05 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/instance.rs Fri Mar 22 18:01:08 2019 +0200
@@ -3,45 +3,43 @@
UnorderedEngineMessage::*, UnsyncedEngineMessage::*, *,
};
-use self::gfx_gl::{CommandBuffer, Resources};
-use gfx::format::{Unorm, D24, R8_G8_B8_A8};
-use gfx_device_gl as gfx_gl;
+use landgen::outline_template::OutlineTemplate;
+use integral_geometry::{Point, Rect, Size};
use super::{ipc::IPC, world::World};
-pub struct EngineGlContext {
- pub device: gfx_gl::Device,
- pub factory: gfx_gl::Factory,
- pub render_target: gfx::handle::RenderTargetView<Resources, (R8_G8_B8_A8, Unorm)>,
- pub depth_buffer: gfx::handle::DepthStencilView<Resources, (D24, Unorm)>,
- pub command_buffer: gfx::Encoder<Resources, CommandBuffer>,
-}
-
pub struct EngineInstance {
pub world: World,
pub ipc: IPC,
- pub gl_context: Option<EngineGlContext>,
}
impl EngineInstance {
pub fn new() -> Self {
- let world = World::new();
+ let mut world = World::new();
+
+ fn template() -> OutlineTemplate {
+ let mut template = OutlineTemplate::new(Size::new(4096*1, 2048*1));
+ template.islands = vec![vec![
+ Rect::from_size_coords(100, 2050, 1, 1),
+ Rect::from_size_coords(100, 500, 400, 1200),
+ Rect::from_size_coords(3600, 500, 400, 1200),
+ Rect::from_size_coords(3900, 2050, 1, 1),
+ ]];
+ template.fill_points = vec![Point::new(1, 0)];
+
+ template
+ }
+
+ world.init(template());
+
Self {
world,
ipc: IPC::new(),
- gl_context: None,
}
}
- pub fn render<R, C>(
- &self,
- command_buffer: &mut gfx::Encoder<R, C>,
- render_target: &gfx::handle::RenderTargetView<R, gfx::format::Rgba8>,
- ) where
- R: gfx::Resources,
- C: gfx::CommandBuffer<R>,
- {
- command_buffer.clear(render_target, [0.0, 0.5, 0.0, 1.0]);
+ pub fn render(&mut self, x: f32, y: f32, w: f32, h: f32) {
+ self.world.render(x, y, w, h);
}
fn process_unordered_message(&mut self, message: &UnorderedEngineMessage) {
--- a/rust/lib-hedgewars-engine/src/lib.rs Thu Mar 21 01:23:05 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/lib.rs Fri Mar 22 18:01:08 2019 +0200
@@ -3,7 +3,6 @@
mod render;
mod world;
-use gfx::{format::Formatted, Encoder};
use std::{
ffi::CString,
io::{Read, Write},
@@ -11,9 +10,7 @@
os::raw::{c_char, c_void},
};
-use gfx_device_gl as gfx_gl;
-
-use self::instance::{EngineGlContext, EngineInstance};
+use self::instance::{EngineInstance};
#[repr(C)]
#[derive(Copy, Clone)]
@@ -84,36 +81,11 @@
height: u16,
gl_loader: extern "C" fn(*const c_char) -> *const c_void,
) {
- let (device, mut factory) = gfx_gl::create(|name| {
- let c_name = CString::new(name).unwrap();
- gl_loader(c_name.as_ptr())
- });
-
- let dimensions = (width, height, 1u16, gfx::texture::AaMode::Single);
- let (render_target, depth_buffer) = gfx_gl::create_main_targets_raw(
- dimensions,
- gfx::format::Rgba8::get_format().0,
- gfx::format::Depth::get_format().0,
- );
-
- let mut command_buffer: Encoder<_, _> = factory.create_command_buffer().into();
-
- engine_state.gl_context = Some(EngineGlContext {
- device,
- factory,
- render_target: gfx::memory::Typed::new(render_target),
- depth_buffer: gfx::memory::Typed::new(depth_buffer),
- command_buffer,
- })
}
#[no_mangle]
pub extern "C" fn render_frame(engine_state: &mut EngineInstance) {
- let mut context = replace(&mut engine_state.gl_context, None);
- if let Some(ref mut c) = context {
- engine_state.render(&mut c.command_buffer, &mut c.render_target)
- }
- replace(&mut engine_state.gl_context, context);
+ //engine_state.render()
}
#[no_mangle]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/lib-hedgewars-engine/src/render/gl.rs Fri Mar 22 18:01:08 2019 +0200
@@ -0,0 +1,419 @@
+ use integral_geometry::Rect;
+
+use std::{
+ mem,
+ slice,
+ ptr,
+ ffi,
+ ffi::CString,
+};
+
+#[derive(Debug)]
+pub struct Texture2D {
+ pub handle: u32,
+}
+
+impl Drop for Texture2D {
+ fn drop(&mut self) {
+ if self.handle != 0 {
+ unsafe {
+ gl::DeleteTextures(1, &self.handle);
+ }
+ }
+ }
+}
+
+impl Texture2D {
+ pub fn with_data(
+ data: &[u8],
+ data_stride: u32,
+ width: u32,
+ height: u32,
+ internal_format: u32,
+ format: u32,
+ ty: u32,
+ filter: u32
+ ) -> Self {
+ let mut handle = 0;
+
+ unsafe {
+ gl::GenTextures(1, &mut handle);
+
+ gl::BindTexture(gl::TEXTURE_2D, handle);
+ gl::PixelStorei(gl::UNPACK_ROW_LENGTH, data_stride as i32);
+ gl::TexImage2D(
+ gl::TEXTURE_2D,
+ 0,
+ internal_format as i32,
+ width as i32,
+ height as i32,
+ 0,
+ format as u32,
+ ty,
+ data.as_ptr() as *const _
+ );
+
+ gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
+ gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
+ gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, filter as i32);
+ gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, filter as i32);
+ }
+
+ Texture2D {
+ handle
+ }
+ }
+
+ pub fn update(&self, region: Rect, data: &[u8], data_stride: u32, format: u32, ty: u32) {
+ unsafe {
+ gl::BindTexture(gl::TEXTURE_2D, self.handle);
+ gl::PixelStorei(gl::UNPACK_ROW_LENGTH, data_stride as i32);
+ gl::TexSubImage2D(
+ gl::TEXTURE_2D,
+ 0, // texture level
+ region.left(), // texture region
+ region.top(),
+ region.width() as i32 - 1,
+ region.height() as i32 - 1,
+ format, // data format
+ ty, // data type
+ data.as_ptr() as *const _, // data ptr
+ );
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct Buffer {
+ pub handle: u32,
+ pub ty: u32,
+ pub usage: u32,
+}
+
+impl Buffer {
+ pub fn empty(
+ ty: u32,
+ usage: u32,
+ //size: isize
+ ) -> Buffer {
+ let mut buffer = 0;
+
+ unsafe {
+ gl::GenBuffers(1, &mut buffer);
+ gl::BindBuffer(ty, buffer);
+ //gl::BufferData(ty, size, ptr::null_mut(), usage);
+ }
+
+ Buffer {
+ handle: buffer,
+ ty,
+ usage,
+ }
+ }
+
+ fn with_data(
+ ty: u32,
+ usage: u32,
+ data: &[u8]
+ ) -> Buffer {
+ let mut buffer = 0;
+
+ unsafe {
+ gl::GenBuffers(1, &mut buffer);
+ gl::BindBuffer(ty, buffer);
+ gl::BufferData(ty, data.len() as isize, data.as_ptr() as _, usage);
+ }
+
+ Buffer {
+ handle: buffer,
+ ty,
+ usage
+ }
+ }
+
+ pub fn ty(&self) -> u32 {
+ self.ty
+ }
+
+ pub fn handle(&self) -> u32 {
+ self.handle
+ }
+
+ pub fn write_typed<T>(&self, data: &[T]) {
+ unsafe {
+ let data = slice::from_raw_parts(
+ data.as_ptr() as *const u8,
+ data.len() * mem::size_of::<T>(),
+ );
+
+ gl::BindBuffer(self.ty, self.handle);
+ gl::BufferData(self.ty, data.len() as isize, data.as_ptr() as *const _ as *const _, self.usage);
+ }
+ }
+
+ pub fn write(&self, data: &[u8]) {
+ unsafe {
+ gl::BindBuffer(self.ty, self.handle);
+ gl::BufferData(self.ty, data.len() as isize, data.as_ptr() as *const _ as *const _, self.usage);
+ }
+ }
+}
+
+impl Drop for Buffer {
+ fn drop(&mut self) {
+ unsafe {
+ gl::DeleteBuffers(1, &self.handle);
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum VariableBinding<'a> {
+ Attribute(&'a str, u32),
+ Uniform(&'a str, u32),
+ UniformBlock(&'a str, u32),
+ Sampler(&'a str, u32),
+}
+
+#[derive(Debug)]
+pub struct Shader {
+ pub program: u32,
+}
+
+impl Drop for Shader {
+ fn drop(&mut self) {
+ unsafe {
+ gl::DeleteProgram(self.program);
+ }
+ }
+}
+
+impl Shader {
+ pub fn new<'a>(
+ vs: &str,
+ ps: Option<&str>,
+ bindings: &[VariableBinding<'a>]
+ ) -> Result<Self, String> {
+ unsafe fn compile_shader(ty: u32, shdr: &str) -> Result<u32, String> {
+ let shader = gl::CreateShader(ty);
+ let len = shdr.len() as i32;
+ let shdr = shdr.as_ptr() as *const i8;
+ gl::ShaderSource(shader, 1, &shdr, &len);
+ gl::CompileShader(shader);
+
+ let mut success = 0i32;
+ gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success as _);
+
+ if success == gl::FALSE as i32 {
+ let mut log_size = 0i32;
+ gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut log_size as _);
+
+ let mut log = vec![0u8; log_size as usize];
+ gl::GetShaderInfoLog(shader, log_size, ptr::null_mut(), log.as_mut_ptr() as _);
+
+ gl::DeleteShader(shader);
+ Err(String::from_utf8_unchecked(log))
+ } else {
+ Ok(shader)
+ }
+ }
+
+ let vs = unsafe { compile_shader(gl::VERTEX_SHADER, vs)? };
+ let ps = if let Some(ps) = ps {
+ Some(unsafe { compile_shader(gl::FRAGMENT_SHADER, ps)? })
+ } else {
+ None
+ };
+
+ unsafe {
+ let program = gl::CreateProgram();
+
+ gl::AttachShader(program, vs);
+ if let Some(ps) = ps {
+ gl::AttachShader(program, ps);
+ }
+
+ for bind in bindings {
+ match bind {
+ &VariableBinding::Attribute(ref name, id) => {
+ let c_str = CString::new(name.as_bytes()).unwrap();
+ gl::BindAttribLocation(program, id, c_str.to_bytes_with_nul().as_ptr() as *const _);
+ },
+ _ => {}
+ }
+ }
+
+ gl::LinkProgram(program);
+
+ let mut success = 0i32;
+ gl::GetProgramiv(program, gl::LINK_STATUS, &mut success);
+ if success == gl::FALSE as i32 {
+ let mut log_size = 0i32;
+ gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut log_size as _);
+
+ let mut log = vec![0u8; log_size as usize];
+ gl::GetProgramInfoLog(program, log_size, ptr::null_mut(), log.as_mut_ptr() as _);
+
+ gl::DeleteProgram(program);
+ return Err(String::from_utf8_unchecked(log));
+ }
+
+ //gl::DetachShader(program, vs);
+ if let Some(ps) = ps {
+ //gl::DetachShader(program, ps);
+ }
+
+ gl::UseProgram(program);
+
+ // after linking we setup sampler bindings as specified in the shader
+ for bind in bindings {
+ match bind {
+ VariableBinding::Uniform(name, id) => {
+ let c_str = CString::new(name.as_bytes()).unwrap();
+ let index = gl::GetUniformLocation(program, c_str.to_bytes_with_nul().as_ptr() as *const _);
+
+ // TODO: impl for block?
+ },
+ VariableBinding::UniformBlock(name, id) => {
+ let c_str = CString::new(name.as_bytes()).unwrap();
+ let index = gl::GetUniformBlockIndex(program, c_str.to_bytes_with_nul().as_ptr() as *const _);
+
+ gl::UniformBlockBinding(program, index, *id);
+ }
+ VariableBinding::Sampler(name, id) => {
+ let c_str = CString::new(name.as_bytes()).unwrap();
+ let index = gl::GetUniformLocation(program, c_str.to_bytes_with_nul().as_ptr() as *const _);
+
+ gl::Uniform1i(index, *id as i32);
+ },
+ _ => {}
+ }
+ }
+
+ Ok(Shader {
+ program
+ })
+ }
+ }
+
+ pub fn bind(&self) {
+ unsafe {
+ gl::UseProgram(self.program);
+ }
+ }
+
+ pub fn set_matrix(&self, name: &str, matrix: *const f32) {
+ unsafe {
+ let c_str = CString::new(name).unwrap();
+ let index = gl::GetUniformLocation(self.program, c_str.to_bytes_with_nul().as_ptr() as *const _);
+
+ gl::UniformMatrix4fv(index, 1, gl::FALSE, matrix);
+ }
+ }
+
+ pub fn bind_texture_2d(&self, index: u32, texture: &Texture2D) {
+ self.bind();
+
+ unsafe {
+ gl::ActiveTexture(gl::TEXTURE0 + index);
+ gl::BindTexture(gl::TEXTURE_2D, texture.handle);
+ }
+ }
+}
+
+pub enum InputFormat {
+ Float(u32, bool),
+ Integer(u32),
+}
+
+pub struct InputElement {
+ pub shader_slot: u32,
+ pub buffer_slot: u32,
+ pub format: InputFormat,
+ pub components: u32,
+ pub stride: u32,
+ pub offset: u32,
+}
+
+// TODO:
+pub struct InputLayout {
+ pub elements: Vec<InputElement>,
+}
+
+pub struct LayoutGuard {
+ vao: u32
+}
+
+impl Drop for LayoutGuard {
+ fn drop(&mut self) {
+ unsafe {
+ gl::DeleteVertexArrays(1, [self.vao].as_ptr());
+ gl::BindVertexArray(0);
+ }
+ }
+}
+
+impl InputLayout {
+ pub fn new(elements: Vec<InputElement>) -> Self {
+ InputLayout {
+ elements,
+ }
+ }
+
+ pub fn bind(&mut self, buffers: &[(u32, &Buffer)], index_buffer: Option<&Buffer>) -> LayoutGuard {
+ let mut vao = 0;
+
+ unsafe {
+ gl::GenVertexArrays(1, &mut vao);
+ gl::BindVertexArray(vao);
+ }
+
+ for &(slot, ref buffer) in buffers {
+ unsafe {
+ gl::BindBuffer(buffer.ty(), buffer.handle());
+ }
+
+ for attr in self.elements.iter().filter(|a| a.buffer_slot == slot) {
+ unsafe {
+ gl::EnableVertexAttribArray(attr.shader_slot);
+ match attr.format {
+ InputFormat::Float(fmt, normalized) => {
+ gl::VertexAttribPointer(
+ attr.shader_slot,
+ attr.components as i32,
+ fmt,
+ if normalized {
+ gl::TRUE
+ } else {
+ gl::FALSE
+ },
+ attr.stride as i32,
+ attr.offset as *const _
+ );
+ }
+ InputFormat::Integer(fmt) => {
+ gl::VertexAttribIPointer(
+ attr.shader_slot,
+ attr.components as i32,
+ fmt,
+ attr.stride as i32,
+ attr.offset as *const _
+ );
+ }
+ }
+
+ }
+ }
+ }
+
+ if let Some(buf) = index_buffer {
+ unsafe {
+ gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, buf.handle());
+ }
+ }
+
+ LayoutGuard {
+ vao
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/lib-hedgewars-engine/src/render/map.rs Fri Mar 22 18:01:08 2019 +0200
@@ -0,0 +1,300 @@
+use integral_geometry::{Point, Rect, Size};
+use land2d::{Land2D};
+use vec2d::{Vec2D};
+
+use super::gl::{
+ Texture2D,
+ Buffer,
+ Shader,
+ InputLayout,
+ VariableBinding,
+ InputElement,
+ InputFormat,
+};
+
+// TODO: temp
+const VERTEX_SHADER: &'static str = r#"
+#version 150
+
+in vec2 Position;
+in vec3 Uv;
+
+out vec3 a_Uv;
+
+//uniform Common {
+uniform mat4 Projection;
+//};
+
+void main()
+{
+ a_Uv = Uv;
+ gl_Position = Projection * vec4(Position, 0.0, 1.0);
+}
+"#;
+
+const PIXEL_SHADER: &'static str = r#"
+#version 150
+
+in vec3 a_Uv;
+
+uniform sampler2D Texture;
+
+out vec4 Target;
+
+void main()
+{
+ Target = texture2D(Texture, a_Uv.xy);
+}
+"#;
+
+pub struct MapTile {
+ // either index into GL texture array or emulated [Texture; N]
+ texture_index: u32,
+
+ width: u32,
+ height: u32,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct TileVertex {
+ pos: [f32; 2],
+ // doesn't hurt to include another float, just in case..
+ uv: [f32; 3],
+}
+
+pub struct DrawTile {
+ texture_index: u32,
+ index_len: u32,
+}
+
+pub struct MapRenderer {
+ tiles: Vec<MapTile>,
+ textures: Vec<Texture2D>,
+
+ tile_vertex_buffer: Buffer,
+ tile_index_buffer: Buffer,
+ tile_vertices: Vec<TileVertex>,
+ tile_indices: Vec<u16>,
+ tile_draw_calls: Vec<DrawTile>,
+ index_offset: u16,
+ tile_shader: Shader,
+ tile_layout: InputLayout,
+
+ tile_width: u32,
+ tile_height: u32,
+ num_tile_x: i32,
+}
+
+impl MapRenderer {
+ pub fn new(tile_width: u32, tile_height: u32) -> Self {
+ let tile_shader = Shader::new(
+ VERTEX_SHADER,
+ Some(PIXEL_SHADER),
+ &[
+ VariableBinding::Attribute("Position", 0),
+ VariableBinding::Attribute("Uv", 1),
+ VariableBinding::Sampler("Texture", 0),
+ ]
+ ).unwrap();
+
+ let tile_layout = InputLayout::new(vec![
+ // position
+ InputElement {
+ shader_slot: 0,
+ buffer_slot: 0,
+ format: InputFormat::Float(gl::FLOAT, false),
+ components: 2,
+ stride: 20,
+ offset: 0
+ },
+ // uv
+ InputElement {
+ shader_slot: 1,
+ buffer_slot: 0,
+ format: InputFormat::Float(gl::FLOAT, false),
+ components: 3,
+ stride: 20,
+ offset: 8
+ },
+ ]);
+
+ MapRenderer {
+ tiles: Vec::new(),
+ textures: Vec::new(),
+
+ tile_vertex_buffer: Buffer::empty(gl::ARRAY_BUFFER, gl::DYNAMIC_DRAW),
+ tile_index_buffer: Buffer::empty(gl::ELEMENT_ARRAY_BUFFER, gl::DYNAMIC_DRAW),
+ tile_vertices: Vec::new(),
+ tile_indices: Vec::new(),
+ index_offset: 0,
+
+ tile_draw_calls: Vec::new(),
+ tile_shader,
+ tile_layout,
+
+ tile_width,
+ tile_height,
+ num_tile_x: 0,
+ }
+ }
+
+ pub fn init(&mut self, land: &Vec2D<u32>) {
+ // clear tiles, but keep our textures for potential re-use
+ self.tiles.clear();
+
+ let tw = self.tile_width as usize;
+ let th = self.tile_height as usize;
+ let lw = land.width();
+ let lh = land.height();
+ let num_tile_x = lw / tw + if lw % tw != 0 { 1 } else { 0 };
+ let num_tile_y = lh / th + if lh % th != 0 { 1 } else { 0 };
+
+ self.num_tile_x = num_tile_x as i32;
+
+ for y in 0..num_tile_y {
+ for x in 0..num_tile_x {
+ let idx = x + y * num_tile_x;
+
+ let (data, stride) = {
+ let bpp = 4;
+
+ let offset = x * tw * bpp + y * th * lw * bpp;
+
+ let data = unsafe { &land.as_bytes()[offset..] };
+ let stride = land.width();
+
+ (data, stride as u32)
+ };
+
+ let texture_index = if idx >= self.textures.len() {
+ let texture = Texture2D::with_data(
+ data,
+ stride,
+ self.tile_width,
+ self.tile_height,
+ gl::RGBA8,
+ gl::RGBA,
+ gl::UNSIGNED_BYTE,
+ gl::NEAREST
+ );
+
+ let texture_index = self.textures.len();
+ self.textures.push(texture);
+
+ texture_index
+ } else {
+ let texture_region = Rect::new(
+ Point::new(0, 0),
+ Point::new(self.tile_width as i32, self.tile_height as i32)
+ );
+
+ self.textures[idx].update(texture_region, data, stride, gl::RGBA, gl::UNSIGNED_BYTE);
+ idx
+ };
+
+ let tile = MapTile {
+ texture_index: texture_index as u32,
+
+ // TODO: are there ever non-power of two textures?
+ width: self.tile_width,
+ height: self.tile_height,
+ };
+ self.tiles.push(tile);
+ }
+ }
+ }
+
+ pub fn update(&mut self, land: &Land2D<u32>, region: Rect) {
+
+ }
+
+ pub fn render(&mut self, viewport: Rect) {
+ self.tile_vertices.clear();
+ self.tile_indices.clear();
+ self.tile_draw_calls.clear();
+ self.index_offset = 0;
+
+ for (idx, tile) in self.tiles.iter().enumerate() {
+ let tile_x = idx as i32 % self.num_tile_x;
+ let tile_y = idx as i32 / self.num_tile_x;
+ let tile_w = self.tile_width as i32;
+ let tile_h = self.tile_height as i32;
+
+ let origin = Point::new(tile_x * tile_w, tile_y * tile_h);
+ let tile_rect = Rect::new(origin, origin + Point::new(tile_w, tile_h));
+
+ if viewport.intersects(&tile_rect) {
+ // lazy
+ //dbg!(origin);
+ let tile_x = origin.x as f32;
+ let tile_y = origin.y as f32;
+ let tile_w = tile_x + tile_w as f32;
+ let tile_h = tile_y + tile_h as f32;
+ let uv_depth = tile.texture_index as f32;
+
+ //dbg!(tile_x);
+ let tl = TileVertex { pos: [tile_x, tile_y], uv: [0f32, 0f32, uv_depth] };
+ let bl = TileVertex { pos: [tile_x, tile_h], uv: [0f32, 1f32, uv_depth] };
+ let br = TileVertex { pos: [tile_w, tile_h], uv: [1f32, 1f32, uv_depth] };
+ let tr = TileVertex { pos: [tile_w, tile_y], uv: [1f32, 0f32, uv_depth] };
+
+ self.tile_vertices.extend(&[tl, bl, br, tr]);
+
+ let i = self.index_offset;
+ self.tile_indices.extend(&[
+ i + 0, i + 1, i + 2,
+ i + 2, i + 3, i + 0,
+ ]);
+ self.index_offset += 4;
+
+ self.tile_draw_calls.push(DrawTile {
+ texture_index: tile.texture_index,
+ index_len: 6
+ });
+ }
+ }
+
+ self.tile_vertex_buffer.write_typed(&self.tile_vertices);
+ self.tile_index_buffer.write_typed(&self.tile_indices);
+
+ let _g = self.tile_layout.bind(&[
+ (0, &self.tile_vertex_buffer)
+ ], Some(&self.tile_index_buffer));
+
+ let ortho = {
+ let l = viewport.left() as f32;
+ let r = viewport.right() as f32;
+ let b = viewport.bottom() as f32;
+ let t = viewport.top() as f32;
+
+ [
+ 2f32 / (r - l), 0f32, 0f32, 0f32,
+ 0f32, 2f32 / (t - b), 0f32, 0f32,
+ 0f32, 0f32, 0.5f32, 0f32,
+ (r + l) / (l - r), (t + b) / (b - t), 0.5f32, 1f32,
+ ]
+ };
+
+ self.tile_shader.bind();
+ self.tile_shader.set_matrix("Projection", ortho.as_ptr());
+
+ let mut draw_offset = 0;
+ for draw_call in &self.tile_draw_calls {
+ unsafe {
+ self.tile_shader.bind_texture_2d(0, &self.textures[draw_call.texture_index as usize]);
+
+ gl::DrawElements(
+ gl::TRIANGLES,
+ draw_call.index_len as i32,
+ gl::UNSIGNED_SHORT,
+ draw_offset as *const _
+ );
+ }
+
+ draw_offset += draw_call.index_len * 2;
+ }
+ }
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/lib-hedgewars-engine/src/render/mod.rs Fri Mar 22 18:01:08 2019 +0200
@@ -0,0 +1,5 @@
+mod map;
+mod gl;
+
+pub use self::map::*;
+use self::gl::*;
--- a/rust/lib-hedgewars-engine/src/world.rs Thu Mar 21 01:23:05 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/world.rs Fri Mar 22 18:01:08 2019 +0200
@@ -10,6 +10,8 @@
use lfprng::LaggedFibonacciPRNG;
use hwphysics as hwp;
+use crate::render::MapRenderer;
+
struct GameState {
land: Land2D<u32>,
physics: hwp::World,
@@ -28,6 +30,7 @@
random_numbers_gen: LaggedFibonacciPRNG,
preview: Option<Land2D<u8>>,
game_state: Option<GameState>,
+ renderer: MapRenderer,
}
impl World {
@@ -36,6 +39,7 @@
random_numbers_gen: LaggedFibonacciPRNG::new(&[]),
preview: None,
game_state: None,
+ renderer: MapRenderer::new(512, 512),
}
}
@@ -77,12 +81,38 @@
let landgen = TemplatedLandGenerator::new(template);
let land = landgen.generate_land(¶ms, &mut self.random_numbers_gen);
+ use mapgen::{
+ MapGenerator,
+ theme::{Theme, slice_u32_to_u8}
+ };
+
+ use std::path::Path;
+
+ let theme = Theme::load(Path::new("../../share/hedgewars/Data/Themes/Cheese/")).unwrap();
+ let texture = MapGenerator::new().make_texture32(&land, &theme);
+ self.renderer.init(&texture);
+
self.game_state = Some(GameState::new(land, physics));
}
+ pub fn render(&mut self, x: f32, y: f32, w: f32, h: f32) {
+ unsafe {
+ gl::ClearColor(0.4f32, 0f32, 0.2f32, 1f32);
+ gl::Clear(gl::COLOR_BUFFER_BIT);
+ }
+
+ self.renderer.render(Rect::new(
+ Point::new(x as _, y as _),
+ Point::new((x + w) as _, (y + h) as _),
+ ));
+ }
+
pub fn step(&mut self) {
if let Some(ref mut state) = self.game_state {
state.physics.step(fp!(1), &state.land);
}
}
}
+
+
+
--- a/rust/mapgen/src/lib.rs Thu Mar 21 01:23:05 2019 +0300
+++ b/rust/mapgen/src/lib.rs Fri Mar 22 18:01:08 2019 +0200
@@ -170,6 +170,72 @@
texture
}
+
+ // TODO: no way to pass both u8 & u32?
+ pub fn make_texture32(&self, land: &Land2D<u32>, theme: &Theme) -> Vec2D<u32> {
+ let mut texture = Vec2D::new(land.size(), 0);
+
+ if let Some(land_sprite) = theme.land_texture() {
+ for (row_index, (land_row, tex_row)) in land.rows()
+ .zip(texture.rows_mut())
+ .enumerate()
+ {
+ let sprite_row = land_sprite.get_row(row_index % land_sprite.height());
+ let mut x_offset = 0;
+ while sprite_row.len() < land.width() - x_offset {
+ let copy_range = x_offset..x_offset + sprite_row.len();
+ tex_row_copy32(
+ &land_row[copy_range.clone()],
+ &mut tex_row[copy_range],
+ sprite_row
+ );
+
+ x_offset += land_sprite.width()
+ }
+
+ if x_offset < land.width() {
+ let final_range = x_offset..land.width();
+ tex_row_copy32(
+ &land_row[final_range.clone()],
+ &mut tex_row[final_range],
+ &sprite_row[..land.width() - x_offset]
+ );
+ }
+ }
+ }
+
+ if let Some(border_sprite) = theme.border_texture() {
+ assert!(border_sprite.height() <= 512);
+ let border_width = (border_sprite.height() / 2) as u8;
+ let border_sprite = border_sprite.to_tiled();
+
+ let mut offsets = vec![255u8; land.width()];
+
+ land_border_pass32(
+ land.rows().rev().zip(texture.rows_mut().rev()),
+ &mut offsets,
+ border_width,
+ |x, y| border_sprite.get_pixel(
+ x % border_sprite.width(),
+ border_sprite.height() - 1 - y,
+ )
+ );
+
+ offsets.iter_mut().for_each(|v| *v = 255);
+
+ land_border_pass32(
+ land.rows().zip(texture.rows_mut()),
+ &mut offsets,
+ border_width,
+ |x, y| border_sprite.get_pixel(
+ x % border_sprite.width(),
+ y,
+ )
+ );
+ }
+
+ texture
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -238,6 +304,31 @@
}
}
+fn land_border_pass32<'a, T, F>(rows: T, offsets: &mut [u8], border_width: u8, pixel_getter: F)
+ where T: Iterator<Item = (&'a [u32], &'a mut [u32])>,
+ F: (Fn(usize, usize) -> u32)
+{
+ for (land_row, tex_row) in rows {
+ for (x, ((land_v, tex_v), offset_v)) in land_row.iter()
+ .zip(tex_row.iter_mut())
+ .zip(offsets.iter_mut())
+ .enumerate()
+ {
+ *offset_v = if *land_v == 0 {
+ if *offset_v < border_width {
+ *tex_v = blend(
+ pixel_getter(x, *offset_v as usize),
+ *tex_v,
+ )
+ }
+ offset_v.saturating_add(1)
+ } else {
+ 0
+ }
+ }
+ }
+}
+
fn tex_row_copy(land_row: &[u8], tex_row: &mut [u32], sprite_row: &[u32]) {
for ((land_v, tex_v), sprite_v) in
land_row.iter().zip(tex_row.iter_mut()).zip(sprite_row)
@@ -250,6 +341,18 @@
}
}
+fn tex_row_copy32(land_row: &[u32], tex_row: &mut [u32], sprite_row: &[u32]) {
+ for ((land_v, tex_v), sprite_v) in
+ land_row.iter().zip(tex_row.iter_mut()).zip(sprite_row)
+ {
+ *tex_v = if *land_v == 0 {
+ *sprite_v
+ } else {
+ 0
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use crate::{
--- a/rust/vec2d/src/lib.rs Thu Mar 21 01:23:05 2019 +0300
+++ b/rust/vec2d/src/lib.rs Fri Mar 22 18:01:08 2019 +0200
@@ -95,6 +95,19 @@
let width = self.width();
self.data.chunks_exact_mut(width)
}
+
+ #[inline]
+ pub unsafe fn as_bytes(&self) -> &[u8] {
+ use std::{
+ slice,
+ mem
+ };
+
+ slice::from_raw_parts(
+ self.data.as_ptr() as *const u8,
+ self.data.len() * mem::size_of::<T>(),
+ )
+ }
}
impl<T: Copy> AsRef<[T]> for Vec2D<T> {