terra 0.2.0

A rendering library for large scale terrains
Documentation
use gfx;
use gfx_core;
use gfx::traits::*;
use gfx::format::*;

use terrain::tile_cache::LayerType;
use super::*;

gfx_defines!{
    vertex NodeState {
        position: [f32; 2] = "vPosition",
        side_length: f32 = "vSideLength",
        min_distance: f32 = "vMinDistance",
        heights_origin: [f32; 3] = "heightsOrigin",
        texture_origin: [f32; 2] ="textureOrigin",
        colors_layer: f32 = "colorsLayer",
        normals_layer: f32 = "normalsLayer",
        water_layer: f32 = "waterLayer",
        texture_step: f32 = "textureStep",
    }
}

gfx_pipeline!( pipe {
    instances: gfx::InstanceBuffer<NodeState> = (),

    model_view_projection: gfx::Global<[[f32; 4]; 4]> = "modelViewProjection",
    resolution: gfx::Global<i32> = "resolution",
    camera_position: gfx::Global<[f32;3]> = "cameraPosition",

    heights: gfx::TextureSampler<f32> = "heights",
    normals: gfx::TextureSampler<[f32; 4]> = "normals",
    colors: gfx::TextureSampler<[f32; 4]> = "colors",
    water: gfx::TextureSampler<[f32; 4]> = "water",
    materials: gfx::TextureSampler<[f32; 4]> = "materials",
    sky: gfx::TextureSampler<[f32; 4]> = "sky",
    ocean_surface: gfx::TextureSampler<[f32; 4]> = "oceanSurface",
    noise: gfx::TextureSampler<[f32; 4]> = "noise",
    noise_wavelength: gfx::Global<f32> = "noiseWavelength",

    color_buffer: gfx::RenderTarget<Srgba8> = "OutColor",
    depth_buffer: gfx::DepthTarget<DepthStencil> = gfx::preset::depth::LESS_EQUAL_WRITE,
});

impl<R, F> QuadTree<R, F>
where
    R: gfx::Resources,
    F: gfx::Factory<R>,
{
    pub(crate) fn make_pso(
        factory: &mut F,
        shader: &gfx::ShaderSet<R>,
    ) -> gfx::PipelineState<R, pipe::Meta> {
        factory
            .create_pipeline_state(
                shader,
                gfx::Primitive::TriangleList,
                gfx::state::Rasterizer {
                    front_face: gfx::state::FrontFace::Clockwise,
                    cull_face: gfx::state::CullFace::Nothing,
                    method: gfx::state::RasterMethod::Fill,
                    offset: None,
                    samples: None,
                },
                pipe::new(),
            )
            .unwrap()
    }

    pub fn update_shaders(&mut self) {
        if self.shader.refresh(
            &mut self.factory,
            &mut self.shaders_watcher,
        )
        {
            self.pso = Self::make_pso(&mut self.factory, self.shader.as_shader_set());
        }
    }

    pub fn render<C: gfx_core::command::Buffer<R>>(&mut self, encoder: &mut gfx::Encoder<R, C>) {
        assert_eq!(self.tile_cache_layers[LayerType::Colors.index()].resolution(),
                   self.tile_cache_layers[LayerType::Normals.index()].resolution());
        assert_eq!(self.tile_cache_layers[LayerType::Colors.index()].border(),
                   self.tile_cache_layers[LayerType::Normals.index()].border());

        let resolution = self.tile_cache_layers[LayerType::Heights.index()].resolution() - 1;
        let texture_resolution = self.tile_cache_layers[LayerType::Normals.index()].resolution();
        let texture_border = self.tile_cache_layers[LayerType::Normals.index()].border();
        let texture_ratio = (texture_resolution - 2 * texture_border) as f32 /
            texture_resolution as f32;
        let texture_step = texture_ratio / resolution as f32;
        let texture_origin = texture_border as f32 / texture_resolution as f32;

        fn find_texture_slots<R: gfx::Resources>(
            nodes: &Vec<Node>,
            tile_cache_layers: &VecMap<TileCache<R>>,
            id: NodeId,
            texture_ratio: f32,
        ) -> (f32, f32, f32, Vector2<f32>, f32) {
            let (ancestor, generations, offset) = Node::find_ancestor(&nodes, id, |id| {
                tile_cache_layers[LayerType::Colors.index()].contains(id)
            }).unwrap();
            let colors_slot = tile_cache_layers[LayerType::Colors.index()]
                .get_slot(ancestor)
                .map(|s| s as f32)
                .unwrap();
            let normals_slot = tile_cache_layers[LayerType::Normals.index()]
                .get_slot(ancestor)
                .map(|s| s as f32)
                .unwrap_or(-1.0);
            let water_slot = tile_cache_layers[LayerType::Water.index()]
                .get_slot(ancestor)
                .map(|s| s as f32)
                .unwrap();
            let scale = (0.5f32).powi(generations as i32);
            let offset = Vector2::new(
                offset.x as f32 * texture_ratio * scale,
                offset.y as f32 * texture_ratio * scale,
            );
            (colors_slot, normals_slot, water_slot, offset, scale)
        };

        self.node_states.clear();
        for &id in self.visible_nodes.iter() {
            let heights_slot = self.tile_cache_layers[LayerType::Heights.index()]
                .get_slot(id)
                .unwrap() as f32;
            let (colors_layer, normals_layer, water_layer, texture_offset, texture_step_scale) =
                find_texture_slots(&self.nodes, &self.tile_cache_layers, id, texture_ratio);
            self.node_states.push(NodeState {
                position: [self.nodes[id].bounds.min.x, self.nodes[id].bounds.min.z],
                side_length: self.nodes[id].side_length,
                min_distance: self.nodes[id].min_distance,
                heights_origin: [0.0, 0.0, heights_slot],
                texture_origin: [
                    texture_origin + texture_offset.x,
                    texture_origin + texture_offset.y,
                ],
                colors_layer,
                normals_layer,
                water_layer,
                texture_step: texture_step * texture_step_scale,
            });
        }
        for &(id, mask) in self.partially_visible_nodes.iter() {
            assert!(mask < 15);
            for i in 0..4u8 {
                if mask & (1 << i) != 0 {
                    let side_length = self.nodes[id].side_length * 0.5;
                    let offset = ((i % 2) as f32, (i / 2) as f32);
                    let heights_slot = self.tile_cache_layers[LayerType::Heights.index()]
                        .get_slot(id)
                        .unwrap() as f32;
                    let (colors_layer,
                         normals_layer,
                         water_layer,
                         texture_offset,
                         texture_step_scale) =
                        find_texture_slots(&self.nodes, &self.tile_cache_layers, id, texture_ratio);
                    self.node_states.push(NodeState {
                        position: [
                            self.nodes[id].bounds.min.x + offset.0 * side_length,
                            self.nodes[id].bounds.min.z + offset.1 * side_length,
                        ],
                        side_length,
                        min_distance: self.nodes[id].min_distance,
                        heights_origin: [
                            offset.0 * (0.5 - 0.5 / (resolution + 1) as f32),
                            offset.1 * (0.5 - 0.5 / (resolution + 1) as f32),
                            heights_slot,
                        ],
                        texture_origin: [
                            texture_origin + texture_offset.x +
                                offset.0 * (0.5 - texture_origin) * texture_step_scale,
                            texture_origin + texture_offset.y +
                                offset.1 * (0.5 - texture_origin) * texture_step_scale,
                        ],
                        colors_layer,
                        normals_layer,
                        water_layer,
                        texture_step: texture_step * texture_step_scale,
                    });
                }
            }
        }

        encoder
            .update_buffer(&self.pipeline_data.instances, &self.node_states[..], 0)
            .unwrap();

        self.pipeline_data.resolution = resolution as i32;
        encoder.draw(
            &gfx::Slice {
                start: 0,
                end: (resolution * resolution * 6) as u32,
                base_vertex: 0,
                instances: Some((self.visible_nodes.len() as u32, 0)),
                buffer: gfx::IndexBuffer::Auto,
            },
            &self.pso,
            &self.pipeline_data,
        );

        self.pipeline_data.resolution = (resolution / 2) as i32;
        encoder.draw(
            &gfx::Slice {
                start: 0,
                end: ((resolution / 2) * (resolution / 2) * 6) as u32,
                base_vertex: 0,
                instances: Some((
                    (self.node_states.len() - self.visible_nodes.len()) as u32,
                    self.visible_nodes.len() as u32,
                )),
                buffer: gfx::IndexBuffer::Auto,
            },
            &self.pso,
            &self.pipeline_data,
        );
    }
}