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)
 | 
			
		||||
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
 | 
			
		||||
HEADERS = $(patsubst %,$(INCLUDE_DIR)/%,$(_HEADERS))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,8 +18,8 @@ struct Camera
 | 
			
		|||
        rotation[1] = 0.0f;
 | 
			
		||||
        rotation[2] = 0.0f;
 | 
			
		||||
 | 
			
		||||
        nearClip = 1.0f;
 | 
			
		||||
        farClip = 1000.0f;
 | 
			
		||||
        nearClip = 5.0f;
 | 
			
		||||
        farClip = 600.0f;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline void SetFOV(float fov, int winWidth, int winHeight)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ struct Engine_Buffer
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
// 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_Shutdown(void);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,16 +4,12 @@
 | 
			
		|||
#include "engine.h"
 | 
			
		||||
#include "point.h"
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// STRUCTURES
 | 
			
		||||
struct Material
 | 
			
		||||
{
 | 
			
		||||
    bool smooth;
 | 
			
		||||
    ColorF32 kAmbient;
 | 
			
		||||
    ColorF32 kDiffuse;
 | 
			
		||||
};
 | 
			
		||||
#define VERTEX_LIMIT (20000)
 | 
			
		||||
#define FACE_LIMIT (30000)
 | 
			
		||||
#define MATERIAL_LIMIT (10)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct Texture
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -22,18 +18,37 @@ struct Texture
 | 
			
		|||
    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 v;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Face
 | 
			
		||||
struct UVList
 | 
			
		||||
{
 | 
			
		||||
    unsigned int vertIndex[3];
 | 
			
		||||
    unsigned int textureIndex[3];
 | 
			
		||||
    Vector normal;
 | 
			
		||||
    ColorF32 color;
 | 
			
		||||
    UV data[VERTEX_LIMIT];
 | 
			
		||||
    size_t size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Vertex
 | 
			
		||||
| 
						 | 
				
			
			@ -43,17 +58,23 @@ struct Vertex
 | 
			
		|||
    ColorF32 color;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Mesh_LocalData
 | 
			
		||||
struct VertexList
 | 
			
		||||
{
 | 
			
		||||
    std::vector<Vertex> verts;
 | 
			
		||||
    std::vector<Face> faces;
 | 
			
		||||
    std::vector<TextureCoord> uvs;
 | 
			
		||||
    Vertex data[VERTEX_LIMIT];
 | 
			
		||||
    size_t size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Mesh_TransformedData
 | 
			
		||||
struct Face
 | 
			
		||||
{
 | 
			
		||||
    std::vector<Vertex> verts;
 | 
			
		||||
    std::vector<Face> faces;
 | 
			
		||||
    int vertIndex[3];
 | 
			
		||||
    int uvIndex[3];
 | 
			
		||||
    int materialIndex;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct FaceList
 | 
			
		||||
{
 | 
			
		||||
    Face data[FACE_LIMIT];
 | 
			
		||||
    size_t size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Mesh
 | 
			
		||||
| 
						 | 
				
			
			@ -74,22 +95,17 @@ struct Mesh
 | 
			
		|||
    Point position;
 | 
			
		||||
    float rotation[3];
 | 
			
		||||
    float scale;
 | 
			
		||||
 | 
			
		||||
    Material material;
 | 
			
		||||
 | 
			
		||||
    Mesh_LocalData local;
 | 
			
		||||
    Mesh_TransformedData transformed;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// PUBLIC FUNCTIONS
 | 
			
		||||
void CullBackfaces(
 | 
			
		||||
        Mesh_LocalData &local, Mesh_TransformedData &transformed,
 | 
			
		||||
        Point &camPosition);
 | 
			
		||||
void ClipAndCull(
 | 
			
		||||
        VertexList &verts, FaceList &localFaces,
 | 
			
		||||
        FaceList &transFaces, Point &camPosition);
 | 
			
		||||
 | 
			
		||||
void RenderMesh(
 | 
			
		||||
        Engine_Buffer &buffer, Mesh_TransformedData &mesh,
 | 
			
		||||
        Texture &texture, std::vector<TextureCoord> &uvs);
 | 
			
		||||
        Engine_Buffer &buffer, FaceList &faces, VertexList &verts,
 | 
			
		||||
        UVList &uvs, TextureList &textures);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define GEOMETRY_H
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,8 +3,8 @@
 | 
			
		|||
#include "geometry.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int LoadMesh(char *filename, Mesh &mesh);
 | 
			
		||||
int LoadTexture(char *filename, Texture &texture);
 | 
			
		||||
int ParseOBJ(char *filename, VertexList &verts, FaceList &faces, UVList &uvs);
 | 
			
		||||
int ParseMTL(char *filename, MaterialList &materials, TextureList &textures);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define LOADER_H
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,36 +22,48 @@ struct Point
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
// OPERATORS
 | 
			
		||||
// v / f
 | 
			
		||||
inline Point operator/(Point v, float f)
 | 
			
		||||
// p / f
 | 
			
		||||
inline Point operator/(Point p, float f)
 | 
			
		||||
{
 | 
			
		||||
    Point result;
 | 
			
		||||
 | 
			
		||||
    float inverse = 1.0f / f;
 | 
			
		||||
 | 
			
		||||
    result.x = v.x * inverse;
 | 
			
		||||
    result.y = v.y * inverse;
 | 
			
		||||
    result.z = v.z * inverse;
 | 
			
		||||
    result.x = p.x * inverse;
 | 
			
		||||
    result.y = p.y * inverse;
 | 
			
		||||
    result.z = p.z * inverse;
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
inline Vector operator-(Point v1, Point v2)
 | 
			
		||||
// p1 - p2
 | 
			
		||||
inline Vector operator-(Point p1, Point p2)
 | 
			
		||||
{
 | 
			
		||||
    Vector result;
 | 
			
		||||
 | 
			
		||||
    result.x = v1.x - v2.x;
 | 
			
		||||
    result.y = v1.y - v2.y;
 | 
			
		||||
    result.z = v1.z - v2.z;
 | 
			
		||||
    result.x = p1.x - p2.x;
 | 
			
		||||
    result.y = p1.y - p2.y;
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										185
									
								
								src/engine.cpp
								
								
								
								
							
							
						
						
									
										185
									
								
								src/engine.cpp
								
								
								
								
							| 
						 | 
				
			
			@ -9,41 +9,61 @@
 | 
			
		|||
#include "util.h"
 | 
			
		||||
#include "vec.h"
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// GLOBALS
 | 
			
		||||
static Mesh mesh;
 | 
			
		||||
static Camera camera;
 | 
			
		||||
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
 | 
			
		||||
static void CheckInputs(uint32_t input);
 | 
			
		||||
static void ComputeNormals(VertexList &verts, FaceList &faces);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
    {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mesh.position.z = 250;
 | 
			
		||||
    mesh.material.kDiffuse = {1.0,1.0,1.0,1.0};
 | 
			
		||||
    mesh.material.kAmbient = {1.0,1.0,1.0,1.0};
 | 
			
		||||
    mesh.material.smooth = true;
 | 
			
		||||
    printf("Verts: %lu\n", localVerts.size);
 | 
			
		||||
    printf("Faces: %lu\n", localFaces.size);
 | 
			
		||||
    printf("Materials: %lu\n", materials.size);
 | 
			
		||||
 | 
			
		||||
    transVerts.size = localVerts.size;
 | 
			
		||||
 | 
			
		||||
    ComputeNormals(localVerts, localFaces);
 | 
			
		||||
 | 
			
		||||
    mesh.position.z = 50;
 | 
			
		||||
    mesh.position.y = -50;
 | 
			
		||||
 | 
			
		||||
    camera.SetFOV(90.0f, buffer.width, buffer.height);
 | 
			
		||||
 | 
			
		||||
    lights.diffuse = (LightDiffuse*)malloc(sizeof(LightDiffuse));
 | 
			
		||||
    lights.diffuseCount = 1;
 | 
			
		||||
    lights.ambient.intensity = 1.0;
 | 
			
		||||
    lights.diffuse[0].intensity = 0.5;
 | 
			
		||||
    lights.diffuse[0].direction = Vector(1,1,1);
 | 
			
		||||
    lights.ambient.intensity = 1.0f;
 | 
			
		||||
    lights.diffuse[0].intensity = 1.0f;
 | 
			
		||||
    lights.diffuse[0].direction = Vector(1.0f, 1.0f, 1.0f);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -66,57 +86,6 @@ void Engine_Render(Engine_Buffer &buffer, uint32_t input)
 | 
			
		|||
 | 
			
		||||
    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
 | 
			
		||||
    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(
 | 
			
		||||
            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
 | 
			
		||||
    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;
 | 
			
		||||
        mesh.transformed.verts[v].point.x /= mesh.transformed.verts[v].point.w;
 | 
			
		||||
        mesh.transformed.verts[v].point.y /= mesh.transformed.verts[v].point.w;
 | 
			
		||||
        mesh.transformed.verts[v].point.z /= mesh.transformed.verts[v].point.w;
 | 
			
		||||
        transVerts.data[v].point *= tScreen;
 | 
			
		||||
        transVerts.data[v].point.x /= transVerts.data[v].point.w;
 | 
			
		||||
        transVerts.data[v].point.y /= transVerts.data[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)
 | 
			
		||||
| 
						 | 
				
			
			@ -209,3 +219,44 @@ static void CheckInputs(uint32_t input)
 | 
			
		|||
        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 "geometry.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// MACROS
 | 
			
		||||
| 
						 | 
				
			
			@ -30,77 +29,77 @@ struct BoundingBox
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
// PUBLIC FUNCTIONS
 | 
			
		||||
void CullBackfaces(
 | 
			
		||||
        Mesh_LocalData &local, Mesh_TransformedData &transformed,
 | 
			
		||||
        Point &camPosition)
 | 
			
		||||
void ClipAndCull(
 | 
			
		||||
        VertexList &verts, FaceList &localFaces,
 | 
			
		||||
        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];
 | 
			
		||||
        unsigned int v1 = local.faces[f].vertIndex[1];
 | 
			
		||||
        unsigned int v2 = local.faces[f].vertIndex[2];
 | 
			
		||||
        Face &face = localFaces.data[f];
 | 
			
		||||
 | 
			
		||||
        Vector v01 = transformed.verts[v1].point - transformed.verts[v0].point;
 | 
			
		||||
        Vector v02 = transformed.verts[v2].point - transformed.verts[v0].point;
 | 
			
		||||
        Vector normal = Vector::Cross(v01, v02);
 | 
			
		||||
        Point &p0 = verts.data[face.vertIndex[0]].point;
 | 
			
		||||
        Point &p1 = verts.data[face.vertIndex[1]].point;
 | 
			
		||||
        Point &p2 = verts.data[face.vertIndex[2]].point;
 | 
			
		||||
 | 
			
		||||
        // Store normal for flat shading
 | 
			
		||||
        local.faces[f].normal = normal;
 | 
			
		||||
        local.faces[f].normal.Normalize();
 | 
			
		||||
        // Ignore this face if its Z is outside the Z clip planes
 | 
			
		||||
        if ( (p0.z < -p0.w)
 | 
			
		||||
            || (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
 | 
			
		||||
        Vector view = camPosition - transformed.verts[v0].point;
 | 
			
		||||
        Vector view = camPosition - p0;
 | 
			
		||||
 | 
			
		||||
        float dot = Vector::Dot(normal, view);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Not a backface; add it to the list
 | 
			
		||||
        if (dot < EPSILON_E3)
 | 
			
		||||
        {
 | 
			
		||||
            transformed.faces.push_back(local.faces[f]);
 | 
			
		||||
            transFaces.data[faceIndex] = face;
 | 
			
		||||
            ++faceIndex;
 | 
			
		||||
            transFaces.size = (size_t)faceIndex;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RenderMesh(
 | 
			
		||||
        Engine_Buffer &buffer, Mesh_TransformedData &mesh,
 | 
			
		||||
        Texture &texture, std::vector<TextureCoord> &uvs)
 | 
			
		||||
        Engine_Buffer &buffer, FaceList &faces, VertexList &verts,
 | 
			
		||||
        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
 | 
			
		||||
        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];
 | 
			
		||||
        Face &face = faces.data[f];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // The UVs of this face's vertices
 | 
			
		||||
        unsigned int tIndex0 = mesh.faces[f].textureIndex[0];
 | 
			
		||||
        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];
 | 
			
		||||
        Point &p0 = verts.data[face.vertIndex[0]].point;
 | 
			
		||||
        Point &p1 = verts.data[face.vertIndex[1]].point;
 | 
			
		||||
        Point &p2 = verts.data[face.vertIndex[2]].point;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Bounding box for barycentric calculations (top-left fill convention)
 | 
			
		||||
        BoundingBox box(v0.point, v1.point, v2.point);
 | 
			
		||||
        int yMin = (int)ceilf(box.yMin);
 | 
			
		||||
        int yMax = (int)ceilf(box.yMax) - 1;
 | 
			
		||||
        int xMin = (int)ceilf(box.xMin);
 | 
			
		||||
        int xMax = (int)ceilf(box.xMax) - 1;
 | 
			
		||||
        BoundingBox box(p0, p1, p2);
 | 
			
		||||
        int yMin = (int)MAX(ceilf(box.yMin), 0);
 | 
			
		||||
        int yMax = (int)MIN(ceilf(box.yMax) - 1, buffer.height - 1);
 | 
			
		||||
        int xMin = (int)MAX(ceilf(box.xMin), 0);
 | 
			
		||||
        int xMax = (int)MIN(ceilf(box.xMax) - 1, buffer.width - 1);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Constants for this triangle used for barycentric calculations
 | 
			
		||||
        Vector v01 = v1.point - v0.point;
 | 
			
		||||
        Vector v02 = v2.point - v0.point;
 | 
			
		||||
        Vector v01 = p1 - p0;
 | 
			
		||||
        Vector v02 = p2 - p0;
 | 
			
		||||
        float dot0101 = Vector::Dot(v01, v01);
 | 
			
		||||
        float dot0102 = Vector::Dot(v01, v02);
 | 
			
		||||
        float dot0202 = Vector::Dot(v02, v02);
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +112,7 @@ void RenderMesh(
 | 
			
		|||
            {
 | 
			
		||||
                // Constant terms used for barycentric calculation
 | 
			
		||||
                Point p(x, y, 1.0f);
 | 
			
		||||
                Vector v0P = p - v0.point;
 | 
			
		||||
                Vector v0P = p - p0;
 | 
			
		||||
                float dot0P01 = Vector::Dot(v0P, v01);
 | 
			
		||||
                float dot0P02 = Vector::Dot(v0P, v02);
 | 
			
		||||
                float denomInv = 1.0f / ((dot0101 * dot0202) - (dot0102 * dot0102));
 | 
			
		||||
| 
						 | 
				
			
			@ -128,43 +127,51 @@ void RenderMesh(
 | 
			
		|||
 | 
			
		||||
                // Point is inside the triangle
 | 
			
		||||
                if ( (barycenter[0] >= 0.0f)
 | 
			
		||||
                    && (barycenter[1] >= 0.0f)
 | 
			
		||||
                    && (barycenter[2] >= 0.0f))
 | 
			
		||||
                        && (barycenter[1] >= 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
 | 
			
		||||
                    float a = barycenter[0] * v1.point.w * v2.point.w;
 | 
			
		||||
                    float b = barycenter[1] * v0.point.w * v2.point.w;
 | 
			
		||||
                    float c = barycenter[2] * v0.point.w * v1.point.w;
 | 
			
		||||
                    float a = barycenter[0] * p1.w * p2.w;
 | 
			
		||||
                    float b = barycenter[1] * p0.w * p2.w;
 | 
			
		||||
                    float c = barycenter[2] * p0.w * p1.w;
 | 
			
		||||
                    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
 | 
			
		||||
                    float uInterp = ((a * t0.u) + (b * t1.u) + (c * t2.u)) * abc;
 | 
			
		||||
                    float vInterp = ((a * t0.v) + (b * t1.v) + (c * t2.v)) * abc;
 | 
			
		||||
                    float uInterp = ((a * uv0.u) + (b * uv1.u) + (c * uv2.u)) * abc;
 | 
			
		||||
                    float vInterp = ((a * uv0.v) + (b * uv1.v) + (c * uv2.v)) * abc;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    // Convert U and V to pixels in the texture image
 | 
			
		||||
                    Texture &texture = textures.data[faces.data[f].materialIndex];
 | 
			
		||||
                    uInterp *= texture.width;
 | 
			
		||||
                    vInterp *= texture.height;
 | 
			
		||||
                    unsigned int u = (unsigned int)uInterp;
 | 
			
		||||
                    unsigned int v = (unsigned int)vInterp;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    // Gouraud shading
 | 
			
		||||
                    ColorF32 shading =
 | 
			
		||||
                        (barycenter[0] * v0.color)
 | 
			
		||||
                        + (barycenter[1] * v1.color)
 | 
			
		||||
                        + (barycenter[2] * v2.color);
 | 
			
		||||
                    // Constant terms for bilinear filtering
 | 
			
		||||
                    float du = uInterp - u;
 | 
			
		||||
                    float dv = vInterp - v;
 | 
			
		||||
                    float duDiff = 1 - du;
 | 
			
		||||
                    float dvDiff = 1 - dv;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    // Bilinear filtering
 | 
			
		||||
                    float du = uInterp - u;
 | 
			
		||||
                    float dv = vInterp - v;
 | 
			
		||||
                    float duDiff = 1-du;
 | 
			
		||||
                    float dvDiff = 1-dv;
 | 
			
		||||
 | 
			
		||||
                    ColorU32 color;
 | 
			
		||||
 | 
			
		||||
                    color.b = (uint8_t)
 | 
			
		||||
                        (duDiff * dvDiff * texture.texels[v][u].b
 | 
			
		||||
                         + du * dvDiff * texture.texels[v][u+1].b
 | 
			
		||||
| 
						 | 
				
			
			@ -193,9 +200,9 @@ void RenderMesh(
 | 
			
		|||
                    // Interpolate 1/z for the z-buffer
 | 
			
		||||
                    float zInv =
 | 
			
		||||
                        1.0f /
 | 
			
		||||
                        ((barycenter[0] * v0.point.w)
 | 
			
		||||
                        + (barycenter[1] * v1.point.w)
 | 
			
		||||
                        + (barycenter[2] * v2.point.w));
 | 
			
		||||
                        ((barycenter[0] * p0.w)
 | 
			
		||||
                         + (barycenter[1] * p1.w)
 | 
			
		||||
                         + (barycenter[2] * p2.w));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    // 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 char *GetLine(char *buffer, int maxLength, FILE *fp);
 | 
			
		||||
static void ComputeNormals(Mesh &mesh);
 | 
			
		||||
static int LoadTexture(char *filename, Texture &texture);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#pragma pack(push, 1)
 | 
			
		||||
struct BMP_Header
 | 
			
		||||
| 
						 | 
				
			
			@ -34,15 +34,9 @@ struct BMP_Header
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
// PUBLIC FUNCTIONS
 | 
			
		||||
int LoadMesh(char *filename, Mesh &mesh)
 | 
			
		||||
int ParseOBJ(char *filename, VertexList &verts, FaceList &faces, UVList &uvs)
 | 
			
		||||
{
 | 
			
		||||
    FILE *fp;
 | 
			
		||||
    char buffer[256];
 | 
			
		||||
    char *token;
 | 
			
		||||
 | 
			
		||||
    char garbage[5];
 | 
			
		||||
 | 
			
		||||
    fp = fopen(filename, "r");
 | 
			
		||||
    FILE *fp = fopen(filename, "r");
 | 
			
		||||
 | 
			
		||||
    if (fp == NULL)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -50,77 +44,158 @@ int LoadMesh(char *filename, Mesh &mesh)
 | 
			
		|||
        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(
 | 
			
		||||
                        token, "%s %f %f %f",
 | 
			
		||||
                        garbage,
 | 
			
		||||
                        &v.point.x,
 | 
			
		||||
                        &v.point.y,
 | 
			
		||||
                        &v.point.z);
 | 
			
		||||
 | 
			
		||||
                mesh.local.verts.push_back(v);
 | 
			
		||||
                ++vertIndex;
 | 
			
		||||
            }
 | 
			
		||||
            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(
 | 
			
		||||
                        token, "%s %f %f",
 | 
			
		||||
                        garbage,
 | 
			
		||||
                        &textureCoord.u,
 | 
			
		||||
                        &textureCoord.v);
 | 
			
		||||
                ++uvIndex;
 | 
			
		||||
            }
 | 
			
		||||
            else if (strcmp(type, "usemtl") == 0)
 | 
			
		||||
            {
 | 
			
		||||
                ++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);
 | 
			
		||||
 | 
			
		||||
    printf("Mesh: %s [v: %lu  f: %lu]\n", filename, mesh.local.verts.size(), mesh.local.faces.size());
 | 
			
		||||
 | 
			
		||||
    mesh.transformed.verts.resize(mesh.local.verts.size());
 | 
			
		||||
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
    verts.size = (size_t)vertIndex;
 | 
			
		||||
    uvs.size = (size_t)uvIndex;
 | 
			
		||||
    faces.size = (size_t)faceIndex;
 | 
			
		||||
 | 
			
		||||
    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");
 | 
			
		||||
    if (fp == NULL)
 | 
			
		||||
| 
						 | 
				
			
			@ -167,57 +242,3 @@ int LoadTexture(char *filename, Texture &texture)
 | 
			
		|||
    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
 | 
			
		||||
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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char *filename = argv[1];
 | 
			
		||||
    char *objFilename = argv[1];
 | 
			
		||||
    char *mtlFilename = argv[2];
 | 
			
		||||
    Platform platform = {};
 | 
			
		||||
 | 
			
		||||
    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.height = platform.surface->h;
 | 
			
		||||
 | 
			
		||||
        result = Engine_Init(buffer, filename);
 | 
			
		||||
        result = Engine_Init(buffer, objFilename, mtlFilename);
 | 
			
		||||
 | 
			
		||||
        if (result < 0)
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,6 @@
 | 
			
		|||
#include "platform.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <SDL2/SDL.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue