aboutsummaryrefslogtreecommitdiffstats
path: root/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/lightgeometry.cpp
blob: dd15a65595380e99f5ddba2cec1d58795e0c1a86 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0

#ifdef QUICK3D_MODULE

#include "lightgeometry.h"

#include <QtQuick3DRuntimeRender/private/qssgrendergeometry_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
#include <QtCore/qmath.h>

#include <limits>

namespace QmlDesigner {
namespace Internal {

LightGeometry::LightGeometry()
    : GeometryBase()
{
}

LightGeometry::~LightGeometry()
{
}

LightGeometry::LightType LightGeometry::lightType() const
{
    return m_lightType;
}

void LightGeometry::setLightType(LightGeometry::LightType lightType)
{
    if (m_lightType == lightType)
        return;

    m_lightType = lightType;

    emit lightTypeChanged();
    updateGeometry();
}

void LightGeometry::doUpdateGeometry()
{
    if (m_lightType == LightType::Invalid)
        return;

    GeometryBase::doUpdateGeometry();

    QByteArray vertexData;
    QByteArray indexData;
    QVector3D minBounds;
    QVector3D maxBounds;

    fillVertexData(vertexData, indexData, minBounds, maxBounds);

    addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0,
                 QQuick3DGeometry::Attribute::U16Type);

    setVertexData(vertexData);
    setIndexData(indexData);
    setBounds(minBounds, maxBounds);
}

void LightGeometry::fillVertexData(QByteArray &vertexData, QByteArray &indexData,
                                   QVector3D &minBounds, QVector3D &maxBounds)
{
    int vertexSize = 0;
    int indexSize = 0;
    const int arc = 12; // Segment lines per cone line in spot/directional light arc
    const int dirLines = 4; // Directional lines in spot/directional light
    const quint16 segments = arc * dirLines;
    const double segment = M_PI * 2. / double(segments);

    if (m_lightType == LightType::Area) {
        // Area light model is a rectangle
        vertexSize = int(sizeof(float)) * 3 * 4;
        indexSize = int(sizeof(quint16)) * 4 * 2;
    } else if (m_lightType == LightType::Directional) {
        // Directional light model is a circle with perpendicular lines on circumference vertices
        vertexSize = int(sizeof(float)) * 3 * (segments + dirLines);
        indexSize = int(sizeof(quint16)) * (segments + dirLines) * 2;
    } else if (m_lightType == LightType::Spot) {
        vertexSize = int(sizeof(float)) * 3 * (segments + 1);
        indexSize = int(sizeof(quint16)) * (segments + dirLines) * 2;
    } else if (m_lightType == LightType::Point) {
        vertexSize = int(sizeof(float)) * 3 * segments;
        indexSize = int(sizeof(quint16)) * segments * 2;
    }
    vertexData.resize(vertexSize);
    indexData.resize(indexSize);

    auto dataPtr = reinterpret_cast<float *>(vertexData.data());
    auto indexPtr = reinterpret_cast<quint16 *>(indexData.data());

    auto createCircle = [&](quint16 startIndex, float zVal, int xIdx, int yIdx, int zIdx) {
        for (quint16 i = 0; i < segments; ++i) {
            float x = float(qCos(i * segment));
            float y = float(qSin(i * segment));
            auto vecPtr = reinterpret_cast<QVector3D *>(dataPtr);
            (*vecPtr)[xIdx] = x;
            (*vecPtr)[yIdx] = y;
            (*vecPtr)[zIdx] = zVal;
            dataPtr += 3;
            *indexPtr++ = startIndex + i; *indexPtr++ = startIndex + i + 1;
        }
        // Adjust the final index to complete the circle
        *(indexPtr - 1) = startIndex;
    };

    if (m_lightType == LightType::Area) {
        *dataPtr++ = -1.f; *dataPtr++ = 1.f;  *dataPtr++ = 0.f;
        *dataPtr++ = -1.f; *dataPtr++ = -1.f; *dataPtr++ = 0.f;
        *dataPtr++ = 1.f;  *dataPtr++ = -1.f; *dataPtr++ = 0.f;
        *dataPtr++ = 1.f;  *dataPtr++ = 1.f;  *dataPtr++ = 0.f;

        *indexPtr++ = 0; *indexPtr++ = 1;
        *indexPtr++ = 1; *indexPtr++ = 2;
        *indexPtr++ = 2; *indexPtr++ = 3;
        *indexPtr++ = 3; *indexPtr++ = 0;
    } else if (m_lightType == LightType::Directional) {
        createCircle(0, 0.f, 0, 1, 2);

        // Dir lines
        for (quint16 i = 0; i < dirLines; ++i) {
            auto circlePtr = reinterpret_cast<float *>(vertexData.data()) + (3 * arc * i);
            *dataPtr++ = *circlePtr; *dataPtr++ = *(circlePtr + 1); *dataPtr++ = -3.f;
            *indexPtr++ = i * arc;
            *indexPtr++ = i + segments;
        }
    } else if (m_lightType == LightType::Spot) {
        createCircle(0, -1.f, 0, 1, 2);

        // Cone tip
        *dataPtr++ = 0.f; *dataPtr++ = 0.f; *dataPtr++ = 0.f;
        quint16 tipIndex = segments;

        // Cone lines
        for (quint16 i = 0; i < dirLines; ++i) {
            *indexPtr++ = tipIndex;
            *indexPtr++ = i * arc;
        }
    } else if (m_lightType == LightType::Point) {
        createCircle(0, 0.f, 0, 1, 2);
    }

    static const float floatMin = std::numeric_limits<float>::lowest();
    static const float floatMax = std::numeric_limits<float>::max();
    auto vertexPtr = reinterpret_cast<QVector3D *>(vertexData.data());
    minBounds = QVector3D(floatMax, floatMax, floatMax);
    maxBounds = QVector3D(floatMin, floatMin, floatMin);
    for (int i = 0; i < vertexSize / 12; ++i) {
        minBounds[0] = qMin((*vertexPtr)[0], minBounds[0]);
        minBounds[1] = qMin((*vertexPtr)[1], minBounds[1]);
        minBounds[2] = qMin((*vertexPtr)[2], minBounds[2]);
        maxBounds[0] = qMax((*vertexPtr)[0], maxBounds[0]);
        maxBounds[1] = qMax((*vertexPtr)[1], maxBounds[1]);
        maxBounds[2] = qMax((*vertexPtr)[2], maxBounds[2]);
        ++vertexPtr;
    }
}

}
}

#endif // QUICK3D_MODULE