summaryrefslogtreecommitdiffhomepage
path: root/MagicEx
diff options
context:
space:
mode:
authorAki <please@ignore.pl>2024-04-04 02:16:26 +0200
committerAki <please@ignore.pl>2024-04-04 02:16:26 +0200
commit56f76f4d524ca12bef5c775da6e7282e8322a951 (patch)
treefce3656dd3048df2c2eb61236a53b0972e9abc17 /MagicEx
parente0f9f411056c235947b809cb21c477aa5acca1b4 (diff)
downloadstarshatter-56f76f4d524ca12bef5c775da6e7282e8322a951.zip
starshatter-56f76f4d524ca12bef5c775da6e7282e8322a951.tar.gz
starshatter-56f76f4d524ca12bef5c775da6e7282e8322a951.tar.bz2
Extracted ModelFile related functionality out of Magic2 to a library
Diffstat (limited to 'MagicEx')
-rw-r--r--MagicEx/CMakeLists.txt10
-rw-r--r--MagicEx/MagicLoad.cpp178
-rw-r--r--MagicEx/MagicLoad.h18
-rw-r--r--MagicEx/ModelFile3DS.cpp281
-rw-r--r--MagicEx/ModelFile3DS.h27
-rw-r--r--MagicEx/ModelFileMAG.cpp823
-rw-r--r--MagicEx/ModelFileMAG.h31
-rw-r--r--MagicEx/ModelFileOBJ.cpp788
-rw-r--r--MagicEx/ModelFileOBJ.h27
-rw-r--r--MagicEx/l3ds.cpp1790
-rw-r--r--MagicEx/l3ds.h457
11 files changed, 4430 insertions, 0 deletions
diff --git a/MagicEx/CMakeLists.txt b/MagicEx/CMakeLists.txt
new file mode 100644
index 0000000..965312c
--- /dev/null
+++ b/MagicEx/CMakeLists.txt
@@ -0,0 +1,10 @@
+project(MagicEx)
+add_library(
+ ${PROJECT_NAME} STATIC
+ l3ds.cpp
+ MagicLoad.cpp
+ ModelFile3DS.cpp
+ ModelFileMAG.cpp
+ ModelFileOBJ.cpp)
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+target_link_libraries(${PROJECT_NAME} PUBLIC StarsEx)
diff --git a/MagicEx/MagicLoad.cpp b/MagicEx/MagicLoad.cpp
new file mode 100644
index 0000000..2b13a93
--- /dev/null
+++ b/MagicEx/MagicLoad.cpp
@@ -0,0 +1,178 @@
+/* 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
new file mode 100644
index 0000000..27b6380
--- /dev/null
+++ b/MagicEx/MagicLoad.h
@@ -0,0 +1,18 @@
+/* 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 <windows.h>
+
+#include <Bitmap.h>
+
+
+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
new file mode 100644
index 0000000..4c4a157
--- /dev/null
+++ b/MagicEx/ModelFile3DS.cpp
@@ -0,0 +1,281 @@
+/* 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 <Bitmap.h>
+#include <List.h>
+#include <Polygon.h>
+#include <Text.h>
+
+#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
new file mode 100644
index 0000000..3cac828
--- /dev/null
+++ b/MagicEx/ModelFile3DS.h
@@ -0,0 +1,27 @@
+/* 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 <Solid.h>
+
+
+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
new file mode 100644
index 0000000..fe076ed
--- /dev/null
+++ b/MagicEx/ModelFileMAG.cpp
@@ -0,0 +1,823 @@
+/* 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 <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/ModelFileMAG.h b/MagicEx/ModelFileMAG.h
new file mode 100644
index 0000000..27466ae
--- /dev/null
+++ b/MagicEx/ModelFileMAG.h
@@ -0,0 +1,31 @@
+/* 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 <Solid.h>
+
+
+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
new file mode 100644
index 0000000..c682bff
--- /dev/null
+++ b/MagicEx/ModelFileOBJ.cpp
@@ -0,0 +1,788 @@
+/* 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 <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/ModelFileOBJ.h b/MagicEx/ModelFileOBJ.h
new file mode 100644
index 0000000..dcffc7d
--- /dev/null
+++ b/MagicEx/ModelFileOBJ.h
@@ -0,0 +1,27 @@
+/* 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 <Solid.h>
+
+
+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/l3ds.cpp b/MagicEx/l3ds.cpp
new file mode 100644
index 0000000..4c34ab5
--- /dev/null
+++ b/MagicEx/l3ds.cpp
@@ -0,0 +1,1790 @@
+// copyright (c) 2001 Lev Povalahev
+
+#include "l3ds.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+//using namespace std;
+
+//-------------------------------------------------------
+// generic stuff
+//-------------------------------------------------------
+
+typedef unsigned long ulong;
+
+#define SEEK_START 1900
+#define SEEK_CURSOR 1901
+
+// common chunks
+// colors
+#define COLOR_F 0x0010
+#define COLOR_24 0x0011
+#define LIN_COLOR_24 0x0012
+#define LIN_COLOR_F 0x0013
+// percentage
+#define INT_PERCENTAGE 0x0030
+#define FLOAT_PERCENTAGE 0x0031
+// ambient light
+#define AMBIENT_LIGHT 0x2100
+
+
+#define MAIN3DS 0x4D4D
+#define EDIT3DS 0x3D3D // this is the start of the editor config
+
+// keyframer chunk ids
+#define KFDATA 0xB000 // the keyframer section
+#define KFHDR 0xB00A
+#define OBJECT_NODE_TAG 0xB002
+#define NODE_HDR 0xB010
+#define PIVOT 0xB013
+#define POS_TRACK_TAG 0xB020
+#define ROT_TRACK_TAG 0xB021
+#define SCL_TRACK_TAG 0xB022
+
+// material entries
+#define MAT_ENTRY 0xAFFF
+#define MAT_NAME 0xA000
+#define MAT_AMBIENT 0xA010
+#define MAT_DIFFUSE 0xA020
+#define MAT_SPECULAR 0xA030
+#define MAT_SHININESS 0xA040
+#define MAT_SHIN2PCT 0xA041
+#define MAT_TRANSPARENCY 0xA050
+#define MAT_SHADING 0xA100
+#define MAT_TWO_SIDE 0xA081
+#define MAT_ADDITIVE 0xA083
+#define MAT_WIRE 0xA085
+#define MAT_FACEMAP 0xA088
+#define MAT_WIRESIZE 0xA087
+#define MAT_DECAL 0xA082
+#define MAT_TEXMAP 0xA200
+#define MAT_MAPNAME 0xA300
+#define MAT_MAP_TILING 0xA351
+#define MAT_MAP_USCALE 0xA354
+#define MAT_MAP_VSCALE 0xA356
+#define MAT_MAP_UOFFSET 0xA358
+#define MAT_MAP_VOFFSET 0xA35A
+#define MAT_MAP_ANG 0xA35C
+#define MAT_TEX2MAP 0xA33A
+#define MAT_OPACMAP 0xA210
+#define MAT_BUMPMAP 0xA230
+#define MAT_SPECMAP 0xA204
+#define MAT_SHINMAP 0xA33C
+#define MAT_REFLMAP 0xA220
+#define MAT_ACUBIC 0xA310
+
+#define EDIT_OBJECT 0x4000
+#define OBJ_TRIMESH 0x4100
+#define OBJ_LIGHT 0x4600
+#define OBJ_CAMERA 0x4700
+#define LIT_OFF 0x4620
+#define LIT_SPOT 0x4610
+#define TRI_VERTEXLIST 0x4110
+#define TRI_VERTEXOPTIONS 0x4111
+
+#define TRI_FACELIST 0x4120
+ #define TRI_MAT_GROUP 0x4130
+ #define TRI_SMOOTH_GROUP 0x4150
+
+#define TRI_FACEMAPPING 0x4140
+#define TRI_MATRIX 0x4160
+
+#define SPOTLIGHT 0x4610
+
+//----------------------------------
+
+#define MAX_SHARED_TRIS 100
+
+// the error reporting routine
+
+void ErrorMsg(const char *msg)
+{
+
+}
+
+struct LChunk
+{
+ unsigned short id;
+ uint start;
+ uint end;
+};
+
+struct LTri
+{
+ unsigned short a;
+ unsigned short b;
+ unsigned short c;
+ ulong smoothingGroups;
+ LVector3 normal;
+ LVector3 tangent;
+ LVector3 binormal;
+ uint materialId;
+};
+
+// globals
+
+LColor3 black = {0, 0, 0};
+
+LVector3 zero3 = {0, 0, 0};
+
+LVector4 zero4 = {0, 0, 0, 0};
+
+LMap emptyMap = {0, "", 1, 1, 0, 0, 0};
+
+LVector3 _4to3(const LVector4 &vec)
+{
+ LVector3 t;
+ t.x = vec.x;
+ t.y = vec.y;
+ t.z = vec.z;
+ return t;
+}
+
+LVector3 AddVectors(const LVector3 &a, const LVector3 &b)
+{
+ LVector3 t;
+ t.x = a.x+b.x;
+ t.y = a.y+b.y;
+ t.z = a.z+b.z;
+ return t;
+}
+
+LVector3 SubtractVectors(const LVector3 &a, const LVector3 &b)
+{
+ LVector3 t;
+ t.x = a.x-b.x;
+ t.y = a.y-b.y;
+ t.z = a.z-b.z;
+ return t;
+}
+
+float VectorLength(const LVector3 &vec)
+{
+ return (float)sqrt(vec.x*vec.x + vec.y*vec.y+vec.z*vec.z);
+}
+
+LVector3 NormalizeVector(const LVector3 &vec)
+{
+ float a = VectorLength(vec);
+ if (a == 0)
+ return vec;
+ float b = 1/a;
+ LVector3 v;
+ v.x = vec.x*b;
+ v.y = vec.y*b;
+ v.z = vec.z*b;
+ return v;
+}
+
+LVector3 CrossProduct(const LVector3 &a, const LVector3 &b)
+{
+ LVector3 v;
+ v.x = a.y*b.z - a.z*b.y;
+ v.y = a.z*b.x - a.x*b.z;
+ v.z = a.x*b.y - a.y*b.x;
+ return v;
+}
+
+void LoadIdentityMatrix(LMatrix4 &m)
+{
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ m.m[i][j] = (i==j) ? 1.0f : 0.0f;
+}
+
+LVector4 VectorByMatrix(const LMatrix4 &m, const LVector4 &vec)
+{
+ LVector4 res;
+
+ res.x = m.m[0][0]*vec.x + m.m[1][0]*vec.y + m.m[2][0]*vec.z + m.m[3][0]*vec.w;
+ res.y = m.m[0][1]*vec.x + m.m[1][1]*vec.y + m.m[2][1]*vec.z + m.m[3][1]*vec.w;
+ res.z = m.m[0][2]*vec.x + m.m[1][2]*vec.y + m.m[2][2]*vec.z + m.m[3][2]*vec.w;
+ res.w = m.m[0][3]*vec.x + m.m[1][3]*vec.y + m.m[2][3]*vec.z + m.m[3][3]*vec.w;
+
+ if (res.w != 0) {
+ float b = 1/res.w;
+ res.x *= b;
+ res.y *= b;
+ res.z *= b;
+ res.w = 1;
+ }
+ else {
+ res.w = 1;
+ }
+
+ return res;
+}
+
+void QuatToMatrix(const LVector4 &quat, LMatrix4 &m)
+{
+
+}
+
+//-------------------------------------------------------
+// LObject implementation
+//-------------------------------------------------------
+
+LObject::LObject()
+{
+ m_name = "";//.clear();
+}
+
+LObject::~LObject()
+{
+ // nothing here
+}
+
+void LObject::SetName(const std::string& value)
+{
+ m_name = value;
+}
+
+const std::string& LObject::GetName()
+{
+ return m_name;
+}
+
+bool LObject::IsObject(const std::string &name)
+{
+ return (m_name == name);
+}
+
+
+//-------------------------------------------------------
+// LMaterial implementation
+//-------------------------------------------------------
+
+LMaterial::LMaterial()
+: LObject()
+{
+ m_id = 0;
+ m_texMap1 = emptyMap;
+ m_texMap2 = emptyMap;
+ m_opacMap = emptyMap;
+ m_bumpMap = emptyMap;
+ m_reflMap = emptyMap;
+ m_specMap = emptyMap;
+ m_ambient = black;
+ m_diffuse = black;
+ m_specular = black;
+ m_shading = sGouraud;
+ m_shininess = 0;
+ m_transparency = 0;
+}
+
+LMaterial::~LMaterial()
+{
+
+}
+
+uint LMaterial::GetID()
+{
+ return m_id;
+}
+
+LMap& LMaterial::GetTextureMap1()
+{
+ return m_texMap1;
+}
+
+LMap& LMaterial::GetTextureMap2()
+{
+ return m_texMap2;
+}
+
+LMap& LMaterial::GetOpacityMap()
+{
+ return m_opacMap;
+}
+
+LMap& LMaterial::GetSpecularMap()
+{
+ return m_specMap;
+}
+
+LMap& LMaterial::GetBumpMap()
+{
+ return m_bumpMap;
+}
+
+LMap& LMaterial::GetReflectionMap()
+{
+ return m_reflMap;
+}
+
+LColor3 LMaterial::GetAmbientColor()
+{
+ return m_ambient;
+}
+
+LColor3 LMaterial::GetDiffuseColor()
+{
+ return m_diffuse;
+}
+
+LColor3 LMaterial::GetSpecularColor()
+{
+ return m_specular;
+}
+
+float LMaterial::GetShininess()
+{
+ return m_shininess;
+}
+
+float LMaterial::GetTransparency()
+{
+ return m_transparency;
+}
+
+LShading LMaterial::GetShadingType()
+{
+ return m_shading;
+}
+
+void LMaterial::SetID(uint value)
+{
+ m_id = value;
+}
+
+void LMaterial::SetAmbientColor(const LColor3 &color)
+{
+ m_ambient = color;
+}
+
+void LMaterial::SetDiffuseColor(const LColor3 &color)
+{
+ m_diffuse = color;
+}
+
+void LMaterial::SetSpecularColor(const LColor3 &color)
+{
+ m_specular = color;
+}
+
+void LMaterial::SetShininess(float value)
+{
+ m_shininess = value;
+ if (m_shininess < 0)
+ m_shininess = 0;
+ if (m_shininess > 1)
+ m_shininess = 1;
+}
+
+void LMaterial::SetTransparency(float value)
+{
+ m_transparency = value;
+ if (m_transparency < 0)
+ m_transparency = 0;
+ if (m_transparency > 1)
+ m_transparency = 1;
+}
+
+void LMaterial::SetShadingType(LShading shading)
+{
+ m_shading = shading;
+}
+
+//-------------------------------------------------------
+// LMesh implementation
+//-------------------------------------------------------
+
+LMesh::LMesh()
+: LObject()
+{
+ Clear();
+}
+
+LMesh::~LMesh()
+{
+ Clear();
+}
+
+void LMesh::Clear()
+{
+ m_vertices.clear();
+ m_normals.clear();
+ m_uv.clear();
+ m_tangents.clear();
+ m_binormals.clear();
+ m_triangles.clear();
+ m_tris.clear();
+ m_materials.clear();
+ LoadIdentityMatrix(m_matrix);
+}
+
+uint LMesh::GetVertexCount()
+{
+ return m_vertices.size();
+}
+
+void LMesh::SetVertexArraySize(uint value)
+{
+ m_vertices.resize(value);
+ m_normals.resize(value);
+ m_uv.resize(value);
+ m_tangents.resize(value);
+ m_binormals.resize(value);
+}
+
+uint LMesh::GetTriangleCount()
+{
+ return m_triangles.size();
+}
+
+void LMesh::SetTriangleArraySize(uint value)
+{
+ m_triangles.resize(value);
+ m_tris.resize(value);
+}
+
+const LVector4& LMesh::GetVertex(uint index)
+{
+ return m_vertices[index];
+}
+
+const LVector3& LMesh::GetNormal(uint index)
+{
+ return m_normals[index];
+}
+
+const LVector2& LMesh::GetUV(uint index)
+{
+ return m_uv[index];
+}
+
+const LVector3& LMesh::GetTangent(uint index)
+{
+ return m_tangents[index];
+}
+
+const LVector3& LMesh::GetBinormal(uint index)
+{
+ return m_binormals[index];
+}
+
+void LMesh::SetVertex(const LVector4 &vec, uint index)
+{
+ if (index >= m_vertices.size())
+ return;
+ m_vertices[index] = vec;
+}
+
+void LMesh::SetNormal(const LVector3 &vec, uint index)
+{
+ if (index >= m_vertices.size())
+ return;
+ m_normals[index] = vec;
+}
+
+void LMesh::SetUV(const LVector2 &vec, uint index)
+{
+ if (index >= m_vertices.size())
+ return;
+ m_uv[index] = vec;
+}
+
+void LMesh::SetTangent(const LVector3 &vec, uint index)
+{
+ if (index >= m_vertices.size())
+ return;
+ m_tangents[index] = vec;
+}
+
+void LMesh::SetBinormal(const LVector3 &vec, uint index)
+{
+ if (index >= m_vertices.size())
+ return;
+ m_binormals[index] = vec;
+}
+
+const LTriangle& LMesh::GetTriangle(uint index)
+{
+ return m_triangles[index];
+}
+
+LTriangle2 LMesh::GetTriangle2(uint index)
+{
+ LTriangle2 f;
+ LTriangle t = GetTriangle(index);
+ f.vertices[0] = GetVertex(t.a);
+ f.vertices[1] = GetVertex(t.b);
+ f.vertices[2] = GetVertex(t.c);
+
+ f.vertexNormals[0] = GetNormal(t.a);
+ f.vertexNormals[1] = GetNormal(t.b);
+ f.vertexNormals[2] = GetNormal(t.c);
+
+ f.textureCoords[0] = GetUV(t.a);
+ f.textureCoords[1] = GetUV(t.b);
+ f.textureCoords[2] = GetUV(t.c);
+
+ LVector3 a, b;
+
+ a = SubtractVectors(_4to3(f.vertices[1]), _4to3(f.vertices[0]));
+ b = SubtractVectors(_4to3(f.vertices[1]), _4to3(f.vertices[2]));
+
+ f.faceNormal = CrossProduct(b, a);
+
+ f.faceNormal = NormalizeVector(f.faceNormal);
+
+ f.materialId = m_tris[index].materialId;
+
+ return f;
+}
+
+LMatrix4 LMesh::GetMatrix()
+{
+ return m_matrix;
+}
+
+void LMesh::SetMatrix(LMatrix4 m)
+{
+ m_matrix = m;
+}
+
+void LMesh::TransformVertices()
+{
+ for (uint i=0; i<m_vertices.size(); i++)
+ m_vertices[i] = VectorByMatrix(m_matrix, m_vertices[i]);
+
+ LoadIdentityMatrix(m_matrix);
+}
+
+void LMesh::CalcNormals(bool useSmoothingGroups)
+{
+ uint i;
+ // first calculate the face normals
+ for (i=0; i<m_triangles.size(); i++)
+ {
+ LVector3 a, b;
+ a = SubtractVectors(_4to3(m_vertices[m_tris[i].b]), _4to3(m_vertices[m_tris[i].a]));
+ b = SubtractVectors(_4to3(m_vertices[m_tris[i].b]), _4to3(m_vertices[m_tris[i].c]));
+ m_tris[i].normal = NormalizeVector(CrossProduct(b, a));
+ }
+
+ std::vector< std::vector<int> > array;
+ array.resize(m_vertices.size());
+ for (i=0; i<m_triangles.size(); i++)
+ {
+ uint k = m_tris[i].a;
+ array[k].push_back(i);
+
+ k = m_tris[i].b;
+ array[k].push_back(i);
+
+ k = m_tris[i].c;
+ array[k].push_back(i);
+ }
+
+ LVector3 temp;
+
+ if (!useSmoothingGroups)
+ {
+ // now calculate the normals without using smoothing groups
+ for (i=0; i<m_vertices.size(); i++)
+ {
+ temp = zero3;
+ int t = array[i].size();
+
+ for (int k=0; k<t; k++)
+ {
+ temp.x += m_tris[array[i][k]].normal.x;
+ temp.y += m_tris[array[i][k]].normal.y;
+ temp.z += m_tris[array[i][k]].normal.z;
+ }
+ m_normals[i] = NormalizeVector(temp);
+ }
+ }
+ else
+ {
+ // now calculate the normals _USING_ smoothing groups
+ // I'm assuming a triangle can only belong to one smoothing group at a time!
+ std::vector<ulong> smGroups;
+ std::vector< std::vector <uint> > smList;
+
+ uint loop_size = m_vertices.size();
+
+ for (i=0; i<loop_size; i++)
+ {
+ int t = array[i].size();
+
+ if (t == 0)
+ continue;
+
+ smGroups.clear();
+ smList.clear();
+ smGroups.push_back(m_tris[array[i][0]].smoothingGroups);
+ smList.resize(smGroups.size());
+ smList[smGroups.size()-1].push_back(array[i][0]);
+
+ // first build a list of smoothing groups for the vertex
+ for (int k=0; k<t; k++)
+ {
+ bool found = false;
+ for (uint j=0; j<smGroups.size(); j++)
+ {
+ if (m_tris[array[i][k]].smoothingGroups == smGroups[j])
+ {
+ smList[j].push_back(array[i][k]);
+ found = true;
+ }
+ }
+ if (!found)
+ {
+ smGroups.push_back(m_tris[array[i][k]].smoothingGroups);
+ smList.resize(smGroups.size());
+ smList[smGroups.size()-1].push_back(array[i][k]);
+ }
+ }
+ // now we have the list of faces for the vertex sorted by smoothing groups
+
+
+ // now duplicate the vertices so that there's only one smoothing group "per vertex"
+ if (smGroups.size() > 1)
+ for (uint j=1; j< smGroups.size(); j++)
+ {
+ m_vertices.push_back(m_vertices[i]);
+ m_normals.push_back(m_normals[i]);
+ m_uv.push_back(m_uv[i]);
+ m_tangents.push_back(m_tangents[i]);
+ m_binormals.push_back(m_binormals[i]);
+
+ uint t = m_vertices.size()-1;
+ for (uint h=0; h<smList[j].size(); h++)
+ {
+ if (m_tris[smList[j][h]].a == i)
+ m_tris[smList[j][h]].a = t;
+ if (m_tris[smList[j][h]].b == i)
+ m_tris[smList[j][h]].b = t;
+ if (m_tris[smList[j][h]].c == i)
+ m_tris[smList[j][h]].c = t;
+ }
+ }
+ }
+
+ // now rebuild a face list for each vertex, since the old one is invalidated
+ for (i=0; i<array.size(); i++)
+ array[i].clear();
+ array.clear();
+ array.resize(m_vertices.size());
+ for (i=0; i<m_triangles.size(); i++)
+ {
+ uint k = m_tris[i].a;
+ array[k].push_back(i);
+
+ k = m_tris[i].b;
+ array[k].push_back(i);
+
+ k = m_tris[i].c;
+ array[k].push_back(i);
+ }
+
+ // now compute the normals
+ for (i=0; i<m_vertices.size(); i++)
+ {
+ temp = zero3;
+ int t = array[i].size();
+
+ for (int k=0; k<t; k++)
+ {
+ temp.x += m_tris[array[i][k]].normal.x;
+ temp.y += m_tris[array[i][k]].normal.y;
+ temp.z += m_tris[array[i][k]].normal.z;
+ }
+ m_normals[i] = NormalizeVector(temp);
+ }
+
+ }
+
+ // copy m_tris to m_triangles
+ for (i=0; i<m_triangles.size(); i++)
+ {
+ m_triangles[i].a = m_tris[i].a;
+ m_triangles[i].b = m_tris[i].b;
+ m_triangles[i].c = m_tris[i].c;
+ }
+
+}
+
+void LMesh::CalcTextureSpace()
+{
+ // a understandable description of how to do that can be found here:
+ // http://members.rogers.com/deseric/tangentspace.htm
+ // first calculate the tangent for each triangle
+ LVector3 x_vec,
+ y_vec,
+ z_vec;
+ LVector3 v1, v2;
+ for (uint i=0; i<m_triangles.size(); i++)
+ {
+ v1.x = m_vertices[m_tris[i].b].x - m_vertices[m_tris[i].a].x;
+ v1.y = m_uv[m_tris[i].b].x - m_uv[m_tris[i].a].x;
+ v1.z = m_uv[m_tris[i].b].y - m_uv[m_tris[i].a].y;
+
+ v2.x = m_vertices[m_tris[i].c].x - m_vertices[m_tris[i].a].x;
+ v2.y = m_uv[m_tris[i].c].x - m_uv[m_tris[i].a].x;
+ v2.z = m_uv[m_tris[i].c].y - m_uv[m_tris[i].a].y;
+
+ x_vec = CrossProduct(v1, v2);
+
+ v1.x = m_vertices[m_tris[i].b].y - m_vertices[m_tris[i].a].y;
+ v1.y = m_uv[m_tris[i].b].x - m_uv[m_tris[i].a].x;
+ v1.z = m_uv[m_tris[i].b].y - m_uv[m_tris[i].a].y;
+
+ v2.x = m_vertices[m_tris[i].c].y - m_vertices[m_tris[i].a].y;
+ v2.y = m_uv[m_tris[i].c].x - m_uv[m_tris[i].a].x;
+ v2.z = m_uv[m_tris[i].c].y - m_uv[m_tris[i].a].y;
+
+ y_vec = CrossProduct(v1, v2);
+
+ v1.x = m_vertices[m_tris[i].b].z - m_vertices[m_tris[i].a].z;
+ v1.y = m_uv[m_tris[i].b].x - m_uv[m_tris[i].a].x;
+ v1.z = m_uv[m_tris[i].b].y - m_uv[m_tris[i].a].y;
+
+ v2.x = m_vertices[m_tris[i].c].z - m_vertices[m_tris[i].a].z;
+ v2.y = m_uv[m_tris[i].c].x - m_uv[m_tris[i].a].x;
+ v2.z = m_uv[m_tris[i].c].y - m_uv[m_tris[i].a].y;
+
+ z_vec = CrossProduct(v1, v2);
+
+ m_tris[i].tangent.x = -(x_vec.y/x_vec.x);
+ m_tris[i].tangent.y = -(y_vec.y/y_vec.x);
+ m_tris[i].tangent.z = -(z_vec.y/z_vec.x);
+
+ m_tris[i].binormal.x = -(x_vec.z/x_vec.x);
+ m_tris[i].binormal.y = -(y_vec.z/y_vec.x);
+ m_tris[i].binormal.z = -(z_vec.z/z_vec.x);
+
+ }
+
+ // now for each vertex build a list of face that share this vertex
+ std::vector< std::vector<int> > array;
+ array.resize(m_vertices.size());
+ for (int i=0; i<(int)m_triangles.size(); i++)
+ {
+ uint k = m_tris[i].a;
+ array[k].push_back(i);
+
+ k = m_tris[i].b;
+ array[k].push_back(i);
+
+ k = m_tris[i].c;
+ array[k].push_back(i);
+ }
+
+ // now average the tangents and compute the binormals as (tangent X normal)
+ for (int i=0; i<(int)m_vertices.size(); i++)
+ {
+ v1 = zero3;
+ v2 = zero3;
+ int t = array[i].size();
+
+ for (int k=0; k<t; k++)
+ {
+ v1.x += m_tris[array[i][k]].tangent.x;
+ v1.y += m_tris[array[i][k]].tangent.y;
+ v1.z += m_tris[array[i][k]].tangent.z;
+
+ v2.x += m_tris[array[i][k]].binormal.x;
+ v2.y += m_tris[array[i][k]].binormal.y;
+ v2.z += m_tris[array[i][k]].binormal.z;
+ }
+ m_tangents[i] = NormalizeVector(v1);
+ //m_binormals[i] = NormalizeVector(v2);
+
+ m_binormals[i] = NormalizeVector(CrossProduct(m_tangents[i], m_normals[i]));
+ }
+}
+
+void LMesh::Optimize(LOptimizationLevel value)
+{
+ switch (value)
+ {
+ case oNone:
+ TransformVertices();
+ break;
+ case oSimple:
+ TransformVertices();
+ CalcNormals(false);
+ break;
+ case oFull:
+ TransformVertices();
+ CalcNormals(true);
+ CalcTextureSpace();
+ break;
+ }
+}
+
+void LMesh::SetTri(const LTri &tri, uint index)
+{
+ if (index >= m_triangles.size())
+ return;
+ m_tris[index] = tri;
+}
+
+LTri& LMesh::GetTri(uint index)
+{
+ return m_tris[index];
+}
+
+uint LMesh::GetMaterial(uint index)
+{
+ return m_materials[index];
+}
+
+uint LMesh::AddMaterial(uint id)
+{
+ m_materials.push_back(id);
+ return m_materials.size()-1;
+}
+
+uint LMesh::GetMaterialCount()
+{
+ return m_materials.size();
+}
+
+//-------------------------------------------------------
+// LLight implementation
+//-------------------------------------------------------
+
+LLight::LLight()
+: LObject()
+{
+ Clear();
+}
+
+LLight::~LLight()
+{
+
+}
+
+void LLight::Clear()
+{
+ m_pos.x = m_pos.y = m_pos.z = 0.0f;
+ m_color.r = m_color.g = m_color.b = 0.0f;
+ m_spotlight = false;
+}
+
+void LLight::SetPosition(LVector3 vec)
+{
+ m_pos = vec;
+}
+
+LVector3 LLight::GetPosition()
+{
+ return m_pos;
+}
+
+void LLight::SetColor(LColor3 color)
+{
+ m_color = color;
+}
+
+LColor3 LLight::GetColor()
+{
+ return m_color;
+}
+
+void LLight::SetSpotlight(bool value)
+{
+ m_spotlight = value;
+}
+
+bool LLight::GetSpotlight()
+{
+ return m_spotlight;
+}
+
+void LLight::SetTarget(LVector3 target)
+{
+ m_target = target;
+}
+
+LVector3 LLight::GetTarget()
+{
+ return m_target;
+}
+
+void LLight::SetHotspot(float value)
+{
+ m_hotspot = value;
+}
+
+float LLight::GetHotspot()
+{
+ return m_hotspot;
+}
+
+void LLight::SetFalloff(float value)
+{
+ m_falloff = value;
+}
+
+float LLight::GetFalloff()
+{
+ return m_falloff;
+}
+
+//-------------------------------------------------------
+// LImporter implementation
+//-------------------------------------------------------
+
+LImporter::LImporter()
+{
+ Clear();
+}
+
+LImporter::~LImporter()
+{
+ Clear();
+}
+
+uint LImporter::GetMeshCount()
+{
+ return m_meshes.size();
+}
+
+uint LImporter::GetLightCount()
+{
+ return m_lights.size();
+}
+
+uint LImporter::GetMaterialCount()
+{
+ return m_materials.size();
+}
+
+LMesh& LImporter::GetMesh(uint index)
+{
+ return m_meshes[index];
+}
+
+LLight& LImporter::GetLight(uint index)
+{
+ return m_lights[index];
+}
+
+LMaterial& LImporter::GetMaterial(uint index)
+{
+ return m_materials[index];
+}
+
+LMaterial* LImporter::FindMaterial(const std::string& name)
+{
+ for (uint i=0; i<m_materials.size(); i++)
+ if (m_materials[i].IsObject(name))
+ return &m_materials[i];
+ return 0;
+}
+
+LMesh* LImporter::FindMesh(const std::string& name)
+{
+ for (uint i=0; i<m_meshes.size(); i++)
+ if (m_meshes[i].IsObject(name))
+ return &m_meshes[i];
+ return 0;
+}
+
+LLight* LImporter::FindLight(const std::string& name)
+{
+ for (uint i=0; i<m_lights.size(); i++)
+ if (m_lights[i].IsObject(name))
+ return &m_lights[i];
+ return 0;
+}
+
+void LImporter::Clear()
+{
+ m_meshes.clear();
+ m_lights.clear();
+ m_materials.clear();
+ m_optLevel = oFull;
+}
+
+void LImporter::SetOptimizationLevel(LOptimizationLevel value)
+{
+ m_optLevel = value;
+}
+
+LOptimizationLevel LImporter::GetOptimizationLevel()
+{
+ return m_optLevel;
+}
+
+//-------------------------------------------------------
+// L3DS implementation
+//-------------------------------------------------------
+
+L3DS::L3DS()
+: LImporter()
+{
+ m_buffer = 0;
+ m_bufferSize = 0;
+ m_pos = 0;
+ m_eof = false;
+}
+
+L3DS::L3DS(const char *filename)
+: LImporter()
+{
+ m_buffer = 0;
+ m_bufferSize = 0;
+ m_pos = 0;
+ m_eof = false;
+ LoadFile(filename);
+}
+
+L3DS::~L3DS()
+{
+ if (m_bufferSize > 0)
+ free(m_buffer);
+}
+
+bool L3DS::LoadFile(const char *filename)
+{
+ FILE *f;
+ f = fopen(filename, "rb");
+ if (f == 0)
+ {
+ ErrorMsg("L3DS::LoadFile - cannot open file");
+ return false;
+ }
+ fseek(f, 0, SEEK_END);
+ m_bufferSize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ m_buffer = (unsigned char*) calloc(m_bufferSize, 1);
+ if (m_buffer == 0)
+ {
+ ErrorMsg("L3DS::LoadFile - not enough memory (malloc failed)");
+ return false;
+ }
+ if (fread(m_buffer, m_bufferSize, 1, f) != 1)
+ {
+ fclose(f);
+ free(m_buffer);
+ m_bufferSize = 0;
+ ErrorMsg("L3DS::LoadFile - error reading from file");
+ return false;
+ }
+ fclose(f);
+ Clear();
+ bool res = Read3DS();
+ free(m_buffer);
+ m_buffer = 0;
+ m_bufferSize = 0;
+ return res;
+}
+
+short L3DS::ReadShort()
+{
+ if ((m_buffer!=0) && (m_bufferSize != 0) && ((m_pos+2)<m_bufferSize))
+ {
+ short *w = (short*)(m_buffer+m_pos);
+ short s = *w;//(short)*(m_buffer+m_pos);
+ m_pos += 2;
+ return s;
+ }
+ m_eof = true;
+ return 0;
+}
+
+int L3DS::ReadInt()
+{
+ if ((m_buffer!=0) && (m_bufferSize != 0) && ((m_pos+4)<m_bufferSize))
+ {
+ int *w = (int*)(m_buffer+m_pos);
+ int s = *w;//(int)*(m_buffer+m_pos);
+ m_pos += 4;
+ return s;
+ }
+ m_eof = true;
+ return 0;
+}
+
+char L3DS::ReadChar()
+{
+ if ((m_buffer!=0) && (m_bufferSize != 0) && ((m_pos+1)<m_bufferSize))
+ {
+ char s = (char)*(m_buffer+m_pos);
+ m_pos += 1;
+ return s;
+ }
+ m_eof = true;
+ return 0;
+}
+
+float L3DS::ReadFloat()
+{
+ if ((m_buffer!=0) && (m_bufferSize != 0) && ((m_pos+4)<m_bufferSize))
+ {
+ float *w = (float*)(m_buffer+m_pos);
+ float s = *w;//(float)*(m_buffer+m_pos);
+ m_pos += 4;
+ return s;
+ }
+ m_eof = true;
+ return 0.0;
+}
+
+byte L3DS::ReadByte()
+{
+ if ((m_buffer!=0) && (m_bufferSize != 0) && ((m_pos+1)<m_bufferSize))
+ {
+ byte s = (byte)*(m_buffer+m_pos);
+ m_pos += 1;
+ return s;
+ }
+ m_eof = true;
+ return 0;
+}
+
+int L3DS::ReadASCIIZ(char *buf, int max_count)
+{
+ int count;
+ if ((m_buffer==0) || (m_bufferSize == 0) || (m_pos>=m_bufferSize))
+ {
+ count = 0;
+ m_eof = true;
+ return count;
+ }
+ count = 0;
+ char c = ReadChar();
+ while ((c!=0) && (count<max_count-1))
+ {
+ buf[count] = c;
+ count ++;
+ c = ReadChar();
+ }
+ buf[count] = 0;
+ return count;
+}
+
+void L3DS::Seek(int offset, int origin)
+{
+ if (origin == SEEK_START)
+ m_pos = offset;
+ if (origin == SEEK_CURSOR)
+ m_pos += offset;
+ if (m_pos >= m_bufferSize)
+ m_pos = m_bufferSize-1;
+ m_eof = false;
+}
+
+uint L3DS::Pos()
+{
+ return m_pos;
+}
+
+LChunk L3DS::ReadChunk()
+{
+ LChunk chunk;
+ chunk.id = ReadShort();
+ int a = ReadInt();
+ chunk.start = Pos();
+ chunk.end = chunk.start+a-6;
+ return chunk;
+}
+
+bool L3DS::FindChunk(LChunk &target, const LChunk &parent)
+{
+ if (Pos() >= parent.end)
+ return false;
+ LChunk chunk;
+ chunk = ReadChunk();
+ while (( chunk.id != target.id) && (chunk.end <= parent.end))
+ {
+ SkipChunk(chunk);
+ if (chunk.end >= parent.end)
+ break;
+ chunk = ReadChunk();
+ }
+ if (chunk.id == target.id)
+ {
+ target.start = chunk.start;
+ target.end = chunk.end;
+ return true;
+ }
+ return false;
+}
+
+void L3DS::SkipChunk(const LChunk &chunk)
+{
+ Seek(chunk.end, SEEK_START);
+}
+
+void L3DS::GotoChunk(const LChunk &chunk)
+{
+ Seek(chunk.start, SEEK_START);
+}
+
+LColor3 L3DS::ReadColor(const LChunk &chunk)
+{
+ LColor3 col = black;
+ GotoChunk(chunk);
+ switch (chunk.id)
+ {
+ case COLOR_F:
+ col.r = ReadFloat();
+ col.g = ReadFloat();
+ col.b = ReadFloat();
+ break;
+ case COLOR_24:
+ col.r = ReadByte()/255.0f;
+ col.g = ReadByte()/255.0f;
+ col.b = ReadByte()/255.0f;
+ break;
+ case LIN_COLOR_F:
+ col.r = ReadFloat();
+ col.g = ReadFloat();
+ col.b = ReadFloat();
+ break;
+ case LIN_COLOR_24:
+ col.r = ReadByte()/255.0f;
+ col.g = ReadByte()/255.0f;
+ col.b = ReadByte()/255.0f;
+ break;
+ default:
+ ErrorMsg("L3DS::ReadColor - error this is not a color chunk");
+ }
+ return col;
+}
+
+float L3DS::ReadPercentage(const LChunk &chunk)
+{
+ GotoChunk(chunk);
+ switch (chunk.id)
+ {
+ case INT_PERCENTAGE:
+ return (ReadShort()/100.0f);
+ case FLOAT_PERCENTAGE:
+ return ReadFloat();
+ }
+ ErrorMsg("L3DS::ReadPercentage - error, the chunk is not a percentage chunk");
+ return 0;
+}
+
+bool L3DS::Read3DS()
+{
+ LChunk mainchunk;
+ LChunk edit;
+ edit.id = EDIT3DS;
+ mainchunk = ReadChunk();
+ if (mainchunk.id != MAIN3DS)
+ {
+ ErrorMsg("L3DS::Read3DS - wrong file format");
+ return false;
+ }
+ if (!FindChunk(edit, mainchunk))
+ return false;
+ LChunk obj;
+ LChunk ml;
+
+ GotoChunk(edit);
+ obj.id = MAT_ENTRY;
+ while (FindChunk(obj, edit))
+ {
+ ReadMaterial(obj);
+ SkipChunk(obj);
+ }
+ GotoChunk(edit);
+
+ obj.id = EDIT_OBJECT;
+ {
+ while (FindChunk(obj, edit))
+ {
+ ReadASCIIZ(m_objName, 99);
+ ml = ReadChunk();
+ if (ml.id == OBJ_TRIMESH)
+ ReadMesh(ml);
+ if (ml.id == OBJ_LIGHT)
+ ReadLight(ml);
+ SkipChunk(obj);
+ }
+ }
+
+ // read the keyframer data here to find out correct object orientation
+
+ LChunk keyframer;
+ keyframer.id = KFDATA;
+
+ LChunk objtrack;
+ objtrack.id = OBJECT_NODE_TAG;
+
+ GotoChunk(mainchunk);
+ if (FindChunk(keyframer, mainchunk))
+ { // keyframer chunk is present
+ GotoChunk(keyframer);
+ while (FindChunk(objtrack, keyframer))
+ {
+ ReadKeyframeData(objtrack);
+ SkipChunk(objtrack);
+ }
+ }
+
+ for (uint i=0; i<m_meshes.size(); i++) {
+ m_meshes[i].Optimize(m_optLevel);
+ }
+
+ m_pos = 0;
+ strcpy(m_objName, "");
+ return true;
+}
+
+void L3DS::ReadLight(const LChunk &parent)
+{
+ float t;
+ LVector3 v;
+ LLight light;
+ light.SetName(m_objName);
+ GotoChunk(parent);
+ LChunk chunk = ReadChunk();
+ if (chunk.id == OBJ_LIGHT)
+ {
+ v.x = ReadFloat();
+ v.y = ReadFloat();
+ v.z = ReadFloat();
+ if (chunk.end < parent.end)
+ chunk = ReadChunk();
+ while (chunk.end <= parent.end)
+ {
+ switch (chunk.id)
+ {
+ case COLOR_24:
+ case COLOR_F:
+ case LIN_COLOR_F:
+ case LIN_COLOR_24:
+ light.SetColor(ReadColor(chunk));
+ break;
+ case SPOTLIGHT:
+ v.x = ReadFloat();
+ v.y = ReadFloat();
+ v.z = ReadFloat();
+ light.SetTarget(v);
+ t = ReadFloat();
+ light.SetHotspot(t);
+ t = ReadFloat();
+ light.SetFalloff(t);
+ break;
+ default:
+ break;
+ }
+ SkipChunk(chunk);
+ if (chunk.end >= parent.end)
+ break;
+ chunk = ReadChunk();
+
+ }
+ }
+ m_lights.push_back(light);
+}
+
+void L3DS::ReadMesh(const LChunk &parent)
+{
+ unsigned short count, i;
+ LVector4 p;
+ LMatrix4 m;
+ LVector2 t;
+ p.w = 1.0f;
+ LMesh mesh;
+ mesh.SetName(m_objName);
+ GotoChunk(parent);
+ LChunk chunk = ReadChunk();
+ while (chunk.end <= parent.end)
+ {
+ switch (chunk.id)
+ {
+ case TRI_VERTEXLIST:
+ count = ReadShort();
+ mesh.SetVertexArraySize(count);
+ for (i=0; i < count; i++)
+ {
+ p.x = ReadFloat();
+ p.y = ReadFloat();
+ p.z = ReadFloat();
+ mesh.SetVertex(p, i);
+ }
+ break;
+ case TRI_FACEMAPPING:
+ count = ReadShort();
+ if (mesh.GetVertexCount() == 0)
+ mesh.SetVertexArraySize(count);
+ for (i=0; i < count; i++)
+ {
+ t.x = ReadFloat();
+ t.y = ReadFloat();
+ mesh.SetUV(t, i);
+ }
+ break;
+ case TRI_FACELIST:
+ ReadFaceList(chunk, mesh);
+ break;
+ case TRI_MATRIX:
+ {
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 3; j++)
+ m.m[i][j] = ReadFloat();
+
+ m.m[0][3] = 0.0f;
+ m.m[1][3] = 0.0f;
+ m.m[2][3] = 0.0f;
+ m.m[3][3] = 1.0f;
+
+ mesh.SetMatrix(m);
+ }
+ break;
+ default:
+ break;
+ }
+ SkipChunk(chunk);
+ if (chunk.end >= parent.end)
+ break;
+ chunk = ReadChunk();
+ }
+ m_meshes.push_back(mesh);
+}
+
+void L3DS::ReadFaceList(const LChunk &chunk, LMesh &mesh)
+{
+ // variables
+ unsigned short count, t;
+ uint i;
+ LTri tri;
+ LChunk ch;
+ char str[20];
+ //uint mat;
+
+ // consistency checks
+ if (chunk.id != TRI_FACELIST)
+ {
+ ErrorMsg("L3DS::ReadFaceList - internal error: wrong chunk passed as parameter");
+ return;
+ }
+ GotoChunk(chunk);
+ tri.smoothingGroups = 1;
+ // read the number of faces
+ count = ReadShort();
+ mesh.SetTriangleArraySize(count);
+ for (i=0; i<count; i++)
+ {
+ tri.a = ReadShort();
+ tri.b = ReadShort();
+ tri.c = ReadShort();
+ ReadShort();
+ mesh.SetTri(tri, i);
+ }
+ // now read the optional chunks
+ ch = ReadChunk();
+ while (ch.end <= chunk.end)
+ {
+ switch (ch.id)
+ {
+ case TRI_MAT_GROUP: {
+ ReadASCIIZ(str, 20);
+ int mat_id = FindMaterial(str)->GetID();
+ mesh.AddMaterial(mat_id);
+ count = ReadShort();
+ for (i=0; i<count; i++) {
+ t = ReadShort();
+ mesh.GetTri(t).materialId = mat_id;
+ }
+ }
+ break;
+
+ case TRI_SMOOTH_GROUP:
+ for (i=0; i<mesh.GetTriangleCount(); i++)
+ mesh.GetTri(i).smoothingGroups = (ulong) ReadInt();
+ break;
+ }
+ SkipChunk(ch);
+ ch = ReadChunk();
+ }
+}
+
+void L3DS::ReadMaterial(const LChunk &parent)
+{
+ // variables
+ LChunk chunk;
+ LChunk child;
+ char str[30];
+ LMaterial mat;
+ short sh;
+
+ GotoChunk(parent);
+
+ chunk = ReadChunk();
+ while (chunk.end <= parent.end)
+ {
+ switch (chunk.id)
+ {
+ case MAT_NAME:
+ ReadASCIIZ(str, 30);
+ mat.SetName(str);
+ break;
+ case MAT_AMBIENT:
+ child = ReadChunk();
+ mat.SetAmbientColor(ReadColor(child));
+ break;
+ case MAT_DIFFUSE:
+ child = ReadChunk();
+ mat.SetDiffuseColor(ReadColor(child));
+ break;
+ case MAT_SPECULAR:
+ child = ReadChunk();
+ mat.SetSpecularColor(ReadColor(child));
+ break;
+ case MAT_SHININESS:
+ child = ReadChunk();
+ mat.SetShininess(ReadPercentage(child));
+ break;
+ case MAT_TRANSPARENCY:
+ child = ReadChunk();
+ mat.SetTransparency(ReadPercentage(child));
+ break;
+ case MAT_SHADING:
+ sh = ReadShort();
+ switch (sh)
+ {
+ case 0:
+ mat.SetShadingType(sWireframe);
+ break;
+ case 1:
+ mat.SetShadingType(sFlat);
+ break;
+ case 2:
+ mat.SetShadingType(sGouraud);
+ break;
+ case 3:
+ mat.SetShadingType(sPhong);
+ break;
+ case 4:
+ mat.SetShadingType(sMetal);
+ break;
+ }
+ break;
+ case MAT_WIRE:
+ mat.SetShadingType(sWireframe);
+ break;
+ case MAT_TEXMAP:
+ ReadMap(chunk, mat.GetTextureMap1());
+ break;
+ case MAT_TEX2MAP:
+ ReadMap(chunk, mat.GetTextureMap2());
+ break;
+ case MAT_OPACMAP:
+ ReadMap(chunk, mat.GetOpacityMap());
+ break;
+ case MAT_BUMPMAP:
+ ReadMap(chunk, mat.GetBumpMap());
+ break;
+ case MAT_SPECMAP:
+ ReadMap(chunk, mat.GetSpecularMap());
+ break;
+ case MAT_REFLMAP:
+ child = ReadChunk();
+ mat.GetReflectionMap().strength = ReadPercentage(child);
+ SkipChunk(child);
+ child = ReadChunk();
+ if (child.id != MAT_MAPNAME)
+ {
+ ErrorMsg("L3DS::ReadMaterial - error, expected chunk not found");
+ return;
+ }
+ ReadASCIIZ(str, 30);
+ if (strcmp(str, "") == 0)
+ strcpy(mat.GetReflectionMap().mapName, "auto");
+ break;
+ }
+
+ SkipChunk(chunk);
+ chunk = ReadChunk();
+ }
+ m_materials.push_back(mat);
+ m_materials[m_materials.size()-1].SetID(m_materials.size()-1);
+}
+
+void L3DS::ReadMap(const LChunk &chunk, LMap& map)
+{
+ LChunk child;
+ char str[20];
+ GotoChunk(chunk);
+ child = ReadChunk();
+ while (child.end <= chunk.end)
+ {
+ switch (child.id)
+ {
+ case INT_PERCENTAGE:
+ map.strength = ReadPercentage(child);
+ break;
+ case MAT_MAPNAME:
+ ReadASCIIZ(str, 20);
+ strcpy(map.mapName, str);
+ break;
+ case MAT_MAP_USCALE:
+ map.uScale = ReadFloat();
+ break;
+ case MAT_MAP_VSCALE:
+ map.vScale = ReadFloat();
+ break;
+ case MAT_MAP_UOFFSET:
+ map.uOffset = ReadFloat();
+ break;
+ case MAT_MAP_VOFFSET:
+ map.vOffset = ReadFloat();
+ break;
+ case MAT_MAP_ANG:
+ map.angle = ReadFloat();
+ break;
+ }
+ SkipChunk(child);
+ child = ReadChunk();
+ }
+}
+
+void L3DS::ReadKeyframeData(const LChunk &parent)
+{
+ uint frames = 0;
+
+ LChunk node_hdr;
+ node_hdr.id = NODE_HDR;
+
+ char str[20];
+ LMesh *mesh;
+
+ GotoChunk(parent);
+ if (!FindChunk(node_hdr, parent))
+ return;
+ GotoChunk(node_hdr);
+ ReadASCIIZ(str, 19);
+ mesh = FindMesh(str);
+ if (mesh == 0)
+ return;
+ GotoChunk(parent);
+
+ // read the pivot
+ LVector3 pivot = zero3;
+
+ LChunk pivotchunk;
+ pivotchunk.id = PIVOT;
+ if (FindChunk(pivotchunk, parent))
+ {
+ GotoChunk(pivotchunk);
+ pivot.x = ReadFloat();
+ pivot.y = ReadFloat();
+ pivot.z = ReadFloat();
+ }
+ GotoChunk(parent);
+
+ // read frame 0 from the position track
+ LVector3 pos = zero3;
+
+ frames = 0;
+
+ LChunk poschunk;
+ poschunk.id = POS_TRACK_TAG;
+ if (FindChunk(poschunk, parent))
+ {
+ GotoChunk(poschunk);
+ // read the trackheader structure
+ ReadShort();
+ ReadInt();
+ ReadInt();
+ frames = ReadInt();
+ if (frames > 0)
+ {
+ ReadKeyheader();
+ pos.x = ReadFloat();
+ pos.y = ReadFloat();
+ pos.z = ReadFloat();
+ }
+ }
+ GotoChunk(parent);
+
+ // now read the rotation track
+ LVector4 rot = zero4;
+
+ LChunk rotchunk;
+ rotchunk.id = ROT_TRACK_TAG;
+
+ frames = 0;
+ if (FindChunk(rotchunk, parent))
+ {
+ GotoChunk(rotchunk);
+ // read the trackheader structure
+ ReadShort();
+ ReadInt();
+ ReadInt();
+ frames = ReadInt();
+ if (frames > 0)
+ {
+ ReadKeyheader();
+ rot.x = ReadFloat();
+ rot.y = ReadFloat();
+ rot.z = ReadFloat();
+ rot.w = ReadFloat();
+ }
+ }
+ GotoChunk(parent);
+
+ // now read the scaling chunk
+ LVector3 scale;
+ scale.x = 1;
+ scale.y = 1;
+ scale.z = 1;
+
+ LChunk scalechunk;
+ scalechunk.id = SCL_TRACK_TAG;
+
+ frames = 0;
+
+ if (FindChunk(scalechunk, parent))
+ {
+ GotoChunk(scalechunk);
+ // read the trackheader structure
+ ReadShort();
+ ReadInt();
+ ReadInt();
+ frames = ReadInt();
+ if (frames > 0)
+ {
+ ReadKeyheader();
+ scale.x = ReadFloat();
+ scale.y = ReadFloat();
+ scale.z = ReadFloat();
+ }
+ }
+ GotoChunk(parent);
+
+
+}
+
+long L3DS::ReadKeyheader()
+{
+ long frame;
+ frame = ReadInt();
+ short opts = ReadShort();
+ if (opts & 32768) // 32768 is 1000000000000000 binary
+ { // tension is present
+ ReadFloat();
+ }
+ if (opts & 16384) // 16384 is 0100000000000000 binary
+ { // continuity is present
+ ReadFloat();
+ }
+ if (opts & 8192)
+ { // bias info present
+ ReadFloat();
+ }
+ if (opts & 4096)
+ { // "ease to" present
+ ReadFloat();
+ }
+ if (opts & 2048)
+ { // "ease from" present
+ ReadFloat();
+ }
+ return frame;
+} \ No newline at end of file
diff --git a/MagicEx/l3ds.h b/MagicEx/l3ds.h
new file mode 100644
index 0000000..ea0a032
--- /dev/null
+++ b/MagicEx/l3ds.h
@@ -0,0 +1,457 @@
+// copyright (c) 2001-2002 Lev Povalahev
+// this is a 3ds importer version 2
+
+#ifndef L3DS_H
+#define L3DS_H
+
+// includes
+#include <vector>
+#include <string>
+
+//---------------------------------------------------------
+
+typedef unsigned int uint;
+typedef unsigned char byte;
+
+enum LShading {sWireframe, sFlat, sGouraud, sPhong, sMetal};
+
+enum LOptimizationLevel {oNone, oSimple, oFull};
+
+// for internal use
+struct LChunk;
+struct LTri;
+
+//------------------------------------------------
+
+struct LVector4
+{
+ float x;
+ float y;
+ float z;
+ float w;
+
+ int operator==(const LVector4& v) const { return x==v.x && y==v.y && z==v.z && w==v.w; }
+};
+
+struct LVector3
+{
+ float x;
+ float y;
+ float z;
+
+ int operator==(const LVector3& v) const { return x==v.x && y==v.y && z==v.z; }
+};
+
+struct LVector2
+{
+ float x;
+ float y;
+
+ int operator==(const LVector2& v) const { return x==v.x && y==v.y; }
+};
+
+struct LColor3
+{
+ float r;
+ float g;
+ float b;
+
+ int operator==(const LColor3& v) const { return r==v.r && g==v.g && b==v.b; }
+};
+
+//------------------------------------------------
+
+struct LTriangle
+{
+ unsigned short a;
+ unsigned short b;
+ unsigned short c;
+};
+
+struct LMatrix4
+{
+ float m[4][4];
+};
+
+struct LTriangle2
+{
+ LVector4 vertices[3];
+ LVector3 vertexNormals[3];
+ LVector2 textureCoords[3];
+ LVector3 faceNormal;
+ uint materialId;
+};
+
+// a structure for a texture map
+struct LMap
+{
+ // the strength of the texture map
+ float strength;
+ // the file name of the map. only 8.3 format in 3ds files :(
+ char mapName[255];
+ float uScale;
+ float vScale;
+ float uOffset;
+ float vOffset;
+ float angle;
+};
+
+//------------------------------------------------
+
+class LObject
+{
+public:
+ // the default constructor, initilializes the m_name here
+ LObject();
+ // the destructor frees memory (m_name)
+ virtual ~LObject();
+ // call this to get the name of the object
+ virtual const std::string& GetName();
+
+ // this methods should not be used by the "user", they're used internally to fill the class
+ // with valid data when reading from file. If you're about to add an importer for another format you'LL
+ // have to use these methods
+ // call this to set the name of the object
+ virtual void SetName(const std::string& value);
+ // returns true if the object's name is the name passed as parameter
+ bool IsObject(const std::string &name);
+protected:
+ // the name of the object
+ std::string m_name;
+};
+
+//------------------------------------------------
+
+class LMaterial : public LObject
+{
+public:
+ // the default constructor, does the initialization
+ LMaterial();
+ // the destructor
+ virtual ~LMaterial();
+ // returns the material ID
+ uint GetID();
+ // returns the pointer to teh texture map 1
+ LMap& GetTextureMap1();
+ // returns the pointer to the texture map 2
+ LMap& GetTextureMap2();
+ // returns the pointer to teh opacity map
+ LMap& GetOpacityMap();
+ // returns the pointer to the specular (gloss) map
+ LMap& GetSpecularMap();
+ // returns the pointer to the bump map
+ LMap& GetBumpMap();
+ // returns the pointer to the reflection map
+ LMap& GetReflectionMap();
+ // returns the ambient color of the material
+ LColor3 GetAmbientColor();
+ // returns the diffuse color of the material
+ LColor3 GetDiffuseColor();
+ // returns the specular color of the material
+ LColor3 GetSpecularColor();
+ // returns the shininess of the material, ranging from 0(matte) to 1(shiny)
+ float GetShininess();
+ // returns the transparency of the material, ranging from 1(fully transparent) to 0(opaque)
+ float GetTransparency();
+ // returns the type of shading, see LShading type
+ LShading GetShadingType();
+
+ // this methods should not be used by the "user", they're used internally to fill the class
+ // with valid data when reading from file. If you're about to add an importer for another format you'LL
+ // have to use these methods
+ // sets the material ID to "value"
+ void SetID(uint value);
+ // call this to set the ambient color of the material
+ void SetAmbientColor(const LColor3 &color);
+ // sets the diffuse color of the material
+ void SetDiffuseColor(const LColor3 &color);
+ // sets the specular color of the material
+ void SetSpecularColor(const LColor3 &color);
+ // sets the shininess of the material
+ void SetShininess(float value);
+ // sets the transparency of the material
+ void SetTransparency(float value);
+ // sets the shading type
+ void SetShadingType(LShading shading);
+protected:
+ // the unique material ID
+ int m_id;
+ // the first texture map
+ LMap m_texMap1;
+ // the second texture map
+ LMap m_texMap2;
+ // the opacity map
+ LMap m_opacMap;
+ // the reflection map
+ LMap m_reflMap;
+ // the bump map
+ LMap m_bumpMap;
+ // specular map
+ LMap m_specMap;
+ // material ambient color
+ LColor3 m_ambient;
+ // material diffuse color
+ LColor3 m_diffuse;
+ // material specular color
+ LColor3 m_specular;
+ // shininess
+ float m_shininess;
+ // transparency
+ float m_transparency;
+ // the shading type for the material
+ LShading m_shading;
+};
+
+//------------------------------------------------
+
+class LMesh : public LObject
+{
+public:
+ // the default constructor
+ LMesh();
+ // the destructor
+ virtual ~LMesh();
+ // clears the mesh, deleteing all data
+ void Clear();
+ // returns the number of vertices in the mesh
+ uint GetVertexCount();
+ // sets the the size fo the vertex array - for internal use
+ void SetVertexArraySize(uint value);
+ // returns the number of triangles in the mesh
+ uint GetTriangleCount();
+ // sets the size of the triangle array - for internal use
+ void SetTriangleArraySize(uint value);
+ // returns given vertex
+ const LVector4& GetVertex(uint index);
+ // returns the given normal
+ const LVector3& GetNormal(uint index);
+ // returns the given texture coordinates vector
+ const LVector2& GetUV(uint index);
+ // returns the pointer to the array of tangents
+ const LVector3& GetTangent(uint index);
+ // returns the pointer to the array of binormals
+ const LVector3& GetBinormal(uint index);
+ // sets the vertex at a given index to "vec" - for internal use
+ void SetVertex(const LVector4 &vec, uint index);
+ // sets the normal at a given index to "vec" - for internal use
+ void SetNormal(const LVector3 &vec, uint index);
+ // sets the texture coordinates vector at a given index to "vec" - for internal use
+ void SetUV(const LVector2 &vec, uint index);
+ // sets the tangent at a given index to "vec" - for internal use
+ void SetTangent(const LVector3 &vec, uint index);
+ // sets the binormal at a given index to "vec" - for internal use
+ void SetBinormal(const LVector3 &vec, uint index);
+ // returns the triangle with a given index
+ const LTriangle& GetTriangle(uint index);
+ // returns the triangle with a given index, see LTriangle2 structure description
+ LTriangle2 GetTriangle2(uint index);
+ // returns the mesh matrix, should be identity matrix after loading
+ LMatrix4 GetMatrix();
+ // sets the mesh matrix to a given matrix - for internal use
+ void SetMatrix(LMatrix4 m);
+ // optimizises the mesh using a given optimization level
+ void Optimize(LOptimizationLevel value);
+ // sets an internal triangle structure with index "index" - for internal use only
+ void SetTri(const LTri &tri, uint index);
+ // returns the pointer to the internal triangle structure - for internal use only
+ LTri& GetTri(uint index);
+ // returns the material id with a given index for the mesh
+ uint GetMaterial(uint index);
+ // adds a material to the mesh and returns its index - for internal use
+ uint AddMaterial(uint id);
+ // returns the number of materials used in the mesh
+ uint GetMaterialCount();
+protected:
+ // the vertices, normals, etc.
+ std::vector<LVector4> m_vertices;
+ std::vector<LVector3> m_normals;
+ std::vector<LVector3> m_binormals;
+ std::vector<LVector3> m_tangents;
+ std::vector<LVector2> m_uv;
+
+ // triangles
+ std::vector<LTriangle> m_triangles;
+
+ //used internally
+ std::vector<LTri> m_tris;
+
+ // the transformation matrix.
+ LMatrix4 m_matrix;
+
+ // the material ID array
+ std::vector<uint> m_materials;
+
+ // calculates the normals, either using the smoothing groups information or not
+ void CalcNormals(bool useSmoothingGroups);
+ // calculates the texture(tangent) space for each vertex
+ void CalcTextureSpace();
+ // transforms the vertices by the mesh matrix
+ void TransformVertices();
+};
+
+//------------------------------------------------
+
+class LLight : public LObject
+{
+public:
+ // the default constructor
+ LLight();
+ // the destructor
+ virtual ~LLight();
+ // clears the data the class holds
+ void Clear();
+ // sets the position of the light source - for internal use
+ void SetPosition(LVector3 vec);
+ // returns the position of the light source
+ LVector3 GetPosition();
+ // sets the color of the light - for internal use
+ void SetColor(LColor3 color);
+ // returns the color of the light
+ LColor3 GetColor();
+ // sets whether the light is a spotlight or not - internal use
+ void SetSpotlight(bool value);
+ // returns true if the light is a spotlight
+ bool GetSpotlight();
+ // sets the target of the light - internal use
+ void SetTarget(LVector3 target);
+ // returns the target of the spotlight
+ LVector3 GetTarget();
+ // sets the hotspot - internal use
+ void SetHotspot(float value);
+ // returns the hotspot
+ float GetHotspot();
+ // sets falloff - internal use
+ void SetFalloff(float value);
+ // returns falloff
+ float GetFalloff();
+protected:
+ LVector3 m_pos;
+ LColor3 m_color;
+ bool m_spotlight;
+ LVector3 m_target;
+ float m_hotspot;
+ float m_falloff;
+};
+
+//------------------------------------------------
+
+class LImporter
+{
+public:
+ // the default constructor
+ LImporter();
+ // the destructor
+ virtual ~LImporter();
+ // reads the model from a file, must be overriden by the child classes
+ virtual bool LoadFile(const char *filename) = 0;
+ // returns the number of meshes in the scene
+ uint GetMeshCount();
+ // returns the number of lights in the scene
+ uint GetLightCount();
+ // returns the number of materials in the scene
+ uint GetMaterialCount();
+ // returns a pointer to a mesh
+ LMesh& GetMesh(uint index);
+ // returns a pointer to a light at a given index
+ LLight& GetLight(uint index);
+ // returns the pointer to the material
+ LMaterial& GetMaterial(uint index);
+ // returns the pointer to the material with a given name, or NULL if the material was not found
+ LMaterial* FindMaterial(const std::string &name);
+ // returns the pointer to the mesh with a given name, or NULL if the mesh with such name
+ // is not present in the scene
+ LMesh* FindMesh(const std::string &name);
+ // returns the pointer to the light with a given name, or NULL if not found
+ LLight* FindLight(const std::string &name);
+ // sets the optimization level to a given value
+ void SetOptimizationLevel(LOptimizationLevel value);
+ // returns the current optimization level
+ LOptimizationLevel GetOptimizationLevel();
+protected:
+ // the lights found in the scene
+ std::vector<LLight> m_lights;
+ // triangular meshes
+ std::vector<LMesh> m_meshes;
+ // the materials in the scene
+ std::vector<LMaterial> m_materials;
+ // level of optimization to perform on the meshes
+ LOptimizationLevel m_optLevel;
+ // clears all data.
+ virtual void Clear();
+};
+//------------------------------------------------
+
+class L3DS : public LImporter
+{
+public:
+ // the default contructor
+ L3DS();
+ // constructs the object and loads the file
+ L3DS(const char *filename);
+ // destructor
+ virtual ~L3DS();
+ // load 3ds file
+ virtual bool LoadFile(const char *filename);
+protected:
+ // used internally for reading
+ char m_objName[100];
+ // true if end of file is reached
+ bool m_eof;
+ // buffer for loading, used for speedup
+ unsigned char *m_buffer;
+ // the size of the buffer
+ uint m_bufferSize;
+ // the current cursor position in the buffer
+ uint m_pos;
+
+ // reads a short value from the buffer
+ short ReadShort();
+ // reads an int value from the buffer
+ int ReadInt();
+ // reads a char from the buffer
+ char ReadChar();
+ //reada a floatvalue from the buffer
+ float ReadFloat();
+ //reads an unsigned byte from the buffer
+ byte ReadByte();
+ //reads an asciiz string
+ int ReadASCIIZ(char *buf, int max_count);
+ // seek wihtin the buffer
+ void Seek(int offset, int origin);
+ // returns the position of the cursor
+ uint Pos();
+
+ // read the chunk and return it.
+ LChunk ReadChunk();
+ // read until given chunk is found
+ bool FindChunk(LChunk &target, const LChunk &parent);
+ // skip to the end of chunk "chunk"
+ void SkipChunk(const LChunk &chunk);
+ // goes to the beginning of the data in teh given chunk
+ void GotoChunk(const LChunk &chunk);
+
+ // the function read the color chunk (any of the color chunks)
+ LColor3 ReadColor(const LChunk &chunk);
+ // the function that read the percentage chunk and returns a float from 0 to 1
+ float ReadPercentage(const LChunk &chunk);
+ // this is where 3ds file is being read
+ bool Read3DS();
+ // read a light chunk
+ void ReadLight(const LChunk &parent);
+ // read a trimesh chunk
+ void ReadMesh(const LChunk &parent);
+ // reads the face list, face materials, smoothing groups... and fill rthe information into the mesh
+ void ReadFaceList(const LChunk &chunk, LMesh &mesh);
+ // reads the material
+ void ReadMaterial(const LChunk &parent);
+ // reads the map info and fills the given map with this information
+ void ReadMap(const LChunk &chunk, LMap& map);
+ // reads keyframer data of the OBJECT_NODE_TAG chunk
+ void ReadKeyframeData(const LChunk &parent);
+ // reads the keyheader structure from the current offset and returns the frame number
+ long ReadKeyheader();
+};
+
+//---------------------------------------------------------
+
+#endif \ No newline at end of file