@@ -126,12 +126,12 @@ public XDocument BuildURDF(ZOSimDocumentRoot documentRoot) {
126126 if ( joints [ 0 ] == joint ) { // if base joint do not apply any offset to parent link
127127 offset = Vector3 . zero ;
128128 }
129- BuildURDFLink ( joint . Parent , robot , offset ) ;
129+ BuildLink ( joint . Parent , robot , offset ) ;
130130 links . Add ( joint . Parent ) ;
131131 }
132132 if ( links . Contains ( joint . Child ) == false ) { // build child link if not exist
133133 Vector3 offset = - 1.0f * joint . ConnectedAnchor ;
134- BuildURDFLink ( joint . Child , robot , offset ) ;
134+ BuildLink ( joint . Child , robot , offset ) ;
135135 links . Add ( joint . Child ) ;
136136 }
137137 }
@@ -142,7 +142,7 @@ public XDocument BuildURDF(ZOSimDocumentRoot documentRoot) {
142142 link . SetAttributeValue ( "name" , "World" ) ;
143143 robot . Add ( link ) ;
144144
145- BuildURDFLink ( simOccurrence , robot , Vector3 . zero ) ;
145+ BuildLink ( simOccurrence , robot , Vector3 . zero ) ;
146146
147147 XElement jointX = new XElement ( "joint" ) ;
148148 jointX . SetAttributeValue ( "name" , $ "World_to_{ simOccurrence . Name } ") ;
@@ -255,12 +255,15 @@ protected void BuildURDFJoints(XElement robot, ZOSimOccurrence parent, ZOSimOccu
255255 /// <summary>
256256 /// URDF has a "flattened" hierarchy where the hierarchy is build by URDF joints.
257257 /// </summary>
258- protected void BuildURDFLink ( ZOSimOccurrence simOccurrence , XElement robot , Vector3 anchorOffset ) {
258+ protected void BuildLink ( ZOSimOccurrence simOccurrence , XElement robot , Vector3 anchorOffset ) {
259259
260260 XElement link = new XElement ( "link" ) ;
261261 link . SetAttributeValue ( "name" , simOccurrence . Name ) ;
262262 robot . Add ( link ) ;
263263
264+ // build inertial
265+ BuildLinkInertial ( simOccurrence , link ) ;
266+
264267
265268 // build the visual elements
266269 // go through the children
@@ -272,25 +275,25 @@ protected void BuildURDFLink(ZOSimOccurrence simOccurrence, XElement robot, Vect
272275 // go through the children of the visuals and get all the models
273276 foreach ( Transform visualsChild in child ) {
274277 // check if it is a primitive type (cube, sphere, cylinder, etc)
275- BuildURDFVisuals ( visualsChild , link , anchorOffset ) ;
278+ BuildLinkVisuals ( visualsChild , link , anchorOffset ) ;
276279
277280 // we will do any collider that are attached to the visual
278- BuildURDFCollisions ( visualsChild , link , anchorOffset ) ;
281+ BuildLinkCollisions ( visualsChild , link , anchorOffset ) ;
279282 }
280283 }
281284
285+ // build collisions
282286 if ( child . name . ToLower ( ) == "collisions" ) {
283287 // go through the children of the collisions and get all the models
284288 foreach ( Transform collisionChild in child ) {
285- BuildURDFCollisions ( collisionChild , link , anchorOffset ) ;
289+ BuildLinkCollisions ( collisionChild , link , anchorOffset ) ;
286290 }
287291 }
288-
289292 }
290293 }
291294
292295
293- protected void BuildURDFCollisions ( Transform collisionTransform , XElement link , Vector3 anchorOffset ) {
296+ protected void BuildLinkCollisions ( Transform collisionTransform , XElement link , Vector3 anchorOffset ) {
294297 Collider [ ] colliders = collisionTransform . GetComponentsInChildren < Collider > ( ) ;
295298 foreach ( Collider collider in colliders ) {
296299 XElement collision = new XElement ( "collision" ) ;
@@ -371,91 +374,121 @@ protected void BuildURDFCollisions(Transform collisionTransform, XElement link,
371374 }
372375 }
373376
374- protected void BuildURDFVisuals ( Transform visualTransform , XElement link , Vector3 anchorOffset ) {
377+ protected void BuildLinkVisuals ( Transform visualTransform , XElement link , Vector3 anchorOffset ) {
375378 // build 3d primitive if exists
376- MeshFilter meshFilter = visualTransform . GetComponent < MeshFilter > ( ) ;
377- if ( meshFilter == null ) {
378- // Check children
379- meshFilter = visualTransform . GetComponentInChildren < MeshFilter > ( ) ;
380- }
381- if ( meshFilter ) {
382-
383- MeshRenderer meshRenderer = visualTransform . GetComponent < MeshRenderer > ( ) ;
384- Collider collider = null ;
385- XElement visual = new XElement ( "visual" ) ;
386- visual . SetAttributeValue ( "name" , visualTransform . name ) ;
387- XElement geometry = new XElement ( "geometry" ) ;
388-
389- if ( meshFilter . sharedMesh . name . Contains ( "Cube" ) ) {
390- XElement box = new XElement ( "box" ) ;
391-
392- Vector3 boxSize = visualTransform . localScale . Unity2RosScale ( ) ;
393- box . SetAttributeValue ( "size" , boxSize . ToXMLString ( ) ) ;
394- geometry . Add ( box ) ;
395-
396- collider = visualTransform . GetComponent < BoxCollider > ( ) ;
397- if ( collider ) {
398- //TODO: add box collider
379+ // Check children
380+ MeshFilter [ ] meshFilters = visualTransform . GetComponentsInChildren < MeshFilter > ( ) ;
381+ foreach ( MeshFilter meshFilter in meshFilters ) {
382+ if ( meshFilter ) {
383+
384+ MeshRenderer meshRenderer = visualTransform . GetComponent < MeshRenderer > ( ) ;
385+ Collider collider = null ;
386+ XElement visual = new XElement ( "visual" ) ;
387+ visual . SetAttributeValue ( "name" , visualTransform . name ) ;
388+ XElement geometry = new XElement ( "geometry" ) ;
389+
390+ if ( meshFilter . sharedMesh . name . Contains ( "Cube" ) ) {
391+ XElement box = new XElement ( "box" ) ;
392+
393+ Vector3 boxSize = visualTransform . localScale . Unity2RosScale ( ) ;
394+ box . SetAttributeValue ( "size" , boxSize . ToXMLString ( ) ) ;
395+ geometry . Add ( box ) ;
396+
397+ } else if ( meshFilter . sharedMesh . name . Contains ( "Sphere" ) ) {
398+ XElement sphere = new XElement ( "sphere" ) ;
399+ float radius = visualTransform . localScale . x / 2.0f ;
400+ sphere . SetAttributeValue ( "radius" , radius ) ;
401+ geometry . Add ( sphere ) ;
402+
403+ } else if ( meshFilter . sharedMesh . name . Contains ( "Cylinder" ) ) {
404+ XElement cylinder = new XElement ( "cylinder" ) ;
405+ float radius = visualTransform . localScale . x / 2.0f ;
406+ float height = visualTransform . localScale . y * 2.0f ;
407+ cylinder . SetAttributeValue ( "radius" , radius ) ;
408+ cylinder . SetAttributeValue ( "length" , height ) ;
409+
410+ geometry . Add ( cylinder ) ;
411+
412+
413+ } else { // regular mesh so export meshes as OBJ
414+ XElement mesh = new XElement ( "mesh" ) ;
415+ mesh . SetAttributeValue ( "filename" , $ "{ visualTransform . name } .obj") ;
416+ Vector3 scale = visualTransform . localScale ;
417+ mesh . SetAttributeValue ( "scale" , scale . ToXMLString ( ) ) ;
418+ geometry . Add ( mesh ) ;
419+
420+ _visualMeshesToExport . Add ( visualTransform ) ;
399421 }
400- } else if ( meshFilter . sharedMesh . name . Contains ( "Sphere" ) ) {
401- XElement sphere = new XElement ( "sphere" ) ;
402- float radius = visualTransform . localScale . x / 2.0f ;
403- sphere . SetAttributeValue ( "radius" , radius ) ;
404- geometry . Add ( sphere ) ;
405422
406- collider = visualTransform . GetComponent < SphereCollider > ( ) ;
407- if ( collider ) {
408- //TODO: add box collider
409- }
423+ if ( geometry . HasElements ) {
424+ // build origin
425+ Vector3 position = visualTransform . localPosition + anchorOffset ;
426+ Vector3 xyz = position . Unity2Ros ( ) ;
427+ Vector3 rpy = new Vector3 ( - visualTransform . localEulerAngles . z * Mathf . Deg2Rad ,
428+ visualTransform . localEulerAngles . x * Mathf . Deg2Rad ,
429+ - visualTransform . localEulerAngles . y * Mathf . Deg2Rad ) ;
410430
411- } else if ( meshFilter . sharedMesh . name . Contains ( "Cylinder" ) ) {
412- XElement cylinder = new XElement ( "cylinder" ) ;
413- float radius = visualTransform . localScale . x / 2.0f ;
414- float height = visualTransform . localScale . y * 2.0f ;
415- cylinder . SetAttributeValue ( "radius" , radius ) ;
416- cylinder . SetAttributeValue ( "length" , height ) ;
431+ XElement origin = new XElement ( "origin" ) ;
432+ origin . SetAttributeValue ( "xyz" , xyz . ToXMLString ( ) ) ;
433+ origin . SetAttributeValue ( "rpy" , rpy . ToXMLString ( ) ) ;
417434
418- geometry . Add ( cylinder ) ;
435+ visual . Add ( origin ) ;
419436
420- collider = visualTransform . GetComponent < MeshCollider > ( ) ;
421- if ( collider ) {
422- //TODO: add box collider
437+ visual . Add ( geometry ) ;
438+ link . Add ( visual ) ;
423439 }
424440
425- } else { // regular mesh so export meshes as OBJ
426- XElement mesh = new XElement ( "mesh" ) ;
427- mesh . SetAttributeValue ( "filename" , $ "{ visualTransform . name } .obj") ;
428- Vector3 scale = visualTransform . localScale ;
429- mesh . SetAttributeValue ( "scale" , scale . ToXMLString ( ) ) ;
430- geometry . Add ( mesh ) ;
431-
432- _visualMeshesToExport . Add ( visualTransform ) ;
433- }
434-
435- if ( geometry . HasElements ) {
436- // build origin
437- Vector3 position = visualTransform . localPosition + anchorOffset ;
438- Vector3 xyz = position . Unity2Ros ( ) ;
439- Vector3 rpy = new Vector3 ( - visualTransform . localEulerAngles . z * Mathf . Deg2Rad ,
440- visualTransform . localEulerAngles . x * Mathf . Deg2Rad ,
441- - visualTransform . localEulerAngles . y * Mathf . Deg2Rad ) ;
442-
443- XElement origin = new XElement ( "origin" ) ;
444- origin . SetAttributeValue ( "xyz" , xyz . ToXMLString ( ) ) ;
445- origin . SetAttributeValue ( "rpy" , rpy . ToXMLString ( ) ) ;
446-
447- visual . Add ( origin ) ;
448-
449- visual . Add ( geometry ) ;
450- link . Add ( visual ) ;
451441 }
452442
453443 }
454-
455-
456444 }
457445
458-
446+ public void BuildLinkInertial ( ZOSimOccurrence simOccurrence , XElement link ) {
447+ Rigidbody rigidbody = simOccurrence . GetComponent < Rigidbody > ( ) ;
448+ if ( rigidbody != null ) {
449+ XElement inertial = new XElement ( "inertial" ) ;
450+ XElement mass = new XElement ( "mass" ) ;
451+ mass . SetAttributeValue ( "value" , rigidbody . mass ) ;
452+
453+ // calculate the inertia components
454+ // Unity/PhysX is kinda weird in that it is represented by a diagonal vector and and rotation quaternion.
455+ Matrix4x4 lambdaMatrix = new Matrix4x4 ( ) ;
456+ lambdaMatrix [ 0 , 0 ] = rigidbody . inertiaTensor . x ;
457+ lambdaMatrix [ 1 , 1 ] = rigidbody . inertiaTensor . y ;
458+ lambdaMatrix [ 2 , 2 ] = rigidbody . inertiaTensor . z ;
459+ lambdaMatrix [ 3 , 3 ] = 1.0f ;
460+
461+ Matrix4x4 qMatrix = Matrix4x4 . Rotate ( rigidbody . inertiaTensorRotation ) ;
462+ Matrix4x4 qMatrixTransposed = qMatrix . transpose ;
463+ Matrix4x4 inertiaMatrix = qMatrix * lambdaMatrix * qMatrixTransposed ;
464+
465+ float ixx = inertiaMatrix [ 2 , 2 ] ;
466+ float ixy = - inertiaMatrix [ 0 , 2 ] ;
467+ float ixz = inertiaMatrix [ 1 , 2 ] ;
468+ float iyy = inertiaMatrix [ 0 , 0 ] ;
469+ float iyz = - inertiaMatrix [ 0 , 1 ] ;
470+ float izz = inertiaMatrix [ 1 , 1 ] ;
471+
472+ XElement inertia = new XElement ( "inertia" ) ;
473+ inertia . SetAttributeValue ( "ixx" , ixx ) ;
474+ inertia . SetAttributeValue ( "ixy" , ixy ) ;
475+ inertia . SetAttributeValue ( "ixz" , ixz ) ;
476+ inertia . SetAttributeValue ( "iyy" , iyy ) ;
477+ inertia . SetAttributeValue ( "iyz" , iyz ) ;
478+ inertia . SetAttributeValue ( "izz" , izz ) ;
479+
480+ inertial . Add ( inertia ) ;
481+
482+ // center of mass
483+ XElement origin = new XElement ( "origin" ) ;
484+ origin . SetAttributeValue ( "xyz" , rigidbody . centerOfMass . Unity2Ros ( ) . ToXMLString ( ) ) ;
485+ origin . SetAttributeValue ( "rpy" , Vector3 . zero . ToXMLString ( ) ) ; // BUG: always zero?
486+ inertial . Add ( origin ) ;
487+
488+
489+ link . Add ( inertial ) ;
490+ }
491+ }
459492
460493
461494
0 commit comments