9
10
11
12
13
14
15
16
17
18
19
22
23
24
25
26
29
30
31
32
33
34
35
38
39
40
43
44
45
46
47
48
51HeightFieldGeometry::HeightFieldGeometry()
56const QUrl &HeightFieldGeometry::source()
const
58 return m_heightMapSource;
61void HeightFieldGeometry::setSource(
const QUrl &newSource)
63 if (m_heightMapSource == newSource)
65 m_heightMapSource = newSource;
69 emit geometryChanged();
74bool HeightFieldGeometry::smoothShading()
const
76 return m_smoothShading;
79void HeightFieldGeometry::setSmoothShading(
bool smooth)
81 if (m_smoothShading == smooth)
83 m_smoothShading = smooth;
87 emit geometryChanged();
89 emit smoothShadingChanged();
92const QVector3D &HeightFieldGeometry::extents()
const
97void HeightFieldGeometry::setExtents(
const QVector3D &newExtents)
99 m_extentsSetExplicitly =
true;
100 if (m_extents == newExtents)
102 m_extents = newExtents;
106 emit geometryChanged();
108 emit extentsChanged();
112struct HeightFieldVertex
120void HeightFieldGeometry::updateData()
122 const QQmlContext *context = qmlContext(
this);
124 const auto resolvedUrl = context ? context->resolvedUrl(m_heightMapSource) : m_heightMapSource;
125 if (!resolvedUrl.isValid())
129 const auto qmlSource = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
131 QImage heightMap(qmlSource);
132 int numRows = heightMap.height();
133 int numCols = heightMap.width();
135 if (numRows < 2 || numCols < 2)
138 const int numVertices = numRows * numCols;
140 if (!m_extentsSetExplicitly) {
141 auto prevExt = m_extents;
142 if (numRows == numCols) {
143 m_extents = {100, 100, 100};
144 }
else if (numRows < numCols) {
145 float f =
float(numRows) /
float(numCols);
146 m_extents = {100.f, 100.f, 100.f * f};
148 float f =
float(numCols) /
float(numRows);
149 m_extents = {100.f * f, 100.f, 100.f};
151 if (m_extents != prevExt) {
152 emit extentsChanged();
156 QVector<HeightFieldVertex> vertices;
157 vertices.reserve(numVertices);
159 const float rowF = m_extents.z() / (numRows - 1);
160 const float rowOffs = -m_extents.z() / 2;
161 const float colF = m_extents.x() / (numCols - 1);
162 const float colOffs = -m_extents.x() / 2;
163 for (
int x = 0; x < numCols; x++) {
164 for (
int y = 0; y < numRows; y++) {
165 float f = heightMap.pixelColor(x, y).valueF() - 0.5;
166 HeightFieldVertex vertex;
167 vertex.position = QVector3D(x * colF + colOffs, f * m_extents.y(), y * rowF + rowOffs);
168 vertex.normal = QVector3D(0, 0, 0);
169 vertex.uv = QVector2D(
float(x) / (numCols - 1), 1.f -
float(y) / (numRows - 1));
170 vertices.push_back(vertex);
174 QVector<quint32> indices;
175 for (
int ix = 0; ix < numCols - 1; ++ix) {
176 for (
int iy = 0; iy < numRows - 1; ++iy) {
177 const int idx = iy + ix * numRows;
179 const auto tri0 = std::array<
int, 3> { idx + numRows + 1, idx + numRows, idx };
180 const auto tri1 = std::array<
int, 3> { idx + 1, idx + numRows + 1, idx };
182 for (
const auto [i0, i1, i2] : { tri0, tri1 }) {
183 indices.push_back(i0);
184 indices.push_back(i1);
185 indices.push_back(i2);
187 if (m_smoothShading) {
189 const QVector3D e0 = vertices[i1].position - vertices[i0].position;
190 const QVector3D e1 = vertices[i2].position - vertices[i0].position;
191 QVector3D normal = QVector3D::crossProduct(e0, e1).normalized();
194 vertices[i0].normal += normal;
195 vertices[i1].normal += normal;
196 vertices[i2].normal += normal;
202 if (m_smoothShading) {
204 for (
auto &vertex : vertices)
205 vertex.normal.normalize();
209 QVector3D boundsMin = vertices[0].position;
210 QVector3D boundsMax = vertices[0].position;
212 for (
const auto &vertex : vertices) {
213 const auto &p = vertex.position;
214 boundsMin = QVector3D(qMin(boundsMin.x(), p.x()), qMin(boundsMin.y(), p.y()), qMin(boundsMin.z(), p.z()));
215 boundsMax = QVector3D(qMax(boundsMax.x(), p.x()), qMax(boundsMax.y(), p.y()), qMax(boundsMax.z(), p.z()));
218 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::F32Type);
219 addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic,
sizeof(QVector3D) * 2, QQuick3DGeometry::Attribute::F32Type);
222 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
sizeof(QVector3D), QQuick3DGeometry::Attribute::F32Type);
224 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0, QQuick3DGeometry::Attribute::ComponentType::U32Type);
226 setStride(
sizeof(HeightFieldVertex));
227 QByteArray vertexBuffer(
reinterpret_cast<
char *>(vertices.data()), vertices.size() *
sizeof(HeightFieldVertex));
228 setVertexData(vertexBuffer);
229 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
230 setBounds(boundsMin, boundsMax);
232 QByteArray indexBuffer(
reinterpret_cast<
char *>(indices.data()), indices.size() *
sizeof(quint32));
233 setIndexData(indexBuffer);