.opf
The .opf
file extension stands for Open Plant Format. It allows to store the topology, the geometry and any other attribute of a plant. The format is heavily inspired by the MTG format (Multi-scale Tree Graph) for the topology representation.
The file is encoded in the XML
format, so many editors can render its structure. The .opf
file describes vertices, faces, normals, texture coordinates, materials (colors), attributes and transformation matrices for the geometry.
A very simple example of a virtual plant with two metamers (two internodes bearing one leaf each) is available here:
The file renders as follows:
You don’t need to understand the details about the structure of the .opf
files if you’re not a developer. A standard use-case for users is to build plants using specialized software such as XPlo, VPalm or PRINCIPES. If you’re a standard user, please go to the next page (especially if it’s your first time reading).
If you are more curious (or don’t like people telling you what to do), or need to develop around .opf
files, more details are presented below.
The .opf
file is a simple XML
file under the hood. It is designed with several parts (i.e. XML nodes):
- meshBDD
- materialBDD
- shapeBDD
- attributeBDD
- topology
Here is our example .opf
minus the details inside each node (stripped information is denoted by [...]
):
<?xml version="1.0" encoding="UTF-8" ?>
<opf version="2.0" editable="true">
<meshBDD>
[...]
</meshBDD>
<materialBDD>
[...]
</materialBDD>
<shapeBDD>
[...]
</shapeBDD>
<attributeBDD>
[...]
</attributeBDD>
<topology class="Scene" scale="0" id="3">
[...]
</topology>
</opf>
The details about each node is given below.
A plant is a collection of similar components repeated many times (e.g. thousands of leaves). The most efficient way to store a plant into a file is to use reference meshes that describe the average geometry of a given component (e.g. a leaf or an internode), and then to transform this reference mesh to match precisely the geometry of each component. This method reduce the amount of data written in the .opf
file because the geometry of each component is only stored in a 4*3
transformation matrix instead of a data heavy mesh. This matrix is then used to transform the reference mesh to match the geometry of a given component.
Hence, the meshBDD
node of the .opf
file lists all the reference meshes used to build the plant geometry. Here is the structure of the meshBDD
from our example. Each reference mesh is represented by a mesh
node with a given id, here we have three meshes with id
1, 2 and 0 respectively:
[...]
<meshBDD>
<mesh name="" shape="" Id="1" enableScale="true" >
[...]
</mesh>
<mesh name="" shape="" Id="2" enableScale="false" >
[...]
</mesh>
<mesh name="" shape="" Id="0" enableScale="false" >
[...]
</mesh>
</meshBDD>
[...]
Our example could be built using 2 meshes only: a reference leaf and a reference internode. It was built using Xplo, and the software decided to use two different reference meshes and materials for the internode for some reason instead of adapting the transformation matrix.
Each mesh node lists itself five information:
- the points coordinates as a vector of the form x1 y1 z1 x2 y2 z2 ...
;
- the normal coordinates as a vector of the same form and order than the points;
- the texture coordinates (textureCoords
) as a vector of the form u1 v1 u2 v2 ...
;
- and the faces, listing face
nodes, each containing a vector of 3 integers referring to the points index used to build a face. Point indices are written as i1 i2 i3
. The preferred way for a face is to be a triangle (i.e. reference 3 points).
Here are more details about the first mesh:
<mesh name="" shape="" Id="1" enableScale="true" >
<points>
0.10162586 -0.0012269035 -0.0 0.3519142 0.22452335 0.03067259 [...]
</points>
<normals>
-0.17780079 -0.25352615 0.9508477 -0.17780079 -0.25352615 0.9508477 [...]
</normals>
<textureCoords>
0.49726775 -0.38636366 0.010928948 0.003787874 0.0 0.46590912 [...]
</textureCoords>
<faces>
<face Id="0">
0 2 4
</face>
<face Id="1">
0 4 3
</face>
<face Id="2">
4 8 6
</face>
[...]
</faces>
</mesh>
The materialBDD
is used to declare the material properties for visualization purposes. This node is not used by ARCHIMED-φ, but is still mandatory to follow the .opf
standard. It is used when rendering the .opf
in e.g. XPlo.
The materialBDD
is rather short. It lists material
nodes that declare the optical properties of each reference node: the emission, ambient, diffuse, specular and shininess.
The materialBDD
of our example is as follows:
[...]
<materialBDD>
<material Id="1">
<emission>0.0 0.0 0.0 0.0 </emission>
<ambient>1.0 0.0 0.0 1.0 </ambient>
<diffuse>1.0 0.0 0.0 1.0 </diffuse>
<specular>1.0 0.0 0.0 1.0 </specular>
<shininess>10.0 </shininess>
</material>
<material Id="0">
<emission>0.0 0.0 0.0 0.0 </emission>
<ambient>1.0 0.8901961 0.0 1.0 </ambient>
<diffuse>1.0 0.8901961 0.0 1.0 </diffuse>
<specular>1.0 0.8901961 0.0 1.0 </specular>
<shininess>10.0 </shininess>
</material>
</materialBDD>
[...]
Note that each material
has an Id
. This id can be different to the one provided in the meshBDD
(see next paragraph). It is used to link the material to the mesh from the meshBDD
by its Id
.
Each material property (e.g. emission
) stores a vector of 4 values for the red, green, blue, and alpha value ([0-1]
).
The shapeBDD
is used to build a shape. A shape is an object that stores a mesh and a material. In other words, it is used to link each mesh to its material properties. Using shape
is very powerful because it allows to recycle information whenever possible. For example different component types can have a different reference mesh
, but share the same material
properties. In this case, no need to create two identical material
properties, we can just reference the same material instead when building the shape
.
Here is the shapeBDD
of our example:
[...]
<shapeBDD>
<shape Id="1">
<name> Mesh1</name>
<meshIndex>1</meshIndex>
<materialIndex>1</materialIndex>
</shape>
<shape Id="2">
<name> Mesh2</name>
<meshIndex>2</meshIndex>
<materialIndex>0</materialIndex>
</shape>
<shape Id="0">
<name> Mesh0</name>
<meshIndex>0</meshIndex>
<materialIndex>0</materialIndex>
</shape>
</shapeBDD>
[...]
It lists 3 different shapes, each with a different reference mesh and material.
Any kind of additional attributes (read “data”) can be attached to a topology node (i.e. decomp, follow and branch, see next paragraph). The attributeBDD
is used to define the attribute
name
and class
, i.e. data type, integer
, float
, string
, or AMAPStudio custom keyword (e.g. Centimetre
).
Here is the attributeBDD
from our example:
[...]
<attributeBDD>
<attribute name="XX" class="Double"/>
<attribute name="YY" class="Double"/>
<attribute name="ZZ" class="Double"/>
<attribute name="FileName" class="String"/>
<attribute name="Length" class="Centimetre"/>
<attribute name="Width" class="Centimetre"/>
<attribute name="XEuler" class="Double"/>
</attributeBDD>
[...]
It declares seven optional attributes
that can be optionally attached to a topology node. These attributes are generally computed by a software and attached to the .opf
. For example ARCHIMED-φ uses attributes to attach simulation outputs to .opf
s from the scene, e.g. the intercepted PAR of each component. These attributes are handy for visualization purposes.
Note that attributes are not updated automatically when other data is modified. For example the example attributeBDD
provides a “Length” attribute
, which is used to get rapidly the length of the component. But if a user modified the actual length of a component of the .opf
by modifying its transformation matrix, the “Length” attribute
will not be updated to the new length. It is then important to keep in mind that attribute
s are optional data attached to a node, but not necessarily up to date.
The topology
node is used to describe the relational connection between the different components of a plant, i.e. the organization of the plant. The components used are a natural decomposition of the plant, i.e. an internode or a leaf that are used to decompose the plant in metamers, and not an artificial decomposition such as a set of 10 cm long sections of axes.
The description follows the rules of the widely used Multi-scale Tree Graph (MTG). Please read the article from Godin and Caraglio (1998) for a nice introduction to the concept.
But the topology
node of the .opf
enhances this concept by adding further attributes to each component, allowing a close coupling between the plant topology and geometry.
From Godin et al. (1999) Topology deals with the physical connections between plant components, while geometry includes the shape, size, orientation and spatial location of the components.
For example, each component to be rendered in 3D has a shapeIndex
that points to the reference shape used to build the geometry of the component, and a geometry
node that stores the transformation matrix applied to the reference mesh to compute the geometry of the component.
The topology
is built by a succession of components, and the link between them is described using three main keywords:
- follow
, for a succession of growth units created by apical meristems;
- branch
for a ramification;
- decomp
, for a decomposition, e.g a reiteration;
Here is the topology
from our example file, without the details:
[...]
<topology class="Scene" scale="0" id="3"> # -> instantiate the topology with a scene (scale 0)
<decomp class="Individual" scale="1" id="2"> # -> instantiate an individual plant (scale 1)
[...]
<decomp class="Axis" scale="2" id="4"> # -> instantiate the base axis of the plant
[...]
<decomp class="Internode" scale="6" id="5"> # -> Start building the plant: first internode
[...]
<branch class="Leaf" scale="2" id="6"> # -> The 1st internode bears a leaf, branched to it
[...]
</branch>
</decomp>
<follow class="Internode" scale="6" id="10"> # -> The 2d internode follows the 1st internode
[...]
<branch class="Leaf" scale="2" id="11"> # -> The 2d leaf is branched into the 2d internode
[...]
</branch>
</follow>
</decomp>
</decomp>
</topology>
[...]
We can build the topology of our plant only by using the three keywords provided by the MTG method. This method is very powerful and is sufficient to build any topology configuration, from very simple plants to the most complex ones.
We see that each component is delimited by how it is connected to the previous one, and each one has also a class
, a scale
and an id
. The scale
is not very important for our purposes, but other two are:
The class
is used to declare a component type
. This type can be anything, e.g. a leaf
or an internode
, but also a young_leaf
and an old_leaf
. The class
is especially important in ARCHIMED-φ (compared to AMAPStudio) because it is then used to associate particular models and parameter values to each component type
. We will see that later in the documentation about the models.
The id
is used as a unique identifier for the components. It helps us find e.g. a particular leaf in the whole plant.
As said previously, the geometry of the plant is the shape, size, orientation and spatial location of the components. It is defined by linking several information together: the reference shape declared earlier (see shapeBDD paragraph), and a transformation matrix given as an attribute to each component of the topology
.
The transformation matrix is declared in a geometry
node. This node also declares the shapeIndex
to point to the reference shape to use, and two nodes called dUp
and dDwn
used for mesh tapering (e.g. make a cone from a cylinder). The geometry
of the first leaf from our example .opf
file is as follows:
[...]
<branch class="Leaf" scale="2" id="6"> # -> declare a component (type: Leaf), branched to the previous component
<Length>10.0</Length> # -> Add optional attribute Length
<Width>6.0</Width> # -> Add optional attribute Width
<geometry class="Mesh"> # -> Add a geometry node
<shapeIndex>1</shapeIndex> # -> The shape 1 will be used as reference
<mat> # -> The shape 1 will be transformed using this matrix
8.6602545 0.0 -0.5 -1.7484555E-7
0.0 1.0 0.0 0.0
5.0 0.0 0.86602545 4.0
</mat>
<dUp>6.0</dUp> # -> Add tapering to the upper part of the shape
<dDwn>6.0</dDwn> # -> Add tapering to the lower part of the shape
</geometry>
</branch>
[...]
The transformation matrix is an homogeneous matrix reduced to a 4*3
matrix because the 4th row of the matrix is always the same: [0 0 0 1]. Please read this wonderful resource for a nice introduction to homogeneous coordinates, translation, scaling and rotation. This method allows to reduce the data needed to describe the geometry in the .opf
for fast IO and low disk space usage.
The geometry
is not mandatory for all components. Some components are simply not rendered, such as the Scene
for example. In this case a geometry
can be optionally declared, but no shapeIndex
is given.
See this online course on the greenlab website and the AMAPStudio documentation for more information.