use roxmltree::Node;
use crate::error::ParseError;
use crate::model::{BodyEntry, BodyId, Model};
use super::parse_utils::{prefix_geom_refs, prefix_site_refs, prefixed};
use super::state::ParseState;
impl ParseState {
pub(super) fn parse_attach(&mut self, node: Node, parent_id: BodyId) -> Result<(), ParseError> {
let model_name = node.attribute("model").unwrap_or("");
let body_name = node.attribute("body").unwrap_or("");
let prefix = node.attribute("prefix").unwrap_or("").to_string();
let Some(sub) = self.sub_models.get(model_name).cloned() else {
log::warn!(
"<attach model={model_name:?} body={body_name:?}>: unknown sub-model \
(no matching <asset><model name=…>); skipping"
);
return Ok(());
};
let sub_body_id = sub.bodies.iter().enumerate().skip(1).find_map(|(i, b)| {
if b.body.name.as_deref() == Some(body_name) {
Some(i)
} else {
None
}
});
let Some(sub_body_id) = sub_body_id else {
log::warn!(
"<attach model={model_name:?} body={body_name:?}>: body not found in \
sub-model; skipping"
);
return Ok(());
};
self.merge_assets(&sub, &prefix);
self.splice_body_subtree(&sub, sub_body_id, parent_id, &prefix);
self.merge_equality(&sub, &prefix);
self.merge_contact(&sub, &prefix);
self.merge_actuators(&sub, &prefix);
self.merge_sensors(&sub, &prefix);
Ok(())
}
fn splice_body_subtree(
&mut self,
sub: &Model,
sub_id: BodyId,
new_parent_id: BodyId,
prefix: &str,
) {
let mut body = sub.bodies[sub_id].body.clone();
body.name = body.name.map(|n| prefixed(prefix, &n));
for j in &mut body.joints {
if let Some(n) = j.name.take() {
j.name = Some(prefixed(prefix, &n));
}
}
for g in &mut body.geoms {
prefix_geom_refs(g, prefix);
}
for s in &mut body.sites {
prefix_site_refs(s, prefix);
}
let new_id = self.model.bodies.len();
self.model.bodies.push(BodyEntry {
parent: Some(new_parent_id),
body,
});
let children: Vec<BodyId> = sub
.bodies
.iter()
.enumerate()
.filter(|(_, b)| b.parent == Some(sub_id))
.map(|(i, _)| i)
.collect();
for child_id in children {
self.splice_body_subtree(sub, child_id, new_id, prefix);
}
}
fn merge_assets(&mut self, sub: &Model, prefix: &str) {
for mesh in &sub.assets.meshes {
let mut m = mesh.clone();
if let Some(n) = m.name.take() {
m.name = Some(prefixed(prefix, &n));
}
self.model.assets.meshes.push(m);
}
for hf in &sub.assets.hfields {
let mut h = hf.clone();
if let Some(n) = h.name.take() {
h.name = Some(prefixed(prefix, &n));
}
self.model.assets.hfields.push(h);
}
for tex in &sub.assets.textures {
let mut t = tex.clone();
if let Some(n) = t.name.take() {
t.name = Some(prefixed(prefix, &n));
}
self.model.assets.textures.push(t);
}
for mat in &sub.assets.materials {
let mut m = mat.clone();
if let Some(n) = m.name.take() {
m.name = Some(prefixed(prefix, &n));
}
if let Some(n) = m.texture.take() {
m.texture = Some(prefixed(prefix, &n));
}
self.model.assets.materials.push(m);
}
}
fn merge_equality(&mut self, sub: &Model, prefix: &str) {
for eq in &sub.equality {
match eq {
crate::equality::Equality::Connect(c) => {
let mut new_c = c.clone();
if let Some(n) = new_c.common.name.take() {
new_c.common.name = Some(prefixed(prefix, &n));
}
new_c.body1 = prefixed(prefix, &new_c.body1);
if let Some(b2) = new_c.body2.take() {
new_c.body2 = Some(prefixed(prefix, &b2));
}
self.model
.equality
.push(crate::equality::Equality::Connect(new_c));
}
crate::equality::Equality::Weld(w) => {
let mut new_w = w.clone();
if let Some(n) = new_w.common.name.take() {
new_w.common.name = Some(prefixed(prefix, &n));
}
new_w.body1 = prefixed(prefix, &new_w.body1);
if let Some(b2) = new_w.body2.take() {
new_w.body2 = Some(prefixed(prefix, &b2));
}
self.model
.equality
.push(crate::equality::Equality::Weld(new_w));
}
crate::equality::Equality::Joint(j) => {
let mut new_j = j.clone();
if let Some(n) = new_j.common.name.take() {
new_j.common.name = Some(prefixed(prefix, &n));
}
new_j.joint1 = prefixed(prefix, &new_j.joint1);
if let Some(j2) = new_j.joint2.take() {
new_j.joint2 = Some(prefixed(prefix, &j2));
}
self.model
.equality
.push(crate::equality::Equality::Joint(new_j));
}
crate::equality::Equality::Tendon(t) => {
let mut new_t = t.clone();
if let Some(n) = new_t.common.name.take() {
new_t.common.name = Some(prefixed(prefix, &n));
}
new_t.tendon1 = prefixed(prefix, &new_t.tendon1);
if let Some(t2) = new_t.tendon2.take() {
new_t.tendon2 = Some(prefixed(prefix, &t2));
}
self.model
.equality
.push(crate::equality::Equality::Tendon(new_t));
}
}
}
}
fn merge_contact(&mut self, sub: &Model, prefix: &str) {
for p in &sub.contact.pairs {
let mut new_p = p.clone();
if let Some(n) = new_p.name.take() {
new_p.name = Some(prefixed(prefix, &n));
}
new_p.geom1 = prefixed(prefix, &new_p.geom1);
new_p.geom2 = prefixed(prefix, &new_p.geom2);
self.model.contact.pairs.push(new_p);
}
for ex in &sub.contact.excludes {
let mut new_e = ex.clone();
if let Some(n) = new_e.name.take() {
new_e.name = Some(prefixed(prefix, &n));
}
new_e.body1 = prefixed(prefix, &new_e.body1);
new_e.body2 = prefixed(prefix, &new_e.body2);
self.model.contact.excludes.push(new_e);
}
}
fn merge_actuators(&mut self, sub: &Model, prefix: &str) {
for a in &sub.actuators {
let mut new_a = a.clone();
if let Some(n) = new_a.name.take() {
new_a.name = Some(prefixed(prefix, &n));
}
if let Some(n) = new_a.joint.take() {
new_a.joint = Some(prefixed(prefix, &n));
}
if let Some(n) = new_a.tendon.take() {
new_a.tendon = Some(prefixed(prefix, &n));
}
if let Some(n) = new_a.body.take() {
new_a.body = Some(prefixed(prefix, &n));
}
if let Some(n) = new_a.site.take() {
new_a.site = Some(prefixed(prefix, &n));
}
self.model.actuators.push(new_a);
}
}
fn merge_sensors(&mut self, sub: &Model, prefix: &str) {
for s in &sub.sensors {
let mut new_s = s.clone();
if let Some(n) = new_s.name.take() {
new_s.name = Some(prefixed(prefix, &n));
}
if let Some(n) = new_s.objname.take() {
new_s.objname = Some(prefixed(prefix, &n));
}
if let Some(n) = new_s.refname.take() {
new_s.refname = Some(prefixed(prefix, &n));
}
self.model.sensors.push(new_s);
}
}
}