Source code

Revision control

Copy as Markdown

// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "testgeometry.h"
#include <qvector4d.h>
#include <qvector3d.h>
#include <qvector2d.h>
#include <qmath.h>
#include <qcolor.h>
struct Vertex {
QVector3D position;
QVector3D normal;
QVector3D tangent;
QVector3D binormal;
QVector2D uv0;
QVector2D uv1;
QVector4D color;
float pad[4];
};
static const int s_vertexSize = sizeof(Vertex);
Q_STATIC_ASSERT(s_vertexSize == 96);
template <typename T>
void byteArrayPtr(T *&ptr, QByteArray &positions)
{
ptr = reinterpret_cast<T *>(positions.data());
}
template <typename T>
void byteArrayPtr(const T *&ptr, const QByteArray &positions)
{
ptr = reinterpret_cast<const T *>(positions.constData());
}
struct Face {
quint32 v0, v1, v2;
Face() {}
Face(quint32 a, quint32 b, quint32 c)
: v0(a), v1(b), v2(c) {}
void ensureHandedness(const QByteArray &pos, QVector3D n)
{
const QVector3D *p = nullptr;
byteArrayPtr(p, pos);
QVector3D a = p[v1] - p[v0];
QVector3D b = p[v2] - p[v1];
QVector3D c = QVector3D::crossProduct(a, b);
if (QVector3D::dotProduct(c, n) < 0.0f)
qSwap(v1, v2);
}
};
static void packVertices(const QByteArray &pos, const QByteArray &norm,
const QByteArray &uv0, const QByteArray &uv1,
const QByteArray &tan, const QByteArray &bin,
const QByteArray &col, QByteArray &dest)
{
int count = int(pos.size() / sizeof(QVector3D));
QByteArray vertices;
vertices.resize(count * s_vertexSize);
vertices.fill(0);
const QVector3D *p = reinterpret_cast<const QVector3D *>(pos.constData());
const QVector3D *n = norm.size() ? reinterpret_cast<const QVector3D *>(norm.constData()) : nullptr;
const QVector2D *u0 = uv0.size() ? reinterpret_cast<const QVector2D *>(uv0.constData()) : nullptr;
const QVector2D *u1 = uv1.size() ? reinterpret_cast<const QVector2D *>(uv1.constData()) : nullptr;
const QVector3D *t = tan.size() ? reinterpret_cast<const QVector3D *>(tan.constData()) : nullptr;
const QVector3D *bn = bin.size() ? reinterpret_cast<const QVector3D *>(bin.constData()) : nullptr;
const QVector4D *c = col.size() ? reinterpret_cast<const QVector4D *>(col.constData()) : nullptr;
Vertex *vert = reinterpret_cast<Vertex *>(vertices.data());
for (int i = 0; i < count; i++) {
Vertex &v = vert[i];
v.position = p[i];
if (n)
v.normal = n[i];
if (u0)
v.uv0 = u0[i];
if (u1)
v.uv1 = u1[i];
if (t)
v.tangent = t[i];
if (bn)
v.binormal = bn[i];
if (c)
v.color = c[i];
}
dest = vertices;
}
static QByteArray s_normals = QByteArray();
QByteArray &generateNormals(const QByteArray &positions, const QByteArray &indices)
{
if (!s_normals.size()) {
int count = int(indices.size() / sizeof(Face));
s_normals.resize(positions.size());
s_normals.fill(0);
const Face *faces = reinterpret_cast<const Face *>(indices.constData());
QVector3D *n = reinterpret_cast<QVector3D *>(s_normals.data());
const QVector3D *p = reinterpret_cast<const QVector3D *>(positions.constData());
for (int i = 0; i < count; i++) {
const Face &f = faces[i];
QVector3D u = p[f.v1] - p[f.v0];
QVector3D v = p[f.v2] - p[f.v0];
QVector3D normal = u.crossProduct(u, v).normalized();
if (normal.y() < 0.0f)
normal *= -1.0f;
n[f.v0] += normal;
n[f.v1] += normal;
n[f.v2] += normal;
}
count = int(positions.size() / sizeof(QVector3D));
for (int i = 0; i < count; i++)
n[i] = n[i].normalized();
}
return s_normals;
}
static QByteArray s_tangents = QByteArray();
QByteArray &generateTangents(const QByteArray &positions, const QByteArray &normals, const QByteArray &indices)
{
if (!s_tangents.size()) {
int count = int(indices.size() / sizeof(Face));
s_tangents.resize(positions.size());
s_tangents.fill(0);
const Face *faces = reinterpret_cast<const Face *>(indices.constData());
const QVector3D *n = reinterpret_cast<const QVector3D *>(normals.constData());
QVector3D *t = reinterpret_cast<QVector3D *>(s_tangents.data());
const QVector3D *p = reinterpret_cast<const QVector3D *>(positions.constData());
for (int i = 0; i < count; i++) {
const Face &f = faces[i];
QVector3D u = p[f.v1] - p[f.v0];
t[f.v0] += QVector3D::crossProduct(n[f.v0], u).normalized();
t[f.v1] += QVector3D::crossProduct(n[f.v1], u).normalized();
t[f.v2] += QVector3D::crossProduct(n[f.v2], u).normalized();
}
count = int(positions.size() / sizeof(QVector3D));
for (int i = 0; i < count; i++)
t[i] = t[i].normalized();
}
return s_tangents;
}
static QByteArray s_binormals = QByteArray();
QByteArray &generateBinormals(const QByteArray &normals, const QByteArray &tangents)
{
if (!s_binormals.size()) {
int count = int(normals.size() / sizeof(QVector3D));
s_binormals.resize(normals.size());
s_binormals.fill(0);
const QVector3D *n = reinterpret_cast<const QVector3D *>(normals.constData());
const QVector3D *t = reinterpret_cast<const QVector3D *>(tangents.constData());
QVector3D *bn = reinterpret_cast<QVector3D *>(s_binormals.data());
for (int i = 0; i < count; i++)
bn[i] = QVector3D::crossProduct(n[i], t[i]).normalized();
}
return s_binormals;
}
static QByteArray s_texcoords0 = QByteArray();
QByteArray &generateTexcoords0(const QByteArray &positions, const QByteArray &normals,
QVector3D &minPos, QVector3D &maxPos, float tstep)
{
if (!s_texcoords0.size()) {
int count = int(positions.size() / sizeof(QVector3D));
s_texcoords0.resize(count * sizeof(QVector2D));
s_texcoords0.fill(0);
QVector2D *t = reinterpret_cast<QVector2D *>(s_texcoords0.data());
const QVector3D *n = normals.size() ? reinterpret_cast<const QVector3D *>(normals.constData()) : nullptr;
const QVector3D *p = reinterpret_cast<const QVector3D *>(positions.constData());
float xScale = 1.f / (maxPos.x() - minPos.x());
float zScale = 1.f / (maxPos.z() - minPos.z());
for (int i = 0; i < count; ++i) {
float u = p[i].x() * xScale;
float v = p[i].z() * zScale;
if (n) {
const QVector3D &normal = n[i];
u -= tstep * normal.x() * xScale;
v -= tstep * normal.z() * zScale;
}
t[i] = QVector2D(u, v);
}
}
return s_texcoords0;
}
static QByteArray s_texcoords1 = QByteArray();
QByteArray &generateTexcoords1(const QByteArray &positions)
{
if (!s_texcoords1.size()) {
int count = int(positions.size() / sizeof(QVector3D));
s_texcoords1.resize(count * sizeof(QVector2D));
s_texcoords1.fill(0);
QVector2D *t = reinterpret_cast<QVector2D *>(s_texcoords1.data());
const QVector3D *p = reinterpret_cast<const QVector3D *>(positions.constData());
for (int i = 0; i < count; ++i) {
float u = 1.0 + p->y();
float v = 1.0 - p->y();
t[i] = QVector2D(u, v);
}
}
return s_texcoords1;
}
static QByteArray s_colors = QByteArray();
QByteArray &generateColors(const QByteArray &positions)
{
if (!s_colors.size()) {
int count = int(positions.size() / sizeof(QVector3D));
s_colors.resize(count * sizeof(QVector4D));
s_colors.fill(0);
QVector4D colorF = QVector4D(1.f, 0.5f, 0.5f, 1.0f);
QVector4D colorL = QVector4D(0.125f, 0.125f, 0.125f, 1.0f);
QVector4D *c = reinterpret_cast<QVector4D *>(s_colors.data());
const QVector3D *p = reinterpret_cast<const QVector3D *>(positions.constData());
float div = 5.0f;
float w = 1.0f;
for (int i = 0; i < count; i++) {
float y = p[i].y();
float t = qFloor(y / div);
y = y - t * div;
if (qAbs(y) < w)
c[i] = colorL;
else
c[i] = colorF;
}
}
return s_colors;
}
template <typename T>
quint32 insertData(const T &c, QByteArray &data)
{
int count = int(data.size() / sizeof(T));
QByteArray d;
d.resize(sizeof(T));
memcpy(d.data(), &c, sizeof(T));
data.append(d);
return count;
}
quint32 findOrInsertVertex(const QVector3D v, QByteArray &vertices, int &lsty, int step)
{
const QVector3D *ptr = reinterpret_cast<const QVector3D *>(vertices.constData());
int count = int(vertices.size() / sizeof(QVector3D));
for (int i = 0; i < count; ++i) {
if (qFuzzyCompare(v, ptr[(i + lsty) % count])) {
lsty = (i + lsty) % count;
return lsty;
}
}
lsty = qMax(count - step, 0);
return insertData(v, vertices);
}
float camp(int x, int z, float fx, float fz, float amp)
{
float y = cos(qDegreesToRadians(z * fz)) * amp;
y += sin(qDegreesToRadians(x * fx)) * amp;
y *= 0.5f;
return y;
}
static QByteArray s_positions = QByteArray();
static QByteArray s_indices = QByteArray();
static QByteArray generatePositions(int count, QByteArray &outIndices, bool flat, QVector3D &minPos, QVector3D &maxPos, float size)
{
float xfrequency = 3*360 / (100 / (size * count) * count);
float zfrequency = 7*360 / (100 / (size * count) * count);
float aplitude = 10.f;
float aplitude2 = 5.f;
float xfrequency2 = 360 / (100 / (size * count) * count);
float zfrequency2 = 360 / (100 / (size * count) * count);
minPos = QVector3D(0, -aplitude2 - aplitude, 0);
maxPos = QVector3D(count * size, aplitude2 + aplitude, count * size);
if (!s_positions.size()) {
int lsdy = 0;
for (int z = 0; z < count; z++) {
for (int x = 0; x < count; x++) {
float y0 = camp(x, z, xfrequency, zfrequency, aplitude);
float y1 = camp(x, z+1, xfrequency, zfrequency, aplitude);
float y2 = camp(x+1, z+1, xfrequency, zfrequency, aplitude);
float y3 = camp(x+1, z, xfrequency, zfrequency, aplitude);
y0 += camp(x, z, xfrequency2, zfrequency2, aplitude2);
y1 += camp(x, z+1, xfrequency2, zfrequency2, aplitude2);
y2 += camp(x+1, z+1, xfrequency2, zfrequency2, aplitude2);
y3 += camp(x+1, z, xfrequency2, zfrequency2, aplitude2);
float x0 = x * size;
float x1 = (x+1) * size;
float z0 = z * size;
float z1 = (z+1) * size;
QVector3D v0(x0, y0, z0);
QVector3D v1(x0, y1, z1);
QVector3D v2(x1, y2, z1);
QVector3D v3(x1, y3, z0);
quint32 t0;
quint32 t1;
quint32 t2;
quint32 t3;
if (!flat) {
t0 = findOrInsertVertex(v0, s_positions, lsdy, count);
t1 = findOrInsertVertex(v1, s_positions, lsdy, count);
t2 = findOrInsertVertex(v2, s_positions, lsdy, count);
t3 = findOrInsertVertex(v3, s_positions, lsdy, count);
} else {
t0 = insertData(v0, s_positions);
t1 = insertData(v1, s_positions);
t2 = insertData(v2, s_positions);
t3 = insertData(v3, s_positions);
}
QVector3D n = QVector3D::crossProduct(v1 - v0, v2 - v1).normalized();
if (n.y() < 0.0f)
n *= -1.0f;
Face f0(t0, t1, t2);
Face f1(t0, t2, t3);
f0.ensureHandedness(s_positions, n);
f1.ensureHandedness(s_positions, n);
insertData(f0, s_indices);
insertData(f1, s_indices);
}
}
}
outIndices = s_indices;
return s_positions;
}
TestGeometry::TestGeometry(QQuick3DObject *parent)
: QQuick3DGeometry(parent)
{
}
bool TestGeometry::position() const
{
return m_position;
}
bool TestGeometry::normal() const
{
return m_normal;
}
bool TestGeometry::texcoord0() const
{
return m_texcoord0;
}
bool TestGeometry::texcoord1() const
{
return m_texcoord1;
}
bool TestGeometry::tangent() const
{
return m_tangent;
}
bool TestGeometry::binormal() const
{
return m_binormal;
}
bool TestGeometry::color() const
{
return m_color;
}
void TestGeometry::setPosition(bool enable)
{
if (enable == m_position)
return;
m_position = enable;
emit positionChanged();
m_dirty = true;
}
void TestGeometry::setNormal(bool enable)
{
if (enable == m_normal)
return;
m_normal = enable;
emit normalChanged();
m_dirty = true;
}
void TestGeometry::setTexcoord0(bool enable)
{
if (enable == m_texcoord0)
return;
m_texcoord0 = enable;
emit texcoord0Changed();
m_dirty = true;
}
void TestGeometry::setTexcoord1(bool enable)
{
if (enable == m_texcoord1)
return;
m_texcoord1 = enable;
emit texcoord1Changed();
m_dirty = true;
}
void TestGeometry::setTangent(bool enable)
{
if (enable == m_tangent)
return;
m_tangent = enable;
emit tangentChanged();
m_dirty = true;
}
void TestGeometry::setBinormal(bool enable)
{
if (enable == m_binormal)
return;
m_binormal = enable;
emit binormalChanged();
m_dirty = true;
}
void TestGeometry::setColor(bool enable)
{
if (enable == m_color)
return;
m_color = enable;
emit colorChanged();
m_dirty = true;
}
QSSGRenderGraphObject *TestGeometry::updateSpatialNode(QSSGRenderGraphObject *node)
{
if (m_dirty) {
QVector3D min, max;
int count = 120;
QByteArray positionData = generatePositions(count, m_indexBuffer, false, min, max, 100.0f / float(count));
QByteArray normalData;
QByteArray uv0Data;
QByteArray uv1Data;
QByteArray colorData;
QByteArray tangentData;
QByteArray binormalData;
clear();
addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0,
QQuick3DGeometry::Attribute::ComponentType::F32Type);
addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0,
QQuick3DGeometry::Attribute::ComponentType::U32Type);
if (normal()) {
normalData = generateNormals(positionData, m_indexBuffer);
addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, offsetof(Vertex, normal),
QQuick3DGeometry::Attribute::ComponentType::F32Type);
}
if (texcoord0()) {
uv0Data = generateTexcoords0(positionData, normalData, min, max, 0.1f);
addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic, offsetof(Vertex, uv0),
QQuick3DGeometry::Attribute::ComponentType::F32Type);
}
if (texcoord1()) {
uv1Data = generateTexcoords1(positionData);
addAttribute(QQuick3DGeometry::Attribute::TexCoord1Semantic, offsetof(Vertex, uv1),
QQuick3DGeometry::Attribute::ComponentType::F32Type);
}
if (tangent()) {
tangentData = generateTangents(positionData, normalData, m_indexBuffer);
addAttribute(QQuick3DGeometry::Attribute::TangentSemantic, offsetof(Vertex, tangent),
QQuick3DGeometry::Attribute::ComponentType::F32Type);
}
if (binormal()) {
binormalData = generateBinormals(normalData, tangentData);
addAttribute(QQuick3DGeometry::Attribute::BinormalSemantic, offsetof(Vertex, binormal),
QQuick3DGeometry::Attribute::ComponentType::F32Type);
}
if (color()) {
colorData = generateColors(positionData);
addAttribute(QQuick3DGeometry::Attribute::ColorSemantic, offsetof(Vertex, color),
QQuick3DGeometry::Attribute::ComponentType::F32Type);
}
packVertices(positionData, normalData, uv0Data, uv1Data, tangentData, binormalData, colorData, m_vertexBuffer);
setStride(s_vertexSize);
setVertexData(m_vertexBuffer);
setIndexData(m_indexBuffer);
setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
setBounds(min, max);
m_dirty = false;
}
node = QQuick3DGeometry::updateSpatialNode(node);
return node;
}