aboutsummaryrefslogtreecommitdiff
path: root/scene.c
diff options
context:
space:
mode:
Diffstat (limited to 'scene.c')
-rw-r--r--scene.c317
1 files changed, 273 insertions, 44 deletions
diff --git a/scene.c b/scene.c
index 4427835..15d3cc0 100644
--- a/scene.c
+++ b/scene.c
@@ -20,16 +20,16 @@ triangulate(OBJElem **newe, OBJElem *e)
{
OBJIndexArray *newidxtab;
OBJIndexArray *idxtab;
- int i, nt;
+ int i;
- nt = 0;
idxtab = &e->indextab[OBJVGeometric];
for(i = 0; i < idxtab->nindex-2; i++){
idxtab = &e->indextab[OBJVGeometric];
- newe[nt++] = emalloc(sizeof **newe);
- newe[nt-1]->type = OBJEFace;
- newe[nt-1]->mtl = e->mtl;
- newidxtab = &newe[nt-1]->indextab[OBJVGeometric];
+ newe[i] = emalloc(sizeof **newe);
+ memset(newe[i], 0, sizeof **newe);
+ newe[i]->type = OBJEFace;
+ newe[i]->mtl = e->mtl;
+ newidxtab = &newe[i]->indextab[OBJVGeometric];
newidxtab->nindex = 3;
newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices));
newidxtab->indices[0] = idxtab->indices[0];
@@ -37,7 +37,7 @@ triangulate(OBJElem **newe, OBJElem *e)
newidxtab->indices[2] = idxtab->indices[i+2];
idxtab = &e->indextab[OBJVTexture];
if(idxtab->nindex > 0){
- newidxtab = &newe[nt-1]->indextab[OBJVTexture];
+ newidxtab = &newe[i]->indextab[OBJVTexture];
newidxtab->nindex = 3;
newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices));
newidxtab->indices[0] = idxtab->indices[0];
@@ -46,7 +46,7 @@ triangulate(OBJElem **newe, OBJElem *e)
}
idxtab = &e->indextab[OBJVNormal];
if(idxtab->nindex > 0){
- newidxtab = &newe[nt-1]->indextab[OBJVNormal];
+ newidxtab = &newe[i]->indextab[OBJVNormal];
newidxtab->nindex = 3;
newidxtab->indices = emalloc(newidxtab->nindex*sizeof(*newidxtab->indices));
newidxtab->indices[0] = idxtab->indices[0];
@@ -55,53 +55,241 @@ triangulate(OBJElem **newe, OBJElem *e)
}
}
- return nt;
+ return i;
+}
+
+typedef struct OBJ2MtlEntry OBJ2MtlEntry;
+typedef struct OBJ2MtlMap OBJ2MtlMap;
+
+struct OBJ2MtlEntry
+{
+ OBJMaterial *objmtl;
+ ulong idx;
+ OBJ2MtlEntry *next;
+};
+
+struct OBJ2MtlMap
+{
+ OBJ2MtlEntry *head;
+ Material *mtls;
+};
+
+static void
+addmtlmap(OBJ2MtlMap *map, OBJMaterial *om, ulong idx)
+{
+ OBJ2MtlEntry *e;
+
+ if(om == nil)
+ return;
+
+ e = emalloc(sizeof *e);
+ memset(e, 0, sizeof *e);
+ e->objmtl = om;
+ e->idx = idx;
+
+ if(map->head == nil){
+ map->head = e;
+ return;
+ }
+
+ e->next = map->head;
+ map->head = e;
+}
+
+static Material *
+getmtlmap(OBJ2MtlMap *map, OBJMaterial *om)
+{
+ OBJ2MtlEntry *e;
+
+ for(e = map->head; e != nil; e = e->next)
+ if(e->objmtl == om)
+ return &map->mtls[e->idx];
+ return nil;
+}
+
+static void
+clrmtlmap(OBJ2MtlMap *map)
+{
+ OBJ2MtlEntry *e, *ne;
+
+ for(e = map->head; e != nil; e = ne){
+ ne = e->next;
+ free(e);
+ }
}
-/*
- * rebuild the cache of renderable OBJElems.
- *
- * run it every time the Model's geometry changes.
- */
int
-refreshmodel(Model *m)
+loadobjmodel(Model *m, OBJ *obj)
{
- OBJElem **trielems;
+ Primitive *p;
+ OBJVertex *pverts, *tverts, *nverts, *v; /* geometric, texture and normals vertices */
+ OBJElem **trielems, *e, *ne;
OBJObject *o;
- OBJElem *e;
OBJIndexArray *idxtab;
- int i, nt;
+ OBJ2MtlMap mtlmap;
+ OBJMaterial *objmtl;
+ Material *mtl;
+ Point3 n; /* surface normal */
+ int i, idx, nt, maxnt, neednormal, gottaclean;
- if(m->obj == nil)
+ if(obj == nil)
return 0;
- if(m->elems != nil){
- free(m->elems);
- m->elems = nil;
+ pverts = obj->vertdata[OBJVGeometric].verts;
+ tverts = obj->vertdata[OBJVTexture].verts;
+ nverts = obj->vertdata[OBJVNormal].verts;
+ trielems = nil;
+ maxnt = 0;
+
+ if(m->prims != nil){
+ free(m->prims);
+ m->prims = nil;
}
- m->nelems = 0;
- for(i = 0; i < nelem(m->obj->objtab); i++)
- for(o = m->obj->objtab[i]; o != nil; o = o->next)
- for(e = o->child; e != nil; e = e->next){
- idxtab = &e->indextab[OBJVGeometric];
- /* discard non-surfaces */
- if(e->type != OBJEFace || idxtab->nindex < 3)
- continue;
- if(idxtab->nindex > 3){
+ 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){
+ mtlmap.mtls = m->materials = erealloc(m->materials, ++m->nmaterials*sizeof(*m->materials));
+ 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->ambient.r = objmtl->Ka.r;
+ mtl->ambient.g = objmtl->Ka.g;
+ mtl->ambient.b = objmtl->Ka.b;
+ mtl->ambient.a = 1;
+ mtl->diffuse.r = objmtl->Kd.r;
+ mtl->diffuse.g = objmtl->Kd.g;
+ mtl->diffuse.b = objmtl->Kd.b;
+ mtl->diffuse.a = 1;
+ mtl->specular.r = objmtl->Ks.r;
+ mtl->specular.g = objmtl->Ks.g;
+ mtl->specular.b = objmtl->Ks.b;
+ mtl->specular.a = 1;
+ mtl->shininess = objmtl->Ns;
+
+ if(objmtl->map_Kd != nil){
+ mtl->diffusemap = allocmemimaged(objmtl->map_Kd->r, objmtl->map_Kd->chan, objmtl->map_Kd->data);
+ if(mtl->diffusemap == nil)
+ sysfatal("allocmemimaged: %r");
+ mtl->diffusemap->data->ref++;
+ }
+
+ addmtlmap(&mtlmap, objmtl, m->nmaterials-1);
+ }
+
+ for(i = 0; i < nelem(obj->objtab); i++)
+ for(o = obj->objtab[i]; o != nil; o = o->next)
+ for(e = o->child; e != nil; e = ne){
+ ne = e->next;
+
+ switch(e->type){
+ case OBJEPoint:
+ m->prims = erealloc(m->prims, ++m->nprims*sizeof(*m->prims));
+ p = &m->prims[m->nprims-1];
+ memset(p, 0, sizeof *p);
+ p->type = PPoint;
+ p->mtl = getmtlmap(&mtlmap, e->mtl);
+
+ idxtab = &e->indextab[OBJVGeometric];
+ v = &pverts[idxtab->indices[0]];
+ p->v[0].p = Pt3(v->x, v->y, v->z, v->w);
+
+ idxtab = &e->indextab[OBJVTexture];
+ if(idxtab->nindex == 1){
+ v = &tverts[idxtab->indices[0]];
+ p->v[0].uv = Pt2(v->u, v->v, 1);
+ }
+ break;
+ case OBJELine:
+ m->prims = erealloc(m->prims, ++m->nprims*sizeof(*m->prims));
+ p = &m->prims[m->nprims-1];
+ memset(p, 0, sizeof *p);
+ p->type = PLine;
+ p->mtl = getmtlmap(&mtlmap, e->mtl);
+
+ for(idx = 0; idx < 2; idx++){
+ idxtab = &e->indextab[OBJVGeometric];
+ v = &pverts[idxtab->indices[idx]];
+ p->v[idx].p = Pt3(v->x, v->y, v->z, v->w);
+
+ idxtab = &e->indextab[OBJVTexture];
+ if(idxtab->nindex == 2){
+ v = &tverts[idxtab->indices[idx]];
+ p->v[idx].uv = Pt2(v->u, v->v, 1);
+ }
+ }
+ break;
+ case OBJEFace:
+ idxtab = &e->indextab[OBJVGeometric];
+ assert(idxtab->nindex >= 3);
+ gottaclean = 0;
+
/* it takes n-2 triangles to fill any given n-gon */
- trielems = emalloc((idxtab->nindex-2)*sizeof(*trielems));
- nt = triangulate(trielems, e);
- m->nelems += nt;
- m->elems = erealloc(m->elems, m->nelems*sizeof(*m->elems));
- while(nt-- > 0)
- m->elems[m->nelems-nt-1] = trielems[nt];
- free(trielems);
- }else{
- m->elems = erealloc(m->elems, ++m->nelems*sizeof(*m->elems));
- m->elems[m->nelems-1] = e;
+ nt = idxtab->nindex-2;
+ if(nt > maxnt){
+ maxnt = nt;
+ trielems = erealloc(trielems, maxnt*sizeof(*trielems));
+ }
+ if(nt > 1){
+ assert(triangulate(trielems, e) == nt);
+ gottaclean = 1;
+ }else
+ trielems[0] = e;
+
+ while(nt-- > 0){
+ e = trielems[nt];
+ neednormal = 0;
+
+ m->prims = erealloc(m->prims, ++m->nprims*sizeof(*m->prims));
+ p = &m->prims[m->nprims-1];
+ memset(p, 0, sizeof *p);
+ p->type = PTriangle;
+ p->mtl = getmtlmap(&mtlmap, e->mtl);
+
+ for(idx = 0; idx < 3; idx++){
+ idxtab = &e->indextab[OBJVGeometric];
+ v = &pverts[idxtab->indices[idx]];
+ p->v[idx].p = Pt3(v->x, v->y, v->z, v->w);
+
+ idxtab = &e->indextab[OBJVNormal];
+ if(idxtab->nindex == 3){
+ v = &nverts[idxtab->indices[idx]];
+ p->v[idx].n = normvec3(Vec3(v->i, v->j, v->k));
+ }else
+ neednormal = 1;
+
+ idxtab = &e->indextab[OBJVTexture];
+ if(idxtab->nindex == 3){
+ v = &tverts[idxtab->indices[idx]];
+ p->v[idx].uv = Pt2(v->u, v->v, 1);
+ }
+ }
+ if(neednormal){
+ /* TODO build a list of per-vertex normals earlier */
+ n = normvec3(crossvec3(subpt3(p->v[1].p, p->v[0].p), subpt3(p->v[2].p, p->v[0].p)));
+ p->v[0].n = p->v[1].n = p->v[2].n = n;
+ }
+ if(gottaclean){
+ free(e->indextab[OBJVGeometric].indices);
+ free(e->indextab[OBJVNormal].indices);
+ free(e->indextab[OBJVTexture].indices);
+ free(e);
+ }
+ }
+ break;
+ default: continue;
}
}
- return m->nelems;
+
+ free(trielems);
+ clrmtlmap(&mtlmap);
+ return m->nprims;
}
Model *
@@ -117,6 +305,19 @@ newmodel(void)
void
delmodel(Model *m)
{
+ if(m == nil)
+ return;
+ if(m->tex != nil)
+ freememimage(m->tex);
+ if(m->nor != nil)
+ freememimage(m->nor);
+ if(m->nmaterials > 0){
+ while(m->nmaterials--)
+ free(m->materials[m->nmaterials].name);
+ free(m->materials);
+ }
+ if(m->nprims > 0)
+ free(m->prims);
free(m);
}
@@ -138,7 +339,10 @@ newentity(Model *m)
void
delentity(Entity *e)
{
- /* TODO free model */
+ if(e == nil)
+ return;
+ if(e->mdl != nil)
+ delmodel(e->mdl);
free(e);
}
@@ -152,6 +356,15 @@ scene_addent(Scene *s, Entity *e)
s->nents++;
}
+static void
+scene_delent(Scene *s, Entity *e)
+{
+ e->prev->next = e->next;
+ e->next->prev = e->prev;
+ e->prev = e->next = nil;
+ s->nents--;
+}
+
Scene *
newscene(char *name)
{
@@ -160,14 +373,30 @@ newscene(char *name)
s = emalloc(sizeof *s);
s->name = name == nil? nil: strdup(name);
s->ents.prev = s->ents.next = &s->ents;
+ s->nents = 0;
s->addent = scene_addent;
+ s->delent = scene_delent;
return s;
}
void
delscene(Scene *s)
{
- /* TODO free ents */
+ if(s == nil)
+ return;
+ clearscene(s);
free(s->name);
free(s);
}
+
+void
+clearscene(Scene *s)
+{
+ Entity *e, *ne;
+
+ for(e = s->ents.next; e != &s->ents; e = ne){
+ ne = e->next;
+ s->delent(s, e);
+ delentity(e);
+ }
+}