ark-api 0.17.0-pre.15

Ark API
Documentation
use super::{ffi, DataHandle, Value, ValueConverter, ValueConverterTrait, WorldData};
pub use crate::api::world::World;
use crate::ColorRgba8;
use crate::Mesh;
pub use ffi::{
    GltfFlags, MaterialDesc, MeshOrigin, MeshProperties, MeshRawWithMaterialAndName,
    MeshRawWithName, MeshSimplifiedFlags, ResourceHandleRepr,
};

/// Represents a reference to a mesh registered with the world.
#[must_use]
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct WorldMesh {
    pub(crate) mesh: WorldData,
}

impl WorldMesh {
    fn convert_to_mesh_raw_with_name(
        mesh: &Mesh,
    ) -> Option<(MeshRawWithName, Vec<ffi::MeshStreamLayout>)> {
        let mesh_data = mesh.data.as_ref()?;

        let crate::MeshData {
            name,
            indices,
            positions,
            normals,
            colors,
        } = mesh_data;

        let mut streams: Vec<ffi::MeshStreamLayout> = vec![];

        if !indices.is_empty() {
            streams.push(ffi::MeshStreamLayout {
                semantic: ffi::MeshStreamSemantic::Indices,
                component_format: ffi::MeshComponentFormat::UInt32,
                component_count: 1,
                buffer_ptr: indices.as_ptr() as u32,
                buffer_size: (indices.len() * 4) as u32,
            });
        }

        streams.push(ffi::MeshStreamLayout {
            semantic: ffi::MeshStreamSemantic::Positions,
            component_format: ffi::MeshComponentFormat::Float32,
            component_count: 3,
            buffer_ptr: positions.as_ptr() as u32,
            buffer_size: (positions.len() * 4 * 3) as u32,
        });

        if let Some(normals) = normals {
            streams.push(ffi::MeshStreamLayout {
                semantic: ffi::MeshStreamSemantic::Normals,
                component_format: ffi::MeshComponentFormat::Float32,
                component_count: 3,
                buffer_ptr: normals.as_ptr() as u32,
                buffer_size: (normals.len() * 4 * 3) as u32,
            });
        }

        if let Some(colors) = colors {
            streams.push(ffi::MeshStreamLayout {
                semantic: ffi::MeshStreamSemantic::Colors,
                component_format: ffi::MeshComponentFormat::UInt8,
                component_count: 4,
                buffer_ptr: colors.as_ptr() as u32,
                buffer_size: (colors.len() * 4) as u32,
            });
        }

        Some((
            ffi::MeshRawWithName {
                primitive_topology: ffi::MeshPrimitiveTopology::TriangleList,
                streams_ptr: streams.as_ptr() as u32,
                num_streams: streams.len() as u32,
                debug_name_ptr: name.as_ptr() as u32,
                debug_name_size: name.len() as u32,
            },
            streams,
        ))
    }

    fn convert_to_mesh_raw_with_material(
        mesh: &Mesh,
        material: WorldMaterial,
    ) -> (MeshRawWithMaterialAndName, Vec<ffi::MeshStreamLayout>) {
        let (inner, streams) = Self::convert_to_mesh_raw_with_name(mesh).unwrap();
        let handle = std::mem::ManuallyDrop::new(material.data).get_data_handle();
        (
            ffi::MeshRawWithMaterialAndName {
                inner,
                material: handle.as_ffi(),
                _pad: Default::default(),
            },
            streams,
        )
    }

    /// Creates a `WorldMesh` from the passed-in raw mesh data.
    pub fn create(mesh: Mesh) -> Self {
        let data = Self::convert_to_mesh_raw_with_name(&mesh).expect("Could not create mesh.");
        Self {
            mesh: WorldData::create_struct(ffi::CreateDataType::MeshRawWithName, &data.0),
        }
    }

    /// Creates a `WorldMesh` from the passed-in raw mesh data.
    pub fn create_with_material(mesh: Mesh, material: WorldMaterial) -> Self {
        let data = Self::convert_to_mesh_raw_with_material(&mesh, material);
        Self {
            mesh: WorldData::create_struct(
                ffi::CreateDataType::MeshRawWithMaterialAndName,
                &data.0,
            ),
        }
    }

    /// Creates a `WorldMesh` from GLTF data and additional buffer data.
    /// Supports .gltf and .glb, and a single additional buffer file.
    pub fn create_from_gltf(
        debug_name: &str,
        gltf_data: &[u8],
        buffer_data: &[u8],
        flags: GltfFlags,
    ) -> Self {
        Self {
            mesh: WorldData::create_struct(
                ffi::CreateDataType::MeshGltfNamed,
                &ffi::MeshGltfNamed {
                    debug_name_ptr: debug_name.as_ptr() as u32,
                    debug_name_size: debug_name.len() as u32,
                    gltf_data_ptr: gltf_data.as_ptr() as u32,
                    gltf_data_size: gltf_data.len() as u32,
                    buffer_data_ptr: buffer_data.as_ptr() as u32,
                    buffer_data_size: buffer_data.len() as u32,
                    flags,
                },
            ),
        }
    }

    /// Creates a `WorldMesh` from a GLTF resource handle and additional buffer resource handle
    pub fn create_from_gltf_resource(
        debug_name: &str,
        gltf_resource: ResourceHandleRepr,
        buffer_resource: Option<ResourceHandleRepr>,
        flags: GltfFlags,
    ) -> Self {
        Self {
            mesh: WorldData::create_struct(
                ffi::CreateDataType::MeshGltfResource,
                &ffi::MeshGltfResource {
                    debug_name_ptr: debug_name.as_ptr() as u32,
                    debug_name_size: debug_name.len() as u32,
                    gltf_resource,
                    buffer_resource: buffer_resource.unwrap_or(ffi::INVALID_RESOURCE_HANDLE),
                    flags,
                },
            ),
        }
    }

    /// Gets some properties of the mesh, such as its internal bounding box and sphere.
    pub fn get_properties(&self) -> MeshProperties {
        World::get_mesh_properties(self.mesh.get_data_handle())
    }

    /// Gets the names of all morph target associated with the mesh.
    ///
    /// It is recommended to store the return value instead of calling it every frame.
    pub fn get_morph_target_names(&self) -> Vec<String> {
        World::get_mesh_morph_target_names(self.mesh.get_data_handle())
    }

    /// Obtain all materials associated with the mesh.
    ///
    /// It is recommended to store the return value instead of calling it every frame.
    pub fn get_material_descs(&self) -> Vec<MaterialDesc> {
        let mesh_handle = self.mesh.get_data_handle();
        let count = ffi::get_mesh_material_count(mesh_handle.as_ffi());
        (0..count)
            .map(|i| ffi::get_mesh_material_desc(mesh_handle.as_ffi(), i))
            .collect()
    }

    /// Obtain all materials and their name associated with the mesh.
    ///
    /// It is recommended to store the return value instead of calling it every frame.
    pub fn get_material_descs_with_names(&self) -> Vec<(Option<String>, MaterialDesc)> {
        let mesh_handle = self.mesh.get_data_handle();
        let count = ffi::get_mesh_material_count(mesh_handle.as_ffi());
        (0..count)
            .map(|i| {
                let len = ffi::get_mesh_material_name(mesh_handle.as_ffi(), i, &mut []);
                if len > 0 {
                    let mut buf: Vec<u8> = vec![0u8; len as usize];
                    ffi::get_mesh_material_name(mesh_handle.as_ffi(), i, &mut buf);
                    String::from_utf8(buf).ok()
                } else {
                    None
                }
            })
            .zip((0..count).map(|i| ffi::get_mesh_material_desc(mesh_handle.as_ffi(), i)))
            .collect()
    }

    /// Get the dominant vertex colors of each mesh section and the area they occupy in vertex space
    ///
    /// A mesh section is a part of the mesh that has its own material index.
    pub fn get_dominant_colors_with_area(&self) -> Vec<(ColorRgba8, f32)> {
        let mesh_handle = self.mesh.get_data_handle();
        let count = ffi::v4::get_mesh_section_count(mesh_handle.as_ffi());
        (0..count)
            .map(|i| {
                let c = ffi::v4::get_mesh_section_dominant_color(mesh_handle.as_ffi(), i);
                (c.color.into(), c.area)
            })
            .collect()
    }

    /// Get the material index assigned to a mesh section
    pub fn get_section_material_index(&self, section_idx: usize) -> usize {
        let mesh_handle = self.mesh.get_data_handle();
        ffi::v4::get_mesh_section_material_index(mesh_handle.as_ffi(), section_idx as u64) as usize
    }

    /// Sets a debug name of this data object. Useful for debugging memory usage and leaks.
    pub fn set_debug_name(&self, name: &str) {
        self.mesh.set_debug_name(name);
    }

    /// Gets the `DataHandle` of this mesh.
    pub fn as_ffi(&self) -> ffi::DataHandle {
        self.mesh.get_data_handle().as_ffi()
    }

    /// Adopts a raw data handle and increases its ref count.
    pub fn from_ffi(handle: ffi::DataHandle) -> Self {
        Self {
            mesh: WorldData::from_data_handle(DataHandle::from_ffi(handle)),
        }
    }

    /// Adopts a raw data handle and increases its ref count if it is a valid data object.
    pub fn try_from_ffi(handle: ffi::DataHandle) -> Option<Self> {
        let handle = DataHandle::from_ffi(handle);

        World::is_valid_data(handle).then(|| Self {
            mesh: WorldData::from_data_handle(handle),
        })
    }

    /// Returns `true` if this is a valid mesh. This should always be the case unless
    /// something has gone very wrong, such as the case where a user created this object from
    // an invalid handle).
    pub fn is_valid(&self) -> bool {
        self.mesh.is_valid()
    }

    /// Returns this mesh as a simplified `WorldMesh` using `target_error` and `threshold` parameters.
    /// `threshold` meaning what percentage (0-1) of the original primitives it should keep.
    /// `target_error` is an approximate measure of the deviation from the original
    /// mesh using distance normalized to 0..1 (so 1e-2f means that simplifier will
    /// try to maintain the error to be below 1% of the mesh extents). Note that because of
    /// topological restrictions and error bounds simplifier isn't guaranteed to reach the
    /// target index count and can stop earlier.
    /// `min_triangle_count` and `max_triangle_count` can be used instead
    /// of threshold or in conjunction to define a minimum limit and maximum budget.
    /// If this range is outside of the number of triangles for the mesh no simplification
    /// will be done (target triangle count will be equal to the amount of triangles in the mesh)
    pub fn simplify(
        &self,
        threshold: f32,
        target_error: f32,
        min_triangle_count: u32,
        max_triangle_count: u32,
        flags: MeshSimplifiedFlags,
    ) -> Self {
        Self {
            mesh: WorldData::create_struct(
                ffi::CreateDataType::MeshSimplified,
                &ffi::MeshSimplified {
                    mesh: self.mesh.get_data_handle().as_ffi(),
                    threshold,
                    target_error,
                    min_triangle_count,
                    max_triangle_count,
                    flags,
                    reserved: [0.0f32; 15],
                },
            ),
        }
    }

    /// Returns a new mesh simplified to `target_triangle_count`, as long as it can keep
    /// the error below `max_error`. `max_error` is an approximate measure of the deviation
    /// from the original mesh using distance normalized to 0..1 (so 1e-2f means that simplifier will
    /// try to maintain the error to be below 1% of the mesh extents).
    pub fn simplify_to_num_triangles(
        &self,
        target_triangle_count: u32,
        max_error: f32,
        flags: MeshSimplifiedFlags,
    ) -> Self {
        self.simplify(
            1.0,
            max_error,
            target_triangle_count,
            target_triangle_count,
            flags,
        )
    }

    /// Returns a new mesh simplified as much as it can while not exceeding `target_error`.
    /// `target_error` is an approximate measure of the deviation from the original mesh using
    /// distance normalized to 0..1 (so 1e-2f means that simplifier will try to maintain the
    /// error to be below 1% of the mesh extents).
    pub fn simplify_to_error(&self, target_error: f32, flags: MeshSimplifiedFlags) -> Self {
        self.simplify(1.0, target_error, 1, 1, flags)
    }
}

impl ValueConverterTrait<WorldMesh> for ValueConverter {
    fn into_value(v: WorldMesh) -> Value {
        // Partial move of mesh (WorldData) here, will not invoke drop
        <Self as ValueConverterTrait<WorldData>>::into_value(v.mesh)
    }
    fn from_value(v: &Value) -> WorldMesh {
        WorldMesh {
            mesh: <Self as ValueConverterTrait<WorldData>>::from_value(v),
        }
    }
}

/// Represents a material registered with the world.

#[derive(Debug, Clone, PartialEq)]
pub struct WorldMaterial {
    data: WorldData,
}

#[allow(dead_code)]
impl WorldMaterial {
    /// Create a material from a material description
    pub fn new(desc: MaterialDesc) -> Self {
        Self {
            data: WorldData::create_struct(ffi::CreateDataType::WorldMaterial, &desc),
        }
    }
}

impl ValueConverterTrait<WorldMaterial> for ValueConverter {
    fn into_value(v: WorldMaterial) -> Value {
        <Self as ValueConverterTrait<WorldData>>::into_value(v.data)
    }
    fn from_value(v: &Value) -> WorldMaterial {
        WorldMaterial {
            data: <Self as ValueConverterTrait<WorldData>>::from_value(v),
        }
    }
}