initial commit

This commit is contained in:
gered 2010-06-20 14:08:51 -04:00
commit 984d590b3f
12 changed files with 1488 additions and 0 deletions

0
.hgignore Normal file
View file

20
MeshConverter.sln Normal file
View file

@ -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

View file

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{AADE0387-ED8A-46E2-B2DA-FC521DF7CB9C}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>MeshConverter</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\assets\material.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\md2\md2.cpp" />
<ClCompile Include="src\obj\obj.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\assets\material.h" />
<ClInclude Include="src\geometry\vector2.h" />
<ClInclude Include="src\geometry\vector3.h" />
<ClInclude Include="src\md2\md2.h" />
<ClInclude Include="src\obj\obj.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -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()
{
}

View file

@ -0,0 +1,40 @@
#ifndef __MATERIAL_H_INCLUDED__
#define __MATERIAL_H_INCLUDED__
#include <string>
#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

View file

@ -0,0 +1,11 @@
#ifndef __VECTOR2_H_INCLUDED__
#define __VECTOR2_H_INCLUDED__
class Vector2
{
public:
float x;
float y;
};
#endif

View file

@ -0,0 +1,319 @@
#ifndef __VECTOR3_H_INCLUDED__
#define __VECTOR3_H_INCLUDED__
#include <math.h>
/**
* 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

View file

@ -0,0 +1,77 @@
#include <stdio.h>
#include <string>
#include <exception>
#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;
}

View file

@ -0,0 +1,244 @@
#include "md2.h"
#include <stdio.h>
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;
}

View file

@ -0,0 +1,91 @@
#ifndef __MD2_H_INCLUDED__
#define __MD2_H_INCLUDED__
#include "../geometry/vector3.h"
#include "../geometry/vector2.h"
#include <string>
#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

View file

@ -0,0 +1,496 @@
#include "obj.h"
#include <stdio.h>
#include <fstream>
#include <sstream>
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;
}

View file

@ -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 <string>
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