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;

import static java.lang.Math.*;

public class GraphicConveyor {


/**
 * Аффинные преобразования
 * Ненавижу матричную логику...
 * @return {@code Matrix4f} - матрица из локальной системы координат в мировую
 */
public static Matrix4f scaleRotateTranslate(Vector3f rotate, Vector3f scale, Vector3f translate) {
    Matrix4f r = calculateRotate(rotate.getX(), rotate.getY(), rotate.getZ());
    Matrix4f s = calculateScale(scale.getX(), scale.getY(), scale.getZ());
    Matrix4f t = calculateTranslate(new Vector3f(translate.getX(), translate.getY(), translate.getZ()));



    t.multiply(r);
    t.multiply(s);

    return new Matrix4f(t);
}

/**
 * Аффинные преобразования - увеличение/уменьшение объекта
 *
 * @param vector3f вектор переноса
 * @return {@code Matrix4f} - матрица переноса
 */

public static Matrix4f calculateTranslate(Vector3f vector3f) {
    return calculateTranslate(new Vector4f(vector3f.getX(), vector3f.getY(), vector3f.getZ(), 1));
}

private static Matrix4f calculateTranslate(Vector4f vector) {
    Matrix4f res = new Matrix4f(1);
    for (int i = 0; i < res.getElements().length - 1; i++) {
        res.setElement(i, 3, vector.getNum(i));
    }
    return res;
}


public static Matrix4f Rotate(float alpha, float beta, float gamma) {
    return calculateRotate(alpha, beta, gamma);
}

// мб потом почитаю что такое кватернион и немного переделаю код, пока пусть так будет
private static Matrix4f calculateRotate(float alpha, float beta, float gamma) {
    // вокруг x
    float[] rotateX = new float[]{
            1, 0, 0, 0,
            0, (float) cos(alpha), (float) sin(alpha), 0,
            0, (float) -sin(alpha), (float) cos(alpha), 0,
            0, 0, 0, 1
    };
    // вокруг y
    float[] rotateY = new float[]{
            (float) cos(beta), 0, (float) sin(beta), 0,
            0, 1, 0, 0,
            (float) -sin(beta), 0, (float) cos(beta), 0,
            0, 0, 0, 1
    };
    // вокруг z
    float[] rotateZ = new float[]{
            (float) cos(gamma), (float) sin(gamma), 0, 0,
            (float) -sin(gamma), (float) cos(gamma), 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
    };

    Matrix4f rotateAboutX = new Matrix4f(rotateX);
    Matrix4f rotateAboutY = new Matrix4f(rotateY);
    Matrix4f rotateAboutZ = new Matrix4f(rotateZ);

    // сначала крутим вокруг x, потом y, потом z (да, в реале умножаем сначала Z, потом ыгрик, потом х (а) - плохо)
    rotateAboutZ.multiply(rotateAboutY);
    rotateAboutZ.multiply(rotateAboutX);

    return rotateAboutZ;
}

/**
 * Аффинные преобразования - увеличение/уменьшение объекта
 *
 * @param scaleX растяжение по x
 * @param scaleY растяжение по y
 * @param scaleZ растяжение по z
 * @return {@code Matrix4f} - матрица растяжения (по факту просто по главной диагонали будут стоять не 1, а растяжение наше)
 */

public static Matrix4f calculateScale(float scaleX, float scaleY, float scaleZ) {
    float[] scale = new float[]{scaleX, scaleY, scaleZ};
    return calculateScale(scale);
}

// надо еще продумать, как обновлять это все... наверное, потом придумаю... надеюсь, молюсь...

private static Matrix4f calculateScale(float[] scale) {
    Matrix4f result = new Matrix4f(1);
    for (int i = 0; i < scale.length; i++) {
        result.setElement(i, i, scale[i]);
    }
    return result;
}

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

/**
 * Система корд камеры +перевод всех объектов сцены в систему координат камеры
 *
 * @param eye    ее позиция в мировых координатах
 * @param target куда смотрит наша камера
 * @param up     единичный вектор для y - основа будущих корд камеры
 * @return {@code Matrix4f} - матрица прямоугольной системы координат камеры, записанная в мировых координатах сцены.
 */

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

/**
 * Матрица перспективы.
 *
 * @param fov         угол обзора
 * @param aspectRatio соотношение сторон экрана
 * @param nearPlane   ближний план
 * @param farPlane    дальний план
 * @return {@code Matrix4f} - матрица перспективы.
 */

public static Matrix4f perspective(
        final float fov,
        final float aspectRatio,
        final float nearPlane,
        final float farPlane) {
    float tangentMinusOnDegree = (float) (1.0F / (tan(fov * 0.5F))); // fov задается как половина от всего угла
    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);
}

/**
 * Получаем последний вектор нормализованный вектор v для трансформации вершины и отображения последней на экране
 *
 * @param matrix P*V*M
 * @param vertex корды точки?
 * @return {@code Vector3f} - нормализованный конечный вектор (скорее всего, нормаль к вершине).
 */
public static Vector3f multiplyMatrix4ByVector3(final Matrix4f matrix, final Vector3f vertex) {
    Vector4f res = matrix.multiply(new Vector4f(vertex.getX(), vertex.getY(), vertex.getZ(), 1));
    float w = res.getW();
    return new Vector3f(res.getX() / w, res.getY() / w, res.getZ() / w);
}

/**
 * Получаем последний вектор нормализованный вектор v для трансформации вершины и отображения последней на экране
 *
 * @param width  ширина
 * @param height высота
 * @param vertex нормаль вершины
 * @return {@code Point2f} - получаем корды точки на экране.
 */

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);
}
}
@FXML
private void recalculationOfNormals() {
TransformationParameters params;
try {
params = parseParameters();
} catch (NumberFormatException e) {
showErrorAlert("Ошибка ввода", "Некорректные данные",
"Пожалуйста, убедитесь, что все поля содержат числовые значения.");
return;
}


    Vector3f rota = new Vector3f((float) params.getAlpha(), (float) params.getBeta(), (float) params.getGamma());
    Vector3f scale = new Vector3f((float) params.getScaleX(), (float) params.getScaleY(), (float) params.getScaleZ());
    Vector3f trans = new Vector3f((float) params.getTranslationX(), (float) params.getTranslationY(), (float) params.getTranslationZ());


    Matrix4f MAT = GraphicConveyor.scaleRotateTranslate(rota, scale, trans);
    mesh.getVertices().replaceAll(vector3f -> GraphicConveyor.multiplyMatrix4ByVector3(MAT, vector3f));
    mesh.calculateNormals();
}
@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();
}
вот мой код.
вот начальные корды объекта:
o Cube
v -1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.657719 0.070615
vt 0.657719 0.288779
vt 0.343923 0.288779
vt 0.343923 0.070615
vt 0.982747 0.538081
vt 0.982747 0.758131
vt 0.677420 0.758131
vt 0.677420 0.538418
vt 0.654372 0.535544
vt 0.654372 0.758215
vt 0.344166 0.758215
vt 0.344166 0.535750
vt 0.327407 0.537083
vt 0.327407 0.759706
vt 0.015414 0.759706
vt 0.015414 0.536937
vt 0.656108 0.299368
vt 0.656108 0.524271
vt 0.344171 0.524271
vt 0.344171 0.299368
vt 0.655295 0.773650
vt 0.655295 0.993820
vt 0.346502 0.993820
vt 0.346502 0.773650
s 0
f 1/1/1 2/2/1 4/3/1 3/4/1
f 3/5/2 4/6/2 8/7/2 7/8/2
f 7/9/3 8/10/3 6/11/3 5/12/3
f 5/13/4 6/14/4 2/15/4 1/16/4
f 3/17/5 7/18/5 5/19/5 1/20/5
f 8/21/6 4/22/6 2/23/6 6/24/6
Я хочу:
1) Translate на 10 по x. и Scale на 2 по x. перерасчет вершин
2) после этого я хочу произвести повторное масштабирование. Где будет находиться правый нижний угол куба?