-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodel.cpp
executable file
·228 lines (159 loc) · 5.99 KB
/
model.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#include "model.h"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <QFile>
#include <QFileInfo>
Model::Model(
const QString& objectFilePath,
const QString& fragmentShaderFilePath) :
vbo(QOpenGLBuffer::VertexBuffer),
ibo(QOpenGLBuffer::IndexBuffer)
{
// Load object from file into memory //
// Make GL functions available
initializeOpenGLFunctions();
// Generate buffer objects
this->vao.create();
this->vbo.create();
this->ibo.create();
// Tell the program to use the generated objects
this->vao.bind();
this->vbo.bind();
this->ibo.bind();
// Load an object file into the asset importer
this->loadObjectFromFile(objectFilePath);
// Populate the VBO with data derived from the loaded object
this->vbo.allocate(this->lengthOfVBO() * sizeof(GLfloat));
GLvoid* vboPtr = this->vbo.map(QOpenGLBuffer::WriteOnly);
Q_ASSERT(vboPtr);
this->generateVBO(static_cast<GLfloat*>(vboPtr));
this->vbo.unmap();
// Populate the IBO with data derived from the loaded object
this->iboLength = this->lengthOfIBO();
this->ibo.allocate(this->iboLength * sizeof(GLuint));
GLvoid* iboPtr = this->ibo.map(QOpenGLBuffer::WriteOnly);
Q_ASSERT(iboPtr);
this->generateIBO(static_cast<GLuint*>(iboPtr));
this->ibo.unmap();
// Communicate the buffer layout to OpenGL //
const std::size_t stride = 3 * sizeof(GLfloat);
// Define the position and layout of each vertex
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (void*)(sizeof(GLfloat) * 0));
// Compile and link shader //
this->shaderProgram = new QOpenGLShaderProgram();
// Compile and attach shader stages for linking
this->shaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vertshader/model.vert");
this->shaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, fragmentShaderFilePath);
// Link attached stages
this->shaderProgram->link();
// Abort the program if any of these steps failed
Q_ASSERT(this->shaderProgram->isLinked());
}
Model::~Model()
{
this->vao.destroy();
this->ibo.destroy();
this->vbo.destroy();
delete this->shaderProgram;
}
void Model::draw(
const QMatrix4x4& projectionMatrix,
const QMatrix4x4& viewMatrix,
const QMatrix4x4& gimbalAngle,
const float scaleFactor)
{
// Generate the roation Matrix //
this->shaderProgram->bind();
QMatrix4x4 model;
model.scale(QVector3D(scaleFactor, scaleFactor, scaleFactor));
model = model * gimbalAngle;
const auto mvpMatrix = projectionMatrix * viewMatrix * model;
this->shaderProgram->setUniformValue(0, mvpMatrix);
// Render //
this->vao.bind();
glDrawElements(GL_TRIANGLES, this->iboLength, GL_UNSIGNED_INT, nullptr);
this->vao.release();
this->shaderProgram->release();
}
void Model::loadObjectFromFile(const QString& objectFilePath)
{
// Load the file's contents into memory //
QFile file(objectFilePath);
if (!file.open(QIODevice::ReadOnly))
throw std::runtime_error(file.errorString().toStdString());
const auto buffer = file.readAll();
file.close();
// Parse the loaded contents and store them in importer //
// Retrieve file extension to give the importer a hint which module to use
const auto fileExtension = QFileInfo(objectFilePath).completeSuffix().toLatin1();
// Load scene
const auto scene = this->importer.ReadFileFromMemory(
buffer.constData(),
buffer.length(),
aiProcess_Triangulate
| aiProcess_JoinIdenticalVertices
| aiProcess_GenSmoothNormals,
fileExtension.constData()
);
// Assert import successful
if(!scene)
throw std::runtime_error(this->importer.GetErrorString());
// Assert there is at least one mesh
if (scene->mNumMeshes == 0 || scene->mMeshes[0]->mNumFaces == 0)
{
importer.FreeScene();
throw std::runtime_error("No meshes loaded");
}
}
void Model::generateVBO(GLfloat* const vbo) const
{
// Get the mesh from the importer //
if(!importer.GetScene()->HasMeshes())
throw std::invalid_argument("Scene has no meshes");
const auto* const mesh = this->importer.GetScene()->mMeshes[0];
// Flatten data to output //
unsigned int n = 0;
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
n = 0;
vbo[i*3+n++] = mesh->mVertices[i].x;
vbo[i*3+n++] = mesh->mVertices[i].y;
vbo[i*3+n++] = mesh->mVertices[i].z;
}
}
void Model::generateIBO(GLuint* const ibo) const
{
// Get the mesh from the importer //
if(!importer.GetScene()->HasMeshes())
throw std::invalid_argument("Scene has no mesh");
const auto* const mesh = this->importer.GetScene()->mMeshes[0];
// Flatten data to output //
for (unsigned int i = 0; i < mesh->mNumFaces; ++i)
{
ibo[i*3+0] = mesh->mFaces[i].mIndices[0];
ibo[i*3+1] = mesh->mFaces[i].mIndices[1];
ibo[i*3+2] = mesh->mFaces[i].mIndices[2];
}
}
unsigned int Model::lengthOfVBO() const
{
// Get the mesh from the importer //
if(!importer.GetScene()->HasMeshes())
throw std::invalid_argument("Scene has no meshes");
const auto* const mesh = this->importer.GetScene()->mMeshes[0];
// Calculate VBO length //
return mesh->mNumVertices * 3;
}
unsigned int Model::lengthOfIBO() const
{
// Get the mesh from the importer //
if(!importer.GetScene()->HasMeshes())
throw std::invalid_argument("Scene has no meshes");
const auto* const mesh = this->importer.GetScene()->mMeshes[0];
// Calculate array length //
if (mesh->mNumFaces == 0)
return 0;
return mesh->mNumFaces * mesh->mFaces[0].mNumIndices;
}