QmlDesigner: Update light gizmos

Directional light model was made constant size, as it doesn't have
actual physical presence.

Spot light cone now shows the cone angle correctly and the length of
the cone is calculated so that 5% of the brightness reaches the
center of the cone bottom.

Area light rectangle matches the area of the light.

Point light mesh changed to three perpendicular circles.
Same formula for size of the circles used as for spotlight length.

All light types share a common brightness indicator arrow.

Task-number: QDS-2037
Change-Id: I534dbcda9cfa2a7768c2537868ba83818979b250
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Miikka Heikkinen
2020-05-04 17:42:22 +03:00
parent 4d23e6300f
commit a3e6e24427
11 changed files with 374 additions and 167 deletions

View File

@@ -29,10 +29,6 @@
#include <QtQuick3DRuntimeRender/private/qssgrendergeometry_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
#include <QtQuick3D/private/qquick3darealight_p.h>
#include <QtQuick3D/private/qquick3ddirectionallight_p.h>
#include <QtQuick3D/private/qquick3dpointlight_p.h>
#include <QtQuick3D/private/qquick3dspotlight_p.h>
#include <QtCore/qmath.h>
#include <limits>
@@ -49,25 +45,25 @@ LightGeometry::~LightGeometry()
{
}
QQuick3DAbstractLight *QmlDesigner::Internal::LightGeometry::light() const
LightGeometry::LightType LightGeometry::lightType() const
{
return m_light;
return m_lightType;
}
void QmlDesigner::Internal::LightGeometry::setLight(QQuick3DAbstractLight *light)
void LightGeometry::setLightType(LightGeometry::LightType lightType)
{
if (m_light == light)
if (m_lightType == lightType)
return;
m_light = light;
m_lightType = lightType;
emit lightChanged();
emit lightTypeChanged();
update();
}
QSSGRenderGraphObject *LightGeometry::updateSpatialNode(QSSGRenderGraphObject *node)
{
if (!m_light)
if (m_lightType == LightType::Invalid)
return node;
node = QQuick3DGeometry::updateSpatialNode(node);
@@ -99,28 +95,26 @@ void LightGeometry::fillVertexData(QByteArray &vertexData, QByteArray &indexData
{
int vertexSize = 0;
int indexSize = 0;
const int dirSegments = 12; // Segment lines in directional light circle
const int spotArc = 6; // Segment lines per cone line in spotlight arc
const int spotCone = 4; // Lines in spotlight cone
const int pointLightDensity = 5;
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 (qobject_cast<QQuick3DAreaLight *>(m_light)) {
// Area light model is a rectangle with perpendicular lines on corners
vertexSize = int(sizeof(float)) * 3 * 8;
indexSize = int(sizeof(quint16)) * 8 * 2;
} else if (qobject_cast<QQuick3DDirectionalLight *>(m_light)) {
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 * dirSegments * 2;
indexSize = int(sizeof(quint16)) * dirSegments * 2 * 2;
} else if (qobject_cast<QQuick3DPointLight *>(m_light)) {
// Point light model is a set of lines radiating from central point.
// We reserve more than we need so we don't have to calculate the actual need here,
// and resize later when we know the exact count.
vertexSize = int(sizeof(float)) * 3 * pointLightDensity * pointLightDensity * 4;
indexSize = int(sizeof(quint16)) * pointLightDensity * pointLightDensity * 4;
} else if (qobject_cast<QQuick3DSpotLight *>(m_light)) {
vertexSize = int(sizeof(float)) * 3 * (spotArc * spotCone + 1);
indexSize = int(sizeof(quint16)) * (spotArc + 1) * spotCone * 2;
vertexSize = int(sizeof(float)) * 3 * (segments + dirLines);
indexSize = int(sizeof(quint16)) * (segments + dirLines) * 2;
} else if (m_lightType == LightType::Point) {
// Point light model is a set of three perpendicular circles
vertexSize = int(sizeof(float)) * 3 * segments * 3;
indexSize = int(sizeof(quint16)) * segments * 2 * 3;
} else if (m_lightType == LightType::Spot) {
vertexSize = int(sizeof(float)) * 3 * (segments + 1);
indexSize = int(sizeof(quint16)) * (segments + dirLines) * 2;
}
vertexData.resize(vertexSize);
indexData.resize(indexSize);
@@ -128,89 +122,56 @@ void LightGeometry::fillVertexData(QByteArray &vertexData, QByteArray &indexData
auto dataPtr = reinterpret_cast<float *>(vertexData.data());
auto indexPtr = reinterpret_cast<quint16 *>(indexData.data());
if (qobject_cast<QQuick3DAreaLight *>(m_light)) {
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;
*dataPtr++ = -1.f; *dataPtr++ = 1.f; *dataPtr++ = -1.f;
*dataPtr++ = -1.f; *dataPtr++ = -1.f; *dataPtr++ = -1.f;
*dataPtr++ = 1.f; *dataPtr++ = -1.f; *dataPtr++ = -1.f;
*dataPtr++ = 1.f; *dataPtr++ = 1.f; *dataPtr++ = -1.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);
*indexPtr++ = 0; *indexPtr++ = 4;
*indexPtr++ = 1; *indexPtr++ = 5;
*indexPtr++ = 2; *indexPtr++ = 6;
*indexPtr++ = 3; *indexPtr++ = 7;
} else if (qobject_cast<QQuick3DDirectionalLight *>(m_light)) {
const double segment = M_PI * 2. / double(dirSegments);
for (quint16 i = 0; i < dirSegments; ++i) {
float x = float(qCos(i * segment));
float y = float(qSin(i * segment));
*dataPtr++ = x; *dataPtr++ = y; *dataPtr++ = 0.f;
*dataPtr++ = x; *dataPtr++ = y; *dataPtr++ = -1.f;
const quint16 base = i * 2;
*indexPtr++ = base; *indexPtr++ = base + 1;
*indexPtr++ = base; *indexPtr++ = base + 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;
}
// Adjust the final index to complete the circle
*(--indexPtr) = 0;
} else if (qobject_cast<QQuick3DPointLight *>(m_light)) {
const double innerRad = .3;
vertexSize = 0;
indexSize = 0;
int vertexIndex = 0;
for (quint16 i = 0; i < pointLightDensity; ++i) {
double latAngle = (((.9 / (pointLightDensity - 1)) * i) + .05) * M_PI;
quint16 longPoints = pointLightDensity * 2 * qSin(latAngle);
latAngle -= M_PI_2;
const double longSegment = M_PI * 2. / double(longPoints);
for (quint16 j = 0; j < longPoints; ++j) {
double longAngle = longSegment * j;
float q = float(qCos(latAngle));
float x = float(qCos(longAngle) * q);
float y = float(qSin(latAngle));
float z = float(qSin(longAngle) * q);
*dataPtr++ = x * innerRad; *dataPtr++ = y * innerRad; *dataPtr++ = z * innerRad;
*dataPtr++ = x; *dataPtr++ = y; *dataPtr++ = z;
*indexPtr++ = vertexIndex; *indexPtr++ = vertexIndex + 1;
vertexIndex += 2;
vertexSize += 6 * sizeof(float);
indexSize += 2 * sizeof(quint16);
}
}
vertexData.resize(vertexSize);
indexData.resize(indexSize);
} else if (qobject_cast<QQuick3DSpotLight *>(m_light)) {
const quint16 segments = spotArc * spotCone;
const double segment = M_PI * 2. / double(segments);
// Circle
for (quint16 i = 0; i < segments; ++i) {
float x = float(qCos(i * segment));
float y = float(qSin(i * segment));
*dataPtr++ = x; *dataPtr++ = y; *dataPtr++ = -2.f;
*indexPtr++ = i; *indexPtr++ = i + 1;
}
// Adjust the final index to complete the circle
*(indexPtr - 1) = 0;
} else if (m_lightType == LightType::Point) {
createCircle(0, 0.f, 0, 1, 2);
createCircle(segments, 0.f, 2, 0, 1);
createCircle(segments * 2, 0.f, 1, 2, 0);
} 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 < spotCone; ++i) {
for (quint16 i = 0; i < dirLines; ++i) {
*indexPtr++ = tipIndex;
*indexPtr++ = i * spotArc;
*indexPtr++ = i * arc;
}
}

View File

@@ -28,7 +28,6 @@
#ifdef QUICK3D_MODULE
#include <QtQuick3D/private/qquick3dgeometry_p.h>
#include <QtQuick3D/private/qquick3dabstractlight_p.h>
namespace QmlDesigner {
namespace Internal {
@@ -36,19 +35,28 @@ namespace Internal {
class LightGeometry : public QQuick3DGeometry
{
Q_OBJECT
Q_PROPERTY(QQuick3DAbstractLight *light READ light WRITE setLight NOTIFY lightChanged)
Q_PROPERTY(LightType lightType READ lightType WRITE setLightType NOTIFY lightTypeChanged)
public:
enum class LightType {
Invalid,
Spot,
Area,
Directional,
Point
};
Q_ENUM(LightType)
LightGeometry();
~LightGeometry() override;
QQuick3DAbstractLight *light() const;
LightType lightType() const;
public Q_SLOTS:
void setLight(QQuick3DAbstractLight *light);
void setLightType(LightType lightType);
Q_SIGNALS:
void lightChanged();
void lightTypeChanged();
protected:
QSSGRenderGraphObject *updateSpatialNode(QSSGRenderGraphObject *node) override;
@@ -56,7 +64,7 @@ protected:
private:
void fillVertexData(QByteArray &vertexData, QByteArray &indexData,
QVector3D &minBounds, QVector3D &maxBounds);
QQuick3DAbstractLight *m_light = nullptr;
LightType m_lightType = LightType::Invalid;
};
}