summaryrefslogtreecommitdiff
path: root/tools/ase
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-30 17:25:44 -0800
committer3gg <3gg@shellblade.net>2025-12-30 17:25:44 -0800
commitfdaa55621fc7f815c236520ff65033eb8c8c6ca0 (patch)
tree29deb2eeff919c2bc17c7f6a8dfb7461ea6ad158 /tools/ase
parenteba20320bf8d542a46dc3fd84ec401057a25b9da (diff)
Parse diffuse texture from material
Diffstat (limited to 'tools/ase')
-rw-r--r--tools/ase/main.c103
1 files changed, 90 insertions, 13 deletions
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 @@
2 2
3#include <assert.h> 3#include <assert.h>
4#include <errno.h> 4#include <errno.h>
5#include <stdbool.h> 5#include <linux/limits.h>
6#include <stddef.h> 6#include <stddef.h>
7#include <stdio.h> 7#include <stdio.h>
8#include <stdlib.h> 8#include <stdlib.h>
9#include <string.h> 9#include <string.h>
10 10
11void GetParentDir(const char* path, char parent[PATH_MAX]) {
12 assert(path);
13 assert(parent);
14 const size_t path_len = strlen(path);
15 size_t parent_len = path_len - 1;
16 for (; parent_len > 0; --parent_len) {
17 if (path[parent_len] == '/') {
18 break;
19 }
20 }
21 memset(parent, 0, PATH_MAX);
22 memcpy(parent, path, parent_len);
23}
24
25void PathConcat(const char* left, const char* right, char out[PATH_MAX]) {
26 assert(left);
27 assert(right);
28 assert(out);
29 snprintf(out, PATH_MAX, "%s/%s", left, right);
30}
31
11typedef struct Lexeme { 32typedef struct Lexeme {
12 const char* str; 33 const char* str;
13 size_t length; 34 size_t length;
@@ -25,6 +46,8 @@ static void LexerMake(const char* data, size_t size, Lexer* lexer) {
25 assert(lexer); 46 assert(lexer);
26 lexer->buffer = data; 47 lexer->buffer = data;
27 lexer->size = size; 48 lexer->size = size;
49 lexer->next = 0;
50 lexer->lexeme = (Lexeme){0};
28} 51}
29 52
30static bool End(const Lexer* lexer) { 53static bool End(const Lexer* lexer) {
@@ -110,6 +133,21 @@ static bool NextLexeme(Lexer* lexer) {
110 return lexer->lexeme.length > 0; 133 return lexer->lexeme.length > 0;
111} 134}
112 135
136static bool ReadLine(Lexer* lexer) {
137 assert(lexer);
138 SkipWhiteSpace(lexer);
139 // Advance until we find a newline character.
140 lexer->lexeme.str = NextPtr(lexer);
141 lexer->lexeme.length = 0;
142 while (HasNext(lexer) && (Next(lexer) != '\n')) {
143 Advance(lexer);
144 lexer->lexeme.length++;
145 }
146 // Skip the newline character.
147 SkipChar(lexer);
148 return lexer->lexeme.length > 0;
149}
150
113static bool ParseFloat(const Lexeme* lex, float* out) { 151static bool ParseFloat(const Lexeme* lex, float* out) {
114 assert(lex); 152 assert(lex);
115 assert(out); 153 assert(out);
@@ -136,19 +174,29 @@ typedef struct ModelData {
136 uint32_t numPositions; 174 uint32_t numPositions;
137 uint32_t numNormals; 175 uint32_t numNormals;
138 uint32_t numTexcoords; 176 uint32_t numTexcoords;
177 Material material;
178 char mtl_file [PATH_MAX];
139 mdTri tris [MAX_TRIS]; 179 mdTri tris [MAX_TRIS];
140 mdVec3 positions[MAX_VERTS]; 180 mdVec3 positions[MAX_VERTS];
141 mdVec3 normals [MAX_VERTS]; 181 mdVec3 normals [MAX_VERTS];
142 mdVec2 texcoords[MAX_VERTS]; 182 mdVec2 texcoords[MAX_VERTS];
143} ModelData; 183} ModelData;
144 184
185#define PRINT(STR) printf("%s%.*s\n", STR, (int)lexer->lexeme.length, lexer->lexeme.str)
186#define LEX(STR) IsLexeme(lexer, STR)
187#define NEXT_LEXEME() { if (!NextLexeme(lexer)) break; else PRINT("~ "); }
188#define NEXT_FLOAT(PTR) { NEXT_LEXEME(); if (!ParseFloat(&lexer->lexeme, PTR)) break; }
189#define COPY_LEXEME(BUF) snprintf(BUF, sizeof(BUF), "%.*s", (int)lexer->lexeme.length, lexer->lexeme.str);
190#define NEXT_STRING(BUF) { NEXT_LEXEME(); COPY_LEXEME(BUF); }
191#define READ_LINE(BUF) { if (!ReadLine(lexer)) break; else { PRINT("~ "); COPY_LEXEME(BUF); } }
192
193// TODO: The current implementation does not support objects within the OBJ
194// file. It assumes one object and one material. Add support for multiple
195// objects and materials.
196
145static bool ParseObj(Lexer* lexer, ModelData* modelData) { 197static bool ParseObj(Lexer* lexer, ModelData* modelData) {
146 assert(lexer); 198 assert(lexer);
147 assert(modelData); 199 assert(modelData);
148 #define PRINT(STR) printf("%s%.*s\n", STR, (int)lexer->lexeme.length, lexer->lexeme.str)
149 #define LEX(STR) IsLexeme(lexer, STR)
150 #define NEXT_LEXEME() { if (!NextLexeme(lexer)) break; else PRINT("~ "); }
151 #define NEXT_FLOAT(PTR) { NEXT_LEXEME(); if (!ParseFloat(&lexer->lexeme, PTR)) break; }
152 bool consumeNext = true; 200 bool consumeNext = true;
153 for (;;) { 201 for (;;) {
154 if (consumeNext) { 202 if (consumeNext) {
@@ -158,7 +206,7 @@ static bool ParseObj(Lexer* lexer, ModelData* modelData) {
158 if (LEX("#")) { 206 if (LEX("#")) {
159 SkipLine(lexer); 207 SkipLine(lexer);
160 } else if (LEX("mtllib")) { 208 } else if (LEX("mtllib")) {
161 NEXT_LEXEME(); // material file 209 NEXT_STRING(modelData->mtl_file);
162 PRINT("> material: "); 210 PRINT("> material: ");
163 } else if (LEX("o")) { 211 } else if (LEX("o")) {
164 NEXT_LEXEME(); // object name 212 NEXT_LEXEME(); // object name
@@ -224,6 +272,21 @@ static bool ParseObj(Lexer* lexer, ModelData* modelData) {
224 return true; 272 return true;
225} 273}
226 274
275static bool ParseMtl(Lexer* lexer, Material* material) {
276 assert(lexer);
277 assert(material);
278 for (;;) {
279 NEXT_LEXEME();
280 if (LEX("newmtl")) {
281 NEXT_LEXEME(); // Material name.
282 PRINT("> material: ");
283 } else if (LEX("map_Kd")) {
284 READ_LINE(material->diffuseTexture);
285 }
286 }
287 return true;
288}
289
227static bool WriteModelFile(const ModelData* modelData, const char* path) { 290static bool WriteModelFile(const ModelData* modelData, const char* path) {
228 assert(modelData); 291 assert(modelData);
229 assert(path); 292 assert(path);
@@ -233,8 +296,8 @@ static bool WriteModelFile(const ModelData* modelData, const char* path) {
233 Model model = {0}; 296 Model model = {0};
234 297
235 // Fill the Model header. 298 // Fill the Model header.
236 IndexedModel* indexed = &model.indexed;
237 model.type = ModelTypeIndexed; 299 model.type = ModelTypeIndexed;
300 IndexedModel* indexed = &model.indexed;
238 indexed->numTris = modelData->numTris; 301 indexed->numTris = modelData->numTris;
239 indexed->numPositions = modelData->numPositions; 302 indexed->numPositions = modelData->numPositions;
240 indexed->numNormals = modelData->numNormals; 303 indexed->numNormals = modelData->numNormals;
@@ -243,7 +306,8 @@ static bool WriteModelFile(const ModelData* modelData, const char* path) {
243 indexed->offsetPositions = indexed->offsetTris + (modelData->numTris * sizeof(mdTri)); 306 indexed->offsetPositions = indexed->offsetTris + (modelData->numTris * sizeof(mdTri));
244 indexed->offsetNormals = indexed->offsetPositions + (modelData->numPositions * sizeof(mdVec3)); 307 indexed->offsetNormals = indexed->offsetPositions + (modelData->numPositions * sizeof(mdVec3));
245 indexed->offsetTexcoords = indexed->offsetNormals + (modelData->numNormals * sizeof(mdVec3)); 308 indexed->offsetTexcoords = indexed->offsetNormals + (modelData->numNormals * sizeof(mdVec3));
246 309 memcpy(&model.material, &modelData->material, sizeof(Material));
310
247 if ((file = fopen(path, "wb")) == nullptr) { 311 if ((file = fopen(path, "wb")) == nullptr) {
248 fprintf(stderr, "Failed opening output file for writing: %s\n", path); 312 fprintf(stderr, "Failed opening output file for writing: %s\n", path);
249 goto cleanup; 313 goto cleanup;
@@ -299,12 +363,11 @@ static bool ReadFile(const char* path, uint8_t** outData, size_t* outSize) {
299 if (fileSize == (size_t)(-1)) { 363 if (fileSize == (size_t)(-1)) {
300 goto cleanup; 364 goto cleanup;
301 } 365 }
302 // Allocate one extra byte so that text file data conveniently ends with null.
303 const size_t size = fileSize + 1;
304 if (fseek(file, 0, SEEK_SET) != 0) { 366 if (fseek(file, 0, SEEK_SET) != 0) {
305 goto cleanup; 367 goto cleanup;
306 } 368 }
307 if ((data = calloc(1, size)) == nullptr) { 369 // Allocate one extra byte so that text file data conveniently ends with null.
370 if ((data = calloc(1, fileSize+1)) == nullptr) {
308 goto cleanup; 371 goto cleanup;
309 } 372 }
310 if (fread(data, fileSize, 1, file) != 1) { 373 if (fread(data, fileSize, 1, file) != 1) {
@@ -312,7 +375,7 @@ static bool ReadFile(const char* path, uint8_t** outData, size_t* outSize) {
312 } 375 }
313 376
314 *outData = data; 377 *outData = data;
315 *outSize = size; 378 *outSize = fileSize;
316 success = true; 379 success = true;
317 380
318cleanup: 381cleanup:
@@ -355,10 +418,24 @@ int main(int argc, const char** argv) {
355 goto cleanup; 418 goto cleanup;
356 } 419 }
357 LexerMake((const char*)fileData, dataSize, &lexer); 420 LexerMake((const char*)fileData, dataSize, &lexer);
358
359 if (!ParseObj(&lexer, modelData)) { 421 if (!ParseObj(&lexer, modelData)) {
360 goto cleanup; 422 goto cleanup;
361 } 423 }
424 if (modelData->mtl_file[0] != 0) {
425 free(fileData);
426 fileData = nullptr;
427 char dir[PATH_MAX];
428 char mtl[PATH_MAX];
429 GetParentDir(filePath, dir);
430 PathConcat(dir, modelData->mtl_file, mtl);
431 if (!ReadFile(mtl, &fileData, &dataSize)) {
432 goto cleanup;
433 }
434 LexerMake((const char*)fileData, dataSize, &lexer);
435 if (!ParseMtl(&lexer, &modelData->material)) {
436 goto cleanup;
437 }
438 }
362 if (!WriteModelFile(modelData, outPath)) { 439 if (!WriteModelFile(modelData, outPath)) {
363 goto cleanup; 440 goto cleanup;
364 } 441 }