Newer
Older
EPQ-3D-renderer / src / main / java / uk / org / floop / epq3d / Player.java
@cory cory on 2 Feb 2023 8 KB Fix #17
package uk.org.floop.epq3d;

import java.awt.event.KeyEvent;

public class Player {

    //public Plane[] frustumPlanes = new Plane[6];
    // image that represents the player's position on the board
    // current position of the player on the board grid
    private final Matrix rotMatrix = new Matrix(3, 3);
    public final Matrix camMatrix = new Matrix(4, 3);
    private final Matrix invRotMatrix = new Matrix(3, 3);
    private final Matrix invCamMatrix = new Matrix(4, 3);
    protected Point3D FPWorldPos;
    protected PointComp[] ScreenCornerPosS = new PointComp[4];
    private final Point3D position = new Point3D(0,0,0);
    private final Point3D rotation = new Point3D(0,Math.PI / 2, 0);
    private final Vector3D direction = new Vector3D(0,0,0);
    public Vector3D viewVector = new Vector3D(0,0,0);
    public double mouseSensitivity = 0.005; // radians per pixel
    // called when the player is initialised
    public Player(drawData drawData) {
        drawData.FPDis = 1/Math.tan(Math.toRadians(drawData.FOV)/2d);
        for(int i = 0; i < 6; i+=1){
            drawData.frustumPlanes[i] = new Plane();
        }
        drawData.playerPos = position;
        drawData.camMatrix = camMatrix;
        double yVal = (double)drawData.scrY / (double)drawData.scrX;
        // flip x and y because reasons
        // thank you past me for this incredible description
        ScreenCornerPosS[0] = new PointComp(-yVal,-1,  0);
        ScreenCornerPosS[1] = new PointComp(yVal,-1,  0);
        ScreenCornerPosS[2] = new PointComp(-yVal,1,  0);
        ScreenCornerPosS[3] = new PointComp(yVal,1,  0);
    }

    public void keyPressed(int key) {
        if (key == KeyEvent.VK_W && direction.x < 1) {
            direction.x += 1;
        } else if (key == KeyEvent.VK_S && -direction.x < 1) {
            direction.x -= 1;
        } else if (key == KeyEvent.VK_A && direction.y < 1) {
            direction.y += 1;
        } else if (key == KeyEvent.VK_D && -direction.y < 1) {
            direction.y -= 1;
        } else if (key == KeyEvent.VK_SPACE && direction.z < 1) {
            direction.z += 1;
        } else if (key == KeyEvent.VK_SHIFT && -direction.z < 1) {
            direction.z -= 1;
        }
    }
    public void keyReleased(int key){
        if (key == KeyEvent.VK_W && direction.x > -1) {
            direction.x -= 1;
        } else if (key == KeyEvent.VK_S && -direction.x > -1) {
            direction.x -= -1;
        } else if (key == KeyEvent.VK_A && direction.y > -1) {
            direction.y -= 1;
        } else if (key == KeyEvent.VK_D && -direction.y > -1) {
            direction.y -= -1;
        } else if (key == KeyEvent.VK_SPACE && direction.z > -1) {
            direction.z -= 1;
        } else if (key == KeyEvent.VK_SHIFT && -direction.z > -1) {
            direction.z -= -1;
        }
    }
    public void tick(drawData drawData) {
        // gets called once every tick, before the repainting process happens.
        // change rotation depending on mouse movement
        rotation.z -= drawData.mouseRel.x * mouseSensitivity;
        // force z rotation to wrap around
        if(rotation.z<-Math.PI){
            rotation.z = Math.PI;
        } else if(rotation.z>Math.PI){
            rotation.z = -Math.PI;
        }
        rotation.y += drawData.mouseRel.y * mouseSensitivity;
        // max out y rotation at 0 and -90 degrees
        if(rotation.y < 0){
            rotation.y = 0;
        } else if(rotation.y > Math.PI){
            rotation.y = Math.PI;
        }
        // define rotation and translation matrices
        Matrix zMat = new Matrix(3, 3);
        zMat.setItems(new double[][]{
                {Math.cos(rotation.z),  Math.sin(rotation.z), 0},
                {-Math.sin(rotation.z), Math.cos(rotation.z), 0},
                {0,                      0,                     1}}
        );
        Matrix yMat = new Matrix(3, 3);
        yMat.setItems(new double[][]{
                {Math.cos(rotation.y), 0, -Math.sin(rotation.y)},
                {0,                    1, 0                    },
                {Math.sin(rotation.y), 0, Math.cos(rotation.y) }}
        );
        Matrix xMat = new Matrix(3, 3);
        xMat.setItems(new double[][]{
                {1, 0,                    0                   },
                {0, Math.cos(rotation.x), Math.sin(rotation.x)},
                {0, -Math.sin(rotation.x), Math.cos(rotation.x)}}
        );
        // calculate inverse matrices
        Matrix izMat = new Matrix(3, 3);
        izMat.setItems(new double[][]{
                {Math.cos(-rotation.z),  Math.sin(-rotation.z), 0},
                {-Math.sin(-rotation.z), Math.cos(-rotation.z), 0},
                {0,                      0,                     1}}
        );
        Matrix iyMat = new Matrix(3, 3);
        iyMat.setItems(new double[][]{
                {Math.cos(-rotation.y), 0, -Math.sin(-rotation.y)},
                {0,                    1, 0                    },
                {Math.sin(-rotation.y), 0, Math.cos(-rotation.y) }}
        );
        Matrix ixMat = new Matrix(3, 3);
        ixMat.setItems(new double[][]{
                {1, 0,                    0                   },
                {0, Math.cos(-rotation.x), Math.sin(-rotation.x)},
                {0, -Math.sin(-rotation.x), Math.cos(-rotation.x)}}
        );
        // apply izMat to direction vector
        try {
            Vector3D dirVec = new Vector3D();
            izMat.multiplyVec3to(direction, dirVec);
            double dirLen = dirVec.getLength();
            if (dirLen != 0) {
                dirVec.x = dirVec.x / (dirLen * 40);
                dirVec.y = dirVec.y / (dirLen * 40);
                dirVec.z = dirVec.z / (dirLen * 40);
            }
            position.translate(dirVec);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        Matrix traMat = new Matrix(4, 3);
        traMat.setItems(new double[][]
                {{1, 0, 0, -position.x},
                 {0, 1, 0, -position.y},
                 {0, 0, 1, -position.z}}
        );
        // multiply out matrices
        xMat.multiplyGetResult(yMat).multiplyTo(zMat, rotMatrix);
        rotMatrix.multiplyTo(traMat, camMatrix);
        izMat.multiplyGetResult(iyMat).multiplyTo(ixMat, invRotMatrix);
        // I really don't want to do it like this, but I don't think there's any other way without messing up
        // all my other matrices :(
        for(int x = 0; x<3; x+=1){
            for(int y = 0; y<3; y+=1){
                invCamMatrix.setItem(x, y, invRotMatrix.getItem(x, y));
            }}
        for(int y = 0; y<3; y+=1){
            invCamMatrix.setItem(3, y, -traMat.getItem(3, y));
        }

        // calculate view vector
        Point3D viewPoint = new Point3D(0,0,0);
        invRotMatrix.multiplyPoint3to(new Point3D(0,0,1), viewPoint);
        viewVector.x = viewPoint.x;viewVector.y=viewPoint.y;viewVector.z=viewPoint.z;

        // calculate camera focal point and edges in world coordinates
        FPWorldPos = new Point3D(
                position.x - viewVector.x*drawData.FPDis,
                position.y - viewVector.y*drawData.FPDis,
                position.z - viewVector.z* drawData.FPDis);
        for (PointComp point:
             ScreenCornerPosS) {
            point.invalidate();
            point.setRotatedPoint(invCamMatrix);
        }
        // find frustum planes
        // near plane
        drawData.frustumPlanes[0].initFrom3Points(ScreenCornerPosS[0].getRotatedPoint(),
                ScreenCornerPosS[1].getRotatedPoint(), ScreenCornerPosS[2].getRotatedPoint());
        // far plane
        int farPlaneDis = 1000;
        drawData.frustumPlanes[1].initFromPointAndVector(
                new Point3D(
                        position.x + viewVector.x*farPlaneDis,
                        position.y + viewVector.y*farPlaneDis,
                        position.z + viewVector.z*farPlaneDis),
                // invert the view vector because the normal needs to point the other way
                new Vector3D(-viewVector.x, -viewVector.y, -viewVector.z));
        // mid planes
        // left plane
        drawData.frustumPlanes[2].initFrom3Points(FPWorldPos,
                ScreenCornerPosS[0].getRotatedPoint(), ScreenCornerPosS[1].getRotatedPoint());
        // right plane
        drawData.frustumPlanes[3].initFrom3Points(FPWorldPos,
                ScreenCornerPosS[3].getRotatedPoint(), ScreenCornerPosS[2].getRotatedPoint());
        // top plane
        drawData.frustumPlanes[4].initFrom3Points(FPWorldPos,
                ScreenCornerPosS[1].getRotatedPoint(), ScreenCornerPosS[3].getRotatedPoint());
        // bottom plane
        drawData.frustumPlanes[5].initFrom3Points(FPWorldPos,
                ScreenCornerPosS[2].getRotatedPoint(), ScreenCornerPosS[0].getRotatedPoint());

        // update drawData
    }

    // returnValue Functions
    public Point3D getPos() {
        return position;
    }
    public Point3D getRot() {
        return rotation;
    }

}