shithub: libobj

Download patch

ref: 60002643548ee5fcde0f8b176dc1360da03adbb8
parent: 3ba5de66c96f3dc7ef35f2880161156434331c95
author: rodri <rgl@antares-labs.eu>
date: Thu Jun 19 14:21:43 EDT 2025

add objexport(2). stop spamming "usemtl" lines for every face in OBJfmt()

--- a/obj.c
+++ b/obj.c
@@ -152,7 +152,6 @@
 		char *ext;
 		char *prog;
 	} fmttab[] = {
-		"tga", "tga",
 		"png", "png",
 		"jpg", "jpg",
 		"jpeg", "jpg",
@@ -186,7 +185,90 @@
 	return t;
 }
 
+typedef struct Enco Enco;
+struct Enco
+{
+	int pfd[2];
+	int outfd;
+	char *prog;
+};
+
 static void
+encproc(void *arg)
+{
+	char buf[32];
+	Enco *d;
+
+	d = arg;
+
+	close(d->pfd[1]);
+	dup(d->pfd[0], 0);
+	close(d->pfd[0]);
+	dup(d->outfd, 1);
+	close(d->outfd);
+
+	snprint(buf, sizeof buf, "/bin/%s", d->prog);
+
+	execl(buf, d->prog, nil);
+	sysfatal("execl: %r");
+}
+
+static int
+genwriteimage(char *prog, char *path, Memimage *i)
+{
+	Enco d;
+	int rc;
+
+	d.prog = prog;
+
+	if(pipe(d.pfd) < 0)
+		sysfatal("pipe: %r");
+	d.outfd = create(path, OWRITE|OEXCL, 0644);
+	if(d.outfd < 0){
+		werrstr("create: %r");
+		return -1;
+	}
+	switch(fork()){
+	case -1:
+		sysfatal("fork: %r");
+	case 0:
+		encproc(&d);
+	default:
+		close(d.pfd[0]);
+		rc = writememimage(d.pfd[1], i);
+		close(d.pfd[1]);
+		close(d.outfd);
+	}
+
+	return rc;
+}
+
+static int
+writeimagefile(char *path, Memimage *i)
+{
+	char *ext;
+	static struct {
+		char *ext;
+		char *prog;
+	} fmttab[] = {
+		"tga", "totga",
+		"png", "topng",
+		"jpg", "tojpg",
+		"jpeg", "tojpg",
+	}, *fmt;
+
+	ext = strrchr(path, '.');
+	if(ext++ != nil){
+		for(fmt = &fmttab[0]; fmt < fmttab+nelem(fmttab); fmt++)
+			if(strcmp(ext, fmt->ext) == 0)
+				return genwriteimage(fmt->prog, path, i);
+		werrstr("file format not supported");
+	}else
+		werrstr("unknown format");
+	return -1;
+}
+
+static void
 addvertva(OBJVertexArray *va, OBJVertex v)
 {
 	va->verts = erealloc(va->verts, ++va->nvert*sizeof(OBJVertex));
@@ -991,6 +1073,70 @@
 }
 
 int
+objexport(char *path, OBJ *obj)
+{
+	Fmt f;
+	OBJMaterial *m;
+	char fmtbuf[8192], buf[256], *bufe, *pe;
+	va_list va;
+	int fd, i;
+
+	bufe = buf + sizeof buf;
+	pe = seprint(buf, bufe, "%s", path);
+
+	seprint(pe, bufe, "/main.obj");
+	fd = create(buf, OWRITE|OEXCL, 0644);
+	if(fd < 0){
+		werrstr("create: %r");
+		return -1;
+	}
+	fmtfdinit(&f, fd, fmtbuf, sizeof fmtbuf);
+	/* NOTE i don't like this either, but i don't want to mess with the fmt table */
+	va = (va_list)&obj;
+	f.args = va;
+	OBJfmt(&f);
+	fmtfdflush(&f);
+	close(fd);
+
+	if(obj->materials == nil)
+		return 0;
+
+	seprint(pe, bufe, "/%s", obj->materials->filename);
+	fd = create(buf, OWRITE|OEXCL, 0644);
+	if(fd < 0){
+		werrstr("create: %r");
+		return -1;
+	}
+	fmtfdinit(&f, fd, fmtbuf, sizeof fmtbuf);
+	va = (va_list)&obj->materials;
+	f.args = va;
+	OBJMaterlistfmt(&f);
+	fmtfdflush(&f);
+	close(fd);
+
+	for(i = 0; i < nelem(obj->materials->mattab); i++)
+		for(m = obj->materials->mattab[i]; m != nil; m = m->next){
+			if(m->map_Kd != nil){
+				seprint(pe, bufe, "/%s", m->map_Kd->filename);
+				if(writeimagefile(buf, m->map_Kd->image) < 0)
+					fprint(2, "writeimagefile: %r");
+			}
+			if(m->map_Ks != nil){
+				seprint(pe, bufe, "/%s", m->map_Ks->filename);
+				if(writeimagefile(buf, m->map_Ks->image) < 0)
+					fprint(2, "writeimagefile: %r");
+			}
+			if(m->norm != nil){
+				seprint(pe, bufe, "/%s", m->norm->filename);
+				if(writeimagefile(buf, m->norm->image) < 0)
+					fprint(2, "writeimagefile: %r");
+			}
+		}
+
+	return 0;
+}
+
+int
 OBJMaterlistfmt(Fmt *f)
 {
 	OBJMaterlist *ml;
@@ -1038,11 +1184,16 @@
 	OBJObject *o;
 	OBJElem *e;
 	OBJVertex v;
+	OBJMaterial *curmtl;
 	int i, j, k, n, pack, maxnindex;
 
 	n = pack = 0;
+	curmtl = nil;
 	obj = va_arg(f->args, OBJ*);
 
+	if(obj->materials != nil)
+		n += fmtprint(f, "mtllib %s\n", obj->materials->filename);
+
 	for(i = 0; i < nelem(obj->vertdata); i++)
 		for(j = 0; j < obj->vertdata[i].nvert; j++){
 			v = obj->vertdata[i].verts[j];
@@ -1062,9 +1213,6 @@
 			}
 		}
 
-	if(obj->materials != nil)
-		n += fmtprint(f, "mtllib %s\n", obj->materials->filename);
-
 	for(i = 0; i < nelem(obj->objtab); i++)
 		for(o = obj->objtab[i]; o != nil; o = o->next){
 			if(strcmp(o->name, "default") != 0)
@@ -1080,8 +1228,10 @@
 					n += fmtprint(f, "l");
 					break;
 				case OBJEFace:
-					if(e->mtl != nil)
+					if(e->mtl != nil && e->mtl != curmtl){
 						n += fmtprint(f, "usemtl %s\n", e->mtl->name);
+						curmtl = e->mtl;
+					}
 					n += fmtprint(f, "f");
 					break;
 				//case OBJECurve:
--- a/obj.h
+++ b/obj.h
@@ -58,8 +58,8 @@
 
 struct OBJTexture
 {
-	Memimage *image;
 	char *filename;
+	Memimage *image;
 };
 
 struct OBJVertexArray
@@ -138,6 +138,7 @@
 void objfree(OBJ*);
 OBJMaterlist *objmtlparse(char*);
 void objmtlfree(OBJMaterlist*);
+int objexport(char*, OBJ*);
 
 int OBJMaterlistfmt(Fmt*);
 int OBJfmt(Fmt*);
--