#include "BBCOgreMeshSerializer.h" namespace BBC { UniqueVertex::UniqueVertex() : position(Ogre::Vector3::ZERO), normal(Ogre::Vector3::ZERO), colour(0), nextIndex(0) { for (int i = 0; i < OGRE_MAX_TEXTURE_COORD_SETS; ++i) uv[i] = Ogre::Vector3::ZERO; } bool UniqueVertex::operator==(const UniqueVertex& rhs) const { bool ret = position == rhs.position && normal == rhs.normal && colour == rhs.colour; if (!ret) return ret; for (int i = 0; i < OGRE_MAX_TEXTURE_COORD_SETS && ret; ++i) { ret = ret && (uv[i] == rhs.uv[i]); } return ret; } void OgreMeshSerializer::setEntity(EntityPtr entity) { mEntity = entity; } EntityPtr OgreMeshSerializer::getEntity() { return mEntity; } OgreMeshSerializer::OgreMeshSerializer() { } OgreMeshSerializer::~OgreMeshSerializer() { } void OgreMeshSerializer::exportMesh(const Ogre::String& fileName, bool mergeSubMeshes = false, bool tangents = false) { Ogre::LogManager::getSingleton().logMessage("** Begin OGRE Mesh Export **"); // Derive the scene root // Construct mesh BBC::MeshPtr pMesh(new BBC::Mesh(Ogre::MeshManager::getSingleton().createManual(fileName, fileName).getPointer())); Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().load(fileName, fileName); Ogre::LogManager::getSingleton().logMessage("Num.SubEntities:" + Ogre::StringConverter::toString(mEntity->getNumSubEntities())); mEntity->setMesh(pMesh); // write the data into a mesh buildMesh(pMesh, mergeSubMeshes); if(tangents) { Ogre::LogManager::getSingleton().logMessage("Calculating tangents"); unsigned short src, dest; if (pMesh->get()->suggestTangentVectorBuildParams(src, dest)) { pMesh->get()->buildTangentVectors(src, dest); } else { Ogre::LogManager::getSingleton().logMessage("Could not derive tangents parameters"); } } Ogre::LogManager::getSingleton().logMessage("** OGRE Mesh Export Complete **"); } void OgreMeshSerializer::buildMesh(MeshPtr pMesh, bool mergeSubmeshes) { if (!mergeSubmeshes) { // build contents of this polymesh into ProtoSubMesh(es) generateEntityAABB(pMesh); // export out at the end of every PolygonMesh exportSubMeshes(pMesh); } // TODO if (mergeSubmeshes) { // build contents of this polymesh into ProtoSubMesh(es) mEntity->mergeSubEntities(); generateEntityAABB(pMesh); // export out the combined result exportSubMeshes(pMesh); } } void OgreMeshSerializer::generateEntityAABB(MeshPtr pMesh) { // Bounds calculation Ogre::Real squaredRadius = 0.0f; Ogre::Vector3 min, max; bool first = true; for (unsigned int iSubEntity = 0; iSubEntity < mEntity->getNumSubEntities(); iSubEntity++) { for (size_t jVertex = 0; jVertex < mEntity->getSubEntity(iSubEntity)->getNumVertices(); jVertex++) { if (first) { squaredRadius = mEntity->getSubEntity(iSubEntity)->getUniqueVertex(jVertex).position.squaredLength(); min = max = mEntity->getSubEntity(iSubEntity)->getUniqueVertex(jVertex).position; first = false; } else { size_t numVertices = (unsigned int)mEntity->getSubEntity(iSubEntity)->getNumVertices(); //Ogre::LogManager::getSingleton().logMessage("NumVertices:" + Ogre::StringConverter::toString(numVertices)); //Ogre::LogManager::getSingleton().logMessage("IDVertex:" + Ogre::StringConverter::toString(jVertex) + "--" + Ogre::StringConverter::toString(iSubEntity) + "--" + Ogre::StringConverter::toString(mEntity->getSubEntity(iSubEntity)->getUniqueVertex(jVertex).position)); squaredRadius = std::max(squaredRadius,mEntity->getSubEntity(iSubEntity)->getUniqueVertex(jVertex).position.squaredLength()); min.makeFloor(mEntity->getSubEntity(iSubEntity)->getUniqueVertex(jVertex).position); max.makeCeil(mEntity->getSubEntity(iSubEntity)->getUniqueVertex(jVertex).position); } } } // Merge bounds Ogre::AxisAlignedBox box; box.setExtents(min, max); box.merge(pMesh->get()->getBounds()); pMesh->get()->_setBounds(box); pMesh->get()->_setBoundingSphereRadius( std::max( pMesh->get()->getBoundingSphereRadius(), Ogre::Math::Sqrt(squaredRadius))); Ogre::LogManager::getSingleton().logMessage("Bounds:" + Ogre::StringConverter::toString(pMesh->get()->getBoundingSphereRadius()) + " -- Generated:" + Ogre::StringConverter::toString(pMesh->get()->getBounds().isNull())); } void OgreMeshSerializer::exportSubMeshes(MeshPtr pMesh) { for (unsigned int iSubEntity = 0; iSubEntity < mEntity->getNumSubEntities(); iSubEntity++) { exportSubMesh(pMesh,mEntity->getSubEntity(iSubEntity)); } } void OgreMeshSerializer::exportSubMesh(MeshPtr pMesh, SubEntityPtr subEntity) { Ogre::SubMesh* sm = 0; if (subEntity->getName() == "") { sm = pMesh->get()->createSubMesh(); } else { sm = pMesh->get()->createSubMesh(subEntity->getName()); } // Set material sm->setMaterialName(subEntity->getMaterialName()); // never use shared geometry sm->useSharedVertices = false; sm->vertexData = new Ogre::VertexData(); // always do triangle list sm->indexData->indexCount = subEntity->getNumFaces()*3; sm->vertexData->vertexCount = subEntity->getNumVertices(); // Determine index size bool use32BitIndexes = false; if (subEntity->getNumVertices() > 65536) { use32BitIndexes = true; } sm->indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer( use32BitIndexes ? Ogre::HardwareIndexBuffer::IT_32BIT : Ogre::HardwareIndexBuffer::IT_16BIT, sm->indexData->indexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); if (use32BitIndexes) { unsigned int* pIdx = static_cast( sm->indexData->indexBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD)); writeIndexes(pIdx,subEntity); sm->indexData->indexBuffer->unlock(); } else { unsigned short int* pIdx = static_cast( sm->indexData->indexBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD)); writeIndexes(pIdx,subEntity); sm->indexData->indexBuffer->unlock(); } // define vertex declaration unsigned buf = 0; size_t offset = 0; // always add position and normal sm->vertexData->vertexDeclaration->addElement(buf, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION); offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3); sm->vertexData->vertexDeclaration->addElement(buf, offset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3); // Optional vertex colour if(subEntity->hasVertexColors()) { sm->vertexData->vertexDeclaration->addElement(buf, offset, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); offset += Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR); } // Define UVs for (unsigned short uvi = 0; uvi < subEntity->getNumTexCoordSets(); uvi++) { Ogre::VertexElementType uvType = Ogre::VertexElement::multiplyTypeCount(Ogre::VET_FLOAT1, subEntity->getTexCoordDimensions(uvi)); sm->vertexData->vertexDeclaration->addElement(buf, offset, uvType, Ogre::VES_TEXTURE_COORDINATES, uvi); offset += Ogre::VertexElement::getTypeSize(uvType); } // create & fill buffer(s) for (unsigned short b = 0; b <= sm->vertexData->vertexDeclaration->getMaxSource(); ++b) { createVertexBuffer(sm->vertexData, b, subEntity->getUniqueVertices()); } } template void OgreMeshSerializer::writeIndexes(T* buf, SubEntityPtr subEntity) { for (unsigned int i = 0; i < subEntity->getNumFaces(); i++) { Ogre::Vector3 indexes = subEntity->getFaceVerticesIDs(i); unsigned int i0, i1, i2; i0 = (unsigned int)indexes[0]; i1 = (unsigned int)indexes[1]; i2 = (unsigned int)indexes[2]; *buf++ = static_cast(i0); *buf++ = static_cast(i1); *buf++ = static_cast(i2); } } void OgreMeshSerializer::createVertexBuffer(Ogre::VertexData* vd, unsigned short bufIdx, UniqueVertexList *uniqueVertexList) { Ogre::HardwareVertexBufferSharedPtr vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( vd->vertexDeclaration->getVertexSize(bufIdx), vd->vertexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); vd->vertexBufferBinding->setBinding(bufIdx, vbuf); size_t vertexSize = vd->vertexDeclaration->getVertexSize(bufIdx); char* pBase = static_cast( vbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD)); Ogre::VertexDeclaration::VertexElementList elems = vd->vertexDeclaration->findElementsBySource(bufIdx); Ogre::VertexDeclaration::VertexElementList::iterator ei, eiend; eiend = elems.end(); float* pFloat; Ogre::RGBA* pRGBA; UniqueVertexList::iterator srci = uniqueVertexList->begin(); for (size_t v = 0; v < vd->vertexCount; ++v, ++srci) { for (ei = elems.begin(); ei != eiend; ++ei) { Ogre::VertexElement& elem = *ei; switch(elem.getSemantic()) { case Ogre::VES_POSITION: elem.baseVertexPointerToElement(pBase, &pFloat); *pFloat++ = srci->position.x; *pFloat++ = srci->position.y; *pFloat++ = srci->position.z; break; case Ogre::VES_NORMAL: elem.baseVertexPointerToElement(pBase, &pFloat); *pFloat++ = srci->normal.x; *pFloat++ = srci->normal.y; *pFloat++ = srci->normal.z; break; case Ogre::VES_DIFFUSE: elem.baseVertexPointerToElement(pBase, &pRGBA); *pRGBA = srci->colour; break; case Ogre::VES_TEXTURE_COORDINATES: elem.baseVertexPointerToElement(pBase, &pFloat); for (int t = 0; t < Ogre::VertexElement::getTypeCount(elem.getType()); ++t) { Ogre::Real val = srci->uv[elem.getIndex()][t]; *pFloat++ = val; } break; } } pBase += vertexSize; } vbuf->unlock(); } }