35 #include <unordered_set>
37 static std::pair<float, float> rotateCoords(
float x,
float y,
float origin_x,
float origin_y,
float r )
39 r = qDegreesToRadians( r );
40 float x0 = x - origin_x, y0 = y - origin_y;
44 float x1 = origin_x + x0 * qCos( r ) - y0 * qSin( r );
45 float y1 = origin_y + x0 * qSin( r ) + y0 * qCos( r );
46 return std::make_pair( x1, y1 );
49 static void make_quad(
float x0,
float y0,
float z0,
float x1,
float y1,
float z1,
float height, QVector<float> &data,
bool addNormals,
bool addTextureCoords,
float textureRotation )
52 float dy = -( y1 - y0 );
55 QVector3D vn( -dy, 0, dx );
64 QVector<double> textureCoordinates;
65 textureCoordinates.reserve( 12 );
67 if ( fabsf( dy ) <= fabsf( dx ) )
98 textureCoordinates.push_back( u0 );
99 textureCoordinates.push_back( v0 );
101 textureCoordinates.push_back( u1 );
102 textureCoordinates.push_back( v1 );
104 textureCoordinates.push_back( u2 );
105 textureCoordinates.push_back( v2 );
107 textureCoordinates.push_back( u2 );
108 textureCoordinates.push_back( v2 );
110 textureCoordinates.push_back( u1 );
111 textureCoordinates.push_back( v1 );
113 textureCoordinates.push_back( u3 );
114 textureCoordinates.push_back( v3 );
116 for (
int i = 0; i < textureCoordinates.size(); i += 2 )
118 std::pair<float, float> rotated = rotateCoords( textureCoordinates[i], textureCoordinates[i + 1], 0, 0, textureRotation );
119 textureCoordinates[i] = rotated.first;
120 textureCoordinates[i + 1] = rotated.second;
125 data << x0 << z0 + height << -y0;
127 data << vn.x() << vn.y() << vn.z();
128 if ( addTextureCoords )
129 data << textureCoordinates[0] << textureCoordinates[1];
131 data << x1 << z1 + height << -y1;
133 data << vn.x() << vn.y() << vn.z();
134 if ( addTextureCoords )
135 data << textureCoordinates[2] << textureCoordinates[3];
137 data << x0 << z0 << -y0;
139 data << vn.x() << vn.y() << vn.z();
140 if ( addTextureCoords )
141 data << textureCoordinates[4] << textureCoordinates[5];
145 data << x0 << z0 << -y0;
147 data << vn.x() << vn.y() << vn.z();
148 if ( addTextureCoords )
149 data << textureCoordinates[6] << textureCoordinates[7];
151 data << x1 << z1 + height << -y1;
153 data << vn.x() << vn.y() << vn.z();
154 if ( addTextureCoords )
155 data << textureCoordinates[8] << textureCoordinates[9];
157 data << x1 << z1 << -y1;
159 data << vn.x() << vn.y() << vn.z();
160 if ( addTextureCoords )
161 data << textureCoordinates[10] << textureCoordinates[11];
166 bool addTextureCoords,
int facade,
float textureRotation )
167 : mOriginX( originX )
168 , mOriginY( originY )
169 , mAddNormals( addNormals )
170 , mInvertNormals( invertNormals )
171 , mAddBackFaces( addBackFaces )
172 , mAddTextureCoords( addTextureCoords )
174 , mTessellatedFacade( facade )
175 , mTextureRotation( textureRotation )
181 bool addTextureCoords,
int facade,
float textureRotation )
183 , mOriginX( mBounds.xMinimum() )
184 , mOriginY( mBounds.yMinimum() )
185 , mAddNormals( addNormals )
186 , mInvertNormals( invertNormals )
187 , mAddBackFaces( addBackFaces )
188 , mAddTextureCoords( addTextureCoords )
190 , mTessellatedFacade( facade )
191 , mTextureRotation( textureRotation )
196 void QgsTessellator::init()
198 mStride = 3 *
sizeof( float );
200 mStride += 3 *
sizeof( float );
201 if ( mAddTextureCoords )
202 mStride += 2 *
sizeof( float );
205 static bool _isRingCounterClockWise(
const QgsCurve &ring )
212 for (
int i = 1; i < count + 1; ++i )
214 ring.
pointAt( i % count, pt, vt );
215 a += ptPrev.
x() * pt.
y() - ptPrev.
y() * pt.
x();
221 static void _makeWalls(
const QgsLineString &ring,
bool ccw,
float extrusionHeight, QVector<float> &data,
222 bool addNormals,
bool addTextureCoords,
double originX,
double originY,
float textureRotation )
227 bool is_counter_clockwise = _isRingCounterClockWise( ring );
231 for (
int i = 1; i < ring.
numPoints(); ++i )
233 pt = ring.
pointN( is_counter_clockwise == ccw ? i : ring.
numPoints() - i - 1 );
234 float x0 = ptPrev.
x() - originX, y0 = ptPrev.
y() - originY;
235 float x1 = pt.
x() - originX, y1 = pt.
y() - originY;
236 float z0 = std::isnan( ptPrev.
z() ) ? 0 : ptPrev.
z();
237 float z1 = std::isnan( pt.
z() ) ? 0 : pt.
z();
240 make_quad( x0, y0, z0, x1, y1, z1, extrusionHeight, data, addNormals, addTextureCoords, textureRotation );
245 static QVector3D _calculateNormal(
const QgsLineString *curve,
double originX,
double originY,
bool invertNormal )
250 return QVector3D( 0, 0, 1 );
258 for (
int i = 1; i < curve->
numPoints(); i++ )
261 if ( pt1.
z() != pt2.
z() )
268 return QVector3D( 0, 0, 1 );
275 double nx = 0, ny = 0, nz = 0;
279 pt1.
setX( pt1.
x() - originX );
280 pt1.
setY( pt1.
y() - originY );
281 for (
int i = 1; i < curve->
numPoints(); i++ )
284 pt2.
setX( pt2.
x() - originX );
285 pt2.
setY( pt2.
y() - originY );
287 if ( std::isnan( pt1.
z() ) || std::isnan( pt2.
z() ) )
290 nx += ( pt1.
y() - pt2.
y() ) * ( pt1.
z() + pt2.
z() );
291 ny += ( pt1.
z() - pt2.
z() ) * ( pt1.
x() + pt2.
x() );
292 nz += ( pt1.
x() - pt2.
x() ) * ( pt1.
y() + pt2.
y() );
297 QVector3D normal( nx, ny, nz );
305 static void _normalVectorToXYVectors(
const QVector3D &pNormal, QVector3D &pXVector, QVector3D &pYVector )
310 if ( pNormal.z() > 0.001 || pNormal.z() < -0.001 )
312 pXVector = QVector3D( 1, 0, -pNormal.x() / pNormal.z() );
314 else if ( pNormal.y() > 0.001 || pNormal.y() < -0.001 )
316 pXVector = QVector3D( 1, -pNormal.x() / pNormal.y(), 0 );
320 pXVector = QVector3D( -pNormal.y() / pNormal.x(), 1, 0 );
322 pXVector.normalize();
323 pYVector = QVector3D::normal( pNormal, pXVector );
328 std::size_t
operator()(
const std::pair<float, float> pair )
const
330 std::size_t h1 = std::hash<float>()( pair.first );
331 std::size_t h2 = std::hash<float>()( pair.second );
337 static void _ringToPoly2tri(
const QgsLineString *ring, std::vector<p2t::Point *> &polyline, QHash<p2t::Point *, float> *zHash )
341 polyline.reserve( pCount );
343 const double *srcXData = ring->
xData();
344 const double *srcYData = ring->
yData();
345 const double *srcZData = ring->
zData();
346 std::unordered_set<std::pair<float, float>,
float_pair_hash> foundPoints;
348 for (
int i = 0; i < pCount - 1; ++i )
350 const float x = *srcXData++;
351 const float y = *srcYData++;
353 auto res = foundPoints.insert( std::make_pair( x, y ) );
360 p2t::Point *pt2 =
new p2t::Point( x, y );
361 polyline.push_back( pt2 );
364 ( *zHash )[pt2] = *srcZData++;
372 const double exp = 1e10;
373 return round( x * exp ) / exp;
377 static QgsCurve *_transform_ring_to_new_base(
const QgsLineString &curve,
const QgsPoint &pt0,
const QMatrix4x4 *toNewBase,
const float scale )
386 double *xData = x.data();
387 double *yData = y.data();
388 double *zData = z.data();
390 const double *srcXData = curve.
xData();
391 const double *srcYData = curve.
yData();
392 const double *srcZData = curve.
is3D() ? curve.
zData() :
nullptr;
394 for (
int i = 0; i < count; ++i )
396 QVector4D v( *srcXData++ - pt0.
x(),
397 *srcYData++ - pt0.
y(),
398 srcZData ? *srcZData++ - pt0.
z() : 0,
401 v = toNewBase->map( v );
404 v.setX( v.x() * scale );
405 v.setY( v.y() * scale );
425 static QgsPolygon *_transform_polygon_to_new_base(
const QgsPolygon &polygon,
const QgsPoint &pt0,
const QMatrix4x4 *toNewBase,
const float scale )
428 p->
setExteriorRing( _transform_ring_to_new_base( *qgsgeometry_cast< const QgsLineString * >( polygon.
exteriorRing() ), pt0, toNewBase, scale ) );
430 p->
addInteriorRing( _transform_ring_to_new_base( *qgsgeometry_cast< const QgsLineString * >( polygon.
interiorRing( i ) ), pt0, toNewBase, scale ) );
434 static bool _check_intersecting_rings(
const QgsPolygon &polygon )
436 std::vector< std::unique_ptr< QgsGeometryEngine > > ringEngines;
446 for (
const std::unique_ptr< QgsGeometryEngine > &ring : ringEngines )
448 if ( !ring->isSimple() )
469 if ( ringEngines.size() > 1 )
471 for (
size_t i = 0; i < ringEngines.size(); ++i )
473 std::unique_ptr< QgsGeometryEngine > &first = ringEngines.at( i );
475 first->prepareGeometry();
482 for (
int interiorRing =
static_cast< int >( i ); interiorRing < polygon.
numInteriorRings(); ++interiorRing )
484 if ( first->intersects( polygon.
interiorRing( interiorRing ) ) )
497 std::vector< const QgsLineString * > rings;
499 rings.emplace_back( qgsgeometry_cast< const QgsLineString * >( polygon.
exteriorRing() ) );
501 rings.emplace_back( qgsgeometry_cast< const QgsLineString * >( polygon.
interiorRing( i ) ) );
506 if ( numPoints <= 1 )
509 const double *srcXData = ring->
xData();
510 const double *srcYData = ring->
yData();
511 double x0 = *srcXData++;
512 double y0 = *srcYData++;
513 for (
int i = 1; i < numPoints; ++i )
515 double x1 = *srcXData++;
516 double y1 = *srcYData++;
517 double d = ( x0 - x1 ) * ( x0 - x1 ) + ( y0 - y1 ) * ( y0 - y1 );
525 return min_d != 1e20 ? std::sqrt( min_d ) : 1e20;
534 const QVector3D pNormal = !mNoZ ? _calculateNormal( exterior, mOriginX, mOriginY, mInvertNormals ) : QVector3D();
535 const int pCount = exterior->
numPoints();
539 float zMin = std::numeric_limits<float>::max();
540 float zMax = std::numeric_limits<float>::min();
542 const float scale = mBounds.
isNull() ? 1.0 : std::max( 10000.0 / mBounds.
width(), 10000.0 / mBounds.
height() );
544 std::unique_ptr<QMatrix4x4> toNewBase, toOldBase;
546 std::unique_ptr<QgsPolygon> polygonNew;
547 auto rotatePolygonToXYPlane = [&]()
549 if ( !mNoZ && pNormal != QVector3D( 0, 0, 1 ) )
553 QVector3D pXVector, pYVector;
554 _normalVectorToXYVectors( pNormal, pXVector, pYVector );
559 toNewBase.reset(
new QMatrix4x4(
560 pXVector.x(), pXVector.y(), pXVector.z(), 0,
561 pYVector.x(), pYVector.y(), pYVector.z(), 0,
562 pNormal.x(), pNormal.y(), pNormal.z(), 0,
566 toOldBase.reset(
new QMatrix4x4( toNewBase->transposed() ) );
575 polygonNew.reset( _transform_polygon_to_new_base( polygon, pt0, toNewBase.get(), scale ) );
581 QVector3D upVector( 0, 0, 1 );
582 float pNormalUpVectorDotProduct = QVector3D::dotProduct( upVector, pNormal );
583 float radsBetwwenUpNormal = qAcos( pNormalUpVectorDotProduct );
585 float detectionDelta = qDegreesToRadians( 10.0f );
587 if ( radsBetwwenUpNormal > M_PI_2 - detectionDelta && radsBetwwenUpNormal < M_PI_2 + detectionDelta ) facade = 1;
588 else if ( radsBetwwenUpNormal > - M_PI_2 - detectionDelta && radsBetwwenUpNormal < -M_PI_2 + detectionDelta ) facade = 1;
591 if ( pCount == 4 && polygon.
numInteriorRings() == 0 && ( mTessellatedFacade & facade ) )
594 if ( mAddTextureCoords )
596 rotatePolygonToXYPlane();
597 triangle = qgsgeometry_cast< QgsLineString * >( polygonNew->exteriorRing() );
598 Q_ASSERT( polygonNew->exteriorRing()->numPoints() >= 3 );
602 const double *xData = exterior->
xData();
603 const double *yData = exterior->
yData();
604 const double *zData = !mNoZ ? exterior->
zData() :
nullptr;
605 for (
int i = 0; i < 3; i++ )
607 float z = mNoZ ? 0 : *zData;
613 mData << *xData - mOriginX << z << - *yData + mOriginY;
615 mData << pNormal.x() << pNormal.z() << - pNormal.y();
616 if ( mAddTextureCoords )
618 std::pair<float, float> p( triangle->
xAt( i ), triangle->
yAt( i ) );
621 p = rotateCoords( p.first, p.second, 0.0f, 0.0f, mTextureRotation );
623 else if ( facade & 2 )
625 p = rotateCoords( p.first, p.second, 0.0f, 0.0f, mTextureRotation );
627 mData << p.first << p.second;
629 xData++; yData++; zData++;
635 for (
int i = 2; i >= 0; i-- )
637 mData << exterior->
xAt( i ) - mOriginX << ( mNoZ ? 0 : exterior->
zAt( i ) ) << - exterior->
yAt( i ) + mOriginY;
639 mData << -pNormal.x() << -pNormal.z() << pNormal.y();
640 if ( mAddTextureCoords )
642 std::pair<float, float> p( triangle->
xAt( i ), triangle->
yAt( i ) );
645 p = rotateCoords( p.first, p.second, 0.0f, 0.0f, mTextureRotation );
647 else if ( facade & 2 )
649 p = rotateCoords( p.first, p.second, 0.0f, 0.0f, mTextureRotation );
651 mData << p.first << p.second;
656 else if ( mTessellatedFacade & facade )
659 rotatePolygonToXYPlane();
668 if ( polygonSimplified.
isNull() )
673 const QgsPolygon *polygonSimplifiedData = qgsgeometry_cast<const QgsPolygon *>( polygonSimplified.
constGet() );
678 QgsMessageLog::logMessage( QObject::tr(
"geometry's coordinates are too close to each other and simplification failed - skipping" ), QObject::tr(
"3D" ) );
683 polygonNew.reset( polygonSimplifiedData->
clone() );
687 if ( !_check_intersecting_rings( *polygonNew ) )
690 QgsMessageLog::logMessage( QObject::tr(
"polygon rings self-intersect or intersect each other - skipping" ), QObject::tr(
"3D" ) );
694 QList< std::vector<p2t::Point *> > polylinesToDelete;
695 QHash<p2t::Point *, float> z;
698 std::vector<p2t::Point *> polyline;
699 _ringToPoly2tri( qgsgeometry_cast< const QgsLineString * >( polygonNew->exteriorRing() ), polyline, mNoZ ?
nullptr : &z );
700 polylinesToDelete << polyline;
702 std::unique_ptr<p2t::CDT> cdt(
new p2t::CDT( polyline ) );
705 for (
int i = 0; i < polygonNew->numInteriorRings(); ++i )
707 std::vector<p2t::Point *> holePolyline;
708 const QgsLineString *hole = qgsgeometry_cast< const QgsLineString *>( polygonNew->interiorRing( i ) );
710 _ringToPoly2tri( hole, holePolyline, mNoZ ?
nullptr : &z );
712 cdt->AddHole( holePolyline );
713 polylinesToDelete << holePolyline;
721 std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();
723 mData.reserve( mData.size() + 3 * triangles.size() * (
stride() /
sizeof(
float ) ) );
724 for (
size_t i = 0; i < triangles.size(); ++i )
726 p2t::Triangle *t = triangles[i];
727 for (
int j = 0; j < 3; ++j )
729 p2t::Point *p = t->GetPoint( j );
730 QVector4D pt( p->x, p->y, mNoZ ? 0 : z[p], 0 );
732 pt = *toOldBase * pt;
733 const double fx = ( pt.x() / scale ) - mOriginX + pt0.
x();
734 const double fy = ( pt.y() / scale ) - mOriginY + pt0.
y();
735 const double fz = mNoZ ? 0 : ( pt.z() + extrusionHeight + pt0.
z() );
741 mData << fx << fz << -fy;
743 mData << pNormal.x() << pNormal.z() << - pNormal.y();
744 if ( mAddTextureCoords )
746 std::pair<float, float> pr = rotateCoords( p->x, p->y, 0.0f, 0.0f, mTextureRotation );
747 mData << pr.first << pr.second;
754 for (
int j = 2; j >= 0; --j )
756 p2t::Point *p = t->GetPoint( j );
757 QVector4D pt( p->x, p->y, mNoZ ? 0 : z[p], 0 );
759 pt = *toOldBase * pt;
760 const double fx = ( pt.x() / scale ) - mOriginX + pt0.
x();
761 const double fy = ( pt.y() / scale ) - mOriginY + pt0.
y();
762 const double fz = mNoZ ? 0 : ( pt.z() + extrusionHeight + pt0.
z() );
763 mData << fx << fz << -fy;
765 mData << -pNormal.x() << -pNormal.z() << pNormal.y();
766 if ( mAddTextureCoords )
768 std::pair<float, float> pr = rotateCoords( p->x, p->y, 0.0f, 0.0f, mTextureRotation );
769 mData << pr.first << pr.second;
780 for (
int i = 0; i < polylinesToDelete.count(); ++i )
781 qDeleteAll( polylinesToDelete[i] );
785 if ( extrusionHeight != 0 && ( mTessellatedFacade & 1 ) )
787 _makeWalls( *exterior,
false, extrusionHeight, mData, mAddNormals, mAddTextureCoords, mOriginX, mOriginY, mTextureRotation );
790 _makeWalls( *qgsgeometry_cast< const QgsLineString * >( polygon.
interiorRing( i ) ),
true, extrusionHeight, mData, mAddNormals, mAddTextureCoords, mOriginX, mOriginY, mTextureRotation );
792 zMax += extrusionHeight;
815 return mData.size() / (
stride() /
sizeof( float ) );
820 std::unique_ptr< QgsMultiPolygon > mp = qgis::make_unique< QgsMultiPolygon >();
821 const QVector<float>
data = mData;
822 mp->reserve( mData.size() );
823 for (
auto it =
data.constBegin(); it !=
data.constEnd(); )