11 #include <argos3/core/simulator/visualization/visualization.h>
12 #include <argos3/plugins/simulator/visualizations/qt-opengl/qtopengl_render.h>
13 #include <argos3/plugins/simulator/visualizations/qt-opengl/qtopengl_main_window.h>
16 #include <QTextStream>
17 #include <QRegularExpression>
26 m_mapMaterials[
"__default_material"];
28 QFile cGeometryFile(GetModelDir() + QString::fromStdString(str_obj_file));
29 if(!cGeometryFile.open(QIODevice::ReadOnly)) {
31 cGeometryFile.fileName().toStdString() <<
"\": " <<
32 cGeometryFile.errorString().toStdString());
34 QTextStream cGeometryStream(&cGeometryFile);
35 ImportGeometry(cGeometryStream);
39 if (m_vecNormals.empty()) {
41 cGeometryFile.fileName().toStdString() <<
42 "\": model does not specify normals.")
45 GenerateOpenGLVectors();
52 auto tIter = m_mapMaterials.find(str_material);
53 if(tIter == std::end(m_mapMaterials)) {
63 for(
const CQTOpenGLObjModel::SMesh& s_mesh : m_vecMeshes) {
65 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, s_mesh.Material->second.Ambient.data());
66 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, s_mesh.Material->second.Diffuse.data());
67 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, s_mesh.Material->second.Emission.data());
68 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, s_mesh.Material->second.Shininess.data());
69 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, s_mesh.Material->second.Specular.data());
71 if(s_mesh.Material->second.EnableTransparency) {
73 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
76 if (!m_vecOpenGLPositions.empty()) {
77 glEnableClientState(GL_VERTEX_ARRAY);
78 glVertexPointer(3, GL_DOUBLE,
sizeof(GLdouble) * 3, m_vecOpenGLPositions.data());
80 if (!m_vecOpenGLNormals.empty()) {
81 glEnableClientState(GL_NORMAL_ARRAY);
82 glNormalPointer(GL_DOUBLE,
sizeof(GLdouble) * 3, m_vecOpenGLNormals.data());
85 glDrawElements(GL_TRIANGLES, s_mesh.Triangles.size(), GL_UNSIGNED_INT, s_mesh.Triangles.data());
87 if (!m_vecOpenGLNormals.empty()) {
88 glDisableClientState(GL_NORMAL_ARRAY);
90 if (!m_vecOpenGLPositions.empty()) {
91 glDisableClientState(GL_VERTEX_ARRAY);
94 if(s_mesh.Material->second.EnableTransparency) {
103 const QString& CQTOpenGLObjModel::GetModelDir()
const {
106 auto& cQtOpenGLRender =
dynamic_cast<CQTOpenGLRender&
>(cVisualization);
114 void CQTOpenGLObjModel::ImportGeometry(QTextStream& c_text_stream) {
116 auto itSelectedMaterial =
117 m_mapMaterials.find(
"__default_material");
121 n_index = (n_index < 0) ? (n_index + un_count) : (n_index - 1);
125 QString strLineBuffer;
127 while(c_text_stream.readLineInto(&strLineBuffer)) {
128 const QStringList& cLineBufferSplit =
129 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
130 strLineBuffer.split(QRegularExpression(
"\\s+"), Qt::SkipEmptyParts);
132 strLineBuffer.split(QRegularExpression(
"\\s+"), QString::SkipEmptyParts);
134 if(cLineBufferSplit.empty()) {
138 else if(cLineBufferSplit[0] ==
"mtllib") {
139 QFile cMaterialsFile(GetModelDir() + cLineBufferSplit.value(1));
140 cMaterialsFile.open(QIODevice::ReadOnly);
141 QTextStream cMaterialsStream(&cMaterialsFile);
142 ImportMaterials(cMaterialsStream);
144 else if(cLineBufferSplit[0] ==
"f") {
146 const QStringList& cCoordinateParts = cLineBufferSplit.value(1).split(
'/');
148 if(cCoordinateParts.count() == 1) {
150 SInt32 nVertex0 = cLineBufferSplit.value(1).toInt();
151 AdjustIndex(nVertex0, m_vecVertexCoords.size());
153 SInt32 nVertex1 = cLineBufferSplit.value(2).toInt();
154 AdjustIndex(nVertex1, m_vecVertexCoords.size());
156 for(
SInt32 n_index = 3; n_index < cLineBufferSplit.count(); n_index++) {
157 SInt32 nVertex2 = cLineBufferSplit.value(n_index).toInt();
158 AdjustIndex(nVertex2, m_vecVertexCoords.size());
160 m_vecTriangles.emplace_back(itSelectedMaterial, std::array<GLuint, 3> {
169 else if(cCoordinateParts.count() == 2) {
171 const QStringList& cVertex0 = cLineBufferSplit.value(1).split(
'/');
172 SInt32 nVertex0 = cVertex0.value(0).toInt();
173 AdjustIndex(nVertex0, m_vecVertexCoords.size());
174 SInt32 nTexture0 = cVertex0.value(1).toInt();
175 AdjustIndex(nTexture0, m_vecTextureCoords.size());
177 const QStringList& cVertex1 = cLineBufferSplit.value(2).split(
'/');
178 SInt32 nVertex1 = cVertex1.value(0).toInt();
179 AdjustIndex(nVertex1, m_vecVertexCoords.size());
180 SInt32 nTexture1 = cVertex1.value(1).toInt();
181 AdjustIndex(nTexture1, m_vecTextureCoords.size());
183 for(
SInt32 n_index = 3; n_index < cLineBufferSplit.count(); n_index++) {
184 const QStringList& cVertex2 = cLineBufferSplit.value(n_index).split(
'/');
185 SInt32 nVertex2 = cVertex2.value(0).toInt();
186 AdjustIndex(nVertex2, m_vecVertexCoords.size());
187 SInt32 nTexture2 = cVertex2.value(1).toInt();
188 AdjustIndex(nTexture2, m_vecTextureCoords.size());
190 m_vecTriangles.emplace_back(itSelectedMaterial, std::array<GLuint, 3> {
191 AddVertex(nVertex0, m_vecVertexCoords[nVertex0],
CVector3::ZERO, m_vecTextureCoords[nTexture0]),
192 AddVertex(nVertex1, m_vecVertexCoords[nVertex1],
CVector3::ZERO, m_vecTextureCoords[nTexture1]),
193 AddVertex(nVertex2, m_vecVertexCoords[nVertex2],
CVector3::ZERO, m_vecTextureCoords[nTexture2]),
197 nTexture1 = nTexture2;
201 else if(cCoordinateParts.count() == 3 && cCoordinateParts[1].isEmpty()) {
203 const QStringList& cVertex0 = cLineBufferSplit.value(1).split(
'/');
204 SInt32 nVertex0 = cVertex0.value(0).toInt();
205 AdjustIndex(nVertex0, m_vecVertexCoords.size());
206 SInt32 nNormal0 = cVertex0.value(2).toInt();
207 AdjustIndex(nNormal0, m_vecNormals.size());
209 const QStringList& cVertex1 = cLineBufferSplit.value(2).split(
'/');
210 SInt32 nVertex1 = cVertex1.value(0).toInt();
211 AdjustIndex(nVertex1, m_vecVertexCoords.size());
212 SInt32 nNormal1 = cVertex1.value(2).toInt();
213 AdjustIndex(nNormal1, m_vecNormals.size());
215 for(
SInt32 n_index = 3; n_index < cLineBufferSplit.count(); n_index++) {
216 const QStringList& cVertex2 = cLineBufferSplit.value(n_index).split(
'/');
217 SInt32 nVertex2 = cVertex2.value(0).toInt();
218 AdjustIndex(nVertex2, m_vecVertexCoords.size());
219 SInt32 nNormal2 = cVertex2.value(2).toInt();
220 AdjustIndex(nNormal2, m_vecNormals.size());
222 m_vecTriangles.emplace_back(itSelectedMaterial, std::array<GLuint, 3> {
223 AddVertex(nVertex0, m_vecVertexCoords[nVertex0], m_vecNormals[nNormal0],
CVector2::ZERO),
224 AddVertex(nVertex1, m_vecVertexCoords[nVertex1], m_vecNormals[nNormal1],
CVector2::ZERO),
225 AddVertex(nVertex2, m_vecVertexCoords[nVertex2], m_vecNormals[nNormal2],
CVector2::ZERO),
233 else if(cCoordinateParts.count() == 3) {
235 const QStringList& cVertex0 = cLineBufferSplit.value(1).split(
'/');
236 SInt32 nVertex0 = cVertex0.value(0).toInt();
237 AdjustIndex(nVertex0, m_vecVertexCoords.size());
238 SInt32 nTexture0 = cVertex0.value(1).toInt();
239 AdjustIndex(nTexture0, m_vecTextureCoords.size());
240 SInt32 nNormal0 = cVertex0.value(2).toInt();
241 AdjustIndex(nNormal0, m_vecNormals.size());
243 const QStringList& cVertex1 = cLineBufferSplit.value(2).split(
'/');
244 SInt32 nVertex1 = cVertex1.value(0).toInt();
245 AdjustIndex(nVertex1, m_vecVertexCoords.size());
246 SInt32 nTexture1 = cVertex1.value(1).toInt();
247 AdjustIndex(nTexture1, m_vecTextureCoords.size());
248 SInt32 nNormal1 = cVertex1.value(2).toInt();
249 AdjustIndex(nNormal1, m_vecNormals.size());
251 for(
SInt32 n_index = 3; n_index < cLineBufferSplit.count(); n_index++) {
252 const QStringList& cVertex2 = cLineBufferSplit.value(n_index).split(
'/');
253 SInt32 nVertex2 = cVertex2.value(0).toInt();
254 AdjustIndex(nVertex2, m_vecVertexCoords.size());
255 SInt32 nTexture2 = cVertex2.value(1).toInt();
256 AdjustIndex(nTexture2, m_vecTextureCoords.size());
257 SInt32 nNormal2 = cVertex2.value(2).toInt();
258 AdjustIndex(nNormal2, m_vecNormals.size());
260 m_vecTriangles.emplace_back(itSelectedMaterial, std::array<GLuint, 3> {
261 AddVertex(nVertex0, m_vecVertexCoords[nVertex0], m_vecNormals[nNormal0], m_vecTextureCoords[nTexture0]),
262 AddVertex(nVertex1, m_vecVertexCoords[nVertex1], m_vecNormals[nNormal1], m_vecTextureCoords[nTexture1]),
263 AddVertex(nVertex2, m_vecVertexCoords[nVertex2], m_vecNormals[nNormal2], m_vecTextureCoords[nTexture2]),
268 nTexture1 = nTexture2;
272 else if(cLineBufferSplit[0] ==
"usemtl") {
273 std::string strName = cLineBufferSplit.value(1).toStdString();
274 itSelectedMaterial = m_mapMaterials.find(strName);
276 if(itSelectedMaterial == std::end(m_mapMaterials)) {
277 itSelectedMaterial = m_mapMaterials.find(
"__default_material");
280 else if(cLineBufferSplit[0] ==
"v") {
281 #ifdef ARGOS_USE_DOUBLE
282 m_vecVertexCoords.emplace_back(cLineBufferSplit.value(1).toDouble(),
283 cLineBufferSplit.value(2).toDouble(),
284 cLineBufferSplit.value(3).toDouble());
286 m_vecVertexCoords.emplace_back(cLineBufferSplit.value(1).toFloat(),
287 cLineBufferSplit.value(2).toFloat(),
288 cLineBufferSplit.value(3).toFloat());
291 else if(cLineBufferSplit[0] ==
"vn") {
292 #ifdef ARGOS_USE_DOUBLE
293 m_vecNormals.emplace_back(cLineBufferSplit.value(1).toDouble(),
294 cLineBufferSplit.value(2).toDouble(),
295 cLineBufferSplit.value(3).toDouble());
297 m_vecNormals.emplace_back(cLineBufferSplit.value(1).toFloat(),
298 cLineBufferSplit.value(2).toFloat(),
299 cLineBufferSplit.value(3).toFloat());
302 else if(cLineBufferSplit[0] ==
"vt") {
303 #ifdef ARGOS_USE_DOUBLE
304 m_vecTextureCoords.emplace_back(cLineBufferSplit.value(1).toDouble(),
305 cLineBufferSplit.value(2).toDouble());
307 m_vecTextureCoords.emplace_back(cLineBufferSplit.value(1).toFloat(),
308 cLineBufferSplit.value(2).toFloat());
317 void CQTOpenGLObjModel::ImportMaterials(QTextStream& c_text_stream) {
320 m_mapMaterials.find(
"__default_material");
322 QString strLineBuffer;
324 while(c_text_stream.readLineInto(&strLineBuffer)) {
325 const QStringList& cLineBufferSplit =
326 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
327 strLineBuffer.split(QRegularExpression(
"\\s+"), Qt::SkipEmptyParts);
329 strLineBuffer.split(QRegularExpression(
"\\s+"), QString::SkipEmptyParts);
331 if(cLineBufferSplit.empty()) {
335 else if(cLineBufferSplit[0] ==
"newmtl") {
336 const QString& strMaterialName = cLineBufferSplit.value(1);
337 itMaterial = m_mapMaterials.find(strMaterialName.toStdString());
338 if(itMaterial != std::end(m_mapMaterials)) {
340 "\" already defined");
342 itMaterial = m_mapMaterials.emplace(strMaterialName.toStdString(), SMaterial()).first;
344 else if(cLineBufferSplit[0] ==
"Ka") {
345 itMaterial->second.Ambient[0] = cLineBufferSplit.value(1).toFloat();
346 itMaterial->second.Ambient[1] = cLineBufferSplit.value(2).toFloat();
347 itMaterial->second.Ambient[2] = cLineBufferSplit.value(3).toFloat();
349 else if(cLineBufferSplit[0] ==
"Kd") {
350 itMaterial->second.Diffuse[0] = cLineBufferSplit.value(1).toFloat();
351 itMaterial->second.Diffuse[1] = cLineBufferSplit.value(2).toFloat();
352 itMaterial->second.Diffuse[2] = cLineBufferSplit.value(3).toFloat();
354 else if(cLineBufferSplit[0] ==
"Ks") {
355 itMaterial->second.Specular[0] = cLineBufferSplit.value(1).toFloat();
356 itMaterial->second.Specular[1] = cLineBufferSplit.value(2).toFloat();
357 itMaterial->second.Specular[2] = cLineBufferSplit.value(3).toFloat();
359 else if(cLineBufferSplit[0] ==
"Tr") {
360 GLfloat fAlpha = 1.0f - cLineBufferSplit.value(1).toFloat();
362 itMaterial->second.EnableTransparency =
true;
363 itMaterial->second.Ambient[3] = fAlpha;
364 itMaterial->second.Diffuse[3] = fAlpha;
365 itMaterial->second.Specular[3] = fAlpha;
367 itMaterial->second.Alpha = fAlpha;
369 else if(cLineBufferSplit[0] ==
"d") {
370 GLfloat fAlpha = cLineBufferSplit.value(1).toFloat();
372 itMaterial->second.EnableTransparency =
true;
373 itMaterial->second.Ambient[3] = fAlpha;
374 itMaterial->second.Diffuse[3] = fAlpha;
375 itMaterial->second.Specular[3] = fAlpha;
377 itMaterial->second.Alpha = fAlpha;
379 else if(cLineBufferSplit[0] ==
"i" && cLineBufferSplit.value(1) ==
"1") {
380 itMaterial->second.Specular = {
381 0.0f, 0.0f, 0.0f, 1.0f
390 GLuint CQTOpenGLObjModel::AddVertex(
SInt32 n_key,
391 const CVector3& c_position,
392 const CVector3& c_normal,
393 const CVector2& c_texture) {
395 typedef std::multimap<SInt32, GLuint>::iterator TCacheIterator;
396 typedef std::multimap<SInt32, GLuint>::value_type TCacheEntry;
398 std::pair<TCacheIterator, TCacheIterator> cRange =
399 m_mapVertexCache.equal_range(n_key);
402 std::find_if(cRange.first,
404 [
this, &c_position, &c_normal, &c_texture] (
const TCacheEntry& c_entry) {
405 return (m_vecVertices[c_entry.second].Position == c_position &&
406 m_vecVertices[c_entry.second].Normal == c_normal &&
407 m_vecVertices[c_entry.second].Texture == c_texture);
410 if(itVertex == cRange.second) {
411 m_vecVertices.emplace_back(c_position, c_normal, c_texture);
412 itVertex = m_mapVertexCache.emplace(n_key, m_vecVertices.size() - 1);
414 return itVertex->second;
420 void CQTOpenGLObjModel::BuildMeshes() {
421 auto itCurrentMaterial = std::end(m_mapMaterials);
422 for(
const STriangle& s_triangle : m_vecTriangles) {
423 if(s_triangle.Material != itCurrentMaterial) {
425 itCurrentMaterial = s_triangle.Material;
427 m_vecMeshes.emplace_back(itCurrentMaterial);
429 m_vecMeshes.back().Triangles.push_back(s_triangle.Indices[0]);
430 m_vecMeshes.back().Triangles.push_back(s_triangle.Indices[1]);
431 m_vecMeshes.back().Triangles.push_back(s_triangle.Indices[2]);
435 std::sort(m_vecMeshes.begin(),
437 [] (
const SMesh& s_left,
438 const SMesh& s_right) {
439 return (s_left.Material->second.Alpha > s_right.Material->second.Alpha);
446 void CQTOpenGLObjModel::GenerateOpenGLVectors() {
448 m_vecOpenGLPositions.reserve(m_vecVertices.size() * 3);
449 m_vecOpenGLNormals.reserve(m_vecVertices.size() * 3);
451 for(
const SVertex& s_vertex : m_vecVertices) {
453 m_vecOpenGLPositions.push_back(s_vertex.Position.GetX());
454 m_vecOpenGLPositions.push_back(s_vertex.Position.GetY());
455 m_vecOpenGLPositions.push_back(s_vertex.Position.GetZ());
457 m_vecOpenGLNormals.push_back(s_vertex.Normal.GetX());
458 m_vecOpenGLNormals.push_back(s_vertex.Normal.GetY());
459 m_vecOpenGLNormals.push_back(s_vertex.Normal.GetZ());
#define THROW_ARGOSEXCEPTION(message)
This macro throws an ARGoS exception with the passed message.
signed int SInt32
32-bit signed integer.
The namespace containing all the ARGoS related code.
CVisualization & GetVisualization()
Returns a reference to the visualization.
static CSimulator & GetInstance()
Returns the instance to the CSimulator class.
static const CVector2 ZERO
The zero vector (0,0)
static const CVector3 ZERO
The zero vector (0,0,0)
const QString & GetModelDir() const
CQTOpenGLObjModel(const std::string &str_model)
SMaterial & GetMaterial(const std::string &str_material)
CQTOpenGLMainWindow & GetMainWindow()