package com.cgvsu.render_engine;

import com.cgvsu.math.Matrix4f;
import com.cgvsu.math.Point2f;
import com.cgvsu.math.Vector3f;
import com.cgvsu.math.Vector4f;


public class GraphicConveyor {

    // матрица M
    public static Matrix4f rotateScaleTranslate() {
        return new Matrix4f(1);
    }

    public static Matrix4f lookAt(Vector3f eye, Vector3f target) {
        return lookAt(eye, target, new Vector3f(0F, 1.0F, 0F));
    }

    public static Matrix4f lookAt(Vector3f eye, Vector3f target, Vector3f up) {
        Vector3f resultX = new Vector3f();
        Vector3f resultY = new Vector3f();
        Vector3f resultZ = new Vector3f();

        resultZ.sub(target, eye);
        resultX = up.cross(resultZ);
        resultY = resultZ.cross(resultX);

        resultX.normalize();
        resultY.normalize();
        resultZ.normalize();

        float[] matrix = new float[]{
                resultX.getX(), resultY.getX(), resultZ.getX(), 0,
                resultX.getY(), resultY.getY(), resultZ.getY(), 0,
                resultX.getZ(), resultY.getZ(), resultZ.getZ(), 0,
                -resultX.dot(eye), -resultY.dot(eye), -resultZ.dot(eye), 1};
        return new Matrix4f(matrix);
    }

    public static Matrix4f perspective(
            final float fov,
            final float aspectRatio,
            final float nearPlane,
            final float farPlane) {
        float tangentMinusOnDegree = (float) (1.0F / (Math.tan(fov * 0.5F)));
        float[] res = {
                tangentMinusOnDegree / aspectRatio, 0, 0, 0,
                0, tangentMinusOnDegree, 0, 0,
                0, 0, (farPlane + nearPlane) / (farPlane - nearPlane), 1.0F,
                0, 0, 2 * (nearPlane * farPlane) / (nearPlane - farPlane), 0
        };
        return new Matrix4f(res);
    }

    public static Vector3f multiplyMatrix4ByVector3(final Matrix4f matrix, final Vector3f vertex) {
        matrix.transposition();
        Vector4f res = matrix.multiply(new Vector4f(vertex.getX(), vertex.getY(), vertex.getZ(), 1));
        matrix.transposition();
        float w = res.getW();
        return new Vector3f(res.getX() / w, res.getY() / w, res.getZ() / w);
    }

    public static Point2f vertexToPoint(final Vector3f vertex, final int width, final int height) {
        return new Point2f(vertex.getX() * width + width / 2.0F, -vertex.getY() * height + height / 2.0F);
    }
}
package com.cgvsu.render_engine;

import java.awt.*;
import java.util.ArrayList;

import com.cgvsu.math.Vector3f;
import com.cgvsu.math.Point2f;

import javafx.scene.canvas.GraphicsContext;
import com.cgvsu.model.Model;
import static com.cgvsu.render_engine.GraphicConveyor.*;
import com.cgvsu.math.Matrix4f;




public class RenderEngine {

    public static void render(
            final GraphicsContext graphicsContext,
            final Camera camera,
            final Model mesh,
            final int width,
            final int height)
    {
        Matrix4f modelMatrix = rotateScaleTranslate();
        Matrix4f viewMatrix = camera.getViewMatrix();
        Matrix4f projectionMatrix = camera.getProjectionMatrix();

        Matrix4f modelViewProjectionMatrix = new Matrix4f(modelMatrix);
        modelViewProjectionMatrix.multiply(viewMatrix);
        modelViewProjectionMatrix.multiply(projectionMatrix);

        final int nPolygons = mesh.polygons.size();
        for (int polygonInd = 0; polygonInd < nPolygons; ++polygonInd) {
            final int nVerticesInPolygon = mesh.polygons.get(polygonInd).getVertexIndices().size();

            ArrayList<Point2f> resultPoints = new ArrayList<>();
            for (int vertexInPolygonInd = 0; vertexInPolygonInd < nVerticesInPolygon; ++vertexInPolygonInd) {
                Vector3f vertex = mesh.vertices.get(mesh.polygons.get(polygonInd).getVertexIndices().get(vertexInPolygonInd));

                Vector3f vertexVecmath = new Vector3f(vertex.getX(), vertex.getY(), vertex.getZ());

                Point2f resultPoint = vertexToPoint(multiplyMatrix4ByVector3(modelViewProjectionMatrix, vertexVecmath), width, height);


                resultPoints.add(resultPoint);
            }

            for (int vertexInPolygonInd = 1; vertexInPolygonInd < nVerticesInPolygon; ++vertexInPolygonInd) {
                graphicsContext.strokeLine(
                        resultPoints.get(vertexInPolygonInd - 1).getX(),
                        resultPoints.get(vertexInPolygonInd - 1).getY(),
                        resultPoints.get(vertexInPolygonInd).getX(),
                        resultPoints.get(vertexInPolygonInd).getY());
            }

            if (nVerticesInPolygon > 0)
                graphicsContext.strokeLine(
                        resultPoints.get(nVerticesInPolygon - 1).getX(),
                        resultPoints.get(nVerticesInPolygon - 1).getY(),
                        resultPoints.get(0).getX(),
                        resultPoints.get(0).getY());
        }
    }
}package com.cgvsu.math;

public class Matrix4f extends AbstractMatrix {
    private static final int SIZE = 4;
    private float[][] elements;

    // Конструкторы + getters and setters
    public Matrix4f(float[] elements) {
        if (elements.length != SIZE * SIZE) {
            throw new IllegalArgumentException("Массив должен содержать ровно " + (SIZE * SIZE) + " элементов.");
        }
        this.elements = new float[SIZE][SIZE];
        int k = 0;
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++, k++) {
                this.elements[i][j] = elements[k];
            }
        }
    }

    public Matrix4f(Matrix4f elements) {
        float[][] el = elements.getElements();
        if (el.length != SIZE && el[0].length != SIZE) {
            throw new IllegalArgumentException("Массив должен содержать ровно " + (SIZE * SIZE) + " элементов.");
        }
        this.elements = new float[SIZE][SIZE];

        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                this.elements[i][j] = elements.getElement(i, j);
            }
        }
    }

    public Matrix4f(int one) {
        //Опять вопрос к Борисову: проверять ли это num на какое-то значение, отличное от нуля, или нет... а, хотя, я наебу систему
        this.elements = new float[SIZE][SIZE];
        for (int i = 0; i < SIZE; i++) {
            this.elements[i][i] = 1;
        }
    }

    public Matrix4f() {
        this.elements = new float[SIZE][SIZE];
    }

    @Override
    public Matrix4f add(AbstractMatrix other) {
        float[] a = new float[16];
        int k = 0;
        if (!isMatrix4x4(other)) {
            throw new IllegalArgumentException("Invalid matrix type for dot product");
        }
        Matrix4f o = (Matrix4f) other;
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++, k++) {
                a[k] = this.elements[i][j] + o.elements[i][j];
            }
        }
        return new Matrix4f(a);
    }

    @Override
    public Matrix4f sub(AbstractMatrix other) {
        float[] a = new float[16];
        int k = 0;
        if (!isMatrix4x4(other)) {
            throw new IllegalArgumentException("Invalid matrix type for dot product");
        }
        Matrix4f o = (Matrix4f) other;
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++, k++) {
                a[k] = this.elements[i][j] - o.elements[i][j];
            }
        }
        return new Matrix4f(a);
    }

    @Override
    public Vector4f multiply(AbstractVector other) {
        if (!isVector4f(other)) {
            throw new IllegalArgumentException("Invalid vector type for dot product");
        }
        Vector4f o = (Vector4f) other;
        Vector4f result = new Vector4f();
        for (int i = 0; i < SIZE; i++) {
            float res = 0;
            for (int j = 0; j < SIZE; j++) {
                res += (elements[i][j] * o.getNum(j));
            }
            result.setNum(i, res);
        }
        return result;
    }

    @Override
    public void multiply(AbstractMatrix other) {
        if (!isMatrix4x4(other)) {
            throw new IllegalArgumentException("Invalid vector type for dot product");
        }
        Matrix4f o = (Matrix4f) other;
        Matrix4f result = new Matrix4f();
        for (int i = 0; i < SIZE; i++) {
            for (int k = 0; k < SIZE; k++) {
                float res = 0;
                for (int j = 0; j < SIZE; j++) {
                    res += this.elements[i][j] * o.elements[j][k];
                }
                result.elements[i][k] = res;
            }
        }
        this.elements = result.getElements();
    }

    @Override
    public void transposition() {
        for (int i = 0; i < SIZE; i++) {
            for (int j = i + 1; j < SIZE; j++) {
                float temp = elements[i][j];
                elements[i][j] = elements[j][i];
                elements[j][i] = temp;
            }
        }
    }

    @Override
    // Вычисление определителя матрицы 4x4
    public float determinant() {
        float det = 0;
        for (int col = 0; col < SIZE; col++) {
            det += (col % 2 == 0 ? 1 : -1) * elements[0][col] * getMinorMatrix(0, col).determinant();
        }
        return det;
    }

    /**
     * Вычисление матрицы алгебраических дополнений для матрицы 4x4
     *
     * @return Матрица алгебраических дополнений 4x4
     */
    private float[][] computeDopMatrix() {
        float[][] cofactors = new float[SIZE][SIZE];
        for (int row = 0; row < SIZE; row++) {
            for (int col = 0; col < SIZE; col++) {
                Matrix3f minorMatrix = getMinorMatrix(row, col);
                float minorDeterminant = minorMatrix.determinant();
                cofactors[row][col] = ((row + col) % 2 == 0 ? 1 : -1) * minorDeterminant;

            }
        }
        return cofactors;
    }

    /**
     * Вычисление матрицы тильда для матрицы 4x4
     *
     * @return Матрица присоединённых 4x4
     */
    private Matrix4f computeTildaMatrix() {
        float[][] cofactors = computeDopMatrix();
        Matrix4f tilda = new Matrix4f();
        tilda.elements = cofactors;

        // Транспонирование матрицы кофакторов
        tilda.transposition();

        return tilda;
    }

    /**
     * Получение минорной матрицы 3x3, исключая указанную строку и столбец
     *
     * @param rowToExclude Строка для исключения
     * @param colToExclude Столбец для исключения
     * @return Минорная матрица 3x3
     */
    private Matrix3f getMinorMatrix(int rowToExclude, int colToExclude) {
        float[] minorElements = new float[9];
        int index = 0;
        for (int i = 0; i < SIZE; i++) {
            if (i == rowToExclude) continue;
            for (int j = 0; j < SIZE; j++) {
                if (j == colToExclude) continue;
                minorElements[index++] = elements[i][j];
            }
        }
        return new Matrix3f(minorElements);
    }


    /**
     * Вычисление обратной матрицы 4x4
     *
     * @return Обратная матрица 4x4
     * @throws ArithmeticException если матрица вырождена и не имеет обратной
     */
    @Override
    public Matrix4f inverse() {
        float det = determinant();
        if (det == 0) {
            throw new ArithmeticException("Матрица вырождена и не имеет обратной.");
        }
        Matrix4f tilda = computeTildaMatrix();
        float[] inverseElements = new float[16];
        int k = 0;
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++, k++) {
                inverseElements[k] = tilda.elements[i][j] / det;
            }
        }
        return new Matrix4f(inverseElements);
    }

    @Override
    public float getElement(int rows, int col) {
        return elements[rows][col];
    }

    @Override
    public void setElement(int rows, int col, float result) {
        elements[rows][col] = result;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (float[] row : elements) {
            for (float elem : row) {
                sb.append(String.format("%.2f\t", elem));
            }
            sb.append("\n");
        }
        boolean a = false;
        return sb.toString();
    }

    private boolean isMatrix4x4(AbstractMatrix other) {
        return other instanceof Matrix4f;
    }

    private boolean isVector4f(AbstractVector other) {
        return other instanceof Vector4f;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof Matrix4f)) return false;
        Matrix4f other = (Matrix4f) obj;
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                if (Float.compare(this.elements[i][j], other.elements[i][j]) != 0) {
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = 1;
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                result = 31 * result + Float.hashCode(elements[i][j]);
            }
        }
        return result;
    }

    // Геттер для элементов матрицы
    public float[][] getElements() {
        return elements;
    }
}
package com.cgvsu.render_engine;
import com.cgvsu.math.Vector3f;
import com.cgvsu.math.Matrix4f;


public class Camera {

    public Camera(
            final Vector3f position,
            final Vector3f target,
            final float fov,
            final float aspectRatio,
            final float nearPlane,
            final float farPlane) {
        this.position = position;
        this.target = target;
        this.fov = fov;
        this.aspectRatio = aspectRatio;
        this.nearPlane = nearPlane;
        this.farPlane = farPlane;
    }

    public void setPosition(final Vector3f position) {
        this.position = position;
    }

    public void setTarget(final Vector3f target) {
        this.target = target;
    }

    public void setAspectRatio(final float aspectRatio) {
        this.aspectRatio = aspectRatio;
    }

    public Vector3f getPosition() {
        return position;
    }

    public Vector3f getTarget() {
        return target;
    }

    public void movePosition(final Vector3f translation) {
        this.position.add(translation);
    }

    public void moveTarget(final Vector3f translation) {
        this.target.add(target);
    }

    // матрица V
    Matrix4f getViewMatrix() {
        return GraphicConveyor.lookAt(position, target);
    }
    // P
    Matrix4f getProjectionMatrix() {
        return GraphicConveyor.perspective(fov, aspectRatio, nearPlane, farPlane);
    }

    private Vector3f position;
    private Vector3f target;
    private float fov;
    private float aspectRatio;
    private float nearPlane;
    private float farPlane;
}
я хочу. чтобы у меня перемножались матрицы в правильном порядке. Как мне это сделать?