/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2006 Torus Knot Software Ltd Also see acknowledgements in Readme.html This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt. You may alternatively use this source under the terms of a specific version of the OGRE Unrestricted License provided you have obtained such a license from Torus Knot Software Ltd. ----------------------------------------------------------------------------- */ #include "OgreStableHeaders.h" #include "OgreMeshSerializerImpl.h" #include "OgreMeshFileFormat.h" #include "OgreMesh.h" #include "OgreSubMesh.h" #include "OgreException.h" #include "OgreLogManager.h" #include "OgreSkeleton.h" #include "OgreHardwareBufferManager.h" #include "OgreMaterial.h" #include "OgreTechnique.h" #include "OgrePass.h" #include "OgreAnimation.h" #include "OgreAnimationTrack.h" #include "OgreKeyFrame.h" #include "OgreRoot.h" #if OGRE_COMPILER == OGRE_COMPILER_MSVC // Disable conversion warnings, we do a lot of them, intentionally # pragma warning (disable : 4267) #endif namespace Ogre { /// stream overhead = ID + size const long STREAM_OVERHEAD_SIZE = sizeof(uint16) + sizeof(uint32); //--------------------------------------------------------------------- MeshSerializerImpl::MeshSerializerImpl() { // Version number mVersion = "[MeshSerializer_v1.40]"; } //--------------------------------------------------------------------- MeshSerializerImpl::~MeshSerializerImpl() { } //--------------------------------------------------------------------- void MeshSerializerImpl::exportMesh(const Mesh* pMesh, const String& filename, Endian endianMode) { LogManager::getSingleton().logMessage("MeshSerializer writing mesh data to " + filename + "..."); // Decide on endian mode determineEndianness(endianMode); // Check that the mesh has it's bounds set if (pMesh->getBounds().isNull() || pMesh->getBoundingSphereRadius() == 0.0f) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "The Mesh you have supplied does not have its" " bounds completely defined. Define them first before exporting.", "MeshSerializerImpl::exportMesh"); } mpfFile = fopen(filename.c_str(), "wb"); if (!mpfFile) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unable to open file " + filename + " for writing", "MeshSerializerImpl::exportMesh"); } writeFileHeader(); LogManager::getSingleton().logMessage("File header written."); LogManager::getSingleton().logMessage("Writing mesh data..."); writeMesh(pMesh); LogManager::getSingleton().logMessage("Mesh data exported."); fclose(mpfFile); LogManager::getSingleton().logMessage("MeshSerializer export successful."); } //--------------------------------------------------------------------- void MeshSerializerImpl::importMesh(DataStreamPtr& stream, Mesh* pMesh) { // Determine endianness (must be the first thing we do!) determineEndianness(stream); // Check header readFileHeader(stream); unsigned short streamID; while(!stream->eof()) { streamID = readChunk(stream); switch (streamID) { case M_MESH: readMesh(stream, pMesh); break; } } } //--------------------------------------------------------------------- void MeshSerializerImpl::writeMesh(const Mesh* pMesh) { // Header writeChunkHeader(M_MESH, calcMeshSize(pMesh)); // bool skeletallyAnimated bool skelAnim = pMesh->hasSkeleton(); writeBools(&skelAnim, 1); // Write shared geometry if (pMesh->sharedVertexData) writeGeometry(pMesh->sharedVertexData); // Write Submeshes for (int i = 0; i < pMesh->getNumSubMeshes(); ++i) { LogManager::getSingleton().logMessage("Writing submesh..."); writeSubMesh(pMesh->getSubMesh(i)); LogManager::getSingleton().logMessage("Submesh exported."); } // Write skeleton info if required if (pMesh->hasSkeleton()) { LogManager::getSingleton().logMessage("Exporting skeleton link..."); // Write skeleton link writeSkeletonLink(pMesh->getSkeletonName()); LogManager::getSingleton().logMessage("Skeleton link exported."); // Write bone assignments if (!pMesh->mBoneAssignments.empty()) { LogManager::getSingleton().logMessage("Exporting shared geometry bone assignments..."); Mesh::VertexBoneAssignmentList::const_iterator vi; for (vi = pMesh->mBoneAssignments.begin(); vi != pMesh->mBoneAssignments.end(); ++vi) { writeMeshBoneAssignment(vi->second); } LogManager::getSingleton().logMessage("Shared geometry bone assignments exported."); } } // 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"); } // Write morph animation writePoses(pMesh); if (pMesh->hasVertexAnimation()) { writeAnimations(pMesh); } // Write submesh extremes writeExtremes(pMesh); } //--------------------------------------------------------------------- // Added by DrEvil void MeshSerializerImpl::writeSubMeshNameTable(const Mesh* pMesh) { // Header writeChunkHeader(M_SUBMESH_NAME_TABLE, calcSubMeshNameTableSize(pMesh)); // Loop through and save out the index and names. Mesh::SubMeshNameMap::const_iterator it = pMesh->mSubMeshNameMap.begin(); while(it != pMesh->mSubMeshNameMap.end()) { // Header writeChunkHeader(M_SUBMESH_NAME_TABLE_ELEMENT, STREAM_OVERHEAD_SIZE + sizeof(unsigned short) + (unsigned long)it->first.length() + 1); // write the index writeShorts(&it->second, 1); // name writeString(it->first); ++it; } } //--------------------------------------------------------------------- void MeshSerializerImpl::writeSubMesh(const SubMesh* s) { // Header writeChunkHeader(M_SUBMESH, calcSubMeshSize(s)); // char* materialName writeString(s->getMaterialName()); // bool useSharedVertices writeBools(&s->useSharedVertices, 1); unsigned int indexCount = s->indexData->indexCount; writeInts(&indexCount, 1); // bool indexes32Bit bool idx32bit = (s->indexData->indexBuffer->getType() == HardwareIndexBuffer::IT_32BIT); writeBools(&idx32bit, 1); // unsigned short* faceVertexIndices ((indexCount) HardwareIndexBufferSharedPtr ibuf = s->indexData->indexBuffer; void* pIdx = ibuf->lock(HardwareBuffer::HBL_READ_ONLY); if (idx32bit) { unsigned int* pIdx32 = static_cast(pIdx); writeInts(pIdx32, s->indexData->indexCount); } else { unsigned short* pIdx16 = static_cast(pIdx); writeShorts(pIdx16, s->indexData->indexCount); } ibuf->unlock(); // M_GEOMETRY stream (Optional: present only if useSharedVertices = false) if (!s->useSharedVertices) { writeGeometry(s->vertexData); } // end of sub mesh chunk // write out texture alias chunks writeSubMeshTextureAliases(s); // Operation type writeSubMeshOperation(s); // Bone assignments if (!s->mBoneAssignments.empty()) { LogManager::getSingleton().logMessage("Exporting dedicated geometry bone assignments..."); SubMesh::VertexBoneAssignmentList::const_iterator vi; for (vi = s->mBoneAssignments.begin(); vi != s->mBoneAssignments.end(); ++vi) { writeSubMeshBoneAssignment(vi->second); } LogManager::getSingleton().logMessage("Dedicated geometry bone assignments exported."); } } //--------------------------------------------------------------------- void MeshSerializerImpl::writeExtremes(const Mesh *pMesh) { bool has_extremes = false; for (int i = 0; i < pMesh->getNumSubMeshes(); ++i) { SubMesh *sm = pMesh->getSubMesh(i); if (sm->extremityPoints.empty()) continue; if (!has_extremes) { has_extremes = true; LogManager::getSingleton().logMessage("Writing submesh extremes..."); } writeSubMeshExtremes(i, sm); } if (has_extremes) LogManager::getSingleton().logMessage("Extremes exported."); } //--------------------------------------------------------------------- void MeshSerializerImpl::writeSubMeshExtremes(unsigned short idx, const SubMesh* s) { size_t chunkSize = STREAM_OVERHEAD_SIZE + sizeof (unsigned short) + s->extremityPoints.size () * sizeof (float) * 3; writeChunkHeader(M_TABLE_EXTREMES, chunkSize); writeShorts(&idx, 1); float *vertices = new float [s->extremityPoints.size() * 3]; float *pVert = vertices; for (std::vector::const_iterator i = s->extremityPoints.begin(); i != s->extremityPoints.end(); ++i) { *pVert++ = i->x; *pVert++ = i->y; *pVert++ = i->z; } writeFloats(vertices, s->extremityPoints.size () * 3); delete [] vertices; } //--------------------------------------------------------------------- void MeshSerializerImpl::writeSubMeshTextureAliases(const SubMesh* s) { size_t chunkSize; AliasTextureNamePairList::const_iterator i; LogManager::getSingleton().logMessage("Exporting submesh texture aliases..."); // iterate through texture aliases and write them out as a chunk for (i = s->mTextureAliases.begin(); i != s->mTextureAliases.end(); ++i) { // calculate chunk size based on string length + 1. Add 1 for the line feed. chunkSize = STREAM_OVERHEAD_SIZE + i->first.length() + i->second.length() + 2; writeChunkHeader(M_SUBMESH_TEXTURE_ALIAS, chunkSize); // write out alias name writeString(i->first); // write out texture name writeString(i->second); } LogManager::getSingleton().logMessage("Submesh texture aliases exported."); } //--------------------------------------------------------------------- void MeshSerializerImpl::writeSubMeshOperation(const SubMesh* sm) { // Header writeChunkHeader(M_SUBMESH_OPERATION, calcSubMeshOperationSize(sm)); // unsigned short operationType unsigned short opType = static_cast(sm->operationType); writeShorts(&opType, 1); } //--------------------------------------------------------------------- void MeshSerializerImpl::writeGeometry(const VertexData* vertexData) { // calc size const VertexDeclaration::VertexElementList& elemList = vertexData->vertexDeclaration->getElements(); const VertexBufferBinding::VertexBufferBindingMap& bindings = vertexData->vertexBufferBinding->getBindings(); VertexBufferBinding::VertexBufferBindingMap::const_iterator vbi, vbiend; size_t size = STREAM_OVERHEAD_SIZE + sizeof(unsigned int) + // base (STREAM_OVERHEAD_SIZE + elemList.size() * (STREAM_OVERHEAD_SIZE + sizeof(unsigned short) * 5)); // elements vbiend = bindings.end(); for (vbi = bindings.begin(); vbi != vbiend; ++vbi) { const HardwareVertexBufferSharedPtr& vbuf = vbi->second; size += (STREAM_OVERHEAD_SIZE * 2) + (sizeof(unsigned short) * 2) + vbuf->getSizeInBytes(); } // Header writeChunkHeader(M_GEOMETRY, size); unsigned int vertexCount = vertexData->vertexCount; writeInts(&vertexCount, 1); // Vertex declaration size = STREAM_OVERHEAD_SIZE + elemList.size() * (STREAM_OVERHEAD_SIZE + sizeof(unsigned short) * 5); writeChunkHeader(M_GEOMETRY_VERTEX_DECLARATION, size); VertexDeclaration::VertexElementList::const_iterator vei, veiend; veiend = elemList.end(); unsigned short tmp; size = STREAM_OVERHEAD_SIZE + sizeof(unsigned short) * 5; for (vei = elemList.begin(); vei != veiend; ++vei) { const VertexElement& elem = *vei; writeChunkHeader(M_GEOMETRY_VERTEX_ELEMENT, size); // unsigned short source; // buffer bind source tmp = elem.getSource(); writeShorts(&tmp, 1); // unsigned short type; // VertexElementType tmp = static_cast(elem.getType()); writeShorts(&tmp, 1); // unsigned short semantic; // VertexElementSemantic tmp = static_cast(elem.getSemantic()); writeShorts(&tmp, 1); // unsigned short offset; // start offset in buffer in bytes tmp = static_cast(elem.getOffset()); writeShorts(&tmp, 1); // unsigned short index; // index of the semantic (for colours and texture coords) tmp = elem.getIndex(); writeShorts(&tmp, 1); } // Buffers and bindings vbiend = bindings.end(); for (vbi = bindings.begin(); vbi != vbiend; ++vbi) { const HardwareVertexBufferSharedPtr& vbuf = vbi->second; size = (STREAM_OVERHEAD_SIZE * 2) + (sizeof(unsigned short) * 2) + vbuf->getSizeInBytes(); writeChunkHeader(M_GEOMETRY_VERTEX_BUFFER, size); // unsigned short bindIndex; // Index to bind this buffer to tmp = vbi->first; writeShorts(&tmp, 1); // unsigned short vertexSize; // Per-vertex size, must agree with declaration at this index tmp = (unsigned short)vbuf->getVertexSize(); writeShorts(&tmp, 1); // Data size = STREAM_OVERHEAD_SIZE + vbuf->getSizeInBytes(); writeChunkHeader(M_GEOMETRY_VERTEX_BUFFER_DATA, size); void* pBuf = vbuf->lock(HardwareBuffer::HBL_READ_ONLY); if (mFlipEndian) { // endian conversion // Copy data unsigned char* tempData = new unsigned char[vbuf->getSizeInBytes()]; memcpy(tempData, pBuf, vbuf->getSizeInBytes()); flipToLittleEndian( tempData, vertexData->vertexCount, vbuf->getVertexSize(), vertexData->vertexDeclaration->findElementsBySource(vbi->first)); writeData(tempData, vbuf->getVertexSize(), vertexData->vertexCount); delete [] tempData; } else { writeData(pBuf, vbuf->getVertexSize(), vertexData->vertexCount); } vbuf->unlock(); } } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcSubMeshNameTableSize(const Mesh* pMesh) { size_t size = STREAM_OVERHEAD_SIZE; // Figure out the size of the Name table. // Iterate through the subMeshList & add up the size of the indexes and names. Mesh::SubMeshNameMap::const_iterator it = pMesh->mSubMeshNameMap.begin(); while(it != pMesh->mSubMeshNameMap.end()) { // size of the index + header size for each element chunk size += STREAM_OVERHEAD_SIZE + sizeof(uint16); // name size += it->first.length() + 1; ++it; } // size of the sub-mesh name table. return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcMeshSize(const Mesh* pMesh) { size_t size = STREAM_OVERHEAD_SIZE; // Num shared vertices size += sizeof(uint32); // Geometry if (pMesh->sharedVertexData && pMesh->sharedVertexData->vertexCount > 0) { size += calcGeometrySize(pMesh->sharedVertexData); } // Submeshes for (unsigned short i = 0; i < pMesh->getNumSubMeshes(); ++i) { size += calcSubMeshSize(pMesh->getSubMesh(i)); } // Skeleton link if (pMesh->hasSkeleton()) { size += calcSkeletonLinkSize(pMesh->getSkeletonName()); } // Submesh name table size += calcSubMeshNameTableSize(pMesh); // Edge list if (pMesh->isEdgeListBuilt()) { size += calcEdgeListSize(pMesh); } // Animations for (unsigned short a = 0; a < pMesh->getNumAnimations(); ++a) { Animation* anim = pMesh->getAnimation(a); size += calcAnimationSize(anim); } return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcSubMeshSize(const SubMesh* pSub) { size_t size = STREAM_OVERHEAD_SIZE; // Material name size += pSub->getMaterialName().length() + 1; // bool useSharedVertices size += sizeof(bool); // unsigned int indexCount size += sizeof(unsigned int); // bool indexes32bit size += sizeof(bool); // unsigned int* faceVertexIndices size += sizeof(unsigned int) * pSub->indexData->indexCount; // Geometry if (!pSub->useSharedVertices) { size += calcGeometrySize(pSub->vertexData); } size += calcSubMeshTextureAliasesSize(pSub); size += calcSubMeshOperationSize(pSub); // Bone assignments if (!pSub->mBoneAssignments.empty()) { SubMesh::VertexBoneAssignmentList::const_iterator vi; for (vi = pSub->mBoneAssignments.begin(); vi != pSub->mBoneAssignments.end(); ++vi) { size += calcBoneAssignmentSize(); } } return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcSubMeshOperationSize(const SubMesh* pSub) { return STREAM_OVERHEAD_SIZE + sizeof(uint16); } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcSubMeshTextureAliasesSize(const SubMesh* pSub) { size_t chunkSize = 0; AliasTextureNamePairList::const_iterator i; // iterate through texture alias map and calc size of strings for (i = pSub->mTextureAliases.begin(); i != pSub->mTextureAliases.end(); ++i) { // calculate chunk size based on string length + 1. Add 1 for the line feed. chunkSize += STREAM_OVERHEAD_SIZE + i->first.length() + i->second.length() + 2; } return chunkSize; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcGeometrySize(const VertexData* vertexData) { size_t size = STREAM_OVERHEAD_SIZE; // Num vertices size += sizeof(unsigned int); const VertexDeclaration::VertexElementList& elems = vertexData->vertexDeclaration->getElements(); VertexDeclaration::VertexElementList::const_iterator i, iend; iend = elems.end(); for (i = elems.begin(); i != iend; ++i) { const VertexElement& elem = *i; // Vertex element size += VertexElement::getTypeSize(elem.getType()) * vertexData->vertexCount; } return size; } //--------------------------------------------------------------------- void MeshSerializerImpl::readGeometry(DataStreamPtr& stream, Mesh* pMesh, VertexData* dest) { dest->vertexStart = 0; unsigned int vertexCount = 0; readInts(stream, &vertexCount, 1); dest->vertexCount = vertexCount; // Find optional geometry streams if (!stream->eof()) { unsigned short streamID = readChunk(stream); while(!stream->eof() && (streamID == M_GEOMETRY_VERTEX_DECLARATION || streamID == M_GEOMETRY_VERTEX_BUFFER )) { switch (streamID) { case M_GEOMETRY_VERTEX_DECLARATION: readGeometryVertexDeclaration(stream, pMesh, dest); break; case M_GEOMETRY_VERTEX_BUFFER: readGeometryVertexBuffer(stream, pMesh, dest); break; } // Get next stream if (!stream->eof()) { streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of non-submesh stream stream->skip(-STREAM_OVERHEAD_SIZE); } } // Perform any necessary colour conversion for an active rendersystem if (Root::getSingletonPtr() && Root::getSingleton().getRenderSystem()) { // We don't know the source type if it's VET_COLOUR, but assume ARGB // since that's the most common. Won't get used unless the mesh is // ambiguous anyway, which will have been warned about in the log dest->convertPackedColour(VET_COLOUR_ARGB, VertexElement::getBestColourVertexElementType()); } } //--------------------------------------------------------------------- void MeshSerializerImpl::readGeometryVertexDeclaration(DataStreamPtr& stream, Mesh* pMesh, VertexData* dest) { // Find optional geometry streams if (!stream->eof()) { unsigned short streamID = readChunk(stream); while(!stream->eof() && (streamID == M_GEOMETRY_VERTEX_ELEMENT )) { switch (streamID) { case M_GEOMETRY_VERTEX_ELEMENT: readGeometryVertexElement(stream, pMesh, dest); break; } // Get next stream if (!stream->eof()) { streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of non-submesh stream stream->skip(-STREAM_OVERHEAD_SIZE); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::readGeometryVertexElement(DataStreamPtr& stream, Mesh* pMesh, VertexData* dest) { unsigned short source, offset, index, tmp; VertexElementType vType; VertexElementSemantic vSemantic; // unsigned short source; // buffer bind source readShorts(stream, &source, 1); // unsigned short type; // VertexElementType readShorts(stream, &tmp, 1); vType = static_cast(tmp); // unsigned short semantic; // VertexElementSemantic readShorts(stream, &tmp, 1); vSemantic = static_cast(tmp); // unsigned short offset; // start offset in buffer in bytes readShorts(stream, &offset, 1); // unsigned short index; // index of the semantic readShorts(stream, &index, 1); dest->vertexDeclaration->addElement(source, offset, vType, vSemantic, index); if (vType == VET_COLOUR) { StringUtil::StrStreamType s; s << "Warning: VET_COLOUR element type is deprecated, you should use " << "one of the more specific types to indicate the byte order. " << "Use OgreMeshUpgrade on " << pMesh->getName() << " as soon as possible. "; LogManager::getSingleton().logMessage(s.str()); } } //--------------------------------------------------------------------- void MeshSerializerImpl::readGeometryVertexBuffer(DataStreamPtr& stream, Mesh* pMesh, VertexData* dest) { unsigned short bindIndex, vertexSize; // unsigned short bindIndex; // Index to bind this buffer to readShorts(stream, &bindIndex, 1); // unsigned short vertexSize; // Per-vertex size, must agree with declaration at this index readShorts(stream, &vertexSize, 1); // Check for vertex data header unsigned short headerID; headerID = readChunk(stream); if (headerID != M_GEOMETRY_VERTEX_BUFFER_DATA) { OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Can't find vertex buffer data area", "MeshSerializerImpl::readGeometryVertexBuffer"); } // Check that vertex size agrees if (dest->vertexDeclaration->getVertexSize(bindIndex) != vertexSize) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Buffer vertex size does not agree with vertex declaration", "MeshSerializerImpl::readGeometryVertexBuffer"); } // Create / populate vertex buffer HardwareVertexBufferSharedPtr vbuf; vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( vertexSize, dest->vertexCount, pMesh->mVertexBufferUsage, pMesh->mVertexBufferShadowBuffer); void* pBuf = vbuf->lock(HardwareBuffer::HBL_DISCARD); stream->read(pBuf, dest->vertexCount * vertexSize); // endian conversion for OSX flipFromLittleEndian( pBuf, dest->vertexCount, vertexSize, dest->vertexDeclaration->findElementsBySource(bindIndex)); vbuf->unlock(); // Set binding dest->vertexBufferBinding->setBinding(bindIndex, vbuf); } //--------------------------------------------------------------------- void MeshSerializerImpl::readSubMeshNameTable(DataStreamPtr& stream, Mesh* pMesh) { // The map for std::map subMeshNames; unsigned short streamID, subMeshIndex; // Need something to store the index, and the objects name // This table is a method that imported meshes can retain their naming // so that the names established in the modelling software can be used // to get the sub-meshes by name. The exporter must support exporting // the optional stream M_SUBMESH_NAME_TABLE. // Read in all the sub-streams. Each sub-stream should contain an index and Ogre::String for the name. if (!stream->eof()) { streamID = readChunk(stream); while(!stream->eof() && (streamID == M_SUBMESH_NAME_TABLE_ELEMENT )) { // Read in the index of the submesh. readShorts(stream, &subMeshIndex, 1); // Read in the String and map it to its index. subMeshNames[subMeshIndex] = readString(stream); // If we're not end of file get the next stream ID if (!stream->eof()) streamID = readChunk(stream); } if (!stream->eof()) { // Backpedal back to start of stream stream->skip(-STREAM_OVERHEAD_SIZE); } } // Set all the submeshes names // ? // Loop through and save out the index and names. std::map::const_iterator it = subMeshNames.begin(); while(it != subMeshNames.end()) { // Name this submesh to the stored name. pMesh->nameSubMesh(it->second, it->first); ++it; } } //--------------------------------------------------------------------- void MeshSerializerImpl::readMesh(DataStreamPtr& stream, Mesh* pMesh) { unsigned short streamID; // Never automatically build edge lists for this version // expect them in the file or not at all pMesh->mAutoBuildEdgeLists = false; // bool skeletallyAnimated bool skeletallyAnimated; readBools(stream, &skeletallyAnimated, 1); // Find all substreams if (!stream->eof()) { streamID = readChunk(stream); while(!stream->eof() && (streamID == M_GEOMETRY || streamID == M_SUBMESH || streamID == M_MESH_SKELETON_LINK || streamID == M_MESH_BONE_ASSIGNMENT || streamID == M_MESH_LOD || streamID == M_MESH_BOUNDS || streamID == M_SUBMESH_NAME_TABLE || streamID == M_EDGE_LISTS || streamID == M_POSES || streamID == M_ANIMATIONS || streamID == M_TABLE_EXTREMES)) { switch(streamID) { case M_GEOMETRY: pMesh->sharedVertexData = new VertexData(); try { readGeometry(stream, pMesh, pMesh->sharedVertexData); } catch (Exception& e) { if (e.getNumber() == Exception::ERR_ITEM_NOT_FOUND) { // duff geometry data entry with 0 vertices delete pMesh->sharedVertexData; pMesh->sharedVertexData = 0; // Skip this stream (pointer will have been returned to just after header) stream->skip(mCurrentstreamLen - STREAM_OVERHEAD_SIZE); } else { throw; } } break; case M_SUBMESH: readSubMesh(stream, pMesh); break; case M_MESH_SKELETON_LINK: readSkeletonLink(stream, pMesh); break; case M_MESH_BONE_ASSIGNMENT: readMeshBoneAssignment(stream, pMesh); break; case M_MESH_LOD: readMeshLodInfo(stream, pMesh); break; case M_MESH_BOUNDS: readBoundsInfo(stream, pMesh); break; case M_SUBMESH_NAME_TABLE: readSubMeshNameTable(stream, pMesh); break; case M_EDGE_LISTS: readEdgeList(stream, pMesh); break; case M_POSES: readPoses(stream, pMesh); break; case M_ANIMATIONS: readAnimations(stream, pMesh); break; case M_TABLE_EXTREMES: readExtremes(stream, pMesh); break; } if (!stream->eof()) { streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of stream stream->skip(-STREAM_OVERHEAD_SIZE); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::readSubMesh(DataStreamPtr& stream, Mesh* pMesh) { unsigned short streamID; SubMesh* sm = pMesh->createSubMesh(); // char* materialName String materialName = readString(stream); sm->setMaterialName(materialName); // bool useSharedVertices readBools(stream,&sm->useSharedVertices, 1); sm->indexData->indexStart = 0; unsigned int indexCount = 0; readInts(stream, &indexCount, 1); sm->indexData->indexCount = indexCount; HardwareIndexBufferSharedPtr ibuf; // bool indexes32Bit bool idx32bit; readBools(stream, &idx32bit, 1); if (idx32bit) { ibuf = HardwareBufferManager::getSingleton(). createIndexBuffer( HardwareIndexBuffer::IT_32BIT, sm->indexData->indexCount, pMesh->mIndexBufferUsage, pMesh->mIndexBufferShadowBuffer); // unsigned int* faceVertexIndices unsigned int* pIdx = static_cast( ibuf->lock(HardwareBuffer::HBL_DISCARD) ); readInts(stream, pIdx, sm->indexData->indexCount); ibuf->unlock(); } else // 16-bit { ibuf = HardwareBufferManager::getSingleton(). createIndexBuffer( HardwareIndexBuffer::IT_16BIT, sm->indexData->indexCount, pMesh->mIndexBufferUsage, pMesh->mIndexBufferShadowBuffer); // unsigned short* faceVertexIndices unsigned short* pIdx = static_cast( ibuf->lock(HardwareBuffer::HBL_DISCARD) ); readShorts(stream, pIdx, sm->indexData->indexCount); ibuf->unlock(); } sm->indexData->indexBuffer = ibuf; // M_GEOMETRY stream (Optional: present only if useSharedVertices = false) if (!sm->useSharedVertices) { streamID = readChunk(stream); if (streamID != M_GEOMETRY) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Missing geometry data in mesh file", "MeshSerializerImpl::readSubMesh"); } sm->vertexData = new VertexData(); readGeometry(stream, pMesh, sm->vertexData); } // Find all bone assignments, submesh operation, and texture aliases (if present) if (!stream->eof()) { streamID = readChunk(stream); while(!stream->eof() && (streamID == M_SUBMESH_BONE_ASSIGNMENT || streamID == M_SUBMESH_OPERATION || streamID == M_SUBMESH_TEXTURE_ALIAS)) { switch(streamID) { case M_SUBMESH_OPERATION: readSubMeshOperation(stream, pMesh, sm); break; case M_SUBMESH_BONE_ASSIGNMENT: readSubMeshBoneAssignment(stream, pMesh, sm); break; case M_SUBMESH_TEXTURE_ALIAS: readSubMeshTextureAlias(stream, pMesh, sm); break; } if (!stream->eof()) { streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of stream stream->skip(-STREAM_OVERHEAD_SIZE); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::readSubMeshOperation(DataStreamPtr& stream, Mesh* pMesh, SubMesh* sm) { // unsigned short operationType unsigned short opType; readShorts(stream, &opType, 1); sm->operationType = static_cast(opType); } //--------------------------------------------------------------------- void MeshSerializerImpl::readSubMeshTextureAlias(DataStreamPtr& stream, Mesh* pMesh, SubMesh* sub) { String aliasName = readString(stream); String textureName = readString(stream); sub->addTextureAlias(aliasName, textureName); } //--------------------------------------------------------------------- void MeshSerializerImpl::writeSkeletonLink(const String& skelName) { writeChunkHeader(M_MESH_SKELETON_LINK, calcSkeletonLinkSize(skelName)); writeString(skelName); } //--------------------------------------------------------------------- void MeshSerializerImpl::readSkeletonLink(DataStreamPtr& stream, Mesh* pMesh) { String skelName = readString(stream); pMesh->setSkeletonName(skelName); } //--------------------------------------------------------------------- void MeshSerializerImpl::readTextureLayer(DataStreamPtr& stream, Mesh* pMesh, MaterialPtr& pMat) { // Material definition section phased out of 1.1 } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcSkeletonLinkSize(const String& skelName) { size_t size = STREAM_OVERHEAD_SIZE; size += skelName.length() + 1; return size; } //--------------------------------------------------------------------- void MeshSerializerImpl::writeMeshBoneAssignment(const VertexBoneAssignment& assign) { writeChunkHeader(M_MESH_BONE_ASSIGNMENT, calcBoneAssignmentSize()); // unsigned int vertexIndex; writeInts(&(assign.vertexIndex), 1); // unsigned short boneIndex; writeShorts(&(assign.boneIndex), 1); // float weight; writeFloats(&(assign.weight), 1); } //--------------------------------------------------------------------- void MeshSerializerImpl::writeSubMeshBoneAssignment(const VertexBoneAssignment& assign) { writeChunkHeader(M_SUBMESH_BONE_ASSIGNMENT, calcBoneAssignmentSize()); // unsigned int vertexIndex; writeInts(&(assign.vertexIndex), 1); // unsigned short boneIndex; writeShorts(&(assign.boneIndex), 1); // float weight; writeFloats(&(assign.weight), 1); } //--------------------------------------------------------------------- void MeshSerializerImpl::readMeshBoneAssignment(DataStreamPtr& stream, Mesh* pMesh) { VertexBoneAssignment assign; // unsigned int vertexIndex; readInts(stream, &(assign.vertexIndex),1); // unsigned short boneIndex; readShorts(stream, &(assign.boneIndex),1); // float weight; readFloats(stream, &(assign.weight), 1); pMesh->addBoneAssignment(assign); } //--------------------------------------------------------------------- void MeshSerializerImpl::readSubMeshBoneAssignment(DataStreamPtr& stream, Mesh* pMesh, SubMesh* sub) { VertexBoneAssignment assign; // unsigned int vertexIndex; readInts(stream, &(assign.vertexIndex),1); // unsigned short boneIndex; readShorts(stream, &(assign.boneIndex),1); // float weight; readFloats(stream, &(assign.weight), 1); sub->addBoneAssignment(assign); } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcBoneAssignmentSize(void) { size_t size = STREAM_OVERHEAD_SIZE; // Vert index size += sizeof(unsigned int); // Bone index size += sizeof(unsigned short); // weight size += sizeof(float); return size; } //--------------------------------------------------------------------- void MeshSerializerImpl::writeLodInfo(const Mesh* pMesh) { unsigned short numLods = pMesh->getNumLodLevels(); bool manual = pMesh->isLodManual(); writeLodSummary(numLods, manual); // Loop from LOD 1 (not 0, this is full detail) for (unsigned short i = 1; i < numLods; ++i) { const MeshLodUsage& usage = pMesh->getLodLevel(i); if (manual) { writeLodUsageManual(usage); } else { writeLodUsageGenerated(pMesh, usage, i); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::writeLodSummary(unsigned short numLevels, bool manual) { // Header size_t size = STREAM_OVERHEAD_SIZE; // unsigned short numLevels; size += sizeof(unsigned short); // bool manual; (true for manual alternate meshes, false for generated) size += sizeof(bool); writeChunkHeader(M_MESH_LOD, size); // Details // unsigned short numLevels; writeShorts(&numLevels, 1); // bool manual; (true for manual alternate meshes, false for generated) writeBools(&manual, 1); } //--------------------------------------------------------------------- void MeshSerializerImpl::writeLodUsageManual(const MeshLodUsage& usage) { // Header size_t size = STREAM_OVERHEAD_SIZE; size_t manualSize = STREAM_OVERHEAD_SIZE; // float fromDepthSquared; size += sizeof(float); // Manual part size // String manualMeshName; manualSize += usage.manualName.length() + 1; size += manualSize; writeChunkHeader(M_MESH_LOD_USAGE, size); writeFloats(&(usage.fromDepthSquared), 1); writeChunkHeader(M_MESH_LOD_MANUAL, manualSize); writeString(usage.manualName); } //--------------------------------------------------------------------- void MeshSerializerImpl::writeLodUsageGenerated(const Mesh* pMesh, const MeshLodUsage& usage, unsigned short lodNum) { // Usage Header size_t size = STREAM_OVERHEAD_SIZE; unsigned short subidx; // float fromDepthSquared; size += sizeof(float); // Calc generated SubMesh sections size for(subidx = 0; subidx < pMesh->getNumSubMeshes(); ++subidx) { // header size += STREAM_OVERHEAD_SIZE; // unsigned int numFaces; size += sizeof(unsigned int); SubMesh* sm = pMesh->getSubMesh(subidx); const IndexData* indexData = sm->mLodFaceList[lodNum - 1]; // bool indexes32Bit size += sizeof(bool); // unsigned short*/int* faceIndexes; if (indexData->indexBuffer->getType() == HardwareIndexBuffer::IT_32BIT) { size += static_cast( sizeof(unsigned int) * indexData->indexCount); } else { size += static_cast( sizeof(unsigned short) * indexData->indexCount); } } writeChunkHeader(M_MESH_LOD_USAGE, size); writeFloats(&(usage.fromDepthSquared), 1); // Now write sections // Calc generated SubMesh sections size for(subidx = 0; subidx < pMesh->getNumSubMeshes(); ++subidx) { size = STREAM_OVERHEAD_SIZE; // unsigned int numFaces; size += sizeof(unsigned int); SubMesh* sm = pMesh->getSubMesh(subidx); const IndexData* indexData = sm->mLodFaceList[lodNum - 1]; // bool indexes32Bit size += sizeof(bool); // unsigned short*/int* faceIndexes; if (indexData->indexBuffer->getType() == HardwareIndexBuffer::IT_32BIT) { size += static_cast( sizeof(unsigned int) * indexData->indexCount); } else { size += static_cast( sizeof(unsigned short) * indexData->indexCount); } writeChunkHeader(M_MESH_LOD_GENERATED, size); unsigned int idxCount = static_cast(indexData->indexCount); writeInts(&idxCount, 1); // Lock index buffer to write HardwareIndexBufferSharedPtr ibuf = indexData->indexBuffer; // bool indexes32bit bool idx32 = (ibuf->getType() == HardwareIndexBuffer::IT_32BIT); writeBools(&idx32, 1); if (idx32) { unsigned int* pIdx = static_cast( ibuf->lock(HardwareBuffer::HBL_READ_ONLY)); writeInts(pIdx, indexData->indexCount); ibuf->unlock(); } else { unsigned short* pIdx = static_cast( ibuf->lock(HardwareBuffer::HBL_READ_ONLY)); writeShorts(pIdx, indexData->indexCount); ibuf->unlock(); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::writeBoundsInfo(const Mesh* pMesh) { // Usage Header unsigned long size = STREAM_OVERHEAD_SIZE; size += sizeof(float) * 7; writeChunkHeader(M_MESH_BOUNDS, size); // float minx, miny, minz const Vector3& min = pMesh->mAABB.getMinimum(); const Vector3& max = pMesh->mAABB.getMaximum(); writeFloats(&min.x, 1); writeFloats(&min.y, 1); writeFloats(&min.z, 1); // float maxx, maxy, maxz writeFloats(&max.x, 1); writeFloats(&max.y, 1); writeFloats(&max.z, 1); // float radius writeFloats(&pMesh->mBoundRadius, 1); } //--------------------------------------------------------------------- void MeshSerializerImpl::readBoundsInfo(DataStreamPtr& stream, Mesh* pMesh) { Vector3 min, max; // float minx, miny, minz readFloats(stream, &min.x, 1); readFloats(stream, &min.y, 1); readFloats(stream, &min.z, 1); // float maxx, maxy, maxz readFloats(stream, &max.x, 1); readFloats(stream, &max.y, 1); readFloats(stream, &max.z, 1); AxisAlignedBox box(min, max); pMesh->_setBounds(box, true); // float radius float radius; readFloats(stream, &radius, 1); pMesh->_setBoundingSphereRadius(radius); } //--------------------------------------------------------------------- void MeshSerializerImpl::readMeshLodInfo(DataStreamPtr& stream, Mesh* pMesh) { unsigned short streamID, i; // unsigned short numLevels; readShorts(stream, &(pMesh->mNumLods), 1); // bool manual; (true for manual alternate meshes, false for generated) readBools(stream, &(pMesh->mIsLodManual), 1); // Preallocate submesh lod face data if not manual if (!pMesh->mIsLodManual) { unsigned short numsubs = pMesh->getNumSubMeshes(); for (i = 0; i < numsubs; ++i) { SubMesh* sm = pMesh->getSubMesh(i); sm->mLodFaceList.resize(pMesh->mNumLods-1); } } // Loop from 1 rather than 0 (full detail index is not in file) for (i = 1; i < pMesh->mNumLods; ++i) { streamID = readChunk(stream); if (streamID != M_MESH_LOD_USAGE) { OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Missing M_MESH_LOD_USAGE stream in " + pMesh->getName(), "MeshSerializerImpl::readMeshLodInfo"); } // Read depth MeshLodUsage usage; readFloats(stream, &(usage.fromDepthSquared), 1); if (pMesh->isLodManual()) { readMeshLodUsageManual(stream, pMesh, i, usage); } else //(!pMesh->isLodManual) { readMeshLodUsageGenerated(stream, pMesh, i, usage); } usage.edgeData = NULL; // Save usage pMesh->mMeshLodUsageList.push_back(usage); } } //--------------------------------------------------------------------- void MeshSerializerImpl::readMeshLodUsageManual(DataStreamPtr& stream, Mesh* pMesh, unsigned short lodNum, MeshLodUsage& usage) { unsigned long streamID; // Read detail stream streamID = readChunk(stream); if (streamID != M_MESH_LOD_MANUAL) { OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Missing M_MESH_LOD_MANUAL stream in " + pMesh->getName(), "MeshSerializerImpl::readMeshLodUsageManual"); } usage.manualName = readString(stream); usage.manualMesh.setNull(); // will trigger load later } //--------------------------------------------------------------------- void MeshSerializerImpl::readMeshLodUsageGenerated(DataStreamPtr& stream, Mesh* pMesh, unsigned short lodNum, MeshLodUsage& usage) { usage.manualName = ""; usage.manualMesh.setNull(); // Get one set of detail per SubMesh unsigned short numSubs, i; unsigned long streamID; numSubs = pMesh->getNumSubMeshes(); for (i = 0; i < numSubs; ++i) { streamID = readChunk(stream); if (streamID != M_MESH_LOD_GENERATED) { OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Missing M_MESH_LOD_GENERATED stream in " + pMesh->getName(), "MeshSerializerImpl::readMeshLodUsageGenerated"); } SubMesh* sm = pMesh->getSubMesh(i); // lodNum - 1 because SubMesh doesn't store full detail LOD sm->mLodFaceList[lodNum - 1] = new IndexData(); IndexData* indexData = sm->mLodFaceList[lodNum - 1]; // unsigned int numIndexes unsigned int numIndexes; readInts(stream, &numIndexes, 1); indexData->indexCount = static_cast(numIndexes); // bool indexes32Bit bool idx32Bit; readBools(stream, &idx32Bit, 1); // unsigned short*/int* faceIndexes; ((v1, v2, v3) * numFaces) if (idx32Bit) { indexData->indexBuffer = HardwareBufferManager::getSingleton(). createIndexBuffer(HardwareIndexBuffer::IT_32BIT, indexData->indexCount, pMesh->mIndexBufferUsage, pMesh->mIndexBufferShadowBuffer); unsigned int* pIdx = static_cast( indexData->indexBuffer->lock( 0, indexData->indexBuffer->getSizeInBytes(), HardwareBuffer::HBL_DISCARD) ); readInts(stream, pIdx, indexData->indexCount); indexData->indexBuffer->unlock(); } else { indexData->indexBuffer = HardwareBufferManager::getSingleton(). createIndexBuffer(HardwareIndexBuffer::IT_16BIT, indexData->indexCount, pMesh->mIndexBufferUsage, pMesh->mIndexBufferShadowBuffer); unsigned short* pIdx = static_cast( indexData->indexBuffer->lock( 0, indexData->indexBuffer->getSizeInBytes(), HardwareBuffer::HBL_DISCARD) ); readShorts(stream, pIdx, indexData->indexCount); indexData->indexBuffer->unlock(); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::flipFromLittleEndian(void* pData, size_t vertexCount, size_t vertexSize, const VertexDeclaration::VertexElementList& elems) { if (mFlipEndian) { flipEndian(pData, vertexCount, vertexSize, elems); } } //--------------------------------------------------------------------- void MeshSerializerImpl::flipToLittleEndian(void* pData, size_t vertexCount, size_t vertexSize, const VertexDeclaration::VertexElementList& elems) { if (mFlipEndian) { flipEndian(pData, vertexCount, vertexSize, elems); } } //--------------------------------------------------------------------- void MeshSerializerImpl::flipEndian(void* pData, size_t vertexCount, size_t vertexSize, const VertexDeclaration::VertexElementList& elems) { void *pBase = pData; for (size_t v = 0; v < vertexCount; ++v) { VertexDeclaration::VertexElementList::const_iterator ei, eiend; eiend = elems.end(); for (ei = elems.begin(); ei != eiend; ++ei) { void *pElem; // re-base pointer to the element (*ei).baseVertexPointerToElement(pBase, &pElem); // Flip the endian based on the type size_t typeSize = 0; switch (VertexElement::getBaseType((*ei).getType())) { case VET_FLOAT1: typeSize = sizeof(float); break; case VET_SHORT1: typeSize = sizeof(short); break; case VET_COLOUR: case VET_COLOUR_ABGR: case VET_COLOUR_ARGB: typeSize = sizeof(RGBA); break; case VET_UBYTE4: typeSize = 0; // NO FLIPPING break; default: assert(false); // Should never happen }; Serializer::flipEndian(pElem, typeSize, VertexElement::getTypeCount((*ei).getType())); } pBase = static_cast( static_cast(pBase) + vertexSize); } } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcEdgeListSize(const Mesh* pMesh) { size_t size = STREAM_OVERHEAD_SIZE; for (ushort i = 0; i < pMesh->getNumLodLevels(); ++i) { const EdgeData* edgeData = pMesh->getEdgeList(i); bool isManual = pMesh->isLodManual() && (i > 0); size += calcEdgeListLodSize(edgeData, isManual); } return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcEdgeListLodSize(const EdgeData* edgeData, bool isManual) { size_t size = STREAM_OVERHEAD_SIZE; // unsigned short lodIndex size += sizeof(uint16); // bool isManual // If manual, no edge data here, loaded from manual mesh size += sizeof(bool); if (!isManual) { // bool isClosed size += sizeof(bool); // unsigned long numTriangles size += sizeof(uint32); // unsigned long numEdgeGroups size += sizeof(uint32); // Triangle* triangleList size_t triSize = 0; // unsigned long indexSet // unsigned long vertexSet // unsigned long vertIndex[3] // unsigned long sharedVertIndex[3] // float normal[4] triSize += sizeof(uint32) * 8 + sizeof(float) * 4; size += triSize * edgeData->triangles.size(); // Write the groups for (EdgeData::EdgeGroupList::const_iterator gi = edgeData->edgeGroups.begin(); gi != edgeData->edgeGroups.end(); ++gi) { const EdgeData::EdgeGroup& edgeGroup = *gi; size += calcEdgeGroupSize(edgeGroup); } } return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcEdgeGroupSize(const EdgeData::EdgeGroup& group) { size_t size = STREAM_OVERHEAD_SIZE; // unsigned long vertexSet size += sizeof(uint32); // unsigned long triStart size += sizeof(uint32); // unsigned long triCount size += sizeof(uint32); // unsigned long numEdges size += sizeof(uint32); // Edge* edgeList size_t edgeSize = 0; // unsigned long triIndex[2] // unsigned long vertIndex[2] // unsigned long sharedVertIndex[2] // bool degenerate edgeSize += sizeof(uint32) * 6 + sizeof(bool); size += edgeSize * group.edges.size(); return size; } //--------------------------------------------------------------------- void MeshSerializerImpl::writeEdgeList(const Mesh* pMesh) { writeChunkHeader(M_EDGE_LISTS, calcEdgeListSize(pMesh)); for (ushort i = 0; i < pMesh->getNumLodLevels(); ++i) { const EdgeData* edgeData = pMesh->getEdgeList(i); bool isManual = pMesh->isLodManual() && (i > 0); writeChunkHeader(M_EDGE_LIST_LOD, calcEdgeListLodSize(edgeData, isManual)); // unsigned short lodIndex writeShorts(&i, 1); // bool isManual // If manual, no edge data here, loaded from manual mesh writeBools(&isManual, 1); if (!isManual) { // bool isClosed writeBools(&edgeData->isClosed, 1); // unsigned long numTriangles uint32 count = static_cast(edgeData->triangles.size()); writeInts(&count, 1); // unsigned long numEdgeGroups count = static_cast(edgeData->edgeGroups.size()); writeInts(&count, 1); // Triangle* triangleList // Iterate rather than writing en-masse to allow endian conversion EdgeData::TriangleList::const_iterator t = edgeData->triangles.begin(); EdgeData::TriangleFaceNormalList::const_iterator fni = edgeData->triangleFaceNormals.begin(); for ( ; t != edgeData->triangles.end(); ++t, ++fni) { const EdgeData::Triangle& tri = *t; // unsigned long indexSet; uint32 tmp[3]; tmp[0] = tri.indexSet; writeInts(tmp, 1); // unsigned long vertexSet; tmp[0] = tri.vertexSet; writeInts(tmp, 1); // unsigned long vertIndex[3]; tmp[0] = tri.vertIndex[0]; tmp[1] = tri.vertIndex[1]; tmp[2] = tri.vertIndex[2]; writeInts(tmp, 3); // unsigned long sharedVertIndex[3]; tmp[0] = tri.sharedVertIndex[0]; tmp[1] = tri.sharedVertIndex[1]; tmp[2] = tri.sharedVertIndex[2]; writeInts(tmp, 3); // float normal[4]; writeFloats(&(fni->x), 4); } // Write the groups for (EdgeData::EdgeGroupList::const_iterator gi = edgeData->edgeGroups.begin(); gi != edgeData->edgeGroups.end(); ++gi) { const EdgeData::EdgeGroup& edgeGroup = *gi; writeChunkHeader(M_EDGE_GROUP, calcEdgeGroupSize(edgeGroup)); // unsigned long vertexSet uint32 vertexSet = static_cast(edgeGroup.vertexSet); writeInts(&vertexSet, 1); // unsigned long triStart uint32 triStart = static_cast(edgeGroup.triStart); writeInts(&triStart, 1); // unsigned long triCount uint32 triCount = static_cast(edgeGroup.triCount); writeInts(&triCount, 1); // unsigned long numEdges count = static_cast(edgeGroup.edges.size()); writeInts(&count, 1); // Edge* edgeList // Iterate rather than writing en-masse to allow endian conversion for (EdgeData::EdgeList::const_iterator ei = edgeGroup.edges.begin(); ei != edgeGroup.edges.end(); ++ei) { const EdgeData::Edge& edge = *ei; uint32 tmp[2]; // unsigned long triIndex[2] tmp[0] = edge.triIndex[0]; tmp[1] = edge.triIndex[1]; writeInts(tmp, 2); // unsigned long vertIndex[2] tmp[0] = edge.vertIndex[0]; tmp[1] = edge.vertIndex[1]; writeInts(tmp, 2); // unsigned long sharedVertIndex[2] tmp[0] = edge.sharedVertIndex[0]; tmp[1] = edge.sharedVertIndex[1]; writeInts(tmp, 2); // bool degenerate writeBools(&(edge.degenerate), 1); } } } } } //--------------------------------------------------------------------- void MeshSerializerImpl::readEdgeList(DataStreamPtr& stream, Mesh* pMesh) { unsigned short streamID; if (!stream->eof()) { streamID = readChunk(stream); while(!stream->eof() && streamID == M_EDGE_LIST_LOD) { // Process single LOD // unsigned short lodIndex unsigned short lodIndex; readShorts(stream, &lodIndex, 1); // bool isManual // If manual, no edge data here, loaded from manual mesh bool isManual; readBools(stream, &isManual, 1); // Only load in non-manual levels; others will be connected up by Mesh on demand if (!isManual) { MeshLodUsage& usage = const_cast(pMesh->getLodLevel(lodIndex)); usage.edgeData = new EdgeData(); // Read detail information of the edge list readEdgeListLodInfo(stream, usage.edgeData); // Postprocessing edge groups EdgeData::EdgeGroupList::iterator egi, egend; egend = usage.edgeData->edgeGroups.end(); for (egi = usage.edgeData->edgeGroups.begin(); egi != egend; ++egi) { EdgeData::EdgeGroup& edgeGroup = *egi; // Populate edgeGroup.vertexData pointers // If there is shared vertex data, vertexSet 0 is that, // otherwise 0 is first dedicated if (pMesh->sharedVertexData) { if (edgeGroup.vertexSet == 0) { edgeGroup.vertexData = pMesh->sharedVertexData; } else { edgeGroup.vertexData = pMesh->getSubMesh( edgeGroup.vertexSet-1)->vertexData; } } else { edgeGroup.vertexData = pMesh->getSubMesh( edgeGroup.vertexSet)->vertexData; } } } if (!stream->eof()) { streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of stream stream->skip(-STREAM_OVERHEAD_SIZE); } } pMesh->mEdgeListsBuilt = true; } //--------------------------------------------------------------------- void MeshSerializerImpl::readEdgeListLodInfo(DataStreamPtr& stream, EdgeData* edgeData) { // bool isClosed readBools(stream, &edgeData->isClosed, 1); // unsigned long numTriangles uint32 numTriangles; readInts(stream, &numTriangles, 1); // Allocate correct amount of memory edgeData->triangles.resize(numTriangles); edgeData->triangleFaceNormals.resize(numTriangles); edgeData->triangleLightFacings.resize(numTriangles); // unsigned long numEdgeGroups uint32 numEdgeGroups; readInts(stream, &numEdgeGroups, 1); // Allocate correct amount of memory edgeData->edgeGroups.resize(numEdgeGroups); // Triangle* triangleList uint32 tmp[3]; for (size_t t = 0; t < numTriangles; ++t) { EdgeData::Triangle& tri = edgeData->triangles[t]; // unsigned long indexSet readInts(stream, tmp, 1); tri.indexSet = tmp[0]; // unsigned long vertexSet readInts(stream, tmp, 1); tri.vertexSet = tmp[0]; // unsigned long vertIndex[3] readInts(stream, tmp, 3); tri.vertIndex[0] = tmp[0]; tri.vertIndex[1] = tmp[1]; tri.vertIndex[2] = tmp[2]; // unsigned long sharedVertIndex[3] readInts(stream, tmp, 3); tri.sharedVertIndex[0] = tmp[0]; tri.sharedVertIndex[1] = tmp[1]; tri.sharedVertIndex[2] = tmp[2]; // float normal[4] readFloats(stream, &(edgeData->triangleFaceNormals[t].x), 4); } for (uint32 eg = 0; eg < numEdgeGroups; ++eg) { unsigned short streamID = readChunk(stream); if (streamID != M_EDGE_GROUP) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Missing M_EDGE_GROUP stream", "MeshSerializerImpl::readEdgeListLodInfo"); } EdgeData::EdgeGroup& edgeGroup = edgeData->edgeGroups[eg]; // unsigned long vertexSet readInts(stream, tmp, 1); edgeGroup.vertexSet = tmp[0]; // unsigned long triStart readInts(stream, tmp, 1); edgeGroup.triStart = tmp[0]; // unsigned long triCount readInts(stream, tmp, 1); edgeGroup.triCount = tmp[0]; // unsigned long numEdges uint32 numEdges; readInts(stream, &numEdges, 1); edgeGroup.edges.resize(numEdges); // Edge* edgeList for (uint32 e = 0; e < numEdges; ++e) { EdgeData::Edge& edge = edgeGroup.edges[e]; // unsigned long triIndex[2] readInts(stream, tmp, 2); edge.triIndex[0] = tmp[0]; edge.triIndex[1] = tmp[1]; // unsigned long vertIndex[2] readInts(stream, tmp, 2); edge.vertIndex[0] = tmp[0]; edge.vertIndex[1] = tmp[1]; // unsigned long sharedVertIndex[2] readInts(stream, tmp, 2); edge.sharedVertIndex[0] = tmp[0]; edge.sharedVertIndex[1] = tmp[1]; // bool degenerate readBools(stream, &(edge.degenerate), 1); } } } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcAnimationsSize(const Mesh* pMesh) { size_t size = STREAM_OVERHEAD_SIZE; for (unsigned short a = 0; a < pMesh->getNumAnimations(); ++a) { Animation* anim = pMesh->getAnimation(a); size += calcAnimationSize(anim); } return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcAnimationSize(const Animation* anim) { size_t size = STREAM_OVERHEAD_SIZE; // char* name size += anim->getName().length() + 1; // float length size += sizeof(float); Animation::VertexTrackIterator trackIt = anim->getVertexTrackIterator(); while (trackIt.hasMoreElements()) { VertexAnimationTrack* vt = trackIt.getNext(); size += calcAnimationTrackSize(vt); } return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcAnimationTrackSize(const VertexAnimationTrack* track) { size_t size = STREAM_OVERHEAD_SIZE; // uint16 type size += sizeof(uint16); // unsigned short target // 0 for shared geometry, size += sizeof(unsigned short); if (track->getAnimationType() == VAT_MORPH) { for (unsigned short i = 0; i < track->getNumKeyFrames(); ++i) { VertexMorphKeyFrame* kf = track->getVertexMorphKeyFrame(i); size += calcMorphKeyframeSize(kf, track->getAssociatedVertexData()->vertexCount); } } else { for (unsigned short i = 0; i < track->getNumKeyFrames(); ++i) { VertexPoseKeyFrame* kf = track->getVertexPoseKeyFrame(i); size += calcPoseKeyframeSize(kf); } } return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcMorphKeyframeSize(const VertexMorphKeyFrame* kf, size_t vertexCount) { size_t size = STREAM_OVERHEAD_SIZE; // float time size += sizeof(float); // bool isOriginalGeometry // point at original geometry? size += sizeof(bool); // float x,y,z size += sizeof(float) * 3 * vertexCount; return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcPoseKeyframeSize(const VertexPoseKeyFrame* kf) { size_t size = STREAM_OVERHEAD_SIZE; // float time size += sizeof(float); size += calcPoseKeyframePoseRefSize() * kf->getPoseReferences().size(); return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcPoseKeyframePoseRefSize(void) { size_t size = STREAM_OVERHEAD_SIZE; // unsigned short poseIndex size += sizeof(uint16); // float influence size += sizeof(float); return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcPosesSize(const Mesh* pMesh) { size_t size = STREAM_OVERHEAD_SIZE; Mesh::ConstPoseIterator poseIt = pMesh->getPoseIterator(); while (poseIt.hasMoreElements()) { size += calcPoseSize(poseIt.getNext()); } return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcPoseSize(const Pose* pose) { size_t size = STREAM_OVERHEAD_SIZE; // char* name (may be blank) size += pose->getName().length() + 1; // unsigned short target size += sizeof(uint16); // vertex offsets size += pose->getVertexOffsets().size() * calcPoseVertexSize(); return size; } //--------------------------------------------------------------------- size_t MeshSerializerImpl::calcPoseVertexSize(void) { size_t size = STREAM_OVERHEAD_SIZE; // unsigned long vertexIndex size += sizeof(uint32); // float xoffset, yoffset, zoffset size += sizeof(float) * 3; return size; } //--------------------------------------------------------------------- void MeshSerializerImpl::writePoses(const Mesh* pMesh) { Mesh::ConstPoseIterator poseIterator = pMesh->getPoseIterator(); if (poseIterator.hasMoreElements()) { writeChunkHeader(M_POSES, calcPosesSize(pMesh)); while (poseIterator.hasMoreElements()) { writePose(poseIterator.getNext()); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::writePose(const Pose* pose) { writeChunkHeader(M_POSE, calcPoseSize(pose)); // char* name (may be blank) writeString(pose->getName()); // unsigned short target ushort val = pose->getTarget(); writeShorts(&val, 1); size_t vertexSize = calcPoseVertexSize(); Pose::ConstVertexOffsetIterator vit = pose->getVertexOffsetIterator(); while (vit.hasMoreElements()) { uint32 vertexIndex = (uint32)vit.peekNextKey(); Vector3 offset = vit.getNext(); writeChunkHeader(M_POSE_VERTEX, vertexSize); // unsigned long vertexIndex writeInts(&vertexIndex, 1); // float xoffset, yoffset, zoffset writeFloats(offset.ptr(), 3); } } //--------------------------------------------------------------------- void MeshSerializerImpl::writeAnimations(const Mesh* pMesh) { writeChunkHeader(M_ANIMATIONS, calcAnimationsSize(pMesh)); for (unsigned short a = 0; a < pMesh->getNumAnimations(); ++a) { Animation* anim = pMesh->getAnimation(a); LogManager::getSingleton().logMessage("Exporting animation " + anim->getName()); writeAnimation(anim); LogManager::getSingleton().logMessage("Animation exported."); } } //--------------------------------------------------------------------- void MeshSerializerImpl::writeAnimation(const Animation* anim) { writeChunkHeader(M_ANIMATION, calcAnimationSize(anim)); // char* name writeString(anim->getName()); // float length float len = anim->getLength(); writeFloats(&len, 1); Animation::VertexTrackIterator trackIt = anim->getVertexTrackIterator(); while (trackIt.hasMoreElements()) { VertexAnimationTrack* vt = trackIt.getNext(); writeAnimationTrack(vt); } } //--------------------------------------------------------------------- void MeshSerializerImpl::writeAnimationTrack(const VertexAnimationTrack* track) { writeChunkHeader(M_ANIMATION_TRACK, calcAnimationTrackSize(track)); // unsigned short type // 1 == morph, 2 == pose uint16 animType = (uint16)track->getAnimationType(); writeShorts(&animType, 1); // unsigned short target uint16 target = track->getHandle(); writeShorts(&target, 1); if (track->getAnimationType() == VAT_MORPH) { for (unsigned short i = 0; i < track->getNumKeyFrames(); ++i) { VertexMorphKeyFrame* kf = track->getVertexMorphKeyFrame(i); writeMorphKeyframe(kf, track->getAssociatedVertexData()->vertexCount); } } else // VAT_POSE { for (unsigned short i = 0; i < track->getNumKeyFrames(); ++i) { VertexPoseKeyFrame* kf = track->getVertexPoseKeyFrame(i); writePoseKeyframe(kf); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::writeMorphKeyframe(const VertexMorphKeyFrame* kf, size_t vertexCount) { writeChunkHeader(M_ANIMATION_MORPH_KEYFRAME, calcMorphKeyframeSize(kf, vertexCount)); // float time float timePos = kf->getTime(); writeFloats(&timePos, 1); // float x,y,z // repeat by number of vertices in original geometry float* pSrc = static_cast( kf->getVertexBuffer()->lock(HardwareBuffer::HBL_READ_ONLY)); writeFloats(pSrc, vertexCount * 3); kf->getVertexBuffer()->unlock(); } //--------------------------------------------------------------------- void MeshSerializerImpl::writePoseKeyframe(const VertexPoseKeyFrame* kf) { writeChunkHeader(M_ANIMATION_POSE_KEYFRAME, calcPoseKeyframeSize(kf)); // float time float timePos = kf->getTime(); writeFloats(&timePos, 1); // pose references VertexPoseKeyFrame::ConstPoseRefIterator poseRefIt = kf->getPoseReferenceIterator(); while (poseRefIt.hasMoreElements()) { writePoseKeyframePoseRef(poseRefIt.getNext()); } } //--------------------------------------------------------------------- void MeshSerializerImpl::writePoseKeyframePoseRef( const VertexPoseKeyFrame::PoseRef& poseRef) { writeChunkHeader(M_ANIMATION_POSE_REF, calcPoseKeyframePoseRefSize()); // unsigned short poseIndex writeShorts(&(poseRef.poseIndex), 1); // float influence writeFloats(&(poseRef.influence), 1); } //--------------------------------------------------------------------- void MeshSerializerImpl::readPoses(DataStreamPtr& stream, Mesh* pMesh) { unsigned short streamID; // Find all substreams if (!stream->eof()) { streamID = readChunk(stream); while(!stream->eof() && (streamID == M_POSE)) { switch(streamID) { case M_POSE: readPose(stream, pMesh); break; } if (!stream->eof()) { streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of stream stream->skip(-STREAM_OVERHEAD_SIZE); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::readPose(DataStreamPtr& stream, Mesh* pMesh) { // char* name (may be blank) String name = readString(stream); // unsigned short target unsigned short target; readShorts(stream, &target, 1); Pose* pose = pMesh->createPose(target, name); // Find all substreams unsigned short streamID; if (!stream->eof()) { streamID = readChunk(stream); while(!stream->eof() && (streamID == M_POSE_VERTEX)) { switch(streamID) { case M_POSE_VERTEX: // create vertex offset uint32 vertIndex; Vector3 offset; // unsigned long vertexIndex readInts(stream, &vertIndex, 1); // float xoffset, yoffset, zoffset readFloats(stream, offset.ptr(), 3); pose->addVertex(vertIndex, offset); break; } if (!stream->eof()) { streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of stream stream->skip(-STREAM_OVERHEAD_SIZE); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::readAnimations(DataStreamPtr& stream, Mesh* pMesh) { unsigned short streamID; // Find all substreams if (!stream->eof()) { streamID = readChunk(stream); while(!stream->eof() && (streamID == M_ANIMATION)) { switch(streamID) { case M_ANIMATION: readAnimation(stream, pMesh); break; } if (!stream->eof()) { streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of stream stream->skip(-STREAM_OVERHEAD_SIZE); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::readAnimation(DataStreamPtr& stream, Mesh* pMesh) { // char* name String name = readString(stream); // float length float len; readFloats(stream, &len, 1); Animation* anim = pMesh->createAnimation(name, len); // tracks unsigned short streamID; if (!stream->eof()) { streamID = readChunk(stream); while(!stream->eof() && streamID == M_ANIMATION_TRACK) { switch(streamID) { case M_ANIMATION_TRACK: readAnimationTrack(stream, anim, pMesh); break; }; if (!stream->eof()) { streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of stream stream->skip(-STREAM_OVERHEAD_SIZE); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::readAnimationTrack(DataStreamPtr& stream, Animation* anim, Mesh* pMesh) { // ushort type uint16 inAnimType; readShorts(stream, &inAnimType, 1); VertexAnimationType animType = (VertexAnimationType)inAnimType; // unsigned short target uint16 target; readShorts(stream, &target, 1); VertexAnimationTrack* track = anim->createVertexTrack(target, pMesh->getVertexDataByTrackHandle(target), animType); // keyframes unsigned short streamID; if (!stream->eof()) { streamID = readChunk(stream); while(!stream->eof() && (streamID == M_ANIMATION_MORPH_KEYFRAME || streamID == M_ANIMATION_POSE_KEYFRAME)) { switch(streamID) { case M_ANIMATION_MORPH_KEYFRAME: readMorphKeyFrame(stream, track); break; case M_ANIMATION_POSE_KEYFRAME: readPoseKeyFrame(stream, track); break; }; if (!stream->eof()) { streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of stream stream->skip(-STREAM_OVERHEAD_SIZE); } } } //--------------------------------------------------------------------- void MeshSerializerImpl::readMorphKeyFrame(DataStreamPtr& stream, VertexAnimationTrack* track) { // float time float timePos; readFloats(stream, &timePos, 1); VertexMorphKeyFrame* kf = track->createVertexMorphKeyFrame(timePos); // Create buffer, allow read and use shadow buffer size_t vertexCount = track->getAssociatedVertexData()->vertexCount; HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( VertexElement::getTypeSize(VET_FLOAT3), vertexCount, HardwareBuffer::HBU_STATIC, true); // float x,y,z // repeat by number of vertices in original geometry float* pDst = static_cast( vbuf->lock(HardwareBuffer::HBL_DISCARD)); readFloats(stream, pDst, vertexCount * 3); vbuf->unlock(); kf->setVertexBuffer(vbuf); } //--------------------------------------------------------------------- void MeshSerializerImpl::readPoseKeyFrame(DataStreamPtr& stream, VertexAnimationTrack* track) { // float time float timePos; readFloats(stream, &timePos, 1); // Create keyframe VertexPoseKeyFrame* kf = track->createVertexPoseKeyFrame(timePos); unsigned short streamID; if (!stream->eof()) { streamID = readChunk(stream); while(!stream->eof() && streamID == M_ANIMATION_POSE_REF) { switch(streamID) { case M_ANIMATION_POSE_REF: uint16 poseIndex; float influence; // unsigned short poseIndex readShorts(stream, &poseIndex, 1); // float influence readFloats(stream, &influence, 1); kf->addPoseReference(poseIndex, influence); break; }; if (!stream->eof()) { streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of stream stream->skip(-STREAM_OVERHEAD_SIZE); } } } //--------------------------------------------------------------------- //--------------------------------------------------------------------- //--------------------------------------------------------------------- MeshSerializerImpl_v1_3::MeshSerializerImpl_v1_3() { // Version number mVersion = "[MeshSerializer_v1.30]"; } //--------------------------------------------------------------------- MeshSerializerImpl_v1_3::~MeshSerializerImpl_v1_3() { } //--------------------------------------------------------------------- void MeshSerializerImpl_v1_3::readEdgeListLodInfo(DataStreamPtr& stream, EdgeData* edgeData) { // unsigned long numTriangles uint32 numTriangles; readInts(stream, &numTriangles, 1); // Allocate correct amount of memory edgeData->triangles.resize(numTriangles); edgeData->triangleFaceNormals.resize(numTriangles); edgeData->triangleLightFacings.resize(numTriangles); // unsigned long numEdgeGroups uint32 numEdgeGroups; readInts(stream, &numEdgeGroups, 1); // Allocate correct amount of memory edgeData->edgeGroups.resize(numEdgeGroups); // Triangle* triangleList uint32 tmp[3]; for (size_t t = 0; t < numTriangles; ++t) { EdgeData::Triangle& tri = edgeData->triangles[t]; // unsigned long indexSet readInts(stream, tmp, 1); tri.indexSet = tmp[0]; // unsigned long vertexSet readInts(stream, tmp, 1); tri.vertexSet = tmp[0]; // unsigned long vertIndex[3] readInts(stream, tmp, 3); tri.vertIndex[0] = tmp[0]; tri.vertIndex[1] = tmp[1]; tri.vertIndex[2] = tmp[2]; // unsigned long sharedVertIndex[3] readInts(stream, tmp, 3); tri.sharedVertIndex[0] = tmp[0]; tri.sharedVertIndex[1] = tmp[1]; tri.sharedVertIndex[2] = tmp[2]; // float normal[4] readFloats(stream, &(edgeData->triangleFaceNormals[t].x), 4); } // Assume the mesh is closed, it will update later edgeData->isClosed = true; for (uint32 eg = 0; eg < numEdgeGroups; ++eg) { unsigned short streamID = readChunk(stream); if (streamID != M_EDGE_GROUP) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Missing M_EDGE_GROUP stream", "MeshSerializerImpl_v1_3::readEdgeListLodInfo"); } EdgeData::EdgeGroup& edgeGroup = edgeData->edgeGroups[eg]; // unsigned long vertexSet readInts(stream, tmp, 1); edgeGroup.vertexSet = tmp[0]; // unsigned long numEdges uint32 numEdges; readInts(stream, &numEdges, 1); edgeGroup.edges.resize(numEdges); // Edge* edgeList for (uint32 e = 0; e < numEdges; ++e) { EdgeData::Edge& edge = edgeGroup.edges[e]; // unsigned long triIndex[2] readInts(stream, tmp, 2); edge.triIndex[0] = tmp[0]; edge.triIndex[1] = tmp[1]; // unsigned long vertIndex[2] readInts(stream, tmp, 2); edge.vertIndex[0] = tmp[0]; edge.vertIndex[1] = tmp[1]; // unsigned long sharedVertIndex[2] readInts(stream, tmp, 2); edge.sharedVertIndex[0] = tmp[0]; edge.sharedVertIndex[1] = tmp[1]; // bool degenerate readBools(stream, &(edge.degenerate), 1); // The mesh is closed only if no degenerate edge here if (edge.degenerate) { edgeData->isClosed = false; } } } reorganiseTriangles(edgeData); } //--------------------------------------------------------------------- void MeshSerializerImpl_v1_3::reorganiseTriangles(EdgeData* edgeData) { size_t numTriangles = edgeData->triangles.size(); if (edgeData->edgeGroups.size() == 1) { // Special case for only one edge group in the edge list, which occuring // most time. In this case, all triangles belongs to that group. edgeData->edgeGroups.front().triStart = 0; edgeData->edgeGroups.front().triCount = numTriangles; } else { EdgeData::EdgeGroupList::iterator egi, egend; egend = edgeData->edgeGroups.end(); // Calculate number of triangles for edge groups for (egi = edgeData->edgeGroups.begin(); egi != egend; ++egi) { egi->triStart = 0; egi->triCount = 0; } bool isGrouped = true; EdgeData::EdgeGroup* lastEdgeGroup = 0; for (size_t t = 0; t < numTriangles; ++t) { // Gets the edge group that the triangle belongs to const EdgeData::Triangle& tri = edgeData->triangles[t]; EdgeData::EdgeGroup* edgeGroup = &edgeData->edgeGroups[tri.vertexSet]; // Does edge group changes from last edge group? if (isGrouped && edgeGroup != lastEdgeGroup) { // Remember last edge group lastEdgeGroup = edgeGroup; // Is't first time encounter this edge group? if (!edgeGroup->triCount && !edgeGroup->triStart) { // setup first triangle of this edge group edgeGroup->triStart = t; } else { // original triangles doesn't grouping by edge group isGrouped = false; } } // Count number of triangles for this edge group ++edgeGroup->triCount; } // // Note that triangles has been sorted by vertex set for a long time, // but never stored to old version mesh file. // // Adopt this fact to avoid remap triangles here. // // Does triangles grouped by vertex set? if (!isGrouped) { // Ok, the triangles of this edge list isn't grouped by vertex set // perfectly, seems ancient mesh file. // // We need work hardly to group triangles by vertex set. // // Calculate triStart and reset triCount to zero for each edge group first size_t triStart = 0; for (egi = edgeData->edgeGroups.begin(); egi != egend; ++egi) { egi->triStart = triStart; triStart += egi->triCount; egi->triCount = 0; } // The map used to mapping original triangle index to new index typedef std::vector TriangleIndexRemap; TriangleIndexRemap triangleIndexRemap(numTriangles); // New triangles information that should be group by vertex set. EdgeData::TriangleList newTriangles(numTriangles); EdgeData::TriangleFaceNormalList newTriangleFaceNormals(numTriangles); // Calculate triangle index map and organise triangles information for (size_t t = 0; t < numTriangles; ++t) { // Gets the edge group that the triangle belongs to const EdgeData::Triangle& tri = edgeData->triangles[t]; EdgeData::EdgeGroup& edgeGroup = edgeData->edgeGroups[tri.vertexSet]; // Calculate new index size_t newIndex = edgeGroup.triStart + edgeGroup.triCount; ++edgeGroup.triCount; // Setup triangle index mapping entry triangleIndexRemap[t] = newIndex; // Copy triangle info to new placement newTriangles[newIndex] = tri; newTriangleFaceNormals[newIndex] = edgeData->triangleFaceNormals[t]; } // Replace with new triangles information edgeData->triangles.swap(newTriangles); edgeData->triangleFaceNormals.swap(newTriangleFaceNormals); // Now, update old triangle indices to new index for (egi = edgeData->edgeGroups.begin(); egi != egend; ++egi) { EdgeData::EdgeList::iterator ei, eend; eend = egi->edges.end(); for (ei = egi->edges.begin(); ei != eend; ++ei) { ei->triIndex[0] = triangleIndexRemap[ei->triIndex[0]]; if (!ei->degenerate) { ei->triIndex[1] = triangleIndexRemap[ei->triIndex[1]]; } } } } } } //--------------------------------------------------------------------- void MeshSerializerImpl::readExtremes(DataStreamPtr& stream, Mesh *pMesh) { unsigned short idx; readShorts(stream, &idx, 1); SubMesh *sm = pMesh->getSubMesh (idx); int n_floats = (mCurrentstreamLen - STREAM_OVERHEAD_SIZE - sizeof (unsigned short)) / sizeof (float); assert ((n_floats % 3) == 0); float *vert = new float[n_floats]; readFloats(stream, vert, n_floats); for (int i = 0; i < n_floats; i += 3) sm->extremityPoints.push_back(Vector3(vert [i], vert [i + 1], vert [i + 2])); delete [] vert; } //--------------------------------------------------------------------- //--------------------------------------------------------------------- //--------------------------------------------------------------------- MeshSerializerImpl_v1_2::MeshSerializerImpl_v1_2() { // Version number mVersion = "[MeshSerializer_v1.20]"; } //--------------------------------------------------------------------- MeshSerializerImpl_v1_2::~MeshSerializerImpl_v1_2() { } //--------------------------------------------------------------------- void MeshSerializerImpl_v1_2::readMesh(DataStreamPtr& stream, Mesh* pMesh) { MeshSerializerImpl::readMesh(stream, pMesh); // Always automatically build edge lists for this version pMesh->mAutoBuildEdgeLists = true; } //--------------------------------------------------------------------- void MeshSerializerImpl_v1_2::readGeometry(DataStreamPtr& stream, Mesh* pMesh, VertexData* dest) { unsigned short texCoordSet = 0; unsigned short bindIdx = 0; dest->vertexStart = 0; unsigned int vertexCount = 0; readInts(stream, &vertexCount, 1); dest->vertexCount = vertexCount; // Vertex buffers readGeometryPositions(bindIdx, stream, pMesh, dest); ++bindIdx; // Find optional geometry streams if (!stream->eof()) { unsigned short streamID = readChunk(stream); while(!stream->eof() && (streamID == M_GEOMETRY_NORMALS || streamID == M_GEOMETRY_COLOURS || streamID == M_GEOMETRY_TEXCOORDS )) { switch (streamID) { case M_GEOMETRY_NORMALS: readGeometryNormals(bindIdx++, stream, pMesh, dest); break; case M_GEOMETRY_COLOURS: readGeometryColours(bindIdx++, stream, pMesh, dest); break; case M_GEOMETRY_TEXCOORDS: readGeometryTexCoords(bindIdx++, stream, pMesh, dest, texCoordSet++); break; } // Get next stream if (!stream->eof()) { streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of non-submesh stream stream->skip(-STREAM_OVERHEAD_SIZE); } } } //--------------------------------------------------------------------- void MeshSerializerImpl_v1_2::readGeometryPositions(unsigned short bindIdx, DataStreamPtr& stream, Mesh* pMesh, VertexData* dest) { float *pFloat = 0; HardwareVertexBufferSharedPtr vbuf; // float* pVertices (x, y, z order x numVertices) dest->vertexDeclaration->addElement(bindIdx, 0, VET_FLOAT3, VES_POSITION); vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( dest->vertexDeclaration->getVertexSize(bindIdx), dest->vertexCount, pMesh->mVertexBufferUsage, pMesh->mIndexBufferShadowBuffer); pFloat = static_cast( vbuf->lock(HardwareBuffer::HBL_DISCARD)); readFloats(stream, pFloat, dest->vertexCount * 3); vbuf->unlock(); dest->vertexBufferBinding->setBinding(bindIdx, vbuf); } //--------------------------------------------------------------------- void MeshSerializerImpl_v1_2::readGeometryNormals(unsigned short bindIdx, DataStreamPtr& stream, Mesh* pMesh, VertexData* dest) { float *pFloat = 0; HardwareVertexBufferSharedPtr vbuf; // float* pNormals (x, y, z order x numVertices) dest->vertexDeclaration->addElement(bindIdx, 0, VET_FLOAT3, VES_NORMAL); vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( dest->vertexDeclaration->getVertexSize(bindIdx), dest->vertexCount, pMesh->mVertexBufferUsage, pMesh->mVertexBufferShadowBuffer); pFloat = static_cast( vbuf->lock(HardwareBuffer::HBL_DISCARD)); readFloats(stream, pFloat, dest->vertexCount * 3); vbuf->unlock(); dest->vertexBufferBinding->setBinding(bindIdx, vbuf); } //--------------------------------------------------------------------- void MeshSerializerImpl_v1_2::readGeometryColours(unsigned short bindIdx, DataStreamPtr& stream, Mesh* pMesh, VertexData* dest) { RGBA* pRGBA = 0; HardwareVertexBufferSharedPtr vbuf; // unsigned long* pColours (RGBA 8888 format x numVertices) dest->vertexDeclaration->addElement(bindIdx, 0, VET_COLOUR, VES_DIFFUSE); vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( dest->vertexDeclaration->getVertexSize(bindIdx), dest->vertexCount, pMesh->mVertexBufferUsage, pMesh->mVertexBufferShadowBuffer); pRGBA = static_cast( vbuf->lock(HardwareBuffer::HBL_DISCARD)); readInts(stream, pRGBA, dest->vertexCount); vbuf->unlock(); dest->vertexBufferBinding->setBinding(bindIdx, vbuf); } //--------------------------------------------------------------------- void MeshSerializerImpl_v1_2::readGeometryTexCoords(unsigned short bindIdx, DataStreamPtr& stream, Mesh* pMesh, VertexData* dest, unsigned short texCoordSet) { float *pFloat = 0; HardwareVertexBufferSharedPtr vbuf; // unsigned short dimensions (1 for 1D, 2 for 2D, 3 for 3D) unsigned short dim; readShorts(stream, &dim, 1); // float* pTexCoords (u [v] [w] order, dimensions x numVertices) dest->vertexDeclaration->addElement( bindIdx, 0, VertexElement::multiplyTypeCount(VET_FLOAT1, dim), VES_TEXTURE_COORDINATES, texCoordSet); vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( dest->vertexDeclaration->getVertexSize(bindIdx), dest->vertexCount, pMesh->mVertexBufferUsage, pMesh->mVertexBufferShadowBuffer); pFloat = static_cast( vbuf->lock(HardwareBuffer::HBL_DISCARD)); readFloats(stream, pFloat, dest->vertexCount * dim); vbuf->unlock(); dest->vertexBufferBinding->setBinding(bindIdx, vbuf); } //--------------------------------------------------------------------- //--------------------------------------------------------------------- //--------------------------------------------------------------------- MeshSerializerImpl_v1_1::MeshSerializerImpl_v1_1() { // Version number mVersion = "[MeshSerializer_v1.10]"; } //--------------------------------------------------------------------- MeshSerializerImpl_v1_1::~MeshSerializerImpl_v1_1() { } //--------------------------------------------------------------------- void MeshSerializerImpl_v1_1::readGeometryTexCoords(unsigned short bindIdx, DataStreamPtr& stream, Mesh* pMesh, VertexData* dest, unsigned short texCoordSet) { float *pFloat = 0; HardwareVertexBufferSharedPtr vbuf; // unsigned short dimensions (1 for 1D, 2 for 2D, 3 for 3D) unsigned short dim; readShorts(stream, &dim, 1); // float* pTexCoords (u [v] [w] order, dimensions x numVertices) dest->vertexDeclaration->addElement( bindIdx, 0, VertexElement::multiplyTypeCount(VET_FLOAT1, dim), VES_TEXTURE_COORDINATES, texCoordSet); vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( dest->vertexDeclaration->getVertexSize(bindIdx), dest->vertexCount, pMesh->getVertexBufferUsage(), pMesh->isVertexBufferShadowed()); pFloat = static_cast( vbuf->lock(HardwareBuffer::HBL_DISCARD)); readFloats(stream, pFloat, dest->vertexCount * dim); // Adjust individual v values to (1 - v) if (dim == 2) { for (size_t i = 0; i < dest->vertexCount; ++i) { ++pFloat; // skip u *pFloat = 1.0 - *pFloat; // v = 1 - v ++pFloat; } } vbuf->unlock(); dest->vertexBufferBinding->setBinding(bindIdx, vbuf); } //--------------------------------------------------------------------- //--------------------------------------------------------------------- //--------------------------------------------------------------------- }