shithub: libgraphics

Download patch

ref: 0efd71b780d84ace7dd3d5a66e6152014b7a541b
parent: d4991f0af282bbc996474af2caf59b8fd7dc35f3
author: rodri <rgl@antares-labs.eu>
date: Sat Jul 5 06:26:40 EDT 2025

model: a better compaction algorithm

--- a/model.c
+++ b/model.c
@@ -198,192 +198,188 @@
 	free(m);
 }
 
-void
-compactmodel(Model *m)
+/*
+ * sequential reindexing table
+ *
+ * the tables are processed in order (hence the sequence) for every
+ * vertex attribute.  if the attribute equals the old index, it's
+ * replaced by the new one; if it's bigger, it's decreased by one.
+ * otherwise it stays the same.
+ */
+typedef struct Reidx Reidx;
+typedef struct Reidxtab Reidxtab;
+
+struct Reidx
 {
-	ItemArray *a1, *a2;
-	Point3 *p1, *p2, *pb, *pe;
-	Point2 *t1, *t2, *tb, *te;
-	Vertex *v, *v1, *v2, *vb, *ve;
-	Primitive *P, *P1, *P2, *Pb, *Pe;
-	void *vp;
-	usize len0, i, j, k;
+	usize old;
+	usize new;
+};
 
-	a1 = m->positions;
-	pb = a1->items;
-	pe = pb + a1->nitems;
-	len0 = a1->nitems;
-	a2 = m->verts;
-	vb = a2->items;
-	ve = vb + a2->nitems;
+struct Reidxtab
+{
+	Reidx *tab;
+	usize len;
+	usize cap;
+};
 
-	for(p1 = pb, i = 0; p1 < pe; p1++, i++)
-	for(p2 = p1+1, j = i+1; p2 < pe; p2++, j++)
-		if(memcmp(p1, p2, sizeof(Point3)) == 0){
-			if(p2 < --pe)
-				memmove(p2, p2+1, (pe - p2)*sizeof(Point3));
-			/* reindex verts */
-			for(v = vb; v < ve; v++)
-				if(v->p == j)
-					v->p = i;
-				else if(v->p > j)
-					v->p--;
-		}
-	a1->nitems = pe - pb;
-	if(a1->nitems != len0){
-		vp = realloc(a1->items, a1->nitems * a1->itemsize);
-		if(vp == nil)
-			fprint(2, "not enough memory to shrink positions array.\n");
-		else
-			a1->items = vp;
+static void
+reidxtabadd(Reidxtab *t, Reidx r)
+{
+	if(t->len == t->cap){
+		t->cap += 8;
+		t->tab = _erealloc(t->tab, t->cap * sizeof(Reidx));
 	}
+	t->tab[t->len++] = r;
+}
 
-	a1 = m->normals;
-	pb = a1->items;
-	pe = pb + a1->nitems;
-	len0 = a1->nitems;
+static void
+freereidxtab(Reidxtab *t)
+{
+	free(t->tab);
+	memset(t, 0, sizeof(*t));
+}
 
-	for(p1 = pb, i = 0; p1 < pe; p1++, i++)
-	for(p2 = p1+1, j = i+1; p2 < pe; p2++, j++)
-		if(memcmp(p1, p2, sizeof(Point3)) == 0){
-			if(p2 < --pe)
-				memmove(p2, p2+1, (pe - p2)*sizeof(Point3));
-			/* reindex verts */
-			for(v = vb; v < ve; v++)
-				if(v->n == j)
-					v->n = i;
-				else if(v->n > j)
-					v->n--;
-		}
-	a1->nitems = pe - pb;
-	if(a1->nitems != len0){
-		vp = realloc(a1->items, a1->nitems * a1->itemsize);
-		if(vp == nil)
-			fprint(2, "not enough memory to shrink normals array.\n");
-		else
-			a1->items = vp;
-	}
+static void
+reindexverts(ItemArray *verts, Reidxtab *t, int aoff)
+{
+	Reidx *reidx;
+	Vertex *v, *vb, *ve;
+	usize *attr;
 
-	a1 = m->texcoords;
-	tb = a1->items;
-	te = tb + a1->nitems;
-	len0 = a1->nitems;
+	if(t->len == 0)
+		return;
 
-	for(t1 = tb, i = 0; t1 < te; t1++, i++)
-	for(t2 = t1+1, j = i+1; t2 < te; t2++, j++)
-		if(memcmp(t1, t2, sizeof(Point2)) == 0){
-			if(t2 < --te)
-				memmove(t2, t2+1, (te - t2)*sizeof(Point2));
-			/* reindex verts */
-			for(v = vb; v < ve; v++)
-				if(v->uv == j)
-					v->uv = i;
-				else if(v->uv > j)
-					v->uv--;
-		}
-	a1->nitems = te - tb;
-	if(a1->nitems != len0){
-		vp = realloc(a1->items, a1->nitems * a1->itemsize);
-		if(vp == nil)
-			fprint(2, "not enough memory to shrink texcoords array.\n");
-		else
-			a1->items = vp;
+	vb = verts->items;
+	ve = vb + verts->nitems;
+
+	for(v = vb; v < ve; v++){
+		attr = (usize*)((char*)v + aoff);
+		for(reidx = t->tab; reidx < t->tab+t->len; reidx++)
+			if(*attr == reidx->old)
+				*attr = reidx->new;
+			else if(*attr > reidx->old)
+				(*attr)--;
 	}
+}
 
-	a1 = m->colors;
-	pb = a1->items;
-	pe = pb + a1->nitems;
-	len0 = a1->nitems;
+static void
+reindexprimtans(ItemArray *prims, Reidxtab *t)
+{
+	Reidx *reidx;
+	Primitive *P, *Pb, *Pe;
 
-	for(p1 = pb, i = 0; p1 < pe; p1++, i++)
-	for(p2 = p1+1, j = i+1; p2 < pe; p2++, j++)
-		if(memcmp(p1, p2, sizeof(Point3)) == 0){
-			if(p2 < --pe)
-				memmove(p2, p2+1, (pe - p2)*sizeof(Point3));
-			/* reindex verts */
-			for(v = vb; v < ve; v++)
-				if(v->c == j)
-					v->c = i;
-				else if(v->c > j)
-					v->c--;
-		}
-	a1->nitems = pe - pb;
-	if(a1->nitems != len0){
-		vp = realloc(a1->items, a1->nitems * a1->itemsize);
-		if(vp == nil)
-			fprint(2, "not enough memory to shrink colors array.\n");
-		else
-			a1->items = vp;
-	}
+	if(t->len == 0)
+		return;
 
-	a1 = m->tangents;
-	pb = a1->items;
-	pe = pb + a1->nitems;
-	len0 = a1->nitems;
-	a2 = m->prims;
-	Pb = a2->items;
-	Pe = Pb + a2->nitems;
+	Pb = prims->items;
+	Pe = Pb + prims->nitems;
 
-	for(p1 = pb, i = 0; p1 < pe; p1++, i++)
-	for(p2 = p1+1, j = i+1; p2 < pe; p2++, j++)
-		if(memcmp(p1, p2, sizeof(Point3)) == 0){
-			if(p2 < --pe)
-				memmove(p2, p2+1, (pe - p2)*sizeof(Point3));
-			/* reindex prims */
-			for(P = Pb; P < Pe; P++)
-				if(P->tangent == j)
-					P->tangent = i;
-				else if(P->tangent > j)
-					P->tangent--;
-		}
-	a1->nitems = pe - pb;
-	if(a1->nitems != len0){
-		vp = realloc(a1->items, a1->nitems * a1->itemsize);
-		if(vp == nil)
-			fprint(2, "not enough memory to shrink tangents array.\n");
-		else
-			a1->items = vp;
-	}
+	for(P = Pb; P < Pe; P++)
+	for(reidx = t->tab; reidx < t->tab+t->len; reidx++)
+		if(P->tangent == reidx->old)
+			P->tangent = reidx->new;
+		else if(P->tangent > reidx->old)
+			P->tangent--;
+}
 
-	a1 = m->verts;
-	vb = a1->items;
-	ve = vb + a1->nitems;
-	len0 = a1->nitems;
+static void
+reindexprimverts(ItemArray *prims, Reidxtab *t)
+{
+	Reidx *reidx;
+	Primitive *P, *Pb, *Pe;
+	usize i;
 
-	for(v1 = vb, i = 0; v1 < ve; v1++, i++)
-	for(v2 = v1+1, j = i+1; v2 < ve; v2++, j++)
-		if(memcmp(v1, v2, sizeof(Vertex)) == 0){
-			if(v2 < --ve)
-				memmove(v2, v2+1, (ve - v2)*sizeof(Vertex));
-			/* reindex prims */
-			for(P = Pb; P < Pe; P++)
-				for(k = 0; k < P->type+1; k++)
-					if(P->v[k] == j)
-						P->v[k] = i;
-					else if(P->v[k] > j)
-						P->v[k]--;
-		}
-	a1->nitems = ve - vb;
-	if(a1->nitems != len0){
-		vp = realloc(a1->items, a1->nitems * a1->itemsize);
-		if(vp == nil)
-			fprint(2, "not enough memory to shrink vertex array.\n");
-		else
-			a1->items = vp;
+	if(t->len == 0)
+		return;
+
+	Pb = prims->items;
+	Pe = Pb + prims->nitems;
+
+	for(P = Pb; P < Pe; P++)
+	for(reidx = t->tab; reidx < t->tab+t->len; reidx++)
+		for(i = 0; i < P->type+1; i++)
+			if(P->v[i] == reidx->old)
+				P->v[i] = reidx->new;
+			else if(P->v[i] > reidx->old)
+				P->v[i]--;
+}
+
+static void
+dedupitemarray(ItemArray *a, Reidxtab *t)
+{
+	char *p1, *p2, *pb, *pe;
+	void *vp;
+	usize nitems0, i, j;
+
+	pb = a->items;
+	pe = pb + a->nitems*a->itemsize;
+
+	if(t != nil){
+		for(p1 = pb, i = 0; p1 < pe; p1 += a->itemsize, i++)
+		for(p2 = p1+a->itemsize, j = i+1; p2 < pe; p2 += a->itemsize, j++)
+			if(memcmp(p1, p2, a->itemsize) == 0){
+				reidxtabadd(t, (Reidx){j, i});
+
+				pe -= a->itemsize;
+				if(p2 < pe){
+					memmove(p2, p2+a->itemsize, pe - p2);
+					p2 -= a->itemsize;
+					j--;
+				}
+			}
+	}else{
+		for(p1 = pb; p1 < pe; p1 += a->itemsize)
+		for(p2 = p1+a->itemsize; p2 < pe; p2 += a->itemsize)
+			if(memcmp(p1, p2, a->itemsize) == 0){
+				pe -= a->itemsize;
+				if(p2 < pe){
+					memmove(p2, p2+a->itemsize, pe - p2);
+					p2 -= a->itemsize;
+				}
+			}
 	}
 
-	len0 = a2->nitems;
-	for(P1 = Pb, i = 0; P1 < Pe; P1++, i++)
-	for(P2 = P1+1, j = i+1; P2 < Pe; P2++, j++)
-		if(memcmp(P1, P2, sizeof(Primitive)) == 0)
-			if(P2 < --Pe)
-				memmove(P2, P2+1, (Pe - P2)*sizeof(Primitive));
-	a2->nitems = Pe - Pb;
-	if(a2->nitems != len0){
-		vp = realloc(a2->items, a2->nitems * a2->itemsize);
-		if(vp == nil)
-			fprint(2, "not enough memory to shrink vertex array.\n");
-		else
-			a2->items = vp;
+	nitems0 = a->nitems;
+	a->nitems = (pe - pb)/a->itemsize;
+	if(a->nitems != nitems0){
+		/* try to shrink it */
+		vp = realloc(a->items, a->nitems * a->itemsize);
+		if(vp != nil)
+			a->items = vp;
 	}
+}
+
+void
+compactmodel(Model *m)
+{
+	Reidxtab itab;
+
+	memset(&itab, 0, sizeof(itab));
+
+	dedupitemarray(m->positions, &itab);
+	reindexverts(m->verts, &itab, offsetof(Vertex, p));
+	itab.len = 0;
+
+	dedupitemarray(m->normals, &itab);
+	reindexverts(m->verts, &itab, offsetof(Vertex, n));
+	itab.len = 0;
+
+	dedupitemarray(m->texcoords, &itab);
+	reindexverts(m->verts, &itab, offsetof(Vertex, uv));
+	itab.len = 0;
+
+	dedupitemarray(m->colors, &itab);
+	reindexverts(m->verts, &itab, offsetof(Vertex, c));
+	itab.len = 0;
+
+	dedupitemarray(m->tangents, &itab);
+	reindexprimtans(m->prims, &itab);
+	itab.len = 0;
+
+	dedupitemarray(m->verts, &itab);
+	reindexprimverts(m->prims, &itab);
+
+	dedupitemarray(m->prims, nil);
+
+	freereidxtab(&itab);
 }
--