VMech.com
VirtualMechanisms.com
Fighting immechanacy on the web

Simulation Clocks on the Java 3D API
Oct 3, 2001
Fred Klingener

Abstract

According to its specification, Java 3D offers great potential as a host for hi-fi simulations of physical processes, but the implementations of the API make it hard to design a decent simulation clock to drive them. This page discusses some of the kluges and workarounds that sort-of-work-but-are-really-too-ugly-to-use-for-anything- important.

The real fix would be for Sun to implement a cross-platform implementation for Behaviors that wakeupOnElapsedTime() reliably.

Background

I've used these dark days to assemble some of my work on physical process simulations with the Java 3D API - What I've tried, what doesn't work, workarounds, and possible fixes. I'm hoping that someone will offer better suggestions, or, failing that, help generate enough interest to get Sun to look the underlying forms.

When I first saw the Java 3D spec, it seemed to have huge promise for hosting hi-fi simulations of physical processes cross-platform and over the web. As someone who had spent thirty or so years doing engineering and process simulations using procedural languages but who had recently become an Object Zealot (the worst kind), I thought the prospect of doing serious simulation on a scene graph tree in retained mode offered a sleek elegance.

The logical arrangement of the scene graph matched the one emerging in modern theoretical kinematics, and the power of the Behaviors seemed to be enough to do almost anything one needed - discrete event, continuous process, combined discrete/continuous processes all within the API, all on a live scene graph.

Simulations

Before I talk about the bad news, I have to back up a little and talk about simulations in general. So far, the physical processes I'm talking about are kinematics and dynamics, but the discussion can be easily extended to many other fields. There are two basic numerical tasks in doing kinematics and dynamics:

  1. solving sets of simultaneous nonlinear equations. The state of a model (the set of values you need to completely describe the positions of all the parts) can consist of independent variables and dependent variables. The latter group are 'dependent' on the independent variables and internal and external constraints. CG, CA, and robotics people call this 'inverse kinematics,' but, old-line mechanical engineers just call it 'kinematics.' If we want to do hi-fi simulations on anything but toy problems, we have to use some numerical technique that gets us arbitrarily close to the 'exact' solutions. For argument's sake because I'm talking about hi-fi, I'm talking about a brute-force solution by formulation of the Jacobian and solution of the set by Newton-Raphson. (If you need more performance, and you can't wait for CPUs or video cards to catch up, there are plenty of related speedy approximate methods.) The scene graph offers some powerful support here. For example, see Inverse Kinematics on the Java 3D Scene Graph
  2. integrating sets of ordinary differential equations (ODEs). Model dynamics (Newton's Second Law) are usually cast in terms of ordinary first-order differential equations. Because I'm talking about hi-fi simulations here, the preferred method is probably no more nor less than 4th order Runge-Kutta with adaptive time stepping.

What these two classes of problems share is the analyst's need to control the time step. In the first, the stability and speed of convergence depend on the quality of the first guess at which the iteration starts. In a typical animation, this first guess will be the model state from the immediately preceding frame. In the second, the stability and the efficiency depends strongly on the size of the time step over which integration proceeds. When things are happening quickly in the model, we want small steps; when things are quiet, we want to take giant leaps.

A side issue that probably belongs here is the relationship between the time step selected for the numerical solutions and the time step between rendering of each animation frame. On the one hand, it is by no means necessary that the heartbeats be the same. There are plenty of simulation architectures in which they're completely separated. In the compute-and-play-back systems, I think this is the norm. With Java 3D, though, we're preoccupied with 'real-time', interactive simulation environments in which the display is an integral part of the deal. Introducing the overhead of the interpolations and backing up required to support mismatches are things I want to avoid if I can. If we're talking about solving these state equations within the processStimulus() of a Behavior, then the only natural approach is for simulation time to advance in step with display time.

Approach

There are a lot of interesting problems to be worked out here, but I'm stuck on the first rung - finding a decent way to control the advance of time in Java 3D. There are two elements in the functional requirements for a simulation clock - 1.) advancing the simulation clock itself, and 2.) controlling the real-time-step between display frames, and the Java 3D API provides several ways to perform these functions, and it scatters them over several different types of objects.

To evaluate the suitability of any of these designs, I built test programs, extensions of the rotating cube of HelloJava3Dc.java, that implemented simulation clocks that performed their time keeping and model control functions in various ways. Even though I introduced the numerical methods above as a motivation for building controllable clocks, all of these example programs use simple interpolator classes, rewritten to use the new configurations. The applets systematically march the time step from 10 milliseconds up to the point at which the user wants to quit. For each setting of the time step DeltaT, the rendering frame delays are averaged over a block (for the examples here, the block is 250 frames), and the results are printed out to the browser Java Console. The results are plotted, average actual frame delay vs. specified time step, and the results compared. A successful clock would be one in which the average observed frame delay had some regular, linear relationship to the specified time step. On a plot of a perfect clock, the values would be equal, meaning that the simulation clock ran at the same speed as the wall clock over the range of time steps selected.

Clocks

In the bullets below, I'll describe six approaches to managing the two functional elements of simulation clocks with the Java 3D API. This is by no means an exhaustive list - just the ones I've thought of and have tested.

  • Plan A - wakeupOnElapsedTime(dt) The most promising of all in the Specification is the Behavior wakeup criterion wakeupOnElapsedTime(DeltaT). This criterion offers a single mechanism by which the model clock can be advanced in step with the Java 3D rendering cycle.
        public class SimClock extends Behavior
        {       long SimTime;
                  int DeltaT;
                  WakeupCriterion yawn;
 
                  public void initialize()
                  {        DeltaT = 25;    //      milliseconds
                              SimTime = 0;
                              yawn = new wakeupOnElapsedTime(DeltaT);
                              wakeupOn(yawn);
                              . . .
                  }
                  public void processStimulus(Enumeration e)
                  {        SimTime += DeltaT;
                              wakeupOn(yawn);
                              [solve state equations, set TGs, reset DeltaT if necessary]
                              . . .
                  }
                . . . 
 
 

Slick. One solution per frame. Higher frame rate when the model is changing rapidly. Lower frame rate when things are slow. All on the scene graph. All in retained mode. Guaranteed to run to completion before rendering begins. What more could you ask for? Well, implementation for one thing. Current implementations completely torpedo the idea in ways I'll get to in the Results section.

  • Plan B - wakeupOnElapsedFrames(0) + View.setMinimumFrameCycleTime(DeltaT)
    The Plan B clock seeks to use a different approach to controlling the display frame rate. By setting the Behavior's wakeup condition to wakeupOnElapsedFrames(0), we guarantee that its processStimulus() runs each frame and that the Behavior itself introduces no frame delay. The frame delay is specified in the View.setMinimumFrameCycleTime(DeltaT) method. There's no particular thing that suggests that this will work. It's just different.
  • Plan C - wakeupOnElapsedFrames(0) + Thread.sleep(DeltaT)
    Another opportunity offered by the API to control the frame delay is by intervening in the external rendering cycle in the preRender() method of an extended Canvas3D. Controlling frame delays in a canvas is purely pragmatic. It doesn't make object sense to put elements of the model clock onto the view side of the scene graph, because there might be many canvases displaying the same model at one time.
  • Plan D - Pure Immediate
    Plan D didn't constitute a proposal for a clock design. The example program was written just to explore how the frame rate was controlled in the extended Canvas3D.
  • Plan E - Clock built onto an extended Canvas3D
    Plan E put both functions (clock advance and frame delay control) on the extended Canvas3D.
  • Plan F
    Plan F was intended to give better control of the frame rate by a supposedly more sophisticated thread design.

Summary

The table summarizes the examples of system clocks. It provides links to example applets, links to example source code listings, and thumbnail descriptions of their general approach..

Example
Applet

Program Source

Type
(Note [1])

Mode

Sim clock advance

Frame delay

1

HelloJava3Dc.java

B'cast

Retained

uses system clock

none explicit - renderer freewheels

 

2

PlanAClock.java

TiVo

Retained

processStimulus(...){
SimClock += DeltaT;
...

wakeupOnElapsedTime(DeltaT)

 

3

PlanBClock.java

TiVo

Retained

processStimulus(...){
SimClock += DeltaT;
...

initialize() {
View.setMinimumFrameDelay(DeltaT)
...

 

4

PlanCClock.java

TiVo

Mixed

processStimulus(...){
SimClock += DeltaT;
...

preRender(){
Thread.sleep(DeltaT)
...

 

5

PlanD_Immed.java

B'cast

Immed.

preRender(){
SimClock += DeltaT;
...

preRender(){
Thread.sleep(DeltaT)
...

 

6

PlanE_Dali.java

TiVo

Mixed

preRender(){
SimClock += DeltaT;
...

preRender() {
Thread.sleep(DeltaT)
...

 

7

PlanF.java

TiVo

Mixed

preRender(){
SimClock += DeltaT;
...

preRender() {
// runner is a Thread that
// sleep(DeltaT) and exits
runner.join();
runner.start();
...

Note 1. The Type refers to the way that the simulation clock behaves during times when the system is off doing other things such as moving or resizing windows. The labeling uses an analogy to watching television. When you're watching normal broadcast television, the program runs whether or not you're in the john or getting a beer in the kitchen. With a TiVo system, you can freeze the program while you're away and pick it up where you left off.

Because they're based on the system clock, which runs no matter what the processor is doing, the Java 3D API Interpolators use 'broadcast' type clocks. After an interruption, the model jumps to the state corresponding to the current reading of the system clock. The most immediate visual clue to this behavior can be seen in Example 1. While the applet is running, the cube stops rotating while the user moves or resizes windows on the desktop, but it then jumps forward once the processor returns to rendering.

The TiVo-type clocks, on the other hand, advance only when the Java 3D renderer is running, and models driven by this type of clock hold their state during an interruption, and then proceed from that state when the renderer starts up again. The most immediate visual clue to this behavior can be seen in the examples after Example 1. While these applets are running, the cube stops rotating while the user moves or resizes windows on the desktop, but it then resumes motion from the its prior state once the processor returns to rendering.

Is one 'better' than another? Depends on what it is you want to do.

In general,

  • If the model is simple, and its state can be determined by an algebra, trigonometry, or a look-up table, given the simulation clock reading, then either the "broadcast" or the "TiVo" will work.
  • If the state of the model must be computed by solving sets of simultaneous nonlinear equations or by numerically integrating sets of ordinary differential equations (classes of solutions that typically depend for their stability and convergence on control of the size of the timestep from one solution to another), then the "TiVo" type of clock is obviously easier to implement because it needn't span the uncontrolled timestep across the interrupt.

Results

The following table summarizes typical results of the performance of the six Java 3D simulation clock test programs on two Windows OS platforms. The Summary section above provided links from which to run the six test applets (identified as Plan A through Plan F), program source listings, and thumbnail descriptions of the general approaches to the design of the clocks.

The table below shows a chart of the results for each test program. On each of the charts, the time step by which the simulation clock advances for each rendered frame is plotted along the X axis, and the average frame delay (as measured on the wall clock) is plotted on the Y axis.

On an ideal clock, the time intervals would have a regular, linear relationship, free of implementation artifacts. This regular relationship would give the analyst the freedom to select the time step size to serve the numerical solution of the simulation equations. Complex relationships, unpredictability, and worst of all, radical differences among OSs torpedo the fundamental appeals of Java and the Java 3D API.

  • Plan A - From the simulation analyst's point of view, Plan A (based on the wakeupOnElapsedTime(DeltaT) criterion) is the most desirable arrangement. It enables her to encapsulate the clock and the equation solution in a single object on the model side of the scene graph - a Behavior. This means that the criteria for setting the time step size and the methods for doing that set can occur in one object. The implementation artifacts, the coarse grain stepping and the cross-platform disparity, put it among the least desirable.
  • Plan B - This plan was motivated by a desire to find some OS hook other than the one used in the Plan A implementation to set the frame delay. Plan B uses the View.setMinimumFrameDelay(DeltaT) to set the frame delay, a purely pragmatic arrangement. It makes no sense from an object point of view - the members (time step sizes) and the methods to change it must be shared among two scene graph objects on different sides of the scene graph, one on the model side and the other on the view side.

    Because the arrangement is so awkward, it's a good thing that it works no better than Plan A.
  • Plan C - To set the frame delay, Plan C steps outside the scene graph entirely and uses a mixed mode extension of the Canvas3D to interject a sleep(DeltaT) into the preRender() method. This arrangement makes even less object sense than Plan B.

    It seems to work better than either of the scene graph based designs in spite of the fact that it leaves some odd spikes in the frame delay at 10, 20, 40, . . . milliseconds (but not 30), it's promising enough to pursue. Too bad.
  • Plan D - This wasn't intended to be an example of a proposed clock design. Plan D was written just to provide a bench mark for performance of the frame delay control of a Pure Immediate Mode sample problem.
  • Plan E - This was an extension of Plan C - moving the clock itself from the Behavior to the Canvas3D extension. Ugly, but its performance is the most regular cross platform design among the tested arrangements. It still exhibits the spikes at 10, 20, 40, . . . milliseconds, but these are isolated well enough than an analyst could tiptoe around them.
  • Plan F - This was a failed attempt to get better control over the frame delay by building a Runnable Canvas3D which spawns a one-shot timer thread and waits for a join before it starts processing each preRender() method. Its performance is even more ragged that Plan E, so it's possible that I don't know how to handle Java Threads.

Clock
Description

Windows 2000

Windows 98

HelloJava3D

NA

NA

Plan A

Plan B

 

Plan C

 

Plan D

 

Plan E

Plan F

 

Conclusions

  • The most desirable arrangement for a simulation clock, Plan A, doesn't work at all with the current implementations.
  • A kluge that stuffs the clock into a Canvas3D object, Plan E, seems to offer the most regular overall performance.

Acknowledgments:

Thanks to Jeff Morse for running the test applets on Windows 98.