From fdaa55621fc7f815c236520ff65033eb8c8c6ca0 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Tue, 30 Dec 2025 17:25:44 -0800 Subject: Parse diffuse texture from material --- tools/ase/main.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 13 deletions(-) (limited to 'tools/ase/main.c') diff --git a/tools/ase/main.c b/tools/ase/main.c index 6ba770a..30c6812 100644 --- a/tools/ase/main.c +++ b/tools/ase/main.c @@ -2,12 +2,33 @@ #include #include -#include +#include #include #include #include #include +void GetParentDir(const char* path, char parent[PATH_MAX]) { + assert(path); + assert(parent); + const size_t path_len = strlen(path); + size_t parent_len = path_len - 1; + for (; parent_len > 0; --parent_len) { + if (path[parent_len] == '/') { + break; + } + } + memset(parent, 0, PATH_MAX); + memcpy(parent, path, parent_len); +} + +void PathConcat(const char* left, const char* right, char out[PATH_MAX]) { + assert(left); + assert(right); + assert(out); + snprintf(out, PATH_MAX, "%s/%s", left, right); +} + typedef struct Lexeme { const char* str; size_t length; @@ -25,6 +46,8 @@ static void LexerMake(const char* data, size_t size, Lexer* lexer) { assert(lexer); lexer->buffer = data; lexer->size = size; + lexer->next = 0; + lexer->lexeme = (Lexeme){0}; } static bool End(const Lexer* lexer) { @@ -110,6 +133,21 @@ static bool NextLexeme(Lexer* lexer) { return lexer->lexeme.length > 0; } +static bool ReadLine(Lexer* lexer) { + assert(lexer); + SkipWhiteSpace(lexer); + // Advance until we find a newline character. + lexer->lexeme.str = NextPtr(lexer); + lexer->lexeme.length = 0; + while (HasNext(lexer) && (Next(lexer) != '\n')) { + Advance(lexer); + lexer->lexeme.length++; + } + // Skip the newline character. + SkipChar(lexer); + return lexer->lexeme.length > 0; +} + static bool ParseFloat(const Lexeme* lex, float* out) { assert(lex); assert(out); @@ -136,19 +174,29 @@ typedef struct ModelData { uint32_t numPositions; uint32_t numNormals; uint32_t numTexcoords; + Material material; + char mtl_file [PATH_MAX]; mdTri tris [MAX_TRIS]; mdVec3 positions[MAX_VERTS]; mdVec3 normals [MAX_VERTS]; mdVec2 texcoords[MAX_VERTS]; } ModelData; +#define PRINT(STR) printf("%s%.*s\n", STR, (int)lexer->lexeme.length, lexer->lexeme.str) +#define LEX(STR) IsLexeme(lexer, STR) +#define NEXT_LEXEME() { if (!NextLexeme(lexer)) break; else PRINT("~ "); } +#define NEXT_FLOAT(PTR) { NEXT_LEXEME(); if (!ParseFloat(&lexer->lexeme, PTR)) break; } +#define COPY_LEXEME(BUF) snprintf(BUF, sizeof(BUF), "%.*s", (int)lexer->lexeme.length, lexer->lexeme.str); +#define NEXT_STRING(BUF) { NEXT_LEXEME(); COPY_LEXEME(BUF); } +#define READ_LINE(BUF) { if (!ReadLine(lexer)) break; else { PRINT("~ "); COPY_LEXEME(BUF); } } + +// TODO: The current implementation does not support objects within the OBJ +// file. It assumes one object and one material. Add support for multiple +// objects and materials. + static bool ParseObj(Lexer* lexer, ModelData* modelData) { assert(lexer); assert(modelData); - #define PRINT(STR) printf("%s%.*s\n", STR, (int)lexer->lexeme.length, lexer->lexeme.str) - #define LEX(STR) IsLexeme(lexer, STR) - #define NEXT_LEXEME() { if (!NextLexeme(lexer)) break; else PRINT("~ "); } - #define NEXT_FLOAT(PTR) { NEXT_LEXEME(); if (!ParseFloat(&lexer->lexeme, PTR)) break; } bool consumeNext = true; for (;;) { if (consumeNext) { @@ -158,7 +206,7 @@ static bool ParseObj(Lexer* lexer, ModelData* modelData) { if (LEX("#")) { SkipLine(lexer); } else if (LEX("mtllib")) { - NEXT_LEXEME(); // material file + NEXT_STRING(modelData->mtl_file); PRINT("> material: "); } else if (LEX("o")) { NEXT_LEXEME(); // object name @@ -224,6 +272,21 @@ static bool ParseObj(Lexer* lexer, ModelData* modelData) { return true; } +static bool ParseMtl(Lexer* lexer, Material* material) { + assert(lexer); + assert(material); + for (;;) { + NEXT_LEXEME(); + if (LEX("newmtl")) { + NEXT_LEXEME(); // Material name. + PRINT("> material: "); + } else if (LEX("map_Kd")) { + READ_LINE(material->diffuseTexture); + } + } + return true; +} + static bool WriteModelFile(const ModelData* modelData, const char* path) { assert(modelData); assert(path); @@ -233,8 +296,8 @@ static bool WriteModelFile(const ModelData* modelData, const char* path) { Model model = {0}; // Fill the Model header. - IndexedModel* indexed = &model.indexed; model.type = ModelTypeIndexed; + IndexedModel* indexed = &model.indexed; indexed->numTris = modelData->numTris; indexed->numPositions = modelData->numPositions; indexed->numNormals = modelData->numNormals; @@ -243,7 +306,8 @@ static bool WriteModelFile(const ModelData* modelData, const char* path) { indexed->offsetPositions = indexed->offsetTris + (modelData->numTris * sizeof(mdTri)); indexed->offsetNormals = indexed->offsetPositions + (modelData->numPositions * sizeof(mdVec3)); indexed->offsetTexcoords = indexed->offsetNormals + (modelData->numNormals * sizeof(mdVec3)); - + memcpy(&model.material, &modelData->material, sizeof(Material)); + if ((file = fopen(path, "wb")) == nullptr) { fprintf(stderr, "Failed opening output file for writing: %s\n", path); goto cleanup; @@ -299,12 +363,11 @@ static bool ReadFile(const char* path, uint8_t** outData, size_t* outSize) { if (fileSize == (size_t)(-1)) { goto cleanup; } - // Allocate one extra byte so that text file data conveniently ends with null. - const size_t size = fileSize + 1; if (fseek(file, 0, SEEK_SET) != 0) { goto cleanup; } - if ((data = calloc(1, size)) == nullptr) { + // Allocate one extra byte so that text file data conveniently ends with null. + if ((data = calloc(1, fileSize+1)) == nullptr) { goto cleanup; } if (fread(data, fileSize, 1, file) != 1) { @@ -312,7 +375,7 @@ static bool ReadFile(const char* path, uint8_t** outData, size_t* outSize) { } *outData = data; - *outSize = size; + *outSize = fileSize; success = true; cleanup: @@ -355,10 +418,24 @@ int main(int argc, const char** argv) { goto cleanup; } LexerMake((const char*)fileData, dataSize, &lexer); - if (!ParseObj(&lexer, modelData)) { goto cleanup; } + if (modelData->mtl_file[0] != 0) { + free(fileData); + fileData = nullptr; + char dir[PATH_MAX]; + char mtl[PATH_MAX]; + GetParentDir(filePath, dir); + PathConcat(dir, modelData->mtl_file, mtl); + if (!ReadFile(mtl, &fileData, &dataSize)) { + goto cleanup; + } + LexerMake((const char*)fileData, dataSize, &lexer); + if (!ParseMtl(&lexer, &modelData->material)) { + goto cleanup; + } + } if (!WriteModelFile(modelData, outPath)) { goto cleanup; } -- cgit v1.2.3