Imagine you intend to drive a spaceship through a J3D scene, lets say with
constant speed. The camera is fixed at the ship. What would be the best way
to do that really smooth? Esp. on Win32?

I tried to create a Behavior that works on the
getViewingPlatform().getViewPlatformTransform(). The behavior wakes up every
frame and the code is very intuitive. I tried to measure time with
System.currentTimeMillis() or with an own clock, that averages a couple of
frames depending on timer resolution (50ms on Win32). This clock is
triggered within Canvas3D.preRender().
But the movement is far from being smooth!

Another way could be to synchronize the renderer with a update cycle. Could
that be better?

When I change a transformgroup within an behavior, when can I expect the
change to be valid? Next frame?

I append my FrameClock and TimerAccuracy classes for convenience. And my
TestBehavior to show how to use them.

Any thoughts on smooth animation??

- J


Joerg 'Herkules' Plewe
HARDCODE Development
http://www.hardcode.de

// TimerAccuracy.java
//**********************************************************************************************
//
//
//                              (C) Copyright 2000 by Dipl. Phys. Joerg Plewe, HARDCODE Development
//                                      All rights reserved. Copying, modification,
//                                      distribution or publication without the prior written 
//                                      consent of the author is prohibited.
//
//
//      Filename:               TimerAccuracy.java
//      Author:                 Herkules
//  Created:            20000119
//  Version:            $Version$
//
//      Module:                 $Module$
//  Dependecies:        $Dependencies$
//
//  History:
//  $Date$              $Who$           $What$
//
/*      Purpose:

$Describe purpose of module$
Measure the resolution of the System timer

*/
//**********************************************************************************************


package HARDCODE.Util;


/**
 * Measure the accuracy of the timer
 */


//
// This classes 'measure' method has to be run at highest thread priority!
//
public class TimerAccuracy
{
	
        final public long measure( long _timeToMeasure )
        {
                // Max./min. ticker difference
                maxRes = 0;
                minRes = 999999;
        	
                // Get current ms ticker
                long ms = System.currentTimeMillis();

                // Calculate time to stop
                long EndMs = ms + _timeToMeasure;
        	
                while( ms < EndMs )
                {	
                        long oldms = ms;

                        ms = System.currentTimeMillis();

                        long diff = ms - oldms;
                	
                        if( diff > maxRes )
                                maxRes = diff;
                	
                        if( diff > 0 && diff < minRes )
                                minRes = diff;
                }

                System.out.println( "Worst timer resolution: " + maxRes ); 
                System.out.println( "Best timer resolution: " + minRes ); 

                return minRes;
        }

	
        //-------------------------------------------------------------------------
        //-------------------------------------------------------------------------
        //-------------------------------------------------------------------------
        //
        // Variables
        //
        public long minRes;
        public long maxRes;
	
	
}
// FrameClock.java
//**********************************************************************************************
//
//
//                              (C) Copyright 2000 by Dipl. Phys. Joerg Plewe, HARDCODE Development
//                                      All rights reserved. Copying, modification,
//                                      distribution or publication without the prior written 
//                                      consent of the author is prohibited.
//
//
//      Filename:               FrameClock.java
//      Author:                 Herkules
//  Created:            20000119
//  Version:            $Version$
//
//      Module:                 $Module$
//  Dependecies:        $Dependencies$
//
//  History:
//  $Date$              $Who$           $What$
//
/*      Purpose:

$Describe purpose of module$
Provide an adjustable (->network) clock 

This class is a singleton. Every method is static
*/
//**********************************************************************************************

package HARDCODE.Util;


public class FrameClock
{

        //-------------------------------------------------------------------------Adjust
        // Herkules
        // Adjust the clock with an offset
        //
        public static void adjust( int _Adjustment )
        {
                // Shift the adjustment
                adjust -= _Adjustment;
                                        	
                // reset the timer
                reset();
        }


        //-------------------------------------------------------------------------maintainFrameTiming
        public static void maintainFrameTiming( )
        {       	
                int currentMillis = (int)System.currentTimeMillis() - adjust;
                int FrameTimeDiff = currentMillis - lastFrameMillis;
        	
                FrameTimer[ FrameTimerIndex ] = FrameTimeDiff;
                FrameTimerIndex++;
                FrameTimerIndex %= FrameTimerCount;
        	
                if( FrameTimeDiff > 0 )
                {
                        lastFrameMillis = currentMillis;
                        countNoTimerChange = 0;
                }
                else
                        countNoTimerChange++;


                // Remember an average FPS value at reasonables distances
                if( FrameTimerIndex == 0) {
                        updateAverages();
//                      System.out.println( getAverageFPS() );
                }
        }

	
	
        //-------------------------------------------------------------------------getTimeNextFrame
        public static int getTimeNextFrame() 
        {
                return lastFrameMillis + ( countNoTimerChange * averageFrameTime ); 	
        }
	

        //-------------------------------------------------------------------------getFPS
        public static float getFPS()
        {
                return averageFPS;
        }

	
        //-------------------------------------------------------------------------getAverageFrameTime
        public static float getAverageFrameTime()
        {
                return (float)averageFrameTime;
        }



        //-------------------------------------------------------------------------reset
        public static void reset()
        {
        	
                FrameTimerCount = (int)new TimerAccuracy().measure( 200 ) / 2;
        	
                if( FrameTimerCount < 5 )
                        FrameTimerCount = 5;
        	
                if( FrameTimerCount >= maxFrameTimerCount )
                        FrameTimerCount = maxFrameTimerCount - 1;
        	
                for( int i = 0; i < FrameTimerCount; ++i )
                {
                        FrameTimer[ i ] = averageFrameTime;
                }
                                                	
                lastFrameMillis = (int)System.currentTimeMillis() - adjust;

                updateAverages();
        	
        }
	
	
        //-------------------------------------------------------------------------updateAverages
        private static void updateAverages()
        {
                averageFrameTime = 0;
                for( int i = 0; i < FrameTimerCount; ++i )
                {
                        averageFrameTime += FrameTimer[ i ];
                }
                averageFrameTime /= FrameTimerCount;

                averageFPS = 1000.0f / (float)averageFrameTime;
        }

	
	
        //*********************************************************************************************
        // Variables
        //*********************************************************************************************

        public static int lastSnap = -1;

        private static int adjust = 0;

        private final static int maxFrameTimerCount = 100;
        private static int FrameTimerCount = maxFrameTimerCount;
        private static int FrameTimer[] = new int[ maxFrameTimerCount ];
        private static int lastFrameMillis;
        private static int FrameTimerIndex = 0;
        private static int averageFrameTime = 40;
        private static int countNoTimerChange = 0;
	
        private static float averageFPS = 25.0f;

}


/*
 * TestBehavior.java
 *
 * Created on 2. Juli 2000, 22:50
 */
 
package HARDCODE.TGAW.Lib3D;

import javax.media.j3d.*;
import javax.vecmath.*;
import HARDCODE.Util.FrameClock;

/** 
 *
 * @author  Herkules
 * @version 
 */
public class TestBehavior extends Behavior
{

        /** Creates new TestBehavior */
        public TestBehavior( TransformGroup tg ) 
        {
                m_TransformGroup = tg;
        }

        public void initialize()
        {	
                m_StartTime = FrameClock.getTimeNextFrame();
                wakeupOn( m_wakeup );
        	
        }
	
        public void processStimulus( java.util.Enumeration criteria )
        {	
//              System.out.println( "Wake up!" );
	
                m_TransformGroup.getTransform( m_T3D );

                angle += FrameClock.getAverageFrameTime() / 2000.0f;
//              int TimeNextFrame = FrameClock.getTimeNextFrame();
//              angle = ( TimeNextFrame - m_StartTime ) / 2000.0f ;
        	
                AxisAngle4f aa4f = new AxisAngle4f(new Vector3f(1.0f, 1.0f, 1.0f), angle );
                m_T3D.setRotation( aa4f );

                m_TransformGroup.setTransform( m_T3D );
                wakeupOn( m_wakeup );
        }

	
        private WakeupOnElapsedFrames m_wakeup = new WakeupOnElapsedFrames( 0 );
        private TransformGroup m_TransformGroup;
        private Transform3D m_T3D = new Transform3D();
        private float angle = 0.0f;
        private int m_StartTime;
}

Reply via email to