コード例 #1
0
void vMarchCube(
    const BoundedVolume<T,roo::TargetHost> vol,
    const BoundedVolume<TColor,roo::TargetHost> volColor,
    int x, int y, int z,
    std::vector<aiVector3D>& verts,
    std::vector<aiVector3D>& norms,
    std::vector<aiFace>& faces,
    std::vector<aiColor4D>& colors,
    float fTargetValue = 0.0f
) {
    const float3 p = vol.VoxelPositionInUnits(x,y,z);
    const float3 fScale = vol.VoxelSizeUnits();

    //Make a local copy of the values at the cube's corners
    float afCubeValue[8];
    for(int iVertex = 0; iVertex < 8; iVertex++) {
        afCubeValue[iVertex] = vol.Get(x+a2fVertexOffset[iVertex][0],y+a2fVertexOffset[iVertex][1],z+a2fVertexOffset[iVertex][2]);
        if(!std::isfinite(afCubeValue[iVertex])) return;
    }

    //Find which vertices are inside of the surface and which are outside
    int iFlagIndex = 0;
    for(int iVertexTest = 0; iVertexTest < 8; iVertexTest++) {
        if(afCubeValue[iVertexTest] <= fTargetValue)
            iFlagIndex |= 1<<iVertexTest;
    }

    //Find which edges are intersected by the surface
    int iEdgeFlags = aiCubeEdgeFlags[iFlagIndex];

    //If the cube is entirely inside or outside of the surface, then there will be no intersections
    if(iEdgeFlags == 0) {
        return;
    }

    //Find the point of intersection of the surface with each edge
    //Then find the normal to the surface at those points
    float3 asEdgeVertex[12];
    float3 asEdgeNorm[12];

    for(int iEdge = 0; iEdge < 12; iEdge++)
    {
        //if there is an intersection on this edge
        if(iEdgeFlags & (1<<iEdge))
        {
            float fOffset = fGetOffset(afCubeValue[ a2iEdgeConnection[iEdge][0] ],
                                 afCubeValue[ a2iEdgeConnection[iEdge][1] ], fTargetValue);

            asEdgeVertex[iEdge] = make_float3(
                p.x + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][0]  +  fOffset * a2fEdgeDirection[iEdge][0]) * fScale.x,
                p.y + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][1]  +  fOffset * a2fEdgeDirection[iEdge][1]) * fScale.y,
                p.z + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][2]  +  fOffset * a2fEdgeDirection[iEdge][2]) * fScale.z
            );

            const float3 deriv = vol.GetUnitsBackwardDiffDxDyDz( asEdgeVertex[iEdge] );
            asEdgeNorm[iEdge] = deriv / length(deriv);

            if( !std::isfinite(asEdgeNorm[iEdge].x) || !std::isfinite(asEdgeNorm[iEdge].y) || !std::isfinite(asEdgeNorm[iEdge].z) ) {
                asEdgeNorm[iEdge] = make_float3(0,0,0);
            }
        }
    }


    //Draw the triangles that were found.  There can be up to five per cube
    for(int iTriangle = 0; iTriangle < 5; iTriangle++)
    {
        if(a2iTriangleConnectionTable[iFlagIndex][3*iTriangle] < 0)
            break;

        aiFace face;
        face.mNumIndices = 3;
        face.mIndices = new unsigned int[face.mNumIndices];

        for(int iCorner = 0; iCorner < 3; iCorner++)
        {
            int iVertex = a2iTriangleConnectionTable[iFlagIndex][3*iTriangle+iCorner];

            face.mIndices[iCorner] = verts.size();
            verts.push_back(aiVector3D(asEdgeVertex[iVertex].x, asEdgeVertex[iVertex].y, asEdgeVertex[iVertex].z) );
            norms.push_back(aiVector3D(asEdgeNorm[iVertex].x,   asEdgeNorm[iVertex].y,   asEdgeNorm[iVertex].z) );

            if(volColor.IsValid()) {
                const TColor c = volColor.GetUnitsTrilinearClamped(asEdgeVertex[iVertex]);
                float3 sColor = roo::ConvertPixel<float3,TColor>(c);
                colors.push_back(aiColor4D(sColor.x, sColor.y, sColor.z, 1.0f));
            }

        }

        faces.push_back(face);
    }
}
コード例 #2
0
// ------------------------------------------------------------------------------------------------
// read an array of color4 tuples
void ParseVectorDataArray(std::vector<aiColor4D>& out, const Element& el)
{
    out.resize( 0 );
    const TokenList& tok = el.Tokens();
    if(tok.empty()) {
        ParseError("unexpected empty element",&el);
    }

    if(tok[0]->IsBinary()) {
        const char* data = tok[0]->begin(), *end = tok[0]->end();

        char type;
        uint32_t count;
        ReadBinaryDataArrayHead(data, end, type, count, el);

        if(count % 4 != 0) {
            ParseError("number of floats is not a multiple of four (4) (binary)",&el);
        }

        if(!count) {
            return;
        }

        if (type != 'd' && type != 'f') {
            ParseError("expected float or double array (binary)",&el);
        }

        std::vector<char> buff;
        ReadBinaryDataArray(type, count, data, end, buff, el);

        ai_assert(data == end);
        ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));

        const uint32_t count4 = count / 4;
        out.reserve(count4);

        if (type == 'd') {
            const double* d = reinterpret_cast<const double*>(&buff[0]);
            for (unsigned int i = 0; i < count4; ++i, d += 4) {
                out.push_back(aiColor4D(static_cast<float>(d[0]),
                    static_cast<float>(d[1]),
                    static_cast<float>(d[2]),
                    static_cast<float>(d[3])));
            }
        }
        else if (type == 'f') {
            const float* f = reinterpret_cast<const float*>(&buff[0]);
            for (unsigned int i = 0; i < count4; ++i, f += 4) {
                out.push_back(aiColor4D(f[0],f[1],f[2],f[3]));
            }
        }
        return;
    }

    const size_t dim = ParseTokenAsDim(*tok[0]);

    //  see notes in ParseVectorDataArray() above
    out.reserve(dim);

    const Scope& scope = GetRequiredScope(el);
    const Element& a = GetRequiredElement(scope,"a",&el);

    if (a.Tokens().size() % 4 != 0) {
        ParseError("number of floats is not a multiple of four (4)",&el);
    }
    for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
        aiColor4D v;
        v.r = ParseTokenAsFloat(**it++);
        v.g = ParseTokenAsFloat(**it++);
        v.b = ParseTokenAsFloat(**it++);
        v.a = ParseTokenAsFloat(**it++);

        out.push_back(v);
    }
}
コード例 #3
0
//-------------------------------------------
void ofxAssimpModelLoader::loadGLResources(){

    ofLog(OF_LOG_VERBOSE, "loading gl resources");

    // create new mesh helpers for each mesh, will populate their data later.
    modelMeshes.reserve(scene->mNumMeshes); 
        
    // create OpenGL buffers and populate them based on each meshes pertinant info.
    for (unsigned int i = 0; i < scene->mNumMeshes; ++i){
        ofLog(OF_LOG_VERBOSE, "loading mesh %u", i);
        // current mesh we are introspecting
        aiMesh* mesh = scene->mMeshes[i];
        
        // the current meshHelper we will be populating data into.
        ofxAssimpMeshHelper meshHelper;
        
        meshHelper.mesh = mesh;
        
        // set the mesh's default values.
        aiColor4D dcolor = aiColor4D(0.8f, 0.8f, 0.8f, 1.0f);
        meshHelper.diffuseColor = dcolor;

        aiColor4D scolor = aiColor4D(0.0f, 0.0f, 0.0f, 1.0f);
        meshHelper.specularColor = scolor;

        aiColor4D acolor = aiColor4D(0.2f, 0.2f, 0.2f, 1.0f);
        meshHelper.ambientColor = acolor;

        aiColor4D ecolor = aiColor4D(0.0f, 0.0f, 0.0f, 1.0f);
        meshHelper.emissiveColor = ecolor;

        // Handle material info
        aiMaterial* mtl = scene->mMaterials[mesh->mMaterialIndex];
        
        // Load Textures
        int texIndex = 0;
        aiString texPath;
        
        //meshHelper.texture = NULL;
        
        // TODO: handle other aiTextureTypes
        if(AI_SUCCESS == mtl->GetTexture(aiTextureType_DIFFUSE, texIndex, &texPath)){
            // This is magic. Thanks Kyle.
            
            textures.push_back(ofImage());            
            
            ofImage& image = textures.back();                

            ofLog(OF_LOG_VERBOSE, "loading image from %s", texPath.data);
            
            image.loadImage(texPath.data);
            image.update();
            
            ofLog(OF_LOG_VERBOSE, "texture width: %f height %f", image.getWidth(), image.getHeight());
            
            //meshHelper.texture = &(image.getTextureReference()); 
            meshHelper.textureIndex = textures.size()-1;
        }
        
        if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &dcolor))
            meshHelper.diffuseColor = dcolor;
        
        if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &scolor))
            meshHelper.specularColor = scolor;
        
        if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &acolor))
            meshHelper.ambientColor = acolor;
        
        if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &ecolor))
            meshHelper.emissiveColor = ecolor;
        
        // Culling
        unsigned int max = 1;
        int two_sided;
        if((AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_TWOSIDED, &two_sided, &max)) && two_sided)
            meshHelper.twoSided = true;
        else
            meshHelper.twoSided = false;
        
        // Create a VBO for our vertices
        GLuint vhandle;
        glGenBuffers(1, &vhandle);
        
        glBindBuffer(GL_ARRAY_BUFFER, vhandle);
        
        if(getAnimationCount())
            glBufferData(GL_ARRAY_BUFFER, sizeof(aiVertex) * mesh->mNumVertices, NULL, GL_STREAM_DRAW/*GL_STATIC_DRAW GL_STREAM_DRAW*/);
        else
            glBufferData(GL_ARRAY_BUFFER, sizeof(aiVertex) * mesh->mNumVertices, NULL, GL_STATIC_DRAW/*GL_STATIC_DRAW GL_STREAM_DRAW*/);

        // populate vertices
        aiVertex* verts = (aiVertex*)glMapBuffer(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
        
        for (unsigned int x = 0; x < mesh->mNumVertices; ++x)
        {
            verts->vPosition = mesh->mVertices[x];
            
            if (NULL == mesh->mNormals)
                verts->vNormal = aiVector3D(0.0f,0.0f,0.0f);
            else
                verts->vNormal = mesh->mNormals[x];
            
            if (mesh->HasVertexColors(0))
            {
                verts->dColorDiffuse = mesh->mColors[0][x];
            }
            else
                verts->dColorDiffuse = aiColor4D(1.0, 1.0, 1.0, 1.0);
            
            // This varies slightly form Assimp View, we support the 3rd texture component.
            if (mesh->HasTextureCoords(0))
                verts->vTextureUV = mesh->mTextureCoords[0][x];
            else
                verts->vTextureUV = aiVector3D(0.5f,0.5f, 0.0f);
           
            // No longer in aiVertex VBO structure
            /*          
            if (NULL == mesh->mTangents)
            {
                verts->vTangent = aiVector3D(0.0f,0.0f,0.0f);
                verts->vBitangent = aiVector3D(0.0f,0.0f,0.0f);
            }
            else
            {
                verts->vTangent = mesh->mTangents[x];
                verts->vBitangent = mesh->mBitangents[x];
            }
             
            if (mesh->HasTextureCoords(1))
                verts->vTextureUV2 = mesh->mTextureCoords[1][x];
            else 
                verts->vTextureUV2 = aiVector3D(0.5f,0.5f, 0.0f);

            if( mesh->HasBones()){
                unsigned char boneIndices[4] = { 0, 0, 0, 0 };
                unsigned char boneWeights[4] = { 0, 0, 0, 0 };
                ai_assert( weightsPerVertex[x].size() <= 4);

                for( unsigned int a = 0; a < weightsPerVertex[x].size(); a++){
                    boneIndices[a] = weightsPerVertex[x][a].mVertexId;
                    boneWeights[a] = (unsigned char) (weightsPerVertex[x][a].mWeight * 255.0f);
                }

                memcpy( verts->mBoneIndices, boneIndices, sizeof( boneIndices));
                memcpy( verts->mBoneWeights, boneWeights, sizeof( boneWeights));
            }
            else{
               // memset( verts->mBoneIndices, 0, sizeof( verts->mBoneIndices));
               // memset( verts->mBoneWeights, 0, sizeof( verts->mBoneWeights));
            }
 */           
            ++verts;
        }
        
        glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); //invalidates verts
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        
        // set the mesh vertex buffer handle to our new vertex buffer.
        meshHelper.vertexBuffer = vhandle;
        
        // Create Index Buffer
        unsigned int nidx;
        switch (mesh->mPrimitiveTypes){
            case aiPrimitiveType_POINT:
                nidx = 1;break;
            case aiPrimitiveType_LINE:
                nidx = 2;break;
            case aiPrimitiveType_TRIANGLE:
                nidx = 3;break;
            default: assert(false);
        }   
        
        GLuint ihandle;
        glGenBuffers(1, &ihandle);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ihandle);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * mesh->mNumFaces * nidx, NULL, GL_STATIC_DRAW/*GL_STATIC_DRAW GL_STREAM_DRAW*/);
        
        unsigned int* indices = (unsigned int*)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY_ARB);
        
        // now fill the index buffer
        for (unsigned int x = 0; x < mesh->mNumFaces; ++x){
            for (unsigned int a = 0; a < nidx; ++a){
                *indices++ = mesh->mFaces[x].mIndices[a];
            }
        }
        
        glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        
        // set the mesh index buffer handle to our new index buffer.
        meshHelper.indexBuffer = ihandle;
        meshHelper.numIndices = mesh->mNumFaces * nidx;
        
        // create the normal buffer. Assimp View creates a second normal buffer. Unsure why. Using only the interleaved normals for now.
        // This is here for reference.
        
        /*
        GLuint nhandle;
        glGenBuffers(1, &nhandle);
        glBindBuffer(GL_ARRAY_BUFFER, nhandle);
        glBufferData(GL_ARRAY_BUFFER, sizeof(aiVector3D)* mesh->mNumVertices, NULL, GL_STATIC_DRAW);

        // populate normals
        aiVector3D* normals = (aiVector3D*)glMapBuffer(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);

        for (unsigned int x = 0; x < mesh->mNumVertices; ++x)
        {
        aiVector3D vNormal = mesh->mNormals[x];
        *normals = vNormal;
        ++normals;
        }

        glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); //invalidates verts
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        meshHelper.normalBuffer = nhandle;
        */
        
        // Create VAO and populate it
        GLuint vaoHandle; 
        glGenVertexArraysAPPLE(1, &vaoHandle);
            
        // TODO: equivalent PC call.
        glBindVertexArrayAPPLE(vaoHandle);
        
        glBindBuffer(GL_ARRAY_BUFFER, meshHelper.vertexBuffer);
        
        glEnableClientState(GL_NORMAL_ARRAY);
        glNormalPointer(GL_FLOAT, sizeof(aiVertex), BUFFER_OFFSET(12));
        
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glTexCoordPointer(3, GL_FLOAT, sizeof(aiVertex), BUFFER_OFFSET(24));
        //TODO: handle second texture

        glEnableClientState(GL_COLOR_ARRAY);
        glColorPointer(4, GL_FLOAT, sizeof(aiVertex), BUFFER_OFFSET(36));
        
        // VertexPointer ought to come last, apparently this is some optimization, since if its set once first, it gets fiddled with every time something else is update.
        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(3, GL_FLOAT, sizeof(aiVertex), 0);
        
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshHelper.indexBuffer);
        
        glBindVertexArrayAPPLE(0);
       
        // save the VAO handle into our mesh helper
        meshHelper.vao = vaoHandle;
        
        modelMeshes.push_back(meshHelper);
    }
    ofLog(OF_LOG_VERBOSE, "finished loading gl resources");
}
コード例 #4
0
// http://sourceforge.net/projects/assimp/forums/forum/817654/topic/3880745
void ofxAssimpModelLoader::updateGLResources(){
        
    // update mesh position for the animation
    for (unsigned int i = 0; i < modelMeshes.size(); ++i){

        // current mesh we are introspecting
        const aiMesh* mesh = modelMeshes[i].mesh;
        
        // calculate bone matrices
        std::vector<aiMatrix4x4> boneMatrices( mesh->mNumBones);
        for( size_t a = 0; a < mesh->mNumBones; ++a)
        {
            const aiBone* bone = mesh->mBones[a];
            
            // find the corresponding node by again looking recursively through the node hierarchy for the same name
            aiNode* node = scene->mRootNode->FindNode(bone->mName);
            
            // start with the mesh-to-bone matrix 
            boneMatrices[a] = bone->mOffsetMatrix;
            // and now append all node transformations down the parent chain until we're back at mesh coordinates again
            const aiNode* tempNode = node;
            while( tempNode)
            {
                // check your matrix multiplication order here!!!
                boneMatrices[a] = tempNode->mTransformation * boneMatrices[a];   
                // boneMatrices[a] = boneMatrices[a] * tempNode->mTransformation;
                tempNode = tempNode->mParent;
            }
        }
        
        // all using the results from the previous code snippet
        std::vector<aiVector3D> resultPos( mesh->mNumVertices); 
        std::vector<aiVector3D> resultNorm( mesh->mNumVertices);
        
        // loop through all vertex weights of all bones
        for( size_t a = 0; a < mesh->mNumBones; ++a)
        {
            const aiBone* bone = mesh->mBones[a];
            const aiMatrix4x4& posTrafo = boneMatrices[a];
            
            // 3x3 matrix, contains the bone matrix without the translation, only with rotation and possibly scaling
            aiMatrix3x3 normTrafo = aiMatrix3x3( posTrafo); 
            for( size_t b = 0; b < bone->mNumWeights; ++b)
            {
                const aiVertexWeight& weight = bone->mWeights[b];
                
                size_t vertexId = weight.mVertexId; 
                const aiVector3D& srcPos = mesh->mVertices[vertexId];
                const aiVector3D& srcNorm = mesh->mNormals[vertexId];
                
                resultPos[vertexId] += weight.mWeight * (posTrafo * srcPos);
                resultNorm[vertexId] += weight.mWeight * (normTrafo * srcNorm);
            }
        }
                
        // now upload the result position and normal along with the other vertex attributes into a dynamic vertex buffer, VBO or whatever
        
        // get mesh helper for this mesh;
        ofxAssimpMeshHelper meshHelper = modelMeshes[i];
        
        glBindBuffer(GL_ARRAY_BUFFER, meshHelper.vertexBuffer);
        aiVertex* verts = (aiVertex*)glMapBuffer(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
        
        for (unsigned int x = 0; x < mesh->mNumVertices; ++x)
        {
            //verts->vPosition = mesh->mVertices[x];
            verts->vPosition = resultPos[x];
            
            if (NULL == mesh->mNormals)
                verts->vNormal = aiVector3D(0.0f,0.0f,0.0f);
            else
                verts->vNormal = resultNorm[x];
            
            if (mesh->HasVertexColors(0))
            {
                verts->dColorDiffuse = mesh->mColors[0][x];
            }
            else
                verts->dColorDiffuse = aiColor4D(1.0, 1.0, 1.0, 1.0);
            
            // This varies slightly form Assimp View, we support the 3rd texture component.
            if (mesh->HasTextureCoords(0))
                verts->vTextureUV = mesh->mTextureCoords[0][x];
            else
                verts->vTextureUV = aiVector3D(0.5f,0.5f, 0.0f);
            
            // No longer in aiVertex VBO structure
            /*          
             if (NULL == mesh->mTangents)
             {
             verts->vTangent = aiVector3D(0.0f,0.0f,0.0f);
             verts->vBitangent = aiVector3D(0.0f,0.0f,0.0f);
             }
             else
             {
             verts->vTangent = mesh->mTangents[x];
             verts->vBitangent = mesh->mBitangents[x];
             }
             
             if (mesh->HasTextureCoords(1))
             verts->vTextureUV2 = mesh->mTextureCoords[1][x];
             else 
             verts->vTextureUV2 = aiVector3D(0.5f,0.5f, 0.0f);
             
             if( mesh->HasBones()){
             unsigned char boneIndices[4] = { 0, 0, 0, 0 };
             unsigned char boneWeights[4] = { 0, 0, 0, 0 };
             ai_assert( weightsPerVertex[x].size() <= 4);
             
             for( unsigned int a = 0; a < weightsPerVertex[x].size(); a++){
             boneIndices[a] = weightsPerVertex[x][a].mVertexId;
             boneWeights[a] = (unsigned char) (weightsPerVertex[x][a].mWeight * 255.0f);
             }
             
             memcpy( verts->mBoneIndices, boneIndices, sizeof( boneIndices));
             memcpy( verts->mBoneWeights, boneWeights, sizeof( boneWeights));
             }
             else{
             // memset( verts->mBoneIndices, 0, sizeof( verts->mBoneIndices));
             // memset( verts->mBoneWeights, 0, sizeof( verts->mBoneWeights));
             }
             */           
            ++verts;
        }
        
        glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); //invalidates verts
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }
}
コード例 #5
0
ファイル: glview.cpp プロジェクト: cuigrey/assimp
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 thru 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 splited 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 thru 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
			if(name.isEmpty()) name += QString("%1").arg(idx_light);// Use index if name is empty.

			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
}