/****************************************************************************
**
** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qenvironmentlight.h"
#include "qenvironmentlight_p.h"
#include "qabstracttexture.h"
#include <QVector3D>

#include <cmath>

QT_BEGIN_NAMESPACE

namespace Qt3DRender
{

/*!
 * \qmltype EnvironmentLight
 * \inqmlmodule Qt3D.Render
 * \instantiates Qt3DRender::QEnvironmentLight
 * \brief Encapsulate an environment light object in a Qt 3D scene.
 * \since 5.9
 *
 * EnvironmentLight uses cubemaps to implement image-based lighting (IBL), a technique
 * often used in conjunction with physically-based rendering (PBR). The cubemaps are
 * typically expected be based on high dynamic range (HDR) images, with a suitable
 * OpenGL format (such as RGBA16F) that can handle the increased range of values.
 *
 * There are a variety of tools that can be used to produce the cubemaps needed by
 * EnvironmentLight. Some examples include
 *
 * \list
 * \li \l {https://github.com/dariomanesku/cmftStudio}{cmftStudio}
 * \li \l {https://github.com/derkreature/IBLBaker}{IBLBaker}
 * \li \l {https://www.knaldtech.com/lys/}{Lys}
 * \endlist
 *
 * \l {https://hdrihaven.com/hdris/}{HDRI Haven} provides many CC0-licensed HDR images
 * that can be used as source material for the above tools.
 */

QEnvironmentLightPrivate::QEnvironmentLightPrivate()
    : m_shaderData(new QShaderData)
    , m_irradiance(nullptr)
    , m_specular(nullptr)
{
}

QEnvironmentLightPrivate::~QEnvironmentLightPrivate()
{
}

void QEnvironmentLightPrivate::_q_updateEnvMapsSize()
{
    QVector3D irradianceSize;
    if (m_irradiance != nullptr)
        irradianceSize = QVector3D(m_irradiance->width(),
                                   m_irradiance->height(),
                                   m_irradiance->depth());
    m_shaderData->setProperty("irradianceSize", QVariant::fromValue(irradianceSize));

    QVector3D specularSize;
    if (m_specular != nullptr)
        specularSize = QVector3D(m_specular->width(),
                                 m_specular->height(),
                                 m_specular->depth());
    m_shaderData->setProperty("specularSize", QVariant::fromValue(specularSize));

    const int levels = int(std::log2(specularSize.x() > 0.0f ? specularSize.x() : 1.0f)) + 1;
    m_shaderData->setProperty("specularMipLevels", QVariant::fromValue(levels));
}

Qt3DCore::QNodeCreatedChangeBasePtr QEnvironmentLight::createNodeCreationChange() const
{
    auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QEnvironmentLightData>::create(this);
    auto &data = creationChange->data;
    Q_D(const QEnvironmentLight);
    data.shaderDataId = qIdForNode(d->m_shaderData);
    return creationChange;
}

/*!
    \class Qt3DRender::QEnvironmentLight
    \inmodule Qt3DRender
    \brief Encapsulate an environment light object in a Qt 3D scene.
    \since 5.9

    QEnvironmentLight uses cubemaps to implement image-based lighting (IBL), a technique
    often used in conjunction with physically-based rendering (PBR). The cubemaps are
    typically expected be based on high dynamic range (HDR) images, with a suitable
    OpenGL format (such as RGBA16F) that can handle the increased range of values.

    There are a variety of tools that can be used to produce the cubemaps needed by
    QEnvironmentLight. Some examples include

    \list
    \li \l {https://github.com/dariomanesku/cmftStudio}{cmftStudio}
    \li \l {https://github.com/derkreature/IBLBaker}{IBLBaker}
    \li \l {https://www.knaldtech.com/lys/}{Lys}
    \endlist

    \l {https://hdrihaven.com/hdris/}{HDRI Haven} provides many CC0-licensed HDR images
    that can be used as source material for the above tools.
*/

QEnvironmentLight::QEnvironmentLight(Qt3DCore::QNode *parent)
    : QComponent(*new QEnvironmentLightPrivate, parent)
{
    Q_D(QEnvironmentLight);
    d->m_shaderData->setParent(this);
}

/*! \internal */
QEnvironmentLight::QEnvironmentLight(QEnvironmentLightPrivate &dd, QNode *parent)
    : QComponent(dd, parent)
{
    Q_D(QEnvironmentLight);
    d->m_shaderData->setParent(this);
}

QEnvironmentLight::~QEnvironmentLight()
{
}

/*!
    \qmlproperty Texture EnvironmentLight::irradiance

    Holds the current environment irradiance map texture.

    By default, the environment irradiance texture is null.

    \note The exact meaning and use of this property is up to the
          material implementation.
*/

/*!
    \property QEnvironmentLight::irradiance

    Holds the current environment irradiance map texture.

    By default, the environment irradiance texture is null.

    \note The exact meaning and use of this property is up to the
          material implementation.
*/
QAbstractTexture *QEnvironmentLight::irradiance() const
{
    Q_D(const QEnvironmentLight);
    return d->m_irradiance;
}

/*!
    \qmlproperty Texture EnvironmentLight::specular

    Holds the current environment specular map texture.

    By default, the environment specular texture is null.

    \note The exact meaning and use of this property is up to the
          material implementation.
*/

/*!
    \property QEnvironmentLight::specular

    Holds the current environment specular map texture.

    By default, the environment specular texture is null.

    \note The exact meaning and use of this property is up to the
          material implementation.
*/
QAbstractTexture *QEnvironmentLight::specular() const
{
    Q_D(const QEnvironmentLight);
    return d->m_specular;
}

void QEnvironmentLight::setIrradiance(QAbstractTexture *i)
{
    Q_D(QEnvironmentLight);
    if (irradiance() == i)
        return;

    if (irradiance()) {
        d->unregisterDestructionHelper(d->m_irradiance);
        QObject::disconnect(d->m_irradiance, SIGNAL(widthChanged(int)), this, SLOT(_q_updateEnvMapsSize()));
        QObject::disconnect(d->m_irradiance, SIGNAL(heightChanged(int)), this, SLOT(_q_updateEnvMapsSize()));
        QObject::disconnect(d->m_irradiance, SIGNAL(depthChanged(int)), this, SLOT(_q_updateEnvMapsSize()));
    }

    if (i && !i->parent())
        i->setParent(this);

    d->m_irradiance = i;
    d->m_shaderData->setProperty("irradiance", QVariant::fromValue(i));
    d->_q_updateEnvMapsSize();

    if (i) {
        d->registerDestructionHelper(i, &QEnvironmentLight::setIrradiance, i);
        QObject::connect(d->m_irradiance, SIGNAL(widthChanged(int)), this, SLOT(_q_updateEnvMapsSize()));
        QObject::connect(d->m_irradiance, SIGNAL(heightChanged(int)), this, SLOT(_q_updateEnvMapsSize()));
        QObject::connect(d->m_irradiance, SIGNAL(depthChanged(int)), this, SLOT(_q_updateEnvMapsSize()));
    }

    emit irradianceChanged(i);
}

void QEnvironmentLight::setSpecular(QAbstractTexture *s)
{
    Q_D(QEnvironmentLight);
    if (specular() == s)
        return;

    if (specular()) {
        d->unregisterDestructionHelper(d->m_specular);
        QObject::disconnect(d->m_specular, SIGNAL(widthChanged(int)), this, SLOT(_q_updateEnvMapsSize()));
        QObject::disconnect(d->m_specular, SIGNAL(heightChanged(int)), this, SLOT(_q_updateEnvMapsSize()));
        QObject::disconnect(d->m_specular, SIGNAL(depthChanged(int)), this, SLOT(_q_updateEnvMapsSize()));
    }

    if (s && !s->parent())
        s->setParent(this);

    d->m_specular = s;
    d->m_shaderData->setProperty("specular", QVariant::fromValue(s));
    d->_q_updateEnvMapsSize();

    if (s) {
        d->registerDestructionHelper(s, &QEnvironmentLight::setSpecular, s);
        QObject::connect(d->m_specular, SIGNAL(widthChanged(int)), this, SLOT(_q_updateEnvMapsSize()));
        QObject::connect(d->m_specular, SIGNAL(heightChanged(int)), this, SLOT(_q_updateEnvMapsSize()));
        QObject::connect(d->m_specular, SIGNAL(depthChanged(int)), this, SLOT(_q_updateEnvMapsSize()));
    }

    emit specularChanged(s);
}

} // namespace Qt3DRender

QT_END_NAMESPACE

#include "moc_qenvironmentlight.cpp"
