//----------------------------------------------------------------------------- //! SLGLShader::createAndCompile creates & compiles the OpenGL shader object SLbool SLGLShader::createAndCompile() { // delete if object already exits if (_objectGL) glDeleteShader(_objectGL); if (_code!="") { switch (_type) { case VertexShader: _objectGL = glCreateShader(GL_VERTEX_SHADER); break; case FragmentShader: _objectGL = glCreateShader(GL_FRAGMENT_SHADER); break; default: SL_EXIT_MSG("SLGLShader::load: Unknown shader type."); } //SLstring verGLSL = SLGLState::getInstance()->glSLVersionNO(); //SLstring srcVersion = "#version " + verGLSL + "\n"; //if (verGLSL > "120") //{ if (_type == VertexShader) // { SLUtils::replaceString(_code, "attribute", "in"); // SLUtils::replaceString(_code, "varying", "out"); // } // if (_type == FragmentShader) // { SLUtils::replaceString(_code, "varying", "in"); // } //} //SLstring scrComplete = srcVersion + _code; SLstring scrComplete = _code; const char* src = scrComplete.c_str(); glShaderSource(_objectGL, 1, &src, 0); glCompileShader(_objectGL); // Check compiler log SLint compileSuccess = 0; glGetShaderiv(_objectGL, GL_COMPILE_STATUS, &compileSuccess); if (compileSuccess == GL_FALSE) { GLchar log[256]; glGetShaderInfoLog(_objectGL, sizeof(log), 0, &log[0]); SL_LOG("*** COMPILER ERROR ***\n"); SL_LOG("Source file: %s\n", _file.c_str()); SL_LOG("%s\n\n", log); return false; } return true; } else SL_WARN_MSG("SLGLShader::createAndCompile: Nothing to compile!"); return false; }
/*! The destructor does the final total deallocation of all global resources. */ SLScene::~SLScene() { unInit(); // delete global SLGLState instance SLGLState::deleteInstance(); // clear light pointers _lights.clear(); // delete materials for (SLuint i=0; i<_materials.size(); ++i) delete _materials[i]; _materials.clear(); // delete textures for (SLuint i=0; i<_textures.size(); ++i) delete _textures[i]; _textures.clear(); // delete shader programs for (SLuint i=0; i<_shaderProgs.size(); ++i) delete _shaderProgs[i]; _shaderProgs.clear(); // delete fonts SLTexFont::deleteFonts(); // delete menus & statistic texts delete _menuGL; _menuGL = 0; delete _menuRT; _menuRT = 0; delete _menuPT; _menuPT = 0; delete _info; _info = 0; delete _infoGL; _infoGL = 0; delete _infoRT; _infoRT = 0; delete _btnAbout; _btnAbout = 0; delete _btnHelp; _btnHelp = 0; delete _btnCredits; _btnCredits = 0; current = 0; SL_LOG("~SLScene\n"); SL_LOG("------------------------------------------------------------------\n"); }
//----------------------------------------------------------------------------- //! SLGLShader::load loads a shader file into string _shaderSource void SLGLShader::load(SLstring filename) { fstream shaderFile(filename.c_str(), ios::in); if (!shaderFile.is_open()) { SL_LOG("File open failed: %s\n", filename.c_str()); exit(1); } std::stringstream buffer; buffer << shaderFile.rdbuf(); // remove comments because some stupid ARM compiler can't handle GLSL comments #ifdef SL_OS_MACIOS _code = buffer.str(); #else _code = SLUtils::removeComments(buffer.str()); #endif }
/*! SLRay::prints prints the rays origin (O), direction (D) and the length to the intersection (L) */ void SLRay::print() { SL_LOG("Ray: O(%.2f, %.2f, %.2f), D(%.2f, %.2f, %.2f), L: %.2f\n", origin.x,origin.y,origin.z, dir.x,dir.y,dir.z, length); }
/*! SLAssimpImporter::loadMesh creates a new SLMesh an copies the meshs vertex data and triangle face indices. Normals & tangents are not loaded. They are calculated in SLMesh. */ SLMesh* SLAssimpImporter::loadMesh(aiMesh *mesh) { // Count first the NO. of triangles in the mesh SLuint numTriangles = 0; for(unsigned int i = 0; i < mesh->mNumFaces; ++i) if(mesh->mFaces[i].mNumIndices == 3) numTriangles++; // We only load meshes that contain triangles if (numTriangles==0 || mesh->mNumVertices==0) return nullptr; // create a new mesh. // The mesh pointer is added automatically to the SLScene::meshes vector. SLstring name = mesh->mName.data; SLMesh *m = new SLMesh(name.empty() ? "Imported Mesh" : name); // create position & normal vector m->P.clear(); m->P.resize(mesh->mNumVertices); // create normal vector if (mesh->HasNormals()) { m->N.clear(); m->N.resize(m->P.size()); } // allocate texCoord vector if needed if (mesh->HasTextureCoords(0)) { m->Tc.clear(); m->Tc.resize(m->P.size()); } // copy vertex positions & texCoord for(SLuint i = 0; i < m->P.size(); ++i) { m->P[i].set(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z); if (m->N.size()) m->N[i].set(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z); if (m->Tc.size()) m->Tc[i].set(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y); } // create face index vector if (m->P.size() < 65536) { m->I16.clear(); m->I16.resize(mesh->mNumFaces * 3); // load face triangle indices only SLuint j = 0; for(SLuint i = 0; i < mesh->mNumFaces; ++i) { if(mesh->mFaces[i].mNumIndices == 3) { m->I16[j++] = mesh->mFaces[i].mIndices[0]; m->I16[j++] = mesh->mFaces[i].mIndices[1]; m->I16[j++] = mesh->mFaces[i].mIndices[2]; } } } else { m->I32.clear(); m->I32.resize(mesh->mNumFaces * 3); // load face triangle indices only SLuint j = 0; for(SLuint i = 0; i < mesh->mNumFaces; ++i) { if(mesh->mFaces[i].mNumIndices == 3) { m->I32[j++] = mesh->mFaces[i].mIndices[0]; m->I32[j++] = mesh->mFaces[i].mIndices[1]; m->I32[j++] = mesh->mFaces[i].mIndices[2]; } } } if (!m->N.size()) m->calcNormals(); // load joints if (mesh->HasBones()) { _skinnedMeshes.push_back(m); m->skeleton(_skeleton); m->Ji.resize(m->P.size()); m->Jw.resize(m->P.size()); // make sure to initialize the weights with 0 vectors std::fill(m->Ji.begin(), m->Ji.end(), SLVec4f(0, 0, 0, 0)); std::fill(m->Jw.begin(), m->Jw.end(), SLVec4f(0, 0, 0, 0)); for (SLuint i = 0; i < mesh->mNumBones; i++) { aiBone* joint = mesh->mBones[i]; SLJoint* slJoint = _skeleton->getJoint(joint->mName.C_Str()); // @todo On OSX it happens from time to time that slJoint is nullptr if (slJoint) { SLuint jointId = slJoint->id(); for (SLuint j = 0; j < joint->mNumWeights; j++) { // add the weight SLuint vertId = joint->mWeights[j].mVertexId; SLfloat weight = joint->mWeights[j].mWeight; m->addWeight(vertId, jointId, weight); // check if the bones max radius changed // @todo this is very specific to this loaded mesh, // when we add a skeleton instances class this radius // calculation has to be done on the instance! slJoint->calcMaxRadius(SLVec3f(mesh->mVertices[vertId].x, mesh->mVertices[vertId].y, mesh->mVertices[vertId].z)); } } else { SL_LOG("Failed to load joint of skeleton in SLAssimpImporter::loadMesh: %s\n", joint->mName.C_Str()); return nullptr; } } } return m; }
/*! Loads the scene from a file and creates materials with textures, the meshes and the nodes for the scene graph. Materials, textures and meshes are added to the according vectors of SLScene for later deallocation. */ SLNode* SLAssimpImporter::load(SLstring file, //!< File with path or on default path SLbool loadMeshesOnly, //!< Only load nodes with meshes SLuint flags) //!< Import flags (see assimp/postprocess.h) { // clear the intermediate data clear(); // Check existance if (!SLFileSystem::fileExists(file)) { file = defaultPath + file; if (!SLFileSystem::fileExists(file)) { SLstring msg = "SLAssimpImporter: File not found: " + file + "\n"; SL_WARN_MSG(msg.c_str()); return nullptr; } } // Import file with assimp importer Assimp::Importer ai; const aiScene* scene = ai.ReadFile(file.c_str(), (SLuint)flags); if (!scene) { SLstring msg = "Failed to load file: " + file + "\n" + ai.GetErrorString() + "\n"; SL_WARN_MSG(msg.c_str()); return nullptr; } // initial scan of the scene performInitialScan(scene); // load skeleton loadSkeleton(nullptr, _skeletonRoot); // load materials SLstring modelPath = SLUtils::getPath(file); SLVMaterial materials; for(SLint i = 0; i < (SLint)scene->mNumMaterials; i++) materials.push_back(loadMaterial(i, scene->mMaterials[i], modelPath)); // load meshes & set their material std::map<int, SLMesh*> meshMap; // map from the ai index to our mesh for(SLint i = 0; i < (SLint)scene->mNumMeshes; i++) { SLMesh* mesh = loadMesh(scene->mMeshes[i]); if (mesh != 0) { mesh->mat = materials[scene->mMeshes[i]->mMaterialIndex]; _meshes.push_back(mesh); meshMap[i] = mesh; } else SL_LOG("SLAsssimpImporter::load failed: %s\nin path: %s\n", file.c_str(), modelPath.c_str()); } // load the scene nodes recursively _sceneRoot = loadNodesRec(nullptr, scene->mRootNode, meshMap, loadMeshesOnly); // load animations vector<SLAnimation*> animations; for (SLint i = 0; i < (SLint)scene->mNumAnimations; i++) animations.push_back(loadAnimation(scene->mAnimations[i])); logMessage(LV_minimal, "\n---------------------------\n\n"); return _sceneRoot; }
/*! SLMesh::calcCenterRad calculates the center and the radius of an almost minimal bounding sphere. Code by Jack Ritter from Graphic Gems. */ void SLMesh::calcCenterRad(SLVec3f& center, SLfloat& radius) { SLint i; SLfloat dx, dy, dz; SLfloat radius2, xspan, yspan, zspan, maxspan; SLfloat old_to_p, old_to_p_sq, old_to_new; SLVec3f xmin, xmax, ymin, ymax, zmin, zmax, dia1, dia2; // FIRST PASS: find 6 minima/maxima points xmin.x = ymin.y = zmin.z= FLT_MAX; xmax.x = ymax.y = zmax.z= -FLT_MAX; for (i=0; i<numV; ++i) { if (P[i].x < xmin.x) xmin = P[i]; else if (P[i].x > xmax.x) xmax = P[i]; if (P[i].y < ymin.y) ymin = P[i]; else if (P[i].y > ymax.y) ymax = P[i]; if (P[i].z < zmin.z) zmin = P[i]; else if (P[i].z > zmax.z) zmax = P[i]; } // Set xspan = distance between the 2 points xmin & xmax (squared) dx = xmax.x - xmin.x; dy = xmax.y - xmin.y; dz = xmax.z - xmin.z; xspan = dx*dx + dy*dy + dz*dz; // Same for y & z spans dx = ymax.x - ymin.x; dy = ymax.y - ymin.y; dz = ymax.z - ymin.z; yspan = dx*dx + dy*dy + dz*dz; dx = zmax.x - zmin.x; dy = zmax.y - zmin.y; dz = zmax.z - zmin.z; zspan = dx*dx + dy*dy + dz*dz; // Set points dia1 & dia2 to the maximally separated pair dia1 = xmin; dia2 = xmax; // assume xspan biggest maxspan = xspan; if (yspan > maxspan) { maxspan = yspan; dia1 = ymin; dia2 = ymax; } if (zspan > maxspan) { dia1 = zmin; dia2 = zmax; } // dia1,dia2 is a diameter of initial sphere // calc initial center center.x = (dia1.x + dia2.x)*0.5f; center.y = (dia1.y + dia2.y)*0.5f; center.z = (dia1.z + dia2.z)*0.5f; // calculate initial radius*radius and radius dx = dia2.x - center.x; // x component of radius vector dy = dia2.y - center.y; // y component of radius vector dz = dia2.z - center.z; // z component of radius vector radius2 = dx*dx + dy*dy + dz*dz; radius = sqrt(radius2); // SECOND PASS: increment current sphere for (i=0; i<numV; ++i) { dx = P[i].x - center.x; dy = P[i].y - center.y; dz = P[i].z - center.z; old_to_p_sq = dx*dx + dy*dy + dz*dz; if (old_to_p_sq > radius2) // do r**2 test first { // this point is outside of current sphere old_to_p = sqrt(old_to_p_sq); // calc radius of new sphere radius = (radius + old_to_p) * 0.5f; radius2 = radius*radius; // for next r**2 compare old_to_new = old_to_p - radius; // calc center of new sphere center.x = (radius*center.x + old_to_new*P[i].x) / old_to_p; center.y = (radius*center.y + old_to_new*P[i].y) / old_to_p; center.z = (radius*center.z + old_to_new*P[i].z) / old_to_p; // Suppress if desired SL_LOG("\n New sphere: center,radius = %f %f %f %f", center.x,center.y,center.z, radius); } } }
/*! SLGroup::printStats() prints the statistic info of this group. */ void SLGroup::printStats() { SLfloat voxelsEmpty = numVoxels ? (SLfloat)numVoxEmpty / (SLfloat)numVoxels*100.0f : 0; SLfloat avgTriPerVox = numVoxels ? (SLfloat)numRTTriangles / (SLfloat)(numVoxels-numVoxEmpty) : 0; SL_LOG("RTVertices : %d\n", numRTVertices); SL_LOG("RTTriangles : %d\n", numRTTriangles); SL_LOG("Voxels : %d\n", numVoxels); SL_LOG("Voxels empty : %4.1f%%\n", voxelsEmpty); SL_LOG("Avg. Tria/Voxel : %4.1f\n", avgTriPerVox); SL_LOG("Max. Tria/Voxel : %d\n", numVoxMaxTria); SL_LOG("MBytes in Meshes : %f\n", (SLfloat)numBytes / 1000000.0f); SL_LOG("Groups : %d\n", numGroups); SL_LOG("Shapes : %d\n", numShapes); SL_LOG("RefShapes : %d\n", numRefShapes); SL_LOG("Lights : %d\n", numLights); SL_LOG("\n"); }
/*! SLGLShaderProg::init creates the OpenGL shaderprogram object, compiles all shader objects and attaches them to the shaderprogram. At the end all shaders are linked. If a shader fails to compile a simple texture only shader is compiled that shows an error message in the texture. */ void SLGLShaderProg::init() { // create program object if it doesn't exist if(!_programObjectGL) _programObjectGL = glCreateProgram(); // if already linked, detach, recreate and compile shaders if (_isLinked) { for (SLuint i=0; i<_shaderList.size(); i++) { if (_isLinked) { glDetachShader(_programObjectGL, _shaderList[i]->_shaderObjectGL); GET_GL_ERROR; } } _isLinked = false; } // compile all shader objects SLbool allSuccuessfullyCompiled = true; for (SLuint i=0; i<_shaderList.size(); i++) { if (!_shaderList[i]->createAndCompile()) { allSuccuessfullyCompiled = false; break; } GET_GL_ERROR; } // try to compile alternative per vertex lighting shaders if (!allSuccuessfullyCompiled) { // delete all shaders and uniforms that where attached for (SLuint i=0; i<_shaderList.size(); i++) delete _shaderList[i]; for (SLuint i=0; i<_uniform1fList.size(); ++i) delete _uniform1fList[i]; for (SLuint i=0; i<_uniform1iList.size(); ++i) delete _uniform1iList[i]; _shaderList.clear(); _uniform1fList.clear(); _uniform1iList.clear(); addShader(new SLGLShader(defaultPath+"ErrorTex.vert", SLVertexShader)); addShader(new SLGLShader(defaultPath+"ErrorTex.frag", SLFragmentShader)); allSuccuessfullyCompiled = true; for (SLuint i=0; i<_shaderList.size(); i++) { if (!_shaderList[i]->createAndCompile()) { allSuccuessfullyCompiled = false; break; } GET_GL_ERROR; } } // attach all shader objects if (allSuccuessfullyCompiled) { for (SLuint i=0; i<_shaderList.size(); i++) { glAttachShader(_programObjectGL, _shaderList[i]->_shaderObjectGL); GET_GL_ERROR; } } else SL_EXIT_MSG("No successufully compiled shaders attached!"); int linked; glLinkProgram(_programObjectGL); GET_GL_ERROR; glGetProgramiv(_programObjectGL, GL_LINK_STATUS, &linked); GET_GL_ERROR; if (linked) { _isLinked = true; for (SLuint i=0; i<_shaderList.size(); i++) _name += "+"+_shaderList[i]->name(); //SL_LOG("Linked: %s", _name.c_str()); } else { SLchar log[256]; glGetProgramInfoLog(_programObjectGL, sizeof(log), 0, &log[0]); SL_LOG("*** LINKER ERROR ***\n"); SL_LOG("Source files: \n"); for (SLuint i=0; i<_shaderList.size(); i++) SL_LOG("%s\n", _shaderList[i]->name().c_str()); SL_LOG("%s\n", log); SL_EXIT_MSG("GLSL linker error"); } }