Skip to content

Dynamic scenes

han16nah edited this page Mar 14, 2024 · 10 revisions

For a better understanding of what a scene is for HELIOS++, please refer to the Scene page of the wiki.

Dynamic scenes

A dynamic scene is a scene that experiences some type of update during the simulation. In other words, any scene for which at least one of its components mutates its state at least one time during simulation. For example, a scene whose geometry represents the road, the sidewalks and the buildings of a city would be a typical static scene. However, if a car moving on the road is added to the scene, then it is a dynamic scene. More specifically, it is a scene with dynamic moving objects. Dynamic moving objects are scene parts which experience at least one type of rigid motion during the simulation.

It is planned that HELIOS++ will support different types of dynamic behaviors in the future. For instance, scenes where there are background components which are the same during the simulation and foreground objects that require repeating the simulation with a different foreground geometry while keeping the background. However, at the moment, dynamic scenes based on dynamic moving objects are the only supported ones.

Dynamic moving objects

A dynamic moving object can be specified through the scene's XML file. This requires defining the sequence of rigid motions that the object is going to perform. Currently, HELIOS++ supports all possible rigid motions in 3D, which are illustrated in the following table.

Rigid motion XML type XML attributes Description
Translation translation vec, autoCRS Add a translation vector
Reflection reflection ortho Reflection with respect to a plane defined by its orthonormal vector
Glide plane glideplane ortho, shift Combination of reflection and translation
Rotation rotation axis, angle, center, autoCRS, selfMode Rotation around given rotation axis (in degrees) around a rotation center (if specified) or around the origin O(0, 0, 0)
Helical motion helical axis, angle, glide Combination of rotation and translation (in degrees)
Rotational symmetry rotsym axis, angle, center Combination of rotation and reflection (in degrees)

Those rigid motions that are defined with respect to an associated linear variety (for instance rotations, because they are associated to a rotation axis which is a 1D linear variety), also support a selfMode XML attribute which specifies that the rigid motion must be applied centered with respect to the object. For example, a rotation which is specified with selfMode="true" implies that the object will be rotated around itself (i.e., centered at the origin).

Any scene part that is meant to be a dynamic moving object must be defined using the following structure (where parameter values may be different and additional <motion> and <dmotion> elements may be present).

<part>
  ...
  <dmotion id="my_motion" loop="0">
    <motion type="rotation" axis="0;0;1" angle="1" selfMode="true"/>
  </dmotion>
</part>

The <dmotion> element defines a dynamic motion sequence that consists of the computation of all <motion> elements contained inside in the exact order that they are specified. The first <dmotion> element also defines the first dynamic motion in the sequence, in case multiple dynamic motions are defined for the same scene part. However, the order of the sequence after the first element must be explicitly specified by defining a next="another_motion" attribute so the dynamic motion <dmotion id="another_motion" ...> ... </dmotion> will be executed after the current one has finished.

Note that the above case also defines "my_motion" as the identifier for the dynamic motion sequence. Furthermore, it will be executed during the entire simulation because the loop="0" attribute means that the motion must be applied as an infinite loop. Specifying loop="n" for any n greater than 0 will lead to the dynamic motion being executed n times before finishing.

Motion definition

The different rigid motions that can be specified are detailed in this section, in the same order as they appear in the table above.

A translation can be defined as

<motion type="translation" vec="x;y;z"/>

The vector components specify how many meters for axis x, y and z will be added to the object position at each iteration. It is, at each loop iteration the new position of a point p can be defined as p' = p + (x, y, z).

For the sake of simplicity, translations can use the autoCRS attribute to automatically align the translation with respect to the internal coordinate reference system of the simulator, e.g.:

<motion type="translation" vec="x;y;z" autoCRS="1" />

A reflection can be defined as

<motion type="reflection" ortho="x;y;z"/>

The vector ortho is understood as the orthonormal vector defining the reflection plane. For example, if the orthonormal vector is (0, 0, 1), then it is the e3 vector of the canonical basis for the 3D space (z axis). Thus, the reflection plane would have e1 and e2 (x and y axis) as its basis.

A glide plane can be defined as

<motion type="glideplane" ortho="u;v;w" shift="x;y;z"/>

The ortho vector is understood as the orthonormal vector defining the reflection plane. The shift vector specifies how many meters for axis x, y and z will be added to the object position at each iteration.

A rotation can be defined as:

<motion type="rotation" axis="x;y;z" angle="a" />

The axis vector specifies the rotation axis. The angle scalar specifies how many degrees of rotation must be applied at each iteration.

Optionally, a rotation center can be specified to rotate around a known point:

<motion type="rotation" axis="x;y;z" angle="a" center="x;y;z"/>

Moreover, the autoCRS attribute can be used to automatically translate the rotation center to the internal reference system of the simulation:

<motion type="rotation" axis="x;y;z" angle="a" center="x;y;z" autoCRS="1" />

To position the rotation center to the object center, use the selfMode argument:

<motion type="rotation" axis="x;y;z" angle="a" selfMode="true" />

A helical motion can be defined as:

<motion type="helical" axis="x;y;z" angle="a" glide="k"/>

The axis vector specifies the rotation axis which is the translation direction too. The angle scalar specifies how many degrees of rotation must be applied at each iteration. The glide scalar defines the magnitude of the translation so that after the rotation the p' = p + k (x,y,z) translation is applied to complete the helical motion.

A rotational symmetry can be defined as:

<motion type="rotsym" axis="x;y;z" angle="a" center="p;q;r" />

The axis vector specifies the rotation axis which is also the orthogonal vector of the reflection plane. The angle scalar specifies how many degrees of rotation must be applied at each iteration. The center point specifies the center of the rotational symmetry, it is the point where the rotation axis and the reflection plane intersect.

Sequence definition

It was stated before that each rigid motion can be defined as a set of <motion ... /> elements contained inside a <dmotion ...> ... </dmotion>. Let us now look at how to define two different rigid movements.

The first rigid motion is a sequence of 4 motions describing a planetary like motion.

<dmotion id ="planetary_motion" loop="0">
  <motion type="rotation" axis="0;0;1" angle="2" selfMode="true" />
  <motion type="translation" vec="-120;-100;0" />
  <motion type="rotation" axis="0;0;1" angle="1" />
  <motion type="translation" vec="120;100;0" />
</dmotion>

The first rotation is configured with selfMode="true" to represent the rotation of a planet around the z axis centered in the object. It is followed by a translation, then a rotation and finally another translation that reverses the first. This last set of three motions is used to represent the translation of a planet which rotates around a given point. Thus, this dynamic motion defines a behavior similar to the one described by planet Earth, which rotates around itself at the same time that it moves around the sun. The entire sequence is computed inside an infinite loop, so it will never end.

The second rigid motion consists of two chained sequences of rigid motions. It represents a particle describing an ascendant helical motion followed by its corresponding descendant helical motion.

<dmotion id="sphere_helical_up" loop="300" next="sphere_helical_down">
  <motion type="translation" vec="-129;-120;0" />
  <motion type="helical" axis="0;0;1" angle="4" glide="0.3"/>
  <motion type="translation" vec="129;120;0" />
</dmotion>
<dmotion id="sphere_helical_down" loop="300" next="sphere_helical_up">
  <motion type="translation" vec="-129;-120;0" />
  <motion type="helical" axis="0;0;1" angle="-4" glide="-0.3"/>
  <motion type="translation" vec="129;120;0" />
</dmotion>

The "sphere_helical_down" sequence is repeated 300 times before proceeding to the "sphere_helical_up" motion. Once the second has been applied 300 times, the first one is executed again. The aforementioned process is repeated until the simulation ends. Each motion starts with a translation and ends with another translation which reverts the first one. Thus, the translations can be seen as a way to centering the helical motion around an arbitrary point. The first helical motion is the ascending one, while the second helical motion is identical to the first but in the opposite direction.

Dynamic motion frequency

By default, all rigid motions operate at the same frequency than the simulation. The operating frequency F for the simulation is defined by the pulse frequency of the scanner. This leads to rigid motions being computed F times per virtual second (seconds in simulated time, not real time seconds). However, it might be interesting because of performance reasons to control the frequency at which dynamic motions are computed.

It is up to the user to understand how much precision a simulation needs to be realistic enough. But it is reasonable to assume that computing a rigid motion 100000 times per virtual second just because this is the pulse frequency is an overkill. To better understand this, typical FPS values for cameras easily range from 25 FPS for typical videos to 960 FPS for super slow motion videos. This means that using a pulse frequency of 100000 implies simulating at a rate that is 4000 and 100 times that of typical and super slow motion videos, respectively.

For those cases where the simulation frequency is considerable greater than needed for an accurate output, it is possible to specify two different relative frequencies to improve the performance of the simulation. Both of them can be specified as an attribute of a <part ...> ... </part> element in the scenes's XML file.

The first attribute is the dynStep="x" which means that the dynamic motion will be applied each x simulation steps. Thus, if F=100000 and dynStep="1000" it means that the dynamic motion will be applied F/dynStep = 100 times per second. Note that this might require to update values such as the magnitude of a translation because adding 100 times a translation vector (0,0,1) leads to an accumulated translation vector (0,0,100) while adding 100000 times the same translation vector leads to an accumulated translation vector (0,0,100000).

The second attribute is the kdtDynStep="y" which means that the KDGrove (a datastructure which handles multiple KDTrees for efficient ray casting on dynamic scenes) will update the KDTree for the dynamic object after y consecutive updates of the object itself. For instance, having kdtDynStep="10" means that the KDTree will be updated after computing 10 iterations of the dynamic motion sequence.

Both dynStep and kdtDynStep can be combined in the most convenient way. To clearly understand the difference between them, the dynStep defines how many simulation steps are needed before updating the dynamic object while the kdtDynStep defines how many updates of the dynamic object are necessary before updating the KDTree. Thus, in cases where a very realistic motion is required but it is not necessary to sample it at the same rate than scanning pulses, it is recommended to keep a lower dynStep and have a higher kdtDynStep.

Moreover, the dynStep="z" attribute can also be specified at scene level instead of as an attribute for a scene part. When specified at scene level, it changes the default frequency of the dynamic scene from 1 to z. Having dynStep="z" at the scene element and dynStep="x" at the scene part element means that the dynamic object will be updated each xz iterations. Also, having dynStep="z" at the scene element with default dynStep="1" for all dynamic objects works similar than having dynStep="z" defined for each dynamic object while having default dynStep="1" for the scene.

An example of how to configure the different frequencies for scene is presented below:

<scene id="dyn_cube_scene" name="DynCubeScene" dynStep="20">

  <!--  Ground plane as a static object -->
  <part>
    <filter type="objloader">
      <param type="string" key="filepath" value="data/sceneparts/basic/groundplane/groundplane.obj" />
    </filter>
    <filter type="scale">
      <param type="double" key="scale" value="120" />
    </filter>
    <filter type="translate">
      <param type="vec3" key="offset" value="50.0;0;0" />
    </filter>
  </part>

  <!--  Cube as a dynamic object -->
  <part dynStep="5" kdtDynStep="10">
    <filter type="objloader">
      <param type="string" key="filepath" value="data/sceneparts/toyblocks/cube.obj" />
    </filter>
    <filter type="rotate">
      <param key="rotation" type="rotation">
        <rot angle_deg="45" axis="z"/>
      </param>
    </filter>
    <filter type="scale">
      <param type="double" key="scale" value="0.75" />
    </filter>
    <filter type="translate">
      <param type="vec3" key="offset" value="-40.0;-5.0;0" />
    </filter>
    <!-- The dynamic motion sequence for the cube -->
    <dmotion id="cube_translation" loop="0">
      <motion type="translation" vec="0.001;-0.003;0"/>
    </dmotion>
  </part>

</scene>

In the above example, the frequency for the dynamic scene is set to 20 while the frequency for the dynamic object is setted to 5, which means that the dynamic object is updated each 20 x 5 = 100 simulation steps. The frequency for KDTree updates is 10, which means the KDTree is updated after 10 dynamic object updates. In this case, the dynamic object has an infinite sequence that repeats the same translation. Thus, it is clear that the KDTree will be updated each 1000 simulation steps. Assuming a pulse frequency of 100000, the dynamic moving object will apply the translation vector 1000 times per virtual second while the KDTree is updated 100 times per virtual second.

Dynamic time step

The dynamic time step exploits the inverse relationship between time and frequency to provide an alternative specification. More concretely, the time step (inside $(0, 1]$) between iterations can be given instead of the discrete dynamic step. When using the time-based specification, the dynTimeStep and kdtDynTimeStep attributes must be used instead of their dynStep and kdtDynStep counterparts.

Note also that the dynTimeStep and kdtDynTimeStep are given as direct time measurements. On the contrary, for the discrete steps, the dynStep of the object is relative to the dynStep of the scene, and the kdtDynStep of the KDT is relative to the dynStep of the object. Thus, when using the time-based specification, the dynTimeStep of the scene must be greater than or equal to the dynTimeStep of each scene part (object), and the kdtDynTimeStep should be greater than or equal to the dynTimeStep of the associated object. The reason is that they represent nested components. Intuitively, updating a KDT representing an object 100 times per second does not make sense when the object is only updated five times per second. Furthermore, values greater than one are not expected. The reason is that the dynamic time steps are the inverse of a frequency that represents the number of iterations per second in the main simulation loop.

The dynamic time steps can be straightforward to interpret. A dynamic scene with dynTimeStep="0.0002" will compute its dynamic simulation logic with a time step of 0.2 ms, a dynamic object with dynTimeStep="0.001" will run its logic with a time step of 1 ms, and a dynamic KDT with dynTimeStep="0.01" will be updated each 10 ms. When not given, the dynamic time step of the KDT is automatically equal to the dynamic time step of its associated object. Similarly, when the dynamic time step of an object is not given, it is automatically equal to the dynamic time step of the scene. Dynamic steps (discrete) and dynamic time steps (continuous) cannot be mixed in the same specification.

The XML below yields the same simulation as the previous example but uses a time-based specification instead.

<scene id="dyn_cube_scene" name="DynCubeScene" dynTimeStep="0.0002">

  <!--  Ground plane as a static object -->
  <part>
    <filter type="objloader">
      <param type="string" key="filepath" value="data/sceneparts/basic/groundplane/groundplane.obj" />
    </filter>
    <filter type="scale">
      <param type="double" key="scale" value="120" />
    </filter>
    <filter type="translate">
      <param type="vec3" key="offset" value="50.0;0;0" />
    </filter>
  </part>

  <!--  Cube as a dynamic object -->
  <part dynTimeStep="0.001" kdtDynTimeStep="0.01">
    <filter type="objloader">
      <param type="string" key="filepath" value="data/sceneparts/toyblocks/cube.obj" />
    </filter>
    <filter type="rotate">
      <param key="rotation" type="rotation">
        <rot angle_deg="45" axis="z"/>
      </param>
    </filter>
    <filter type="scale">
      <param type="double" key="scale" value="0.75" />
    </filter>
    <filter type="translate">
      <param type="vec3" key="offset" value="-40.0;-5.0;0" />
    </filter>
    <!-- The dynamic motion sequence for the cube -->
    <dmotion id="cube_translation" loop="0">
      <motion type="translation" vec="0.001;-0.003;0"/>
    </dmotion>
  </part>

</scene>

Examples

Multiple examples for different rigid motions can be found in the example scene for moving toyblocks