midpoint_ui/renderer/
Landscape.rs

1use image::io::Reader as ImageReader;
2use nalgebra::{Matrix4, Vector3};
3use rapier3d::math::Point;
4use wgpu::util::{DeviceExt, TextureDataOrder};
5
6use crate::renderer::core::Vertex;
7
8use crate::renderer::Transform::{matrix4_to_raw_array, Transform};
9
10use crate::renderer::core::LandscapeData;
11
12pub struct Landscape {
13    pub transform: Transform,
14    pub vertex_buffer: wgpu::Buffer,
15    pub index_buffer: wgpu::Buffer,
16    pub index_count: u32,
17    pub bind_group: wgpu::BindGroup,
18    pub texture_bind_group: wgpu::BindGroup,
19}
20
21impl Landscape {
22    pub fn new(
23        data: &LandscapeData,
24        device: &wgpu::Device,
25        queue: &wgpu::Queue,
26        bind_group_layout: &wgpu::BindGroupLayout,
27        texture_bind_group_layout: &wgpu::BindGroupLayout,
28        color_render_mode_buffer: &wgpu::Buffer,
29    ) -> Self {
30        // .tif heightmap load
31
32        // Load the heightmap image
33        // TODO: load image bytes from background
34
35        // potential texture from the heightmap data
36        // seems unnecessary
37        // let heightmap_texture = device.create_texture_with_data(
38        //     &queue,
39        //     &wgpu::TextureDescriptor {
40        //         label: Some("Heightmap Texture"),
41        //         size: wgpu::Extent3d {
42        //             width: heightmap.width(),
43        //             height: heightmap.height(),
44        //             depth_or_array_layers: 1,
45        //         },
46        //         mip_level_count: 1,
47        //         sample_count: 1,
48        //         dimension: wgpu::TextureDimension::D2,
49        //         format: wgpu::TextureFormat::R16Uint,
50        //         usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
51        //         view_formats: &[],
52        //     },
53        //     TextureDataOrder::MipMajor,
54        //     &heightmap,
55        // );
56
57        // TODO: .png mask (soil, rocks) load
58
59        // load actual vertices and indices (most important for now)
60        let scale = 1.0;
61        let (vertices, indices, rapier_vertices) = Self::generate_terrain(data, scale);
62
63        let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
64            label: Some("Landscape Vertex Buffer"),
65            contents: bytemuck::cast_slice(&vertices),
66            usage: wgpu::BufferUsages::VERTEX,
67        });
68
69        let index_buffer: wgpu::Buffer =
70            device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
71                label: Some("Landscape Index Buffer"),
72                contents: bytemuck::cast_slice(&indices),
73                usage: wgpu::BufferUsages::INDEX,
74            });
75
76        // set uniform buffer for transforms
77        let empty_buffer = Matrix4::<f32>::identity();
78        let raw_matrix = matrix4_to_raw_array(&empty_buffer);
79
80        let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
81            label: Some("Model GLB Uniform Buffer"),
82            contents: bytemuck::cast_slice(&raw_matrix),
83            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
84        });
85
86        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
87            layout: &bind_group_layout,
88            entries: &[wgpu::BindGroupEntry {
89                binding: 0,
90                resource: uniform_buffer.as_entire_binding(),
91            }],
92            label: None,
93        });
94
95        // creating default texture view and sampler just like in Model, for use when model has no textures, but uses same shader
96        // Create a default empty texture and sampler
97        let default_texture = device.create_texture(&wgpu::TextureDescriptor {
98            label: Some("Default Empty Texture"),
99            size: wgpu::Extent3d {
100                width: 1,
101                height: 1,
102                depth_or_array_layers: 1,
103            },
104            mip_level_count: 1,
105            sample_count: 1,
106            dimension: wgpu::TextureDimension::D2,
107            format: wgpu::TextureFormat::Rgba8UnormSrgb,
108            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
109            view_formats: &[],
110        });
111
112        let default_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
113            address_mode_u: wgpu::AddressMode::ClampToEdge,
114            address_mode_v: wgpu::AddressMode::ClampToEdge,
115            address_mode_w: wgpu::AddressMode::ClampToEdge,
116            mag_filter: wgpu::FilterMode::Linear,
117            min_filter: wgpu::FilterMode::Linear,
118            mipmap_filter: wgpu::FilterMode::Nearest,
119            ..Default::default()
120        });
121
122        let default_texture_view =
123            default_texture.create_view(&wgpu::TextureViewDescriptor::default());
124
125        // potential texture bind group
126        let texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
127            layout: &texture_bind_group_layout,
128            entries: &[
129                wgpu::BindGroupEntry {
130                    binding: 0,
131                    resource: wgpu::BindingResource::TextureView(&default_texture_view),
132                },
133                wgpu::BindGroupEntry {
134                    binding: 1,
135                    resource: wgpu::BindingResource::Sampler(&default_sampler),
136                },
137                wgpu::BindGroupEntry {
138                    binding: 2,
139                    resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
140                        buffer: color_render_mode_buffer,
141                        offset: 0,
142                        size: None,
143                    }),
144                },
145            ],
146            label: None,
147        });
148
149        Self {
150            index_count: indices.len() as u32,
151            vertex_buffer,
152            index_buffer,
153            bind_group,
154            texture_bind_group,
155            transform: Transform::new(
156                Vector3::new(0.0, 0.0, 0.0),
157                Vector3::new(0.0, 0.0, 0.0),
158                Vector3::new(1.0, 1.0, 1.0),
159                uniform_buffer,
160            ),
161        }
162    }
163
164    // Generate vertex buffer from heightmap data
165    pub fn generate_terrain(
166        data: &LandscapeData,
167        scale: f32,
168    ) -> (Vec<Vertex>, Vec<u32>, Vec<Point<f32>>) {
169        // let width = heightmap.width() as usize;
170        // let height = heightmap.height() as usize;
171        let mut vertices = Vec::with_capacity(data.width * data.height);
172        let mut rapier_vertices = Vec::with_capacity(data.width * data.height);
173        let mut indices = Vec::new();
174
175        for y in 0..data.height {
176            for x in 0..data.width {
177                // let pixel = heightmap.get_pixel(x as u32, y as u32);
178                // let height_value = pixel[0] as f32 / 255.0 * scale;
179                // let position = [
180                //     x as f32 / width as f32 * 2.0 - 1.0,
181                //     height_value,
182                //     y as f32 / height as f32 * 2.0 - 1.0,
183                // ];
184                // let tex_coords = [x as f32 / width as f32, y as f32 / height as f32];
185                vertices.push(Vertex {
186                    position: data.pixel_data[y][x].position,
187                    normal: [0.0, 0.0, 0.0],
188                    tex_coords: data.pixel_data[y][x].tex_coords,
189                    color: [1.0, 1.0, 1.0],
190                });
191                rapier_vertices.push(Point::new(
192                    data.pixel_data[y][x].position[0],
193                    data.pixel_data[y][x].position[1],
194                    data.pixel_data[y][x].position[2],
195                ));
196            }
197        }
198
199        for y in 0..(data.height - 1) {
200            for x in 0..(data.width - 1) {
201                let top_left = (y * data.width + x) as u32;
202                let top_right = top_left + 1;
203                let bottom_left = top_left + data.width as u32;
204                let bottom_right = bottom_left + 1;
205
206                // // Triangle 1
207                // indices.push(top_left);
208                // indices.push(bottom_left);
209                // indices.push(top_right);
210
211                // // Triangle 2
212                // indices.push(top_right);
213                // indices.push(bottom_left);
214                // indices.push(bottom_right);
215
216                // like everdaygui
217                indices.push(top_left);
218                indices.push(top_right);
219                indices.push(bottom_right);
220
221                indices.push(bottom_right);
222                indices.push(bottom_left);
223                indices.push(top_left);
224            }
225        }
226
227        (vertices, indices, rapier_vertices)
228    }
229}