assimp/tools/assimp_qt_viewer/glview.cpp

1162 lines
38 KiB
C++

/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2018, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
#include "glview.hpp"
// Header files, Qt.
#include <QTime>
// Header files, OpenGL.
#if defined(__APPLE__)
# include <OpenGL/glu.h>
#else
# include <GL/glu.h>
#endif
// Header files, DevIL.
// Header files, Assimp.
#include <assimp/DefaultLogger.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include "contrib/stb_image/stb_image.h"
CGLView::SHelper_Mesh::SHelper_Mesh(const size_t pQuantity_Point, const size_t pQuantity_Line, const size_t pQuantity_Triangle, const SBBox& pBBox)
: Quantity_Point(pQuantity_Point)
, Quantity_Line(pQuantity_Line)
, Quantity_Triangle(pQuantity_Triangle)
, BBox(pBBox) {
Index_Point = pQuantity_Point ? new GLuint[pQuantity_Point * 1] : nullptr;
Index_Line = pQuantity_Line ? new GLuint[pQuantity_Line * 2] : nullptr;
Index_Triangle = pQuantity_Triangle ? new GLuint[pQuantity_Triangle * 3] : nullptr;
}
CGLView::SHelper_Mesh::~SHelper_Mesh() {
delete [] Index_Point;
delete [] Index_Line;
delete [] Index_Triangle;
}
void CGLView::SHelper_Camera::SetDefault() {
Position.Set(0, 0, 0);
Target.Set(0, 0, -1);
Rotation_AroundCamera = aiMatrix4x4();
Rotation_Scene = aiMatrix4x4();
Translation_ToScene.Set(0, 0, 2);
}
static void set_float4(float f[4], float a, float b, float c, float d) {
f[0] = a;
f[1] = b;
f[2] = c;
f[3] = d;
}
static void color4_to_float4(const aiColor4D *c, float f[4]) {
f[0] = c->r;
f[1] = c->g;
f[2] = c->b;
f[3] = c->a;
}
void CGLView::Material_Apply(const aiMaterial* pMaterial) {
GLfloat tcol[4];
aiColor4D taicol;
unsigned int max;
int ret1, ret2;
int texture_index = 0;
aiString texture_path;
///TODO: cache materials
// Disable color material because glMaterial is used.
glDisable(GL_COLOR_MATERIAL);///TODO: cache
// Set texture. If assigned.
if(AI_SUCCESS == pMaterial->GetTexture(aiTextureType_DIFFUSE, texture_index, &texture_path)) {
//bind texture
unsigned int texture_ID = mTexture_IDMap.value(texture_path.data, 0);
glBindTexture(GL_TEXTURE_2D, texture_ID);
}
//
// Set material parameters from scene or default values.
//
// Diffuse
set_float4(tcol, 0.8f, 0.8f, 0.8f, 1.0f);
if ( AI_SUCCESS == aiGetMaterialColor( pMaterial, AI_MATKEY_COLOR_DIFFUSE, &taicol )) {
color4_to_float4( &taicol, tcol );
}
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, tcol);
// Specular
set_float4(tcol, 0.0f, 0.0f, 0.0f, 1.0f);
if ( AI_SUCCESS == aiGetMaterialColor( pMaterial, AI_MATKEY_COLOR_SPECULAR, &taicol )) {
color4_to_float4( &taicol, tcol );
}
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, tcol);
// Ambient
set_float4(tcol, 0.2f, 0.2f, 0.2f, 1.0f);
if ( AI_SUCCESS == aiGetMaterialColor( pMaterial, AI_MATKEY_COLOR_AMBIENT, &taicol )) {
color4_to_float4( &taicol, tcol );
}
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, tcol);
// Emission
set_float4(tcol, 0.0f, 0.0f, 0.0f, 1.0f);
if(AI_SUCCESS == aiGetMaterialColor(pMaterial, AI_MATKEY_COLOR_EMISSIVE, &taicol)) color4_to_float4(&taicol, tcol);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, tcol);
// Shininess
ai_real shininess, strength;
max = 1;
ret1 = aiGetMaterialFloatArray(pMaterial, AI_MATKEY_SHININESS, &shininess, &max);
// Shininess strength
max = 1;
ret2 = aiGetMaterialFloatArray(pMaterial, AI_MATKEY_SHININESS_STRENGTH, &strength, &max);
if((ret1 == AI_SUCCESS) && (ret2 == AI_SUCCESS)) {
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess * strength);///TODO: cache
} else {
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0f);///TODO: cache
set_float4(tcol, 0.0f, 0.0f, 0.0f, 0.0f);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, tcol);
}
// Fill mode
GLenum fill_mode;
int wireframe;
max = 1;
if(AI_SUCCESS == aiGetMaterialIntegerArray(pMaterial, AI_MATKEY_ENABLE_WIREFRAME, &wireframe, &max))
fill_mode = wireframe ? GL_LINE : GL_FILL;
else
fill_mode = GL_FILL;
glPolygonMode(GL_FRONT_AND_BACK, fill_mode);///TODO: cache
// Fill side
int two_sided;
max = 1;
if((AI_SUCCESS == aiGetMaterialIntegerArray(pMaterial, AI_MATKEY_TWOSIDED, &two_sided, &max)) && two_sided)///TODO: cache
glDisable(GL_CULL_FACE);
else
glEnable(GL_CULL_FACE);
}
void CGLView::Matrix_NodeToRoot(const aiNode* pNode, aiMatrix4x4& pOutMatrix)
{
const aiNode* node_cur;
std::list<aiMatrix4x4> mat_list;
pOutMatrix = aiMatrix4x4();
// starting walk from current element to root
node_cur = pNode;
if(node_cur != nullptr)
{
do
{
// if cur_node is group then store group transformation matrix in list.
mat_list.push_back(node_cur->mTransformation);
node_cur = node_cur->mParent;
} while(node_cur != nullptr);
}
// multiply all matrices in reverse order
for ( std::list<aiMatrix4x4>::reverse_iterator rit = mat_list.rbegin(); rit != mat_list.rend(); ++rit)
{
pOutMatrix = pOutMatrix * (*rit);
}
}
void CGLView::ImportTextures(const QString& scenePath) {
auto LoadTexture = [&](const QString& pFileName) -> bool ///TODO: IME texture mode, operation.
{
GLuint id_ogl_texture;// OpenGL texture ID.
if(!pFileName.startsWith(AI_EMBEDDED_TEXNAME_PREFIX))
{
QString basepath = scenePath.left(scenePath.lastIndexOf('/') + 1);// path with '/' at the end.
QString fileloc = (basepath + pFileName);
fileloc.replace('\\', "/");
int x, y, n;
unsigned char *data = stbi_load(fileloc.toLocal8Bit(), &x, &y, &n, STBI_rgb_alpha );
if ( nullptr == data ) {
LogError(QString("Couldn't load Image: %1").arg(fileloc));
return false;
}
// Convert every colour component into unsigned byte. If your image contains alpha channel you can replace IL_RGB with IL_RGBA.
glGenTextures(1, &id_ogl_texture);// Texture ID generation.
mTexture_IDMap[pFileName] = id_ogl_texture;// save texture ID for filename in map
glBindTexture(GL_TEXTURE_2D, id_ogl_texture);// Binding of texture ID.
// Redefine standard texture values
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// We will use linear interpolation for magnification filter.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);// We will use linear interpolation for minifying filter.
glTexImage2D(GL_TEXTURE_2D, 0, n, x, y, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, data );// Texture specification.
stbi_image_free(data);
// Cleanup
}
else
{
struct SPixel_Description
{
const char* FormatHint;
const GLint Image_InternalFormat;
const GLint Pixel_Format;
};
constexpr SPixel_Description Pixel_Description[] = {
{"rgba8880", GL_RGB, GL_RGB},
{"rgba8888", GL_RGBA, GL_RGBA}
};
constexpr size_t Pixel_Description_Count = sizeof(Pixel_Description) / sizeof(SPixel_Description);
size_t idx_description;
// Get texture index.
bool ok;
size_t idx_texture = pFileName.right(strlen(AI_EMBEDDED_TEXNAME_PREFIX)).toULong(&ok);
if(!ok)
{
LogError("Can not get index of the embedded texture from path in material.");
return false;
}
// Create alias for conveniance.
const aiTexture& als = *mScene->mTextures[idx_texture];
if(als.mHeight == 0)// Compressed texture.
{
LogError("IME: compressed embedded textures are not implemented.");
}
else
{
ok = false;
for(size_t idx = 0; idx < Pixel_Description_Count; idx++)
{
if(als.CheckFormat(Pixel_Description[idx].FormatHint))
{
idx_description = idx;
ok = true;
break;
}
}
if(!ok)
{
LogError(QString("Unsupported format hint for embedded texture: [%1]").arg(als.achFormatHint));
return false;
}
glGenTextures(1, &id_ogl_texture);// Texture ID generation.
mTexture_IDMap[pFileName] = id_ogl_texture;// save texture ID for filename in map
glBindTexture(GL_TEXTURE_2D, id_ogl_texture);// Binding of texture ID.
// Redefine standard texture values
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// We will use linear interpolation for magnification filter.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);// We will use linear interpolation for minifying filter.
// Texture specification.
glTexImage2D(GL_TEXTURE_2D, 0, Pixel_Description[idx_description].Image_InternalFormat, als.mWidth, als.mHeight, 0,
Pixel_Description[idx_description].Pixel_Format, GL_UNSIGNED_BYTE, (uint8_t*)als.pcData);
}// if(als.mHeight == 0) else
}// if(!filename.startsWith(AI_EMBEDDED_TEXNAME_PREFIX)) else
return true;
};// auto LoadTexture = [&](const aiString& pPath)
if(mScene == nullptr)
{
LogError("Trying to load textures for empty scene.");
return;
}
//
// Load textures.
//
// Get textures file names and number of textures.
for(size_t idx_material = 0; idx_material < mScene->mNumMaterials; idx_material++) {
int idx_texture = 0;
aiString path;
do {
if (mScene->mMaterials[ idx_material ]->GetTexture( aiTextureType_DIFFUSE, idx_texture, &path ) != AI_SUCCESS) {
break;
}
LoadTexture(QString(path.C_Str()));
idx_texture++;
} while(true);
}// for(size_t idx_material = 0; idx_material < mScene->mNumMaterials; idx_material++)
// Textures list is empty, exit.
if(mTexture_IDMap.empty()) {
LogInfo("No textures for import.");
}
}
void CGLView::BBox_GetForNode(const aiNode& pNode, const aiMatrix4x4& pParent_TransformationMatrix, SBBox& pNodeBBox, bool& pFirstAssign)
{
aiMatrix4x4 mat_trans = pParent_TransformationMatrix * pNode.mTransformation;
// Check if node has meshes
for(size_t idx_idx_mesh = 0; idx_idx_mesh < pNode.mNumMeshes; idx_idx_mesh++)
{
size_t idx_mesh;
SBBox bbox_local;
aiVector3D bbox_vertices[8];
idx_mesh = pNode.mMeshes[idx_idx_mesh];
// Get vertices of mesh BBox
BBox_GetVertices(mHelper_Mesh[idx_mesh]->BBox, bbox_vertices);
// Transform vertices
for(size_t idx_vert = 0; idx_vert < 8; idx_vert++) bbox_vertices[idx_vert] *= mat_trans;
// And create BBox for transformed mesh
BBox_GetFromVertices(bbox_vertices, 8, bbox_local);
if(!pFirstAssign)
{
BBox_Extend(bbox_local, pNodeBBox);
}
else
{
pFirstAssign = false;
pNodeBBox = bbox_local;
}
}// for(size_t idx_idx_mesh = 0; idx_idx_mesh < pNode.mNumMeshes; idx_idx_mesh++)
for(size_t idx_node = 0; idx_node < pNode.mNumChildren; idx_node++)
{
BBox_GetForNode(*pNode.mChildren[idx_node], mat_trans, pNodeBBox, pFirstAssign);
}
}
void CGLView::BBox_Extend(const SBBox& pChild, SBBox& pParent)
{
// search minimal...
AssignIfLesser(&pParent.Minimum.x, pChild.Minimum.x);
AssignIfLesser(&pParent.Minimum.y, pChild.Minimum.y);
AssignIfLesser(&pParent.Minimum.z, pChild.Minimum.z);
// and maximal values
AssignIfGreater(&pParent.Maximum.x, pChild.Maximum.x);
AssignIfGreater(&pParent.Maximum.y, pChild.Maximum.y);
AssignIfGreater(&pParent.Maximum.z, pChild.Maximum.z);
}
void CGLView::BBox_GetVertices(const SBBox& pBBox, aiVector3D pVertex[8])
{
pVertex[0] = pBBox.Minimum;
pVertex[1].Set(pBBox.Minimum.x, pBBox.Minimum.y, pBBox.Maximum.z);
pVertex[2].Set(pBBox.Minimum.x, pBBox.Maximum.y, pBBox.Maximum.z);
pVertex[3].Set(pBBox.Minimum.x, pBBox.Maximum.y, pBBox.Minimum.z);
pVertex[4].Set(pBBox.Maximum.x, pBBox.Minimum.y, pBBox.Minimum.z);
pVertex[5].Set(pBBox.Maximum.x, pBBox.Minimum.y, pBBox.Maximum.z);
pVertex[6] = pBBox.Maximum;
pVertex[7].Set(pBBox.Maximum.x, pBBox.Maximum.y, pBBox.Minimum.z);
}
void CGLView::BBox_GetFromVertices(const aiVector3D* pVertices, const size_t pVerticesQuantity, SBBox& pBBox)
{
if(pVerticesQuantity == 0)
{
pBBox.Maximum.Set(0, 0, 0);
pBBox.Minimum.Set(0, 0, 0);
return;
}
// Assign first values.
pBBox.Minimum = pVertices[0];
pBBox.Maximum = pVertices[0];
for(size_t idx_vert = 1; idx_vert < pVerticesQuantity; idx_vert++)
{
const ai_real x = pVertices[idx_vert].x;
const ai_real y = pVertices[idx_vert].y;
const ai_real z = pVertices[idx_vert].z;
// search minimal...
AssignIfLesser(&pBBox.Minimum.x, x);
AssignIfLesser(&pBBox.Minimum.y, y);
AssignIfLesser(&pBBox.Minimum.z, z);
// and maximal values
AssignIfGreater(&pBBox.Maximum.x, x);
AssignIfGreater(&pBBox.Maximum.y, y);
AssignIfGreater(&pBBox.Maximum.z, z);
}
}
void CGLView::LogInfo(const QString& pMessage) {
Assimp::DefaultLogger::get()->info(pMessage.toStdString());
}
void CGLView::LogError(const QString& pMessage) {
Assimp::DefaultLogger::get()->error(pMessage.toStdString());
}
void CGLView::Draw_Node(const aiNode* pNode) {
aiMatrix4x4 mat_node = pNode->mTransformation;
// Apply node transformation matrix.
mat_node.Transpose();
glPushMatrix();
#ifdef ASSIMP_DOUBLE_PRECISION
glMultMatrixd((GLdouble*)mat_node[0]);
#else
glMultMatrixf((GLfloat*)&mat_node);
#endif // ASSIMP_DOUBLE_PRECISION
// Draw all meshes assigned to this node
for(size_t idx_mesh_arr = 0; idx_mesh_arr < pNode->mNumMeshes; idx_mesh_arr++) Draw_Mesh(pNode->mMeshes[idx_mesh_arr]);
// Draw all children nodes
for(size_t idx_node = 0; idx_node < pNode->mNumChildren; idx_node++) Draw_Node(pNode->mChildren[idx_node]);
// Restore transformation matrix.
glPopMatrix();
}
void CGLView::Draw_Mesh(const size_t pMesh_Index)
{
// Check argument
if(pMesh_Index >= mHelper_Mesh_Quantity) return;
aiMesh& mesh_cur = *mScene->mMeshes[pMesh_Index];
if(!mesh_cur.HasPositions()) return;// Nothing to draw.
// If mesh use material then apply it
if(mScene->HasMaterials()) Material_Apply(mScene->mMaterials[mesh_cur.mMaterialIndex]);
//
// Vertices array
//
glEnableClientState(GL_VERTEX_ARRAY);
#if ASSIMP_DOUBLE_PRECISION
glVertexPointer(3, GL_DOUBLE, 0, mesh_cur.mVertices);
#else
glVertexPointer(3, GL_FLOAT, 0, mesh_cur.mVertices);
#endif // ASSIMP_DOUBLE_PRECISION
if(mesh_cur.HasVertexColors(0))
{
glEnable(GL_COLOR_MATERIAL);///TODO: cache
glEnableClientState(GL_COLOR_ARRAY);
#ifdef ASSIMP_DOUBLE_PRECISION
glColorPointer(4, GL_DOUBLE, 0, mesh_cur.mColors[0]);
#else
glColorPointer(4, GL_FLOAT, 0, mesh_cur.mColors[0]);
#endif // ASSIMP_DOUBLE_PRECISION
}
//
// Texture coordinates array
//
if(mesh_cur.HasTextureCoords(0))
{
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
#ifdef ASSIMP_DOUBLE_PRECISION
glTexCoordPointer(2, GL_DOUBLE, sizeof(aiVector3D), mesh_cur.mTextureCoords[0]);
#else
glTexCoordPointer(2, GL_FLOAT, sizeof(aiVector3D), mesh_cur.mTextureCoords[0]);
#endif // ASSIMP_DOUBLE_PRECISION
}
//
// Normals array
//
if(mesh_cur.HasNormals())
{
glEnableClientState(GL_NORMAL_ARRAY);
#ifdef ASSIMP_DOUBLE_PRECISION
glNormalPointer(GL_DOUBLE, 0, mesh_cur.mNormals);
#else
glNormalPointer(GL_FLOAT, 0, mesh_cur.mNormals);
#endif // ASSIMP_DOUBLE_PRECISION
}
//
// Draw arrays
//
SHelper_Mesh& helper_cur = *mHelper_Mesh[pMesh_Index];
if(helper_cur.Quantity_Triangle > 0) glDrawElements(GL_TRIANGLES, helper_cur.Quantity_Triangle * 3, GL_UNSIGNED_INT, helper_cur.Index_Triangle);
if(helper_cur.Quantity_Line > 0) glDrawElements(GL_LINES,helper_cur.Quantity_Line * 2, GL_UNSIGNED_INT, helper_cur.Index_Line);
if(helper_cur.Quantity_Point > 0) glDrawElements(GL_POINTS, helper_cur.Quantity_Point, GL_UNSIGNED_INT, helper_cur.Index_Point);
//
// Clean up
//
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}
void CGLView::Draw_BBox(const SBBox& pBBox)
{
aiVector3D vertex[8];
BBox_GetVertices(pBBox, vertex);
// Draw
if(mLightingEnabled) glDisable(GL_LIGHTING);///TODO: display list
glEnable(GL_COLOR_MATERIAL);
glBindTexture(GL_TEXTURE_1D, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindTexture(GL_TEXTURE_3D, 0);
const QColor c_w(Qt::white);
glColor3f(c_w.redF(), c_w.greenF(), c_w.blueF());
glBegin(GL_LINE_STRIP);
# ifdef ASSIMP_DOUBLE_PRECISION
glVertex3dv(&vertex[0][0]), glVertex3dv(&vertex[1][0]), glVertex3dv(&vertex[2][0]), glVertex3dv(&vertex[3][0]), glVertex3dv(&vertex[0][0]);// "Minimum" side.
glVertex3dv(&vertex[4][0]), glVertex3dv(&vertex[5][0]), glVertex3dv(&vertex[6][0]), glVertex3dv(&vertex[7][0]), glVertex3dv(&vertex[4][0]);// Edge and "maximum" side.
# else
glVertex3fv(&vertex[0][0]), glVertex3fv(&vertex[1][0]), glVertex3fv(&vertex[2][0]), glVertex3fv(&vertex[3][0]), glVertex3fv(&vertex[0][0]);// "Minimum" side.
glVertex3fv(&vertex[4][0]), glVertex3fv(&vertex[5][0]), glVertex3fv(&vertex[6][0]), glVertex3fv(&vertex[7][0]), glVertex3fv(&vertex[4][0]);// Edge and "maximum" side.
# endif // ASSIMP_DOUBLE_PRECISION
glEnd();
glBegin(GL_LINES);
# ifdef ASSIMP_DOUBLE_PRECISION
glVertex3dv(&vertex[1][0]), glVertex3dv(&vertex[5][0]);
glVertex3dv(&vertex[2][0]), glVertex3dv(&vertex[6][0]);
glVertex3dv(&vertex[3][0]), glVertex3dv(&vertex[7][0]);
# else
glVertex3fv(&vertex[1][0]), glVertex3fv(&vertex[5][0]);
glVertex3fv(&vertex[2][0]), glVertex3fv(&vertex[6][0]);
glVertex3fv(&vertex[3][0]), glVertex3fv(&vertex[7][0]);
# endif // ASSIMP_DOUBLE_PRECISION
glEnd();
glDisable(GL_COLOR_MATERIAL);
if(mLightingEnabled) glEnable(GL_LIGHTING);
}
void CGLView::Enable_Textures(const bool pEnable) {
if(pEnable) {
glEnable(GL_TEXTURE_1D);
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_3D);
} else {
glDisable(GL_TEXTURE_1D);
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_3D);
}
}
void CGLView::initializeGL() {
mGLContext_Current = true;
initializeOpenGLFunctions();
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glEnable(GL_NORMALIZE);
glEnable(GL_TEXTURE_2D);
glEnable( GL_MULTISAMPLE );
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT);
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
glDisable(GL_COLOR_MATERIAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
}
void CGLView::resizeGL(int width, int height) {
mCamera_Viewport_AspectRatio = (GLdouble)width / height;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(mCamera_FOVY, mCamera_Viewport_AspectRatio, 1.0, 100000.0);///TODO: znear/zfar depend on scene size.
}
void CGLView::drawCoordSystem() {
// Disable lighting. Colors must be bright and colorful)
if ( mLightingEnabled ) glDisable( GL_LIGHTING );///TODO: display list
// For same reason - disable textures.
glBindTexture(GL_TEXTURE_1D, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindTexture(GL_TEXTURE_3D, 0);
glEnable(GL_COLOR_MATERIAL);
glBegin(GL_LINES);
// X, -X
glColor3f(1.0f, 0.0f, 0.0f), glVertex3f(0.0, 0.0, 0.0), glVertex3f(100000.0, 0.0, 0.0);
glColor3f(0.5f, 0.5f, 1.0f), glVertex3f(0.0, 0.0, 0.0), glVertex3f(-100000.0, 0.0, 0.0);
// Y, -Y
glColor3f(0.0f, 1.0f, 0.0f), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 100000.0, 0.0);
glColor3f(1.0f, 0.0f, 1.0f), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, -100000.0, 0.0);
// Z, -Z
glColor3f(0.0f, 0.0f, 1.0f), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 0.0, 100000.0);
glColor3f(1.0f, 1.0f, 0.0f), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 0.0, -100000.0);
glColor3f(1.0f, 1.0f, 1.0f);
glEnd();
// Restore previous state of lighting.
if (mLightingEnabled) {
glEnable( GL_LIGHTING );
}
}
void CGLView::paintGL() {
QTime time_paintbegin;
time_paintbegin = QTime::currentTime();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Apply current camera transformations.
#if ASSIMP_DOUBLE_PRECISION
glMultMatrixd((GLdouble*)&mHelper_Camera.Rotation_AroundCamera);
glTranslated(-mHelper_Camera.Translation_ToScene.x, -mHelper_Camera.Translation_ToScene.y, -mHelper_Camera.Translation_ToScene.z);
glMultMatrixd((GLdouble*)&mHelper_Camera.Rotation_Scene);
#else
glMultMatrixf((GLfloat*)&mHelper_Camera.Rotation_AroundCamera);
glTranslatef(-mHelper_Camera.Translation_ToScene.x, -mHelper_Camera.Translation_ToScene.y, -mHelper_Camera.Translation_ToScene.z);
glMultMatrixf((GLfloat*)&mHelper_Camera.Rotation_Scene);
#endif // ASSIMP_DOUBLE_PRECISION
// Coordinate system
if ( mScene_AxesEnabled ) {
drawCoordSystem();
}
glDisable(GL_COLOR_MATERIAL);
// Scene
if(mScene != nullptr) {
Draw_Node(mScene->mRootNode);
// Scene BBox
if (mScene_DrawBBox) {
Draw_BBox( mScene_BBox );
}
}
emit Paint_Finished((size_t) time_paintbegin.msecsTo(QTime::currentTime()), mHelper_Camera.Translation_ToScene.Length());
}
CGLView::CGLView( QWidget *pParent )
: QOpenGLWidget( pParent )
, mGLContext_Current( false ) {
// set initial view
mHelper_CameraDefault.SetDefault();
Camera_Set( 0 );
}
CGLView::~CGLView() {
FreeScene();
}
void CGLView::FreeScene() {
// Set scene to null and after that \ref paintGL will not try to render it.
mScene = nullptr;
// Clean helper objects.
if(mHelper_Mesh != nullptr)
{
for(size_t idx_mesh = 0; idx_mesh < mHelper_Mesh_Quantity; idx_mesh++) delete mHelper_Mesh[idx_mesh];
delete [] mHelper_Mesh;
mHelper_Mesh = nullptr;
}
mHelper_Mesh_Quantity = 0;
// Delete textures
const int id_tex_size = mTexture_IDMap.size();
if(id_tex_size)
{
GLuint* id_tex = new GLuint[id_tex_size];
QMap<QString, GLuint>::iterator it = mTexture_IDMap.begin();
for(int idx = 0; idx < id_tex_size; idx++, ++it)
{
id_tex[idx] = it.value();
}
glDeleteTextures(id_tex_size, id_tex);
mTexture_IDMap.clear();
delete [] id_tex;
}
}
void CGLView::SetScene(const aiScene *pScene, const QString& pScenePath) {
FreeScene();// Clear old data
// Why checking here, not at begin of function. Because old scene may not exist at know. So, need cleanup.
if (pScene == nullptr) {
return;
}
mScene = pScene;// Copy pointer of new scene.
//
// Meshes
//
// Create helper objects for meshes. This allow to render meshes as OpenGL arrays.
if(mScene->HasMeshes())
{
// Create mesh helpers array.
mHelper_Mesh_Quantity = mScene->mNumMeshes;
mHelper_Mesh = new SHelper_Mesh*[mScene->mNumMeshes];
// Walk through the meshes and extract needed data and, also calculate BBox.
for(size_t idx_mesh = 0; idx_mesh < mScene->mNumMeshes; idx_mesh++)
{
aiMesh& mesh_cur = *mScene->mMeshes[idx_mesh];
//
// Calculate BBox
//
SBBox mesh_bbox;
BBox_GetFromVertices(mesh_cur.mVertices, mesh_cur.mNumVertices, mesh_bbox);
//
// Create vertices indices arrays splitted by primitive type.
//
size_t indcnt_p = 0;// points quantity
size_t indcnt_l = 0;// lines quantity
size_t indcnt_t = 0;// triangles quantity
if(mesh_cur.HasFaces())
{
// Usual way: all faces are triangles
if(mesh_cur.mPrimitiveTypes == aiPrimitiveType_TRIANGLE)
{
indcnt_t = mesh_cur.mNumFaces;
}
else
{
// Calculate count of primitives by types.
for(size_t idx_face = 0; idx_face < mesh_cur.mNumFaces; idx_face++)
{
if(mesh_cur.mFaces[idx_face].mNumIndices == 3)
indcnt_t++;
else if(mesh_cur.mFaces[idx_face].mNumIndices == 2)
indcnt_l++;
else if(mesh_cur.mFaces[idx_face].mNumIndices == 1)
indcnt_p++;
}
}// if(mesh_cur.mPrimitiveTypes == aiPrimitiveType_TRIANGLE) else
// Create helper
mHelper_Mesh[idx_mesh] = new SHelper_Mesh(indcnt_p, indcnt_l, indcnt_t, mesh_bbox);
// Fill indices arrays
indcnt_p = 0, indcnt_l = 0, indcnt_t = 0;// Reuse variables as indices
for(size_t idx_face = 0; idx_face < mesh_cur.mNumFaces; idx_face++)
{
if(mesh_cur.mFaces[idx_face].mNumIndices == 3)
{
mHelper_Mesh[idx_mesh]->Index_Triangle[indcnt_t++] = mesh_cur.mFaces[idx_face].mIndices[0];
mHelper_Mesh[idx_mesh]->Index_Triangle[indcnt_t++] = mesh_cur.mFaces[idx_face].mIndices[1];
mHelper_Mesh[idx_mesh]->Index_Triangle[indcnt_t++] = mesh_cur.mFaces[idx_face].mIndices[2];
}
else if(mesh_cur.mFaces[idx_face].mNumIndices == 2)
{
mHelper_Mesh[idx_mesh]->Index_Line[indcnt_l++] = mesh_cur.mFaces[idx_face].mIndices[0];
mHelper_Mesh[idx_mesh]->Index_Line[indcnt_l++] = mesh_cur.mFaces[idx_face].mIndices[1];
}
else if(mesh_cur.mFaces[idx_face].mNumIndices == 1)
{
mHelper_Mesh[idx_mesh]->Index_Point[indcnt_p++] = mesh_cur.mFaces[idx_face].mIndices[0];
}
}// for(size_t idx_face = 0; idx_face < mesh_cur.mNumFaces; idx_face++)
}// if(mesh_cur.HasFaces())
else
{
// If mesh has no faces then vertices can be just points set.
indcnt_p = mesh_cur.mNumVertices;
// Create helper
mHelper_Mesh[idx_mesh] = new SHelper_Mesh(indcnt_p, 0, 0, mesh_bbox);
// Fill indices arrays
for(size_t idx = 0; idx < indcnt_p; idx++) mHelper_Mesh[idx_mesh]->Index_Point[idx] = idx;
}// if(mesh_cur.HasFaces()) else
}// for(size_t idx_mesh = 0; idx_mesh < mScene->mNumMeshes; idx_mesh++)
}// if(mScene->HasMeshes())
//
// Scene BBox
//
// For calculating right BBox we must walk through all nodes and apply transformation to meshes BBoxes
if(mHelper_Mesh_Quantity > 0)
{
bool first_assign = true;
aiMatrix4x4 mat_root;
BBox_GetForNode(*mScene->mRootNode, mat_root, mScene_BBox, first_assign);
mScene_Center = mScene_BBox.Maximum + mScene_BBox.Minimum;
mScene_Center /= 2;
}
else
{
mScene_BBox = {{0, 0, 0}, {0, 0, 0}};
mScene_Center = {0, 0, 0};
}// if(mHelper_Mesh_Count > 0) else
//
// Textures
//
ImportTextures(pScenePath);
//
// Light sources
//
Lighting_Enable();
// If scene has no lights then enable default
if(!mScene->HasLights())
{
const GLfloat col_amb[4] = { 0.2, 0.2, 0.2, 1.0 };
SLightParameters lp;
lp.Type = aiLightSource_POINT;
lp.Ambient.r = col_amb[0], lp.Ambient.g = col_amb[1], lp.Ambient.b = col_amb[2], lp.Ambient.a = col_amb[3];
lp.Diffuse = { 1.0, 1.0, 1.0, 1.0 };
lp.Specular = lp.Diffuse;
lp.For.Point.Position = mScene_Center;
lp.For.Point.Attenuation_Constant = 1;
lp.For.Point.Attenuation_Linear = 0;
lp.For.Point.Attenuation_Quadratic = 0;
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col_amb);
Lighting_EditSource(0, lp);
emit SceneObject_LightSource("_default");// Light source will be enabled in signal handler.
}
else
{
for(size_t idx_light = 0; idx_light < mScene->mNumLights; idx_light++)
{
SLightParameters lp;
QString name;
const aiLight& light_cur = *mScene->mLights[idx_light];
auto col3_to_col4 = [](const aiColor3D& pCol3) -> aiColor4D { return aiColor4D(pCol3.r, pCol3.g, pCol3.b, 1.0); };
///TODO: find light source node and apply all transformations
// General properties
name = light_cur.mName.C_Str();
lp.Ambient = col3_to_col4(light_cur.mColorAmbient);
lp.Diffuse = col3_to_col4(light_cur.mColorDiffuse);
lp.Specular = col3_to_col4(light_cur.mColorSpecular);
lp.Type = light_cur.mType;
// Depend on type properties
switch(light_cur.mType)
{
case aiLightSource_DIRECTIONAL:
lp.For.Directional.Direction = light_cur.mDirection;
break;
case aiLightSource_POINT:
lp.For.Point.Position = light_cur.mPosition;
lp.For.Point.Attenuation_Constant = light_cur.mAttenuationConstant;
lp.For.Point.Attenuation_Linear = light_cur.mAttenuationLinear;
lp.For.Point.Attenuation_Quadratic = light_cur.mAttenuationQuadratic;
break;
case aiLightSource_SPOT:
lp.For.Spot.Position = light_cur.mPosition;
lp.For.Spot.Direction = light_cur.mDirection;
lp.For.Spot.Attenuation_Constant = light_cur.mAttenuationConstant;
lp.For.Spot.Attenuation_Linear = light_cur.mAttenuationLinear;
lp.For.Spot.Attenuation_Quadratic = light_cur.mAttenuationQuadratic;
lp.For.Spot.CutOff = light_cur.mAngleOuterCone;
break;
case aiLightSource_AMBIENT:
lp.For.Point.Position = light_cur.mPosition, lp.For.Point.Attenuation_Constant = 1, lp.For.Point.Attenuation_Linear = 0, lp.For.Point.Attenuation_Quadratic = 0;
name.append("_unsup_ambient");
break;
case aiLightSource_AREA:
lp.For.Point.Position = light_cur.mPosition, lp.For.Point.Attenuation_Constant = 1, lp.For.Point.Attenuation_Linear = 0, lp.For.Point.Attenuation_Quadratic = 0;
name.append("_unsup_area");
break;
case aiLightSource_UNDEFINED:
lp.For.Point.Position = light_cur.mPosition, lp.For.Point.Attenuation_Constant = 1, lp.For.Point.Attenuation_Linear = 0, lp.For.Point.Attenuation_Quadratic = 0;
name.append("_unsup_undefined");
break;
default:
lp.For.Point.Position = light_cur.mPosition, lp.For.Point.Attenuation_Constant = 1, lp.For.Point.Attenuation_Linear = 0, lp.For.Point.Attenuation_Quadratic = 0;
name.append("_unsupported_invalid");
break;
}// switch(light_cur.mType)
// Add light source
// Use index if name is empty.
if (name.isEmpty()) {
name += QString( "%1" ).arg( idx_light );
}
Lighting_EditSource(idx_light, lp);
emit SceneObject_LightSource(name);// Light source will be enabled in signal handler.
}// for(size_t idx_light = 0; idx_light < mScene->mNumLights; idx_light++)
}// if(!mScene->HasLights()) else
//
// Cameras
//
if(!mScene->HasCameras())
{
mCamera_DefaultAdded = true;
mHelper_CameraDefault.SetDefault();
// Calculate distance from camera to scene. Distance must be enoguh for that viewport contain whole scene.
const GLfloat tg_angle = tan(mCamera_FOVY / 2);
GLfloat val_x = ((mScene_BBox.Maximum.x - mScene_BBox.Minimum.x) / 2) / (mCamera_Viewport_AspectRatio * tg_angle);
GLfloat val_y = ((mScene_BBox.Maximum.y - mScene_BBox.Minimum.y) / 2) / tg_angle;
GLfloat val_step = val_x;
AssignIfGreater(val_step, val_y);
mHelper_CameraDefault.Translation_ToScene.Set(mScene_Center.x, mScene_Center.y, val_step + mScene_BBox.Maximum.z);
emit SceneObject_Camera("_default");
}
else
{
mCamera_DefaultAdded = false;
for(size_t idx_cam = 0; idx_cam < mScene->mNumCameras; idx_cam++)
{
emit SceneObject_Camera(mScene->mCameras[idx_cam]->mName.C_Str());
}
}// if(!mScene->HasCameras()) else
}
void CGLView::Lighting_Enable() {
mLightingEnabled = true;
glEnable(GL_LIGHTING);
}
void CGLView::Lighting_Disable() {
glDisable( GL_LIGHTING );
mLightingEnabled = false;
}
void CGLView::Lighting_EditSource(const size_t pLightNumber, const SLightParameters& pLightParameters)
{
const size_t light_num = GL_LIGHT0 + pLightNumber;
GLfloat farr[4];
if(pLightNumber >= GL_MAX_LIGHTS) return;///TODO: return value;
// Ambient color
farr[0] = pLightParameters.Ambient.r;
farr[1] = pLightParameters.Ambient.g;
farr[2] = pLightParameters.Ambient.b;
farr[3] = pLightParameters.Ambient.a;
glLightfv(light_num, GL_AMBIENT, farr);
// Diffuse color
farr[0] = pLightParameters.Diffuse.r;
farr[1] = pLightParameters.Diffuse.g;
farr[2] = pLightParameters.Diffuse.b;
farr[3] = pLightParameters.Diffuse.a;
glLightfv(light_num, GL_DIFFUSE, farr);
// Specular color
farr[0] = pLightParameters.Specular.r;
farr[1] = pLightParameters.Specular.g;
farr[2] = pLightParameters.Specular.b;
farr[3] = pLightParameters.Specular.a;
glLightfv(light_num, GL_SPECULAR, farr);
// Other parameters
switch(pLightParameters.Type)
{
case aiLightSource_DIRECTIONAL:
// Direction
farr[0] = pLightParameters.For.Directional.Direction.x, farr[1] = pLightParameters.For.Directional.Direction.y;
farr[2] = pLightParameters.For.Directional.Direction.z; farr[3] = 0;
glLightfv(light_num, GL_POSITION, farr);
break;
case aiLightSource_POINT:
// Position
farr[0] = pLightParameters.For.Point.Position.x, farr[1] = pLightParameters.For.Point.Position.y;
farr[2] = pLightParameters.For.Point.Position.z; farr[3] = 1;
glLightfv(light_num, GL_POSITION, farr);
// Attenuation
glLightf(light_num, GL_CONSTANT_ATTENUATION, pLightParameters.For.Point.Attenuation_Constant);
glLightf(light_num, GL_LINEAR_ATTENUATION, pLightParameters.For.Point.Attenuation_Linear);
glLightf(light_num, GL_QUADRATIC_ATTENUATION, pLightParameters.For.Point.Attenuation_Quadratic);
glLightf(light_num, GL_SPOT_CUTOFF, 180.0);
break;
case aiLightSource_SPOT:
// Position
farr[0] = pLightParameters.For.Spot.Position.x, farr[1] = pLightParameters.For.Spot.Position.y, farr[2] = pLightParameters.For.Spot.Position.z; farr[3] = 1;
glLightfv(light_num, GL_POSITION, farr);
// Attenuation
glLightf(light_num, GL_CONSTANT_ATTENUATION, pLightParameters.For.Spot.Attenuation_Constant);
glLightf(light_num, GL_LINEAR_ATTENUATION, pLightParameters.For.Spot.Attenuation_Linear);
glLightf(light_num, GL_QUADRATIC_ATTENUATION, pLightParameters.For.Spot.Attenuation_Quadratic);
// Spot specific
farr[0] = pLightParameters.For.Spot.Direction.x, farr[1] = pLightParameters.For.Spot.Direction.y, farr[2] = pLightParameters.For.Spot.Direction.z; farr[3] = 0;
glLightfv(light_num, GL_SPOT_DIRECTION, farr);
glLightf(light_num, GL_SPOT_CUTOFF, pLightParameters.For.Spot.CutOff);
break;
default:// For unknown light source types use point source.
// Position
farr[0] = pLightParameters.For.Point.Position.x, farr[1] = pLightParameters.For.Point.Position.y;
farr[2] = pLightParameters.For.Point.Position.z; farr[3] = 1;
glLightfv(light_num, GL_POSITION, farr);
// Attenuation
glLightf(light_num, GL_CONSTANT_ATTENUATION, 1);
glLightf(light_num, GL_LINEAR_ATTENUATION, 0);
glLightf(light_num, GL_QUADRATIC_ATTENUATION, 0);
glLightf(light_num, GL_SPOT_CUTOFF, 180.0);
break;
}// switch(pLightParameters.Type)
}
void CGLView::Lighting_EnableSource(const size_t pLightNumber) {
if(pLightNumber >= GL_MAX_LIGHTS) return;///TODO: return value;
glEnable(GL_LIGHT0 + pLightNumber);
}
void CGLView::Lighting_DisableSource(const size_t pLightNumber)
{
if(pLightNumber >= GL_MAX_LIGHTS) return;///TODO: return value;
glDisable(GL_LIGHT0 + pLightNumber);
}
void CGLView::Camera_Set(const size_t pCameraNumber)
{
SHelper_Camera& hcam = mHelper_Camera;// reference with short name for conveniance.
aiVector3D up;
if(mCamera_DefaultAdded || (pCameraNumber >= mScene->mNumCameras))// If default camera used then 'pCameraNumber' doesn't matter.
{
// Transformation parameters
hcam = mHelper_CameraDefault;
up.Set(0, 1, 0);
}
else
{
const aiCamera& camera_cur = *mScene->mCameras[pCameraNumber];
const aiNode* camera_node;
aiMatrix4x4 camera_mat;
aiQuaternion camera_quat_rot;
aiVector3D camera_tr;
up = camera_cur.mUp;
//
// Try to get real coordinates of the camera.
//
// Find node
camera_node = mScene->mRootNode->FindNode(camera_cur.mName);
if(camera_node != nullptr) Matrix_NodeToRoot(camera_node, camera_mat);
hcam.Position = camera_cur.mLookAt;
hcam.Target = camera_cur.mPosition;
hcam.Rotation_AroundCamera = aiMatrix4x4(camera_quat_rot.GetMatrix());
hcam.Rotation_AroundCamera.Transpose();
// get components of transformation matrix.
camera_mat.DecomposeNoScaling(camera_quat_rot, camera_tr);
hcam.Rotation_Scene = aiMatrix4x4();
hcam.Translation_ToScene = camera_tr;
}
// Load identity matrix - travel to world begin.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Set camera and update picture
gluLookAt(hcam.Position.x, hcam.Position.y, hcam.Position.z, hcam.Target.x, hcam.Target.y, hcam.Target.z, up.x, up.y, up.z);
}
void CGLView::Camera_RotateScene(const GLfloat pAngle_X, const GLfloat pAngle_Y, const GLfloat pAngle_Z, const aiMatrix4x4* pMatrix_Rotation_Initial) {
auto deg2rad = [](const GLfloat pDegree) -> GLfloat {
return pDegree * AI_MATH_PI / 180.0f;
};
aiMatrix4x4 mat_rot;
mat_rot.FromEulerAnglesXYZ(deg2rad(pAngle_X), deg2rad(pAngle_Y), deg2rad(pAngle_Z));
if(pMatrix_Rotation_Initial != nullptr)
mHelper_Camera.Rotation_Scene = *pMatrix_Rotation_Initial * mat_rot;
else
mHelper_Camera.Rotation_Scene *= mat_rot;
}
void CGLView::Camera_Rotate(const GLfloat pAngle_X, const GLfloat pAngle_Y, const GLfloat pAngle_Z, const aiMatrix4x4* pMatrix_Rotation_Initial)
{
auto deg2rad = [](const GLfloat pDegree) -> GLfloat { return pDegree * AI_MATH_PI / 180.0; };
aiMatrix4x4 mat_rot;
mat_rot.FromEulerAnglesXYZ(deg2rad(pAngle_X), deg2rad(pAngle_Y), deg2rad(pAngle_Z));
if(pMatrix_Rotation_Initial != nullptr)
mHelper_Camera.Rotation_AroundCamera = *pMatrix_Rotation_Initial * mat_rot;
else
mHelper_Camera.Rotation_AroundCamera *= mat_rot;
}
void CGLView::Camera_Translate(const GLfloat pTranslate_X, const GLfloat pTranslate_Y, const GLfloat pTranslate_Z)
{
aiVector3D vect_tr(pTranslate_X, pTranslate_Y, pTranslate_Z);
vect_tr *= mHelper_Camera.Rotation_AroundCamera;
mHelper_Camera.Translation_ToScene += vect_tr;
}
void CGLView::Camera_Matrix(aiMatrix4x4& pRotation_Camera, aiMatrix4x4& pRotation_Scene, aiVector3D& pTranslation_Camera)
{
pRotation_Camera = mHelper_Camera.Rotation_AroundCamera;
pRotation_Scene = mHelper_Camera.Rotation_Scene;
pTranslation_Camera = mHelper_Camera.Translation_ToScene;
}