224 lines
5.4 KiB
C++
224 lines
5.4 KiB
C++
#include "loader.h"
|
|
#include <cctype>
|
|
#include <cstdio>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
|
|
// STATIC PROTOTYPES
|
|
static char *GetLine(char *buffer, int maxLength, FILE *fp);
|
|
static void ComputeNormals(Mesh &mesh);
|
|
|
|
#pragma pack(push, 1)
|
|
struct BMP_Header
|
|
{
|
|
uint16_t fileType;
|
|
uint32_t fileSize;
|
|
uint16_t reserved0;
|
|
uint16_t reserved1;
|
|
uint32_t bitmapOffset;
|
|
uint32_t size;
|
|
int32_t width;
|
|
int32_t height;
|
|
uint16_t planes;
|
|
uint16_t bitsPerPixel;
|
|
uint32_t compression;
|
|
uint32_t sizeOfBitmap;
|
|
int32_t horizRes;
|
|
int32_t vertRes;
|
|
uint32_t colorsUsed;
|
|
uint32_t colorsImportant;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
|
|
// PUBLIC FUNCTIONS
|
|
int LoadMesh(char *filename, Mesh &mesh)
|
|
{
|
|
FILE *fp;
|
|
char buffer[256];
|
|
char *token;
|
|
|
|
char garbage[5];
|
|
|
|
fp = fopen(filename, "r");
|
|
|
|
if (fp == NULL)
|
|
{
|
|
fprintf(stderr, "Error loading file: %s\n", filename);
|
|
return -1;
|
|
}
|
|
|
|
token = GetLine(buffer, sizeof(buffer), fp);
|
|
|
|
while (token != NULL)
|
|
{
|
|
if (token[0] == 'v')
|
|
{
|
|
if (token[1] == ' ')
|
|
{
|
|
Vertex v;
|
|
|
|
sscanf(
|
|
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')
|
|
{
|
|
TextureCoord textureCoord;
|
|
|
|
sscanf(
|
|
token, "%s %f %f",
|
|
garbage,
|
|
&textureCoord.u,
|
|
&textureCoord.v);
|
|
|
|
mesh.local.uvs.push_back(textureCoord);
|
|
}
|
|
}
|
|
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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int LoadTexture(char *filename, Texture &texture)
|
|
{
|
|
FILE *fp = fopen(filename, "r");
|
|
if (fp == NULL)
|
|
{
|
|
fprintf(stderr, "Could not open file: %s\n", filename);
|
|
return -1;
|
|
}
|
|
|
|
BMP_Header header = {};
|
|
fread((void*)&header, sizeof(BMP_Header), 1, fp);
|
|
fseek(fp, header.bitmapOffset, SEEK_SET);
|
|
|
|
texture.texels = (ColorU32**)malloc((size_t)header.height * sizeof(ColorU32*));
|
|
for (int row = 0; row < header.height; ++row)
|
|
{
|
|
texture.texels[row] = (ColorU32*)calloc(1, (size_t)header.width * sizeof(ColorU32));
|
|
}
|
|
|
|
// Padding is added to image to align to 4-byte boundaries
|
|
size_t paddingSize = header.width % 4;
|
|
uint8_t *padding = (uint8_t*)malloc(paddingSize * sizeof(*padding));
|
|
|
|
for (int y = 0; y < header.height; ++y)
|
|
{
|
|
for (int x = 0; x < header.width; ++x)
|
|
{
|
|
fread(&texture.texels[y][x].b, 1, 1, fp);
|
|
fread(&texture.texels[y][x].g, 1, 1, fp);
|
|
fread(&texture.texels[y][x].r, 1, 1, fp);
|
|
}
|
|
|
|
// Discard padding byte
|
|
if (paddingSize != 0)
|
|
{
|
|
fread(padding, paddingSize, 1, fp);
|
|
}
|
|
}
|
|
|
|
texture.width = (unsigned int)header.width;
|
|
texture.height = (unsigned int)header.height;
|
|
|
|
fclose(fp);
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|