-- glslfx version 0.1

//
// Copyright 2016 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
//    names, trademarks, service marks, or product names of the Licensor
//    and its affiliates, except as required to comply with Section 4(c) of
//    the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//

--- This is what an import might look like.
--- #import $TOOLS/hdSt/shaders/instancing.glslfx

--- --------------------------------------------------------------------------
-- glsl Instancing.Transform

// quaternion to matrix. xyz = imaginary, w = real
MAT4 GetRotationMatrix(vec4 q)
{
    MAT4 r;
    r[0].xyzw = vec4(1 - 2 * (q.y * q.y + q.z * q.z),
                         2 * (q.x * q.y + q.z * q.w),
                         2 * (q.x * q.z - q.y * q.w),
                     0);
    r[1].xyzw = vec4(    2 * (q.x * q.y - q.z * q.w),
                     1 - 2 * (q.x * q.x + q.z * q.z),
                         2 * (q.y * q.z + q.x * q.w),
                     0);
    r[2].xyzw = vec4(    2 * (q.x * q.z + q.y * q.w),
                         2 * (q.y * q.z - q.x * q.w),
                     1 - 2 * (q.x * q.x + q.y * q.y),
                     0);
    r[3] = vec4(0, 0, 0, 1);
    return r;
}

// ---------------------------------------------------------------------------

MAT4 GetInstanceTransform(int level)
{
    MAT4 m = MAT4(1);
#ifdef HD_HAS_INSTANCE_instanceTransform
    m = HdGetInstance_instanceTransform(level, MAT4(1)) * m;
#endif

    // instance transform elements are applied:
    //   scale then rotate then translate
    //   i.e. (T * R * S) * position

#ifdef HD_HAS_INSTANCE_scale
    vec3 s = HdGetInstance_scale(level, /*default=*/vec3(1));
    m = MAT4(s.x,   0,   0, 0,
               0, s.y,   0, 0,
               0,   0, s.z, 0,
               0,   0,   0, 1) * m;
#endif

#ifdef HD_HAS_INSTANCE_rotate // GfQuat(ix, iy, iz, real)
    vec4 q = HdGetInstance_rotate(level, /*default=*/vec4(0));
    m = GetRotationMatrix(q) * m;
#endif

#ifdef HD_HAS_INSTANCE_translate
    vec3 t = HdGetInstance_translate(level, /*default=*/vec3(0));
    m = MAT4(  1,   0,   0,  0,
               0,   1,   0,  0,
               0,   0,   1,  0,
             t.x, t.y, t.z,  1) * m;
#endif
    return m;
}

MAT4 GetInstanceTransformInverse(int level)
{
    MAT4 m = MAT4(1);

#ifdef HD_HAS_INSTANCE_instanceTransform
    m = inverse(HdGetInstance_instanceTransform(level, MAT4(1))) * m;
#endif

#ifdef HD_HAS_INSTANCE_translate
    vec3 it = -HdGetInstance_translate(level, /*default=*/vec3(0)); // negate
    m = MAT4(   1,    0,    0, 0,
                0,    1,    0, 0,
                0,    0,    1, 0,
             it.x, it.y, it.z, 1) * m;
#endif

#ifdef HD_HAS_INSTANCE_rotate
    vec4 q = HdGetInstance_rotate(level, /*default=*/vec4(0));
    q.xyz = -q.xyz; // inverse rotataion axis
    m = GetRotationMatrix(q) * m;
#endif

#ifdef HD_HAS_INSTANCE_scale
    vec3 is = 1.0/HdGetInstance_scale(level, /*default=*/vec3(1)); // inverse scale
    m = MAT4(is.x,    0,    0,  0,
                0, is.y,    0,  0,
                0,    0, is.z,  0,
                0,    0,    0,  1) * m;
#endif
    return m;
}

// ---------------------------------------------------------------------------

MAT4 GetInstanceTransform()
{
    MAT4 m = MAT4(1);
#ifdef HD_INSTANCER_NUM_LEVELS
    for (int i = 0; i < HD_INSTANCER_NUM_LEVELS; ++i) {
        m = GetInstanceTransform(i) * m;
#ifdef HD_HAS_instancerTransform
        m = HdGet_instancerTransform(i) * m;
#endif
    }
#endif
    return m;
}

MAT4 GetInstanceTransformInverse()
{
    MAT4 m = MAT4(1);
#ifdef HD_INSTANCER_NUM_LEVELS
    for (int i = 0; i < HD_INSTANCER_NUM_LEVELS; ++i) {
        m = m * GetInstanceTransformInverse(i);
#ifdef HD_HAS_instancerTransformInverse
        m = m * HdGet_instancerTransformInverse(i);
#endif
    }
#endif
    return m;
}

// ---------------------------------------------------------------------------

MAT4 ApplyInstanceTransform(MAT4 m)
{
    return GetInstanceTransform() * m;
}

MAT4 ApplyInstanceTransformInverse(MAT4 m)
{
    return m * GetInstanceTransformInverse();
}

bool IsFlipped()
{
#if defined(HD_HAS_isFlipped)
    bool flip = (HdGet_isFlipped() != 0);
#elif defined(HD_HAS_transform)
    // The sign of the determinant indicates whether m flips handedness
    bool flip = (determinant(HdGet_transform()) < 0.0);
#else
    bool flip = false;
#endif

#ifdef HD_HAS_INSTANCE_scale
    for (int i = 0; i < HD_INSTANCER_NUM_LEVELS; ++i) {
        vec3 scale = HdGetInstance_scale(i, /*default=*/vec3(1));
        flip = flip != ((sign(scale.x) * sign(scale.y) * sign(scale.z)) < 0);
    }
#endif

#ifdef HD_HAS_INSTANCE_instanceTransform
    for (int i = 0; i < HD_INSTANCER_NUM_LEVELS; ++i) {
        MAT4 m = HdGetInstance_instanceTransform(i, MAT4(1));
        flip = flip != (determinant(m) < 0.0);
    }
#endif

    return flip;
}
