feat(prototype): retain unit component provenance
This commit is contained in:
@@ -1810,6 +1810,7 @@ mod tests {
|
|||||||
scale: [2.0, 3.0, 4.0],
|
scale: [2.0, 3.0, 4.0],
|
||||||
visual_ids: Vec::new(),
|
visual_ids: Vec::new(),
|
||||||
properties: Vec::new(),
|
properties: Vec::new(),
|
||||||
|
unit_components: Vec::new(),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mission_position_scale_transform(&draft),
|
mission_position_scale_transform(&draft),
|
||||||
@@ -1850,6 +1851,7 @@ mod tests {
|
|||||||
scale: [1.0; 3],
|
scale: [1.0; 3],
|
||||||
visual_ids: Vec::new(),
|
visual_ids: Vec::new(),
|
||||||
properties: Vec::new(),
|
properties: Vec::new(),
|
||||||
|
unit_components: Vec::new(),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let commands = render_snapshot_commands_with_assets(&snapshot, None, Some(&drafts))?;
|
let commands = render_snapshot_commands_with_assets(&snapshot, None, Some(&drafts))?;
|
||||||
@@ -1887,6 +1889,7 @@ mod tests {
|
|||||||
scale: [1.0; 3],
|
scale: [1.0; 3],
|
||||||
visual_ids: Vec::new(),
|
visual_ids: Vec::new(),
|
||||||
properties: Vec::new(),
|
properties: Vec::new(),
|
||||||
|
unit_components: Vec::new(),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let commands = render_snapshot_commands_with_assets(&snapshot, None, Some(&drafts))?;
|
let commands = render_snapshot_commands_with_assets(&snapshot, None, Some(&drafts))?;
|
||||||
|
|||||||
@@ -3528,6 +3528,7 @@ mod tests {
|
|||||||
roots: vec![prototype.key.clone()],
|
roots: vec![prototype.key.clone()],
|
||||||
prototype_requests: vec![prototype.key.clone()],
|
prototype_requests: vec![prototype.key.clone()],
|
||||||
root_prototype_request_spans: std::iter::once(0..1).collect(),
|
root_prototype_request_spans: std::iter::once(0..1).collect(),
|
||||||
|
root_unit_components: vec![Vec::new()],
|
||||||
visual_dependencies_expanded: false,
|
visual_dependencies_expanded: false,
|
||||||
nodes: vec![root_node, prototype_node, mesh_node],
|
nodes: vec![root_node, prototype_node, mesh_node],
|
||||||
edges: vec![
|
edges: vec![
|
||||||
|
|||||||
@@ -128,6 +128,13 @@ pub struct PrototypeGraph {
|
|||||||
pub prototype_requests: Vec<PrototypeKey>,
|
pub prototype_requests: Vec<PrototypeKey>,
|
||||||
/// Mission object-local spans of effective prototype requests.
|
/// Mission object-local spans of effective prototype requests.
|
||||||
pub root_prototype_request_spans: Vec<std::ops::Range<usize>>,
|
pub root_prototype_request_spans: Vec<std::ops::Range<usize>>,
|
||||||
|
/// Ordered raw unit DAT component records for each mission root.
|
||||||
|
///
|
||||||
|
/// This vector is aligned with [`Self::roots`]. Direct roots and legacy
|
||||||
|
/// unit bindings have an empty record list. Records are preserved without
|
||||||
|
/// assigning runtime semantics to their `kind`, links, descriptions, or
|
||||||
|
/// opaque tails.
|
||||||
|
pub root_unit_components: Vec<Vec<UnitComponentRecord>>,
|
||||||
/// Whether visual dependency expansion has already been applied.
|
/// Whether visual dependency expansion has already been applied.
|
||||||
pub visual_dependencies_expanded: bool,
|
pub visual_dependencies_expanded: bool,
|
||||||
/// Materialized prototype dependency graph nodes.
|
/// Materialized prototype dependency graph nodes.
|
||||||
@@ -637,6 +644,7 @@ fn resolve_direct_prototype(
|
|||||||
struct ResolvedPrototypeRequests {
|
struct ResolvedPrototypeRequests {
|
||||||
expected_count: usize,
|
expected_count: usize,
|
||||||
prototypes: Vec<EffectivePrototype>,
|
prototypes: Vec<EffectivePrototype>,
|
||||||
|
unit_components: Vec<UnitComponentRecord>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_prototype_requests(
|
fn resolve_prototype_requests(
|
||||||
@@ -652,6 +660,7 @@ fn resolve_prototype_requests(
|
|||||||
Ok(ResolvedPrototypeRequests {
|
Ok(ResolvedPrototypeRequests {
|
||||||
expected_count: 1,
|
expected_count: 1,
|
||||||
prototypes: prototype.into_iter().collect(),
|
prototypes: prototype.into_iter().collect(),
|
||||||
|
unit_components: Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,6 +695,7 @@ fn resolve_unit_dat_prototype_requests(
|
|||||||
return Ok(ResolvedPrototypeRequests {
|
return Ok(ResolvedPrototypeRequests {
|
||||||
expected_count: unit.records.len(),
|
expected_count: unit.records.len(),
|
||||||
prototypes,
|
prototypes,
|
||||||
|
unit_components: unit.records,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,6 +707,7 @@ fn resolve_unit_dat_prototype_requests(
|
|||||||
Ok(ResolvedPrototypeRequests {
|
Ok(ResolvedPrototypeRequests {
|
||||||
expected_count: 1,
|
expected_count: 1,
|
||||||
prototypes: prototype.into_iter().collect(),
|
prototypes: prototype.into_iter().collect(),
|
||||||
|
unit_components: Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -741,6 +752,9 @@ pub fn build_prototype_graph(
|
|||||||
graph.roots.push(key);
|
graph.roots.push(key);
|
||||||
let start = graph.prototype_requests.len();
|
let start = graph.prototype_requests.len();
|
||||||
let expansion = resolve_prototype_requests(repository, vfs, root)?;
|
let expansion = resolve_prototype_requests(repository, vfs, root)?;
|
||||||
|
graph
|
||||||
|
.root_unit_components
|
||||||
|
.push(expansion.unit_components.clone());
|
||||||
let root_provenance = provenance_for_root(root_index, root);
|
let root_provenance = provenance_for_root(root_index, root);
|
||||||
for prototype in expansion.prototypes {
|
for prototype in expansion.prototypes {
|
||||||
let prototype_node = PrototypeGraphNode::prototype(
|
let prototype_node = PrototypeGraphNode::prototype(
|
||||||
@@ -841,6 +855,9 @@ pub fn build_prototype_graph_report(
|
|||||||
match resolve_prototype_requests(repository, vfs, root) {
|
match resolve_prototype_requests(repository, vfs, root) {
|
||||||
Ok(expansion) => {
|
Ok(expansion) => {
|
||||||
let expected = expansion.expected_count;
|
let expected = expansion.expected_count;
|
||||||
|
graph
|
||||||
|
.root_unit_components
|
||||||
|
.push(expansion.unit_components.clone());
|
||||||
if edge == PrototypeGraphEdge::MissionToUnitDat {
|
if edge == PrototypeGraphEdge::MissionToUnitDat {
|
||||||
report.unit_component_count += expected;
|
report.unit_component_count += expected;
|
||||||
}
|
}
|
||||||
@@ -929,6 +946,9 @@ pub fn build_prototype_graph_report(
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
if graph.root_unit_components.len() <= root_index {
|
||||||
|
graph.root_unit_components.push(Vec::new());
|
||||||
|
}
|
||||||
let end = graph.prototype_requests.len();
|
let end = graph.prototype_requests.len();
|
||||||
graph.root_prototype_request_spans.push(start..end);
|
graph.root_prototype_request_spans.push(start..end);
|
||||||
}
|
}
|
||||||
@@ -1770,6 +1790,16 @@ mod tests {
|
|||||||
assert_eq!(graph.prototype_requests.len(), 2);
|
assert_eq!(graph.prototype_requests.len(), 2);
|
||||||
assert_eq!(graph.prototype_requests[0].0 .0, b"component_a");
|
assert_eq!(graph.prototype_requests[0].0 .0, b"component_a");
|
||||||
assert_eq!(graph.prototype_requests[1].0 .0, b"component_b");
|
assert_eq!(graph.prototype_requests[1].0 .0, b"component_b");
|
||||||
|
assert_eq!(graph.root_unit_components.len(), 1);
|
||||||
|
assert_eq!(graph.root_unit_components[0].len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
cstr_bytes(&graph.root_unit_components[0][0].resource_raw),
|
||||||
|
b"component_a"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cstr_bytes(&graph.root_unit_components[0][1].resource_raw),
|
||||||
|
b"component_b"
|
||||||
|
);
|
||||||
assert_eq!(resolved.len(), 2);
|
assert_eq!(resolved.len(), 2);
|
||||||
assert_eq!(report.unit_reference_count, 1);
|
assert_eq!(report.unit_reference_count, 1);
|
||||||
assert_eq!(report.unit_component_count, 2);
|
assert_eq!(report.unit_component_count, 2);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ use fparkan_assets::{
|
|||||||
use fparkan_path::{normalize_relative, NormalizedPath, PathError, PathPolicy};
|
use fparkan_path::{normalize_relative, NormalizedPath, PathError, PathPolicy};
|
||||||
use fparkan_prototype::{
|
use fparkan_prototype::{
|
||||||
build_prototype_graph_report, PrototypeGraph, PrototypeGraphFailure, PrototypeGraphReport,
|
build_prototype_graph_report, PrototypeGraph, PrototypeGraphFailure, PrototypeGraphReport,
|
||||||
|
UnitComponentRecord,
|
||||||
};
|
};
|
||||||
use fparkan_resource::{resource_name, CachedResourceRepository, ResourceRepository};
|
use fparkan_resource::{resource_name, CachedResourceRepository, ResourceRepository};
|
||||||
use fparkan_terrain::SurfaceQuery;
|
use fparkan_terrain::SurfaceQuery;
|
||||||
@@ -229,6 +230,11 @@ pub struct MissionObjectDraft {
|
|||||||
pub visual_ids: Vec<AssetId<PreparedVisual>>,
|
pub visual_ids: Vec<AssetId<PreparedVisual>>,
|
||||||
/// Ordered mission properties.
|
/// Ordered mission properties.
|
||||||
pub properties: Vec<MissionObjectProperty>,
|
pub properties: Vec<MissionObjectProperty>,
|
||||||
|
/// Ordered raw Unit DAT components that selected this mission root.
|
||||||
|
///
|
||||||
|
/// The records preserve source provenance for future controller loading;
|
||||||
|
/// their fields intentionally have no inferred Control/physics semantics.
|
||||||
|
pub unit_components: Vec<UnitComponentRecord>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
@@ -912,6 +918,11 @@ fn load_mission_with_options_and_progress(
|
|||||||
name_raw: property.name_raw.clone(),
|
name_raw: property.name_raw.clone(),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
unit_components: prototype_graph
|
||||||
|
.root_unit_components
|
||||||
|
.get(index)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let mut new_runtime_world = new_world(WorldConfig);
|
let mut new_runtime_world = new_world(WorldConfig);
|
||||||
@@ -1608,6 +1619,7 @@ mod tests {
|
|||||||
.expect("load mission");
|
.expect("load mission");
|
||||||
let drafts = loaded_mission_object_drafts(&engine).expect("object drafts");
|
let drafts = loaded_mission_object_drafts(&engine).expect("object drafts");
|
||||||
let assets = loaded_mission_assets(&engine).expect("mission assets");
|
let assets = loaded_mission_assets(&engine).expect("mission assets");
|
||||||
|
let graph = loaded_prototype_graph(&engine).expect("prototype graph");
|
||||||
|
|
||||||
assert_eq!(drafts.len(), loaded.object_count);
|
assert_eq!(drafts.len(), loaded.object_count);
|
||||||
assert!(drafts.iter().any(|draft| !draft.visual_ids.is_empty()));
|
assert!(drafts.iter().any(|draft| !draft.visual_ids.is_empty()));
|
||||||
@@ -1617,6 +1629,7 @@ mod tests {
|
|||||||
u32::try_from(index).ok().map(OriginalObjectId)
|
u32::try_from(index).ok().map(OriginalObjectId)
|
||||||
);
|
);
|
||||||
assert_eq!(draft.visual_ids, assets.visuals_for_object(index));
|
assert_eq!(draft.visual_ids, assets.visuals_for_object(index));
|
||||||
|
assert_eq!(draft.unit_components, graph.root_unit_components[index]);
|
||||||
assert!(draft.position.iter().all(|component| component.is_finite()));
|
assert!(draft.position.iter().all(|component| component.is_finite()));
|
||||||
assert!(draft
|
assert!(draft
|
||||||
.orientation_raw
|
.orientation_raw
|
||||||
|
|||||||
@@ -468,6 +468,13 @@ string длиной 32 байта. Требование обязательног
|
|||||||
Части 2. Все соответствуют формуле размера, `kind == 1` и
|
Части 2. Все соответствуют формуле размера, `kind == 1` и
|
||||||
`archive_name == "objects.rlb"`.
|
`archive_name == "objects.rlb"`.
|
||||||
|
|
||||||
|
При построении mission prototype graph FParkan сохраняет для каждого root
|
||||||
|
упорядоченный список этих raw records. Список передаётся в
|
||||||
|
`MissionObjectDraft` без преобразования `kind`, `parent_or_link`, description
|
||||||
|
или tails в предполагаемые роли. Так runtime уже располагает исходными данными
|
||||||
|
составного unit для будущих Control/physics/AniMesh consumers, но не выдаёт
|
||||||
|
структурное сходство за доказанную семантику.
|
||||||
|
|
||||||
## Вспомогательные форматы
|
## Вспомогательные форматы
|
||||||
|
|
||||||
MSH, материал и текстура отвечают за видимую форму. Полноценный прототип
|
MSH, материал и текстура отвечают за видимую форму. Полноценный прототип
|
||||||
|
|||||||
@@ -276,6 +276,12 @@ component как ordered raw-string/resource provenance, но не для при
|
|||||||
этим строкам смысловых имён до трассировки private update methods. Extractor:
|
этим строкам смысловых имён до трассировки private update methods. Extractor:
|
||||||
`tools/ghidra/ExportAniMeshControlCaller.java`.
|
`tools/ghidra/ExportAniMeshControlCaller.java`.
|
||||||
|
|
||||||
|
Runtime сохраняет ordered raw Unit DAT records рядом с каждым mission object
|
||||||
|
draft. Это создаёт проверяемую границу передачи данных от loader-а к будущему
|
||||||
|
Control consumer-у: никакой компонент пока не получает имя `Control` только
|
||||||
|
по `kind`, `parent_or_link` или description; semantic binding появится лишь
|
||||||
|
после trace private update/load methods.
|
||||||
|
|
||||||
### Control system и physical model
|
### Control system и physical model
|
||||||
|
|
||||||
`LoadControlSystem` загружает настройки controller-а: ограничения скорости,
|
`LoadControlSystem` загружает настройки controller-а: ограничения скорости,
|
||||||
|
|||||||
Reference in New Issue
Block a user