/* =========================================================================== maya2q3 - export .md3 files from maya Copyright (C) 2005 HermitWorks Entertainment Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. =========================================================================== */ #include "common.h" #include #include "string.h" void check_status( MStatus status ) { if( status != MS::kSuccess ) throw maya_error( status ); } MayaAnimIt::MayaAnimIt( void ) { saveTime = MAnimControl::currentTime(); startTime = MAnimControl::minTime(); stepTime = MTime( MAnimControl::playbackBy(), MTime::uiUnit() ); currTime = startTime; currFrame = 0; numFrames = max( (uint)ceil( (MAnimControl::maxTime() - startTime).as( MTime::uiUnit()) / stepTime.as( MTime::uiUnit() ) ), (uint)1 ); MAnimControl::setCurrentTime( currTime ); } MayaAnimIt::MayaAnimIt( MTime startTime, MTime endTime ) { saveTime = MAnimControl::currentTime(); MayaAnimIt::startTime = startTime; stepTime = MTime( MAnimControl::playbackBy(), MTime::uiUnit() ); currTime = startTime; currFrame = 0; numFrames = max( (uint)ceil( (endTime - startTime).as( MTime::uiUnit()) / stepTime.as( MTime::uiUnit() ) ), (uint)1 ); MAnimControl::setCurrentTime( currTime ); } MayaAnimIt::MayaAnimIt( MTime startTime, uint numFrames ) { saveTime = MAnimControl::currentTime(); MayaAnimIt::startTime = startTime; stepTime = MTime( MAnimControl::playbackBy(), MTime::uiUnit() ); currTime = startTime; currFrame = 0; MayaAnimIt::numFrames = numFrames; MAnimControl::setCurrentTime( currTime ); } MayaAnimIt::MayaAnimIt( MTime startTime, MTime endTime, MTime stepTime ) { saveTime = MAnimControl::currentTime(); MayaAnimIt::startTime = startTime; MayaAnimIt::stepTime = stepTime; currTime = startTime; currFrame = 0; numFrames = max( (uint)ceil( (endTime - startTime).as( MTime::uiUnit()) / stepTime.as( MTime::uiUnit() ) ), (uint)1 ); MAnimControl::setCurrentTime( currTime ); } MayaAnimIt::MayaAnimIt( MTime startTime, uint numFrames, MTime stepTime ) { saveTime = MAnimControl::currentTime(); MayaAnimIt::startTime = startTime; MayaAnimIt::stepTime = stepTime; currTime = startTime; currFrame = 0; MayaAnimIt::numFrames = numFrames; MAnimControl::setCurrentTime( currTime ); } MayaAnimIt::~MayaAnimIt( void ) { MAnimControl::setCurrentTime( saveTime ); } bool MayaAnimIt::isDone( void ) const { return currFrame >= numFrames; } void MayaAnimIt::next( void ) { currFrame++; currTime += stepTime; MAnimControl::setCurrentTime( currTime ); } uint MayaAnimIt::frameNum( void ) const { return currFrame; } uint MayaAnimIt::frameCount( void ) const { return numFrames; } namespace gl { #ifdef _MSC_VER Context::Context( void ) : m_glrc( null ), m_gldc( null ) { } bool Context::operator == ( const Context &other ) const { return m_glrc == other.m_glrc && m_gldc == other.m_gldc; } bool Context::operator != ( const Context &other ) const { return m_glrc != other.m_glrc || m_gldc != other.m_gldc; } Context::operator bool( void ) const { return m_glrc && m_gldc; } bool Context::MakeCurrent( void ) const { return wglMakeCurrent( m_gldc, m_glrc ) != 0; } /* static */ Context Context::GetCurrent( void ) { Context ret; ret.m_gldc = wglGetCurrentDC(); ret.m_glrc = wglGetCurrentContext(); return ret; } #elif __linux__ Context::Context( void ) : m_glrc( null ), m_gldc( null ), m_gldr( null ) { } bool Context::operator == ( const Context &other ) const { return m_glrc == other.m_glrc && m_gldc == other.m_gldc && m_gldr == other.m_gldr; } bool Context::operator != ( const Context &other ) const { return m_glrc != other.m_glrc || m_gldc != other.m_gldc || m_gldr != other.m_gldr; } Context::operator bool( void ) const { return m_glrc && m_gldc && m_gldr; } bool Context::MakeCurrent( void ) const { return glXMakeCurrent(m_gldc, m_gldr, m_glrc) != 0; } /* static */ Context Context::GetCurrent( void ) { Context ret; ret.m_gldc = glXGetCurrentDisplay(); ret.m_glrc = glXGetCurrentContext(); ret.m_gldr = glXGetCurrentDrawable(); return ret; } #else #error ToDo: implement on OS X #endif /* static */ Context Context::GetNull( void ) { Context ret; //is null by default return ret; } }; namespace q3 { qname MangleMayaShaderName( const char *s ) { q3::qname name = s; //replace a double _ at the end of the name with a . for( size_t i = name.Length(); i > 0; i-- ) if( name[i] == '_' && name[i - 1] == '_' ) { name[i - 1] = '.'; //shuffle characters back, stop after the null do { name[i] = name[i + 1]; } while( name[i++] ); break; } name.Replace( '_', '/' ); return name; } qname GetShaderName( const MObject &shaderObj ) { MFnDependencyNode shader( shaderObj ); if( shader.typeId() == shader::g_shaderTypeID ) { MPlug shaderName = shader.findPlug( "shaderName", true ); if( !shaderName.isNull() ) { MString value; if( shaderName.getValue( value ) == MS::kSuccess ) return q3::qname( value.asChar() ); } } return MangleMayaShaderName( shader.name().asChar() ); } qname GetShaderSetName( const MObject &setNode ) { MFnDependencyNode node( setNode ); MPlug shaderPlug = node.findPlug( "surfaceShader", true ); assert( !shaderPlug.isNull(), "can't find shader plug" ); MPlugArray connected; shaderPlug.connectedTo( connected, true, false ); assert( connected.length() == 1, "bad shader connection" ); return GetShaderName( connected[0].node() ); } affine_t MayaMatrixToQ3( const MMatrix &mat ) { affine_t ret; //transposing as we go (maya matrices are row-major) ret.axis[0][0] = (float)mat[0][0]; ret.axis[0][1] = (float)mat[0][1]; ret.axis[0][2] = (float)mat[0][2]; ret.axis[1][0] = (float)mat[1][0]; ret.axis[1][1] = (float)mat[1][1]; ret.axis[1][2] = (float)mat[1][2]; ret.axis[2][0] = (float)mat[2][0]; ret.axis[2][1] = (float)mat[2][1]; ret.axis[2][2] = (float)mat[2][2]; ret.origin[0] = (float)mat[3][0]; ret.origin[1] = (float)mat[3][1]; ret.origin[2] = (float)mat[3][2]; return ret; } bool DagIsTag( const MDagPath &path ) { if( path.hasFn( MFn::kTransform ) && path.hasFn( MFn::kDependencyNode ) ) { MFnDependencyNode node( path.node() ); MPlug treatAsTag = node.findPlug( "treat_as_tag", true ); if( !treatAsTag.isNull() ) { MString val; treatAsTag.getValue( val ); if( val != "" ) return true; } /* MString name = node.name(); if( name.length() > 4 ) { const char *str = name.toLowerCase().asChar(); if( str[0] == 't' && str[1] == 'a' && str[2] == 'g' && str[3] == '_' ) return true; } */ } return false; } MString GetTagName( const MDagPath &path ) { MFnDependencyNode depNode( path.node() ); MPlug treatAsTag = depNode.findPlug( "treat_as_tag", true ); if( !treatAsTag.isNull() ) { MString val; treatAsTag.getValue( val ); return val; } else return depNode.name(); } //based on FS_PathCmp in files.c int ComparePaths( const char *s0, const char *s1 ) { char c0, c1; do { c0 = *s0++; c1 = *s1++; if( c0 >= 'a' && c0 <= 'z' ) c0 -= 'a' - 'A'; if( c1 >= 'a' && c1 <= 'z' ) c1 -= 'a' - 'A'; if( c0 == '\\' || c0 == ':' ) c0 = '/'; if( c1 == '\\' || c1 == ':' ) c1 = '/'; if( c0 < c1 ) return -1; if( c0 > c1 ) return 1; } while( c0 ); // strings are equal return 0; } static bool g_PHYSFS_is_initialized = false; /* static */ void ResourceLoader::ClearDirectories( void ) { if( g_PHYSFS_is_initialized ) PHYSFS_deinit(); g_PHYSFS_is_initialized = false; } /* static */ bool CDECL ResourceLoader::PakSort( const ospath &p0, const ospath &p1 ) { //does p0 come before p1? //an "overriding" pak comes before an older one return ComparePaths( p0, p1 ) > 0; } /* static */ void ResourceLoader::AddDirectories( void ) { MString paths; if( MGlobal::executeCommand( "workspace -q -rte \"Q3GameData\" ;", paths ) == MS::kSuccess ) { const char *dirs = paths.asChar(); std::vector< ospath > dirList; for( ; ; ) { const char *pos = strchr( dirs, ';'); if( !pos ) //last string pos = dirs + strlen( dirs ); size_t len = pos - dirs; if( len ) { if( len > ospath::MaxLength - 1 ) len = ospath::MaxLength - 1; ospath dir; memcpy( dir, dirs, len ); dir[len] = '\0'; MString retpath; MString cmd = MString( "workspace -en \"" ) + dir + "\" ;"; if( MGlobal::executeCommand( cmd, retpath ) == MS::kSuccess ) dirList.push_back( retpath.asChar() ); } if( !*pos ) break; dirs = pos + 1; } //add in reverse order so that first listed is first searched for( int c = dirList.size(); c--; ) AddDirectory( dirList[c] ); } } /* static */ void ResourceLoader::AddDirectory( const char *dirName ) { PHYSFS_addToSearchPath( dirName, 0 ); std::vector< ospath > paks; ospath path = dirName; #ifdef _WIN32 //path comes from user input, hit it with the platform stick path.Replace( '/', '\\' ); if( path[path.Length() - 1] != '\\' ) //we want a file in a folder, not a folder that ends in .pk3 path.Append( "\\" ); ospath search = path; search.Append( "*.pk3" ); WIN32_FIND_DATA findData; HANDLE hFind = FindFirstFile( search, &findData ); if( hFind == INVALID_HANDLE_VALUE ) //no such files, we're done return; do { ospath pakpath = path; pakpath.Append( findData.cFileName ); paks.push_back( pakpath ); } while( FindNextFile( hFind, &findData ) ); ::FindClose( hFind ); #elif __linux__ if( path[path.Length() - 1] != '/' ) //we want a file in a folder, not a folder that ends in .pk3 path.Append( "/" ); ospath search = path; search.Append( "*.pk3" ); glob_t globbuf; if( glob( search, 0, NULL, &globbuf ) ) { for( int i=0; i 0; ) PHYSFS_addToSearchPath( paks[i], 0 ); } /* static */ bool ResourceLoader::ResourceExists( const char *resName ) { if( !g_PHYSFS_is_initialized ) { PHYSFS_init( "maya" ); AddDirectories(); g_PHYSFS_is_initialized = true; } return PHYSFS_exists( resName ) != 0; } /* static */ bool ResourceLoader::GetData( const char *resName, void *&data, size_t &dataSize ) { if( !g_PHYSFS_is_initialized ) { PHYSFS_init( "maya" ); AddDirectories(); g_PHYSFS_is_initialized = true; } PHYSFS_file* file = PHYSFS_openRead( resName ); if( !file ) { data = null; dataSize = 0; return false; } PHYSFS_sint64 size = PHYSFS_fileLength( file ); demand( size <= (size_t)~0, "file is too big" ); dataSize = (size_t)size; data = malloc( dataSize + 1 ); PHYSFS_read( file, data, 1, dataSize ); ((char*)data)[dataSize] = 0; PHYSFS_close( file ); return true; } /* static */ void ResourceLoader::FreeData( void *data ) { free( data ); } /* static */ const char** ResourceLoader::FindFiles( const char *dirName, const char *ext ) { if( !g_PHYSFS_is_initialized ) { PHYSFS_init( "maya" ); AddDirectories(); g_PHYSFS_is_initialized = true; } char **ret; char **files = PHYSFS_enumerateFiles( dirName ); if( ext ) { std::vector< ospath > retList; size_t extLen = strlen( ext ); for( char** cch = files; *cch; cch++ ) { size_t len = strlen( *cch ); if( !stricmp( *cch + len - extLen, ext ) ) retList.push_back( *cch ); } size_t totalLen = sizeof( char* ) * (retList.size() + 1); for( size_t i = 0; i < retList.size(); i++ ) totalLen += retList[i].Length() + 1; ret = (char**)malloc( totalLen ); char **list = ret; char *data = ((char*)ret) + sizeof( char* ) * (retList.size() + 1); for( size_t i = 0; i < retList.size(); i++ ) { *(list++) = data; size_t len = retList[i].Length(); memcpy( data, &retList[i], len + 1 ); data += len + 1; } *list = null; } else { size_t totalLen = 0; size_t count = 0; for( char** cch = files; *cch; cch++ ) { totalLen += strlen( *cch ) + 1; count++; } totalLen += sizeof( char* ) * (count + 1); ret = (char**)malloc( totalLen ); char **list = ret; char *data = ((char*)ret) + sizeof( char* ) * (count + 1); for( size_t i = 0; i < count; i++ ) { *(list++) = data; size_t len = strlen( files[i] ); memcpy( data, files[i], len + 1 ); data += len + 1; } *list = null; } PHYSFS_freeList( files ); return (const char**)ret; } /* static */ const char** ResourceLoader::FindDirectories( const char *dirName ) { if( !g_PHYSFS_is_initialized ) { PHYSFS_init( "maya" ); AddDirectories(); g_PHYSFS_is_initialized = true; } char **ret; char **files = PHYSFS_enumerateFiles( dirName ); std::vector< ospath > retList; for( char** cch = files; *cch; cch++ ) { ospath path = dirName; if( path[path.Length() - 1] != '/' ) path.Append( "/" ); path.Append( *cch ); if( PHYSFS_isDirectory( path ) ) retList.push_back( *cch ); } size_t totalLen = sizeof( char* ) * (retList.size() + 1); for( size_t i = 0; i < retList.size(); i++ ) totalLen += retList[i].Length() + 1; ret = (char**)malloc( totalLen ); char **list = ret; char *data = ((char*)ret) + sizeof( char* ) * (retList.size() + 1); for( size_t i = 0; i < retList.size(); i++ ) { *(list++) = data; size_t len = retList[i].Length(); memcpy( data, &retList[i], len + 1 ); data += len + 1; } *list = null; PHYSFS_freeList( files ); return (const char**)ret; } /* static */ void ResourceLoader::FindClose( const char **findData ) { free( findData ); } };