import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.event.*;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.image.TextureLoader;
import com.sun.j3d.utils.image.TextureLoader;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.util.Vector;
import java.lang.Math;
import java.lang.Thread;
interface Tickable {
    public void tick( double dtime );
}
class Sprite extends Shape3D{
    public Sprite( Texture tex ) {
        Appearance app = new Appearance();
        app.setTexture( tex );
        TextureAttributes texAttr = new TextureAttributes();
        texAttr.setTextureMode( TextureAttributes.MODULATE );
        app.setTextureAttributes( texAttr );
        this.setAppearance( app );

        TriangleArray tri = new TriangleArray(6,TriangleArray.COORDINATES|TriangleArray.COLOR_3);
        tri.setCoordinate(0, new Point3f(	1.0f, 	-1.0f,	0.0f ) );
        tri.setCoordinate(1, new Point3f(	1.0f, 	1.0f,	0.0f ) );
        tri.setCoordinate(2, new Point3f(	-1.0f, 	1.0f,	0.0f ) );
        tri.setCoordinate(3, new Point3f(	-1.0f, 	1.0f,	0.0f ) );
        tri.setCoordinate(4, new Point3f(	-1.0f,         -1.0f,	0.0f ) );
        tri.setCoordinate(5, new Point3f(	1.0f, 	-1.0f,	0.0f ) );

        tri.setColor(0, new Color3f(1.0f, 0.0f, 0.0f));
        tri.setColor(1, new Color3f(0.0f, 1.0f, 0.0f));
        tri.setColor(2, new Color3f(0.0f, 0.0f, 1.0f));
        tri.setColor(3, new Color3f(0.0f, 0.0f, 1.0f));
        tri.setColor(4, new Color3f(0.0f, 1.0f, 0.0f));
        tri.setColor(5, new Color3f(1.0f, 0.0f, 0.0f));

        this.setGeometry(tri);
    }
}
class Particle implements Tickable { 
    double scale = .1;
    BranchGroup vis;
    TransformGroup tnode;
    public Vector3d p = new Vector3d();
    public Vector3d v = new Vector3d();
    public Vector3d a = new Vector3d();
    public Quat4d q = new Quat4d();

    public double rv() {
        return Math.random() * .1 - .05;
    }
    public Particle( World world, BranchGroup parent ){
        p.set( 	0.0,	0.0,	0.0 );
        double speed = Math.random() * .2;
        double psi = Math.random() * Math.PI * 2.0;
        double rho = Math.random() * Math.PI * 2.0;
        double speed_xz = speed * Math.cos( rho );
        v.y = speed * Math.sin( rho );
        v.x = speed_xz * Math.cos( psi );
        v.z = speed_xz * Math.sin( psi );

        vis = new BranchGroup();
        tnode = new TransformGroup();
        tnode.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        vis.addChild( tnode );
        tnode.addChild( new Sprite(world.particleTexture) /*ColorCube(0.01)*/ );
        parent.addChild( vis );
    }
    public void tick( double dtime ) {
        //p += v * dt
        Vector3d v1 = new Vector3d();
        v.get( v1 );
        v1.scale( dtime );
        p.add( v1 );

        //v += f / m * dt
        Vector3d a1 = new Vector3d();
        a.get( a1 );
        a1.scale( dtime );
        v.add( a1 );
        
        a.set( -p.x + p.z / 3.0, -p.y, -p.z );
        a.scale( .1 );

        Transform3D t = new Transform3D( q, p, scale );
        tnode.setTransform( t );
        //System.out.println( "t : " + t.toString() );
    }
    public String toString(){
        return p.toString() + "\t" + v.toString();
    }
}
class Triggerer extends Thread {
    double dt = .01;
    World world;
    public Triggerer( World world ) {
        this.world = world;
    }
    public void run() {
        while(true) {
            for( int i = 0; i < world.tickables.size(); i ++ ){
	Particle p = (Particle) world.tickables.get(i);
	p.tick( dt );
            }
            try {
	sleep( (long) (dt * 1000.0) );
            } catch ( InterruptedException e ){}
        }
    }
}
class World {
    public BranchGroup scene;
    public Vector tickables = new Vector();
    public Texture particleTexture;
    public World( BranchGroup scene ) {
        this.scene = scene;
        for( int i = 0; i < numberOfParticles; i ++ ){
            tickables.add( new Particle( this ,scene ) );
        }
    }
    int numberOfParticles = 100;
}
/**
  IDEAS
  create a bunch of particles
  let them bounce around
 **/
public class Particles extends Applet {

    Vector particles = new Vector();
    private SimpleUniverse u = null;

    public BranchGroup createSceneGraph() {
        //create root
        BranchGroup objRoot = new BranchGroup();
        return objRoot;
    }

    public Particles() {
    }

    /******************************************************************************************************
      loads everything
     ******************************************************************************************************/
    public void init() {
        //set our layout
        setLayout(new BorderLayout());
        //pull the simple universe config
        GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
        //create the canvas to paint on
        Canvas3D c = new Canvas3D(config);
        //add it
        add("Center", c);
        // Create a simple scene and attach it to the virtual universe
        // Create the scenegraph
        BranchGroup scene = createSceneGraph();
        //create the simple universe
        u = new SimpleUniverse(c);
        // This will move the ViewPlatform back a bit so the
        // objects in the scene can be viewed.
        u.getViewingPlatform().setNominalViewingTransform();
        //add as the root branch
        mainLoop( scene );
        u.addBranchGraph(scene);
    }
    public void destroy() {
        u.cleanup();
    }
    public static void main(String[] args) {
        new MainFrame(new Particles(), 512, 512);
    }
    void mainLoop( BranchGroup scene ){
        World world 	= new World( scene );
        world.particleTexture = TextureLoader( "tex.gif", this ).getTexture();
        Triggerer triggerer 	= new Triggerer( world );
        triggerer.start();
    }
}
