Overview
|
|
Interesting Bits
- Initially had issues incorporating skinning data, ultimately solving it by drawing debug bones to the screen
- Modeled custom, binary file format after the one used in Quake III BSP map files
- Beginning of file format serves as a table of contents, where each chapter holds a cluster of similar data (vertices, bones, etc.)
- Put vertices with the same materials together in triangle batches
Source Code
//------------------------------------------------------------------------ // FileFormat.hpp // // Author: Benjamin Klingler // Email: bhklingler@gmail.com // Website: http://www.benjaminklingler.com/ // // Copyright (C) 2013 Benjamin Klingler. All rights reserved. //------------------------------------------------------------------------ #ifndef BHKLINGLER_GUARD_FILEFORMAT_HPP #define BHKLINGLER_GUARD_FILEFORMAT_HPP #include "Vector2.hpp" #include "Vector3.hpp" #include "Matrix44.hpp" namespace bhklingler { namespace BLM { // Type definitions. typedef unsigned int Index; typedef unsigned int Size; // Constants. const unsigned int VERSION_NUMBER = 2; const Index INVALID_INDEX = static_cast< Index >( -1 ); const Size MAX_NUM_BONES = 4; const Size MAX_STRING_SIZE = 64; // Overall information about the scene, including compatibility and frames per second. struct Header { unsigned int version; unsigned int framesPerSecond; }; // The lumps serve as a table of contents, telling code where key pieces of information // are in the scene's binary file. enum LumpType { kSceneNodes, kTriangleBatches, kDiffuseMaterials, kAnimationFrames, kVertices, kIndices, kBones, kCameras, kMaxLumps }; // Lump data telling where information is in a file and how many bytes long it is. struct Lump { unsigned int offset; unsigned int length; }; // The vertex data of a scene node. struct Vertex { Vector3f position; Vector2f texCoord; Vector3f normal; float tangent[ 4 ]; float boneWeights[ MAX_NUM_BONES ]; Index boneIndices[ MAX_NUM_BONES ]; }; // Diffuse material textures. struct DiffuseMaterial { char name[ MAX_STRING_SIZE ]; char fileName[ MAX_STRING_SIZE ]; }; // Holds all information about a scene node. struct SceneNode { char name[ MAX_STRING_SIZE ]; Index parentIndex; Size numChildren; Index startTriangleBatchIndex; Size numTriangleBatches; Index startAnimationFrameIndex; Size numAnimationFrames; Index startBoneIndex; Size numBones; Matrix44f inverseWorldTransformation; }; // A batch of vertices for a particular material. struct TriangleBatch { Index materialIndex; Index startIndex; Size numIndices; }; // A frame of animation--for now, just a transformation. struct AnimationFrame { Matrix44f transformation; }; // A bone, which for now just references a scene node index. struct Bone { Index sceneNodeIndex; }; // A camera, referencing a scene node index. struct Camera { Index sceneNodeIndex; }; } } #endif // BHKLINGLER_GUARD_FILEFORMAT_HPP
//------------------------------------------------------------------------ // BlackMagicExporter.hpp // // Author: Benjamin Klingler // Email: bhklingler@gmail.com // Website: http://www.benjaminklingler.com/ // // Copyright (C) 2013 Benjamin Klingler. All rights reserved. //------------------------------------------------------------------------ namespace bhklingler { //------------------------------------------------------------------------ BlackMagicExporter::BlackMagicExporter() : m_pIGame( nullptr ) , m_nStartBoneIndexForCurrentSceneNode( BLM::INVALID_INDEX ) { } //------------------------------------------------------------------------ BlackMagicExporter::~BlackMagicExporter() { _destroy(); } //------------------------------------------------------------------------ int BlackMagicExporter::DoExport( const TCHAR* name, ExpInterface* ei, Interface* i, BOOL suppressprompts, DWORD options ) { try { // Redirect all output to the console window when in debug mode. RedirectIOToConsole(); // Begin exporting. const bool exportselected = 0 != (options & SCENE_EXPORT_SELECTED); _log( "Starting export process...\n" ); // Initialize IGame. m_pIGame = GetIGameInterface(); // Throw an exception if unable to initialize the IGame interface. if ( !m_pIGame ) throw std::exception( "Failed to initialize IGame interface" ); // Set the coordinate system to that of OpenGLs. IGameConversionManager * cm = GetConversionManager(); cm->SetCoordSystem( IGameConversionManager::IGAME_OGL ); // Initialize the IGame to ready for exporting. m_pIGame->InitialiseIGame( exportselected ); m_pIGame->SetStaticFrame( 0 ); // Map all nodes in the tree to their future index into scene nodes vector. _mapIGameNodesToSceneNodeIndices(); // Export animation frames first. _exportAnimationFrames(); // Export the nodes themselves. _exportNodes(); // Write everything to file. _writeToFile( name ); // Release initialized stuff. _destroy(); } catch ( std::exception& e ) { TSTR tstr( e.what() ); MessageBox( i->GetMAXHWnd(), tstr, _T( "Export Error" ), MB_OK | MB_ICONERROR ); _destroy(); } return TRUE; } //------------------------------------------------------------------------ void BlackMagicExporter::ShowAbout( HWND hwnd ) { } //------------------------------------------------------------------------ int BlackMagicExporter::ExtCount() { return 1; } //------------------------------------------------------------------------ const TCHAR* BlackMagicExporter::Ext( int /*n*/ ) { return _T("blm"); } //------------------------------------------------------------------------ const TCHAR* BlackMagicExporter::LongDesc() { return _T("Black Magic"); } //------------------------------------------------------------------------ const TCHAR* BlackMagicExporter::ShortDesc() { return _T("Black Magic"); } //------------------------------------------------------------------------ const TCHAR* BlackMagicExporter::AuthorName() { return _T("Benjamin Klingler"); } //------------------------------------------------------------------------ const TCHAR* BlackMagicExporter::CopyrightMessage() { return _T("Copyright (C) Benjamin Klingler 2012"); } //------------------------------------------------------------------------ const TCHAR* BlackMagicExporter::OtherMessage1() { return _T(""); } //------------------------------------------------------------------------ const TCHAR* BlackMagicExporter::OtherMessage2() { return _T(""); } //------------------------------------------------------------------------ unsigned int BlackMagicExporter::Version() { return 1; } //------------------------------------------------------------------------ BOOL BlackMagicExporter::SupportsOptions( int ext, DWORD options ) { return TRUE; } //------------------------------------------------------------------------ void BlackMagicExporter::_arrangeFacesByMaterial( IGameMesh* mesh, std::unordered_map< IGameMaterial*, std::vector< int > >& outMaterialToFaces, std::vector< int >& outPlainFaceIds ) { int nNumFaces = mesh->GetNumberOfFaces(); FaceEx* pFace; IGameMaterial* pMaterial; // Iterate through all of the faces again to see if there were any without materials. for( int i = 0; i < nNumFaces; ++i ) { pFace = mesh->GetFace( i ); if( pFace ) { pMaterial = mesh->GetMaterialFromFace( pFace ); if( pMaterial ) outMaterialToFaces[ pMaterial ].push_back( i ); else outPlainFaceIds.push_back( i ); } } } //------------------------------------------------------------------------ void BlackMagicExporter::_destroy() { if( m_fileOut.is_open() ) m_fileOut.close(); if ( m_pIGame ) { m_pIGame->ReleaseIGame(); m_pIGame = nullptr; } } //------------------------------------------------------------------------ void BlackMagicExporter::_mapIGameNodesToSceneNodeIndices() { // Acquire the amount of root nodes. const int nTopLevelNodeCount = m_pIGame->GetTopLevelNodeCount(); BLM::Index nIndexIntoSceneNodes = 0; // Iterate through all top level nodes and export each of their subtrees. for( int i = 0; i < nTopLevelNodeCount; ++i ) _mapIGameNodesToSceneNodeIndices( m_pIGame->GetTopLevelNode( i ), nIndexIntoSceneNodes ); } //------------------------------------------------------------------------ void BlackMagicExporter::_mapIGameNodesToSceneNodeIndices( IGameNode* root, BLM::Index& index ) { // If it's nonexistent, bail. if( !root ) return; // Determine how many children are dependent upon this particular node. const int nNumChildren = root->GetChildCount(); // Record the mapping, and increment global index counter. m_mapIGameNodesToSceneNodeIndex[ root ] = index; index += 1; // Iterate through each of the children nodes, if there are any. for( int i = 0; i < nNumChildren; ++i ) _mapIGameNodesToSceneNodeIndices( root->GetNodeChild( i ), index ); } //------------------------------------------------------------------------ void BlackMagicExporter::_exportAnimationFrames() { // Acquire the amount of root nodes. const int nTopLevelNodeCount = m_pIGame->GetTopLevelNodeCount(); const TimeValue nSceneStartTime = m_pIGame->GetSceneStartTime(); const TimeValue nSceneEndTime = m_pIGame->GetSceneEndTime(); const TimeValue nTicksPerFrame = m_pIGame->GetSceneTicks(); const TimeValue nFramesPerSecond = 4800 / nTicksPerFrame; // Determine the fps and total number of animation frames a node could have (so we can figure // out how big the animation vectors should be. m_cHeader.framesPerSecond = nFramesPerSecond; m_nMaxNumberAnimationFrames = abs( nSceneEndTime - nSceneStartTime ) / nTicksPerFrame; // Acquire all transformations in any sort of animation for this node. for( TimeValue time = nSceneStartTime; time < nSceneEndTime; time += nTicksPerFrame ) { // Iterate through all top level nodes and export each of their subtrees. for( int index = 0; index < nTopLevelNodeCount; ++index ) _exportAnimationFrame( m_pIGame->GetTopLevelNode( index ), time ); } } //------------------------------------------------------------------------ void BlackMagicExporter::_exportAnimationFrame( IGameNode* node, TimeValue time ) { // Bail if the node is invalid. if( !node ) return; // Data variables. BLM::AnimationFrame frame; const int nNumChildren = node->GetChildCount(); GMatrix matToParent; // If we have a parent, get it's inverse so that we can compute our local transformation. if( node->GetNodeParent() ) matToParent = node->GetNodeParent()->GetWorldTM( time ).Inverse(); // Copy over the local-space transformation matrix for this particular frame. memcpy( frame.transformation, reinterpret_cast< float* >( ( node->GetWorldTM( time ) * matToParent ).GetAddr() ), 16 * sizeof( float ) ); // Put the data into the map of nodes to animations (later to be // condensed into a single large vector). std::vector< BLM::AnimationFrame >& vecAnimationFrames = m_mapIGameNodesToAnimationFrames[ node ]; // If the vector is empty, reserve enough room for the amount of // matrices we'll be adding. if( vecAnimationFrames.empty() ) vecAnimationFrames.reserve( m_nMaxNumberAnimationFrames ); // Put the frame into the vector. vecAnimationFrames.emplace_back( frame ); // Iterate through each of the children nodes, if there are any. for( int i = 0; i < nNumChildren; ++i ) _exportAnimationFrame( node->GetNodeChild( i ), time ); } //------------------------------------------------------------------------ bool BlackMagicExporter::_isAnimationChanging( const std::vector< BLM::AnimationFrame >& vecAnimations ) const { auto iterBegin = vecAnimations.cbegin(); auto iterEnd = vecAnimations.cend(); auto iterOld = vecAnimations.cbegin(); // Iterate through each transformation in the animation vector. for( auto iter = iterBegin; iter != iterEnd; ++iter ) { // If the current transformation and the last are not the same, // the animation is changing, so return true. if( std::memcmp( reinterpret_cast< const void* >( ( *iter ).transformation ), reinterpret_cast< const void* >( ( *iterOld ).transformation ), 16 * sizeof( float ) ) != 0 ) return true; iterOld = iter; } // If we reach here, the animation is not changing. return false; } //------------------------------------------------------------------------ void BlackMagicExporter::_exportNodes() { // Acquire the amount of root nodes. const int nTopLevelNodeCount = m_pIGame->GetTopLevelNodeCount(); // Iterate through all top level nodes and export each of their subtrees. for( int i = 0; i < nTopLevelNodeCount; ++i ) _exportNode( m_pIGame->GetTopLevelNode( i ) ); } //------------------------------------------------------------------------ void BlackMagicExporter::_exportNode( IGameNode* node, BLM::Index parentIndex ) { // Bail if the node is non-existent. if( !node ) return; Box3 box; node->GetIGameObject()->GetBoundingBox( box ); if( box.Min() < m_boundingBox.Min() ) m_boundingBox.pmin = box.Min(); if( box.Max() > m_boundingBox.Max() ) m_boundingBox.pmax = box.Max(); // Data variables. Matrix44 matrixToLocalSpace; BLM::SceneNode sceneNode; IGameObject* pIGameObject = node->GetIGameObject(); // Bail if the game object is non-existent. if( !pIGameObject ) return; const int nNumChildren = node->GetChildCount(); const BLM::Index nCurrentSceneNodeIndex = static_cast< BLM::Index >( m_vecSceneNodes.size() ); // Debug message notifying exporting for this particular scene node has begun. _log( "Exporting scene node '%s'...\n", node->GetName() ); // Acquire the name of the node, padding it to fill up 64 bytes. strcpy_s( sceneNode.name, BLM::MAX_STRING_SIZE, node->GetName() ); // Set up who the parent is (-1 if top-most node) and record index start. sceneNode.parentIndex = parentIndex; sceneNode.startTriangleBatchIndex = static_cast< BLM::Index >( m_vecTriangleBatches.size() ); sceneNode.startBoneIndex = static_cast< BLM::Index >( m_vecBones.size() ); m_nStartBoneIndexForCurrentSceneNode = sceneNode.startBoneIndex; // Acquire the transformation matrices needed. matrixToLocalSpace.setData( reinterpret_cast< float* >( node->GetWorldTM( 0 ).Inverse().GetAddr() ) ); memcpy( sceneNode.inverseWorldTransformation, matrixToLocalSpace.getData(), 16 * sizeof( float ) ); // Figure out what type of object this is. if( pIGameObject->GetIGameType() == IGameObject::IGAME_MESH ) { // Gather any new materials beforehand, if any. _log( "\tExporting materials...\n" ); _exportMaterials( node->GetNodeMaterial() ); // Gather all the vertices and indices needed for this mesh. _log( "\tExporting faces...\n" ); _exportMesh( reinterpret_cast< IGameMesh* >( pIGameObject ), matrixToLocalSpace ); } else if( pIGameObject->GetIGameType() == IGameObject::IGAME_CAMERA ) { // If we found a camera, add it to our list of cameras. BLM::Camera camera; camera.sceneNodeIndex = nCurrentSceneNodeIndex; m_vecCameras.emplace_back( camera ); } // Acquire the animation data for this node. std::vector< BLM::AnimationFrame >& vecAnimationFramesForNode = m_mapIGameNodesToAnimationFrames[ node ]; // If the animation is constant, get rid of everything but the first frame. if( !_isAnimationChanging( vecAnimationFramesForNode ) && vecAnimationFramesForNode.size() > 1 ) vecAnimationFramesForNode.erase( vecAnimationFramesForNode.begin() + 1, vecAnimationFramesForNode.end() ); // Set the number of indices and stick the mesh into the vector. sceneNode.numTriangleBatches = static_cast< BLM::Size >( m_vecTriangleBatches.size() ) - sceneNode.startTriangleBatchIndex; sceneNode.numChildren = static_cast< BLM::Size >( nNumChildren ); sceneNode.startAnimationFrameIndex = static_cast< BLM::Index >( m_vecAnimationFrames.size() ); sceneNode.numAnimationFrames = static_cast< BLM::Size >( vecAnimationFramesForNode.size() ); sceneNode.numBones = static_cast< BLM::Size >( m_vecBones.size() ) - sceneNode.startBoneIndex; // Add the mesh and its animation frames to their respected containers. m_vecSceneNodes.emplace_back( sceneNode ); m_vecAnimationFrames.insert( m_vecAnimationFrames.end(), vecAnimationFramesForNode.begin(), vecAnimationFramesForNode.end() ); // Iterate through each of the children nodes, if there are any. for( int i = 0; i < nNumChildren; ++i ) _exportNode( node->GetNodeChild( i ), nCurrentSceneNodeIndex ); // Free memory. node->ReleaseIGameObject(); } //------------------------------------------------------------------------ void BlackMagicExporter::_exportMaterials( IGameMaterial* material ) { // Bail if the given material was non-existent. if( !material ) return; // Data variables. bool bIsDiffuseTexture; int nNumSubMaterials = material->GetSubMaterialCount(); int nNumTextureMaps = material->GetNumberOfTextureMaps(); BLM::DiffuseMaterial newMaterial; IGameTextureMap* pIGameTextureMap; // Iterate through each of the texture maps in the material. for( int i = 0; i < nNumTextureMaps; ++i ) { pIGameTextureMap = material->GetIGameTextureMap( i ); // Skip over this one if the texture map is non-existent. if( !pIGameTextureMap ) continue; // Determine if the node is a diffuse texture. bIsDiffuseTexture = pIGameTextureMap->GetStdMapSlot() == ID_DI; // If it is, export the material. if( bIsDiffuseTexture ) { // If it's a diffuse texture and has not already been stored, store it. if( m_mapDiffuseMaterialsToIndex.find( material ) == m_mapDiffuseMaterialsToIndex.end() ) { // Set the material information to be written to the file. strcpy_s( newMaterial.name, BLM::MAX_STRING_SIZE, material->GetMaterialName() ); _extractFileName( newMaterial.fileName, pIGameTextureMap->GetBitmapFileName() ); // Record the new material so we may reference it later (map for telling the // exporter which material id corresponds to which index in the // vector-to-be-written-to-file). m_mapDiffuseMaterialsToIndex[ material ] = static_cast< BLM::Index >( m_vecDiffuseMaterials.size() ); m_vecDiffuseMaterials.emplace_back( newMaterial ); } } } // Recurse through each of the sub-materials. for( int i = 0; i < nNumSubMaterials; ++i ) _exportMaterials( material->GetSubMaterial( i ) ); } //------------------------------------------------------------------------ void BlackMagicExporter::_exportMesh( IGameMesh* mesh, const Matrix44& matrixToLocalSpace ) { std::unordered_map< IGameMaterial*, std::vector< int > > mapMaterialsToFaces; std::vector< int > vecPlainFaces; // Bail if the mesh cannot initialize. if( !mesh || !mesh->InitializeData() ) return; // Arrange the faces by material for nice outputting. _log( "\t\tArranging faces by material (because I'm nice)...\n" ); _arrangeFacesByMaterial( mesh, mapMaterialsToFaces, vecPlainFaces ); // For each material used in this mesh, export all of the faces associated. _log( "\t\tExporting faces with materials...\n" ); for( auto iter = mapMaterialsToFaces.begin(); iter != mapMaterialsToFaces.end(); ++iter ) _exportFacesInVector( mesh, iter->first, matrixToLocalSpace, iter->second ); // Finally, export any faces that did not have any materials associated with it. _log( "\t\tExporting faces without materials...\n" ); _exportFacesInVector( mesh, nullptr, matrixToLocalSpace, vecPlainFaces ); } //------------------------------------------------------------------------ void BlackMagicExporter::_exportFace( IGameMesh* mesh, FaceEx* face, const Matrix44& matrixToLocalSpace ) { BLM::Vertex vertex; BLM::Index nIndexToUse = 0; Point3 tangent, bitangent; int nTangentBiTangentIndex = 0; // Bail if there's no face to export. if( !face ) return; // Iterate over each corner of the current face. for( int k = 0; k < 3; ++k ) { // Acquire the tangent / bitangent from the mesh. nTangentBiTangentIndex = mesh->GetFaceVertexTangentBinormal( face->meshFaceIndex, k ); tangent = mesh->GetTangent( nTangentBiTangentIndex ); bitangent = mesh->GetBinormal( nTangentBiTangentIndex ); // Acquire vertex data. vertex.position = mesh->GetVertex( face->vert[ k ] ); vertex.texCoord = mesh->GetTexVertex( face->texCoord[ k ] ); vertex.texCoord.y *= -1.0f; vertex.normal = mesh->GetNormal( face->norm[ k ] ); vertex.tangent = tangent; // Calculate the handedness so that we do not have to store the bitangent. vertex.tangent.w = ( DotProd( CrossProd( vertex.normal, tangent ), bitangent ) < 0.0f ) ? -1.0f : 1.0f; // If the mesh we are on is skinned, export the bone data, too. if( mesh->IsObjectSkinned() ) _exportSkinningDataToVertex( mesh->GetIGameSkin(), face->vert[ k ], vertex ); else { // Put the data into local space. vertex.position = matrixToLocalSpace.calcTransformPoint3D( vertex.position ); vertex.normal = matrixToLocalSpace.calcTransformVector3D( vertex.normal ); vertex.normal = vertex.normal.Normalize(); } // Check if this vertex has already been added. If it hasn't, add it in; otherwise, // use the existing index already placed. if( m_mapVerticesToIndices.find( vertex ) == m_mapVerticesToIndices.end() ) { nIndexToUse = static_cast< BLM::Index >( m_vecVertices.size() ); m_mapVerticesToIndices[ vertex ] = nIndexToUse; m_vecVertices.emplace_back( vertex ); } else nIndexToUse = m_mapVerticesToIndices[ vertex ]; // Add index to list of indices. m_vecIndices.emplace_back( nIndexToUse ); } } //------------------------------------------------------------------------ void BlackMagicExporter::_exportFacesInVector( IGameMesh* mesh, IGameMaterial* material, const Matrix44& matrixToLocalSpace, const std::vector< int >& vecFaceIds ) { BLM::TriangleBatch triangleBatch; // Start filling in triangle batch information. triangleBatch.materialIndex = ( material ? m_mapDiffuseMaterialsToIndex[ material ] : BLM::INVALID_INDEX ); triangleBatch.startIndex = static_cast< BLM::Index >( m_vecIndices.size() ); // Iterate through each face and export it. for( auto faceIter = vecFaceIds.begin(); faceIter != vecFaceIds.end(); ++faceIter ) _exportFace( mesh, mesh->GetFace( *faceIter ), matrixToLocalSpace ); // Finish up filling out the triangle batch information, then record it for file output. triangleBatch.numIndices = static_cast< BLM::Size >( m_vecIndices.size() ) - triangleBatch.startIndex; m_vecTriangleBatches.emplace_back( triangleBatch ); } //------------------------------------------------------------------------ void BlackMagicExporter::_exportSkinningDataToVertex( IGameSkin* skinModifier, int vertexIndexIntoFace, BLM::Vertex& outVertex ) { float fTotalWeight = 0; // Clear out any previously gathered BoneWeights. m_vecBoneWeights.clear(); // Go through each of the bones connected to this particular vertex. for( int i = 0; i < skinModifier->GetNumberOfBones( vertexIndexIntoFace ); ++i ) { BoneWeight boneWeight; // Acquire the necessary data from each bone. boneWeight.bone = skinModifier->GetIGameBone( vertexIndexIntoFace, i ); boneWeight.weight = skinModifier->GetWeight( vertexIndexIntoFace, i ); // Stick it in a vector to-be-reassessed later. m_vecBoneWeights.emplace_back( boneWeight ); } // Sort the bone weights according to their individual weights. std::sort( m_vecBoneWeights.begin(), m_vecBoneWeights.end() ); // Restrict the number of bones we allow per vertex (kill off any extras). m_vecBoneWeights.resize( BLM::MAX_NUM_BONES ); // Iterate through each of the picked bones, and calculate the total weight. for( auto iter = m_vecBoneWeights.begin(); iter != m_vecBoneWeights.end(); ++iter ) { BoneWeight& boneWeight = *iter; // Only total up the known weights (prevent from adding undefined weight // from nonexistent bone). if( boneWeight.bone ) fTotalWeight += boneWeight.weight; } // Compute one-over-total-weight so that we can multiply it by each weight to get a percentage. const float fOneOverTotalWeight = 1.0f / fTotalWeight; size_t i = 0; // Iterate through each weight and update their value to be a percentage of the total // weight sum calculated. for( auto iter = m_vecBoneWeights.begin(); iter != m_vecBoneWeights.end(); ++iter, ++i ) { BLM::Bone bone; BoneWeight& boneWeight = *iter; // If we reach a bone that doesn't exist, set it's index to 0, since we can guarantee // it will exist, and set the weight to 0 so that the shader inside the importer will // essentially disregard this. if( !boneWeight.bone ) { outVertex.boneIndices[ i ] = 0; outVertex.boneWeights[ i ] = 0.0f; continue; } // Find the scene node index of the bone, and adjust it's weight to be a percentage. const BLM::Index nBoneSceneNodeIndex = m_mapIGameNodesToSceneNodeIndex[ boneWeight.bone ]; bone.sceneNodeIndex = nBoneSceneNodeIndex; boneWeight.weight *= fOneOverTotalWeight; // See if the bone is already there and find a local index for it if it is / isn't. auto iterLocationOfBone = std::find( m_vecBones.begin() + m_nStartBoneIndexForCurrentSceneNode, m_vecBones.end(), bone ); BLM::Index nLocalIndexOfBoneReference = static_cast< BLM::Index >( iterLocationOfBone - ( m_vecBones.begin() + m_nStartBoneIndexForCurrentSceneNode) ); const bool bIsReferencingNewBone = ( iterLocationOfBone == m_vecBones.end() ); // Add the bone into this triangle batch's used bone list if it is not already in there. if( bIsReferencingNewBone ) { nLocalIndexOfBoneReference = static_cast< BLM::Index >( m_vecBones.size() ) - m_nStartBoneIndexForCurrentSceneNode; m_vecBones.emplace_back( bone ); } // Set the vertex data. outVertex.boneWeights[ i ] = boneWeight.weight; outVertex.boneIndices[ i ] = nLocalIndexOfBoneReference; } } //------------------------------------------------------------------------ void BlackMagicExporter::_extractFileName( char* dest, const char* src ) { std::string sInput( src ); std::string sDestination; int nLocationOfSlash = -1; size_t nLocationOfExtension = sInput.find_last_of( '.' ) + 1; // If the texture file name is incompatible, rewrite it out as .PNG. if( strcmp( &sInput[ nLocationOfExtension ], "dds" ) == 0 ) sInput.replace( nLocationOfExtension, 3, "png" ); // Find the location of a slash to get rid of any file paths. nLocationOfSlash = static_cast< int >( sInput.find_last_of( '\\' ) ); if( nLocationOfSlash == std::string::npos ) nLocationOfSlash = static_cast< int >( sInput.find_last_of( '/' ) ); // Output the new string. strcpy_s( dest, BLM::MAX_STRING_SIZE, sInput.substr( nLocationOfSlash + 1 ).c_str() ); } //------------------------------------------------------------------------ void BlackMagicExporter::_log( const char* format, ... ) { #ifdef BLM_ENABLE_MESSAGE_LOGGING va_list args; va_start( args, format ); vprintf( format, args ); va_end( args ); #endif } //------------------------------------------------------------------------ void BlackMagicExporter::_printDebugInformation() { _log( "\n\n============================================\n" ); _log( "Number of Meshes: %d\n", m_vecSceneNodes.size() ); _log( "Number of Triangle Batches: %d\n", m_vecTriangleBatches.size() ); _log( "Number of Diffuse Materials: %d\n", m_vecDiffuseMaterials.size() ); _log( "Number of Vertices: %d\n", m_vecVertices.size() ); _log( "Number of Indices: %d\n", m_vecIndices.size() ); _log( "Number of Cameras: %d\n", m_vecCameras.size() ); _log( "============================================\n\n" ); } //------------------------------------------------------------------------ void BlackMagicExporter::_writeToFile( const TCHAR* fileName ) { // Open file for writing. m_fileOut.open( fileName, std::ios_base::binary ); // Bail if the file could not open. if( !m_fileOut.is_open() ) { _log( "Unable to open file '%s'!\n", fileName ); return; } // Output the final results of all the exporting. _printDebugInformation(); // Write out the file. _log( "Writing scene to file: '%s'\n", fileName ); _writeHeader(); _writeLumps(); _writeData( m_rgLumps[ BLM::kSceneNodes ], m_vecSceneNodes ); _writeData( m_rgLumps[ BLM::kTriangleBatches ], m_vecTriangleBatches ); _writeData( m_rgLumps[ BLM::kDiffuseMaterials ], m_vecDiffuseMaterials ); _writeData( m_rgLumps[ BLM::kAnimationFrames ], m_vecAnimationFrames ); _writeData( m_rgLumps[ BLM::kVertices ], m_vecVertices ); _writeData( m_rgLumps[ BLM::kIndices ], m_vecIndices ); _writeData( m_rgLumps[ BLM::kBones ], m_vecBones ); _writeData( m_rgLumps[ BLM::kCameras ], m_vecCameras ); _log( "Done!\n" ); } /* * Note: Other source code removed. */ }