/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2005 The OGRE Team 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. ----------------------------------------------------------------------------- */ #include "Ogre.h" #include "OgreMeshSerializer.h" #include "OgreSkeletonSerializer.h" #include "OgreDefaultHardwareBufferManager.h" #include #include using namespace std; void help(void) { // Print help message cout << endl << "OgreMeshUpgrader: Upgrades .mesh files to the latest version." << endl; cout << "Provided for OGRE by Steve Streeting 2004" << endl << endl; cout << "Usage: OgreMeshUpgrader [-e] sourcefile [destfile] " << endl; cout << "-e = DON'T generate edge lists (for stencil shadows)" << endl; cout << "-t = Generate tangents (for normal mapping)" << endl; cout << "sourcefile = name of file to convert" << endl; cout << "destfile = optional name of file to write to. If you don't" << endl; cout << " specify this OGRE overwrites the existing file." << endl; cout << endl; } using namespace Ogre; // Crappy globals // NB some of these are not directly used, but are required to // instantiate the singletons used in the dlls LogManager* logMgr; Math* mth; MaterialManager* matMgr; SkeletonManager* skelMgr; MeshSerializer* meshSerializer; SkeletonSerializer* skeletonSerializer; DefaultHardwareBufferManager *bufferManager; ResourceGroupManager* rgm; MeshManager* meshMgr; String describeSemantic(VertexElementSemantic sem) { switch (sem) { case VES_POSITION: return "Positions"; case VES_NORMAL: return "Normals"; case VES_BLEND_WEIGHTS: return "Blend Weights"; case VES_BLEND_INDICES: return "Blend Indices"; case VES_DIFFUSE: return "Diffuse"; case VES_SPECULAR: return "Specular"; case VES_TEXTURE_COORDINATES: return "Texture coordinates"; case VES_BINORMAL: return "Binormals"; case VES_TANGENT: return "Tangents"; }; return ""; } void displayVertexBuffers(VertexDeclaration::VertexElementList& elemList) { // Iterate per buffer unsigned short currentBuffer = 999; unsigned short elemNum = 0; VertexDeclaration::VertexElementList::iterator i, iend; iend = elemList.end(); for (i = elemList.begin(); i != iend; ++i) { if (i->getSource() != currentBuffer) { currentBuffer = i->getSource(); cout << "> Buffer " << currentBuffer << ":" << endl; } cout << " - Element " << elemNum++ << ": " << describeSemantic(i->getSemantic()); if (i->getSemantic() == VES_TEXTURE_COORDINATES) { cout << " (index " << i->getIndex() << ")"; } cout << endl; } } // Sort routine for VertexElement bool vertexElementLess(const VertexElement& e1, const VertexElement& e2) { // Sort by source first if (e1.getSource() < e2.getSource()) { return true; } else if (e1.getSource() == e2.getSource()) { // Use ordering of semantics to sort if (e1.getSemantic() < e2.getSemantic()) { return true; } else if (e1.getSemantic() == e2.getSemantic()) { // Use index to sort if (e1.getIndex() < e2.getIndex()) { return true; } } } return false; } void copyElems(VertexDeclaration* decl, VertexDeclaration::VertexElementList* elemList) { elemList->clear(); const VertexDeclaration::VertexElementList& origElems = decl->getElements(); VertexDeclaration::VertexElementList::const_iterator i, iend; iend = origElems.end(); for (i = origElems.begin(); i != iend; ++i) { elemList->push_back(*i); } elemList->sort(VertexDeclaration::vertexElementLess); } // Utility function to allow the user to modify the layout of vertex buffers. void reorganiseVertexBuffers(const String& desc, Mesh& mesh, VertexData* vertexData) { cout << endl << desc << ":- " << endl; // Copy elements into a list VertexDeclaration::VertexElementList elemList; copyElems(vertexData->vertexDeclaration, &elemList); bool finish = false; bool anyChanges = false; while (!finish) { displayVertexBuffers(elemList); cout << endl; cout << "Options: (a)utomatic" << endl; cout << " (m)ove element" << endl; cout << " (d)elete element" << endl; cout << " (r)eset" << endl; cout << " (f)inish" << endl; String response = ""; while (response.empty()) { cin >> response; StringUtil::toLowerCase(response); if (response == "m") { String moveResp; cout << "Which element do you want to move (type number): "; cin >> moveResp; if (!moveResp.empty()) { int eindex = StringConverter::parseInt(moveResp); VertexDeclaration::VertexElementList::iterator movei = elemList.begin(); std::advance(movei, eindex); cout << endl << "Move element " << eindex << "(" + describeSemantic(movei->getSemantic()) << ") to which buffer: "; cin >> moveResp; if (!moveResp.empty()) { int bindex = StringConverter::parseInt(moveResp); // Move (note offset will be wrong) *movei = VertexElement(bindex, 0, movei->getType(), movei->getSemantic(), movei->getIndex()); elemList.sort(vertexElementLess); anyChanges = true; } } } else if (response == "a") { // Automatic VertexDeclaration* newDcl = vertexData->vertexDeclaration->getAutoOrganisedDeclaration( mesh.hasSkeleton()); copyElems(newDcl, &elemList); HardwareBufferManager::getSingleton().destroyVertexDeclaration(newDcl); anyChanges = true; } else if (response == "d") { String moveResp; cout << "Which element do you want to delete (type number): "; cin >> moveResp; if (!moveResp.empty()) { int eindex = StringConverter::parseInt(moveResp); VertexDeclaration::VertexElementList::iterator movei = elemList.begin(); std::advance(movei, eindex); cout << std::endl << "Delete element " << eindex << "(" + describeSemantic(movei->getSemantic()) << ")?: "; cin >> moveResp; StringUtil::toLowerCase(moveResp); if (moveResp == "y") { elemList.erase(movei); anyChanges = true; } } } else if (response == "r") { // reset copyElems(vertexData->vertexDeclaration, &elemList); anyChanges = false; } else if (response == "f") { // finish finish = true; } else { response == ""; } } } if (anyChanges) { String response; while (response.empty()) { displayVertexBuffers(elemList); cout << "Really reorganise the vertex buffers this way?"; cin >> response; StringUtil::toLowerCase(response); if (response == "y") { VertexDeclaration* newDecl = HardwareBufferManager::getSingleton().createVertexDeclaration(); VertexDeclaration::VertexElementList::iterator i, iend; iend = elemList.end(); unsigned short currentBuffer = 999; size_t offset; for (i = elemList.begin(); i != iend; ++i) { // Calc offsets since reorg changes them if (i->getSource() != currentBuffer) { offset = 0; currentBuffer = i->getSource(); } newDecl->addElement( currentBuffer, offset, i->getType(), i->getSemantic(), i->getIndex()); offset += VertexElement::getTypeSize(i->getType()); } // Usages don't matter here since we're onlly exporting BufferUsageList bufferUsages; for (size_t u = 0; u <= newDecl->getMaxSource(); ++u) bufferUsages.push_back(HardwareBuffer::HBU_STATIC_WRITE_ONLY); vertexData->reorganiseBuffers(newDecl, bufferUsages); } else if (response == "n") { // do nothing } else { response = ""; } } } } // Utility function to allow the user to modify the layout of vertex buffers. void reorganiseVertexBuffers(Mesh& mesh) { if (mesh.sharedVertexData) { reorganiseVertexBuffers("Shared Geometry", mesh, mesh.sharedVertexData); } Mesh::SubMeshIterator smIt = mesh.getSubMeshIterator(); unsigned short idx = 0; while (smIt.hasMoreElements()) { SubMesh* sm = smIt.getNext(); if (!sm->useSharedVertices) { StringUtil::StrStreamType str; str << "SubMesh " << idx++; reorganiseVertexBuffers(str.str(), mesh, sm->vertexData); } } } int main(int numargs, char** args) { if (numargs < 2) { help(); return -1; } logMgr = new LogManager(); logMgr->createLog("OgreMeshUpgrade.log", true); rgm = new ResourceGroupManager(); mth = new Math(); matMgr = new MaterialManager(); matMgr->initialise(); skelMgr = new SkeletonManager(); meshSerializer = new MeshSerializer(); skeletonSerializer = new SkeletonSerializer(); bufferManager = new DefaultHardwareBufferManager(); // needed because we don't have a rendersystem meshMgr = new MeshManager(); // don't pad during upgrade meshMgr->setBoundsPaddingFactor(0.0f); UnaryOptionList unOptList; BinaryOptionList binOptList; unOptList["-e"] = false; unOptList["-t"] = false; int startIdx = findCommandLineOpts(numargs, args, unOptList, binOptList); String source(args[startIdx]); logMgr->createLog("OgreMeshUpgrader.log"); // Load the mesh struct stat tagStat; FILE* pFile = fopen( source.c_str(), "rb" ); if (!pFile) { OGRE_EXCEPT(Exception::ERR_FILE_NOT_FOUND, "File " + source + " not found.", "OgreMeshUpgrade"); } stat( source.c_str(), &tagStat ); MemoryDataStream* memstream = new MemoryDataStream(source, tagStat.st_size, true); fread( (void*)memstream->getPtr(), tagStat.st_size, 1, pFile ); fclose( pFile ); Mesh mesh(meshMgr, "conversion", 0, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); DataStreamPtr stream(memstream); meshSerializer->importMesh(stream, &mesh); // Write out the converted mesh String dest; if (numargs == startIdx + 2) { dest = args[startIdx + 1]; } else { dest = source; } String response; // Check to see whether we would like to reorganise vertex buffers std::cout << "\nWould you like to reorganise the vertex buffers for this mesh?"; while (response.empty()) { cin >> response; StringUtil::toLowerCase(response); if (response == "y") { reorganiseVertexBuffers(mesh); } else if (response == "n") { // Do nothing } else { response = ""; } } // Prompt for LOD generation bool genLod = false; response = ""; if (mesh.getNumLodLevels() > 1) { std::cout << "\nMesh already contains level-of detail information.\n" "Do you want to: (u)se it, (r)eplace it, or (d)rop it?"; while (response.empty()) { cin >> response; StringUtil::toLowerCase(response); if (response == "u") { // Do nothing } else if (response == "d") { mesh.removeLodLevels(); } else if (response == "r") { genLod = true; } else { response = ""; } }// while response == "" } else // no existing LOD { std::cout << "\nWould you like to generate LOD information? (y/n)"; while (response == "") { cin >> response; StringUtil::toLowerCase(response); if (response == "n") { // Do nothing } else if (response == "y") { genLod = true; } } } if (genLod) { unsigned short numLod; ProgressiveMesh::VertexReductionQuota quota; Real reduction; cout << "\nHow many extra LOD levels would you like to generate?"; cin >> numLod; cout << "\nWhat unit of reduction would you like to use:" "\n(f)ixed or (p)roportional?"; cin >> response; StringUtil::toLowerCase(response); if (response == "f") { quota = ProgressiveMesh::VRQ_CONSTANT; cout << "\nHow many vertices should be removed at each LOD?"; } else { quota = ProgressiveMesh::VRQ_PROPORTIONAL; cout << "\nWhat proportion of remaining vertices should be removed " << "at each LOD (e.g. 0.5)?"; } cin >> reduction; cout << "\nEnter the distance for each LOD to come into effect."; Real distance; Mesh::LodDistanceList distanceList; for (unsigned short iLod = 0; iLod < numLod; ++iLod) { cout << "\nLOD Level " << (iLod+1) << ":"; cin >> distance; distanceList.push_back(distance); } mesh.generateLodLevels(distanceList, quota, reduction); } // Make sure we generate edge lists, provided they are not deliberately disabled UnaryOptionList::iterator ui = unOptList.find("-e"); if (!ui->second) { cout << "\nGenerating edge lists.." << std::endl; mesh.buildEdgeList(); } // Generate tangents? ui = unOptList.find("-t"); bool generateTangents = ui->second; if (generateTangents) { unsigned short srcTex, destTex; bool existing = mesh.suggestTangentVectorBuildParams(srcTex, destTex); if (existing) { std::cout << "\nThis mesh appears to already have a set of 3D texture coordinates, " << "which would suggest tangent vectors have already been calculated. Do you really " << "want to generate new tangent vectors (may duplicate)? (y/n)"; while (response == "") { cin >> response; StringUtil::toLowerCase(response); if (response == "y") { // Do nothing } else if (response == "n") { generateTangents = false; } else { response = ""; } } } if (generateTangents) { cout << "Generating tangent vectors...." << std::endl; mesh.buildTangentVectors(srcTex, destTex); } } meshSerializer->exportMesh(&mesh, dest); delete meshMgr; delete skeletonSerializer; delete meshSerializer; delete skelMgr; delete matMgr; delete mth; delete rgm; delete logMgr; return 0; }