Skip to content
Permalink
Browse files

Calculate normals of terrain tile vertices

  • Loading branch information
wonder-sk committed Nov 11, 2018
1 parent eab40d0 commit cf24e654b8a954c2f6ba48ca2bd01aabc1d3e837
@@ -27,7 +27,7 @@
using namespace Qt3DRender;


static QByteArray createPlaneVertexData( int res, float skirtHeight, const QByteArray &heights )
static QByteArray createPlaneVertexData( int res, float side, float vertScale, float skirtHeight, const QByteArray &heights )
{
Q_ASSERT( res >= 2 );
Q_ASSERT( heights.count() == res * res * static_cast<int>( sizeof( float ) ) );
@@ -58,17 +58,20 @@ static QByteArray createPlaneVertexData( int res, float skirtHeight, const QByte
// as we do not create valid triangles that would use such vertices
const float noDataHeight = 0;

const int iMax = resolution.width() - 1;
const int jMax = resolution.height() - 1;

// Iterate over z
for ( int j = -1; j <= resolution.height(); ++j )
{
int jBound = qBound( 0, j, resolution.height() - 1 );
int jBound = qBound( 0, j, jMax );
const float z = z0 + static_cast<float>( jBound ) * dz;
const float v = static_cast<float>( jBound ) * dv;

// Iterate over x
for ( int i = -1; i <= resolution.width(); ++i )
{
int iBound = qBound( 0, i, resolution.width() - 1 );
int iBound = qBound( 0, i, iMax );
const float x = x0 + static_cast<float>( iBound ) * dx;
const float u = static_cast<float>( iBound ) * du;

@@ -83,18 +86,49 @@ static QByteArray createPlaneVertexData( int res, float skirtHeight, const QByte

// position
*fptr++ = x;
*fptr++ = height;
*fptr++ = height / side * vertScale;
*fptr++ = z;

// texture coordinates
*fptr++ = u;
*fptr++ = v;

// TODO: compute correct normals based on neighboring pixels
// normal
*fptr++ = 0.0f;
*fptr++ = 1.0f;
*fptr++ = 0.0f;
// calculate normal coordinates
#define zAt( ii, jj ) zData[ jj * resolution.width() + ii ] * vertScale
float zi0 = zAt( qBound( 0, i - 1, iMax ), jBound );
float zi1 = zAt( qBound( 0, i + 1, iMax ), jBound );
float zj0 = zAt( iBound, qBound( 0, j - 1, jMax ) );
float zj1 = zAt( iBound, qBound( 0, j + 1, jMax ) );

QVector3D n;
if ( std::isnan( zi0 ) || std::isnan( zi1 ) || std::isnan( zj0 ) || std::isnan( zj1 ) )
n = QVector3D( 0, 1, 0 );
else
{
float di, dj;
float zij = height * vertScale;

if ( i == 0 )
di = 2 * ( zij - zi1 );
else if ( i == iMax )
di = 2 * ( zi0 - zij );
else
di = zi0 - zi1;

if ( j == 0 )
dj = 2 * ( zij - zj1 );
else if ( j == jMax )
dj = 2 * ( zj0 - zij );
else
dj = zj0 - zj1;

n = QVector3D( di, 2 * side / res, dj );
n.normalize();
}

*fptr++ = n.x();
*fptr++ = n.y();
*fptr++ = n.z();
}
}

@@ -176,22 +210,26 @@ static QByteArray createPlaneIndexData( int res, const QByteArray &heightMap )
class PlaneVertexBufferFunctor : public QBufferDataGenerator
{
public:
explicit PlaneVertexBufferFunctor( int resolution, float skirtHeight, const QByteArray &heightMap )
explicit PlaneVertexBufferFunctor( int resolution, float side, float vertScale, float skirtHeight, const QByteArray &heightMap )
: mResolution( resolution )
, mSide( side )
, mVertScale( vertScale )
, mSkirtHeight( skirtHeight )
, mHeightMap( heightMap )
{}

QByteArray operator()() final
{
return createPlaneVertexData( mResolution, mSkirtHeight, mHeightMap );
return createPlaneVertexData( mResolution, mSide, mVertScale, mSkirtHeight, mHeightMap );
}

bool operator ==( const QBufferDataGenerator &other ) const final
{
const PlaneVertexBufferFunctor *otherFunctor = functor_cast<PlaneVertexBufferFunctor>( &other );
if ( otherFunctor != nullptr )
return ( otherFunctor->mResolution == mResolution &&
otherFunctor->mSide == mSide &&
otherFunctor->mVertScale == mVertScale &&
otherFunctor->mSkirtHeight == mSkirtHeight &&
otherFunctor->mHeightMap == mHeightMap );
return false;
@@ -201,6 +239,8 @@ class PlaneVertexBufferFunctor : public QBufferDataGenerator

private:
int mResolution;
float mSide;
float mVertScale;
float mSkirtHeight;
QByteArray mHeightMap;
};
@@ -239,9 +279,11 @@ class PlaneIndexBufferFunctor : public QBufferDataGenerator
// ------------


DemTerrainTileGeometry::DemTerrainTileGeometry( int resolution, float skirtHeight, const QByteArray &heightMap, DemTerrainTileGeometry::QNode *parent )
DemTerrainTileGeometry::DemTerrainTileGeometry( int resolution, float side, float vertScale, float skirtHeight, const QByteArray &heightMap, DemTerrainTileGeometry::QNode *parent )
: QGeometry( parent )
, mResolution( resolution )
, mSide( side )
, mVertScale( vertScale )
, mSkirtHeight( skirtHeight )
, mHeightMap( heightMap )
{
@@ -357,7 +399,7 @@ void DemTerrainTileGeometry::init()

// switched to setting data instead of just setting data generators because we also need the buffers
// available for ray-mesh intersections and we can't access the private copy of data in Qt (if there is any)
mVertexBuffer->setData( PlaneVertexBufferFunctor( mResolution, mSkirtHeight, mHeightMap )() );
mVertexBuffer->setData( PlaneVertexBufferFunctor( mResolution, mSide, mVertScale, mSkirtHeight, mHeightMap )() );
mIndexBuffer->setData( PlaneIndexBufferFunctor( mResolution, mHeightMap )() );

addAttribute( mPositionAttribute );
@@ -59,14 +59,16 @@ class DemTerrainTileGeometry : public Qt3DRender::QGeometry
* Constructs a terrain tile geometry. Resolution is the number of vertices on one side of the tile,
* heightMap is array of float values with one height value for each vertex
*/
explicit DemTerrainTileGeometry( int resolution, float skirtHeight, const QByteArray &heightMap, QNode *parent = nullptr );
explicit DemTerrainTileGeometry( int resolution, float side, float vertScale, float skirtHeight, const QByteArray &heightMap, QNode *parent = nullptr );

bool rayIntersection( const QgsRayCastingUtils::Ray3D &ray, const QMatrix4x4 &worldTransform, QVector3D &intersectionPoint );

private:
void init();

int mResolution;
float mSide;
float mVertScale;
float mSkirtHeight;
QByteArray mHeightMap;
Qt3DRender::QAttribute *mPositionAttribute = nullptr;
@@ -76,12 +76,20 @@ Qt3DCore::QEntity *QgsDemTerrainTileLoader::createEntity( Qt3DCore::QEntity *par
return nullptr;
}

const Qgs3DMapSettings &map = terrain()->map3D();
QgsRectangle extent = map.terrainGenerator()->tilingScheme().tileToExtent( mNode->tileX(), mNode->tileY(), mNode->tileZ() ); //node->extent;
double x0 = extent.xMinimum() - map.origin().x();
double y0 = extent.yMinimum() - map.origin().y();
double side = extent.width();
double half = side / 2;


QgsTerrainTileEntity *entity = new QgsTerrainTileEntity;

// create geometry renderer

Qt3DRender::QGeometryRenderer *mesh = new Qt3DRender::QGeometryRenderer;
mesh->setGeometry( new DemTerrainTileGeometry( mResolution, mSkirtHeight, mHeightMap, mesh ) );
mesh->setGeometry( new DemTerrainTileGeometry( mResolution, side, map.terrainVerticalScale(), mSkirtHeight, mHeightMap, mesh ) );
entity->addComponent( mesh ); // takes ownership if the component has no parent

// create material
@@ -94,14 +102,7 @@ Qt3DCore::QEntity *QgsDemTerrainTileLoader::createEntity( Qt3DCore::QEntity *par
transform = new Qt3DCore::QTransform();
entity->addComponent( transform );

const Qgs3DMapSettings &map = terrain()->map3D();
QgsRectangle extent = map.terrainGenerator()->tilingScheme().tileToExtent( mNode->tileX(), mNode->tileY(), mNode->tileZ() ); //node->extent;
double x0 = extent.xMinimum() - map.origin().x();
double y0 = extent.yMinimum() - map.origin().y();
double side = extent.width();
double half = side / 2;

transform->setScale3D( QVector3D( side, map.terrainVerticalScale(), side ) );
transform->setScale( side );
transform->setTranslation( QVector3D( x0 + half, 0, - ( y0 + half ) ) );

mNode->setExactBbox( QgsAABB( x0, zMin * map.terrainVerticalScale(), -y0, x0 + side, zMax * map.terrainVerticalScale(), -( y0 + side ) ) );

0 comments on commit cf24e65

Please sign in to comment.
You can’t perform that action at this time.