// Path: src/main/java/com/simsilica/sim/SimTime.java
// public class SimTime {
//     private long frame;
//     private long lastRealTime;
//     private boolean rebase;
//     
//     /**
//      *  gameTime is the 0-based 'game time' that is adjusted based on
//      *  the advancement of 'real time' provided by update().
//      */
//     private long gameTime;
//     
//     private double tpf;
//     private double invTimeScale = 1000000000.0; // nanos  
//     private double timeScale = 1.0 / invTimeScale;
//     
//     public SimTime() {
//     }
// 
//     public void update( long realTime ) {
//         long timeDelta;
//         if( frame == 0 || rebase ) {
//             timeDelta = 0;
//             rebase = false;
//         } else {
//             timeDelta = realTime - lastRealTime;
//         }
//         lastRealTime = realTime;
//         
//         frame++;
//         tpf = timeDelta * timeScale;
//         this.gameTime += timeDelta;
//     }
//  
//     /**
//      *  Returns the SimTime version of the specified timestamp that
//      *  is compatible with what would normally be provided to update().
//      *  SimTime.getTime() will provide 'frame locked' timestamps that will
//      *  not update during a frame which is what the systems will want 99.99%
//      *  of the time.  Rarely it is desirable to get a "real" timestamp that 
//      *  is not locked to frames, usually as part of providing matching accurate
//      *  timesource information to something else that is trying to synch.
//      */   
//     public long getUnlockedTime( long realTime ) {
//         // Figure out what time would be if realTime were
//         // passed to update.
//         long timeDelta;
//         if( frame == 0 || rebase ) {
//             timeDelta = 0;
//         } else {
//             timeDelta = realTime - lastRealTime;
//         }
//         return gameTime + timeDelta;
//     }
//  
//     public long toSimTime( double seconds ) {
//         return (long)(seconds * invTimeScale);
//     }
//  
//     public long getFutureTime( double seconds ) {
//         return gameTime + toSimTime(seconds); 
//     }        
//     
//     public final double getTpf() {
//         return tpf;
//     }
//     
//     public final long getFrame() {
//         return frame;
//     }
//     
//     public final long getTime() {
//         return gameTime;
//     }
//     
//     /**
//      *  Force the game time to a certain value.  Note that going backwards while
//      *  systems are running may have undesirable effects.  
//      *  The next immediate call to getTime() will return the specified value.
//      *  If update() is called then getTime() will return this value plus whatever
//      *  time has passed since the last time update() was called.
//      *  This is primarily used as a way for games to preset the game time to
//      *  some known value, say when restoring a saved game or a persistent world.
//      */
//     public final void setCurrentTime( long time ) {
//         this.gameTime = time;
//         this.rebase = true;
//     }
//     
//     public final double getTimeInSeconds() {
//         return gameTime * timeScale;
//     }
//     
//     public final double getTimeScale() {
//         return timeScale;
//     }
//  
//     public final long addMillis( long ms ) {
//         return gameTime + ms * 1000000L;
//     }
//  
//     @Override   
//     public String toString() {
//         return getClass().getSimpleName() + "[tpf=" + getTpf() + ", seconds=" + getTimeInSeconds() + "]";
//     }
// }


import java.util.Random;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.objects.PhysicsRigidBody; 
import com.jme3.math.*;
import com.simsilica.es.*;
import com.simsilica.mathd.*;
import com.simsilica.sim.SimTime;
import com.simsilica.bullet.*;

//System.out.println("++++ contact:" + otherBody);        
// Just until we can tweak our ghost size more appropriately        
canJump = true;        
            PhysicsRigidBody rb = (PhysicsRigidBody)otherBody;
            rb.getLinearVelocity(vTemp);
 
            groundVelocity.addLocal(vTemp);
            groundContactCount++;
            
//System.out.println("Standing on:" + otherBody);            
            // Whatever the most recent ground entity is
            groundEntity = otherBody.getId();
        }        
    }
 
    protected void calculateCollisionData() {
        if( groundContactCount > 0 ) {
            // Average the various ground velocities
            groundVelocity.multLocal(1f/groundContactCount);
        }
    }
    
    protected void invalidateCollisionData() {
        groundEntity = null;
        groundContactCount = 0;
        groundVelocity.set(0, 0, 0);
        canJump = false;
    }      
    
    @Override

// Path: src/main/java/com/simsilica/sim/SimTime.java
// public class SimTime {
//     private long frame;
//     private long lastRealTime;
//     private boolean rebase;
//     
//     /**
//      *  gameTime is the 0-based 'game time' that is adjusted based on
//      *  the advancement of 'real time' provided by update().
//      */
//     private long gameTime;
//     
//     private double tpf;
//     private double invTimeScale = 1000000000.0; // nanos  
//     private double timeScale = 1.0 / invTimeScale;
//     
//     public SimTime() {
//     }
// 
//     public void update( long realTime ) {
//         long timeDelta;
//         if( frame == 0 || rebase ) {
//             timeDelta = 0;
//             rebase = false;
//         } else {
//             timeDelta = realTime - lastRealTime;
//         }
//         lastRealTime = realTime;
//         
//         frame++;
//         tpf = timeDelta * timeScale;
//         this.gameTime += timeDelta;
//     }
//  
//     /**
//      *  Returns the SimTime version of the specified timestamp that
//      *  is compatible with what would normally be provided to update().
//      *  SimTime.getTime() will provide 'frame locked' timestamps that will
//      *  not update during a frame which is what the systems will want 99.99%
//      *  of the time.  Rarely it is desirable to get a "real" timestamp that 
//      *  is not locked to frames, usually as part of providing matching accurate
//      *  timesource information to something else that is trying to synch.
//      */   
//     public long getUnlockedTime( long realTime ) {
//         // Figure out what time would be if realTime were
//         // passed to update.
//         long timeDelta;
//         if( frame == 0 || rebase ) {
//             timeDelta = 0;
//         } else {
//             timeDelta = realTime - lastRealTime;
//         }
//         return gameTime + timeDelta;
//     }
//  
//     public long toSimTime( double seconds ) {
//         return (long)(seconds * invTimeScale);
//     }
//  
//     public long getFutureTime( double seconds ) {
//         return gameTime + toSimTime(seconds); 
//     }        
//     
//     public final double getTpf() {
//         return tpf;
//     }
//     
//     public final long getFrame() {
//         return frame;
//     }
//     
//     public final long getTime() {
//         return gameTime;
//     }
//     
//     /**
//      *  Force the game time to a certain value.  Note that going backwards while
//      *  systems are running may have undesirable effects.  
//      *  The next immediate call to getTime() will return the specified value.
//      *  If update() is called then getTime() will return this value plus whatever
//      *  time has passed since the last time update() was called.
//      *  This is primarily used as a way for games to preset the game time to
//      *  some known value, say when restoring a saved game or a persistent world.
//      */
//     public final void setCurrentTime( long time ) {
//         this.gameTime = time;
//         this.rebase = true;
//     }
//     
//     public final double getTimeInSeconds() {
//         return gameTime * timeScale;
//     }
//     
//     public final double getTimeScale() {
//         return timeScale;
//     }
//  
//     public final long addMillis( long ms ) {
//         return gameTime + ms * 1000000L;
//     }
//  
//     @Override   
//     public String toString() {
//         return getClass().getSimpleName() + "[tpf=" + getTpf() + ", seconds=" + getTimeInSeconds() + "]";
//     }
// }


// Path: demos/bullet-char/src/main/java/com/simsilica/demo/bullet/CharInputDriver.java
import java.util.Random;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.objects.PhysicsRigidBody; 
import com.jme3.math.*;
import com.simsilica.es.*;
import com.simsilica.mathd.*;
import com.simsilica.sim.SimTime;
import com.simsilica.bullet.*;

//System.out.println("++++ contact:" + otherBody);        
// Just until we can tweak our ghost size more appropriately        
canJump = true;        
            PhysicsRigidBody rb = (PhysicsRigidBody)otherBody;
            rb.getLinearVelocity(vTemp);
 
            groundVelocity.addLocal(vTemp);
            groundContactCount++;
            
//System.out.println("Standing on:" + otherBody);            
            // Whatever the most recent ground entity is
            groundEntity = otherBody.getId();
        }        
    }
 
    protected void calculateCollisionData() {
        if( groundContactCount > 0 ) {
            // Average the various ground velocities
            groundVelocity.multLocal(1f/groundContactCount);
        }
    }
    
    protected void invalidateCollisionData() {
        groundEntity = null;
        groundContactCount = 0;
        groundVelocity.set(0, 0, 0);
        canJump = false;
    }      
    
    @Override

    public void update( SimTime time, EntityRigidBody body ) {
