1// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "basegeometryloader_p.h"
5
6#include <Qt3DCore/qattribute.h>
7#include <Qt3DCore/qbuffer.h>
8#include <Qt3DCore/qgeometry.h>
9
10#include <Qt3DRender/private/qaxisalignedboundingbox_p.h>
11#include <Qt3DRender/private/renderlogging_p.h>
12
13QT_BEGIN_NAMESPACE
14
15using namespace Qt3DCore;
16
17namespace Qt3DRender {
18
19Q_LOGGING_CATEGORY(BaseGeometryLoaderLog, "Qt3D.BaseGeometryLoader", QtWarningMsg)
20
21BaseGeometryLoader::BaseGeometryLoader()
22 : m_loadTextureCoords(true)
23 , m_generateTangents(true)
24 , m_centerMesh(false)
25 , m_geometry(nullptr)
26{
27}
28
29Qt3DCore::QGeometry *BaseGeometryLoader::geometry() const
30{
31 return m_geometry;
32}
33
34bool BaseGeometryLoader::load(QIODevice *ioDev, const QString &subMesh)
35{
36 if (!doLoad(ioDev, subMesh))
37 return false;
38
39 if (m_normals.empty())
40 generateAveragedNormals(points: m_points, normals&: m_normals, faces: m_indices);
41
42 if (m_generateTangents && !m_texCoords.empty())
43 generateTangents(points: m_points, normals: m_normals, faces: m_indices, texCoords: m_texCoords, tangents&: m_tangents);
44
45 if (m_centerMesh)
46 center(points&: m_points);
47
48 qCDebug(BaseGeometryLoaderLog) << "Loaded mesh:";
49 qCDebug(BaseGeometryLoaderLog) << " " << m_points.size() << "points";
50 qCDebug(BaseGeometryLoaderLog) << " " << m_indices.size() / 3 << "triangles.";
51 qCDebug(BaseGeometryLoaderLog) << " " << m_normals.size() << "normals";
52 qCDebug(BaseGeometryLoaderLog) << " " << m_tangents.size() << "tangents ";
53 qCDebug(BaseGeometryLoaderLog) << " " << m_texCoords.size() << "texture coordinates.";
54
55 generateGeometry();
56
57 return true;
58}
59
60void BaseGeometryLoader::generateAveragedNormals(const std::vector<QVector3D> &points,
61 std::vector<QVector3D> &normals,
62 const std::vector<unsigned int> &faces) const
63{
64 for (size_t i = 0; i < points.size(); ++i)
65 normals.push_back(x: QVector3D());
66
67 for (size_t i = 0; i < faces.size(); i += 3) {
68 const QVector3D &p1 = points[ faces[i] ];
69 const QVector3D &p2 = points[ faces[i+1] ];
70 const QVector3D &p3 = points[ faces[i+2] ];
71
72 const QVector3D a = p2 - p1;
73 const QVector3D b = p3 - p1;
74 const QVector3D n = QVector3D::crossProduct(v1: a, v2: b).normalized();
75
76 normals[ faces[i] ] += n;
77 normals[ faces[i+1] ] += n;
78 normals[ faces[i+2] ] += n;
79 }
80
81 for (size_t i = 0; i < normals.size(); ++i)
82 normals[i].normalize();
83}
84
85void BaseGeometryLoader::generateGeometry()
86{
87 QByteArray bufferBytes;
88 const uint count = uint(m_points.size());
89 const quint32 elementSize = 3 + (hasTextureCoordinates() ? 2 : 0)
90 + (hasNormals() ? 3 : 0)
91 + (hasTangents() ? 4 : 0);
92 const quint32 stride = elementSize * sizeof(float);
93 bufferBytes.resize(size: stride * count);
94 float *fptr = reinterpret_cast<float*>(bufferBytes.data());
95
96 for (size_t index = 0; index < count; ++index) {
97 *fptr++ = m_points.at(n: index).x();
98 *fptr++ = m_points.at(n: index).y();
99 *fptr++ = m_points.at(n: index).z();
100
101 if (hasTextureCoordinates()) {
102 *fptr++ = m_texCoords.at(n: index).x();
103 *fptr++ = m_texCoords.at(n: index).y();
104 }
105
106 if (hasNormals()) {
107 *fptr++ = m_normals.at(n: index).x();
108 *fptr++ = m_normals.at(n: index).y();
109 *fptr++ = m_normals.at(n: index).z();
110 }
111
112 if (hasTangents()) {
113 *fptr++ = m_tangents.at(n: index).x();
114 *fptr++ = m_tangents.at(n: index).y();
115 *fptr++ = m_tangents.at(n: index).z();
116 *fptr++ = m_tangents.at(n: index).w();
117 }
118 } // of buffer filling loop
119
120 auto *buf = new Qt3DCore::QBuffer();
121 buf->setData(bufferBytes);
122
123 if (m_geometry)
124 qDebug(catFunc: BaseGeometryLoaderLog, msg: "Existing geometry instance getting overridden.");
125 m_geometry = new QGeometry();
126
127 QAttribute *positionAttribute = new QAttribute(buf, QAttribute::defaultPositionAttributeName(), QAttribute::Float, 3, count, 0, stride);
128 m_geometry->addAttribute(attribute: positionAttribute);
129 quint32 offset = sizeof(float) * 3;
130
131 if (hasTextureCoordinates()) {
132 QAttribute *texCoordAttribute = new QAttribute(buf, QAttribute::defaultTextureCoordinateAttributeName(), QAttribute::Float, 2, count, offset, stride);
133 m_geometry->addAttribute(attribute: texCoordAttribute);
134 offset += sizeof(float) * 2;
135 }
136
137 if (hasNormals()) {
138 QAttribute *normalAttribute = new QAttribute(buf, QAttribute::defaultNormalAttributeName(), QAttribute::Float, 3, count, offset, stride);
139 m_geometry->addAttribute(attribute: normalAttribute);
140 offset += sizeof(float) * 3;
141 }
142
143 if (hasTangents()) {
144 QAttribute *tangentAttribute = new QAttribute(buf, QAttribute::defaultTangentAttributeName(),QAttribute::Float, 4, count, offset, stride);
145 m_geometry->addAttribute(attribute: tangentAttribute);
146 offset += sizeof(float) * 4;
147 }
148
149 QByteArray indexBytes;
150 QAttribute::VertexBaseType ty;
151 if (m_indices.size() < 65536) {
152 // we can use USHORT
153 ty = QAttribute::UnsignedShort;
154 indexBytes.resize(size: m_indices.size() * sizeof(quint16));
155 quint16 *usptr = reinterpret_cast<quint16*>(indexBytes.data());
156 for (int i = 0; i < int(m_indices.size()); ++i)
157 *usptr++ = static_cast<quint16>(m_indices.at(n: i));
158 } else {
159 // use UINT - no conversion needed, but let's ensure int is 32-bit!
160 ty = QAttribute::UnsignedInt;
161 Q_ASSERT(sizeof(int) == sizeof(quint32));
162 indexBytes.resize(size: m_indices.size() * sizeof(quint32));
163 memcpy(dest: indexBytes.data(), src: reinterpret_cast<const char*>(m_indices.data()), n: indexBytes.size());
164 }
165
166 auto *indexBuffer = new Qt3DCore::QBuffer();
167 indexBuffer->setData(indexBytes);
168 QAttribute *indexAttribute = new QAttribute(indexBuffer, ty, 1, uint(m_indices.size()));
169 indexAttribute->setAttributeType(QAttribute::IndexAttribute);
170 m_geometry->addAttribute(attribute: indexAttribute);
171}
172
173void BaseGeometryLoader::generateTangents(const std::vector<QVector3D> &points,
174 const std::vector<QVector3D> &normals,
175 const std::vector<unsigned int> &faces,
176 const std::vector<QVector2D> &texCoords,
177 std::vector<QVector4D> &tangents) const
178{
179 tangents.clear();
180 std::vector<QVector3D> tan1Accum;
181 std::vector<QVector3D> tan2Accum;
182
183 tan1Accum.resize(new_size: points.size());
184 tan2Accum.resize(new_size: points.size());
185 tangents.resize(new_size: points.size());
186
187 // Compute the tangent vector
188 for (size_t i = 0; i < faces.size(); i += 3) {
189 const QVector3D &p1 = points[ faces[i] ];
190 const QVector3D &p2 = points[ faces[i+1] ];
191 const QVector3D &p3 = points[ faces[i+2] ];
192
193 const QVector2D &tc1 = texCoords[ faces[i] ];
194 const QVector2D &tc2 = texCoords[ faces[i+1] ];
195 const QVector2D &tc3 = texCoords[ faces[i+2] ];
196
197 const QVector3D q1 = p2 - p1;
198 const QVector3D q2 = p3 - p1;
199 const float s1 = tc2.x() - tc1.x(), s2 = tc3.x() - tc1.x();
200 const float t1 = tc2.y() - tc1.y(), t2 = tc3.y() - tc1.y();
201 const float r = 1.0f / (s1 * t2 - s2 * t1);
202 const QVector3D tan1((t2 * q1.x() - t1 * q2.x()) * r,
203 (t2 * q1.y() - t1 * q2.y()) * r,
204 (t2 * q1.z() - t1 * q2.z()) * r);
205 const QVector3D tan2((s1 * q2.x() - s2 * q1.x()) * r,
206 (s1 * q2.y() - s2 * q1.y()) * r,
207 (s1 * q2.z() - s2 * q1.z()) * r);
208 tan1Accum[ faces[i] ] += tan1;
209 tan1Accum[ faces[i+1] ] += tan1;
210 tan1Accum[ faces[i+2] ] += tan1;
211 tan2Accum[ faces[i] ] += tan2;
212 tan2Accum[ faces[i+1] ] += tan2;
213 tan2Accum[ faces[i+2] ] += tan2;
214 }
215
216 for (size_t i = 0; i < points.size(); ++i) {
217 const QVector3D &n = normals[i];
218 const QVector3D &t1 = tan1Accum[i];
219 const QVector3D &t2 = tan2Accum[i];
220
221 // Gram-Schmidt orthogonalize
222 tangents[i] = QVector4D(QVector3D(t1 - QVector3D::dotProduct(v1: n, v2: t1) * n).normalized(), 0.0f);
223
224 // Store handedness in w
225 tangents[i].setW((QVector3D::dotProduct(v1: QVector3D::crossProduct(v1: n, v2: t1), v2: t2) < 0.0f) ? -1.0f : 1.0f);
226 }
227}
228
229void BaseGeometryLoader::center(std::vector<QVector3D> &points)
230{
231 if (points.empty())
232 return;
233
234 const QAxisAlignedBoundingBox bb(points);
235 const QVector3D center = bb.center();
236
237 // Translate center of the AABB to the origin
238 for (size_t i = 0; i < points.size(); ++i) {
239 QVector3D &point = points[i];
240 point = point - center;
241 }
242}
243
244} // namespace Qt3DRender
245
246QT_END_NAMESPACE
247
248#include "moc_basegeometryloader_p.cpp"
249

source code of qt3d/src/plugins/geometryloaders/default/basegeometryloader.cpp