QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsdemterraintilegeometry_p.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdemterraintilegeometry_p.cpp
3  --------------------------------------
4  Date : July 2017
5  Copyright : (C) 2017 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 #include <Qt3DRender/qattribute.h>
18 #include <Qt3DRender/qbuffer.h>
19 #include <Qt3DRender/qbufferdatagenerator.h>
20 #include <limits>
21 #include <cmath>
22 
24 
25 using namespace Qt3DRender;
26 
27 
28 static QByteArray createPlaneVertexData( int res, float skirtHeight, const QByteArray &heights )
29 {
30  Q_ASSERT( res >= 2 );
31  Q_ASSERT( heights.count() == res * res * ( int )sizeof( float ) );
32 
33  const float *zData = ( const float * ) heights.constData();
34  const float *zBits = zData;
35 
36  const int nVerts = ( res + 2 ) * ( res + 2 );
37 
38  // Populate a buffer with the interleaved per-vertex data with
39  // vec3 pos, vec2 texCoord, vec3 normal, vec4 tangent
40  const quint32 elementSize = 3 + 2 + 3;
41  const quint32 stride = elementSize * sizeof( float );
42  QByteArray bufferBytes;
43  bufferBytes.resize( stride * nVerts );
44  float *fptr = reinterpret_cast<float *>( bufferBytes.data() );
45 
46  float w = 1, h = 1;
47  QSize resolution( res, res );
48  const float x0 = -w / 2.0f;
49  const float z0 = -h / 2.0f;
50  const float dx = w / ( resolution.width() - 1 );
51  const float dz = h / ( resolution.height() - 1 );
52  const float du = 1.0 / ( resolution.width() - 1 );
53  const float dv = 1.0 / ( resolution.height() - 1 );
54 
55  // the height of vertices with no-data value... the value should not really matter
56  // as we do not create valid triangles that would use such vertices
57  const float noDataHeight = 0;
58 
59  // Iterate over z
60  for ( int j = -1; j <= resolution.height(); ++j )
61  {
62  int jBound = qBound( 0, j, resolution.height() - 1 );
63  const float z = z0 + static_cast<float>( jBound ) * dz;
64  const float v = static_cast<float>( jBound ) * dv;
65 
66  // Iterate over x
67  for ( int i = -1; i <= resolution.width(); ++i )
68  {
69  int iBound = qBound( 0, i, resolution.width() - 1 );
70  const float x = x0 + static_cast<float>( iBound ) * dx;
71  const float u = static_cast<float>( iBound ) * du;
72 
73  float height;
74  if ( i == iBound && j == jBound )
75  height = *zBits++;
76  else
77  height = zData[ jBound * resolution.width() + iBound ] - skirtHeight;
78 
79  if ( std::isnan( height ) )
80  height = noDataHeight;
81 
82  // position
83  *fptr++ = x;
84  *fptr++ = height;
85  *fptr++ = z;
86 
87  // texture coordinates
88  *fptr++ = u;
89  *fptr++ = v;
90 
91  // TODO: compute correct normals based on neighboring pixels
92  // normal
93  *fptr++ = 0.0f;
94  *fptr++ = 1.0f;
95  *fptr++ = 0.0f;
96  }
97  }
98 
99  return bufferBytes;
100 }
101 
102 inline int ijToHeightMapIndex( int i, int j, int numVerticesX, int numVerticesZ )
103 {
104  i = qBound( 1, i, numVerticesX - 1 ) - 1;
105  j = qBound( 1, j, numVerticesZ - 1 ) - 1;
106  return j * ( numVerticesX - 2 ) + i;
107 }
108 
109 
110 static bool hasNoData( int i, int j, const float *heightMap, int numVerticesX, int numVerticesZ )
111 {
112  return std::isnan( heightMap[ ijToHeightMapIndex( i, j, numVerticesX, numVerticesZ ) ] ) ||
113  std::isnan( heightMap[ ijToHeightMapIndex( i + 1, j, numVerticesX, numVerticesZ ) ] ) ||
114  std::isnan( heightMap[ ijToHeightMapIndex( i, j + 1, numVerticesX, numVerticesZ ) ] ) ||
115  std::isnan( heightMap[ ijToHeightMapIndex( i + 1, j + 1, numVerticesX, numVerticesZ ) ] );
116 }
117 
118 static QByteArray createPlaneIndexData( int res, const QByteArray &heightMap )
119 {
120  QSize resolution( res, res );
121  int numVerticesX = resolution.width() + 2;
122  int numVerticesZ = resolution.height() + 2;
123 
124  // Create the index data. 2 triangles per rectangular face
125  const int faces = 2 * ( numVerticesX - 1 ) * ( numVerticesZ - 1 );
126  const quint32 indices = 3 * faces;
127  Q_ASSERT( indices < std::numeric_limits<quint32>::max() );
128  QByteArray indexBytes;
129  indexBytes.resize( indices * sizeof( quint32 ) );
130  quint32 *indexPtr = reinterpret_cast<quint32 *>( indexBytes.data() );
131 
132  const float *heightMapFloat = reinterpret_cast<const float *>( heightMap.constData() );
133 
134  // Iterate over z
135  for ( int j = 0; j < numVerticesZ - 1; ++j )
136  {
137  const int rowStartIndex = j * numVerticesX;
138  const int nextRowStartIndex = ( j + 1 ) * numVerticesX;
139 
140  // Iterate over x
141  for ( int i = 0; i < numVerticesX - 1; ++i )
142  {
143  if ( hasNoData( i, j, heightMapFloat, numVerticesX, numVerticesZ ) )
144  {
145  // at least one corner of the quad has no-data value
146  // so let's make two invalid triangles
147  *indexPtr++ = rowStartIndex + i;
148  *indexPtr++ = rowStartIndex + i;
149  *indexPtr++ = rowStartIndex + i;
150 
151  *indexPtr++ = rowStartIndex + i;
152  *indexPtr++ = rowStartIndex + i;
153  *indexPtr++ = rowStartIndex + i;
154  continue;
155  }
156 
157  // Split quad into two triangles
158  *indexPtr++ = rowStartIndex + i;
159  *indexPtr++ = nextRowStartIndex + i;
160  *indexPtr++ = rowStartIndex + i + 1;
161 
162  *indexPtr++ = nextRowStartIndex + i;
163  *indexPtr++ = nextRowStartIndex + i + 1;
164  *indexPtr++ = rowStartIndex + i + 1;
165  }
166  }
167 
168  return indexBytes;
169 }
170 
171 
172 
174 class PlaneVertexBufferFunctor : public QBufferDataGenerator
175 {
176  public:
177  explicit PlaneVertexBufferFunctor( int resolution, float skirtHeight, const QByteArray &heightMap )
178  : mResolution( resolution )
179  , mSkirtHeight( skirtHeight )
180  , mHeightMap( heightMap )
181  {}
182 
183  QByteArray operator()() final
184  {
185  return createPlaneVertexData( mResolution, mSkirtHeight, mHeightMap );
186  }
187 
188  bool operator ==( const QBufferDataGenerator &other ) const final
189  {
190  const PlaneVertexBufferFunctor *otherFunctor = functor_cast<PlaneVertexBufferFunctor>( &other );
191  if ( otherFunctor != nullptr )
192  return ( otherFunctor->mResolution == mResolution &&
193  otherFunctor->mSkirtHeight == mSkirtHeight &&
194  otherFunctor->mHeightMap == mHeightMap );
195  return false;
196  }
197 
198  QT3D_FUNCTOR( PlaneVertexBufferFunctor )
199 
200  private:
201  int mResolution;
202  float mSkirtHeight;
203  QByteArray mHeightMap;
204 };
205 
206 
208 class PlaneIndexBufferFunctor : public QBufferDataGenerator
209 {
210  public:
211  explicit PlaneIndexBufferFunctor( int resolution, const QByteArray &heightMap )
212  : mResolution( resolution )
213  , mHeightMap( heightMap )
214  {}
215 
216  QByteArray operator()() final
217  {
218  return createPlaneIndexData( mResolution, mHeightMap );
219  }
220 
221  bool operator ==( const QBufferDataGenerator &other ) const final
222  {
223  const PlaneIndexBufferFunctor *otherFunctor = functor_cast<PlaneIndexBufferFunctor>( &other );
224  if ( otherFunctor != nullptr )
225  return ( otherFunctor->mResolution == mResolution );
226  return false;
227  }
228 
229  QT3D_FUNCTOR( PlaneIndexBufferFunctor )
230 
231  private:
232  int mResolution;
233  QByteArray mHeightMap;
234 };
235 
236 
237 // ------------
238 
239 
240 DemTerrainTileGeometry::DemTerrainTileGeometry( int resolution, float skirtHeight, const QByteArray &heightMap, DemTerrainTileGeometry::QNode *parent )
241  : QGeometry( parent )
242  , mResolution( resolution )
243  , mSkirtHeight( skirtHeight )
244  , mHeightMap( heightMap )
245 {
246  init();
247 }
248 
249 void DemTerrainTileGeometry::init()
250 {
251  mPositionAttribute = new QAttribute( this );
252  mNormalAttribute = new QAttribute( this );
253  mTexCoordAttribute = new QAttribute( this );
254  mIndexAttribute = new QAttribute( this );
255  mVertexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, this );
256  mIndexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::IndexBuffer, this );
257 
258  int nVertsX = mResolution + 2;
259  int nVertsZ = mResolution + 2;
260  const int nVerts = nVertsX * nVertsZ;
261  const int stride = ( 3 + 2 + 3 ) * sizeof( float );
262  const int faces = 2 * ( nVertsX - 1 ) * ( nVertsZ - 1 );
263 
264  mPositionAttribute->setName( QAttribute::defaultPositionAttributeName() );
265  mPositionAttribute->setVertexBaseType( QAttribute::Float );
266  mPositionAttribute->setVertexSize( 3 );
267  mPositionAttribute->setAttributeType( QAttribute::VertexAttribute );
268  mPositionAttribute->setBuffer( mVertexBuffer );
269  mPositionAttribute->setByteStride( stride );
270  mPositionAttribute->setCount( nVerts );
271 
272  mTexCoordAttribute->setName( QAttribute::defaultTextureCoordinateAttributeName() );
273  mTexCoordAttribute->setVertexBaseType( QAttribute::Float );
274  mTexCoordAttribute->setVertexSize( 2 );
275  mTexCoordAttribute->setAttributeType( QAttribute::VertexAttribute );
276  mTexCoordAttribute->setBuffer( mVertexBuffer );
277  mTexCoordAttribute->setByteStride( stride );
278  mTexCoordAttribute->setByteOffset( 3 * sizeof( float ) );
279  mTexCoordAttribute->setCount( nVerts );
280 
281  mNormalAttribute->setName( QAttribute::defaultNormalAttributeName() );
282  mNormalAttribute->setVertexBaseType( QAttribute::Float );
283  mNormalAttribute->setVertexSize( 3 );
284  mNormalAttribute->setAttributeType( QAttribute::VertexAttribute );
285  mNormalAttribute->setBuffer( mVertexBuffer );
286  mNormalAttribute->setByteStride( stride );
287  mNormalAttribute->setByteOffset( 5 * sizeof( float ) );
288  mNormalAttribute->setCount( nVerts );
289 
290  mIndexAttribute->setAttributeType( QAttribute::IndexAttribute );
291  mIndexAttribute->setVertexBaseType( QAttribute::UnsignedInt );
292  mIndexAttribute->setBuffer( mIndexBuffer );
293 
294  // Each primitive has 3 vertives
295  mIndexAttribute->setCount( faces * 3 );
296 
297  mVertexBuffer->setDataGenerator( QSharedPointer<PlaneVertexBufferFunctor>::create( mResolution, mSkirtHeight, mHeightMap ) ); // skip-keyword-check
298  mIndexBuffer->setDataGenerator( QSharedPointer<PlaneIndexBufferFunctor>::create( mResolution, mHeightMap ) ); // skip-keyword-check
299 
300  addAttribute( mPositionAttribute );
301  addAttribute( mTexCoordAttribute );
302  addAttribute( mNormalAttribute );
303  addAttribute( mIndexAttribute );
304 }
305 
bool operator==(const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2)