1/****************************************************************************
2**
3** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qt3dquick_global_p.h"
41
42#include <Qt3DQuick/private/qt3dquicknodefactory_p.h>
43#include <Qt3DQuick/private/qt3dquickvaluetypes_p.h>
44#include <QtQml/private/qqmlglobal_p.h>
45#include <QtQml/private/qv4engine_p.h>
46#include <QtQml/private/qv4object_p.h>
47
48QT_BEGIN_NAMESPACE
49
50namespace Qt3DCore {
51namespace Quick {
52
53class Quick3DColorProvider : public QQmlColorProvider
54{
55public:
56 QVariant colorFromString(const QString &s, bool *ok) override
57 {
58 QColor c(s);
59 if (c.isValid()) {
60 if (ok) *ok = true;
61 return QVariant(c);
62 }
63
64 if (ok) *ok = false;
65 return QVariant();
66 }
67
68 unsigned rgbaFromString(const QString &s, bool *ok) override
69 {
70 QColor c(s);
71 if (c.isValid()) {
72 if (ok) *ok = true;
73 return c.rgba();
74 }
75
76 if (ok) *ok = false;
77 return 0;
78 }
79
80 QString stringFromRgba(unsigned rgba)
81 {
82 QColor c(QColor::fromRgba(rgba));
83 if (c.isValid()) {
84 return QVariant(c).toString();
85 }
86
87 return QString();
88 }
89
90 QVariant fromRgbF(double r, double g, double b, double a) override
91 {
92 return QVariant(QColor::fromRgbF(r, g, b, a));
93 }
94
95 QVariant fromHslF(double h, double s, double l, double a) override
96 {
97 return QVariant(QColor::fromHslF(h, s, l, a));
98 }
99
100 QVariant fromHsvF(double h, double s, double v, double a) override
101 {
102 return QVariant(QColor::fromHsvF(h, s, v, a));
103 }
104
105 QVariant lighter(const QVariant &var, qreal factor) override
106 {
107 QColor color = var.value<QColor>();
108 color = color.lighter(int(qRound(factor*100.)));
109 return QVariant::fromValue(color);
110 }
111
112 QVariant darker(const QVariant &var, qreal factor) override
113 {
114 QColor color = var.value<QColor>();
115 color = color.darker(int(qRound(factor*100.)));
116 return QVariant::fromValue(color);
117 }
118
119 QVariant tint(const QVariant &baseVar, const QVariant &tintVar) override
120 {
121 QColor tintColor = tintVar.value<QColor>();
122
123 int tintAlpha = tintColor.alpha();
124 if (tintAlpha == 0xFF) {
125 return tintVar;
126 } else if (tintAlpha == 0x00) {
127 return baseVar;
128 }
129
130 // tint the base color and return the final color
131 QColor baseColor = baseVar.value<QColor>();
132 qreal a = tintColor.alphaF();
133 qreal inv_a = 1.0 - a;
134
135 qreal r = tintColor.redF() * a + baseColor.redF() * inv_a;
136 qreal g = tintColor.greenF() * a + baseColor.greenF() * inv_a;
137 qreal b = tintColor.blueF() * a + baseColor.blueF() * inv_a;
138
139 return QVariant::fromValue(QColor::fromRgbF(r, g, b, a + inv_a * baseColor.alphaF()));
140 }
141};
142
143
144// Note: The functions in this class provide handling only for the types
145// that the QML engine will currently actually call them for, so many
146// appear incompletely implemented. For some functions, the implementation
147// would be obvious, but for others (particularly create and createFromString)
148// the exact semantics are unknown. For this reason unused functionality
149// has been omitted.
150
151class Quick3DValueTypeProvider : public QQmlValueTypeProvider
152{
153public:
154
155#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
156 #define ASSERT_VALID_SIZE(size, min) Q_UNUSED(size)
157#else
158 #define ASSERT_VALID_SIZE(size, min) Q_ASSERT(size >= min)
159#endif
160
161 static QVector2D vector2DFromString(const QString &s, bool *ok)
162 {
163 if (s.count(QLatin1Char(',')) == 1) {
164 int index = s.indexOf(QLatin1Char(','));
165
166 bool xGood, yGood;
167 float xCoord = s.leftRef(index).toFloat(&xGood);
168 float yCoord = s.midRef(index+1).toFloat(&yGood);
169
170 if (xGood && yGood) {
171 if (ok) *ok = true;
172 return QVector2D(xCoord, yCoord);
173 }
174 }
175
176 if (ok) *ok = false;
177 return QVector2D();
178 }
179
180 static QVector3D vector3DFromString(const QString &s, bool *ok)
181 {
182 if (s.count(QLatin1Char(',')) == 2) {
183 int index = s.indexOf(QLatin1Char(','));
184 int index2 = s.indexOf(QLatin1Char(','), index+1);
185
186 bool xGood, yGood, zGood;
187 float xCoord = s.leftRef(index).toFloat(&xGood);
188 float yCoord = s.midRef(index+1, index2-index-1).toFloat(&yGood);
189 float zCoord = s.midRef(index2+1).toFloat(&zGood);
190
191 if (xGood && yGood && zGood) {
192 if (ok) *ok = true;
193 return QVector3D(xCoord, yCoord, zCoord);
194 }
195 }
196
197 if (ok) *ok = false;
198 return QVector3D();
199 }
200
201 static QVector4D vector4DFromString(const QString &s, bool *ok)
202 {
203 if (s.count(QLatin1Char(',')) == 3) {
204 int index = s.indexOf(QLatin1Char(','));
205 int index2 = s.indexOf(QLatin1Char(','), index+1);
206 int index3 = s.indexOf(QLatin1Char(','), index2+1);
207
208 bool xGood, yGood, zGood, wGood;
209 float xCoord = s.leftRef(index).toFloat(&xGood);
210 float yCoord = s.midRef(index+1, index2-index-1).toFloat(&yGood);
211 float zCoord = s.midRef(index2+1, index3-index2-1).toFloat(&zGood);
212 float wCoord = s.midRef(index3+1).toFloat(&wGood);
213
214 if (xGood && yGood && zGood && wGood) {
215 if (ok) *ok = true;
216 return QVector4D(xCoord, yCoord, zCoord, wCoord);
217 }
218 }
219
220 if (ok) *ok = false;
221 return QVector4D();
222 }
223
224 static QQuaternion quaternionFromString(const QString &s, bool *ok)
225 {
226 if (s.count(QLatin1Char(',')) == 3) {
227 int index = s.indexOf(QLatin1Char(','));
228 int index2 = s.indexOf(QLatin1Char(','), index+1);
229 int index3 = s.indexOf(QLatin1Char(','), index2+1);
230
231 bool sGood, xGood, yGood, zGood;
232 qreal sCoord = s.leftRef(index).toDouble(&sGood);
233 qreal xCoord = s.midRef(index+1, index2-index-1).toDouble(&xGood);
234 qreal yCoord = s.midRef(index2+1, index3-index2-1).toDouble(&yGood);
235 qreal zCoord = s.midRef(index3+1).toDouble(&zGood);
236
237 if (sGood && xGood && yGood && zGood) {
238 if (ok) *ok = true;
239 return QQuaternion(sCoord, xCoord, yCoord, zCoord);
240 }
241 }
242
243 if (ok) *ok = false;
244 return QQuaternion();
245 }
246
247 static QMatrix4x4 matrix4x4FromString(const QString &s, bool *ok)
248 {
249 if (s.count(QLatin1Char(',')) == 15) {
250 float matValues[16];
251 bool vOK = true;
252 QStringRef mutableStr(&s);
253 for (int i = 0; vOK && i < 16; ++i) {
254 int cidx = mutableStr.indexOf(QLatin1Char(','));
255 matValues[i] = mutableStr.left(cidx).toDouble(&vOK);
256 mutableStr = mutableStr.mid(cidx + 1);
257 }
258
259 if (vOK) {
260 if (ok) *ok = true;
261 return QMatrix4x4(matValues);
262 }
263 }
264
265 if (ok) *ok = false;
266 return QMatrix4x4();
267 }
268
269 static QMatrix4x4 matrix4x4FromObject(const QV4::Value &object, QV4::ExecutionEngine *v4, bool *ok)
270 {
271 if (ok) *ok = false;
272 QV4::Scope scope(v4);
273 QV4::ScopedArrayObject array(scope, object);
274 if (!array)
275 return QMatrix4x4();
276
277 if (array->getLength() != 16)
278 return QMatrix4x4();
279
280 float matVals[16];
281 QV4::ScopedValue v(scope);
282 for (quint32 i = 0; i < 16; ++i) {
283 v = array->get(i);
284 if (!v->isNumber())
285 return QMatrix4x4();
286 matVals[i] = v->asDouble();
287 }
288
289 if (ok) *ok = true;
290 return QMatrix4x4(matVals);
291 }
292
293 const QMetaObject *getMetaObjectForMetaType(int type) override
294 {
295 switch (type) {
296 case QMetaType::QColor:
297 return &Quick3DColorValueType::staticMetaObject;
298 case QMetaType::QVector2D:
299 return &Quick3DVector2DValueType::staticMetaObject;
300 case QMetaType::QVector3D:
301 return &Quick3DVector3DValueType::staticMetaObject;
302 case QMetaType::QVector4D:
303 return &Quick3DVector4DValueType::staticMetaObject;
304 case QMetaType::QQuaternion:
305 return &Quick3DQuaternionValueType::staticMetaObject;
306 case QMetaType::QMatrix4x4:
307 return &Quick3DMatrix4x4ValueType::staticMetaObject;
308 default:
309 break;
310 }
311
312 return nullptr;
313 }
314
315 bool init(int type, QVariant& dst) override
316 {
317 switch (type) {
318 case QMetaType::QColor:
319 dst.setValue<QColor>(QColor());
320 return true;
321 case QMetaType::QVector2D:
322 dst.setValue<QVector2D>(QVector2D());
323 return true;
324 case QMetaType::QVector3D:
325 dst.setValue<QVector3D>(QVector3D());
326 return true;
327 case QMetaType::QVector4D:
328 dst.setValue<QVector4D>(QVector4D());
329 return true;
330 case QMetaType::QQuaternion:
331 dst.setValue<QQuaternion>(QQuaternion());
332 return true;
333 case QMetaType::QMatrix4x4:
334 dst.setValue<QMatrix4x4>(QMatrix4x4());
335 return true;
336 default: break;
337 }
338
339 return false;
340 }
341
342 bool create(int type, int argc, const void *argv[], QVariant *v) override
343 {
344 switch (type) {
345 case QMetaType::QVector2D:
346 if (argc == 1) {
347 const float *xy = reinterpret_cast<const float*>(argv[0]);
348 QVector2D v2(xy[0], xy[1]);
349 *v = QVariant(v2);
350 return true;
351 }
352 break;
353 case QMetaType::QVector3D:
354 if (argc == 1) {
355 const float *xyz = reinterpret_cast<const float*>(argv[0]);
356 QVector3D v3(xyz[0], xyz[1], xyz[2]);
357 *v = QVariant(v3);
358 return true;
359 }
360 break;
361 case QMetaType::QVector4D:
362 if (argc == 1) {
363 const float *xyzw = reinterpret_cast<const float*>(argv[0]);
364 QVector4D v4(xyzw[0], xyzw[1], xyzw[2], xyzw[3]);
365 *v = QVariant(v4);
366 return true;
367 }
368 break;
369 case QMetaType::QQuaternion:
370 if (argc == 1) {
371 const qreal *sxyz = reinterpret_cast<const qreal*>(argv[0]);
372 QQuaternion q(sxyz[0], sxyz[1], sxyz[2], sxyz[3]);
373 *v = QVariant(q);
374 return true;
375 }
376 break;
377 case QMetaType::QMatrix4x4:
378 if (argc == 0) {
379 QMatrix4x4 m;
380 *v = QVariant(m);
381 return true;
382 } else if (argc == 1) {
383 const qreal *vals = reinterpret_cast<const qreal*>(argv[0]);
384 QMatrix4x4 m(vals[0], vals[1], vals[2], vals[3],
385 vals[4], vals[5], vals[6], vals[7],
386 vals[8], vals[9], vals[10], vals[11],
387 vals[12], vals[13], vals[14], vals[15]);
388 *v = QVariant(m);
389 return true;
390 }
391 break;
392 default: break;
393 }
394
395 return false;
396 }
397
398 template<typename T>
399 bool createFromStringTyped(void *data, size_t dataSize, T initValue)
400 {
401 ASSERT_VALID_SIZE(dataSize, sizeof(T));
402 T *t = reinterpret_cast<T *>(data);
403 new (t) T(initValue);
404 return true;
405 }
406
407 bool createFromString(int type, const QString &s, void *data, size_t dataSize) override
408 {
409 bool ok = false;
410
411 switch (type) {
412 case QMetaType::QColor:
413 return createFromStringTyped<QColor>(data, dataSize, QColor(s));
414 case QMetaType::QVector2D:
415 return createFromStringTyped<QVector2D>(data, dataSize, vector2DFromString(s, &ok));
416 case QMetaType::QVector3D:
417 return createFromStringTyped<QVector3D>(data, dataSize, vector3DFromString(s, &ok));
418 case QMetaType::QVector4D:
419 return createFromStringTyped<QVector4D>(data, dataSize, vector4DFromString(s, &ok));
420 case QMetaType::QQuaternion:
421 return createFromStringTyped<QQuaternion>(data, dataSize, quaternionFromString(s, &ok));
422 case QMetaType::QMatrix4x4:
423 return createFromStringTyped<QMatrix4x4>(data, dataSize, matrix4x4FromString(s, &ok));
424 default: break;
425 }
426
427 return false;
428 }
429
430 bool createStringFrom(int type, const void *data, QString *s) override
431 {
432 if (type == QMetaType::QColor) {
433 const QColor *color = reinterpret_cast<const QColor *>(data);
434 new (s) QString(QVariant(*color).toString());
435 return true;
436 }
437
438 return false;
439 }
440
441 bool variantFromString(const QString &s, QVariant *v) override
442 {
443 QColor c(s);
444 if (c.isValid()) {
445 *v = QVariant::fromValue(c);
446 return true;
447 }
448
449 bool ok = false;
450
451 QVector2D v2 = vector2DFromString(s, &ok);
452 if (ok) {
453 *v = QVariant::fromValue(v2);
454 return true;
455 }
456
457 QVector3D v3 = vector3DFromString(s, &ok);
458 if (ok) {
459 *v = QVariant::fromValue(v3);
460 return true;
461 }
462
463 QVector4D v4 = vector4DFromString(s, &ok);
464 if (ok) {
465 *v = QVariant::fromValue(v4);
466 return true;
467 }
468
469 QQuaternion q = quaternionFromString(s, &ok);
470 if (ok) {
471 *v = QVariant::fromValue(q);
472 return true;
473 }
474
475 QMatrix4x4 m = matrix4x4FromString(s, &ok);
476 if (ok) {
477 *v = QVariant::fromValue(m);
478 return true;
479 }
480
481 return false;
482 }
483
484 bool variantFromString(int type, const QString &s, QVariant *v) override
485 {
486 bool ok = false;
487
488 switch (type) {
489 case QMetaType::QColor:
490 {
491 QColor c(s);
492 *v = QVariant::fromValue(c);
493 return true;
494 }
495 case QMetaType::QVector2D:
496 {
497 *v = QVariant::fromValue(vector2DFromString(s, &ok));
498 return true;
499 }
500 case QMetaType::QVector3D:
501 {
502 *v = QVariant::fromValue(vector3DFromString(s, &ok));
503 return true;
504 }
505 case QMetaType::QVector4D:
506 {
507 *v = QVariant::fromValue(vector4DFromString(s, &ok));
508 return true;
509 }
510 case QMetaType::QQuaternion:
511 {
512 *v = QVariant::fromValue(quaternionFromString(s, &ok));
513 return true;
514 }
515 case QMetaType::QMatrix4x4:
516 {
517 *v = QVariant::fromValue(matrix4x4FromString(s, &ok));
518 return true;
519 }
520 default:
521 break;
522 }
523
524 return false;
525 }
526
527 bool variantFromJsObject(int type, const QV4::Value &object, QV4::ExecutionEngine *v4, QVariant *v) override
528 {
529 QV4::Scope scope(v4);
530#ifndef QT_NO_DEBUG
531 QV4::ScopedObject obj(scope, object);
532 Q_ASSERT(obj);
533#endif
534 bool ok = false;
535 switch (type) {
536 case QMetaType::QMatrix4x4:
537 *v = QVariant::fromValue(matrix4x4FromObject(object, v4, &ok));
538 default: break;
539 }
540
541 return ok;
542 }
543
544 template<typename T>
545 bool typedEqual(const void *lhs, const QVariant& rhs)
546 {
547 return (*(reinterpret_cast<const T *>(lhs)) == rhs.value<T>());
548 }
549
550 bool equal(int type, const void *lhs, const QVariant &rhs) override
551 {
552 switch (type) {
553 case QMetaType::QColor:
554 return typedEqual<QColor>(lhs, rhs);
555 case QMetaType::QVector2D:
556 return typedEqual<QVector2D>(lhs, rhs);
557 case QMetaType::QVector3D:
558 return typedEqual<QVector3D>(lhs, rhs);
559 case QMetaType::QVector4D:
560 return typedEqual<QVector4D>(lhs, rhs);
561 case QMetaType::QQuaternion:
562 return typedEqual<QQuaternion>(lhs, rhs);
563 case QMetaType::QMatrix4x4:
564 return typedEqual<QMatrix4x4>(lhs, rhs);
565 default: break;
566 }
567
568 return false;
569 }
570
571 template<typename T>
572 bool typedStore(const void *src, void *dst, size_t dstSize)
573 {
574 ASSERT_VALID_SIZE(dstSize, sizeof(T));
575 const T *srcT = reinterpret_cast<const T *>(src);
576 T *dstT = reinterpret_cast<T *>(dst);
577 new (dstT) T(*srcT);
578 return true;
579 }
580
581 bool store(int type, const void *src, void *dst, size_t dstSize) override
582 {
583 switch (type) {
584 case QMetaType::QColor:
585 {
586 Q_ASSERT(dstSize >= sizeof(QColor));
587 Q_UNUSED(dstSize);
588 const QRgb *rgb = reinterpret_cast<const QRgb *>(src);
589 QColor *color = reinterpret_cast<QColor *>(dst);
590 new (color) QColor(QColor::fromRgba(*rgb));
591 return true;
592 }
593 default: break;
594 }
595
596 return false;
597 }
598
599 template<typename T>
600 bool typedRead(const QVariant& src, int dstType, void *dst)
601 {
602 T *dstT = reinterpret_cast<T *>(dst);
603 if (src.type() == QVariant::Type(dstType)) {
604 *dstT = src.value<T>();
605 } else {
606 *dstT = T();
607 }
608 return true;
609 }
610
611 bool read(const QVariant &src, void *dst, int dstType) override
612 {
613 switch (dstType) {
614 case QMetaType::QColor:
615 return typedRead<QColor>(src, dstType, dst);
616 case QMetaType::QVector2D:
617 return typedRead<QVector2D>(src, dstType, dst);
618 case QMetaType::QVector3D:
619 return typedRead<QVector3D>(src, dstType, dst);
620 case QMetaType::QVector4D:
621 return typedRead<QVector4D>(src, dstType, dst);
622 case QMetaType::QQuaternion:
623 return typedRead<QQuaternion>(src, dstType, dst);
624 case QMetaType::QMatrix4x4:
625 return typedRead<QMatrix4x4>(src, dstType, dst);
626 default: break;
627 }
628
629 return false;
630 }
631
632 template<typename T>
633 bool typedWrite(const void *src, QVariant& dst)
634 {
635 const T *srcT = reinterpret_cast<const T *>(src);
636 if (dst.value<T>() != *srcT) {
637 dst = *srcT;
638 return true;
639 }
640 return false;
641 }
642
643 bool write(int type, const void *src, QVariant& dst) override
644 {
645 switch (type) {
646 case QMetaType::QColor:
647 return typedWrite<QColor>(src, dst);
648 case QMetaType::QVector2D:
649 return typedWrite<QVector2D>(src, dst);
650 case QMetaType::QVector3D:
651 return typedWrite<QVector3D>(src, dst);
652 case QMetaType::QVector4D:
653 return typedWrite<QVector4D>(src, dst);
654 case QMetaType::QQuaternion:
655 return typedWrite<QQuaternion>(src, dst);
656 case QMetaType::QMatrix4x4:
657 return typedWrite<QMatrix4x4>(src, dst);
658 default: break;
659 }
660
661 return false;
662 }
663#undef ASSERT_VALID_SIZE
664};
665
666Quick3DValueTypeProvider *valueTypeProvider = nullptr;
667static Quick3DValueTypeProvider *getValueTypeProvider()
668{
669 if (valueTypeProvider == nullptr)
670 valueTypeProvider = new Quick3DValueTypeProvider();
671 return valueTypeProvider;
672}
673
674static Quick3DColorProvider *getColorProvider()
675{
676 static Quick3DColorProvider colorProvider;
677 return &colorProvider;
678}
679
680static QQmlPrivate::AutoParentResult qquick3ditem_autoParent(QObject *obj, QObject *parent)
681{
682 // When setting a parent (especially during dynamic object creation) in QML,
683 // also try to set up the analogous item/window relationship.
684 auto parentNode = qmlobject_cast<Qt3DCore::QNode *>(parent);
685 if (parentNode) {
686 auto node = qmlobject_cast<Qt3DCore::QNode *>(obj);
687 if (node) {
688 // A QNode has another QNode child
689 node->setParent(parentNode);
690 return QQmlPrivate::Parented;
691 }
692 } else {
693 return QQmlPrivate::IncompatibleParent;
694 }
695 return QQmlPrivate::IncompatibleObject;
696}
697
698void Quick3D_initialize()
699{
700 Qt3DCore::Quick::Quick3DValueTypes::registerValueTypes();
701 QQml_addValueTypeProvider(getValueTypeProvider());
702 QQml_setColorProvider(getColorProvider());
703 QAbstractNodeFactory::registerNodeFactory(QuickNodeFactory::instance());
704
705 // Register a hook called when we do component.create() that sets the
706 // parent. We need this as QObject::setParent() is insufficient to propagate
707 // the arbiter and scene to the children (see QNode::setParent(QNode *).
708 // TODO: Replace this with virtual void QObjectPrivate::setParent(QObject *)
709 // that can be called from QObject ctor and QObject::setParent(). That would
710 // allow removal of this hook here and in QtQuick.
711 QQmlPrivate::RegisterAutoParent autoparent = { 0, &qquick3ditem_autoParent };
712 QQmlPrivate::qmlregister(QQmlPrivate::AutoParentRegistration, &autoparent);
713}
714
715void Quick3D_uninitialize()
716{
717 delete valueTypeProvider;
718 valueTypeProvider = nullptr;
719}
720
721void Quick3D_registerType(const char *className, const char *quickName, int major, int minor)
722{
723 QuickNodeFactory::instance()->registerType(className, quickName, major, minor);
724}
725
726} // namespace Quick
727} // namespace Qt3DCore
728
729QT_END_NAMESPACE
730

source code of qt3d/src/quick3d/quick3d/qt3dquick_global.cpp