From 984d590b3f9325170ba85e4a1349a07f97545a92 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 20 Jun 2010 14:08:51 -0400 Subject: [PATCH] initial commit --- .hgignore | 0 MeshConverter.sln | 20 ++ MeshConverter/MeshConverter.vcxproj | 92 +++++ MeshConverter/src/assets/material.cpp | 14 + MeshConverter/src/assets/material.h | 40 +++ MeshConverter/src/geometry/vector2.h | 11 + MeshConverter/src/geometry/vector3.h | 319 +++++++++++++++++ MeshConverter/src/main.cpp | 77 ++++ MeshConverter/src/md2/md2.cpp | 244 +++++++++++++ MeshConverter/src/md2/md2.h | 91 +++++ MeshConverter/src/obj/obj.cpp | 496 ++++++++++++++++++++++++++ MeshConverter/src/obj/obj.h | 84 +++++ 12 files changed, 1488 insertions(+) create mode 100644 .hgignore create mode 100644 MeshConverter.sln create mode 100644 MeshConverter/MeshConverter.vcxproj create mode 100644 MeshConverter/src/assets/material.cpp create mode 100644 MeshConverter/src/assets/material.h create mode 100644 MeshConverter/src/geometry/vector2.h create mode 100644 MeshConverter/src/geometry/vector3.h create mode 100644 MeshConverter/src/main.cpp create mode 100644 MeshConverter/src/md2/md2.cpp create mode 100644 MeshConverter/src/md2/md2.h create mode 100644 MeshConverter/src/obj/obj.cpp create mode 100644 MeshConverter/src/obj/obj.h diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..e69de29 diff --git a/MeshConverter.sln b/MeshConverter.sln new file mode 100644 index 0000000..d11635a --- /dev/null +++ b/MeshConverter.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MeshConverter", "MeshConverter\MeshConverter.vcxproj", "{AADE0387-ED8A-46E2-B2DA-FC521DF7CB9C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AADE0387-ED8A-46E2-B2DA-FC521DF7CB9C}.Debug|Win32.ActiveCfg = Debug|Win32 + {AADE0387-ED8A-46E2-B2DA-FC521DF7CB9C}.Debug|Win32.Build.0 = Debug|Win32 + {AADE0387-ED8A-46E2-B2DA-FC521DF7CB9C}.Release|Win32.ActiveCfg = Release|Win32 + {AADE0387-ED8A-46E2-B2DA-FC521DF7CB9C}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/MeshConverter/MeshConverter.vcxproj b/MeshConverter/MeshConverter.vcxproj new file mode 100644 index 0000000..ef10192 --- /dev/null +++ b/MeshConverter/MeshConverter.vcxproj @@ -0,0 +1,92 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {AADE0387-ED8A-46E2-B2DA-FC521DF7CB9C} + Win32Proj + MeshConverter + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MeshConverter/src/assets/material.cpp b/MeshConverter/src/assets/material.cpp new file mode 100644 index 0000000..c92f80a --- /dev/null +++ b/MeshConverter/src/assets/material.cpp @@ -0,0 +1,14 @@ +#include "material.h" + +Material::Material(unsigned long ambient, unsigned long diffuse, unsigned long specular, unsigned long emission, std::string texture) +{ + m_ambient = ambient; + m_diffuse = diffuse; + m_specular = specular; + m_emission = emission; + m_texture = texture; +} + +Material::~Material() +{ +} diff --git a/MeshConverter/src/assets/material.h b/MeshConverter/src/assets/material.h new file mode 100644 index 0000000..0c8c6b4 --- /dev/null +++ b/MeshConverter/src/assets/material.h @@ -0,0 +1,40 @@ +#ifndef __MATERIAL_H_INCLUDED__ +#define __MATERIAL_H_INCLUDED__ + +#include + +#define RGB_32(r, g, b, a) ((b)|((g) << 8)|((r) << 16)|((a) << 24)) +#define RGB_24(r, g, b) ((b)|((g) << 8)|((r) << 16)) +#define RGB_32_f(r, g, b, a) RGB_32((int)((r) * 255), (int)((g) * 255), (int)((b) * 255), (int)((a) * 255)) +#define RGB_24_f(r, g, b) RGB_24((int)((r) * 255), (int)((g) * 255), (int)((b) * 255)) + +class Material +{ +public: + Material(unsigned long ambient = 0, unsigned long diffuse = 0, unsigned long specular = 0, unsigned long emission = 0, std::string texture = ""); + virtual ~Material(); + + void SetAmbient(unsigned long ambient) { m_ambient = ambient; } + void SetDiffuse(unsigned long diffuse) { m_diffuse = diffuse; } + void SetSpecular(unsigned long specular) { m_specular = specular; } + void SetEmission(unsigned long emission) { m_emission = emission; } + void SetTexture(std::string texture) { m_texture = texture; } + unsigned long GetAmbient() { return m_ambient; } + unsigned long GetDiffuse() { return m_diffuse; } + unsigned long GetSpecular() { return m_specular; } + unsigned long GetEmission() { return m_emission; } + std::string GetTexture() { return m_texture; } + + static void ApplyDefault(); + static Material GetDefault(); + void Apply(); + +private: + unsigned long m_ambient; + unsigned long m_diffuse; + unsigned long m_specular; + unsigned long m_emission; + std::string m_texture; +}; + +#endif \ No newline at end of file diff --git a/MeshConverter/src/geometry/vector2.h b/MeshConverter/src/geometry/vector2.h new file mode 100644 index 0000000..41633e0 --- /dev/null +++ b/MeshConverter/src/geometry/vector2.h @@ -0,0 +1,11 @@ +#ifndef __VECTOR2_H_INCLUDED__ +#define __VECTOR2_H_INCLUDED__ + +class Vector2 +{ +public: + float x; + float y; +}; + +#endif \ No newline at end of file diff --git a/MeshConverter/src/geometry/vector3.h b/MeshConverter/src/geometry/vector3.h new file mode 100644 index 0000000..debac95 --- /dev/null +++ b/MeshConverter/src/geometry/vector3.h @@ -0,0 +1,319 @@ +#ifndef __VECTOR3_H_INCLUDED__ +#define __VECTOR3_H_INCLUDED__ + +#include + +/** + * Represents a 3D vector and provides common methods/operators + * for vector math + */ +class Vector3 +{ +public: + Vector3() {} + Vector3(float vx, float vy, float vz) { x = vx; y = vy; z = vz; } + Vector3(const float *v) { x = v[0]; y = v[1]; z = v[2]; } + ~Vector3() {} + + static Vector3 Cross(const Vector3 &a, const Vector3 &b); + static float Dot(const Vector3 &a, const Vector3 &b); + static Vector3 Normalize(const Vector3 &a); + static Vector3 SurfaceNormal(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3); + static float Magnitude(const Vector3 &a); + static float SquaredLength(const Vector3 &a); + static Vector3 SetLength(const Vector3 &v, float length); + static float Distance(const Vector3 &a, const Vector3 &b); + static bool IsPointInTriangle(const Vector3 &point, const Vector3 &pa, const Vector3 &pb, const Vector3 &pc); + + float x; + float y; + float z; +}; + +bool operator==(const Vector3 &left, const Vector3 &right); +Vector3 operator-(const Vector3 &left); +Vector3 operator+(const Vector3 &left, const Vector3 &right); +Vector3 &operator+=(Vector3 &left, const Vector3 &right); +Vector3 operator-(const Vector3 &left, const Vector3 &right); +Vector3 &operator-=(Vector3 &left, const Vector3 &right); +Vector3 operator*(const Vector3 &left, float right); +Vector3 &operator*=(Vector3 &left, float right); +Vector3 operator/(const Vector3 &left, float right); +Vector3 &operator/=(Vector3 &left, float right); +Vector3 operator*(const Vector3 &left, const Vector3 &right); +Vector3 &operator*=(Vector3 &left, const Vector3 &right); +Vector3 operator/(const Vector3 &left, const Vector3 &right); +Vector3 &operator/=(Vector3 &left, const Vector3 &right); + +#define ZERO_VECTOR Vector3(0.0f, 0.0f, 0.0f) + +#define X_AXIS Vector3(1.0f, 0.0f, 0.0f) +#define Y_AXIS Vector3(0.0f, 1.0f, 0.0f) +#define Z_AXIS Vector3(0.0f, 0.0f, 1.0f) + +#define UP_VECTOR Vector3(0.0f, 1.0f, 0.0f) + +/** + * Computes the cross product of 2 vectors. + * x = a.y * b.z - b.y * a.z + * y = a.z * b.x - b.z * a.x + * z = a.x * b.y - b.x * a.y + * @param a first vector + * @param b second vector + * + * @return Vector3 the cross product + */ +inline Vector3 Vector3::Cross(const Vector3 &a, const Vector3 &b) +{ + return Vector3( + (a.y * b.z) - (b.y * a.z), + (a.z * b.x) - (b.z * a.x), + (a.x * b.y) - (b.x * a.y) + ); +} + +/** + * Computes the dot product of 2 vectors. + * dot = (a.x * b.x) + (a.y * b.y) + (a.z * b.z) + * @param a first vector + * @param b second vector + * + * @return float the dot product + */ +inline float Vector3::Dot(const Vector3 &a, const Vector3 &b) +{ + return (a.x * b.x) + + (a.y * b.y) + + (a.z * b.z); +} + +/** + * Normalizes a vector + * x = a.x / ||a|| + * y = a.y / ||a|| + * z = a.z / ||a|| + * @param a vector to normalize + * + * @return Vector3 the normalized vector + */ +inline Vector3 Vector3::Normalize(const Vector3 &a) +{ + float magnitudeSquared = (a.x * a.x) + (a.y * a.y) + (a.z * a.z); + if (magnitudeSquared > 0.0f) + { + float inverseMagnitude = 1.0f / sqrtf(magnitudeSquared); + return Vector3( + a.x * inverseMagnitude, + a.y * inverseMagnitude, + a.z * inverseMagnitude + ); + } + else + return a; +} + +/** + * Calculates a normal vector for the given 3 vectors making up + * a triangle (counter-clockwise order) + * @param v1 first vertex + * @param v2 second vertex + * @param v3 third vertex + * + * @return Vector3 normal vector for the triangle + */ +inline Vector3 Vector3::SurfaceNormal(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3) +{ + return Vector3::Normalize(Vector3::Cross(v2 - v1, v3 - v1)); +} + +/** + * Returns magnitude of a vector. + * ||a|| = sqrt((a.x * a.x) + (a.y * a.y) + (a.z * a.z)) + * @param a vector to calculate the magnitude of + * + * @return float vector magnitude + */ +inline float Vector3::Magnitude(const Vector3 &a) +{ + return sqrtf( + (a.x * a.x) + + (a.y * a.y) + + (a.z * a.z) + ); +} + +/** + * Returns the squared length of a vector (the magnitude minus + * the sqrt call) + * @param a vector to calculate the squared length of + * + * @return float squared length of the vector + */ +inline float Vector3::SquaredLength(const Vector3 &a) +{ + return + (a.x * a.x) + + (a.y * a.y) + + (a.z * a.z); +} + +/** + * Adjusts a vector so that it's magnitude is equal to the given + * length + * @param v the original vector to be adjusted + * @param length desired vector magnitude + * + * @return Vector3 the resulting vector after it's length has + * been converted to the desired amount + */ +inline Vector3 Vector3::SetLength(const Vector3 &v, float length) +{ + float magnitude = Vector3::Magnitude(v); + return v * (length / magnitude); +} + +/** + * Calculates the distance between two points + * @param a the first point + * @param b the second point + * + * @return float the distance between both points + */ +inline float Vector3::Distance(const Vector3 &a, const Vector3 &b) +{ + return sqrtf( + ((b.x - a.x) * (b.x - a.x)) + + ((b.y - a.y) * (b.y - a.y)) + + ((b.z - a.z) * (b.z - a.z)) + ); +} + +/** + * Checks if a given point lies inside a triangle or not + * @param point point to test + * @param a first vector of the triangle + * @param b second vector of the triangle + * @param c third vector of the triangle + * + * @return BOOL TRUE if the point lies inside the triangle, + * FALSE if it doesn't + */ +inline bool Vector3::IsPointInTriangle(const Vector3 &point, const Vector3 &pa, const Vector3 &pb, const Vector3 &pc) +{ + Vector3 edge1 = pb - pa; + Vector3 edge2 = pc - pa; + + float a = Vector3::Dot(edge1, edge1); + float b = Vector3::Dot(edge1, edge2); + float c = Vector3::Dot(edge2, edge2); + float ac_bb = (a * c) - (b * b); + Vector3 vp(point.x - pa.x, point.y - pa.y, point.z - pa.z); + + float d = Vector3::Dot(vp, edge1); + float e = Vector3::Dot(vp, edge2); + float x = (d * c) - (e * b); + float y = (e * a) - (d * b); + float z = x + y - ac_bb; + + int result = (( ((unsigned int&) z)& ~(((unsigned int&) x)|((unsigned int&) y)) ) & 0x80000000); + if (result == 0) + return false; + else + return true; +} + +inline bool operator==(const Vector3 &left, const Vector3 &right) +{ + return (left.x == right.x && left.y == right.y && left.z == right.z); +} + +inline Vector3 operator-(const Vector3 &left) +{ + return Vector3(-left.x, -left.y, -left.z); +} + +inline Vector3 operator+(const Vector3 &left, const Vector3 &right) +{ + return Vector3(left.x + right.x, left.y + right.y, left.z + right.z); +} + +inline Vector3 &operator+=(Vector3 &left, const Vector3 &right) +{ + left.x += right.x; + left.y += right.y; + left.z += right.z; + + return left; +} + +inline Vector3 operator-(const Vector3 &left, const Vector3 &right) +{ + return Vector3(left.x - right.x, left.y - right.y, left.z - right.z); +} + +inline Vector3 &operator-=(Vector3 &left, const Vector3 &right) +{ + left.x -= right.x; + left.y -= right.y; + left.z -= right.z; + + return left; +} + +inline Vector3 operator*(const Vector3 &left, float right) +{ + return Vector3(left.x * right, left.y * right, left.z * right); +} + +inline Vector3 &operator*=(Vector3 &left, float right) +{ + left.x *= right; + left.y *= right; + left.z *= right; + + return left; +} + +inline Vector3 operator/(const Vector3 &left, float right) +{ + return Vector3(left.x / right, left.y / right, left.z / right); +} + +inline Vector3 &operator/=(Vector3 &left, float right) +{ + left.x /= right; + left.y /= right; + left.z /= right; + + return left; +} + +inline Vector3 operator*(const Vector3 &left, const Vector3 &right) +{ + return Vector3(left.x * right.x, left.y * right.y, left.z * right.z); +} + +inline Vector3 &operator*=(Vector3 &left, const Vector3 &right) +{ + left.x *= right.x; + left.y *= right.y; + left.z *= right.z; + + return left; +} + +inline Vector3 operator/(const Vector3 &left, const Vector3 &right) +{ + return Vector3(left.x / right.x, left.y / right.y, left.z / right.z); +} + +inline Vector3 &operator/=(Vector3 &left, const Vector3 &right) +{ + left.x /= right.x; + left.y /= right.y; + left.z /= right.z; + + return left; +} + +#endif \ No newline at end of file diff --git a/MeshConverter/src/main.cpp b/MeshConverter/src/main.cpp new file mode 100644 index 0000000..b843c71 --- /dev/null +++ b/MeshConverter/src/main.cpp @@ -0,0 +1,77 @@ +#include +#include +#include + +#include "md2/md2.h" +#include "obj/obj.h" + +int main(int argc, char **argv) +{ + printf("MESH Converter\n"); + + if (argc == 1) + { + printf("No input file specified.\n"); + printf("Usage: meshconverter.exe [inputfile]\n\n"); + return 1; + } + + std::string file = argv[1]; + std::string extension; + + try + { + extension = file.substr(file.find_last_of('.'), std::string::npos); + for (int i = 0; i < extension.size(); ++i) + extension[i] = tolower(extension[i]); + } + catch (std::exception &e) + { + extension = ""; + } + + std::string meshFile = ""; + if (extension.length() > 0) + { + meshFile = file; + meshFile.erase(meshFile.find_last_of('.'), std::string::npos); + meshFile.append(".mesh"); + } + + if (extension == ".obj") + { + Obj *obj = new Obj(); + if (!obj->Load(file, "./")) + { + printf("Error loading OBJ file.\n\n"); + return 1; + } + if (!obj->ConvertToMesh(meshFile)) + { + printf("Error converting OBJ to MESH.\n\n"); + return 1; + } + } + else if (extension == ".md2") + { + Md2 *md2 = new Md2(); + if (!md2->Load(file)) + { + printf("Error loading MD2 file.\n\n"); + return 1; + } + if (!md2->ConvertToMesh(meshFile)) + { + printf("Error converting MD2 to MESH.\n\n"); + return 1; + } + } + else + { + printf("Unrecognized file type.\n\n"); + return 1; + } + + return 0; + +} \ No newline at end of file diff --git a/MeshConverter/src/md2/md2.cpp b/MeshConverter/src/md2/md2.cpp new file mode 100644 index 0000000..c0eeefe --- /dev/null +++ b/MeshConverter/src/md2/md2.cpp @@ -0,0 +1,244 @@ +#include "md2.h" + +#include + +Md2::Md2() +{ + m_numFrames = 0; + m_numPolys = 0; + m_numTexCoords = 0; + m_numVertices = 0; + m_numSkins = 0; + m_frames = NULL; + m_polys = NULL; + m_texCoords = NULL; + m_skins = NULL; +} + +void Md2::Release() +{ + delete[] m_frames; + delete[] m_polys; + delete[] m_texCoords; + delete[] m_skins; + m_numFrames = 0; + m_numPolys = 0; + m_numTexCoords = 0; + m_numVertices = 0; + m_numSkins = 0; + m_frames = NULL; + m_polys = NULL; + m_texCoords = NULL; + m_skins = NULL; +} + +bool Md2::Load(const std::string &file) +{ + FILE *fp; + unsigned char c; + unsigned short u, v, t; + float x, y, z; + Md2Header header; + Vector3 scale, translate; + + fp = fopen(file.c_str(), "rb"); + if (!fp) + return false; + + // Simple filetype verification + fread(&header.ident, 4, 1, fp); + if (header.ident[0] != 'I' || header.ident[1] != 'D' || header.ident[2] != 'P' || header.ident[3] != '2') + { + fclose(fp); + return false; + } + fread(&header.version, 4, 1, fp); + if (header.version != 8) + { + fclose(fp); + return false; + } + + Release(); + + // Read rest of the MD2 header + fread(&header.skinWidth, 4, 1, fp); + fread(&header.skinHeight, 4, 1, fp); + fread(&header.frameSize, 4, 1, fp); + fread(&header.numSkins, 4, 1, fp); + fread(&header.numVertices, 4, 1, fp); + fread(&header.numTexCoords, 4, 1, fp); + fread(&header.numPolys, 4, 1, fp); + fread(&header.numGlCmds, 4, 1, fp); + fread(&header.numFrames, 4, 1, fp); + fread(&header.offsetSkins, 4, 1, fp); + fread(&header.offsetTexCoords, 4, 1, fp); + fread(&header.offsetPolys, 4, 1, fp); + fread(&header.offsetFrames, 4, 1, fp); + fread(&header.offsetGlCmds, 4, 1, fp); + fread(&header.offsetEnd, 4, 1, fp); + + // Allocate memory + if (header.numSkins > 0) + { + m_skins = new std::string[header.numSkins]; + //ASSERT(m_skins != NULL); + } + m_texCoords = new Vector2[header.numTexCoords]; + m_polys = new Md2Polygon[header.numPolys]; + m_frames = new Md2Frame[header.numFrames]; + + //ASSERT(m_texCoords != NULL); + //ASSERT(m_polys != NULL); + //ASSERT(m_frames != NULL); + + // Save model properties + m_numFrames = header.numFrames; + m_numPolys = header.numPolys; + m_numSkins = header.numSkins; + m_numTexCoords = header.numTexCoords; + m_numVertices = header.numVertices; + + // Read skin info + fseek(fp, header.offsetSkins, SEEK_SET); + for (int i = 0; i < header.numSkins; ++i) + { + // Not wasting the full 64 characters stored in the file here + for (int j = 0; j < MD2_SKIN_NAME_LENGTH; ++j) + { + fread(&c, 1, 1, fp); + if (!c) + { + fseek(fp, MD2_SKIN_NAME_LENGTH - j - 1, SEEK_CUR); + break; + } + else + m_skins[i].append(1, c); + } + } + + // Read texture coordinates + fseek(fp, header.offsetTexCoords, SEEK_SET); + for (int i = 0; i < header.numTexCoords; ++i) + { + fread(&u, 2, 1, fp); + fread(&v, 2, 1, fp); + m_texCoords[i].x = u / (float)header.skinWidth; + m_texCoords[i].y = v / (float)header.skinHeight; + } + + // Read polygons (this is all just indexes into m_texCoords and m_frames[].vertices) + fseek(fp, header.offsetPolys, SEEK_SET); + for (int i = 0; i < header.numPolys; ++i) + { + fread(&t, 2, 1, fp); + m_polys[i].vertex[0] = t; + fread(&t, 2, 1, fp); + m_polys[i].vertex[2] = t; + fread(&t, 2, 1, fp); + m_polys[i].vertex[1] = t; + + // HACK: Not sure why some of these indexes are invalid? This seems to fix the problem + fread(&t, 2, 1, fp); + m_polys[i].texCoord[0] = (t == 65535 ? 0 : t); + fread(&t, 2, 1, fp); + m_polys[i].texCoord[2] = (t == 65535 ? 0 : t); + fread(&t, 2, 1, fp); + m_polys[i].texCoord[1] = (t == 65535 ? 0 : t); + } + + // Read frames + fseek(fp, header.offsetFrames, SEEK_SET); + for (int i = 0; i < header.numFrames; ++i) + { + // Allocate enough memory for this frame's vertex/normal indexes + m_frames[i].vertices = new Vector3[header.numVertices]; + //m_frames[i].normals = new Vector3[header.numPolys]; + m_frames[i].normals = new Vector3[header.numVertices]; + //ASSERT(m_frames[i].vertices != NULL); + //ASSERT(m_frames[i].normals != NULL); + + fread(&scale.x, 4, 1, fp); + fread(&scale.y, 4, 1, fp); + fread(&scale.z, 4, 1, fp); + + fread(&translate.x, 4, 1, fp); + fread(&translate.y, 4, 1, fp); + fread(&translate.z, 4, 1, fp); + + // Store the text name of the frame (we won't waste the full 16 characters + // reserved in the file here) + for (int j = 0; j < MD2_FRAME_NAME_LENGTH; ++j) + { + fread(&c, 1, 1, fp); + if (!c) + { + fseek(fp, MD2_FRAME_NAME_LENGTH - j - 1, SEEK_CUR); + break; + } + else + m_frames[i].name += c; + } + + // Read vertices, and decompress as we load them for performance when rendering + for (int j = 0; j < header.numVertices; ++j) + { + fread(&c, 1, 1, fp); + x = (float)c; + fread(&c, 1, 1, fp); + y = (float)c; + fread(&c, 1, 1, fp); + z = (float)c; + + // Convert to OpenGL's coordinate system, otherwise models will need to be rotated to be drawn upright + m_frames[i].vertices[j].x = (x * scale.x) + translate.x; + m_frames[i].vertices[j].y = (z * scale.z) + translate.z; + m_frames[i].vertices[j].z = -1.0f * ((y * scale.y) + translate.y); + + fread(&c, 1, 1, fp); // Dummy command to increment file pointer (we don't care about the normal index) + } + + //m_frameMap[m_frames[i].name] = i; + } + + // Cleanup and finishing touches. + // Vertex coordinates, as of now, are waaay out of range (most likely, unless the model is tiny). + // We could've scaled them down above while reading them in, but I noticed issues calculating normals + // when that was done (probably due to lacking precision). So, we calculate the normals using the + // un-touched coordinates (get the most accurate normal calc that way), then scale the vertex down. + for (int i = 0; i < header.numFrames; ++i) + { + // Calculate vertex normals + Vector3 sumNormal; + int sum; + for (int j = 0; j < header.numVertices; ++j) + { + sum = 0; + sumNormal = Vector3(0, 0, 0); + for (int k = 0; k < header.numPolys; ++k) + { + if (m_polys[k].vertex[0] == j || m_polys[k].vertex[1] == j || m_polys[k].vertex[2] == j) + { + ++sum; + sumNormal += Vector3::SurfaceNormal(m_frames[i].vertices[m_polys[k].vertex[0]], + m_frames[i].vertices[m_polys[k].vertex[1]], + m_frames[i].vertices[m_polys[k].vertex[2]]); + } + } + m_frames[i].normals[j] = sumNormal / (float)sum; + } + + //// Done, now scale the vertices down + //for (int j = 0; j < header.numVertices; ++j) + //{ + // m_frames[i].vertices[j] /= (float)(MD2_SCALE_FACTOR); + //} + } + + return true; +} + +bool Md2::ConvertToMesh(const std::string &file) +{ + return false; +} diff --git a/MeshConverter/src/md2/md2.h b/MeshConverter/src/md2/md2.h new file mode 100644 index 0000000..3f6880b --- /dev/null +++ b/MeshConverter/src/md2/md2.h @@ -0,0 +1,91 @@ +#ifndef __MD2_H_INCLUDED__ +#define __MD2_H_INCLUDED__ + +#include "../geometry/vector3.h" +#include "../geometry/vector2.h" + +#include + +#define MD2_SKIN_NAME_LENGTH 64 +#define MD2_FRAME_NAME_LENGTH 16 + +typedef struct +{ + char ident[4]; // Should be "IDP2" + int version; // Should be 8 + int skinWidth; // Texture dimensions + int skinHeight; + int frameSize; // Size of a single frame in bytes + int numSkins; + int numVertices; + int numTexCoords; + int numPolys; + int numGlCmds; + int numFrames; + int offsetSkins; + int offsetTexCoords; + int offsetPolys; + int offsetFrames; + int offsetGlCmds; + int offsetEnd; +} Md2Header; + +// Vertex and texcoord indices +typedef struct +{ + unsigned short vertex[3]; + unsigned short texCoord[3]; +} Md2Polygon; + +// For each keyframe, stores the vertices, normals, and the string name +typedef struct Md2Frame +{ + std::string name; + Vector3 *vertices; + Vector3 *normals; + + Md2Frame() + { + vertices = NULL; + normals = NULL; + } + + ~Md2Frame() + { + delete[] vertices; + delete[] normals; + } +} Md2Frame; + +class Md2 +{ +public: + Md2(); + virtual ~Md2() { Release(); } + + void Release(); + bool Load(const std::string &file); + bool ConvertToMesh(const std::string &file); + + int GetNumFrames() { return m_numFrames; } + int GetNumVertices() { return m_numVertices; } + int GetNumTexCoords() { return m_numTexCoords; } + int GetNumPolys() { return m_numPolys; } + int GetNumSkins() { return m_numSkins; } + Md2Frame* GetFrames() { return m_frames; } + Md2Polygon* GetPolygons() { return m_polys; } + Vector2* GetTexCoords() { return m_texCoords; } + +private: + int m_numFrames; + int m_numVertices; + int m_numTexCoords; + int m_numPolys; + int m_numSkins; + Md2Frame *m_frames; + Md2Polygon *m_polys; + Vector2 *m_texCoords; + std::string *m_skins; +}; + +#endif \ No newline at end of file diff --git a/MeshConverter/src/obj/obj.cpp b/MeshConverter/src/obj/obj.cpp new file mode 100644 index 0000000..f3a9b54 --- /dev/null +++ b/MeshConverter/src/obj/obj.cpp @@ -0,0 +1,496 @@ +#include "obj.h" + +#include +#include +#include + +Obj::Obj() +{ + m_vertices = NULL; + m_normals = NULL; + m_texCoords = NULL; + m_materials = NULL; + m_numVertices = 0; + m_numNormals = 0; + m_numTexCoords = 0; + m_numMaterials = 0; +} + +void Obj::Release() +{ + delete[] m_vertices; + delete[] m_normals; + delete[] m_texCoords; + delete[] m_materials; + m_vertices = NULL; + m_normals = NULL; + m_texCoords = NULL; + m_materials = NULL; + m_numVertices = 0; + m_numNormals = 0; + m_numTexCoords = 0; + m_numMaterials = 0; +} + +bool Obj::Load(const std::string &file, const std::string &texturePath) +{ + std::ifstream input; + std::string line; + std::string op; + std::string path; + std::string tempName; + Vector3 vertex; + Vector3 normal; + ObjMaterial *currentMaterial = NULL; + int currentVertex = 0; + int currentNormal = 0; + int currentTexCoord = 0; + int numGroups = 0; + + // Get pathname from filename given (if present) + // Need this as we assume any .mtl files specified are in the same path as this .obj file + if (file.find_last_of('/') != std::string::npos) + path = file.substr(0, file.find_last_of('/') + 1); + + if (!FindAndLoadMaterials(path, texturePath, file)) + return false; + if (!GetDataSizes(file)) + return false; + + input.open(file.c_str()); + if (input.fail()) + return false; + + // Extract name of model from the filename given (basically, chop off the extension and path) + //if (file.find_last_of('.') != std::string::npos) + // model->name = file.substr(path.length(), file.find_last_of('.') - path.length()); + + // Begin reading in model data + while (!input.eof()) + { + if (numGroups > 1) + break; + + std::getline(input, line, '\n'); + + op = line.substr(0, line.find(' ')); + + // Comments + if (op == "#") + { + } + + // Vertex + else if (op == "v") + { + sscanf(line.c_str(), "v %f %f %f", &m_vertices[currentVertex].x, &m_vertices[currentVertex].y, &m_vertices[currentVertex].z); + ++currentVertex; + } + + // Texture coordinate + else if (op == "vt") + { + sscanf(line.c_str(), "vt %f %f", &m_texCoords[currentTexCoord].x, &m_texCoords[currentTexCoord].y); + m_texCoords[currentTexCoord].x = -m_texCoords[currentTexCoord].y; + ++currentTexCoord; + } + + // Vertex normal + else if (op == "vn") + { + sscanf(line.c_str(), "vn %f %f %f", &m_normals[currentNormal].x, &m_normals[currentNormal].y, &m_normals[currentNormal].z); + ++currentNormal; + } + + // Face definition + else if (op == "f") + { + ParseFaceDefinition(line, currentMaterial); + } + + // Group name + else if (op == "g") + { + ++numGroups; + } + + // Object name + else if (op == "o") + { + } + + // Material + else if (op == "usemtl") + { + tempName = line.substr(line.find(' ') + 1); + + currentMaterial = NULL; + + // Find the named material and set it as current + for (unsigned int i = 0; i < m_numMaterials; ++i) + { + if (m_materials[i].name == tempName) + { + currentMaterial = &m_materials[i]; + break; + } + } + + //ASSERT(currentMaterial != NULL); + } + + // Material file + else if (op == "mtllib") + { + // Already would have been loaded + } + } + + input.close(); + + return true; +} + +void Obj::ParseFaceDefinition(const std::string &faceDefinition, ObjMaterial *currentMaterial) +{ + static int numFaceVertices = 0; + static OBJ_FACE_VERTEX_TYPE vertexType; + std::string def; + int pos; + int n = 0; + + // Just get the vertex index part of the line (can be variable length, and we dont want the newline at the end) + pos = faceDefinition.find(' ') + 1; + def = faceDefinition.substr(pos); + + // Few different face formats, and variable amount of vertices per face possible + + // How many vertices are there in this face definition? (only calc this once) + // Also calc the vertex format + if (!numFaceVertices) + { + pos = 0; + while (def.length() > 0 && def[def.length() - 1] == ' ') + def = def.substr(0, def.length() - 1); + //ASSERT(def.length() > 0); + + while (pos != std::string::npos) + { + ++pos; + ++numFaceVertices; + pos = def.find(' ', pos); + } + + std::string tempVertex = def.substr(0, def.find(' ')); + if (tempVertex.find("//") != std::string::npos) + vertexType = OBJ_VERTEX_NORMAL; + else + { + pos = 0; + while (pos != std::string::npos) + { + ++pos; + ++n; + pos = tempVertex.find('/', pos); + } + + if (n == 1) + vertexType = OBJ_VERTEX_TEXCOORD; + else + vertexType = OBJ_VERTEX_FULL; + } + } + + // Parse out vertices in this face + // We also store only triangles. Since OBJ file face definitions can have any number + // of vertices per face, we need to split it up into triangles here for easy rendering + // This is done as follows: + // - first 3 vertices = first triangle for face + // - for each additional 1 vertex, take the first vertex read for this face + the previously + // read vertex for this face and combine to make a new triangle + std::istringstream parser; + std::string currentVertex; + int thisVertex[3]; + int firstVertex[3]; + int lastReadVertex[3]; + int thisTriangle[3][3]; + ObjFace face; + + memset(&firstVertex, 0, sizeof(int) * 3); + memset(&lastReadVertex, 0, sizeof(int) * 3); + memset(&thisTriangle, 0, sizeof(int) * (3 * 3)); + parser.clear(); + parser.str(def); + + for (int i = 0; i < numFaceVertices; ++i) + { + // Get current vertex for this face + parser >> currentVertex; + + // Add vertex/texcoord/normal indexes to the data arrays + // (OBJ file indexes are NOT zero based. We fix that here) + memset(&thisVertex, 0, sizeof(int) * 3); + switch (vertexType) + { + case OBJ_VERTEX_FULL: // v/vt/vn + sscanf(currentVertex.c_str(), "%d/%d/%d", &thisVertex[0], &thisVertex[1], &thisVertex[2]); + break; + case OBJ_VERTEX_NORMAL: // v//vn + sscanf(currentVertex.c_str(), "%d//%d", &thisVertex[0], &thisVertex[2]); + break; + case OBJ_VERTEX_TEXCOORD: // v/vt + sscanf(currentVertex.c_str(), "%d/%d", &thisVertex[0]); + break; + } + + // Save the first vertex read for a face + if (i == 0) + memcpy(&firstVertex, &thisVertex, sizeof(int) * 3); + + // First 3 vertices simply form a triangle + if (i <= 2) + { + face.vertices[i] = thisVertex[0] - 1; + face.texcoords[i] = thisVertex[1] - 1; + face.normals[i] = thisVertex[2] - 1; + } + + // Store the first triangle + if (i == 2) + { + //ASSERT(currentMaterial != NULL); + currentMaterial->faces[currentMaterial->lastFaceIndex] = face; + ++currentMaterial->lastFaceIndex; + } + + // Combine vertices to form additional triangles + if (i > 2) + { + //ASSERT(currentMaterial != NULL); + face.vertices[0] = firstVertex[0] - 1; face.texcoords[0] = firstVertex[1] - 1; face.normals[0] = firstVertex[2] - 1; + face.vertices[1] = lastReadVertex[0] - 1; face.texcoords[1] = lastReadVertex[1] - 1; face.normals[1] = lastReadVertex[2] - 1; + face.vertices[2] = thisVertex[0] - 1; face.texcoords[2] = thisVertex[1] - 1; face.normals[2] = thisVertex[2] - 1; + currentMaterial->faces[currentMaterial->lastFaceIndex] = face; + ++currentMaterial->lastFaceIndex; + } + + // Save as "previously read vertex" + memcpy(&lastReadVertex, &thisVertex, sizeof(int) * 3); + } +} + +bool Obj::GetDataSizes(const std::string &file) +{ + std::ifstream input; + std::string line; + std::string op; + int countVertices = 0; + int countNormals = 0; + int countTexCoords = 0; + int numGroups = 0; + std::string useMtlName; + ObjMaterial *currentMaterial; + + input.open(file.c_str()); + if (input.fail()) + return false; + + while (!input.eof()) + { + if (numGroups > 1) + break; + + std::getline(input, line, '\n'); + + op = line.substr(0, line.find(' ')); + + if (op == "g") + ++numGroups; + else if (op == "v") + ++countVertices; + else if (op == "vt") + ++countTexCoords; + else if (op == "vn") + ++countNormals; + else if (op == "f") + // TODO: count number of vertices per face definition, and adjust the ++ operation accordingly (i.e. for 4 vertices per face, needs to be "+= 2") + ++currentMaterial->numFaces; + + else if (op == "usemtl") + { + std::string useMtlName = line.substr(line.find(' ') + 1); + + currentMaterial = NULL; + + // Find the named material and set it as current + for (unsigned int i = 0; i < m_numMaterials; ++i) + { + if (m_materials[i].name == useMtlName) + { + currentMaterial = &m_materials[i]; + break; + } + } + + //ASSERT(currentMaterial != NULL); + } + } + + input.close(); + + m_numVertices = countVertices; + m_numTexCoords = countTexCoords; + m_numNormals = countNormals; + m_vertices = new Vector3[m_numVertices]; + m_texCoords = new Vector2[m_numTexCoords]; + m_normals = new Vector3[m_numNormals]; + for (unsigned int i = 0; i < m_numMaterials; ++i) + { + m_materials[i].faces = new ObjFace[m_materials[i].numFaces]; + } + + return true; +} + +bool Obj::LoadMaterialLibrary(const std::string &file, const std::string &texturePath) +{ + std::ifstream input; + std::string line; + std::string op; + int currentMaterial = -1; + float r, g, b; + + if (!CountDefinedMaterials(file)) + return false; + m_materials = new ObjMaterial[m_numMaterials]; + + input.open(file.c_str()); + if (input.fail()) + return false; + + while (!input.eof()) + { + std::getline(input, line, '\n'); + + op = line.substr(0, line.find(' ')); + + // New material definition (possibility of multiple per .mtl file) + if (op == "newmtl") + { + ++currentMaterial; + m_materials[currentMaterial].name = line.substr(op.length() + 1); + } + + // Ambient color + else if (op == "Ka") + { + //ASSERT(currentMaterial >= 0); + sscanf(line.c_str(), "Ka %f %f %f", &r, &g, &b); + m_materials[currentMaterial].material->SetAmbient(RGB_24_f(r, g, b)); + } + + // Diffuse color + else if (op == "Kd") + { + //ASSERT(currentMaterial >= 0); + sscanf(line.c_str(), "Kd %f %f %f", &r, &g, &b); + m_materials[currentMaterial].material->SetDiffuse(RGB_24_f(r, g, b)); + } + + // Specular color + else if (op == "Ks") + { + //ASSERT(currentMaterial >= 0); + sscanf(line.c_str(), "Ks %f %f %f", &r, &g, &b); + m_materials[currentMaterial].material->SetSpecular(RGB_24_f(r, g, b)); + } + + // Alpha value + else if (op == "d" || op == "Tr") + { + //ASSERT(currentMaterial >= 0); + } + + // Shininess + else if (op == "Ns") + { + //ASSERT(currentMaterial >= 0); + } + + // Illumination model + else if (op == "illum") + { + //ASSERT(currentMaterial >= 0); + } + + // Texture + else if (op == "map_Ka" || op == "map_Kd") + { + //ASSERT(currentMaterial >= 0); + m_materials[currentMaterial].material->SetTexture(texturePath + line.substr(op.length() + 1)); + } + + } + + input.close(); + + return true; +} + +bool Obj::CountDefinedMaterials(const std::string &file) +{ + std::ifstream input; + std::string line; + std::string op; + int count = 0; + + input.open(file.c_str()); + if (input.fail()) + return false; + + while (!input.eof()) + { + std::getline(input, line, '\n'); + + op = line.substr(0, line.find(' ')); + if (op == "newmtl") + ++m_numMaterials; + } + + input.close(); + + return true; +} + +bool Obj::FindAndLoadMaterials(const std::string &materialPath, const std::string &texturePath, const std::string &file) +{ + std::ifstream input; + std::string line; + std::string op; + + input.open(file.c_str()); + if (input.fail()) + return false; + + while (!input.eof()) + { + std::getline(input, line, '\n'); + + op = line.substr(0, line.find(' ')); + + if (op == "mtllib") + { + LoadMaterialLibrary(materialPath + line.substr(line.find(' ') + 1), texturePath); + break; + } + } + + input.close(); + + return true; +} + +bool Obj::ConvertToMesh(const std::string &file) +{ + return false; +} diff --git a/MeshConverter/src/obj/obj.h b/MeshConverter/src/obj/obj.h new file mode 100644 index 0000000..a1c7387 --- /dev/null +++ b/MeshConverter/src/obj/obj.h @@ -0,0 +1,84 @@ +#ifndef __OBJ_H_INCLUDED__ +#define __OBJ_H_INCLUDED__ + +#include "../geometry/vector3.h" +#include "../geometry/vector2.h" +#include "../assets/material.h" + +#include + +typedef enum OBJ_FACE_VERTEX_TYPE +{ + OBJ_VERTEX_TEXCOORD, + OBJ_VERTEX_NORMAL, + OBJ_VERTEX_FULL +}; + +typedef struct +{ + unsigned int vertices[3]; + unsigned int texcoords[3]; + unsigned int normals[3]; +} ObjFace; + +typedef struct ObjMaterial +{ + std::string name; + Material *material; + ObjFace *faces; + unsigned int numFaces; + unsigned int lastFaceIndex; + + ObjMaterial() + { + material = new Material(); + faces = NULL; + numFaces = 0; + lastFaceIndex = 0; + } + + ~ObjMaterial() + { + delete material; + delete[] faces; + } +} ObjMaterial; + +class Obj +{ +public: + Obj(); + virtual ~Obj() { Release(); } + + void Release(); + bool Load(const std::string &file, const std::string &texturePath); + bool ConvertToMesh(const std::string &file); + + int GetNumVertices() { return m_numVertices; } + int GetNumNormals() { return m_numNormals; } + int GetNumTexCoords() { return m_numTexCoords; } + int GetNumMaterials() { return m_numMaterials; } + Vector3* GetVertices() { return m_vertices; } + Vector3* GetNormals() { return m_normals; } + Vector2* GetTexCoords() { return m_texCoords; } + ObjMaterial* GetMaterials() { return m_materials; } + +private: + bool GetDataSizes(const std::string &file); + bool LoadMaterialLibrary(const std::string &file, const std::string &texturePath); + bool CountDefinedMaterials(const std::string &file); + bool FindAndLoadMaterials(const std::string &materialPath, const std::string &texturePath, const std::string &file); + void ParseFaceDefinition(const std::string &faceDefinition, ObjMaterial *currentMaterial); + + Vector3 *m_vertices; + Vector3 *m_normals; + Vector2 *m_texCoords; + ObjMaterial *m_materials; + unsigned int m_numVertices; + unsigned int m_numNormals; + unsigned int m_numTexCoords; + unsigned int m_numMaterials; + +}; + +#endif \ No newline at end of file