shithub: libgraphics

Download patch

ref: 2846ffebfc29bd997339f71f5ea6bd785e940097
parent: 5b04bd5b3a152d0b2d5a8d8e1e25887b71c338d5
author: rodri <rgl@antares-labs.eu>
date: Sun Mar 22 18:03:29 EDT 2026

turn the Model.materials into an ItemArray. rewrite dup and copy routines

now ItemArray and Model are refcounted. duplicating them creates
another reference, copying them creates another instance.

now we can instantiate models and change specific attributes
for better reuse of resources.

also got rid of useless dup scenes for lights, entities and scenes.

--- a/graphics.h
+++ b/graphics.h
@@ -87,6 +87,7 @@
 
 struct ItemArray
 {
+	Ref;
 	void	*items;
 	usize	nitems;
 	usize	itemsize;
@@ -210,6 +211,7 @@
 
 struct Model
 {
+	Ref;
 	char		*name;
 	ItemArray	*positions;
 	ItemArray	*normals;
@@ -218,8 +220,7 @@
 	ItemArray	*tangents;
 	ItemArray	*verts;
 	ItemArray	*prims;
-	Material	*materials;
-	ulong		nmaterials;
+	ItemArray	*materials;
 
 	usize		(*addposition)(Model*, Point3);
 	usize		(*addnormal)(Model*, Point3);
@@ -228,7 +229,7 @@
 	usize		(*addtangent)(Model*, Point3);
 	usize		(*addvert)(Model*, Vertex);
 	usize		(*addprim)(Model*, Primitive);
-	int		(*addmaterial)(Model*, Material);
+	usize		(*addmaterial)(Model*, Material);
 	Material*	(*getmaterial)(Model*, char*);
 };
 
@@ -472,15 +473,12 @@
 LightSource*	newpointlight(Point3, Color);
 LightSource*	newdireclight(Point3, Point3, Color);
 LightSource*	newspotlight(Point3, Point3, Color, double, double);
-LightSource*	duplight(LightSource*);
 void		dellight(LightSource*);
 Entity*		newentity(char*, Model*);
-Entity*		dupentity(Entity*);
 void		delentity(Entity*);
 Scene*		newscene(char*);
-Scene*		dupscene(Scene*);
-void		delscene(Scene*);
 void		clearscene(Scene*);
+void		delscene(Scene*);
 
 /* model */
 Vertex		mkvert(void);
@@ -488,7 +486,8 @@
 Material*	newmaterial(char*);
 void		delmaterial(Material*);
 Model*		newmodel(void);
-Model*		dupmodel(Model*);
+void		copymodel(Model*, Model*);
+Model*		dupmodel(Model*, Model**);
 void		delmodel(Model*);
 void		compactmodel(Model*);
 
@@ -523,7 +522,7 @@
 usize		itemarrayadd(ItemArray*, void*);
 void*		itemarrayget(ItemArray*, usize);
 usize		copyitemarray(ItemArray*, ItemArray*);
-ItemArray*	dupitemarray(ItemArray*);
+ItemArray*	dupitemarray(ItemArray*, ItemArray**);
 void		rmitemarray(ItemArray*);
 
 /* color */
--- a/itemarray.c
+++ b/itemarray.c
@@ -16,6 +16,7 @@
 	a = _emalloc(sizeof *a);
 	memset(a, 0, sizeof *a);
 	a->itemsize = is;
+	incref(a);
 	return a;
 }
 
@@ -48,33 +49,39 @@
 }
 
 usize
-copyitemarray(ItemArray *d, ItemArray *s)
+copyitemarray(ItemArray *s, ItemArray *d)
 {
 	usize len;
 
-	assert(d->itemsize == s->itemsize);
 	len = s->nitems * s->itemsize;
-
 	free(d->items);
 	d->items = _emalloc(len);
 	d->nitems = s->nitems;
+	d->itemsize = s->itemsize;
 	memmove(d->items, s->items, len);
 	return d->nitems;
 }
 
+/*
+ * deferring rmitemarray() makes this operation idempotent.
+ */
 ItemArray *
-dupitemarray(ItemArray *a)
+dupitemarray(ItemArray *s, ItemArray **d)
 {
-	ItemArray *na;
+	ItemArray *t;
 
-	na = mkitemarray(a->itemsize);
-	copyitemarray(na, a);
-	return na;
+	t = *d;
+	*d = s;
+	incref(*d);
+	rmitemarray(t);
+	return *d;
 }
 
 void
 rmitemarray(ItemArray *a)
 {
-	free(a->items);
-	free(a);
+	if(decref(a) == 0){
+		free(a->items);
+		free(a);
+	}
 }
--- a/marshal.c
+++ b/marshal.c
@@ -24,15 +24,14 @@
 
 struct Mtlentry
 {
-	Material;
-	ulong idx;
-	Mtlentry *next;
+	char		*name;
+	ulong		idx;
+	Mtlentry	*next;
 };
 
 struct Mtltab
 {
-	Mtlentry *mtls[MTLHTSIZ];
-	int loaded;			/* was the table loaded into a model already? */
+	Mtlentry	*mtls[MTLHTSIZ];
 };
 
 static void
@@ -71,18 +70,8 @@
 	return t;
 }
 
-static void
-freemtlentry(Mtlentry *m)
-{
-	freetexture(m->normalmap);
-	freetexture(m->specularmap);
-	freetexture(m->diffusemap);
-	free(m->name);
-	free(m);
-}
-
 static Mtlentry *
-mtltabadd(Mtltab *t, Material *m)
+mtltabadd(Mtltab *t, Material *m, ulong idx)
 {
 	Mtlentry *nm, *mp, *prev;
 	uint h;
@@ -89,7 +78,8 @@
 
 	nm = _emalloc(sizeof *nm);
 	memset(nm, 0, sizeof *nm);
-	nm->Material = *m;
+	nm->name = m->name;
+	nm->idx = idx;
 	nm->next = nil;
 
 	prev = nil;
@@ -121,18 +111,6 @@
 }
 
 static void
-mtltabloadmodel(Model *m, Mtltab *t)
-{
-	Mtlentry *e;
-	int i;
-
-	for(i = 0; i < nelem(t->mtls); i++)
-		for(e = t->mtls[i]; e != nil; e = e->next)
-			e->idx = m->addmaterial(m, *e);
-	t->loaded++;
-}
-
-static void
 rmmtltab(Mtltab *t)
 {
 	Mtlentry *m, *nm;
@@ -141,10 +119,7 @@
 	for(i = 0; i < nelem(t->mtls); i++)
 		for(m = t->mtls[i]; m != nil; m = nm){
 			nm = m->next;
-			if(t->loaded)
-				free(m);
-			else
-				freemtlentry(m);
+			free(m);
 		}
 }
 
@@ -152,7 +127,7 @@
 readmodel(int fd)
 {
 	Curline curline;
-	ItemArray *pa, *na, *ta, *ca, *Ta, *va, *Pa;
+	ItemArray *pa, *na, *ta, *ca, *Ta, *va, *Pa, *ma;
 	Mtltab *mtltab;
 	Mtlentry *me;
 	Point3 p, n, T;
@@ -159,7 +134,7 @@
 	Point2 t;
 	Color c;
 	Vertex v;
-	Primitive P, *prim;
+	Primitive P;
 	Material mtl;
 	Model *m;
 	Memimage *mi;
@@ -166,7 +141,7 @@
 	Biobuf *bin;
 	void *vp;
 	char *line, *f[10], *s, assets[200], buf[256];
-	usize idx, i;
+	usize idx;
 	int nf, nv, inamaterial, texfd;
 
 	n.w = T.w = 0;
@@ -184,6 +159,7 @@
 	Ta = mkitemarray(sizeof(T));
 	va = mkitemarray(sizeof(v));
 	Pa = mkitemarray(sizeof(P));
+	ma = mkitemarray(sizeof(mtl));
 	mtltab = mkmtltab();
 
 	memset(&curline, 0, sizeof curline);
@@ -212,7 +188,8 @@
 				*s = 0;
 
 			if(strcmp(f[0], "}") == 0){
-				if(mtltabadd(mtltab, &mtl) == nil){
+				idx = itemarrayadd(ma, &mtl);
+				if(mtltabadd(mtltab, &mtl, idx) == nil){
 					error(&curline, "mtltabadd: %r");
 					goto getout;
 				}
@@ -446,11 +423,12 @@
 				/* ignore 4th field (nf == 4) */
 
 				if(nf == 5){
-					P.mtl = mtltabget(mtltab, f[4]);
-					if(P.mtl == nil){
+					me = mtltabget(mtltab, f[4]);
+					if(me == nil){
 						error(&curline, "material '%s' not found", f[4]);
 						goto getout;
 					}
+					P.mtl = itemarrayget(ma, me->idx);
 				}
 				break;
 			case PLine:
@@ -476,11 +454,12 @@
 				/* ignore 5th field (nf == 5) */
 
 				if(nf == 6){
-					P.mtl = mtltabget(mtltab, f[5]);
-					if(P.mtl == nil){
+					me = mtltabget(mtltab, f[5]);
+					if(me == nil){
 						error(&curline, "material '%s' not found", f[5]);
 						goto getout;
 					}
+					P.mtl = itemarrayget(ma, me->idx);
 				}
 				break;
 			case PTriangle:
@@ -523,11 +502,12 @@
 				}
 
 				if(nf == 7){
-					P.mtl = mtltabget(mtltab, f[6]);
-					if(P.mtl == nil){
+					me = mtltabget(mtltab, f[6]);
+					if(me == nil){
 						error(&curline, "material '%s' not found", f[6]);
 						goto getout;
 					}
+					P.mtl = itemarrayget(ma, me->idx);
 				}
 				break;
 			default:
@@ -557,21 +537,14 @@
 	}
 
 	m = newmodel();
-	mtltabloadmodel(m, mtltab);
-	copyitemarray(m->positions, pa);
-	copyitemarray(m->normals, na);
-	copyitemarray(m->texcoords, ta);
-	copyitemarray(m->colors, ca);
-	copyitemarray(m->tangents, Ta);
-	copyitemarray(m->verts, va);
-	copyitemarray(m->prims, Pa);
-	for(i = 0; i < Pa->nitems; i++){
-		prim = itemarrayget(m->prims, i);
-		if(prim->mtl != nil){
-			me = mtltabget(mtltab, prim->mtl->name);
-			prim->mtl = &m->materials[me->idx];
-		}
-	}
+	dupitemarray(pa, &m->positions);
+	dupitemarray(na, &m->normals);
+	dupitemarray(ta, &m->texcoords);
+	dupitemarray(ca, &m->colors);
+	dupitemarray(Ta, &m->tangents);
+	dupitemarray(va, &m->verts);
+	dupitemarray(Pa, &m->prims);
+	dupitemarray(ma, &m->materials);
 
 getout:
 	rmitemarray(pa);
@@ -581,6 +554,7 @@
 	rmitemarray(Ta);
 	rmitemarray(va);
 	rmitemarray(Pa);
+	rmitemarray(ma);
 	rmmtltab(mtltab);
 	Bterm(bin);
 	return m;
@@ -769,8 +743,8 @@
 		sysfatal("Bfdopen: %r");
 
 	n = 0;
-	for(i = 0; i < m->nmaterials; i++)
-		n += Bprintmtl(out, &m->materials[i]);
+	for(i = 0; i < m->materials->nitems; i++)
+		n += Bprintmtl(out, itemarrayget(m->materials, i));
 
 	for(i = 0; i < m->positions->nitems; i++)
 		n += Bprintp(out, itemarrayget(m->positions, i));
@@ -814,16 +788,15 @@
 exportmodel(char *path, Model *m)
 {
 	static char Esmallbuf[] = "buf too small to hold path";
-	Material *mtl;
+	Material *mtl, *lastmtl;
 	char buf[256], *pe, *me;
 	int fd, idx;
 
-	idx = 0;
-
 	if((pe = seprint(buf, buf + sizeof buf, "%s", path)) == nil)
 		sysfatal(Esmallbuf);
 
-	for(mtl = m->materials; mtl < m->materials+m->nmaterials; mtl++, idx++){
+	mtl = m->materials->items;
+	for(idx = 0, lastmtl = mtl + m->materials->nitems; mtl < lastmtl; mtl++, idx++){
 		if(mtl->name == nil){
 			fprint(2, "warning: material #%d has no name. skipping...\n", idx);
 			continue;
@@ -881,7 +854,8 @@
 	}
 	close(fd);
 
-	for(mtl = m->materials; mtl < m->materials+m->nmaterials; mtl++){
+	mtl = m->materials->items;
+	for(lastmtl = mtl + m->materials->nitems; mtl < lastmtl; mtl++){
 		if(mtl->name == nil)
 			continue;
 
--- a/model.c
+++ b/model.c
@@ -30,7 +30,7 @@
 {
 	Material *mtl;
 
-	if(name == nil){
+	if(name == nil || name[0] == '\0'){
 		werrstr("needs a name");
 		return nil;
 	}
@@ -51,6 +51,7 @@
 	freetexture(mtl->specularmap);
 	freetexture(mtl->normalmap);
 	free(mtl->name);
+	free(mtl);
 }
 
 static usize
@@ -95,20 +96,23 @@
 	return itemarrayadd(m->prims, &P);
 }
 
-static int
+static usize
 model_addmaterial(Model *m, Material mtl)
 {
-	m->materials = _erealloc(m->materials, ++m->nmaterials*sizeof(*m->materials));
-	m->materials[m->nmaterials-1] = mtl;
-	return m->nmaterials-1;
+	assert(mtl.name != nil);
+	return itemarrayadd(m->materials, &mtl);
 }
 
 static Material *
 model_getmaterial(Model *m, char *name)
 {
-	Material *mtl;
+	Material *mtl, *last;
 
-	for(mtl = m->materials; mtl < m->materials+m->nmaterials; mtl++)
+	if(name == nil)
+		return nil;
+
+	mtl = m->materials->items;
+	for(last = mtl + m->materials->nitems; mtl < last; mtl++)
 		if(strcmp(mtl->name, name) == 0)
 			return mtl;
 	return nil;
@@ -128,6 +132,7 @@
 	m->tangents = mkitemarray(sizeof(Point3));
 	m->verts = mkitemarray(sizeof(Vertex));
 	m->prims = mkitemarray(sizeof(Primitive));
+	m->materials = mkitemarray(sizeof(Material));
 	m->addposition = model_addposition;
 	m->addnormal = model_addnormal;
 	m->addtexcoord = model_addtexcoord;
@@ -137,46 +142,36 @@
 	m->addprim = model_addprim;
 	m->addmaterial = model_addmaterial;
 	m->getmaterial = model_getmaterial;
+	incref(m);
 	return m;
 }
 
+void
+copymodel(Model *s, Model *d)
+{
+	if(d->name)
+		free(d->name);
+	d->name = s->name? _estrdup(s->name): nil;
+	dupitemarray(s->positions, &d->positions);
+	dupitemarray(s->normals, &d->normals);
+	dupitemarray(s->texcoords, &d->texcoords);
+	dupitemarray(s->colors, &d->colors);
+	dupitemarray(s->tangents, &d->tangents);
+	dupitemarray(s->verts, &d->verts);
+	dupitemarray(s->prims, &d->prims);
+	dupitemarray(s->materials, &d->materials);
+}
+
 Model *
-dupmodel(Model *m)
+dupmodel(Model *s, Model **d)
 {
-	Model *nm;
-	Primitive *prim, *nprim;
-	int i;
+	Model *t;
 
-	if(m == nil)
-		return nil;
-
-	nm = newmodel();
-	if(m->nmaterials > 0){
-		nm->nmaterials = m->nmaterials;
-		nm->materials = _emalloc(nm->nmaterials*sizeof(*nm->materials));
-		for(i = 0; i < m->nmaterials; i++){
-			nm->materials[i] = m->materials[i];
-			nm->materials[i].diffusemap = duptexture(m->materials[i].diffusemap);
-			nm->materials[i].specularmap = duptexture(m->materials[i].specularmap);
-			nm->materials[i].normalmap = duptexture(m->materials[i].normalmap);
-			nm->materials[i].name = _estrdup(m->materials[i].name);
-		}
-	}
-	nm->positions = dupitemarray(m->positions);
-	nm->normals = dupitemarray(m->normals);
-	nm->texcoords = dupitemarray(m->texcoords);
-	nm->colors = dupitemarray(m->colors);
-	nm->tangents = dupitemarray(m->tangents);
-	nm->verts = dupitemarray(m->verts);
-	nm->prims = dupitemarray(m->prims);
-	for(i = 0; i < m->prims->nitems && nm->nmaterials > 0; i++){
-		prim = itemarrayget(m->prims, i);
-		if(prim->mtl != nil){
-			nprim = itemarrayget(nm->prims, i);
-			nprim->mtl = &nm->materials[prim->mtl - m->materials];
-		}
-	}
-	return nm;
+	t = *d;
+	*d = s;
+	incref(*d);
+	delmodel(t);
+	return *d;
 }
 
 void
@@ -185,17 +180,18 @@
 	if(m == nil)
 		return;
 
-	while(m->nmaterials--)
-		delmaterial(&m->materials[m->nmaterials]);
-	free(m->materials);
-	rmitemarray(m->positions);
-	rmitemarray(m->normals);
-	rmitemarray(m->texcoords);
-	rmitemarray(m->colors);
-	rmitemarray(m->tangents);
-	rmitemarray(m->verts);
-	rmitemarray(m->prims);
-	free(m);
+	if(decref(m) == 0){
+		rmitemarray(m->positions);
+		rmitemarray(m->normals);
+		rmitemarray(m->texcoords);
+		rmitemarray(m->colors);
+		rmitemarray(m->tangents);
+		rmitemarray(m->verts);
+		rmitemarray(m->prims);
+		rmitemarray(m->materials);
+		free(m->name);
+		free(m);
+	}
 }
 
 /*
--- a/scene.c
+++ b/scene.c
@@ -42,18 +42,6 @@
 	return newlight(LightSpot, p, dir, c, 1000, θu, θp);
 }
 
-LightSource *
-duplight(LightSource *l)
-{
-	LightSource *nl;
-
-	nl = _emalloc(sizeof *nl);
-	memset(nl, 0, sizeof *nl);
-	*nl = *l;
-	nl->prev = nl->next = nil;
-	return nl;
-}
-
 void
 dellight(LightSource *l)
 {
@@ -76,23 +64,6 @@
 	return e;
 }
 
-Entity *
-dupentity(Entity *e)
-{
-	Entity *ne;
-
-	if(e == nil)
-		return nil;
-
-	ne = newentity(nil, nil);
-	*ne = *e;
-	if(e->name != nil)
-		ne->name = _estrdup(e->name);
-	ne->mdl = dupmodel(e->mdl);
-	ne->prev = ne->next = nil;
-	return ne;
-}
-
 void
 delentity(Entity *e)
 {
@@ -161,25 +132,6 @@
 	s->getent = scene_getent;
 	s->addlight = scene_addlight;
 	return s;
-}
-
-Scene *
-dupscene(Scene *s)
-{
-	Scene *ns;
-	Entity *e;
-	LightSource *l;
-
-	if(s == nil)
-		return nil;
-
-	ns = newscene(s->name);
-	for(e = s->ents.next; e != &s->ents; e = e->next)
-		ns->addent(ns, dupentity(e));
-	for(l = s->lights.next; l != &s->lights; l = l->next)
-		ns->addlight(ns, duplight(l));
-	ns->skybox = dupcubemap(s->skybox);
-	return ns;
 }
 
 void
--