#[macro_use] extern crate glium; #[macro_use] extern crate load_file; use glium::texture::SrgbTexture2d; use phf::phf_map; use std::io::Cursor; use std::time::Instant; #[allow(unused_imports)] use glium::{glutin, Surface}; mod support; #[derive(Copy, Clone, Debug)] struct PerInstance { pub w_position: (f32, f32, f32), pub w_rotation: f32, } implement_vertex!(PerInstance, w_position, w_rotation); const STRUCTURES_TEXTURES_NUMBER: usize = 78; const STRUCTURES_TEXTURES: [&str; STRUCTURES_TEXTURES_NUMBER] = [ "redNoise.jpg", "cargoLightBlue.jpg", "beigeNoise.jpg", "wallStone.jpg", "cargoWhite.jpg", "b_ficusc2d_f.png", "basketball court.jpg", "zinc.jpg", "solarPowerPlant.png", "cargoSand.jpg", "water.jpg", "solarPowerPlantAccumulator.jpg", "blueNoise.jpg", "30kmh.jpg", "white.jpg", "blackYellow.jpg", "stopText.jpg", "cargoOrange.jpg", "redWhite.jpg", "grid.png", "blueTile.jpg", "blueWhiteNoise.jpg", "reinforcedConcrete.jpg", "log.jpg", "green.jpg", "fanWallGrey.jpg", "cargoCyan.jpg", "glass.png", "wallGrey.jpg", "greenGate.png", "greyNoise.jpg", "cattail.png", "palmLayer.png", "cargoLightGreen.jpg", "palm.png", "branch1.png", "cargoYellow.jpg", "blackNoise.jpg", "pipes.jpg", "whiteTile.jpg", "darkBlueNoise.jpg", "planks.png", "trunk.jpg", "greenNoise.jpg", "wallBlue.jpg", "stop.jpg", "wallGreyATM.jpg", "iron bars.png", "tile.jpg", "horizontalBrick.jpg", "steel.jpg", "yellowBars.png", "whiteDoor.jpg", "cargoBlue.jpg", "barbed.png", "woodBlue.jpg", "bamboo.png", "b_ficusc1s_f.png", "whiteNoise.jpg", "fanWall.jpg", "wood.jpg", "crossover.jpg", "gridHexa.png", "lightBlue.jpg", "helipad.png", "binBarrel.jpg", "brick.jpg", "wallBlueATM.jpg", "yellow.jpg", "blueDoor.jpg", "parking.jpg", "cargoGrey.jpg", "haystack.jpg", "cargoRed.jpg", "yellowNoise.jpg", "gridFull.jpg", "rust.jpg", "cargo.jpg", ]; // This approach works as there isn't file names having several file extensions. // `phf` doesn't propose any `bimap`. static STRUCTURES_TEXTURES_REVERSED: phf::Map<&'static str, u32> = phf_map! { "redNoise" => 0, "cargoLightBlue" => 1, "beigeNoise" => 2, "wallStone" => 3, "cargoWhite" => 4, "b_ficusc2d_f" => 5, "basketball court" => 6, "zinc" => 7, "solarPowerPlant" => 8, "cargoSand" => 9, "water" => 10, "solarPowerPlantAccumulator" => 11, "blueNoise" => 12, "30kmh" => 13, "white" => 14, "blackYellow" => 15, "stopText" => 16, "cargoOrange" => 17, "redWhite" => 18, "grid" => 19, "blueTile" => 20, "blueWhiteNoise" => 21, "reinforcedConcrete" => 22, "log" => 23, "green" => 24, "fanWallGrey" => 25, "cargoCyan" => 26, "glass" => 27, "wallGrey" => 28, "greenGate" => 29, "greyNoise" => 30, "cattail" => 31, "palmLayer" => 32, "cargoLightGreen" => 33, "palm" => 34, "branch1" => 35, "cargoYellow" => 36, "blackNoise" => 37, "pipes" => 38, "whiteTile" => 39, "darkBlueNoise" => 40, "planks" => 41, "trunk" => 42, "greenNoise" => 43, "wallBlue" => 44, "stop" => 45, "wallGreyATM" => 46, "iron bars" => 47, "tile" => 48, "horizontalBrick" => 49, "steel" => 50, "yellowBars" => 51, "whiteDoor" => 52, "cargoBlue" => 53, "barbed" => 54, "woodBlue" => 55, "bamboo" => 56, "b_ficusc1s_f" => 57, "whiteNoise" => 58, "fanWall" => 59, "wood" => 60, "crossover" => 61, "gridHexa" => 62, "lightBlue" => 63, "helipad" => 64, "binBarrel" => 65, "brick" => 66, "wallBlueATM" => 67, "yellow" => 68, "blueDoor" => 69, "parking" => 70, "cargoGrey" => 71, "haystack" => 72, "cargoRed" => 73, "yellowNoise" => 74, "gridFull" => 75, "rust" => 76, "cargo" => 77, }; const BIOMES_NUMBER: usize = 19; const BIOMES: [&str; BIOMES_NUMBER] = [ "dirt", "grassDry", "grassGreen", "concrete", "soil", "beach", "seabed", "thorn", "wildField", "rock", "grassWild", "stony", "forestPine", "mud", "stonyThistle", "marsh", "dead", "desert", "weed", ]; fn main() { // building the display, ie. the main object let event_loop = glutin::event_loop::EventLoop::new(); let wb = glutin::window::WindowBuilder::new() .with_title("LemnosLife") .with_maximized(true); let cb = glutin::ContextBuilder::new().with_depth_buffer(24); let display = glium::Display::new(wb, cb, &event_loop).unwrap(); // building the vertex and index buffers let ground_vertex_buffer = support::load_ground(&display); let ground_textures: [SrgbTexture2d; BIOMES_NUMBER] = BIOMES.map(|biome| { println!("{}", biome); let image = image::load( Cursor::new(&load_bytes!(&format!( "../Extensions/LemnosLife/Assets/Pictures/Map/Ground/{}.jpg", biome ))), image::ImageFormat::Jpeg, ) .unwrap() .to_rgba8(); let image_dimensions = image.dimensions(); let image = glium::texture::RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dimensions); glium::texture::SrgbTexture2d::new(&display, image).unwrap() }); let structures_textures: [SrgbTexture2d; STRUCTURES_TEXTURES_NUMBER] = STRUCTURES_TEXTURES.map(|structure_texture| { println!("{}", structure_texture); let image = image::load( Cursor::new(&load_bytes!(&format!( "../Extensions/LemnosLife/Assets/Downloads/{}", structure_texture ))), if structure_texture.ends_with(".png") { image::ImageFormat::Png } else { image::ImageFormat::Jpeg }, ) .unwrap() .to_rgba8(); let image_dimensions = image.dimensions(); let image = glium::texture::RawImage2d::from_raw_rgba_reversed( &image.into_raw(), image_dimensions, ); glium::texture::SrgbTexture2d::new(&display, image).unwrap() }); let uniform_sampler_2d_str = (0..BIOMES_NUMBER) .map(|i| format!("tex{}", i)) .collect::>() .join(", "); let switch_v_biome_str = (0..BIOMES_NUMBER) .map(|i| { format!( " case {}u: f_color = texture(tex{}, v_tex_coords); break; ", i, i ) }) .collect::>() .join("") + &format!( " default: // case {}u: f_color = texture(tex{}, v_tex_coords); ", BIOMES_NUMBER - 1, BIOMES_NUMBER - 1 ); let ground_fragment = &format!( " #version 140 in vec2 v_tex_coords; flat in uint v_biome; out vec4 f_color; uniform sampler2D {}; void main() {{ switch (v_biome) {{ {} }} }} ", uniform_sampler_2d_str, switch_v_biome_str ); let uniform_sampler_2d_str = (0..STRUCTURES_TEXTURES_NUMBER) .map(|i| format!("tex{}", i)) .collect::>() .join(", "); let switch_v_texture_index_str = (0..STRUCTURES_TEXTURES_NUMBER) .map(|i| { format!( " case {}u: f_color = texture(tex{}, v_texture_coordinates); break; ", i, i ) }) .collect::>() .join("") + &format!( " default: // case {}u: f_color = texture(tex{}, v_texture_coordinates); ", STRUCTURES_TEXTURES_NUMBER - 1, STRUCTURES_TEXTURES_NUMBER - 1 ); let structures_fragment = &format!( " #version 140 in vec2 v_texture_coordinates; flat in uint v_texture_index; out vec4 f_color; uniform sampler2D {}; void main() {{ switch (v_texture_index) {{ {} }} }} ", uniform_sampler_2d_str, switch_v_texture_index_str ); // the program let ground_program = program!(&display, 140 => { vertex: " #version 140 uniform mat4 persp_matrix; uniform mat4 view_matrix; in uint index; in float altitude; in uint biome; in uvec2 chunk; out vec2 v_tex_coords; flat out uint v_biome; const uint TILE_SIZE = 4u; const uint TILES_PER_CHUNK_ROW = 250u; const uint TRIANGLES_PER_CHUNK_ROW = TILES_PER_CHUNK_ROW * 2u + 2u; void main() { uint index_mod_triangles_per_chunk_row = index % TRIANGLES_PER_CHUNK_ROW; uint index_div_triangles_per_chunk_row = index / TRIANGLES_PER_CHUNK_ROW; uint index_div_triangles_per_chunk_row_mod_2 = index_div_triangles_per_chunk_row % 2u; uint index_mod_triangles_per_chunk_row_div_2 = index_mod_triangles_per_chunk_row / 2u; uint x = index_div_triangles_per_chunk_row_mod_2 == 0u ? index_mod_triangles_per_chunk_row_div_2 : TILES_PER_CHUNK_ROW - index_mod_triangles_per_chunk_row_div_2; uint z = index_div_triangles_per_chunk_row + index % 2u; switch (index % 4u) { case 0u: v_tex_coords = vec2(index_div_triangles_per_chunk_row_mod_2, 0); break; case 1u: v_tex_coords = vec2(index_div_triangles_per_chunk_row_mod_2, 1); break; case 2u: v_tex_coords = vec2(1u - index_div_triangles_per_chunk_row_mod_2, 0); break; default: // case 3u v_tex_coords = vec2(1u - index_div_triangles_per_chunk_row_mod_2, 1); break; } v_biome = biome; x *= TILE_SIZE; z *= TILE_SIZE; x += chunk.x; z += chunk.y; gl_Position = persp_matrix * view_matrix * vec4(vec3(x, altitude, z), 1.0); } ", fragment: ground_fragment, }, ) .unwrap(); let mut camera = support::camera::CameraState::new(); let vertex_buffer = support::load_wavefront( &display, load_bytes!("../Extensions/LemnosLife/Map/Common/Structures/24.obj"), ); let structures_program = program!(&display, 140 => { vertex: " #version 140 uniform mat4 persp_matrix; uniform mat4 view_matrix; in vec3 w_position; in float w_rotation; in vec3 position; in uint texture_index; in vec2 texture_coordinates; flat out uint v_texture_index; out vec2 v_texture_coordinates; void main() { v_texture_index = texture_index; v_texture_coordinates = texture_coordinates; gl_Position = persp_matrix * view_matrix * vec4(position + w_position, 1.0); } ", fragment: structures_fragment, }, ) .unwrap(); let per_instance = vec![ PerInstance { w_position: (0.0, 0.0, 0.0), w_rotation: 0.0, }, PerInstance { w_position: (1.0, 0.0, 0.0), w_rotation: 0.0, }, ]; // the main loop support::start_loop(event_loop, move |events| { camera.update(); // building the uniforms let ground_uniforms = uniform! { persp_matrix: camera.get_perspective(), view_matrix: camera.get_view(), tex0: &ground_textures[0], tex1: &ground_textures[1], tex2: &ground_textures[2], tex3: &ground_textures[3], tex4: &ground_textures[4], tex5: &ground_textures[5], tex6: &ground_textures[6], tex7: &ground_textures[7], tex8: &ground_textures[8], tex9: &ground_textures[9], tex10: &ground_textures[10], tex11: &ground_textures[11], tex12: &ground_textures[12], tex13: &ground_textures[13], tex14: &ground_textures[14], tex15: &ground_textures[15], tex16: &ground_textures[16], tex17: &ground_textures[17], tex18: &ground_textures[18], }; let structures_uniforms = uniform! { persp_matrix: camera.get_perspective(), view_matrix: camera.get_view(), tex0: &structures_textures[0], tex1: &structures_textures[1], tex2: &structures_textures[2], tex3: &structures_textures[3], tex4: &structures_textures[4], tex5: &structures_textures[5], tex6: &structures_textures[6], tex7: &structures_textures[7], tex8: &structures_textures[8], tex9: &structures_textures[9], tex10: &structures_textures[10], tex11: &structures_textures[11], tex12: &structures_textures[12], tex13: &structures_textures[13], tex14: &structures_textures[14], tex15: &structures_textures[15], tex16: &structures_textures[16], tex17: &structures_textures[17], tex18: &structures_textures[18], tex19: &structures_textures[19], tex20: &structures_textures[20], tex21: &structures_textures[21], tex22: &structures_textures[22], tex23: &structures_textures[23], tex24: &structures_textures[24], tex25: &structures_textures[25], tex26: &structures_textures[26], tex27: &structures_textures[27], tex28: &structures_textures[28], tex29: &structures_textures[29], tex30: &structures_textures[30], tex31: &structures_textures[31], tex32: &structures_textures[32], tex33: &structures_textures[33], tex34: &structures_textures[34], tex35: &structures_textures[35], tex36: &structures_textures[36], tex37: &structures_textures[37], tex38: &structures_textures[38], tex39: &structures_textures[39], tex40: &structures_textures[40], tex41: &structures_textures[41], tex42: &structures_textures[42], tex43: &structures_textures[43], tex44: &structures_textures[44], tex45: &structures_textures[45], tex46: &structures_textures[46], tex47: &structures_textures[47], tex48: &structures_textures[48], tex49: &structures_textures[49], tex50: &structures_textures[50], tex51: &structures_textures[51], tex52: &structures_textures[52], tex53: &structures_textures[53], tex54: &structures_textures[54], tex55: &structures_textures[55], tex56: &structures_textures[56], tex57: &structures_textures[57], tex58: &structures_textures[58], tex59: &structures_textures[59], tex60: &structures_textures[60], tex61: &structures_textures[61], tex62: &structures_textures[62], tex63: &structures_textures[63], tex64: &structures_textures[64], tex65: &structures_textures[65], tex66: &structures_textures[66], tex67: &structures_textures[67], tex68: &structures_textures[68], tex69: &structures_textures[69], tex70: &structures_textures[70], tex71: &structures_textures[71], tex72: &structures_textures[72], tex73: &structures_textures[73], tex74: &structures_textures[74], tex75: &structures_textures[75], tex76: &structures_textures[76], tex77: &structures_textures[77], }; // draw parameters let ground_params = glium::DrawParameters { depth: glium::Depth { test: glium::DepthTest::IfLess, write: true, ..Default::default() }, // Verify that it helps from the FPS point of view. //backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockwise, ..Default::default() }; let mut structures_params = ground_params.clone(); structures_params.blend = glium::draw_parameters::Blend::alpha_blending(); let per_instance_buffer = glium::vertex::VertexBuffer::new(&display, &per_instance).unwrap(); // drawing a frame let start = Instant::now(); let mut target = display.draw(); target.clear_color_and_depth((0.0, 0.0, 0.0, 0.0), 1.0); target .draw( &ground_vertex_buffer, &glium::index::NoIndices(glium::index::PrimitiveType::TriangleStrip), &ground_program, &ground_uniforms, &ground_params, ) .unwrap(); target .draw( (&vertex_buffer, per_instance_buffer.per_instance().unwrap()), &glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList), &structures_program, &structures_uniforms, &structures_params, ) .unwrap(); target.finish().unwrap(); let duration = start.elapsed(); println!("Time elapsed is: {:?}", duration); let mut action = support::Action::Continue; // polling and handling the events received by the window for event in events { if let glutin::event::Event::WindowEvent { event, .. } = event { match event { glutin::event::WindowEvent::CloseRequested => action = support::Action::Stop, ev => camera.process_input(ev), } } } action }); }