Test QOpenGLWidget example with meshlite
commit
7d8e8c4bc5
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include "mainwindow.h"
|
||||
|
||||
|
||||
MainWindow::MainWindow()
|
||||
{
|
||||
modelingWidget = new ModelingWidget(this);
|
||||
setCentralWidget(modelingWidget);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
Loading…
Reference in New Issue