#include "loader.h" #include #include #include #include #include // 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(); } } }