All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.ru.casperix.math.quad_matrix.float32.Matrix4f.kt Maven / Gradle / Ivy

package ru.casperix.math.quad_matrix.float32

import ru.casperix.math.quad_matrix.QuadMatrix
import ru.casperix.math.quad_matrix.QuadMatrixBuilder
import ru.casperix.math.quad_matrix.float64.Matrix4d
import ru.casperix.math.quaternion.float32.QuaternionFloat
import ru.casperix.math.vector.float32.Vector3f
import ru.casperix.misc.clone
import kotlinx.serialization.Serializable
import kotlin.math.PI
import kotlin.math.tan

@Serializable
data class Matrix4f(val data: FloatArray) : QuadMatrix {

//    constructor(vararg data: Float) : this(floatArrayOf(*data))

    init {
        if (data.size != 16) throw Error("Source expected 16 elements, but actual ${data.size}")
    }

    operator fun get(x: Int, y: Int): Float {
        return data[x + y * 4]
    }

    operator fun get(index: Int): Float {
        return data[index]
    }

    fun getTranslate(): Vector3f {
        return Vector3f(data[12], data[13], data[14])
    }

    fun transform(value: Vector3f): Vector3f {
        val m = data

        val rx = value.x * m[0] + value.y * m[4] + value.z * m[8] + m[12]
        val ry = value.x * m[1] + value.y * m[5] + value.z * m[9] + m[13]
        val rz = value.x * m[2] + value.y * m[6] + value.z * m[10] + m[14]
        val rw = 1 / (value.x * m[3] + value.y * m[7] + value.z * m[11] + m[15])

        return Vector3f(rx * rw, ry * rw, rz * rw)
    }

    override operator fun times(other: Matrix4f): Matrix4f {
        val A = data
        val B = other.data
        val target = floatArrayOf(
            A[i00] * B[i00] + A[i10] * B[i01] + A[i20] * B[i02] + A[i30] * B[i03],
            A[i00] * B[i10] + A[i10] * B[i11] + A[i20] * B[i12] + A[i30] * B[i13],
            A[i00] * B[i20] + A[i10] * B[i21] + A[i20] * B[i22] + A[i30] * B[i23],
            A[i00] * B[i30] + A[i10] * B[i31] + A[i20] * B[i32] + A[i30] * B[i33],

            A[i01] * B[i00] + A[i11] * B[i01] + A[i21] * B[i02] + A[i31] * B[i03],
            A[i01] * B[i10] + A[i11] * B[i11] + A[i21] * B[i12] + A[i31] * B[i13],
            A[i01] * B[i20] + A[i11] * B[i21] + A[i21] * B[i22] + A[i31] * B[i23],
            A[i01] * B[i30] + A[i11] * B[i31] + A[i21] * B[i32] + A[i31] * B[i33],

            A[i02] * B[i00] + A[i12] * B[i01] + A[i22] * B[i02] + A[i32] * B[i03],
            A[i02] * B[i10] + A[i12] * B[i11] + A[i22] * B[i12] + A[i32] * B[i13],
            A[i02] * B[i20] + A[i12] * B[i21] + A[i22] * B[i22] + A[i32] * B[i23],
            A[i02] * B[i30] + A[i12] * B[i31] + A[i22] * B[i32] + A[i32] * B[i33],

            A[i03] * B[i00] + A[i13] * B[i01] + A[i23] * B[i02] + A[i33] * B[i03],
            A[i03] * B[i10] + A[i13] * B[i11] + A[i23] * B[i12] + A[i33] * B[i13],
            A[i03] * B[i20] + A[i13] * B[i21] + A[i23] * B[i22] + A[i33] * B[i23],
            A[i03] * B[i30] + A[i13] * B[i31] + A[i23] * B[i32] + A[i33] * B[i33],
        )

        return Matrix4f(target)
    }

    fun toMatrix4d(): Matrix4d {
        return Matrix4d(DoubleArray(16) { this[it].toDouble() })
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other == null || this::class != other::class) return false

        other as Matrix4f

        if (!data.contentEquals(other.data)) return false

        return true
    }

    override fun hashCode(): Int {
        return data.hashCode()
    }

    override fun transpose(): Matrix4f {
        return Matrix4f(
            floatArrayOf(
                data[i00], data[i01], data[i02], data[i03],
                data[i10], data[i11], data[i12], data[i13],
                data[i20], data[i21], data[i22], data[i23],
                data[i30], data[i31], data[i32], data[i33],
            )
        )
    }

    override fun determinant(): Float {
        return data[i30] * data[i21] * data[i12] * data[i03] - data[i20] * data[i31] * data[i12] * data[i03] - data[i30] * data[i11] * data[i22] * data[i03] + data[i10] * data[i31] * data[i22] * data[i03] + data[i20] * data[i11] * data[i32] * data[i03] - data[i10] * data[i21] * data[i32] * data[i03] - data[i30] * data[i21] * data[i02] * data[i13] + data[i20] * data[i31] * data[i02] * data[i13] + data[i30] * data[i01] * data[i22] * data[i13] - data[i00] * data[i31] * data[i22] * data[i13] - data[i20] * data[i01] * data[i32] * data[i13] + data[i00] * data[i21] * data[i32] * data[i13] + data[i30] * data[i11] * data[i02] * data[i23] - data[i10] * data[i31] * data[i02] * data[i23] - data[i30] * data[i01] * data[i12] * data[i23] + data[i00] * data[i31] * data[i12] * data[i23] + data[i10] * data[i01] * data[i32] * data[i23] - data[i00] * data[i11] * data[i32] * data[i23] - data[i20] * data[i11] * data[i02] * data[i33] + data[i10] * data[i21] * data[i02] * data[i33] + data[i20] * data[i01] * data[i12] * data[i33] - data[i00] * data[i21] * data[i12] * data[i33] - data[i10] * data[i01] * data[i22] * data[i33] + data[i00] * data[i11] * data[i22] * data[i33]
    }

    fun setTranslate(value: Vector3f): Matrix4f {
        val output = data.clone()
        output.set(12, value.x)
        output.set(13, value.y)
        output.set(14, value.z)
        return Matrix4f(output)

    }

    fun toMatrix3f(): Matrix3f {
        return Matrix3f(
            floatArrayOf(
                data[0], data[1], data[2],
                data[4], data[5], data[6],
                data[8], data[9], data[10],
            )
        )
    }

    override fun inverse(): Matrix4f {
        val l_det = data[3] * data[6] * data[9] * data[12] -
                data[2] * data[7] * data[9] * data[12] -
                data[3] * data[5] * data[10] * data[12] +
                data[1] * data[7] * data[10] * data[12] +
                data[2] * data[5] * data[11] * data[12] -
                data[1] * data[6] * data[11] * data[12] -
                data[3] * data[6] * data[8] * data[13] +
                data[2] * data[7] * data[8] * data[13] +
                data[3] * data[4] * data[10] * data[13] -
                data[0] * data[7] * data[10] * data[13] -
                data[2] * data[4] * data[11] * data[13] +
                data[0] * data[6] * data[11] * data[13] +
                data[3] * data[5] * data[8] * data[14] -
                data[1] * data[7] * data[8] * data[14] -
                data[3] * data[4] * data[9] * data[14] +
                data[0] * data[7] * data[9] * data[14] +
                data[1] * data[4] * data[11] * data[14] -
                data[0] * data[5] * data[11] * data[14] -
                data[2] * data[5] * data[8] * data[15] +
                data[1] * data[6] * data[8] * data[15] +
                data[2] * data[4] * data[9] * data[15] -
                data[0] * data[6] * data[9] * data[15] -
                data[1] * data[4] * data[10] * data[15] +
                data[0] * data[5] * data[10] * data[15]

        if (l_det == 0f) throw RuntimeException("non-indataertible matrix");
        val m0 =
            data[9] * data[14] * data[7] - data[13] * data[10] * data[7] + data[13] * data[6] * data[11] - data[5] * data[14] * data[11] - data[9] * data[6] * data[15] + data[5] * data[10] * data[15]
        val m4 =
            data[12] * data[10] * data[7] - data[8] * data[14] * data[7] - data[12] * data[6] * data[11] + data[4] * data[14] * data[11] + data[8] * data[6] * data[15] - data[4] * data[10] * data[15]
        val m8 =
            data[8] * data[13] * data[7] - data[12] * data[9] * data[7] + data[12] * data[5] * data[11] - data[4] * data[13] * data[11] - data[8] * data[5] * data[15] + data[4] * data[9] * data[15]
        val m12 =
            data[12] * data[9] * data[6] - data[8] * data[13] * data[6] - data[12] * data[5] * data[10] + data[4] * data[13] * data[10] + data[8] * data[5] * data[14] - data[4] * data[9] * data[14]
        val m1 =
            data[13] * data[10] * data[3] - data[9] * data[14] * data[3] - data[13] * data[2] * data[11] + data[1] * data[14] * data[11] + data[9] * data[2] * data[15] - data[1] * data[10] * data[15]
        val m5 =
            data[8] * data[14] * data[3] - data[12] * data[10] * data[3] + data[12] * data[2] * data[11] - data[0] * data[14] * data[11] - data[8] * data[2] * data[15] + data[0] * data[10] * data[15]
        val m9 =
            data[12] * data[9] * data[3] - data[8] * data[13] * data[3] - data[12] * data[1] * data[11] + data[0] * data[13] * data[11] + data[8] * data[1] * data[15] - data[0] * data[9] * data[15]
        val m13 =
            data[8] * data[13] * data[2] - data[12] * data[9] * data[2] + data[12] * data[1] * data[10] - data[0] * data[13] * data[10] - data[8] * data[1] * data[14] + data[0] * data[9] * data[14]
        val m2 =
            data[5] * data[14] * data[3] - data[13] * data[6] * data[3] + data[13] * data[2] * data[7] - data[1] * data[14] * data[7] - data[5] * data[2] * data[15] + data[1] * data[6] * data[15]
        val m6 =
            data[12] * data[6] * data[3] - data[4] * data[14] * data[3] - data[12] * data[2] * data[7] + data[0] * data[14] * data[7] + data[4] * data[2] * data[15] - data[0] * data[6] * data[15]
        val m10 =
            data[4] * data[13] * data[3] - data[12] * data[5] * data[3] + data[12] * data[1] * data[7] - data[0] * data[13] * data[7] - data[4] * data[1] * data[15] + data[0] * data[5] * data[15]
        val m14 =
            data[12] * data[5] * data[2] - data[4] * data[13] * data[2] - data[12] * data[1] * data[6] + data[0] * data[13] * data[6] + data[4] * data[1] * data[14] - data[0] * data[5] * data[14]
        val m3 =
            data[9] * data[6] * data[3] - data[5] * data[10] * data[3] - data[9] * data[2] * data[7] + data[1] * data[10] * data[7] + data[5] * data[2] * data[11] - data[1] * data[6] * data[11]
        val m7 =
            data[4] * data[10] * data[3] - data[8] * data[6] * data[3] + data[8] * data[2] * data[7] - data[0] * data[10] * data[7] - data[4] * data[2] * data[11] + data[0] * data[6] * data[11]
        val m11 =
            data[8] * data[5] * data[3] - data[4] * data[9] * data[3] - data[8] * data[1] * data[7] + data[0] * data[9] * data[7] + data[4] * data[1] * data[11] - data[0] * data[5] * data[11]
        val m15 =
            data[4] * data[9] * data[2] - data[8] * data[5] * data[2] + data[8] * data[1] * data[6] - data[0] * data[9] * data[6] - data[4] * data[1] * data[10] + data[0] * data[5] * data[10]

        val inv_det = 1.0f / l_det;
        return Matrix4f(
            floatArrayOf(
                m0 * inv_det,
                m1 * inv_det,
                m2 * inv_det,
                m3 * inv_det,
                m4 * inv_det,
                m5 * inv_det,
                m6 * inv_det,
                m7 * inv_det,
                m8 * inv_det,
                m9 * inv_det,
                m10 * inv_det,
                m11 * inv_det,
                m12 * inv_det,
                m13 * inv_det,
                m14 * inv_det,
                m15 * inv_det,
            )
        )
    }

    companion object : QuadMatrixBuilder {
        private val i00 = 0
        private val i10 = 1
        private val i20 = 2
        private val i30 = 3
        private val i01 = 4
        private val i11 = 5
        private val i21 = 6
        private val i31 = 7
        private val i02 = 8
        private val i12 = 9
        private val i22 = 10
        private val i32 = 11
        private val i03 = 12
        private val i13 = 13
        private val i23 = 14
        private val i33 = 15

        fun byIndex(factory: (Int) -> Float): Matrix4f {
            return Matrix4f(FloatArray(16) { factory(it) })
        }

        fun fromValues(vararg values: Float): Matrix4f {
            return byIndex { values.getOrNull(it) ?: 0f }
        }

        val IDENTITY = byIndex { if (it % 5 == 0) 1f else 0f }

        override fun translate(value: Vector3f): Matrix4f {
            return fromValues(
                1f, 0f, 0f, 0f,
                0f, 1f, 0f, 0f,
                0f, 0f, 1f, 0f,
                value.x, value.y, value.z, 1f
            )
        }

        override fun scale(value: Vector3f): Matrix4f {
            return fromValues(
                value.x, 0f, 0f, 0f,
                0f, value.y, 0f, 0f,
                0f, 0f, value.z, 0f,
                0f, 0f, 0f, 1f
            )
        }


        override fun rotate(value: QuaternionFloat): Matrix4f {
            val xx = value.x * value.x
            val yy = value.y * value.y
            val zz = value.z * value.z
            val xy = value.x * value.y
            val zw = value.z * value.w
            val zx = value.z * value.x
            val yw = value.y * value.w
            val yz = value.y * value.z
            val xw = value.x * value.w

            return Matrix4f(
                floatArrayOf(
                    /*0*/ (1f - (2f * (yy + zz))),
                    /*1*/ 2f * (xy + zw),
                    /*2*/ 2f * (zx - yw),
                    /*3*/ 0f,

                    /*4*/ 2f * (xy - zw),
                    /*5*/ (1f - (2f * (zz + xx))),
                    /*6*/ 2f * (yz + xw),
                    /*7*/ 0f,

                    /*8*/ 2f * (zx + yw),
                    /*9*/ 2f * (yz - xw),
                    /*10*/ (1f - (2f * (yy + xx))),
                    /*11*/ 0f,

                    /*12*/ 0f,
                    /*13*/ 0f,
                    /*14*/ 0f,
                    /*15*/ 1f
                )
            )
        }

        fun compose(translate: Vector3f, scale: Vector3f, rotation: QuaternionFloat): Matrix4f {
            val xx = rotation.x * rotation.x
            val yy = rotation.y * rotation.y
            val zz = rotation.z * rotation.z
            val xy = rotation.x * rotation.y
            val zw = rotation.z * rotation.w
            val zx = rotation.z * rotation.x
            val yw = rotation.y * rotation.w
            val yz = rotation.y * rotation.z
            val xw = rotation.x * rotation.w

            return Matrix4f(
                floatArrayOf(
                    /*0*/ (1f - (2f * (yy + zz))) * scale.x,
                    /*1*/ 2f * (xy + zw) * scale.x,
                    /*2*/ 2f * (zx - yw) * scale.x,
                    /*3*/ 0f,

                    /*4*/ 2f * (xy - zw) * scale.y,
                    /*5*/ (1f - (2f * (zz + xx))) * scale.y,
                    /*6*/ 2f * (yz + xw) * scale.y,
                    /*7*/ 0f,

                    /*8*/ 2f * (zx + yw) * scale.z,
                    /*9*/ 2f * (yz - xw) * scale.z,
                    /*10*/ (1f - (2f * (yy + xx))) * scale.z,
                    /*11*/ 0f,

                    /*12*/ translate.x,
                    /*13*/ translate.y,
                    /*14*/ translate.z,
                    /*15*/ 1f
                )
            )
        }

        fun orthographic(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float): Matrix4f {
            val tx = -(right + left) / (right - left)
            val ty = -(top + bottom) / (top - bottom)
            val tz = -(far + near) / (far - near)
            return Matrix4f(
                floatArrayOf(
                    2f / (right - left), 0f, 0f, 0f,
                    0f, 2f / (top - bottom), 0f, 0f,
                    0f, 0f, -2f / (far - near), 0f,
                    tx, ty, tz, 1f,
                )
            )
        }

        fun frustum(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float): Matrix4f {
            val a = (right + left) / (right - left)
            val b = (top + bottom) / (top - bottom)
            val c = -(far + near) / (far - near)
            val d = -(2f * far * near) / (far - near)
            return Matrix4f(
                floatArrayOf(
                    2f * near / (right - left), 0f, 0f, 0f,
                    0f, 2f * near / (top - bottom), 0f, 0f,
                    a, b, c, -1f,
                    0f, 0f, d, 0f,
                )
            )
        }

        /**
         * Creates a perspective projection matrix. Similar to
         * `gluPerspective(fovy, aspec, zNear, zFar)`.
         *
         * @param fovy   Field of view angle in degrees
         * @param aspect The aspect ratio is the ratio of width to height
         * @param near   Distance from the viewer to the near clipping plane, must
         * be positive
         * @param far    Distance from the viewer to the far clipping plane, must be
         * positive
         *
         * @return Perspective matrix
         */
        fun perspective(fovy: Float, aspect: Float, near: Float, far: Float): Matrix4f {
            val f = (1f / tan(fovy.toDouble() * PI / 180f / 2f)).toFloat()

            return Matrix4f(
                floatArrayOf(
                    f / aspect, 0f, 0f, 0f,
                    0f, f, 0f, 0f,
                    0f, 0f, (far + near) / (near - far), -1f,
                    0f, 0f, 2f * far * near / (near - far), 0f,
                )
            )
        }

        fun createByAxis(xAxis: Vector3f, yAxis: Vector3f, zAxis: Vector3f): Matrix4f {
            return fromValues(
                xAxis.x, yAxis.x, zAxis.x, 0f,
                xAxis.y, yAxis.y, zAxis.y, 0f,
                xAxis.z, yAxis.z, zAxis.z, 0f,
                0f, 0f, 0f, 1f
            )
        }

        fun fromQuaternion(quaternion: QuaternionFloat): Matrix4f {
            val xx = quaternion.x * quaternion.x
            val yy = quaternion.y * quaternion.y
            val zz = quaternion.z * quaternion.z
            val xy = quaternion.x * quaternion.y
            val zw = quaternion.z * quaternion.w
            val zx = quaternion.z * quaternion.x
            val yw = quaternion.y * quaternion.w
            val yz = quaternion.y * quaternion.z
            val xw = quaternion.x * quaternion.w

            return fromValues(
                /*0*/ 1f - (2f * (yy + zz)),
                /*1*/ 2f * (xy + zw),
                /*2*/ 2f * (zx - yw),
                /*3*/ 0f,

                /*4*/ 2f * (xy - zw),
                /*5*/ 1f - (2f * (zz + xx)),
                /*6*/ 2f * (yz + xw),
                /*7*/ 0f,

                /*8*/ 2f * (zx + yw),
                /*9*/ 2f * (yz - xw),
                /*10*/ 1f - (2f * (yy + xx)),
                /*11*/ 0f,

                /*12*/ 0f,
                /*13*/ 0f,
                /*14*/ 0f,
                /*15*/ 1f
            )
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy