java left logo
java middle logo
java right logo
 

Home arrow Other API Tips arrow JOGL arrow Introduction to physical simulations - NeHe Tutorial JOGL Port
 
 
Main Menu
Home
Java Tutorials
Book Reviews
Java SE Tips
Java ME Tips
Java EE Tips
Other API Tips
Java Applications
Java Libraries
Java Games
Java Network
Java Forums
Java Blog




Most Visited Tips
Java SE Tips
Java ME Tips
Java EE Tips
Other API Tips
Java Applications
Java Libraries
Java Games
Book Reviews
Top Rated Tips
Java SE Tips
Java ME Tips
Java EE Tips
Other API Tips
Java Applications
Java Libraries
Java Games
Book Reviews


Statistics
Registered Users: 4100
Java SE Tips: 614
Java ME Tips: 202
Java EE Tips: 183
Other API Tips: 779
Java Applications: 298
Java Libraries: 209
Java Games: 16
Book Reviews:
 
 
 
Introduction to physical simulations - NeHe Tutorial JOGL Port E-mail
User Rating: / 3
PoorBest 

This example demonstrates motion under gravity, a mass with constant velocity, and a mass connected to a spring.

This is the Java port of the one of the NeHe OpenGL tutorials.

You can get complete IntelliJ IDEA project structure (all source, resources, build script, ) by downloading the source distribution from here.

The original post of the programmer who ported the examples can be found here.


Image

package demos.nehe.lesson39;

import demos.common.GLDisplay;

/**
 @author Pepijn Van Eeckhoudt
 */
public class Lesson39 {
    public static void main(String[] args) {
        GLDisplay neheGLDisplay = GLDisplay.createGLDisplay(
                "Lesson 39: Physical simulation engine");
        Renderer renderer = new Renderer();
        InputHandler inputHandler = new InputHandler(renderer, neheGLDisplay);
        neheGLDisplay.addGLEventListener(renderer);
        neheGLDisplay.addKeyListener(inputHandler);
        neheGLDisplay.start();
    }
}


package demos.nehe.lesson39;

import demos.common.GLDisplay;

import javax.swing.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

class InputHandler extends KeyAdapter {
    private Renderer renderer;

    public InputHandler(Renderer renderer, GLDisplay glDisplay) {
        this.renderer = renderer;
        glDisplay.registerKeyStrokeForHelp(
                KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0)"Normal speed");
        
        glDisplay.registerKeyStrokeForHelp(
                KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0)"Slow motion");
    }

    public void keyReleased(KeyEvent e) {
        switch (e.getKeyCode()) {
            case KeyEvent.VK_F2: // Is the F2 Being Pressed?
                // Set slowMotionRatio To 1.0f (Normal Motion)
                renderer.setSlowMotionRatio(1.0f);  
                break;

            case KeyEvent.VK_F3: // Is The F3 Being Pressed?
                // Set slowMotionRatio To 10.0f (Very Slow Motion)
                renderer.setSlowMotionRatio(10.0f);                       
                break;
        }
    }
}


package demos.nehe.lesson39;

/*
  class ConstantVelocity is derived from class Simulation
  It creates 1 mass with mass value 1 kg and sets its velocity to (1.0f, 0.0f, 0.0f)
  so that the mass moves in the x direction with 1 m/s velocity.
*/

class ConstantVelocity extends Simulation {
    // Constructor firstly constructs its super class with 1 mass and 1 kg
    public ConstantVelocity()   
    {
        Mass mass = new Mass(1.0f);
        // a mass was created and we set its position to the origin
        mass.pos = new Vector3D(0.0f0.0f0.0f);    
        // we set the mass's velocity to (1.0f, 0.0f, 0.0f)
        mass.vel = new Vector3D(1.0f0.0f0.0f);    
        addMass(mass);
    }

}


package demos.nehe.lesson39;

/**
 * Created by IntelliJ IDEA.
 * User: pepijnve
 * Date: Sep 21, 2003
 * Time: 1:06:04 PM
 * To change this template use Options | File Templates.
 */
class Mass {
    float m;            // The mass value
    Vector3D pos;  // Position in space
    Vector3D vel;  // Velocity
    Vector3D force;  // Force applied on this mass at an instance

    public Mass(float m// Constructor
    {
        this.m = m;
        pos = new Vector3D();
        vel = new Vector3D();
        force = new Vector3D();
    }

    /*
      void applyForce(Vector3D force) method is used to add external force to the mass.
      At an instance in time, several sources of force might affect the mass. 
      The vector sum of these forces make up the net force applied to the mass at 
      the instance.
    */
    public void applyForce(Vector3D force) {
        this.force.add(force);  // The external force is added to the force of the mass
    }

    /*
      void init() method sets the force values to zero
    */
    public void init() {
        force.x = 0;
        force.y = 0;
        force.z = 0;
    }

    /*
      void simulate(float dt) method calculates the new velocity and new position of
      the mass according to change in time (dt). Here, a simulation method called
      "The Euler Method" is used. The Euler Method is not always accurate, but it is
      simple. It is suitable for most of physical simulations that we know in common
      computer and video games.
    */
    public void simulate(float dt) {
        Vector3D forceScaled = new Vector3D(this.force);
        // Change in velocity is added to the velocity.
        vel.add(forceScaled.scale(dt / m));  
        // The change is proportinal with the acceleration (forceScaled / m) and 
        // change in time

        Vector3D velScaled = new Vector3D(this.vel);
        pos.add(velScaled.scale(dt));  // Change in position is added to the position.
        // Change in position is velocity times the change in time
    }

}


package demos.nehe.lesson39;

import java.util.Iterator;

/*
  class MassConnectedWithSpring is derived from class Simulation
  It creates 1 mass with mass value 1 kg and binds the mass to an arbitrary 
  constant point with a spring. This point is refered as the connectionPos and 
  the spring has a springConstant value to represent its stiffness.
*/

class MassConnectedWithSpring extends Simulation {
    float springConstant;  // more the springConstant, stiffer the spring force
    Vector3D connectionPos;  // the arbitrary constant point that the mass is connected

    // Constructor firstly constructs its super class with 1 mass and 1 kg
    public MassConnectedWithSpring(float springConstant)                    
    {
        this.springConstant = springConstant;  // set the springConstant

        connectionPos = new Vector3D(0.0f, -5.0f0.0f)// set the connectionPos

        Mass mass = new Mass(1.0f);
        // set the position of the mass 10 meters to the right side of the connectionPos
        mass.pos = new Vector3D(connectionPos).add(new Vector3D(10.0f0.0f0.0f));    
        // set the velocity of the mass to zero
        mass.vel = new Vector3D(0.0f0.0f0.0f);            
        addMass(mass);
    }

    public void solve()      // the spring force will be applied
    {
        // we will apply force to all masses (actually we have 1 mass, but 
  // we can extend it in the future)
        Iterator masses = getMasses()
        while (masses.hasNext()) {
            Mass mass = (Massmasses.next();
            // find a vector from the position of the mass to the connectionPos
            Vector3D springVector = new Vector3D(mass.pos).sub(connectionPos);      
            // apply the force according to the famous spring force formulation
            mass.applyForce(springVector.inverse().scale(springConstant));      
        }
    }
}


package demos.nehe.lesson39;

import java.util.Iterator;

/*
  class MotionUnderGravitation is derived from class Simulation
  It creates 1 mass with mass value 1 kg and sets its velocity to (10.0f, 15.0f, 0.0f) 
  and its position to (-10.0f, 0.0f, 0.0f). The purpose of this application is 
  to apply a gravitational force to the mass and observe the path it follows. 
  The above velocity and position provides a fine projectile path with a 9.81 m/s/s 
  downward gravitational acceleration. 9.81 m/s/s is a very close value to the 
  gravitational acceleration we experience on the earth.
*/

class MotionUnderGravitation extends Simulation {
    private Vector3D gravitation;  // the gravitational acceleration

    // Constructor firstly constructs its super class with 1 mass and 1 kg
    public MotionUnderGravitation(Vector3D gravitation)                     
    {  // Vector3D gravitation, is the gravitational acceleration
        this.gravitation = gravitation;  // set this class's gravitation
        Mass mass = new Mass(1.0f);
        // a mass was created and we set its position to the origin
        mass.pos = new Vector3D(-10.0f0.0f0.0f);    
        // we set the mass's velocity to (1.0f, 0.0f, 0.0f)
        mass.vel = new Vector3D(10.0f15.0f0.0f);    
        addMass(mass);
    }

    // gravitational force will be applied therefore we need a "solve" method.
    public void solve()  
    {
        // we will apply force to all masses (actually we have 1 mass, but we 
        // can extend it in the future)
        Iterator masses = getMasses();  
        while (masses.hasNext()) {
            Mass mass = (Massmasses.next();
            // gravitational force is as F = m * g. (mass times the 
            // gravitational acceleration)
            mass.applyForce(new Vector3D(gravitation).scale(mass.m));        
        }
    }

}

package demos.nehe.lesson39;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: pepijnve
 * Date: Sep 21, 2003
 * Time: 1:12:19 PM
 * To change this template use Options | File Templates.
 */
class Simulation {
    private List masses = new ArrayList();  // masses

    public Simulation()  // Constructor creates some masses with mass values m
    {
    }

    public void addMass(Mass mass) {
        masses.add(mass);
    }

    public void removeMass(Mass mass) {
        masses.remove(mass);
    }

    public Iterator getMasses() {
        return masses.iterator();  // get the mass at the index
    }

    public void init()  // this method will call the init() method of every mass
    {
        for (int i = 0; i < masses.size(); i++) {
            Mass mass = (Massmasses.get(i);       // We will init() every mass
            mass.init();                            // call init() method of the mass
        }
    }

    // no implementation because no forces are wanted in this basic container
    public void solve()  
    {
        // in advanced containers, this method will be overrided and some forces 
        // will act on masses
    }

    public void simulate(float dt// Iterate the masses by the change in time
    {
        for (int i = 0; i < masses.size(); i++) {   // We will iterate every mass
            Mass mass = (Massmasses.get(i);
            // Iterate the mass and obtain new position and new velocity
            mass.simulate(dt);  
        }
    }

    // The complete procedure of simulation
    public void operate(float dt)          
    {
        init();    // Step 1: reset forces to zero
        solve();  // Step 2: apply forces
        simulate(dt);  // Step 3: iterate the masses by the change in time
    }
}


package demos.nehe.lesson39;

class Vector3D {
    public float x;  // the x value of this Vector3D
    public float y;  // the y value of this Vector3D
    public float z;  // the z value of this Vector3D

    public Vector3D()  // Constructor to set x = y = z = 0
    {
        x = 0;
        y = 0;
        z = 0;
    }

    public Vector3D(Vector3D vector)  // Constructor to set x = y = z = 0
    {
        x = vector.x;
        y = vector.y;
        z = vector.z;
    }

    // Constructor that initializes this Vector3D to the intended values of x, y and z
    public Vector3D(float x, float y, float z)  
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    // operator+= is used to add another Vector3D to this Vector3D.
    public Vector3D add(Vector3D v)      
    {
        x += v.x;
        y += v.y;
        z += v.z;
        return this;
    }

    // operator-= is used to subtract another Vector3D from this Vector3D.
    public Vector3D sub(Vector3D v)      
    {
        x -= v.x;
        y -= v.y;
        z -= v.z;
        return this;
    }

    // operator*= is used to scale this Vector3D by a value.
    public Vector3D scale(float value)      
    {
        x *= value;
        y *= value;
        z *= value;
        return this;
    }

    // operator- is used to set this Vector3D's x, y, and z to the negative of them.
    public Vector3D inverse()            
    {
        x = -x;
        y = -y;
        z = -z;
        return this;
    }

    // length() returns the length of this Vector3D
    public float length()                
    {
        return (floatMath.sqrt(x * x + y * y + z * z);
    };

    // normalize() normalizes this Vector3D that its direction remains the 
    // same but its length is 1.
    public void normalize()                
    {
        float length = length();

        if (length == 0)
            return;

        x /= length;
        y /= length;
        z /= length;
    }

    public String toString() {
        return x + ", " + y + ", " + z;
    }
}


package demos.nehe.lesson39;

import com.sun.opengl.util.GLUT;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import java.text.NumberFormat;
import java.util.Iterator;

class Renderer implements GLEventListener {
    /**
     ConstantVelocity is an object from Physics1.h. It is a container for 
     simulating masses. Specifically, it creates a mass and sets its velocity 
     as (1, 0, 0) so that the mass moves with 1.0f meters / second in the x direction.
     */
    private ConstantVelocity constantVelocity = new ConstantVelocity();

    /**
     MotionUnderGravitation is an object from Physics1.h. It is a container for 
     simulating masses. This object applies gravitation to all masses it contains. 
     This gravitation is set by the constructor which is (0.0f, -9.81f, 0.0f) for now 
     (see below). This means a gravitational acceleration of 9.81 meter per 
     (second * second) in the negative y direction. MotionUnderGravitation 
     creates one mass by default and sets its position to (-10, 0, 0) and its 
     velocity to (10, 15, 0)
     */
    private MotionUnderGravitation motionUnderGravitation = 
            new MotionUnderGravitation(new Vector3D(0.0f, -9.81f0.0f));

    /**
     MassConnectedWithSpring is an object from Physics1.h. It is a container for 
     simulating masses. This object has a member called connectionPos, which is 
     the connection position of the spring it simulates. All masses in this container 
     are pulled towards the connectionPos by a spring with a constant of stiffness. 
     This constant is set by the constructor and for now it is 2.0
     (see below).
     */
    private MassConnectedWithSpring massConnectedWithSpring = 
            new MassConnectedWithSpring(2.0f);

    // slowMotionRatio Is A Value To Slow Down The Simulation, Relative To Real World Time
    private float slowMotionRatio = 10.0f;  
    // Elapsed Time In The Simulation (Not Equal To Real World's Time Unless 
    // slowMotionRatio Is 1
    private float timeElapsed = 0;                      
    private long previousTime = System.currentTimeMillis();

    private GLUT glut;
    private GLU glu = new GLU();
    private static final int FONT = GLUT.STROKE_MONO_ROMAN;
    private NumberFormat numberFormat;

    public Renderer() {
        numberFormat = NumberFormat.getNumberInstance();
        numberFormat.setMinimumFractionDigits(2);
        numberFormat.setMaximumFractionDigits(2);
    }

    public float getSlowMotionRatio() {
        return slowMotionRatio;
    }

    public void setSlowMotionRatio(float slowMotionRatio) {
        this.slowMotionRatio = slowMotionRatio;
    }

    // Custom GL "Print" Routine
    private void glPrint(GL gl, float x, float y, float z, int font, String string)  
    {
        gl.glPushMatrix();
        gl.glTranslatef(x, y, z);  // Position Text On The Screen
        gl.glScalef(0.005f0.005f0.005f);
        int width = glut.glutStrokeLength(font, string);
        gl.glTranslatef(-width, 00);    // Right align text with position
        for (int i = 0; i < string.length(); i++) {
            glut.glutStrokeCharacter(font, string.charAt(i));
        }
        gl.glPopMatrix();
    }

    public void init(GLAutoDrawable drawable) {
        GL gl = drawable.getGL();
        glut = new GLUT();

        gl.glClearColor(0.0f0.0f0.0f0.5f);  // Black Background
        gl.glShadeModel(GL.GL_SMOOTH);      // Select Smooth Shading
        // Set Perspective Calculations To Most Accurate
        gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);      
    }

    public void display(GLAutoDrawable drawable) {
        long currentTime = System.currentTimeMillis();
        update(currentTime - previousTime);
        previousTime = currentTime;

        GL gl = drawable.getGL();

        // Clear Screen And Depth Buffer
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);    

        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();    // Reset The Modelview Matrix

        // Position Camera 40 Meters Up In Z-Direction.
        // Set The Up Vector In Y-Direction So That +X Directs To Right And 
        // +Y Directs To Up On The Window.
        gl.glTranslatef(00, -40);

        // Drawing The Coordinate Plane Starts Here.
        // We Will Draw Horizontal And Vertical Lines With A Space Of 1 Meter 
        // Between Them.
        gl.glColor3f(001);    // Draw In Blue
        gl.glBegin(GL.GL_LINES);

        // Draw The Vertical Lines
        // x += 1.0f Stands For 1 Meter Of Space In This Example
        for (float x = -20; x <= 20; x += 1.0f)  
        {
            gl.glVertex3f(x, 200);
            gl.glVertex3f(x, -200);
        }

        // Draw The Horizontal Lines
        // y += 1.0f Stands For 1 Meter Of Space In This Example
        for (float y = -20; y <= 20; y += 1.0f)  
        {
            gl.glVertex3f(20, y, 0);
            gl.glVertex3f(-20, y, 0);
        }

        gl.glEnd();
        // Drawing The Coordinate Plane Ends Here.

        // Draw All Masses In constantVelocity Simulation (Actually There Is Only 
        // One Mass In This Example Of Code)
        gl.glColor3f(100);  // Draw In Red
        Iterator masses = constantVelocity.getMasses();
        while (masses.hasNext()) {
            Mass mass = (Massmasses.next();
            Vector3D pos = mass.pos;

            // Center Our Text On The Screen
            glPrint(gl, pos.x, pos.y + 1, pos.z, FONT, "Mass with constant vel");

            gl.glPointSize(4);
            gl.glBegin(GL.GL_POINTS);
            gl.glVertex3f(pos.x, pos.y, pos.z);
            gl.glEnd();
        }
        // Drawing Masses In constantVelocity Simulation Ends Here.

        // Draw All Masses In motionUnderGravitation Simulation (Actually 
        // There Is Only One Mass In This Example Of Code)
        gl.glColor3f(110);  // Draw In Yellow
        masses = motionUnderGravitation.getMasses();
        while (masses.hasNext()) {
            Mass mass = (Massmasses.next();
            Vector3D pos = mass.pos;

            // Center Our Text On The Screen
            glPrint(gl, pos.x, pos.y + 1, pos.z, FONT, "Motion under gravitation");

            gl.glPointSize(4);
            gl.glBegin(GL.GL_POINTS);
            gl.glVertex3f(pos.x, pos.y, pos.z);
            gl.glEnd();
        }
        // Drawing Masses In motionUnderGravitation Simulation Ends Here.

        // Draw All Masses In massConnectedWithSpring Simulation (Actually 
        // There Is Only One Mass In This Example Of Code)
        gl.glColor3f(010);        // Draw In Green
        masses = massConnectedWithSpring.getMasses();
        while (masses.hasNext()) {
            Mass mass = (Massmasses.next();
            Vector3D pos = mass.pos;

            // Center Our Text On The Screen
            glPrint(gl, pos.x, pos.y + 1, pos.z, FONT, "Mass connected with spring");

            gl.glPointSize(4);
            gl.glBegin(GL.GL_POINTS);
            gl.glVertex3f(pos.x, pos.y, pos.z);
            gl.glEnd();

            // Draw A Line From The Mass Position To Connection Position To 
            // Represent The Spring
            gl.glBegin(GL.GL_LINES);
            gl.glVertex3f(pos.x, pos.y, pos.z);
            pos = massConnectedWithSpring.connectionPos;
            gl.glVertex3f(pos.x, pos.y, pos.z);
            gl.glEnd();
        }
        // Drawing Masses In massConnectedWithSpring Simulation Ends Here.


        gl.glColor3f(111);    // Draw In White
        glPrint(gl, -5.0f140, FONT, "Time elapsed (seconds): " 
                numberFormat.format(timeElapsed));  // Print timeElapsed
        
        glPrint(gl, -5.0f130, FONT, "Slow motion ratio: " 
                numberFormat.format(slowMotionRatio));  // Print slowMotionRatio
        
        glPrint(gl, -5.0f120, FONT, "Press F2 for normal motion");
        glPrint(gl, -5.0f110, FONT, "Press F3 for slow motion");
    }

    private void update(long milliseconds)  // Perform Motion Updates Here
    {
        // dt Is The Time Interval (As Seconds) From The Previous Frame To The 
        // Current Frame. dt Will Be Used To Iterate Simulation Values Such As 
        // Velocity And Position Of Masses.

        float dt = milliseconds / 1000.0f;  // Let's Convert Milliseconds To Seconds

        dt /= slowMotionRatio;  // Divide dt By slowMotionRatio And Obtain The New dt

        timeElapsed += dt;  // Iterate Elapsed Time

        float maxPossible_dt = 0.1f;  // Say That The Maximum Possible dt Is 0.1 Seconds
        // This Is Needed So We Do Not Pass Over A Non Precise dt Value

        // Calculate Number Of Iterations To Be Made At This Update Depending 
        // On maxPossible_dt And dt
        int numOfIterations = (int) (dt / maxPossible_dt1;    
        if (numOfIterations != 0)  // Avoid Division By Zero
            dt = dt / numOfIterations;  // dt Should Be Updated According To numOfIterations

        // We Need To Iterate Simulations "numOfIterations" Times
        for (int a = 0; a < numOfIterations; ++a)          
        {
            // Iterate constantVelocity Simulation By dt Seconds
            constantVelocity.operate(dt);  
            // Iterate motionUnderGravitation Simulation By dt Seconds
            motionUnderGravitation.operate(dt);          
            // Iterate massConnectedWithSpring Simulation By dt Seconds
            massConnectedWithSpring.operate(dt);          
        }
    }

    public void reshape(GLAutoDrawable drawable,
                        int x,
                        int y,
                        int width,
                        int height) {
        GL gl = drawable.getGL();

        height = (height == 0: height;

        gl.glViewport(00, width, height);
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();

        glu.gluPerspective(45(floatwidth / height, 1100);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
    }

    public void displayChanged(GLAutoDrawable drawable,
                               boolean modeChanged,
                               boolean deviceChanged) {
    }
}

 Related Tips

 
< Prev   Next >

Page 1 of 0 ( 0 comments )

You can share your information about this topic using the form below!

Please do not post your questions with this form! Thanks.


Name (required)


E-Mail (required)

Your email will not be displayed on the site - only to our administrator
Homepage(optional)



Comment Enable HTML code : Yes No



 
       
         
     
 
 
 
   
 
 
java bottom left
java bottom middle
java bottom right
RSS 0.91 FeedRSS 1.0 FeedRSS 2.0 FeedATOM FeedOPML Feed

Home - About Us - Privacy Policy
Copyright 2005 - 2008 www.java-tips.org
Java is a trademark of Sun Microsystems, Inc.