mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-07 00:15:48 -04:00
[FEATURE] rendering of 3d wide lines, rendering of polygon edges
This commit is contained in:
parent
d15cae9ae2
commit
28b349f04a
@ -35,6 +35,8 @@ SET(QGIS_3D_SRCS
|
||||
symbols/qgsabstract3dsymbol.cpp
|
||||
symbols/qgsline3dsymbol.cpp
|
||||
symbols/qgsline3dsymbol_p.cpp
|
||||
symbols/qgslinematerial_p.cpp
|
||||
symbols/qgslinevertexdata_p.cpp
|
||||
symbols/qgsmesh3dsymbol.cpp
|
||||
symbols/qgsmesh3dsymbol_p.cpp
|
||||
symbols/qgspoint3dsymbol.cpp
|
||||
@ -75,6 +77,8 @@ SET(QGIS_3D_MOC_HDRS
|
||||
|
||||
processing/qgs3dalgorithms.h
|
||||
|
||||
symbols/qgslinematerial_p.h
|
||||
|
||||
terrain/qgsdemterraintileloader_p.h
|
||||
terrain/qgsflatterraingenerator.h
|
||||
terrain/qgsterrainentity_p.h
|
||||
@ -122,6 +126,8 @@ SET(QGIS_3D_HDRS
|
||||
symbols/qgsabstract3dsymbol.h
|
||||
symbols/qgsline3dsymbol.h
|
||||
symbols/qgsline3dsymbol_p.h
|
||||
symbols/qgslinematerial_p.h
|
||||
symbols/qgslinevertexdata_p.h
|
||||
symbols/qgsmesh3dsymbol.h
|
||||
symbols/qgsmesh3dsymbol_p.h
|
||||
symbols/qgspoint3dsymbol.h
|
||||
|
@ -50,6 +50,8 @@
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsvectorlayer3drenderer.h"
|
||||
|
||||
#include "qgslinematerial_p.h"
|
||||
|
||||
Qgs3DMapScene::Qgs3DMapScene( const Qgs3DMapSettings &map, QgsAbstract3DEngine *engine )
|
||||
: mMap( map )
|
||||
, mEngine( engine )
|
||||
@ -570,6 +572,25 @@ void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
|
||||
newEntity->addComponent( picker );
|
||||
connect( picker, &Qt3DRender::QObjectPicker::pressed, this, &Qgs3DMapScene::onLayerEntityPickEvent );
|
||||
}
|
||||
|
||||
// this is probably not the best place for material-specific configuration,
|
||||
// maybe this could be more generalized when other materials need some specific treatment
|
||||
QgsLineMaterial *lm = newEntity->findChild<QgsLineMaterial *>();
|
||||
if ( lm )
|
||||
{
|
||||
connect( mCameraController, &QgsCameraController::cameraChanged, lm, [lm, this]
|
||||
{
|
||||
Qt3DRender::QCamera *cam = mCameraController->camera();
|
||||
lm->setCameraParameters( cam->position(), cam->viewVector(), cam->nearPlane() );
|
||||
} );
|
||||
connect( mCameraController, &QgsCameraController::viewportChanged, lm, [lm, this]
|
||||
{
|
||||
lm->setViewportSize( mCameraController->viewport().size() );
|
||||
} );
|
||||
|
||||
lm->setCameraParameters( cameraController()->camera()->position(), cameraController()->camera()->viewVector(), cameraController()->camera()->nearPlane() );
|
||||
lm->setViewportSize( cameraController()->viewport().size() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,5 +3,8 @@
|
||||
<file>shaders/instanced.frag</file>
|
||||
<file>shaders/instanced.vert</file>
|
||||
<file>shaders/light.inc.frag</file>
|
||||
<file>shaders/lines.vert</file>
|
||||
<file>shaders/lines.geom</file>
|
||||
<file>shaders/lines.frag</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
30
src/3d/shaders/lines.frag
Normal file
30
src/3d/shaders/lines.frag
Normal file
@ -0,0 +1,30 @@
|
||||
#version 150
|
||||
|
||||
uniform vec4 lineColor;
|
||||
uniform bool useTex;
|
||||
uniform sampler2D tex0;
|
||||
|
||||
in VertexData{
|
||||
vec2 mTexCoord;
|
||||
// vec3 mColor;
|
||||
} VertexIn;
|
||||
|
||||
out vec4 oColor;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
if (!useTex)
|
||||
{
|
||||
// option 1: plain color
|
||||
oColor = lineColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
// option 2: textured color
|
||||
oColor = texture(tex0, VertexIn.mTexCoord.xy );
|
||||
}
|
||||
|
||||
//vec4 clr = texture( tex0, VertexIn.mTexCoord.xy );
|
||||
//oColor.rgb = VertexIn.mColor * clr.rgb;
|
||||
//oColor.a = clr.a;
|
||||
}
|
174
src/3d/shaders/lines.geom
Normal file
174
src/3d/shaders/lines.geom
Normal file
@ -0,0 +1,174 @@
|
||||
#version 150
|
||||
|
||||
uniform float THICKNESS; // the thickness of the line in pixels
|
||||
uniform float MITER_LIMIT; // 1.0: always miter, -1.0: never miter, 0.75: default
|
||||
uniform vec2 WIN_SCALE; // the size of the viewport in pixels
|
||||
|
||||
uniform mat4 modelViewProjection;
|
||||
|
||||
uniform vec3 camNearPlanePoint;
|
||||
uniform vec3 camNearPlaneNormal;
|
||||
|
||||
layout( lines_adjacency ) in;
|
||||
layout( triangle_strip, max_vertices = 7 ) out;
|
||||
|
||||
|
||||
in VertexData{
|
||||
vec3 worldPosition;
|
||||
// vec3 mColor;
|
||||
} VertexIn[4];
|
||||
|
||||
out VertexData{
|
||||
vec2 mTexCoord;
|
||||
// vec3 mColor;
|
||||
} VertexOut;
|
||||
|
||||
vec2 toScreenSpace( vec4 vertex )
|
||||
{
|
||||
return vec2( vertex.xy / vertex.w ) * WIN_SCALE;
|
||||
}
|
||||
|
||||
vec4 clip_line_point(vec3 pt0, vec3 pt1, vec4 projected)
|
||||
{
|
||||
// we have line segment given by pt0 and pt1 (in world coordinates) and 'projected' point
|
||||
// (in clip coordinates) that is one of the endpoints. If the projected point's w >= 1
|
||||
// then everything is fine because the point is in front of the camera's near plane and
|
||||
// it is projected correctly. If not, the projected point is wrong and needs to be adjusted.
|
||||
// we place it at the intersection of the line and near plane to fix its position.
|
||||
|
||||
if (projected.w < 1)
|
||||
{
|
||||
vec3 lineDir = pt1 - pt0;
|
||||
float d = dot(camNearPlaneNormal, camNearPlanePoint - pt0) / dot(lineDir, camNearPlaneNormal);
|
||||
if (d > 0 && d < 1)
|
||||
{
|
||||
// figure out the intersection point of line and near plane
|
||||
vec3 wpIntersect = pt0 + lineDir * d;
|
||||
vec4 wpIntersectProj = modelViewProjection * vec4( wpIntersect, 1.0 );
|
||||
return wpIntersectProj;
|
||||
}
|
||||
}
|
||||
return projected;
|
||||
}
|
||||
|
||||
void main( void )
|
||||
{
|
||||
// these are original positions in world coordinates
|
||||
vec3 wp0 = VertexIn[0].worldPosition;
|
||||
vec3 wp1 = VertexIn[1].worldPosition;
|
||||
vec3 wp2 = VertexIn[2].worldPosition;
|
||||
vec3 wp3 = VertexIn[3].worldPosition;
|
||||
|
||||
// Perform line clipping first. we search for intersection between the line and the near plane.
|
||||
// In case the near plane intersects line between segment's endpoints, we need to adjust the line
|
||||
// otherwise we would use completely non-sense points when points get 'behind' the camera.
|
||||
// We do this also for the 'previous' and 'next' segments to get the miters right.
|
||||
vec4 projp0 = clip_line_point(wp0, wp1, gl_in[0].gl_Position);
|
||||
vec4 projp1 = clip_line_point(wp1, wp2, gl_in[1].gl_Position);
|
||||
vec4 projp2 = clip_line_point(wp1, wp2, gl_in[2].gl_Position);
|
||||
vec4 projp3 = clip_line_point(wp2, wp3, gl_in[3].gl_Position);
|
||||
|
||||
// get the four vertices passed to the shader:
|
||||
vec2 p0 = toScreenSpace( projp0 ); // start of previous segment
|
||||
vec2 p1 = toScreenSpace( projp1 ); // end of previous segment, start of current segment
|
||||
vec2 p2 = toScreenSpace( projp2 ); // end of current segment, start of next segment
|
||||
vec2 p3 = toScreenSpace( projp3 ); // end of next segment
|
||||
|
||||
// these are already 'final' depths in range [0,1] so we don't need to further transform them
|
||||
float p1z = projp1.z / projp1.w;
|
||||
float p2z = projp2.z / projp2.w;
|
||||
|
||||
// determine the direction of each of the 3 segments (previous, current, next)
|
||||
vec2 v0 = normalize( p1 - p0 );
|
||||
vec2 v1 = normalize( p2 - p1 );
|
||||
vec2 v2 = normalize( p3 - p2 );
|
||||
|
||||
// Martin's addition to fix flicker on starting ending point of lines
|
||||
if (p1 == p0) v0 = v1;
|
||||
if (p3 == p2) v2 = v1;
|
||||
|
||||
// determine the normal of each of the 3 segments (previous, current, next)
|
||||
vec2 n0 = vec2( -v0.y, v0.x );
|
||||
vec2 n1 = vec2( -v1.y, v1.x );
|
||||
vec2 n2 = vec2( -v2.y, v2.x );
|
||||
|
||||
// determine miter lines by averaging the normals of the 2 segments
|
||||
vec2 miter_a = normalize( n0 + n1 ); // miter at start of current segment
|
||||
vec2 miter_b = normalize( n1 + n2 ); // miter at end of current segment
|
||||
|
||||
// determine the length of the miter by projecting it onto normal and then inverse it
|
||||
float length_a = THICKNESS / dot( miter_a, n1 );
|
||||
float length_b = THICKNESS / dot( miter_b, n1 );
|
||||
|
||||
// prevent excessively long miters at sharp corners
|
||||
if( dot( v0, v1 ) < -MITER_LIMIT ) {
|
||||
miter_a = n1;
|
||||
length_a = THICKNESS;
|
||||
|
||||
// close the gap
|
||||
if( dot( v0, n1 ) > 0 ) {
|
||||
VertexOut.mTexCoord = vec2( 0, 0 );
|
||||
//VertexOut.mColor = VertexIn[1].mColor;
|
||||
gl_Position = vec4( ( p1 + THICKNESS * n1 ) / WIN_SCALE, p1z, 1.0 );
|
||||
EmitVertex();
|
||||
|
||||
VertexOut.mTexCoord = vec2( 0, 0 );
|
||||
//VertexOut.mColor = VertexIn[1].mColor;
|
||||
gl_Position = vec4( ( p1 + THICKNESS * n0 ) / WIN_SCALE, p1z, 1.0 );
|
||||
EmitVertex();
|
||||
|
||||
VertexOut.mTexCoord = vec2( 0, 0.5 );
|
||||
//VertexOut.mColor = VertexIn[1].mColor;
|
||||
gl_Position = vec4( p1 / WIN_SCALE, p1z, 1.0 );
|
||||
EmitVertex();
|
||||
|
||||
EndPrimitive();
|
||||
}
|
||||
else {
|
||||
VertexOut.mTexCoord = vec2( 0, 1 );
|
||||
//VertexOut.mColor = VertexIn[1].mColor;
|
||||
gl_Position = vec4( ( p1 - THICKNESS * n0 ) / WIN_SCALE, p1z, 1.0 );
|
||||
EmitVertex();
|
||||
|
||||
VertexOut.mTexCoord = vec2( 0, 1 );
|
||||
//VertexOut.mColor = VertexIn[1].mColor;
|
||||
gl_Position = vec4( ( p1 - THICKNESS * n1 ) / WIN_SCALE, p1z, 1.0 );
|
||||
EmitVertex();
|
||||
|
||||
VertexOut.mTexCoord = vec2( 0, 0.5 );
|
||||
//VertexOut.mColor = VertexIn[1].mColor;
|
||||
gl_Position = vec4( p1 / WIN_SCALE, p1z, 1.0 );
|
||||
EmitVertex();
|
||||
|
||||
EndPrimitive();
|
||||
}
|
||||
}
|
||||
|
||||
if( dot( v1, v2 ) < -MITER_LIMIT ) {
|
||||
miter_b = n1;
|
||||
length_b = THICKNESS;
|
||||
}
|
||||
|
||||
// generate the triangle strip
|
||||
VertexOut.mTexCoord = vec2( 0, 0 );
|
||||
//VertexOut.mColor = VertexIn[1].mColor;
|
||||
gl_Position = vec4( ( p1 + length_a * miter_a ) / WIN_SCALE, p1z, 1.0 );
|
||||
EmitVertex();
|
||||
|
||||
VertexOut.mTexCoord = vec2( 0, 1 );
|
||||
//VertexOut.mColor = VertexIn[1].mColor;
|
||||
gl_Position = vec4( ( p1 - length_a * miter_a ) / WIN_SCALE, p1z, 1.0 );
|
||||
EmitVertex();
|
||||
|
||||
VertexOut.mTexCoord = vec2( 0, 0 );
|
||||
//VertexOut.mColor = VertexIn[2].mColor;
|
||||
gl_Position = vec4( ( p2 + length_b * miter_b ) / WIN_SCALE, p2z, 1.0 );
|
||||
EmitVertex();
|
||||
|
||||
VertexOut.mTexCoord = vec2( 0, 1 );
|
||||
//VertexOut.mColor = VertexIn[2].mColor;
|
||||
gl_Position = vec4( ( p2 - length_b * miter_b ) / WIN_SCALE, p2z, 1.0 );
|
||||
EmitVertex();
|
||||
|
||||
EndPrimitive();
|
||||
}
|
19
src/3d/shaders/lines.vert
Normal file
19
src/3d/shaders/lines.vert
Normal file
@ -0,0 +1,19 @@
|
||||
#version 150
|
||||
|
||||
uniform mat4 modelViewProjection;
|
||||
|
||||
in vec3 vertexPosition;
|
||||
//in vec3 ciColor;
|
||||
|
||||
out VertexData{
|
||||
// vec3 mColor;
|
||||
vec3 worldPosition;
|
||||
} VertexOut;
|
||||
|
||||
|
||||
void main(void)
|
||||
{
|
||||
//VertexOut.mColor = ciColor;
|
||||
gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 );
|
||||
VertexOut.worldPosition = vertexPosition;
|
||||
}
|
@ -16,6 +16,8 @@
|
||||
#include "qgsline3dsymbol_p.h"
|
||||
|
||||
#include "qgsline3dsymbol.h"
|
||||
#include "qgslinematerial_p.h"
|
||||
#include "qgslinevertexdata_p.h"
|
||||
#include "qgstessellatedpolygongeometry.h"
|
||||
#include "qgs3dmapsettings.h"
|
||||
//#include "qgsterraingenerator.h"
|
||||
@ -186,9 +188,6 @@ class QgsSimpleLine3DSymbolHandler : public QgsFeature3DHandler
|
||||
QgsSimpleLine3DSymbolHandler( const QgsLine3DSymbol &symbol, const QgsFeatureIds &selectedIds )
|
||||
: mSymbol( symbol ), mSelectedIds( selectedIds )
|
||||
{
|
||||
// the first index is invalid, we use it for primitive restart
|
||||
outNormal.vertices << QVector3D();
|
||||
outSelected.vertices << QVector3D();
|
||||
}
|
||||
|
||||
bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
|
||||
@ -197,14 +196,7 @@ class QgsSimpleLine3DSymbolHandler : public QgsFeature3DHandler
|
||||
|
||||
private:
|
||||
|
||||
//! temporary data we will pass to the tessellator
|
||||
struct LineData
|
||||
{
|
||||
QVector<QVector3D> vertices;
|
||||
QVector<unsigned int> indexes;
|
||||
};
|
||||
|
||||
void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected );
|
||||
void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected );
|
||||
Qt3DExtras::QPhongMaterial *material( const QgsLine3DSymbol &symbol ) const;
|
||||
|
||||
// input specific for this class
|
||||
@ -213,59 +205,44 @@ class QgsSimpleLine3DSymbolHandler : public QgsFeature3DHandler
|
||||
QgsFeatureIds mSelectedIds;
|
||||
|
||||
// outputs
|
||||
LineData outNormal; //!< Features that are not selected
|
||||
LineData outSelected; //!< Features that are selected
|
||||
QgsLineVertexData outNormal; //!< Features that are not selected
|
||||
QgsLineVertexData outSelected; //!< Features that are selected
|
||||
};
|
||||
|
||||
|
||||
|
||||
bool QgsSimpleLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
|
||||
{
|
||||
Q_UNUSED( context );
|
||||
Q_UNUSED( attributeNames );
|
||||
|
||||
outNormal.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
|
||||
outSelected.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsSimpleLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
|
||||
{
|
||||
Q_UNUSED( context );
|
||||
if ( f.geometry().isNull() )
|
||||
return;
|
||||
|
||||
LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
|
||||
|
||||
QgsPoint centroid;
|
||||
if ( mSymbol.altitudeBinding() == Qgs3DTypes::AltBindCentroid )
|
||||
centroid = QgsPoint( f.geometry().centroid().asPoint() );
|
||||
QgsLineVertexData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
|
||||
|
||||
QgsGeometry geom = f.geometry();
|
||||
const QgsAbstractGeometry *g = geom.constGet();
|
||||
if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
|
||||
{
|
||||
for ( int i = 0; i < ls->vertexCount(); ++i )
|
||||
{
|
||||
QgsPoint p = ls->pointN( i );
|
||||
float z = Qgs3DUtils::clampAltitude( p, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), centroid, context.map() );
|
||||
out.vertices << QVector3D( p.x() - context.map().origin().x(), z, -( p.y() - context.map().origin().y() ) );
|
||||
out.indexes << out.vertices.count() - 1;
|
||||
}
|
||||
out.addLineString( *ls );
|
||||
}
|
||||
else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
|
||||
{
|
||||
for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
|
||||
{
|
||||
const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) );
|
||||
for ( int i = 0; i < ls->vertexCount(); ++i )
|
||||
{
|
||||
QgsPoint p = ls->pointN( i );
|
||||
float z = Qgs3DUtils::clampAltitude( p, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), centroid, context.map() );
|
||||
out.vertices << QVector3D( p.x() - context.map().origin().x(), z, -( p.y() - context.map().origin().y() ) );
|
||||
out.indexes << out.vertices.count() - 1;
|
||||
}
|
||||
out.indexes << 0; // add primitive restart
|
||||
out.addLineString( *ls );
|
||||
}
|
||||
}
|
||||
|
||||
out.indexes << 0; // add primitive restart
|
||||
}
|
||||
|
||||
void QgsSimpleLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
|
||||
@ -276,7 +253,7 @@ void QgsSimpleLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qg
|
||||
}
|
||||
|
||||
|
||||
void QgsSimpleLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected )
|
||||
void QgsSimpleLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected )
|
||||
{
|
||||
if ( out.indexes.isEmpty() )
|
||||
return;
|
||||
@ -292,54 +269,125 @@ void QgsSimpleLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const
|
||||
|
||||
// geometry renderer
|
||||
|
||||
QByteArray vertexBufferData;
|
||||
vertexBufferData.resize( out.vertices.size() * 3 * sizeof( float ) );
|
||||
float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
|
||||
int idx = 0;
|
||||
for ( const auto &v : qgis::as_const( out.vertices ) )
|
||||
{
|
||||
rawVertexArray[idx++] = v.x();
|
||||
rawVertexArray[idx++] = v.y();
|
||||
rawVertexArray[idx++] = v.z();
|
||||
}
|
||||
|
||||
QByteArray indexBufferData;
|
||||
indexBufferData.resize( out.indexes.size() * sizeof( int ) );
|
||||
unsigned int *rawIndexArray = reinterpret_cast<unsigned int *>( indexBufferData.data() );
|
||||
idx = 0;
|
||||
for ( unsigned int indexVal : qgis::as_const( out.indexes ) )
|
||||
{
|
||||
rawIndexArray[idx++] = indexVal;
|
||||
}
|
||||
|
||||
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
|
||||
|
||||
Qt3DRender::QBuffer *vertexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, entity );
|
||||
vertexBuffer->setData( vertexBufferData );
|
||||
|
||||
Qt3DRender::QBuffer *indexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::IndexBuffer, entity );
|
||||
indexBuffer->setData( indexBufferData );
|
||||
|
||||
Qt3DRender::QAttribute *positionAttribute = new Qt3DRender::QAttribute( entity );
|
||||
positionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
|
||||
positionAttribute->setBuffer( vertexBuffer );
|
||||
positionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
|
||||
positionAttribute->setVertexSize( 3 );
|
||||
positionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() );
|
||||
|
||||
Qt3DRender::QAttribute *indexAttribute = new Qt3DRender::QAttribute( entity );
|
||||
indexAttribute->setAttributeType( Qt3DRender::QAttribute::IndexAttribute );
|
||||
indexAttribute->setBuffer( indexBuffer );
|
||||
indexAttribute->setVertexBaseType( Qt3DRender::QAttribute::UnsignedInt );
|
||||
|
||||
Qt3DRender::QGeometry *geom = new Qt3DRender::QGeometry;
|
||||
geom->addAttribute( positionAttribute );
|
||||
geom->addAttribute( indexAttribute );
|
||||
Qt3DRender::QGeometry *geom = out.createGeometry( entity );
|
||||
|
||||
Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
|
||||
renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStrip );
|
||||
renderer->setGeometry( geom );
|
||||
renderer->setVertexCount( out.vertices.count() );
|
||||
renderer->setVertexCount( out.indexes.count() );
|
||||
renderer->setPrimitiveRestartEnabled( true );
|
||||
renderer->setRestartIndexValue( 0 );
|
||||
|
||||
// make entity
|
||||
entity->addComponent( renderer );
|
||||
entity->addComponent( mat );
|
||||
entity->setParent( parent );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --------------
|
||||
|
||||
|
||||
class QgsThickLine3DSymbolHandler : public QgsFeature3DHandler
|
||||
{
|
||||
public:
|
||||
QgsThickLine3DSymbolHandler( const QgsLine3DSymbol &symbol, const QgsFeatureIds &selectedIds )
|
||||
: mSymbol( symbol ), mSelectedIds( selectedIds )
|
||||
{
|
||||
}
|
||||
|
||||
bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
|
||||
void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
|
||||
void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected );
|
||||
Qt3DExtras::QPhongMaterial *material( const QgsLine3DSymbol &symbol ) const;
|
||||
|
||||
// input specific for this class
|
||||
const QgsLine3DSymbol &mSymbol;
|
||||
// inputs - generic
|
||||
QgsFeatureIds mSelectedIds;
|
||||
|
||||
// outputs
|
||||
QgsLineVertexData outNormal; //!< Features that are not selected
|
||||
QgsLineVertexData outSelected; //!< Features that are selected
|
||||
};
|
||||
|
||||
|
||||
|
||||
bool QgsThickLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
|
||||
{
|
||||
Q_UNUSED( attributeNames );
|
||||
|
||||
outNormal.withAdjacency = true;
|
||||
outSelected.withAdjacency = true;
|
||||
outNormal.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
|
||||
outSelected.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsThickLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
|
||||
{
|
||||
Q_UNUSED( context );
|
||||
if ( f.geometry().isNull() )
|
||||
return;
|
||||
|
||||
QgsLineVertexData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
|
||||
|
||||
QgsGeometry geom = f.geometry();
|
||||
const QgsAbstractGeometry *g = geom.constGet();
|
||||
if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
|
||||
{
|
||||
out.addLineString( *ls );
|
||||
}
|
||||
else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
|
||||
{
|
||||
for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
|
||||
{
|
||||
const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) );
|
||||
out.addLineString( *ls );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsThickLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
|
||||
{
|
||||
// create entity for selected and not selected
|
||||
makeEntity( parent, context, outNormal, false );
|
||||
makeEntity( parent, context, outSelected, true );
|
||||
}
|
||||
|
||||
|
||||
void QgsThickLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected )
|
||||
{
|
||||
if ( out.indexes.isEmpty() )
|
||||
return;
|
||||
|
||||
// material (only ambient color is used for the color)
|
||||
|
||||
QgsLineMaterial *mat = new QgsLineMaterial;
|
||||
mat->setLineColor( mSymbol.material().ambient() );
|
||||
mat->setLineWidth( mSymbol.width() );
|
||||
if ( selected )
|
||||
{
|
||||
// update the material with selection colors
|
||||
mat->setLineColor( context.map().selectionColor() );
|
||||
}
|
||||
|
||||
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
|
||||
|
||||
// geometry renderer
|
||||
Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
|
||||
renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
|
||||
renderer->setGeometry( out.createGeometry( entity ) );
|
||||
renderer->setVertexCount( out.indexes.count() );
|
||||
renderer->setPrimitiveRestartEnabled( true );
|
||||
renderer->setRestartIndexValue( 0 );
|
||||
|
||||
@ -359,7 +407,8 @@ namespace Qgs3DSymbolImpl
|
||||
QgsFeature3DHandler *handlerForLine3DSymbol( QgsVectorLayer *layer, const QgsLine3DSymbol &symbol )
|
||||
{
|
||||
if ( symbol.renderAsSimpleLines() )
|
||||
return new QgsSimpleLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
|
||||
return new QgsThickLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
|
||||
//return new QgsSimpleLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
|
||||
else
|
||||
return new QgsBufferedLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
|
||||
}
|
||||
|
120
src/3d/symbols/qgslinematerial_p.cpp
Normal file
120
src/3d/symbols/qgslinematerial_p.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
/***************************************************************************
|
||||
qgslinematerial_p.cpp
|
||||
--------------------------------------
|
||||
Date : Apr 2019
|
||||
Copyright : (C) 2019 by Martin Dobias
|
||||
Email : wonder dot sk at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgslinematerial_p.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QSizeF>
|
||||
#include <QUrl>
|
||||
#include <QVector3D>
|
||||
|
||||
#include <Qt3DRender/QBlendEquation>
|
||||
#include <Qt3DRender/QBlendEquationArguments>
|
||||
#include <Qt3DRender/QCamera>
|
||||
#include <Qt3DRender/QEffect>
|
||||
#include <Qt3DRender/QGraphicsApiFilter>
|
||||
#include <Qt3DRender/QParameter>
|
||||
#include <Qt3DRender/QRenderPass>
|
||||
#include <Qt3DRender/QTechnique>
|
||||
|
||||
/// @cond PRIVATE
|
||||
|
||||
|
||||
QgsLineMaterial::QgsLineMaterial()
|
||||
: mParameterThickness( new Qt3DRender::QParameter( "THICKNESS", 10, this ) )
|
||||
, mParameterMiterLimit( new Qt3DRender::QParameter( "MITER_LIMIT", -1, this ) ) // 0.75
|
||||
, mParameterLineColor( new Qt3DRender::QParameter( "lineColor", QColor( 0, 255, 0 ), this ) )
|
||||
, mParameterWindowScale( new Qt3DRender::QParameter( "WIN_SCALE", QSizeF(), this ) )
|
||||
, mParameterCameraNearPlanePoint( new Qt3DRender::QParameter( "camNearPlanePoint", QVector3D(), this ) )
|
||||
, mParameterCameraNearPlaneNormal( new Qt3DRender::QParameter( "camNearPlaneNormal", QVector3D(), this ) )
|
||||
{
|
||||
addParameter( mParameterThickness );
|
||||
addParameter( mParameterMiterLimit );
|
||||
addParameter( mParameterLineColor );
|
||||
addParameter( mParameterWindowScale );
|
||||
addParameter( mParameterCameraNearPlanePoint );
|
||||
addParameter( mParameterCameraNearPlaneNormal );
|
||||
|
||||
//Parameter { name: "tex0"; value: txt },
|
||||
//Parameter { name: "useTex"; value: false },
|
||||
|
||||
Qt3DRender::QShaderProgram *shaderProgram = new Qt3DRender::QShaderProgram( this );
|
||||
shaderProgram->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/lines.vert" ) ) ) );
|
||||
shaderProgram->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/lines.frag" ) ) ) );
|
||||
shaderProgram->setGeometryShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/lines.geom" ) ) ) );
|
||||
|
||||
Qt3DRender::QBlendEquation *blendEquation = new Qt3DRender::QBlendEquation( this );
|
||||
blendEquation->setBlendFunction( Qt3DRender::QBlendEquation::Add );
|
||||
|
||||
Qt3DRender::QBlendEquationArguments *blendEquationArgs = new Qt3DRender::QBlendEquationArguments( this );
|
||||
blendEquationArgs->setSourceRgb( Qt3DRender::QBlendEquationArguments::SourceAlpha );
|
||||
blendEquationArgs->setDestinationRgb( Qt3DRender::QBlendEquationArguments::OneMinusSourceAlpha );
|
||||
|
||||
Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass( this );
|
||||
renderPass->setShaderProgram( shaderProgram );
|
||||
renderPass->addRenderState( blendEquation );
|
||||
renderPass->addRenderState( blendEquationArgs );
|
||||
|
||||
// without this filter the default forward renderer would not render this
|
||||
Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey;
|
||||
filterKey->setName( QStringLiteral( "renderingStyle" ) );
|
||||
filterKey->setValue( "forward" );
|
||||
|
||||
Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique;
|
||||
technique->addFilterKey( filterKey );
|
||||
technique->addRenderPass( renderPass );
|
||||
technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
|
||||
technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
|
||||
technique->graphicsApiFilter()->setMajorVersion( 3 );
|
||||
technique->graphicsApiFilter()->setMinorVersion( 1 );
|
||||
|
||||
Qt3DRender::QEffect *effect = new Qt3DRender::QEffect( this );
|
||||
effect->addTechnique( technique );
|
||||
|
||||
setEffect( effect );
|
||||
}
|
||||
|
||||
void QgsLineMaterial::setLineColor( const QColor &color )
|
||||
{
|
||||
mParameterLineColor->setValue( color );
|
||||
}
|
||||
|
||||
QColor QgsLineMaterial::lineColor() const
|
||||
{
|
||||
return mParameterLineColor->value().value<QColor>();
|
||||
}
|
||||
|
||||
void QgsLineMaterial::setLineWidth( float width )
|
||||
{
|
||||
mParameterThickness->setValue( width );
|
||||
}
|
||||
|
||||
float QgsLineMaterial::lineWidth() const
|
||||
{
|
||||
return mParameterThickness->value().toFloat();
|
||||
}
|
||||
|
||||
void QgsLineMaterial::setCameraParameters( const QVector3D &position, const QVector3D &viewVector, float nearPlane )
|
||||
{
|
||||
mParameterCameraNearPlanePoint->setValue( position + viewVector * nearPlane );
|
||||
mParameterCameraNearPlaneNormal->setValue( viewVector );
|
||||
}
|
||||
|
||||
void QgsLineMaterial::setViewportSize( const QSizeF &viewportSize )
|
||||
{
|
||||
mParameterWindowScale->setValue( viewportSize );
|
||||
}
|
||||
|
||||
/// @endcond
|
83
src/3d/symbols/qgslinematerial_p.h
Normal file
83
src/3d/symbols/qgslinematerial_p.h
Normal file
@ -0,0 +1,83 @@
|
||||
/***************************************************************************
|
||||
qgslinematerial_p.h
|
||||
--------------------------------------
|
||||
Date : Apr 2019
|
||||
Copyright : (C) 2019 by Martin Dobias
|
||||
Email : wonder dot sk at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSLINEMATERIAL_P_H
|
||||
#define QGSLINEMATERIAL_P_H
|
||||
|
||||
/// @cond PRIVATE
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the QGIS API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
|
||||
#include <Qt3DRender/QMaterial>
|
||||
|
||||
namespace Qt3DRender
|
||||
{
|
||||
class QCamera;
|
||||
}
|
||||
|
||||
/**
|
||||
* \ingroup 3d
|
||||
* Implementation of material that renders 3D linestrings.
|
||||
*
|
||||
* Supports:
|
||||
* - arbitrary line width (in pixels)
|
||||
* - bevel and miter line joins (including limit for miter join to avoid very long miters on sharp angles)
|
||||
* - flat line caps
|
||||
* - alpha blending
|
||||
*
|
||||
* The material needs information about viewport size (to correctly scale line widths) and to camera
|
||||
* parameters (to correctly clip lines).
|
||||
*
|
||||
* It is implemented by using a geometry shader that accepts primitive type Line strip with adjacency
|
||||
* (i.e. we have access to points p1-p2 which define line segment's endpoints, and in addition to that,
|
||||
* we have access to previous (p0) and next (p3) points). Geometry shader generates two triangles
|
||||
* for each segment and possibly another triangle for bevel join.
|
||||
*/
|
||||
class QgsLineMaterial : public Qt3DRender::QMaterial
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QgsLineMaterial();
|
||||
|
||||
void setLineColor( const QColor &color );
|
||||
QColor lineColor() const;
|
||||
|
||||
void setLineWidth( float width );
|
||||
float lineWidth() const;
|
||||
|
||||
Q_INVOKABLE void setCameraParameters( const QVector3D &position, const QVector3D &viewVector, float nearPlane );
|
||||
Q_INVOKABLE void setViewportSize( const QSizeF &viewportSize );
|
||||
|
||||
private:
|
||||
Qt3DRender::QParameter *mParameterThickness = nullptr;
|
||||
Qt3DRender::QParameter *mParameterMiterLimit = nullptr;
|
||||
Qt3DRender::QParameter *mParameterLineColor = nullptr;
|
||||
|
||||
Qt3DRender::QParameter *mParameterWindowScale = nullptr;
|
||||
Qt3DRender::QParameter *mParameterCameraNearPlanePoint = nullptr;
|
||||
Qt3DRender::QParameter *mParameterCameraNearPlaneNormal = nullptr;
|
||||
|
||||
};
|
||||
|
||||
/// @endcond
|
||||
|
||||
#endif // QGSLINEMATERIAL_P_H
|
123
src/3d/symbols/qgslinevertexdata_p.cpp
Normal file
123
src/3d/symbols/qgslinevertexdata_p.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
/***************************************************************************
|
||||
qgslinevertexdata_p.cpp
|
||||
--------------------------------------
|
||||
Date : Apr 2019
|
||||
Copyright : (C) 2019 by Martin Dobias
|
||||
Email : wonder dot sk at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgslinevertexdata_p.h"
|
||||
|
||||
#include <Qt3DRender/QAttribute>
|
||||
#include <Qt3DRender/QBuffer>
|
||||
#include <Qt3DRender/QGeometry>
|
||||
|
||||
#include "qgslogger.h"
|
||||
#include "qgs3dutils.h"
|
||||
#include "qgslinestring.h"
|
||||
|
||||
/// @cond PRIVATE
|
||||
|
||||
|
||||
QgsLineVertexData::QgsLineVertexData()
|
||||
{
|
||||
// the first index is invalid, we use it for primitive restart
|
||||
vertices << QVector3D();
|
||||
}
|
||||
|
||||
void QgsLineVertexData::init( Qgs3DTypes::AltitudeClamping clamping, Qgs3DTypes::AltitudeBinding binding, float height, const Qgs3DMapSettings *map )
|
||||
{
|
||||
altClamping = clamping;
|
||||
altBinding = binding;
|
||||
baseHeight = height;
|
||||
mapSettings = map;
|
||||
}
|
||||
|
||||
QByteArray QgsLineVertexData::createVertexBuffer()
|
||||
{
|
||||
QByteArray vertexBufferData;
|
||||
vertexBufferData.resize( vertices.size() * 3 * sizeof( float ) );
|
||||
float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
|
||||
int idx = 0;
|
||||
for ( const auto &v : qgis::as_const( vertices ) )
|
||||
{
|
||||
rawVertexArray[idx++] = v.x();
|
||||
rawVertexArray[idx++] = v.y();
|
||||
rawVertexArray[idx++] = v.z();
|
||||
}
|
||||
return vertexBufferData;
|
||||
}
|
||||
|
||||
QByteArray QgsLineVertexData::createIndexBuffer()
|
||||
{
|
||||
QByteArray indexBufferData;
|
||||
indexBufferData.resize( indexes.size() * sizeof( int ) );
|
||||
unsigned int *rawIndexArray = reinterpret_cast<unsigned int *>( indexBufferData.data() );
|
||||
int idx = 0;
|
||||
for ( unsigned int indexVal : qgis::as_const( indexes ) )
|
||||
{
|
||||
rawIndexArray[idx++] = indexVal;
|
||||
}
|
||||
return indexBufferData;
|
||||
}
|
||||
|
||||
Qt3DRender::QGeometry *QgsLineVertexData::createGeometry( Qt3DCore::QNode *parent )
|
||||
{
|
||||
Qt3DRender::QBuffer *vertexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, parent );
|
||||
vertexBuffer->setData( createVertexBuffer() );
|
||||
|
||||
Qt3DRender::QBuffer *indexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::IndexBuffer, parent );
|
||||
indexBuffer->setData( createIndexBuffer() );
|
||||
|
||||
QgsDebugMsg( QString( "vertex buffer %1 MB index buffer %2 MB " ).arg( vertexBuffer->data().count() / 1024. / 1024. ).arg( indexBuffer->data().count() / 1024. / 1024. ) );
|
||||
|
||||
Qt3DRender::QAttribute *positionAttribute = new Qt3DRender::QAttribute( parent );
|
||||
positionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
|
||||
positionAttribute->setBuffer( vertexBuffer );
|
||||
positionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
|
||||
positionAttribute->setVertexSize( 3 );
|
||||
positionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() );
|
||||
|
||||
Qt3DRender::QAttribute *indexAttribute = new Qt3DRender::QAttribute( parent );
|
||||
indexAttribute->setAttributeType( Qt3DRender::QAttribute::IndexAttribute );
|
||||
indexAttribute->setBuffer( indexBuffer );
|
||||
indexAttribute->setVertexBaseType( Qt3DRender::QAttribute::UnsignedInt );
|
||||
|
||||
Qt3DRender::QGeometry *geom = new Qt3DRender::QGeometry;
|
||||
geom->addAttribute( positionAttribute );
|
||||
geom->addAttribute( indexAttribute );
|
||||
return geom;
|
||||
}
|
||||
|
||||
void QgsLineVertexData::addLineString( const QgsLineString &lineString )
|
||||
{
|
||||
if ( withAdjacency )
|
||||
indexes << vertices.count(); // add the following vertex (for adjacency)
|
||||
|
||||
QgsPoint centroid;
|
||||
if ( altBinding == Qgs3DTypes::AltBindCentroid )
|
||||
centroid = lineString.centroid();
|
||||
|
||||
for ( int i = 0; i < lineString.vertexCount(); ++i )
|
||||
{
|
||||
QgsPoint p = lineString.pointN( i );
|
||||
float z = Qgs3DUtils::clampAltitude( p, altClamping, altBinding, baseHeight, centroid, *mapSettings );
|
||||
|
||||
vertices << QVector3D( p.x() - mapSettings->origin().x(), z, -( p.y() - mapSettings->origin().y() ) );
|
||||
indexes << vertices.count() - 1;
|
||||
}
|
||||
|
||||
if ( withAdjacency )
|
||||
indexes << vertices.count() - 1; // add the last vertex (for adjacency)
|
||||
|
||||
indexes << 0; // add primitive restart
|
||||
}
|
||||
|
||||
/// @endcond
|
86
src/3d/symbols/qgslinevertexdata_p.h
Normal file
86
src/3d/symbols/qgslinevertexdata_p.h
Normal file
@ -0,0 +1,86 @@
|
||||
/***************************************************************************
|
||||
qgslinevertexdata_p.h
|
||||
--------------------------------------
|
||||
Date : Apr 2019
|
||||
Copyright : (C) 2019 by Martin Dobias
|
||||
Email : wonder dot sk at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSLINEVERTEXDATA_P_H
|
||||
#define QGSLINEVERTEXDATA_P_H
|
||||
|
||||
/// @cond PRIVATE
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the QGIS API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
|
||||
#include <QVector>
|
||||
#include <QVector3D>
|
||||
|
||||
#include "qgs3dtypes.h"
|
||||
|
||||
namespace Qt3DCore
|
||||
{
|
||||
class QNode;
|
||||
}
|
||||
namespace Qt3DRender
|
||||
{
|
||||
class QGeometry;
|
||||
}
|
||||
|
||||
class QgsLineString;
|
||||
class Qgs3DMapSettings;
|
||||
|
||||
|
||||
/**
|
||||
* \ingroup 3d
|
||||
* Helper class to store vertex buffer and index buffer data that will be used to render
|
||||
* lines (either using "line strip" or "line strip with adjacency" primitive.
|
||||
*
|
||||
* Index zero is used for primitive restart (to separate two linestrings).
|
||||
*
|
||||
* It is expected that client code:
|
||||
* 1. calls init()
|
||||
* 2. calls addLineString() many times
|
||||
* 3. calls createGeometry()
|
||||
*/
|
||||
struct QgsLineVertexData
|
||||
{
|
||||
QVector<QVector3D> vertices;
|
||||
QVector<unsigned int> indexes;
|
||||
|
||||
bool withAdjacency = false; //!< Whether line strip with adjacency primitive will be used
|
||||
|
||||
// extra info to calculate elevation
|
||||
Qgs3DTypes::AltitudeClamping altClamping = Qgs3DTypes::AltClampRelative;
|
||||
Qgs3DTypes::AltitudeBinding altBinding = Qgs3DTypes::AltBindVertex;
|
||||
float baseHeight = 0;
|
||||
const Qgs3DMapSettings *mapSettings = nullptr;
|
||||
|
||||
QgsLineVertexData();
|
||||
|
||||
void init( Qgs3DTypes::AltitudeClamping clamping, Qgs3DTypes::AltitudeBinding binding, float height, const Qgs3DMapSettings *map );
|
||||
|
||||
QByteArray createVertexBuffer();
|
||||
QByteArray createIndexBuffer();
|
||||
Qt3DRender::QGeometry *createGeometry( Qt3DCore::QNode *parent );
|
||||
|
||||
void addLineString( const QgsLineString &lineString );
|
||||
};
|
||||
|
||||
/// @endcond
|
||||
|
||||
#endif // QGSLINEVERTEXDATA_P_H
|
@ -28,8 +28,11 @@
|
||||
#include <Qt3DRender/QGeometryRenderer>
|
||||
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgslinestring.h"
|
||||
#include "qgsmultipolygon.h"
|
||||
|
||||
#include "qgslinevertexdata_p.h"
|
||||
#include "qgslinematerial_p.h"
|
||||
|
||||
/// @cond PRIVATE
|
||||
|
||||
@ -66,11 +69,19 @@ class QgsPolygon3DSymbolHandler : public QgsFeature3DHandler
|
||||
// outputs
|
||||
PolygonData outNormal; //!< Features that are not selected
|
||||
PolygonData outSelected; //!< Features that are selected
|
||||
|
||||
bool addEdges = true; //!< TODO: should go to polygon symbol
|
||||
QColor edgeColor = QColor( 0, 0, 0 ); //!< TODO: go to polygon symbol
|
||||
float edgeWidth = 2; //!< TODO: go to polygon symbol
|
||||
QgsLineVertexData outEdges; //!< When highlighting edges, this holds data for vertex/index buffer
|
||||
};
|
||||
|
||||
|
||||
bool QgsPolygon3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
|
||||
{
|
||||
outEdges.withAdjacency = true;
|
||||
outEdges.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
|
||||
|
||||
QSet<QString> attrs = mSymbol.dataDefinedProperties().referencedFields( context.expressionContext() );
|
||||
attributeNames.unite( attrs );
|
||||
return true;
|
||||
@ -78,6 +89,16 @@ bool QgsPolygon3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet
|
||||
|
||||
void QgsPolygon3DSymbolHandler::processPolygon( QgsPolygon *polyClone, QgsFeatureId fid, float height, bool hasDDExtrusion, float extrusionHeight, const Qgs3DRenderContext &context, PolygonData &out )
|
||||
{
|
||||
if ( addEdges )
|
||||
{
|
||||
// add edges before the polygon gets the Z values modified because addLineString() does its own altitude handling
|
||||
outEdges.addLineString( *static_cast<const QgsLineString *>( polyClone->exteriorRing() ) );
|
||||
for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
|
||||
outEdges.addLineString( *static_cast<const QgsLineString *>( polyClone->interiorRing( i ) ) );
|
||||
|
||||
// TODO: if has extrusion: also add vertical edges for each vertex
|
||||
}
|
||||
|
||||
Qgs3DUtils::clampAltitudes( polyClone, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), height, context.map() );
|
||||
out.polygons.append( polyClone );
|
||||
out.fids.append( fid );
|
||||
@ -148,6 +169,29 @@ void QgsPolygon3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3D
|
||||
// create entity for selected and not selected
|
||||
makeEntity( parent, context, outNormal, false );
|
||||
makeEntity( parent, context, outSelected, true );
|
||||
|
||||
// add entity for edges
|
||||
if ( addEdges && !outEdges.indexes.isEmpty() )
|
||||
{
|
||||
QgsLineMaterial *mat = new QgsLineMaterial;
|
||||
mat->setLineColor( edgeColor );
|
||||
mat->setLineWidth( edgeWidth );
|
||||
|
||||
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
|
||||
|
||||
// geometry renderer
|
||||
Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
|
||||
renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
|
||||
renderer->setGeometry( outEdges.createGeometry( entity ) );
|
||||
renderer->setVertexCount( outEdges.indexes.count() );
|
||||
renderer->setPrimitiveRestartEnabled( true );
|
||||
renderer->setRestartIndexValue( 0 );
|
||||
|
||||
// make entity
|
||||
entity->addComponent( renderer );
|
||||
entity->addComponent( mat );
|
||||
entity->setParent( parent );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -67,6 +67,6 @@ QgsLine3DSymbol QgsLine3DSymbolWidget::symbol() const
|
||||
void QgsLine3DSymbolWidget::updateGuiState()
|
||||
{
|
||||
bool simple = chkSimpleLines->isChecked();
|
||||
spinWidth->setEnabled( !simple );
|
||||
//spinWidth->setEnabled( !simple );
|
||||
spinExtrusion->setEnabled( !simple );
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user