///|
pub(all) struct SceneSnapshotNode {
index : Int
parent : Int?
child_count : Int
mesh : Int?
skin : Int?
camera : Int?
depth : Int
signature : String
} derive(Debug, ToJson, Eq)
///|
pub(all) struct SceneSnapshot {
scene : Int
nodes : Array[SceneSnapshotNode]
roots : Array[Int]
max_depth : Int
mesh_nodes : Int
skinned_nodes : Int
leaf_nodes : Int
signature : String
} derive(Debug, ToJson, Eq)
///|
pub(all) struct SceneSnapshotChange {
node : Int
kind : String
before : String?
after : String?
} derive(Debug, ToJson, Eq)
///|
pub(all) struct SceneSnapshotDiff {
changes : Array[SceneSnapshotChange]
added : Int
removed : Int
changed : Int
unchanged : Int
compatible : Bool
} derive(Debug, ToJson, Eq)
///|
pub fn scene_snapshot_node_signature(node : GltfNode) -> String {
let name = match node.name {
Some(value) => value
None => ""
}
let mesh = match node.mesh {
Some(value) => "\{value}"
None => "-"
}
let skin = match node.skin {
Some(value) => "\{value}"
None => "-"
}
let camera = match node.camera {
Some(value) => "\{value}"
None => "-"
}
"\{name}|\{mesh}|\{skin}|\{camera}|\{node.children.length()}"
}
///|
pub fn scene_snapshot_node(
index : Int,
parent : Int?,
node : GltfNode,
depth : Int,
) -> SceneSnapshotNode {
{
index,
parent,
child_count: node.children.length(),
mesh: node.mesh,
skin: node.skin,
camera: node.camera,
depth,
signature: scene_snapshot_node_signature(node),
}
}
///|
pub fn scene_snapshot(doc : GltfDocument, scene_index : Int) -> SceneSnapshot {
let scene = match doc.scenes.get(scene_index) {
Some(value) => value
None => { name: None, nodes: [] }
}
let snapshots : Array[SceneSnapshotNode] = []
let roots = scene.nodes.copy()
let visited : Map[Int, Bool] = Map([])
let mut max_depth = 0
let mut mesh_nodes = 0
let mut skinned_nodes = 0
let mut leaf_nodes = 0
for root in roots {
let frontier : Array[(Int, Int, Int?)] = [(root, 1, None)]
while frontier.length() > 0 {
let item = frontier.remove(0)
let index = item.0
let depth = item.1
let parent = item.2
if visited.contains(index) {
continue
}
visited[index] = true
match doc.nodes.get(index) {
Some(node) => {
snapshots.push(scene_snapshot_node(index, parent, node, depth))
if depth > max_depth {
max_depth = depth
}
match node.mesh {
Some(_) => mesh_nodes = mesh_nodes + 1
None => ()
}
match node.skin {
Some(_) => skinned_nodes = skinned_nodes + 1
None => ()
}
if node.children.length() == 0 {
leaf_nodes = leaf_nodes + 1
}
for child in node.children {
frontier.push((child, depth + 1, Some(index)))
}
}
None => ()
}
}
}
let signature = scene_snapshot_signature(snapshots)
{
scene: scene_index,
nodes: snapshots,
roots,
max_depth,
mesh_nodes,
skinned_nodes,
leaf_nodes,
signature,
}
}
///|
pub fn scene_snapshot_signature(nodes : Array[SceneSnapshotNode]) -> String {
let parts : Array[String] = []
for node in nodes {
parts.push("\{node.index}:\{node.signature}")
}
parts.join(";")
}
///|
pub fn scene_snapshot_node_at(
snapshot : SceneSnapshot,
node : Int,
) -> SceneSnapshotNode? {
for value in snapshot.nodes {
if value.index == node {
return Some(value)
}
}
None
}
///|
pub fn scene_snapshot_leaf_indices(snapshot : SceneSnapshot) -> Array[Int] {
snapshot.nodes
.filter(fn(node) { node.child_count == 0 })
.map(fn(node) { node.index })
}
///|
pub fn scene_snapshot_mesh_indices(snapshot : SceneSnapshot) -> Array[Int] {
snapshot.nodes
.filter(fn(node) { node.mesh is Some(_) })
.map(fn(node) { node.index })
}
///|
pub fn scene_snapshot_skin_indices(snapshot : SceneSnapshot) -> Array[Int] {
snapshot.nodes
.filter(fn(node) { node.skin is Some(_) })
.map(fn(node) { node.index })
}
///|
pub fn scene_snapshot_depth_histogram(
snapshot : SceneSnapshot,
) -> Map[Int, Int] {
let histogram : Map[Int, Int] = Map([])
for node in snapshot.nodes {
histogram[node.depth] = match histogram.get(node.depth) {
Some(value) => value + 1
None => 1
}
}
histogram
}
///|
pub fn scene_snapshot_diff(
before : SceneSnapshot,
after : SceneSnapshot,
) -> SceneSnapshotDiff {
let changes : Array[SceneSnapshotChange] = []
let mut added = 0
let mut removed = 0
let mut changed = 0
let mut unchanged = 0
for old_node in before.nodes {
let mut found : SceneSnapshotNode? = None
for new_node in after.nodes {
if new_node.index == old_node.index {
found = Some(new_node)
break
}
}
match found {
None => {
removed = removed + 1
changes.push({
node: old_node.index,
kind: "removed",
before: Some(old_node.signature),
after: None,
})
}
Some(new_node) =>
if old_node.signature == new_node.signature {
unchanged = unchanged + 1
} else {
changed = changed + 1
changes.push({
node: old_node.index,
kind: "changed",
before: Some(old_node.signature),
after: Some(new_node.signature),
})
}
}
}
for new_node in after.nodes {
if !before.nodes.any(fn(old_node) { old_node.index == new_node.index }) {
added = added + 1
changes.push({
node: new_node.index,
kind: "added",
before: None,
after: Some(new_node.signature),
})
}
}
{ changes, added, removed, changed, unchanged, compatible: removed == 0 }
}
///|
pub fn scene_snapshot_diff_kind_count(
diff : SceneSnapshotDiff,
kind : String,
) -> Int {
diff.changes.filter(fn(change) { change.kind == kind }).length()
}
///|
pub fn scene_snapshot_lines(snapshot : SceneSnapshot) -> Array[String] {
[
"scene=\{snapshot.scene}",
"nodes=\{snapshot.nodes.length()}",
"roots=\{snapshot.roots.length()}",
"max_depth=\{snapshot.max_depth}",
"mesh_nodes=\{snapshot.mesh_nodes}",
"skinned_nodes=\{snapshot.skinned_nodes}",
"leaf_nodes=\{snapshot.leaf_nodes}",
"signature=\{snapshot.signature}",
]
}
///|
pub fn scene_snapshot_diff_lines(diff : SceneSnapshotDiff) -> Array[String] {
[
"added=\{diff.added}",
"removed=\{diff.removed}",
"changed=\{diff.changed}",
"unchanged=\{diff.unchanged}",
"compatible=\{diff.compatible}",
]
}