use std::cell::Cell;
use std::collections::{BTreeMap, BTreeSet};
use std::marker::PhantomData;
use std::sync::{Arc, Weak};
use slotmap::{SlotMap, new_key_type};
use crate::animation::{AnimationMixer, AnimationMixerKey};
use crate::assets::{GeometryHandle, MaterialHandle, ModelHandle};
use crate::diagnostics::LookupError;
use crate::geometry::{Aabb, Primitive};
use crate::material::Color;
use crate::picking::InteractionContext;
mod anchors;
mod annotations;
mod builders;
mod callouts;
mod camera;
mod camera_controls;
mod clipping;
mod connectors;
mod default;
mod dirty;
mod exploded;
mod framing;
mod import;
#[cfg(feature = "inspection")]
mod inspection;
mod inspection_tools;
mod instances;
mod labels;
mod lights;
mod lod;
mod materials;
mod math;
mod measurements;
mod mixers;
mod morphs;
mod origin;
mod particles;
mod picking;
mod placement;
pub mod recipe;
mod removal;
mod render_nodes;
mod skinning;
mod transforms;
mod view;
pub(crate) mod view_math;
mod visibility;
pub use anchors::AnchorFrame;
pub use annotations::{
AnnotationAnchor, AnnotationAnchorTarget, AnnotationProjectionReportV1, AnnotationProjectionV1,
SCENE_ANNOTATION_PROJECTION_SCHEMA_V1,
};
pub use builders::{MeshBuilder, ModelBuilder};
pub use callouts::{Callout, CalloutAnchor, CalloutAnchorKind, CalloutReport};
pub use camera::{Camera, DepthRange, OrthographicCamera, PerspectiveCamera};
pub use clipping::{ClippingPlane, ClippingPlaneSet, SectionBox};
pub use connectors::{
ConnectOptions, ConnectionAlignment, ConnectionError, ConnectionLineOverlay,
ConnectionMagnetPreview, ConnectionMagnetVisualCue, ConnectionParenting, ConnectionPreview,
ConnectionRequest, ConnectionRoll, ConnectionWarning, ConnectorFrame, ConnectorMetadata,
ConnectorPolarity, ConnectorRollPolicy,
};
pub use dirty::SceneDirtyState;
pub use exploded::{ExplodedTransformUpdate, ExplodedView, ExplodedViewPlan};
pub use framing::{
FramingOptions, FramingOutcome, GridFloorHandles, GridFloorOptions, ProjectedPoint, ScreenRect,
};
pub use import::{
ImportAnchor, ImportAnchorDebugMetadata, ImportClip, ImportConnector, ImportOptions,
ImportPivot, SceneImport, SourceCoordinateSystem, SourceUnits,
};
#[cfg(feature = "inspection")]
pub use inspection::{
SCENE_INSPECTION_SCHEMA_V1, SceneCameraFrustumInspection, SceneCameraFrustumInspectionV1,
SceneDrawInspection, SceneDrawInspectionV1, SceneHostInstanceEntryInspectionV1,
SceneHostInstanceSetInspectionV1, SceneImportInspectionV1, SceneInspectionCountsV1,
SceneInspectionReport, SceneInspectionReportV1, SceneInspectionRevisionsV1,
SceneMaterialInspection, SceneMaterialInspectionV1, SceneMaterialSlotInspectionV1,
SceneMaterialSourceInspectionV1, SceneNodeInspection, SceneNodeInspectionV1,
SceneNormalInspection, SceneNormalInspectionV1, SceneTextureInspection,
};
use inspection_tools::InspectionToolkitState;
pub use inspection_tools::{
InspectionHelperKind, InspectionHelperReport, InspectionToolkitReport, SceneTintSnapshot,
SceneTintSnapshotEntry, SceneVisibilitySnapshot, SceneVisibilitySnapshotEntry,
};
pub use instances::{Instance, InstanceId, InstanceSet};
pub use labels::{LabelBillboard, LabelDesc, LabelFontError, LabelFontFace, LabelMetrics};
pub use lights::{
AreaLight, AreaLightShape, DirectionalLight, Light, LightBuilder, PointLight, SpotLight,
StudioLightingHandles,
};
pub use lod::MeshLodLevel;
pub use math::{Angle, Quat, Transform, Vec3};
pub use measurements::{
MeasurementAxis, MeasurementKind, MeasurementOverlay, MeasurementOverlayReport,
MeasurementReport, UnitFormat,
};
pub use particles::{Particle, ParticleSet, ParticleSetError};
pub use placement::{
SCENE_PLACEMENT_RESULT_SCHEMA_V1, ScenePlacementDiagnosticV1, ScenePlacementResultV1,
placement_align_to_feature_transform, placement_center_transform,
placement_fit_to_size_transform, placement_ground_transform, placement_look_at_transform,
placement_place_on_feature_transform,
};
pub use skinning::SceneSkinBinding;
new_key_type! {
pub struct NodeKey;
pub struct CameraKey;
pub struct LightKey;
pub struct ClippingPlaneKey;
pub struct InstanceSetKey;
pub struct ParticleSetKey;
pub struct LabelKey;
pub struct AnchorKey;
pub struct ConnectorKey;
}
#[derive(Debug)]
pub struct Scene {
identity: Arc<()>,
nodes: SlotMap<NodeKey, Node>,
cameras: SlotMap<CameraKey, Camera>,
lights: SlotMap<LightKey, Light>,
instance_sets: SlotMap<InstanceSetKey, InstanceSet>,
particle_sets: SlotMap<ParticleSetKey, ParticleSet>,
animation_mixers: SlotMap<AnimationMixerKey, AnimationMixer>,
labels: SlotMap<LabelKey, LabelDesc>,
anchors: SlotMap<AnchorKey, AnchorFrame>,
annotations: BTreeMap<String, AnnotationAnchor>,
callouts: BTreeMap<String, callouts::SceneCalloutState>,
measurements: BTreeMap<String, measurements::SceneMeasurementOverlayState>,
connectors: SlotMap<ConnectorKey, ConnectorFrame>,
connection_locked_nodes: BTreeSet<NodeKey>,
node_bounds: BTreeMap<NodeKey, Aabb>,
section_box: Option<clipping::SceneSectionBoxState>,
mesh_lods: BTreeMap<NodeKey, Vec<MeshLodLevel>>,
morph_weights: BTreeMap<NodeKey, Vec<f32>>,
skin_bindings: BTreeMap<NodeKey, SceneSkinBinding>,
clipping_planes: SlotMap<ClippingPlaneKey, ClippingPlane>,
active_clipping_planes: ClippingPlaneSet,
origin_shift: Vec3,
root: NodeKey,
active_camera: Option<CameraKey>,
camera_layer_masks: BTreeMap<CameraKey, u64>,
interaction: InteractionContext,
inspection_toolkit: InspectionToolkitState,
structure_revision: u64,
transform_revision: u64,
appearance_revision: u64,
visibility_revision: u64,
not_sync: PhantomData<Cell<()>>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Node {
parent: Option<NodeKey>,
children: Vec<NodeKey>,
transform: Transform,
kind: NodeKind,
visible: bool,
tags: BTreeSet<String>,
layer_mask: u64,
render_group: i16,
helper_on_top: bool,
tint: Option<Color>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum NodeKind {
Empty,
Renderable(RenderableNode),
Mesh(MeshNode),
Model(ModelNode),
InstanceSet(InstanceSetKey),
ParticleSet(ParticleSetKey),
Label(LabelKey),
Camera(CameraKey),
Light(LightKey),
}
#[derive(Debug, Clone, PartialEq)]
pub struct RenderableNode {
primitives: Vec<Primitive>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MeshNode {
geometry: GeometryHandle,
material: MaterialHandle,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ModelNode {
model: ModelHandle,
}
impl Scene {
pub fn new() -> Self {
let mut nodes = SlotMap::with_key();
let root = nodes.insert(Node::empty_root());
Self {
identity: Arc::new(()),
nodes,
cameras: SlotMap::with_key(),
lights: SlotMap::with_key(),
instance_sets: SlotMap::with_key(),
particle_sets: SlotMap::with_key(),
animation_mixers: SlotMap::with_key(),
labels: SlotMap::with_key(),
anchors: SlotMap::with_key(),
annotations: BTreeMap::new(),
callouts: BTreeMap::new(),
measurements: BTreeMap::new(),
connectors: SlotMap::with_key(),
connection_locked_nodes: BTreeSet::new(),
node_bounds: BTreeMap::new(),
section_box: None,
mesh_lods: BTreeMap::new(),
morph_weights: BTreeMap::new(),
skin_bindings: BTreeMap::new(),
clipping_planes: SlotMap::with_key(),
active_clipping_planes: ClippingPlaneSet::new(),
origin_shift: Vec3::ZERO,
root,
active_camera: None,
camera_layer_masks: BTreeMap::new(),
interaction: InteractionContext::default(),
inspection_toolkit: InspectionToolkitState::default(),
structure_revision: 0,
transform_revision: 0,
appearance_revision: 0,
visibility_revision: 0,
not_sync: PhantomData,
}
}
pub fn root(&self) -> NodeKey {
self.root
}
pub fn active_camera(&self) -> Option<CameraKey> {
self.active_camera
}
pub fn set_active_camera(&mut self, camera: CameraKey) -> Result<(), LookupError> {
if self.cameras.contains_key(camera) {
self.active_camera = Some(camera);
Ok(())
} else {
Err(LookupError::CameraNotFound(camera))
}
}
pub fn node(&self, node: NodeKey) -> Option<&Node> {
self.nodes.get(node)
}
pub fn camera(&self, camera: CameraKey) -> Option<&Camera> {
self.cameras.get(camera)
}
pub fn add_empty(
&mut self,
parent: NodeKey,
transform: Transform,
) -> Result<NodeKey, LookupError> {
self.insert_node(parent, NodeKind::Empty, transform)
}
pub fn add_renderable(
&mut self,
parent: NodeKey,
primitives: Vec<Primitive>,
transform: Transform,
) -> Result<NodeKey, LookupError> {
self.insert_node(
parent,
NodeKind::Renderable(RenderableNode { primitives }),
transform,
)
}
pub fn mesh(&mut self, geometry: GeometryHandle, material: MaterialHandle) -> MeshBuilder<'_> {
let parent = self.root;
MeshBuilder::new(self, parent, geometry, material)
}
pub fn model(&mut self, model: ModelHandle) -> ModelBuilder<'_> {
let parent = self.root;
ModelBuilder::new(self, parent, model)
}
pub fn add_perspective_camera(
&mut self,
parent: NodeKey,
camera: PerspectiveCamera,
transform: Transform,
) -> Result<CameraKey, LookupError> {
self.insert_camera(parent, Camera::Perspective(camera), transform)
}
pub fn add_orthographic_camera(
&mut self,
parent: NodeKey,
camera: OrthographicCamera,
transform: Transform,
) -> Result<CameraKey, LookupError> {
self.insert_camera(parent, Camera::Orthographic(camera), transform)
}
pub(crate) fn identity(&self) -> Weak<()> {
Arc::downgrade(&self.identity)
}
pub(crate) fn structure_revision(&self) -> u64 {
self.structure_revision
.saturating_add(self.interaction.revision())
}
pub(crate) const fn transform_revision(&self) -> u64 {
self.transform_revision
}
pub(crate) const fn appearance_revision(&self) -> u64 {
self.appearance_revision
}
pub(crate) const fn visibility_revision(&self) -> u64 {
self.visibility_revision
}
fn insert_camera(
&mut self,
parent: NodeKey,
camera: Camera,
transform: Transform,
) -> Result<CameraKey, LookupError> {
let camera = self.cameras.insert(camera);
if let Err(error) = self.insert_node(parent, NodeKind::Camera(camera), transform) {
self.cameras.remove(camera);
return Err(error);
}
self.camera_layer_masks.insert(camera, u64::MAX);
Ok(camera)
}
fn insert_node(
&mut self,
parent: NodeKey,
kind: NodeKind,
transform: Transform,
) -> Result<NodeKey, LookupError> {
if !self.nodes.contains_key(parent) {
return Err(LookupError::NodeNotFound(parent));
}
let node = self.nodes.insert(Node {
parent: Some(parent),
children: Vec::new(),
transform,
kind,
visible: true,
tags: BTreeSet::new(),
layer_mask: u64::MAX,
render_group: 0,
helper_on_top: false,
tint: None,
});
self.nodes[parent].children.push(node);
self.structure_revision = self.structure_revision.saturating_add(1);
Ok(node)
}
}
impl Node {
fn empty_root() -> Self {
Self {
parent: None,
children: Vec::new(),
transform: Transform::IDENTITY,
kind: NodeKind::Empty,
visible: true,
tags: BTreeSet::new(),
layer_mask: u64::MAX,
render_group: 0,
helper_on_top: false,
tint: None,
}
}
pub fn parent(&self) -> Option<NodeKey> {
self.parent
}
pub fn children(&self) -> &[NodeKey] {
&self.children
}
pub fn transform(&self) -> Transform {
self.transform
}
pub fn kind(&self) -> &NodeKind {
&self.kind
}
pub const fn visible(&self) -> bool {
self.visible
}
pub fn tags(&self) -> impl Iterator<Item = &str> {
self.tags.iter().map(String::as_str)
}
pub const fn layer_mask(&self) -> u64 {
self.layer_mask
}
pub const fn render_group(&self) -> i16 {
self.render_group
}
pub const fn helper_on_top(&self) -> bool {
self.helper_on_top
}
pub const fn tint(&self) -> Option<Color> {
self.tint
}
}
impl RenderableNode {
pub fn primitives(&self) -> &[Primitive] {
&self.primitives
}
}
impl MeshNode {
pub const fn geometry(&self) -> GeometryHandle {
self.geometry
}
pub const fn material(&self) -> MaterialHandle {
self.material
}
}
impl ModelNode {
pub const fn model(&self) -> ModelHandle {
self.model
}
}