current revision

This commit is contained in:
2016-09-28 12:16:45 +02:00
parent 075d8bb633
commit d47322e73b
90 changed files with 8228 additions and 606 deletions

222
ui/map/3D/FloorRenderer.h Normal file
View File

@@ -0,0 +1,222 @@
#ifndef FLOORRENDERER_H
#define FLOORRENDERER_H
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <Indoor/floorplan/v2/Floorplan.h>
#include "GL.h"
class FloorRenderer : protected QOpenGLFunctions {
private:
Floorplan::Floor* floor;
QOpenGLBuffer arrayBuf;
QOpenGLBuffer indexBuf;
QOpenGLTexture* texture = nullptr;
std::vector<VertNormTex> vertices;
std::vector<GLushort> indices;
public:
/** ctor */
FloorRenderer(Floorplan::Floor* floor) : floor(floor), arrayBuf(QOpenGLBuffer::VertexBuffer), indexBuf(QOpenGLBuffer::IndexBuffer) {
;
}
/** dctor */
~FloorRenderer() {
arrayBuf.destroy();
indexBuf.destroy();
delete texture;
}
/** render the floor */
void render(QOpenGLShaderProgram *program) {
// Tell OpenGL which VBOs to use
arrayBuf.bind();
indexBuf.bind();
texture->bind();
// vertices
int vertLoc = program->attributeLocation("a_position");
program->enableAttributeArray(vertLoc);
program->setAttributeBuffer(vertLoc, GL_FLOAT, vertices[0].getVertOffset(), 3, sizeof(vertices[0]));
// Tell OpenGL programmable pipeline how to locate vertex texture coordinate data
int normLoc = program->attributeLocation("a_normal");
program->enableAttributeArray(normLoc);
program->setAttributeBuffer(normLoc, GL_FLOAT, vertices[0].getNormOffset(), 3, sizeof(vertices[0]));
int texcoordLocation = program->attributeLocation("a_texcoord");
program->enableAttributeArray(texcoordLocation);
program->setAttributeBuffer(texcoordLocation, GL_FLOAT, vertices[0].getTexOffset(), 2, sizeof(vertices[0]));
// texture
program->setUniformValue("texture", 0);
// Draw cube geometry using indices from VBO 1
glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, 0);
}
void initGL() {
initializeOpenGLFunctions();
build();
}
private:
void build() {
QImage img(":/res/gl/tex/wall1.jpg");
texture = new QOpenGLTexture(img);
texture->setMinificationFilter(QOpenGLTexture::Nearest);
texture->setMagnificationFilter(QOpenGLTexture::Linear);
texture->setWrapMode(QOpenGLTexture::Repeat);
// build array of vertices and indices
for (Floorplan::FloorObstacle* obstacle : floor->obstacles) {
add(floor, obstacle, vertices, indices);
}
// Transfer vertex data to VBO 0
arrayBuf.create();
arrayBuf.bind();
arrayBuf.allocate(vertices.data(), vertices.size() * sizeof(vertices[0]));
// Transfer index data to VBO 1
indexBuf.create();
indexBuf.bind();
indexBuf.allocate(indices.data(), indices.size() * sizeof(indices[0]));
}
void add(Floorplan::Floor* floor, Floorplan::FloorObstacle* obstacle, std::vector<VertNormTex>& vertices, std::vector<GLushort>& indices) {
if (dynamic_cast<Floorplan::FloorObstacleLine*>(obstacle)) {
Floorplan::FloorObstacleLine* line = dynamic_cast<Floorplan::FloorObstacleLine*>(obstacle);
if (line->type != Floorplan::ObstacleType::WALL) {return;}
// QVector3D p1(line->from.x, floor->getStartingZ(), line->from.y);
// QVector3D p2(line->to.x, floor->getStartingZ(), line->to.y);
// QVector3D p3(line->to.x, floor->getEndingZ(), line->to.y);
// QVector3D p4(line->from.x, floor->getEndingZ(), line->from.y);
// addFace(p1,p2,p3,p4, vertices, indices);
addFace(line->from, line->to, floor->getStartingZ(), floor->getEndingZ(), vertices, indices);
addFace(line->to, line->from, floor->getStartingZ(), floor->getEndingZ(), vertices, indices);
}
}
void addFace(const Point2 from, const Point2 to, const float h1, const float h2, std::vector<VertNormTex>& vertices, std::vector<GLushort>& indices) {
// unit-face with unit normal (facing the camera)
QVector3D p1(-0.5, -0.0, 0);
QVector3D p2(+0.5, -0.0, 0);
QVector3D p3(+0.5, +1.0, 0);
QVector3D p4(-0.5, +1.0, 0);
QVector3D norm(0, 0, 1);
// how to scale the unit-face to match the wall
const float h = h2-h1;
const float s = from.getDistance(to);
QMatrix4x4 scale; scale.scale(s, h, 1);
// how to rotate the unit-face to match the wall
const float angle = std::atan2(to.y - from.y, to.x - from.x);
const float deg = angle * 180 / M_PI;
QMatrix4x4 rot; rot.rotate(deg, QVector3D(0,1,0));
// how to translate the unit-face to match the wall
const Point2 center = (from + to) / 2;
QMatrix4x4 move; move.translate(center.x, h1, center.y);
// final matrix
QMatrix4x4 mat = move * rot * scale;
// texture coordinates (scale only)
const QVector2D tex1 = (scale * p1).toVector2D() / 5;
const QVector2D tex2 = (scale * p2).toVector2D() / 5;
const QVector2D tex3 = (scale * p3).toVector2D() / 5;
const QVector2D tex4 = (scale * p4).toVector2D() / 5;
// modify vertices
p1 = mat * p1;
p2 = mat * p2;
p3 = mat * p3;
p4 = mat * p4;
norm = (rot * norm).normalized();
const int start = vertices.size();
vertices.push_back(VertNormTex(p1, norm, tex1));
vertices.push_back(VertNormTex(p2, norm, tex2));
vertices.push_back(VertNormTex(p3, norm, tex3));
vertices.push_back(VertNormTex(p4, norm, tex4));
indices.push_back(start+0);
indices.push_back(start+1);
indices.push_back(start+2);
indices.push_back(start+3);
}
// void addFace(QVector3D p1, QVector3D p2, QVector3D p3, QVector3D p4, std::vector<VertNorm>& vertices, std::vector<GLushort>& indices) {
// const float s = 50;
// // ensure camera facing (for culling)
// if (p1.x() != p2.x()) {
// if (p1.x() > p2.x()) {std::swap(p1, p2), std::swap(p3, p4);}
// } else {
// if (p1.z() > p2.z()) {std::swap(p1, p2), std::swap(p3, p4);}
// }
// // corresponding normal vector
// QVector3D norm = QVector3D::crossProduct((p2-p1), (p3-p1)).normalized();
// // orient towards the viewport
// const QVector3D view(-99,-99,-99);
// if ((view-norm).length() > (view+norm).length()) {norm = -norm;}
// const int start = vertices.size();
// indices.push_back(start+0);
// indices.push_back(start+1);
// indices.push_back(start+2);
// indices.push_back(start+3);
// vertices.push_back(VertNorm(p1/s, norm));
// vertices.push_back(VertNorm(p2/s, norm));
// vertices.push_back(VertNorm(p3/s, norm));
// vertices.push_back(VertNorm(p4/s, norm));
// // and the other side (clockwise, negated normal)
// indices.push_back(start+0);
// indices.push_back(start+3);
// indices.push_back(start+2);
// indices.push_back(start+1);
// vertices.push_back(VertNorm(p1/s, -norm));
// vertices.push_back(VertNorm(p2/s, -norm));
// vertices.push_back(VertNorm(p3/s, -norm));
// vertices.push_back(VertNorm(p4/s, -norm));
// }
};
#endif // FLOORRENDERER_H

302
ui/map/3D/MapView3D.cpp Normal file
View File

@@ -0,0 +1,302 @@
#include "MapView3D.h"
#include <QMouseEvent>
#include <QGLShaderProgram>
#include "elements/Walls.h"
#include "elements/Ground.h"
#include "elements/Handrails.h"
#include "elements/Stairs.h"
#include "elements/Doors.h"
#include "elements/Path.h"
#include "elements/ColorPoints.h"
#include "elements/Object.h"
#include "../Settings.h"
#include <Indoor/data/Timestamp.h>
#include <QDebug>
/**
* before adding elements to the MapView via setMap(),
* the MapViews openGL context must be initialized
* that means: the MapView must have been added to a window,
* which is already visible!
*/
MapView3D::MapView3D(QWidget* parent) : QOpenGLWidget(parent) {
};
void MapView3D::clear() {
for (Renderable* r : elements) {delete r;}
elements.clear();
}
void MapView3D::setMap(Floorplan::IndoorMap* map) {
clear();
if (!isGLInitialized) {throw Exception("openGL is not yet initialized. add mapView to a visible window!");}
// first to be rendered -> the colored debug points
this->colorPoints = new ColorPoints();
elements.push_back(this->colorPoints);
// 2nd is the path to the destination (if any)
this->pathToDest = new Path();
elements.push_back(this->pathToDest);
this->pathWalked = new Path();
this->pathWalked->setWidth(0.3);
this->pathWalked->setColor(Qt::yellow);
elements.push_back(this->pathWalked);
// 3rd is the dude, walking along the path
//leDude = new Object("/mnt/firma/tmp/3D/minion/minion.obj", "/mnt/firma/tmp/3D/minion/minion.png", "", 0.35);
//leDude = new Object("/mnt/firma/tmp/3D/gnome/gnome.obj", "/mnt/firma/tmp/3D/gnome/gnome_diffuse.jpg", "/mnt/firma/tmp/3D/gnome/gnome_normal.jpg", 0.033);
//leDude = new Object("/mnt/firma/tmp/3D/squirrel/squirrel.obj", "/mnt/firma/tmp/3D/squirrel/squirrel.jpg", "/mnt/firma/tmp/3D/squirrel/squirrel_normal.jpg", 0.033);
//elements.push_back(leDude);
// hereafter follows the floorplan (floors, doors, walls, stairs, ..)
for (Floorplan::Floor* floor : map->floors) {
elements.push_back(new Ground(floor));
elements.push_back(new Walls(floor));
elements.push_back(new Handrails(floor));
elements.push_back(new Stairs(floor));
elements.push_back(new Doors(floor));
}
// initialize the OpenGL context of all contained elements
for (Renderable* r : elements) {
r->initGL();
}
// i want the focus! needed for key-events
setFocusPolicy(Qt::StrongFocus);
}
void MapView3D::setPathToDestination(const std::vector<Point3>& path) {
this->pathToDest->set(path);
}
void MapView3D::setPathWalked(const std::vector<Point3>& path) {
this->pathWalked->set(path);
}
void MapView3D::timerEvent(QTimerEvent *) {
update();
}
void MapView3D::initializeGL() {
initializeOpenGLFunctions();
// basic config
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
// start background update timer
timer.start(Settings::MapView::msPerFrame.ms(), this);
// OpenGL is now initialized
isGLInitialized = true;
}
void MapView3D::paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw();
}
void MapView3D::resizeGL(int w, int h) {
// Calculate aspect ratio
qreal aspect = qreal(w) / qreal(h ? h : 1);
// viewing frustrum [0:50] meter
const qreal zNear = 0.02, zFar = 50, fov = 50.0;
// Reset projection
matProject.setToIdentity();
matProject.scale(-1, 1, 1);
glCullFace(GL_FRONT);
//matProject.scale(0.05, 0.05, 0.05);
matProject.perspective(fov, aspect, zNear, zFar);
//matProject.scale(-0.01, 0.01, 0.01);
}
void MapView3D::rebuildLookat() {
// QVector3D qDir(lookAt.dir.x, lookAt.dir.z, lookAt.dir.y);
// QVector3D at = QVector3D(lookAt.pos.x, lookAt.pos.z, lookAt.pos.y);
// QVector3D eye = at + qDir * 0.1;
// QVector3D up = QVector3D(0,1,0);
// matView.setToIdentity();
// //matView.scale(0.01, 0.01, 0.01);
// matView.lookAt(eye, at, up);
// //matView.scale(0.99, 1, 1);
// //matView.translate(0.7, 0, 0);
// lightPos = eye + QVector3D(0.0, 4.0, 0.0);
// eyePos = eye;
const QVector3D qDir = lookAt.getDir();
//QVector3D qDir(dir.x, dir.z, dir.y);
const QVector3D eye = lookAt.eye_m;//(lookAt.eye_m.x, lookAt.eye_m.z, lookAt.eye_m.y);
const QVector3D at = eye + qDir * 0.5;
const QVector3D up = QVector3D(0,1,0);
matView.setToIdentity();
matView.lookAt(eye, at, up);
lightPos = eye + QVector3D(0.0, 0.5, 0.0) + qDir * 1.2;
eyePos = eye;
}
void MapView3D::setCurrentEstimation(const Point3 pos, const Point3 dir) {
const float angle = std::atan2(dir.y, dir.x) * 180 / M_PI;
if (leDude) {
leDude->setPosition(pos.x, pos.y, pos.z);
leDude->setRotation(0, 0, -angle + 90);
}
}
void MapView3D::setLookAt(const Point3 pos_m, const Point3 dir) {
lookAt.eye_m = QVector3D(pos_m.x, pos_m.z, pos_m.y) + QVector3D(dir.x, dir.z, dir.y) * 0.1;
lookAt.dir = QVector3D(dir.x, dir.z, dir.y);
rebuildLookat();
}
void MapView3D::setLookDir(const Point3 dir) {
lookAt.dir = QVector3D(dir.x, dir.z, dir.y);
rebuildLookat();
}
void MapView3D::setLookEye(const Point3 eye_m) {
lookAt.eye_m = QVector3D(eye_m.x, eye_m.z, eye_m.y);
rebuildLookat();
}
void MapView3D::mousePressEvent(QMouseEvent* evt) {
mouseState.down = true;
mouseState.x = evt->x();
mouseState.y = evt->y();
}
void MapView3D::mouseMoveEvent(QMouseEvent* evt) {
const float dx = evt->x() - mouseState.x;
const float dy = evt->y() - mouseState.y;
mouseState.x = evt->x();
mouseState.y = evt->y();
// PI*0.3 head movement left/right and up/down
//const float yFac = (this->height() / 2) / (M_PI * 0.3);
//const float xFac = (this->width() / 2) / (M_PI * 0.3);
const float angleX = dx/3.0f;
const float angleY = dy/3.0f;
lookAt.ax += angleX;
lookAt.ay += angleY;
lookAt.dirRot.setToIdentity();
lookAt.dirRot.rotate(lookAt.ax, QVector3D(0,1,0));
lookAt.dirRot.rotate(lookAt.ay, QVector3D(0,0,1));
//lookAt.dirRot.rotate(angleX, QVector3D(0,1,0));
//lookAt.dirRot.rotate(angleY, QVector3D(0,0,1));
rebuildLookat();
}
void MapView3D::mouseReleaseEvent(QMouseEvent* evt) {
(void) evt;
mouseState.down = false;
}
void MapView3D::keyPressEvent(QKeyEvent* evt) {
const QVector3D dir = lookAt.getDir() * 0.5;
const QVector3D side(-dir.z(), 0, dir.x());
if (evt->key() == Qt::Key_W) {lookAt.eye_m += dir; rebuildLookat();}
if (evt->key() == Qt::Key_S) {lookAt.eye_m -= dir; rebuildLookat();}
if (evt->key() == Qt::Key_A) {lookAt.eye_m += side; rebuildLookat();}
if (evt->key() == Qt::Key_D) {lookAt.eye_m -= side; rebuildLookat();}
}
void MapView3D::toggleRenderMode() {
renderMode = (RenderMode) (((int)renderMode + 1) % 2); // normally %3 but lines crash the smartphone
for (Renderable* r : elements) {
switch (renderMode) {
case RenderMode::OUTLINE:
r->setOutlineOnly(true);
r->setTransparent(false);
break;
case RenderMode::TRANSPARENT:
r->setOutlineOnly(false);
r->setTransparent(true);
break;
case RenderMode::NORMAL:
r->setOutlineOnly(false);
r->setTransparent(false);
break;
}
}
}
void MapView3D::draw() {
//const Timestamp ts1 = Timestamp::fromUnixTime();
// clear everything
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
for (Renderable* r : elements) {
QOpenGLShaderProgram& program = r->getProgram();
program.bind();
// set the matrices
program.setUniformValue("m_matrix", r->modelMatrix.mat);
program.setUniformValue("mv_matrix", matView * r->modelMatrix.mat);
program.setUniformValue("mvp_matrix", matProject * matView * r->modelMatrix.mat);
program.setUniformValue("lightWorldPos", lightPos);
program.setUniformValue("eyeWorldPos", eyePos);
r->render(renderParams);
}
//const Timestamp ts2 = Timestamp::fromUnixTime();
//qDebug("%d ms", (ts2-ts1).ms());
}

169
ui/map/3D/MapView3D.h Normal file
View File

@@ -0,0 +1,169 @@
#ifndef MAPVIEW_H
#define MAPVIEW_H
#include <../misc/fixc11.h>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QBasicTimer>
#include <Indoor/geo/Point3.h>
#include <Indoor/nav/dijkstra/DijkstraPath.h>
#include "elements/Path.h"
#include "elements/ColorPoints.h"
#include "elements/Object.h"
#include "../nav/State.h"
namespace Floorplan {
class IndoorMap;
}
class Renderable;
class Path;
class MapView3D : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT
private:
QMatrix4x4 matProject;
QMatrix4x4 matView;
QVector3D lightPos;
QVector3D eyePos;
RenderParams renderParams;
QBasicTimer timer;
std::vector<Renderable*> elements;
Path* pathToDest = nullptr;
Path* pathWalked = nullptr;
ColorPoints* colorPoints = nullptr;
Object* leDude = nullptr;
struct LookAt {
QVector3D eye_m = QVector3D(0,0,1);
QVector3D dir = QVector3D(1,0,-0.1);
float ax = 0;
float ay = 0;
QMatrix4x4 dirRot;
QVector3D getDir() const {return this->dirRot * this->dir;}
LookAt() {
dirRot.setToIdentity();
}
} lookAt;
struct MouseState {
float x = 0;
float y = 0;
bool down = false;
} mouseState;
void rebuildLookat();
void clear();
public:
MapView3D(QWidget* parent = 0);
/** set the map to display */
void setMap(Floorplan::IndoorMap* map);
/** the position to look at + looking direction */
void setLookAt(const Point3 pos, const Point3 dir = Point3(1, 0, -0.1));
/** set the eye's looking direction (looking from eye into this direction) */
void setLookDir(const Point3 dir);
/** set the eye's position (looking from here) */
void setLookEye(const Point3 eye_m);
/** do not render elements higher than the given height */
void setClipAbove(const float height_m) {renderParams.clipAboveHeight_m = height_m;}
/** set the currently estimated position */
void setCurrentEstimation(const Point3 pos, const Point3 dir);
/** set the path to the destination MUST BE CALLED FROM THE MAIN THREAD */
void setPathToDestination(const std::vector<Point3>& path);
/** set the path to disply. MUST BE CALLED FROM THE MAIN THREAD */
template <typename Node> void setPathToDestination(const DijkstraPath<Node>* path) {this->pathToDest->set(*path);}
/** set the path to disply. MUST BE CALLED FROM THE MAIN THREAD*/
Q_INVOKABLE void setPathToDestination(const void* path) { setPathToDestination( (const DijkstraPath<MyGridNode>*) path); }
/** set the walked path. MUST BE CALLED FROM THE MAIN THREAD */
void setPathWalked(const std::vector<Point3>& path);
/** set the walked path. MUST BE CALLED FROM THE MAIN THREAD */
Q_INVOKABLE void setPathWalked(const void* path) { this->pathWalked->set( *((const std::vector<Point3>*) path)); }
/** NOTE: must be called from Qt's main thread! */
void showGridImportance(Grid<MyGridNode>* grid) {
this->colorPoints->setFromGridImportance(grid);
}
/** NOTE: must be called from Qt's main thread! */
Q_INVOKABLE void showParticles(const void* particles) {
showParticles((const std::vector<K::Particle<MyState>>*) particles);
}
/** NOTE: must be called from Qt's main thread! */
void showParticles(const std::vector<K::Particle<MyState>>* particles) {
this->colorPoints->setFromParticles(*particles);
}
enum RenderMode {
NORMAL,
TRANSPARENT,
OUTLINE,
};
RenderMode renderMode = RenderMode::NORMAL;
void toggleRenderMode();
public slots:
void mousePressEvent(QMouseEvent*);
void mouseMoveEvent(QMouseEvent*);
void mouseReleaseEvent(QMouseEvent*);
void keyPressEvent(QKeyEvent*);
protected:
void timerEvent(QTimerEvent *e) Q_DECL_OVERRIDE;
void initializeGL();
void paintGL();
void resizeGL(int width, int height);
private:
bool isGLInitialized = false;
void draw();
};
#endif // MAPVIEW_H

11
ui/map/3D/RenderParams.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef RENDERPARAMS_H
#define RENDERPARAMS_H
struct RenderParams {
/** clip (do not render) everything above this height */
float clipAboveHeight_m = 99999;
};
#endif // RENDERPARAMS_H

79
ui/map/3D/Renderable.h Normal file
View File

@@ -0,0 +1,79 @@
#ifndef RENDERABLE_H
#define RENDERABLE_H
#include <QOpenGLShaderProgram>
#include "RenderParams.h"
class Renderable {
protected:
QOpenGLShaderProgram program;
public:
/** dtor */
virtual ~Renderable() {;}
/** get the renderable's shader */
QOpenGLShaderProgram& getProgram() {return program;}
/** render the renderable */
void render(const RenderParams& params) {
program.bind();
_render(params);
}
struct ModelMatrix {
QVector3D pos = QVector3D(0,0,0);
QVector3D rot = QVector3D(0,0,0);
QVector3D scale = QVector3D(1,1,1);
QMatrix4x4 mat;
ModelMatrix() {mat.setToIdentity();}
void update() {
const QVector3D _rot = rot.normalized();
const float rotDeg = rot.length();
mat.setToIdentity();
mat.scale(scale.x(), scale.y(), scale.z());
mat.translate(pos.x(), pos.y(), pos.z());
mat.rotate(rotDeg, _rot.x(), _rot.y(), _rot.z());
}
} modelMatrix;
void setPosition(QVector3D vec) {
modelMatrix.pos = vec * 0.99;
modelMatrix.update();
}
void setPosition(const float x, const float y, const float z) {
setPosition(QVector3D(x,z,y));
}
/** in degrees! */
void setRotation(const float x, const float y, const float z) {
modelMatrix.rot = QVector3D(x,z,y);
modelMatrix.update();
}
virtual void setOutlineOnly(const bool outline) { (void) outline; }
virtual void setTransparent(const bool transparent) { (void) transparent; }
virtual void initGL() = 0;
virtual void _render(const RenderParams& params) = 0;
protected:
/** helper method to build the shader */
void loadShader(const QString& vertex, const QString& fragment) {
program.removeAllShaders();
if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, vertex)) {throw "1";}
if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, fragment)) {throw "2";}
if (!program.link()) {throw "3";}
if (!program.bind()) {throw "4";}
}
};
#endif // RENDERABLE_H

View File

@@ -0,0 +1,141 @@
#ifndef GL_PARTICLES_H
#define GL_PARTICLES_H
#include <Indoor/floorplan/v2/Floorplan.h>
#include <KLib/math/filter/particles/ParticleFilter.h>
#include "../gl/GLHelper.h"
#include "../gl/GLPoints.h"
//#include "../gl/GLTriangles.h"
#include "../Renderable.h"
#include "../../../../nav/Node.h"
class ColorPoints : public Renderable {
private:
GLPoints points;
float size = 3.0f;
public:
/** ctor */
ColorPoints() {
}
/** NOTE: must be called from Qt's main thread! */
void setFromGridImportance(Grid<MyGridNode>* grid) {
points.clear();
float min = +INFINITY;
float max = -INFINITY;
for (const MyGridNode& n : *grid) {
const float f = n.getWalkImportance();
if (f < min) {min = f;}
if (f > max) {max = f;}
}
max = 1.2;
for (const MyGridNode& n : *grid) {
const QVector3D pt(n.x_cm/100.0f, n.z_cm/100.0f + 0.1f, n.y_cm/100.0f); // swap z and y
const float f = n.getWalkImportance();
float h = 0.66 - ((f-min)/(max-min)) * 0.66; // 0.66 is blue on the HSV-scale
if (h < 0) {h = 0;}
if (h > 1) {h = 1;}
const QColor color = QColor::fromHsvF(h, 1, 1);
points.addPoint(pt, color);
}
size = 8.0f;
points.rebuild();
}
/** NOTE: must be called from Qt's main thread! */
template <typename T> void setFromParticles(const std::vector<K::Particle<T>>& particles) {
points.clear();
// group particles by grid-point
std::unordered_map<GridPoint, float> weights;
for (const K::Particle<T>& p : particles) {
const GridPoint gp = p.state.position;
if (weights.find(gp) != weights.end()) {continue;}
weights[gp] += p.weight;
}
// find min/max
float min = +INFINITY;
float max = -INFINITY;
for (auto it : weights) {
if (it.second > max) {max = it.second;}
if (it.second < min) {min = it.second;}
}
// draw colored
for (auto it : weights) {
const GridPoint gp = it.first;
const float w = it.second;
const float p = (w-min) / (max-min); // [0:1]
const QVector3D pt(gp.x_cm/100.0f, gp.z_cm/100.0f + 0.1f, gp.y_cm/100.0f); // swap z and y
float h = 0.66 - (p*0.66); // 0.66 is blue on the HSV-scale
const QColor color = QColor::fromHsvF(h, 1, 1);
points.addPoint(pt, color);
}
// for (const K::Particle<T>& p : particles) {
// const GridPoint gp = p.state.position;
// const QVector3D pt(gp.x_cm/100.0f, gp.z_cm/100.0f + 0.1f, gp.y_cm/100.0f); // swap z and y
// const QColor color = Qt::blue;
// points.addPoint(pt, color);
// }
size = 8.0f;
points.rebuild();
}
// void addPoint(const QVector3D pt, const QColor color) {
// const float s = 5 / 100.0f;
// const QVector3D col(color.redF(), color.greenF(), color.blueF());
// const VertColor vc1(pt + QVector3D(-s, -s, 0), col);
// const VertColor vc2(pt + QVector3D(+s, -s, 0), col);
// const VertColor vc3(pt + QVector3D(+s, +s, 0), col);
// const VertColor vc4(pt + QVector3D(-s, +s, 0), col);
// points.addQuad(vc1, vc2, vc3, vc4);
// }
void initGL() override {
loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentColorPoint.glsl");
//program.setUniformValue("color", QVector4D(0.5, 0.5, 0.5, 1.0));
points.initGL();
}
/** render the floor */
void _render(const RenderParams& params) override {
(void) params;
//glDisable(GL_DEPTH_TEST);
#ifndef ANDROID
glPointSize(size);
#endif
points.render(&program);
//glEnable(GL_DEPTH_TEST);
}
};
#endif // GL_PARTICLES_H

106
ui/map/3D/elements/Doors.h Normal file
View File

@@ -0,0 +1,106 @@
#ifndef DOORS_H
#define DOORS_H
#include <Indoor/floorplan/v2/Floorplan.h>
#include "../gl/GLHelper.h"
#include "../gl/GLTriangles.h"
#include "../Renderable.h"
class Doors : public Renderable {
private:
Floorplan::Floor* floor;
GLTriangles<VertNormTexTan> doors;
public:
/** ctor */
Doors(Floorplan::Floor* floor) : floor(floor) {
;
}
void initGL() override {
build();
doors.setDiffuse(":/res/gl/tex/door2.jpg");
doors.setNormalMap(":/res/gl/tex/door2_normal.jpg");
doors.build();
loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTex.glsl");
program.setUniformValue("texDiffuse", 0);
program.setUniformValue("texNormalMap", 1);
}
/** render the floor */
void _render(const RenderParams& params) override {
// skip me?
if (params.clipAboveHeight_m < floor->atHeight) {return;}
doors.render(&program);
}
private:
void build() {
for (Floorplan::FloorObstacle* obstacle : floor->obstacles) {
if (dynamic_cast<Floorplan::FloorObstacleDoor*>(obstacle)) {
Floorplan::FloorObstacleDoor* door = dynamic_cast<Floorplan::FloorObstacleDoor*>(obstacle);
addFace(door->from, door->to, floor->getStartingZ(), floor->getStartingZ() + door->height, door->swap);
}
}
}
void addFace(const Point2 from, Point2 to, const float h1, const float h2, const bool swap) {
to = from + (to-from).rotated(60/180.0f*M_PI * ((swap)?(-1):(+1)) );
const QVector3D vert1(from.x, h1, from.y);
const QVector3D vert2(to.x, h1, to.y);
const QVector3D vert3(to.x, h2, to.y);
const QVector3D vert4(from.x, h2, from.y);
const QVector3D n1 = GLHelper::getNormal(vert1, vert2, vert3);
const QVector3D n2 = -n1;
QVector3D tan = (vert1-vert2).normalized();
tan = GLHelper::isCCW(vert1, vert2, vert3) ? (tan) : (-tan);
const QVector2D tex1(0, 0);
const QVector2D tex2(1, 0);
const QVector2D tex3(1, 1);
const QVector2D tex4(0, 1);
{
const VertNormTexTan vnt1(vert1, n1, tex1, tan);
const VertNormTexTan vnt2(vert2, n1, tex2, tan);
const VertNormTexTan vnt3(vert3, n1, tex3, tan);
const VertNormTexTan vnt4(vert4, n1, tex4, tan);
doors.addQuadCCW(vnt1, vnt2, vnt3, vnt4);
} {
const VertNormTexTan vnt1(vert1, n2, tex1, -tan);
const VertNormTexTan vnt2(vert2, n2, tex2, -tan);
const VertNormTexTan vnt3(vert3, n2, tex3, -tan);
const VertNormTexTan vnt4(vert4, n2, tex4, -tan);
doors.addQuadCW(vnt1, vnt2, vnt3, vnt4);
}
}
};
#endif // DOORS_H

178
ui/map/3D/elements/Ground.h Normal file
View File

@@ -0,0 +1,178 @@
#ifndef GROUND_H
#define GROUND_H
#include <Indoor/floorplan/v2/Floorplan.h>
#include "../gl/GLHelper.h"
#include "../gl/GLTriangles.h"
#include "../gl/GLLines.h"
#include "../Renderable.h"
#include "../../../../lib/gpc/Polygon.h"
class Ground : public Renderable {
private:
Floorplan::Floor* floor;
GLTriangles<VertNormTexTan> flooring;
GLTriangles<VertNormTexTan> ceiling;
GLLines outline;
bool outlineOnly = false;
bool transparent = false;
public:
/** ctor */
Ground(Floorplan::Floor* floor) : floor(floor) {
setOutlineOnly(false);
}
void initGL() override {
build();
flooring.setDiffuse(":/res/gl/tex/floor4.jpg");
flooring.setNormalMap(":/res/gl/tex/floor4_normal.jpg");
ceiling.setDiffuse(":/res/gl/tex/floor4.jpg");
ceiling.setNormalMap(":/res/gl/tex/floor4_normal.jpg");
flooring.build();
ceiling.build();
outline.build();
//loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTex.glsl");
//program.setUniformValue("texDiffuse", 0);
//program.setUniformValue("texNormalMap", 1);
}
/** render the floor */
void _render(const RenderParams& params) override {
// skip me?
if (params.clipAboveHeight_m < floor->atHeight) {return;}
if (transparent) {
program.setUniformValue("alpha", 0.5f);
glEnable(GL_BLEND);
} else {
program.setUniformValue("alpha", 1.0f);
glDisable(GL_BLEND);
}
if (outlineOnly) {
glLineWidth(5);
outline.render(&program);
} else {
flooring.render(&program);
ceiling.render(&program);
}
glDisable(GL_BLEND);
}
/** render only the outline? */
void setOutlineOnly(const bool outline) override {
(void) outline;
// this->outlineOnly = outline;
// if (outlineOnly) {
// loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentLine.glsl");
// program.setUniformValue("color", QVector4D(0.0, 0.0, 0.4, 1.0));
// } else {
loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTex.glsl");
program.setUniformValue("texDiffuse", 0);
program.setUniformValue("texNormalMap", 1);
// }
}
void setTransparent(const bool transparent) override {
this->transparent = transparent;
}
private:
void build() {
std::vector<gpc_polygon> add;
std::vector<gpc_polygon> rem;
const std::vector<Floorplan::FloorOutlinePolygon*>& polys = floor->outline;
Polygon pol;
for (Floorplan::FloorOutlinePolygon* poly : polys) {
switch (poly->method) {
case Floorplan::OutlineMethod::ADD: pol.add(poly->poly); break;
case Floorplan::OutlineMethod::REMOVE: pol.remove(poly->poly); break;
default: throw 1;
}
}
std::vector<std::vector<Point3>> trias = pol.get(floor->atHeight);
// is stored as TRIANGLE_STRIP
// => triangle might have more than 3 points
for (const std::vector<Point3>& tria : trias) {
const QVector3D normFloor(0, +1, 0);
const QVector3D normCeil(0, -1, 0);
const QVector3D t(1,0,0);
const float s = 0.6;
// add vertices
for (int i = 2; i < (int) tria.size(); i+=1) {
const Point3 p1 = tria[i-2];
const Point3 p2 = tria[i-1];
const Point3 p3 = tria[i-0];
const QVector3D vert1(p1.x, p1.z, p1.y);
const QVector3D vert2(p2.x, p2.z, p2.y);
const QVector3D vert3(p3.x, p3.z, p3.y);
{
const VertNormTexTan vnt1(vert1, normFloor, tex(vert1*s), t);
const VertNormTexTan vnt2(vert2, normFloor, tex(vert2*s), t);
const VertNormTexTan vnt3(vert3, normFloor, tex(vert3*s), t);
flooring.addFaceCCW(vnt1, vnt2, vnt3);
} {
const VertNormTexTan vnt1(vert1, normCeil, tex(vert1*s), t);
const VertNormTexTan vnt2(vert2, normCeil, tex(vert2*s), t);
const VertNormTexTan vnt3(vert3, normCeil, tex(vert3*s), t);
ceiling.addFaceCW(vnt1, vnt2, vnt3);
}
outline.addLine(vert1, vert2);
outline.addLine(vert2, vert3);
outline.addLine(vert3, vert1);
}
}
}
private:
QVector2D tex(const QVector3D vert) {
return QVector2D(vert.x(), vert.z());
}
};
#endif // GROUND_H

View File

@@ -0,0 +1,90 @@
#ifndef HANDRAIL_H
#define HANDRAIL_H
#include <Indoor/floorplan/v2/Floorplan.h>
#include "../gl/GLHelper.h"
#include "../gl/GLLines.h"
#include "../Renderable.h"
class Handrails : public Renderable {
private:
Floorplan::Floor* floor;
GLLines lines;
public:
/** ctor */
Handrails(Floorplan::Floor* floor) : floor(floor) {
;
}
void initGL() override {
build();
lines.build();
loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentLine.glsl");
program.setUniformValue("color", QVector4D(0.5, 0.5, 0.5, 1.0));
}
/** render the floor */
void _render(const RenderParams& params) override {
if (params.clipAboveHeight_m < floor->atHeight) {return;}
glLineWidth(2);
lines.render(&program);
}
private:
void build() {
for (Floorplan::FloorObstacle* obstacle : floor->obstacles) {
if (dynamic_cast<Floorplan::FloorObstacleLine*>(obstacle)) {
Floorplan::FloorObstacleLine* line = dynamic_cast<Floorplan::FloorObstacleLine*>(obstacle);
if (line->type != Floorplan::ObstacleType::HANDRAIL) {continue;}
add(line->from, line->to, floor->getStartingZ());
}
}
}
void add(const Point2 from, const Point2 to, const float h1) {
// handrail height
const float h2 = h1 + 0.8;
const QVector3D v1(to.x, h2, to.y);
const QVector3D v2(from.x, h2, from.y);
// upper
lines.addLine(v1, v2);
const float stepSize = 0.5;
const float len = from.getDistance(to);
const float steps = std::round(len / stepSize);
for (int i = 0; i <= steps; ++i) {
const float percent = (float) i / (float) steps;
const Point2 pos = from + (to-from) * percent;
const QVector3D v1(pos.x, h1, pos.y);
const QVector3D v2(pos.x, h2, pos.y);
lines.addLine(v1, v2);
}
}
};
#endif // HANDRAIL_H

View File

@@ -0,0 +1,85 @@
#ifndef OBJECT_H
#define OBJECT_H
#include <Indoor/floorplan/v2/Floorplan.h>
#include "../gl/GLHelper.h"
#include "../gl/GLTriangles.h"
#include "../Renderable.h"
#include <KLib/data/obj/ObjectFile.h>
class Object : public Renderable {
private:
GLTriangles<VertNormTex> triangles;
public:
/** ctor */
Object(const std::string& file, const std::string& colorTexture, std::string normalsTexture, const float scale = 1.0) {
K::ObjFileReader reader(file, false);
if (normalsTexture.empty()) {normalsTexture = ":/res/gl/tex/empty_normals.jpg";}
triangles.setDiffuse(colorTexture.c_str());
triangles.setNormalMap(normalsTexture.c_str());
for (const K::ObjFileReader::Face& face : reader.getData().faces) {
const QVector3D vertex1(face.vnt[0].vertex.x, face.vnt[0].vertex.y, face.vnt[0].vertex.z);
const QVector3D vertex2(face.vnt[1].vertex.x, face.vnt[1].vertex.y, face.vnt[1].vertex.z);
const QVector3D vertex3(face.vnt[2].vertex.x, face.vnt[2].vertex.y, face.vnt[2].vertex.z);
const QVector3D normal1(face.vnt[0].normal.x, face.vnt[0].normal.y, face.vnt[0].normal.z);
const QVector3D normal2(face.vnt[1].normal.x, face.vnt[1].normal.y, face.vnt[1].normal.z);
const QVector3D normal3(face.vnt[2].normal.x, face.vnt[2].normal.y, face.vnt[2].normal.z);
const QVector2D texture1(face.vnt[0].texture.x, face.vnt[0].texture.y);
const QVector2D texture2(face.vnt[1].texture.x, face.vnt[1].texture.y);
const QVector2D texture3(face.vnt[2].texture.x, face.vnt[2].texture.y);
const QVector3D o(0, 0.0, 0);
const VertNormTex vnt1(vertex1*scale+o, normal1, texture1);
const VertNormTex vnt2(vertex2*scale+o, normal2, texture2);
const VertNormTex vnt3(vertex3*scale+o, normal3, texture3);
triangles.addFace(vnt1, vnt2, vnt3);
}
}
void initGL() override {
build();
triangles.build();
loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTex.glsl");
program.setUniformValue("texDiffuse", 0);
program.setUniformValue("texNormalMap", 1);
}
/** render the floor */
void _render(const RenderParams& params) override {
(void) params;
triangles.render(&program);
}
private:
void build() {
triangles.build();
}
};
#endif // OBJECT_H

220
ui/map/3D/elements/Path.h Normal file
View File

@@ -0,0 +1,220 @@
#ifndef PATH_H
#define PATH_H
#include <Indoor/floorplan/v2/Floorplan.h>
#include <Indoor/nav/dijkstra/DijkstraPath.h>
#include "../gl/GLHelper.h"
#include "../gl/GLTriangles.h"
#include "../Renderable.h"
class Path : public Renderable {
private:
GLTriangles<VertNormTex> lines;
/** path's width (in meter) */
float width;
/** the path's color */
QVector4D color;
public:
/** ctor */
Path() {
setColor(QVector4D(0.0, 0.4, 1.0, 0.6));
setWidth(0.8);
}
void initGL() override {
loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentLine.glsl");
//loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTexSimple.glsl");
lines.setDiffuse(":/res/gl/tex/arrows.png");
program.setUniformValue("texNormalMap", 0);
}
/** set the path's color */
void setColor(const QVector4D& color) {
this->color = color;
}
/** set the path's color */
void setColor(const QColor& color) {
setColor(QVector4D(color.redF(), color.greenF(), color.blueF(), color.alphaF()));
}
/** set the path's width (in meter) */
void setWidth(const float width) {
this->width = width;
}
/** render the floor */
void _render(const RenderParams& params) override {
(void) params;
program.setUniformValue("color", color);
//glEnable(GL_BLEND);
glDisable(GL_CULL_FACE);
//glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
lines.render(&program);
//glDisable(GL_BLEND);
glEnable(GL_CULL_FACE);
}
template <typename Node> void set(const DijkstraPath<Node>& path) {
std::vector<Point3> out;
for (const DijkstraNode<Node>* node : path.getVector()) {
if (!node) {break;}
const Node* elem = node->element;
out.push_back(Point3(elem->x_cm/100.0f, elem->y_cm/100.0f, elem->z_cm/100.0f));
}
set(out);
}
/** combine nodes while the direction stays the same (many small quads -> one large quad) */
std::vector<Point3> simplify(const std::vector<Point3>& path) {
// copy
std::vector<Point3> out = path;
// remove unneccesary nodes
for (int i = 1; i < (int) out.size() - 1; ++i) {
const Point3 pa = out[i-1];
const Point3 pb = out[i-0];
const Point3 pc = out[i+1];
// same direction as last segment? combine segments!
const float dir1 = std::atan2(pb.y-pa.y, pb.x-pa.x); // last edge
const float dir2 = std::atan2(pc.y-pb.y, pc.x-pb.x); // next edge
const bool isSameDir = std::abs(dir1-dir2) < 0.03; // last-edge and next-edge have (approx) the same direction?
if (isSameDir) {out.erase(out.begin()+i); --i; continue;} // no additional information! remove the center node
// too many changes in a small space? -> remove some!
const float d1 = pb.getDistance(pa); // distance to last node
const float d2 = pb.getDistance(pc); // distance to next node
const float min = 0.8;
const bool isPackedChange = d1 < min && d2 < min; // both distances below a threshold?
if (isPackedChange) {out.erase(out.begin()+i); --i; continue;} // -> many changes in a small area -> remove current node!
}
return out;
}
/** MUST BE CALLED FROM THE MAIN THREAD */
void set(const std::vector<Point3>& _path) {
std::vector<Point3> path = simplify(_path);
// reset
lines.clear();
// half the width of the path
const float s = this->width * 0.5;
std::vector<Floorplan::Quad3> quads;
for (int i = 1; i < (int) path.size(); ++i) {
Point3 pa = path[i-1];
Point3 pb = path[i-0];
const Point3 pc(0, 0, 1);
Point3 dir = (pb - pa).normalized();
// produce a small gap between path-segments
// those segments will be smoothly connected using another quad
pa += dir * 0.35;
pb -= dir * 0.35;
const Point3 perb = cross(pa-pb, pc).normalized();
// quad's edges
const Point3 p1 = pa - perb*s;
const Point3 p2 = pa + perb*s;
const Point3 p3 = pb + perb*s;
const Point3 p4 = pb - perb*s;
// add
quads.push_back(Floorplan::Quad3(p1,p2,p3,p4));
}
float l1 = 0;
float l2 = 0;
for (int i = 0; i < (int) quads.size(); ++i) {
// add the line-segment
const Floorplan::Quad3 q1 = quads[i];
l2 += ((q1.p1 + q1.p2) / 2).getDistance( (q1.p3 + q1.p4) / 2 );
addQuad(q1, l1, l2);
l1 = l2;
// done?
if (i == (int) quads.size() - 1) {break;}
// construct the quad between adjacent segments
const Floorplan::Quad3 q2 = quads[i+1];
const Floorplan::Quad3 q3(q1.p4, q1.p3, q2.p2, q2.p1);
l2 += ((q3.p1 + q3.p2) / 2).getDistance( (q3.p3 + q3.p4) / 2 );
addQuad(q3, l1, l2);
l1 = l2;
}
lines.rebuild();
}
private:
void addQuad(const Floorplan::Quad3& q, const float l1, const float l2) {
// move the path upwards (slightly above the ground)
const float h = 0.40;
const QVector3D v1(q.p1.x, q.p1.z+h, q.p1.y);
const QVector3D v2(q.p2.x, q.p2.z+h, q.p2.y);
const QVector3D v3(q.p3.x, q.p3.z+h, q.p3.y);
const QVector3D v4(q.p4.x, q.p4.z+h, q.p4.y);
const QVector3D n(0,1,0);
const QVector2D tex1(0, l1);
const QVector2D tex2(1, l1);
const QVector2D tex3(1, l2);
const QVector2D tex4(0, l2);
// const QVector2D tex1(q.p1.x, q.p1.y);
// const QVector2D tex2(q.p2.x, q.p2.y);
// const QVector2D tex3(q.p3.x, q.p3.y);
// const QVector2D tex4(q.p4.x, q.p4.y);
const VertNormTex vnt1(v1, n, tex1);
const VertNormTex vnt2(v2, n, tex2);
const VertNormTex vnt3(v3, n, tex3);
const VertNormTex vnt4(v4, n, tex4);
lines.addQuadCCW(vnt1, vnt2, vnt3, vnt4);
}
};
#endif // PATH_H

154
ui/map/3D/elements/Stairs.h Normal file
View File

@@ -0,0 +1,154 @@
#ifndef STAIRS_H
#define STAIRS_H
#include <Indoor/floorplan/v2/Floorplan.h>
#include "../gl/GLHelper.h"
#include "../gl/GLTriangles.h"
#include "../Renderable.h"
class Stairs : public Renderable {
private:
Floorplan::Floor* floor;
GLTriangles<VertNormTex> parts;
public:
/** ctor */
Stairs(Floorplan::Floor* floor) : floor(floor) {
;
}
void initGL() override {
build();
// parts.setDiffuse(":/res/gl/tex/granite1.jpg");
// parts.setNormalMap(":/res/gl/tex/granite1_normal.jpg");
parts.setDiffuse(":/res/gl/tex/floor4.jpg");
parts.setNormalMap(":/res/gl/tex/floor4_normal.jpg");
parts.build();
loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTex.glsl");
program.setUniformValue("texDiffuse", 0);
program.setUniformValue("texNormalMap", 1);
}
/** render the floor */
void _render(const RenderParams& params) override {
(void) params;
parts.render(&program);
}
private:
void build() {
for (Floorplan::Stair* stair : floor->stairs) {
if (dynamic_cast<Floorplan::Stair*>(stair)) {
Floorplan::StairFreeform* freeform = dynamic_cast<Floorplan::StairFreeform*>(stair);
add(Floorplan::getQuads(freeform->getParts(), floor));
}
}
}
void add(const std::vector<Floorplan::Quad3>& quads) {
for (const Floorplan::Quad3& quad : quads) {
//void addQuad(quad);
stepify(quad);
}
}
void stepify(const Floorplan::Quad3& quad) {
const float len = (quad.p4 - quad.p1).length();
const float stepLen = 0.3;
const int steps = std::round(len / stepLen);
for (int i = 0; i < steps; ++i) {
const float per1 = (float) (i+0) / (float) steps;
const float per2 = (float) (i+1) / (float) steps;
const Point3 p1 = quad.p1 + (quad.p4 - quad.p1) * per1;
const Point3 p2 = quad.p2 + (quad.p3 - quad.p2) * per1;
const Point3 p5 = quad.p2 + (quad.p3 - quad.p2) * per2;
const Point3 p6 = quad.p1 + (quad.p4 - quad.p1) * per2;
Point3 p3 = p5; p3.z = p2.z;
Point3 p4 = p6; p4.z = p1.z;
addQuad(Floorplan::Quad3(p1, p2, p3, p4));
addQuad(Floorplan::Quad3(p3, p4, p6, p5));
}
}
void addQuad(const Floorplan::Quad3& quad) {
const QVector3D vert1(quad.p1.x, quad.p1.z, quad.p1.y);
const QVector3D vert2(quad.p2.x, quad.p2.z, quad.p2.y);
const QVector3D vert3(quad.p3.x, quad.p3.z, quad.p3.y);
const QVector3D vert4(quad.p4.x, quad.p4.z, quad.p4.y);
const QVector3D n1 = GLHelper::getNormal(vert1, vert2, vert3);
const QVector3D n2 = -n1;
// const float o =
// const QVector2D tex1(quad.p1.length(), quad.p1.y);
// const QVector2D tex2(quad.p2.x, quad.p2.y+quad.p2.z);
// const QVector2D tex3(quad.p3.x, quad.p3.y+quad.p3.z);
// const QVector2D tex4(quad.p4.x, quad.p4.y+quad.p4.z);
const float h = quad.p4.getDistance(quad.p1);
const float l = quad.p1.getDistance(quad.p2);
const float o = quad.p1.length();
const float s = 1.1; // 0.5;
const QVector2D tex1(o+0, h); // start texturing at the ceiling so above-door-sections and walls have the same textre
const QVector2D tex2(o+l, h);
const QVector2D tex3(o+l, 0);
const QVector2D tex4(o+0, 0);
// const VertNormTex vnt1(vert1, n1, tex1*s);
// const VertNormTex vnt2(vert2, n1, tex2*s);
// const VertNormTex vnt3(vert3, n1, tex3*s);
// const VertNormTex vnt4(vert4, n1, tex4*s);
{
const VertNormTex vnt1(vert1, n1, tex1*s);
const VertNormTex vnt2(vert2, n1, tex2*s);
const VertNormTex vnt3(vert3, n1, tex3*s);
const VertNormTex vnt4(vert4, n1, tex4*s);
parts.addQuadCCW(vnt1, vnt2, vnt3, vnt4);
} {
const VertNormTex vnt1(vert1, n2, tex1*s);
const VertNormTex vnt2(vert2, n2, tex2*s);
const VertNormTex vnt3(vert3, n2, tex3*s);
const VertNormTex vnt4(vert4, n2, tex4*s);
parts.addQuadCW(vnt1, vnt2, vnt3, vnt4);
}
}
};
#endif // STAIRS_H

163
ui/map/3D/elements/Walls.h Normal file
View File

@@ -0,0 +1,163 @@
#ifndef WALLS_H
#define WALLS_H
#include <Indoor/floorplan/v2/Floorplan.h>
#include "../gl/GLHelper.h"
#include "../gl/GLTriangles.h"
#include "../gl/GLLines.h"
#include "../Renderable.h"
#include "../gl/Shader.h"
class Walls : public Renderable {
private:
Floorplan::Floor* floor;
GLTriangles<VertNormTexTan> triangles;
GLLines outlines;
bool outlineOnly = false;
bool transparent = false;
public:
/** ctor */
Walls(Floorplan::Floor* floor) : floor(floor) {
setOutlineOnly(false);
}
void initGL() override {
build();
triangles.build();
triangles.setDiffuse(":/res/gl/tex/wall3.jpg");
triangles.setNormalMap(":/res/gl/tex/wall3_normal.jpg");
outlines.build();
}
/** render the floor */
void _render(const RenderParams& params) override {
// skip me?
if (params.clipAboveHeight_m < floor->atHeight) {return;}
if (transparent) {
program.setUniformValue("alpha", 0.5f);
glEnable(GL_BLEND);
} else {
program.setUniformValue("alpha", 1.0f);
glDisable(GL_BLEND);
}
if (outlineOnly) {
glLineWidth(1);
outlines.render(&program);
} else {
triangles.render(&program);
}
glDisable(GL_BLEND);
}
/** render only the outline? */
void setOutlineOnly(const bool outline) override {
this->outlineOnly = outline;
if (outlineOnly) {
loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentLine.glsl");
program.setUniformValue("color", QVector4D(0.9, 0.9, 0.9, 1.0));
} else {
loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTex.glsl");
program.setUniformValue("texDiffuse", 0);
program.setUniformValue("texNormalMap", 1);
}
}
void setTransparent(const bool transparent) override {
this->transparent = transparent;
}
private:
void build() {
for (Floorplan::FloorObstacle* obstacle : floor->obstacles) {
if (dynamic_cast<Floorplan::FloorObstacleLine*>(obstacle)) {
Floorplan::FloorObstacleLine* line = dynamic_cast<Floorplan::FloorObstacleLine*>(obstacle);
if (line->type != Floorplan::ObstacleType::WALL) {continue;}
addFace(line->from, line->to, floor->getStartingZ(), floor->getEndingZ());
} else if (dynamic_cast<Floorplan::FloorObstacleDoor*>(obstacle)) {
Floorplan::FloorObstacleDoor* door = dynamic_cast<Floorplan::FloorObstacleDoor*>(obstacle);
addFace(door->from, door->to, floor->getStartingZ() + door->height, floor->getEndingZ());
}
}
}
void addFace(const Point2 from, const Point2 to, const float h1, const float h2) {
const QVector3D vert1(from.x, h1, from.y);
const QVector3D vert2(to.x, h1, to.y);
const QVector3D vert3(to.x, h2, to.y);
const QVector3D vert4(from.x, h2, from.y);
const QVector3D n1 = GLHelper::getNormal(vert1, vert2, vert3);
const QVector3D n2 = -n1;
QVector3D tan = (vert1-vert2).normalized();
tan = GLHelper::isCCW(vert1, vert2, vert3) ? (tan) : (-tan);
const float l = from.getDistance(to);
const float h = h2-h1;
const float o = std::min(from.length(), to.length());
const QVector2D tex1(o+0, h); // start texturing at the ceiling so above-door-sections and walls have the same textre
const QVector2D tex2(o+l, h);
const QVector2D tex3(o+l, 0);
const QVector2D tex4(o+0, 0);
const float s = 0.65;
{
const VertNormTexTan vnt1(vert1, n1, tex1*s, tan);
const VertNormTexTan vnt2(vert2, n1, tex2*s, tan);
const VertNormTexTan vnt3(vert3, n1, tex3*s, tan);
const VertNormTexTan vnt4(vert4, n1, tex4*s, tan);
triangles.addQuadCCW(vnt1, vnt2, vnt3, vnt4);
} {
const VertNormTexTan vnt1(vert1, n2, tex1*s, -tan);
const VertNormTexTan vnt2(vert2, n2, tex2*s, -tan);
const VertNormTexTan vnt3(vert3, n2, tex3*s, -tan);
const VertNormTexTan vnt4(vert4, n2, tex4*s, -tan);
triangles.addQuadCW(vnt1, vnt2, vnt3, vnt4);
}
outlines.addLine(vert1, vert2);
outlines.addLine(vert2, vert3);
outlines.addLine(vert3, vert4);
outlines.addLine(vert4, vert1);
}
//private:
//
// QVector2D tex(const QVector3D vert) {
// return QVector2D(vert.x(), vert.y());
// }
};
#endif // WALLS_H

82
ui/map/3D/gl/GL.h Normal file
View File

@@ -0,0 +1,82 @@
#ifndef HELPER_GL_H
#define HELPER_GL_H
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>
struct Vert {
QVector3D vert;
Vert(QVector3D vert) : vert(vert) {;}
int getVertOffset() const {return 0;}
bool operator == (const Vert& o) const {return (vert == o.vert);}
};
struct VertColor {
QVector3D vert;
QVector3D color;
VertColor(QVector3D vert, QVector3D color) : vert(vert), color(color) {;}
int getVertOffset() const {return 0;}
int getColorOffset() const {return sizeof(QVector3D);}
int getTanOffset() const {throw "error";}
int getNormOffset() const {throw "error";}
int getTexOffset() const {throw "error";}
bool operator == (const VertColor& o) const {return (vert == o.vert) && (color == o.color);}
static bool hasTangent() {return false;}
static bool hasColor() {return true;}
static bool hasNormal() {return false;}
static bool hasTexCoord() {return false;}
};
struct VertNorm {
QVector3D vert;
QVector3D norm;
VertNorm(QVector3D vert, QVector3D norm) : vert(vert), norm(norm) {;}
int getVertOffset() const {return 0;}
int getNormOffset() const {return sizeof(QVector3D);}
int getTanOffset() const {throw "error";}
int getColorOffset() const {throw "error";}
bool operator == (const VertNorm& o) const {return (vert == o.vert) && (norm == o.norm);}
static bool hasTangent() {return false;}
static bool hasNormal() {return true;}
static bool hasTexCoord() {return false;}
static bool hasColor() {return false;}
};
struct VertNormTex {
QVector3D vert;
QVector3D norm;
QVector2D tex;
VertNormTex(QVector3D vert, QVector3D norm, QVector3D tex) : vert(vert), norm(norm), tex(tex) {;}
int getVertOffset() const {return 0;}
int getNormOffset() const {return sizeof(QVector3D);}
int getTexOffset() const {return sizeof(QVector3D)*2;}
int getTanOffset() const {throw "error";}
int getColorOffset() const {throw "error";}
bool operator == (const VertNormTex& o) const {return (vert == o.vert) && (norm == o.norm) && (tex == o.tex);}
static bool hasTangent() {return false;}
static bool hasNormal() {return true;}
static bool hasTexCoord() {return true;}
static bool hasColor() {return false;}
};
struct VertNormTexTan {
QVector3D vert;
QVector3D norm;
QVector2D tex;
QVector3D tan;
VertNormTexTan(QVector3D vert, QVector3D norm, QVector3D tex, QVector3D tan) : vert(vert), norm(norm), tex(tex), tan(tan) {;}
int getVertOffset() const {return 0;}
int getNormOffset() const {return sizeof(QVector3D);}
int getTexOffset() const {return sizeof(QVector3D)*2;}
int getTanOffset() const {return sizeof(QVector3D)*2 + sizeof(QVector2D);}
int getColorOffset() const {throw "error";}
bool operator == (const VertNormTexTan& o) const {return (vert == o.vert) && (norm == o.norm) && (tex == o.tex) && (tan == o.tan);}
static bool hasTangent() {return true;}
static bool hasNormal() {return true;}
static bool hasTexCoord() {return true;}
static bool hasColor() {return false;}
};
#endif // HELPER_GL_H

67
ui/map/3D/gl/GLHelper.h Normal file
View File

@@ -0,0 +1,67 @@
#ifndef MAP_HELPER_H
#define MAP_HELPER_H
#include <Indoor/geo/Point3.h>
#include <QOpenGLFunctions>
class GLHelper {
public:
static QVector3D getNormal(const QVector3D& v1, const QVector3D& v2, const QVector3D& v3) {
// get two of the triangle's edges
const QVector4D v21 = v2-v1;
const QVector4D v31 = v3-v1;
const QVector3D n = QVector3D::crossProduct(v21.toVector3D(), v31.toVector3D()).normalized();
return isCCW(v1, v2, v3) ? (n) : (-n);
}
/**
* is the triangle given by p1,p2,p3 CCW?
* NOTE: uses OUR coordinate system (x,y,z) where z is the floor-height
*/
static bool isCCW(const Point3 p1, const Point3 p2, const Point3 p3) {
const QVector3D v1(p1.x, p1.z, p1.y);
const QVector3D v2(p2.x, p2.z, p2.y);
const QVector3D v3(p3.x, p3.z, p3.y);
return isCCW(v1, v2, v3);
}
/**
* is the triangle given by p1,p2,p3 CCW?
* NOTE: uses OpenGL coordinate system (x,z,y) (our z is the floor-height)
*/
static bool isCCW(const QVector3D& p1, const QVector3D& p2, const QVector3D& p3) {
// camera position
QMatrix4x4 proj; proj.lookAt(QVector3D(-1,20,-1), QVector3D(0,0,0), QVector3D(0,1,0));
// to camera space
QVector4D v1(p1.x(), p1.y(), p1.z(), 1);
QVector4D v2(p2.x(), p2.y(), p2.z(), 1);
QVector4D v3(p3.x(), p3.y(), p3.z(), 1);
v1 = proj * v1;
v2 = proj * v2;
v3 = proj * v3;
// get two of the triangle's edges
const QVector4D v21 = v2-v1;
const QVector4D v31 = v3-v1;
// check the angle between both
const float angle = QVector2D::dotProduct(v21.toVector2D(), v31.toVector2D());
return angle > 0;
// const QVector3D n = QVector3D::crossProduct(v21.toVector3D(), v31.toVector3D());
// return n.z() > 0;
}
};
#endif // MAP_HELPER_H

126
ui/map/3D/gl/GLLines.h Normal file
View File

@@ -0,0 +1,126 @@
#ifndef GLLINES_H
#define GLLINES_H
#include <QOpenGLFunctions>
#include "GL.h"
#include "GLHelper.h"
#include <Indoor/geo/Point3.h>
class GLLines : protected QOpenGLFunctions {
private:
QOpenGLBuffer arrayBuf;
QOpenGLBuffer indexBuf;
std::vector<Vert> vertices;
std::vector<GLushort> indices;
int mode = GL_LINES;
public:
/** ctor */
GLLines() : arrayBuf(QOpenGLBuffer::VertexBuffer), indexBuf(QOpenGLBuffer::IndexBuffer) {
;
}
/** dtor */
~GLLines() {
arrayBuf.destroy();
indexBuf.destroy();
}
/** add a new face to this element */
void addLine(const QVector3D& p1, const QVector3D& p2) {
// add vertices (remove duplicates!)
const int i1 = addOnce(p1);
const int i2 = addOnce(p2);
// add indices
indices.push_back(i1);
indices.push_back(i2);
}
void addVertex(const QVector3D& p1) {
const int i1 = addOnce(p1);
indices.push_back(i1);
}
/** build the underlying buffers */
void build() {
initializeOpenGLFunctions();
// Transfer vertex data to VBO 0
arrayBuf.create();
arrayBuf.bind();
arrayBuf.allocate(vertices.data(), vertices.size() * sizeof(vertices[0]));
// Transfer index data to VBO 1
indexBuf.create();
indexBuf.bind();
indexBuf.allocate(indices.data(), indices.size() * sizeof(indices[0]));
}
void rebuild() {
if (indexBuf.isCreated()) {indexBuf.destroy();}
if (arrayBuf.isCreated()) {arrayBuf.destroy();}
build();
}
void clear() {
indices.clear();
vertices.clear();
}
void setMode(const int mode) {
this->mode = mode;
}
/** render the element */
void render(QOpenGLShaderProgram *program) {
// Tell OpenGL which VBOs to use
arrayBuf.bind();
indexBuf.bind();
// vertices
int vertLoc = program->attributeLocation("a_position");
program->enableAttributeArray(vertLoc);
program->setAttributeBuffer(vertLoc, GL_FLOAT, vertices[0].getVertOffset(), 3, sizeof(vertices[0]));
// Draw cube geometry using indices from VBO 1
glDrawElements(mode, indices.size(), GL_UNSIGNED_SHORT, 0);
}
private:
/** to conserve memory, avoid duplicate VNTs! */
int addOnce(const Vert& vnt) {
const auto it = std::find(vertices.begin(), vertices.end(), vnt);
if (it == vertices.end()) {
const int idx = vertices.size();
vertices.push_back(vnt);
return idx;
} else {
const int idx = it - vertices.begin();
return idx;
}
}
};
#endif // GLLINES_H

134
ui/map/3D/gl/GLPoints.h Normal file
View File

@@ -0,0 +1,134 @@
#ifndef GLPOINTS_H
#define GLPOINTS_H
#include <QOpenGLFunctions>
#include "GL.h"
#include "GLHelper.h"
#include <Indoor/geo/Point3.h>
class GLPoints : protected QOpenGLFunctions {
private:
QOpenGLBuffer arrayBuf;
QOpenGLBuffer indexBuf;
std::vector<VertColor> vertices;
std::vector<GLuint> indices;
// use rectangles instead of GL_POINTS
int mode = GL_TRIANGLES;
bool initOnce = true;
public:
/** ctor */
GLPoints() : arrayBuf(QOpenGLBuffer::VertexBuffer), indexBuf(QOpenGLBuffer::IndexBuffer) {
alloc();
}
/** dtor */
~GLPoints() {
destroy();
}
/** add a new face to this element */
void addPoint(const QVector3D& pt, const QColor& color) {
const QVector3D c(color.redF(), color.greenF(), color.blueF());
float s = 10 / 100.0f;
const VertColor vc1(pt + QVector3D(-s, 0, -s), c);
const VertColor vc2(pt + QVector3D(+s, 0, -s), c);
const VertColor vc3(pt + QVector3D(+s, 0, +s), c);
const VertColor vc4(pt + QVector3D(-s, 0, +s), c);
const int start = vertices.size();
vertices.push_back(vc1);
vertices.push_back(vc2);
vertices.push_back(vc3);
vertices.push_back(vc4);
indices.push_back(start + 3);
indices.push_back(start + 2);
indices.push_back(start + 1);
indices.push_back(start + 3);
indices.push_back(start + 1);
indices.push_back(start + 0);
}
void alloc() {
if (!indexBuf.isCreated()) {indexBuf.create();}
if (!arrayBuf.isCreated()) {arrayBuf.create();}
}
void destroy() {
if (indexBuf.isCreated()) {indexBuf.destroy();}
if (arrayBuf.isCreated()) {arrayBuf.destroy();}
}
/** build the underlying buffers */
void build() {
// Transfer vertex data to VBO 0
arrayBuf.bind();
arrayBuf.allocate(vertices.data(), vertices.size() * sizeof(vertices[0]));
// Transfer index data to VBO 1
indexBuf.bind();
indexBuf.allocate(indices.data(), indices.size() * sizeof(indices[0]));
}
void initGL() {
initializeOpenGLFunctions();
}
void rebuild() {
build();
}
void clear() {
indices.clear();
vertices.clear();
}
void setMode(const int mode) {
this->mode = mode;
}
/** render the element */
void render(QOpenGLShaderProgram *program) {
if (indices.empty()) {return;}
// Tell OpenGL which VBOs to use
arrayBuf.bind();
indexBuf.bind();
// vertices
int vertLoc = program->attributeLocation("a_position");
program->enableAttributeArray(vertLoc);
program->setAttributeBuffer(vertLoc, GL_FLOAT, vertices[0].getVertOffset(), 3, sizeof(vertices[0]));
// colors
int colorLoc = program->attributeLocation("a_color");
program->enableAttributeArray(colorLoc);
program->setAttributeBuffer(colorLoc, GL_FLOAT, vertices[0].getColorOffset(), 3, sizeof(vertices[0]));
// Draw cube geometry using indices from VBO 1
glDrawElements(mode, indices.size(), GL_UNSIGNED_INT, 0);
}
};
#endif // GLPOINTS_H

223
ui/map/3D/gl/GLTriangles.h Normal file
View File

@@ -0,0 +1,223 @@
#ifndef GLTRIANGLES_H
#define GLTRIANGLES_H
#include <QOpenGLFunctions>
#include "GL.h"
#include "GLHelper.h"
#include <type_traits>
#include <Indoor/geo/Point3.h>
template <typename T> class GLTriangles : protected QOpenGLFunctions {
private:
QOpenGLBuffer arrayBuf;
QOpenGLBuffer indexBuf;
QOpenGLTexture* textures[4];
std::vector<T> vertices;
std::vector<GLuint> indices;
public:
/** ctor */
GLTriangles() : arrayBuf(QOpenGLBuffer::VertexBuffer), indexBuf(QOpenGLBuffer::IndexBuffer), textures() {
;
}
/** dtor */
~GLTriangles() {
arrayBuf.destroy();
indexBuf.destroy();
for (int i = 0; i < 4; ++i) {delete textures[i];}
}
/** set the to-be-used texture */
void setTexture(const int slot, const QString& textureFile) {
const QImage img(textureFile);
if (img.width() <= 0) {throw "error";}
QOpenGLTexture* texture = new QOpenGLTexture(img);
texture->setMinificationFilter(QOpenGLTexture::Linear);
texture->setMagnificationFilter(QOpenGLTexture::Linear);
texture->setWrapMode(QOpenGLTexture::Repeat);
texture->generateMipMaps();
textures[slot] = texture;
}
void setDiffuse(const QString& textureFile) {
setTexture(0, textureFile);
}
void setNormalMap(const QString& textureFile) {
setTexture(1, textureFile);
}
/** add a new face to this element */
void addFace(const T& vnt1, const T& vnt2, const T& vnt3) {
addFace(vnt1, vnt2, vnt3, 0);
}
/** add a new face to this element */
void addFaceCCW(const T& vnt1, const T& vnt2, const T& vnt3) {
addFace(vnt1, vnt2, vnt3, 1);
}
/** add a new face to this element */
void addFaceCW(const T& vnt1, const T& vnt2, const T& vnt3) {
addFace(vnt1, vnt2, vnt3, 2);
}
/** add a new quad to this element */
void addQuadCCW(const T& vnt1, const T& vnt2, const T& vnt3, const T& vnt4) {
addFace(vnt1, vnt2, vnt3, 1);
addFace(vnt3, vnt4, vnt1, 1);
}
/** add a new quad to this element */
void addQuadCW(const T& vnt1, const T& vnt2, const T& vnt3, const T& vnt4) {
addFace(vnt1, vnt2, vnt3, 2);
addFace(vnt3, vnt4, vnt1, 2);
}
/** add a new quad to this element */
void addQuad(const T& vnt1, const T& vnt2, const T& vnt3, const T& vnt4) {
addFace(vnt1, vnt2, vnt3, 0);
addFace(vnt3, vnt4, vnt1, 0);
}
/** build the underlying buffers */
void build() {
initializeOpenGLFunctions();
// Transfer vertex data to VBO 0
arrayBuf.create();
arrayBuf.bind();
arrayBuf.allocate(vertices.data(), vertices.size() * sizeof(vertices[0]));
// Transfer index data to VBO 1
indexBuf.create();
indexBuf.bind();
indexBuf.allocate(indices.data(), indices.size() * sizeof(indices[0]));
}
void rebuild() {
if (indexBuf.isCreated()) {indexBuf.destroy();}
if (arrayBuf.isCreated()) {arrayBuf.destroy();}
build();
}
void clear() {
indices.clear();
vertices.clear();
}
/** render the element */
void render(QOpenGLShaderProgram* program) {
// nothing to-do?
if (indices.empty()) {return;}
// Tell OpenGL which VBOs to use
arrayBuf.bind();
indexBuf.bind();
for (int i = 0; i < 4; ++i) {
if (textures[i]) { textures[i]->bind(i); }
}
// vertices
int vertLoc = program->attributeLocation("a_position");
program->enableAttributeArray(vertLoc);
program->setAttributeBuffer(vertLoc, GL_FLOAT, vertices[0].getVertOffset(), 3, sizeof(vertices[0]));
// Tell OpenGL programmable pipeline how to locate vertex texture coordinate data
if (T::hasNormal()) {
int normLoc = program->attributeLocation("a_normal");
program->enableAttributeArray(normLoc);
program->setAttributeBuffer(normLoc, GL_FLOAT, vertices[0].getNormOffset(), 3, sizeof(vertices[0]));
}
if (T::hasTexCoord()) {
int texcoordLocation = program->attributeLocation("a_texcoord");
program->enableAttributeArray(texcoordLocation);
program->setAttributeBuffer(texcoordLocation, GL_FLOAT, vertices[0].getTexOffset(), 2, sizeof(vertices[0]));
}
// bind tangent data?
if (T::hasTangent()) {
int tanLocation = program->attributeLocation("a_tangent");
program->enableAttributeArray(tanLocation);
program->setAttributeBuffer(tanLocation, GL_FLOAT, vertices[0].getTanOffset(), 3, sizeof(vertices[0]));
}
// bind color data?
if (T::hasColor()) {
int colorLoc = program->attributeLocation("a_color");
program->enableAttributeArray(colorLoc);
program->setAttributeBuffer(colorLoc, GL_FLOAT, vertices[0].getColorOffset(), 3, sizeof(vertices[0]));
}
// texture
program->setUniformValue("texture", 0);
// Draw cube geometry using indices from VBO 1
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
}
private:
void addFace(const T& vnt1, const T& vnt2, const T& vnt3, const int mode) {
// add vertices (remove duplicates!)
const int i1 = addOnce(vnt1);
const int i2 = addOnce(vnt2);
const int i3 = addOnce(vnt3);
// get current orientation
const bool ccw = GLHelper::isCCW(vnt1.vert, vnt2.vert, vnt3.vert);
// create indices
if (mode == 0 || (mode == 1 && ccw) || (mode == 2 && !ccw) ) {
indices.push_back(i1);
indices.push_back(i2);
indices.push_back(i3);
} else {
indices.push_back(i3);
indices.push_back(i2);
indices.push_back(i1);
}
}
/** to conserve memory, avoid duplicate VNTs! */
int addOnce(const T& vnt) {
const auto it = std::find(vertices.begin(), vertices.end(), vnt);
if (it == vertices.end()) {
const int idx = vertices.size();
vertices.push_back(vnt);
return idx;
} else {
const int idx = it - vertices.begin();
return idx;
}
}
};
#endif // GLTRIANGLES_H

31
ui/map/3D/gl/Shader.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef SHADER_H
#define SHADER_H
#include <QOpenGLShaderProgram>
/**
* just some helper methods
*/
class Shader {
private:
QOpenGLShaderProgram program;
public:
/** get the underlying program */
QOpenGLShaderProgram* getProgram() {return &program;}
/** helper method to build the shader */
void loadShaderFromFile(const QString& vertex, const QString& fragment) {
if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, vertex)) {throw "1";}
if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, fragment)) {throw "2";}
if (!program.link()) {throw "3";}
if (!program.bind()) {throw "4";}
}
};
#endif // SHADER_H