summaryrefslogtreecommitdiffhomepage
path: root/MagicEx/src
diff options
context:
space:
mode:
Diffstat (limited to 'MagicEx/src')
-rw-r--r--MagicEx/src/MagicLoad.cpp178
-rw-r--r--MagicEx/src/ModelFile3DS.cpp281
-rw-r--r--MagicEx/src/ModelFileMAG.cpp823
-rw-r--r--MagicEx/src/ModelFileOBJ.cpp788
-rw-r--r--MagicEx/src/mag2obj.cpp27
-rw-r--r--MagicEx/src/obj2mag.cpp27
6 files changed, 2124 insertions, 0 deletions
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 <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/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 <ModelFile3DS.h>
+
+#include <Bitmap.h>
+#include <l3ds.h>
+#include <List.h>
+#include <Polygon.h>
+#include <Text.h>
+
+#include <MagicLoad.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/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 <ModelFileMAG.h>
+
+#include <Bitmap.h>
+#include <List.h>
+#include <Polygon.h>
+
+#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<Bitmap> textures;
+
+ ListIter<Material> 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<Bitmap> 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<Surface> 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<Bitmap> 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 <ModelFileOBJ.h>
+
+#include <Bitmap.h>
+#include <Polygon.h>
+#include <List.h>
+#include <Text.h>
+
+#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<Material> 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<Material> 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<Material> 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<Surface> 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 <memory>
+
+#include <Solid.h>
+
+#include <MagicLoad.h>
+#include <ModelFileMAG.h>
+#include <ModelFileOBJ.h>
+
+
+int
+main(int argc, char* argv[])
+{
+ if (argc < 2)
+ return 1;
+ std::string pathname {argv[1]};
+ auto solid = std::make_unique<Solid>();
+ if (!ImportInto<ModelFileMAG>(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 <memory>
+
+#include <Solid.h>
+
+#include <MagicLoad.h>
+#include <ModelFileMAG.h>
+#include <ModelFileOBJ.h>
+
+
+int
+main(int argc, char* argv[])
+{
+ if (argc < 2)
+ return 1;
+ std::string pathname {argv[1]};
+ auto solid = std::make_unique<Solid>();
+ if (!ImportInto<ModelFileOBJ>(pathname.c_str(), solid.get()))
+ return 1;
+ ModelFileMAG exporter {(pathname + ".mag").c_str()};
+ exporter.Save(solid->GetModel());
+}