constructive 0.0.1

Navmesh and path finding library based on constructive solid geometry
Documentation
use constructive::{brush::Brush, tree::BspTree};
use glam::{vec3, Mat4, Quat, Vec2, Vec3};
use ivy_engine::{
    flax::{Entity, World},
    input::layer::InputLayer,
    ivy_assets::AssetCache,
    ivy_core::{
        palette::{Srgb, Srgba},
        update_layer::{FixedTimeStep, Plugin, ScheduledLayer},
        EngineLayer, EntityBuilderExt,
    },
    ivy_game::{
        orbit_camera::OrbitCameraPlugin,
        viewport_camera::{CameraSettings, ViewportCameraLayer},
    },
    ivy_graphics::{
        mesh::{MeshData, NORMAL_ATTRIBUTE, POSITION_ATTRIBUTE, TEX_COORD_ATTRIBUTE},
        texture::TextureData,
    },
    ivy_wgpu::{
        components::forward_pass,
        driver::WinitDriver,
        layer::GraphicsLayer,
        material_desc::{MaterialData, PbrMaterialData},
        mesh_desc::MeshDesc,
        renderer::{EnvironmentData, RenderObjectBundle},
    },
    postprocessing::preconfigured::{SurfacePbrPipelineDesc, SurfacePbrRenderer},
    App, TransformBundle,
};
use winit::{dpi::PhysicalSize, window::WindowAttributes};

pub fn main() -> anyhow::Result<()> {
    tracing_subscriber::fmt::init();

    App::builder()
        .with_driver(WinitDriver::new(
            WindowAttributes::default().with_inner_size(PhysicalSize::new(1920, 1080)),
        ))
        .with_layer(EngineLayer::new())
        .with_layer(GraphicsLayer::new(|world, assets, store, gpu, surface| {
            Ok(SurfacePbrRenderer::new(
                world,
                assets,
                store,
                gpu,
                surface,
                SurfacePbrPipelineDesc::default(),
            ))
        }))
        .with_layer(InputLayer::new())
        .with_layer(
            ScheduledLayer::new(FixedTimeStep::new(0.02))
                .with_plugin(OrbitCameraPlugin)
                .with_plugin(ExamplePlugin),
        )
        .with_layer(ViewportCameraLayer::new(CameraSettings {
            environment_data: EnvironmentData {
                fog_color: Srgb::new(0.0, 0.1, 0.05),
                fog_density: 0.001,
                fog_blend: 1.0,
            },
            fov: 1.0,
        }))
        .run()
}

struct ExamplePlugin;

impl Plugin for ExamplePlugin {
    fn install(
        &self,
        world: &mut World,
        assets: &AssetCache,
        _: &mut ivy_engine::ivy_core::update_layer::ScheduleSetBuilder,
    ) -> anyhow::Result<()> {
        let mut brush = Brush::cube();
        brush.transform(Mat4::from_scale(vec3(10.0, 0.4, 10.0)));
        let mut brush = BspTree::build(brush.faces()).unwrap();

        let clip_brushes = [
            Brush::cube().with_transform(Mat4::from_rotation_translation(
                Quat::from_axis_angle(vec3(0.0, 1.0, 1.0).normalize(), 1.0),
                vec3(1.5, 0.0, 0.5),
            )),
            Brush::cube().with_transform(Mat4::from_rotation_translation(
                Quat::IDENTITY,
                vec3(0.5, 0.9, -0.5),
            )),
            Brush::cube().with_transform(Mat4::from_rotation_translation(
                Quat::from_axis_angle(Vec3::Y, 2.0),
                vec3(3.0, 0.5, 4.0),
            )),
            Brush::uv_sphere().with_transform(Mat4::from_rotation_translation(
                Quat::IDENTITY,
                vec3(7.0, 0.5, 0.0),
            )),
        ];

        for clip in &clip_brushes {
            brush.clip_to(&BspTree::build(clip.faces()).unwrap());
        }

        let flat_material = PbrMaterialData::new();

        Entity::builder()
            .mount(TransformBundle::default())
            .mount(RenderObjectBundle::new(
                MeshDesc::Content(assets.insert(brush_to_mesh(&Brush::new(brush.polygons())))),
                &[(
                    forward_pass(),
                    MaterialData::UnlitMaterial(flat_material.clone()),
                )],
            ))
            .spawn(world);

        for clip in clip_brushes {
            let flat_material = PbrMaterialData::new()
                .with_albedo(TextureData::srgba(Srgba::new(1.0, 0.0, 0.0, 1.0)));

            Entity::builder()
                .mount(TransformBundle::default())
                .mount(RenderObjectBundle::new(
                    MeshDesc::Content(assets.insert(brush_to_mesh(&clip))),
                    &[(
                        forward_pass(),
                        MaterialData::WireframeMaterial(flat_material),
                    )],
                ))
                .spawn(world);
        }

        Ok(())
    }
}

fn brush_to_mesh(brush: &Brush) -> MeshData {
    let vertex_count = brush.faces().len() * 3;
    let mut mesh = MeshData::new()
        .with_attribute(
            POSITION_ATTRIBUTE,
            brush.faces().iter().flat_map(|v| v.points()),
        )
        .with_attribute(
            TEX_COORD_ATTRIBUTE,
            (0..vertex_count as u32).map(|_| Vec2::ZERO),
        )
        .with_attribute(
            NORMAL_ATTRIBUTE,
            brush.faces().iter().flat_map(|v| {
                let n = v.normal();
                [n, n, n]
            }),
        )
        .with_indices(0..vertex_count as u32);

    mesh.generate_tangents().unwrap();
    mesh
}