Add MTL file loading
This commit is contained in:
parent
21fb0058ad
commit
c3c154e4ff
2
Makefile
2
Makefile
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
185
src/engine.cpp
185
src/engine.cpp
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
143
src/geometry.cpp
143
src/geometry.cpp
|
@ -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
|
||||||
|
|
257
src/loader.cpp
257
src/loader.cpp
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue