/*========================================================================== * (C) 2005 Universitat Jaume I *========================================================================== * PROYECT: GAME TOOLS *==========================================================================*/ /* CONTENT: * * * @file GeoMeshSaver.cpp *==========================================================================*/ #include using namespace Geometry; using namespace std; //--------------------------------------------------------------------------- // Public: //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Constructors. //--------------------------------------------------------------------------- GeoMeshSaver::GeoMeshSaver() { leavesSubMesh = -1; leavesVB = NULL; numindices = 0; indices = NULL; } //--------------------------------------------------------------------------- // Destroyer. //--------------------------------------------------------------------------- GeoMeshSaver::~GeoMeshSaver() { delete mGeoMesh; } //--------------------------------------------------------------------------- // Saves a Mesh into a file. //--------------------------------------------------------------------------- size_t GeoMeshSaver::save(Mesh *geoMesh, const char *fileNameMesh) { size_t size; String name(fileNameMesh); SubMesh *geosubmesh; mGeoMesh = new Mesh(); // Set the mesh. *mGeoMesh = *geoMesh; // Debug. cout << endl << endl << "-----------------------------------" << endl << "\t SAVE MESH" << endl << "-----------------------------------" << endl << endl; // Unnormalize geometry model. unnormalizeModel(mGeoMesh); if ((leavesSubMesh > -1) && leavesVB) { // Gets the leaves submesh. geosubmesh = &mGeoMesh->mSubMesh[leavesSubMesh]; Vector3 bmax( geoMesh->mMeshBounds.maxX, geoMesh->mMeshBounds.maxY, geoMesh->mMeshBounds.maxZ); Vector3 bmin( geoMesh->mMeshBounds.minX, geoMesh->mMeshBounds.minY, geoMesh->mMeshBounds.minZ); Vector3 center = (bmax + bmin) * 0.5f; float scale = geoMesh->mMeshBounds.scaleFactor; geosubmesh->mSharedVertexBuffer = false; geosubmesh->mStripCount = 0; geosubmesh->mIndexCount = numindices; size_t icount = geosubmesh->mIndexCount; geosubmesh->mIndex = new Index[icount]; for (size_t i = 0; i < icount; i++) { geosubmesh->mIndex[i] = indices[i]; } geosubmesh->mType = GEO_TRIANGLE_LIST; geosubmesh->mVertexBuffer = new VertexBuffer(); geosubmesh->mVertexBuffer->mVertexInfo = VERTEX_POSITION; geosubmesh->mVertexBuffer->mVertexCount = leavesVB->mVertexCount; size_t vcount; vcount = geosubmesh->mVertexBuffer->mVertexCount; geosubmesh->mVertexBuffer->mPosition = new Vector3[vcount]; geosubmesh->mVertexBuffer->mNormal = new Vector3[vcount]; geosubmesh->mVertexBuffer->mTexCoords = new Vector2[vcount]; for (size_t i = 0; i < vcount; i++) { geosubmesh->mVertexBuffer->mPosition[i].x = leavesVB->mPosition[i].x / scale + center.x; geosubmesh->mVertexBuffer->mPosition[i].y = leavesVB->mPosition[i].y / scale + center.y; geosubmesh->mVertexBuffer->mPosition[i].z = leavesVB->mPosition[i].z / scale + center.z; geosubmesh->mVertexBuffer->mNormal[i].x = leavesVB->mNormal[i].x; geosubmesh->mVertexBuffer->mNormal[i].y = leavesVB->mNormal[i].y; geosubmesh->mVertexBuffer->mNormal[i].z = leavesVB->mNormal[i].z; geosubmesh->mVertexBuffer->mTexCoords[i].x = leavesVB->mTexCoords[i].x; geosubmesh->mVertexBuffer->mTexCoords[i].y = leavesVB->mTexCoords[i].y; } } // Open the file. mSerializer = new Serializer(name,Serializer::WRITE); // Write the file header. writeFileHeader(); // Write the mesh data. writeMesh(mGeoMesh); size = mSerializer->GetSize(); // Close the file. delete mSerializer; // Return the number of bytes written. return size; } //------------------------------------------ // Private: //------------------------------------------ // Write the main mesh. void GeoMeshSaver::writeMesh(Mesh *geoMesh) { // Header writeChunkHeader(M_MESH, (unsigned long)calcMeshSize(geoMesh)); // Debug. cout << " M_MESH" << endl; // Write the skeletally animated flag. writeBools(geoMesh->hasSkeleton,1); // Write shared geometry. if (geoMesh->mVertexBuffer->mVertexCount > 0) { writeGeometry(geoMesh->mVertexBuffer); } // Write submeshes. for (size_t i = 0; i < geoMesh->mSubMeshCount; i++) { writeSubMesh(&geoMesh->mSubMesh[i]); } // Write skeleton info if required if (geoMesh->hasSkeleton) { // Write skeleton link writeSkeletonLink(geoMesh->mSkeletonName); // Write bone assignments if (!geoMesh->mBones.empty()) { for (size_t i = 0; i< geoMesh->mBones.size(); i++) { writeMeshBoneAssignment(geoMesh->mBones[i]); } } } // Write the mesh bounds. writeMeshBounds(geoMesh); /* // Write LOD data if any if (pMesh->getNumLodLevels() > 1) { LogManager::getSingleton().logMessage("Exporting LOD information...."); writeLodInfo(pMesh); LogManager::getSingleton().logMessage("LOD information exported."); } // Write bounds information LogManager::getSingleton().logMessage("Exporting bounds information...."); writeBoundsInfo(pMesh); LogManager::getSingleton().logMessage("Bounds information exported."); // Write submesh name table LogManager::getSingleton().logMessage("Exporting submesh name table..."); writeSubMeshNameTable(pMesh); LogManager::getSingleton().logMessage("Submesh name table exported."); // Write edge lists if (pMesh->isEdgeListBuilt()) { LogManager::getSingleton().logMessage("Exporting edge lists..."); writeEdgeList(pMesh); LogManager::getSingleton().logMessage("Edge lists exported"); } */ }// End write mesh. // Write a submesh. void GeoMeshSaver::writeSubMesh(SubMesh *geoSubMesh) { bool idx32bit; size_t indexCount; //Index *index; //Index *indexBegin; //Index *indexEnd; // Header. writeChunkHeader(M_SUBMESH, (unsigned long)calcSubMeshSize(geoSubMesh)); // Debug. cout << " M_SUBMESH" << endl; // Material Name. strcat(geoSubMesh->mMaterialName,"\n"); mSerializer->WriteData(geoSubMesh->mMaterialName); // bool useSharedVertices writeBools(geoSubMesh->mSharedVertexBuffer, 1); indexCount = geoSubMesh->mIndexCount; /* // If the submesh is in triangle strips. if (geoSubMesh->mType == GEO_TRIANGLE_STRIPS) { indexCount += (2 * geoSubMesh->mStripCount) - 2; } */ // Write index count. writeInts((unsigned long)indexCount, 1); // bool indexes32Bit idx32bit = true; writeBools(idx32bit, 1); /* // If the submesh is in triangle strips. if (geoSubMesh->mType == GEO_TRIANGLE_STRIPS) { // For each one of the strips. for (int strip = 0; strip < geoSubMesh->mStripCount; strip++) { // First index of the strip. indexBegin = geoSubMesh->mStrip[strip]; // If is the final strip if (strip == (geoSubMesh->mStripCount - 1)) { // The end of the index array. indexEnd = &geoSubMesh->mIndex[geoSubMesh->mIndexCount]; } else { // The beginning of the next strip. indexEnd = geoSubMesh->mStrip[strip + 1]; } int i; i = 0; // Degenerate. if (strip != 0) { writeInts(indexBegin[i], 1); } // For each index of the strip. for (index = indexBegin; index < indexEnd; index++) { writeInts(indexBegin[i], 1); // Increments i. i++; } // Degenerate. if (strip != (geoSubMesh->mStripCount - 1)) { writeInts(indexBegin[i - 1], 1); } } } // If the submesh is in triangle list. else { */ // Write the index array. for (size_t i = 0; i < geoSubMesh->mIndexCount; i++) { writeInts(geoSubMesh->mIndex[i], 1); } //} // M_GEOMETRY stream (Optional: present only if useSharedVertices = false) if (!geoSubMesh->mSharedVertexBuffer) { writeGeometry(geoSubMesh->mVertexBuffer); } // Operation type writeSubMeshOperation(geoSubMesh); // Bone assignments. if (!geoSubMesh->mBones.empty()) { for (size_t i = 0; i < geoSubMesh->mBones.size(); i++) { writeSubMeshBoneAssignment(geoSubMesh->mBones[i]); } } } // Write the submesh operation chunk and data. void GeoMeshSaver::writeSubMeshOperation(const SubMesh *geoSubMesh) { unsigned short opType; unsigned long size; size = CHUNK_OVERHEAD_SIZE + sizeof(unsigned short); // Header writeChunkHeader(M_SUBMESH_OPERATION, size); // Debug. cout << " M_SUBMESH_OPERATION" << endl; // If the mesh is in triangle strips. if (geoSubMesh->mType == GEO_TRIANGLE_STRIPS) { opType = 5; } // If the mesh is in triangle list. else { opType = 4; } writeShorts(opType, 1); } // Write geometry. void GeoMeshSaver::writeGeometry(VertexBuffer *vertexBuffer) { unsigned short element; unsigned long size; unsigned short buffer_count; buffer_count = 3; // Calculate the size in bytes of the geometry chunk. size = (unsigned long)calcGeometrySize(vertexBuffer); // Header. // // Write the M_GEOMETRY header. writeChunkHeader(M_GEOMETRY, size); // Debug. cout << " M_GEOMETRY" << endl; writeInts((unsigned long)vertexBuffer->mVertexCount,1); // Vertex declaration. // Calculate the size of vertex declaration. size = CHUNK_OVERHEAD_SIZE + buffer_count * (CHUNK_OVERHEAD_SIZE + (sizeof(unsigned short) * 5)); // Write the vertex declaration header. writeChunkHeader( M_GEOMETRY_VERTEX_DECLARATION,size); // Debug. cout << " M_GEOMETRY_VERTEX_DECLARATION" << endl; // Obtain the size of the vertex element chunk. size = CHUNK_OVERHEAD_SIZE + (sizeof(unsigned short) * 5); // Positions. // Write the vertex element header for position. writeChunkHeader( M_GEOMETRY_VERTEX_ELEMENT,size); // Debug. cout << " M_GEOMETRY_VERTEX_ELEMENT" << endl; element = 0; writeShorts(element,1); element = VET_FLOAT3; writeShorts(element,1); element = VES_POSITION; writeShorts(element,1); element = 0; writeShorts(element,1); element = 0; writeShorts(element,1); // Normals. // Write the vertex element header for position. writeChunkHeader( M_GEOMETRY_VERTEX_ELEMENT,size); // Debug. cout << " M_GEOMETRY_VERTEX_ELEMENT" << endl; element = 1; writeShorts(element,1); element = VET_FLOAT3; writeShorts(element,1); element = VES_NORMAL; writeShorts(element,1); element = 0; writeShorts(element,1); element = 0; writeShorts(element,1); // Textures. // Write the vertex element header for position. writeChunkHeader(M_GEOMETRY_VERTEX_ELEMENT,size); // Debug. cout << " M_GEOMETRY_VERTEX_ELEMENT" << endl; element = 2; writeShorts(element,1); element = VET_FLOAT2; writeShorts(element,1); element = VES_TEXTURE_COORDINATES; writeShorts(element,1); element = 0; writeShorts(element,1); element = 0; writeShorts(element,1); // Obtain the size for vertex buffer header for positons. size = (2 * CHUNK_OVERHEAD_SIZE) + (2 * sizeof(unsigned short)); // Write the vertex buffer header for positions. writeChunkHeader(M_GEOMETRY_VERTEX_BUFFER, size); // Debug. cout << " M_GEOMETRY_VERTEX_BUFFER" << endl; element = 0; writeShorts(element,1); element = 12; writeShorts(element,1); // Obtain the size for the vertex buffer data header for positions. size = CHUNK_OVERHEAD_SIZE + ((sizeof(float) * 3) * vertexBuffer->mVertexCount); // Write the vertex buffer data header for positions. writeChunkHeader(M_GEOMETRY_VERTEX_BUFFER_DATA, size); // Debug. cout << " M_GEOMETRY_VERTEX_BUFFER_DATA" << endl; // Write all the positions coords. mSerializer->WriteArray(vertexBuffer->mPosition, vertexBuffer->mVertexCount); // Obtain the size for vertex buffer header for normals. size = (2 * CHUNK_OVERHEAD_SIZE) + (2 * sizeof(unsigned short)); // Write the vertex buffer header. writeChunkHeader( M_GEOMETRY_VERTEX_BUFFER, size); // Debug. cout << " M_GEOMETRY_VERTEX_BUFFER" << endl; element = 1; writeShorts(element,1); element = 12; writeShorts(element,1); // Obtain the size for the vertex buffer data header for normals. size = CHUNK_OVERHEAD_SIZE + ((sizeof(float) * 3) * vertexBuffer->mVertexCount); // Write the vertex buffer data header for normals. writeChunkHeader(M_GEOMETRY_VERTEX_BUFFER_DATA, size); // Debug. cout << " M_GEOMETRY_VERTEX_BUFFER_DATA" << endl; // Write all the normals coords. mSerializer->WriteArray(vertexBuffer->mNormal, vertexBuffer->mVertexCount); // Obtain the size for vertex buffer header for textures. size = (2 * CHUNK_OVERHEAD_SIZE) + (2 * sizeof(unsigned short)); // Write the vertex buffer header for textures. writeChunkHeader(M_GEOMETRY_VERTEX_BUFFER, size); // Debug. cout << " M_GEOMETRY_VERTEX_BUFFER" << endl; element = 2; writeShorts(element,1); element = 8; writeShorts(element,1); // Obtain the size for the vertex buffer data header for textures. size = CHUNK_OVERHEAD_SIZE + ((sizeof(float) * 2) * vertexBuffer->mVertexCount); // Write the vertex buffer data header for textures. writeChunkHeader(M_GEOMETRY_VERTEX_BUFFER_DATA, size); // Debug. cout << " M_GEOMETRY_VERTEX_BUFFER_DATA" << endl; // Write all the texture coords. mSerializer->WriteArray(vertexBuffer->mTexCoords, vertexBuffer->mVertexCount); } // Write Mesh Bounds. void GeoMeshSaver::writeMeshBounds(Mesh *geoMesh) { size_t size; size = CHUNK_OVERHEAD_SIZE + (sizeof(float) * 7); writeChunkHeader(M_MESH_BOUNDS, (unsigned long)size); // Debug. cout << " M_MESH_BOUNDS" << endl; writeFloats(geoMesh->mMeshBounds.minX,1); writeFloats(geoMesh->mMeshBounds.minY,1); writeFloats(geoMesh->mMeshBounds.minZ,1); writeFloats(geoMesh->mMeshBounds.maxX,1); writeFloats(geoMesh->mMeshBounds.maxY,1); writeFloats(geoMesh->mMeshBounds.maxZ,1); writeFloats(geoMesh->mMeshBounds.radius,1); } // Calculate the mesh size in bytes. size_t GeoMeshSaver::calcMeshSize(const Mesh *geoMesh) { size_t size = CHUNK_OVERHEAD_SIZE; // Number of shared vertices size += sizeof(uint32); // Geometry if (geoMesh->mVertexBuffer->mVertexCount > 0) { size += calcGeometrySize(geoMesh->mVertexBuffer); } // Submeshes for (unsigned short i = 0; i < geoMesh->mSubMeshCount; ++i) { size += calcSubMeshSize(&geoMesh->mSubMesh[i]); } // Mesh Bounds size added. size += CHUNK_OVERHEAD_SIZE + (sizeof(float) * 7); // Skeleton link if (geoMesh->hasSkeleton) { size += calcSkeletonLinkSize(geoMesh); } /* // Submesh name table size += calcSubMeshNameTableSize(geoMesh); // Edge list if (geoMesh->isEdgeListBuilt()) { size += calcEdgeListSize(geoMesh); } */ return size; } // Calc the size in bytes for the submesh. size_t GeoMeshSaver::calcSubMeshSize(const SubMesh *geoSubMesh) { size_t size = CHUNK_OVERHEAD_SIZE; // Material name size += strlen(geoSubMesh->mMaterialName); // bool useSharedVertices size += sizeof(bool); // unsigned int indexCount size += sizeof(unsigned int); // bool indexes32bit size += sizeof(bool); // unsigned int* faceVertexIndices size += sizeof(unsigned int) * geoSubMesh->mIndexCount; // Geometry if (!geoSubMesh->mSharedVertexBuffer) { size += calcGeometrySize(geoSubMesh->mVertexBuffer); } return size; } // Calculate the geometry size in bytes. size_t GeoMeshSaver::calcGeometrySize(const VertexBuffer* vertexBuffer) { unsigned long size; unsigned long buffer_count; // and another for normals. buffer_count = 3; // Calculate the size of the Geometry chunk. size = CHUNK_OVERHEAD_SIZE + sizeof(unsigned int); size = size + CHUNK_OVERHEAD_SIZE + buffer_count * (CHUNK_OVERHEAD_SIZE + (sizeof(unsigned short) * 5)); size = size + buffer_count * ( (CHUNK_OVERHEAD_SIZE * 2) + (sizeof(unsigned short) * 2) + vertexBuffer->mVertexCount ); return size; } // Calculate the skeleton link size in bytes. size_t GeoMeshSaver::calcSkeletonLinkSize(const Mesh *geoMesh) { size_t size = CHUNK_OVERHEAD_SIZE; size += strlen(geoMesh->mSkeletonName); // Debug. //cout << "Length Skeleton Link: " // << strlen(geoMesh->mSkeletonName) // << endl; return size; } // Write the file header. void GeoMeshSaver::writeFileHeader(void) { String mesh_version("[MeshSerializer_v1.30]\n"); writeShorts(M_HEADER, 1); writeString(mesh_version); } // Write a header chunk given. void GeoMeshSaver::writeChunkHeader(unsigned short id, unsigned long size) { mSerializer->WriteData(&id,sizeof(unsigned short),1); mSerializer->WriteData(&size,sizeof(unsigned long),1); } // Write integers into the file. void GeoMeshSaver::writeInts(unsigned long id, unsigned long count) { mSerializer->WriteData(&id,sizeof(id),count); } // Write shorts into the file void GeoMeshSaver::writeShorts(unsigned short id, unsigned long count) { mSerializer->WriteData(&id,sizeof(id),count); } // Write float into the file. void GeoMeshSaver::writeFloats(float id, unsigned long count) { mSerializer->WriteData(&id,sizeof(id),count); } // Write a string into the file. void GeoMeshSaver::writeString(const String &string) { mSerializer->WriteData(string); } // Write booleans into the file. void GeoMeshSaver::writeBools( const bool id, unsigned long count) { mSerializer->WriteData(&id,sizeof(bool),count); } void GeoMeshSaver::writeSkeletonLink(const String& skelName) { writeChunkHeader( M_MESH_SKELETON_LINK, (unsigned long)calcSkeletonLinkSize(mGeoMesh)); // Debug. cout << " M_MESH_SKELETON_LINK" << endl; writeString(skelName); } void GeoMeshSaver::writeMeshBoneAssignment(const VertexBoneAssignment& assign) { size_t size = CHUNK_OVERHEAD_SIZE + sizeof(unsigned int) + sizeof(unsigned short)+ sizeof(float); writeChunkHeader(M_MESH_BONE_ASSIGNMENT, (unsigned long)size); // unsigned int vertexIndex; writeInts(assign.vertexIndex, 1); // unsigned short boneIndex; writeShorts(assign.boneIndex, 1); // float weight; writeFloats(assign.weight, 1); } void GeoMeshSaver::writeSubMeshBoneAssignment(const VertexBoneAssignment& assign) { size_t size = CHUNK_OVERHEAD_SIZE + sizeof(unsigned int) + sizeof(unsigned short)+ sizeof(float); writeChunkHeader(M_SUBMESH_BONE_ASSIGNMENT, (unsigned long)size); // unsigned int vertexIndex; writeInts(assign.vertexIndex, 1); // unsigned short boneIndex; writeShorts(assign.boneIndex, 1); // float weight; writeFloats(assign.weight, 1); } // unnormalize geometry model. void GeoMeshSaver::unnormalizeModel(Mesh *geoMesh) { float maxx; float maxy; float maxz; float minx; float miny; float minz; float cx; float cy; float cz; float scale; VertexBuffer *vertex_buffer; maxx = geoMesh->mMeshBounds.maxX; maxy = geoMesh->mMeshBounds.maxY; maxz = geoMesh->mMeshBounds.maxZ; minx = geoMesh->mMeshBounds.minX; miny = geoMesh->mMeshBounds.minY; minz = geoMesh->mMeshBounds.minZ; scale = geoMesh->mMeshBounds.scaleFactor; // Calculate center of the model. cx = (maxx + minx) / 2.0f; cy = (maxy + miny) / 2.0f; cz = (maxz + minz) / 2.0f; // Translate around center then scale. // For each submesh. bool sharedScaled = false; for (size_t submesh = 0; submesh < geoMesh->mSubMeshCount; submesh++) { // Gets the actual submesh. vertex_buffer = geoMesh->mSubMesh[submesh].mVertexBuffer; if (geoMesh->mSubMesh[submesh].mSharedVertexBuffer && sharedScaled) { continue; } // For each index of the strip. for (size_t i = 0; i < vertex_buffer->mVertexCount; i++) { vertex_buffer->mPosition[i].x /= scale; vertex_buffer->mPosition[i].y /= scale; vertex_buffer->mPosition[i].z /= scale; vertex_buffer->mPosition[i].x += cx; vertex_buffer->mPosition[i].y += cy; vertex_buffer->mPosition[i].z += cz; } // If is a shared vertex Buffer. if (geoMesh->mSubMesh[submesh].mSharedVertexBuffer) { sharedScaled = true; } } }