Last active
January 22, 2025 18:10
-
-
Save cs127/69353b1f17aee5b13ea8596ca6189f05 to your computer and use it in GitHub Desktop.
Valhalla Vrender V3D to Wavefront OBJ converter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// v3dobj by cs127 | |
// version 0.04 | |
// 2025-01-22 | |
#include <stdbool.h> | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#define V3D_SCALE 64.0 | |
#define V3D_MSG_COL true | |
#if V3D_MSG_COL | |
#define V3D_MSG_RED "\e[31m" | |
#define V3D_MSG_YELLOW "\e[33m" | |
#define V3D_MSG_RESET "\e[39m" | |
#else | |
#define V3D_MSG_RED "" | |
#define V3D_MSG_YELLOW "" | |
#define V3D_MSG_RESET "" | |
#endif | |
#define v3d_warnf(e, ...) \ | |
do \ | |
{ \ | |
fputs(V3D_MSG_YELLOW, stderr); \ | |
fprintf(stderr, V3D_WARNMSG[e], __VA_ARGS__); \ | |
fputs(V3D_MSG_RESET, stderr); \ | |
fputc('\n', stderr); \ | |
} \ | |
while (false) | |
#define v3d_assertf(c, e, ...) \ | |
do \ | |
{ \ | |
if (!(c)) \ | |
{ \ | |
fputs(V3D_MSG_RED, stderr); \ | |
fprintf(stderr, V3D_ERRMSG[e], __VA_ARGS__); \ | |
fputs(V3D_MSG_RESET, stderr); \ | |
fputc('\n', stderr); \ | |
ret = e; \ | |
goto l_error; \ | |
} \ | |
} \ | |
while (false) | |
#define v3d_warn(e) v3d_warnf(e, 0) | |
#define v3d_assert(c, e) v3d_assertf(c, e, 0) | |
#define v3d_destroy(p, f) do {if (p) {f(p); p = NULL;}} while (false) | |
#define v3d_free(p) v3d_destroy(p, free) | |
#define v3d_fclose(p) v3d_destroy(p, fclose) | |
#define v3d_ok(f) (!feof(f) && !ferror(f)) | |
#define v3d_skip(f, n) fseek(f, n, SEEK_CUR) | |
typedef enum v3d_error | |
{ | |
V3D_ERR_OK = 0, | |
V3D_ERR_ARG = 1, | |
V3D_ERR_OPEN, | |
V3D_ERR_READ, | |
V3D_ERR_WRITE, | |
V3D_ERR_MEM, | |
V3D_ERR_VIDX | |
} | |
v3d_error_t; | |
typedef enum v3d_warn | |
{ | |
V3D_WARN_OK = 0, | |
V3D_WARN_0SIZE = 1, | |
V3D_WARN_NOSIZE, | |
V3D_WARN_BOUND | |
} | |
v3d_warn_t; | |
typedef struct v3d_vert | |
{ | |
int16_t x, y, z; | |
} | |
v3d_vert_t; | |
typedef struct v3d_tcoords | |
{ | |
int16_t u, v; | |
} | |
v3d_tcoords_t; | |
typedef struct v3d_tri | |
{ | |
uint16_t verts [3]; | |
v3d_tcoords_t uv [3]; | |
} | |
v3d_tri_t; | |
const char* V3D_ERRMSG [] = | |
{ | |
[V3D_ERR_ARG] = "incorrect number of arguments.", | |
[V3D_ERR_OPEN] = "could not open file %s.", | |
[V3D_ERR_READ] = "failed to read from file %s. (malformed file?)", | |
[V3D_ERR_WRITE] = "failed to write to file %s.", | |
[V3D_ERR_MEM] = "not enough memory.", | |
[V3D_ERR_VIDX] = "invalid vertex index in tri %zu." | |
}; | |
const char* V3D_WARNMSG [] = | |
{ | |
[V3D_WARN_0SIZE] = "invalid texture size. ignoring UV map.", | |
[V3D_WARN_NOSIZE] = "no texture size specified. ignoring UV map.", | |
[V3D_WARN_BOUND] = "uv coordinate %c=%hd out of bounds in tri %zu." | |
}; | |
int main(int argc, char** argv) | |
{ | |
v3d_error_t ret; | |
uint16_t texw = 0, texh = 0; | |
FILE* fp = NULL; | |
uint16_t nverts, ntris; | |
v3d_vert_t* verts = NULL; | |
v3d_tri_t* tris = NULL; | |
size_t i, j; | |
v3d_assert(argc == 2 || argc == 4, V3D_ERR_ARG); | |
// get texture size from command line | |
if (argc == 4) | |
{ | |
sscanf(argv[2], "%hu", &texw); | |
sscanf(argv[3], "%hu", &texh); | |
if (!texw || !texh) v3d_warn(V3D_WARN_0SIZE); | |
} | |
else v3d_warn(V3D_WARN_NOSIZE); | |
fp = fopen(argv[1], "rb"); | |
v3d_assertf(fp, V3D_ERR_OPEN, argv[1]); | |
fread(&nverts, 2, 1, fp); | |
fread(&ntris, 2, 1, fp); | |
v3d_assertf(v3d_ok(fp), V3D_ERR_READ, argv[1]); | |
verts = calloc(nverts, sizeof(v3d_vert_t)); | |
tris = calloc(ntris, sizeof(v3d_tri_t)); | |
v3d_assert(verts && tris, V3D_ERR_MEM); | |
// read vertices | |
for (i = 0; i < nverts; i++) | |
{ | |
fread(&verts[i].x, 2, 1, fp); | |
fread(&verts[i].y, 2, 1, fp); | |
fread(&verts[i].z, 2, 1, fp); | |
v3d_skip(fp, 6); | |
} | |
v3d_assertf(v3d_ok(fp), V3D_ERR_READ, argv[1]); | |
// read tris | |
for (i = 0; i < ntris; i++) | |
{ | |
uint32_t o [3]; | |
int16_t u, v; | |
// vertex indices | |
for (j = 0; j < 3; j++) | |
{ | |
fread(&o[j], 4, 1, fp); | |
v3d_assertf(v3d_ok(fp), V3D_ERR_READ, argv[1]); | |
v3d_assertf(o[j] % 12 == 0, V3D_ERR_VIDX, i); | |
o[j] /= 12; | |
v3d_assertf(o[j] < nverts, V3D_ERR_VIDX, i); | |
tris[i].verts[j] = o[j]; | |
} | |
v3d_skip(fp, 4); | |
// vertex uv coordinates | |
for (j = 0; j < 3; j++) | |
{ | |
fread(&v, 2, 1, fp); | |
fread(&u, 2, 1, fp); | |
v3d_assertf(v3d_ok(fp), V3D_ERR_READ, argv[1]); | |
if (texw && texh) | |
{ | |
if (u < 0 || u > texw) | |
v3d_warnf(V3D_WARN_BOUND, 'u', u, i); | |
if (v < 0 || v > texh) | |
v3d_warnf(V3D_WARN_BOUND, 'v', v, i); | |
} | |
tris[i].uv[j].u = u; | |
tris[i].uv[j].v = v; | |
} | |
v3d_skip(fp, 4); | |
} | |
v3d_assertf(v3d_ok(fp), V3D_ERR_READ, argv[1]); | |
v3d_fclose(fp); | |
// write vertices | |
for (i = 0; i < nverts; i++) | |
{ | |
double x = (double)verts[i].x / V3D_SCALE; | |
double y = (double)verts[i].y / V3D_SCALE; | |
double z = (double)verts[i].z / V3D_SCALE; | |
printf("v %f %f %f\n", x, y, z); | |
} | |
// write uv coordinates (if texture size is specified) | |
if (texw && texh) | |
{ | |
for (i = 0; i < ntris; i++) | |
{ | |
for (j = 0; j < 3; j++) | |
{ | |
double u = (double)tris[i].uv[j].u / texw; | |
double v = 1 - (double)tris[i].uv[j].v / texh; | |
printf("vt %f %f\n", u, v); | |
} | |
} | |
} | |
// write tris | |
for (i = 0; i < ntris; i++) | |
{ | |
putchar('f'); | |
for (j = 0; j < 3; j++) | |
{ | |
// vertex indices | |
printf(" %hu", tris[i].verts[j] + 1); | |
// uv coordinate indices (if texture size is specified) | |
if (texw && texh) | |
printf("/%zu", i * 3 + j + 1); | |
} | |
putchar('\n'); | |
} | |
fflush(stdout); | |
v3d_free(verts); | |
v3d_free(tris); | |
return V3D_ERR_OK; | |
l_error: | |
v3d_fclose(fp); | |
v3d_free(verts); | |
v3d_free(tris); | |
return ret; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment