crystal-engine 0.2.1

A simple 3D engine
Documentation
use super::data::ModelDataGroup;
use crate::render::{Material, RenderPipeline};
use cgmath::Matrix4;
use std::{mem, sync::Arc};
use vulkano::{
    command_buffer::AutoCommandBufferBuilder, descriptor::descriptor_set::PersistentDescriptorSet,
    sync::GpuFuture,
};

impl super::Model {
    pub fn render(
        &self,
        mut future: Box<dyn GpuFuture>,
        groups: &[ModelDataGroup],
        base_matrix: Matrix4<f32>,
        data: &mut vs::ty::Data,
        mut command_buffer_builder: AutoCommandBufferBuilder,

        pipeline: &mut RenderPipeline,
    ) -> (AutoCommandBufferBuilder, Box<dyn GpuFuture>) {
        if !self.texture_future.read().is_empty() {
            let texture_futures = mem::replace(&mut *self.texture_future.write(), Vec::new());
            for fut in texture_futures {
                future = Box::new(future.join(fut)) as _;
            }
        }
        let layout = pipeline.pipeline.descriptor_set_layout(0).unwrap();

        for (group, group_data) in self.groups.iter().zip(groups.iter()) {
            let texture = group
                .texture
                .as_ref()
                .unwrap_or(&pipeline.empty_texture)
                .clone();

            data.world = (base_matrix * group_data.matrix).into();
            update_uniform_material(data, group.material.as_ref());

            let uniform_buffer_subbuffer = pipeline.uniform_buffer.next(*data).unwrap();

            let set = Arc::new(
                PersistentDescriptorSet::start(layout.clone())
                    .add_buffer(uniform_buffer_subbuffer)
                    .unwrap()
                    .add_sampled_image(texture, pipeline.sampler.clone())
                    .unwrap()
                    .build_with_pool(&mut pipeline.descriptor_pool)
                    .unwrap(),
            );

            let vertex_buffer = group
                .vertex_buffer
                .as_ref()
                .or_else(|| self.vertex_buffer.as_ref())
                .expect("Model has no valid vertex buffer");

            command_buffer_builder = if let Some(index) = group.index.as_ref() {
                command_buffer_builder
                    .draw_indexed(
                        pipeline.pipeline.clone(),
                        &pipeline.dynamic_state,
                        vec![vertex_buffer.clone()],
                        index.clone(),
                        set.clone(),
                        (),
                    )
                    .unwrap()
            } else {
                command_buffer_builder
                    .draw(
                        pipeline.pipeline.clone(),
                        &pipeline.dynamic_state,
                        vec![vertex_buffer.clone()],
                        set,
                        (),
                    )
                    .unwrap()
            };
        }

        (command_buffer_builder, future)
    }
}

fn update_uniform_material(data: &mut vs::ty::Data, material: Option<&Material>) {
    let material = material.cloned().unwrap_or_default();
    data.material_ambient_r = material.ambient[0];
    data.material_ambient_g = material.ambient[1];
    data.material_ambient_b = material.ambient[2];
    data.material_specular_r = material.specular[0];
    data.material_specular_g = material.specular[1];
    data.material_specular_b = material.specular[2];
    data.material_diffuse_r = material.diffuse[0];
    data.material_diffuse_g = material.diffuse[1];
    data.material_diffuse_b = material.diffuse[2];
    data.material_shininess = material.shininess;
}

pub mod vs {
    vulkano_shaders::shader! {
        ty: "vertex",
        src: "#version 450

layout(location = 0) in vec3 position_in;
layout(location = 1) in vec3 normal_in;
layout(location = 2) in vec2 tex_coord_in;

layout(location = 0) out vec2 fragment_tex_coord;
layout(location = 1) out vec3 fragment_normal;

struct DirectionalLight {
    float direction_x;
    float direction_y;
    float direction_z;
    float color_ambient_r;
    float color_ambient_g;
    float color_ambient_b;
    float color_diffuse_r;
    float color_diffuse_g;
    float color_diffuse_b;
    float color_specular_r;
    float color_specular_g;
    float color_specular_b;
};

layout(set = 0, binding = 0) uniform Data {
    mat4 world;
    mat4 view;
    mat4 proj;
    DirectionalLight[100] lights;
    int lightCount;

    float camera_x;
    float camera_y;
    float camera_z;

    float material_ambient_r;
    float material_ambient_g;
    float material_ambient_b;
    float material_diffuse_r;
    float material_diffuse_g;
    float material_diffuse_b;
    float material_specular_r;
    float material_specular_g;
    float material_specular_b;
    float material_shininess;
} uniforms;

void main() {
    mat4 worldview = uniforms.view * uniforms.world;
    gl_Position = uniforms.proj * worldview * vec4(position_in, 1.0);
    fragment_tex_coord = tex_coord_in;

    fragment_normal = transpose(inverse(mat3(worldview))) * normal_in;
}
"
    }
}

pub mod fs {
    vulkano_shaders::shader! {
        ty: "fragment",
        src: "#version 450

layout(location = 0) in vec2 fragment_tex_coord;
layout(location = 1) in vec3 fragment_normal;

layout(location = 0) out vec4 f_color;

struct DirectionalLight {
    float direction_x;
    float direction_y;
    float direction_z;
    float color_ambient_r;
    float color_ambient_g;
    float color_ambient_b;
    float color_diffuse_r;
    float color_diffuse_g;
    float color_diffuse_b;
    float color_specular_r;
    float color_specular_g;
    float color_specular_b;
};

layout(set = 0, binding = 1) uniform sampler2D tex;
layout(set = 0, binding = 0) uniform Data {
    mat4 world;
    mat4 view;
    mat4 proj;
    DirectionalLight[100] lights;
    int lightCount;

    float camera_x;
    float camera_y;
    float camera_z;

    float material_ambient_r;
    float material_ambient_g;
    float material_ambient_b;
    float material_diffuse_r;
    float material_diffuse_g;
    float material_diffuse_b;
    float material_specular_r;
    float material_specular_g;
    float material_specular_b;
    float material_shininess;
} uniforms;

vec3 max_member(vec3 lhs, vec3 rhs) {
    return vec3(
        max(lhs.x, rhs.x),
        max(lhs.y, rhs.y),
        max(lhs.z, rhs.z)
    );
}

vec4 min_member(vec4 lhs, vec4 rhs) {
    return vec4(
        min(lhs.x, rhs.x),
        min(lhs.y, rhs.y),
        min(lhs.z, rhs.z),
        min(lhs.w, rhs.w)
    );
}

vec4 CalcDirLight(DirectionalLight light, vec4 tex_color, vec3 normal, vec3 viewDir)
{
    vec3 direction = vec3(light.direction_x, light.direction_y, light.direction_z);
    vec3 ambient = vec3(light.color_ambient_r, light.color_ambient_g, light.color_ambient_b);
    vec3 diffuse = vec3(light.color_diffuse_r, light.color_diffuse_g, light.color_diffuse_b);
    vec3 specular = vec3(light.color_specular_r, light.color_specular_g, light.color_specular_b);

    vec3 material_ambient = vec3(uniforms.material_ambient_r, uniforms.material_ambient_g, uniforms.material_ambient_b);
    vec3 material_diffuse = vec3(uniforms.material_diffuse_r, uniforms.material_diffuse_g, uniforms.material_diffuse_b);
    vec3 material_specular = vec3(uniforms.material_specular_r, uniforms.material_specular_g, uniforms.material_specular_b);

    vec3 lightDir = normalize(-direction);
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), uniforms.material_shininess);
    // combine results
    ambient  = ambient  * material_ambient;
    diffuse  = diffuse  * diff * material_diffuse;
    specular = specular * spec * material_specular;
    return tex_color * min_member(vec4(ambient + diffuse + specular, 1.0), vec4(1.0, 1.0, 1.0, 1.0));
} 


void main() {
    if(fragment_tex_coord.x < 0.0 && fragment_tex_coord.y < 0.0) {
        f_color = vec4(uniforms.material_ambient_r, uniforms.material_ambient_g, uniforms.material_ambient_b, 1);
    } else {
        f_color = texture(tex, fragment_tex_coord);
    }

    vec3 camera_pos = vec3(uniforms.camera_x, uniforms.camera_y, uniforms.camera_z);
    
    for(int i = 0; i < uniforms.lightCount; i++) {
        f_color = CalcDirLight(
            uniforms.lights[i],
            f_color,
            fragment_normal,
            camera_pos
        );
    }
}
"
    }
}