nuclear@5: #include nuclear@5: #include nuclear@27: #include nuclear@5: #include nuclear@27: #include nuclear@5: #include nuclear@5: #include nuclear@5: #include "max.h" nuclear@5: #include "impexp.h" // SceneExport nuclear@5: #include "iparamb2.h" // ClassDesc2 nuclear@5: #include "plugapi.h" nuclear@5: #include "IGame.h" nuclear@5: #include "IGameExport.h" nuclear@5: #include "IConversionmanager.h" nuclear@25: #include "goat3d.h" nuclear@10: #include "config.h" nuclear@5: nuclear@5: nuclear@5: #pragma comment (lib, "core.lib") nuclear@5: #pragma comment (lib, "geom.lib") nuclear@5: #pragma comment (lib, "gfx.lib") nuclear@5: #pragma comment (lib, "mesh.lib") nuclear@5: #pragma comment (lib, "maxutil.lib") nuclear@5: #pragma comment (lib, "maxscrpt.lib") nuclear@5: #pragma comment (lib, "paramblk2.lib") nuclear@5: #pragma comment (lib, "msxml2.lib") nuclear@5: #pragma comment (lib, "igame.lib") nuclear@5: #pragma comment (lib, "comctl32.lib") nuclear@5: nuclear@5: nuclear@10: #define VERSION(major, minor) \ nuclear@10: ((major) * 100 + ((minor) < 10 ? (minor) * 10 : (minor))) nuclear@10: nuclear@5: static FILE *logfile; nuclear@5: static HINSTANCE hinst; nuclear@5: nuclear@5: class GoatExporter : public SceneExport { nuclear@27: private: nuclear@28: std::map mtlmap; nuclear@28: std::map nodemap; nuclear@27: nuclear@5: public: nuclear@5: IGameScene *igame; nuclear@5: nuclear@5: int ExtCount(); nuclear@5: const TCHAR *Ext(int n); nuclear@5: const TCHAR *LongDesc(); nuclear@5: const TCHAR *ShortDesc(); nuclear@5: const TCHAR *AuthorName(); nuclear@5: const TCHAR *CopyrightMessage(); nuclear@5: const TCHAR *OtherMessage1(); nuclear@5: const TCHAR *OtherMessage2(); nuclear@5: unsigned int Version(); nuclear@5: void ShowAbout(HWND win); nuclear@5: nuclear@5: int DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent = FALSE, DWORD opt = 0); nuclear@5: nuclear@27: void process_materials(goat3d *goat); nuclear@27: nuclear@27: void process_node(goat3d *goat, goat3d_node *parent, IGameNode *maxnode); nuclear@27: nuclear@27: void process_mesh(goat3d *goat, goat3d_mesh *mesh, IGameObject *maxobj); nuclear@27: void process_light(goat3d *goat, goat3d_light *light, IGameObject *maxobj); nuclear@27: void process_camera(goat3d *goat, goat3d_camera *cam, IGameObject *maxobj); nuclear@5: }; nuclear@5: nuclear@5: nuclear@5: int GoatExporter::ExtCount() nuclear@5: { nuclear@5: return 1; nuclear@5: } nuclear@5: nuclear@5: const TCHAR *GoatExporter::Ext(int n) nuclear@5: { nuclear@27: return L"xml"; nuclear@5: } nuclear@5: nuclear@5: const TCHAR *GoatExporter::LongDesc() nuclear@5: { nuclear@5: return L"Goat3D scene file"; nuclear@5: } nuclear@5: nuclear@5: const TCHAR *GoatExporter::ShortDesc() nuclear@5: { nuclear@5: return L"Goat3D"; nuclear@5: } nuclear@5: nuclear@5: const TCHAR *GoatExporter::AuthorName() nuclear@5: { nuclear@5: return L"John Tsiombikas"; nuclear@5: } nuclear@5: nuclear@5: const TCHAR *GoatExporter::CopyrightMessage() nuclear@5: { nuclear@5: return L"Copyright 2013 (C) John Tsiombikas - GNU General Public License v3, see COPYING for details."; nuclear@5: } nuclear@5: nuclear@5: const TCHAR *GoatExporter::OtherMessage1() nuclear@5: { nuclear@5: return L"other1"; nuclear@5: } nuclear@5: nuclear@5: const TCHAR *GoatExporter::OtherMessage2() nuclear@5: { nuclear@5: return L"other2"; nuclear@5: } nuclear@5: nuclear@5: unsigned int GoatExporter::Version() nuclear@5: { nuclear@10: return VERSION(VER_MAJOR, VER_MINOR); nuclear@5: } nuclear@5: nuclear@5: void GoatExporter::ShowAbout(HWND win) nuclear@5: { nuclear@5: MessageBoxA(win, "Goat3D exporter plugin", "About this plugin", 0); nuclear@5: } nuclear@5: nuclear@5: int GoatExporter::DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, nuclear@5: BOOL non_interactive, DWORD opt) nuclear@5: { nuclear@28: mtlmap.clear(); nuclear@28: nodemap.clear(); nuclear@27: nuclear@5: char fname[512]; nuclear@5: wcstombs(fname, name, sizeof fname - 1); nuclear@5: nuclear@58: fprintf(logfile, "Exporting Goat3D Scene (text) file: %s\n", fname); nuclear@5: if(!(igame = GetIGameInterface())) { nuclear@5: fprintf(logfile, "failed to get the igame interface\n"); nuclear@5: return IMPEXP_FAIL; nuclear@5: } nuclear@5: IGameConversionManager *cm = GetConversionManager(); nuclear@5: cm->SetCoordSystem(IGameConversionManager::IGAME_OGL); nuclear@5: igame->InitialiseIGame(); nuclear@5: igame->SetStaticFrame(0); nuclear@5: nuclear@25: goat3d *goat = goat3d_create(); nuclear@5: nuclear@27: process_materials(goat); nuclear@27: nuclear@27: // process all nodes nuclear@27: for(int i=0; iGetTopLevelNodeCount(); i++) { nuclear@27: IGameNode *node = igame->GetTopLevelNode(i); nuclear@27: process_node(goat, 0, node); nuclear@27: } nuclear@5: nuclear@25: if(goat3d_save(goat, fname) == -1) { nuclear@25: goat3d_free(goat); nuclear@25: return IMPEXP_FAIL; nuclear@25: } nuclear@25: nuclear@25: goat3d_free(goat); nuclear@5: return IMPEXP_SUCCESS; nuclear@5: } nuclear@5: nuclear@25: static const char *max_string(const MCHAR *wstr) nuclear@25: { nuclear@25: if(!wstr) return 0; nuclear@25: static char str[512]; nuclear@25: wcstombs(str, wstr, sizeof str - 1); nuclear@25: return str; nuclear@25: } nuclear@25: nuclear@27: void GoatExporter::process_materials(goat3d *goat) nuclear@5: { nuclear@5: IGameProperty *prop; nuclear@5: nuclear@5: int num_mtl = igame->GetRootMaterialCount(); nuclear@25: for(int i=0; iGetRootMaterial(i); nuclear@25: if(maxmtl) { nuclear@25: goat3d_material *mtl = goat3d_create_mtl(); nuclear@5: nuclear@25: const char *name = max_string(maxmtl->GetMaterialName()); nuclear@25: if(name) { nuclear@25: goat3d_set_mtl_name(mtl, name); nuclear@5: } nuclear@5: nuclear@25: // diffuse nuclear@25: if((prop = maxmtl->GetDiffuseData())) { nuclear@25: Point3 diffuse(1, 1, 1); nuclear@5: prop->GetPropertyValue(diffuse); nuclear@25: goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_DIFFUSE, diffuse[0], nuclear@25: diffuse[1], diffuse[2]); nuclear@5: } nuclear@25: // specular nuclear@25: if((prop = maxmtl->GetSpecularData())) { nuclear@25: Point3 specular(0, 0, 0); nuclear@5: prop->GetPropertyValue(specular); nuclear@25: nuclear@25: float sstr = 1.0; nuclear@25: if((prop = maxmtl->GetSpecularLevelData())) { nuclear@25: prop->GetPropertyValue(sstr); nuclear@25: } nuclear@25: goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_SPECULAR, specular[0] * sstr, nuclear@25: specular[1] * sstr, specular[2] * sstr); nuclear@5: } nuclear@25: // shininess nuclear@25: if((prop = maxmtl->GetGlossinessData())) { nuclear@25: float shin; nuclear@5: prop->GetPropertyValue(shin); nuclear@25: goat3d_set_mtl_attrib1f(mtl, GOAT3D_MAT_ATTR_SHININESS, shin * 100.0); nuclear@5: } nuclear@5: nuclear@25: // textures nuclear@25: for(int j=0; jGetNumberOfTextureMaps(); j++) { nuclear@25: IGameTextureMap *tex = maxmtl->GetIGameTextureMap(j); nuclear@5: nuclear@25: const char *fname = max_string(tex->GetBitmapFileName()); nuclear@25: if(!fname) { nuclear@25: continue; nuclear@25: } nuclear@25: nuclear@25: int slot = tex->GetStdMapSlot(); nuclear@25: switch(slot) { nuclear@25: case ID_DI: // diffuse nuclear@25: goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_DIFFUSE, fname); nuclear@25: break; nuclear@25: nuclear@25: case ID_SP: nuclear@25: case ID_SS: nuclear@25: goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SPECULAR, fname); nuclear@25: break; nuclear@25: nuclear@25: case ID_SH: nuclear@25: goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SHININESS, fname); nuclear@25: break; nuclear@25: nuclear@25: case ID_BU: nuclear@25: goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_NORMAL, fname); nuclear@25: break; nuclear@25: nuclear@25: case ID_RL: nuclear@25: goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_REFLECTION, fname); nuclear@25: break; nuclear@25: nuclear@25: case ID_RR: nuclear@25: goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_TRANSMISSION, fname); nuclear@25: break; nuclear@25: nuclear@25: default: nuclear@25: break; nuclear@5: } nuclear@5: } nuclear@25: nuclear@25: goat3d_add_mtl(goat, mtl); nuclear@28: mtlmap[maxmtl] = mtl; nuclear@5: } nuclear@5: } nuclear@25: } nuclear@5: nuclear@27: void GoatExporter::process_node(goat3d *goat, goat3d_node *parent, IGameNode *maxnode) nuclear@25: { nuclear@27: goat3d_node *node = goat3d_create_node(); nuclear@27: goat3d_add_node(goat, node); nuclear@25: nuclear@27: const char *name = max_string(maxnode->GetName()); nuclear@27: if(name) { nuclear@27: goat3d_set_node_name(node, name); nuclear@27: } nuclear@27: nuclear@27: // no animation yet, just get the static PRS nuclear@27: GMatrix maxmatrix = maxnode->GetObjectTM(); nuclear@27: Point3 trans = maxmatrix.Translation(); nuclear@27: Quat rot = maxmatrix.Rotation(); nuclear@27: Point3 scale = maxmatrix.Scaling(); nuclear@27: nuclear@27: goat3d_set_node_position(node, trans.x, trans.y, trans.z, 0); nuclear@27: goat3d_set_node_rotation(node, rot.x, rot.y, rot.z, rot.w, 0); nuclear@27: goat3d_set_node_scaling(node, scale.x, scale.y, scale.z, 0); nuclear@27: nuclear@27: IGameObject *maxobj = maxnode->GetIGameObject(); nuclear@27: IGameObject::ObjectTypes type = maxobj->GetIGameType(); nuclear@27: nuclear@27: switch(type) { nuclear@27: case IGameObject::IGAME_MESH: nuclear@27: { nuclear@27: goat3d_mesh *mesh = goat3d_create_mesh(); nuclear@27: if(name) goat3d_set_mesh_name(mesh, name); nuclear@27: goat3d_set_node_object(node, GOAT3D_NODE_MESH, mesh); nuclear@27: nuclear@27: // get the node material and assign it to the mesh nuclear@27: IGameMaterial *maxmtl = maxnode->GetNodeMaterial(); nuclear@28: goat3d_material *mtl = mtlmap[maxmtl]; nuclear@27: if(mtl) { nuclear@27: goat3d_set_mesh_mtl(mesh, mtl); nuclear@27: } nuclear@27: nuclear@27: process_mesh(goat, mesh, maxobj); nuclear@30: goat3d_add_mesh(goat, mesh); nuclear@27: } nuclear@27: break; nuclear@27: nuclear@27: case IGameObject::IGAME_LIGHT: nuclear@27: { nuclear@27: goat3d_light *light = goat3d_create_light(); nuclear@27: //if(name) goat3d_set_light_name(light, name); nuclear@27: goat3d_set_node_object(node, GOAT3D_NODE_LIGHT, light); nuclear@27: nuclear@27: process_light(goat, light, maxobj); nuclear@30: goat3d_add_light(goat, light); nuclear@27: } nuclear@27: break; nuclear@27: nuclear@27: case IGameObject::IGAME_CAMERA: nuclear@27: { nuclear@27: goat3d_camera *cam = goat3d_create_camera(); nuclear@27: //if(name) goat3d_set_camera_name(camera, name); nuclear@27: goat3d_set_node_object(node, GOAT3D_NODE_CAMERA, cam); nuclear@27: nuclear@27: process_camera(goat, cam, maxobj); nuclear@30: goat3d_add_camera(goat, cam); nuclear@27: } nuclear@27: break; nuclear@27: nuclear@27: default: nuclear@27: // otherwise don't assign an object, essentially treating it as a null node nuclear@27: break; nuclear@27: } nuclear@27: nuclear@27: nuclear@27: for(int i=0; iGetChildCount(); i++) { nuclear@27: process_node(goat, node, maxnode->GetNodeChild(i)); nuclear@25: } nuclear@5: } nuclear@5: nuclear@27: void GoatExporter::process_mesh(goat3d *goat, goat3d_mesh *mesh, IGameObject *maxobj) nuclear@27: { nuclear@27: IGameMesh *maxmesh = (IGameMesh*)maxobj; nuclear@27: nuclear@27: maxmesh->SetCreateOptimizedNormalList(); // not needed any more according to docs nuclear@27: maxobj->InitializeData(); nuclear@27: nuclear@27: int num_verts = maxmesh->GetNumberOfVerts(); nuclear@30: int num_faces = maxmesh->GetNumberOfFaces(); nuclear@28: //assert(maxmesh->GetNumberOfTexVerts() == num_verts); nuclear@27: nuclear@27: float *vertices = new float[num_verts * 3]; nuclear@27: float *normals = new float[num_verts * 3]; nuclear@28: //float *texcoords = new float[num_verts * 2]; nuclear@30: int *indices = new int[num_faces * 3]; nuclear@27: nuclear@27: for(int i=0; iGetVertex(i, true); nuclear@27: vertices[i * 3] = v.x; nuclear@27: vertices[i * 3 + 1] = v.y; nuclear@27: vertices[i * 3 + 2] = v.z; nuclear@27: } nuclear@27: nuclear@27: for(int i=0; iGetNumberOfNormals(); i++) { nuclear@27: Point3 norm = maxmesh->GetNormal(i); nuclear@27: nuclear@27: int vidx = maxmesh->GetNormalVertexIndex(i); nuclear@27: normals[vidx * 3] = norm.x; nuclear@27: normals[vidx * 3 + 1] = norm.y; nuclear@27: normals[vidx * 3 + 2] = norm.z; nuclear@27: } nuclear@27: nuclear@28: /*for(int i=0; iGetNumberOfTexVerts(); i++) { nuclear@27: Point3 tex = maxmesh->GetTexVertex(i); nuclear@27: nuclear@27: texcoords[i * 2] = tex.x; nuclear@27: texcoords[i * 2 + 1] = tex.y; nuclear@28: }*/ nuclear@27: nuclear@30: // get the faces nuclear@30: for(int i=0; iGetFace(i); nuclear@30: indices[i * 3] = face->vert[0]; nuclear@30: indices[i * 3 + 1] = face->vert[1]; nuclear@30: indices[i * 3 + 2] = face->vert[2]; nuclear@30: // TODO at some point I'll have to split based on normal/texcoord indices nuclear@30: } nuclear@30: nuclear@27: goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_VERTEX, vertices, num_verts); nuclear@27: goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_NORMAL, normals, num_verts); nuclear@28: //goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_TEXCOORD, texcoords, num_verts); nuclear@30: goat3d_set_mesh_faces(mesh, indices, num_faces); nuclear@27: nuclear@27: delete [] vertices; nuclear@27: delete [] normals; nuclear@28: //delete [] texcoords; nuclear@30: delete [] indices; nuclear@27: } nuclear@27: nuclear@27: void GoatExporter::process_light(goat3d *goat, goat3d_light *light, IGameObject *maxobj) nuclear@27: { nuclear@27: } nuclear@27: nuclear@27: void GoatExporter::process_camera(goat3d *goat, goat3d_camera *cam, IGameObject *maxobj) nuclear@27: { nuclear@27: } nuclear@27: nuclear@27: nuclear@5: // ------------------------------------------ nuclear@5: nuclear@5: class GoatClassDesc : public ClassDesc2 { nuclear@5: public: nuclear@5: int IsPublic() { return TRUE; } nuclear@5: void *Create(BOOL loading = FALSE) { return new GoatExporter; } nuclear@5: const TCHAR *ClassName() { return L"GoatExporter"; } nuclear@5: SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } nuclear@5: Class_ID ClassID() { return Class_ID(0x77050f0d, 0x7d4c5ab5); } nuclear@5: const TCHAR *Category() { return L"Mutant Stargoat"; } nuclear@5: nuclear@5: const TCHAR *InternalName() { return L"GoatExporter"; } nuclear@5: HINSTANCE HInstance() { return hinst; } nuclear@5: }; nuclear@5: nuclear@58: // TODO: make 2 class descriptors, one for goat3d, one for goat3danim nuclear@5: static GoatClassDesc class_desc; nuclear@5: nuclear@5: BOOL WINAPI DllMain(HINSTANCE inst_handle, ULONG reason, void *reserved) nuclear@5: { nuclear@5: if(reason == DLL_PROCESS_ATTACH) { nuclear@5: hinst = inst_handle; nuclear@5: DisableThreadLibraryCalls(hinst); nuclear@5: } nuclear@5: return TRUE; nuclear@5: } nuclear@5: nuclear@5: extern "C" { nuclear@5: nuclear@5: __declspec(dllexport) const TCHAR *LibDescription() nuclear@5: { nuclear@5: return L"test exporter"; nuclear@5: } nuclear@5: nuclear@5: __declspec(dllexport) int LibNumberClasses() nuclear@5: { nuclear@5: return 1; nuclear@5: } nuclear@5: nuclear@5: __declspec(dllexport) ClassDesc *LibClassDesc(int i) nuclear@5: { nuclear@5: return i == 0 ? &class_desc : 0; nuclear@5: } nuclear@5: nuclear@5: __declspec(dllexport) ULONG LibVersion() nuclear@5: { nuclear@5: return Get3DSMAXVersion(); nuclear@5: } nuclear@5: nuclear@5: __declspec(dllexport) int LibInitialize() nuclear@5: { nuclear@5: static char path[1024]; nuclear@5: nuclear@5: SHGetFolderPathA(0, CSIDL_PERSONAL, 0, 0, path); nuclear@5: strcat(path, "/testexp.log"); nuclear@5: nuclear@5: if((logfile = fopen(path, "w"))) { nuclear@5: setvbuf(logfile, 0, _IONBF, 0); nuclear@5: } nuclear@5: return TRUE; nuclear@5: } nuclear@5: nuclear@5: __declspec(dllexport) int LibShutdown() nuclear@5: { nuclear@5: if(logfile) { nuclear@5: fclose(logfile); nuclear@5: logfile = 0; nuclear@5: } nuclear@5: return TRUE; nuclear@5: } nuclear@5: nuclear@5: } // extern "C"