game.FasT.java Source code

Java tutorial

Introduction

Here is the source code for game.FasT.java

Source

/*
 *  FasT -- A Fast algorithm for simulaTions. 
 * 
 *  Copyright  2016 Tourdetour
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *  
 *  Contact me by email : <TPEFasT@mail.com>
 */

package game;

import game.entities.Ball;
import game.entities.Box;
import game.entities.Entity;
import game.entities.EntityHandler;
import game.entities.Wall;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Label;
import java.awt.Toolkit;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.UUID;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;

import log.Logger;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

import com.sun.glass.ui.Cursor;

import physics.BB;
import physics.Physics;
import physics.maths.Angle;
import physics.maths.C;
import physics.maths.Normal;
import physics.maths.Point;
import physics.maths.Vector;
import render.Render;

public class FasT {
    ///CONFIG

    //Force du rebond + raction du support
    private boolean pos = true;
    public boolean debug = false;

    private volatile boolean shouldExit = false;

    /*Engines (volatile to prevent being corrupted by threads)*/
    private volatile Logger log = new Logger();
    private volatile Render render = new Render();
    private volatile EntityHandler entityHandler = new EntityHandler();
    private volatile Physics physics = new Physics();
    private static volatile FasT theFasT;

    public Logger getLogger() {
        return this.log;
    }

    public EntityHandler getEntityHandler() {
        return this.entityHandler;
    }

    public Physics getPhysicsHandler() {
        return this.physics;
    }

    public Render getRender() {
        return this.render;
    }

    public static FasT getFasT() {
        return theFasT;
    }

    //TIME
    private volatile boolean on = false;
    private volatile boolean pause = true;
    final private double periodinseconds = Math.pow(10, -3);
    final private double freq = 1 / (periodinseconds * 10);
    private double period = Math.pow(10, 8) / freq;
    final private int updatesBeforeRender = 5;
    final private double targetFPS = 60;
    //Higher is this, less CPU the program will consume but slower it will get (default is 100)
    final private int maxSleep = 20;
    final private double targetRenderPeriod = Math.pow(10, 8) / targetFPS;
    private volatile int frameCount = 0;
    private volatile int fps = 60;

    private volatile double lastUpdateTime = -1;

    public void setPaused(boolean b) {
        this.pause = b;
        this.render.play.setText(b ? "play" : "pause");

        if (this.isPaused()) {

            //TODO : Tweaking is here !

            /*double tmpy = 0;
            for(Point p : entityHandler.get(this.theBall).positions)
            {
               if(p.getY()>tmpy)
                  tmpy=p.getY();
            }*/
            //double r = tmpy-entityHandler.get(this.theBall).positions.get(0).getY();
            //this.getLogger().debug("H = "+r);
        }

    }

    public boolean isPaused() {
        return this.pause;
    }

    //DISPLAY
    private final int width = 870;//850;//1000;
    private final int height = 660;//550;//650;
    private final String title = "FasT";

    private Point ballInit;

    public UUID theBall;
    public UUID theBox = UUID.randomUUID();

    public FasT() throws LWJGLException {
        this.on = true;
        //this.pause=false;
        FasT.theFasT = this;
        //Vector v = new Vector(-1,0,true);
        //C c = new C(-1,1);
        log.horodate("We'llcome too FasT !");
        log.info("[Starting]");

        //log.debug(c.toString());
        //log.debug(c.toStringAng(true));
        //this.getLogger().debug(v.complex.ToPolar().toString());
        //throw new LWJGLException();
    }

    private void init() throws LWJGLException {
        render.init(this.width, this.height, this.title);
        //   this.theBall = entityHandler.spawn(new Ball(new Point(1,3),this.getEntityHandler()));

        //DEFAULT
        this.theBall = entityHandler.spawn(new Ball(new Point(0, 1), this.getEntityHandler()));

        //TWEAK
        //this.theBall = entityHandler.spawn(new Ball(new Point(0,50),this.getEntityHandler(),0.010));

        //VELOCITYentityHandler.get(theBall).setVelocity(new C(0.5,0));

        //BALL2 entityHandler.spawn(new Ball(new Point(1,0),this.getEntityHandler()));

        //entityHandler.spawn(new Ball(new Point(40,500)));
        //entityHandler.spawn(new Wall(new Point(0,20),new Point(this.width,90)));
        ballInit = entityHandler.get(this.theBall).getPosition();

        //INIT = 
        entityHandler.spawn(new Wall(new Point(-2, -0.5), 4, new Angle(0), this.getEntityHandler()));
        entityHandler.spawn(new Box(new Point(-4, -3), new Point(4, -1), 1, Liquid.WATER(), this.entityHandler));

        this.spawnWalls();

        render.updateLabels();
    }

    private void spawnWalls() {
        Box l = (Box) FasT.getFasT().getEntityHandler().get(FasT.getFasT().theBox);
        entityHandler.destroy(this.theBox);
        //   this.theBox=entityHandler.spawn(new Box(new Point(-this.width/2,-this.height/2).toReal(), new Point(this.width/2,this.height/2).toReal(),4,this.entityHandler));

        this.theBox = entityHandler.spawn(new Box(new Point(0, 0).mouseToReal(),
                new Point(Display.getWidth(), Display.getHeight()).mouseToReal(), 4,
                l == null ? Liquid.AIR() : l.getLiquid(), this.entityHandler), true);

        this.getRender().game.remove(this.getRender().liquid);

        this.getRender().liquid = new JMenu("Liquid");

        Box b = (Box) FasT.getFasT().getEntityHandler().get(FasT.getFasT().theBox);
        b.getPopupMenuFrom(this.getRender().liquid);

        JCheckBoxMenuItem apply = new JCheckBoxMenuItem("Apply Everywhere");
        apply.setState(FasT.getFasT().getPhysicsHandler().applyEverywhere);
        apply.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                FasT.getFasT().getPhysicsHandler().applyEverywhere = apply.getState();
            }
        });
        this.getRender().liquid.add(apply);

        this.getRender().game.add(this.getRender().liquid);
        // this.getRender().popupMenu.updateUI();

        /*entityHandler.clear("wall");
        entityHandler.spawn(new Wall(new Point(0,0).toReal(),Normal.toReal(this.width),new Angle(0),this.entityHandler));
        entityHandler.spawn(new Wall(new Point(1,0).toReal(),Normal.toReal(this.height),new Angle(Math.PI/2),this.entityHandler));
        entityHandler.spawn(new Wall(new Point(this.width,this.height-1).toReal(),Normal.toReal(this.width),new Angle(Math.PI),this.entityHandler));
        entityHandler.spawn(new Wall(new Point(this.width,this.height).toReal(),Normal.toReal(this.height),new Angle(Math.PI*3/2),this.entityHandler));*/
    }

    public void run() {
        Thread loop = new Thread() {
            public void run() {
                try {
                    init();
                } catch (LWJGLException e) {
                    log.error(e.toString());
                }
                gameLoop();
            }
        };
        loop.start();
    }

    private void gameLoop() {
        double lastUpdateTime = System.nanoTime();
        double lastRenderTime = System.nanoTime();

        int lastSecondTime = (int) (lastUpdateTime / 1000000000);

        while (this.on) {
            //if(Display.isCloseRequested())
            //   exit();

            double now = System.nanoTime();
            int updateCount = 0;

            while (now - lastUpdateTime > this.period && updateCount < this.updatesBeforeRender) {
                this.update(Math.pow(10, -9) * (now - lastUpdateTime));
                lastUpdateTime += this.period;
                updateCount++;

            }

            if (now - lastUpdateTime > this.period) {
                lastUpdateTime = now - this.period;
            }

            // float interpolation = Math.min(1.0f, (float) ((now - lastUpdateTime) / this.period) );
            //   double beforeRender = System.nanoTime();
            this.render();

            if (Display.wasResized())
                this.spawnWalls();
            lastRenderTime = now;
            //log.info("TIME TO RENDER : "+((System.nanoTime()-beforeRender)/1000000)+"ms");
            //Update the frames we got.
            int thisSecond = (int) (lastUpdateTime / 1000000000);
            if (thisSecond > lastSecondTime) {
                //log.info("NEW SECOND " + thisSecond + " " + frameCount);
                //this.TIME = this.TIME+1;

                fps = frameCount;
                frameCount = 0;
                lastSecondTime = thisSecond;
                //   log.info("X distance = "+(entityHandler.get(this.theBall).getPosition().getX()-this.ballInit.getX())+"|Y distance = "+(entityHandler.get(this.theBall).getPosition().getY()-this.ballInit.getY()));
                if (this.entityHandler.get(this.theBall) != null) {
                    this.ballInit = entityHandler.get(this.theBall).getPosition();
                }
            }
            //Yield until it has been at least the target time between renders. This saves the CPU from hogging.
            while (now - lastRenderTime < this.targetRenderPeriod && now - lastUpdateTime < this.period) {
                Thread.yield();
                //-XX:PerfDataSamplingInterval=500
                //This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
                //You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
                //FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this.
                try {
                    Thread.sleep(this.maxSleep);
                } catch (Exception e) {
                }

                now = System.nanoTime();
            }
        }
        exit();

    }

    public double debugtimeinseconds = 0.2;

    private void handleKeyboard() {
        while (Keyboard.next()) {

            if (!Keyboard.getEventKeyState())
                continue;

            log.info("Key '" + Keyboard.getEventCharacter() + "' pressed ! ");

            if (Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) {
                exit();
            }
            if (Keyboard.getEventKey() == Keyboard.KEY_SPACE) {
                this.setPaused(!this.isPaused());
                if (!this.pause)
                    TIME = System.nanoTime();
            }
            if (Keyboard.getEventKey() == Keyboard.KEY_RIGHT) {
                this.setPaused(true);
                this.updateEntities(debugtimeinseconds);
            } else if (Keyboard.getEventKey() == Keyboard.KEY_LEFT) {
                this.setPaused(true);
                this.updateEntities(-debugtimeinseconds);
            }
            if (Keyboard.getEventKey() == 13) {
                Normal.unzoom(24);
                this.spawnWalls();
            }
            if (Keyboard.getEventKey() == 53) {
                Normal.zoom(24);
                this.spawnWalls();
            }

            if (!this.log.shallLog)
                return;

            if (Keyboard.getEventKey() == Keyboard.KEY_T) {
                entityHandler.spawn(new Ball(new Point(10, 500).toPlan(), this.getEntityHandler()));
                this.period = Math.pow(10, 8) / 0.1;
            }
            if (Keyboard.getEventKey() == Keyboard.KEY_1) {
                entityHandler.spawn(new Ball(new Point(Mouse.getX() - Normal.x, Mouse.getY() - Normal.y).toReal(),
                        0.3, this.getEntityHandler()));
            }
            if (Keyboard.getEventKey() == Keyboard.KEY_2) {
                entityHandler.spawn(new Ball(new Point(Mouse.getX() - Normal.x, Mouse.getY() - Normal.y).toReal(),
                        0.70, this.getEntityHandler()));
            }
            if (Keyboard.getEventKey() == Keyboard.KEY_3) {
                entityHandler.spawn(new Ball(new Point(Mouse.getX() - Normal.x, Mouse.getY() - Normal.y).toReal(),
                        1.5, this.getEntityHandler()));
            }
            if (Keyboard.getEventKey() == Keyboard.KEY_L) {
                //Coup spcial : les 10 camion qui te tombe sur la tte : 19620 Newtons dans ta geulle 
                entityHandler.get(this.theBall).applyForce(new C(new Angle(Angle.convertToRad(90)), 0.1));
            }
            if (Keyboard.getEventKey() == Keyboard.KEY_P) {
                //log.info("Width : " + render.getFrame().getWidth() + " & Height = " + render.getFrame().getHeight());
                try {
                    Display.setDisplayMode(
                            new DisplayMode(Display.getParent().getWidth(), Display.getParent().getHeight() - 1));
                } catch (LWJGLException e) {

                    e.printStackTrace();
                }

                //   Display.setLocation(Display.getParent().getX(), Display.getParent().getY());
                //render.getFrame().setSize(new Dimension(render.getFrame().getWidth(), render.getFrame().getHeight()+1));
                //      render.getFrame().pack();
            }
            if (Keyboard.getEventKey() == Keyboard.KEY_D) {
                this.entityHandler.destroy(
                        this.entityHandler.getEntityUnder(new Point(Mouse.getEventX(), Mouse.getEventY())));
            }

            if (Keyboard.getEventKey() == Keyboard.KEY_Z) {
                GL11.glOrtho(0, 5, 0, 5, -1.0, 1.0);
            }
            if (Keyboard.getEventKey() == Keyboard.KEY_Y) {
                GL11.glOrtho(5, 0, 5, 0, -1.0, 1.0);
            }
            if (Keyboard.getEventKey() == Keyboard.KEY_F) {
                this.getRender();
                Render.requestToggleFullScreen(this.getRender().getFrame());
            }
        }
    }

    private void handleMouse() {
        boolean c = true;
        for (Entity entitye : this.getEntityHandler().getEntities()) {
            if (entitye.getBeingDragged())
                c = false;

        }
        Entity entityd;
        if (c && !((entityd = this.getEntityHandler()
                .getEntityUnder(new Point(Mouse.getX(), Mouse.getY()).mouseToReal())) != null
                && (entityd.hoover(new Point(Mouse.getX(), Mouse.getY()).mouseToReal())))) {
            //FasT.getFasT().getRender().getFrame().setCursor(java.awt.Cursor.getDefaultCursor());
            this.getRender().getCanvas().setCursor(java.awt.Cursor.getDefaultCursor());
        }

        //TODO : Mouse
        while (Mouse.next()) {
            /*for(Entity entityc : this.getEntityHandler().getEntities())
            {
               if(BB.distanceBetweenTwoPoints(new Point(Mouse.getX(),Mouse.getY()).mouseToReal().toPlan(),entityc.getPosition().toPlan())<=5)
               {
                  FasT.getFasT().getRender().getFrame().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.SW_RESIZE_CURSOR));
               }
            }*/

            //log.warning("Wheel speed="+Mouse.getEventDWheel());
            //Let's handle the mouse wheel (to change the zoom)
            if (pos == true) {
                if (Mouse.getEventDWheel() > 1) {
                    Normal.unzoom(1);
                    this.spawnWalls();
                    pos = true;
                } else {
                    pos = false;
                }
            } else {
                if (Mouse.getEventDWheel() < -1) {
                    Normal.zoom(1);
                    this.spawnWalls();
                    pos = false;
                } else {
                    pos = true;
                }
            }

            if (Mouse.getEventButtonState() && Mouse.getEventButton() == 1) {
                Point p = new Point(Mouse.getEventX(), this.getRender().glCanvas.getHeight() - Mouse.getEventY());
                Point pr = new Point(Mouse.getEventX(), Mouse.getEventY());
                Entity entity;
                if ((entity = FasT.getFasT().getEntityHandler()
                        .getEntityUnder(new Point(pr.getX(), pr.getY()).mouseToReal())) != null
                        && entity.shouldMenu(new Point(pr.getX(), pr.getY()).mouseToReal())) {
                    entity.getPopupMenu().show();
                    entity.getPopupMenu().show(this.getRender().glCanvas, (int) p.getX(), (int) p.getY());
                } else {
                    render.popupMenu.show();
                    render.popupMenu.show(this.getRender().glCanvas, (int) p.getX(), (int) p.getY());
                }
                continue;
            } else if (Mouse.getEventButtonState()) {
                render.popupMenu.hide();
                for (Entity entity : FasT.getFasT().getEntityHandler().getEntities()) {
                    entity.getPopupMenu().hide();
                }
            }

            if (Mouse.isButtonDown(0) && Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)
                    && !Keyboard.isKeyDown(Keyboard.KEY_RMENU)) {
                Normal.x += Mouse.getEventDX();
                Normal.rx = Normal.toReal(Normal.x);
                Normal.y += Mouse.getEventDY();
                Normal.ry = Normal.toReal(Normal.y);
                this.spawnWalls();
                continue;
            }

            //Handeling home made drag events
            if (!Mouse.isButtonDown(0) || Keyboard.isKeyDown(Keyboard.KEY_RMENU)) {
                for (Entity entity : this.entityHandler.getEntities()) {
                    if (Mouse.getEventDX() == Mouse.getEventDX() && Mouse.getEventDY() == Mouse.getEventDY()) {
                        entity.setBeingDragged(false,
                                new Point(Mouse.getEventDX(), Mouse.getEventDY()).mouseToReal());
                    }
                }
            }

            boolean r = false;
            if (Mouse.getEventButtonState() && Mouse.getEventButton() == 0) {
                Entity e;
                if ((e = entityHandler
                        .getEntityUnder(new Point(Mouse.getEventX(), Mouse.getEventY()).mouseToReal())) != null) {
                    if (Keyboard.isKeyDown(Keyboard.KEY_RMENU)) {
                        if (!Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) {
                            for (Entity l : this.entityHandler.getEntities()) {
                                if (l != e)
                                    l.setSelected(false);
                            }
                        }
                        e.setSelected(!e.isSelected());
                        r = true;
                    } else {
                        if (Mouse.getEventDX() == Mouse.getEventDX() && Mouse.getEventDY() == Mouse.getEventDY())
                            e.setBeingDragged(true, new Point(Mouse.getEventX(), Mouse.getEventY()).mouseToReal());
                    }
                } else if (!Keyboard.isKeyDown(Keyboard.KEY_RSHIFT) && Keyboard.isKeyDown(Keyboard.KEY_RMENU)) {
                    for (Entity l : this.entityHandler.getEntities()) {
                        l.setSelected(false);
                    }
                }
            }

            for (Entity b : this.entityHandler.getEntities()) {
                if (b.getBeingDragged()) {
                    r = b.drag(new Point(Mouse.getEventDX(), Mouse.getEventDY()).toReal(),
                            Mouse.getEventNanoseconds());
                    break;
                }
            }

            //   if(r)
            //      continue;

            /*Entity entityd;
            if(!((entityd = this.getEntityHandler().getEntityUnder(new Point(Mouse.getX(),Mouse.getY()).mouseToReal())) != null && entityd.hoover(new Point(Mouse.getX(),Mouse.getY()).mouseToReal())))
               this.getRender().getCanvas().setCursor(java.awt.Cursor.getDefaultCursor());*/

            //Handle moving panel when draging with shift enabled

            /*if(Mouse.getEventButton()==1 && Mouse.getEventButtonState())
            {
               /* Entity entity;
                  if((entity=FasT.getFasT().getEntityHandler().getEntityUnder(new Point(e.getX(),e.getY())))!=null)
                  {
              FasT.getFasT().getLogger().warning("THING UNDER MOUSE DETECTED");
                  }
                  else
                  {
                this.render.popupMenu.show(Display.getParent(),Mouse.getEventX(), this.height-Mouse.getEventY()-10);
                  }
            }*/
        }
    }

    //Main method : Update the positions of the entities and handle the mouse / keyboard

    long TIME = 0;

    private void update(double deltat) {
        if (this.shouldExit)
            exit();
        this.handleKeyboard();
        this.handleMouse();

        if (!this.isPaused()) {
            double currentTime = System.nanoTime();
            double deltatime = currentTime - this.lastUpdateTime;
            double deltatimeinseconds = deltatime * Math.pow(10, -9);
            //log.debug("Hello, " + deltatimeinseconds);
            if (this.lastUpdateTime == -1)
                deltatimeinseconds = 0;

            this.updateEntities(deltatimeinseconds);

            //TODO : TWEAKHERE
            /*long t = System.nanoTime()-TIME;
            double time = (t*Math.pow(10, -9));
                
            if(time>=2 && time <=2.1)
            {
               FasT.getFasT().getLogger().debug("REPONSE QUAND T = 2 ="+this.getEntityHandler().get(this.theBall).getVelocity().getRho());
            }
            //FasT.getFasT().getLogger().debug(time);
            Ball ball = (Ball) this.getEntityHandler().get(this.theBall);
            if(ball.getPosition().getY()-ball.getRadius()<=0)
            {
               this.setPaused(true);
               FasT.getFasT().getLogger().debug("REPONSE AU SOL (T = "+time+")="+this.getEntityHandler().get(this.theBall).getVelocity().getRho());
            }*/

        }
        this.lastUpdateTime = System.nanoTime();

    }

    private void updateEntities(double deltatimeinseconds) {
        this.getLogger().resetV();
        for (Entity e : this.entityHandler.getEntities()) {
            e.update(physics, deltatimeinseconds, this.entityHandler.getEntities());
        }

        this.getPhysicsHandler().updateCollisions(this.entityHandler.getEntities());

    }

    //Render entities and everything
    public void render() {
        boolean b = true;
        for (Entity entity : this.getEntityHandler().getEntities()) {
            if (entity.isSelected()) {
                if (!this.getRender().BPanel.isVisible()) {
                    this.getRender().BPanel.setVisible(true);
                    this.getRender().panel.updateUI();
                    this.getRender().setFlag();
                }
                b = false;
            }
        }
        if (b && this.getRender().BPanel.isVisible()) {
            this.getRender().BPanel.setVisible(false);
            this.getRender().panel.updateUI();
            this.getRender().setFlag();
        }

        render.StartRender();

        //   render.drawSquare(new physics.maths.Point(20,20), new physics.maths.Point(200,200)); //Just a little square test

        for (Entity e : this.entityHandler.getEntities()) {
            e.render(render);
        }

        render.EndRender();

        frameCount++;
    }

    public void exit() {
        this.on = false;
        log.warning("Exiting !");
        Display.destroy();
        System.exit(0);
    }

    public void quit() {
        this.on = false;
    }

    public void threadExit() {
        this.shouldExit = true;
    }

}