/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. 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 3 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, see . Additional permissions under GNU GPL version 3 section 7 If you modify this Program, or any covered work, by linking or combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK (or a modified version of those libraries), containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the licensors of this Program grant you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of OpenSSL and IJG JPEG Library used as well as that of the covered work. You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ // // 3DSMax HeadSpin exporter // #include "HeadSpin.h" #include "SimpleExport.h" #include "notify.h" #include "plExportErrorMsg.h" #include "plExportLogErrorMsg.h" #include "../MaxConvert/UserPropMgr.h" #include "hsExceptionStack.h" #include "../MaxConvert/hsConverterUtils.h" #include "../MaxConvert/plBitmapCreator.h" #include "../pfPython/plPythonFileMod.h" #include "../MaxMain/plPluginResManager.h" #include "../plResMgr/plRegistryHelpers.h" #include "../plResMgr/plRegistryNode.h" #include "hsStream.h" #include "../MaxConvert/plConvert.h" #include "../MaxConvert/hsMaterialConverter.h" #include "../plPhysX/plSimulationMgr.h" #include "../plSDL/plSDL.h" #include "../MaxMain/plMaxCFGFile.h" // For texture export/cleanup #include "../MaxMain/plTextureExportLog.h" #include "../pnKeyedObject/plKey.h" #include "../pnKeyedObject/plUoid.h" #include "../plGImage/plCubicEnvironmap.h" #include "../plGImage/plDynamicTextMap.h" #include "../plGImage/plMipmap.h" #include "../plScene/plSceneNode.h" #include "plExportDlg.h" #include "../plStatusLog/plStatusLog.h" #include "../plFile/plFileUtils.h" #include "../plAvatar/plAvatarMgr.h" extern UserPropMgr gUserPropMgr; #ifdef HS_DEBUGGING #define HS_NO_TRY #endif // // .MSH export module functions follow: // HSExport2::HSExport2() { } HSExport2::~HSExport2() { } int HSExport2::ExtCount() { return 2; } // // Extensions supported for import/export modules // const TCHAR *HSExport2::Ext(int n) { static char str[64]; switch(n) { case 0: return ""; case 1: return "prd"; } return _T(""); } // // Long ASCII description (i.e. "Targa 2.0 Image File") // const TCHAR *HSExport2::LongDesc() { return "Plasma 2.0"; } // // Short ASCII description (i.e. "Targa") // const TCHAR *HSExport2::ShortDesc() { #ifdef HS_DEBUGGING return "Plasma 2.0 Debug"; #else return "Plasma 2.0"; #endif } // // ASCII Author name // const TCHAR *HSExport2::AuthorName() { return "Billy Bob"; } // // ASCII Copyright message // const TCHAR *HSExport2::CopyrightMessage() { return "Copyright 1997 HeadSpin Technology Inc."; } // // Other message #1 // const TCHAR *HSExport2::OtherMessage1() { return _T(""); } // // Other message #2 // const TCHAR *HSExport2::OtherMessage2() { return _T(""); } // // Version number * 100 (i.e. v3.01 = 301) // unsigned int HSExport2::Version() { return 100; } // // Optional // void HSExport2::ShowAbout(HWND hWnd) { } void IGetPath(const char* name, char* path) { int i; // find the last backslash in the full path for ( i=strlen(name)-1; i>=0 ; i-- ) { if ( name[i] == '\\' ) break; } if ( i >= 0 && i < 256) // if either we couldn't the backslash or the path was too big { strncpy(path,name,i+1); path[i+1] = '\0'; //null terminate string (cause strncpy might not) } else path[0] = '\0'; // otherwise just make it a null string } // Another little helper class to help write out a list of textures to a log file class plTextureLoggerCBack : public plRegistryKeyIterator { protected: plTextureExportLog* fTELog; public: plTextureLoggerCBack(plTextureExportLog* teLog) { fTELog = teLog; } virtual hsBool EatKey(const plKey& key) { plBitmap* bmap = plBitmap::ConvertNoRef(key->ObjectIsLoaded()); if (bmap != nil) fTELog->AddTexture(bmap); return true; // Always continue } }; // Yet another key iterator, this one to call OptimizeDrawables() on each sceneNode class plOptimizeIterator : public plRegistryKeyIterator { public: virtual hsBool EatKey(const plKey& key) { if (key->GetUoid().GetClassType() == plSceneNode::Index()) { plSceneNode* sn = plSceneNode::ConvertNoRef(key->ObjectIsLoaded()); if (sn != nil) sn->OptimizeDrawables(); } return true; // Always continue } }; // // // int HSExport2::DoExport(const TCHAR *name,ExpInterface *ei,Interface *gi, BOOL suppressPrompts, DWORD options) { BOOL backupEnabled = gi->AutoBackupEnabled(); gi->EnableAutoBackup(FALSE); BOOL bmmSilentMode = TheManager->SilentMode(); TheManager->SetSilentMode(TRUE); bool mbSuppressPrompts = hsMessageBox_SuppressPrompts; hsMessageBox_SuppressPrompts = (suppressPrompts)?true:false; // Disable save so we don't crash in export or // otherwise screw database. SimpleExportExitCallback exitCB; gi->RegisterExitMAXCallback(&exitCB); gUserPropMgr.OpenQuickTable(); hsConverterUtils::Instance().CreateNodeSearchCache(); BroadcastNotification(NOTIFY_PRE_EXPORT); // get just the path (not the file) of where we are going to export to char out_path[256]; IGetPath(name, out_path); // Apparently this was implied by the open dialog, but not if you call Max's ExportToFile() func SetCurrentDirectory(out_path); // // Setup ErrorMsg // // Needs to be outside try/catch so it doesn't stack unwind... plExportErrorMsg hituser_errorMessage; // This is the errorMessage that slaps user TSTR filename = gi->GetCurFileName(); hsStrncpy(fName, filename, 128); char *dot = strrchr(fName, '.'); if (dot) *dot = 0; char ErrorLogName[512]; sprintf(ErrorLogName, "%s%s.err", out_path, fName); plExportLogErrorMsg logonly_errorMessage(ErrorLogName); // This errorMessage just writes it all to a file // now decide which errorMessage object to use plErrorMsg* errorMessage; if (suppressPrompts) errorMessage = &logonly_errorMessage; else errorMessage = &hituser_errorMessage; // For export time stats DWORD exportTime = timeGetTime(); _SYSTEMTIME tm; GetSystemTime(&tm); // // Let's get cracking! Convert the scene... // plConvertSettings settings; if (plExportDlg::Instance().IsExporting()) { settings.fDoPreshade = plExportDlg::Instance().GetDoPreshade(); settings.fPhysicalsOnly = plExportDlg::Instance().GetPhysicalsOnly(); settings.fDoLightMap = plExportDlg::Instance().GetDoLightMap(); settings.fExportPage = plExportDlg::Instance().GetExportPage(); } plConvert::Instance().Init(gi, errorMessage, &settings); // We want to incorporate any SDL changes since the last export, so we DeInit() // and re-initialize. char buf[MAX_PATH]; strcpy(buf, plMaxConfig::GetClientPath()); strcat(buf, "sdl"); plSDLMgr::GetInstance()->SetSDLDir(buf); plSDLMgr::GetInstance()->DeInit(); plSDLMgr::GetInstance()->Init(); // Add disk source for writing char datPath[MAX_PATH]; strcpy(datPath, out_path); plFileUtils::AddSlash(datPath); strcat(datPath, "dat\\"); CreateDirectory(datPath, NULL); plPluginResManager::ResMgr()->SetDataPath(datPath); if (hsgResMgr::Reset()) { plSimulationMgr::Init(); plAvatarMgr::GetInstance(); // Verify the pages here manually, since it's a separate step now plPluginResManager::ResMgr()->VerifyPages(); plPythonFileMod::SetAtConvertTime(); // Convert!!! hsBool convertOK = plConvert::Instance().Convert(); // Free the material cache. This will delete unused materials. hsMaterialConverter::Instance().FreeMaterialCache(out_path); if (convertOK) { // Optimize the drawables plOptimizeIterator optIterator; plPluginResManager::ResMgr()->IterateKeys( &optIterator ); // And save. plPluginResManager::ResMgr()->WriteAllPages(); // Write out a texture log file char textureLog[MAX_PATH]; sprintf(textureLog, "log\\exportedTextures_%s.log", fName); plTextureExportLog textureExportLog( textureLog ); plTextureLoggerCBack loggerCallback( &textureExportLog ); plPluginResManager::ResMgr()->IterateKeys( &loggerCallback ); textureExportLog.Write(); // Moving this to the end of writing the files out. Yes, this means that any unused mipmaps still get // written to disk, including ones loaded on preload, but it's the only way to get shared texture pages // to work without loading in the entire age worth of reffing objects. - 5.30.2002 mcn plBitmapCreator::Instance().DeInit(); } // Have the resMgr clean up after export. This includes paging out any converted pages plPluginResManager::ResMgr()->EndExport(); plSimulationMgr::Shutdown(); plAvatarMgr::ShutDown(); // Reset the resmgr so we free all the memory it allocated hsgResMgr::Reset(); } //---------------------------------------------- // Write a log entry to the Db file name for now //---------------------------------------------- hsUNIXStream dbLog; dbLog.Open(name,"at"); char str[256]; exportTime = (timeGetTime() - exportTime) / 1000; sprintf(str,"Export from Max File \"%s\" on %02d/%02d/%4d took %d:%02d\n",filename,tm.wMonth,tm.wDay,tm.wYear, exportTime/60, exportTime%60); dbLog.WriteString(str); dbLog.Close(); // Allow plugins to clean up after export BroadcastNotification(NOTIFY_POST_EXPORT); hsConverterUtils::Instance().DestroyNodeSearchCache(); gUserPropMgr.CloseQuickTable(); gi->UnRegisterExitMAXCallback(&exitCB); hsMessageBox_SuppressPrompts = mbSuppressPrompts; TheManager->SetSilentMode(bmmSilentMode); gi->EnableAutoBackup(backupEnabled); MessageBeep(MB_ICONASTERISK); return 1; }