Newer
Older
EPQ-3D-renderer / src / main / java / uk / org / floop / epq3d / Screen.java
package uk.org.floop.epq3d;

import uk.ac.manchester.tornado.api.annotations.Parallel;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

public class Screen extends JPanel implements ActionListener, KeyListener, MouseListener, MouseMotionListener {
    // __working variables__

    public boolean initialised;
    public long lastTime;
        // mouse
    private boolean captured = false;
    private final Point2D mouseRel = new Point2D(0,0);
    private final Cursor invisibleCursor;

    // uber class for storing all draw data which needs to be passed to functions
    public final drawData drawData;

    // mainCollection stores all the background information in the scene
    public ObjectCollection mainCollection;
    // stores the skybox
    // I should probably make this work at some point
    public Object3d skyBox;

    // suppress serialization warning (future me says ??)
    private static final long serialVersionUID = 490905409104883233L;

    // keep a reference to the frameTimer object that triggers actionPerformed() in
    // case we need access to it in another method
    private final Timer frameTimer;
    private final Player player;

    public Screen(ObjectCollection _mainCollection) {
        mainCollection = _mainCollection;

        // initialise drawData
        drawData = new drawData("config.json");

        // set the game board size
        setPreferredSize(new Dimension(drawData.scrX, drawData.scrY));
        // set the game board background color
        setBackground(new Color(232, 232, 232));
        // hide the mouse cursor (https://stackoverflow.com/questions/191592/how-do-i-get-rid-of-the-mouse-cursor-in-full-screen-exclusive-mode)
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Point hotSpot = new Point(0,0);
        BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TRANSLUCENT);
        invisibleCursor = toolkit.createCustomCursor(cursorImage, hotSpot, "InvisibleCursor");


        // initialize the game state
        player = new Player(drawData);
        lastTime = System.nanoTime();
        // this frameTimer will call the actionPerformed() method every DELAY ms
        frameTimer = new Timer(drawData.delay, this);
        frameTimer.start();
        initialised = true;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // get mouse rel
        drawData.mouseRel = get_mouse_rel();
        // run the tick of the player before the board is redrawn
        player.tick(drawData);
        // draw screen
        repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        // draw graphics.
        drawScreen(g);
        // player.draw(g, this);
        Toolkit.getDefaultToolkit().sync();
    }
    private void drawScreen(Graphics g)  {
        drawData.lastFrameTime = (System.nanoTime()- lastTime)/1000000d;
        lastTime = System.nanoTime();
        mainCollection.invalidate(true);

        ArrayList<Integer> FrustumInfo = new ArrayList<>();
        for(int i = 0; i<6; i += 1){FrustumInfo.add(i);}
        mainCollection.draw(drawData, FrustumInfo);
        // DEBUG DRAWING
        {
            //debugImg.getGraphics().drawString(Math.round(1000 / (float) (System.currentTimeMillis() - lastTime)) + " fps", 10, 10);
            drawData.debugImg.getGraphics().drawString("FrameTime: " + (drawData.lastFrameTime) + " millis", 10, 10);
            if(drawData.debugHud) {
                drawData.debugImg.getGraphics().drawString("fpPos: " +
                        String.format("%.2f", player.FPWorldPos.x) + " " +
                        String.format("%.2f", player.FPWorldPos.y) + " " +
                        String.format("%.2f", player.FPWorldPos.z) + " ", 100, 30);
                g.drawImage(drawData.debugImg, 0, 0, this);
                drawData.debugImg.getGraphics().drawString("playerPos: " +
                        String.format("%.2f", player.getPos().x) + " " +
                        String.format("%.2f", player.getPos().y) + " " +
                        String.format("%.2f", player.getPos().z) + " ", 100, 50);
                drawData.debugImg.getGraphics().drawString("rotation: " +
                        Math.round(Math.toDegrees(player.getRot().x)) + " " +
                        Math.round(Math.toDegrees(player.getRot().y)) + " " +
                        Math.round(Math.toDegrees(player.getRot().z)) + " ", 300, 50);
                drawData.debugImg.getGraphics().drawString("viewVec: " +
                        String.format("%.2f", player.viewVector.x) + " " +
                        String.format("%.2f", player.viewVector.y) + " " +
                        String.format("%.2f", player.viewVector.z) + " ", 100, 70);
                for (int i = 0; i < 4; i += 1) {
                    drawData.debugImg.getGraphics().drawString("camPoint " + i + ": " +
                            String.format("%.2f", player.ScreenCornerPosS[i].getRotatedPoint().x) + " " +
                            String.format("%.2f", player.ScreenCornerPosS[i].getRotatedPoint().y) + " " +
                            String.format("%.2f", player.ScreenCornerPosS[i].getRotatedPoint().z), 10, 90 + 20 * i);
                }
                for (int i = 0; i < 6; i += 1) {
                    drawData.debugImg.getGraphics().drawString("plane " + i + ": " +
                            String.format("%.2f", drawData.frustumPlanes[i].mainPoint.x) + " " +
                            String.format("%.2f", drawData.frustumPlanes[i].mainPoint.y) + " " +
                            String.format("%.2f", drawData.frustumPlanes[i].mainPoint.z) + ", normal: " +
                            String.format("%.2f", drawData.frustumPlanes[i].normalVector.x) + " " +
                            String.format("%.2f", drawData.frustumPlanes[i].normalVector.y) + " " +
                            String.format("%.2f", drawData.frustumPlanes[i].normalVector.z), 10, 170 + 20 * i);
                }
            }
            long drawTime = System.nanoTime();

            //DataBuffer db = drawData.bufImg.getRaster().getDataBuffer();
            for(@Parallel int x = 0; x < drawData.scrX; x +=1){
                //drawData.bufImg.setRGB(x, 0, 1, drawData.scrY, drawData.drawImg[x], 0, 1);
                for(@Parallel int y = 0; y < drawData.scrY; y += 1){
                    //db.setElem(y* drawData.scrX + x, drawData.drawImg[x][y]);
                    //drawData.bufImg.setRGB(x, y, drawData.drawImg[x][y]);
                    //drawData.drawImg[x][y] = 0;
                    drawData.zBuf[x][y] = 0;
                }
            }
            g.drawImage(drawData.bufImg, 0, 0, this);
            drawData.bufImg.getGraphics().fillRect(0,0,drawData.scrX, drawData.scrY);
            drawTime = System.nanoTime() - drawTime;
            long current = System.nanoTime();
            System.out.println("Frame: "
                    + "total:     " + (current - lastTime)/1000000d
                    + " drawing:  " + (drawTime)/1000000d
                    + " chopping: " + drawData.timeSpentChoppingFaces
                    + " tris    : " + drawData.timeSpentDrawingNormalFaces
                    + " pixels    : " + drawData.timeSpentGettingPixels/1000000d
            );
            System.out.print("TriBreakdown: ");
            for (int x = 0; x<10; x+=1) {
                System.out.print(drawData.trisTimeList[x]/1000000d + ", ");
                drawData.trisTimeList[x] = 0;
            }
            System.out.println();

        }
        drawData.timeSpentChoppingFaces = 0;
        drawData.timeSpentDrawingNormalFaces = 0;
        drawData.timeSpentDrawingTris = 0;
        drawData.timeSpentGettingPixels = 0;
    }

    @Override
    public void keyTyped(KeyEvent e) {}

    @Override
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();

        if (key == KeyEvent.VK_ESCAPE){
            captured = false;
            setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
        }
        // pass key-press to the player, so they can handle it as well
        player.keyPressed(key, drawData);
    }

    @Override
    public void keyReleased(KeyEvent e) {
        int key = e.getKeyCode();

        // pass key-release to the player, so they can handle it as well
        player.keyReleased(key);
    }

    // gets the relative position of the mouse since the last time this function was called
    public Point2D get_mouse_rel(){
        Point2D rel;
        // this doesn't work if the mouse isn't captured. If I need it to, I can
        // probably make it, but I don't know what the functionality looks like.
        if (captured) {
            rel = new Point2D(MouseInfo.getPointerInfo().getLocation().x - getLocationOnScreen().x - getSize().width / 2 + mouseRel.x,
                    MouseInfo.getPointerInfo().getLocation().y - getLocationOnScreen().y - getSize().height / 2 + mouseRel.y);
            mouseRel.set(0, 0);
            // set the position of the mouse back to (0,0)
            try {
                Robot robot = new Robot();
                robot.mouseMove(getLocationOnScreen().x + getSize().width / 2,
                        getLocationOnScreen().y + getSize().height / 2);
            } catch (AWTException ex) {
                ex.printStackTrace();
            }
        }
        else{
            rel = new Point2D(0,0);
        }
        return rel;
    }
    @Override
    public void mouseClicked(MouseEvent mouseEvent) {
        if (mouseEvent.getButton() == MouseEvent.BUTTON1){
            captured = true;
            get_mouse_rel();
            setCursor(invisibleCursor);
        }
    }

    @Override
    public void mousePressed(MouseEvent mouseEvent) {}

    @Override
    public void mouseReleased(MouseEvent mouseEvent) {}

    @Override
    public void mouseEntered(MouseEvent mouseEvent) {}

    @Override
    public void mouseExited(MouseEvent mouseEvent) {
        if (captured) {
            // add current mouse relative location to mouseRel so mouse movements are not lost
            mouseRel.x += mouseEvent.getX() - getSize().width / 2d;
            mouseRel.y += mouseEvent.getY() - getSize().height / 2d;

            // System.out.println(mouseEvent.getX() + " " + mouseEvent.getY());
            // set the position of the mouse back to (0,0)
            try {
                Robot robot = new Robot();
                robot.mouseMove(getLocationOnScreen().x + getSize().width / 2,
                                getLocationOnScreen().y + getSize().height / 2);
            } catch (AWTException ex) {
                ex.printStackTrace();
            }
        }
    }

    @Override
    public void mouseDragged(MouseEvent mouseEvent) {}

    @Override
    public void mouseMoved(MouseEvent mouseEvent) {
    }
}