25 Commits

Author SHA1 Message Date
Benjamin d6cc193e4e Mention Extensions/ source in README.md 2026-06-08 19:08:41 +02:00
Benjamin_Loison fd087469df Add instructions to README.md to build and run the client 2022-11-03 02:43:18 +01:00
Benjamin_Loison 5934f3527a Apply cargo fmt 2022-11-03 02:35:20 +01:00
Benjamin_Loison 5fa146e583 Add rotation to structures rendering 2022-11-01 03:57:16 +01:00
Benjamin_Loison b59aa96b97 Structures display implemented on map except their rotations 2022-11-01 03:22:59 +01:00
Benjamin_Loison ed693ef144 Ready to implement multiple structures 2022-11-01 01:58:55 +01:00
Benjamin_Loison 87c9093a4e Set the correct texture 2022-11-01 01:15:35 +01:00
Benjamin_Loison 20898bf139 Make the arbitrary texture set in a more modular way 2022-11-01 00:14:12 +01:00
Benjamin_Loison ed1e341e09 Make ready rotation implementation 2022-10-31 21:45:36 +01:00
Benjamin_Loison b23fa769fa Add an arbitrary texture to structures 2022-10-31 20:43:53 +01:00
Benjamin_Loison e4ad531a7e Display multiple instances of OBJs 2022-10-31 20:12:46 +01:00
Benjamin_Loison 89387d82fd Use more iterators 2022-10-25 00:39:19 +02:00
Benjamin_Loison 2984d147fa Use iterators for the generalization of the ground vertex and fragment shaders 2022-10-24 23:10:03 +02:00
Benjamin_Loison b1710f7f71 Apply cargo clippy 2022-10-23 03:50:07 +02:00
Benjamin_Loison 9f64ebc0f9 Generalize tiles per chunk row 2022-10-23 03:40:14 +02:00
Benjamin_Loison 0694b1ef92 Define dynamically the fragment shader 2022-10-23 03:32:15 +02:00
Benjamin_Loison f81a31c821 Make the window maximized by default 2022-10-23 02:40:29 +02:00
Benjamin_Loison e22f1f0e83 Reduce under the ground workload
As nobody (even developers) should go/see under the map.
2022-10-23 02:36:14 +02:00
Benjamin_Loison 76d64b6660 Fix #2: Optimize ground rendering using triangle strip 2022-10-23 01:52:24 +02:00
Benjamin_Loison f028ad7bb7 Make vertex shader use a switch 2022-10-23 00:12:09 +02:00
Benjamin_Loison 1fd3893e27 Add a frame timer 2022-10-23 00:03:38 +02:00
Benjamin_Loison 732c65914f Remove normals to try to firstly optimize chunks rendered 2022-10-22 23:52:08 +02:00
Benjamin_Loison fc8c713067 Add window title 2022-10-22 23:18:11 +02:00
Benjamin_Loison e102332d0b Make biomes display work correctly 2022-10-22 22:59:09 +02:00
Benjamin_Loison e36ce58f75 Shift chunks where they are supposed to be 2022-10-22 21:19:58 +02:00
5 changed files with 776 additions and 217 deletions
+2
View File
@@ -7,4 +7,6 @@ edition = "2021"
glium = "0.32.1" glium = "0.32.1"
image = "0.24.4" image = "0.24.4"
glutin = "0.29.1" glutin = "0.29.1"
obj = "0.10.2"
phf = { version = "0.11.1", features = ["macros"]}
load_file = "1.0.1" load_file = "1.0.1"
+8
View File
@@ -1 +1,9 @@
# LemnosLife Rust # LemnosLife Rust
Download `Extensions/` from [issues/8#issue-732](https://gitea.lemnoslife.com/LemnosLife/LemnosLife_Rust/issues/8#issue-732).
First install [Rust](https://rustup.rs), then build and run the client by executing:
```
cargo run --release
```
+586 -94
View File
@@ -5,13 +5,191 @@ extern crate glium;
extern crate load_file; extern crate load_file;
use glium::texture::SrgbTexture2d; use glium::texture::SrgbTexture2d;
use phf::phf_map;
use std::f32::consts::PI;
use std::fs;
use std::io::Cursor; use std::io::Cursor;
use std::time::Instant;
use crate::support::CHUNK_SIZE;
use glium::vertex::VertexBufferAny;
#[allow(unused_imports)] #[allow(unused_imports)]
use glium::{glutin, Surface}; use glium::{glutin, Surface};
mod support; 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_NUMBER: usize = 19;
const BIOMES: [&str; BIOMES_NUMBER] = [ const BIOMES: [&str; BIOMES_NUMBER] = [
"dirt", "dirt",
@@ -38,14 +216,16 @@ const BIOMES: [&str; BIOMES_NUMBER] = [
fn main() { fn main() {
// building the display, ie. the main object // building the display, ie. the main object
let event_loop = glutin::event_loop::EventLoop::new(); let event_loop = glutin::event_loop::EventLoop::new();
let wb = glutin::window::WindowBuilder::new(); let wb = glutin::window::WindowBuilder::new()
.with_title("LemnosLife")
.with_maximized(true);
let cb = glutin::ContextBuilder::new().with_depth_buffer(24); let cb = glutin::ContextBuilder::new().with_depth_buffer(24);
let display = glium::Display::new(wb, cb, &event_loop).unwrap(); let display = glium::Display::new(wb, cb, &event_loop).unwrap();
// building the vertex and index buffers // building the vertex and index buffers
let (vertex_buffer, chunks) = support::load_ground(&display); let ground_vertex_buffer = support::load_ground(&display);
let textures: [SrgbTexture2d; BIOMES_NUMBER] = BIOMES.map(|biome| { let ground_textures: [SrgbTexture2d; BIOMES_NUMBER] = BIOMES.map(|biome| {
println!("{}", biome); println!("{}", biome);
let image = image::load( let image = image::load(
Cursor::new(&load_bytes!(&format!( Cursor::new(&load_bytes!(&format!(
@@ -62,8 +242,125 @@ fn main() {
glium::texture::SrgbTexture2d::new(&display, image).unwrap() 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::<Vec<String>>()
.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::<Vec<String>>()
.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::<Vec<String>>()
.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::<Vec<String>>()
.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 // the program
let program = program!(&display, let ground_program = program!(&display,
140 => { 140 => {
vertex: " vertex: "
#version 140 #version 140
@@ -73,110 +370,277 @@ fn main() {
in uint index; in uint index;
in float altitude; in float altitude;
in uint biome;
in uvec2 chunk;
out vec2 v_tex_coords; 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() { void main() {
uint index_mod = index % 6u; uint index_mod_triangles_per_chunk_row = index % TRIANGLES_PER_CHUNK_ROW;
uint index_div = index / 6u; uint index_div_triangles_per_chunk_row = index / TRIANGLES_PER_CHUNK_ROW;
uint z = index_div / 250u; uint index_div_triangles_per_chunk_row_mod_2 = index_div_triangles_per_chunk_row % 2u;
uint x = index_div % 250u; uint index_mod_triangles_per_chunk_row_div_2 = index_mod_triangles_per_chunk_row / 2u;
if(index_mod == 0u)
v_tex_coords = vec2(0, 0); 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;
else if(index_mod == 1u || index_mod == 4u) uint z = index_div_triangles_per_chunk_row + index % 2u;
{
v_tex_coords = vec2(0, 1); switch (index % 4u) {
x++; 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;
} }
else if(index_mod == 2u || index_mod == 3u) v_biome = biome;
{
v_tex_coords = vec2(1, 0); x *= TILE_SIZE;
z++; z *= TILE_SIZE;
}
else //if(index_mod == 5u) x += chunk.x;
{ z += chunk.y;
v_tex_coords = vec2(1, 1);
x++; gl_Position = persp_matrix * view_matrix * vec4(vec3(x, altitude, z), 1.0);
z++;
}
gl_Position = persp_matrix * view_matrix * vec4(vec3(x, altitude, z) * 0.005, 1.0);
} }
", ",
fragment: " fragment: ground_fragment,
#version 140
flat in vec3 normal;
in vec2 v_tex_coords;
out vec4 f_color;
flat in uint biome;
uniform sampler2D tex0, tex1, tex2, tex3, tex4, tex5, tex6, tex7, tex8, tex9, tex10, tex11, tex12, tex13, tex14, tex15, tex16, tex17, tex18;
const vec3 LIGHT = vec3(0.0, -1.0, 0.0);
void main() {
float lum = max(dot(normalize(normal), normalize(LIGHT)), 0.0);
if(biome == 0u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex0, v_tex_coords);
else if(biome == 1u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex1, v_tex_coords);
else if(biome == 2u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex2, v_tex_coords);
else if(biome == 3u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex3, v_tex_coords);
else if(biome == 4u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex4, v_tex_coords);
else if(biome == 5u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex5, v_tex_coords);
else if(biome == 6u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex6, v_tex_coords);
else if(biome == 7u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex7, v_tex_coords);
else if(biome == 8u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex8, v_tex_coords);
else if(biome == 9u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex9, v_tex_coords);
else if(biome == 10u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex10, v_tex_coords);
else if(biome == 11u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex11, v_tex_coords);
else if(biome == 12u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex12, v_tex_coords);
else if(biome == 13u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex13, v_tex_coords);
else if(biome == 14u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex14, v_tex_coords);
else if(biome == 15u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex15, v_tex_coords);
else if(biome == 16u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex16, v_tex_coords);
else if(biome == 17u) f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex17, v_tex_coords);
else /*if(biome == 18u)*/ f_color = (0.3 + 0.7 * lum, 1.0) * texture(tex18, v_tex_coords);
}
"
}, },
) )
.unwrap(); .unwrap();
let mut camera = support::camera::CameraState::new(); let mut camera = support::camera::CameraState::new();
let structures_folder = "Extensions/LemnosLife/Map/Common/Structures/";
let paths = fs::read_dir(structures_folder).unwrap();
let mut structures_vertex_buffer: Vec<(usize, VertexBufferAny)> = vec![];
for path in paths {
let path = path.unwrap().path();
let file_path = path.to_str().unwrap();
if file_path.ends_with(".obj") {
let file = file_path.replace(structures_folder, "");
println!("{}", file);
structures_vertex_buffer.push((
file.replace(".obj", "").parse().unwrap(),
support::load_wavefront(&display, load_bytes!(&format!("../{}", file_path))),
))
}
}
let mut structures_per_instance: Vec<Vec<PerInstance>> = Vec::with_capacity(926); // As the maximum structure id is 925.
for _ in 0..=925 {
structures_per_instance.push(vec![]);
}
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() {
float cos_rotation = cos(w_rotation);
float sin_rotation = sin(w_rotation);
vec3 rotated_position = position;
rotated_position.x = cos_rotation * position.x + sin_rotation * position.z;
rotated_position.z = -sin_rotation * position.x + cos_rotation * position.z;
v_texture_index = texture_index;
v_texture_coordinates = texture_coordinates;
gl_Position = persp_matrix * view_matrix * vec4(rotated_position + w_position, 1.0);
}
",
fragment: structures_fragment,
},
)
.unwrap();
let objects_folder = "Extensions/LemnosLife/Map/Altis/Objects/";
let paths = fs::read_dir(objects_folder).unwrap();
for path in paths {
let path = path.unwrap().path();
let file_path = path.to_str().unwrap();
let file = file_path.replace(objects_folder, "");
println!("{}", file);
let file_name = file.replace(".objects", "");
let file_name_parts: Vec<&str> = file_name.split(' ').collect();
let objects_contents = fs::read_to_string(file_path)
.unwrap_or_else(|_| panic!("Unable to load {} file!", file_path));
let chunk = [0, 1].map(|file_name_parts_index| {
file_name_parts[file_name_parts_index]
.parse::<f32>()
.unwrap()
* (CHUNK_SIZE as f32)
});
let objects_lines: Vec<&str> = objects_contents.split('\n').collect();
let (mut offset_x, mut offset_y) = (0.0, 0.0);
for object_line in objects_lines {
let object_line_parts: Vec<&str> = object_line.split(' ').collect();
if object_line_parts.len() == 2 {
offset_x = object_line_parts[0].parse::<f32>().unwrap() * 100.0 + chunk[0];
offset_y = object_line_parts[1].parse::<f32>().unwrap() * 100.0 + chunk[1];
} else {
let structure_id: usize = object_line_parts[0].parse().unwrap();
let x: f32 = object_line_parts[1].parse().unwrap();
let y: f32 = object_line_parts[2].parse().unwrap();
let z = object_line_parts[3].parse().unwrap();
let rotation: f32 = object_line_parts[4].parse().unwrap();
let structure_per_instance: &mut Vec<PerInstance> =
structures_per_instance.get_mut(structure_id).unwrap();
structure_per_instance.push(PerInstance {
w_position: (offset_x + x, z, offset_y + y),
w_rotation: 2.0 * PI * rotation / 360.0,
});
}
}
}
// the main loop // the main loop
support::start_loop(event_loop, move |events| { support::start_loop(event_loop, move |events| {
camera.update(); camera.update();
// building the uniforms // building the uniforms
let uniforms = uniform! { let ground_uniforms = uniform! {
persp_matrix: camera.get_perspective(), persp_matrix: camera.get_perspective(),
view_matrix: camera.get_view(), view_matrix: camera.get_view(),
tex0: &textures[0], tex0: &ground_textures[0],
tex1: &textures[1], tex1: &ground_textures[1],
tex2: &textures[2], tex2: &ground_textures[2],
tex3: &textures[3], tex3: &ground_textures[3],
tex4: &textures[4], tex4: &ground_textures[4],
tex5: &textures[5], tex5: &ground_textures[5],
tex6: &textures[6], tex6: &ground_textures[6],
tex7: &textures[7], tex7: &ground_textures[7],
tex8: &textures[8], tex8: &ground_textures[8],
tex9: &textures[9], tex9: &ground_textures[9],
tex10: &textures[10], tex10: &ground_textures[10],
tex11: &textures[11], tex11: &ground_textures[11],
tex12: &textures[12], tex12: &ground_textures[12],
tex13: &textures[13], tex13: &ground_textures[13],
tex14: &textures[14], tex14: &ground_textures[14],
tex15: &textures[15], tex15: &ground_textures[15],
tex16: &textures[16], tex16: &ground_textures[16],
tex17: &textures[17], tex17: &ground_textures[17],
tex18: &textures[18], tex18: &ground_textures[18],
chunks: &chunks, };
// That way we *load* far too many textures per structure but until haven't treated http://gitea.lemnoslife.com:3006/Benjamin_Loison/LemnosLife_Rust/issues/6#issuecomment-240, can't do better.
// Except if we proceed with a disjunction on the number of textures used in a structure which is 8, thanks to `find Extensions/LemnosLife/Map/Common/Structures/ -name '*.obj' -exec sh -c 'grep -o usemtl {} | wc -l' \; | sort -n | tail -1`.
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 // draw parameters
let params = glium::DrawParameters { let ground_params = glium::DrawParameters {
depth: glium::Depth { depth: glium::Depth {
test: glium::DepthTest::IfLess, test: glium::DepthTest::IfLess,
write: true, write: true,
@@ -187,30 +651,58 @@ fn main() {
..Default::default() ..Default::default()
}; };
let mut structures_params = ground_params.clone();
structures_params.blend = glium::draw_parameters::Blend::alpha_blending(); // Could disable per opaque structure to optimize.
// Doesn't seem to apply transparency from a structure instance to the other.
// drawing a frame // drawing a frame
let start = Instant::now();
let mut target = display.draw(); let mut target = display.draw();
target.clear_color_and_depth((0.0, 0.0, 0.0, 0.0), 1.0); target.clear_color_and_depth((0.0, 0.0, 0.0, 0.0), 1.0);
target target
.draw( .draw(
&vertex_buffer, &ground_vertex_buffer,
&glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList), &glium::index::NoIndices(glium::index::PrimitiveType::TriangleStrip),
&program, &ground_program,
&uniforms, &ground_uniforms,
&params, &ground_params,
) )
.unwrap(); .unwrap();
for (structure_id, structure_vertex_buffer) in &structures_vertex_buffer {
let structure_per_instance = &structures_per_instance[*structure_id];
if !structure_per_instance.is_empty() {
let structure_per_instance_buffer =
glium::vertex::VertexBuffer::new(&display, structure_per_instance).unwrap();
target
.draw(
(
structure_vertex_buffer,
structure_per_instance_buffer.per_instance().unwrap(),
),
&glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList),
&structures_program,
&structures_uniforms,
&structures_params,
)
.unwrap();
}
}
target.finish().unwrap(); target.finish().unwrap();
let duration = start.elapsed();
println!("Time elapsed is: {:?}", duration);
let mut action = support::Action::Continue; let mut action = support::Action::Continue;
// polling and handling the events received by the window // polling and handling the events received by the window
for event in events { for event in events {
match event { if let glutin::event::Event::WindowEvent { event, .. } = event {
glutin::event::Event::WindowEvent { event, .. } => match event { match event {
glutin::event::WindowEvent::CloseRequested => action = support::Action::Stop, glutin::event::WindowEvent::CloseRequested => action = support::Action::Stop,
ev => camera.process_input(&ev), ev => camera.process_input(ev),
}, }
_ => (),
} }
} }
+24 -22
View File
@@ -13,12 +13,14 @@ pub struct CameraState {
rotate_right: bool, rotate_right: bool,
} }
const SPEED: f32 = 10.0; //1.0;
impl CameraState { impl CameraState {
pub fn new() -> CameraState { pub fn new() -> CameraState {
CameraState { CameraState {
aspect_ratio: 1024.0 / 768.0, aspect_ratio: 1_920.0 / 1_080.0,
position: (0.1, 0.1, 1.0),
// The second coordinate is for the altitude. // The second coordinate is for the altitude.
position: (3_646.41, 12.3622, 13_113.7),
direction: (0.0, 0.0, -1.0), direction: (0.0, 0.0, -1.0),
moving_up: false, moving_up: false,
moving_left: false, moving_left: false,
@@ -32,8 +34,8 @@ impl CameraState {
} }
pub fn get_perspective(&self) -> [[f32; 4]; 4] { pub fn get_perspective(&self) -> [[f32; 4]; 4] {
let fov: f32 = 3.141592 / 2.0; let fov: f32 = std::f32::consts::PI / 2.0;
let zfar = 8000.0; let zfar = 53_209.0;
let znear = 0.01; let znear = 0.01;
let f = 1.0 / (fov / 2.0).tan(); let f = 1.0 / (fov / 2.0).tan();
@@ -119,39 +121,39 @@ impl CameraState {
); );
if self.moving_up { if self.moving_up {
self.position.0 += u.0 * 0.01; self.position.0 += u.0 * SPEED;
self.position.1 += u.1 * 0.01; self.position.1 += u.1 * SPEED;
self.position.2 += u.2 * 0.01; self.position.2 += u.2 * SPEED;
} }
if self.moving_left { if self.moving_left {
self.position.0 -= s.0 * 0.01; self.position.0 -= s.0 * SPEED;
self.position.1 -= s.1 * 0.01; self.position.1 -= s.1 * SPEED;
self.position.2 -= s.2 * 0.01; self.position.2 -= s.2 * SPEED;
} }
if self.moving_down { if self.moving_down {
self.position.0 -= u.0 * 0.01; self.position.0 -= u.0 * SPEED;
self.position.1 -= u.1 * 0.01; self.position.1 -= u.1 * SPEED;
self.position.2 -= u.2 * 0.01; self.position.2 -= u.2 * SPEED;
} }
if self.moving_right { if self.moving_right {
self.position.0 += s.0 * 0.01; self.position.0 += s.0 * SPEED;
self.position.1 += s.1 * 0.01; self.position.1 += s.1 * SPEED;
self.position.2 += s.2 * 0.01; self.position.2 += s.2 * SPEED;
} }
if self.moving_forward { if self.moving_forward {
self.position.0 += f.0 * 0.01; self.position.0 += f.0 * SPEED;
self.position.1 += f.1 * 0.01; self.position.1 += f.1 * SPEED;
self.position.2 += f.2 * 0.01; self.position.2 += f.2 * SPEED;
} }
if self.moving_backward { if self.moving_backward {
self.position.0 -= f.0 * 0.01; self.position.0 -= f.0 * SPEED;
self.position.1 -= f.1 * 0.01; self.position.1 -= f.1 * SPEED;
self.position.2 -= f.2 * 0.01; self.position.2 -= f.2 * SPEED;
} }
if self.rotate_left { if self.rotate_left {
+156 -101
View File
@@ -1,4 +1,5 @@
#![allow(dead_code)] #![allow(dead_code)]
use crate::STRUCTURES_TEXTURES_REVERSED;
use glium::glutin::event::{Event, StartCause}; use glium::glutin::event::{Event, StartCause};
use glium::glutin::event_loop::{ControlFlow, EventLoop}; use glium::glutin::event_loop::{ControlFlow, EventLoop};
use glium::vertex::VertexBufferAny; use glium::vertex::VertexBufferAny;
@@ -21,10 +22,10 @@ where
let mut next_frame_time = Instant::now(); let mut next_frame_time = Instant::now();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
let run_callback = match event.to_static() { let run_callback = match event.to_static() {
Some(Event::NewEvents(cause)) => match cause { Some(Event::NewEvents(cause)) => matches!(
StartCause::ResumeTimeReached { .. } | StartCause::Init => true, cause,
_ => false, StartCause::ResumeTimeReached { .. } | StartCause::Init
}, ),
Some(event) => { Some(event) => {
events_buffer.push(event); events_buffer.push(event);
false false
@@ -57,157 +58,211 @@ where
fn get_biome(line: &str, index: usize) -> u32 { fn get_biome(line: &str, index: usize) -> u32 {
let biome_char = line.chars().nth(index).unwrap(); let biome_char = line.chars().nth(index).unwrap();
return if '0' <= biome_char && biome_char <= '9' { if ('0'..='9').contains(&biome_char) {
biome_char.to_digit(10).unwrap() biome_char.to_digit(10).unwrap()
} else { } else {
(10 + (biome_char as u8 - 'A' as u8)) as u32 (10 + (biome_char as u8 - b'A')) as u32
}; }
} }
fn get_altitude(height_columns: &Vec<&str>, columns_index: usize) -> f32 { fn get_altitude(height_columns: &[&str], columns_index: usize) -> f32 {
let height_str = height_columns[columns_index]; let height_str = height_columns[columns_index];
return if height_str == "N" { if height_str == "N" {
-100.0 SEABED_ALTITUDE
} else { } else {
height_str.parse::<f32>().unwrap() height_str.parse::<f32>().unwrap()
}; }
} }
const SEABED_ALTITUDE: f32 = -100.0;
// seabed altitude - biggest drop * tiles in a chunk * (chunks in a row - 1)
const ALTITUDE_BETWEEN_CHUNKS: f32 = SEABED_ALTITUDE - 16.17 * 250.0 * 30.0;
pub const CHUNK_SIZE: u32 = 1_000;
/// Returns a vertex buffer that should be rendered as `TrianglesList`. /// Returns a vertex buffer that should be rendered as `TrianglesList`.
pub fn load_ground(display: &Display) -> (VertexBufferAny, Vec<[u32; 2]>) { pub fn load_ground(display: &Display) -> VertexBufferAny {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct Vertex { struct Vertex {
altitude: f32, altitude: f32,
normal: [f32; 3],
index: u32, index: u32,
biome: u32, biome: u32,
chunk: [u32; 2],
} }
implement_vertex!(Vertex, altitude, normal, index, biome); implement_vertex!(Vertex, altitude, index, biome, chunk);
let mut vertex_data: Vec<Vertex> = Vec::new(); let mut vertex_data: Vec<Vertex> = Vec::new();
let mut chunks: Vec<[u32; 2]> = Vec::new();
let ground_folder = "Extensions/LemnosLife/Map/Altis/Ground/"; let ground_folder = "Extensions/LemnosLife/Map/Altis/Ground/";
let paths = fs::read_dir(ground_folder).unwrap(); let paths = fs::read_dir(ground_folder).unwrap();
let mut i: u32 = 0; for (i, path) in (0_u32..).zip(paths) {
for path in paths {
let file_name = path let file_name = path
.unwrap() .unwrap()
.path() .path()
.to_str() .to_str()
.unwrap() .unwrap()
.replace("Extensions/LemnosLife/Map/Altis/Ground/", "") .replace(ground_folder, "")
.replace(".height", ""); .replace(".height", "");
println!("{}", file_name); println!("{}", file_name);
let file_name_parts: Vec<&str> = file_name.split(' ').collect(); let file_name_parts: Vec<&str> = file_name.split(' ').collect();
chunks.push([file_name_parts[0].parse().unwrap(), file_name_parts[1].parse().unwrap()]); let chunk = [0, 1].map(|file_name_parts_index| {
file_name_parts[file_name_parts_index]
.parse::<u32>()
.unwrap()
* CHUNK_SIZE
});
// have deleted a few height files to make it work // have deleted a few height files to make it work
// TODO: use triangle strip let [height_contents, biomes_contents] =
let height_contents = fs::read_to_string(&format!( [["Ground", "height"], ["Biomes", "biomes"]].map(|[folder, extension]| {
"Extensions/LemnosLife/Map/Altis/Ground/{}.height", fs::read_to_string(&format!(
file_name "Extensions/LemnosLife/Map/Altis/{folder}/{file_name}.{extension}"
)) ))
.expect("Unable to load ground file!"); .unwrap_or_else(|_| panic!("Unable to load {} file!", extension))
});
let biomes_contents = fs::read_to_string(&format!( let height_lines: Vec<&str> = height_contents.split('\n').rev().collect();
"Extensions/LemnosLife/Map/Altis/Biomes/{}.biomes",
file_name
))
.expect("Unable to load biomes file!");
println!("alpha");
let height_lines: Vec<&str> = height_contents.split('\n').collect();
let biomes_lines: Vec<&str> = biomes_contents.split('\n').collect(); let biomes_lines: Vec<&str> = biomes_contents.split('\n').collect();
println!("beta");
let mut index: u32 = 0; let mut index: u32 = 0;
for lines_index in 0..height_lines.len() - 1 { for lines_index in 0..height_lines.len() - 1 {
let height_line = height_lines[lines_index]; let [height_columns, height_next_columns]: [Vec<&str>; 2] =
let height_next_line = height_lines[lines_index + 1]; [height_lines[lines_index], height_lines[lines_index + 1]]
let height_columns: Vec<&str> = height_line.split(' ').collect(); .map(|height_line| height_line.split(' '))
let height_next_columns: Vec<&str> = height_next_line.split(' ').collect(); .map(|height_columns_split| {
if lines_index % 2 == 0 {
height_columns_split.collect()
} else {
height_columns_split.rev().collect()
}
});
let biomes_line = biomes_lines[lines_index / 2]; let biomes_line = biomes_lines[lines_index / 2];
for columns_index in 0..height_columns.len() - 1 { for columns_index in 0..height_columns.len() {
let column = get_altitude(&height_columns, columns_index); let [column, column_below]: [f32; 2] = [&height_columns, &height_next_columns]
let column_right = get_altitude(&height_columns, columns_index + 1); .map(|height_columns| get_altitude(height_columns, columns_index));
let column_below = get_altitude(&height_next_columns, columns_index);
let column_below_right = get_altitude(&height_next_columns, columns_index + 1);
let biome = get_biome(biomes_line, columns_index / 2); let biome_index = if lines_index % 2 == 0 {
columns_index
} else {
height_columns.len() - columns_index
} / 2;
let biome = get_biome(biomes_line, biome_index);
// TODO: compute normals if necessary. if lines_index == 0 && columns_index == 0 {
// These vertexes respectively define the end of the wall below the ground and make the cursor ready to be used.
[ALTITUDE_BETWEEN_CHUNKS, column]
.into_iter()
.for_each(|altitude| {
vertex_data.push(Vertex {
altitude,
index,
biome,
chunk,
})
});
}
// First triangle. [column, column_below].into_iter().for_each(|altitude| {
vertex_data.push(Vertex {
vertex_data.push(Vertex { altitude,
altitude: column, index,
normal: [0.0, 0.0, 1.0], biome,
index, chunk,
biome, });
index += 1
}); });
index += 1;
vertex_data.push(Vertex {
altitude: column_right,
normal: [0.0, 0.0, 1.0],
index,
biome,
});
index += 1;
vertex_data.push(Vertex {
altitude: column_below,
normal: [0.0, 0.0, 1.0],
index,
biome,
});
index += 1;
// Second triangle.
vertex_data.push(Vertex {
altitude: column_below,
normal: [0.0, 0.0, 1.0],
index,
biome,
});
index += 1;
vertex_data.push(Vertex {
altitude: column_right,
normal: [0.0, 0.0, 1.0],
index,
biome,
});
index += 1;
vertex_data.push(Vertex {
altitude: column_below_right,
normal: [0.0, 0.0, 1.0],
index,
biome,
});
index += 1;
} }
} }
i += 1; // This vertex remove the wall across the chunks above the ground.
vertex_data.push(Vertex {
altitude: ALTITUDE_BETWEEN_CHUNKS,
index,
biome: 0,
chunk,
});
// i == 250 thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: BufferCreationError(OutOfMemory)', src/support/mod.rs:203:10 // i == 250 thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: BufferCreationError(OutOfMemory)', src/support/mod.rs:203:10
if i == 200 { /*if i == 1 {
break; break;
} }*/
} }
println!("charlie"); println!("charlie");
(glium::vertex::VertexBuffer::new(display, &vertex_data) glium::vertex::VertexBuffer::new(display, &vertex_data)
.unwrap() .unwrap()
.into(), chunks) .into()
}
pub fn load_wavefront(display: &Display, data: &[u8]) -> VertexBufferAny {
#[derive(Copy, Clone)]
struct Vertex {
position: [f32; 3],
texture_index: u32,
texture_coordinates: [f32; 2],
}
implement_vertex!(Vertex, position, texture_index, texture_coordinates);
let mut data = ::std::io::BufReader::new(data);
let data = obj::ObjData::load_buf(&mut data).unwrap();
let mut vertex_data = Vec::new();
for object in data.objects.iter() {
for group in object.groups.iter() {
for poly in &group.polys {
match poly {
obj::SimplePolygon(indices) => {
for v in indices.iter().take(3) {
let position = data.position[v.0];
let position = [position[0], position[2], position[1]];
let texture_coordinates = data.texture[v.1.unwrap()];
let texture_index = if let obj::ObjMaterial::Ref(texture) =
group.material.as_ref().unwrap()
{
STRUCTURES_TEXTURES_REVERSED[texture]
} else {
panic!()
};
vertex_data.push(Vertex {
position,
texture_index,
texture_coordinates,
})
}
for i in [0, 2, 3] {
let v = indices[i];
let position = data.position[v.0];
let position = [position[0], position[2], position[1]];
let texture_coordinates = data.texture[v.1.unwrap()];
let texture_index = if let obj::ObjMaterial::Ref(texture) =
group.material.as_ref().unwrap()
{
STRUCTURES_TEXTURES_REVERSED[texture]
} else {
panic!()
};
vertex_data.push(Vertex {
position,
texture_index,
texture_coordinates,
})
}
}
}
}
}
}
glium::vertex::VertexBuffer::new(display, &vertex_data)
.unwrap()
.into()
} }