diff --git a/GiantsTools.sln b/GiantsTools.sln index 3cfe19e..41cbdd1 100644 --- a/GiantsTools.sln +++ b/GiantsTools.sln @@ -111,8 +111,8 @@ Global {49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Release|Any CPU.Build.0 = Release|Any CPU {49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Release|x86.ActiveCfg = Release|Any CPU {49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Release|x86.Build.0 = Release|Any CPU - {448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|Any CPU.ActiveCfg = Debug|x64 - {448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|Any CPU.Build.0 = Debug|x64 + {448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|Any CPU.ActiveCfg = Hybrid|x64 + {448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|Any CPU.Build.0 = Hybrid|x64 {448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|x86.ActiveCfg = Debug|x64 {448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|x86.Build.0 = Debug|x64 {448F061E-AE05-4E06-84A1-C95660FD048C}.Release|Any CPU.ActiveCfg = Release|x64 diff --git a/Plugins/exp_gtp/exp_gtp.dle b/Plugins/exp_gtp/exp_gtp.dle new file mode 100644 index 0000000..237419a Binary files /dev/null and b/Plugins/exp_gtp/exp_gtp.dle differ diff --git a/Plugins/gtp2gb2/gtp2gb2.exe b/Plugins/gtp2gb2/gtp2gb2.exe new file mode 100644 index 0000000..ed598c3 Binary files /dev/null and b/Plugins/gtp2gb2/gtp2gb2.exe differ diff --git a/Plugins/imp_gbs/Gb2Data.cpp b/Plugins/imp_gbs/Gb2Data.cpp new file mode 100644 index 0000000..9800f1c --- /dev/null +++ b/Plugins/imp_gbs/Gb2Data.cpp @@ -0,0 +1,120 @@ +#include "Importer.h" + +#define MemReadInt(m,v) { (v) = *(int*)(m); (m)+=sizeof(int); } +#define MemReadFloat(m,v) { (v) = *(float*)(m); (m)+=sizeof(float); } +#define MemReadData(m,v,l) { memcpy( (v), (m), (l) ); (m)+=(l); } +#define MemSkipData(m,l) { (m)+=(l); } + +void Gb2Data::Read(FILE* file) +{ + char name[16], texname[16]; + int numobj, offset, flags, matflags; + float falloff, blend; + + fseek(file, 0, SEEK_END); + int zlen = ftell(file); + fseek(file, 0, SEEK_SET); + + auto zmemArray = std::vector(zlen); + UBYTE* zmem = zmemArray.data(); + fread(zmem, zlen, 1, file); + + UBYTE* memptr = zmem; + fclose(file); + + DWORD versionHeader; + MemReadInt(memptr, versionHeader); + if (versionHeader != GB2_VERSION) + { + throw std::exception("File does not appear to be a GB2 file."); + } + + // Read # of objects + MemReadInt(memptr, numobj); + + this->objdata.resize(numobj); + + // Process each object + UBYTE* tablemem = memptr; + for (int ns = 0; ns < numobj; ns++) + { + SingleObjectData& obj = this->objdata.at(ns); + + memptr = tablemem + ns * sizeof(int); + + MemReadInt(memptr, offset); + memptr = zmem + offset; + MemReadData(memptr, name, 16); + + obj.name = name; + + MemReadInt(memptr, flags); + MemReadFloat(memptr, falloff); + if (FlagIsSet(flags, GBXFlagRGBs)) + { + MemReadFloat(memptr, blend); + } + else + blend = 0; + + MemReadInt(memptr, matflags); + + if (FlagIsSet(flags, GBXFlagUVs)) + { + MemReadData(memptr, texname, sizeof(texname)); + obj.texname = texname; + } + + MemReadInt(memptr, obj.nverts); + MemReadInt(memptr, obj.ntris); + + // Allocate vertex & tri lists + obj.verts.resize(obj.nverts); + if (FlagIsSet(flags, GBXFlagUVs)) + { + obj.vertuv.resize(obj.nverts); + } + + if (FlagIsSet(flags, GBXFlagRGBs)) + { + obj.vertrgb.resize(obj.nverts); + } + + // Read vertices + for (int i = 0; i < obj.nverts; i++) + { + MemReadData(memptr, &obj.verts.at(i), sizeof(P3D)); + } + + if (FlagIsSet(flags, GBXFlagNormals)) + { + MemSkipData(memptr, sizeof(P3D) * obj.nverts); // Normals are auto-generated by the game + } + + if (FlagIsSet(flags, GBXFlagUVs)) + { + // read uvs + for (int i = 0; i < obj.nverts; i++) + { + MemReadData(memptr, &obj.vertuv.at(i), sizeof(UV)); + obj.vertuv.at(i).v = obj.vertuv.at(i).v + 1.0f; + } + } + + if (FlagIsSet(flags, GBXFlagRGBs)) + { + for (int i = 0; i < obj.nverts; i++) + { + // read rgbs + MemReadData(memptr, &obj.vertrgb.at(i), sizeof(VertRGB)); + } + } + + obj.tris.resize(obj.ntris * 3); + // Read tris + for (int i = 0; i < obj.ntris * 3; i++) + { + MemReadData(memptr, &obj.tris.at(i), sizeof(int)); + } + } +} \ No newline at end of file diff --git a/Plugins/imp_gbs/Gb2Data.h b/Plugins/imp_gbs/Gb2Data.h new file mode 100644 index 0000000..299771d --- /dev/null +++ b/Plugins/imp_gbs/Gb2Data.h @@ -0,0 +1,22 @@ +#pragma once + +#define GB2_VERSION 0xaa0100ab + +struct SingleObjectData +{ + std::vector verts; + std::vector tris; + int ntris{}; + std::vector vertrgb; + int nverts{}; + std::vector vertuv; + std::string name; + std::string texname; +}; + +struct Gb2Data +{ + void Read(FILE* file); + + std::vector objdata; +}; diff --git a/Plugins/imp_gbs/Gb2Importer.cpp b/Plugins/imp_gbs/Gb2Importer.cpp new file mode 100644 index 0000000..12281dc --- /dev/null +++ b/Plugins/imp_gbs/Gb2Importer.cpp @@ -0,0 +1,126 @@ +#include "Gb2Importer.h" +#include "StdUtil.h" + +void Gb2Importer::ImportFile(const MCHAR* name, ImpInterface* ii, Interface* i, BOOL suppressPrompts) +{ + ReadGb2File(name); + + BuildMeshes(ii); + ii->RedrawViews(); +} + + +void Gb2Importer::ReadGb2File(const MCHAR* Name) +{ + FILE* file = nullptr; + try + { + errno_t err = _tfopen_s(&file, Name, (_T("rb"))); + if (err != 0) + { + throw std::exception("Could not open input file."); + } + + // Read object into memory + m_gb2Data.Read(file); + + fclose(file); + } + catch (const std::exception& e) + { + if (file) + fclose(file); + + throw e; + } +} + +void Gb2Importer::BuildMeshes(ImpInterface* ii) +{ + for (auto& obj : this->m_gb2Data.objdata) + { + assert(obj.ntris > 0); + assert(obj.nverts > 0); + + Mesh mesh; + mesh.setNumVerts(obj.nverts); + mesh.setNumTVerts(obj.nverts); + mesh.setNumVertCol(obj.nverts); + mesh.setNumFaces(obj.ntris); + mesh.setNumTVFaces(obj.ntris); + mesh.setNumVCFaces(obj.ntris); + + for (int i = 0; i < obj.nverts; i++) + { + Point3 point(obj.verts[i].x, obj.verts[i].y, obj.verts[i].z); + mesh.setVert(i, point); + + const UV& uv = obj.vertuv.at(i); + mesh.setTVert(i, UVVert(uv.u, uv.v, 0.0f)); + } + + int faceIndex = 0; + for (int i = 0; i < obj.tris.size(); i += 3) + { + int v0 = obj.tris.at(i); + int v1 = obj.tris.at(i + 1); + int v2 = obj.tris.at(i + 2); + + // Create faces + Face& face = mesh.faces[faceIndex]; + face.setVerts(v0, v1, v2); + face.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS); + + // Set per-vertex coloring (appears to be unused by game) + TVFace& vcFace = mesh.vcFace[faceIndex]; + vcFace.setTVerts(v0, v1, v2); + + // Set textured (UV) vertices + TVFace& tvFace = mesh.tvFace[faceIndex]; + tvFace.setTVerts(v0, v1, v2); + + faceIndex++; + } + + mesh.buildNormals(); + mesh.buildBoundingBox(); + mesh.InvalidateEdgeList(); + + TriObject* tri = CreateNewTriObject(); + tri->mesh = mesh; + Matrix3 tm; + tm.IdentityMatrix(); + + ImpNode* Node = ii->CreateNode(); + if (!Node) + { + throw std::exception("Could not create Node"); + } + + Mtl* material = BuildMaterial(obj); + + Node->Reference(tri); + Node->SetTransform(0, tm); + ii->AddNodeToScene(Node); + INode* iNode = Node->GetINode(); + iNode->SetMtl(material); + Node->SetName(util::to_wstring(obj.name).c_str()); + } +} + +Mtl* Gb2Importer::BuildMaterial(SingleObjectData& obj) +{ + auto bitmapTex = NewDefaultBitmapTex(); + bitmapTex->SetName(util::to_wstring(obj.texname).c_str()); + bitmapTex->SetMapName(util::to_wstring(obj.texname).c_str()); + + StdMat2* stdMat = NewDefaultStdMat(); + stdMat->EnableMap(ID_DI, true); + if (bitmapTex) + { + stdMat->SetSubTexmap(ID_DI, bitmapTex); + } + + stdMat->SetName(util::to_wstring(obj.texname).c_str()); + return stdMat; +} \ No newline at end of file diff --git a/Plugins/imp_gbs/Gb2Importer.h b/Plugins/imp_gbs/Gb2Importer.h new file mode 100644 index 0000000..1c9da7c --- /dev/null +++ b/Plugins/imp_gbs/Gb2Importer.h @@ -0,0 +1,19 @@ +#pragma once + +#include "IGiantsImporter.h" +#include "Gb2Data.h" + +class Gb2Importer : IGiantsImporter +{ +public: + // Inherited via IGiantsImporter + virtual void ImportFile(const MCHAR* name, ImpInterface* ii, Interface* i, BOOL suppressPrompts) override; + +private: + void ReadGb2File(const MCHAR* Name); + void BuildMeshes(ImpInterface* ii); + Mtl* BuildMaterial(SingleObjectData& obj); + + FILE* m_OpenFile{}; + Gb2Data m_gb2Data; +}; diff --git a/Plugins/imp_gbs/GbsImporter.cpp b/Plugins/imp_gbs/GbsImporter.cpp new file mode 100644 index 0000000..daa5a2d --- /dev/null +++ b/Plugins/imp_gbs/GbsImporter.cpp @@ -0,0 +1,304 @@ +#include "GbsImporter.h" +#include "StdUtil.h" + +void GbsImporter::ImportFile(const MCHAR* name, ImpInterface* ii, Interface* i, BOOL suppressPrompts) +{ + ReadGbsFile(name); + + BuildMeshes(ii); + ii->RedrawViews(); +} + +void GbsImporter::ReadGbsFile(const MCHAR* Name) +{ + FILE* file = nullptr; + try + { + errno_t err = _tfopen_s(&file, Name, (_T("rb"))); + if (err != 0) + { + throw std::exception("Could not open input file."); + } + + // Read object into memory + m_gbsData.Read(file); + + fclose(file); + } + catch (const std::exception& e) + { + if (file) + fclose(file); + + throw e; + } +} + +int GbsImporter::GetLocalVertex(Point3* avert, const Mesh& mesh) +{ + for (int i = 0; i < mesh.getNumVerts(); i++) + { + auto& vert = mesh.verts[i]; + + if (vert.x == avert->x && vert.y == avert->y && vert.z == avert->z) + { + return i; + } + } + + assert(false && "Invalid vertex for object"); + return -1; +} + +void GbsImporter::BuildMeshes(ImpInterface* EI) +{ + for (const auto& mobj : m_gbsData.MaxObjs) + { + assert(mobj.fcount > 0); + assert(mobj.vcount > 0); + + Mesh mesh; + mesh.setNumVerts(mobj.vcount); + mesh.setNumTVerts(mobj.vcount); + mesh.setNumVertCol(mobj.vcount); + mesh.setNumFaces(mobj.fcount); + mesh.setNumTVFaces(mobj.fcount); + mesh.setNumVCFaces(mobj.fcount); + + for (int i = 0; i < mobj.vcount; i++) + { + int vstart = mobj.vstart; + int index = i + vstart; + assert(index < m_gbsData.naverts); + + UV* uvptr = (UV*)(m_gbsData.vertuv.data() + (mobj.vstart + i)); + + Point3 point(m_gbsData.averts[index].x, m_gbsData.averts[index].y, m_gbsData.averts[index].z); + + mesh.setVert(i, point); + mesh.setTVert(i, UVVert(uvptr->u, uvptr->v, 0.0f)); + //mesh.setTVert(i, UVVert(uvptr->u, -uvptr->v, 0.0f)); + } + + Mtl* topLevelMaterial = nullptr; + const char* objName = nullptr; + int materialLevel = 0; + + int faceIndex = 0; + for (int subObjIndex = mobj.sostart; subObjIndex < mobj.sostart + mobj.socount; subObjIndex++) + { + SubObject& obj = m_gbsData.SubObjs.at(subObjIndex); + if (mobj.socount > 1) + { + // Max object consists of multiple sub objects + if (materialLevel == 0) + { + // Build parent material (either MixMat or multi-mtl): + topLevelMaterial = BuildParentMaterial(obj, mobj.socount); + objName = obj.objname; + } + + // Add sub-material: + Mtl* subMtl = BuildMaterial(obj, topLevelMaterial); + topLevelMaterial->SetSubMtl(materialLevel, subMtl); + } + else + { + // Just one subobj, build the single material and be done with it: + topLevelMaterial = BuildMaterial(obj, nullptr); + objName = obj.objname; + } + + int v1, v2, v3; + unsigned short tidx = -1; + unsigned short* tptr = obj.tridata.data(); + + unsigned short count = 0; + while (EvaluateTriData(&tptr, &tidx, &count, &v1, &v2, &v3)) + { + assert(faceIndex < mesh.getNumFaces()); + int vstart = mobj.vstart; + Face& face = mesh.faces[faceIndex]; + + // Map from display to animation vertices, then to a "local" index for this sub object, starting from zero: + int remappedV0 = GetLocalVertex(&m_gbsData.averts[m_gbsData.iverts.at(v1)], mesh); + int remappedV1 = GetLocalVertex(&m_gbsData.averts[m_gbsData.iverts.at(v2)], mesh); + int remappedV2 = GetLocalVertex(&m_gbsData.averts[m_gbsData.iverts.at(v3)], mesh); + + VertColor v0Col = VertColor(m_gbsData.vertrgb.at(v1).r / 255.0f, m_gbsData.vertrgb.at(v1).g / 255.0f, m_gbsData.vertrgb.at(v1).b / 255.0f); + VertColor v1Col = VertColor(m_gbsData.vertrgb.at(v2).r / 255.0f, m_gbsData.vertrgb.at(v2).g / 255.0f, m_gbsData.vertrgb.at(v2).b / 255.0f); + VertColor v2Col = VertColor(m_gbsData.vertrgb.at(v3).r / 255.0f, m_gbsData.vertrgb.at(v3).g / 255.0f, m_gbsData.vertrgb.at(v3).b / 255.0f); + + mesh.vertCol[remappedV0] = v0Col; + mesh.vertCol[remappedV1] = v1Col; + mesh.vertCol[remappedV2] = v2Col; + + assert(remappedV0 >= 0 && remappedV1 >= 0 && remappedV2 >= 0); + assert(remappedV0 < mobj.vcount&& remappedV1 < mobj.vcount&& remappedV2 < mobj.vcount); + assert(remappedV0 < m_gbsData.naverts&& remappedV1 < m_gbsData.naverts&& remappedV2 < m_gbsData.naverts); + + face.setVerts(remappedV0, remappedV1, remappedV2); + face.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS); + face.setMatID(materialLevel); + + TVFace& tvFace = mesh.tvFace[faceIndex]; + tvFace.setTVerts(remappedV0, remappedV1, remappedV2); + + TVFace& vcFace = mesh.vcFace[faceIndex]; + vcFace.setTVerts(remappedV0, remappedV1, remappedV2); + + faceIndex++; + } + + materialLevel++; + } + + mesh.buildNormals(); + mesh.buildBoundingBox(); + mesh.InvalidateEdgeList(); + + TriObject* tri = CreateNewTriObject(); + tri->mesh = mesh; + Matrix3 tm; + tm.IdentityMatrix(); + + ImpNode* Node = EI->CreateNode(); + if (!Node) + { + throw std::exception("Could not create Node"); + } + + Node->Reference(tri); + Node->SetTransform(0, tm); + EI->AddNodeToScene(Node); + INode* iNode = Node->GetINode(); + iNode->SetMtl(topLevelMaterial); + Node->SetName(util::to_wstring(objName).c_str()); + } +} + +Mtl* GbsImporter::BuildParentMaterial(SubObject& obj, int numSubMaterials) +{ + // Check if we need to create a MixMat blend material + // (this check is iffy, not fully certain how to identify MixMat exports but this seems close enough) + if (obj.blend > 0.50 && (obj.flags & 0x10 || obj.flags & 0x20)) + { + //if (bitmapTex) + //{ + // //bitmapTex->GetUVGen()->SetUVWSource(2); + //} + + // Create custom MixMat material (from official 3dsmax plugin) + Mtl* mixMatMaterial = (Mtl*)CreateInstance(SClass_ID(MATERIAL_CLASS_ID), Class_ID(MIXMAT_CLASS_ID, 0)); + mixMatMaterial->SetName(util::to_wstring(obj.objname).c_str()); + + int ns = mixMatMaterial->NumSubMtls(); + + // Set the blend value + IParamBlock2* parameterBlock = mixMatMaterial->GetParamBlock(0); + parameterBlock->SetValue(0, 0, obj.blend); + + return mixMatMaterial; + } + else + { + // Standard multi-material + MultiMtl* multiMtl = NewDefaultMultiMtl(); + multiMtl->SetName(util::to_wstring(obj.objname).c_str()); + multiMtl->SetNumSubMtls(numSubMaterials); + + return multiMtl; + } +} + +Mtl* GbsImporter::BuildMaterial(SubObject& obj, Mtl* parentMaterial) +{ + BitmapTex* bitmapTex = nullptr; + + if (obj.texname && obj.texname[0]) + { + std::wstring mapName = util::to_wstring(obj.texname).c_str(); + mapName += L".tga"; // DDS partially supported but not in use, okay to hardcode for now + + bitmapTex = NewDefaultBitmapTex(); + bitmapTex->SetName(util::to_wstring(obj.texname).c_str()); + bitmapTex->SetMapName(mapName.c_str()); + } + + StdMat2* stdMat = NewDefaultStdMat(); + if (FlagIsSet(obj.flags, GBSFlagMaxLit) || parentMaterial) + { + // This isn't quite right, not all models with blend materials have this flag set. See ripper.gbs. + // Behavior is correct enough for now though. + stdMat->SetWireUnits(TRUE); + } + + stdMat->EnableMap(ID_DI, true); + + if (obj.flags & 0x10) + { + stdMat->SetTransparencyType(TRANSP_ADDITIVE); + } + else if (obj.flags & 0x20) + { + stdMat->SetTransparencyType(TRANSP_SUBTRACTIVE); + } + stdMat->SetShinStr(1.0f, 0); + stdMat->SetOpacFalloff(obj.falloff, 0); + + if (obj.emissive != 0) + { + Color emissive(GetRValue(obj.emissive), GetGValue(obj.emissive), GetBValue(obj.emissive)); + stdMat->SetSelfIllumColor(emissive, 0); + } + + if (bitmapTex) + { + stdMat->SetSubTexmap(ID_DI, bitmapTex); + } + + Color diffuse(GetRValue(obj.diffuse), GetGValue(obj.diffuse), GetBValue(obj.diffuse)); + stdMat->SetDiffuse(diffuse, 0); + Color ambient(GetRValue(obj.ambient), GetGValue(obj.ambient), GetBValue(obj.ambient)); + stdMat->SetAmbient(ambient, 0); + Color specular(GetRValue(obj.specular), GetGValue(obj.specular), GetBValue(obj.specular)); + stdMat->SetSpecular(specular, 0); + stdMat->SetShininess(obj.power / 100.0f, 0); + + stdMat->SetName(util::to_wstring(obj.objname).c_str()); + + return stdMat; +} + +bool GbsImporter::EvaluateTriData(unsigned short** pTriData, unsigned short* pTriIdx, unsigned short* acount, int* pV1, int* pV2, int* pV3) +{ + unsigned short* triData = *pTriData; + unsigned short triIdx = *pTriIdx; + unsigned short count = *acount; + + if (!count) + { + if (!(count = *triData)) + { + return false; + } + + triIdx = 0; + } + + *pV1 = triData[triIdx + 1]; + *pV2 = triData[triIdx + 2]; + *pV3 = triData[triIdx + 3]; + triIdx += 3; + + if (!--count) + { + triData += *triData * 3 + 1; + } + + *pTriData = triData; + *pTriIdx = triIdx; + *acount = count; + + return true; +} \ No newline at end of file diff --git a/Plugins/imp_gbs/GbsImporter.h b/Plugins/imp_gbs/GbsImporter.h new file mode 100644 index 0000000..a962ffd --- /dev/null +++ b/Plugins/imp_gbs/GbsImporter.h @@ -0,0 +1,22 @@ +#pragma once + +#include "IGiantsImporter.h" +#include "GbsData.h" + +class GbsImporter : IGiantsImporter +{ +public: + // Inherited via IGiantsImporter + virtual void ImportFile(const MCHAR* name, ImpInterface* ii, Interface* i, BOOL suppressPrompts) override; + +private: + void ReadGbsFile(const MCHAR* Name); + Mtl* BuildParentMaterial(SubObject& obj, int numSubMaterials); + Mtl* BuildMaterial(SubObject& obj, Mtl* parentMaterial); + int GetLocalVertex(Point3* avert, const Mesh& mesh); + void BuildMeshes(ImpInterface* EI); + bool EvaluateTriData(unsigned short** pTriData, unsigned short* pTriIdx, unsigned short* acount, int* pV1, int* pV2, int* pV3); + + FILE* m_OpenFile{}; + GbsData m_gbsData; +}; \ No newline at end of file diff --git a/Plugins/imp_gbs/IGiantsImporter.h b/Plugins/imp_gbs/IGiantsImporter.h new file mode 100644 index 0000000..88252d5 --- /dev/null +++ b/Plugins/imp_gbs/IGiantsImporter.h @@ -0,0 +1,6 @@ +#pragma once + +struct IGiantsImporter +{ + virtual void ImportFile(const MCHAR* name, ImpInterface* ii, Interface* i, BOOL suppressPrompts) = 0; +}; diff --git a/Plugins/imp_gbs/Importer.cpp b/Plugins/imp_gbs/Importer.cpp index 98fb7ea..0cbe156 100644 --- a/Plugins/imp_gbs/Importer.cpp +++ b/Plugins/imp_gbs/Importer.cpp @@ -2,14 +2,14 @@ // #include "Importer.h" -#include "StdUtil.h" +#include "ImporterFactory.h" class ImpGbsClassDesc : public ClassDesc2 { public: int IsPublic() { return TRUE; } - VOID* Create(BOOL Loading) { return new GbsImporter; } + VOID* Create(BOOL Loading) { return new GiantsImporter; } const MCHAR* ClassName() { return _M("ClassName"); } SClass_ID SuperClassID() { return SCENE_IMPORT_CLASS_ID; } Class_ID ClassID() { return GIANTSIMP_CLASSID; } @@ -30,53 +30,55 @@ void DisplayMessage(const char* msg) MessageBoxA(GetActiveWindow(), msg, "GBS Import Error", MB_OK); } -int GbsImporter::ExtCount() +int GiantsImporter::ExtCount() { - return 1; + return 2; } -const MCHAR* GbsImporter::Ext(int n) +const MCHAR* GiantsImporter::Ext(int n) { switch (n) { case 0: return _M("gbs"); + case 1: + return _M("gb2"); default: return (_M("")); } } -const MCHAR* GbsImporter::LongDesc() +const MCHAR* GiantsImporter::LongDesc() { return _M("Long Description"); } -const MCHAR* GbsImporter::ShortDesc() +const MCHAR* GiantsImporter::ShortDesc() { return _M("Giants Model"); } -const MCHAR* GbsImporter::AuthorName() +const MCHAR* GiantsImporter::AuthorName() { return _M("Author"); } -const MCHAR* GbsImporter::CopyrightMessage() +const MCHAR* GiantsImporter::CopyrightMessage() { return _M("Copyright"); } -const MCHAR* GbsImporter::OtherMessage1() +const MCHAR* GiantsImporter::OtherMessage1() { return _M("OtherMessage1"); } -const MCHAR* GbsImporter::OtherMessage2() +const MCHAR* GiantsImporter::OtherMessage2() { return _M("OtherMessage2"); } -UINT GbsImporter::Version() +UINT GiantsImporter::Version() { return 100; } @@ -86,324 +88,25 @@ static BOOL CALLBACK AboutDlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lPa return FALSE; } -VOID GbsImporter::ShowAbout(HWND hWnd) +VOID GiantsImporter::ShowAbout(HWND hWnd) { } -bool GbsImporter::EvaluateTriData(unsigned short** pTriData, unsigned short* pTriIdx, unsigned short* acount, int* pV1, int* pV2, int* pV3) -{ - unsigned short* triData = *pTriData; - unsigned short triIdx = *pTriIdx; - unsigned short count = *acount; - - if (!count) - { - if (!(count = *triData)) - { - return false; - } - - triIdx = 0; - } - - *pV1 = triData[triIdx + 1]; - *pV2 = triData[triIdx + 2]; - *pV3 = triData[triIdx + 3]; - triIdx += 3; - - if (!--count) - { - triData += *triData * 3 + 1; - } - - *pTriData = triData; - *pTriIdx = triIdx; - *acount = count; - - return true; -} - -int GbsImporter::DoImport(const MCHAR* Name, ImpInterface* EI, Interface* I, BOOL SupressPrompts) +int GiantsImporter::DoImport(const MCHAR* Name, ImpInterface* EI, Interface* I, BOOL suppressPrompts) { try { - m_gbsData = ReadGbsFile(Name); + ImporterFactory::ImportFile(Name, EI, I, suppressPrompts); } catch (const std::exception& e) { DisplayMessage(e.what()); } - BuildMeshes(EI); - EI->RedrawViews(); - return TRUE; } -GbsData GbsImporter::ReadGbsFile(const MCHAR* Name) -{ - FILE* file = nullptr; - try - { - errno_t err = _tfopen_s(&file, Name, (_T("rb"))); - if (err != 0) - { - throw std::exception("Could not open input file."); - } - - // Read object into memory - GbsData gbsFile; - gbsFile.Read(file); - - fclose(file); - return gbsFile; - } - catch (const std::exception& e) - { - if (file) - fclose(file); - - throw e; - } -} - -int GbsImporter::GetLocalVertex(Point3* avert, const Mesh& mesh) -{ - for (int i = 0; i < mesh.getNumVerts(); i++) - { - auto& vert = mesh.verts[i]; - - if (vert.x == avert->x && vert.y == avert->y && vert.z == avert->z) - { - return i; - } - } - - assert(false && "Invalid vertex for object"); - return -1; -} - -void GbsImporter::BuildMeshes(ImpInterface* EI) -{ - for (const auto& mobj : m_gbsData.MaxObjs) - { - assert(mobj.fcount > 0); - assert(mobj.vcount > 0); - - Mesh mesh; - mesh.setNumVerts(mobj.vcount); - mesh.setNumTVerts(mobj.vcount); - mesh.setNumVertCol(mobj.vcount); - mesh.setNumFaces(mobj.fcount); - mesh.setNumTVFaces(mobj.fcount); - mesh.setNumVCFaces(mobj.fcount); - - for (int i = 0; i < mobj.vcount; i++) - { - int vstart = mobj.vstart; - int index = i + vstart; - assert(index < m_gbsData.naverts); - - UV* uvptr = (UV*)(m_gbsData.vertuv.data() + (mobj.vstart + i)); - - Point3 point(m_gbsData.averts[index].x, m_gbsData.averts[index].y, m_gbsData.averts[index].z); - - mesh.setVert(i, point); - mesh.setTVert(i, UVVert(uvptr->u, uvptr->v, 0.0f)); - //mesh.setTVert(i, UVVert(uvptr->u, -uvptr->v, 0.0f)); - } - - Mtl* topLevelMaterial = nullptr; - const char* objName = nullptr; - int materialLevel = 0; - - int faceIndex = 0; - for (int subObjIndex = mobj.sostart; subObjIndex < mobj.sostart + mobj.socount; subObjIndex++) - { - SubObject& obj = m_gbsData.SubObjs.at(subObjIndex); - if (mobj.socount > 1) - { - // Max object consists of multiple sub objects - if (materialLevel == 0) - { - // Build parent material (either MixMat or multi-mtl): - topLevelMaterial = BuildParentMaterial(obj, mobj.socount); - objName = obj.objname; - } - - // Add sub-material: - Mtl* subMtl = BuildMaterial(obj, topLevelMaterial); - topLevelMaterial->SetSubMtl(materialLevel, subMtl); - } - else - { - // Just one subobj, build the single material and be done with it: - topLevelMaterial = BuildMaterial(obj, nullptr); - objName = obj.objname; - } - - int v1, v2, v3; - unsigned short tidx = -1; - unsigned short* tptr = obj.tridata.data(); - - unsigned short count = 0; - while (EvaluateTriData(&tptr, &tidx, &count, &v1, &v2, &v3)) - { - assert(faceIndex < mesh.getNumFaces()); - int vstart = mobj.vstart; - Face& face = mesh.faces[faceIndex]; - - // Map from display to animation vertices, then to a "local" index for this sub object, starting from zero: - int remappedV0 = GetLocalVertex(&m_gbsData.averts[m_gbsData.iverts.at(v1)], mesh); - int remappedV1 = GetLocalVertex(&m_gbsData.averts[m_gbsData.iverts.at(v2)], mesh); - int remappedV2 = GetLocalVertex(&m_gbsData.averts[m_gbsData.iverts.at(v3)], mesh); - - VertColor v0Col = VertColor(m_gbsData.vertrgb.at(v1).r / 255.0f, m_gbsData.vertrgb.at(v1).g / 255.0f, m_gbsData.vertrgb.at(v1).b / 255.0f); - VertColor v1Col = VertColor(m_gbsData.vertrgb.at(v2).r / 255.0f, m_gbsData.vertrgb.at(v2).g / 255.0f, m_gbsData.vertrgb.at(v2).b / 255.0f); - VertColor v2Col = VertColor(m_gbsData.vertrgb.at(v3).r / 255.0f, m_gbsData.vertrgb.at(v3).g / 255.0f, m_gbsData.vertrgb.at(v3).b / 255.0f); - - mesh.vertCol[remappedV0] = v0Col; - mesh.vertCol[remappedV1] = v1Col; - mesh.vertCol[remappedV2] = v2Col; - - assert(remappedV0 >= 0 && remappedV1 >= 0 && remappedV2 >= 0); - assert(remappedV0 < mobj.vcount && remappedV1 < mobj.vcount && remappedV2 < mobj.vcount); - assert(remappedV0 < m_gbsData.naverts && remappedV1 < m_gbsData.naverts && remappedV2 < m_gbsData.naverts); - - face.setVerts(remappedV0, remappedV1, remappedV2); - face.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS); - face.setMatID(materialLevel); - - TVFace& tvFace = mesh.tvFace[faceIndex]; - tvFace.setTVerts(remappedV0, remappedV1, remappedV2); - - TVFace& vcFace = mesh.vcFace[faceIndex]; - vcFace.setTVerts(remappedV0, remappedV1, remappedV2); - - faceIndex++; - } - - materialLevel++; - } - - mesh.buildNormals(); - mesh.buildBoundingBox(); - mesh.InvalidateEdgeList(); - - TriObject* tri = CreateNewTriObject(); - tri->mesh = mesh; - Matrix3 tm; - tm.IdentityMatrix(); - - ImpNode* Node = EI->CreateNode(); - if (!Node) - { - throw std::exception("Could not create Node"); - } - - Node->Reference(tri); - Node->SetTransform(0, tm); - EI->AddNodeToScene(Node); - INode* iNode = Node->GetINode(); - iNode->SetMtl(topLevelMaterial); - Node->SetName(util::to_wstring(objName).c_str()); - } -} - -Mtl* GbsImporter::BuildParentMaterial(SubObject& obj, int numSubMaterials) -{ - // Check if we need to create a MixMat blend material - // (this check is iffy, not fully certain how to identify MixMat exports but this seems close enough) - if (obj.blend > 0.50 && (obj.flags & 0x10 || obj.flags & 0x20)) - { - //if (bitmapTex) - //{ - // //bitmapTex->GetUVGen()->SetUVWSource(2); - //} - - // Create custom MixMat material (from official 3dsmax plugin) - Mtl* mixMatMaterial = (Mtl*)CreateInstance(SClass_ID(MATERIAL_CLASS_ID), Class_ID(MIXMAT_CLASS_ID, 0)); - mixMatMaterial->SetName(util::to_wstring(obj.objname).c_str()); - - int ns = mixMatMaterial->NumSubMtls(); - - // Set the blend value - IParamBlock2* parameterBlock = mixMatMaterial->GetParamBlock(0); - parameterBlock->SetValue(0, 0, obj.blend); - - return mixMatMaterial; - } - else - { - // Standard multi-material - MultiMtl* multiMtl = NewDefaultMultiMtl(); - multiMtl->SetName(util::to_wstring(obj.objname).c_str()); - multiMtl->SetNumSubMtls(numSubMaterials); - - return multiMtl; - } -} - -Mtl* GbsImporter::BuildMaterial(SubObject& obj, Mtl* parentMaterial) -{ - BitmapTex* bitmapTex = nullptr; - - if (obj.texname && obj.texname[0]) - { - std::wstring mapName = util::to_wstring(obj.texname).c_str(); - mapName += L".tga"; // DDS partially supported but not in use, okay to hardcode for now - - bitmapTex = NewDefaultBitmapTex(); - bitmapTex->SetName(util::to_wstring(obj.texname).c_str()); - bitmapTex->SetMapName(mapName.c_str()); - } - - StdMat2* stdMat = NewDefaultStdMat(); - if (FlagIsSet(obj.flags, GBSFlagMaxLit) || parentMaterial) - { - // This isn't quite right, not all models with blend materials have this flag set. See ripper.gbs. - // Behavior is correct enough for now though. - stdMat->SetWireUnits(TRUE); - } - - stdMat->EnableMap(ID_DI, true); - - if (obj.flags & 0x10) - { - stdMat->SetTransparencyType(TRANSP_ADDITIVE); - } - else if (obj.flags & 0x20) - { - stdMat->SetTransparencyType(TRANSP_SUBTRACTIVE); - } - stdMat->SetShinStr(1.0f, 0); - stdMat->SetOpacFalloff(obj.falloff, 0); - - if (obj.emissive != 0) - { - Color emissive(GetRValue(obj.emissive), GetGValue(obj.emissive), GetBValue(obj.emissive)); - stdMat->SetSelfIllumColor(emissive, 0); - } - - if (bitmapTex) - { - stdMat->SetSubTexmap(ID_DI, bitmapTex); - } - - Color diffuse(GetRValue(obj.diffuse), GetGValue(obj.diffuse), GetBValue(obj.diffuse)); - stdMat->SetDiffuse(diffuse, 0); - Color ambient(GetRValue(obj.ambient), GetGValue(obj.ambient), GetBValue(obj.ambient)); - stdMat->SetAmbient(ambient, 0); - Color specular(GetRValue(obj.specular), GetGValue(obj.specular), GetBValue(obj.specular)); - stdMat->SetSpecular(specular, 0); - stdMat->SetShininess(obj.power / 100.0f, 0); - - stdMat->SetName(util::to_wstring(obj.objname).c_str()); - - return stdMat; -} - -BOOL GbsImporter::SupportsOptions(int Ext, DWORD Options) +BOOL GiantsImporter::SupportsOptions(int Ext, DWORD Options) { return FALSE; } \ No newline at end of file diff --git a/Plugins/imp_gbs/Importer.h b/Plugins/imp_gbs/Importer.h index 370d768..ad79899 100644 --- a/Plugins/imp_gbs/Importer.h +++ b/Plugins/imp_gbs/Importer.h @@ -1,10 +1,15 @@ #pragma once #include "GbsData.h" +#include "Gb2Data.h" + +#define GBXFlagNormals 0x0001 +#define GBXFlagUVs 0x0002 +#define GBXFlagRGBs 0x0004 #define GIANTSIMP_CLASSID Class_ID(0x552cac79, 0x46f2d727) -class GbsImporter : public SceneImport +class GiantsImporter : public SceneImport { public: static HWND hParams; @@ -19,21 +24,10 @@ public: const MCHAR* OtherMessage2(); // Other message #2 unsigned int Version(); // Version number * 100 (i.e. v3.01 = 301) void ShowAbout(HWND hWnd); // Show DLL's "About..." box - int DoImport(const MCHAR *name,ImpInterface *i,Interface *gi, BOOL suppressPrompts=FALSE); // Import file + int DoImport(const MCHAR* name, ImpInterface* ii, Interface* i, BOOL suppressPrompts); // Import file //Constructor/Destructor BOOL SupportsOptions(int Ext,DWORD Options); - -private: - GbsData ReadGbsFile(const MCHAR* Name); - Mtl* BuildParentMaterial(SubObject& obj, int numSubMaterials); - Mtl* BuildMaterial(SubObject& obj, Mtl* parentMaterial); - int GetLocalVertex(Point3* avert, const Mesh& mesh); - void BuildMeshes(ImpInterface* EI); - bool EvaluateTriData(unsigned short** pTriData, unsigned short* pTriIdx, unsigned short* acount, int* pV1, int* pV2, int* pV3); - - FILE* m_OpenFile{}; - GbsData m_gbsData; }; extern HINSTANCE hInstance; \ No newline at end of file diff --git a/Plugins/imp_gbs/ImporterFactory.cpp b/Plugins/imp_gbs/ImporterFactory.cpp new file mode 100644 index 0000000..4176615 --- /dev/null +++ b/Plugins/imp_gbs/ImporterFactory.cpp @@ -0,0 +1,19 @@ +#include "ImporterFactory.h" +#include "GbsImporter.h" +#include "Gb2Importer.h" + +void ImporterFactory::ImportFile(const MCHAR* Name, ImpInterface* EI, Interface* I, BOOL SupressPrompts) +{ + M_STD_STRING strName = Name; + + if (strName.rfind(L".gbs") != -1) + { + GbsImporter importer; + importer.ImportFile(Name, EI, I, SupressPrompts); + } + else if (strName.rfind(L".gb2") != -1) + { + Gb2Importer importer; + importer.ImportFile(Name, EI, I, SupressPrompts); + } +} \ No newline at end of file diff --git a/Plugins/imp_gbs/ImporterFactory.h b/Plugins/imp_gbs/ImporterFactory.h new file mode 100644 index 0000000..ed3d444 --- /dev/null +++ b/Plugins/imp_gbs/ImporterFactory.h @@ -0,0 +1,7 @@ +#pragma once + +class ImporterFactory +{ +public: + static void ImportFile(const MCHAR* name, ImpInterface* ii, Interface* i, BOOL suppressPrompts); +}; \ No newline at end of file diff --git a/Plugins/imp_gbs/imp_gbs.vcxproj b/Plugins/imp_gbs/imp_gbs.vcxproj index 277fb6e..507a818 100644 --- a/Plugins/imp_gbs/imp_gbs.vcxproj +++ b/Plugins/imp_gbs/imp_gbs.vcxproj @@ -99,8 +99,7 @@ $(OutDir)GiantsImp.lib - - + copy "$(TargetPath)" "$(ADSK_3DSMAX_x64_2021)\stdplugs\$(TargetName).dli @@ -113,7 +112,7 @@ MultiThreadedDebugDLL Use Level3 - ProgramDatabase + EditAndContinue Default stdcpp17 stdafx.h @@ -135,8 +134,7 @@ - - + copy "$(TargetPath)" "$(ADSK_3DSMAX_x64_2021)\stdplugs\$(TargetName).dli @@ -169,14 +167,17 @@ $(OutDir)GiantsImp.lib - - + copy "$(TargetPath)" "$(ADSK_3DSMAX_x64_2021)\stdplugs\$(TargetName).dli + + + + Create Create @@ -184,8 +185,13 @@ + + + + + diff --git a/Plugins/imp_gbs/imp_gbs.vcxproj.filters b/Plugins/imp_gbs/imp_gbs.vcxproj.filters index 94757cc..4005ccc 100644 --- a/Plugins/imp_gbs/imp_gbs.vcxproj.filters +++ b/Plugins/imp_gbs/imp_gbs.vcxproj.filters @@ -23,6 +23,18 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -37,6 +49,21 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files +