/********************************************************************************************** * Copyright (c) DreamWorks Interactive. 1996 * * Implementation of module GroffExp. * * Bugs: * * To do: * * 1. Make sure the vertex and face normals are properly exteracted from the iNodes. * * 2. Add support to allow the user to move the pivot point without moving the geometry as it * appears within the GUIApp. * ********************************************************************************************** * * $Log:: /JP2_PC/Source/Tools/GroffExp/GroffExp.cpp $ * * 21 7/15/97 6:58p Gstull * Made organizational changes to make management of the exporter more reasonable. * * 20 7/14/97 3:36p Gstull * Made substantial changes to support tasks for the August 18th milestone for attributes, * object representation, geometry checking, dialog interfacing, logfile support, string tables * and string support. * * 19 6/18/97 7:33p Gstull * Added changes to support fast exporting. * * 18 4/15/97 10:43p Gstull * Added changes to support export options specified in a dialog box at export time. * * 17 4/14/97 7:44p Gstull * Added changes to support log file management, * * 16 3/19/97 6:09p Gstull * Fixed bugs #18 qnd #19. Fixes resolved problems detecting and reporting material ID range * errors and objects without geometry. * * 15 2/25/97 7:51p Gstull * Resolved issues with proper object placement in the scene and object validation. * * 14 2/24/97 8:03p Gstull * Changed the target file paths in the exporter to use the relative paths instead. * * 13 2/21/97 6:55p Gstull * Added code to perform a number of additional checks * on data as well as a new GUI interface class, removal * of the bump map hack. * * 12 12/16/96 11:19p Gstull * Added progress status bar for Kyle. * * 11 12/16/96 3:04p Gstull * Added fix to force Process_Bitmap to use a temporary palette for loading the transparency * palette rather than the quantization palette. * * 10 12/16/96 11:22a Gstull * Made changes to support quantization to a single palette. * * 9 11/26/96 6:07p Gstull * Added changes to the file IO for support of write of 0 bytes to sections. * * 8 11/20/96 1:13p Gstull * Slight modifications to the Groff structures for integration into the GUIApp. * * 7 11/15/96 7:29p Gstull * File changes to create initial version of the multi-section groff loader. * * 6 11/15/96 11:08a Gstull * Added substantial changes to the GroffExporter including for support of mutiple section * files. * * 5 11/06/96 7:27p Gstull * Latest version of groff file exporter. Now handles transparency maps, texture maps and bump * maps with color quantization. * * 4 11/04/96 8:34p Gstull * Latest version of the new bitmap exporter stuff. * * 2 9/16/96 8:27p Gstull * Update the paths to be project relative. * * 1 9/16/96 2:56p Gstull * *********************************************************************************************/ #include #include //********************************************************************************************** // Include the main 3D Studio MAX include file. // #include "Max.h" //********************************************************************************************** // Include file generated by the developer studio containing the #defines for the resources. // #include "GroffExp.h" //********************************************************************************************** // 3d Studio Max SDK include files. // #include "bmmlib.h" #include "stdmat.h" #include "istdplug.h" //********************************************************************************************** // Jurassic Park II - Trespasser include files. // #include "StandardTypes.hpp" #include "ObjectDef.hpp" #include "Geometry.hpp" #include "Lib/Sys/SysLog.hpp" #include "Bitmap.hpp" #include "Lib/Sys/Symtab.hpp" #include "Export.hpp" #include "GUIInterface.hpp" #include "Mathematics.hpp" #include "Lib/Groff/EasyString.hpp" //********************************************************************************************** // Local definitions for the export plugin. // #define EXPORTER_VERSION_NUMBER 110 //********************************************************************************************** // Define global classes for this DLL. // CGUIInterface guiInterface; CSysLog slLogfile; //********************************************************************************************* // This class forms the interface which is used by 3D Studio MAX to export scenes, from the // internal representation to a user define format for external use. This interface is NOT // intended to be used by users directly and should only be invoked by 3D Studio MAX. // // Example: // Since this class forms a DLL, it is called exclusively by 3D Studio MAX during a scene // export request made by the user. // class JP2Export : public SceneExport // // Prefix: // { protected: char shortdesc[40]; public: //****************************************************************************************** // Constructor. // JP2Export ( ) { shortdesc[0] = 0; }; //****************************************************************************************** // Destructor. // ~JP2Export ( ) { }; //****************************************************************************************** // Number of extensions supported by this plug-in. // int ExtCount ( ) { // Currently there is only one format supported. return 1; } //****************************************************************************************** // Extension number 0 (i.e. "GRF") // const TCHAR* Ext ( int n ); //****************************************************************************************** // Long ASCII file export description. // const TCHAR* LongDesc ( ); //****************************************************************************************** // Short ASCII file export description. // const TCHAR* ShortDesc ( ); //****************************************************************************************** // ASCII Author name. // const TCHAR* AuthorName ( ); //****************************************************************************************** // ASCII Copyright message. // const TCHAR* CopyrightMessage ( ); //****************************************************************************************** // Other message #1. // const TCHAR* OtherMessage1 ( ) { return _T(""); } //****************************************************************************************** // Other message #2. // const TCHAR* OtherMessage2 ( ) { return _T(""); } //****************************************************************************************** // Version number * 100 (i.e. v3.01 = 301). // unsigned int Version ( ); //****************************************************************************************** // Export the object into a file. // void ShowAbout ( HWND hWnd ); //****************************************************************************************** // Export the object into a file. // int DoExport ( const TCHAR* tchr_export_name, // File name and path to export to. ExpInterface* pei_export_interface, // Class for enumerating the nodes in the scene. Interface* pip_interface // Interface pointer for calling MAX API's. ); }; //********************************************************************************************* // class CClassDesc: public ClassDesc // // Prefix: cldlg // // This class implements the virtual functions which describe the features and capabilities of // the export plugin to 3D Studio Max. // // //********************************************************************************************* { public: //****************************************************************************************** // int IsPublic ( ) { return true; } //****************************************************************************************** // void* Create ( BOOL loading = false ) { return new JP2Export; } //****************************************************************************************** // const TCHAR* ClassName ( ) { return guiInterface.strGetString(IDS_JP2_CLASSNAME); } //****************************************************************************************** // SClass_ID SuperClassID ( ) { return SCENE_EXPORT_CLASS_ID; } //****************************************************************************************** // Class_ID ClassID ( ) { return Class_ID(0x297455F, 0x17C9693D); } //****************************************************************************************** // const TCHAR* Category ( ) { return guiInterface.strGetString(IDS_JP2_EXPORTER); } }; static CClassDesc cld_jp2_desc; //********************************************************************************************* // class CObjectEntry // // Prefix: obe // // This class keeps track of the objects which are deemed to be 'exportable' to the outside // world. In the simplest case, this would include objects which can be converted to a trimesh // object. // // Example: // //********************************************************************************************* { public: TSTR tstrName; // Node's object name. INode* pinINode; // INode of the object. Object* pobjObject; // Object. int iType; // OBTYPE_MESH, OBTYPE_BONE CObjectEntry* pobeNext; // Next scene entry node in the list. public: //****************************************************************************************** // CObjectEntry ( INode* pin_node, Object* pobj_object, int i_type ); }; //********************************************************************************************** // CObjectEntry::CObjectEntry ( INode* pin_node, Object* pobj_object, int i_type ) { pinINode = pin_node; // Setup a pointer to the INode. pobjObject = pobj_object; // Setup a pointer to the object. iType = i_type; // The object type (i.e. tri-mesh). pobeNext = 0; // Terminate the next node pointer. } //********************************************************************************************** // class CSceneEnumProc : public ITreeEnumProc // // Prefix: sep // // This class is dreived from the base class iTreeEnumProc, which passes each of the INodes in // the scene to the exporter through the 'callback' procedure. The callback then determines // which objects can be converted to a tri-mesh object, and can then be exported. // // Example: // //********************************************************************************************** { private: uint uNodeCount; // The number of nodes in the list. public: TimeValue tvTime; // The actual time when the export occurred. IScene* theScene; // A pointe Interface* pipInterface; // Pointer to the interface structure. CObjectEntry* pobeHead; // Pointer to the first object entry in the list. CObjectEntry* pobeTail; // Pointer to the last object entry in the list. //***************************************************************************************** // Constructors. // CSceneEnumProc ( IScene* pis_scene, TimeValue tv_time, Interface* pip_interface ); //***************************************************************************************** // Destructors. // ~CSceneEnumProc ( ); //***************************************************************************************** // This function is serves as the callback function which determines which objects in // the scene are passed to the exporter. It is this procedure which builds the list of // of objects which the exporter eventually exports. // // Note: It is not clear to me at this point why this call is necessary. It seems to be // unnecessary since selection of the objects which are the objects could // int callback ( INode* pin_inode ); //***************************************************************************************** // void Append ( INode* pin_inode, Object* pobj_object, int i_type ); //***************************************************************************************** // CObjectEntry* pobeLookup ( INode* pin_inode ); //***************************************************************************************** // uint uCount ( ) { return uNodeCount; }; //***************************************************************************************** // Box3 bx3Bound ( ); //***************************************************************************************** // void BuildNames ( ); //****************************************************************************************** // CObjectEntry* operator[] ( uint u_index ); }; CSceneEnumProc* psep_the_scene_enum = 0; //********************************************************************************************** // CSceneEnumProc::CSceneEnumProc(IScene* pis_scene, TimeValue tv_time, Interface* pip_interface) { tvTime = tv_time; theScene = pis_scene; uNodeCount = 0; pobeHead = 0; pobeTail = 0; pipInterface = pip_interface; // Display the scene enumeration initiation banner. slLogfile.Printf(guiInterface.strGetString(IDS_SCENEENUM_START)); // // Call the EnumTree function, from class, IScene which will cause our call back to be // be invoked for each INode which exists in the scene. // pis_scene->EnumTree(this); // Display the scene enumeration completion banner. slLogfile.Printf(guiInterface.strGetString(IDS_SCENEENUM_END)); } //********************************************************************************************** // CSceneEnumProc::~CSceneEnumProc() { CObjectEntry* pobe_next; // Destroy the scene entry list one node at a time. while (pobeHead) { // Setup a pointer to the next node. pobe_next = pobeHead->pobeNext; // Delete this node. delete pobeHead; // Setup head to point to the saved node. pobeHead = pobe_next; } // Terminate the head and tail list pointers. pobeHead = 0; pobeTail = 0; // Set the scene entry count to 0. uNodeCount = 0; } #define TRIMESH_OBJECT 10 //********************************************************************************************** // int CSceneEnumProc::callback(INode* pin_inode) { // Setup a pointer to the object in the scene Object* pobj_object = pin_inode->EvalWorldState(tvTime).obj; // Log the inode, name and conversion type of this object. slLogfile.Printf(guiInterface.strGetString(IDS_SCENEENUM_INODE), pin_inode, pin_inode->GetName()); slLogfile.Printf(guiInterface.strGetString(IDS_CONV_TO_TRIMESH)); // Can this object be converted to a TRI-MESH object? if (pobj_object->CanConvertToType(triObjectClassID)) { // Log the fact that a Tri-mesh was located. slLogfile.Printf(guiInterface.strGetString(IDS_YES)); // Yes! Then add it to the Scene list for later exporting Append(pin_inode, pobj_object, TRIMESH_OBJECT); } else { // This object is not convertable to a tri-mesh object. Add check here for a 'bones' object. slLogfile.Printf(guiInterface.strGetString(IDS_NO)); } // Process the next object return TREE_CONTINUE; } //********************************************************************************************** // void CSceneEnumProc::Append(INode* pin_inode, Object* pobj_object, int i_type) { CObjectEntry* pobe_entry = new CObjectEntry(pin_inode, pobj_object, i_type); // Does the list contain more than one scene entry already? if (pobeTail) { // Yes! Then add this object to the list. pobeTail->pobeNext = pobe_entry; } // Since this node is at the end of the list, the tail pointer should point at the new node. pobeTail = pobe_entry; // Is this node the first in the list? if (!pobeHead) { // Yes! Then the head pointer should point at it. pobeHead = pobe_entry; } // Increment the count of the total number of objects in the list. uNodeCount++; } //********************************************************************************************** // Box3 CSceneEnumProc::bx3Bound() { Box3 bx3_scene_box; ViewExp* pve_viewport = pipInterface->GetViewport(0); CObjectEntry* pobe_entry = pobeHead; // Initialize the bounding box parameters. bx3_scene_box.Init(); // While there are still objects in the list to see. while (pobe_entry) { Box3 bx3_object_box; // Get the world bounding box. pobe_entry->pobjObject->GetWorldBoundBox(tvTime, pobe_entry->pinINode, pve_viewport, bx3_object_box); // Make sure this new object falls into the bounding box. bx3_scene_box += bx3_object_box; // Move to the next node in the list. pobe_entry = pobe_entry->pobeNext; } // Return the boundiung box. return bx3_scene_box; } //********************************************************************************************** // CObjectEntry* CSceneEnumProc::pobeLookup(INode* pin_inode) { CObjectEntry* pobe_object = pobeHead; // While there are object nodes in the list. while (pobe_object) { // Is this the INode we are looking for? if (pobe_object->pinINode == pin_inode) { // Yes! Then return it. return pobe_object; } // No! Then advance to the next object in the list. pobe_object = pobe_object->pobeNext; } // Uh oh! The node was not found in the list, so return null. return false; } //********************************************************************************************** // CObjectEntry* CSceneEnumProc::operator[](uint u_index) { CObjectEntry* pobe_entry = pobeHead; // Is the index in range? if (u_index > uNodeCount) { // Yes! Return null. return 0; } // While there are still object entries in the list. while (pobe_entry) { // Is this the index we are looking for? if (u_index-- == 0) { // Yes! Then return this node. return pobe_entry; } // No! Advance to the next node in the list. pobe_entry = pobe_entry->pobeNext; } // Uh oh! This means that the list has fewer nodes than the uCount field believes should be in the // object netry list, so return null to the user. return 0; } //********************************************************************************************* // class CObjectListNode // // Prefix: oln // // This class is dreived from the base class iTreeEnumProc, which passes each of the INodes in // the scene to the exporter through the 'callback' procedure. The callback then determines // which objects can be converted to a tri-mesh object, and can then be exported. // // Example: // //********************************************************************************************* { public: CObjectEntry* pobeEntry; CObjectListNode* polnNext; CObjectListNode ( CObjectEntry* pobe_entry ) { pobeEntry = pobe_entry; polnNext = 0; } }; //********************************************************************************************* // class CObjectList // // Prefix: ol // // This class is derived from the base class iTreeEnumProc, which passes each of the INodes in // the scene to the exporter through the 'callback' procedure. The callback then determines // which objects can be converted to a tri-mesh object, and can then be exported. // // Example: // //********************************************************************************************* { private: uint uObjectCount; public: CObjectListNode* polnHead; CObjectListNode* polnTail; //****************************************************************************************** // CObjectList ( CSceneEnumProc& scene ); //****************************************************************************************** // ~CObjectList ( ); //****************************************************************************************** // void Append ( CObjectEntry* pobe_object ); //****************************************************************************************** // uint uCount ( ) { return uObjectCount; } //****************************************************************************************** // int operator[] ( Object* pobj_object ); //****************************************************************************************** // int operator[] ( INode* pin_inode ); //****************************************************************************************** // CObjectListNode* operator[] ( uint u_index ); }; //********************************************************************************************** // CObjectList::CObjectList(CSceneEnumProc& sep_scene) { polnHead = 0; polnTail = 0; uObjectCount = 0; uint u_object_count = sep_scene.uCount(); // // Add each of the object entries to the object list. Note: this seems redundant since // the user should be able to operate on the object entries in the scene enum proc list // rather than copying them to the object list. Will need to look into this later in // more datail to determine why this is occurring. // for (uint u_i = 0; u_i < u_object_count; u_i++) { // Add the object entry to the object list. Append(sep_scene[u_i]); } } //********************************************************************************************** // CObjectList::~CObjectList() { // While there are object entry nodes in the list. while (polnHead) { // Setup a pointer to the next object entry node. CObjectListNode *poln_next = polnHead->polnNext; // Delete the object list node at the front of the list. delete polnHead; // Advance the head pointer to the next node in the list. polnHead = poln_next; } // The list is now empty so terminate the list pointer and zero the count. polnHead = 0; polnTail = 0; uObjectCount = 0; } //********************************************************************************************** // void CObjectList::Append(CObjectEntry* pobe_object) { CObjectListNode* poln_node = new CObjectListNode(pobe_object); // Is there already a node in the list? if (polnTail) { // Yes! Then add this node to the end of the list. polnTail->polnNext = poln_node; } // Setup the tail pointer to point to the last node in the list. polnTail = poln_node; // Was the list previously empty? if (polnHead == 0) { // Yes! Then setup the head list pointer to point to this node. polnHead = poln_node; } // Increment the list counter. uObjectCount++; } //********************************************************************************************** // CObjectListNode* CObjectList::operator[](uint u_index) { CObjectListNode* poln_node = polnHead; // Is the index in range? if (u_index > uObjectCount) { // No! Return a null pointer. return 0; } // While there are still nodes in the list. while (poln_node) { // Is this the node we are looking for? if (u_index-- == 0) { // Yes! Return a pointer to this node. return poln_node; } // Advance to the next node in the list. poln_node = poln_node->polnNext; } // Uh oh! This means that the node count and the node list length do not match. Return null. return false; } //********************************************************************************************** // int CObjectList::operator[](Object* pobj_object) { int i_index = 0; CObjectListNode* poln_node = polnHead; // While there are still nodes in the list to process. while (poln_node) { // Is this the node we are looking for? if (poln_node->pobeEntry->pobjObject == pobj_object) { // Return the index to this node. return i_index; } // Advance to the next node in the object list. poln_node = poln_node->polnNext; // Advance in index pointer. i_index++; } // The node was not located in the list so return an error. return -1; } //********************************************************************************************** // int CObjectList::operator[](INode* pin_inode) { int i_index = 0; CObjectListNode* poln_node = polnHead; // While there are still nodes in the list. while (poln_node) { // Is this the node we are looking for? if (poln_node->pobeEntry->pinINode == pin_inode) { // Yes! Then return the index to this node to the user. return i_index; } // Advance to the next node in the list. poln_node = poln_node->polnNext; // Increment the index node pointer. i_index++; } // The node was not located in the list so return an error. return -1; } //********************************************************************************************* // class CObjectName // // Prefix: on // // Example: // //********************************************************************************************* { public: TSTR tstrName; CObjectName* ponNext; //****************************************************************************************** // CObjectName ( TSTR tstr_object_name ) { tstrName = tstr_object_name; ponNext = 0; } }; //********************************************************************************************* // class CObjectNameList // // Prefix: onl // // This class is derived from the base class iTreeEnumProc, which passes each of the INodes in // the scene to the exporter through the 'callback' procedure. The callback then determines // which objects can be converted to a tri-mesh object, and can then be exported. // // Example: // //********************************************************************************************* { uint uObjectCount; public: CObjectName* ponHead; CObjectName* ponTail; //****************************************************************************************** // CObjectNameList ( ) { ponHead = 0; ponTail = 0; uObjectCount = 0; } //****************************************************************************************** // ~CObjectNameList ( ); //****************************************************************************************** // void Append ( TSTR& tstr_object_name ); //****************************************************************************************** // int iLookup ( TSTR& tstr_object_name ); //****************************************************************************************** // uint uCount ( ) { return uObjectCount; } //****************************************************************************************** // void MakeUnique ( TSTR& tstr_object_name ); //****************************************************************************************** // int operator[] ( TSTR& tstr_object_name ); }; //********************************************************************************************** // CObjectNameList::~CObjectNameList() { // While there are nodes in the list. while (ponHead) { // Advance the pointer to the next node in the list. CObjectName* pon_node = ponHead->ponNext; // Remove the node from the list. delete ponHead; // Advance the head pointer to the next node. ponHead = pon_node; } // Set the head, tail node pointers and count to null. ponHead = 0; ponTail = 0; uObjectCount = 0; } //********************************************************************************************** // void CObjectNameList::Append(TSTR& tstr_object_name) { CObjectName* pon_node = new CObjectName(tstr_object_name); // Does the list have any nodes in it? if (ponTail) { // Yes! Add this node to the list. ponTail->ponNext = pon_node; } // Setup the tail pointer to point to the end of the list. ponTail = pon_node; // Was the list previously empty? if (ponHead == 0) { // Yes! Then the new node is the only node in the list. ponHead = pon_node; } // Increment the node counter. uObjectCount++; } //********************************************************************************************** // void CObjectNameList::MakeUnique(TSTR& tstr_object_name) { // Is this name already in the list? if (iLookup(tstr_object_name) == -1) { // No! Then add it to the list. Append(tstr_object_name); } else { // Yes! Then attempt to build a unique name. for (int i = 0; i < 10000000; ++i) { char buf[12]; // Setup a counter in strings. sprintf(buf,":%d",i); TSTR tstr_num(buf); TSTR tstr_work = tstr_object_name; // Concatenate the numeric text string onto the end of this string. tstr_work = tstr_work + tstr_num; // Is the new name now unique? if (iLookup(tstr_work) == -1) { // Add the new name to the object name list. Append(tstr_work); // Return the new object name to the caller. tstr_object_name = tstr_work; break; } } } } //********************************************************************************************** // int CObjectNameList::iLookup(TSTR& tstr_object_name) { int i_index = 0; CObjectName* pon_node = ponHead; // While there are still nodes in the list. while (pon_node) { // Is this the node we are looking for? if (pon_node->tstrName == tstr_object_name) { // Return the index to this node. return i_index; } // Adavance to the next node in the list. pon_node = pon_node->ponNext; // Advance in the index counter. i_index++; } // The node was not found so return an invalid index. return -1; } CObjectNameList on_the_object_names; //********************************************************************************************** // void CSceneEnumProc::BuildNames() { CObjectNameList onl_name_list; CObjectEntry* pobe_node = pobeHead; // While there are still objects in the list. while (pobe_node) { // Get the objects actual name. pobe_node->tstrName = pobe_node->pinINode->GetName(); // Make sure the objects name is unique. onl_name_list.MakeUnique(pobe_node->tstrName); // Advance to the next node in the list. pobe_node = pobe_node->pobeNext; } } //********************************************************************************************** // Define the DLL's interface information. // bool WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved) { // Why was the DLL's main called? switch (fdwReason) { // Is a new process attaching to this DLL? case DLL_PROCESS_ATTACH: // Is there already a process attached to this DLL? if (guiInterface.GetInstance() != 0) { // Yes! Since this code is not multi-threaded, return an error. // were unsuccessful. return(false); } // Get the DLL's instance handle and set it in the interface class. guiInterface.SetInstance(hDLLInst); break; // Is an existing process attempting to detach from this DLL? case DLL_PROCESS_DETACH: // Delete the instance handle of this process from the interface class. guiInterface.SetInstance(0); break; // A thread want to attach to the DLL, and this is allowed. case DLL_THREAD_ATTACH: break; // A thread want to dettach to the DLL, and this is allowed. case DLL_THREAD_DETACH: break; } // Let the caller know that we were successful. return true; } //********************************************************************************************** // Function which returns a description of the DLL. // __declspec(dllexport) const TCHAR* LibDescription() { // Return an ASCII string from the string table. return _T(guiInterface.strGetString(IDS_JP2_COPYRIGHT)); } //********************************************************************************************** // Function which returns the number of classes in this library. // __declspec(dllexport) int LibNumberClasses() { // Currently there is just one class in this library. return 1; } //********************************************************************************************** // Function which returns a pointer to a class based upon an index. // __declspec(dllexport) ClassDesc *LibClassDesc(int i) { // Which class is being referenced? switch(i) { // Class 1 in this DLL. case 0: // Return a pointer to it. return &cld_jp2_desc; break; // Otherwise, return 0 as the requested class was not found. default: return 0; break; } } //********************************************************************************************** // Function which returns the version of the DLL. // __declspec(dllexport) ULONG LibVersion() { // Return the current version number from the version table. return(EXPORTER_VERSION_NUMBER); } //********************************************************************************************** // Routines for processing the bitmaps associated with the objects // int Materials; CObjectDef* podCurrent; CObjectDefList theList; //********************************************************************************************** // Global static variables for keeping session default values around until the Max application // is exited. // Change the default checkbox settings in the development environment. #ifndef _DEBUG static bool b_quantize_bmp = true; static bool b_gen_groff = true; static bool b_gen_logfiles = false; #else static bool b_quantize_bmp = false; static bool b_gen_groff = true; static bool b_gen_logfiles = true; #endif //********************************************************************************************** // static BOOL CALLBACK ExportDlgProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) { // Now process the message. switch(msg) { case WM_INITDIALOG: // Center the window on the screen. CenterWindow(hdlg, GetParent(hdlg)); // Setup the defaults for the check buttons. CheckDlgButton(hdlg, IDC_QUANTIZE_BMP, b_quantize_bmp); CheckDlgButton(hdlg, IDC_GEN_GROFF, b_gen_groff); CheckDlgButton(hdlg, IDC_GEN_LOGFILES, b_gen_logfiles); // Indicate that we processed a message. return true; case WM_COMMAND: // Process the windows command. switch(LOWORD(wparam)) { // Did the user press the cancel button, implying he want to bail out? case IDCANCEL: // Yes! Remove the dialog box and return false to the creator of the dialog. EndDialog(hdlg, false); break; // Does the user want to perform the export now? case IDOK: // Yes! Then get the button selection options. b_quantize_bmp = IsDlgButtonChecked(hdlg, IDC_QUANTIZE_BMP); b_gen_groff = IsDlgButtonChecked(hdlg, IDC_GEN_GROFF); b_gen_logfiles = IsDlgButtonChecked(hdlg, IDC_GEN_LOGFILES); // Remove the dialog box and return true to the caller. EndDialog(hdlg, true); break; } break; } // Return a failure result. return false; } //********************************************************************************************** // void Convert(SColor24* ac24_dst, BMM_Color_48* ac48_src, uint u_count) { for (uint u_i = 0; u_i < u_count; u_i++) { ac24_dst[u_i].u1Red = uint8 (ac48_src[u_i].r >> 8); ac24_dst[u_i].u1Green = uint8 (ac48_src[u_i].g >> 8); ac24_dst[u_i].u1Blue = uint8 (ac48_src[u_i].b >> 8); } } //********************************************************************************************** // void MaterialDefaults(StdMat* psmtl_material) { slLogfile.Printf(guiInterface.strGetString(IDS_MATERIAL_CONFIG)); // Do we have a valid material? if (psmtl_material == 0) { // No! Return return; } // Is face mapping enabled? if (psmtl_material->GetFaceMap()) { slLogfile.Printf(guiInterface.strGetString(IDS_FACE_MAP_ENABLED)); } else { slLogfile.Printf(guiInterface.strGetString(IDS_FACE_MAP_DISABLED)); } // Are two sided polygons in use? if (psmtl_material->GetTwoSided()) { slLogfile.Printf(guiInterface.strGetString(IDS_FACE_MODE_DOUBLE)); } else { slLogfile.Printf(guiInterface.strGetString(IDS_FACE_MODE_SINGLE)); } // Get the shininess value. slLogfile.Printf(guiInterface.strGetString(IDS_SHININESS), psmtl_material->GetShininess(0)); // Get the shininess strength value. slLogfile.Printf(guiInterface.strGetString(IDS_SHININESS_STRENGTH), psmtl_material->GetShinStr(0)); // Get the self-illumination slLogfile.Printf(guiInterface.strGetString(IDS_SELF_ILLUMINATION), psmtl_material->GetSelfIllum(0)); // Look through all the materials and determine the maps in use and report them. for (uint u_i = ID_AM; u_i < ID_RR; u_i++) { // Is the map enabled? if (psmtl_material->MapEnabled(u_i)) { // Get the strength field. float f_strength = psmtl_material->GetTexmapAmt(u_i, 0); // Yes! Report the map type and name. switch(u_i) { case ID_AM: slLogfile.Printf(guiInterface.strGetString(IDS_AMBIENT_MAP), f_strength); break; case ID_DI: slLogfile.Printf(guiInterface.strGetString(IDS_DIFFUSE_MAP), f_strength); break; case ID_SP: slLogfile.Printf(guiInterface.strGetString(IDS_SPECULAR_MAP), f_strength); break; case ID_SH: slLogfile.Printf(guiInterface.strGetString(IDS_SHININESS_MAP), f_strength); break; case ID_SS: slLogfile.Printf(guiInterface.strGetString(IDS_SHININESS_STR_MAP), f_strength); break; case ID_SI: slLogfile.Printf(guiInterface.strGetString(IDS_SELF_ILLUM_MAP), f_strength); break; case ID_OP: slLogfile.Printf(guiInterface.strGetString(IDS_OPACITY_MAP), f_strength); break; case ID_FI: slLogfile.Printf(guiInterface.strGetString(IDS_FILTER_MAP), f_strength); break; case ID_BU: slLogfile.Printf(guiInterface.strGetString(IDS_BUMP_MAP), f_strength); break; case ID_RL: slLogfile.Printf(guiInterface.strGetString(IDS_REFLECTION_MAP), f_strength); break; case ID_RR: slLogfile.Printf(guiInterface.strGetString(IDS_REFRACTION_MAP), f_strength); break; } } } } //********************************************************************************************** // bool QuantizeBitmap(Quantizer* pq_quantizer, TSTR& Diffuse) { bool b_success = true; TSTR tstr_progress_title; // Build the progress meter title. tstr_progress_title.printf(guiInterface.strGetString(IDS_QUANTIZE_BM), Diffuse.data()); // Notify the user of what is going on. guiInterface.InitProgressMeter((char *) tstr_progress_title.data()); // Setup a bitmap info structure BitmapInfo bi_bitmap; // Setup the name of the bitmap bi_bitmap.SetName(Diffuse.data()); // Load the bitmap. Use Max's bitmap manager to the bitmap since it could be an unsupported type. Bitmap* pbm_bitmap = TheManager->Load(&bi_bitmap); // Were we successful? if (!pbm_bitmap) { // Report error to the user. guiInterface.bErrorPrintf(IDS_QB_CANT_LOAD_DIF_BMP, Diffuse.data()); // Return an error. return false; } // Again attempt to determine what kind of bitmap we are looking at. Is it paletted? if (bi_bitmap.Type() != BMM_TRUE_24) { // Report the error to the user. guiInterface.bErrorPrintf(IDS_QB_TEX_BMP_NOT_TRUECOLOR, Diffuse.data()); // Return an error result. return false; } else { // // We now know we have a 24 bit bitmap and that it has been loaded. We now need // to feed the bitmap // // Allocate a buffer for conversion of the bitmap. PixelBuf pxb_line(pbm_bitmap->Width()); // Were we successful? if (!pxb_line.Ptr()) { // No! Notify the user. guiInterface.bErrorMsg(IDS_PB_CANT_ALLOC_PXL_BUFFER); // Close the bitmap structure pbm_bitmap->Close(&bi_bitmap); return false; } // Convert the image from 24 bit to 8 bit. for (int i_row = 0; i_row < pbm_bitmap->Height(); i_row++) { // Update the progress bar. And determine if the user wants to abort. if (guiInterface.bUpdateProgressMeter((101 * i_row)/pbm_bitmap->Height())) { // Destroy the progress meter. guiInterface.DestroyProgressMeter(); // Yes! The user is requesting to abort this process. return false; } // Attempt to get a row of pixels. Were we successful? if (!pbm_bitmap->GetPixels(0, i_row, pbm_bitmap->Width(), pxb_line.Ptr())) { // No! Something went wrong. guiInterface.bErrorMsg(IDS_PXL_LOOP_READ_ERR); break; } else { // Add these colors to the histogram. pq_quantizer->AddToHistogram(pxb_line.Ptr(), pbm_bitmap->Width()); } } } // Destroy the progress meter. guiInterface.DestroyProgressMeter(); // Return a successful result. return true; } //********************************************************************************************** // bool FileExists(TSTR tstr_filename) { HANDLE hFile; // Attempt to open the file. hFile = CreateFile(tstr_filename.data(), GENERIC_READ, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); // Does the file exist? if (hFile != INVALID_HANDLE_VALUE) { // Yes! The file exists so close the file and return TRUE. CloseHandle(hFile); return true; } // No! Return false. return false; } //********************************************************************************************** // void Convert_Path(TSTR& tstr_map_name) { TSTR tstr_path; TSTR tstr_file; // Construct the texture file name for the progress meter. SplitPathFile(tstr_map_name, &tstr_path, &tstr_file); // Build the absolute path for the target of the bitmap. tstr_path.printf("%s%s", guiInterface.strGetBitmapDirName(), tstr_file.data()); // Assign the new path name to the file. tstr_map_name = tstr_path; } //********************************************************************************************** // bool ProcessBitmap( TSTR& tstr_diffuse, // The name of the source diffuse bitmap. TSTR& tstr_opacity, // The name of the source opacity bitmap. TSTR& tstr_bump, // The name of the source bump bitmap. BMM_Color_48* bmm_palette, // The quantized 48 bit palette for conversion. SColor24* ac24_palette, // The target palette for the bitmap. const uint u_palette_entries // The number of entries in the target palette. ) { bool b_success = true; TSTR tstr_diffuse_path; TSTR tstr_diffuse_file; // Construct the texture file name for the progress meter. TSTR tstr_progress_title; SplitPathFile(tstr_diffuse, &tstr_diffuse_path, &tstr_diffuse_file); tstr_progress_title.printf(guiInterface.strGetString(IDS_BITMAP_FMT), tstr_diffuse_file.data()); // Setup a bitmap info structure BitmapInfo bi_bitmap; // Setup the name of the source bitmap bi_bitmap.SetName(tstr_diffuse.data()); // Load the bitmap. Bitmap* pbm_bitmap = TheManager->Load(&bi_bitmap); // Were we successful? if (!pbm_bitmap) { // Report error to the user. guiInterface.bErrorPrintf(IDS_PB_CANT_LOAD_DIF_BMP, tstr_diffuse.data()); return false; } // Log a bunch of information about the logfile. slLogfile.Printf(guiInterface.strGetString(IDS_BMP_TITLE), (char *) tstr_diffuse.data()); slLogfile.Printf(guiInterface.strGetString(IDS_BMP_WIDTH_FMT), bi_bitmap.Width()); slLogfile.Printf(guiInterface.strGetString(IDS_BMP_HEIGHT_FMT), bi_bitmap.Height()); slLogfile.Printf(guiInterface.strGetString(IDS_BMP_FLAGS_FMT), bi_bitmap.Flags()); slLogfile.Printf(guiInterface.strGetString(IDS_BMP_MAX_RGB_LVL_FMT), pbm_bitmap->MaxRGBLevel()); // Again attempt to determine what kind of bitmap we are looking at. Is it paletted? if (bi_bitmap.Type() == BMM_PALETTED) { // Close the bitmap structure pbm_bitmap->Close(&bi_bitmap); // No! Report the error and return a failure result. guiInterface.bErrorPrintf(IDS_PB_DIF_BMP_NOT_TRUECOLOR, tstr_diffuse.data()); return false; } // This had better be a true color, 24 bit bitmap. else if (bi_bitmap.Type() == BMM_TRUE_24) { // The bitmap is true color. slLogfile.Printf(guiInterface.strGetString(IDS_PB_DIF_BMP_TRUECOLOR)); // // We now know we have a 24 bit bitmap and that it has been loaded. We now need // to create an 8 bit bitmap and place it into the export directory. // // Convert the bitmap path to the destination. Convert_Path(tstr_diffuse); // Configure the bitmap. CBitmapInfo bi_bitmap_info; bi_bitmap_info.Name(tstr_diffuse.data()); bi_bitmap_info.Width(bi_bitmap.Width()); bi_bitmap_info.Height(bi_bitmap.Height()); bi_bitmap_info.BitmapFormat(EFMT_BMP); bi_bitmap_info.Depth(8); // Attempt to create the bitmap. CBitmapImage bmi_bitmap; // Attempt to create the bitmap. Were we successful? if (!bmi_bitmap.bCreate(bi_bitmap_info, true)) { // No! Close the bitmap structure. pbm_bitmap->Close(&bi_bitmap); // Report the error and return a failure result. guiInterface.bErrorPrintf(IDS_PB_CANT_CREATE_DST_TXT_BMP, tstr_diffuse.data()); return false; } // Allocate a buffer for conversion of the bitmap. PixelBuf pxb_line(pbm_bitmap->Width()); // Were we successful? if (!pxb_line.Ptr()) { // No! Notify the user. pbm_bitmap->Close(&bi_bitmap); // Report the error and return a failure result. guiInterface.bErrorMsg(IDS_PB_CANT_ALLOC_PXL_BUFFER); return false; } // Attempt to save the palette in the new bitmap. Were we successful? if (bmi_bitmap.uSetPaletteEntries(0, u_palette_entries+1, ac24_palette) != u_palette_entries+1) { // No! Report an error and return. pbm_bitmap->Close(&bi_bitmap); // Report the error and return a failure result. guiInterface.bErrorMsg(IDS_PB_CANT_CFG_NEW_IMG_PAL); return false; } // Setup a progress meter since pixel conversion is a very slow operation. guiInterface.InitProgressMeter((char *) tstr_progress_title.data()); ColorPacker* pcp_color_packer = BMMNewColorPacker(pbm_bitmap->Width(), &bmm_palette[1], u_palette_entries); uint8 u1_scanline[MAX_BITMAP_WIDTH]; // Convert the image from 24 bit to 8 bit. for (int i_row = 0; i_row < pbm_bitmap->Height(); i_row++) { // Attempt to get a row of pixels. Were we successful? if (!pbm_bitmap->GetPixels(0, i_row, pbm_bitmap->Width(), pxb_line.Ptr())) { // No! Something went wrong. guiInterface.bErrorMsg(IDS_PXL_LOOP_READ_ERR); // Deallocate the color packer stuff. pcp_color_packer->DeleteThis(); // Destroy the progress meter. guiInterface.DestroyProgressMeter(); // Close the bitmap structure pbm_bitmap->Close(&bi_bitmap); return false; } else { // Update the progress meter. Does the user wish to abort? if (guiInterface.bUpdateProgressMeter((101 * i_row) / pbm_bitmap->Height())) { // // Yes! Clean things up here and abort. // // Deallocate the color packer stuff. pcp_color_packer->DeleteThis(); // Close the bitmap structure pbm_bitmap->Close(&bi_bitmap); // Destroy the progress meter. guiInterface.DestroyProgressMeter(); // Return a failure result. return false; } // Pack these colors into a single scan line. pcp_color_packer->PackLine(pxb_line.Ptr(), &u1_scanline[0], pbm_bitmap->Width()); // // Increment all of the pixel indices since the palette to which the 24 bit was quantized artificially // contained a transparency in the zero index. This means that each pixel index must be incremented. // This is less efficient, but it eliminates the possibility of some color being mapped to the // transparency pixel. // for (int i_col = 0; i_col < pbm_bitmap->Width(); i_col++) { // Increment the pixel value. u1_scanline[i_col]++; } // Place them in the bitmap. if (bmi_bitmap.uSetPalettedPixels(0, (uint) i_row, pbm_bitmap->Width(), &u1_scanline[0]) != (uint) pbm_bitmap->Width()) { // No! Report the error and return. guiInterface.bErrorMsg(IDS_PB_CANT_WR_PPXL_TO_BMP); // Deallocate the color packer stuff. pcp_color_packer->DeleteThis(); // Destroy the progress meter. guiInterface.DestroyProgressMeter(); // Close the bitmap structure pbm_bitmap->Close(&bi_bitmap); // Return a failure result. return false; } } } // Destroy the progress meter. guiInterface.DestroyProgressMeter(); // Deallocate the color packer stuff. pcp_color_packer->DeleteThis(); // Close the bitmap structure pbm_bitmap->Close(&bi_bitmap); // Was a transparency map supplied? if (tstr_opacity.length() > 0) { // Attempt to load the bitmap. CBitmapImage bmi_opacity; CBitmapIO bio_opacity; // Does this bitmap exist? if (!bio_opacity.bLoad((char *) tstr_opacity.data(), bmi_opacity)) { // No! Report the error. guiInterface.bErrorPrintf(IDS_PB_CANT_LOAD_OP_BMP, tstr_opacity.data()); // Return a failure result. return false; } // There will NEVER be an opacity map in the destination directory, so delete the reference to it. tstr_opacity.printf(""); // // Perform a number of reality checks on the data: Dimensions must be equal, Opacity map must be 2 color. // CBitmapInfo bi_opacity; bi_opacity = bmi_opacity.biBitmapInfo(); // Are the dimensions equal? if (bi_bitmap_info.uWidth() != bi_opacity.uWidth() || bi_bitmap_info.uHeight() != bi_opacity.uHeight()) { // No! Report the error. if (guiInterface.bErrorPrintf(IDS_PB_BMP_DIMS_INCOMPAT, bi_bitmap_info.strName(), bi_bitmap_info.uWidth(), bi_bitmap_info.uHeight(), bi_opacity.strName(), bi_opacity.uWidth(), bi_opacity.uHeight())) { // Yes! The user wants to quit so let him. return false; } } // Is the opacity map paletted? else if (!bmi_opacity.bIsPaletted()) { // Does the user want to quit? if (guiInterface.bErrorPrintf(IDS_PB_OP_BMP_NOT_PAL, bi_opacity.strName())) { // Yes! The user wants to quit so let him. return false; } } else { // // We now have the opacity map loaded and it is known to be valid. Modify the texture // map to incorporate transparent pixels. // // Determine which index value represents transparent. SColor24 ac24_tmp_palette[256]; if (bmi_opacity.uGetPaletteEntries(0, bi_opacity.uPaletteEntries(), &ac24_tmp_palette[0]) != bi_opacity.uPaletteEntries()) { // Does the user want to quit? if (guiInterface.bErrorPrintf(IDS_PB_CANT_RD_OP_BMP_PAL, bi_opacity.strName())) { // Yes! The user wants to quit so let him. return false; } } else { uint u_xparent_index = 256; // Locate the transparency value which is defined to be the RGB triple with values (0,0,0). for (uint8 u1_i = 0; u1_i < bi_opacity.uPaletteEntries(); u1_i++) { // Is this the value we are looking for? if (ac24_tmp_palette[u1_i].u1Red == 0 && ac24_tmp_palette[u1_i].u1Green == 0 && ac24_tmp_palette[u1_i].u1Blue == 0) { // Yes! Record this index value and exit the loop. u_xparent_index = u1_i; break; } } // Were we successful? if (u_xparent_index == 256) { // Does the user want to quit? if (guiInterface.bErrorPrintf(IDS_PB_CANT_FIND_XPAR_PXL, bi_opacity.strName())) { // Yes! The user wants to quit so let him. return false; } } else { // Keep track of the number of transparent pixels in this map. uint u_transparent_pixels = 0; // Process the texture map one row at a time. for (uint u_row = 0; u_row < bi_opacity.uHeight(); u_row++) { // Attempt to read the scanline. uint8 u1_opacity_scanline[MAX_BITMAP_WIDTH]; if (bmi_opacity.uGetPalettedPixels(0, (bi_opacity.uHeight()-1)-u_row, bi_opacity.uWidth(), &u1_opacity_scanline[0]) != bi_opacity.uWidth()) { // Does the user want to quit? if (guiInterface.bErrorPrintf(IDS_PB_CANT_RD_OP_BMP_SCANLINE, bi_opacity.strName())) { // Yes! The user wants to quit so let him. return false; } break; } // Read the texture map scanline. if (bmi_bitmap.uGetPalettedPixels(0, u_row, bi_opacity.uWidth(), &u1_scanline[0]) != bi_opacity.uWidth()) { // Does the user want to quit? if (guiInterface.bErrorPrintf(IDS_PB_CANT_RD_DIF_BMP_SCANLINE, bi_bitmap_info.strName())) { // Yes! The user wants to quit so let him. return false; } break; } // Loop through the scanline and process all of the pixels. for (uint u_col = 0; u_col < bi_opacity.uWidth(); u_col++) { // Is this pixel supposed to be transparent? if (u1_opacity_scanline[u_col] == u_xparent_index) { // Make this pixel transparent. u1_scanline[u_col] = 0; // Increment the transparent pixel count. u_transparent_pixels++; } } // Attempt to write this bitmap back to the texture map. if (bmi_bitmap.uSetPalettedPixels(0, u_row, bi_opacity.uWidth(), &u1_scanline[0]) != bi_opacity.uWidth()) { // Does the user want to quit? if (guiInterface.bErrorPrintf(IDS_PB_CANT_WR_SCANLINE_2_DIF_BMP, bi_bitmap_info.strName())) { // Yes! The user wants to quit so let him. return false; } break; } } // Log the transparent pixel ratio to the logfile for the artists. slLogfile.Printf(guiInterface.strGetString(IDS_PB_PXL_PRCS_STATS), bi_opacity.uWidth()*bi_opacity.uHeight(), u_transparent_pixels,((float) u_transparent_pixels*100.0)/((float) (bi_opacity.uWidth()*bi_opacity.uHeight()))); } } } } // Was a bump map file supplied? if (tstr_bump.length() > 0) { CBitmapIO bio_bump; CBitmapImage bmi_bump; // Attempt to load the bumpmap. if (!bio_bump.bLoad((char *) tstr_bump.data(), bmi_bump)) { // Does the user want to quit? if (guiInterface.bErrorPrintf(IDS_PB_CANT_LOAD_BU_BMP, tstr_bump.data())) { // Yes! The user wants to quit so let him. return false; } } CBitmapInfo bi_bump = bmi_bump.biBitmapInfo(); // Are the dimensions equal? if (bi_bitmap_info.uWidth() != bi_bump.uWidth() || bi_bitmap_info.uHeight() != bi_bump.uHeight()) { // Does the user want to quit? if (guiInterface.bErrorPrintf(IDS_PB_BMP_DIMS_INCOMPAT, bi_bitmap_info.strName(), bi_bitmap_info.uWidth(), bi_bitmap_info.uHeight(), bi_bump.strName(), bi_bump.uWidth(), bi_bump.uHeight())) { // Yes! The user wants to quit so let him. return false; } } // Is the opacity map paletted? else if (!bmi_bump.bIsPaletted()) { // Does the user want to quit? if (guiInterface.bErrorPrintf(IDS_PB_BU_BMP_NOT_PAL, bi_bump.strName())) { // Yes! The user wants to quit so let him. return false; } } else { // Get the info on the bumpmap. CBitmapInfo bi_bump = bmi_bump.biBitmapInfo(); // Convert the bitmap path to the destination. Convert_Path(tstr_bump); // Change the name of the bump map to correspond to the new bump map. bi_bump.Name(tstr_bump.data()); // Attempt to update the name in the bitmap image. if (!bmi_bump.bBitmapInfo(bi_bump)) { // Does the user want to quit? if (guiInterface.bErrorPrintf(IDS_PB_CANT_UPDATE_DST_BU_NAME, bi_bump.strName())) { // Yes! The user wants to quit so let him. return false; } } // Invert the bump map vertically. for (uint u_row = 0; u_row < bi_bump.uHeight() / 2; u_row++) { // Attempt to read the scanline. uint8 u1_bump_scanline[2][MAX_BITMAP_WIDTH]; // Get a scanline from the top of the bitmap. if (bmi_bump.uGetPalettedPixels(0, (bi_bump.uHeight()-1)-u_row, bi_bump.uWidth(), &u1_bump_scanline[0][0]) != bi_bump.uWidth()) { // Does the user want to quit? if (guiInterface.bErrorPrintf(IDS_PB_CANT_RD_BU_BMP_SCANLINE, bi_bump.strName())) { // Yes! The user wants to quit so let him. return false; } break; } // Get a scanline from the bottom of the bitmap. if (bmi_bump.uGetPalettedPixels(0, u_row, bi_bump.uWidth(), &u1_bump_scanline[1][0]) != bi_bump.uWidth()) { // Does the user want to quit? if (guiInterface.bErrorPrintf(IDS_PB_CANT_RD_BU_BMP_SCANLINE, bi_bump.strName())) { // Yes! The user wants to quit so let him. return false; } break; } // Attempt to write the scanlines to the bitmaps. if (bmi_bump.uSetPalettedPixels(0, (bi_bump.uHeight()-1)-u_row, bi_bump.uWidth(), &u1_bump_scanline[1][0]) != bi_bump.uWidth()) { // Display the Error message to the user. guiInterface.bErrorPrintf(IDS_PB_CANT_WR_SCANLINE_2_BU_BMP, bi_bump.strName()); break; } if (bmi_bump.uSetPalettedPixels(0, u_row, bi_bump.uWidth(), &u1_bump_scanline[0][0]) != bi_bump.uWidth()) { // Something went wrong in here, so report the error and exit the loop. guiInterface.bErrorPrintf(IDS_PB_CANT_WR_SCANLINE_2_BU_BMP, bi_bump.strName()); break; } } // Attempt to save the bumpmap to disk. if (!bio_bump.bSave(bmi_bump)) { // Something went wrong in here, so report the error and exit the loop. guiInterface.bErrorPrintf(IDS_PB_CANT_WR_BU_BMP_2_DSK, bi_bump.strName()); // Return a failure result to the user. return false; } } } // Finally attempt to write the bitmap to disk. CBitmapIO bio_bitmap; // Were we sucessful? if (!bio_bitmap.bSave(bmi_bitmap)) { // No! Return a failure result. return false; } } // Uh oh! We are seeing an unrecognized bitmap format. else { // Generate an error message and report it to the user. guiInterface.bErrorPrintf(IDS_PB_UNKNOWN_BMP_TYPE, bi_bitmap.Type()); // Close the bitmap structure pbm_bitmap->Close(&bi_bitmap); // Return a failure result. return false; } // Return a successful result return true; } //********************************************************************************************** // bool ProcessObjectBitmaps(CObjectDefList* pod_object_list, bool b_quantize_bitmaps, bool b_gen_groff) { uint u_object = 0; bool b_result = true; CSymTab st_texture_bitmaps; SColor24 ac24_palette[256]; BMM_Color_48 bmm_palette[256]; uint u_palette_entries; // Do we have a valid object list? if (pod_object_list->podHead == 0) { // No! Return an error. return false; } // Setup a pointer to first node in the object list. CObjectDef* pod_node = pod_object_list->podHead; // Does the user want to quanitize the bitmaps? if (b_quantize_bitmaps) { // Allocate a quantizer. Quantizer* pq_quantizer = BMMNewQuantizer(); // Were we successful? if (pq_quantizer == 0) { // No! Report the error and return an error. return guiInterface.bErrorMsg(guiInterface.strGetString(IDS_POB_CANT_ALLOC_QUANT)); } // Attempt to allocate the quantizer histogram. Were we successful? if (!pq_quantizer->AllocHistogram()) { // No! Report the error, aeallocate the quantizer and return an error. guiInterface.bErrorMsg(guiInterface.strGetString(IDS_POB_CANT_ALLOC_HIST)); // Delete the quantizer. pq_quantizer->DeleteThis(); return false; } // // Loop through the list and observe all the pixels in each of the bitmaps so that similar // kinds of bitmaps can eventually be quantized to a common palette. // // Throw up a progress meter indicating the progress through building the quantization of the objects. guiInterface.InitProgressMeter(guiInterface.strGetString(IDS_QUANTIZING)); // Loop through all the objects in the scene. while (pod_node != 0) { // Update the progress indicator. if (guiInterface.bUpdateProgressMeter((int) (u_object*101)/pod_object_list->uObjectCount())) { // Remove the progress indicator. guiInterface.DestroyProgressMeter(); return false; } TSTR tstr_texture_name; // // First start out by making two lists which contains the texture map names and // the export map names. // // Loop through the material list for this object. for (uint u_i=0; u_i < pod_node->uMaterialCount; u_i++) { // Get the diffuse, opacity and bump bitmap names. tstr_texture_name.printf("%s", pod_node->astrTextureMap[u_i]); // We have not seen this bitmap before so add it to the bitmap list. if (!st_texture_bitmaps.shLookup((char *) tstr_texture_name.data())) { // We have not seen this bitmap before so add it to the bitmap list. st_texture_bitmaps.shInsert((char *) tstr_texture_name.data()); // Pass the bitmaps into the quantizer for observation. Were we successful? if (!QuantizeBitmap(pq_quantizer, tstr_texture_name)) { // Set the error result flag to indicate something went wrong. b_result = false; // Exit the loop. break; } } // Increment the progress counter. u_object++; } // Advance to the next node. pod_node = pod_node->podNext; } // Destroy the progress meter. guiInterface.DestroyProgressMeter(); // Were we successful at quantizing the bitmaps? if (!b_result) { // No! Deallocate the quantizer and return an error. pq_quantizer->DeleteThis(); return false; } // // Partition the histogram into a palette. // // Set palette entry 0 to contain the color definition of 100% blue to designate which pixels // are transparent which viewed in a bitmap viewer. bmm_palette[0].r = 0; bmm_palette[0].g = 0; bmm_palette[0].b = 65535; u_palette_entries = pq_quantizer->Partition(&bmm_palette[1], 255, 0); // // The color at index 0 needs to be relocated to another index in the palette since index // 0 has been reserved as the transparency index, when transparency is in use. This should // always be possible because the maximum number of palette colors is 255. This therefore // leaves 1 palette entry which can be used as a transparent pixel index. // // Since the quantizer has no knowledge of transparent pixels, and our implementation which // uses index 0 for the transparent pixel index, all pixels which are currently using pixel // index 0, must be relocated to some other index, otherwise they will appear transparent. // This is accomplished by locating the first free pixel index in the palette and replacing // all pixels with an index of 0 with the new index value. For example, if the number of // palette entries returned from the partition request is 253, then all pixels with the // index value of 0 will be replaced with the value 253 and the palette entry at index 253 // will contain the pixel definition from palette entry 0. Palette entry 0 will then be // given the value which corresponds to (RGB) (0.0, 0.0, 1.0), which is full blue. This is // done so that bitmaps which have been encoded with transparency information, allow tools // such as photoshop, to display all the transparent regions with a color which is unlikely // to appear in the bitmaps itself. // // Convert the palette entries from Max format 48bit to our internal format 24bit. Convert(&ac24_palette[0], &bmm_palette[0], u_palette_entries+1); // Deallocate the quantizer, since it is no longer needed. pq_quantizer->DeleteThis(); } // Flush the symbol table. CSymTab st_export_bitmaps; // Prepare to loop through the list again and start processing bitmaps. pod_node = pod_object_list->podHead; // Reset the object counter to 0 u_object = 0; // Loop through all the objects in the scene. while(pod_node) { // Loop through the material list for this object. for (uint u_i=0; u_i < pod_node->uMaterialCount; u_i++) { TSTR tstr_texture_name; TSTR tstr_opacity_name; TSTR tstr_bump_name; // Get the diffuse, opacity and bump bitmap names if they are present. if (pod_node->astrTextureMap[u_i]) { tstr_texture_name.printf("%s", pod_node->astrTextureMap[u_i]); } if (pod_node->astrOpacityMap[u_i]) { tstr_opacity_name.printf("%s", pod_node->astrOpacityMap[u_i]); } if (pod_node->astrBumpMap[u_i]) { tstr_bump_name.printf("%s", pod_node->astrBumpMap[u_i]); } // We have not seen this bitmap before so add it to the bitmap list. if (!st_export_bitmaps.shLookup(pod_node->astrTextureMap[u_i])) { // We have not seen this bitmap before so add it to the bitmap list. st_export_bitmaps.shInsert((char *) tstr_texture_name.data()); // Does the user want to quantize the bitmaps? if (b_quantize_bitmaps) { // Process the bitmap. Were we successful? if (!ProcessBitmap(tstr_texture_name, tstr_opacity_name, tstr_bump_name, bmm_palette, ac24_palette, u_palette_entries)) { // Return an error. b_result = false; } } else { // // No! Then make sure all the required bitmaps exist, otherwise a generated GROFF // file will not be able to load successfully. // TSTR tstr_error_msg; TSTR tstr_path; TSTR tstr_file; // Did the user supply a texture map? if (tstr_texture_name.length() > 0 ) { // Convert the bitmap to it's new name. Convert_Path(tstr_texture_name); // Does a bitmap exist in the map directory? if (b_gen_groff && !FileExists(tstr_texture_name.data())) { // // No! Then this is an error, but treat it as a warning because the user may // know what he is doing. He has been warned. // SplitPathFile(tstr_texture_name, &tstr_path, &tstr_file); tstr_path.printf("%s%s", guiInterface.strGetExportPath(), guiInterface.strGetBitmapDirName()); tstr_error_msg.printf(guiInterface.strGetString(IDS_POB_MISSING_QUANTIZED_BMP), pod_node->estrObjectName.strData(), tstr_file.data(), tstr_path.data()); // Report the message to the user. guiInterface.bWarningMsg(tstr_error_msg.data()); } } // Did the user supply an opacity map? if (tstr_opacity_name.length() > 0) { Convert_Path(tstr_opacity_name); } // Did the user supply a bump map? if (tstr_bump_name.length() > 0) { Convert_Path(tstr_bump_name); // Does a bitmap exist in the map directory? if (b_gen_groff && !FileExists(tstr_bump_name.data())) { // // No! Then this is an error, but treat it as a warning because the user may // know what he is doing. He has been warned. // SplitPathFile(tstr_bump_name, &tstr_path, &tstr_file); tstr_path.printf("%s%s", guiInterface.strGetExportPath(), guiInterface.strGetBitmapDirName()); tstr_error_msg.printf(guiInterface.strGetString(IDS_POB_MISSING_BU_BMP), pod_node->estrObjectName.strData(), tstr_file.data(), tstr_path.data()); // Report the message to the user. guiInterface.bWarningMsg(tstr_error_msg.data()); } } } } else { // // Major hack for now. Since the map has been seen, we must build the converted map names // for the textures manually. // if (tstr_texture_name.length() > 0 ) { Convert_Path(tstr_texture_name); } if (tstr_opacity_name.length() > 0) { Convert_Path(tstr_opacity_name); } if (tstr_bump_name.length() > 0) { Convert_Path(tstr_bump_name); } } // Update all the bitmap names since they have changed. pod_node->TextureName(u_i, tstr_texture_name.data()); pod_node->OpacityName(u_i, tstr_opacity_name.data()); pod_node->BumpmapName(u_i, tstr_bump_name.data()); } // Increment the object counter. u_object++; // Advance to the next object in the list. pod_node = pod_node->podNext; } // Return a successful result. return b_result; } //********************************************************************************************** // int SingleMaterial(INode* pin_inode, CMathematicsUtil& mutil_utils) { Mtl* pmtl_material = pin_inode->GetMtl(); Class_ID cid_class_id; TSTR tstr_export_name; // Do we have a valid material? if (pmtl_material == false) { // Report the error to the user. mutil_utils.guiInterface.bErrorMsg(IDS_SM_MISSING_MTL); // Return 0 indicating a material definition was not successfully processed. return 0; } else { mutil_utils.slLogfile.Printf(guiInterface.strGetString(IDS_SINGLE_MTL)); } // Make sure we are only handling standard materials. cid_class_id = pmtl_material->ClassID(); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_MTL_CLASS_ID), cid_class_id.PartA(), cid_class_id.PartB()); // Is this material something other than a default material? if (cid_class_id != Class_ID(DMTL_CLASS_ID, 0)) { // Yes! We cannot do anything about this now so return an error. mutil_utils.guiInterface.bErrorMsg(IDS_SM_MUST_USE_SM); // Return 0 indicating a material definition was not successfully processed. return 0; } // // We now have identified this material as a default material. Setup a pointer to the // standard material and determine the material's properties. // StdMat* psmtl_material = (StdMat *) pmtl_material; // Get all the material default parameters. MaterialDefaults(psmtl_material); // Check the diffuse channel for a bitmap. TSTR tstr_diffuse_name; Texmap* ptm_diffuse_map = (BitmapTex *) psmtl_material->GetSubTexmap(ID_DI); // Does this material have a map on the diffuse channel? if (ptm_diffuse_map == false) { // No! Since a diffuse bitmap is required, report the error. mutil_utils.guiInterface.bErrorMsg(IDS_SM_DIF_BMP_MISSING); // Return 0 indicating a material definition was not successfully processed. return 0; } // Determine if the diffuse texture map is a bitmap. cid_class_id = ptm_diffuse_map->ClassID(); mutil_utils.slLogfile.Printf(guiInterface.strGetString(IDS_SM_DIF_BMP_ID), cid_class_id.PartA(), cid_class_id.PartB()); // Is this a bitmap texture? if (cid_class_id != Class_ID(BMTEX_CLASS_ID, 0)) { // No! This is not a bitmap so return an error. mutil_utils.guiInterface.bErrorMsg(IDS_SM_DIF_MTL_INVALID_BMP); // Return 0 indicating a material definition was not successfully processed. return 0; } // Get the bitmap information. BitmapTex* pbm_diffuse = (BitmapTex *) ptm_diffuse_map; // Get the name of the bitmap. tstr_diffuse_name = pbm_diffuse->GetMapName(); // Does a name exist with this bitmap? if (tstr_diffuse_name.data() == 0) { // No! This should never happen as it indicates an internal MAX error. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_BMP_MISSING_NAME)); // Return 0 indicating a material definition was not successfully processed. return 0; } mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_DIF_BMP_NAME), tstr_diffuse_name.data()); // // Does this material have a map on the opacity channel? // TSTR tstr_opacity_name; Texmap* ptm_opacity_map = (BitmapTex *) psmtl_material->GetSubTexmap(ID_OP); if (ptm_opacity_map) { // Determine if the texture map is a Now make sure the texture map is a bitmap. cid_class_id = ptm_opacity_map->ClassID(); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_SM_OP_BMP_ID), cid_class_id.PartA(), cid_class_id.PartB()); // Is this a bitmap texture? if (cid_class_id != Class_ID(BMTEX_CLASS_ID, 0)) { mutil_utils.guiInterface.bErrorMsg(IDS_SM_OP_MTL_INVALID_BMP); // Return 0 indicating a material definition was not successfully processed. return 0; } // Get the bitmap information. BitmapTex* pbm_opacity = (BitmapTex *) ptm_opacity_map; // Get the name of the bitmap. tstr_opacity_name = pbm_opacity->GetMapName(); // Sanity check to make sure the name exists if (tstr_opacity_name.data() == 0) { mutil_utils.guiInterface.bErrorMsg(IDS_BMP_MISSING_NAME); // Return 0 indicating a material definition was not successfully processed. return 0; } mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_OP_BMP_NAME), tstr_opacity_name.data()); } // // Does this material have a map on the bump channel? // TSTR tstr_bump_name; Texmap* ptm_bump_map = (BitmapTex *) psmtl_material->GetSubTexmap(ID_BU); if (ptm_bump_map) { // Determine if the texture map is a Now make sure the texture map is a bitmap. cid_class_id = ptm_bump_map->ClassID(); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_SM_BU_BMP_ID), cid_class_id.PartA(), cid_class_id.PartB()); // Is this a bitmap texture? if (cid_class_id != Class_ID(BMTEX_CLASS_ID, 0)) { mutil_utils.guiInterface.bErrorMsg(IDS_SM_BU_MTL_INVALID_BMP); // Return 0 indicating a material definition was not successfully processed. return 0; } // Get the bitmap information. BitmapTex* pbm_bump = (BitmapTex *) ptm_bump_map; // Get the name of the bitmap. tstr_bump_name = pbm_bump->GetMapName(); // Sanity check to make sure the name exists if (tstr_bump_name.data() == 0) { mutil_utils.guiInterface.bErrorMsg(IDS_BMP_MISSING_NAME); // Return 0 indicating a material definition was not successfully processed. return 0; } mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_BU_BMP_NAME), tstr_bump_name.data()); // Get a copy of the texture output structure to determine texture strength. TextureOutput* pto_texture_out = pbm_bump->GetTexout(); // Were we successful? if (pto_texture_out != 0) { // Yes! Attempt to get the amount value for the bump map. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_BU_STRENGTH), pto_texture_out->GetOutputLevel(0)); } } // We are using a single object material. podCurrent->MaterialCount(1); // Record the name of the diffuse bitmap. if (tstr_diffuse_name.length() > 0) { podCurrent->TextureName(0, tstr_diffuse_name.data()); } // Record the name of the opacity bitmap. if (tstr_opacity_name.length() > 0) { podCurrent->OpacityName(0, tstr_opacity_name.data()); } // Record the name of the bump bitmap. if (tstr_bump_name.length() > 0) { podCurrent->BumpmapName(0, tstr_bump_name.data()); } // Return 1 indicating the number of material definitions successfuly processed. return 1; } //********************************************************************************************** // int MultiMaterial(INode* inode, CMathematicsUtil& mutil_utils) { Mtl* mtl = inode->GetMtl(); Class_ID CL_ID; CSymTab st_symtab; // Make sure we are only handling multi materials. CL_ID = mtl->ClassID(); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_MULTI_MTL_DEF)); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_MTL_CLASS_ID), CL_ID.PartA(), CL_ID.PartB()); // Only multi-materials types are supports. if (CL_ID != Class_ID(MULTI_CLASS_ID, 0)) { // Report the error. mutil_utils.guiInterface.bErrorMsg(IDS_MM_MUST_USE_MM); // Return an error result. return false; } // Are there any submaterials? There should be. int i; int SubMtls = mtl->NumSubMtls(); Mtl* sub; mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_SUB_MTL_COUNT), SubMtls); // We must be looking at a multi-sub object material. We now know how many textures are // associated with this material, so record it. podCurrent->MaterialCount(SubMtls); // Loop through all the materials and identify the bitmapped ones. for (i = 0; i < SubMtls; i++) { mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_SUB_MTL_NUM), i); // Get each submaterial and make sure it is valid. sub = mtl->GetSubMtl(i); // Is this clot in use? if (sub == 0) { // Continue on to next material continue; } // Setup a pointer to the standard material. StdMat* sm = (StdMat *) sub; // Get the default material mapping, selection, values. MaterialDefaults(sm); // Is there a texture map on the diffuse channel? Texmap* tm = (BitmapTex *) sm->GetSubTexmap(ID_DI); if (!tm) { // No! report the error and move onto the next channel. mutil_utils.guiInterface.bErrorPrintf(IDS_MM_DIF_BMP_MISSING, podCurrent->estrObjectName.strData(), i+1); continue; } // Make sure this is a bitmap texture since we cannot process anything else CL_ID = tm->ClassID(); if (CL_ID != Class_ID(BMTEX_CLASS_ID, 0)) { mutil_utils.guiInterface.bErrorPrintf(IDS_MM_DIF_MTL_INVALID_BMP, i); } BitmapTex* bm = (BitmapTex *) tm; TSTR tstr_diffuse_name; TSTR tstr_export_name; TSTR tstr_file; TSTR tstr_path; tstr_diffuse_name = bm->GetMapName(); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_EXP_FILENAME), tstr_export_name.data()); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_MM_DIF_BMP_ID), i, CL_ID.PartA(), CL_ID.PartB()); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_DIF_BMP_NAME), tstr_diffuse_name.data()); // Is there a bitmap map on the opacity channel? TSTR tstr_opacity_name; tstr_opacity_name.printf(""); // Get te texture associated with the opacity channel. tm = (BitmapTex *) sm->GetSubTexmap(ID_OP); if (tm) { // Make sure this is a bitmap texture since we don't support anything else. CL_ID = tm->ClassID(); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_MM_OP_BMP_ID), i, CL_ID.PartA(), CL_ID.PartB()); // Make sure this texture is a bitmap. Is it? if (CL_ID == Class_ID(BMTEX_CLASS_ID, 0)) { bm = (BitmapTex *) tm; // Get the opacity map name. tstr_opacity_name = bm->GetMapName(); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_OP_BMP_NAME), tstr_opacity_name.data()); } else { mutil_utils.guiInterface.bErrorPrintf(IDS_MM_OP_MTL_INVALID_BMP, i); } } // Is there a texture map on the bump channel? TSTR tstr_bump_name; tstr_bump_name.printf(""); tm = (BitmapTex *) sm->GetSubTexmap(ID_BU); if (tm) { // Make sure this is a bitmap texture since we don't support anything else. CL_ID = tm->ClassID(); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_MM_BU_BMP_ID), i, CL_ID.PartA(), CL_ID.PartB()); // Make sure this texture is a bitmap. Is it? if (CL_ID == Class_ID(BMTEX_CLASS_ID, 0)) { bm = (BitmapTex *) tm; // Get the opacity map name. tstr_bump_name = bm->GetMapName(); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_BU_BMP_NAME), (char *) tstr_bump_name.data()); } else { mutil_utils.guiInterface.bErrorPrintf(IDS_MM_BU_MTL_INVALID_BMP, i); } } // Record the name of the diffuse bitmap. if (tstr_diffuse_name.length() > 0) { podCurrent->TextureName(i, tstr_diffuse_name.data()); } // Record the name of the opacity bitmap. if (tstr_opacity_name.length() > 0) { podCurrent->OpacityName(i, tstr_opacity_name.data()); } // Record the name of the bump bitmap. if (tstr_bump_name.length() > 0) { podCurrent->BumpmapName(i, tstr_bump_name.data()); } } // Return the bitmap texture structure to the caller. return SubMtls; } //********************************************************************************************** // uint uExtractFaces(Mesh& mesh, CMathematicsUtil& mutil_utils) { // Build the rendering normals. mesh.buildRenderNormals(); // Setup constants for the face and vertex counts. const uint u_faces = mesh.getNumFaces(); const uint u_vertices = mesh.getNumVerts(); // Display the title banner for the faces. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_FACES_TITLE), u_faces); // Register the polygon faces and the normals podCurrent->FaceCount(u_faces); podCurrent->FaceNormalCount(u_faces); podCurrent->VertexNormalCount(u_faces); // Loop through all the faces in the list and get the face vertex indices. for (uint u_face = 0; u_face < u_faces; u_face++) { // Get the face vertex indices. Face& face = mesh.faces[u_face]; mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_FACE_DEFS), u_face, face.v[0], face.v[1], face.v[2]); } // Display the title banner for the face normals. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_FACE_NORMAL_TITLE), u_faces); // Loop through all the faces in the list and get the face normals. for (uint u_face = 0; u_face < u_faces; u_face++) { // Get the face normal. Point3& pt3_normal = mesh.getFaceNormal(u_face); mutil_utils.slLogfile.Printf(guiInterface.strGetString(IDS_FACE_NORMAL_DEFS), u_face, pt3_normal.x, pt3_normal.y, pt3_normal.z); // Record the face normal. podCurrent->FaceNormal(u_face, pt3_normal[0], pt3_normal[1], pt3_normal[2]); } // Display the title banner for the vertex normals. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_VERTEX_NORMAL_TITLE), u_faces); // Now get the vertex normal list. for (uint u_face = 0; u_face < u_faces; u_face++) { // Get the face vertex indices. Face& face = mesh.faces[u_face]; // Register each of the vertex indices. podCurrent->Face(u_face, (uint) face.v[0], (uint) face.v[1], (uint) face.v[2]); // Get the smoothing group for this face. uint u_smoothing_group = mesh.faces[u_face].getSmGroup(); // Loop through the vertex list. for (uint u_vertex = 0; u_vertex < 3; u_vertex++) { int cv = mesh.faces[u_face].v[u_vertex]; // Get the rendering vertex. RVertex* prv_render_vertex = mesh.getRVertPtr(cv); Point3 pt3_normal; if (prv_render_vertex->rFlags & SPECIFIED_NORMAL) { // Get the rendering normal (Circumstances???) pt3_normal = prv_render_vertex->rn.getNormal(); } else { // Get the normal count for this face. uint u_normal_count = (uint) (prv_render_vertex->rFlags & NORCT_MASK); // If there are normals and the smoothing group is > 0 (i.e. not flat shaded). if (u_normal_count && u_smoothing_group) { // Are the normals stored in the rendering information? if (u_normal_count == 1) { // Yes! Then the normal is stored in in rn. pt3_normal = prv_render_vertex->rn.getNormal(); } else { // No! They are in the corrent place. for (uint u_index = 0; u_index < u_normal_count; u_index++) { // Get the list of normals??? pt3_normal = prv_render_vertex->ern[u_index].getNormal(); } } } else { // // Either the smoothing group is zero or the normal count is zero. For // now just use the face normal. // pt3_normal = mesh.getFaceNormal(u_face); } } // Write the vertex normal out to the list. podCurrent->VertexNormal(u_face, u_vertex, (float) pt3_normal.x, (float) pt3_normal.y, (float) pt3_normal.z); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_VERTEX_NORMAL_DEFS), u_face, u_vertex, pt3_normal.x, pt3_normal.y, pt3_normal.z); } } // Display the title banner for the face-material definitions. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_FACE_MTL_TITLE), u_faces); // Do we need to dump a material index as well? if (Materials > 0) { // Yes! So inform the object manager that there are materials associated // with this object. podCurrent->TextureFaceCount((int) u_faces); // For each of the faces, dump the material index. for (uint u_face = 0; u_face < u_faces; u_face++) { mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_FACE_MTL_DEFS), u_face, mesh.faces[u_face].getMatID()); // Record the material index for this face. podCurrent->FaceMaterialIndex(u_face, mesh.faces[u_face].getMatID()); } } // Return the face count. return u_faces; } //********************************************************************************************** // uint uExtractTextureVertices(Mesh& mesh, CMathematicsUtil& mutil_utils) { // Determine the number of texture vertices to process. const int i_tvert_count = mesh.getNumTVerts(); podCurrent->TextureVertexCount(i_tvert_count); // Log the texture vertext count to the log file. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_TVERT_TITLE), i_tvert_count); // Extract the texture vertex list. for (int i_i = 0; i_i < i_tvert_count; i_i++) { // Dump the texture vertex definition into the logfile. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_TVERT_DEFS), i_i, mesh.tVerts[i_i].x, mesh.tVerts[i_i].y); // Record the texture vertex definition. podCurrent->TextureVertex(i_i, mesh.tVerts[i_i].x, mesh.tVerts[i_i].y); } // Setup a count to the number of faces. const int i_face_count = mesh.getNumFaces(); // Log the texture face title and count into the log file. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_TFACE_TITLE), i_face_count); // Process the texture face list for (int i_i = 0; i_i < i_face_count; i_i++) { // Dump the texture face definition into the logfile. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_TFACE_DEFS), i_i, mesh.tvFace[i_i].t[0], mesh.tvFace[i_i].t[1], mesh.tvFace[i_i].t[2]); // Record the texture face definition. podCurrent->TextureFace(i_i, mesh.tvFace[i_i].t[0], mesh.tvFace[i_i].t[1], mesh.tvFace[i_i].t[2]); } // Return the texture vertex count. return i_tvert_count; } //********************************************************************************************** // uint uExtractVertices(Mesh& mesh, CMathematicsUtil& mutil_utils) { // uint u_count = mesh.getNumVerts(); mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_VERTEX_TITLE), u_count); // Register the number of vertices into the object podCurrent->VertexCount((int) u_count); // Loop through the vertex list and record each one. Point3 p3_vertex; for (uint u_i = 0; u_i < u_count; u_i++) { // Get a vertex. p3_vertex = mesh.verts[u_i]; // Register the vertex in the logfile. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_VERTEX_DEFS), u_i, p3_vertex[0], p3_vertex[1], p3_vertex[2]); // Record the vertex. podCurrent->Vertex(u_i, p3_vertex[0], p3_vertex[1], p3_vertex[2]); } // Return the number of vertices in the list. return u_count; } //********************************************************************************************** // bool bExtractMesh(CObjectListNode& oln_node, CMathematicsUtil& mutil_utils) { bool b_error = false; // Add an object the object definition list podCurrent = theList.podAddObject(oln_node.pobeEntry->tstrName.data()); // // Display the banner for this object and the classification of the node. // mutil_utils.DisplayNewline(); mutil_utils.DisplayPrompt(guiInterface.strGetString(IDS_EQ_SEPARATOR)); // Get the INode of the object INode* pin_inode = oln_node.pobeEntry->pinINode; // Evaluate the world state at a specific point in time. pin_inode->EvalWorldState(psep_the_scene_enum->tvTime).obj; // // Process the hierarchy of this object. // // Is this object a parent object a child? if (pin_inode->IsRootNode()) { mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_ROOT_OBJECT), oln_node.pobeEntry->tstrName.data(), pin_inode->NumberOfChildren()); } else { // Is this object a child node, with children or a leaf? int i_children = pin_inode->NumberOfChildren(); if (i_children) { // This is a child object so display the number of children. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_CHILD_OBJECT), oln_node.pobeEntry->tstrName.data(), pin_inode->NumberOfChildren()); } else { // This is a leaf object with no children. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_LEAF_OBJECT), oln_node.pobeEntry->tstrName.data(), pin_inode->NumberOfChildren()); } } // Complete the banner. mutil_utils.DisplayPrompt(guiInterface.strGetString(IDS_EQ_SEPARATOR)); // // Process the Object attribute information. // // // Display the OAL attribute source if it exists. // TSTR tstr_property_buffer; pin_inode->GetUserPropBuffer(tstr_property_buffer); // Is there any source associated with this object? if (tstr_property_buffer.length() > 0) { // Yes! Then place it into the log file. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_OAL_SOURCE), tstr_property_buffer.data()); } // // Construct a tri-mesg object for geometry processing. // // Setup a pointer to the scene object record then convert the object to a Tri-object. Object* pobj_object = oln_node.pobeEntry->pobjObject; TriObject* ptri_tri_object = (TriObject *) pobj_object->ConvertToType(psep_the_scene_enum->pipInterface->GetTime(), triObjectClassID); // Setup a reference to the mesh. Mesh& mesh = ptri_tri_object->mesh; // // Extract all of the placement information. // // Get the object TM before WSM. Matrix3 m3_tm_before_wsm = pin_inode->GetObjTMBeforeWSM(psep_the_scene_enum->tvTime); mutil_utils.DisplayMatrix(mutil_utils.guiInterface.strGetString(IDS_TMBeforeWSM), m3_tm_before_wsm); // Record it. podCurrent->TMBeforeWSM(m3_tm_before_wsm); // Get the object TM after WSM. Matrix3 m3_tm_after_wsm = pin_inode->GetObjTMAfterWSM(psep_the_scene_enum->tvTime); mutil_utils.DisplayMatrix(mutil_utils.guiInterface.strGetString(IDS_TMAfterWSM), m3_tm_after_wsm); // Record it. podCurrent->TMAfterWSM(m3_tm_after_wsm); // Get the object TM. Matrix3 m3_object_tm = pin_inode->GetObjectTM(psep_the_scene_enum->tvTime); mutil_utils.DisplayMatrix(mutil_utils.guiInterface.strGetString(IDS_ObjectTM), m3_object_tm); // Record it. podCurrent->ObjectTM(m3_object_tm); // Get the parent TM. Matrix3 m3_parent_tm = pin_inode->GetParentTM(psep_the_scene_enum->tvTime); mutil_utils.DisplayMatrix(mutil_utils.guiInterface.strGetString(IDS_ParentTM), m3_parent_tm); // Record it. podCurrent->ParentTM(m3_parent_tm); // Get the node TM and display it. Matrix3 m3_node_tm = pin_inode->GetNodeTM(psep_the_scene_enum->tvTime); mutil_utils.DisplayMatrix(mutil_utils.guiInterface.strGetString(IDS_NodeTM), m3_node_tm); // Record it. podCurrent->NodeTM(m3_node_tm); // Calculate the relative TM between the node and the parent. Matrix3 m3_relative_tm = m3_node_tm * Inverse(m3_parent_tm); mutil_utils.DisplayMatrix(mutil_utils.guiInterface.strGetString(IDS_RelativeTM), m3_relative_tm); // Record it. podCurrent->RelativeTM(m3_relative_tm); // Display the objects positional information. mutil_utils.DisplayPrompt(mutil_utils.guiInterface.strGetString(IDS_OBJ_OFF_TITLE1)); // Get the pivot offset information. Point3 p3_offset_pos = pin_inode->GetObjOffsetPos(); mutil_utils.DisplayPrompt(mutil_utils.guiInterface.strGetString(IDS_PIVOT_OFF)); mutil_utils.DisplayVector(p3_offset_pos, 1); // Record it. podCurrent->PivotOffset(p3_offset_pos); // Get the pivot orientation information. Quat q_offset_rot = pin_inode->GetObjOffsetRot(); mutil_utils.DisplayQuat(mutil_utils.guiInterface.strGetString(IDS_PIVOT_ROT), q_offset_rot); // Record it. podCurrent->PivotRotation(q_offset_rot); // For now setup the scaling values assuming uniform scaling. podCurrent->Scale(1.0f); // Get the object position. Point3 p3_position = m3_tm_after_wsm.GetRow(3); mutil_utils.DisplayPrompt(mutil_utils.guiInterface.strGetString(IDS_OBJ_POS)); mutil_utils.DisplayVector(p3_position); // Record it. podCurrent->Position(p3_position); // // Convert the rotation matrix to a quaternion. First, make sure that we are starting // with an ortho-normal matrix since the constructor blindly assumes the matrix is // orthogonal. The result being that a matrix with a non-unity (skewed) determinant // will cause an invalid quaternion to be created which results in invalid placement // of the object in the world. This effect is introduced by non-uniformly scaling an // object, then collapsing it's stack. // m3_tm_after_wsm.Orthogonalize(); Quat q_orient(m3_tm_after_wsm); mutil_utils.DisplayQuat(mutil_utils.guiInterface.strGetString(IDS_OBJ_ROT_QUAT), q_orient); // Record it. podCurrent->Rotation(q_orient); // Get the Euler form of the rotation. Point3 p3_rotation; p3_rotation = podCurrent->p3GetRotation(); // Display the orientation angles angles as Convert the Euler angles to degrees. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_F3_EULER), p3_rotation[0] * 180.0/3.1415926, p3_rotation[1] * 180.0/3.1415926, p3_rotation[2] * 180.0/3.1415926); // // Get the bounding box of the mesh. // mesh.buildBoundingBox(); Box3 bx3_bounding_box = mesh.getBoundingBox(); mutil_utils.DisplayBox(mutil_utils.guiInterface.strGetString(IDS_OBJ_BBOX), bx3_bounding_box); // Record it. podCurrent->BoundingBox(bx3_bounding_box); // // Process the materials which are assigned to the object. // Mtl* mtl = pin_inode->GetMtl(); // // Counter to keep track of the number of materials mapped on a surface, // which is important because we must dump a material index for each of the // textured faces within an object. // Materials = 0; // Does this object have an associated amterial? if (mtl != 0) { mutil_utils.DisplayPrompt(mutil_utils.guiInterface.strGetString(IDS_MTL_TITLE)); // Is this a single or multi-material? if (mtl->IsMultiMtl()) { // This is a multi-material object, so process it as such. Materials = MultiMaterial(pin_inode, mutil_utils); // Log the number of materials located for this object. mutil_utils.slLogfile.Msg(mutil_utils.guiInterface.strGetString(IDS_MULTI_MTL), Materials); } else { // This is a single material object, so process it as such. Materials = SingleMaterial(pin_inode, mutil_utils); // Log the fact that this is a single material object. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_SINGLE_MTL)); } } else { // // In the future allow the user the ability for the user to export this object // with a "flat shaded" material option so it can continue to work. // mutil_utils.guiInterface.bErrorPrintf(mutil_utils.guiInterface.strGetString(IDS_ERR_NO_MTLS), oln_node.pobeEntry->tstrName.data()); // Set the global error flag to true. b_error = true; // Set the material counter to 0. Materials = 0; } // // Does the mesh contain any vertices? if (mesh.getNumVerts()) { // Attempt to dump the point array. Were we successful? if (uExtractVertices(mesh, mutil_utils) == 0) { // No! If the object was not already a tri_mesh, then delete the temporary copy. if (pobj_object != (Object *) ptri_tri_object) { // Delete the mesh. ptri_tri_object->DeleteThis(); } // Return an error. return false; } // Display a title banner for the world space vertex coordinates. mutil_utils.DisplayPrompt(mutil_utils.guiInterface.strGetString(IDS_WORLD_SPACE_TITLE)); // Calculate the world space coordinate locations of the object. Point3 p3_tmp; for (int u_ii = 0; u_ii < mesh.getNumVerts(); u_ii++) { // Get a vertex from the file. p3_tmp[0] = podCurrent->afv3Vertex[u_ii].X; p3_tmp[1] = podCurrent->afv3Vertex[u_ii].Y; p3_tmp[2] = podCurrent->afv3Vertex[u_ii].Z; // Transform the objects vertices into world space. p3_tmp = m3_tm_after_wsm * p3_tmp; // Log the world vertex coordinate. mutil_utils.slLogfile.Printf(mutil_utils.guiInterface.strGetString(IDS_WORLD_VERTEX_FMT), u_ii, p3_tmp[0], p3_tmp[1], p3_tmp[2]); } // Does this mesh have any faces? if (mesh.getNumFaces()) { // Process the face array. if (uExtractFaces(mesh, mutil_utils) == 0) { // No! If the object was not already a tri_mesh, then delete the temporary copy. if (pobj_object != (Object *) ptri_tri_object) { // Delete the mesh. ptri_tri_object->DeleteThis(); } // Return an error. return false; } } // Are there any texture vertices? if (mesh.getNumTVerts()) { // Process the texture vertices. if (uExtractTextureVertices(mesh, mutil_utils) == 0) { // No! If the object was not already a tri_mesh, then delete the temporary copy. if (pobj_object != (Object *) ptri_tri_object) { // Delete the mesh. ptri_tri_object->DeleteThis(); } // Return an error. return false; } } } else { // No! Report the error, then exit. mutil_utils.guiInterface.bErrorPrintf(mutil_utils.guiInterface.strGetString(IDS_ERR_NO_GEOMETRY), oln_node.pobeEntry->tstrName.data()); // Set the global error flag to true. b_error = true; // Return an error. return false; } // Make sure that we delete any meshes that were created during scene enumeration. Is this a candidate? if (pobj_object != (Object *) ptri_tri_object) { // Yes! Delete it. ptri_tri_object->DeleteThis(); } // Return an error. return false; } //********************************************************************************************** // uint uExtractNodes(CObjectList& ol_object_list, CMathematicsUtil& mutil_utils) { // Loop through all the objects in the scene. for (uint u_i = 0; u_i < ol_object_list.uCount(); u_i++) { // Setup a pointer to a node in the list. CObjectListNode* poln_node = ol_object_list[u_i]; // Determine what type of object we are looking at. if (poln_node->pobeEntry->iType == TRIMESH_OBJECT) { // Yes! Were we able to succesfully extract the object information? if (!bExtractMesh(*poln_node, mutil_utils)) { // No! Return 0 as the number of nodes successfully extracted. return 0; } } else { // This is an unrecognized object type so report the error and return an error. mutil_utils.guiInterface.bErrorMsg(IDS_INVALID_EXP_OBJ_TYPE); } } // Return the number of objects successfully processed. return ol_object_list.uCount(); } //********************************************************************************************** // const TCHAR* JP2Export::Ext(int n) { // Since there is only one extension, just return it. return guiInterface.strGetString(IDS_JP2_EXTENSION); } //********************************************************************************************** // const TCHAR* JP2Export::LongDesc() { // Return the long description. return guiInterface.strGetString(IDS_JP2_LONGDESC); } //********************************************************************************************** // const TCHAR* JP2Export::ShortDesc() { // Return the short description. return guiInterface.strGetString(IDS_JP2_SHORTDESC); } //********************************************************************************************** // const TCHAR* JP2Export::AuthorName() { // Return the author's name. return guiInterface.strGetString(IDS_JP2_AUTHOR); } //********************************************************************************************** // const TCHAR* JP2Export::CopyrightMessage() { // Return the copyright message. return guiInterface.strGetString(IDS_JP2_COPYRIGHT); } //********************************************************************************************** // unsigned int JP2Export::Version() { // Return the current exporter version number. return EXPORTER_VERSION_NUMBER; } //********************************************************************************************** // void JP2Export::ShowAbout(HWND hWnd) { // Display an informational about box. MessageBox(hWnd, guiInterface.strGetString(IDS_JP2_ABOUT_BOX), guiInterface.strGetString(IDS_JP2_ABOUT_TITLE), MB_ICONINFORMATION | MB_OK); } //********************************************************************************************** // int JP2Export::DoExport(const TCHAR* tchr_export_filename, ExpInterface* pei_export_interface, Interface* pip_interface) { // Setup a local copy of the export filename. CEasyString estr_export_filename(tchr_export_filename); // Setup the parameter dialog. if (!DialogBoxParam(guiInterface.GetInstance(), MAKEINTRESOURCE(IDD_EXPORT_OPTIONS), GetActiveWindow(), ExportDlgProc, (LPARAM) this)) { // Return an error. return true; } // This type of export should generate a log file. guiInterface.GenerateLogfiles(b_gen_logfiles); // Attempt to build the Groff export file hierarchy. Were we successful? if (!guiInterface.bBuildExportHierarchy(tchr_export_filename)) { // No! Report the error then return. guiInterface.bErrorMsg(guiInterface.strGetString(IDS_ERR_CONST_EXP_HIER)); // Return an error. return true; } // Setup the GUI interface pointer in the GUI interface class. guiInterface.SetInterface(pip_interface); // Construct the complete file path. char str_logfile[256]; guiInterface.BuildPath(str_logfile, guiInterface.strGetLogfileDirPath(), guiInterface.strGetString(IDS_FILENAME_EXPORT)); // Does the user want logfiles generated? if (guiInterface.bGenerateLogfiles()) { slLogfile.Open(str_logfile); slLogfile.Enable(); } else { // Disable a potentially open logfile. slLogfile.Disable(); } // Start out by enumerating all the nodes in the scene which we are interested in. CSceneEnumProc sep_the_scene(pei_export_interface->theScene, pip_interface->GetTime(), pip_interface); // Were any nodes located? if (sep_the_scene.uCount() == 0) { // No! No inodes were located so notify the user and return. guiInterface.bWarningMsg(IDS_NO_ENUMERATED_INODES); // No! Display an error message. guiInterface.bErrorMsg(guiInterface.strGetString(IDS_NO_GROFF_GEN)); // Free all the memory used by the code generator theList.DeleteObjects(); // Return a successful result. return true; } // Process all of the object names to make sure there are no duplicates. sep_the_scene.BuildNames(); // Construct and object list. CObjectList ol_the_objects(sep_the_scene); // Setup global pointers to the object list and the scene enumeration list. psep_the_scene_enum = &sep_the_scene; // Setup the math utility structure. CMathematicsUtil mutil_utility(guiInterface, slLogfile); // Attempt to process the objects. Were we successful? if (uExtractNodes(ol_the_objects, mutil_utility) == false) { // No! Display an error message. guiInterface.bErrorMsg(guiInterface.strGetString(IDS_NO_GROFF_GEN)); // Free all the memory used by the code generator theList.DeleteObjects(); // Return a successful result. return true; } // Dump the list into a logfile. theList.Dump(); // Check the geometry and make sure it's OK before processing the bitmaps. CGeometry geo_geometry; bool b_result = geo_geometry.bCheckScene(&theList, estr_export_filename); // Close the logfile if it was open. slLogfile.Close(); // Were we successful? if (b_result) { // Process all the bitmaps. if (ProcessObjectBitmaps(&theList, b_quantize_bmp, b_gen_groff)) { CGroffExport ge; // Does the user want a groff file to be generated? if (b_gen_groff) { // Generate a GROFF file. ge.bSaveScene(&theList, tchr_export_filename); // Let the user know he has a GROFF file. guiInterface.InfoMsg(guiInterface.strGetString(IDS_GROFF_GEN)); } else { // Did the user request logfile creation only? if (b_quantize_bmp && b_gen_logfiles) { // Yes! Then notify the user the logfile creation os complete. guiInterface.InfoMsg(guiInterface.strGetString(IDS_QUANT_AND_GEN_LOGS)); } // Did the user request logfile creation only? else if (!b_quantize_bmp && b_gen_logfiles) { // Yes! Then notify the user the logfile creation os complete. guiInterface.InfoMsg(guiInterface.strGetString(IDS_GEN_LOGFILES)); } // Did the user request quantization only else if (b_quantize_bmp && !b_gen_logfiles) { // Yes! Then notify the user the bitmaps were successfully quantized. guiInterface.InfoMsg(guiInterface.strGetString(IDS_QUANTIZE_BMPS)); } } } } else { // No! Display an error message. guiInterface.bErrorMsg(guiInterface.strGetString(IDS_NO_GROFF_GEN)); } // Free all the memory used by the code generator theList.DeleteObjects(); // Set the GUI interface pointer to null. return true; } //Global variables and functions declared elsewhere as extern //needed by the libraries bool bIsTrespasser = false; bool bUseReplayFile = false; bool bInvertMouse = false; bool bUseOutputFiles = false; unsigned int g_u4NotifyParam = 0; unsigned int u4LookupResourceString(int, char*, unsigned int) { return 0; } void LineColour(int, int, int) {} void* hwndGetMainHwnd() { return nullptr; } HINSTANCE hinstGetMainHInstance() { return nullptr; } void ResetAppData() {} PFNWORLDLOADNOTIFY g_pfnWorldLoadNotify = nullptr;