Gnac

Understanding LDD's LXFML Schema

Recommended Posts

For a while now, I have been itching to know just how LXFML files work. Although there is official documentation to explain the purpose of the <nodes>, information which details the attributes of these nodes is scarce. The most useful thing to understand would be the "transformation" attribute of the <Bone> nodes, which appears to contain data about the position and orientation of each <brick>.

However, before we can even begin to try to understand the comma-seperated values here, we need to understand the LDD grid, how the application displays it to you, the user, and how it relates to the 3D Cartesian Co-ordinate System. Take a look at this image here!

gujobw.jpg

This is what I have learned about the LDD grid from doing a series of tests on an instance of part 3039, placed in the very centre of the LDD grid. I planned 32 test cases which resulted in all the basic positions for a brick that I could think of, and made a PHP script to read the LXFs, extract their values, and display them in a comparison table. The X, Y and Z movement values are pretty obvious, but the rotation values less so. 10 tests are pending (because I think I really should have made sense of these values by now), and I see no sense in doing them until I have an "epiphany". Seriously, Just LOOK at the changed values (in red) on that table. Could there be some sort of bitwise combinations going on with fields 1-9? My brain is getting smashed in by plastic rocks here.

Share this post


Link to post
Share on other sites

That transformation is not more not less than a standard 3D rotatation matrix.

Just write it into 4 rows, 3 numbers each and add 4th column containing just 0,0,0,1.

And use matrix math to calculate final coordinates of a given brick. Take input position as (0,0,0,0) and apply the matrix to get output position of a brick in standard coordinates (x, y, z, i). Ignore the "i" value.

Share this post


Link to post
Share on other sites

As a person whose brain goes through a gestalt collapse upon dealing with abstract concepts like discerning left from light, reading analogue clocks, and mathematics, I will need to read your post several times to understand what you mean, but thanks!

Share this post


Link to post
Share on other sites

Hehe, ok, I will be more precise:

transformation="1,2,3,4,5,6,7,8,9,10,11,12"

should be written into matrix form like:

1,2,3

4,5,6

7,8,9

10,11,12

and the add the 4th column:

1,2,3,0

4,5,6,0

7,8,9,0

10,11,12,1

and use that in math like

(0,0,0,0) multiply by

(1,2,3,0

4,5,6,0

7,8,9,0

10,11,12,1)

to get output coordinates.

Share this post


Link to post
Share on other sites

I too have been looking at the LXFML files trying to figure out which brick is attached to which. The key seems to be in the "boneRefs" element of "Rigid", however they are always listed in linear descending order. From playing with it in LDD, it seems it will even shuffle around the "boneRefs" to ensure the order in "Rigid" is this way. In any case, it seems impossible to determine how branching structures are connected. (Unless they use the transformation data, but that seems absurd)

There must be some logic to it, as importing LXFMLs gives all the connections as you'd expect. Can anybody shed any light?

This seemed an appropriate thread to ask in, apologies if not.

Share this post


Link to post
Share on other sites

I can probably help with this topic since I've been unpicking the schema myself and using code to generate mathematical shapes such as the following. It will take me a little while (perhaps a couple of days to refine my knowledge down into some posts).

1375302212m_SPLASH.jpg

If you haven't already done so, get the details of the schema from this thread: LXF Schema

Edited by The_Cook

Share this post


Link to post
Share on other sites

A very simple lxfml file of a light fitting, comprising of a Technic cross-hole brick with a stick with holder inserted into it, that in turn is holding an assembly element (palm tree top) with a 1x1 round and a dish. Feel free to cut-and-paste this into notepad, save it as light1.lxfml and load it into LDD to see what I mean.

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<LXFML versionMajor="5" versionMinor="0" name="Light1">
 <Meta>
   <Application name="LEGO Digital Designer" versionMajor="4" versionMinor="3"/>
   <Brand name="LDDExtended"/>
   <BrickSet version="1264"/>
 </Meta>
 <Cameras>
   <Camera refID="0" fieldOfView="80" distance="79.281982421875" transformation="0.030392518267035484,0,-0.99953794479370117,-0.93535268306732178,0.35257109999656677,-0.028440864756703377,0.3524080216884613,0.93578499555587769,0.010715517215430737,27.939624786376953,74.190887451171875,0.84954798221588135"/>
 </Cameras>
 <Bricks cameraRef="0">
   <Brick refID="0" designID="3062">
  <Part refID="0" designID="3062" materials="44,0" decoration="0">
    <Bone refID="0" transformation="0.70710670948028564,0,0.7071068286895752,0,1,0,-0.7071068286895752,0,0.70710670948028564,-0.79999923706054688,1.5809999704360962,3.200000524520874">
    </Bone>
  </Part>
   </Brick>
   <Brick refID="1" designID="2566">
  <Part refID="1" designID="2566" materials="26">
    <Bone refID="1" transformation="1,0,0,0,1,0,0,0,1,-0.79999935626983643,-0.1390000581741333,3.2000002861022949">
    </Bone>
  </Part>
   </Brick>
   <Brick refID="2" designID="4740">
  <Part refID="2" designID="4740" materials="26,0" decoration="0">
    <Bone refID="2" transformation="0.7071068286895752,0,-0.70710670948028564,0,1,0,0.70710670948028564,0,0.7071068286895752,-0.7999987006187439,2.5409998893737793,3.1999998092651367">
    </Bone>
  </Part>
   </Brick>
   <Brick refID="3" designID="48729">
  <Part refID="3" designID="48729" materials="26">
    <Bone refID="3" transformation="0.99999988079071045,0,0,0,0,1,0,-1,0,-0.79999971389770508,0.58100003004074097,2.0000002384185791">
    </Bone>
  </Part>
   </Brick>
   <Brick refID="4" designID="32064">
  <Part refID="4" designID="32064" materials="194">
    <Bone refID="4" transformation="1,0,0,0,1,0,0,0,1,-1.2000000476837158,0.0010000000474974513,2">
    </Bone>
  </Part>
   </Brick>
 </Bricks>
 <RigidSystems>
   <RigidSystem>
  <Rigid refID="0" transformation="0.70710670948028564,0,0.7071068286895752,0,1,0,-0.7071068286895752,0,0.70710670948028564,-0.79999923706054688,1.5809999704360962,3.200000524520874" boneRefs="0,1,2"/>
  <Rigid refID="1" transformation="0.99999988079071045,0,0,0,0,1,0,-1,0,-0.79999971389770508,0.58100003004074097,2.0000002384185791" boneRefs="3"/>
  <Rigid refID="2" transformation="1,0,0,0,1,0,0,0,1,-1.2000000476837158,0.0010000000474974513,2" boneRefs="4"/>
  <Joint type="hinge">
    <RigidRef rigidRef="1" a="0,0,1" z="1,0,0" t="0,1.2000000476837158,-0.1600000411272049"/>
    <RigidRef rigidRef="0" a="0,-1,0" z="-1,0,0" t="0,-0.84000003337860107,0"/>
  </Joint>
  <Joint type="hinge">
    <RigidRef rigidRef="1" a="0,1,0" z="1,0,0" t="0,0.39999991655349731,0"/>
    <RigidRef rigidRef="2" a="0,0,1" z="1,0,0" t="0.40000000596046448,0.57999998331069946,0.40000000596046448"/>
  </Joint>
   </RigidSystem>
 </RigidSystems>
 <GroupSystems>
   <GroupSystem>
   </GroupSystem>
 </GroupSystems>
 <BuildingInstructions>
 </BuildingInstructions>
</LXFML>

So, lets pick this simple lxfml file apart and see what we've got.

  • The first line simply states that the file is XML and what version and encoding is being used. All fairly straightforward boilerplate stuff.
  • Next we're into our first XML node that is unique to lxfml. Again there's nothing complex here the attributes are detailing the version and the name of the file the actual filename is "light1.lxfml" so it tends to match but doesn't need to.
  • The next node in is the meta node with three child nodes.
    • Application details which application created the file. In this case it was LDD but one assumes TLG have other non-public applications that they can use internally to create LXFML files. I'm fairly sure I've seen screenshots of apps that they use help them create the giant models such as the X-Wing or Bag-End.
    • Brand. This actually seems to map to the mode that LDD was in, in this case I was using LDDExtended.
    • Brickset. The version of the bricks database and models that is being used. Other on the forum track this more intently than me, I'm just using the latest version of LDD.

    [*]Camera node. Details of the camera. I'll admit that I've not investigated the transformation attribute in too much detail, it's obviously 12 double precision floating point values, but I don't know whether it's detailing where the camera is in worldspace or whether it's detailing how to get worldspace aligned to camera, one being the inverse transform of the other.

    [*]Bricks node. A list of bricks. I'm not sure what the camera node attribute is doing, I've just been defaulting it to the id of the one camera that was defined in the previous node.

    • Brick node. The entry for a brick, where a brick can be made up of several parts. In this example all the bricks are plain simple bricks, but in the case of something like Minifigure Legs which count as a single brick that is made up of three parts, hips, left leg, right leg. refId is a unique identifier for every brick in the build, it appears to start counting upwards from 0. designId is the Part# number for the brick which you can see in the status bar at the bottom of LDD when you click on a placed brick.
      • Part node. Again a unique identifier. My notes don't tell me whether this increments separately from the brick node, in this example they are the same but that might not be the case if you were using bricks made up of multiple parts. For all simple bricks the designId is the same Part# number from the brick. Materials is a comma separated list of colour numbers that represent the colours of the part. Most parts just take one colour, colour numbers can be found in the status bar at the bottom of LDD or others on this forum keep a complete list of all the possible colours above and beyond what LDD provides access to. Decoration is the decal applied to the part, eg. minifig faces. Again lists of the decoraitons are maintained by others on this forum. Only parts that could have decorations on them in real life are likely to be properly set up to accept decorations, all others will probably end up a bit of a mess.
        • Bone node. Bone is a term dervied from computer graphics, in this particular instance it's easiest to consider that it's storing the information about the placement of the brick. Again the refId is a unique reference for this bone. Transformation contains 12 comma separated double precision floating point values that represent the first three columns of a 3d transformation matrix and are thus able to handle any linear transformation. It's something that you'll need to learn for yourself but the first 9 entries handle rotation, the last 3 are translation. It's worth noting that the origin of most bricks is at the bottom with the y-axis heading up through the center of a stud, this makes rotations around the axis of the stud slightly easier. Brick width is 0.8, brick height is 0.96 and plate height 0.32. This probably ties in with real world dimensions in some form, but I don't have the details to hand. The double precision floating point format can't necessary cope with precise decimal values as we might expect to see them, therefore you'll see slight deviations from the values given above, eg. -0.79999935626983643 instead of -0.8. Those 0.707 values are 45 degree rotations, they correspond to the sin or cosine of 45 degrees (or PI/4 in radians) and are part of the transformation. Transformations are in world coordinates.

    [*]RigidSystems. This node contains all the rigid systems, ie. groups of bricks that are stuck together and therefore rotate, and of the way in which these systems are joined together.

    • RigidSystem. Another level of container, not sure why it's needed...
      • Rigid. Each group of bricks gets it's own rigid system, each rigid system has a unique identifier. The transformation represents the orientation of the system within world coordinates. BoneRefs is a comma separated list of all the bones (and therefore parts) that are part of the system. Note that in hinge bricks the base part will be in one rigidsystem and the rocking part in another rigid system.
      • Joint. The relationship between two rigid systems. I've seen both "hinge" for parts where the relationship is constrained to a rotation around a single axis, eg. hinges, turntables, minifig hands, etc... and "spherical" (I think that's the phrase, my notes are lacking because I've never needed to use it) for ball and socket joints where there are 3 full axis of manipulation.
        • RigidRef. There needs to be 2 of these for each joint. The rigidRef attribute contains the identifier of on of the above rigid nodes. The a, z and t represent the axis of the rotation, a direction perpendicular to that from which rotation starts and the origin of the axis of rotation in the actual brick's frame of coordinates. These are all obtained from data within the bricks. Whilst lxfml is a public format that we can discuss and play with; TLG don't officially publish details of the xml format that describes the bricks and investigating that could be considered a violation of the EULA for LDD.

    [*]GroupSystems - I've not investiaged this

    [*]BuildingInstructions - Ive not investigated this

In truth LDD might not use all of the features of the LXFML file format, eg. GroupSystems and BuildingInstructions, they might only be utilised by some of TLG's internal tools.Anyone wanting to understand and manipulate the format really does need to learn about 3d matrix transformations in computer graphics since it is a thorough understanding of them that will allow you to place bricks where you want them.Once you understand the format you can create entire models without ever actually placing a brick in LDD. Computer programs can be used to place all the bricks according to algorithms, my own work is this space has been to create an involute spiral as the basis of a Tower Of Babel, detail is then layered over procedurally.1398363626m_SPLASH.jpg

Edited by The_Cook

Share this post


Link to post
Share on other sites

The good news is that if you are programming tools to work with the LXFML stuff, there should be matrix math libraries out there for most programming languages that provide all the options you need

Edited by jonwil

Share this post


Link to post
Share on other sites

The good news is that if you are programming tools to work with the LXFML stuff, there should be matrix math libraries out there for most programming languages that provide all the options you need

Yes, I have my own personal set of matrix math libraries from previous projects and a background in computer graphics. The concepts are the same it's just that it's bricks not triangles that are being placed.

Edited by The_Cook

Share this post


Link to post
Share on other sites

Camera Exercise for Beginners:

Start LDD with new file and place a red 1x1 plate at the center of the scene and black round 1x1 next to it but not attached. Save as “cam.lxfml”

Open the LXFML file with a text editor (Note Pad or TextEdit). The red plate will have material=21 and the black plate will have the material=26. Copy & Paste the “<Camera fefID=”0” transfromation” to the black plate’s transfromation. Save as “cam1.lxfml” You could also delete the lines between <RigidSystems>

Open cam1.lxfml with LDD. I keep both LDD and the text editor open for this exercise and just go back and forth. You should see the red plate and note a change in the stud grid. Now use the Camera control command “Reset view”

You found the black plate and will need to rotate the view and reset a few times to get both the red and black plate in the scene at the same time. The black plate is where the camera was at the beginning (in cam.lxfml) and we have move the camera to a new location.

Now we have all kinds of questions to work on. If this does not go smoothly and you get errors the tricky parts are in editing the LXFML file. All the little symbols (< > “ / , ) must be in the right place.

Share this post


Link to post
Share on other sites

post-121589-0-99033800-1402688497_thumb.gif

The pic is the final result of a study into the transformations twelve CSV. This study is build on the knowledge supplied by the contributors to Gnac’s post. The sphere is 202 1x1 round brick with hole #85861. It confirms my suspicions that CSV twelve are nine trigonometric functions and three real coordinates. I used a spreadsheet to make the calculations and generate the LXFML file. Thanks to bbqqq for sharing the process he uses to generate the random min figs with decorations.

Share this post


Link to post
Share on other sites

It confirms my suspicions that CSV twelve are nine trigonometric functions and three real coordinates.

Correct it's the 12 values needed to produce an affine transformations in 3d space. As an aside: theoretically there should be 16 numbers, the last 4 are used to skew and shear the geometry but since physical plastic bricks can't be skewed those additional numbers aren't required in LDD.

Whilst there are many tutorials on-line that describe the affine transformations my personal preference is for: 3d Computer Graphics by Alan Watt who I always felt presented a fairly readable introduction to the topic.

Share this post


Link to post
Share on other sites

The source for most of this study was:

Appendix C Coordinate Transformations from the book, Global Positioning System, Inertial Navigation, and Integration, Mohinder S. Grewal, Lawrence R. Weill, Angus P. Andrews

When time permits I will be setting up an account on BrickShelf to post notes and files.

Share this post


Link to post
Share on other sites

I have uploaded some files to BrickShelf

Cartesian fig3.gif reworks the standard axis to LDD axis.

http://www.brickshelf.com/gallery/Oldnut/Sphere/cartesiam_fig3.gif

Cartesian.rtf is the notes (rough) for the study to date.

http://www.brickshelf.com/cgi-bin/gallery.cgi?f=544802

Sphere_w_axis.LXF is a complete sphere with spears on the three axis.

http://www.brickshelf.com/gallery/Oldnut/Sphere/sphere_w_axis.lxf

Edited by OLDNUT

Share this post


Link to post
Share on other sites
On 4/24/2014 at 8:40 PM, The_Cook said:

Brick width is 0.8, brick height is 0.96 and plate height 0.32. This probably ties in with real world dimensions in some form, but I don't have the details to hand.

It is called a Centimeter and it's part of that new invention they use in the modern part of the world for a couple of centuries now, it is called "The Metrical System", sacrificing tradition for clean usability :laugh_hard::laugh_hard::laugh_hard:
I don't understand why they did not go for mm (0.1 cm) which is more common, but the good thing: within the magic metrical system it is just a digit it to the left, aint that awsome?
Sorry for the sarcasm, no offense.

Share this post


Link to post
Share on other sites
On 2/26/2014 at 2:07 PM, hrontos said:

and use that in math like

(0,0,0,0) multiply by

(1,2,3,0

4,5,6,0

7,8,9,0

10,11,12,1)

to get output coordinates.

Uhm as I understand I do have the output coordinates already in the last three numbers, But i'd like to get something like one Euler angle between 2 rotation matrices to get the difference, any suggestion or hint where to get that? most things i found on the web tell me: these matrices are unambiguous so how do I use them (something simple would be nice using floats, vecor3 or maybe quaternions)
 

Share this post


Link to post
Share on other sites

I guess B*transpose(A) is what you are looking for ? Also be aware the Matrix(1,2,....12) is a transformation matrix (which also contains movement in x,y,z axis) and not only a rotation matrix as far as I remember.

 

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.