ref: cc6fd02b8a99876a09c9d31f3e5297fc9ea27168
parent: 0c2dfb69ce90eded24c97c2a1412cb4ac1352e28
author: rodri <rgl@antares-labs.eu>
date: Sun Jun 22 13:22:45 EDT 2025
new tool: toobj† † it still has some rough edges to sand. i think there's a memory corruption somewhere that makes writememimage(2) fail when using this tool, and not when using the libobj test program.
--- a/mkfile
+++ b/mkfile
@@ -8,6 +8,7 @@
projtest\
procgen\
obj\
+ toobj\
stl\
tostl\
plot3\
--- a/obj.c
+++ b/obj.c
@@ -132,9 +132,6 @@
Point3 n; /* surface normal */
int i, idx, nt, maxnt, neednormal, gottaclean;
- if(obj == nil)
- return 0;
-
pverts = obj->vertdata[OBJVGeometric].verts;
tverts = obj->vertdata[OBJVTexture].verts;
nverts = obj->vertdata[OBJVNormal].verts;
@@ -141,12 +138,6 @@
trielems = nil;
maxnt = 0;
- if(m->prims != nil){
- free(m->prims);
- m->prims = nil;
- }
- m->nprims = 0;
-
mtlmap.head = nil;
for(i = 0; obj->materials != nil && i < nelem(obj->materials->mattab); i++)
for(objmtl = obj->materials->mattab[i]; objmtl != nil; objmtl = objmtl->next){
@@ -154,11 +145,10 @@
mtl = &m->materials[m->nmaterials-1];
memset(mtl, 0, sizeof *mtl);
- if(objmtl->name != nil){
- mtl->name = strdup(objmtl->name);
- if(mtl->name == nil)
- sysfatal("strdup: %r");
- }
+ mtl->name = strdup(objmtl->name);
+ if(mtl->name == nil)
+ sysfatal("strdup: %r");
+
if(objmtl->Ka.a > 0)
mtl->ambient = objmtl->Ka;
if(objmtl->Kd.a > 0)
--- /dev/null
+++ b/toobj.c
@@ -1,0 +1,220 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+#include "libgraphics/graphics.h"
+#include "fns.h"
+#include "libobj/obj.h"
+
+static OBJVertex
+GP2V(Point2 p)
+{
+ OBJVertex v;
+
+ v.x = p.x;
+ v.y = p.y;
+ v.z = p.w;
+ v.w = 0;
+ return v;
+}
+
+static OBJVertex
+GP3V(Point3 p)
+{
+ OBJVertex v;
+
+ v.x = p.x;
+ v.y = p.y;
+ v.z = p.z;
+ v.w = p.w;
+ return v;
+}
+
+static int
+loadobjmodel(OBJ *obj, Model *m)
+{
+ Primitive *prim;
+ OBJVertex v;
+ OBJElem *e;
+ OBJObject *o;
+ OBJMaterial *objmtl;
+ Material *mtl;
+ int i, idx;
+
+ obj->materials = objallocmtl("main.mtl");
+ for(mtl = m->materials; mtl < m->materials + m->nmaterials; mtl++){
+ objmtl = objallocmt(mtl->name);
+
+ if(mtl->ambient.a > 0)
+ objmtl->Ka = mtl->ambient;
+ if(mtl->diffuse.a > 0)
+ objmtl->Kd = mtl->diffuse;
+ if(mtl->specular.a > 0)
+ objmtl->Ks = mtl->specular;
+ objmtl->Ns = mtl->shininess;
+
+ /* TODO the default export format should be set by the user */
+ if(mtl->diffusemap != nil){
+ objmtl->map_Kd = emalloc(sizeof(OBJTexture));
+ objmtl->map_Kd->image = dupmemimage(mtl->diffusemap->image);
+ objmtl->map_Kd->filename = mtl->diffusemap->file != nil?
+ strdup(mtl->diffusemap->file):
+ smprint("%s_diffuse.png", mtl->name);
+ }
+
+ if(mtl->specularmap != nil){
+ objmtl->map_Ks = emalloc(sizeof(OBJTexture));
+ objmtl->map_Ks->image = dupmemimage(mtl->specularmap->image);
+ objmtl->map_Ks->filename = mtl->specularmap->file != nil?
+ strdup(mtl->specularmap->file):
+ smprint("%s_specular.png", mtl->name);
+ }
+
+ if(mtl->normalmap != nil){
+ objmtl->norm = emalloc(sizeof(OBJTexture));
+ objmtl->norm->image = dupmemimage(mtl->normalmap->image);
+ objmtl->norm->filename = mtl->normalmap->file != nil?
+ strdup(mtl->normalmap->file):
+ smprint("%s_normal.png", mtl->name);
+ }
+
+ objaddmtl(obj->materials, objmtl);
+ }
+
+ o = objallocobject("default");
+ objpushobject(obj, o);
+ for(prim = m->prims; prim < m->prims + m->nprims; prim++){
+ /*
+ * XXX A Model doesn't have the indexed attribute
+ * structure an OBJ has, so this conversion is very
+ * inefficient without a good deduplication algorithm.
+ */
+ switch(prim->type){
+ case PPoint:
+ e = objallocelem(OBJEPoint);
+
+ v = GP3V(prim->v[0].p);
+ idx = objaddvertex(obj, v, OBJVGeometric);
+ objaddelemidx(e, OBJVGeometric, idx);
+
+ if(memcmp(&prim->v[0].n, &ZP3, sizeof(Point3)) != 0){
+ v = GP3V(prim->v[0].n);
+ idx = objaddvertex(obj, v, OBJVNormal);
+ objaddelemidx(e, OBJVNormal, idx);
+ }
+
+ if(memcmp(&prim->v[0].uv, &ZP2, sizeof(Point2)) != 0){
+ v = GP2V(prim->v[0].uv);
+ idx = objaddvertex(obj, v, OBJVTexture);
+ objaddelemidx(e, OBJVTexture, idx);
+ }
+
+ if(prim->mtl != nil)
+ e->mtl = objgetmtl(obj->materials, prim->mtl->name);
+ objaddelem(o, e);
+ break;
+ case PLine:
+ e = objallocelem(OBJELine);
+
+ for(i = 0; i < prim->type+1; i++){
+ v = GP3V(prim->v[i].p);
+ idx = objaddvertex(obj, v, OBJVGeometric);
+ objaddelemidx(e, OBJVGeometric, idx);
+
+ if(memcmp(&prim->v[i].n, &ZP3, sizeof(Point3)) != 0){
+ v = GP3V(prim->v[i].n);
+ idx = objaddvertex(obj, v, OBJVNormal);
+ objaddelemidx(e, OBJVNormal, idx);
+ }
+
+ if(memcmp(&prim->v[i].uv, &ZP2, sizeof(Point2)) != 0){
+ v = GP2V(prim->v[i].uv);
+ idx = objaddvertex(obj, v, OBJVTexture);
+ objaddelemidx(e, OBJVTexture, idx);
+ }
+ }
+
+ if(prim->mtl != nil)
+ e->mtl = objgetmtl(obj->materials, prim->mtl->name);
+ objaddelem(o, e);
+ break;
+ case PTriangle:
+ e = objallocelem(OBJEFace);
+
+ for(i = 0; i < prim->type+1; i++){
+ v = GP3V(prim->v[i].p);
+ idx = objaddvertex(obj, v, OBJVGeometric);
+ objaddelemidx(e, OBJVGeometric, idx);
+
+ if(memcmp(&prim->v[i].n, &ZP3, sizeof(Point3)) != 0){
+ v = GP3V(prim->v[i].n);
+ idx = objaddvertex(obj, v, OBJVNormal);
+ objaddelemidx(e, OBJVNormal, idx);
+ }
+
+ if(memcmp(&prim->v[i].uv, &ZP2, sizeof(Point2)) != 0){
+ v = GP2V(prim->v[i].uv);
+ idx = objaddvertex(obj, v, OBJVTexture);
+ objaddelemidx(e, OBJVTexture, idx);
+ }
+ }
+
+ if(prim->mtl != nil)
+ e->mtl = objgetmtl(obj->materials, prim->mtl->name);
+ objaddelem(o, e);
+ break;
+ }
+ }
+
+ return m->nprims;
+}
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s [mdlfile [dstdir]]\n", argv0);
+ exits("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+ Model *m;
+ OBJ *obj;
+ char *infile, *dstdir;
+ int fd;
+
+ OBJfmtinstall();
+ ARGBEGIN{
+ default: usage();
+ }ARGEND;
+ if(argc > 2)
+ usage();
+
+ infile = argc > 0? argv[0]: "/fd/0";
+ dstdir = argc == 2? argv[1]: nil;
+
+ fd = open(infile, OREAD);
+ if(fd < 0)
+ sysfatal("open: %r");
+ m = readmodel(fd);
+ if(m == nil)
+ sysfatal("readmodel: %r");
+ close(fd);
+
+ obj = emalloc(sizeof *obj);
+ memset(obj, 0, sizeof *obj);
+ loadobjmodel(obj, m);
+
+ if(dstdir == nil){
+ if(fprint(1, "%O", obj) == 0)
+ sysfatal("could not write obj model");
+ }else{
+ if(objexport(dstdir, obj) < 0)
+ sysfatal("exportmodel: %r");
+ }
+
+ exits(nil);
+}
--
⑨