---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by HUAFEI2.
--- DateTime: 2020/6/29 12:01
---

---@param pOut LVec4
---@param pV LVec4
---@param pM LMatrix
---@return LVec4
function TransTransform(pOut, pV, pM)
    local x = pV.x;
    local y = pV.y;
    local z = pV.z;
    local w = pV.w;
    local f = pM.f;
    pOut.x = f[1] * x + f[5] * y + f[9] * z + f[13] * w;
    pOut.y = f[2] * x + f[6] * y + f[10] * z + f[14] * w;
    pOut.z = f[3] * x + f[7] * y + f[11] * z + f[15] * w;
    pOut.w = f[4] * x + f[8] * y + f[12] * z + f[16] * w;
end

---@param pOut LVec3
---@param pV LVec3
---@param pM LMatrix
---@return LVec3
function TransTransformNormal(pOut, pV, pM)
    local x = pV.x;
    local y = pV.y;
    local z = pV.z;
    local f = pM.f;
    pOut.x = f[1] * x + f[5] * y + f[9] * z;
    pOut.y = f[2] * x + f[6] * y + f[10] * z;
    pOut.z = f[3] * x + f[7] * y + f[11] * z;
end

---@param pOut LVec3
---@param pV LVec3
---@param pM LMatrix
---@return LVec3
function TransTransformCoord(pOut, pV, pM)
    local x = pV.x;
    local y = pV.y;
    local z = pV.z;
    local inv_w = 1.0 / (pM._14 * x + pM._24 * y + pM._34 * z + pM._44);
    local f = pM.f;
    pOut.x = (f[1] * x + f[5] * y + f[9] * z + f[13]) * inv_w;
    pOut.y = (f[2] * x + f[6] * y + f[10] * z + f[14]) * inv_w;
    pOut.z = (f[3] * x + f[7] * y + f[11] * z + f[15]) * inv_w;
end

---@param r LVec3
---@param v LVec3
---@param viewportWidth number
---@param viewportHeight number
---@param ViewProjMat LMatrix
---@param worldMat | nil LMatrix
---@return LVec3
function TransVec3ProjectViewport(r, v, viewportWidth, viewportHeight, ViewProjMat, worldMat)
    local v0 = LVec4:new(v.x, v.y, v.z, 1.0);
    local v1 = LVec4:new();

    if worldMat then
        local m = LMatrix:new();
        MatrixMultiply(m, worldMat, ViewProjMat);
        TransTransform(v1, v0, m);
    else
        TransTransform(v1, v0, ViewProjMat);
    end

    local x = v1.x / v1.w * 0.5 + 0.5;
    local y = -v1.y / v1.w * 0.5 + 0.5;
    local z = v1.z / v1.w;
    r.x = x * viewportWidth;
    r.y = y * viewportHeight;
    r.z = z;
    return r;
end

---@param r LVec3
---@param v LVec3
---@param projMat LMatrix
---@param viewMat LMatrix
---@param worldMat | nil LMatrix
---@returns LVec3
function TransVec3ProjectScreen(r, v, projMat, viewMat, worldMat)
    local v0 = LVec4:new(v.x, v.y, v.z, 1.0);
    local v1 = LVec4:new();
    local m = LMatrix:new();
    if worldMat then
        MatrixMultiply(m, worldMat, viewMat);
        MatrixMultiply(m, m, projMat);
    else
        MatrixMultiply(m, viewMat, projMat);
    end
    TransTransform(v1, v0, m);
    r.x = v1.x / v1.w;
    r.y = v1.y / v1.w;
    r.z = v1.z / v1.w;
    return r;
end

---@param r LVec3
---@param v LVec3
---@param projMat LMatrix
---@param viewMat LMatrix
---@param worldMat | nil LMatrix
---@returns LVec3
function TransVec3ProjectScreenUV(r, v, projMat, viewMat, worldMat)
    local v0 = LVec4:new(v.x, v.y, v.z, 1.0);
    local v1 = LVec4:new();
    local m = LMatrix:new();
    if worldMat then
        MatrixMultiply(m, worldMat, viewMat);
        MatrixMultiply(m, m, projMat);
    else
        MatrixMultiply(m, viewMat, projMat);
    end
    TransTransform(v1, v0, m);

    r.x =(v1.x / v1.w) * 0.5 + 0.5;
    r.y =(v1.y / v1.w) * 0.5 + 0.5;
    r.z = v1.z / v1.w;
    return r;
end

---@param vOut LVec2
---@param vIn LVec2
---@param w number
---@param h number
---@return LVec3
function TransScreenPointToSurfacePoint(vOut, vIn, w, h)
    vOut.x = (vIn.x / w) * 2.0 - 1.0;
    vOut.y = -(vIn.y / h) * 2.0 + 1.0;
    return vOut;
end

---@param vOut LVec2
---@param vIn LVec2
---@param w number
---@param h number
---@return LVec3
function TransSurfacePointToScreenPoint(vOut, vIn, w, h)
    vOut.x =  (vIn.x + 1.0) / 2.0 * w;
    vOut.y = -(vIn.y - 1.0) / 2.0 * h;
    return vOut;
end
