1
0
Fork 0

Add MTL file loading

This commit is contained in:
Austin Morlan 2018-09-14 18:46:30 -07:00
parent 21fb0058ad
commit c3c154e4ff
Signed by: austin
GPG Key ID: FD6B27654AF5E348
11 changed files with 415 additions and 308 deletions

View File

@ -40,7 +40,7 @@ WARNINGS_OFF=-Wno-missing-braces -Wno-gnu-anonymous-struct -Wno-old-style-cast\
CFLAGS=$(D) $(O) -std=c++11 $(WARNINGS_ON) $(WARNINGS_OFF) -I$(INCLUDE_DIR) CFLAGS=$(D) $(O) -std=c++11 $(WARNINGS_ON) $(WARNINGS_OFF) -I$(INCLUDE_DIR)
LIBS=-lSDL2 LIBS=-lSDL2
_HEADERS = color.h engine.h geometry.h light.h loader.h platform.h point.h\ _HEADERS = camera.h color.h engine.h geometry.h light.h loader.h platform.h point.h\
transform.h util.h vec.h transform.h util.h vec.h
HEADERS = $(patsubst %,$(INCLUDE_DIR)/%,$(_HEADERS)) HEADERS = $(patsubst %,$(INCLUDE_DIR)/%,$(_HEADERS))

View File

@ -18,8 +18,8 @@ struct Camera
rotation[1] = 0.0f; rotation[1] = 0.0f;
rotation[2] = 0.0f; rotation[2] = 0.0f;
nearClip = 1.0f; nearClip = 5.0f;
farClip = 1000.0f; farClip = 600.0f;
} }
inline void SetFOV(float fov, int winWidth, int winHeight) inline void SetFOV(float fov, int winWidth, int winHeight)

View File

@ -34,7 +34,7 @@ struct Engine_Buffer
// FUNCTIONS // FUNCTIONS
int Engine_Init(Engine_Buffer &buffer, char *filename); int Engine_Init(Engine_Buffer &buffer, char *objFilename, char *mtlFilename);
void Engine_Render(Engine_Buffer &buffer, uint32_t input); void Engine_Render(Engine_Buffer &buffer, uint32_t input);
void Engine_Shutdown(void); void Engine_Shutdown(void);

View File

@ -4,16 +4,12 @@
#include "engine.h" #include "engine.h"
#include "point.h" #include "point.h"
#include <cstdint> #include <cstdint>
#include <vector>
// STRUCTURES #define VERTEX_LIMIT (20000)
struct Material #define FACE_LIMIT (30000)
{ #define MATERIAL_LIMIT (10)
bool smooth;
ColorF32 kAmbient;
ColorF32 kDiffuse;
};
struct Texture struct Texture
{ {
@ -22,18 +18,37 @@ struct Texture
unsigned int height; unsigned int height;
}; };
struct TextureCoord struct TextureList
{
Texture data[MATERIAL_LIMIT];
size_t size;
};
struct Material
{
ColorF32 ambient;
ColorF32 diffuse;
ColorF32 specular;
float specularExp;
float opacity;
};
struct MaterialList
{
Material data[MATERIAL_LIMIT];
size_t size;
};
struct UV
{ {
float u; float u;
float v; float v;
}; };
struct Face struct UVList
{ {
unsigned int vertIndex[3]; UV data[VERTEX_LIMIT];
unsigned int textureIndex[3]; size_t size;
Vector normal;
ColorF32 color;
}; };
struct Vertex struct Vertex
@ -43,17 +58,23 @@ struct Vertex
ColorF32 color; ColorF32 color;
}; };
struct Mesh_LocalData struct VertexList
{ {
std::vector<Vertex> verts; Vertex data[VERTEX_LIMIT];
std::vector<Face> faces; size_t size;
std::vector<TextureCoord> uvs;
}; };
struct Mesh_TransformedData struct Face
{ {
std::vector<Vertex> verts; int vertIndex[3];
std::vector<Face> faces; int uvIndex[3];
int materialIndex;
};
struct FaceList
{
Face data[FACE_LIMIT];
size_t size;
}; };
struct Mesh struct Mesh
@ -74,22 +95,17 @@ struct Mesh
Point position; Point position;
float rotation[3]; float rotation[3];
float scale; float scale;
Material material;
Mesh_LocalData local;
Mesh_TransformedData transformed;
}; };
// PUBLIC FUNCTIONS // PUBLIC FUNCTIONS
void CullBackfaces( void ClipAndCull(
Mesh_LocalData &local, Mesh_TransformedData &transformed, VertexList &verts, FaceList &localFaces,
Point &camPosition); FaceList &transFaces, Point &camPosition);
void RenderMesh( void RenderMesh(
Engine_Buffer &buffer, Mesh_TransformedData &mesh, Engine_Buffer &buffer, FaceList &faces, VertexList &verts,
Texture &texture, std::vector<TextureCoord> &uvs); UVList &uvs, TextureList &textures);
#define GEOMETRY_H #define GEOMETRY_H

View File

@ -3,8 +3,8 @@
#include "geometry.h" #include "geometry.h"
int LoadMesh(char *filename, Mesh &mesh); int ParseOBJ(char *filename, VertexList &verts, FaceList &faces, UVList &uvs);
int LoadTexture(char *filename, Texture &texture); int ParseMTL(char *filename, MaterialList &materials, TextureList &textures);
#define LOADER_H #define LOADER_H

View File

@ -22,36 +22,48 @@ struct Point
// OPERATORS // OPERATORS
// v / f // p / f
inline Point operator/(Point v, float f) inline Point operator/(Point p, float f)
{ {
Point result; Point result;
float inverse = 1.0f / f; float inverse = 1.0f / f;
result.x = v.x * inverse; result.x = p.x * inverse;
result.y = v.y * inverse; result.y = p.y * inverse;
result.z = v.z * inverse; result.z = p.z * inverse;
return result; return result;
} }
// v /= f // v /= f
inline Point &operator/=(Point &v, float f) inline Point &operator/=(Point &p, float f)
{ {
v = v / f; p = p / f;
return v; return p;
} }
// v1 - v2 // p1 - p2
inline Vector operator-(Point v1, Point v2) inline Vector operator-(Point p1, Point p2)
{ {
Vector result; Vector result;
result.x = v1.x - v2.x; result.x = p1.x - p2.x;
result.y = v1.y - v2.y; result.y = p1.y - p2.y;
result.z = v1.z - v2.z; result.z = p1.z - p2.z;
return result;
}
// -p
inline Point operator-(Point p)
{
Point result;
result.x = -p.x;
result.y = -p.y;
result.z = -p.z;
return result; return result;
} }

View File

@ -9,41 +9,61 @@
#include "util.h" #include "util.h"
#include "vec.h" #include "vec.h"
#include <cstring> #include <cstring>
#include <cstdio>
// GLOBALS // GLOBALS
static Mesh mesh; static Mesh mesh;
static Camera camera; static Camera camera;
static LightList lights; static LightList lights;
static Texture texture; static VertexList localVerts;
static VertexList transVerts;
static FaceList localFaces;
static FaceList transFaces;
static UVList uvs;
static MaterialList materials;
static TextureList textures;
// PRIVATE PROTOTYPES // PRIVATE PROTOTYPES
static void CheckInputs(uint32_t input); static void CheckInputs(uint32_t input);
static void ComputeNormals(VertexList &verts, FaceList &faces);
// PUBLIC FUNCTIONS // PUBLIC FUNCTIONS
int Engine_Init(Engine_Buffer &buffer, char *filename) int Engine_Init(Engine_Buffer &buffer, char *objFilename, char *mtlFilename)
{ {
int result = LoadMesh(filename, mesh); int result = ParseOBJ(objFilename, localVerts, localFaces, uvs);
if (result < 0)
{
return result;
}
result = ParseMTL(mtlFilename, materials, textures);
if (result < 0) if (result < 0)
{ {
return -1; return -1;
} }
mesh.position.z = 250; printf("Verts: %lu\n", localVerts.size);
mesh.material.kDiffuse = {1.0,1.0,1.0,1.0}; printf("Faces: %lu\n", localFaces.size);
mesh.material.kAmbient = {1.0,1.0,1.0,1.0}; printf("Materials: %lu\n", materials.size);
mesh.material.smooth = true;
transVerts.size = localVerts.size;
ComputeNormals(localVerts, localFaces);
mesh.position.z = 50;
mesh.position.y = -50;
camera.SetFOV(90.0f, buffer.width, buffer.height); camera.SetFOV(90.0f, buffer.width, buffer.height);
lights.diffuse = (LightDiffuse*)malloc(sizeof(LightDiffuse)); lights.diffuse = (LightDiffuse*)malloc(sizeof(LightDiffuse));
lights.diffuseCount = 1; lights.diffuseCount = 1;
lights.ambient.intensity = 1.0; lights.ambient.intensity = 1.0f;
lights.diffuse[0].intensity = 0.5; lights.diffuse[0].intensity = 1.0f;
lights.diffuse[0].direction = Vector(1,1,1); lights.diffuse[0].direction = Vector(1.0f, 1.0f, 1.0f);
return 0; return 0;
} }
@ -66,57 +86,6 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input)
Matrix tScale = Transform_Scale(mesh.scale); Matrix tScale = Transform_Scale(mesh.scale);
for (size_t v = 0; v < mesh.local.verts.size(); ++v)
{
mesh.transformed.verts[v].point = mesh.local.verts[v].point * tScale * tRotate * tTranslate;
mesh.transformed.verts[v].normal = mesh.local.verts[v].normal * tScale * tRotate * tTranslate;
}
// Cull backfaces before computing colors
CullBackfaces(mesh.local, mesh.transformed, camera.position);
// Color the vertices for Gouraud shading
if (mesh.material.smooth)
{
for (size_t f = 0; f < mesh.transformed.faces.size(); ++f)
{
for (int i = 0; i < 3; ++i)
{
unsigned int v = mesh.transformed.faces[f].vertIndex[i];
ColorF32 totalColor = lights.ambient.ComputeColor(mesh.material.kAmbient);
for (int c = 0; c < lights.diffuseCount; ++c)
{
totalColor += lights.diffuse[c].ComputeColor(
mesh.material.kDiffuse, mesh.transformed.verts[v].normal);
}
mesh.transformed.verts[v].color = totalColor;
}
}
}
// Color the face for flat shading
else
{
for (size_t f = 0; f < mesh.transformed.faces.size(); ++f)
{
ColorF32 totalColor = lights.ambient.ComputeColor(mesh.material.kAmbient);
for (int c = 0; c < lights.diffuseCount; ++c)
{
totalColor += lights.diffuse[c].ComputeColor(
mesh.material.kDiffuse, mesh.transformed.faces[f].normal);
}
mesh.transformed.faces[f].color = totalColor;
}
}
// World space to camera (view) space // World space to camera (view) space
Matrix tView = Transform_View(camera.position, camera.rotation); Matrix tView = Transform_View(camera.position, camera.rotation);
@ -124,18 +93,59 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input)
Matrix tPersp = Transform_Perspective( Matrix tPersp = Transform_Perspective(
camera.xZoom, camera.yZoom, camera.nearClip, camera.farClip); camera.xZoom, camera.yZoom, camera.nearClip, camera.farClip);
for (size_t v = 0; v < localVerts.size; ++v)
{
transVerts.data[v].point = localVerts.data[v].point * tScale * tRotate * tTranslate * tView * tPersp;
transVerts.data[v].normal = localVerts.data[v].normal * tScale * tRotate * tTranslate;
}
// Clip near and far Z, and cull backfaces
ClipAndCull(transVerts, localFaces, transFaces, camera.position);
// Lighting
for (size_t f = 0; f < transFaces.size; ++f)
{
Face &face = transFaces.data[f];
Material &material = materials.data[face.materialIndex];
// TODO: Fix weird lighting
material.ambient = {1.0,1.0,1.0,1.0};
material.diffuse = {0.5,0.5,0.5,0.5};
// Gouraud shading
for (int i = 0; i < 3; ++i)
{
Vertex &vert = transVerts.data[face.vertIndex[i]];
ColorF32 totalColor = lights.ambient.ComputeColor(material.ambient);
for (int c = 0; c < lights.diffuseCount; ++c)
{
totalColor += lights.diffuse[c].ComputeColor(
material.diffuse, vert.normal);
}
vert.color = totalColor;
}
}
// Perspective to screen // Perspective to screen
Matrix tScreen = Transform_Screen(camera.xScale, camera.yScale); Matrix tScreen = Transform_Screen(camera.xScale, camera.yScale);
for (size_t v = 0; v < mesh.transformed.verts.size(); ++v) for (size_t v = 0; v < transVerts.size; ++v)
{ {
mesh.transformed.verts[v].point *= tView * tPersp * tScreen; transVerts.data[v].point *= tScreen;
mesh.transformed.verts[v].point.x /= mesh.transformed.verts[v].point.w; transVerts.data[v].point.x /= transVerts.data[v].point.w;
mesh.transformed.verts[v].point.y /= mesh.transformed.verts[v].point.w; transVerts.data[v].point.y /= transVerts.data[v].point.w;
mesh.transformed.verts[v].point.z /= mesh.transformed.verts[v].point.w; transVerts.data[v].point.z /= transVerts.data[v].point.w;
} }
RenderMesh(buffer, mesh.transformed, texture, mesh.local.uvs);
// Render
RenderMesh(buffer, transFaces, transVerts, uvs, textures);
} }
void Engine_Shutdown(void) void Engine_Shutdown(void)
@ -209,3 +219,44 @@ static void CheckInputs(uint32_t input)
mesh.scale -= 0.1f; mesh.scale -= 0.1f;
} }
} }
static void ComputeNormals(VertexList &verts, FaceList &faces)
{
int vertexNormalCount[VERTEX_LIMIT];
for (size_t f = 0; f < faces.size; ++f)
{
size_t v0 = (size_t)faces.data[f].vertIndex[0];
size_t v1 = (size_t)faces.data[f].vertIndex[1];
size_t v2 = (size_t)faces.data[f].vertIndex[2];
Point &p0 = verts.data[v0].point;
Point &p1 = verts.data[v1].point;
Point &p2 = verts.data[v2].point;
Vector v01 = p1 - p0;
Vector v02 = p2 - p0;
Vector normal = Vector::Cross(v01, v02);
// Add each vertex's normal to the sum for future averaging
verts.data[v0].normal += normal;
verts.data[v1].normal += normal;
verts.data[v2].normal += normal;
++vertexNormalCount[v0];
++vertexNormalCount[v1];
++vertexNormalCount[v2];
}
for (size_t v = 0; v < localVerts.size; ++v)
{
if (vertexNormalCount[v] > 0)
{
// Compute the average normal for this vertex
verts.data[v].normal /= vertexNormalCount[v];
verts.data[v].normal.Normalize();
}
}
}

View File

@ -2,7 +2,6 @@
#include "engine.h" #include "engine.h"
#include "geometry.h" #include "geometry.h"
#include "util.h" #include "util.h"
#include <cstdio>
// MACROS // MACROS
@ -30,77 +29,77 @@ struct BoundingBox
// PUBLIC FUNCTIONS // PUBLIC FUNCTIONS
void CullBackfaces( void ClipAndCull(
Mesh_LocalData &local, Mesh_TransformedData &transformed, VertexList &verts, FaceList &localFaces,
Point &camPosition) FaceList &transFaces, Point &camPosition)
{ {
transformed.faces.clear(); int faceIndex = 0;
for (size_t f = 0; f < local.faces.size(); ++f) for (size_t f = 0; f < localFaces.size; ++f)
{ {
unsigned int v0 = local.faces[f].vertIndex[0]; Face &face = localFaces.data[f];
unsigned int v1 = local.faces[f].vertIndex[1];
unsigned int v2 = local.faces[f].vertIndex[2];
Vector v01 = transformed.verts[v1].point - transformed.verts[v0].point; Point &p0 = verts.data[face.vertIndex[0]].point;
Vector v02 = transformed.verts[v2].point - transformed.verts[v0].point; Point &p1 = verts.data[face.vertIndex[1]].point;
Vector normal = Vector::Cross(v01, v02); Point &p2 = verts.data[face.vertIndex[2]].point;
// Store normal for flat shading // Ignore this face if its Z is outside the Z clip planes
local.faces[f].normal = normal; if ( (p0.z < -p0.w)
local.faces[f].normal.Normalize(); || (p0.z > p0.w)
|| (p1.z < -p1.w)
|| (p1.z > p1.w)
|| (p2.z < -p2.w)
|| (p2.z > p2.w))
{
continue;
}
// Invert for Blender-compatibility
normal = -normal; // Calculate the face's normal (inverted for Blender-compatibility)
Vector v01 = p1 - p0;
Vector v02 = p2 - p0;
Vector normal = -Vector::Cross(v01, v02);
// Eye vector to viewport // Eye vector to viewport
Vector view = camPosition - transformed.verts[v0].point; Vector view = camPosition - p0;
float dot = Vector::Dot(normal, view); float dot = Vector::Dot(normal, view);
// Not a backface; add it to the list
if (dot < EPSILON_E3) if (dot < EPSILON_E3)
{ {
transformed.faces.push_back(local.faces[f]); transFaces.data[faceIndex] = face;
++faceIndex;
transFaces.size = (size_t)faceIndex;
} }
} }
} }
void RenderMesh( void RenderMesh(
Engine_Buffer &buffer, Mesh_TransformedData &mesh, Engine_Buffer &buffer, FaceList &faces, VertexList &verts,
Texture &texture, std::vector<TextureCoord> &uvs) UVList &uvs, TextureList &textures)
{ {
for(size_t f = 0; f < mesh.faces.size(); ++f) for(size_t f = 0; f < faces.size; ++f)
{ {
// The vertices of this face Face &face = faces.data[f];
unsigned int vIndex0 = mesh.faces[f].vertIndex[0];
unsigned int vIndex1 = mesh.faces[f].vertIndex[1];
unsigned int vIndex2 = mesh.faces[f].vertIndex[2];
Vertex &v0 = mesh.verts[vIndex0];
Vertex &v1 = mesh.verts[vIndex1];
Vertex &v2 = mesh.verts[vIndex2];
Point &p0 = verts.data[face.vertIndex[0]].point;
// The UVs of this face's vertices Point &p1 = verts.data[face.vertIndex[1]].point;
unsigned int tIndex0 = mesh.faces[f].textureIndex[0]; Point &p2 = verts.data[face.vertIndex[2]].point;
unsigned int tIndex1 = mesh.faces[f].textureIndex[1];
unsigned int tIndex2 = mesh.faces[f].textureIndex[2];
TextureCoord &t0 = uvs[tIndex0];
TextureCoord &t1 = uvs[tIndex1];
TextureCoord &t2 = uvs[tIndex2];
// Bounding box for barycentric calculations (top-left fill convention) // Bounding box for barycentric calculations (top-left fill convention)
BoundingBox box(v0.point, v1.point, v2.point); BoundingBox box(p0, p1, p2);
int yMin = (int)ceilf(box.yMin); int yMin = (int)MAX(ceilf(box.yMin), 0);
int yMax = (int)ceilf(box.yMax) - 1; int yMax = (int)MIN(ceilf(box.yMax) - 1, buffer.height - 1);
int xMin = (int)ceilf(box.xMin); int xMin = (int)MAX(ceilf(box.xMin), 0);
int xMax = (int)ceilf(box.xMax) - 1; int xMax = (int)MIN(ceilf(box.xMax) - 1, buffer.width - 1);
// Constants for this triangle used for barycentric calculations // Constants for this triangle used for barycentric calculations
Vector v01 = v1.point - v0.point; Vector v01 = p1 - p0;
Vector v02 = v2.point - v0.point; Vector v02 = p2 - p0;
float dot0101 = Vector::Dot(v01, v01); float dot0101 = Vector::Dot(v01, v01);
float dot0102 = Vector::Dot(v01, v02); float dot0102 = Vector::Dot(v01, v02);
float dot0202 = Vector::Dot(v02, v02); float dot0202 = Vector::Dot(v02, v02);
@ -113,7 +112,7 @@ void RenderMesh(
{ {
// Constant terms used for barycentric calculation // Constant terms used for barycentric calculation
Point p(x, y, 1.0f); Point p(x, y, 1.0f);
Vector v0P = p - v0.point; Vector v0P = p - p0;
float dot0P01 = Vector::Dot(v0P, v01); float dot0P01 = Vector::Dot(v0P, v01);
float dot0P02 = Vector::Dot(v0P, v02); float dot0P02 = Vector::Dot(v0P, v02);
float denomInv = 1.0f / ((dot0101 * dot0202) - (dot0102 * dot0102)); float denomInv = 1.0f / ((dot0101 * dot0202) - (dot0102 * dot0102));
@ -128,43 +127,51 @@ void RenderMesh(
// Point is inside the triangle // Point is inside the triangle
if ( (barycenter[0] >= 0.0f) if ( (barycenter[0] >= 0.0f)
&& (barycenter[1] >= 0.0f) && (barycenter[1] >= 0.0f)
&& (barycenter[2] >= 0.0f)) && (barycenter[2] >= 0.0f))
{ {
ColorF32 &c0 = verts.data[face.vertIndex[0]].color;
ColorF32 &c1 = verts.data[face.vertIndex[1]].color;
ColorF32 &c2 = verts.data[face.vertIndex[2]].color;
// Gouraud shading
ColorF32 shading =
(barycenter[0] * c0)
+ (barycenter[1] * c1)
+ (barycenter[2] * c2);
// Constant terms used in the U and V interpolation // Constant terms used in the U and V interpolation
float a = barycenter[0] * v1.point.w * v2.point.w; float a = barycenter[0] * p1.w * p2.w;
float b = barycenter[1] * v0.point.w * v2.point.w; float b = barycenter[1] * p0.w * p2.w;
float c = barycenter[2] * v0.point.w * v1.point.w; float c = barycenter[2] * p0.w * p1.w;
float abc = 1.0f / (a + b + c); float abc = 1.0f / (a + b + c);
UV &uv0 = uvs.data[face.uvIndex[0]];
UV &uv1 = uvs.data[face.uvIndex[1]];
UV &uv2 = uvs.data[face.uvIndex[2]];
// Interpolate U and V // Interpolate U and V
float uInterp = ((a * t0.u) + (b * t1.u) + (c * t2.u)) * abc; float uInterp = ((a * uv0.u) + (b * uv1.u) + (c * uv2.u)) * abc;
float vInterp = ((a * t0.v) + (b * t1.v) + (c * t2.v)) * abc; float vInterp = ((a * uv0.v) + (b * uv1.v) + (c * uv2.v)) * abc;
// Convert U and V to pixels in the texture image // Convert U and V to pixels in the texture image
Texture &texture = textures.data[faces.data[f].materialIndex];
uInterp *= texture.width; uInterp *= texture.width;
vInterp *= texture.height; vInterp *= texture.height;
unsigned int u = (unsigned int)uInterp; unsigned int u = (unsigned int)uInterp;
unsigned int v = (unsigned int)vInterp; unsigned int v = (unsigned int)vInterp;
// Constant terms for bilinear filtering
// Gouraud shading float du = uInterp - u;
ColorF32 shading = float dv = vInterp - v;
(barycenter[0] * v0.color) float duDiff = 1 - du;
+ (barycenter[1] * v1.color) float dvDiff = 1 - dv;
+ (barycenter[2] * v2.color);
// Bilinear filtering // Bilinear filtering
float du = uInterp - u;
float dv = vInterp - v;
float duDiff = 1-du;
float dvDiff = 1-dv;
ColorU32 color; ColorU32 color;
color.b = (uint8_t) color.b = (uint8_t)
(duDiff * dvDiff * texture.texels[v][u].b (duDiff * dvDiff * texture.texels[v][u].b
+ du * dvDiff * texture.texels[v][u+1].b + du * dvDiff * texture.texels[v][u+1].b
@ -193,9 +200,9 @@ void RenderMesh(
// Interpolate 1/z for the z-buffer // Interpolate 1/z for the z-buffer
float zInv = float zInv =
1.0f / 1.0f /
((barycenter[0] * v0.point.w) ((barycenter[0] * p0.w)
+ (barycenter[1] * v1.point.w) + (barycenter[1] * p1.w)
+ (barycenter[2] * v2.point.w)); + (barycenter[2] * p2.w));
// Draw the pixel if it's closer than what's in the z-buffer // Draw the pixel if it's closer than what's in the z-buffer

View File

@ -7,8 +7,8 @@
// STATIC PROTOTYPES // STATIC PROTOTYPES
static char *GetLine(char *buffer, int maxLength, FILE *fp); static int LoadTexture(char *filename, Texture &texture);
static void ComputeNormals(Mesh &mesh);
#pragma pack(push, 1) #pragma pack(push, 1)
struct BMP_Header struct BMP_Header
@ -34,15 +34,9 @@ struct BMP_Header
// PUBLIC FUNCTIONS // PUBLIC FUNCTIONS
int LoadMesh(char *filename, Mesh &mesh) int ParseOBJ(char *filename, VertexList &verts, FaceList &faces, UVList &uvs)
{ {
FILE *fp; FILE *fp = fopen(filename, "r");
char buffer[256];
char *token;
char garbage[5];
fp = fopen(filename, "r");
if (fp == NULL) if (fp == NULL)
{ {
@ -50,77 +44,158 @@ int LoadMesh(char *filename, Mesh &mesh)
return -1; return -1;
} }
token = GetLine(buffer, sizeof(buffer), fp);
while (token != NULL) char line[256];
int vertIndex = 0;
int uvIndex = 0;
int faceIndex = 0;
int materialIndex = -1;
while (fgets(line, sizeof(line), fp))
{ {
if (token[0] == 'v') char *separator = strchr(line, ' ');
if (separator != NULL)
{ {
if (token[1] == ' ') *separator = '\0';
char *type = line;
char *data = separator + 1;
if (strcmp(type, "v") == 0)
{ {
Vertex v; sscanf( data, "%f %f %f",
&verts.data[vertIndex].point.x,
&verts.data[vertIndex].point.y,
&verts.data[vertIndex].point.z);
sscanf( ++vertIndex;
token, "%s %f %f %f",
garbage,
&v.point.x,
&v.point.y,
&v.point.z);
mesh.local.verts.push_back(v);
} }
else if (token[1] == 't')
else if (strcmp(type, "vt") == 0)
{ {
TextureCoord textureCoord; sscanf( data, "%f %f",
&uvs.data[uvIndex].u,
&uvs.data[uvIndex].v);
sscanf( ++uvIndex;
token, "%s %f %f", }
garbage, else if (strcmp(type, "usemtl") == 0)
&textureCoord.u, {
&textureCoord.v); ++materialIndex;
}
mesh.local.uvs.push_back(textureCoord); else if (strcmp(type, "f") == 0)
{
sscanf( data, "%d/%d %d/%d %d/%d",
&faces.data[faceIndex].vertIndex[0],
&faces.data[faceIndex].uvIndex[0],
&faces.data[faceIndex].vertIndex[1],
&faces.data[faceIndex].uvIndex[1],
&faces.data[faceIndex].vertIndex[2],
&faces.data[faceIndex].uvIndex[2]);
// Convert to 0-indexed
faces.data[faceIndex].vertIndex[0] -= 1;
faces.data[faceIndex].vertIndex[1] -= 1;
faces.data[faceIndex].vertIndex[2] -= 1;
faces.data[faceIndex].uvIndex[0] -= 1;
faces.data[faceIndex].uvIndex[1] -= 1;
faces.data[faceIndex].uvIndex[2] -= 1;
faces.data[faceIndex].materialIndex = materialIndex;
++faceIndex;
} }
} }
else if (token[0] == 'f')
{
Face f;
sscanf(token, "%s %d/%d %d/%d %d/%d",
garbage,
&f.vertIndex[0],
&f.textureIndex[0],
&f.vertIndex[1],
&f.textureIndex[1],
&f.vertIndex[2],
&f.textureIndex[2]);
// Convert to 0-indexed
f.vertIndex[0] -= 1;
f.vertIndex[1] -= 1;
f.vertIndex[2] -= 1;
f.textureIndex[0] -= 1;
f.textureIndex[1] -= 1;
f.textureIndex[2] -= 1;
mesh.local.faces.push_back(f);
}
token = GetLine(buffer, sizeof(buffer), fp);
} }
ComputeNormals(mesh); verts.size = (size_t)vertIndex;
uvs.size = (size_t)uvIndex;
printf("Mesh: %s [v: %lu f: %lu]\n", filename, mesh.local.verts.size(), mesh.local.faces.size()); faces.size = (size_t)faceIndex;
mesh.transformed.verts.resize(mesh.local.verts.size());
fclose(fp);
return 0; return 0;
} }
int LoadTexture(char *filename, Texture &texture) int ParseMTL(char *filename, MaterialList &materials, TextureList &textures)
{
FILE *fp = fopen(filename, "r");
if (fp == NULL)
{
fprintf(stderr, "Error loading file: %s\n", filename);
return -1;
}
char line[256];
int materialIndex = -1;
while (fgets(line, sizeof(line), fp))
{
char *separator = strchr(line, ' ');
if (separator != NULL)
{
*separator = '\0';
char *type = line;
char *data = separator + 1;
if (strcmp(type, "newmtl") == 0)
{
++materialIndex;
}
else if (strcmp(type, "Ns") == 0)
{
sscanf( data, "%f",
&materials.data[materialIndex].specularExp);
}
else if (strcmp(type, "Ka") == 0)
{
sscanf( data, "%f %f %f",
&materials.data[materialIndex].ambient.r,
&materials.data[materialIndex].ambient.g,
&materials.data[materialIndex].ambient.b);
}
else if (strcmp(type, "Kd") == 0)
{
sscanf( data, "%f %f %f",
&materials.data[materialIndex].diffuse.r,
&materials.data[materialIndex].diffuse.g,
&materials.data[materialIndex].diffuse.b);
}
else if (strcmp(type, "Ks") == 0)
{
sscanf( data, "%f %f %f",
&materials.data[materialIndex].specular.r,
&materials.data[materialIndex].specular.g,
&materials.data[materialIndex].specular.b);
}
else if (strcmp(type, "d") == 0)
{
sscanf( data, "%f",
&materials.data[materialIndex].opacity);
}
else if (strcmp(type, "map_Kd") == 0)
{
char *textureFilename = data;
textureFilename[strcspn(textureFilename, "\r\n")] = 0;
LoadTexture(textureFilename, textures.data[materialIndex]);
}
}
}
materials.size = (size_t)(materialIndex + 1);
return 0;
}
static int LoadTexture(char *filename, Texture &texture)
{ {
FILE *fp = fopen(filename, "r"); FILE *fp = fopen(filename, "r");
if (fp == NULL) if (fp == NULL)
@ -167,57 +242,3 @@ int LoadTexture(char *filename, Texture &texture)
return 0; return 0;
} }
static char *GetLine(char *buffer, int maxLength, FILE *fp)
{
while(true)
{
if (!fgets(buffer, maxLength, fp))
{
return NULL;
}
if (buffer[0] != 'v' && buffer[0] != 'f')
{
continue;
}
return buffer;
}
}
static void ComputeNormals(Mesh &mesh)
{
int *vertexNormalCount = (int*)calloc((size_t)(mesh.local.verts.size()), sizeof(int));
for (size_t f = 0; f < mesh.local.faces.size(); ++f)
{
unsigned int v0 = mesh.local.faces[f].vertIndex[0];
unsigned int v1 = mesh.local.faces[f].vertIndex[1];
unsigned int v2 = mesh.local.faces[f].vertIndex[2];
Vector v01 = mesh.local.verts[v1].point - mesh.local.verts[v0].point;
Vector v02 = mesh.local.verts[v2].point - mesh.local.verts[v0].point;
Vector normal = Vector::Cross(v01, v02);
// Add each vertex's normal to the sum for future averaging
mesh.local.verts[v0].normal += normal;
mesh.local.verts[v1].normal += normal;
mesh.local.verts[v2].normal += normal;
++vertexNormalCount[v0];
++vertexNormalCount[v1];
++vertexNormalCount[v2];
}
for (size_t v = 0; v < mesh.local.verts.size(); ++v)
{
if (vertexNormalCount[v] > 0)
{
// Compute the average normal for this vertex
mesh.local.verts[v].normal /= vertexNormalCount[v];
mesh.local.verts[v].normal.Normalize();
}
}
}

View File

@ -14,13 +14,14 @@ const unsigned int WINDOW_FPS = 30;
// MAIN // MAIN
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
if (argc != 2) if (argc != 3)
{ {
fprintf(stderr, "Usage: %s <OBJ>\n", argv[0]); fprintf(stderr, "Usage: %s <OBJ> <MTL>\n", argv[0]);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
char *filename = argv[1]; char *objFilename = argv[1];
char *mtlFilename = argv[2];
Platform platform = {}; Platform platform = {};
int result = Platform_Init(platform, WINDOW_WIDTH, WINDOW_HEIGHT); int result = Platform_Init(platform, WINDOW_WIDTH, WINDOW_HEIGHT);
@ -34,7 +35,7 @@ int main(int argc, char *argv[])
buffer.width = platform.surface->w; buffer.width = platform.surface->w;
buffer.height = platform.surface->h; buffer.height = platform.surface->h;
result = Engine_Init(buffer, filename); result = Engine_Init(buffer, objFilename, mtlFilename);
if (result < 0) if (result < 0)
{ {

View File

@ -2,7 +2,6 @@
#include "platform.h" #include "platform.h"
#include "util.h" #include "util.h"
#include <cstdint> #include <cstdint>
#include <cstdio>
#include <SDL2/SDL.h> #include <SDL2/SDL.h>