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