@@ -382,9 +382,15 @@ void mjXWriter::OneJoint(XMLElement* elem,
382382 &def->Joint ().spec ,
383383 kJointAttrs ,
384384 kJointAttrsN );
385- if (joint->type != mjJNT_FREE) { WriteAttr (elem, " pos" , 3 , joint->pos , def->Joint ().spec .pos ); }
385+ // pos and axis relative to the joint's frame
386+ double pos[3 ], axis[3 ], iquat[4 ] = {1 , 0 , 0 , 0 };
387+ mjuu_copyvec (pos, joint->pos , 3 );
388+ mjuu_copyvec (axis, joint->axis , 3 );
389+ FrameLocal (joint->frame , pos, iquat);
390+ mjuu_rotVecQuat (axis, axis, iquat);
391+ if (joint->type != mjJNT_FREE) { WriteAttr (elem, " pos" , 3 , pos, def->Joint ().spec .pos ); }
386392 if (joint->type != mjJNT_FREE && joint->type != mjJNT_BALL) {
387- WriteAttr (elem, " axis" , 3 , joint-> axis , def->Joint ().spec .axis );
393+ WriteAttr (elem, " axis" , 3 , axis, def->Joint ().spec .axis );
388394 }
389395 if (joint->type != mjJNT_FREE) {
390396 WriteAttrKey (elem, " limited" , FalseTrueAuto_map, 3 , joint->limited , def->Joint ().limited );
@@ -422,30 +428,16 @@ void mjXWriter::OneGeom(XMLElement* elem, const mjCGeom* geom, mjCDef* def, stri
422428 }
423429 if (mjuu_defined (geom->mass )) { mass = geom->GetVolume () * def->Geom ().density ; }
424430
425- // mesh geom
426- if (geom->type == mjGEOM_MESH || geom->type == mjGEOM_SDF) {
427- mjCMesh* mesh = geom->mesh ;
428-
429- // write pos/quat if there is a difference
430- if (!SameVector (geom->pos , mesh->GetPosPtr (), 3 ) ||
431- !SameVector (geom->quat , mesh->GetQuatPtr (), 4 )) {
432- // recover geom pos/quat before mesh frame transformation
433- double p[3 ], q[4 ];
434- mjuu_copyvec (p, geom->pos , 3 );
435- mjuu_copyvec (q, geom->quat , 4 );
436- mjuu_frameaccuminv (p, q, mesh->GetPosPtr (), mesh->GetQuatPtr ());
437-
438- // write
439- WriteAttr (elem, " pos" , 3 , p, unitq + 1 );
440- WriteAttr (elem, " quat" , 4 , q, unitq);
441- }
442- }
443-
444- // non-mesh geom
445- else {
446- WriteAttr (elem, " pos" , 3 , geom->pos , unitq + 1 );
447- WriteAttr (elem, " quat" , 4 , geom->quat , unitq);
431+ // pose: undo the mesh transformation, then the frame
432+ double pos[3 ], quat[4 ];
433+ mjuu_copyvec (pos, geom->pos , 3 );
434+ mjuu_copyvec (quat, geom->quat , 4 );
435+ if ((geom->type == mjGEOM_MESH || geom->type == mjGEOM_SDF) && geom->mesh ) {
436+ mjuu_frameaccuminv (pos, quat, geom->mesh ->GetPosPtr (), geom->mesh ->GetQuatPtr ());
448437 }
438+ FrameLocal (geom->frame , pos, quat);
439+ WriteAttr (elem, " pos" , 3 , pos, unitq + 1 );
440+ WriteAttr (elem, " quat" , 4 , quat, unitq);
449441 } else {
450442 WriteAttr (elem, " size" , 3 , geom->size , def->Geom ().size );
451443 }
@@ -505,30 +497,16 @@ void mjXWriter::OneSite(XMLElement* elem, const mjCSite* site, mjCDef* def, stri
505497 WriteAttr (elem, " size" , mjGEOMINFO[site->type ], site->size , def->Site ().size );
506498 }
507499
508- // mesh site
509- if (site->type == mjGEOM_MESH) {
510- mjCMesh* mesh = site->mesh ;
511-
512- // write pos/quat if there is a difference
513- if (!SameVector (site->pos , mesh->GetPosPtr (), 3 ) ||
514- !SameVector (site->quat , mesh->GetQuatPtr (), 4 )) {
515- // recover site pos/quat before mesh frame transformation
516- double p[3 ], q[4 ];
517- mjuu_copyvec (p, site->pos , 3 );
518- mjuu_copyvec (q, site->quat , 4 );
519- mjuu_frameaccuminv (p, q, mesh->GetPosPtr (), mesh->GetQuatPtr ());
520-
521- // write
522- WriteAttr (elem, " pos" , 3 , p, unitq + 1 );
523- WriteAttr (elem, " quat" , 4 , q, unitq);
524- }
525- }
526-
527- // non-mesh site
528- else {
529- WriteAttr (elem, " pos" , 3 , site->pos , unitq + 1 );
530- WriteAttr (elem, " quat" , 4 , site->quat , unitq);
500+ // pose: undo the mesh transformation, then the frame
501+ double pos[3 ], quat[4 ];
502+ mjuu_copyvec (pos, site->pos , 3 );
503+ mjuu_copyvec (quat, site->quat , 4 );
504+ if (site->type == mjGEOM_MESH && site->mesh ) {
505+ mjuu_frameaccuminv (pos, quat, site->mesh ->GetPosPtr (), site->mesh ->GetQuatPtr ());
531506 }
507+ FrameLocal (site->frame , pos, quat);
508+ WriteAttr (elem, " pos" , 3 , pos, unitq + 1 );
509+ WriteAttr (elem, " quat" , 4 , quat, unitq);
532510 } else {
533511 WriteAttr (elem, " size" , 3 , site->size , def->Site ().size );
534512 }
@@ -562,22 +540,22 @@ void mjXWriter::OneCamera(XMLElement* elem,
562540 string_view classname) {
563541 double unitq[4 ] = {1 , 0 , 0 , 0 };
564542
543+ // pose relative to the camera's frame
544+ mjsCamera local = *static_cast <const mjsCamera*>(camera);
545+ FrameLocal (camera->frame , local.pos , local.quat );
546+
565547 // regular
566548 if (!writingdefaults) {
567549 WriteAttrTxt (elem, " name" , camera->name );
568550 if (classname != camera->classname && camera->classname != " main" ) {
569551 WriteAttrTxt (elem, " class" , camera->classname );
570552 }
571553 WriteAttrTxt (elem, " target" , camera->get_targetbody ());
572- WriteAttr (elem, " quat" , 4 , camera-> quat , unitq);
554+ WriteAttr (elem, " quat" , 4 , local. quat , unitq);
573555 }
574556
575557 // defaults and regular
576- WriteAttrTable (elem,
577- static_cast <const mjsCamera*>(camera),
578- &def->Camera ().spec ,
579- kCameraAttrs ,
580- kCameraAttrsN );
558+ WriteAttrTable (elem, &local, &def->Camera ().spec , kCameraAttrs , kCameraAttrsN );
581559
582560 // camera intrinsics if specified
583561 if (camera->sensor_size [0 ] > 0 && camera->sensor_size [1 ] > 0 ) {
@@ -612,12 +590,14 @@ void mjXWriter::OneLight(XMLElement* elem,
612590 WriteAttrTxt (elem, " target" , light->get_targetbody ());
613591 }
614592
593+ // pos and dir relative to the light's frame
594+ mjsLight local = *static_cast <const mjsLight*>(light);
595+ double iquat[4 ] = {1 , 0 , 0 , 0 };
596+ FrameLocal (light->frame , local.pos , iquat);
597+ mjuu_rotVecQuat (local.dir , local.dir , iquat);
598+
615599 // defaults and regular
616- WriteAttrTable (elem,
617- static_cast <const mjsLight*>(light),
618- &def->Light ().spec ,
619- kLightAttrs ,
620- kLightAttrsN );
600+ WriteAttrTable (elem, &local, &def->Light ().spec , kLightAttrs , kLightAttrsN );
621601 WriteAttrKey (elem, " type" , lighttype_map, lighttype_sz, light->type , def->Light ().type );
622602 WriteAttrTxt (elem, " texture" , light->get_texture ());
623603}
@@ -1544,17 +1524,42 @@ void mjXWriter::Asset(XMLElement* root) {
15441524}
15451525
15461526
1547- XMLElement* mjXWriter::OneFrame (XMLElement* elem, mjCFrame* frame) {
1527+ // strip a frame from a compiled pose: pos/quat become relative to the frame
1528+ void mjXWriter::FrameLocal (const mjCFrame* frame, double pos[3 ], double quat[4 ]) {
1529+ if (!frame) { return ; }
1530+ double ipos[3 ], iquat[4 ];
1531+ mjuu_frameinvert (ipos, iquat, frame->pos , frame->quat );
1532+ mjuu_frameaccumChild (ipos, iquat, pos, quat);
1533+ }
1534+
1535+
1536+ XMLElement* mjXWriter::OneFrame (XMLElement* elem, mjCFrame* frame, string_view childclass) {
15481537 if (!frame) { return elem; }
1538+ double unitq[4 ] = {1 , 0 , 0 , 0 };
15491539
1550- // TODO: empty classname should not occur (but does)
1551- if (frame->name .empty () && (frame->classname .empty () || frame->classname == " main" )) {
1540+ // pose relative to the parent frame
1541+ double pos[3 ], quat[4 ];
1542+ mjuu_copyvec (pos, frame->pos , 3 );
1543+ mjuu_copyvec (quat, frame->quat , 4 );
1544+ FrameLocal (frame->frame , pos, quat);
1545+
1546+ // omit unnamed identity frame with no childclass
1547+ bool has_class = !frame->classname .empty () && frame->classname != childclass;
1548+ if (frame->name .empty () && !has_class &&
1549+ SameVector (pos, unitq + 1 , 3 ) && SameVector (quat, unitq, 4 )) {
15521550 return elem;
15531551 }
15541552
15551553 XMLElement* frame_elem = InsertEnd (elem, " frame" );
15561554 WriteAttrTxt (frame_elem, " name" , frame->name );
1557- if (frame->classname != " main" ) { WriteAttrTxt (frame_elem, " childclass" , frame->classname ); }
1555+
1556+ // childclass, unless inherited from the enclosing frame or body
1557+ if (has_class) {
1558+ WriteAttrTxt (frame_elem, " childclass" , frame->classname );
1559+ }
1560+
1561+ WriteAttr (frame_elem, " pos" , 3 , pos, unitq + 1 );
1562+ WriteAttr (frame_elem, " quat" , 4 , quat, unitq);
15581563 return frame_elem;
15591564}
15601565
@@ -1574,9 +1579,13 @@ void mjXWriter::Body(XMLElement* elem, mjCBody* body, mjCFrame* frame, string_vi
15741579 WriteAttrTxt (elem, " childclass" , body->classname );
15751580 }
15761581
1577- // write pos if it's not {0, 0, 0}
1578- if (body->pos [0 ] || body->pos [1 ] || body->pos [2 ]) { WriteAttr (elem, " pos" , 3 , body->pos ); }
1579- WriteAttr (elem, " quat" , 4 , body->quat , unitq);
1582+ // pose relative to the body's frame
1583+ double pos[3 ], quat[4 ];
1584+ mjuu_copyvec (pos, body->pos , 3 );
1585+ mjuu_copyvec (quat, body->quat , 4 );
1586+ FrameLocal (body->frame , pos, quat);
1587+ WriteAttr (elem, " pos" , 3 , pos, unitq + 1 );
1588+ WriteAttr (elem, " quat" , 4 , quat, unitq);
15801589 if (body->mocap ) { WriteAttrKey (elem, " mocap" , bool_map, 2 , 1 ); }
15811590
15821591 // gravity compensation
@@ -1666,8 +1675,8 @@ void mjXWriter::Body(XMLElement* elem, mjCBody* body, mjCFrame* frame, string_vi
16661675 classname.empty () ? childclass : classname);
16671676 }
16681677
1669- // write plugin
1670- if (body->plugin .active ) { OnePlugin (InsertEnd (elem, " plugin" ), &body->plugin ); }
1678+ // write plugin, once: not again inside the body's frames
1679+ if (!frame && body->plugin .active ) { OnePlugin (InsertEnd (elem, " plugin" ), &body->plugin ); }
16711680
16721681 // write children recursively
16731682 int i = 0 , j = 0 ;
@@ -1694,9 +1703,12 @@ void mjXWriter::Body(XMLElement* elem, mjCBody* body, mjCFrame* frame, string_vi
16941703
16951704 // write frame if its frame matches the current frame
16961705 if (fframe->frame == frame) {
1697- string classname =
1698- fframe && !fframe->classname .empty () ? fframe->classname : body->classname ;
1699- Body (OneFrame (elem, fframe), body, fframe, childclass);
1706+ // class active around the new frame: the current frame's, else the body's
1707+ string classname = frame && !frame->classname .empty () ? frame->classname : body->classname ;
1708+ Body (OneFrame (elem, fframe, classname.empty () ? childclass : classname),
1709+ body,
1710+ fframe,
1711+ childclass);
17001712 }
17011713 }
17021714
0 commit comments