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

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 = 0;
        // 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
    public Object3d skyBox;
        // testing\
//    private Line2d line = new Line2d(
//            new Point2D(200, 200),
//                new Point2D(1, 1),
//                true);
//    private Triangle Triangle = new Triangle(
//            new Point2D(200, 200), new Point2D(1, 1), new Point2D(400,200)
//    ,new boolean[]{false, false, false},
//            null,
//            new Matrix(3, 3));
//    private final Point3D[] points = new Point3D[]{
//            new Point3D(10,10,-1),
//        new Point3D(-10, 10, -1),
//        new Point3D(-10, -10, -1),
//        new Point3D(10, -10, -1)};
    // double ang = 0;
    // __config variables__
    // controls the delay between each tick in ms

    // suppress serialization warning
    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.currentTimeMillis();
        // 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);
        // when calling g.drawImage() we can use "this" for the ImageObserver 
        // because Component implements the ImageObserver interface, and JPanel 
        // extends from Component. So "this" Board instance, as a Component, can 
        // react to imageUpdate() events triggered by g.drawImage()

        // draw graphics.
        drawScreen(g);
        // player.draw(g, this);
        // this smooths out animations on some systems
        Toolkit.getDefaultToolkit().sync();
    }
    private void drawScreen(Graphics g)  {
//        System.out.println(zBuf.getRGB(0,0));
//        zBuf.setRGB(0,0, 100);
//        System.out.println(zBuf.getRGB(0,0));

//        line.point2.x = 100*Math.sin(ang) + 200;
//        line.point2.y = 100*Math.cos(ang) + 200;
//        Triangle.point2.x = 100*Math.sin(ang+3) + 200;
//        Triangle.point2.y = 100*Math.cos(ang+3) + 200;
//        line.draw(img);
      /*  ArrayList<Point3D> newPoints = new ArrayList<Point3D>();
        for (Point3D point: points) {
            Point3D _new = new Point3D(0,0,0);
            player.camMatrix.multiplyPoint3to(point, _new);
            if(_new.z > .1) {
                newPoints.add(_new);
            } else{
                newPoints.add(null);
            }
        }
        Triangle t1 = null;
        Triangle t2 = null;
        try {
            t1 = new Triangle(
                    newPoints.get(0).project(player.FPDis, getWidth(), getHeight()),
                    newPoints.get(1).project(player.FPDis, getWidth(), getHeight()),
                    newPoints.get(2).project(player.FPDis, getWidth(), getHeight()));
        } catch(NullPointerException ignored){}
        try{
            t2 = new Triangle(
                newPoints.get(0).project(player.FPDis, getWidth(), getHeight()),
                newPoints.get(2).project(player.FPDis, getWidth(), getHeight()),
                newPoints.get(3).project(player.FPDis, getWidth(), getHeight()));
        } catch(NullPointerException ignored){}

        try{t1.draw(img);}catch (NullPointerException ignored){}
        try{t2.draw(img);}catch (NullPointerException ignored){}
        ang += 0.02;*/
        drawData.lastFrameTime = System.currentTimeMillis()- lastTime;
        lastTime = System.currentTimeMillis();
        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(f"FrameTime: " + (System.currentTimeMillis() - lastTime) + " 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);
                }
            }
            // write debug overlay
            for(int x = 0; x < drawData.scrX; x +=1){
                for(int y = 0; y < drawData.scrY; y += 1){
                        //drawData.bufImg.setRGB(x, y, Color.getHSBColor(0, 0, (float)drawData.zBuf[x][y]/2147483648f).getRGB());
                        //drawData.bufImg.setRGB(x, y, Color.getHSBColor((float)drawData.zBuf[x][y]/2147483648f, 1, 1).getRGB());
                    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);
            g.drawImage(drawData.debugImg, 0, 0, this);
            for(int x = 0; x < drawData.scrX; x +=1){
                for(int y = 0; y < drawData.scrY; y += 1){
                    drawData.debugImg.setRGB(x, y, 0);
                }
            }
        }
        //System.out.println(drawData.timeSpentChoppingFaces + ", " + drawData.timeSpentDrawingNormalFaces);
        drawData.timeSpentChoppingFaces = 0;
        drawData.timeSpentDrawingNormalFaces = 0;

//        HTTPPost post = new HTTPPost();
//        // json WRITING
//        json = new JsonWriter();
//        Point3D[] vertexList;
//        int[][] faceList;
//
//        // camera object
//        vertexList = new Point3D[5];
//        faceList = new int[][]{{0, 1, 2, 3}};
//        for (int i = 0; i < 4; i += 1) {
//            vertexList[i] = player.ScreenCornerPosS[i].getRotatedPoint();
//        }
//        vertexList[4] = player.FPWorldPos;
//        json.addBlenderObject(vertexList, faceList);
//
//        post.invokePost(json);
    }

    @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);
    }

    @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) {
    }
}