QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
qgstessellatedpolygongeometry.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgstessellatedpolygongeometry.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
18#include <QMatrix4x4>
19
20#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
21#include <Qt3DRender/QAttribute>
22#include <Qt3DRender/QBuffer>
23typedef Qt3DRender::QAttribute Qt3DQAttribute;
24typedef Qt3DRender::QBuffer Qt3DQBuffer;
25#else
26#include <Qt3DCore/QAttribute>
27#include <Qt3DCore/QBuffer>
28typedef Qt3DCore::QAttribute Qt3DQAttribute;
29typedef Qt3DCore::QBuffer Qt3DQBuffer;
30#endif
31
32#include "qgstessellator.h"
33#include "qgspolygon.h"
34
35QgsTessellatedPolygonGeometry::QgsTessellatedPolygonGeometry( bool _withNormals, bool _invertNormals, bool _addBackFaces, bool _addTextureCoords, QNode *parent )
36 : QGeometry( parent )
37 , mWithNormals( _withNormals )
38 , mInvertNormals( _invertNormals )
39 , mAddBackFaces( _addBackFaces )
40 , mAddTextureCoords( _addTextureCoords )
41{
42 mVertexBuffer = new Qt3DQBuffer( this );
43
44 const QgsTessellator tmpTess( 0, 0, mWithNormals, false, false, false, mAddTextureCoords );
45 const int stride = tmpTess.stride();
46
47 mPositionAttribute = new Qt3DQAttribute( this );
48 mPositionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
49 mPositionAttribute->setVertexBaseType( Qt3DQAttribute::Float );
50 mPositionAttribute->setVertexSize( 3 );
51 mPositionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
52 mPositionAttribute->setBuffer( mVertexBuffer );
53 mPositionAttribute->setByteStride( stride );
54 mPositionAttribute->setByteOffset( 0 );
55 addAttribute( mPositionAttribute );
56
57 if ( mWithNormals )
58 {
59 mNormalAttribute = new Qt3DQAttribute( this );
60 mNormalAttribute->setName( Qt3DQAttribute::defaultNormalAttributeName() );
61 mNormalAttribute->setVertexBaseType( Qt3DQAttribute::Float );
62 mNormalAttribute->setVertexSize( 3 );
63 mNormalAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
64 mNormalAttribute->setBuffer( mVertexBuffer );
65 mNormalAttribute->setByteStride( stride );
66 mNormalAttribute->setByteOffset( 3 * sizeof( float ) );
67 addAttribute( mNormalAttribute );
68 }
69 if ( mAddTextureCoords )
70 {
71 mTextureCoordsAttribute = new Qt3DQAttribute( this );
72 mTextureCoordsAttribute->setName( Qt3DQAttribute::defaultTextureCoordinateAttributeName() );
73 mTextureCoordsAttribute->setVertexBaseType( Qt3DQAttribute::Float );
74 mTextureCoordsAttribute->setVertexSize( 2 );
75 mTextureCoordsAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
76 mTextureCoordsAttribute->setBuffer( mVertexBuffer );
77 mTextureCoordsAttribute->setByteStride( stride );
78 mTextureCoordsAttribute->setByteOffset( mWithNormals ? 6 * sizeof( float ) : 3 * sizeof( float ) );
79 addAttribute( mTextureCoordsAttribute );
80 }
81}
82
83static bool intersectionTriangles( const QByteArray &vertexBuf, const int &stride, const QgsRayCastingUtils::Ray3D &r, const QMatrix4x4 &worldTransform, QVector3D &intPt, int &triangleIndex )
84{
85 const float *vertices = reinterpret_cast<const float *>( vertexBuf.constData() );
86
87 const int vertexCount = vertexBuf.size() / stride;
88 const int triangleCount = vertexCount / 3;
89
90 QVector3D intersectionPt, minIntersectionPt;
91 float minDistance = -1;
92
93 const int vertexSize = stride / sizeof( float );
94 const int triangleSize = 3 * vertexSize;
95 for ( int i = 0; i < triangleCount; ++i )
96 {
97 const int v0 = i * triangleSize, v1 = i * triangleSize + vertexSize, v2 = i * triangleSize + vertexSize + vertexSize;
98
99 const QVector3D a( vertices[v0], vertices[v0 + 1], vertices[v0 + 2] );
100 const QVector3D b( vertices[v1], vertices[v1 + 1], vertices[v1 + 2] );
101 const QVector3D c( vertices[v2], vertices[v2 + 1], vertices[v2 + 2] );
102
103 // Currently the worldTransform only has vertical offset, so this could be optimized by applying the transform
104 // to the ray and the resulting intersecting point instead of all triangles
105 // Need to check for potential performance gains.
106 const QVector3D tA = worldTransform * a;
107 const QVector3D tB = worldTransform * b;
108 const QVector3D tC = worldTransform * c;
109
110 QVector3D uvw;
111 float t = 0;
112
113 // We're testing both triangle orientations here and ignoring the culling mode.
114 // We should probably respect the culling mode used for the entity and perform a
115 // single test using the properly oriented triangle.
116 if ( QgsRayCastingUtils::rayTriangleIntersection( r, tA, tB, tC, uvw, t ) ||
117 QgsRayCastingUtils::rayTriangleIntersection( r, tA, tC, tB, uvw, t ) )
118 {
119 intersectionPt = r.point( t * r.distance() );
120 const float distance = r.projectedDistance( intersectionPt );
121
122 // we only want the first intersection of the ray with the mesh (closest to the ray origin)
123 if ( minDistance == -1 || distance < minDistance )
124 {
125 triangleIndex = static_cast<int>( i );
126 minDistance = distance;
127 minIntersectionPt = intersectionPt;
128 }
129 }
130 }
131
132 if ( minDistance != -1 )
133 {
134 intPt = minIntersectionPt;
135 return true;
136 }
137 else
138 return false;
139}
140
141void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &polygons, const QList<QgsFeatureId> &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon )
142{
143 Q_ASSERT( polygons.count() == featureIds.count() );
144 mTriangleIndexStartingIndices.reserve( polygons.count() );
145 mTriangleIndexFids.reserve( polygons.count() );
146
147 QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals, mAddBackFaces, false, mAddTextureCoords );
148 for ( int i = 0; i < polygons.count(); ++i )
149 {
150 Q_ASSERT( tessellator.dataVerticesCount() % 3 == 0 );
151 const uint startingTriangleIndex = static_cast<uint>( tessellator.dataVerticesCount() / 3 );
152 mTriangleIndexStartingIndices.append( startingTriangleIndex );
153 mTriangleIndexFids.append( featureIds[i] );
154
155 QgsPolygon *polygon = polygons.at( i );
156 const float extr = extrusionHeightPerPolygon.isEmpty() ? extrusionHeight : extrusionHeightPerPolygon.at( i );
157 tessellator.addPolygon( *polygon, extr );
158 }
159
160 qDeleteAll( polygons );
161
162 const QByteArray data( ( const char * )tessellator.data().constData(), tessellator.data().count() * sizeof( float ) );
163 const int nVerts = data.count() / tessellator.stride();
164
165 mVertexBuffer->setData( data );
166 mPositionAttribute->setCount( nVerts );
167 if ( mNormalAttribute )
168 mNormalAttribute->setCount( nVerts );
169 if ( mAddTextureCoords )
170 mTextureCoordsAttribute->setCount( nVerts );
171}
172
173void QgsTessellatedPolygonGeometry::setData( const QByteArray &vertexBufferData, int vertexCount, const QVector<QgsFeatureId> &triangleIndexFids, const QVector<uint> &triangleIndexStartingIndices )
174{
175 mTriangleIndexStartingIndices = triangleIndexStartingIndices;
176 mTriangleIndexFids = triangleIndexFids;
177
178 mVertexBuffer->setData( vertexBufferData );
179 mPositionAttribute->setCount( vertexCount );
180 if ( mNormalAttribute )
181 mNormalAttribute->setCount( vertexCount );
182 if ( mTextureCoordsAttribute )
183 mTextureCoordsAttribute->setCount( vertexCount );
184}
185
186bool QgsTessellatedPolygonGeometry::rayIntersection( const QgsRayCastingUtils::Ray3D &ray, const QMatrix4x4 &worldTransform, QVector3D &intersectionPoint, QgsFeatureId &fid )
187{
188 int triangleIndex = -1;
189 const int stride = static_cast<int>( mPositionAttribute->byteStride() );
190 bool success = intersectionTriangles( mVertexBuffer->data(), stride, ray, worldTransform, intersectionPoint, triangleIndex );
191
192 fid = success ? triangleIndexToFeatureId( triangleIndex ) : FID_NULL;
193
194 return success;
195}
196
197// run binary search on a sorted array, return index i where data[i] <= v < data[i+1]
198static int binary_search( uint v, const uint *data, int count )
199{
200 int idx0 = 0;
201 int idx1 = count - 1;
202
203 if ( v < data[0] )
204 return -1; // not in the array
205
206 if ( v >= data[count - 1] )
207 return count - 1; // for larger values the last bin is returned
208
209 while ( idx0 != idx1 )
210 {
211 const int idxPivot = ( idx0 + idx1 ) / 2;
212 const uint pivot = data[idxPivot];
213 if ( pivot <= v )
214 {
215 if ( data[idxPivot + 1] > v )
216 return idxPivot; // we're done!
217 else // continue searching values greater than the pivot
218 idx0 = idxPivot;
219 }
220 else // continue searching values lower than the pivot
221 idx1 = idxPivot;
222 }
223 return idx0;
224}
225
226
228{
229 const int i = binary_search( triangleIndex, mTriangleIndexStartingIndices.constData(), mTriangleIndexStartingIndices.count() );
230 return i != -1 ? mTriangleIndexFids[i] : FID_NULL;
231}
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Polygon geometry type.
Definition: qgspolygon.h:34
bool rayIntersection(const QgsRayCastingUtils::Ray3D &ray, const QMatrix4x4 &worldTransform, QVector3D &intersectionPoint, QgsFeatureId &fid)
Tests whether the geometry is intersected by ray.
void setPolygons(const QList< QgsPolygon * > &polygons, const QList< QgsFeatureId > &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList< float > &extrusionHeightPerPolygon=QList< float >())
Initializes vertex buffer from given polygons. Takes ownership of passed polygon geometries.
QgsFeatureId triangleIndexToFeatureId(uint triangleIndex) const
Returns ID of the feature to which given triangle index belongs (used for picking).
QgsTessellatedPolygonGeometry(bool _withNormals=true, bool invertNormals=false, bool addBackFaces=false, bool addTextureCoords=false, QNode *parent=nullptr)
Constructor.
void setData(const QByteArray &vertexBufferData, int vertexCount, const QVector< QgsFeatureId > &triangleIndexFids, const QVector< uint > &triangleIndexStartingIndices)
Initializes vertex buffer (and other members) from data that were already tessellated.
Class that takes care of tessellation of polygons into triangles.
QVector< float > data() const
Returns array of triangle vertex data.
int stride() const
Returns size of one vertex entry in bytes.
void addPolygon(const QgsPolygon &polygon, float extrusionHeight)
Tessellates a triangle and adds its vertex entries to the output data array.
int dataVerticesCount() const
Returns the number of vertices stored in the output data array.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define FID_NULL
Definition: qgsfeatureid.h:29
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer