use crate::assets::{GeometryHandle, MaterialHandle};
use crate::diagnostics::LookupError;
use crate::material::Color;
use super::{InstanceSetKey, NodeKey, NodeKind, Scene, Transform};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InstanceId(u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InstanceCullingPolicy {
CpuBoundingBoxFallback,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Instance {
id: InstanceId,
transform: Transform,
visible: bool,
tint: Option<Color>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct InstanceSet {
geometry: GeometryHandle,
material: MaterialHandle,
instances: Vec<Instance>,
next_id: u64,
culling_policy: InstanceCullingPolicy,
}
impl Scene {
pub fn add_instance_set(
&mut self,
parent: NodeKey,
geometry: GeometryHandle,
material: MaterialHandle,
transform: Transform,
) -> Result<InstanceSetKey, LookupError> {
self.add_instance_set_node(parent, geometry, material, transform)
.map(|(_, instance_set)| instance_set)
}
pub fn add_instance_set_node(
&mut self,
parent: NodeKey,
geometry: GeometryHandle,
material: MaterialHandle,
transform: Transform,
) -> Result<(NodeKey, InstanceSetKey), LookupError> {
let instance_set = self
.instance_sets
.insert(InstanceSet::new(geometry, material));
match self.insert_node(parent, NodeKind::InstanceSet(instance_set), transform) {
Ok(node) => Ok((node, instance_set)),
Err(error) => {
self.instance_sets.remove(instance_set);
Err(error)
}
}
}
pub fn instance_set(&self, instance_set: InstanceSetKey) -> Option<&InstanceSet> {
self.instance_sets.get(instance_set)
}
pub fn reserve_instances(
&mut self,
instance_set: InstanceSetKey,
additional: usize,
) -> Result<(), LookupError> {
self.instance_set_mut(instance_set)?.reserve(additional);
Ok(())
}
pub fn push_instance(
&mut self,
instance_set: InstanceSetKey,
transform: Transform,
) -> Result<InstanceId, LookupError> {
let id = self.instance_set_mut(instance_set)?.push(transform);
self.structure_revision = self.structure_revision.saturating_add(1);
Ok(id)
}
pub fn set_instance_transform(
&mut self,
instance_set: InstanceSetKey,
instance: InstanceId,
transform: Transform,
) -> Result<(), LookupError> {
if self
.instance_set_mut(instance_set)?
.set_transform(instance_set, instance, transform)?
{
self.transform_revision = self.transform_revision.saturating_add(1);
}
Ok(())
}
pub fn set_instance_visible(
&mut self,
instance_set: InstanceSetKey,
instance: InstanceId,
visible: bool,
) -> Result<(), LookupError> {
if self
.instance_set_mut(instance_set)?
.set_visible(instance_set, instance, visible)?
{
self.visibility_revision = self.visibility_revision.saturating_add(1);
}
Ok(())
}
pub fn set_instance_tint(
&mut self,
instance_set: InstanceSetKey,
instance: InstanceId,
tint: Option<Color>,
) -> Result<(), LookupError> {
if self
.instance_set_mut(instance_set)?
.set_tint(instance_set, instance, tint)?
{
self.appearance_revision = self.appearance_revision.saturating_add(1);
}
Ok(())
}
pub fn remove_instance(
&mut self,
instance_set: InstanceSetKey,
instance: InstanceId,
) -> Result<Option<Instance>, LookupError> {
let removed = self.instance_set_mut(instance_set)?.remove(instance);
if removed.is_some() {
self.structure_revision = self.structure_revision.saturating_add(1);
}
Ok(removed)
}
pub fn clear_instances(&mut self, instance_set: InstanceSetKey) -> Result<(), LookupError> {
let changed = self.instance_set_mut(instance_set)?.clear();
if changed {
self.structure_revision = self.structure_revision.saturating_add(1);
}
Ok(())
}
fn instance_set_mut(
&mut self,
instance_set: InstanceSetKey,
) -> Result<&mut InstanceSet, LookupError> {
self.instance_sets
.get_mut(instance_set)
.ok_or(LookupError::InstanceSetNotFound(instance_set))
}
}
impl InstanceId {
pub const fn as_u64(self) -> u64 {
self.0
}
}
impl Instance {
pub const fn id(self) -> InstanceId {
self.id
}
pub const fn transform(self) -> Transform {
self.transform
}
pub const fn visible(self) -> bool {
self.visible
}
pub const fn tint(self) -> Option<Color> {
self.tint
}
}
impl InstanceSet {
const fn new(geometry: GeometryHandle, material: MaterialHandle) -> Self {
Self {
geometry,
material,
instances: Vec::new(),
next_id: 1,
culling_policy: InstanceCullingPolicy::CpuBoundingBoxFallback,
}
}
pub const fn geometry(&self) -> GeometryHandle {
self.geometry
}
pub const fn material(&self) -> MaterialHandle {
self.material
}
pub const fn culling_policy(&self) -> InstanceCullingPolicy {
self.culling_policy
}
pub fn len(&self) -> usize {
self.instances.len()
}
pub fn is_empty(&self) -> bool {
self.instances.is_empty()
}
pub fn contains(&self, instance: InstanceId) -> bool {
self.instances
.iter()
.any(|candidate| candidate.id == instance)
}
pub fn instances(&self) -> impl ExactSizeIterator<Item = &Instance> {
self.instances.iter()
}
fn reserve(&mut self, additional: usize) {
self.instances.reserve(additional);
}
fn push(&mut self, transform: Transform) -> InstanceId {
let id = InstanceId(self.next_id);
self.next_id = self.next_id.saturating_add(1);
self.instances.push(Instance {
id,
transform,
visible: true,
tint: None,
});
id
}
fn set_transform(
&mut self,
instance_set: InstanceSetKey,
instance: InstanceId,
transform: Transform,
) -> Result<bool, LookupError> {
let instance = self.instance_mut(instance_set, instance)?;
let changed = instance.transform != transform;
if changed {
instance.transform = transform;
}
Ok(changed)
}
fn set_visible(
&mut self,
instance_set: InstanceSetKey,
instance: InstanceId,
visible: bool,
) -> Result<bool, LookupError> {
let instance = self.instance_mut(instance_set, instance)?;
let changed = instance.visible != visible;
if changed {
instance.visible = visible;
}
Ok(changed)
}
fn set_tint(
&mut self,
instance_set: InstanceSetKey,
instance: InstanceId,
tint: Option<Color>,
) -> Result<bool, LookupError> {
let instance = self.instance_mut(instance_set, instance)?;
let changed = instance.tint != tint;
if changed {
instance.tint = tint;
}
Ok(changed)
}
fn remove(&mut self, instance: InstanceId) -> Option<Instance> {
let index = self
.instances
.iter()
.position(|candidate| candidate.id == instance)?;
Some(self.instances.remove(index))
}
fn clear(&mut self) -> bool {
let changed = !self.instances.is_empty();
self.instances.clear();
changed
}
fn instance_mut(
&mut self,
instance_set: InstanceSetKey,
instance: InstanceId,
) -> Result<&mut Instance, LookupError> {
self.instances
.iter_mut()
.find(|candidate| candidate.id == instance)
.ok_or(LookupError::InstanceNotFound {
instance_set,
instance,
})
}
}