monstertruck-render 0.2.0

Shape and polygon mesh visualization
Documentation
mod common;
use common::Plane;
use image::{DynamicImage, ImageBuffer, Rgba};
use monstertruck_gpu::*;
use monstertruck_meshing::prelude::obj;
use monstertruck_modeling::*;
use monstertruck_render::*;
use std::sync::Arc;
use wgpu::*;

const CUBE_OBJ: &[u8] = include_bytes!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/../resources/obj/cube.obj",
));

const PICTURE_SIZE: (u32, u32) = (1024, 768);

fn test_scene(backend: Backends) -> Scene {
    let instance = wgpu::Instance::new(&InstanceDescriptor {
        backends: backend,
        ..Default::default()
    });
    let handler = common::init_device(&instance);
    Scene::new(
        handler,
        &SceneDescriptor {
            studio: StudioConfig {
                camera: Camera {
                    matrix: Matrix4::look_at_rh(
                        Point3::new(-1.0, 2.5, 2.0),
                        Point3::new(0.25, 0.25, 0.25),
                        Vector3::unit_y(),
                    )
                    .invert()
                    .unwrap(),
                    method: ProjectionMethod::perspective(Rad(std::f64::consts::PI / 4.0)),
                    near_clip: 0.1,
                    far_clip: 100.0,
                },
                lights: vec![Light {
                    position: Point3::new(-3.0, 4.0, -2.0),
                    color: Vector3::new(1.0, 1.0, 1.0),
                    light_type: LightType::Point,
                }],
                ..Default::default()
            },
            render_texture: RenderTextureConfig {
                canvas_size: PICTURE_SIZE,
                ..Default::default()
            },
            ..Default::default()
        },
    )
}

fn nontex_raytracing(scene: &mut Scene) -> Vec<u8> {
    let mut shader = include_str!("../src/shaders/microfacet-module.wgsl").to_string();
    shader += include_str!("shaders/raytraces.wgsl");
    let plane = Plane {
        shader: &shader,
        vs_entpt: "vs_main",
        fs_entpt: "nontex_raytracing",
        id: RenderId::generate(),
    };
    common::render_one(scene, &plane)
}

fn nontex_polygon(scene: &mut Scene, creator: &InstanceCreator) -> Vec<u8> {
    let cube: PolygonInstance = creator.create_instance(
        &obj::read(CUBE_OBJ).unwrap(),
        &PolygonState {
            material: Material {
                albedo: Vector4::new(1.0, 1.0, 1.0, 1.0),
                roughness: 0.5,
                reflectance: 0.25,
                ambient_ratio: 0.02,
                background_ratio: 0.0,
                alpha_blend: false,
            },
            ..Default::default()
        },
    );
    common::render_one(scene, &cube)
}

fn exec_nontex_render_test(backend: Backends, out_dir: &str) {
    let out_dir = out_dir.to_string();
    std::fs::create_dir_all(&out_dir).unwrap();
    let mut scene = test_scene(backend);
    let creator = scene.instance_creator();
    let buffer0 = nontex_raytracing(&mut scene);
    let buffer1 = nontex_polygon(&mut scene, &creator);
    let filename = out_dir.clone() + "nontex-raytracing.png";
    common::save_buffer(filename, &buffer0, PICTURE_SIZE);
    let filename = out_dir + "nontex-polygon.png";
    common::save_buffer(filename, &buffer1, PICTURE_SIZE);
    let diff = common::count_difference(&buffer0, &buffer1);
    println!("{diff} pixel difference: ray-tracing and polymesh");
    assert!(diff < 10);
}

#[test]
fn nontex_render_test() { common::os_alt_exec_test(exec_nontex_render_test); }

fn generate_texture(scene: &mut Scene, out_dir: String) -> DynamicImage {
    let buffer = common::gradation_texture(scene);
    common::save_buffer(out_dir + "gradation-texture.png", &buffer, PICTURE_SIZE);
    let image_buffer =
        ImageBuffer::<Rgba<_>, _>::from_raw(PICTURE_SIZE.0, PICTURE_SIZE.1, buffer).unwrap();
    DynamicImage::ImageRgba8(image_buffer)
}

fn tex_raytracing(scene: &mut Scene) -> Vec<u8> {
    let shader = include_str!("../src/shaders/microfacet-module.wgsl").to_string()
        + include_str!("shaders/raytraces.wgsl");
    let plane = Plane {
        shader: &shader,
        vs_entpt: "vs_main",
        fs_entpt: "tex_raytracing",
        id: RenderId::generate(),
    };
    common::render_one(scene, &plane)
}

fn tex_polygon(
    scene: &mut Scene,
    creator: &InstanceCreator,
    gradtex: &Arc<DynamicImage>,
) -> Vec<u8> {
    let attach = creator.create_texture(gradtex);
    let cube: PolygonInstance = creator.create_instance(
        &obj::read(CUBE_OBJ).unwrap(),
        &PolygonState {
            material: Material {
                albedo: Vector4::new(1.0, 1.0, 1.0, 1.0),
                roughness: 0.5,
                reflectance: 0.25,
                ambient_ratio: 0.02,
                background_ratio: 0.0,
                alpha_blend: false,
            },
            texture: Some(attach),
            ..Default::default()
        },
    );
    common::render_one(scene, &cube)
}

fn exec_tex_render_test(backend: Backends, out_dir: &str) {
    let out_dir = out_dir.to_string();
    std::fs::create_dir_all(&out_dir).unwrap();
    let mut scene = test_scene(backend);
    let creator = scene.instance_creator();
    let image = Arc::new(generate_texture(&mut scene, out_dir.clone()));
    let anti_buffer = nontex_raytracing(&mut scene);
    let buffer0 = tex_raytracing(&mut scene);
    let buffer1 = tex_polygon(&mut scene, &creator, &image);
    let filename = out_dir.clone() + "tex-raytracing.png";
    common::save_buffer(filename, &buffer0, PICTURE_SIZE);
    let filename = out_dir + "tex-polygon.png";
    common::save_buffer(filename, &buffer1, PICTURE_SIZE);
    let diff = common::count_difference(&buffer0, &buffer1);
    let anti_diff = common::count_difference(&anti_buffer, &buffer0);
    println!("{diff} pixel difference: ray-tracing and polymesh");
    assert!(diff < 10);
    assert!(anti_diff > 1000);
}

#[test]
fn tex_render_test() { common::os_alt_exec_test(exec_tex_render_test) }