ref: b4d8077f0ce99505f7b65a67fc92aa20dc96ed78
dir: /obj.c/
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <bio.h>
#include <obj.h>
#undef isspace(c)
#define isspace(c) ((c) == ' ' || (c) == '\t')
typedef struct Line Line;
struct Line
{
char *file;
ulong lineno;
};
static Line curline;
static void
error(char *fmt, ...)
{
va_list va;
char buf[ERRMAX], *bp;
va_start(va, fmt);
bp = seprint(buf, buf + sizeof buf, "%s:%lud ", curline.file, curline.lineno);
vseprint(bp, buf + sizeof buf, fmt, va);
va_end(va);
werrstr("%s\n", buf);
}
static void *
emalloc(ulong n)
{
void *p;
p = malloc(n);
if(p == nil)
sysfatal("malloc: %r");
memset(p, 0, n);
setmalloctag(p, getcallerpc(&n));
return p;
}
static void *
erealloc(void *v, ulong n)
{
void *nv;
nv = realloc(v, n);
if(nv == nil)
sysfatal("realloc: %r");
setrealloctag(nv, getcallerpc(&v));
return nv;
}
static int
max(int a, int b)
{
return a > b? a: b;
}
static uint
hash(char *s)
{
uint h;
h = 0x811c9dc5;
while(*s != 0)
h = (h^(uchar)*s++) * 0x1000193;
return h % OBJHTSIZE;
}
static void
addvertva(OBJVertexArray *va, OBJVertex v)
{
va->verts = erealloc(va->verts, ++va->nvert*sizeof(OBJVertex));
va->verts[va->nvert-1] = v;
}
static void
addvert(OBJ *obj, OBJVertex v, int vtype)
{
addvertva(&obj->vertdata[vtype], v);
}
static void
addelem(OBJObject *o, OBJElem *e)
{
OBJElem *ep;
if(o->child == nil){
o->child = e;
return;
}
for(ep = o->child; ep->next != nil; ep = ep->next)
;
ep->next = e;
}
static OBJElem *
allocelem(int t)
{
OBJElem *e;
e = emalloc(sizeof(OBJElem));
e->type = t;
return e;
}
static void
addelemidx(OBJElem *e, int idxtab, int idx)
{
OBJIndexArray *tab;
tab = &e->indextab[idxtab];
tab->indices = erealloc(tab->indices, ++tab->nindex*sizeof(int));
tab->indices[tab->nindex-1] = idx;
}
static void
freeelem(OBJElem *e)
{
int i;
for(i = 0; i < nelem(e->indextab); i++)
free(e->indextab[i].indices);
free(e);
}
static OBJObject *
alloco(char *n)
{
OBJObject *o;
o = emalloc(sizeof(OBJObject));
o->name = strdup(n);
return o;
}
static void
freeo(OBJObject *o)
{
OBJElem *e, *ne;
free(o->name);
for(e = o->child; e != nil; e = ne){
ne = e->next;
freeelem(e);
}
free(o);
}
static void
pusho(OBJ *obj, OBJObject *o)
{
OBJObject *op, *prev;
uint h;
prev = nil;
h = hash(o->name);
for(op = obj->objtab[h]; op != nil; prev = op, op = op->next)
if(strcmp(op->name, o->name) == 0){
o->next = op->next;
freeo(op);
break;
}
if(prev == nil){
obj->objtab[h] = o;
return;
}
prev->next = o;
}
static OBJObject *
geto(OBJ *obj, char *n)
{
OBJObject *o;
uint h;
h = hash(n);
for(o = obj->objtab[h]; o != nil; o = o->next)
if(strcmp(o->name, n) == 0)
break;
return o;
}
OBJ *
objparse(char *file)
{
Biobuf *bin;
OBJ *obj;
OBJObject *o;
OBJElem *e;
OBJVertex v;
double *d;
char c, buf[256], *p;
int vtype, idxtab, idx, sign;
o = nil;
bin = Bopen(file, OREAD);
if(bin == nil)
sysfatal("Bopen: %r");
curline.file = file;
curline.lineno = 1;
obj = emalloc(sizeof(OBJ));
while((c = Bgetc(bin)) != Beof){
switch(c){
case 'v':
d = (double*)&v;
c = Bgetc(bin);
vtype = OBJVGeometric;
switch(c){
case 't': vtype = OBJVTexture; break;
case 'p': vtype = OBJVParametric; break;
case 'n': vtype = OBJVNormal; break;
default:
if(!isspace(c)){
error("wrong vertex type");
goto error;
}
}
while(c = Bgetc(bin), c != Beof && c != '\n' && d-(double*)&v < 4){
while(isspace(c))
c = Bgetc(bin);
if(c == '\\'){
while(c != '\n')
c = Bgetc(bin);
continue;
}
if(c != '-' && !isdigit(c)){
error("unexpected character '%c'", c);
goto error;
}
Bungetc(bin);
Bgetd(bin, d++);
}
switch(vtype){
case OBJVGeometric:
if(d-(double*)&v < 3){
error("not enough coordinates");
goto error;
}
if(d-(double*)&v < 4)
*d = 1; /* default w value */
break;
case OBJVTexture:
if(d-(double*)&v < 1){
error("not enough coordinates");
goto error;
}
while(d-(double*)&v < 3)
*d++ = 0; /* default v and w values */
break;
case OBJVParametric:
if(d-(double*)&v < 2){
error("not enough coordinates");
goto error;
}
if(d-(double*)&v < 3)
*d = 1; /* default w value */
break;
case OBJVNormal:
if(d-(double*)&v < 3){
error("not enough coordinates");
goto error;
}
}
addvert(obj, v, vtype);
break;
case 'o':
p = buf;
c = Bgetc(bin);
if(!isspace(c)){
error("syntax error");
goto error;
}
while(isspace(c))
c = Bgetc(bin);
if(!isalnum(c)){
error("unexpected character '%c'", c);
goto error;
}
do{
*p++ = c;
}while(c = Bgetc(bin), isalnum(c) && p-buf < sizeof(buf)-1);
*p = 0;
o = geto(obj, buf);
if(o == nil){
o = alloco(buf);
pusho(obj, o);
}
break;
case 'g':
case 's':
/* element and smoothing groups ignored for now */
while(c != '\n')
c = Bgetc(bin);
break;
case 'p':
c = Bgetc(bin);
if(!isspace(c)){
error("syntax error");
goto error;
}
while(c = Bgetc(bin), c != '\n'){
idx = 0;
sign = 0;
while(isspace(c))
c = Bgetc(bin);
if(c == '\\'){
while(c != '\n')
c = Bgetc(bin);
continue;
}
if(c != '-' && !isdigit(c)){
error("unexpected character '%c'", c);
goto error;
}
if(c == '-'){
sign = 1;
c = Bgetc(bin);
if(!isdigit(c)){
error("unexpected character '%c'", c);
goto error;
}
}
do{
idx = idx*10 + c-'0';
}while(c = Bgetc(bin), isdigit(c));
Bungetc(bin);
idx = sign ? obj->vertdata[OBJVGeometric].nvert-idx : idx-1;
if(idx+1 > obj->vertdata[OBJVGeometric].nvert){
error("not enough vertices");
goto error;
}
e = allocelem(OBJEPoint);
addelemidx(e, OBJVGeometric, idx);
if(o == nil){
o = alloco("default");
pusho(obj, o);
}
addelem(o, e);
}
break;
case 'l':
c = Bgetc(bin);
if(!isspace(c)){
error("syntax error");
goto error;
}
while(c = Bgetc(bin), c != '\n'){
idx = 0;
sign = 0;
while(isspace(c))
c = Bgetc(bin);
if(c == '\\'){
while(c != '\n')
c = Bgetc(bin);
continue;
}
if(c != '-' && !isdigit(c)){
error("unexpected character '%c'", c);
goto error;
}
if(c == '-'){
sign = 1;
c = Bgetc(bin);
if(!isdigit(c)){
error("unexpected character '%c'", c);
goto error;
}
}
do{
idx = idx*10 + c-'0';
}while(c = Bgetc(bin), isdigit(c));
idx = sign ? obj->vertdata[OBJVGeometric].nvert-idx : idx-1;
if(idx+1 > obj->vertdata[OBJVGeometric].nvert){
error("not enough vertices");
goto error;
}
e = allocelem(OBJELine);
addelemidx(e, OBJVGeometric, idx);
Line2:
idx = 0;
sign = 0;
while(isspace(c))
c = Bgetc(bin);
if(c == '\\'){
while(c != '\n')
c = Bgetc(bin);
c = Bgetc(bin);
goto Line2;
}
if(c != '-' && !isdigit(c)){
freeelem(e);
error("unexpected character '%c'", c);
goto error;
}
if(c == '-'){
sign = 1;
c = Bgetc(bin);
if(!isdigit(c)){
freeelem(e);
error("unexpected character '%c'", c);
goto error;
}
}
do{
idx = idx*10 + c-'0';
}while(c = Bgetc(bin), isdigit(c));
Bungetc(bin);
idx = sign ? obj->vertdata[OBJVGeometric].nvert-idx : idx-1;
if(idx+1 > obj->vertdata[OBJVGeometric].nvert){
freeelem(e);
error("not enough vertices");
goto error;
}
addelemidx(e, OBJVGeometric, idx);
if(o == nil){
o = alloco("default");
pusho(obj, o);
}
addelem(o, e);
}
break;
case 'f':
e = allocelem(OBJEFace);
idxtab = 0;
c = Bgetc(bin);
if(!isspace(c)){
freeelem(e);
error("syntax error");
goto error;
}
while(c = Bgetc(bin), c != '\n'){
idx = 0;
sign = 0;
if(isspace(c))
idxtab = 0;
while(isspace(c))
c = Bgetc(bin);
if(c == '\\'){
while(c != '\n')
c = Bgetc(bin);
continue;
}
if(c == '/'){
if(++idxtab >= OBJNVERT){
freeelem(e);
error("unknown vertex type '%d'", idxtab);
goto error;
}
continue;
}
if(c != '-' && !isdigit(c)){
freeelem(e);
error("unexpected character '%c'", c);
goto error;
}
if(c == '-'){
sign = 1;
c = Bgetc(bin);
if(!isdigit(c)){
freeelem(e);
error("unexpected character '%c'", c);
goto error;
}
}
do{
idx = idx*10 + c-'0';
}while(c = Bgetc(bin), isdigit(c));
Bungetc(bin);
idx = sign ? obj->vertdata[idxtab].nvert-idx : idx-1;
if(idx+1 > obj->vertdata[idxtab].nvert){
freeelem(e);
error("not enough vertices");
goto error;
}
addelemidx(e, idxtab, idx);
}
if(o == nil){
o = alloco("default");
pusho(obj, o);
}
addelem(o, e);
break;
case 'm':
case 'u':
p = buf;
do{
*p++ = c;
}while(c = Bgetc(bin), isalpha(c) && p-buf < sizeof(buf)-1);
*p = 0;
if(strcmp(buf, "mtllib") != 0 && strcmp(buf, "usemtl") != 0){
error("syntax error");
goto error;
}
while(c != '\n')
c = Bgetc(bin);
break;
case '#':
while(c != '\n')
c = Bgetc(bin);
break;
}
do{
if(c == '\n'){
curline.lineno++;
break;
}
if(!isspace(c)){
error("syntax error");
goto error;
}
}while((c = Bgetc(bin)) != Beof);
}
Bterm(bin);
return obj;
error:
objfree(obj);
Bterm(bin);
return nil;
}
void
objfree(OBJ *obj)
{
OBJObject *o, *no;
int i;
if(obj == nil)
return;
for(i = 0; i < nelem(obj->vertdata); i++)
free(obj->vertdata[i].verts);
for(i = 0; i < nelem(obj->objtab); i++)
for(o = obj->objtab[i]; o != nil; o = no){
no = o->next;
freeo(o);
}
free(obj);
}
int
OBJfmt(Fmt *f)
{
OBJ *obj;
OBJObject *o;
OBJElem *e;
OBJVertex v;
int i, j, k, n, pack, maxnindex;
n = pack = 0;
obj = va_arg(f->args, OBJ*);
for(i = 0; i < nelem(obj->vertdata); i++)
for(j = 0; j < obj->vertdata[i].nvert; j++){
v = obj->vertdata[i].verts[j];
switch(i){
case OBJVGeometric:
n += fmtprint(f, "v %g %g %g %g\n", v.x, v.y, v.z, v.w);
break;
case OBJVTexture:
n += fmtprint(f, "vt %g %g %g\n", v.u, v.v, v.vv);
break;
case OBJVNormal:
n += fmtprint(f, "vn %g %g %g\n", v.i, v.j, v.k);
break;
case OBJVParametric:
n += fmtprint(f, "vp %g %g %g\n", v.u, v.v, v.vv);
break;
}
}
for(i = 0; i < nelem(obj->objtab); i++)
for(o = obj->objtab[i]; o != nil; o = o->next){
if(strcmp(o->name, "default") != 0)
n += fmtprint(f, "o %s\n", o->name);
for(e = o->child; e != nil; e = e->next){
switch(e->type){
case OBJEPoint:
if(pack == 0)
n += fmtprint(f, "p");
pack = pack > 0 ? --pack : 8-1;
break;
case OBJELine:
n += fmtprint(f, "l");
break;
case OBJEFace:
n += fmtprint(f, "f");
break;
//case OBJECurve:
//case OBJECurve2:
//case OBJESurface:
}
for(maxnindex = 0, j = 0; j < nelem(e->indextab); j++)
maxnindex = max(e->indextab[j].nindex, maxnindex);
for(k = 0; k < maxnindex; k++){
n += fmtprint(f, " ");
for(j = 0; j < nelem(e->indextab); j++){
if(k >= e->indextab[j].nindex)
continue;
if(j > 0)
n += fmtprint(f, "/");
n += fmtprint(f, "%d", e->indextab[j].indices[k]+1);
}
}
if(e->type != OBJEPoint || pack == 0)
n += fmtprint(f, "\n");
}
}
return n;
}
void
OBJfmtinstall(void)
{
fmtinstall('O', OBJfmt);
}