7
8
9
10
11
12
13
14
15
16
17
20
21
22
23
24
27
28
29
30
31
32
33
36
37
38
41
42
43
44
45
46
49HeightFieldGeometry::HeightFieldGeometry()
54const QUrl &HeightFieldGeometry::source()
const
56 return m_heightMapSource;
59void HeightFieldGeometry::setSource(
const QUrl &newSource)
61 if (m_heightMapSource == newSource)
63 m_heightMapSource = newSource;
71bool HeightFieldGeometry::smoothShading()
const
73 return m_smoothShading;
76void HeightFieldGeometry::setSmoothShading(
bool smooth)
78 if (m_smoothShading == smooth)
80 m_smoothShading = smooth;
85 emit smoothShadingChanged();
88const QVector3D &HeightFieldGeometry::extents()
const
93void HeightFieldGeometry::setExtents(
const QVector3D &newExtents)
95 m_extentsSetExplicitly =
true;
96 if (m_extents == newExtents)
98 m_extents = newExtents;
102 emit extentsChanged();
106struct HeightFieldVertex
114void HeightFieldGeometry::updateData()
116 const QQmlContext *context = qmlContext(
this);
118 const auto resolvedUrl = context ? context->resolvedUrl(m_heightMapSource) : m_heightMapSource;
119 if (!resolvedUrl.isValid())
123 const auto qmlSource = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
125 QImage heightMap(qmlSource);
126 int numRows = heightMap.height();
127 int numCols = heightMap.width();
129 if (numRows < 2 || numCols < 2)
132 const int numVertices = numRows * numCols;
134 if (!m_extentsSetExplicitly) {
135 auto prevExt = m_extents;
136 if (numRows == numCols) {
137 m_extents = {100, 100, 100};
138 }
else if (numRows < numCols) {
139 float f =
float(numRows) /
float(numCols);
140 m_extents = {100.f, 100.f, 100.f * f};
142 float f =
float(numCols) /
float(numRows);
143 m_extents = {100.f * f, 100.f, 100.f};
145 if (m_extents != prevExt) {
146 emit extentsChanged();
150 QVector<HeightFieldVertex> vertices;
151 vertices.reserve(numVertices);
153 const float rowF = m_extents.z() / (numRows - 1);
154 const float rowOffs = -m_extents.z() / 2;
155 const float colF = m_extents.x() / (numCols - 1);
156 const float colOffs = -m_extents.x() / 2;
157 for (
int x = 0; x < numCols; x++) {
158 for (
int y = 0; y < numRows; y++) {
159 float f = heightMap.pixelColor(x, y).valueF() - 0.5;
160 HeightFieldVertex vertex;
161 vertex.position = QVector3D(x * colF + colOffs, f * m_extents.y(), y * rowF + rowOffs);
162 vertex.normal = QVector3D(0, 0, 0);
163 vertex.uv = QVector2D(
float(x) / (numCols - 1), 1.f -
float(y) / (numRows - 1));
164 vertices.push_back(vertex);
168 QVector<quint32> indices;
169 for (
int ix = 0; ix < numCols - 1; ++ix) {
170 for (
int iy = 0; iy < numRows - 1; ++iy) {
171 const int idx = iy + ix * numRows;
173 const auto tri0 = std::array<
int, 3> { idx + numRows + 1, idx + numRows, idx };
174 const auto tri1 = std::array<
int, 3> { idx + 1, idx + numRows + 1, idx };
176 for (
const auto [i0, i1, i2] : { tri0, tri1 }) {
177 indices.push_back(i0);
178 indices.push_back(i1);
179 indices.push_back(i2);
181 if (m_smoothShading) {
183 const QVector3D e0 = vertices[i1].position - vertices[i0].position;
184 const QVector3D e1 = vertices[i2].position - vertices[i0].position;
185 QVector3D normal = QVector3D::crossProduct(e0, e1).normalized();
188 vertices[i0].normal += normal;
189 vertices[i1].normal += normal;
190 vertices[i2].normal += normal;
196 if (m_smoothShading) {
198 for (
auto &vertex : vertices)
199 vertex.normal.normalize();
203 QVector3D boundsMin = vertices[0].position;
204 QVector3D boundsMax = vertices[0].position;
206 for (
const auto &vertex : vertices) {
207 const auto &p = vertex.position;
208 boundsMin = QVector3D(qMin(boundsMin.x(), p.x()), qMin(boundsMin.y(), p.y()), qMin(boundsMin.z(), p.z()));
209 boundsMax = QVector3D(qMax(boundsMax.x(), p.x()), qMax(boundsMax.y(), p.y()), qMax(boundsMax.z(), p.z()));
212 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::F32Type);
213 addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic,
sizeof(QVector3D) * 2, QQuick3DGeometry::Attribute::F32Type);
216 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
sizeof(QVector3D), QQuick3DGeometry::Attribute::F32Type);
218 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0, QQuick3DGeometry::Attribute::ComponentType::U32Type);
220 setStride(
sizeof(HeightFieldVertex));
221 QByteArray vertexBuffer(
reinterpret_cast<
char *>(vertices.data()), vertices.size() *
sizeof(HeightFieldVertex));
222 setVertexData(vertexBuffer);
223 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
224 setBounds(boundsMin, boundsMax);
226 QByteArray indexBuffer(
reinterpret_cast<
char *>(indices.data()), indices.size() *
sizeof(quint32));
227 setIndexData(indexBuffer);