Test QOpenGLWidget example with meshlite

master
Jeremy Hu 2018-03-10 14:57:14 +08:00
commit 7d8e8c4bc5
9 changed files with 519 additions and 0 deletions

5
README.md Normal file
View File

@ -0,0 +1,5 @@
Build
--------
```
$ qmake -spec macx-xcode
```

21
dust3d.pro Normal file
View File

@ -0,0 +1,21 @@
QT += core widgets opengl
CONFIG += debug
INCLUDEPATH += src
SOURCES += src/mainwindow.cpp
HEADERS += src/mainwindow.h
SOURCES += src/modelingwidget.cpp
HEADERS += src/modelingwidget.h
SOURCES += src/mesh.cpp
HEADERS += src/mesh.h
SOURCES += src/main.cpp
INCLUDEPATH += ../meshlite/include
LIBS += -L../meshlite/target/debug -lmeshlite
target.path = ./
INSTALLS += target

28
src/main.cpp Normal file
View File

@ -0,0 +1,28 @@
#include <QApplication>
#include <QDesktopWidget>
#include "mainwindow.h"
#include "meshlite.h"
int main(int argc, char ** argv)
{
/*
void *lite = meshlite_create_context();
int first = meshlite_import(lite, "/Users/jeremy/cube.obj");
int second = meshlite_import(lite, "/Users/jeremy/ball.obj");
meshlite_scale(lite, first, 0.65);
int result = meshlite_intersect(lite, first, second);
meshlite_export(lite, result, "/Users/jeremy/testlib.obj");
*/
QApplication app(argc, argv);
QCoreApplication::setApplicationName("Dust 3D");
MainWindow mainWindow;
mainWindow.resize(mainWindow.sizeHint());
int desktopArea = QApplication::desktop()->width() *
QApplication::desktop()->height();
int widgetArea = mainWindow.width() * mainWindow.height();
if (((float)widgetArea / (float)desktopArea) < 0.75f)
mainWindow.show();
else
mainWindow.showMaximized();
return app.exec();
}

9
src/mainwindow.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "mainwindow.h"
MainWindow::MainWindow()
{
modelingWidget = new ModelingWidget(this);
setCentralWidget(modelingWidget);
}

17
src/mainwindow.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef MAIN_WINDOW_H
#define MAIN_WINDOW_H
#include <QMainWindow>
#include "modelingwidget.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
private:
ModelingWidget *modelingWidget;
};
#endif

57
src/mesh.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "mesh.h"
#include "meshlite.h"
#include <assert.h>
Mesh::Mesh(void *meshlite, int mesh_id) :
m_vertices(NULL),
m_vertexCount(0)
{
int positionCount = meshlite_get_vertex_count(meshlite, mesh_id);
GLfloat *positions = new GLfloat[positionCount * 3];
int loadedPositionItemCount = meshlite_get_vertex_position_array(meshlite, mesh_id, positions, positionCount * 3);
int triangleCount = meshlite_get_face_count(meshlite, mesh_id);
int *triangleIndices = new int[triangleCount * 3];
int loadedIndexItemCount = meshlite_get_triangle_index_array(meshlite, mesh_id, triangleIndices, triangleCount * 3);
GLfloat *normals = new GLfloat[triangleCount * 3];
int loadedNormalItemCount = meshlite_get_triangle_normal_array(meshlite, mesh_id, normals, triangleCount * 3);
m_vertexCount = triangleCount * 3;
m_vertices = new Vertex[m_vertexCount * 3];
for (int i = 0; i < triangleCount; i++) {
int firstIndex = i * 3;
for (int j = 0; j < 3; j++) {
assert(firstIndex + j < loadedIndexItemCount);
int posIndex = triangleIndices[firstIndex + j] * 3;
assert(posIndex < loadedPositionItemCount);
Vertex *v = &m_vertices[firstIndex + j];
v->posX = positions[posIndex + 0];
v->posY = positions[posIndex + 1];
v->posZ = positions[posIndex + 2];
assert(firstIndex + 2 < loadedNormalItemCount);
v->normX = normals[firstIndex + 0];
v->normY = normals[firstIndex + 1];
v->normZ = normals[firstIndex + 2];
}
}
delete[] positions;
delete[] triangleIndices;
delete[] normals;
}
Mesh::~Mesh()
{
delete[] m_vertices;
m_vertexCount = 0;
}
Vertex *Mesh::vertices()
{
return m_vertices;
}
int Mesh::vertexCount()
{
return m_vertexCount;
}

31
src/mesh.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef MESH_H
#define MESH_H
#include <QObject>
#include <QOpenGLFunctions>
#pragma pack(push)
#pragma pack(1)
typedef struct
{
GLfloat posX;
GLfloat posY;
GLfloat posZ;
GLfloat normX;
GLfloat normY;
GLfloat normZ;
} Vertex;
#pragma pack(pop)
class Mesh
{
public:
Mesh(void *meshlite, int mesh_id);
~Mesh();
Vertex *vertices();
int vertexCount();
private:
Vertex *m_vertices;
int m_vertexCount;
};
#endif

280
src/modelingwidget.cpp Normal file
View File

@ -0,0 +1,280 @@
#include "modelingwidget.h"
#include <QMouseEvent>
#include <QOpenGLShaderProgram>
#include <QCoreApplication>
#include <math.h>
#include "meshlite.h"
// Modifed from http://doc.qt.io/qt-5/qtopengl-hellogl2-glwidget-cpp.html
bool ModelingWidget::m_transparent = false;
ModelingWidget::ModelingWidget(QWidget *parent)
: QOpenGLWidget(parent),
m_xRot(0),
m_yRot(0),
m_zRot(0),
m_program(0),
m_mesh(NULL)
{
m_core = QSurfaceFormat::defaultFormat().profile() == QSurfaceFormat::CoreProfile;
// --transparent causes the clear color to be transparent. Therefore, on systems that
// support it, the widget will become transparent apart from the logo.
if (m_transparent) {
QSurfaceFormat fmt = format();
fmt.setAlphaBufferSize(8);
setFormat(fmt);
}
void *lite = meshlite_create_context();
int first = meshlite_import(lite, "/Users/jeremy/cube.obj");
int second = meshlite_import(lite, "/Users/jeremy/ball.obj");
meshlite_scale(lite, first, 0.65);
int intersect = meshlite_intersect(lite, first, second);
int triangulate = meshlite_triangulate(lite, intersect);
meshlite_export(lite, triangulate, "/Users/jeremy/testlib.obj");
Mesh *mesh = new Mesh(lite, triangulate);
updateMesh(mesh);
}
ModelingWidget::~ModelingWidget()
{
cleanup();
delete m_mesh;
}
QSize ModelingWidget::minimumSizeHint() const
{
return QSize(50, 50);
}
QSize ModelingWidget::sizeHint() const
{
return QSize(400, 400);
}
static void qNormalizeAngle(int &angle)
{
while (angle < 0)
angle += 360 * 16;
while (angle > 360 * 16)
angle -= 360 * 16;
}
void ModelingWidget::setXRotation(int angle)
{
qNormalizeAngle(angle);
if (angle != m_xRot) {
m_xRot = angle;
emit xRotationChanged(angle);
update();
}
}
void ModelingWidget::setYRotation(int angle)
{
qNormalizeAngle(angle);
if (angle != m_yRot) {
m_yRot = angle;
emit yRotationChanged(angle);
update();
}
}
void ModelingWidget::setZRotation(int angle)
{
qNormalizeAngle(angle);
if (angle != m_zRot) {
m_zRot = angle;
emit zRotationChanged(angle);
update();
}
}
void ModelingWidget::cleanup()
{
if (m_program == nullptr)
return;
makeCurrent();
m_modelVbo.destroy();
delete m_program;
m_program = 0;
doneCurrent();
}
static const char *vertexShaderSourceCore =
"#version 150\n"
"in vec4 vertex;\n"
"in vec3 normal;\n"
"out vec3 vert;\n"
"out vec3 vertNormal;\n"
"uniform mat4 projMatrix;\n"
"uniform mat4 mvMatrix;\n"
"uniform mat3 normalMatrix;\n"
"void main() {\n"
" vert = vertex.xyz;\n"
" vertNormal = normalMatrix * normal;\n"
" gl_Position = projMatrix * mvMatrix * vertex;\n"
"}\n";
static const char *fragmentShaderSourceCore =
"#version 150\n"
"in highp vec3 vert;\n"
"in highp vec3 vertNormal;\n"
"out highp vec4 fragColor;\n"
"uniform highp vec3 lightPos;\n"
"void main() {\n"
" highp vec3 L = normalize(lightPos - vert);\n"
" highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n"
" highp vec3 color = vec3(0.39, 1.0, 0.0);\n"
" highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n"
" fragColor = vec4(col, 1.0);\n"
"}\n";
static const char *vertexShaderSource =
"attribute vec4 vertex;\n"
"attribute vec3 normal;\n"
"varying vec3 vert;\n"
"varying vec3 vertNormal;\n"
"uniform mat4 projMatrix;\n"
"uniform mat4 mvMatrix;\n"
"uniform mat3 normalMatrix;\n"
"void main() {\n"
" vert = vertex.xyz;\n"
" vertNormal = normalMatrix * normal;\n"
" gl_Position = projMatrix * mvMatrix * vertex;\n"
"}\n";
static const char *fragmentShaderSource =
"varying highp vec3 vert;\n"
"varying highp vec3 vertNormal;\n"
"uniform highp vec3 lightPos;\n"
"void main() {\n"
" highp vec3 L = normalize(lightPos - vert);\n"
" highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n"
" highp vec3 color = vec3(0.39, 1.0, 0.0);\n"
" highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n"
" gl_FragColor = vec4(col, 1.0);\n"
"}\n";
void ModelingWidget::initializeGL()
{
// In this example the widget's corresponding top-level window can change
// several times during the widget's lifetime. Whenever this happens, the
// QOpenGLWidget's associated context is destroyed and a new one is created.
// Therefore we have to be prepared to clean up the resources on the
// aboutToBeDestroyed() signal, instead of the destructor. The emission of
// the signal will be followed by an invocation of initializeGL() where we
// can recreate all resources.
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ModelingWidget::cleanup);
initializeOpenGLFunctions();
glClearColor(0, 0, 0, m_transparent ? 0 : 1);
m_program = new QOpenGLShaderProgram;
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, m_core ? vertexShaderSourceCore : vertexShaderSource);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, m_core ? fragmentShaderSourceCore : fragmentShaderSource);
m_program->bindAttributeLocation("vertex", 0);
m_program->bindAttributeLocation("normal", 1);
m_program->link();
m_program->bind();
m_projMatrixLoc = m_program->uniformLocation("projMatrix");
m_mvMatrixLoc = m_program->uniformLocation("mvMatrix");
m_normalMatrixLoc = m_program->uniformLocation("normalMatrix");
m_lightPosLoc = m_program->uniformLocation("lightPos");
// Create a vertex array object. In OpenGL ES 2.0 and OpenGL 2.x
// implementations this is optional and support may not be present
// at all. Nonetheless the below code works in all cases and makes
// sure there is a VAO when one is needed.
m_vao.create();
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
// Setup our vertex buffer object.
m_modelVbo.create();
m_modelVbo.bind();
m_modelVbo.allocate(m_mesh->vertices(), m_mesh->vertexCount() * sizeof(Vertex));
// Store the vertex attribute bindings for the program.
setupVertexAttribs();
// Our camera never changes in this example.
m_camera.setToIdentity();
m_camera.translate(0, 0, -4.5);
// Light position is fixed.
m_program->setUniformValue(m_lightPosLoc, QVector3D(0, 0, 70));
m_program->release();
}
void ModelingWidget::setupVertexAttribs()
{
m_modelVbo.bind();
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), reinterpret_cast<void *>(3 * sizeof(GLfloat)));
m_modelVbo.release();
}
void ModelingWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
m_world.setToIdentity();
m_world.rotate(180.0f - (m_xRot / 16.0f), 1, 0, 0);
m_world.rotate(m_yRot / 16.0f, 0, 1, 0);
m_world.rotate(m_zRot / 16.0f, 0, 0, 1);
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
m_program->bind();
m_program->setUniformValue(m_projMatrixLoc, m_proj);
m_program->setUniformValue(m_mvMatrixLoc, m_camera * m_world);
QMatrix3x3 normalMatrix = m_world.normalMatrix();
m_program->setUniformValue(m_normalMatrixLoc, normalMatrix);
glDrawArrays(GL_TRIANGLES, 0, m_mesh->vertexCount());
m_program->release();
}
void ModelingWidget::resizeGL(int w, int h)
{
m_proj.setToIdentity();
m_proj.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
}
void ModelingWidget::mousePressEvent(QMouseEvent *event)
{
m_lastPos = event->pos();
}
void ModelingWidget::mouseMoveEvent(QMouseEvent *event)
{
int dx = event->x() - m_lastPos.x();
int dy = event->y() - m_lastPos.y();
if (event->buttons() & Qt::LeftButton) {
setXRotation(m_xRot - 8 * dy);
setYRotation(m_yRot - 8 * dx);
} else if (event->buttons() & Qt::RightButton) {
setXRotation(m_xRot - 8 * dy);
setZRotation(m_zRot - 8 * dx);
}
m_lastPos = event->pos();
}
void ModelingWidget::updateMesh(Mesh *mesh)
{
QMutexLocker lock(&m_meshMutex);
if (mesh != m_mesh) {
delete m_mesh;
m_mesh = mesh;
}
}

71
src/modelingwidget.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef MODELING_WIDGET_H
#define MODELING_WIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QMatrix4x4>
#include <QMutex>
#include "mesh.h"
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
class ModelingWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
ModelingWidget(QWidget *parent = 0);
~ModelingWidget();
static bool isTransparent() { return m_transparent; }
static void setTransparent(bool t) { m_transparent = t; }
QSize minimumSizeHint() const override;
QSize sizeHint() const override;
void updateMesh(Mesh *mesh);
public slots:
void setXRotation(int angle);
void setYRotation(int angle);
void setZRotation(int angle);
void cleanup();
signals:
void xRotationChanged(int angle);
void yRotationChanged(int angle);
void zRotationChanged(int angle);
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int width, int height) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
private:
void setupVertexAttribs();
bool m_core;
int m_xRot;
int m_yRot;
int m_zRot;
QPoint m_lastPos;
QOpenGLVertexArrayObject m_vao;
QOpenGLBuffer m_modelVbo;
QOpenGLShaderProgram *m_program;
int m_projMatrixLoc;
int m_mvMatrixLoc;
int m_normalMatrixLoc;
int m_lightPosLoc;
QMatrix4x4 m_proj;
QMatrix4x4 m_camera;
QMatrix4x4 m_world;
static bool m_transparent;
Mesh *m_mesh;
QMutex m_meshMutex;
};
#endif