Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make coordinates of vector data chunks independent from scene origin #59174

Merged
merged 13 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions python/3d/auto_generated/symbols/qgspoint3dsymbol.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,9 @@ Returns transform for individual objects represented by the symbol
Sets transform for individual objects represented by the symbol
%End

QMatrix4x4 billboardTransform() const;
float billboardHeight() const;
%Docstring
Returns transform for billboards
Returns how much the billboard should be elevated upwards
%End

private:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,9 @@ Returns transform for individual objects represented by the symbol
Sets transform for individual objects represented by the symbol
%End

QMatrix4x4 billboardTransform() const;
float billboardHeight() const;
%Docstring
Returns transform for billboards
Returns how much the billboard should be elevated upwards
%End

private:
Expand Down
7 changes: 5 additions & 2 deletions src/3d/qgs3dutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ QMatrix4x4 Qgs3DUtils::stringToMatrix4x4( const QString &str )
return m;
}

void Qgs3DUtils::extractPointPositions( const QgsFeature &f, const Qgs3DRenderContext &context, Qgis::AltitudeClamping altClamp, QVector<QVector3D> &positions )
void Qgs3DUtils::extractPointPositions( const QgsFeature &f, const Qgs3DRenderContext &context, const QgsVector3D &chunkOrigin, Qgis::AltitudeClamping altClamp, QVector<QVector3D> &positions )
{
const QgsAbstractGeometry *g = f.geometry().constGet();
for ( auto it = g->vertices_begin(); it != g->vertices_end(); ++it )
Expand All @@ -513,7 +513,10 @@ void Qgs3DUtils::extractPointPositions( const QgsFeature &f, const Qgs3DRenderCo
h = terrainZ + geomZ;
break;
}
positions.append( QVector3D( pt.x() - context.origin().x(), h, -( pt.y() - context.origin().y() ) ) );
positions.append( QVector3D(
static_cast<float>( pt.x() - chunkOrigin.x() ),
static_cast<float>( pt.y() - chunkOrigin.y() ),
h ) );
QgsDebugMsgLevel( QStringLiteral( "%1 %2 %3" ).arg( positions.last().x() ).arg( positions.last().y() ).arg( positions.last().z() ), 2 );
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/3d/qgs3dutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class _3D_EXPORT Qgs3DUtils
static QMatrix4x4 stringToMatrix4x4( const QString &str );

//! Calculates (x,y,z) positions of (multi)point from the given feature
static void extractPointPositions( const QgsFeature &f, const Qgs3DRenderContext &context, Qgis::AltitudeClamping altClamp, QVector<QVector3D> &positions );
static void extractPointPositions( const QgsFeature &f, const Qgs3DRenderContext &context, const QgsVector3D &chunkOrigin, Qgis::AltitudeClamping altClamp, QVector<QVector3D> &positions );

/**
* Returns TRUE if bbox is completely outside the current viewing volume.
Expand Down
8 changes: 4 additions & 4 deletions src/3d/qgsfeature3dhandler_p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ void QgsFeature3DHandler::updateZRangeFromPositions( const QVector<QVector3D> &p
{
for ( const QVector3D &pos : positions )
{
if ( pos.y() < mZMin )
mZMin = pos.y();
if ( pos.y() > mZMax )
mZMax = pos.y();
if ( pos.z() < mZMin )
mZMin = pos.z();
if ( pos.z() > mZMax )
mZMax = pos.z();
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/3d/qgsfeature3dhandler_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class QgsFeature3DHandler
* Called before feature iteration starts to initialize, get required attributes.
* \returns TRUE on success (on FALSE the handler failed to initialize and processFeature() / finalize() should not be called
*/
virtual bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) = 0;
virtual bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsVector3D &chunkOrigin ) = 0;

/**
* Called for every feature to extract information out of it into some
Expand Down
10 changes: 9 additions & 1 deletion src/3d/qgsrubberband3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ QgsRubberBand3D::QgsRubberBand3D( Qgs3DMapSettings &map, QgsWindow3DEngine *engi

mLineEntity->addComponent( mLineMaterial );

Qt3DCore::QTransform *lineTransform = new Qt3DCore::QTransform;
lineTransform->setRotation( QQuaternion::fromAxisAndAngle( QVector3D( 1, 0, 0 ), -90 ) ); // flip map (x,y,z) to world (x,z,-y)
mLineEntity->addComponent( lineTransform );

// Rubberband vertex markers
mMarkerEntity = new Qt3DCore::QEntity( parentEntity );
mMarkerGeometry = new QgsBillboardGeometry();
Expand All @@ -99,6 +103,10 @@ QgsRubberBand3D::QgsRubberBand3D( Qgs3DMapSettings &map, QgsWindow3DEngine *engi
mMarkerSymbol = QgsMarkerSymbol::createSimple( props );
updateMarkerMaterial();
mMarkerEntity->addComponent( mMarkerGeometryRenderer );

Qt3DCore::QTransform *markerTransform = new Qt3DCore::QTransform;
markerTransform->setRotation( QQuaternion::fromAxisAndAngle( QVector3D( 1, 0, 0 ), -90 ) ); // flip map (x,y,z) to world (x,z,-y)
mMarkerEntity->addComponent( markerTransform );
}

QgsRubberBand3D::~QgsRubberBand3D()
Expand Down Expand Up @@ -167,7 +175,7 @@ void QgsRubberBand3D::updateGeometry()
{
QgsLineVertexData lineData;
lineData.withAdjacency = true;
lineData.init( Qgis::AltitudeClamping::Absolute, Qgis::AltitudeBinding::Vertex, 0, Qgs3DRenderContext::fromMapSettings( mMapSettings ) );
lineData.init( Qgis::AltitudeClamping::Absolute, Qgis::AltitudeBinding::Vertex, 0, Qgs3DRenderContext::fromMapSettings( mMapSettings ), mMapSettings->origin() );
lineData.addLineString( mLineString );

mPositionAttribute->buffer()->setData( lineData.createVertexBuffer() );
Expand Down
6 changes: 3 additions & 3 deletions src/3d/qgsrulebased3drenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,12 +262,12 @@ void QgsRuleBased3DRenderer::Rule::createHandlers( QgsVectorLayer *layer, QgsRul
}


void QgsRuleBased3DRenderer::Rule::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, QgsRuleBased3DRenderer::RuleToHandlerMap &handlers ) const
void QgsRuleBased3DRenderer::Rule::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsVector3D &chunkOrigin, QgsRuleBased3DRenderer::RuleToHandlerMap &handlers ) const
{
if ( mSymbol )
{
QgsFeature3DHandler *handler = handlers[this];
if ( !handler->prepare( context, attributeNames ) )
if ( !handler->prepare( context, attributeNames, chunkOrigin ) )
{
handlers.remove( this );
delete handler;
Expand All @@ -283,7 +283,7 @@ void QgsRuleBased3DRenderer::Rule::prepare( const Qgs3DRenderContext &context, Q
// call recursively
for ( Rule *rule : std::as_const( mChildren ) )
{
rule->prepare( context, attributeNames, handlers );
rule->prepare( context, attributeNames, chunkOrigin, handlers );
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/3d/qgsrulebased3drenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ class _3D_EXPORT QgsRuleBased3DRenderer : public QgsAbstractVectorLayer3DRendere
* call prepare() on handlers and populate attributeNames
* \note not available in Python bindings
*/
void prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, RuleToHandlerMap &handlers ) const SIP_SKIP;
void prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsVector3D &chunkOrigin, RuleToHandlerMap &handlers ) const SIP_SKIP;

/**
* register individual features
Expand Down
11 changes: 7 additions & 4 deletions src/3d/qgsrulebasedchunkloader_p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ QgsRuleBasedChunkLoader::QgsRuleBasedChunkLoader( const QgsRuleBasedChunkLoaderF

QgsVectorLayer *layer = mFactory->mLayer;

// only a subset of data to be queried
const QgsRectangle rect = Qgs3DUtils::worldToMapExtent( node->bbox(), mContext.origin() );
// origin for coordinates of the chunk - it is kind of arbitrary, but it should be
// picked so that the coordinates are relatively small to avoid numerical precision issues
QgsVector3D chunkOrigin( rect.center().x(), rect.center().y(), 0 );

QgsExpressionContext exprContext( Qgs3DUtils::globalProjectLayerExpressionContext( layer ) );
exprContext.setFields( layer->fields() );
mContext.setExpressionContext( exprContext );
Expand All @@ -63,15 +69,12 @@ QgsRuleBasedChunkLoader::QgsRuleBasedChunkLoader( const QgsRuleBasedChunkLoaderF
mRootRule->createHandlers( layer, mHandlers );

QSet<QString> attributeNames;
mRootRule->prepare( mContext, attributeNames, mHandlers );
mRootRule->prepare( mContext, attributeNames, chunkOrigin, mHandlers );

// build the feature request
QgsFeatureRequest req;
req.setDestinationCrs( mContext.crs(), mContext.transformContext() );
req.setSubsetOfAttributes( attributeNames, layer->fields() );

// only a subset of data to be queried
const QgsRectangle rect = Qgs3DUtils::worldToMapExtent( node->bbox(), mContext.origin() );
req.setFilterRect( rect );

//
Expand Down
36 changes: 0 additions & 36 deletions src/3d/qgstessellatedpolygongeometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,42 +82,6 @@ QgsTessellatedPolygonGeometry::QgsTessellatedPolygonGeometry( bool _withNormals,
}
}

void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &polygons, const QList<QgsFeatureId> &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon )
{
Q_ASSERT( polygons.count() == featureIds.count() );
mTriangleIndexStartingIndices.reserve( polygons.count() );
mTriangleIndexFids.reserve( polygons.count() );

QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals, mAddBackFaces, false, mAddTextureCoords );
for ( int i = 0; i < polygons.count(); ++i )
{
Q_ASSERT( tessellator.dataVerticesCount() % 3 == 0 );
const uint startingTriangleIndex = static_cast<uint>( tessellator.dataVerticesCount() / 3 );
mTriangleIndexStartingIndices.append( startingTriangleIndex );
mTriangleIndexFids.append( featureIds[i] );

QgsPolygon *polygon = polygons.at( i );
const float extr = extrusionHeightPerPolygon.isEmpty() ? extrusionHeight : extrusionHeightPerPolygon.at( i );
tessellator.addPolygon( *polygon, extr );
}
if ( !tessellator.error().isEmpty() )
{
QgsMessageLog::logMessage( tessellator.error(), QObject::tr( "3D" ) );
}

qDeleteAll( polygons );

const QByteArray data( ( const char * )tessellator.data().constData(), tessellator.data().count() * sizeof( float ) );
const int nVerts = data.count() / tessellator.stride();

mVertexBuffer->setData( data );
mPositionAttribute->setCount( nVerts );
if ( mNormalAttribute )
mNormalAttribute->setCount( nVerts );
if ( mAddTextureCoords )
mTextureCoordsAttribute->setCount( nVerts );
}

void QgsTessellatedPolygonGeometry::setData( const QByteArray &vertexBufferData, int vertexCount, const QVector<QgsFeatureId> &triangleIndexFids, const QVector<uint> &triangleIndexStartingIndices )
{
mTriangleIndexStartingIndices = triangleIndexStartingIndices;
Expand Down
3 changes: 0 additions & 3 deletions src/3d/qgstessellatedpolygongeometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,6 @@ class QgsTessellatedPolygonGeometry : public Qt3DCore::QGeometry
*/
void setAddTextureCoords( bool add ) { mAddTextureCoords = add; }

//! Initializes vertex buffer from given polygons. Takes ownership of passed polygon geometries
void setPolygons( const QList<QgsPolygon *> &polygons, const QList<QgsFeatureId> &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon = QList<float>() );

/**
* Initializes vertex buffer (and other members) from data that were already tessellated.
* This is an alternative to setPolygons() - this method does not do any expensive work in the body.
Expand Down
11 changes: 7 additions & 4 deletions src/3d/qgsvectorlayerchunkloader_p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,18 @@ QgsVectorLayerChunkLoader::QgsVectorLayerChunkLoader( const QgsVectorLayerChunkL
}
mHandler.reset( handler );

// only a subset of data to be queried
const QgsRectangle rect = Qgs3DUtils::worldToMapExtent( node->bbox(), mRenderContext.origin() );
// origin for coordinates of the chunk - it is kind of arbitrary, but it should be
// picked so that the coordinates are relatively small to avoid numerical precision issues
QgsVector3D chunkOrigin( rect.center().x(), rect.center().y(), 0 );

QgsExpressionContext exprContext( Qgs3DUtils::globalProjectLayerExpressionContext( layer ) );
exprContext.setFields( layer->fields() );
mRenderContext.setExpressionContext( exprContext );

QSet<QString> attributeNames;
if ( !mHandler->prepare( mRenderContext, attributeNames ) )
if ( !mHandler->prepare( mRenderContext, attributeNames, chunkOrigin ) )
{
QgsDebugError( QStringLiteral( "Failed to prepare 3D feature handler!" ) );
return;
Expand All @@ -76,9 +82,6 @@ QgsVectorLayerChunkLoader::QgsVectorLayerChunkLoader( const QgsVectorLayerChunkL
QgsCoordinateTransform( layer->crs3D(), mRenderContext.crs(), mRenderContext.transformContext() )
);
req.setSubsetOfAttributes( attributeNames, layer->fields() );

// only a subset of data to be queried
const QgsRectangle rect = Qgs3DUtils::worldToMapExtent( node->bbox(), mRenderContext.origin() );
req.setFilterRect( rect );

//
Expand Down
31 changes: 21 additions & 10 deletions src/3d/shaders/instanced.vert
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ in vec3 pos;
out vec3 worldPosition;
out vec3 worldNormal;

uniform mat4 modelView;
uniform mat3 modelViewNormal;
uniform mat4 modelViewProjection;
uniform mat4 modelMatrix;
uniform mat3 modelNormalMatrix;
uniform mat4 mvp;

uniform mat4 inst; // transform of individual object instance
uniform mat4 instNormal; // should be mat3 but Qt3D only supports mat4...
Expand All @@ -20,15 +20,26 @@ uniform mat4 instNormal; // should be mat3 but Qt3D only supports mat4...

void main()
{
// TODO: i think this is not entirely correct: the translation by "pos" works
// like this only because we assume that "inst" matrix only does translation/scale/rotation
// which all keep "w" set to 1. correctly we should use translation matrix...
vec4 offsetPos = inst * vec4(vertexPosition, 1.0) + vec4(pos, 0.0);
// vertexPosition uses XZ plane as the base plane, with Y going upwards
// and the coordinates are local to the object

worldNormal = normalize(mat3(instNormal) * vertexNormal);
worldPosition = vec3(offsetPos);
// first let's apply user defined transform for each object (translation, rotation, scaling)
vec3 vertexPositionObject = vec3(inst * vec4(vertexPosition, 1.0));
vec3 vertexNormalObject = mat3(instNormal) * vertexNormal;

gl_Position = modelViewProjection * offsetPos;
// next let's flip axes, so we have XY plane as the base plane (like in map coordinates)
vertexPositionObject = vec3(vertexPositionObject.x, -vertexPositionObject.z, vertexPositionObject.y);
vertexNormalObject = vec3(vertexNormalObject.x, -vertexNormalObject.z, vertexNormalObject.y);

// add offset of the object relative to the chunk's origin
vec3 vertexPositionChunk = vertexPositionObject + pos;

// Transform position and normal to world space
worldPosition = vec3(modelMatrix * vec4(vertexPositionChunk, 1.0));
worldNormal = normalize(modelNormalMatrix * vertexNormalObject);

// Calculate vertex position in clip coordinates
gl_Position = mvp * vec4(vertexPositionChunk, 1.0);

#ifdef CLIPPING
setClipDistance(worldPosition);
Expand Down
Loading
Loading