void FLwsWebSocket::InitLwsProtocols() { LwsProtocols.Empty(Protocols.Num()+1); for(FString Protocol : Protocols) { FTCHARToUTF8 ConvertName(*Protocol); // We need to hold on to the converted strings ANSICHAR *Converted = static_cast<ANSICHAR*>(FMemory::Malloc(ConvertName.Length()+1)); FCStringAnsi::Strcpy(Converted, ConvertName.Length(), ConvertName.Get()); LwsProtocols.Add({Converted, &FLwsWebSocket::CallbackWrapper, 0, 65536}); } // Add a zero terminator at the end as we don't pass the length to LWS LwsProtocols.Add({nullptr, nullptr, 0, 0}); }
// ----------------------------------------------------------------------------------- // Write a single node as text dump void WriteNode(const aiNode* node, FILE* out, unsigned int depth) { char prefix[512]; for (unsigned int i = 0; i < depth;++i) prefix[i] = '\t'; prefix[depth] = '\0'; const aiMatrix4x4& m = node->mTransformation; aiString name; ConvertName(name,node->mName); fprintf(out,"%s<Node name=\"%s\"> \n" "%s\t<Matrix4> \n" "%s\t\t%0 6f %0 6f %0 6f %0 6f\n" "%s\t\t%0 6f %0 6f %0 6f %0 6f\n" "%s\t\t%0 6f %0 6f %0 6f %0 6f\n" "%s\t\t%0 6f %0 6f %0 6f %0 6f\n" "%s\t</Matrix4> \n", prefix,name.data,prefix, prefix,m.a1,m.a2,m.a3,m.a4, prefix,m.b1,m.b2,m.b3,m.b4, prefix,m.c1,m.c2,m.c3,m.c4, prefix,m.d1,m.d2,m.d3,m.d4,prefix); if (node->mNumMeshes) { fprintf(out, "%s\t<MeshRefs num=\"%i\">\n%s\t", prefix,node->mNumMeshes,prefix); for (unsigned int i = 0; i < node->mNumMeshes;++i) { fprintf(out,"%i ",node->mMeshes[i]); } fprintf(out,"\n%s\t</MeshRefs>\n",prefix); } if (node->mNumChildren) { fprintf(out,"%s\t<NodeList num=\"%i\">\n", prefix,node->mNumChildren); for (unsigned int i = 0; i < node->mNumChildren;++i) { WriteNode(node->mChildren[i],out,depth+2); } fprintf(out,"%s\t</NodeList>\n",prefix); } fprintf(out,"%s</Node>\n",prefix); }
// ----------------------------------------------------------------------------------- // Write a text model dump void WriteDump(const aiScene* scene, FILE* out, const char* src, const char* cmd, bool shortened) { time_t tt = ::time(NULL); tm* p = ::gmtime(&tt); std::string c = cmd; std::string::size_type s; // https://sourceforge.net/tracker/?func=detail&aid=3167364&group_id=226462&atid=1067632 // -- not allowed in XML comments while((s = c.find("--")) != std::string::npos) { c[s] = '?'; } aiString name; // write header fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<ASSIMP format_id=\"1\">\n\n" "<!-- XML Model dump produced by assimp dump\n" " Library version: %i.%i.%i\n" " Source: %s\n" " Command line: %s\n" " %s\n" "-->" " \n\n" "<Scene flags=\"%i\" postprocessing=\"%i\">\n", aiGetVersionMajor(),aiGetVersionMinor(),aiGetVersionRevision(),src,c.c_str(),asctime(p), scene->mFlags, 0 /*globalImporter->GetEffectivePostProcessing()*/); // write the node graph WriteNode(scene->mRootNode, out, 0); #if 0 // write cameras for (unsigned int i = 0; i < scene->mNumCameras;++i) { aiCamera* cam = scene->mCameras[i]; ConvertName(name,cam->mName); // camera header fprintf(out,"\t<Camera parent=\"%s\">\n" "\t\t<Vector3 name=\"up\" > %0 8f %0 8f %0 8f </Vector3>\n" "\t\t<Vector3 name=\"lookat\" > %0 8f %0 8f %0 8f </Vector3>\n" "\t\t<Vector3 name=\"pos\" > %0 8f %0 8f %0 8f </Vector3>\n" "\t\t<Float name=\"fov\" > %f </Float>\n" "\t\t<Float name=\"aspect\" > %f </Float>\n" "\t\t<Float name=\"near_clip\" > %f </Float>\n" "\t\t<Float name=\"far_clip\" > %f </Float>\n" "\t</Camera>\n", name.data, cam->mUp.x,cam->mUp.y,cam->mUp.z, cam->mLookAt.x,cam->mLookAt.y,cam->mLookAt.z, cam->mPosition.x,cam->mPosition.y,cam->mPosition.z, cam->mHorizontalFOV,cam->mAspect,cam->mClipPlaneNear,cam->mClipPlaneFar,i); } // write lights for (unsigned int i = 0; i < scene->mNumLights;++i) { aiLight* l = scene->mLights[i]; ConvertName(name,l->mName); // light header fprintf(out,"\t<Light parent=\"%s\"> type=\"%s\"\n" "\t\t<Vector3 name=\"diffuse\" > %0 8f %0 8f %0 8f </Vector3>\n" "\t\t<Vector3 name=\"specular\" > %0 8f %0 8f %0 8f </Vector3>\n" "\t\t<Vector3 name=\"ambient\" > %0 8f %0 8f %0 8f </Vector3>\n", name.data, (l->mType == aiLightSource_DIRECTIONAL ? "directional" : (l->mType == aiLightSource_POINT ? "point" : "spot" )), l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b, l->mColorSpecular.r,l->mColorSpecular.g,l->mColorSpecular.b, l->mColorAmbient.r, l->mColorAmbient.g, l->mColorAmbient.b); if (l->mType != aiLightSource_DIRECTIONAL) { fprintf(out, "\t\t<Vector3 name=\"pos\" > %0 8f %0 8f %0 8f </Vector3>\n" "\t\t<Float name=\"atten_cst\" > %f </Float>\n" "\t\t<Float name=\"atten_lin\" > %f </Float>\n" "\t\t<Float name=\"atten_sqr\" > %f </Float>\n", l->mPosition.x,l->mPosition.y,l->mPosition.z, l->mAttenuationConstant,l->mAttenuationLinear,l->mAttenuationQuadratic); } if (l->mType != aiLightSource_POINT) { fprintf(out, "\t\t<Vector3 name=\"lookat\" > %0 8f %0 8f %0 8f </Vector3>\n", l->mDirection.x,l->mDirection.y,l->mDirection.z); } if (l->mType == aiLightSource_SPOT) { fprintf(out, "\t\t<Float name=\"cone_out\" > %f </Float>\n" "\t\t<Float name=\"cone_inn\" > %f </Float>\n", l->mAngleOuterCone,l->mAngleInnerCone); } fprintf(out,"\t</Light>\n"); } #endif // write textures if (scene->mNumTextures) { fprintf(out,"<TextureList num=\"%i\">\n",scene->mNumTextures); for (unsigned int i = 0; i < scene->mNumTextures;++i) { aiTexture* tex = scene->mTextures[i]; bool compressed = (tex->mHeight == 0); // mesh header fprintf(out,"\t<Texture width=\"%i\" height=\"%i\" compressed=\"%s\"> \n", (compressed ? -1 : tex->mWidth),(compressed ? -1 : tex->mHeight), (compressed ? "true" : "false")); if (compressed) { fprintf(out,"\t\t<Data length=\"%i\"> \n",tex->mWidth); if (!shortened) { for (unsigned int n = 0; n < tex->mWidth;++n) { fprintf(out,"\t\t\t%2x",reinterpret_cast<uint8_t*>(tex->pcData)[n]); if (n && !(n % 50)) { fprintf(out,"\n"); } } } } else if (!shortened){ fprintf(out,"\t\t<Data length=\"%i\"> \n",tex->mWidth*tex->mHeight*4); // const unsigned int width = (unsigned int)log10((double)std::max(tex->mHeight,tex->mWidth))+1; for (unsigned int y = 0; y < tex->mHeight;++y) { for (unsigned int x = 0; x < tex->mWidth;++x) { aiTexel* tx = tex->pcData + y*tex->mWidth+x; unsigned int r = tx->r,g=tx->g,b=tx->b,a=tx->a; fprintf(out,"\t\t\t%2x %2x %2x %2x",r,g,b,a); // group by four for readibility if (0 == (x+y*tex->mWidth) % 4) fprintf(out,"\n"); } } } fprintf(out,"\t\t</Data>\n\t</Texture>\n"); } fprintf(out,"</TextureList>\n"); } // write materials if (scene->mNumMaterials) { fprintf(out,"<MaterialList num=\"%i\">\n",scene->mNumMaterials); for (unsigned int i = 0; i< scene->mNumMaterials; ++i) { const aiMaterial* mat = scene->mMaterials[i]; fprintf(out,"\t<Material>\n"); fprintf(out,"\t\t<MatPropertyList num=\"%i\">\n",mat->mNumProperties); for (unsigned int n = 0; n < mat->mNumProperties;++n) { const aiMaterialProperty* prop = mat->mProperties[n]; const char* sz = ""; if (prop->mType == aiPTI_Float) { sz = "float"; } else if (prop->mType == aiPTI_Integer) { sz = "integer"; } else if (prop->mType == aiPTI_String) { sz = "string"; } else if (prop->mType == aiPTI_Buffer) { sz = "binary_buffer"; } fprintf(out,"\t\t\t<MatProperty key=\"%s\" \n\t\t\ttype=\"%s\" tex_usage=\"%s\" tex_index=\"%i\"", prop->mKey.data, sz, ::TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex); if (prop->mType == aiPTI_Float) { fprintf(out," size=\"%i\">\n\t\t\t\t", static_cast<int>(prop->mDataLength/sizeof(float))); for (unsigned int p = 0; p < prop->mDataLength/sizeof(float);++p) { fprintf(out,"%f ",*((float*)(prop->mData+p*sizeof(float)))); } } else if (prop->mType == aiPTI_Integer) { fprintf(out," size=\"%i\">\n\t\t\t\t", static_cast<int>(prop->mDataLength/sizeof(int))); for (unsigned int p = 0; p < prop->mDataLength/sizeof(int);++p) { fprintf(out,"%i ",*((int*)(prop->mData+p*sizeof(int)))); } } else if (prop->mType == aiPTI_Buffer) { fprintf(out," size=\"%i\">\n\t\t\t\t", static_cast<int>(prop->mDataLength)); for (unsigned int p = 0; p < prop->mDataLength;++p) { fprintf(out,"%2x ",prop->mData[p]); if (p && 0 == p%30) { fprintf(out,"\n\t\t\t\t"); } } } else if (prop->mType == aiPTI_String) { fprintf(out,">\n\t\t\t\"%s\"",prop->mData+4 /* skip length */); } fprintf(out,"\n\t\t\t</MatProperty>\n"); } fprintf(out,"\t\t</MatPropertyList>\n"); fprintf(out,"\t</Material>\n"); } fprintf(out,"</MaterialList>\n"); } // write animations if (scene->mNumAnimations) { fprintf(out,"<AnimationList num=\"%i\">\n",scene->mNumAnimations); for (unsigned int i = 0; i < scene->mNumAnimations;++i) { aiAnimation* anim = scene->mAnimations[i]; // anim header ConvertName(name,anim->mName); fprintf(out,"\t<Animation name=\"%s\" duration=\"%e\" tick_cnt=\"%e\">\n", name.data, anim->mDuration, anim->mTicksPerSecond); // write bone animation channels if (anim->mNumChannels) { fprintf(out,"\t\t<NodeAnimList num=\"%i\">\n",anim->mNumChannels); for (unsigned int n = 0; n < anim->mNumChannels;++n) { aiNodeAnim* nd = anim->mChannels[n]; // node anim header ConvertName(name,nd->mNodeName); fprintf(out,"\t\t\t<NodeAnim node=\"%s\">\n",name.data); if (!shortened) { // write position keys if (nd->mNumPositionKeys) { fprintf(out,"\t\t\t\t<PositionKeyList num=\"%i\">\n",nd->mNumPositionKeys); for (unsigned int a = 0; a < nd->mNumPositionKeys;++a) { aiVectorKey* vc = nd->mPositionKeys+a; fprintf(out,"\t\t\t\t\t<PositionKey time=\"%e\">\n" "\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t</PositionKey>\n", vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z); } fprintf(out,"\t\t\t\t</PositionKeyList>\n"); } // write scaling keys if (nd->mNumScalingKeys) { fprintf(out,"\t\t\t\t<ScalingKeyList num=\"%i\">\n",nd->mNumScalingKeys); for (unsigned int a = 0; a < nd->mNumScalingKeys;++a) { aiVectorKey* vc = nd->mScalingKeys+a; fprintf(out,"\t\t\t\t\t<ScalingKey time=\"%e\">\n" "\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t</ScalingKey>\n", vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z); } fprintf(out,"\t\t\t\t</ScalingKeyList>\n"); } // write rotation keys if (nd->mNumRotationKeys) { fprintf(out,"\t\t\t\t<RotationKeyList num=\"%i\">\n",nd->mNumRotationKeys); for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) { aiQuatKey* vc = nd->mRotationKeys+a; fprintf(out,"\t\t\t\t\t<RotationKey time=\"%e\">\n" "\t\t\t\t\t\t%0 8f %0 8f %0 8f %0 8f\n\t\t\t\t\t</RotationKey>\n", vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z,vc->mValue.w); } fprintf(out,"\t\t\t\t</RotationKeyList>\n"); } } fprintf(out,"\t\t\t</NodeAnim>\n"); } fprintf(out,"\t\t</NodeAnimList>\n"); } fprintf(out,"\t</Animation>\n"); } fprintf(out,"</AnimationList>\n"); } // write meshes if (scene->mNumMeshes) { fprintf(out,"<MeshList num=\"%i\">\n",scene->mNumMeshes); for (unsigned int i = 0; i < scene->mNumMeshes;++i) { aiMesh* mesh = scene->mMeshes[i]; // const unsigned int width = (unsigned int)log10((double)mesh->mNumVertices)+1; // mesh header fprintf(out,"\t<Mesh types=\"%s %s %s %s\" material_index=\"%i\">\n", (mesh->mPrimitiveTypes & aiPrimitiveType_POINT ? "points" : ""), (mesh->mPrimitiveTypes & aiPrimitiveType_LINE ? "lines" : ""), (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE ? "triangles" : ""), (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON ? "polygons" : ""), mesh->mMaterialIndex); // bones if (mesh->mNumBones) { fprintf(out,"\t\t<BoneList num=\"%i\">\n",mesh->mNumBones); for (unsigned int n = 0; n < mesh->mNumBones;++n) { aiBone* bone = mesh->mBones[n]; ConvertName(name,bone->mName); // bone header fprintf(out,"\t\t\t<Bone name=\"%s\">\n" "\t\t\t\t<Matrix4> \n" "\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n" "\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n" "\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n" "\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n" "\t\t\t\t</Matrix4> \n", name.data, bone->mOffsetMatrix.a1,bone->mOffsetMatrix.a2,bone->mOffsetMatrix.a3,bone->mOffsetMatrix.a4, bone->mOffsetMatrix.b1,bone->mOffsetMatrix.b2,bone->mOffsetMatrix.b3,bone->mOffsetMatrix.b4, bone->mOffsetMatrix.c1,bone->mOffsetMatrix.c2,bone->mOffsetMatrix.c3,bone->mOffsetMatrix.c4, bone->mOffsetMatrix.d1,bone->mOffsetMatrix.d2,bone->mOffsetMatrix.d3,bone->mOffsetMatrix.d4); if (!shortened && bone->mNumWeights) { fprintf(out,"\t\t\t\t<WeightList num=\"%i\">\n",bone->mNumWeights); // bone weights for (unsigned int a = 0; a < bone->mNumWeights;++a) { aiVertexWeight* wght = bone->mWeights+a; fprintf(out,"\t\t\t\t\t<Weight index=\"%i\">\n\t\t\t\t\t\t%f\n\t\t\t\t\t</Weight>\n", wght->mVertexId,wght->mWeight); } fprintf(out,"\t\t\t\t</WeightList>\n"); } fprintf(out,"\t\t\t</Bone>\n"); } fprintf(out,"\t\t</BoneList>\n"); } // faces if (!shortened && mesh->mNumFaces) { fprintf(out,"\t\t<FaceList num=\"%i\">\n",mesh->mNumFaces); for (unsigned int n = 0; n < mesh->mNumFaces; ++n) { aiFace& f = mesh->mFaces[n]; fprintf(out,"\t\t\t<Face num=\"%i\">\n" "\t\t\t\t",f.mNumIndices); for (unsigned int j = 0; j < f.mNumIndices;++j) fprintf(out,"%i ",f.mIndices[j]); fprintf(out,"\n\t\t\t</Face>\n"); } fprintf(out,"\t\t</FaceList>\n"); } // vertex positions if (mesh->HasPositions()) { fprintf(out,"\t\t<Positions num=\"%i\" set=\"0\" num_components=\"3\"> \n",mesh->mNumVertices); if (!shortened) { for (unsigned int n = 0; n < mesh->mNumVertices; ++n) { fprintf(out,"\t\t%0 8f %0 8f %0 8f\n", mesh->mVertices[n].x, mesh->mVertices[n].y, mesh->mVertices[n].z); } } fprintf(out,"\t\t</Positions>\n"); } // vertex normals if (mesh->HasNormals()) { fprintf(out,"\t\t<Normals num=\"%i\" set=\"0\" num_components=\"3\"> \n",mesh->mNumVertices); if (!shortened) { for (unsigned int n = 0; n < mesh->mNumVertices; ++n) { fprintf(out,"\t\t%0 8f %0 8f %0 8f\n", mesh->mNormals[n].x, mesh->mNormals[n].y, mesh->mNormals[n].z); } } else { } fprintf(out,"\t\t</Normals>\n"); } // vertex tangents and bitangents if (mesh->HasTangentsAndBitangents()) { fprintf(out,"\t\t<Tangents num=\"%i\" set=\"0\" num_components=\"3\"> \n",mesh->mNumVertices); if (!shortened) { for (unsigned int n = 0; n < mesh->mNumVertices; ++n) { fprintf(out,"\t\t%0 8f %0 8f %0 8f\n", mesh->mTangents[n].x, mesh->mTangents[n].y, mesh->mTangents[n].z); } } fprintf(out,"\t\t</Tangents>\n"); fprintf(out,"\t\t<Bitangents num=\"%i\" set=\"0\" num_components=\"3\"> \n",mesh->mNumVertices); if (!shortened) { for (unsigned int n = 0; n < mesh->mNumVertices; ++n) { fprintf(out,"\t\t%0 8f %0 8f %0 8f\n", mesh->mBitangents[n].x, mesh->mBitangents[n].y, mesh->mBitangents[n].z); } } fprintf(out,"\t\t</Bitangents>\n"); } // texture coordinates for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { if (!mesh->mTextureCoords[a]) break; fprintf(out,"\t\t<TextureCoords num=\"%i\" set=\"%i\" num_components=\"%i\"> \n",mesh->mNumVertices, a,mesh->mNumUVComponents[a]); if (!shortened) { if (mesh->mNumUVComponents[a] == 3) { for (unsigned int n = 0; n < mesh->mNumVertices; ++n) { fprintf(out,"\t\t%0 8f %0 8f %0 8f\n", mesh->mTextureCoords[a][n].x, mesh->mTextureCoords[a][n].y, mesh->mTextureCoords[a][n].z); } } else { for (unsigned int n = 0; n < mesh->mNumVertices; ++n) { fprintf(out,"\t\t%0 8f %0 8f\n", mesh->mTextureCoords[a][n].x, mesh->mTextureCoords[a][n].y); } } } fprintf(out,"\t\t</TextureCoords>\n"); } // vertex colors for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) { if (!mesh->mColors[a]) break; fprintf(out,"\t\t<Colors num=\"%i\" set=\"%i\" num_components=\"4\"> \n",mesh->mNumVertices,a); if (!shortened) { for (unsigned int n = 0; n < mesh->mNumVertices; ++n) { fprintf(out,"\t\t%0 8f %0 8f %0 8f %0 8f\n", mesh->mColors[a][n].r, mesh->mColors[a][n].g, mesh->mColors[a][n].b, mesh->mColors[a][n].a); } } fprintf(out,"\t\t</Color>\n"); } fprintf(out,"\t</Mesh>\n"); } fprintf(out,"</MeshList>\n"); } fprintf(out,"</Scene>\n</ASSIMP>"); }
//Find one empty short directory entry in a cluster chain start from dwStartCluster, //and save the short entry pointed by pfse into this entry.If can not find a free one //in the whole cluster chain,then append a free cluster in the chain and save it. BOOL CreateDirEntry(__FAT32_FS* pFat32Fs,DWORD dwStartCluster,__FAT32_SHORTENTRY* pDirEntry) { __FAT32_SHORTENTRY DirEntry; __FAT32_SHORTENTRY* pfse = NULL; DWORD dwSector = 0; DWORD dwCurrCluster = 0; DWORD dwNextCluster = 0; BYTE* pBuffer = NULL; CHAR DirName[13] = {0}; DWORD i; BOOL bFind = FALSE; BOOL bResult = FALSE; if((NULL == pFat32Fs) || (dwStartCluster < 2) || IS_EOC(dwStartCluster) || (NULL == pDirEntry)) { goto __TERMINAL; } if(!ConvertName(pDirEntry,(BYTE*)&DirName[0])) { goto __TERMINAL; } //Check if the directory to be created has already in directory. if(GetShortEntry(pFat32Fs,dwStartCluster,DirName,&DirEntry,NULL,NULL)) //Directory already exists. { goto __TERMINAL; } pBuffer = (BYTE*)LocalAlloc(LPTR,pFat32Fs->dwClusterSize); if(NULL == pBuffer) { goto __TERMINAL; } //Try to find a free directory entry in the given directory,if can not find,then //allocate a free cluster,append it to the given directory. dwNextCluster = dwStartCluster; while(!IS_EOC(dwNextCluster)) { dwCurrCluster = dwNextCluster; dwSector = GetClusterSector(pFat32Fs,dwCurrCluster); if(0 == dwSector) { //PrintLine(" In CreateDirEntry,Condition 2"); goto __TERMINAL; } if(!ReadDeviceSector(pFat32Fs->pPartition,pFat32Fs->dwPartitionSatrt+dwSector, pFat32Fs->SectorPerClus,pBuffer)) { //PrintLine(" In CreateDirEntry,Condition 3"); goto __TERMINAL; } //Search this cluster from begin. pfse = (__FAT32_SHORTENTRY*)pBuffer; for(i = 0;i < pFat32Fs->dwClusterSize / sizeof(__FAT32_SHORTENTRY);i++) { if((0 == pfse->FileName[0]) || (0xE5 == (BYTE)pfse->FileName[0])) //Find a free slot. { bFind = TRUE; break; } pfse ++; } if(bFind) //Find a free directory entry,no need to check further. { break; } //Can not find a free directory slot,try to search next cluster. if(!GetNextCluster(pFat32Fs,&dwNextCluster)) { //PrintLine(" In CreateDirEntry,Condition 4"); goto __TERMINAL; } } if(bFind) //Has found a free directory slot. { memcpy((char*)pfse,(const char*)pDirEntry,sizeof(__FAT32_SHORTENTRY)); if(!WriteDeviceSector(pFat32Fs->pPartition,pFat32Fs->dwPartitionSatrt+dwSector,pFat32Fs->SectorPerClus,pBuffer)) { goto __TERMINAL; } } else //Can not find a free slot,allocate a new cluster for parent directory. { if(!AppendClusterToChain(pFat32Fs,&dwCurrCluster)) { goto __TERMINAL; } ZeroMemory(pBuffer,pFat32Fs->dwClusterSize); memcpy((char*)pBuffer,(const char*)pDirEntry,sizeof(__FAT32_SHORTENTRY)); dwSector = GetClusterSector(pFat32Fs,dwCurrCluster); if(!WriteDeviceSector(pFat32Fs->pPartition,pFat32Fs->dwPartitionSatrt+dwSector,pFat32Fs->SectorPerClus,pBuffer)) { goto __TERMINAL; } } bResult = TRUE; __TERMINAL: if(pBuffer) { LocalFree(pBuffer); } return bResult; }
BOOL GetShortEntry(__FAT32_FS* pFat32Fs,DWORD dwStartCluster,CHAR* pFileName,__FAT32_SHORTENTRY* pShortEntry, DWORD* pDirClus,DWORD* pDirOffset) { BOOL bResult = FALSE; __FAT32_SHORTENTRY* pfse = NULL; BYTE* pBuffer = NULL; DWORD dwCurrClus = 0; DWORD dwSector = 0; BYTE FileName[13]; int i; if((NULL == pFat32Fs) || (NULL == pFileName) || (pShortEntry == pfse)) { goto __TERMINAL; } //Create local buffer to contain one cluster. pBuffer = (BYTE*)LocalAlloc(LPTR,pFat32Fs->SectorPerClus * pFat32Fs->dwBytePerSector); if(NULL == pBuffer) { goto __TERMINAL; } dwCurrClus = dwStartCluster; while(!IS_EOC(dwCurrClus)) //Main loop to check the root directory. { dwSector = GetClusterSector(pFat32Fs,dwCurrClus); if(0 == dwSector) //Fatal error. { //PrintLine(" In GetShortEntry: Can not get cluster sector."); goto __TERMINAL; } if(!ReadDeviceSector(pFat32Fs->pPartition, pFat32Fs->dwPartitionSatrt+dwSector, pFat32Fs->SectorPerClus, pBuffer)) //Can not read the appropriate sector(s). { //PrintLine(" In GetShortEntry: Can not read sector from device."); goto __TERMINAL; } //Now check the root directory to seek the volume ID entry. pfse = (__FAT32_SHORTENTRY*)pBuffer; for(i = 0;i < pFat32Fs->SectorPerClus * 16;i ++) { if(0xE5 == (BYTE)pfse->FileName[0]) //Empty entry. { pfse += 1; //Seek to the next entry. continue; } if(0 == pfse->FileName[0]) //All rest part is zero,no need to check futher. { break; } if(FILE_ATTR_LONGNAME == pfse->FileAttributes) //Long file name entry. { pfse += 1; continue; } if(FILE_ATTR_VOLUMEID & pfse->FileAttributes) //Volume label entry. { pfse += 1; continue; } if(ConvertName(pfse,FileName)) //Can not convert to regular file name string. { if(strcmpi((CHAR*)pFileName,(CHAR*)&FileName[0]) == 0) //Found. { memcpy((char*)pShortEntry,(const char*)pfse,sizeof(__FAT32_SHORTENTRY)); if(pDirClus) { *pDirClus = dwCurrClus; } if(pDirOffset) { *pDirOffset = (BYTE*)pfse - pBuffer; } bResult = TRUE; goto __TERMINAL; } } pfse += 1; } if(!GetNextCluster(pFat32Fs,&dwCurrClus)) { break; } } __TERMINAL: if(pBuffer) { LocalFree(pBuffer); } return bResult; }
//Find one empty short directory entry in a cluster chain start from dwStartCluster, //and save the short entry pointed by pfse into this entry.If can not find a free one //in the whole cluster chain,then append a free cluster in the chain and save it. BOOL CreateDirEntry(__FAT32_FS* pFat32Fs,DWORD dwStartCluster,__FAT32_SHORTENTRY* pDirEntry) { __FAT32_SHORTENTRY DirEntry; __FAT32_SHORTENTRY* pfse = NULL; DWORD dwSector = 0; DWORD dwCurrCluster = 0; DWORD dwNextCluster = 0; BYTE* pBuffer = NULL; CHAR DirName[13] = {0}; DWORD i; BOOL bFind = FALSE; BOOL bResult = FALSE; if((NULL == pFat32Fs) || (dwStartCluster < 2) || IS_EOC(dwStartCluster) || (NULL == pDirEntry)) { PrintLine(" In CreateDirEntry,Condition 0"); goto __TERMINAL; } if(!ConvertName(pDirEntry,(BYTE*)&DirName[0])) { PrintLine(" In CreateDirEntry,Condition 1"); goto __TERMINAL; } //Check if the directory to be created has already in directory. if(GetShortEntry(pFat32Fs,dwStartCluster,DirName,&DirEntry,NULL,NULL)) //Directory already exists. { PrintLine(" In CreateDirEntry: The specified directory already exist."); goto __TERMINAL; } pBuffer = (BYTE*)KMemAlloc(pFat32Fs->dwClusterSize,KMEM_SIZE_TYPE_ANY); if(NULL == pBuffer) { PrintLine(" In CreateDirEntry,can not allocate memory for temporary buffer."); goto __TERMINAL; } //Try to find a free directory entry in the given directory,if can not find,then //allocate a free cluster,append it to the given directory. dwNextCluster = dwStartCluster; while(!IS_EOC(dwNextCluster)) { dwCurrCluster = dwNextCluster; dwSector = GetClusterSector(pFat32Fs,dwCurrCluster); if(0 == dwSector) { PrintLine(" In CreateDirEntry,Condition 2"); goto __TERMINAL; } if(!ReadDeviceSector((__COMMON_OBJECT*)pFat32Fs->pPartition, dwSector, pFat32Fs->SectorPerClus, pBuffer)) { PrintLine(" In CreateDirEntry,Condition 3"); goto __TERMINAL; } //Search this cluster from begin. pfse = (__FAT32_SHORTENTRY*)pBuffer; for(i = 0;i < pFat32Fs->dwClusterSize / sizeof(__FAT32_SHORTENTRY);i++) { if((0 == pfse->FileName[0]) || ((BYTE)0xE5 == pfse->FileName[0])) //Find a free slot. { bFind = TRUE; break; } pfse ++; } if(bFind) //Find a free directory entry,no need to check further. { break; } //Can not find a free directory slot,try to search next cluster. if(!GetNextCluster(pFat32Fs,&dwNextCluster)) { PrintLine(" In CreateDirEntry,Condition 4"); goto __TERMINAL; } } if(bFind) //Has found a free directory slot. { memcpy((char*)pfse,(const char*)pDirEntry,sizeof(__FAT32_SHORTENTRY)); if(!WriteDeviceSector((__COMMON_OBJECT*)pFat32Fs->pPartition, dwSector, pFat32Fs->SectorPerClus, pBuffer)) { PrintLine(" In CreateDirEntry,Condition 5"); goto __TERMINAL; } } else //Can not find a free slot,allocate a new cluster for parent directory. { if(!AppendClusterToChain(pFat32Fs,&dwCurrCluster)) { PrintLine(" In CreateDirEntry: Can not append a free cluster to this dir."); goto __TERMINAL; } memzero(pBuffer,pFat32Fs->dwClusterSize); memcpy((char*)pBuffer,(const char*)pDirEntry,sizeof(__FAT32_SHORTENTRY)); dwSector = GetClusterSector(pFat32Fs,dwCurrCluster); if(!WriteDeviceSector((__COMMON_OBJECT*)pFat32Fs->pPartition, dwSector, pFat32Fs->SectorPerClus, pBuffer)) { PrintLine(" In CreateDirEntry,Condition 6"); goto __TERMINAL; } } /* _hx_sprintf(pBuffer,"In CreateDirEntry: dwSector = %d,dwCurrCluster = %d,offset = %d", dwSector, dwCurrCluster, (BYTE*)pfse - pBuffer); PrintLine(pBuffer);*/ bResult = TRUE; __TERMINAL: if(pBuffer) { KMemFree(pBuffer,KMEM_SIZE_TYPE_ANY,0); } return bResult; }
// ----------------------------------------------------------------------------------- // Write a text model dump void WriteDump(const aiScene* scene, FILE* out, const char* src, const char* cmd, bool shortened) { time_t tt = ::time(NULL); tm* p = ::gmtime(&tt); aiString name; // write header ::fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<ASSIMP >\n\n" "<!-- XML Model dump produced by assimp dump\n" " Library version: %i.%i.%i\n" " Source: %s\n" " Command line: %s\n" " %s\n" "-->" " \n\n" "<Scene NumberOfMeshes=\"%i\" NumberOfMaterials=\"%i\" NumberOfTextures=\"%i\" NumberOfCameras=\"%i\" NumberOfLights=\"%i\" NumberOfAnimations=\"%i\">\n", aiGetVersionMajor(),aiGetVersionMinor(),aiGetVersionRevision(),src,cmd,::asctime(p), scene->mNumMeshes, scene->mNumMaterials,scene->mNumTextures, scene->mNumCameras,scene->mNumLights,scene->mNumAnimations); // write the node graph WriteNode(scene->mRootNode, out, 1); // write cameras for (unsigned int i = 0; i < scene->mNumCameras;++i) { aiCamera* cam = scene->mCameras[i]; ConvertName(name,cam->mName); // camera header ::fprintf(out,"\t<Camera parent=\"%s\">\n" "\t\t<Vector3 name=\"up\" > %0 8f %0 8f %0 8f </Vector3>\n" "\t\t<Vector3 name=\"lookat\" > %0 8f %0 8f %0 8f </Vector3>\n" "\t\t<Vector3 name=\"pos\" > %0 8f %0 8f %0 8f </Vector3>\n" "\t\t<Float name=\"fov\" > %f </Float>\n" "\t\t<Float name=\"aspect\" > %f </Float>\n" "\t\t<Float name=\"near_clip\" > %f </Float>\n" "\t\t<Float name=\"far_clip\" > %f </Float>\n" "\t</Camera>\n", name.data, cam->mUp.x,cam->mUp.y,cam->mUp.z, cam->mLookAt.x,cam->mLookAt.y,cam->mLookAt.z, cam->mPosition.x,cam->mPosition.y,cam->mPosition.z, cam->mHorizontalFOV,cam->mAspect,cam->mClipPlaneNear,cam->mClipPlaneFar,i); } // write lights for (unsigned int i = 0; i < scene->mNumLights;++i) { aiLight* l = scene->mLights[i]; ConvertName(name,l->mName); // light header ::fprintf(out,"\t<Light parent=\"%s\"> type=\"%s\"\n" "\t\t<Vector3 name=\"diffuse\" > %0 8f %0 8f %0 8f </Vector3>\n" "\t\t<Vector3 name=\"specular\" > %0 8f %0 8f %0 8f </Vector3>\n" "\t\t<Vector3 name=\"ambient\" > %0 8f %0 8f %0 8f </Vector3>\n", name.data, (l->mType == aiLightSource_DIRECTIONAL ? "directional" : (l->mType == aiLightSource_POINT ? "point" : "spot" )), l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b, l->mColorSpecular.r,l->mColorSpecular.g,l->mColorSpecular.b, l->mColorAmbient.r, l->mColorAmbient.g, l->mColorAmbient.b); if (l->mType != aiLightSource_DIRECTIONAL) { ::fprintf(out, "\t\t<Vector3 name=\"pos\" > %0 8f %0 8f %0 8f </Vector3>\n" "\t\t<Float name=\"atten_cst\" > %f </Float>\n" "\t\t<Float name=\"atten_lin\" > %f </Float>\n" "\t\t<Float name=\"atten_sqr\" > %f </Float>\n", l->mPosition.x,l->mPosition.y,l->mPosition.z, l->mAttenuationConstant,l->mAttenuationLinear,l->mAttenuationQuadratic); } if (l->mType != aiLightSource_POINT) { ::fprintf(out, "\t\t<Vector3 name=\"lookat\" > %0 8f %0 8f %0 8f </Vector3>\n", l->mDirection.x,l->mDirection.y,l->mDirection.z); } if (l->mType == aiLightSource_SPOT) { ::fprintf(out, "\t\t<Float name=\"cone_out\" > %f </Float>\n" "\t\t<Float name=\"cone_inn\" > %f </Float>\n", l->mAngleOuterCone,l->mAngleInnerCone); } ::fprintf(out,"\t</Light>\n"); } // write textures for (unsigned int i = 0; i < scene->mNumTextures;++i) { aiTexture* tex = scene->mTextures[i]; bool compressed = (tex->mHeight == 0); // mesh header ::fprintf(out,"\t<Texture> \n" "\t\t<Integer name=\"width\" > %i </Integer>\n", "\t\t<Integer name=\"height\" > %i </Integer>\n", "\t\t<Boolean name=\"compressed\" > %s </Boolean>\n", (compressed ? -1 : tex->mWidth),(compressed ? -1 : tex->mHeight), (compressed ? "true" : "false")); if (compressed) { ::fprintf(out,"\t\t<Data length=\"%i\"> %i \n",tex->mWidth); if (!shortened) { for (unsigned int n = 0; n < tex->mWidth;++n) { ::fprintf(out,"\t\t\t%2x",tex->pcData[n]); if (n && !(n % 50)) ::fprintf(out,"\n"); } } } else if (!shortened){ ::fprintf(out,"\t\t<Data length=\"%i\"> %i \n",tex->mWidth*tex->mHeight*4); const unsigned int width = (unsigned int)log10((double)std::max(tex->mHeight,tex->mWidth))+1; for (unsigned int y = 0; y < tex->mHeight;++y) { for (unsigned int x = 0; x < tex->mWidth;++x) { aiTexel* tx = tex->pcData + y*tex->mWidth+x; unsigned int r = tx->r,g=tx->g,b=tx->b,a=tx->a; ::fprintf(out,"\t\t\t%2x %2x %2x %2x",r,g,b,a); // group by four for readibility if (0 == (x+y*tex->mWidth) % 4) ::fprintf(out,"\n"); } } } ::fprintf(out,"\t\t</Data>\n\t</Texture>\n"); } // write materials for (unsigned int i = 0; i< scene->mNumMaterials; ++i) { const aiMaterial* mat = scene->mMaterials[i]; ::fprintf(out, "\t<Material NumberOfProperties=\"%i\">\n",mat->mNumProperties); for (unsigned int n = 0; n < mat->mNumProperties;++n) { const aiMaterialProperty* prop = mat->mProperties[n]; const char* sz = ""; if (prop->mType == aiPTI_Float) sz = "float"; else if (prop->mType == aiPTI_Integer) sz = "integer"; else if (prop->mType == aiPTI_String) sz = "string"; else if (prop->mType == aiPTI_Buffer) sz = "binary_buffer"; ::fprintf(out, "\t\t<MatProperty key=\"%s\" \n\t\t\ttype=\"%s\" tex_usage=\"%s\" tex_index=\"%i\"", prop->mKey.data, sz, TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex); if (prop->mType == aiPTI_Float) { ::fprintf(out, " size=\"%i\">\n\t\t\t", prop->mDataLength/sizeof(float)); for (unsigned int p = 0; p < prop->mDataLength/sizeof(float);++p) ::fprintf(out,"%f ",*((float*)(prop->mData+p*sizeof(float)))); } else if (prop->mType == aiPTI_Integer) { ::fprintf(out, " size=\"%i\">\n\t\t\t", prop->mDataLength/sizeof(int)); for (unsigned int p = 0; p < prop->mDataLength/sizeof(int);++p) ::fprintf(out,"%i ",*((int*)(prop->mData+p*sizeof(int)))); } else if (prop->mType == aiPTI_Buffer) { ::fprintf(out, " size=\"%i\">\n\t\t\t", prop->mDataLength); for (unsigned int p = 0; p < prop->mDataLength;++p) { ::fprintf(out,"%2x ",prop->mData[p]); if (p && 0 == p%30) ::fprintf(out,"\n\t\t\t"); } } else if (prop->mType == aiPTI_String) { ::fprintf(out,">\n\t\t\t\"%s\"",prop->mData+4 /* skip length */); } ::fprintf(out,"\n\t\t</MatProperty>\n"); } ::fprintf(out,"\t</Material>\n"); } // write animations for (unsigned int i = 0; i < scene->mNumAnimations;++i) { aiAnimation* anim = scene->mAnimations[i]; // anim header ConvertName(name,anim->mName); ::fprintf(out,"\t<Animation name=\"%s\">\n" "\t\t<Integer name=\"num_chan\" > %i </Integer>\n" "\t\t<Float name=\"duration\" > %e </Float>\n" "\t\t<Float name=\"tick_cnt\" > %e </Float>\n", name.data, anim->mNumChannels,anim->mDuration, anim->mTicksPerSecond); // write bone animation channels for (unsigned int n = 0; n < anim->mNumChannels;++n) { aiNodeAnim* nd = anim->mChannels[n]; // node anim header ConvertName(name,nd->mNodeName); ::fprintf(out,"\t\t<Channel node=\"%s\">\n" "\t\t\t<Integer name=\"num_pos_keys\" > %i </Integer>\n" "\t\t\t<Integer name=\"num_scl_keys\" > %i </Integer>\n" "\t\t\t<Integer name=\"num_rot_keys\" > %i </Integer>\n", name.data,nd->mNumPositionKeys,nd->mNumScalingKeys,nd->mNumRotationKeys); if (!shortened) { // write position keys for (unsigned int a = 0; a < nd->mNumPositionKeys;++a) { aiVectorKey* vc = nd->mPositionKeys+a; ::fprintf(out,"\t\t\t<PositionKey time=\"%e\">\n" "\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t</PositionKey>\n", vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z,a); } // write scaling keys for (unsigned int a = 0; a < nd->mNumScalingKeys;++a) { aiVectorKey* vc = nd->mScalingKeys+a; ::fprintf(out,"\t\t\t<ScalingKey time=\"%e\">\n" "\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t</ScalingKey>\n", vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z,a); } // write rotation keys for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) { aiQuatKey* vc = nd->mRotationKeys+a; ::fprintf(out,"\t\t\t<RotationKey time=\"%e\">\n" "\t\t\t\t%0 8f %0 8f %0 8f %0 8f\n\t\t\t</RotationKey>\n", vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z,vc->mValue.w,a); } } ::fprintf(out,"\t\t</Channel>\n",n); } ::fprintf(out,"\t</Animation>\n",i); } // write meshes for (unsigned int i = 0; i < scene->mNumMeshes;++i) { aiMesh* mesh = scene->mMeshes[i]; const unsigned int width = (unsigned int)log10((double)mesh->mNumVertices)+1; // mesh header ::fprintf(out,"\t<Mesh types=\"%s %s %s %s\">\n" "\t\t<Integer name=\"num_verts\" > %i </Integer>\n" "\t\t<Integer name=\"num_faces\" > %i </Integer>\n", (mesh->mPrimitiveTypes & aiPrimitiveType_POINT ? "points" : ""), (mesh->mPrimitiveTypes & aiPrimitiveType_LINE ? "lines" : ""), (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE ? "triangles" : ""), (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON ? "polygons" : ""), mesh->mNumVertices,mesh->mNumFaces); // bones for (unsigned int n = 0; n < mesh->mNumBones;++n) { aiBone* bone = mesh->mBones[n]; ConvertName(name,bone->mName); // bone header ::fprintf(out,"\t\t<Bone name=\"%s\">\n" "\t\t\t<Matrix4 name=\"offset\" > \n" "\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n" "\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n" "\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n" "\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n" "\t\t\t</Matrix4> \n" "\t\t\t<Integer name=\"num_weights\" > %i </Integer>\n", name.data, bone->mOffsetMatrix.a1,bone->mOffsetMatrix.a2,bone->mOffsetMatrix.a3,bone->mOffsetMatrix.a4, bone->mOffsetMatrix.b1,bone->mOffsetMatrix.b2,bone->mOffsetMatrix.b3,bone->mOffsetMatrix.b4, bone->mOffsetMatrix.c1,bone->mOffsetMatrix.c2,bone->mOffsetMatrix.c3,bone->mOffsetMatrix.c4, bone->mOffsetMatrix.d1,bone->mOffsetMatrix.d2,bone->mOffsetMatrix.d3,bone->mOffsetMatrix.d4, bone->mNumWeights); if (!shortened) { // bone weights for (unsigned int a = 0; a < bone->mNumWeights;++a) { aiVertexWeight* wght = bone->mWeights+a; ::fprintf(out,"\t\t\t<VertexWeight index=\"%i\">\n\t\t\t\t%f\n\t\t\t</VertexWeight>\n", wght->mVertexId,wght->mWeight); } } ::fprintf(out,"\t\t</Bone>\n",n); } // faces if (!shortened) { for (unsigned int n = 0; n < mesh->mNumFaces; ++n) { aiFace& f = mesh->mFaces[n]; ::fprintf(out,"\t\t<Face num_indices=\"%i\">\n" "\t\t\t",f.mNumIndices); for (unsigned int j = 0; j < f.mNumIndices;++j) ::fprintf(out,"%i ",f.mIndices[j]); ::fprintf(out,"\n\t\t</Face>\n"); } } // vertex positions if (mesh->HasPositions()) { ::fprintf(out,"\t\t<Positions> \n"); if (!shortened) { for (unsigned int n = 0; n < mesh->mNumVertices; ++n) { ::fprintf(out,"\t\t%0 8f %0 8f %0 8f\n", mesh->mVertices[n].x, mesh->mVertices[n].y, mesh->mVertices[n].z); } } else { } ::fprintf(out,"\t\t</Positions>\n"); } // vertex normals if (mesh->HasNormals()) { ::fprintf(out,"\t\t<Normals> \n"); if (!shortened) { for (unsigned int n = 0; n < mesh->mNumVertices; ++n) { ::fprintf(out,"\t\t%0 8f %0 8f %0 8f\n", mesh->mNormals[n].x, mesh->mNormals[n].y, mesh->mNormals[n].z); } } else { } ::fprintf(out,"\t\t</Normals>\n"); } // vertex tangents and bitangents if (mesh->HasTangentsAndBitangents()) { ::fprintf(out,"\t\t<Tangents> \n"); if (!shortened) { for (unsigned int n = 0; n < mesh->mNumVertices; ++n) { ::fprintf(out,"\t\t%0 8f %0 8f %0 8f \t %0 8f %0 8f %0 8f\n", mesh->mTangents[n].x, mesh->mTangents[n].y, mesh->mTangents[n].z, mesh->mBitangents[n].x, mesh->mBitangents[n].y, mesh->mBitangents[n].z); } } else { } ::fprintf(out,"\t\t</Tangents>\n"); } // texture coordinates for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { if (!mesh->mTextureCoords[a]) break; ::fprintf(out,"\t\t<TextureCoords set=\"%i\" num_components=\"%i\"> \n",a,mesh->mNumUVComponents[a]); if (!shortened) { for (unsigned int n = 0; n < mesh->mNumVertices; ++n) { ::fprintf(out,"\t\t%0 8f %0 8f %0 8f\n", mesh->mTextureCoords[a][n].x, mesh->mTextureCoords[a][n].y, mesh->mTextureCoords[a][n].z); } } else { } ::fprintf(out,"\t\t</TextureCoords>\n"); } // vertex colors for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) { if (!mesh->mColors[a]) break; //::fprintf(out,"\t\t<Colors set=\"%i\"> \n",a); if (!shortened) { for (unsigned int n = 0; n < mesh->mNumVertices; ++n) { ::fprintf(out,"\t\t%0 8f %0 8f %0 8f %0 8f\n", mesh->mColors[a][n].r, mesh->mColors[a][n].g, mesh->mColors[a][n].b, mesh->mColors[a][n].a); } } else { } ::fprintf(out,"\t\t</Color>\n"); } ::fprintf(out,"\t</Mesh>\n"); } ::fprintf(out,"</Scene>\n</ASSIMP>"); }