From fbe5f352ff0f238266bc690a0b750674f80b1f02 Mon Sep 17 00:00:00 2001 From: Aki Date: Sat, 6 Apr 2024 02:34:19 +0200 Subject: Added obj2mag and mag2obj utilities --- Magic2/MagicDoc.cpp | 153 +------- MagicEx/CMakeLists.txt | 25 +- MagicEx/MagicLoad.cpp | 178 --------- MagicEx/MagicLoad.h | 18 - MagicEx/ModelFile3DS.cpp | 281 -------------- MagicEx/ModelFile3DS.h | 27 -- MagicEx/ModelFileMAG.cpp | 823 ---------------------------------------- MagicEx/ModelFileMAG.h | 31 -- MagicEx/ModelFileOBJ.cpp | 788 -------------------------------------- MagicEx/ModelFileOBJ.h | 27 -- MagicEx/include/MagicLoad.h | 23 ++ MagicEx/include/MagicLoad.inl.h | 32 ++ MagicEx/include/ModelFile3DS.h | 27 ++ MagicEx/include/ModelFileMAG.h | 31 ++ MagicEx/include/ModelFileOBJ.h | 27 ++ MagicEx/src/MagicLoad.cpp | 178 +++++++++ MagicEx/src/ModelFile3DS.cpp | 281 ++++++++++++++ MagicEx/src/ModelFileMAG.cpp | 823 ++++++++++++++++++++++++++++++++++++++++ MagicEx/src/ModelFileOBJ.cpp | 788 ++++++++++++++++++++++++++++++++++++++ MagicEx/src/mag2obj.cpp | 27 ++ MagicEx/src/obj2mag.cpp | 27 ++ 21 files changed, 2301 insertions(+), 2314 deletions(-) delete mode 100644 MagicEx/MagicLoad.cpp delete mode 100644 MagicEx/MagicLoad.h delete mode 100644 MagicEx/ModelFile3DS.cpp delete mode 100644 MagicEx/ModelFile3DS.h delete mode 100644 MagicEx/ModelFileMAG.cpp delete mode 100644 MagicEx/ModelFileMAG.h delete mode 100644 MagicEx/ModelFileOBJ.cpp delete mode 100644 MagicEx/ModelFileOBJ.h create mode 100644 MagicEx/include/MagicLoad.h create mode 100644 MagicEx/include/MagicLoad.inl.h create mode 100644 MagicEx/include/ModelFile3DS.h create mode 100644 MagicEx/include/ModelFileMAG.h create mode 100644 MagicEx/include/ModelFileOBJ.h create mode 100644 MagicEx/src/MagicLoad.cpp create mode 100644 MagicEx/src/ModelFile3DS.cpp create mode 100644 MagicEx/src/ModelFileMAG.cpp create mode 100644 MagicEx/src/ModelFileOBJ.cpp create mode 100644 MagicEx/src/mag2obj.cpp create mode 100644 MagicEx/src/obj2mag.cpp diff --git a/Magic2/MagicDoc.cpp b/Magic2/MagicDoc.cpp index 55ecd8d..c57d922 100644 --- a/Magic2/MagicDoc.cpp +++ b/Magic2/MagicDoc.cpp @@ -15,6 +15,7 @@ #include "Magic.h" #include "MagicDoc.h" +#include "MagicLoad.h" #include "ModelFileMAG.h" #include "ModelFileOBJ.h" #include "ModelFile3DS.h" @@ -22,12 +23,8 @@ #include "Selector.h" #include "Editor.h" #include "Command.h" - -#include "Bitmap.h" -#include "Color.h" #include "D3DXImage.h" #include "Geometry.h" -#include "Pcx.h" #include "Polygon.h" #include "Solid.h" @@ -236,146 +233,30 @@ BOOL MagicDoc::OnOpenDocument(LPCTSTR path_name) return TRUE; } + bool MagicDoc::ImportFile(LPCTSTR path_name) { if (strstr(path_name, ".obj") || strstr(path_name, ".OBJ")) { - ModelFileOBJ obj_file(path_name); - - if (solid->GetModel()) { - Solid* s = new Solid; - - if (s->Load(&obj_file)) { - // todo: insert command here - Model* orig = solid->GetModel(); - Model* imported = s->GetModel(); - - orig->GetMaterials().append(imported->GetMaterials()); - orig->GetSurfaces().append(imported->GetSurfaces()); - orig->OptimizeMaterials(); - - imported->GetMaterials().clear(); - imported->GetSurfaces().clear(); - - SetModifiedFlag(FALSE); - UpdateAllViews(NULL); - delete s; - return true; - } - - delete s; - } - else { - if (solid->Load(&obj_file)) { - SetModifiedFlag(FALSE); - UpdateAllViews(NULL); - return true; - } - } - - return false; + if (!ImportInto(path_name, solid)) + return false; + SetModifiedFlag(FALSE); + UpdateAllViews(NULL); + return true; } - if (strstr(path_name, ".3ds") || strstr(path_name, ".3DS")) { - ModelFile3DS model_file(path_name); - - if (solid->GetModel()) { - Solid* s = new Solid; - - if (s->Load(&model_file)) { - // todo: insert command here - Model* orig = solid->GetModel(); - Model* imported = s->GetModel(); - - orig->GetMaterials().append(imported->GetMaterials()); - orig->GetSurfaces().append(imported->GetSurfaces()); - orig->OptimizeMaterials(); - - imported->GetMaterials().clear(); - imported->GetSurfaces().clear(); - - SetModifiedFlag(FALSE); - UpdateAllViews(NULL); - delete s; - return true; - } - - delete s; - } - else { - if (solid->Load(&model_file)) { - SetModifiedFlag(FALSE); - UpdateAllViews(NULL); - return true; - } - } - - return false; - } - - FILE* fp = fopen(path_name, "rb"); - if (!fp) { - ::MessageBox(0, "Import Failed: could not open file", "ERROR", MB_OK); - return false; - } - - int version = 1; - char file_id[5]; - fread(file_id, 4, 1, fp); - file_id[4] = '\0'; - fclose(fp); - - if (strncmp(file_id, "MAG", 3)) { - ::MessageBox(0, "Open Failed: Invalid file type", "ERROR", MB_OK); - return false; - } - - switch (file_id[3]) { - case '6': version = 6; break; - case '5': version = 5; break; - default: version = 0; break; + if (!ImportInto(path_name, solid)) + return false; + SetModifiedFlag(FALSE); + UpdateAllViews(NULL); + return true; } - - if (version < 5 || version > 6) { - ::MessageBox(0, "Open Failed: Unsupported version", "ERROR", MB_OK); + if (!ImportInto(path_name, solid)) return false; - } - - ModelFileMAG mag_file(path_name); - - if (solid->GetModel()) { - Solid* s = new Solid; - if (s->Load(&mag_file)) { - // todo: insert command here - Model* orig = solid->GetModel(); - Model* imported = s->GetModel(); - - orig->GetMaterials().append(imported->GetMaterials()); - orig->GetSurfaces().append(imported->GetSurfaces()); - orig->OptimizeMaterials(); - - imported->GetMaterials().clear(); - imported->GetSurfaces().clear(); - - SetModifiedFlag(FALSE); - UpdateAllViews(NULL); - delete s; - return true; - } - - delete s; - } - else { - InitCommandStack(); - - if (solid->Load(&mag_file)) { - SetModifiedFlag(FALSE); - UpdateAllViews(NULL); - return true; - } - } - - return false; + InitCommandStack(); + SetModifiedFlag(FALSE); + UpdateAllViews(NULL); + return true; } bool diff --git a/MagicEx/CMakeLists.txt b/MagicEx/CMakeLists.txt index 9c8038a..5c552fc 100644 --- a/MagicEx/CMakeLists.txt +++ b/MagicEx/CMakeLists.txt @@ -1,9 +1,24 @@ project(MagicEx) add_library( ${PROJECT_NAME} STATIC - MagicLoad.cpp - ModelFile3DS.cpp - ModelFileMAG.cpp - ModelFileOBJ.cpp) -target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + src/MagicLoad.cpp + src/ModelFile3DS.cpp + src/ModelFileMAG.cpp + src/ModelFileOBJ.cpp) +target_include_directories(${PROJECT_NAME} PUBLIC include/) target_link_libraries(${PROJECT_NAME} PUBLIC StarsEx PRIVATE l3ds) + + +add_executable(${PROJECT_NAME}_obj2mag src/obj2mag.cpp) +target_link_libraries(${PROJECT_NAME}_obj2mag PRIVATE ${PROJECT_NAME}) +set_target_properties(${PROJECT_NAME}_obj2mag PROPERTIES OUTPUT_NAME obj2mag) + + +add_executable(${PROJECT_NAME}_mag2obj src/mag2obj.cpp) +target_link_libraries(${PROJECT_NAME}_mag2obj PRIVATE ${PROJECT_NAME}) +set_target_properties(${PROJECT_NAME}_mag2obj PROPERTIES OUTPUT_NAME mag2obj) + + +install( + TARGETS ${PROJECT_NAME}_obj2mag ${PROJECT_NAME}_mag2obj RUNTIME + COMPONENT Tools DESTINATION ${CMAKE_INSTALL_PREFIX}) diff --git a/MagicEx/MagicLoad.cpp b/MagicEx/MagicLoad.cpp deleted file mode 100644 index 2b13a93..0000000 --- a/MagicEx/MagicLoad.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo -*/ - -#include "MagicLoad.h" - -#include "Bitmap.h" -#include "Color.h" -#include "D3DXImage.h" -#include "Geometry.h" -#include "Pcx.h" - - -int LoadBuffer(const char* filename, BYTE*& buf, bool null_terminate) -{ - buf = 0; - - FILE* f = ::fopen(filename, "rb"); - - if (f) { - ::fseek(f, 0, SEEK_END); - int len = ftell(f); - ::fseek(f, 0, SEEK_SET); - - if (null_terminate) { - buf = new BYTE[len+1]; - if (buf) - buf[len] = 0; - } - - else { - buf = new BYTE[len]; - } - - if (buf) - ::fread(buf, len, 1, f); - - ::fclose(f); - - return len; - } - - return 0; -} - - -int LoadTexture(const char* fname, Bitmap*& bitmap, int type) -{ - int result = 0; - - if (!fname || !*fname) - return result; - - bitmap = Bitmap::CheckCache(fname); - - if (!bitmap) { - bool pcx_file = strstr(fname, ".pcx") || strstr(fname, ".PCX"); - - // handle PCX formats: - if (pcx_file) { - PcxImage pcx; - - if (pcx.Load((char*) fname) == PCX_OK) { - bitmap = new Bitmap; - - // 32-bit image - if (pcx.himap) { - bitmap->CopyHighColorImage(pcx.width, pcx.height, pcx.himap); - } - - // 8-bit image, check for 32-bit image as well - else if (pcx.bitmap) { - bitmap->CopyImage(pcx.width, pcx.height, pcx.bitmap); - - char tmp[256]; - int len = strlen(fname); - bool found = false; - - ZeroMemory(tmp, sizeof(tmp)); - - for (int i = 0; i < len && !found; i++) { - if (strstr(fname + i, ".pcx") == (fname+i)) { - found = true; - } - else { - tmp[i] = fname[i]; - } - } - - if (found) { - strcat_s(tmp, "+.pcx"); - if (pcx.Load(tmp) == PCX_OK && pcx.himap != 0) { - bitmap->CopyHighColorImage(pcx.width, pcx.height, pcx.himap); - } - } - } - } - } - - // for all other formats, use D3DX: - else { - D3DXImage d3dx; - if (d3dx.Load((char*) fname)) { - bitmap = new Bitmap; - bitmap->CopyHighColorImage(d3dx.width, d3dx.height, d3dx.image); - } - } - - if (bitmap) { - LoadAlpha(fname, *bitmap, type); - - bitmap->SetFilename(fname); - bitmap->SetType(type); - bitmap->MakeTexture(); - - Bitmap::AddToCache(bitmap); - } - } - - return result; -} - -int LoadAlpha(const char* name, Bitmap& bitmap, int type) -{ - PcxImage pcx; - D3DXImage d3dx; - bool pcx_file = strstr(name, ".pcx") || strstr(name, ".PCX"); - bool bmp_file = strstr(name, ".bmp") || strstr(name, ".BMP"); - bool jpg_file = strstr(name, ".jpg") || strstr(name, ".JPG"); - bool png_file = strstr(name, ".png") || strstr(name, ".PNG"); - bool tga_file = strstr(name, ".tga") || strstr(name, ".TGA"); - - // check for an associated alpha-only (grayscale) bitmap: - char filename[256]; - strcpy_s(filename, name); - - char* dot = strrchr(filename, '.'); - if (dot && pcx_file) - strcpy(dot, "@.pcx"); - else if (dot && bmp_file) - strcpy(dot, "@.bmp"); - else if (dot && jpg_file) - strcpy(dot, "@.jpg"); - else if (dot && png_file) - strcpy(dot, "@.png"); - else if (dot && tga_file) - strcpy(dot, "@.tga"); - else - return 0; - - // first try to load from current directory: - bool loaded = false; - - if (pcx_file) - loaded = pcx.Load(filename) == PCX_OK; - - else - loaded = d3dx.Load(filename); - - // now copy the alpha values into the bitmap: - if (loaded) { - if (pcx_file && pcx.bitmap) { - bitmap.CopyAlphaImage(pcx.width, pcx.height, pcx.bitmap); - } - else if (pcx_file && pcx.himap) { - bitmap.CopyAlphaRedChannel(pcx.width, pcx.height, pcx.himap); - } - else if (d3dx.image) { - bitmap.CopyAlphaRedChannel(d3dx.width, d3dx.height, d3dx.image); - } - } - - return 0; -} diff --git a/MagicEx/MagicLoad.h b/MagicEx/MagicLoad.h deleted file mode 100644 index 27b6380..0000000 --- a/MagicEx/MagicLoad.h +++ /dev/null @@ -1,18 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo -*/ - -#pragma once - -#include - -#include - - -int LoadBuffer(const char* filename, BYTE*& buf, bool null_terminate=false); -int LoadTexture(const char* name, Bitmap*& bmp, int type=0); -int LoadAlpha(const char* name, Bitmap& bitmap, int type=0); diff --git a/MagicEx/ModelFile3DS.cpp b/MagicEx/ModelFile3DS.cpp deleted file mode 100644 index 4c4a157..0000000 --- a/MagicEx/ModelFile3DS.cpp +++ /dev/null @@ -1,281 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - File loader for 3DStudio MAX 3DS format models -*/ - -#include "ModelFile3DS.h" - -#include -#include -#include -#include - -#include "MagicLoad.h" -#include "l3ds.h" - -// +--------------------------------------------------------------------+ - -ModelFile3DS::ModelFile3DS(const char* fname) - : ModelFile(fname) -{ -} - -ModelFile3DS::~ModelFile3DS() -{ -} - -// +--------------------------------------------------------------------+ - -static int mcomp(const void* a, const void* b) -{ - Poly* pa = (Poly*) a; - Poly* pb = (Poly*) b; - - if (pa->sortval == pb->sortval) - return 0; - - if (pa->sortval < pb->sortval) - return 1; - - return -1; -} - -bool -ModelFile3DS::Load(Model* model, double scale) -{ - if (model && scale > 0 && strlen(filename) > 0) { - ModelFile::Load(model, scale); - - L3DS loader; - - if (!loader.LoadFile(filename)) { - ::MessageBox(0, "3DS Import Failed: Magic could not open the file for reading", "ERROR", MB_OK); - return false; - } - - int ntex = 0; - int nsurfs = 0; - int nverts = 0; - int npolys = 0; - int nmatls = 0; - int nmeshs = loader.GetMeshCount(); - - int* mesh_verts = new int[nmeshs]; - - for (int m = 0; m < nmeshs; m++) { - LMesh& mesh = loader.GetMesh(m); - - mesh_verts[m] = nverts; - - // count verts and polys: - nverts += mesh.GetVertexCount(); - npolys += mesh.GetTriangleCount(); - } - - if (nverts > Model::MAX_VERTS || npolys > Model::MAX_POLYS) { - ::MessageBox(0, "3DS Import Failed: model is too complicated (too many vertices/polys)!", "ERROR", MB_OK); - delete [] mesh_verts; - return FALSE; - } - - // get materials: - nmatls = loader.GetMaterialCount(); - - for (int i = 0; i < nmatls; i++) { - LMaterial& matl = loader.GetMaterial(i); - LMap& tex_diff = matl.GetTextureMap1(); - LMap& tex_spec = matl.GetSpecularMap(); - LMap& tex_bump = matl.GetBumpMap(); - LMap& tex_glow = matl.GetTextureMap2(); - - Material* material = new Material; - - strncpy(material->name, matl.GetName().c_str(), Material::NAMELEN); - - material->Ka.Set( matl.GetAmbientColor().r, - matl.GetAmbientColor().g, - matl.GetAmbientColor().b ); - - material->ambient_value = 1.0f; - material->ambient_color = material->Ka.ToColor(); - - material->Kd.Set( matl.GetDiffuseColor().r, - matl.GetDiffuseColor().g, - matl.GetDiffuseColor().b ); - - material->diffuse_value = 1.0f; - material->diffuse_color = material->Kd.ToColor(); - - material->Ks.Set( matl.GetSpecularColor().r, - matl.GetSpecularColor().g, - matl.GetSpecularColor().b ); - - material->specular_value = 1.0f; - material->specular_color = material->Ks.ToColor(); - - material->power = matl.GetShininess() * 100; - - if (tex_diff.mapName[0]) - LoadTexture(tex_diff.mapName, material->tex_diffuse); - - if (tex_spec.mapName[0]) - LoadTexture(tex_spec.mapName, material->tex_specular); - - if (tex_bump.mapName[0]) - LoadTexture(tex_bump.mapName, material->tex_bumpmap); - - if (tex_glow.mapName[0]) - LoadTexture(tex_glow.mapName, material->tex_emissive); - - model->GetMaterials().append(material); - } - - Surface* surface = new Surface; - model->GetSurfaces().append(surface); - - surface->CreateVerts(nverts); - surface->CreatePolys(npolys); - - VertexSet* vset = surface->GetVertexSet(); - Poly* polys = surface->GetPolys(); - float radius = 0; - int v = 0; - - for (int m = 0; m < nmeshs && v < nverts; m++) { - LMesh& mesh = loader.GetMesh(m); - - // read vertex set: - for (int i = 0; i < mesh.GetVertexCount() && v < nverts; i++) { - vset->loc[v].x = mesh.GetVertex(i).x; - vset->loc[v].y = mesh.GetVertex(i).z; - vset->loc[v].z = mesh.GetVertex(i).y; - - vset->nrm[v].x = mesh.GetNormal(i).x; - vset->nrm[v].y = mesh.GetNormal(i).z; - vset->nrm[v].z = mesh.GetNormal(i).y; - - vset->tu[v] = mesh.GetUV(i).x; - vset->tv[v] = mesh.GetUV(i).y; - - float d = vset->loc[v].length(); - if (d > radius) - radius = d; - - v++; - } - } - - if (radius < 16) { - model->ScaleBy(256.0f / radius); - radius = 256.0f; - } - - int n = 0; - - for (int m = 0; m < nmeshs && n < npolys; m++) { - LMesh& mesh = loader.GetMesh(m); - - // read polys: - int mesh_tris = mesh.GetTriangleCount(); - for (int i = 0; i < mesh_tris && n < npolys; i++) { - Poly* p = surface->GetPolys() + n++; - const LTriangle& tri = mesh.GetTriangle(i); - LTriangle2 tri2 = mesh.GetTriangle2(i); - - p->nverts = 3; - - p->verts[0] = tri.a + mesh_verts[m]; - p->verts[1] = tri.b + mesh_verts[m]; - p->verts[2] = tri.c + mesh_verts[m]; - - if (p->verts[0] > nverts || p->verts[1] > nverts || p->verts[2] > nverts) { - p->verts[0] = 0; - p->verts[1] = 1; - p->verts[2] = 2; - } - - if (tri2.vertexNormals[0] == tri2.vertexNormals[1] && - tri2.vertexNormals[0] == tri2.vertexNormals[2]) - p->flatness = 1.0f; - else - p->flatness = 0.0f; - - p->vertex_set = vset; - p->material = model->GetMaterials()[ tri2.materialId ]; - p->sortval = tri2.materialId; - - p->plane = Plane(vset->loc[ p->verts[0] ], - vset->loc[ p->verts[2] ], - vset->loc[ p->verts[1] ]); - - surface->AddIndices(p->nverts); - } - } - - // sort the polys by material index: - qsort((void*) polys, npolys, sizeof(Poly), mcomp); - - // then assign them to cohesive segments: - Segment* segment = 0; - - for (int n = 0; n < npolys; n++) { - if (segment && segment->material == polys[n].material) { - segment->npolys++; - } - else { - segment = 0; - } - - if (!segment) { - segment = new Segment; - - segment->npolys = 1; - segment->polys = &polys[n]; - segment->material = segment->polys->material; - - surface->GetSegments().append(segment); - } - } - - *pnverts = nverts; - *pnpolys = npolys; - *pradius = radius; - - model->Normalize(); - return true; - } - - return false; -} - -// +--------------------------------------------------------------------+ - -bool -ModelFile3DS::Save(Model* m) -{ - if (m) { - ModelFile::Save(m); - - FILE* f = fopen(filename, "w"); - if (!f) { - ::MessageBox(0, "3DS Export Failed: Magic could not open the file for writing", "ERROR", MB_OK); - return false; - } - - fclose(f); - - return true; - } - - return false; -} - -// +--------------------------------------------------------------------+ diff --git a/MagicEx/ModelFile3DS.h b/MagicEx/ModelFile3DS.h deleted file mode 100644 index 3cac828..0000000 --- a/MagicEx/ModelFile3DS.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - File loader for 3DStudio MAX 3DS format models -*/ - -#pragma once - -#include - - -class ModelFile3DS : public ModelFile -{ -public: - ModelFile3DS(const char* fname); - virtual ~ModelFile3DS(); - - virtual bool Load(Model* m, double scale=1.0); - virtual bool Save(Model* m); -}; diff --git a/MagicEx/ModelFileMAG.cpp b/MagicEx/ModelFileMAG.cpp deleted file mode 100644 index fe076ed..0000000 --- a/MagicEx/ModelFileMAG.cpp +++ /dev/null @@ -1,823 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - File loader for MAG format models -*/ - -#include "ModelFileMAG.h" - -#include -#include -#include - -#include "MagicLoad.h" - -// +--------------------------------------------------------------------+ - -struct MaterialMag6 { - char name[Material::NAMELEN]; - char shader[Material::NAMELEN]; - float power; // highlight sharpness (big=shiny) - float brilliance; // diffuse power function - float bump; // bump level (0=none) - DWORD blend; // alpha blend type - bool shadow; // material casts shadow - bool luminous; // verts have their own lighting - - Color ambient_color; - Color diffuse_color; - Color specular_color; - Color emissive_color; - - float ambient_value; - float diffuse_value; - float specular_value; - float emissive_value; - - BYTE tex_diffuse; - BYTE tex_specular; - BYTE tex_bumpmap; - BYTE tex_emissive; -}; - -// +--------------------------------------------------------------------+ - -ModelFileMAG::ModelFileMAG(const char* fname) - : ModelFile(fname) -{ -} - -ModelFileMAG::~ModelFileMAG() -{ -} - -// +--------------------------------------------------------------------+ - -bool -ModelFileMAG::Load(Model* m, double scale) -{ - if (m && scale > 0 && strlen(filename) > 0) { - ModelFile::Load(m, scale); - - bool result = false; - FILE* fp = fopen(filename, "rb"); - - // check MAG file: - if (!fp) { - ::MessageBox(0, "File Open Failed:\nMagic could not open the requested file.", "ERROR", MB_OK); - return result; - } - - ZeroMemory(pname, 64); - strncpy(pname, filename, 63); - - char file_id[5]; - fread(file_id, 4, 1, fp); - file_id[4] = '\0'; - int version = 1; - - if (!strcmp(file_id, "MAG6")) { - version = 6; - } - else if (!strcmp(file_id, "MAG5")) { - version = 5; - } - else if (!strcmp(file_id, "MAG4")) { - version = 4; - } - else { - ::MessageBox(0, "File Open Failed:\nThe requested file uses an invalid format.", "ERROR", MB_OK); - fclose(fp); - return result; - } - - // get ready to load, delete existing model: - m->GetSurfaces().destroy(); - m->GetMaterials().destroy(); - *pnverts = 0; - *pnpolys = 0; - - // now load the model: - switch (version) { - case 4: - case 5: - result = LoadMag5(fp, m, scale); - break; - - case 6: - result = LoadMag6(fp, m, scale); - break; - - default: - break; - } - - fclose(fp); - return true; - } - - return false; -} - -// +--------------------------------------------------------------------+ - -bool -ModelFileMAG::Save(Model* m) -{ - if (m) { - ModelFile::Save(m); - - FILE* fp = fopen(filename, "wb"); - if (!fp) { - ::MessageBox(0, "Save Failed:\nMagic could not open the file for writing.", "ERROR", MB_OK); - return FALSE; - } - - fwrite("MAG6", 4, 1, fp); - - int i = 0; - int ntex = 0; - int nmtls = 0; - int nsurfs = m->NumSurfaces(); - List textures; - - ListIter m_iter = m->GetMaterials(); - while (++m_iter) { - Material* mtl = m_iter.value(); - Bitmap* bmp = mtl->tex_diffuse; - - if (bmp && !textures.contains(bmp)) { - textures.append(bmp); - } - - bmp = mtl->tex_specular; - - if (bmp && !textures.contains(bmp)) { - textures.append(bmp); - } - - bmp = mtl->tex_emissive; - - if (bmp && !textures.contains(bmp)) { - textures.append(bmp); - } - - bmp = mtl->tex_bumpmap; - - if (bmp && !textures.contains(bmp)) { - textures.append(bmp); - } - - nmtls++; - } - - ListIter t_iter = textures; - while (++t_iter) { - Bitmap* bmp = t_iter.value(); - ntex += strlen(bmp->GetFilename()) + 1; - } - - nsurfs = m->GetSurfaces().size(); - - fwrite(&ntex, 4, 1, fp); - fwrite(&nmtls, 4, 1, fp); - fwrite(&nsurfs, 4, 1, fp); - - if (ntex) { - t_iter.reset(); - while (++t_iter) { - Bitmap* bmp = t_iter.value(); - - fwrite(bmp->GetFilename(), - strlen(bmp->GetFilename()) + 1, - 1, - fp); - } - } - - if (nmtls) { - m_iter.reset(); - while (++m_iter) { - Material* mtl = m_iter.value(); - MaterialMag6 m6; - - ZeroMemory(&m6, sizeof(m6)); - - CopyMemory(m6.name, mtl->name, Material::NAMELEN); - CopyMemory(m6.shader, mtl->shader, Material::NAMELEN); - - m6.ambient_value = mtl->ambient_value; - m6.ambient_color = mtl->ambient_color; - m6.diffuse_value = mtl->diffuse_value; - m6.diffuse_color = mtl->diffuse_color; - m6.specular_value = mtl->specular_value; - m6.specular_color = mtl->specular_color; - m6.emissive_value = mtl->emissive_value; - m6.emissive_color = mtl->emissive_color; - - m6.power = mtl->power; - m6.brilliance = mtl->brilliance; - m6.bump = mtl->bump; - m6.blend = mtl->blend; - m6.shadow = mtl->shadow; - m6.luminous = mtl->luminous; - - if (mtl->tex_diffuse) - m6.tex_diffuse = textures.index(mtl->tex_diffuse) + 1; - - if (mtl->tex_specular) - m6.tex_specular = textures.index(mtl->tex_specular) + 1; - - if (mtl->tex_emissive) - m6.tex_emissive = textures.index(mtl->tex_emissive) + 1; - - if (mtl->tex_bumpmap) - m6.tex_bumpmap = textures.index(mtl->tex_bumpmap) + 1; - - fwrite(&m6, sizeof(m6), 1, fp); - } - } - - ListIter s_iter = m->GetSurfaces(); - while (++s_iter) { - Surface* s = s_iter.value(); - - int nverts = s->NumVerts(); - int npolys = s->NumPolys(); - BYTE namelen = strlen(s->Name()) + 1; - - fwrite(&nverts, 4, 1, fp); - fwrite(&npolys, 4, 1, fp); - fwrite(&namelen, 1, 1, fp); - fwrite(s->Name(), 1, namelen, fp); - - VertexSet* vset = s->GetVertexSet(); - Poly* polys = s->GetPolys(); - - // write vertex set: - for (int v = 0; v < nverts; v++) { - fwrite(&vset->loc[v], sizeof(float), 3, fp); - fwrite(&vset->nrm[v], sizeof(float), 3, fp); - fwrite(&vset->tu[v], sizeof(float), 1, fp); - fwrite(&vset->tv[v], sizeof(float), 1, fp); - } - - // write polys: - for (int n = 0; n < npolys; n++) { - Poly& poly = polys[n]; - BYTE poly_nverts = (BYTE) poly.nverts; - BYTE material_index = 0; - WORD poly_verts[8]; - - m_iter.reset(); - while (++m_iter && !material_index) { - if (poly.material == m_iter.value()) - material_index = m_iter.index() + 1; - } - - for (int i = 0; i < poly_nverts; i++) { - poly_verts[i] = poly.verts[i]; - } - - fwrite(&poly_nverts, sizeof(BYTE), 1, fp); - fwrite(&material_index, sizeof(BYTE), 1, fp); - fwrite(&poly_verts[0], sizeof(WORD), poly_nverts, fp); - } - } - - return true; - } - - return false; -} - -// +--------------------------------------------------------------------+ - -struct HomogenousPlane -{ - double distance; - double normal_x; - double normal_y; - double normal_z; - double normal_w; -}; - -static void LoadPlane(Plane& p, FILE* fp) -{ - HomogenousPlane tmp; - fread(&tmp, sizeof(HomogenousPlane), 1, fp); -} - -static void LoadFlags(LPDWORD flags, FILE* fp) -{ - DWORD magic_flags; - fread(&magic_flags, sizeof(DWORD), 1, fp); - - /** MAGIC FLAGS - enum { FLAT_SHADED = 1, - LUMINOUS = 2, - TRANSLUCENT = 4, \\ must swap - CHROMAKEY = 8, // these two - FOREGROUND = 16, -- not used - WIREFRAME = 32, -- not used - SPECULAR1 = 64, - SPECULAR2 = 128 }; - ***/ - - const DWORD magic_mask = 0x0fc3; - - *flags = magic_flags & magic_mask; -} - -// +--------------------------------------------------------------------+ - -static int mcomp(const void* a, const void* b) -{ - Poly* pa = (Poly*) a; - Poly* pb = (Poly*) b; - - if (pa->sortval == pb->sortval) - return 0; - - if (pa->sortval < pb->sortval) - return -1; - - return 1; -} - -bool -ModelFileMAG::LoadMag5(FILE* fp, Model* m, double scale) -{ - bool result = false; - int ntex = 0; - int nsurfs = 0; - double radius = 0; - - fread(&ntex, sizeof(ntex), 1, fp); - fread(&nsurfs, sizeof(nsurfs), 1, fp); - - // create a default gray material: - Material* mtl = new Material; - - if (mtl) { - mtl->Ka = Color::DarkGray; - mtl->Kd = Color::LightGray; - mtl->Ks = ColorValue(0.1f,0.1f,0.1f); - mtl->power = 10.0f; - - mtl->ambient_value = 0.2f; - mtl->ambient_color = Color::DarkGray; - mtl->diffuse_value = 0.8f; - mtl->diffuse_color = Color::LightGray; - mtl->specular_value = 0.5f; - mtl->specular_color = Color::White; - strcpy_s(mtl->name, "(default)"); - - m->GetMaterials().append(mtl); - } - - // read texture list: - for (int i = 0; i < ntex; i++) { - Material* mtl = new Material; - char tname[32]; - - if (mtl) { - mtl->Ka = ColorValue(0.5f,0.5f,0.5f); - mtl->Kd = ColorValue(1.0f,1.0f,1.0f); - mtl->Ks = ColorValue(0.2f,0.2f,0.2f); - mtl->power = 20.0f; - - mtl->ambient_value = 1.0f; - mtl->ambient_color = Color::Gray; - mtl->diffuse_value = 1.0f; - mtl->diffuse_color = Color::White; - mtl->specular_value = 0.2f; - mtl->specular_color = Color::White; - - fread(tname, 32, 1, fp); - LoadTexture(tname, mtl->tex_diffuse, Bitmap::BMP_SOLID); - strcpy_s(mtl->name, tname); - - char* dot = strrchr(mtl->name, '.'); - if (dot) - *dot = 0; - - char* plus = strrchr(mtl->name, '+'); - if (plus) - *plus = 0; - - m->GetMaterials().append(mtl); - } - } - - int nverts = 0; - int npolys = 0; - - fread(&nverts, 4, 1, fp); - fread(&npolys, 4, 1, fp); - - // plan on creating four verts per poly: - int mag_nverts = nverts; - int next_vert = nverts; - - nverts = npolys * 4; - Surface* s = new Surface; - VertexSet* vset = 0; - Poly* polys = 0; - - if (s) { - s->SetName("default"); - s->CreateVerts(nverts); - s->CreatePolys(npolys); - - vset = s->GetVertexSet(); - polys = s->GetPolys(); - - ZeroMemory(vset->loc, nverts * sizeof(Vec3)); - ZeroMemory(vset->diffuse, nverts * sizeof(DWORD)); - ZeroMemory(vset->specular, nverts * sizeof(DWORD)); - ZeroMemory(vset->tu, nverts * sizeof(float)); - ZeroMemory(vset->tv, nverts * sizeof(float)); - ZeroMemory(vset->rw, nverts * sizeof(float)); - - // read vertex set: - int v; - for (v = 0; v < mag_nverts; v++) { - Vec3 vert, norm; - DWORD vstate; - - fread(&vert, sizeof(Vec3), 1, fp); - fread(&norm, sizeof(Vec3), 1, fp); - fread(&vstate, sizeof(DWORD), 1, fp); - - vert.SwapYZ(); - vert *= (float) scale; - - norm.SwapYZ(); - - vset->loc[v] = vert; - vset->nrm[v] = norm; - - double d = vert.length(); - if (d > radius) - radius = (float) d; - } - - while (v < nverts) - vset->nrm[v++] = Vec3(1,0,0); - - // read polys: - Vec3 dummy_center; - DWORD dummy_flags; - DWORD dummy_color; - int texture_num; - int poly_nverts; - int vert_index_buffer[32]; - float texture_index_buffer[32]; - - for (int n = 0; n < npolys; n++) { - Poly& poly = polys[n]; - poly.vertex_set = vset; - - fread(&dummy_flags, sizeof(DWORD), 1, fp); - fread(&dummy_center, sizeof(Vec3), 1, fp); - LoadPlane(poly.plane, fp); - fread(&dummy_color, sizeof(DWORD), 1, fp); - fread(&texture_num, sizeof(int), 1, fp); - - if (texture_num >= 0 && texture_num < ntex) { - texture_num++; - - poly.material = m->GetMaterials()[texture_num]; - poly.sortval = texture_num; - - if (dummy_flags & 2) { // luminous - Material* mtl = m->GetMaterials()[texture_num]; - - mtl->ambient_value = 0.0f; - mtl->ambient_color = Color::Black; - mtl->diffuse_value = 0.0f; - mtl->diffuse_color = Color::Black; - mtl->specular_value = 0.0f; - mtl->specular_color = Color::Black; - mtl->emissive_value = 1.0f; - mtl->emissive_color = Color::White; - - mtl->Ka = ColorValue(0,0,0,0); - mtl->Kd = ColorValue(0,0,0,0); - mtl->Ks = ColorValue(0,0,0,0); - mtl->Ke = ColorValue(1,1,1,1); - - mtl->tex_emissive = mtl->tex_diffuse; - } - } - else { - poly.material = m->GetMaterials().first(); // default material - poly.sortval = 1000; - } - - // hack: store flat shaded flag in unused visible byte - poly.visible = (BYTE) (dummy_flags & 1); - - fread(&poly_nverts, sizeof(int), 1, fp); - fread(vert_index_buffer, sizeof(int), poly_nverts, fp); - - if (poly_nverts == 3) - s->AddIndices(3); - - else if (poly_nverts == 4) - s->AddIndices(6); - - poly.nverts = poly_nverts; - for (int vi = 0; vi < poly_nverts; vi++) { - int v = vert_index_buffer[vi]; - - if (vset->rw[v] > 0) { - vset->CopyVertex(next_vert, v); - v = next_vert++; - } - - vset->rw[v] = 1; - poly.verts[vi] = v; - } - - fread(texture_index_buffer, sizeof(float), poly_nverts, fp); // tu's - for (int vi = 0; vi < poly_nverts; vi++) { - int v = poly.verts[vi]; - vset->tu[v] = texture_index_buffer[vi]; - } - - fread(texture_index_buffer, sizeof(float), poly_nverts, fp); // tv's - for (int vi = 0; vi < poly_nverts; vi++) { - int v = poly.verts[vi]; - vset->tv[v] = texture_index_buffer[vi]; - } - - DWORD unused[32]; - fread(unused, 16, 1, fp); - } - - // pass 2 (adjust vertex normals for flat polys): - for (int n = 0; n < npolys; n++) { - Poly& poly = polys[n]; - - poly.plane = Plane(vset->loc[poly.verts[0]], - vset->loc[poly.verts[2]], - vset->loc[poly.verts[1]]); - - // hack: retrieve flat shaded flag from unused visible byte - if (poly.visible) { - int poly_nverts = poly.nverts; - - for (int vi = 0; vi < poly_nverts; vi++) { - int v = poly.verts[vi]; - vset->nrm[v] = poly.plane.normal; - } - } - } - - // sort the polys by material index: - qsort((void*) polys, npolys, sizeof(Poly), mcomp); - - // then assign them to cohesive segments: - Segment* segment = 0; - - for (int n = 0; n < npolys; n++) { - if (segment && segment->material == polys[n].material) { - segment->npolys++; - } - else { - segment = 0; - } - - if (!segment) { - segment = new Segment; - - segment->npolys = 1; - segment->polys = &polys[n]; - segment->material = segment->polys->material; - - s->GetSegments().append(segment); - } - } - - s->BuildHull(); - m->GetSurfaces().append(s); - - *pnverts = nverts; - *pnpolys = npolys; - *pradius = (float) radius; - - result = nverts && npolys; - } - - return result; -} - -// +--------------------------------------------------------------------+ - -bool -ModelFileMAG::LoadMag6(FILE* fp, Model* m, double scale) -{ - bool result = false; - int i = 0; - int ntex = 0; - int nmtls = 0; - int nsurfs = 0; - double radius = 0; - List textures; - - fread(&ntex, sizeof(ntex), 1, fp); // size of texture block - fread(&nmtls, sizeof(nmtls), 1, fp); // number of materials - fread(&nsurfs, sizeof(nsurfs), 1, fp); // number of surfaces - - // read texture list: - if (ntex) { - char* buffer = new char[ntex]; - char* p = buffer; - Bitmap* bmp = 0; - - fread(buffer, ntex, 1, fp); - - while (p < buffer + ntex) { - LoadTexture(p, bmp, Bitmap::BMP_SOLID); - textures.append(bmp); - - p += strlen(p) + 1; - } - - delete [] buffer; - } - - for (i = 0; i < nmtls; i++) { - MaterialMag6 m6; - Material* mtl = new Material; - - fread(&m6, sizeof(m6), 1, fp); - - if (mtl) { - CopyMemory(mtl->name, m6.name, Material::NAMELEN); - CopyMemory(mtl->shader, m6.shader, Material::NAMELEN); - - mtl->ambient_value = m6.ambient_value; - mtl->ambient_color = m6.ambient_color; - mtl->diffuse_value = m6.diffuse_value; - mtl->diffuse_color = m6.diffuse_color; - mtl->specular_value = m6.specular_value; - mtl->specular_color = m6.specular_color; - mtl->emissive_value = m6.emissive_value; - mtl->emissive_color = m6.emissive_color; - - mtl->Ka = ColorValue(mtl->ambient_color) * mtl->ambient_value; - mtl->Kd = ColorValue(mtl->diffuse_color) * mtl->diffuse_value; - mtl->Ks = ColorValue(mtl->specular_color) * mtl->specular_value; - mtl->Ke = ColorValue(mtl->emissive_color) * mtl->emissive_value; - - mtl->power = m6.power; - mtl->brilliance = m6.brilliance; - mtl->bump = m6.bump; - mtl->blend = m6.blend; - mtl->shadow = m6.shadow; - mtl->luminous = m6.luminous; - - if (m6.tex_diffuse && m6.tex_diffuse <= textures.size()) - mtl->tex_diffuse = textures[m6.tex_diffuse - 1]; - - if (m6.tex_specular && m6.tex_specular <= textures.size()) - mtl->tex_specular = textures[m6.tex_specular - 1]; - - if (m6.tex_emissive && m6.tex_emissive <= textures.size()) - mtl->tex_emissive = textures[m6.tex_emissive - 1]; - - if (m6.tex_bumpmap && m6.tex_bumpmap <= textures.size()) - mtl->tex_bumpmap = textures[m6.tex_bumpmap - 1]; - - m->GetMaterials().append(mtl); - } - } - - for (i = 0; i < nsurfs; i++) { - int nverts = 0; - int npolys = 0; - BYTE namelen = 0; - char name[128]; - - fread(&nverts, 4, 1, fp); - fread(&npolys, 4, 1, fp); - fread(&namelen, 1, 1, fp); - fread(name, 1, namelen, fp); - - Surface* surface = new Surface; - surface->SetName(name); - surface->CreateVerts(nverts); - surface->CreatePolys(npolys); - - VertexSet* vset = surface->GetVertexSet(); - Poly* polys = surface->GetPolys(); - - ZeroMemory(polys, sizeof(Poly) * npolys); - - // read vertex set: - for (int v = 0; v < nverts; v++) { - fread(&vset->loc[v], sizeof(float), 3, fp); - fread(&vset->nrm[v], sizeof(float), 3, fp); - fread(&vset->tu[v], sizeof(float), 1, fp); - fread(&vset->tv[v], sizeof(float), 1, fp); - - double d = vset->loc[v].length(); - if (d > radius) - radius = d; - } - - // read polys: - for (int n = 0; n < npolys; n++) { - Poly& poly = polys[n]; - BYTE poly_nverts = 0; - BYTE material_index = 0; - WORD poly_verts[8]; - - fread(&poly_nverts, sizeof(BYTE), 1, fp); - fread(&material_index, sizeof(BYTE), 1, fp); - fread(&poly_verts[0], sizeof(WORD), poly_nverts, fp); - - if (poly_nverts >= 3) { - poly.nverts = poly_nverts; - - for (int i = 0; i < poly_nverts; i++) { - poly.verts[i] = poly_verts[i]; - } - } - else { - poly.sortval = 666; - } - - if (material_index > 0) { - poly.material = m->GetMaterials()[material_index-1]; - poly.sortval = material_index; - } - else if (m->NumMaterials()) { - poly.material = m->GetMaterials().first(); - poly.sortval = 1; - } - else { - poly.sortval = 1000; - } - - if (poly.nverts == 3) - surface->AddIndices(3); - - else if (poly.nverts == 4) - surface->AddIndices(6); - - poly.vertex_set = vset; - poly.plane = Plane(vset->loc[poly.verts[0]], - vset->loc[poly.verts[2]], - vset->loc[poly.verts[1]]); - } - - // sort the polys by material index: - qsort((void*) polys, npolys, sizeof(Poly), mcomp); - - // then assign them to cohesive segments: - Segment* segment = 0; - - for (int n = 0; n < npolys; n++) { - if (segment && segment->material == polys[n].material) { - segment->npolys++; - } - else { - segment = 0; - } - - if (!segment) { - segment = new Segment; - - segment->npolys = 1; - segment->polys = &polys[n]; - segment->material = segment->polys->material; - - surface->GetSegments().append(segment); - } - } - - surface->ComputeTangents(); - surface->BuildHull(); - m->GetSurfaces().append(surface); - - *pnverts = nverts; - *pnpolys = npolys; - *pradius = (float) radius; - - result = nverts && npolys; - } - - return result; -} - diff --git a/MagicEx/ModelFileMAG.h b/MagicEx/ModelFileMAG.h deleted file mode 100644 index 27466ae..0000000 --- a/MagicEx/ModelFileMAG.h +++ /dev/null @@ -1,31 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - File loader for MAG format models -*/ - -#pragma once - -#include - - -class ModelFileMAG : public ModelFile -{ -public: - ModelFileMAG(const char* fname); - virtual ~ModelFileMAG(); - - virtual bool Load(Model* m, double scale=1.0); - virtual bool Save(Model* m); - -protected: - virtual bool LoadMag5(FILE* fp, Model* m, double scale); - virtual bool LoadMag6(FILE* fp, Model* m, double scale); -}; diff --git a/MagicEx/ModelFileOBJ.cpp b/MagicEx/ModelFileOBJ.cpp deleted file mode 100644 index c682bff..0000000 --- a/MagicEx/ModelFileOBJ.cpp +++ /dev/null @@ -1,788 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - File loader for Wavefront/OBJ format models -*/ - -#include "ModelFileOBJ.h" - -#include -#include -#include -#include - -#include "MagicLoad.h" - -// +--------------------------------------------------------------------+ - -ModelFileOBJ::ModelFileOBJ(const char* fname) - : ModelFile(fname) -{ -} - -ModelFileOBJ::~ModelFileOBJ() -{ -} - -// +--------------------------------------------------------------------+ - -const int MAX_OBJ_FACE_VERTS = 32; - -struct ObjFace { - int v[MAX_OBJ_FACE_VERTS]; - int n[MAX_OBJ_FACE_VERTS]; - int t[MAX_OBJ_FACE_VERTS]; - - int nverts; - - ObjFace() { - nverts = 0; - - for (int i = 0; i < MAX_OBJ_FACE_VERTS; i++) - v[i] = n[i] = t[i] = 0; - } - - ObjFace(const ObjFace& f) { - nverts = f.nverts; - - for (int i = 0; i < MAX_OBJ_FACE_VERTS; i++) { - v[i] = f.v[i]; - n[i] = f.n[i]; - t[i] = f.t[i]; - } - } - - void ReverseOrder() { - int i, tmp[MAX_OBJ_FACE_VERTS]; - - for (i = 0; i < nverts; i++) tmp[i] = v[i]; - for (i = 0; i < nverts; i++) v[i] = tmp[nverts-1-i]; - - for (i = 0; i < nverts; i++) tmp[i] = n[i]; - for (i = 0; i < nverts; i++) n[i] = tmp[nverts-1-i]; - - for (i = 0; i < nverts; i++) tmp[i] = t[i]; - for (i = 0; i < nverts; i++) t[i] = tmp[nverts-1-i]; - } -}; - -static void ParsePoly(const char* line, ObjFace* face) -{ - int v[MAX_OBJ_FACE_VERTS]; - int n[MAX_OBJ_FACE_VERTS]; - int t[MAX_OBJ_FACE_VERTS]; - - for (int i = 0; i < MAX_OBJ_FACE_VERTS; i++) { - v[i] = n[i] = t[i] = 0; - } - - const char* p = line + 1; - - while (isspace(*p)) - p++; - - int i = 0; - while (*p && i < MAX_OBJ_FACE_VERTS) { - int factor = 1; - - if (*p == '-') { - factor = -1; - p++; - } - - while (isdigit(*p)) { - v[i] = v[i]*10 + *p - '0'; - p++; - } - v[i] *= factor; - - if (*p == '/') { p++; // slash one - - factor = 1; - - if (*p == '-') { - factor = -1; - p++; - } - - while (isdigit(*p)) { - t[i] = t[i]*10 + *p - '0'; - p++; - } - t[i] *= factor; - - if (*p == '/') { p++; // slash two - - factor = 1; - - if (*p == '-') { - factor = -1; - p++; - } - - while (isdigit(*p)) { - n[i] = n[i]*10 + *p - '0'; - p++; - } - n[i] *= factor; - }} - - while (isspace(*p)) p++; - i++; - } - - face->nverts = i; - - for (i = 0; i < MAX_OBJ_FACE_VERTS; i++) { - face->v[i] = v[i]; - face->n[i] = n[i]; - face->t[i] = t[i]; - } -} - -static int LoadMatls(const char* lpszPathName, Model* m) -{ - int nmatls = 0; - - FILE* fp = fopen(lpszPathName, "r"); - if (!fp) { - ::MessageBox(0, "Open Failed: could not open file", "ERROR", MB_OK); - return 0; - } - - Material* mtl = 0; - - while (!feof(fp)) { - char raw_line[512]; - char line[512]; - fgets(raw_line, 512, fp); - - strcpy(line, Text(raw_line).trim().data()); - - if (strstr(line, "newmtl")) { - mtl = new Material; - strncpy(mtl->name, line+7, Material::NAMELEN - 1); - - m->GetMaterials().append(mtl); - } - - else if (line[0] == 'K' && line[1] == 'a') { - float r,g,b; - sscanf(line, "Ka %f %f %f", &r, &g, &b); - mtl->Ka = ColorValue(r,g,b); - - mtl->ambient_value = 1.0f; - mtl->ambient_color = mtl->Ka.ToColor(); - } - - else if (line[0] == 'K' && line[1] == 'd') { - float r,g,b; - sscanf(line, "Kd %f %f %f", &r, &g, &b); - mtl->Kd = ColorValue(r,g,b); - - mtl->diffuse_value = 1.0f; - mtl->diffuse_color = mtl->Kd.ToColor(); - } - - else if (line[0] == 'K' && line[1] == 's') { - float r,g,b; - sscanf(line, "Ks %f %f %f", &r, &g, &b); - mtl->Ks = ColorValue(r,g,b); - - mtl->specular_value = 1.0f; - mtl->specular_color = mtl->Ks.ToColor(); - } - - else if (line[0] == 'N' && line[1] == 's') { - float ns; - sscanf(line, "Ns %f", &ns); - mtl->power = ns; - } - - else if (strstr(line, "map_Kd")) { - const char* src = strstr(line, "map_Kd") + 7; - while (!isalnum(*src)) src++; - - LoadTexture(src, mtl->tex_diffuse, Bitmap::BMP_SOLID); - } - } - - fclose(fp); - return nmatls; -} - -static Material* FindMatl(const char* mtl_name, Model* model) -{ - ListIter iter = model->GetMaterials(); - while (++iter) { - Material* m = iter.value(); - if (!strcmp(m->name, mtl_name)) - return m; - } - - return 0; -} - -// +--------------------------------------------------------------------+ - -static int mcomp(const void* a, const void* b) -{ - Poly* pa = (Poly*) a; - Poly* pb = (Poly*) b; - - if (pa->sortval == pb->sortval) - return 0; - - if (pa->sortval < pb->sortval) - return 1; - - return -1; -} - -bool -ModelFileOBJ::Load(Model* m, double scale) -{ - if (m && scale > 0 && strlen(filename) > 0) { - ModelFile::Load(m, scale); - - FILE* fp = fopen(filename, "rb"); - - if (fp == NULL) { - ::MessageBox(0, "Wavefront/OBJ Import Failed: Unable to open file", "ERROR", MB_OK); - return false; - } - - // ok, now start reading the data: - int ntex = 0; - int nverts = 0; - int npv = 0; - int npolys = 0; - int vi = 0; - int vn = 0; - int vt = 0; - - char root_path[256]; - ZeroMemory(root_path, 256); - - if (strrchr(filename, '\\')) { - strcpy(root_path, filename); - char* p = strrchr(root_path, '\\'); - if (p) - *(p+1) = 0; - } - - else if (strrchr(filename, '/')) { - strcpy(root_path, filename); - char* p = strrchr(root_path, '/'); - if (p) - *(p+1) = 0; - } - - // count verts and polys: - while (!feof(fp)) { - char line[256]; - fgets(line, 255, fp); - - if (line[0] == 'v') { - switch (line[1]) { - case ' ': vi++; break; - case 'n': vn++; break; - case 't': vt++; break; - } - } - - else if (line[0] == 'f' && line[1] == ' ') { - npolys++; - - ObjFace f; - ParsePoly(line, &f); - f.ReverseOrder(); - - npv += f.nverts; - } - - else if (strstr(line, "mtllib")) { - const char* libname = strstr(line, "mtllib"); - libname += 7; - while (isspace(*libname)) - libname++; - - char libpath[256]; - strcpy(libpath, root_path); - strcat(libpath, libname); - int n = strlen(libpath); - if (n > 0) { - char* p = &libpath[n-1]; - while (isspace(*p)) *p-- = 0; - } - - int nmatls = LoadMatls(libpath, model); - } - } - - nverts = npv; - if (vi > nverts) nverts = vi; - if (vn > nverts) nverts = vn; - if (vt > nverts) nverts = vt; - - if (nverts > Model::MAX_VERTS || npolys > Model::MAX_POLYS) { - ::MessageBox(0, "Wavefront/OBJ Import Failed: model is too complicated (too many vertices/polys)!", "ERROR", MB_OK); - return false; - } - - vi = 0; - vn = 0; - vt = 0; - - fseek(fp, 0, SEEK_SET); - - Surface* surface = new Surface; - m->GetSurfaces().append(surface); - - surface->CreateVerts(nverts); - surface->CreatePolys(npolys); - - VertexSet* vset = surface->GetVertexSet(); - Poly* polys = surface->GetPolys(); - - // read vertex set: - Vec3* vloc = new Vec3[nverts]; - Vec3* vnrm = new Vec3[nverts]; - float* vtu = new float[nverts]; - float* vtv = new float[nverts]; - - float radius = 0; - - while (!feof(fp)) { - char line[256]; - fgets(line, 255, fp); - - if (line[0] == 'v') { - if (line[1] == ' ') { - const char* p = line + 2; - - while (isspace(*p)) p++; - sscanf(p, "%f", &vloc[vi].x); - - while (!isspace(*p)) p++; - while (isspace(*p)) p++; - sscanf(p, "%f", &vloc[vi].y); - - while (!isspace(*p)) p++; - while (isspace(*p)) p++; - sscanf(p, "%f", &vloc[vi].z); - - float d = vloc[vi].length(); - if (d > radius) - radius = d; - - vi++; - } - - else if (line[1] == 'n') { - const char* p = line + 2; - - while (isspace(*p)) p++; - sscanf(p, "%f", &vnrm[vn].x); - - while (!isspace(*p)) p++; - while (isspace(*p)) p++; - sscanf(p, "%f", &vnrm[vn].y); - - while (!isspace(*p)) p++; - while (isspace(*p)) p++; - sscanf(p, "%f", &vnrm[vn].z); - - vn++; - } - - else if (line[1] == 't') { - const char* p = line + 2; - - while (isspace(*p)) p++; - sscanf(p, "%f", &vtu[vt]); - - while (!isspace(*p)) p++; - while (isspace(*p)) p++; - sscanf(p, "%f", &vtv[vt]); - - vtv[vt] = 1.0f - vtv[vt]; - - vt++; - } - } - } - - fseek(fp, 0, SEEK_SET); - - // read polys: - int poly = 0; - char line[256]; - ObjFace face; - Material* material = 0; - int mtl_index = 0; - - if (m->NumMaterials()) - material = m->GetMaterials().first(); - - int current_v = 1; - int current_vn = 1; - int current_vt = 1; - int v = 0; // vset index pointer - - while (!feof(fp)) { - char raw_line[256]; - fgets(raw_line, 256, fp); - - strcpy(line, Text(raw_line).trim().data()); - - if (strstr(line, "usemtl")) { - material = FindMatl(line + 7, model); - ListIter iter = model->GetMaterials(); - while (++iter) - if (material == iter.value()) - mtl_index = iter.index(); - } - - else if (line[0] == 'v') { - if (line[1] == ' ') current_v++; - else if (line[1] == 'n') current_vn++; - else if (line[1] == 't') current_vt++; - } - - else if (line[0] == 'f') { - ParsePoly(line, &face); - face.ReverseOrder(); - - for (int n = 0; n < face.nverts; n++) { - if (face.v[n] < 0) - face.v[n] += current_v; - - if (face.n[n] < 0) - face.n[n] += current_vn; - - if (face.t[n] < 0) - face.t[n] += current_vt; - } - - if (face.nverts > 4) { - npolys += face.nverts - 3; - - for (int tri = 2; tri < face.nverts; tri++) { - Poly* p = polys + poly; - poly++; - - p->nverts = 3; - - vset->loc[v+0] = vloc[face.v[ tri ] -1]; - vset->loc[v+1] = vloc[face.v[ tri-1 ] -1]; - vset->loc[v+2] = vloc[face.v[ 0] -1]; - - if (face.n[0] > 0) { - vset->nrm[v+0] = vnrm[face.n[ tri ] -1]; - vset->nrm[v+1] = vnrm[face.n[ tri-1 ] -1]; - vset->nrm[v+2] = vnrm[face.n[ 0] -1]; - } - else { - vset->nrm[v+0] = vloc[v+0]; vset->nrm[v+0].Normalize(); - vset->nrm[v+1] = vloc[v+1]; vset->nrm[v+1].Normalize(); - vset->nrm[v+2] = vloc[v+2]; vset->nrm[v+2].Normalize(); - } - - if (face.t[0] > 0) { - vset->tu[v+0] = vtu[face.t[ tri ] -1]; - vset->tv[v+0] = vtv[face.t[ tri ] -1]; - vset->tu[v+1] = vtu[face.t[ tri-1 ] -1]; - vset->tv[v+1] = vtv[face.t[ tri-1 ] -1]; - vset->tu[v+2] = vtu[face.t[ 0 ] -1]; - vset->tv[v+2] = vtv[face.t[ 0 ] -1]; - } - - p->verts[0] = v+0; - p->verts[1] = v+1; - p->verts[2] = v+2; - - p->material = material; - p->sortval = mtl_index; - p->vertex_set = vset; - - p->plane = Plane(vset->loc[p->verts[0]], - vset->loc[p->verts[2]], - vset->loc[p->verts[1]]); - - v += p->nverts; - } - } - - else if (face.nverts == 3 || face.nverts == 4) { - Poly* p = polys + poly; - poly++; - - p->nverts = face.nverts; - - bool flat = true; - int first = v; - - for (int i = 0; i < p->nverts; i++) { - int face_index = i; - - vset->loc[v] = vloc[face.v[face_index]-1]; - - if (face.n[face_index] > 0) - vset->nrm[v] = vnrm[face.n[face_index]-1]; - else - vset->nrm[v] = vset->loc[v]; - - vset->nrm[v].Normalize(); - - if (vset->nrm[v] != vset->nrm[first]) - flat = false; - - if (face.t[face_index] > 0) { - vset->tu[v] = vtu [face.t[face_index]-1]; - vset->tv[v] = vtv [face.t[face_index]-1]; - } - - p->verts[i] = v++; - } - - p->material = material; - p->sortval = mtl_index; - p->flatness = flat ? 1.0f : 0.0f; - - if (p->nverts == 3) - surface->AddIndices(3); - - else if (p->nverts == 4) - surface->AddIndices(6); - - p->vertex_set = vset; - p->plane = Plane(vset->loc[p->verts[0]], - vset->loc[p->verts[2]], - vset->loc[p->verts[1]]); - } - - if (poly >= npolys) - break; - } - } - - // sort the polys by material index: - qsort((void*) polys, npolys, sizeof(Poly), mcomp); - - // then assign them to cohesive segments: - Segment* segment = 0; - - for (int n = 0; n < npolys; n++) { - if (segment && segment->material == polys[n].material) { - segment->npolys++; - } - else { - segment = 0; - } - - if (!segment) { - segment = new Segment; - - segment->npolys = 1; - segment->polys = &polys[n]; - segment->material = segment->polys->material; - - surface->GetSegments().append(segment); - } - } - - delete [] vloc; - delete [] vnrm; - delete [] vtu; - delete [] vtv; - - *pnverts = nverts; - *pnpolys = npolys; - *pradius = radius; - - fclose(fp); - - m->Normalize(); - return true; - } - - return false; -} - -// +--------------------------------------------------------------------+ - -static bool -SaveWaveFrontMatLib(const char* path, const char* root, Model* model) -{ - char filename[256]; - sprintf(filename, "%s%s.mtl", path, root); - - FILE* f = fopen(filename, "w"); - if (f) { - fprintf(f, "#\n# %s Material Library exported by Magic 2.0\n#\n\n", root); - - ListIter iter = model->GetMaterials(); - while (++iter) { - Material* mtl = iter.value(); - - fprintf(f, "newmtl %s\n", mtl->name); - fprintf(f, "Ka %.5f %.5f %.5f\n", mtl->Ka.Red(), mtl->Ka.Green(), mtl->Ka.Blue()); - fprintf(f, "Kd %.5f %.5f %.5f\n", mtl->Kd.Red(), mtl->Kd.Green(), mtl->Kd.Blue()); - fprintf(f, "Ks %.5f %.5f %.5f\n", mtl->Ks.Red(), mtl->Ks.Green(), mtl->Ks.Blue()); - fprintf(f, "Ns %.5f\n", mtl->power); - fprintf(f, "illum 2\n"); - if (mtl->tex_diffuse) - fprintf(f, "map_Kd %s\n", mtl->tex_diffuse->GetFilename()); - fprintf(f, "\n"); - } - - fclose(f); - return true; - } - - return false; -} - -bool -ModelFileOBJ::Save(Model* m) -{ - if (m) { - ModelFile::Save(m); - char pathname[256]; - char rootname[256]; - - ZeroMemory(pathname, sizeof(pathname)); - ZeroMemory(rootname, sizeof(rootname)); - - const char* ext = strstr(filename, ".obj"); - if (!ext) - ext = strstr(filename, ".OBJ"); - - const char* sep = strrchr(filename, '/'); - if (!sep) - sep = strrchr(filename, '\\'); - - const char* src = filename; - char* dst = pathname; - - if (sep) { - while (src != sep) - *dst++ = *src++; - *dst++ = *src++; - } - - if (ext) { - dst = rootname; - while (src != ext) - *dst++ = *src++; - } - else { - strcpy(rootname, src); - } - - strcpy(filename, pathname); - strcat(filename, rootname); - strcat(filename, ".obj"); - - FILE* f = fopen(filename, "w"); - if (!f) { - ::MessageBox(0, "Export Failed: Magic could not open the file for writing", "ERROR", MB_OK); - return false; - } - - fprintf(f, "# Wavefront OBJ exported by Magic 2.0\n\n"); - fprintf(f, "mtllib %s.mtl\n", rootname); - - ListIter s_iter = m->GetSurfaces(); - - // vertex locations - fprintf(f, "\n# VERTEX LOCATIONS: %d\n", m->NumVerts()); - while (++s_iter) { - Surface* s = s_iter.value(); - VertexSet* vset = s->GetVertexSet(); - - for (int n = 0; n < vset->nverts; n++) { - fprintf(f, "v %12.5f %12.5f %12.5f\n", - vset->loc[n].x, - vset->loc[n].y, - vset->loc[n].z); - } - } - - s_iter.reset(); - - // vertex normals - fprintf(f, "\n# VERTEX NORMALS: %d\n", m->NumVerts()); - while (++s_iter) { - Surface* s = s_iter.value(); - VertexSet* vset = s->GetVertexSet(); - - for (int n = 0; n < vset->nverts; n++) { - fprintf(f, "vn %8.3f %8.3f %8.3f\n", - vset->nrm[n].x, - vset->nrm[n].y, - vset->nrm[n].z); - } - } - - s_iter.reset(); - - // texture coordinates - fprintf(f, "\n# TEXTURE COORDINATES: %d\n", m->NumVerts()); - while (++s_iter) { - Surface* s = s_iter.value(); - VertexSet* vset = s->GetVertexSet(); - - for (int n = 0; n < vset->nverts; n++) { - fprintf(f, "vt %8.3f %8.3f\n", - vset->tu[n], 1 - vset->tv[n]); - } - } - - s_iter.reset(); - - // faces - Material* current_material = 0; - - fprintf(f, "\n# FACES: %d\n", m->NumPolys()); - while (++s_iter) { - Surface* s = s_iter.value(); - - for (int n = 0; n < s->NumPolys(); n++) { - const Poly* p = s->GetPolys() + n; - int nv = p->nverts; - Material* mtl = p->material; - - if (current_material != mtl) { - fprintf(f, "\n\nusemtl %s\n", mtl->name); - current_material = mtl; - } - - fprintf(f, "\nf "); - for (int v = nv-1; v >= 0; v--) { - fprintf(f, "%d/%d/%d ", - p->verts[v] + 1, - p->verts[v] + 1, - p->verts[v] + 1); - } - } - } - - fprintf(f, "\n\n\n# END OF FILE.\n"); - fclose(f); - - return SaveWaveFrontMatLib(pathname, rootname, m); - } - - return false; -} - -// +--------------------------------------------------------------------+ diff --git a/MagicEx/ModelFileOBJ.h b/MagicEx/ModelFileOBJ.h deleted file mode 100644 index dcffc7d..0000000 --- a/MagicEx/ModelFileOBJ.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - File loader for Wavefront/OBJ format models -*/ - -#pragma once - -#include - - -class ModelFileOBJ : public ModelFile -{ -public: - ModelFileOBJ(const char* fname); - virtual ~ModelFileOBJ(); - - virtual bool Load(Model* m, double scale=1.0); - virtual bool Save(Model* m); -}; diff --git a/MagicEx/include/MagicLoad.h b/MagicEx/include/MagicLoad.h new file mode 100644 index 0000000..f447945 --- /dev/null +++ b/MagicEx/include/MagicLoad.h @@ -0,0 +1,23 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. +*/ + +#pragma once + +#include + +#include + +#include +#include + + +int LoadBuffer(const char* filename, BYTE*& buf, bool null_terminate=false); +int LoadTexture(const char* name, Bitmap*& bmp, int type=0); +int LoadAlpha(const char* name, Bitmap& bitmap, int type=0); +template bool ImportInto(const std::filesystem::path& pathname, Solid* target); + + +#include "MagicLoad.inl.h" diff --git a/MagicEx/include/MagicLoad.inl.h b/MagicEx/include/MagicLoad.inl.h new file mode 100644 index 0000000..e32ba60 --- /dev/null +++ b/MagicEx/include/MagicLoad.inl.h @@ -0,0 +1,32 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. +*/ + +#include + +#include + + +template +bool +ImportInto(const char* pathname, Solid* target) +{ + ModelLoader loader {pathname}; + if (auto* original = target->GetModel()) { + auto solid = std::make_unique(); + if (!solid->Load(&loader)) + return false; + auto* imported = solid->GetModel(); + original->GetMaterials().append(imported->GetMaterials()); + original->GetSurfaces().append(imported->GetSurfaces()); + original->OptimizeMaterials(); + imported->GetMaterials().clear(); + imported->GetSurfaces().clear(); + return true; + } + return target->Load(&loader); +} + + diff --git a/MagicEx/include/ModelFile3DS.h b/MagicEx/include/ModelFile3DS.h new file mode 100644 index 0000000..e539d17 --- /dev/null +++ b/MagicEx/include/ModelFile3DS.h @@ -0,0 +1,27 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + File loader for 3DStudio MAX 3DS format models +*/ + +#pragma once + +#include + + +class ModelFile3DS : public ModelFile +{ +public: + ModelFile3DS(const char* fname); + virtual ~ModelFile3DS(); + + virtual bool Load(Model* m, double scale=1.0); + virtual bool Save(Model* m); +}; diff --git a/MagicEx/include/ModelFileMAG.h b/MagicEx/include/ModelFileMAG.h new file mode 100644 index 0000000..e11d81c --- /dev/null +++ b/MagicEx/include/ModelFileMAG.h @@ -0,0 +1,31 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + File loader for MAG format models +*/ + +#pragma once + +#include + + +class ModelFileMAG : public ModelFile +{ +public: + ModelFileMAG(const char* fname); + virtual ~ModelFileMAG(); + + virtual bool Load(Model* m, double scale=1.0); + virtual bool Save(Model* m); + +protected: + virtual bool LoadMag5(FILE* fp, Model* m, double scale); + virtual bool LoadMag6(FILE* fp, Model* m, double scale); +}; diff --git a/MagicEx/include/ModelFileOBJ.h b/MagicEx/include/ModelFileOBJ.h new file mode 100644 index 0000000..3d9089c --- /dev/null +++ b/MagicEx/include/ModelFileOBJ.h @@ -0,0 +1,27 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + File loader for Wavefront/OBJ format models +*/ + +#pragma once + +#include + + +class ModelFileOBJ : public ModelFile +{ +public: + ModelFileOBJ(const char* fname); + virtual ~ModelFileOBJ(); + + virtual bool Load(Model* m, double scale=1.0); + virtual bool Save(Model* m); +}; diff --git a/MagicEx/src/MagicLoad.cpp b/MagicEx/src/MagicLoad.cpp new file mode 100644 index 0000000..6687f8b --- /dev/null +++ b/MagicEx/src/MagicLoad.cpp @@ -0,0 +1,178 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo +*/ + +#include + +#include +#include +#include +#include +#include + + +int LoadBuffer(const char* filename, BYTE*& buf, bool null_terminate) +{ + buf = 0; + + FILE* f = ::fopen(filename, "rb"); + + if (f) { + ::fseek(f, 0, SEEK_END); + int len = ftell(f); + ::fseek(f, 0, SEEK_SET); + + if (null_terminate) { + buf = new BYTE[len+1]; + if (buf) + buf[len] = 0; + } + + else { + buf = new BYTE[len]; + } + + if (buf) + ::fread(buf, len, 1, f); + + ::fclose(f); + + return len; + } + + return 0; +} + + +int LoadTexture(const char* fname, Bitmap*& bitmap, int type) +{ + int result = 0; + + if (!fname || !*fname) + return result; + + bitmap = Bitmap::CheckCache(fname); + + if (!bitmap) { + bool pcx_file = strstr(fname, ".pcx") || strstr(fname, ".PCX"); + + // handle PCX formats: + if (pcx_file) { + PcxImage pcx; + + if (pcx.Load((char*) fname) == PCX_OK) { + bitmap = new Bitmap; + + // 32-bit image + if (pcx.himap) { + bitmap->CopyHighColorImage(pcx.width, pcx.height, pcx.himap); + } + + // 8-bit image, check for 32-bit image as well + else if (pcx.bitmap) { + bitmap->CopyImage(pcx.width, pcx.height, pcx.bitmap); + + char tmp[256]; + int len = strlen(fname); + bool found = false; + + ZeroMemory(tmp, sizeof(tmp)); + + for (int i = 0; i < len && !found; i++) { + if (strstr(fname + i, ".pcx") == (fname+i)) { + found = true; + } + else { + tmp[i] = fname[i]; + } + } + + if (found) { + strcat_s(tmp, "+.pcx"); + if (pcx.Load(tmp) == PCX_OK && pcx.himap != 0) { + bitmap->CopyHighColorImage(pcx.width, pcx.height, pcx.himap); + } + } + } + } + } + + // for all other formats, use D3DX: + else { + D3DXImage d3dx; + if (d3dx.Load((char*) fname)) { + bitmap = new Bitmap; + bitmap->CopyHighColorImage(d3dx.width, d3dx.height, d3dx.image); + } + } + + if (bitmap) { + LoadAlpha(fname, *bitmap, type); + + bitmap->SetFilename(fname); + bitmap->SetType(type); + bitmap->MakeTexture(); + + Bitmap::AddToCache(bitmap); + } + } + + return result; +} + +int LoadAlpha(const char* name, Bitmap& bitmap, int type) +{ + PcxImage pcx; + D3DXImage d3dx; + bool pcx_file = strstr(name, ".pcx") || strstr(name, ".PCX"); + bool bmp_file = strstr(name, ".bmp") || strstr(name, ".BMP"); + bool jpg_file = strstr(name, ".jpg") || strstr(name, ".JPG"); + bool png_file = strstr(name, ".png") || strstr(name, ".PNG"); + bool tga_file = strstr(name, ".tga") || strstr(name, ".TGA"); + + // check for an associated alpha-only (grayscale) bitmap: + char filename[256]; + strcpy_s(filename, name); + + char* dot = strrchr(filename, '.'); + if (dot && pcx_file) + strcpy(dot, "@.pcx"); + else if (dot && bmp_file) + strcpy(dot, "@.bmp"); + else if (dot && jpg_file) + strcpy(dot, "@.jpg"); + else if (dot && png_file) + strcpy(dot, "@.png"); + else if (dot && tga_file) + strcpy(dot, "@.tga"); + else + return 0; + + // first try to load from current directory: + bool loaded = false; + + if (pcx_file) + loaded = pcx.Load(filename) == PCX_OK; + + else + loaded = d3dx.Load(filename); + + // now copy the alpha values into the bitmap: + if (loaded) { + if (pcx_file && pcx.bitmap) { + bitmap.CopyAlphaImage(pcx.width, pcx.height, pcx.bitmap); + } + else if (pcx_file && pcx.himap) { + bitmap.CopyAlphaRedChannel(pcx.width, pcx.height, pcx.himap); + } + else if (d3dx.image) { + bitmap.CopyAlphaRedChannel(d3dx.width, d3dx.height, d3dx.image); + } + } + + return 0; +} diff --git a/MagicEx/src/ModelFile3DS.cpp b/MagicEx/src/ModelFile3DS.cpp new file mode 100644 index 0000000..aca54de --- /dev/null +++ b/MagicEx/src/ModelFile3DS.cpp @@ -0,0 +1,281 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + File loader for 3DStudio MAX 3DS format models +*/ + +#include + +#include +#include +#include +#include +#include + +#include + +// +--------------------------------------------------------------------+ + +ModelFile3DS::ModelFile3DS(const char* fname) + : ModelFile(fname) +{ +} + +ModelFile3DS::~ModelFile3DS() +{ +} + +// +--------------------------------------------------------------------+ + +static int mcomp(const void* a, const void* b) +{ + Poly* pa = (Poly*) a; + Poly* pb = (Poly*) b; + + if (pa->sortval == pb->sortval) + return 0; + + if (pa->sortval < pb->sortval) + return 1; + + return -1; +} + +bool +ModelFile3DS::Load(Model* model, double scale) +{ + if (model && scale > 0 && strlen(filename) > 0) { + ModelFile::Load(model, scale); + + L3DS loader; + + if (!loader.LoadFile(filename)) { + ::MessageBox(0, "3DS Import Failed: Magic could not open the file for reading", "ERROR", MB_OK); + return false; + } + + int ntex = 0; + int nsurfs = 0; + int nverts = 0; + int npolys = 0; + int nmatls = 0; + int nmeshs = loader.GetMeshCount(); + + int* mesh_verts = new int[nmeshs]; + + for (int m = 0; m < nmeshs; m++) { + LMesh& mesh = loader.GetMesh(m); + + mesh_verts[m] = nverts; + + // count verts and polys: + nverts += mesh.GetVertexCount(); + npolys += mesh.GetTriangleCount(); + } + + if (nverts > Model::MAX_VERTS || npolys > Model::MAX_POLYS) { + ::MessageBox(0, "3DS Import Failed: model is too complicated (too many vertices/polys)!", "ERROR", MB_OK); + delete [] mesh_verts; + return FALSE; + } + + // get materials: + nmatls = loader.GetMaterialCount(); + + for (int i = 0; i < nmatls; i++) { + LMaterial& matl = loader.GetMaterial(i); + LMap& tex_diff = matl.GetTextureMap1(); + LMap& tex_spec = matl.GetSpecularMap(); + LMap& tex_bump = matl.GetBumpMap(); + LMap& tex_glow = matl.GetTextureMap2(); + + Material* material = new Material; + + strncpy(material->name, matl.GetName().c_str(), Material::NAMELEN); + + material->Ka.Set( matl.GetAmbientColor().r, + matl.GetAmbientColor().g, + matl.GetAmbientColor().b ); + + material->ambient_value = 1.0f; + material->ambient_color = material->Ka.ToColor(); + + material->Kd.Set( matl.GetDiffuseColor().r, + matl.GetDiffuseColor().g, + matl.GetDiffuseColor().b ); + + material->diffuse_value = 1.0f; + material->diffuse_color = material->Kd.ToColor(); + + material->Ks.Set( matl.GetSpecularColor().r, + matl.GetSpecularColor().g, + matl.GetSpecularColor().b ); + + material->specular_value = 1.0f; + material->specular_color = material->Ks.ToColor(); + + material->power = matl.GetShininess() * 100; + + if (tex_diff.mapName[0]) + LoadTexture(tex_diff.mapName, material->tex_diffuse); + + if (tex_spec.mapName[0]) + LoadTexture(tex_spec.mapName, material->tex_specular); + + if (tex_bump.mapName[0]) + LoadTexture(tex_bump.mapName, material->tex_bumpmap); + + if (tex_glow.mapName[0]) + LoadTexture(tex_glow.mapName, material->tex_emissive); + + model->GetMaterials().append(material); + } + + Surface* surface = new Surface; + model->GetSurfaces().append(surface); + + surface->CreateVerts(nverts); + surface->CreatePolys(npolys); + + VertexSet* vset = surface->GetVertexSet(); + Poly* polys = surface->GetPolys(); + float radius = 0; + int v = 0; + + for (int m = 0; m < nmeshs && v < nverts; m++) { + LMesh& mesh = loader.GetMesh(m); + + // read vertex set: + for (int i = 0; i < mesh.GetVertexCount() && v < nverts; i++) { + vset->loc[v].x = mesh.GetVertex(i).x; + vset->loc[v].y = mesh.GetVertex(i).z; + vset->loc[v].z = mesh.GetVertex(i).y; + + vset->nrm[v].x = mesh.GetNormal(i).x; + vset->nrm[v].y = mesh.GetNormal(i).z; + vset->nrm[v].z = mesh.GetNormal(i).y; + + vset->tu[v] = mesh.GetUV(i).x; + vset->tv[v] = mesh.GetUV(i).y; + + float d = vset->loc[v].length(); + if (d > radius) + radius = d; + + v++; + } + } + + if (radius < 16) { + model->ScaleBy(256.0f / radius); + radius = 256.0f; + } + + int n = 0; + + for (int m = 0; m < nmeshs && n < npolys; m++) { + LMesh& mesh = loader.GetMesh(m); + + // read polys: + int mesh_tris = mesh.GetTriangleCount(); + for (int i = 0; i < mesh_tris && n < npolys; i++) { + Poly* p = surface->GetPolys() + n++; + const LTriangle& tri = mesh.GetTriangle(i); + LTriangle2 tri2 = mesh.GetTriangle2(i); + + p->nverts = 3; + + p->verts[0] = tri.a + mesh_verts[m]; + p->verts[1] = tri.b + mesh_verts[m]; + p->verts[2] = tri.c + mesh_verts[m]; + + if (p->verts[0] > nverts || p->verts[1] > nverts || p->verts[2] > nverts) { + p->verts[0] = 0; + p->verts[1] = 1; + p->verts[2] = 2; + } + + if (tri2.vertexNormals[0] == tri2.vertexNormals[1] && + tri2.vertexNormals[0] == tri2.vertexNormals[2]) + p->flatness = 1.0f; + else + p->flatness = 0.0f; + + p->vertex_set = vset; + p->material = model->GetMaterials()[ tri2.materialId ]; + p->sortval = tri2.materialId; + + p->plane = Plane(vset->loc[ p->verts[0] ], + vset->loc[ p->verts[2] ], + vset->loc[ p->verts[1] ]); + + surface->AddIndices(p->nverts); + } + } + + // sort the polys by material index: + qsort((void*) polys, npolys, sizeof(Poly), mcomp); + + // then assign them to cohesive segments: + Segment* segment = 0; + + for (int n = 0; n < npolys; n++) { + if (segment && segment->material == polys[n].material) { + segment->npolys++; + } + else { + segment = 0; + } + + if (!segment) { + segment = new Segment; + + segment->npolys = 1; + segment->polys = &polys[n]; + segment->material = segment->polys->material; + + surface->GetSegments().append(segment); + } + } + + *pnverts = nverts; + *pnpolys = npolys; + *pradius = radius; + + model->Normalize(); + return true; + } + + return false; +} + +// +--------------------------------------------------------------------+ + +bool +ModelFile3DS::Save(Model* m) +{ + if (m) { + ModelFile::Save(m); + + FILE* f = fopen(filename, "w"); + if (!f) { + ::MessageBox(0, "3DS Export Failed: Magic could not open the file for writing", "ERROR", MB_OK); + return false; + } + + fclose(f); + + return true; + } + + return false; +} + +// +--------------------------------------------------------------------+ diff --git a/MagicEx/src/ModelFileMAG.cpp b/MagicEx/src/ModelFileMAG.cpp new file mode 100644 index 0000000..72e6b5b --- /dev/null +++ b/MagicEx/src/ModelFileMAG.cpp @@ -0,0 +1,823 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + File loader for MAG format models +*/ + +#include + +#include +#include +#include + +#include + +// +--------------------------------------------------------------------+ + +struct MaterialMag6 { + char name[Material::NAMELEN]; + char shader[Material::NAMELEN]; + float power; // highlight sharpness (big=shiny) + float brilliance; // diffuse power function + float bump; // bump level (0=none) + DWORD blend; // alpha blend type + bool shadow; // material casts shadow + bool luminous; // verts have their own lighting + + Color ambient_color; + Color diffuse_color; + Color specular_color; + Color emissive_color; + + float ambient_value; + float diffuse_value; + float specular_value; + float emissive_value; + + BYTE tex_diffuse; + BYTE tex_specular; + BYTE tex_bumpmap; + BYTE tex_emissive; +}; + +// +--------------------------------------------------------------------+ + +ModelFileMAG::ModelFileMAG(const char* fname) + : ModelFile(fname) +{ +} + +ModelFileMAG::~ModelFileMAG() +{ +} + +// +--------------------------------------------------------------------+ + +bool +ModelFileMAG::Load(Model* m, double scale) +{ + if (m && scale > 0 && strlen(filename) > 0) { + ModelFile::Load(m, scale); + + bool result = false; + FILE* fp = fopen(filename, "rb"); + + // check MAG file: + if (!fp) { + ::MessageBox(0, "File Open Failed:\nMagic could not open the requested file.", "ERROR", MB_OK); + return result; + } + + ZeroMemory(pname, 64); + strncpy(pname, filename, 63); + + char file_id[5]; + fread(file_id, 4, 1, fp); + file_id[4] = '\0'; + int version = 1; + + if (!strcmp(file_id, "MAG6")) { + version = 6; + } + else if (!strcmp(file_id, "MAG5")) { + version = 5; + } + else if (!strcmp(file_id, "MAG4")) { + version = 4; + } + else { + ::MessageBox(0, "File Open Failed:\nThe requested file uses an invalid format.", "ERROR", MB_OK); + fclose(fp); + return result; + } + + // get ready to load, delete existing model: + m->GetSurfaces().destroy(); + m->GetMaterials().destroy(); + *pnverts = 0; + *pnpolys = 0; + + // now load the model: + switch (version) { + case 4: + case 5: + result = LoadMag5(fp, m, scale); + break; + + case 6: + result = LoadMag6(fp, m, scale); + break; + + default: + break; + } + + fclose(fp); + return true; + } + + return false; +} + +// +--------------------------------------------------------------------+ + +bool +ModelFileMAG::Save(Model* m) +{ + if (m) { + ModelFile::Save(m); + + FILE* fp = fopen(filename, "wb"); + if (!fp) { + ::MessageBox(0, "Save Failed:\nMagic could not open the file for writing.", "ERROR", MB_OK); + return FALSE; + } + + fwrite("MAG6", 4, 1, fp); + + int i = 0; + int ntex = 0; + int nmtls = 0; + int nsurfs = m->NumSurfaces(); + List textures; + + ListIter m_iter = m->GetMaterials(); + while (++m_iter) { + Material* mtl = m_iter.value(); + Bitmap* bmp = mtl->tex_diffuse; + + if (bmp && !textures.contains(bmp)) { + textures.append(bmp); + } + + bmp = mtl->tex_specular; + + if (bmp && !textures.contains(bmp)) { + textures.append(bmp); + } + + bmp = mtl->tex_emissive; + + if (bmp && !textures.contains(bmp)) { + textures.append(bmp); + } + + bmp = mtl->tex_bumpmap; + + if (bmp && !textures.contains(bmp)) { + textures.append(bmp); + } + + nmtls++; + } + + ListIter t_iter = textures; + while (++t_iter) { + Bitmap* bmp = t_iter.value(); + ntex += strlen(bmp->GetFilename()) + 1; + } + + nsurfs = m->GetSurfaces().size(); + + fwrite(&ntex, 4, 1, fp); + fwrite(&nmtls, 4, 1, fp); + fwrite(&nsurfs, 4, 1, fp); + + if (ntex) { + t_iter.reset(); + while (++t_iter) { + Bitmap* bmp = t_iter.value(); + + fwrite(bmp->GetFilename(), + strlen(bmp->GetFilename()) + 1, + 1, + fp); + } + } + + if (nmtls) { + m_iter.reset(); + while (++m_iter) { + Material* mtl = m_iter.value(); + MaterialMag6 m6; + + ZeroMemory(&m6, sizeof(m6)); + + CopyMemory(m6.name, mtl->name, Material::NAMELEN); + CopyMemory(m6.shader, mtl->shader, Material::NAMELEN); + + m6.ambient_value = mtl->ambient_value; + m6.ambient_color = mtl->ambient_color; + m6.diffuse_value = mtl->diffuse_value; + m6.diffuse_color = mtl->diffuse_color; + m6.specular_value = mtl->specular_value; + m6.specular_color = mtl->specular_color; + m6.emissive_value = mtl->emissive_value; + m6.emissive_color = mtl->emissive_color; + + m6.power = mtl->power; + m6.brilliance = mtl->brilliance; + m6.bump = mtl->bump; + m6.blend = mtl->blend; + m6.shadow = mtl->shadow; + m6.luminous = mtl->luminous; + + if (mtl->tex_diffuse) + m6.tex_diffuse = textures.index(mtl->tex_diffuse) + 1; + + if (mtl->tex_specular) + m6.tex_specular = textures.index(mtl->tex_specular) + 1; + + if (mtl->tex_emissive) + m6.tex_emissive = textures.index(mtl->tex_emissive) + 1; + + if (mtl->tex_bumpmap) + m6.tex_bumpmap = textures.index(mtl->tex_bumpmap) + 1; + + fwrite(&m6, sizeof(m6), 1, fp); + } + } + + ListIter s_iter = m->GetSurfaces(); + while (++s_iter) { + Surface* s = s_iter.value(); + + int nverts = s->NumVerts(); + int npolys = s->NumPolys(); + BYTE namelen = strlen(s->Name()) + 1; + + fwrite(&nverts, 4, 1, fp); + fwrite(&npolys, 4, 1, fp); + fwrite(&namelen, 1, 1, fp); + fwrite(s->Name(), 1, namelen, fp); + + VertexSet* vset = s->GetVertexSet(); + Poly* polys = s->GetPolys(); + + // write vertex set: + for (int v = 0; v < nverts; v++) { + fwrite(&vset->loc[v], sizeof(float), 3, fp); + fwrite(&vset->nrm[v], sizeof(float), 3, fp); + fwrite(&vset->tu[v], sizeof(float), 1, fp); + fwrite(&vset->tv[v], sizeof(float), 1, fp); + } + + // write polys: + for (int n = 0; n < npolys; n++) { + Poly& poly = polys[n]; + BYTE poly_nverts = (BYTE) poly.nverts; + BYTE material_index = 0; + WORD poly_verts[8]; + + m_iter.reset(); + while (++m_iter && !material_index) { + if (poly.material == m_iter.value()) + material_index = m_iter.index() + 1; + } + + for (int i = 0; i < poly_nverts; i++) { + poly_verts[i] = poly.verts[i]; + } + + fwrite(&poly_nverts, sizeof(BYTE), 1, fp); + fwrite(&material_index, sizeof(BYTE), 1, fp); + fwrite(&poly_verts[0], sizeof(WORD), poly_nverts, fp); + } + } + + return true; + } + + return false; +} + +// +--------------------------------------------------------------------+ + +struct HomogenousPlane +{ + double distance; + double normal_x; + double normal_y; + double normal_z; + double normal_w; +}; + +static void LoadPlane(Plane& p, FILE* fp) +{ + HomogenousPlane tmp; + fread(&tmp, sizeof(HomogenousPlane), 1, fp); +} + +static void LoadFlags(LPDWORD flags, FILE* fp) +{ + DWORD magic_flags; + fread(&magic_flags, sizeof(DWORD), 1, fp); + + /** MAGIC FLAGS + enum { FLAT_SHADED = 1, + LUMINOUS = 2, + TRANSLUCENT = 4, \\ must swap + CHROMAKEY = 8, // these two + FOREGROUND = 16, -- not used + WIREFRAME = 32, -- not used + SPECULAR1 = 64, + SPECULAR2 = 128 }; + ***/ + + const DWORD magic_mask = 0x0fc3; + + *flags = magic_flags & magic_mask; +} + +// +--------------------------------------------------------------------+ + +static int mcomp(const void* a, const void* b) +{ + Poly* pa = (Poly*) a; + Poly* pb = (Poly*) b; + + if (pa->sortval == pb->sortval) + return 0; + + if (pa->sortval < pb->sortval) + return -1; + + return 1; +} + +bool +ModelFileMAG::LoadMag5(FILE* fp, Model* m, double scale) +{ + bool result = false; + int ntex = 0; + int nsurfs = 0; + double radius = 0; + + fread(&ntex, sizeof(ntex), 1, fp); + fread(&nsurfs, sizeof(nsurfs), 1, fp); + + // create a default gray material: + Material* mtl = new Material; + + if (mtl) { + mtl->Ka = Color::DarkGray; + mtl->Kd = Color::LightGray; + mtl->Ks = ColorValue(0.1f,0.1f,0.1f); + mtl->power = 10.0f; + + mtl->ambient_value = 0.2f; + mtl->ambient_color = Color::DarkGray; + mtl->diffuse_value = 0.8f; + mtl->diffuse_color = Color::LightGray; + mtl->specular_value = 0.5f; + mtl->specular_color = Color::White; + strcpy_s(mtl->name, "(default)"); + + m->GetMaterials().append(mtl); + } + + // read texture list: + for (int i = 0; i < ntex; i++) { + Material* mtl = new Material; + char tname[32]; + + if (mtl) { + mtl->Ka = ColorValue(0.5f,0.5f,0.5f); + mtl->Kd = ColorValue(1.0f,1.0f,1.0f); + mtl->Ks = ColorValue(0.2f,0.2f,0.2f); + mtl->power = 20.0f; + + mtl->ambient_value = 1.0f; + mtl->ambient_color = Color::Gray; + mtl->diffuse_value = 1.0f; + mtl->diffuse_color = Color::White; + mtl->specular_value = 0.2f; + mtl->specular_color = Color::White; + + fread(tname, 32, 1, fp); + LoadTexture(tname, mtl->tex_diffuse, Bitmap::BMP_SOLID); + strcpy_s(mtl->name, tname); + + char* dot = strrchr(mtl->name, '.'); + if (dot) + *dot = 0; + + char* plus = strrchr(mtl->name, '+'); + if (plus) + *plus = 0; + + m->GetMaterials().append(mtl); + } + } + + int nverts = 0; + int npolys = 0; + + fread(&nverts, 4, 1, fp); + fread(&npolys, 4, 1, fp); + + // plan on creating four verts per poly: + int mag_nverts = nverts; + int next_vert = nverts; + + nverts = npolys * 4; + Surface* s = new Surface; + VertexSet* vset = 0; + Poly* polys = 0; + + if (s) { + s->SetName("default"); + s->CreateVerts(nverts); + s->CreatePolys(npolys); + + vset = s->GetVertexSet(); + polys = s->GetPolys(); + + ZeroMemory(vset->loc, nverts * sizeof(Vec3)); + ZeroMemory(vset->diffuse, nverts * sizeof(DWORD)); + ZeroMemory(vset->specular, nverts * sizeof(DWORD)); + ZeroMemory(vset->tu, nverts * sizeof(float)); + ZeroMemory(vset->tv, nverts * sizeof(float)); + ZeroMemory(vset->rw, nverts * sizeof(float)); + + // read vertex set: + int v; + for (v = 0; v < mag_nverts; v++) { + Vec3 vert, norm; + DWORD vstate; + + fread(&vert, sizeof(Vec3), 1, fp); + fread(&norm, sizeof(Vec3), 1, fp); + fread(&vstate, sizeof(DWORD), 1, fp); + + vert.SwapYZ(); + vert *= (float) scale; + + norm.SwapYZ(); + + vset->loc[v] = vert; + vset->nrm[v] = norm; + + double d = vert.length(); + if (d > radius) + radius = (float) d; + } + + while (v < nverts) + vset->nrm[v++] = Vec3(1,0,0); + + // read polys: + Vec3 dummy_center; + DWORD dummy_flags; + DWORD dummy_color; + int texture_num; + int poly_nverts; + int vert_index_buffer[32]; + float texture_index_buffer[32]; + + for (int n = 0; n < npolys; n++) { + Poly& poly = polys[n]; + poly.vertex_set = vset; + + fread(&dummy_flags, sizeof(DWORD), 1, fp); + fread(&dummy_center, sizeof(Vec3), 1, fp); + LoadPlane(poly.plane, fp); + fread(&dummy_color, sizeof(DWORD), 1, fp); + fread(&texture_num, sizeof(int), 1, fp); + + if (texture_num >= 0 && texture_num < ntex) { + texture_num++; + + poly.material = m->GetMaterials()[texture_num]; + poly.sortval = texture_num; + + if (dummy_flags & 2) { // luminous + Material* mtl = m->GetMaterials()[texture_num]; + + mtl->ambient_value = 0.0f; + mtl->ambient_color = Color::Black; + mtl->diffuse_value = 0.0f; + mtl->diffuse_color = Color::Black; + mtl->specular_value = 0.0f; + mtl->specular_color = Color::Black; + mtl->emissive_value = 1.0f; + mtl->emissive_color = Color::White; + + mtl->Ka = ColorValue(0,0,0,0); + mtl->Kd = ColorValue(0,0,0,0); + mtl->Ks = ColorValue(0,0,0,0); + mtl->Ke = ColorValue(1,1,1,1); + + mtl->tex_emissive = mtl->tex_diffuse; + } + } + else { + poly.material = m->GetMaterials().first(); // default material + poly.sortval = 1000; + } + + // hack: store flat shaded flag in unused visible byte + poly.visible = (BYTE) (dummy_flags & 1); + + fread(&poly_nverts, sizeof(int), 1, fp); + fread(vert_index_buffer, sizeof(int), poly_nverts, fp); + + if (poly_nverts == 3) + s->AddIndices(3); + + else if (poly_nverts == 4) + s->AddIndices(6); + + poly.nverts = poly_nverts; + for (int vi = 0; vi < poly_nverts; vi++) { + int v = vert_index_buffer[vi]; + + if (vset->rw[v] > 0) { + vset->CopyVertex(next_vert, v); + v = next_vert++; + } + + vset->rw[v] = 1; + poly.verts[vi] = v; + } + + fread(texture_index_buffer, sizeof(float), poly_nverts, fp); // tu's + for (int vi = 0; vi < poly_nverts; vi++) { + int v = poly.verts[vi]; + vset->tu[v] = texture_index_buffer[vi]; + } + + fread(texture_index_buffer, sizeof(float), poly_nverts, fp); // tv's + for (int vi = 0; vi < poly_nverts; vi++) { + int v = poly.verts[vi]; + vset->tv[v] = texture_index_buffer[vi]; + } + + DWORD unused[32]; + fread(unused, 16, 1, fp); + } + + // pass 2 (adjust vertex normals for flat polys): + for (int n = 0; n < npolys; n++) { + Poly& poly = polys[n]; + + poly.plane = Plane(vset->loc[poly.verts[0]], + vset->loc[poly.verts[2]], + vset->loc[poly.verts[1]]); + + // hack: retrieve flat shaded flag from unused visible byte + if (poly.visible) { + int poly_nverts = poly.nverts; + + for (int vi = 0; vi < poly_nverts; vi++) { + int v = poly.verts[vi]; + vset->nrm[v] = poly.plane.normal; + } + } + } + + // sort the polys by material index: + qsort((void*) polys, npolys, sizeof(Poly), mcomp); + + // then assign them to cohesive segments: + Segment* segment = 0; + + for (int n = 0; n < npolys; n++) { + if (segment && segment->material == polys[n].material) { + segment->npolys++; + } + else { + segment = 0; + } + + if (!segment) { + segment = new Segment; + + segment->npolys = 1; + segment->polys = &polys[n]; + segment->material = segment->polys->material; + + s->GetSegments().append(segment); + } + } + + s->BuildHull(); + m->GetSurfaces().append(s); + + *pnverts = nverts; + *pnpolys = npolys; + *pradius = (float) radius; + + result = nverts && npolys; + } + + return result; +} + +// +--------------------------------------------------------------------+ + +bool +ModelFileMAG::LoadMag6(FILE* fp, Model* m, double scale) +{ + bool result = false; + int i = 0; + int ntex = 0; + int nmtls = 0; + int nsurfs = 0; + double radius = 0; + List textures; + + fread(&ntex, sizeof(ntex), 1, fp); // size of texture block + fread(&nmtls, sizeof(nmtls), 1, fp); // number of materials + fread(&nsurfs, sizeof(nsurfs), 1, fp); // number of surfaces + + // read texture list: + if (ntex) { + char* buffer = new char[ntex]; + char* p = buffer; + Bitmap* bmp = 0; + + fread(buffer, ntex, 1, fp); + + while (p < buffer + ntex) { + LoadTexture(p, bmp, Bitmap::BMP_SOLID); + textures.append(bmp); + + p += strlen(p) + 1; + } + + delete [] buffer; + } + + for (i = 0; i < nmtls; i++) { + MaterialMag6 m6; + Material* mtl = new Material; + + fread(&m6, sizeof(m6), 1, fp); + + if (mtl) { + CopyMemory(mtl->name, m6.name, Material::NAMELEN); + CopyMemory(mtl->shader, m6.shader, Material::NAMELEN); + + mtl->ambient_value = m6.ambient_value; + mtl->ambient_color = m6.ambient_color; + mtl->diffuse_value = m6.diffuse_value; + mtl->diffuse_color = m6.diffuse_color; + mtl->specular_value = m6.specular_value; + mtl->specular_color = m6.specular_color; + mtl->emissive_value = m6.emissive_value; + mtl->emissive_color = m6.emissive_color; + + mtl->Ka = ColorValue(mtl->ambient_color) * mtl->ambient_value; + mtl->Kd = ColorValue(mtl->diffuse_color) * mtl->diffuse_value; + mtl->Ks = ColorValue(mtl->specular_color) * mtl->specular_value; + mtl->Ke = ColorValue(mtl->emissive_color) * mtl->emissive_value; + + mtl->power = m6.power; + mtl->brilliance = m6.brilliance; + mtl->bump = m6.bump; + mtl->blend = m6.blend; + mtl->shadow = m6.shadow; + mtl->luminous = m6.luminous; + + if (m6.tex_diffuse && m6.tex_diffuse <= textures.size()) + mtl->tex_diffuse = textures[m6.tex_diffuse - 1]; + + if (m6.tex_specular && m6.tex_specular <= textures.size()) + mtl->tex_specular = textures[m6.tex_specular - 1]; + + if (m6.tex_emissive && m6.tex_emissive <= textures.size()) + mtl->tex_emissive = textures[m6.tex_emissive - 1]; + + if (m6.tex_bumpmap && m6.tex_bumpmap <= textures.size()) + mtl->tex_bumpmap = textures[m6.tex_bumpmap - 1]; + + m->GetMaterials().append(mtl); + } + } + + for (i = 0; i < nsurfs; i++) { + int nverts = 0; + int npolys = 0; + BYTE namelen = 0; + char name[128]; + + fread(&nverts, 4, 1, fp); + fread(&npolys, 4, 1, fp); + fread(&namelen, 1, 1, fp); + fread(name, 1, namelen, fp); + + Surface* surface = new Surface; + surface->SetName(name); + surface->CreateVerts(nverts); + surface->CreatePolys(npolys); + + VertexSet* vset = surface->GetVertexSet(); + Poly* polys = surface->GetPolys(); + + ZeroMemory(polys, sizeof(Poly) * npolys); + + // read vertex set: + for (int v = 0; v < nverts; v++) { + fread(&vset->loc[v], sizeof(float), 3, fp); + fread(&vset->nrm[v], sizeof(float), 3, fp); + fread(&vset->tu[v], sizeof(float), 1, fp); + fread(&vset->tv[v], sizeof(float), 1, fp); + + double d = vset->loc[v].length(); + if (d > radius) + radius = d; + } + + // read polys: + for (int n = 0; n < npolys; n++) { + Poly& poly = polys[n]; + BYTE poly_nverts = 0; + BYTE material_index = 0; + WORD poly_verts[8]; + + fread(&poly_nverts, sizeof(BYTE), 1, fp); + fread(&material_index, sizeof(BYTE), 1, fp); + fread(&poly_verts[0], sizeof(WORD), poly_nverts, fp); + + if (poly_nverts >= 3) { + poly.nverts = poly_nverts; + + for (int i = 0; i < poly_nverts; i++) { + poly.verts[i] = poly_verts[i]; + } + } + else { + poly.sortval = 666; + } + + if (material_index > 0) { + poly.material = m->GetMaterials()[material_index-1]; + poly.sortval = material_index; + } + else if (m->NumMaterials()) { + poly.material = m->GetMaterials().first(); + poly.sortval = 1; + } + else { + poly.sortval = 1000; + } + + if (poly.nverts == 3) + surface->AddIndices(3); + + else if (poly.nverts == 4) + surface->AddIndices(6); + + poly.vertex_set = vset; + poly.plane = Plane(vset->loc[poly.verts[0]], + vset->loc[poly.verts[2]], + vset->loc[poly.verts[1]]); + } + + // sort the polys by material index: + qsort((void*) polys, npolys, sizeof(Poly), mcomp); + + // then assign them to cohesive segments: + Segment* segment = 0; + + for (int n = 0; n < npolys; n++) { + if (segment && segment->material == polys[n].material) { + segment->npolys++; + } + else { + segment = 0; + } + + if (!segment) { + segment = new Segment; + + segment->npolys = 1; + segment->polys = &polys[n]; + segment->material = segment->polys->material; + + surface->GetSegments().append(segment); + } + } + + surface->ComputeTangents(); + surface->BuildHull(); + m->GetSurfaces().append(surface); + + *pnverts = nverts; + *pnpolys = npolys; + *pradius = (float) radius; + + result = nverts && npolys; + } + + return result; +} + diff --git a/MagicEx/src/ModelFileOBJ.cpp b/MagicEx/src/ModelFileOBJ.cpp new file mode 100644 index 0000000..9ab7e69 --- /dev/null +++ b/MagicEx/src/ModelFileOBJ.cpp @@ -0,0 +1,788 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + File loader for Wavefront/OBJ format models +*/ + +#include + +#include +#include +#include +#include + +#include + +// +--------------------------------------------------------------------+ + +ModelFileOBJ::ModelFileOBJ(const char* fname) + : ModelFile(fname) +{ +} + +ModelFileOBJ::~ModelFileOBJ() +{ +} + +// +--------------------------------------------------------------------+ + +const int MAX_OBJ_FACE_VERTS = 32; + +struct ObjFace { + int v[MAX_OBJ_FACE_VERTS]; + int n[MAX_OBJ_FACE_VERTS]; + int t[MAX_OBJ_FACE_VERTS]; + + int nverts; + + ObjFace() { + nverts = 0; + + for (int i = 0; i < MAX_OBJ_FACE_VERTS; i++) + v[i] = n[i] = t[i] = 0; + } + + ObjFace(const ObjFace& f) { + nverts = f.nverts; + + for (int i = 0; i < MAX_OBJ_FACE_VERTS; i++) { + v[i] = f.v[i]; + n[i] = f.n[i]; + t[i] = f.t[i]; + } + } + + void ReverseOrder() { + int i, tmp[MAX_OBJ_FACE_VERTS]; + + for (i = 0; i < nverts; i++) tmp[i] = v[i]; + for (i = 0; i < nverts; i++) v[i] = tmp[nverts-1-i]; + + for (i = 0; i < nverts; i++) tmp[i] = n[i]; + for (i = 0; i < nverts; i++) n[i] = tmp[nverts-1-i]; + + for (i = 0; i < nverts; i++) tmp[i] = t[i]; + for (i = 0; i < nverts; i++) t[i] = tmp[nverts-1-i]; + } +}; + +static void ParsePoly(const char* line, ObjFace* face) +{ + int v[MAX_OBJ_FACE_VERTS]; + int n[MAX_OBJ_FACE_VERTS]; + int t[MAX_OBJ_FACE_VERTS]; + + for (int i = 0; i < MAX_OBJ_FACE_VERTS; i++) { + v[i] = n[i] = t[i] = 0; + } + + const char* p = line + 1; + + while (isspace(*p)) + p++; + + int i = 0; + while (*p && i < MAX_OBJ_FACE_VERTS) { + int factor = 1; + + if (*p == '-') { + factor = -1; + p++; + } + + while (isdigit(*p)) { + v[i] = v[i]*10 + *p - '0'; + p++; + } + v[i] *= factor; + + if (*p == '/') { p++; // slash one + + factor = 1; + + if (*p == '-') { + factor = -1; + p++; + } + + while (isdigit(*p)) { + t[i] = t[i]*10 + *p - '0'; + p++; + } + t[i] *= factor; + + if (*p == '/') { p++; // slash two + + factor = 1; + + if (*p == '-') { + factor = -1; + p++; + } + + while (isdigit(*p)) { + n[i] = n[i]*10 + *p - '0'; + p++; + } + n[i] *= factor; + }} + + while (isspace(*p)) p++; + i++; + } + + face->nverts = i; + + for (i = 0; i < MAX_OBJ_FACE_VERTS; i++) { + face->v[i] = v[i]; + face->n[i] = n[i]; + face->t[i] = t[i]; + } +} + +static int LoadMatls(const char* lpszPathName, Model* m) +{ + int nmatls = 0; + + FILE* fp = fopen(lpszPathName, "r"); + if (!fp) { + ::MessageBox(0, "Open Failed: could not open file", "ERROR", MB_OK); + return 0; + } + + Material* mtl = 0; + + while (!feof(fp)) { + char raw_line[512]; + char line[512]; + fgets(raw_line, 512, fp); + + strcpy(line, Text(raw_line).trim().data()); + + if (strstr(line, "newmtl")) { + mtl = new Material; + strncpy(mtl->name, line+7, Material::NAMELEN - 1); + + m->GetMaterials().append(mtl); + } + + else if (line[0] == 'K' && line[1] == 'a') { + float r,g,b; + sscanf(line, "Ka %f %f %f", &r, &g, &b); + mtl->Ka = ColorValue(r,g,b); + + mtl->ambient_value = 1.0f; + mtl->ambient_color = mtl->Ka.ToColor(); + } + + else if (line[0] == 'K' && line[1] == 'd') { + float r,g,b; + sscanf(line, "Kd %f %f %f", &r, &g, &b); + mtl->Kd = ColorValue(r,g,b); + + mtl->diffuse_value = 1.0f; + mtl->diffuse_color = mtl->Kd.ToColor(); + } + + else if (line[0] == 'K' && line[1] == 's') { + float r,g,b; + sscanf(line, "Ks %f %f %f", &r, &g, &b); + mtl->Ks = ColorValue(r,g,b); + + mtl->specular_value = 1.0f; + mtl->specular_color = mtl->Ks.ToColor(); + } + + else if (line[0] == 'N' && line[1] == 's') { + float ns; + sscanf(line, "Ns %f", &ns); + mtl->power = ns; + } + + else if (strstr(line, "map_Kd")) { + const char* src = strstr(line, "map_Kd") + 7; + while (!isalnum(*src)) src++; + + LoadTexture(src, mtl->tex_diffuse, Bitmap::BMP_SOLID); + } + } + + fclose(fp); + return nmatls; +} + +static Material* FindMatl(const char* mtl_name, Model* model) +{ + ListIter iter = model->GetMaterials(); + while (++iter) { + Material* m = iter.value(); + if (!strcmp(m->name, mtl_name)) + return m; + } + + return 0; +} + +// +--------------------------------------------------------------------+ + +static int mcomp(const void* a, const void* b) +{ + Poly* pa = (Poly*) a; + Poly* pb = (Poly*) b; + + if (pa->sortval == pb->sortval) + return 0; + + if (pa->sortval < pb->sortval) + return 1; + + return -1; +} + +bool +ModelFileOBJ::Load(Model* m, double scale) +{ + if (m && scale > 0 && strlen(filename) > 0) { + ModelFile::Load(m, scale); + + FILE* fp = fopen(filename, "rb"); + + if (fp == NULL) { + ::MessageBox(0, "Wavefront/OBJ Import Failed: Unable to open file", "ERROR", MB_OK); + return false; + } + + // ok, now start reading the data: + int ntex = 0; + int nverts = 0; + int npv = 0; + int npolys = 0; + int vi = 0; + int vn = 0; + int vt = 0; + + char root_path[256]; + ZeroMemory(root_path, 256); + + if (strrchr(filename, '\\')) { + strcpy(root_path, filename); + char* p = strrchr(root_path, '\\'); + if (p) + *(p+1) = 0; + } + + else if (strrchr(filename, '/')) { + strcpy(root_path, filename); + char* p = strrchr(root_path, '/'); + if (p) + *(p+1) = 0; + } + + // count verts and polys: + while (!feof(fp)) { + char line[256]; + fgets(line, 255, fp); + + if (line[0] == 'v') { + switch (line[1]) { + case ' ': vi++; break; + case 'n': vn++; break; + case 't': vt++; break; + } + } + + else if (line[0] == 'f' && line[1] == ' ') { + npolys++; + + ObjFace f; + ParsePoly(line, &f); + f.ReverseOrder(); + + npv += f.nverts; + } + + else if (strstr(line, "mtllib")) { + const char* libname = strstr(line, "mtllib"); + libname += 7; + while (isspace(*libname)) + libname++; + + char libpath[256]; + strcpy(libpath, root_path); + strcat(libpath, libname); + int n = strlen(libpath); + if (n > 0) { + char* p = &libpath[n-1]; + while (isspace(*p)) *p-- = 0; + } + + int nmatls = LoadMatls(libpath, model); + } + } + + nverts = npv; + if (vi > nverts) nverts = vi; + if (vn > nverts) nverts = vn; + if (vt > nverts) nverts = vt; + + if (nverts > Model::MAX_VERTS || npolys > Model::MAX_POLYS) { + ::MessageBox(0, "Wavefront/OBJ Import Failed: model is too complicated (too many vertices/polys)!", "ERROR", MB_OK); + return false; + } + + vi = 0; + vn = 0; + vt = 0; + + fseek(fp, 0, SEEK_SET); + + Surface* surface = new Surface; + m->GetSurfaces().append(surface); + + surface->CreateVerts(nverts); + surface->CreatePolys(npolys); + + VertexSet* vset = surface->GetVertexSet(); + Poly* polys = surface->GetPolys(); + + // read vertex set: + Vec3* vloc = new Vec3[nverts]; + Vec3* vnrm = new Vec3[nverts]; + float* vtu = new float[nverts]; + float* vtv = new float[nverts]; + + float radius = 0; + + while (!feof(fp)) { + char line[256]; + fgets(line, 255, fp); + + if (line[0] == 'v') { + if (line[1] == ' ') { + const char* p = line + 2; + + while (isspace(*p)) p++; + sscanf(p, "%f", &vloc[vi].x); + + while (!isspace(*p)) p++; + while (isspace(*p)) p++; + sscanf(p, "%f", &vloc[vi].y); + + while (!isspace(*p)) p++; + while (isspace(*p)) p++; + sscanf(p, "%f", &vloc[vi].z); + + float d = vloc[vi].length(); + if (d > radius) + radius = d; + + vi++; + } + + else if (line[1] == 'n') { + const char* p = line + 2; + + while (isspace(*p)) p++; + sscanf(p, "%f", &vnrm[vn].x); + + while (!isspace(*p)) p++; + while (isspace(*p)) p++; + sscanf(p, "%f", &vnrm[vn].y); + + while (!isspace(*p)) p++; + while (isspace(*p)) p++; + sscanf(p, "%f", &vnrm[vn].z); + + vn++; + } + + else if (line[1] == 't') { + const char* p = line + 2; + + while (isspace(*p)) p++; + sscanf(p, "%f", &vtu[vt]); + + while (!isspace(*p)) p++; + while (isspace(*p)) p++; + sscanf(p, "%f", &vtv[vt]); + + vtv[vt] = 1.0f - vtv[vt]; + + vt++; + } + } + } + + fseek(fp, 0, SEEK_SET); + + // read polys: + int poly = 0; + char line[256]; + ObjFace face; + Material* material = 0; + int mtl_index = 0; + + if (m->NumMaterials()) + material = m->GetMaterials().first(); + + int current_v = 1; + int current_vn = 1; + int current_vt = 1; + int v = 0; // vset index pointer + + while (!feof(fp)) { + char raw_line[256]; + fgets(raw_line, 256, fp); + + strcpy(line, Text(raw_line).trim().data()); + + if (strstr(line, "usemtl")) { + material = FindMatl(line + 7, model); + ListIter iter = model->GetMaterials(); + while (++iter) + if (material == iter.value()) + mtl_index = iter.index(); + } + + else if (line[0] == 'v') { + if (line[1] == ' ') current_v++; + else if (line[1] == 'n') current_vn++; + else if (line[1] == 't') current_vt++; + } + + else if (line[0] == 'f') { + ParsePoly(line, &face); + face.ReverseOrder(); + + for (int n = 0; n < face.nverts; n++) { + if (face.v[n] < 0) + face.v[n] += current_v; + + if (face.n[n] < 0) + face.n[n] += current_vn; + + if (face.t[n] < 0) + face.t[n] += current_vt; + } + + if (face.nverts > 4) { + npolys += face.nverts - 3; + + for (int tri = 2; tri < face.nverts; tri++) { + Poly* p = polys + poly; + poly++; + + p->nverts = 3; + + vset->loc[v+0] = vloc[face.v[ tri ] -1]; + vset->loc[v+1] = vloc[face.v[ tri-1 ] -1]; + vset->loc[v+2] = vloc[face.v[ 0] -1]; + + if (face.n[0] > 0) { + vset->nrm[v+0] = vnrm[face.n[ tri ] -1]; + vset->nrm[v+1] = vnrm[face.n[ tri-1 ] -1]; + vset->nrm[v+2] = vnrm[face.n[ 0] -1]; + } + else { + vset->nrm[v+0] = vloc[v+0]; vset->nrm[v+0].Normalize(); + vset->nrm[v+1] = vloc[v+1]; vset->nrm[v+1].Normalize(); + vset->nrm[v+2] = vloc[v+2]; vset->nrm[v+2].Normalize(); + } + + if (face.t[0] > 0) { + vset->tu[v+0] = vtu[face.t[ tri ] -1]; + vset->tv[v+0] = vtv[face.t[ tri ] -1]; + vset->tu[v+1] = vtu[face.t[ tri-1 ] -1]; + vset->tv[v+1] = vtv[face.t[ tri-1 ] -1]; + vset->tu[v+2] = vtu[face.t[ 0 ] -1]; + vset->tv[v+2] = vtv[face.t[ 0 ] -1]; + } + + p->verts[0] = v+0; + p->verts[1] = v+1; + p->verts[2] = v+2; + + p->material = material; + p->sortval = mtl_index; + p->vertex_set = vset; + + p->plane = Plane(vset->loc[p->verts[0]], + vset->loc[p->verts[2]], + vset->loc[p->verts[1]]); + + v += p->nverts; + } + } + + else if (face.nverts == 3 || face.nverts == 4) { + Poly* p = polys + poly; + poly++; + + p->nverts = face.nverts; + + bool flat = true; + int first = v; + + for (int i = 0; i < p->nverts; i++) { + int face_index = i; + + vset->loc[v] = vloc[face.v[face_index]-1]; + + if (face.n[face_index] > 0) + vset->nrm[v] = vnrm[face.n[face_index]-1]; + else + vset->nrm[v] = vset->loc[v]; + + vset->nrm[v].Normalize(); + + if (vset->nrm[v] != vset->nrm[first]) + flat = false; + + if (face.t[face_index] > 0) { + vset->tu[v] = vtu [face.t[face_index]-1]; + vset->tv[v] = vtv [face.t[face_index]-1]; + } + + p->verts[i] = v++; + } + + p->material = material; + p->sortval = mtl_index; + p->flatness = flat ? 1.0f : 0.0f; + + if (p->nverts == 3) + surface->AddIndices(3); + + else if (p->nverts == 4) + surface->AddIndices(6); + + p->vertex_set = vset; + p->plane = Plane(vset->loc[p->verts[0]], + vset->loc[p->verts[2]], + vset->loc[p->verts[1]]); + } + + if (poly >= npolys) + break; + } + } + + // sort the polys by material index: + qsort((void*) polys, npolys, sizeof(Poly), mcomp); + + // then assign them to cohesive segments: + Segment* segment = 0; + + for (int n = 0; n < npolys; n++) { + if (segment && segment->material == polys[n].material) { + segment->npolys++; + } + else { + segment = 0; + } + + if (!segment) { + segment = new Segment; + + segment->npolys = 1; + segment->polys = &polys[n]; + segment->material = segment->polys->material; + + surface->GetSegments().append(segment); + } + } + + delete [] vloc; + delete [] vnrm; + delete [] vtu; + delete [] vtv; + + *pnverts = nverts; + *pnpolys = npolys; + *pradius = radius; + + fclose(fp); + + m->Normalize(); + return true; + } + + return false; +} + +// +--------------------------------------------------------------------+ + +static bool +SaveWaveFrontMatLib(const char* path, const char* root, Model* model) +{ + char filename[256]; + sprintf(filename, "%s%s.mtl", path, root); + + FILE* f = fopen(filename, "w"); + if (f) { + fprintf(f, "#\n# %s Material Library exported by Magic 2.0\n#\n\n", root); + + ListIter iter = model->GetMaterials(); + while (++iter) { + Material* mtl = iter.value(); + + fprintf(f, "newmtl %s\n", mtl->name); + fprintf(f, "Ka %.5f %.5f %.5f\n", mtl->Ka.Red(), mtl->Ka.Green(), mtl->Ka.Blue()); + fprintf(f, "Kd %.5f %.5f %.5f\n", mtl->Kd.Red(), mtl->Kd.Green(), mtl->Kd.Blue()); + fprintf(f, "Ks %.5f %.5f %.5f\n", mtl->Ks.Red(), mtl->Ks.Green(), mtl->Ks.Blue()); + fprintf(f, "Ns %.5f\n", mtl->power); + fprintf(f, "illum 2\n"); + if (mtl->tex_diffuse) + fprintf(f, "map_Kd %s\n", mtl->tex_diffuse->GetFilename()); + fprintf(f, "\n"); + } + + fclose(f); + return true; + } + + return false; +} + +bool +ModelFileOBJ::Save(Model* m) +{ + if (m) { + ModelFile::Save(m); + char pathname[256]; + char rootname[256]; + + ZeroMemory(pathname, sizeof(pathname)); + ZeroMemory(rootname, sizeof(rootname)); + + const char* ext = strstr(filename, ".obj"); + if (!ext) + ext = strstr(filename, ".OBJ"); + + const char* sep = strrchr(filename, '/'); + if (!sep) + sep = strrchr(filename, '\\'); + + const char* src = filename; + char* dst = pathname; + + if (sep) { + while (src != sep) + *dst++ = *src++; + *dst++ = *src++; + } + + if (ext) { + dst = rootname; + while (src != ext) + *dst++ = *src++; + } + else { + strcpy(rootname, src); + } + + strcpy(filename, pathname); + strcat(filename, rootname); + strcat(filename, ".obj"); + + FILE* f = fopen(filename, "w"); + if (!f) { + ::MessageBox(0, "Export Failed: Magic could not open the file for writing", "ERROR", MB_OK); + return false; + } + + fprintf(f, "# Wavefront OBJ exported by Magic 2.0\n\n"); + fprintf(f, "mtllib %s.mtl\n", rootname); + + ListIter s_iter = m->GetSurfaces(); + + // vertex locations + fprintf(f, "\n# VERTEX LOCATIONS: %d\n", m->NumVerts()); + while (++s_iter) { + Surface* s = s_iter.value(); + VertexSet* vset = s->GetVertexSet(); + + for (int n = 0; n < vset->nverts; n++) { + fprintf(f, "v %12.5f %12.5f %12.5f\n", + vset->loc[n].x, + vset->loc[n].y, + vset->loc[n].z); + } + } + + s_iter.reset(); + + // vertex normals + fprintf(f, "\n# VERTEX NORMALS: %d\n", m->NumVerts()); + while (++s_iter) { + Surface* s = s_iter.value(); + VertexSet* vset = s->GetVertexSet(); + + for (int n = 0; n < vset->nverts; n++) { + fprintf(f, "vn %8.3f %8.3f %8.3f\n", + vset->nrm[n].x, + vset->nrm[n].y, + vset->nrm[n].z); + } + } + + s_iter.reset(); + + // texture coordinates + fprintf(f, "\n# TEXTURE COORDINATES: %d\n", m->NumVerts()); + while (++s_iter) { + Surface* s = s_iter.value(); + VertexSet* vset = s->GetVertexSet(); + + for (int n = 0; n < vset->nverts; n++) { + fprintf(f, "vt %8.3f %8.3f\n", + vset->tu[n], 1 - vset->tv[n]); + } + } + + s_iter.reset(); + + // faces + Material* current_material = 0; + + fprintf(f, "\n# FACES: %d\n", m->NumPolys()); + while (++s_iter) { + Surface* s = s_iter.value(); + + for (int n = 0; n < s->NumPolys(); n++) { + const Poly* p = s->GetPolys() + n; + int nv = p->nverts; + Material* mtl = p->material; + + if (current_material != mtl) { + fprintf(f, "\n\nusemtl %s\n", mtl->name); + current_material = mtl; + } + + fprintf(f, "\nf "); + for (int v = nv-1; v >= 0; v--) { + fprintf(f, "%d/%d/%d ", + p->verts[v] + 1, + p->verts[v] + 1, + p->verts[v] + 1); + } + } + } + + fprintf(f, "\n\n\n# END OF FILE.\n"); + fclose(f); + + return SaveWaveFrontMatLib(pathname, rootname, m); + } + + return false; +} + +// +--------------------------------------------------------------------+ diff --git a/MagicEx/src/mag2obj.cpp b/MagicEx/src/mag2obj.cpp new file mode 100644 index 0000000..a8ba386 --- /dev/null +++ b/MagicEx/src/mag2obj.cpp @@ -0,0 +1,27 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. +*/ + +#include + +#include + +#include +#include +#include + + +int +main(int argc, char* argv[]) +{ + if (argc < 2) + return 1; + std::string pathname {argv[1]}; + auto solid = std::make_unique(); + if (!ImportInto(pathname.c_str(), solid.get())) + return 1; + ModelFileOBJ exporter {(pathname + ".obj").c_str()}; + exporter.Save(solid->GetModel()); +} diff --git a/MagicEx/src/obj2mag.cpp b/MagicEx/src/obj2mag.cpp new file mode 100644 index 0000000..c9d22e0 --- /dev/null +++ b/MagicEx/src/obj2mag.cpp @@ -0,0 +1,27 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. +*/ + +#include + +#include + +#include +#include +#include + + +int +main(int argc, char* argv[]) +{ + if (argc < 2) + return 1; + std::string pathname {argv[1]}; + auto solid = std::make_unique(); + if (!ImportInto(pathname.c_str(), solid.get())) + return 1; + ModelFileMAG exporter {(pathname + ".mag").c_str()}; + exporter.Save(solid->GetModel()); +} -- cgit v1.1