blob: 6b34f3719cec72aa6094d984279dbef84dd6e5df [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2014 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 "qt3dquick_global_p.h"
#include <Qt3DQuick/private/qt3dquicknodefactory_p.h>
#include <Qt3DQuick/private/qt3dquickvaluetypes_p.h>
#include <QtQml/private/qqmlglobal_p.h>
#include <QtQml/private/qv4engine_p.h>
#include <QtQml/private/qv4object_p.h>
QT_BEGIN_NAMESPACE
namespace Qt3DCore {
namespace Quick {
class Quick3DColorProvider : public QQmlColorProvider
{
public:
QVariant colorFromString(const QString &s, bool *ok) override
{
QColor c(s);
if (c.isValid()) {
if (ok) *ok = true;
return QVariant(c);
}
if (ok) *ok = false;
return QVariant();
}
unsigned rgbaFromString(const QString &s, bool *ok) override
{
QColor c(s);
if (c.isValid()) {
if (ok) *ok = true;
return c.rgba();
}
if (ok) *ok = false;
return 0;
}
QString stringFromRgba(unsigned rgba)
{
QColor c(QColor::fromRgba(rgba));
if (c.isValid()) {
return QVariant(c).toString();
}
return QString();
}
QVariant fromRgbF(double r, double g, double b, double a) override
{
return QVariant(QColor::fromRgbF(r, g, b, a));
}
QVariant fromHslF(double h, double s, double l, double a) override
{
return QVariant(QColor::fromHslF(h, s, l, a));
}
QVariant fromHsvF(double h, double s, double v, double a) override
{
return QVariant(QColor::fromHsvF(h, s, v, a));
}
QVariant lighter(const QVariant &var, qreal factor) override
{
QColor color = var.value<QColor>();
color = color.lighter(int(qRound(factor*100.)));
return QVariant::fromValue(color);
}
QVariant darker(const QVariant &var, qreal factor) override
{
QColor color = var.value<QColor>();
color = color.darker(int(qRound(factor*100.)));
return QVariant::fromValue(color);
}
QVariant tint(const QVariant &baseVar, const QVariant &tintVar) override
{
QColor tintColor = tintVar.value<QColor>();
int tintAlpha = tintColor.alpha();
if (tintAlpha == 0xFF) {
return tintVar;
} else if (tintAlpha == 0x00) {
return baseVar;
}
// tint the base color and return the final color
QColor baseColor = baseVar.value<QColor>();
qreal a = tintColor.alphaF();
qreal inv_a = 1.0 - a;
qreal r = tintColor.redF() * a + baseColor.redF() * inv_a;
qreal g = tintColor.greenF() * a + baseColor.greenF() * inv_a;
qreal b = tintColor.blueF() * a + baseColor.blueF() * inv_a;
return QVariant::fromValue(QColor::fromRgbF(r, g, b, a + inv_a * baseColor.alphaF()));
}
};
// Note: The functions in this class provide handling only for the types
// that the QML engine will currently actually call them for, so many
// appear incompletely implemented. For some functions, the implementation
// would be obvious, but for others (particularly create and createFromString)
// the exact semantics are unknown. For this reason unused functionality
// has been omitted.
class Quick3DValueTypeProvider : public QQmlValueTypeProvider
{
public:
#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
#define ASSERT_VALID_SIZE(size, min) Q_UNUSED(size)
#else
#define ASSERT_VALID_SIZE(size, min) Q_ASSERT(size >= min)
#endif
static QVector2D vector2DFromString(const QString &s, bool *ok)
{
if (s.count(QLatin1Char(',')) == 1) {
int index = s.indexOf(QLatin1Char(','));
bool xGood, yGood;
float xCoord = s.leftRef(index).toFloat(&xGood);
float yCoord = s.midRef(index+1).toFloat(&yGood);
if (xGood && yGood) {
if (ok) *ok = true;
return QVector2D(xCoord, yCoord);
}
}
if (ok) *ok = false;
return QVector2D();
}
static QVector3D vector3DFromString(const QString &s, bool *ok)
{
if (s.count(QLatin1Char(',')) == 2) {
int index = s.indexOf(QLatin1Char(','));
int index2 = s.indexOf(QLatin1Char(','), index+1);
bool xGood, yGood, zGood;
float xCoord = s.leftRef(index).toFloat(&xGood);
float yCoord = s.midRef(index+1, index2-index-1).toFloat(&yGood);
float zCoord = s.midRef(index2+1).toFloat(&zGood);
if (xGood && yGood && zGood) {
if (ok) *ok = true;
return QVector3D(xCoord, yCoord, zCoord);
}
}
if (ok) *ok = false;
return QVector3D();
}
static QVector4D vector4DFromString(const QString &s, bool *ok)
{
if (s.count(QLatin1Char(',')) == 3) {
int index = s.indexOf(QLatin1Char(','));
int index2 = s.indexOf(QLatin1Char(','), index+1);
int index3 = s.indexOf(QLatin1Char(','), index2+1);
bool xGood, yGood, zGood, wGood;
float xCoord = s.leftRef(index).toFloat(&xGood);
float yCoord = s.midRef(index+1, index2-index-1).toFloat(&yGood);
float zCoord = s.midRef(index2+1, index3-index2-1).toFloat(&zGood);
float wCoord = s.midRef(index3+1).toFloat(&wGood);
if (xGood && yGood && zGood && wGood) {
if (ok) *ok = true;
return QVector4D(xCoord, yCoord, zCoord, wCoord);
}
}
if (ok) *ok = false;
return QVector4D();
}
static QQuaternion quaternionFromString(const QString &s, bool *ok)
{
if (s.count(QLatin1Char(',')) == 3) {
int index = s.indexOf(QLatin1Char(','));
int index2 = s.indexOf(QLatin1Char(','), index+1);
int index3 = s.indexOf(QLatin1Char(','), index2+1);
bool sGood, xGood, yGood, zGood;
qreal sCoord = s.leftRef(index).toDouble(&sGood);
qreal xCoord = s.midRef(index+1, index2-index-1).toDouble(&xGood);
qreal yCoord = s.midRef(index2+1, index3-index2-1).toDouble(&yGood);
qreal zCoord = s.midRef(index3+1).toDouble(&zGood);
if (sGood && xGood && yGood && zGood) {
if (ok) *ok = true;
return QQuaternion(sCoord, xCoord, yCoord, zCoord);
}
}
if (ok) *ok = false;
return QQuaternion();
}
static QMatrix4x4 matrix4x4FromString(const QString &s, bool *ok)
{
if (s.count(QLatin1Char(',')) == 15) {
float matValues[16];
bool vOK = true;
QStringRef mutableStr(&s);
for (int i = 0; vOK && i < 16; ++i) {
int cidx = mutableStr.indexOf(QLatin1Char(','));
matValues[i] = mutableStr.left(cidx).toDouble(&vOK);
mutableStr = mutableStr.mid(cidx + 1);
}
if (vOK) {
if (ok) *ok = true;
return QMatrix4x4(matValues);
}
}
if (ok) *ok = false;
return QMatrix4x4();
}
static QMatrix4x4 matrix4x4FromObject(const QV4::Value &object, QV4::ExecutionEngine *v4, bool *ok)
{
if (ok) *ok = false;
QV4::Scope scope(v4);
QV4::ScopedArrayObject array(scope, object);
if (!array)
return QMatrix4x4();
if (array->getLength() != 16)
return QMatrix4x4();
float matVals[16];
QV4::ScopedValue v(scope);
for (quint32 i = 0; i < 16; ++i) {
v = array->get(i);
if (!v->isNumber())
return QMatrix4x4();
matVals[i] = v->asDouble();
}
if (ok) *ok = true;
return QMatrix4x4(matVals);
}
const QMetaObject *getMetaObjectForMetaType(int type) override
{
switch (type) {
case QMetaType::QColor:
return &Quick3DColorValueType::staticMetaObject;
case QMetaType::QVector2D:
return &Quick3DVector2DValueType::staticMetaObject;
case QMetaType::QVector3D:
return &Quick3DVector3DValueType::staticMetaObject;
case QMetaType::QVector4D:
return &Quick3DVector4DValueType::staticMetaObject;
case QMetaType::QQuaternion:
return &Quick3DQuaternionValueType::staticMetaObject;
case QMetaType::QMatrix4x4:
return &Quick3DMatrix4x4ValueType::staticMetaObject;
default:
break;
}
return nullptr;
}
bool init(int type, QVariant& dst) override
{
switch (type) {
case QMetaType::QColor:
dst.setValue<QColor>(QColor());
return true;
case QMetaType::QVector2D:
dst.setValue<QVector2D>(QVector2D());
return true;
case QMetaType::QVector3D:
dst.setValue<QVector3D>(QVector3D());
return true;
case QMetaType::QVector4D:
dst.setValue<QVector4D>(QVector4D());
return true;
case QMetaType::QQuaternion:
dst.setValue<QQuaternion>(QQuaternion());
return true;
case QMetaType::QMatrix4x4:
dst.setValue<QMatrix4x4>(QMatrix4x4());
return true;
default: break;
}
return false;
}
bool create(int type, int argc, const void *argv[], QVariant *v) override
{
switch (type) {
case QMetaType::QVector2D:
if (argc == 1) {
const float *xy = reinterpret_cast<const float*>(argv[0]);
QVector2D v2(xy[0], xy[1]);
*v = QVariant(v2);
return true;
}
break;
case QMetaType::QVector3D:
if (argc == 1) {
const float *xyz = reinterpret_cast<const float*>(argv[0]);
QVector3D v3(xyz[0], xyz[1], xyz[2]);
*v = QVariant(v3);
return true;
}
break;
case QMetaType::QVector4D:
if (argc == 1) {
const float *xyzw = reinterpret_cast<const float*>(argv[0]);
QVector4D v4(xyzw[0], xyzw[1], xyzw[2], xyzw[3]);
*v = QVariant(v4);
return true;
}
break;
case QMetaType::QQuaternion:
if (argc == 1) {
const qreal *sxyz = reinterpret_cast<const qreal*>(argv[0]);
QQuaternion q(sxyz[0], sxyz[1], sxyz[2], sxyz[3]);
*v = QVariant(q);
return true;
}
break;
case QMetaType::QMatrix4x4:
if (argc == 0) {
QMatrix4x4 m;
*v = QVariant(m);
return true;
} else if (argc == 1) {
const qreal *vals = reinterpret_cast<const qreal*>(argv[0]);
QMatrix4x4 m(vals[0], vals[1], vals[2], vals[3],
vals[4], vals[5], vals[6], vals[7],
vals[8], vals[9], vals[10], vals[11],
vals[12], vals[13], vals[14], vals[15]);
*v = QVariant(m);
return true;
}
break;
default: break;
}
return false;
}
template<typename T>
bool createFromStringTyped(void *data, size_t dataSize, T initValue)
{
ASSERT_VALID_SIZE(dataSize, sizeof(T));
T *t = reinterpret_cast<T *>(data);
new (t) T(initValue);
return true;
}
bool createFromString(int type, const QString &s, void *data, size_t dataSize) override
{
bool ok = false;
switch (type) {
case QMetaType::QColor:
return createFromStringTyped<QColor>(data, dataSize, QColor(s));
case QMetaType::QVector2D:
return createFromStringTyped<QVector2D>(data, dataSize, vector2DFromString(s, &ok));
case QMetaType::QVector3D:
return createFromStringTyped<QVector3D>(data, dataSize, vector3DFromString(s, &ok));
case QMetaType::QVector4D:
return createFromStringTyped<QVector4D>(data, dataSize, vector4DFromString(s, &ok));
case QMetaType::QQuaternion:
return createFromStringTyped<QQuaternion>(data, dataSize, quaternionFromString(s, &ok));
case QMetaType::QMatrix4x4:
return createFromStringTyped<QMatrix4x4>(data, dataSize, matrix4x4FromString(s, &ok));
default: break;
}
return false;
}
bool createStringFrom(int type, const void *data, QString *s) override
{
if (type == QMetaType::QColor) {
const QColor *color = reinterpret_cast<const QColor *>(data);
new (s) QString(QVariant(*color).toString());
return true;
}
return false;
}
bool variantFromString(const QString &s, QVariant *v) override
{
QColor c(s);
if (c.isValid()) {
*v = QVariant::fromValue(c);
return true;
}
bool ok = false;
QVector2D v2 = vector2DFromString(s, &ok);
if (ok) {
*v = QVariant::fromValue(v2);
return true;
}
QVector3D v3 = vector3DFromString(s, &ok);
if (ok) {
*v = QVariant::fromValue(v3);
return true;
}
QVector4D v4 = vector4DFromString(s, &ok);
if (ok) {
*v = QVariant::fromValue(v4);
return true;
}
QQuaternion q = quaternionFromString(s, &ok);
if (ok) {
*v = QVariant::fromValue(q);
return true;
}
QMatrix4x4 m = matrix4x4FromString(s, &ok);
if (ok) {
*v = QVariant::fromValue(m);
return true;
}
return false;
}
bool variantFromString(int type, const QString &s, QVariant *v) override
{
bool ok = false;
switch (type) {
case QMetaType::QColor:
{
QColor c(s);
*v = QVariant::fromValue(c);
return true;
}
case QMetaType::QVector2D:
{
*v = QVariant::fromValue(vector2DFromString(s, &ok));
return true;
}
case QMetaType::QVector3D:
{
*v = QVariant::fromValue(vector3DFromString(s, &ok));
return true;
}
case QMetaType::QVector4D:
{
*v = QVariant::fromValue(vector4DFromString(s, &ok));
return true;
}
case QMetaType::QQuaternion:
{
*v = QVariant::fromValue(quaternionFromString(s, &ok));
return true;
}
case QMetaType::QMatrix4x4:
{
*v = QVariant::fromValue(matrix4x4FromString(s, &ok));
return true;
}
default:
break;
}
return false;
}
bool variantFromJsObject(int type, const QV4::Value &object, QV4::ExecutionEngine *v4, QVariant *v) override
{
QV4::Scope scope(v4);
#ifndef QT_NO_DEBUG
QV4::ScopedObject obj(scope, object);
Q_ASSERT(obj);
#endif
bool ok = false;
switch (type) {
case QMetaType::QMatrix4x4:
*v = QVariant::fromValue(matrix4x4FromObject(object, v4, &ok));
default: break;
}
return ok;
}
template<typename T>
bool typedEqual(const void *lhs, const QVariant& rhs)
{
return (*(reinterpret_cast<const T *>(lhs)) == rhs.value<T>());
}
bool equal(int type, const void *lhs, const QVariant &rhs) override
{
switch (type) {
case QMetaType::QColor:
return typedEqual<QColor>(lhs, rhs);
case QMetaType::QVector2D:
return typedEqual<QVector2D>(lhs, rhs);
case QMetaType::QVector3D:
return typedEqual<QVector3D>(lhs, rhs);
case QMetaType::QVector4D:
return typedEqual<QVector4D>(lhs, rhs);
case QMetaType::QQuaternion:
return typedEqual<QQuaternion>(lhs, rhs);
case QMetaType::QMatrix4x4:
return typedEqual<QMatrix4x4>(lhs, rhs);
default: break;
}
return false;
}
template<typename T>
bool typedStore(const void *src, void *dst, size_t dstSize)
{
ASSERT_VALID_SIZE(dstSize, sizeof(T));
const T *srcT = reinterpret_cast<const T *>(src);
T *dstT = reinterpret_cast<T *>(dst);
new (dstT) T(*srcT);
return true;
}
bool store(int type, const void *src, void *dst, size_t dstSize) override
{
switch (type) {
case QMetaType::QColor:
{
Q_ASSERT(dstSize >= sizeof(QColor));
Q_UNUSED(dstSize);
const QRgb *rgb = reinterpret_cast<const QRgb *>(src);
QColor *color = reinterpret_cast<QColor *>(dst);
new (color) QColor(QColor::fromRgba(*rgb));
return true;
}
default: break;
}
return false;
}
template<typename T>
bool typedRead(const QVariant& src, int dstType, void *dst)
{
T *dstT = reinterpret_cast<T *>(dst);
if (src.type() == QVariant::Type(dstType)) {
*dstT = src.value<T>();
} else {
*dstT = T();
}
return true;
}
bool read(const QVariant &src, void *dst, int dstType) override
{
switch (dstType) {
case QMetaType::QColor:
return typedRead<QColor>(src, dstType, dst);
case QMetaType::QVector2D:
return typedRead<QVector2D>(src, dstType, dst);
case QMetaType::QVector3D:
return typedRead<QVector3D>(src, dstType, dst);
case QMetaType::QVector4D:
return typedRead<QVector4D>(src, dstType, dst);
case QMetaType::QQuaternion:
return typedRead<QQuaternion>(src, dstType, dst);
case QMetaType::QMatrix4x4:
return typedRead<QMatrix4x4>(src, dstType, dst);
default: break;
}
return false;
}
template<typename T>
bool typedWrite(const void *src, QVariant& dst)
{
const T *srcT = reinterpret_cast<const T *>(src);
if (dst.value<T>() != *srcT) {
dst = *srcT;
return true;
}
return false;
}
bool write(int type, const void *src, QVariant& dst) override
{
switch (type) {
case QMetaType::QColor:
return typedWrite<QColor>(src, dst);
case QMetaType::QVector2D:
return typedWrite<QVector2D>(src, dst);
case QMetaType::QVector3D:
return typedWrite<QVector3D>(src, dst);
case QMetaType::QVector4D:
return typedWrite<QVector4D>(src, dst);
case QMetaType::QQuaternion:
return typedWrite<QQuaternion>(src, dst);
case QMetaType::QMatrix4x4:
return typedWrite<QMatrix4x4>(src, dst);
default: break;
}
return false;
}
#undef ASSERT_VALID_SIZE
};
Quick3DValueTypeProvider *valueTypeProvider = nullptr;
static Quick3DValueTypeProvider *getValueTypeProvider()
{
if (valueTypeProvider == nullptr)
valueTypeProvider = new Quick3DValueTypeProvider();
return valueTypeProvider;
}
static Quick3DColorProvider *getColorProvider()
{
static Quick3DColorProvider colorProvider;
return &colorProvider;
}
static QQmlPrivate::AutoParentResult qquick3ditem_autoParent(QObject *obj, QObject *parent)
{
// When setting a parent (especially during dynamic object creation) in QML,
// also try to set up the analogous item/window relationship.
auto parentNode = qmlobject_cast<Qt3DCore::QNode *>(parent);
if (parentNode) {
auto node = qmlobject_cast<Qt3DCore::QNode *>(obj);
if (node) {
// A QNode has another QNode child
node->setParent(parentNode);
return QQmlPrivate::Parented;
}
} else {
return QQmlPrivate::IncompatibleParent;
}
return QQmlPrivate::IncompatibleObject;
}
void Quick3D_initialize()
{
Qt3DCore::Quick::Quick3DValueTypes::registerValueTypes();
QQml_addValueTypeProvider(getValueTypeProvider());
QQml_setColorProvider(getColorProvider());
QAbstractNodeFactory::registerNodeFactory(QuickNodeFactory::instance());
// Register a hook called when we do component.create() that sets the
// parent. We need this as QObject::setParent() is insufficient to propagate
// the arbiter and scene to the children (see QNode::setParent(QNode *).
// TODO: Replace this with virtual void QObjectPrivate::setParent(QObject *)
// that can be called from QObject ctor and QObject::setParent(). That would
// allow removal of this hook here and in QtQuick.
QQmlPrivate::RegisterAutoParent autoparent = { 0, &qquick3ditem_autoParent };
QQmlPrivate::qmlregister(QQmlPrivate::AutoParentRegistration, &autoparent);
}
void Quick3D_uninitialize()
{
delete valueTypeProvider;
valueTypeProvider = nullptr;
}
void Quick3D_registerType(const char *className, const char *quickName, int major, int minor)
{
QuickNodeFactory::instance()->registerType(className, quickName, major, minor);
}
} // namespace Quick
} // namespace Qt3DCore
QT_END_NAMESPACE