/*% cyntax % && cc -go # % -lpicfile -lfb * cpr -- cheezy polygon renderer. * syntax: * { push transformation stack * } pop transformation stack * v fov near far ex ey ez lx ly lz ux uy uz Set viewing parameters * Q r i j k Quaternion rotate * R angle axis rotate * T dx dy dz translate * S sx sy sz scale * M m00 m01 m02 m03 m10 m11 ... m33 transformation matrix * t x0 y0 z0 x1 y1 z1 x2 y2 z2 c0 c1 Render a triangle * p c0 c1 [x y z]*; Render a polygon * c num r g b a Set a color table entry * l x y z Set light source direction * b r g b a Clear the background * h c0 c1 nx ny x0 y0 x1 y1 file Render a height field * s c0 c1 x y z r Render a sphere */ #include #include #include #include #include #include void depthclip(void); void geode(Point3 *a, Point3 *b, Point3 *c, Point3 cen, double r, int c1, int c2, int n); int getnormal(void); void input(FILE *ifd); void insert(struct edge *e, struct edge **bp); double len(Point3 p); void main(int argc, char *argv[]); char *mkchan(void); void normalize(Point3 *v); void outline(PICFILE *f, unsigned char *p); void polyren(int c, int other); void readbg(FILE *f); void readcolor(FILE *f); void readcomment(FILE *f); void readheight(FILE *f); void readlight(FILE *f); void readmat(FILE *f); void readpoly(FILE *f); void readpop(FILE *f); void readpush(FILE *f); void readquat(FILE *f); void readrot(FILE *f); void readscale(FILE *f); void readsphere(FILE *f); void readtran(FILE *f); void readtri(FILE *f); void readview(FILE *f); void span(int z, double x0, double y0, double x1, double y1); #define F_UNCOVERED HUGE /* * Screen characteristics */ double aspect; /* aspect ratio of whole picture */ int nz=480; int nx=640; typedef struct color{ unsigned char r, g, b, a; }Color; struct pix{ Color rgba; float y; }**pix; Color rgba; #define NCOLOR 500 Color color[NCOLOR]; Point3 light={-.6, .48, .64, 1}; Point3 eye; Point3 yclip; /* actually a plane */ #define NEDGE 2048 Point3 vert[NEDGE]; Point3 *evert; struct edge{ Point3 p, q; double x, y, dx, dy; struct edge *next; }edge[NEDGE]; struct edge *eedge; Point3 normal; int npoly=1; struct edge **buck; struct edge **b0, **b1; #define R 0x01 #define G 0x02 #define B 0x04 #define A 0x08 #define Z 0xf0 int chan; Space *mx; /* modeling space */ Space *wx; /* world space */ void main(int argc, char *argv[]){ struct pix *pp; int i, z; PICFILE *ofd; FILE *ifd; argc=getflags(argc, argv, "a:1[aspect]c:1[rgbaz]w:2[nx nz]"); if(argc<=0) usage("file ..."); if(flag['w']){ nx=atoi(flag['w'][0]); nz=atoi(flag['w'][1]); } if(flag['a']) aspect=atof(flag['a'][0]); else aspect=(double)nz/nx; buck=malloc((nz+1)*sizeof(struct edge *)); b0=&buck[0]; b1=&buck[nz]; if(buck==0){ NoSpace: fprint(2, "%s: can't malloc\n", argv[0]); exits("no space"); } pix=malloc((nz+1)*sizeof(struct pix *)); /* pix[nz] is end of array */ if(pix==0) goto NoSpace; pix[0]=malloc(nx*nz*sizeof(struct pix)); if(pix[0]==0) goto NoSpace; for(z=1;z<=nz;z++) pix[z]=pix[z-1]+nx; if((ofd=picopen_w("OUT", "runcode", 0, 0, nx, nz, mkchan(), 0, 0))==0){ perror(argv[0]); exits("create output"); } for(pp=pix[0];pp!=pix[nz];pp++) pp->y=F_UNCOVERED; for(z=0;z!=256;z++){ color[z].r=color[z].g=color[z].b=z; color[z].a=255; } for(z=0;z!=12;z++){ color[z+256].r=color[z+256].g=color[z+256].b=pow(255., z/11.); color[z+256].a=255; } for(z=0;z!=20;z++){ color[z+268].r=color[z+268].g=color[z+268].b=pow(255., z/19.); color[z+268].a=255; } mx=pushmat(0); if(argc==1) input(stdin); else for(i=1;i!=argc;i++){ ifd=fopen(argv[i], "r"); if(ifd==0){ perror(argv[i]); exits("open input"); } input(ifd); fclose(ifd); } for(z=0;z!=nz;z++) outline(ofd, (unsigned char *)&pix[z][0]); exits(""); } void input(FILE *ifd){ int c; while((c=getc(ifd))!=EOF) switch(c){ case ' ': case '\t': case '\n': break; default: fprintf(stderr, "Bad input `%c", c); while((c=getc(ifd))!='\n' && c!=EOF) putc(c, stderr); fprintf(stderr, "'\n"); break; case '{': readpush(ifd); break; case '}': readpop(ifd); break; case 'v': readview(ifd); break; case 'Q': readquat(ifd); break; case 'R': readrot(ifd); break; case 'T': readtran(ifd); break; case 'S': readscale(ifd); break; case 'M': readmat(ifd); break; case 'c': readcolor(ifd); break; case 'l': readlight(ifd); break; case 'b': readbg(ifd); break; case 't': readtri(ifd); break; case 'p': readpoly(ifd); break; case 'h': readheight(ifd); break; case 's': readsphere(ifd); break; case '#': readcomment(ifd); break; } } char *mkchan(void){ if(!flag['c'] || strcmp(flag['c'][0], "rgb")==0){ chan=R|G|B; return "rgb"; } if(strcmp(flag['c'][0], "rgba")==0){ chan=R|G|B|A; return "rgba"; } if(strcmp(flag['c'][0], "rgbaz")==0){ chan=R|G|B|A|Z; return sizeof(float)==4?"rgbaz...":"rgbaz......."; } fprintf(stderr, "Bad channels %s\n", flag['c'][0]); exits("bad chan"); return 0; /* for cyntax */ } void readcomment(FILE *f){ register c; do c=getc(f); while(c!='\n' && c!=EOF); } void readbg(FILE *f){ int r, g, b, a; Color c; register struct pix *p; if(fscanf(f, "%d%d%d%d", &r, &g, &b, &a)!=4){ fprintf(stderr, "cpr: bad background\n"); exits("bad bg"); } c.r=r; c.g=g; c.b=b; c.a=a; for(p=pix[0];p!=pix[nz];p++) p->rgba=c; } void readcolor(FILE *f){ int n, r, g, b, a; if(fscanf(f, "%d%d%d%d%d", &n, &r, &g, &b, &a)!=5 || n<0 || NCOLOR<=n){ fprintf(stderr, "cpr: Bad color\n"); exits("bad color"); } color[n].r=r; color[n].g=g; color[n].b=b; color[n].a=a; } double len(Point3 p){ return sqrt(p.x*p.x+p.y*p.y+p.z*p.z); } void readlight(FILE *f){ double l; if(fscanf(f, "%lf%lf%lf", &light.x, &light.y, &light.z)!=3){ fprintf(stderr, "cpr: Bad light\n"); exits("bad light"); } l=len(light); if(l!=0){ light.x/=l; light.y/=l; light.z/=l; } } void readview(FILE *f){ double fov, near, far; Point3 e, v, u; double l; e.w=1; v.w=1; u.w=1; if(fscanf(f, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf\n", &fov, &near, &far, &e.x, &e.y, &e.z, &v.x, &v.y, &v.z, &u.x, &u.y, &u.z)!=12){ fprintf(stderr, "cpr: Bad view spec\n"); exits("bad view"); } /* viewport transformation, then perspective, then viewing direction */ while(mx) mx=popmat(mx); mx=pushmat(0); move(mx, .5*nx, 0., .5*nz); scale(mx, .5*nx, 1., -.5*aspect*nz); persp(mx, fov, near, far); look(mx, e, v, u); wx=mx; mx=pushmat(mx); eye=e; yclip=sub3(v, e); l=near/sqrt(yclip.x*yclip.x+yclip.y*yclip.y+yclip.z*yclip.z); yclip.w=-((e.x+l*yclip.x)*yclip.x+(e.y+l*yclip.y)*yclip.y+(e.z+l*yclip.z)*yclip.z); } void readpush(FILE *f){ mx=pushmat(mx); } void readpop(FILE *f){ mx=popmat(mx); } void readquat(FILE *f){ Quaternion q; if(fscanf(f, "%lf%lf%lf%lf", &q.r, &q.i, &q.j, &q.k)!=4 || q.r==0 && q.i==0 && q.j==0 && q.k==0){ fprintf(stderr, "bad rotate\n"); exits("bad rot"); } q=qunit(q); qrot(mx, q); } void readrot(FILE *f){ double theta; int axis; if(fscanf(f, "%lf%d", &theta, &axis)!=2 || axis<0 || 2p.x-edge[0].p.x; b.y=p->p.y-edge[0].p.y; b.z=p->p.z-edge[0].p.z; normal.x+=a.y*b.z-b.y*a.z; normal.y+=b.x*a.z-a.x*b.z; normal.z+=a.x*b.y-a.y*b.x; a.x=b.x; a.y=b.y; a.z=b.z; } l=len(normal); if(l==0) return 0; /* what should we do here? */ normal.x/=l; normal.y/=l; normal.z/=l; if(normal.x*(eye.x-edge[0].p.x) +normal.y*(eye.y-edge[0].p.y) +normal.z*(eye.z-edge[0].p.z)>=0){ normal.x=-normal.x; normal.y=-normal.y; normal.z=-normal.z; return 1; } return 0; } void readpoly(FILE *f){ Point3 in; int c, c0, c1; if(fscanf(f, "%d%d", &c0, &c1)!=2){ Bad: fprintf(stderr, "cpr: bad polygon %d\n", npoly); exits("bad polygon"); } in.x=in.y=in.z=0; in.w=1.; for(evert=vert;evert!=&vert[NEDGE];evert++){ do c=getc(f); while(c==' ' || c=='\t' || c=='\n'); if(c==';' || c==EOF) break; ungetc(c, f); if(fscanf(f, "%lf%lf%lf", &in.x, &in.y, &in.z)!=3) goto Bad; *evert=in; } if(evertx*r; vert[0].y=cen.y+a->y*r; vert[0].z=cen.z+a->z*r; vert[0].w=1; vert[1].x=cen.x+b->x*r; vert[1].y=cen.y+b->y*r; vert[1].z=cen.z+b->z*r; vert[1].w=1; vert[2].x=cen.x+c->x*r; vert[2].y=cen.y+c->y*r; vert[2].z=cen.z+c->z*r; vert[2].w=1; evert=&vert[3]; polyren(c1, c2); return; } pa.x=b->x+c->x; pa.y=b->y+c->y; pa.z=b->z+c->z; normalize(&pa); pb.x=a->x+c->x; pb.y=a->y+c->y; pb.z=a->z+c->z; normalize(&pb); pc.x=b->x+a->x; pc.y=b->y+a->y; pc.z=b->z+a->z; normalize(&pc); geode(&pc, &pb, a, cen, r, c1, c2, n-1); geode(c, &pb, &pa, cen, r, c1, c2, n-1); geode(&pc, b, &pa, cen, r, c1, c2, n-1); geode(&pa, &pb, &pc, cen, r, c1, c2, n-1); } void normalize(Point3 *v){ double l=sqrt(v->x*v->x+v->y*v->y+v->z*v->z); if(l!=0.){ v->x/=l; v->y/=l; v->z/=l; } } void insert(struct edge *e, struct edge **bp){ while(*bp && (*bp)->xx) bp=&((*bp)->next); e->next=*bp; *bp=e; } void depthclip(void){ Point3 q, *v; struct edge *e; int pin, qin; double pd, qd, alpha; q=evert[-1]; qd=q.x*yclip.x+q.y*yclip.y+q.z*yclip.z+yclip.w; qin=qd>=0; eedge=edge; for(v=vert;v!=evert;v++){ pd=v->x*yclip.x+v->y*yclip.y+v->z*yclip.z+yclip.w; pin=pd>=0; if(pin^qin){ alpha=pd/(pd-qd); eedge->p.x=v->x+alpha*(q.x-v->x); eedge->p.y=v->y+alpha*(q.y-v->y); eedge->p.z=v->z+alpha*(q.z-v->z); eedge->p.w=1; eedge++; } if(pin) eedge++->p=*v; q=*v; qd=pd; qin=pin; } } void polyren(int c, int other){ register struct edge *e, *f, *g, **b; register z; Point3 p, in, xin, n, *v; double t; Color col; ++npoly; for(v=vert;v!=evert;v++) *v=xformpoint(*v, wx, mx); depthclip(); if(eedgep=xformpointd(e->p, 0, wx); if(e==edge) eedge[-1].q=e->p; else e[-1].q=e->p; } for(b=b0;b<=b1;b++) *b=0; for(e=edge;e!=eedge;e++){ if(e->p.z>e->q.z){ p=e->p; e->p=e->q; e->q=p; } if(e->p.z==e->q.z || nz<=e->p.z || e->q.z<=0) continue; if(e->p.z<0) z=0; else z=ceil(e->p.z); if(zq.z){ t=(z-e->p.z)/(e->q.z-e->p.z); e->x=e->p.x+t*(e->q.x-e->p.x); e->y=e->p.y+t*(e->q.y-e->p.y); t=1/(e->q.z-e->p.z); e->dx=t*(e->q.x-e->p.x); e->dy=t*(e->q.y-e->p.y); insert(e, &buck[z]); } } if(c<0){ c=-c; t=(normal.x*light.x+normal.y*light.y+normal.z*light.z+1)/2; rgba.r=(color[c].r&255)*t; rgba.g=(color[c].g&255)*t; rgba.b=(color[c].b&255)*t; rgba.a=color[c].a; } else if(c>=300){ c-=300; rgba.r=c&0xFF; rgba.g=(c>>8)&0xFF; rgba.b=(c>>16)&0xFF; rgba.a=255; } else rgba=color[c]; for(b=b0,z=0;b!=b1;b++,z++) if(*b){ for(e=*b;e && (f=e->next);e=g){ g=f->next; span(z, e->x, e->y, f->x, f->y); if(z+1q.z){ e->x+=e->dx; e->y+=e->dy; insert(e, b+1); } if(z+1q.z){ f->x+=f->dx; f->y+=f->dy; insert(f, b+1); } } } } void span(int z, double x0, double y0, double x1, double y1){ register struct pix *pp; register x, ix0, ix1; double dy, y; if(x0==x1) return; ix0=ceil(x0); if(ix0<0) ix0=0; ix1=floor(x1); if(nx<=ix1) ix1=nx-1; dy=(y1-y0)/(x1-x0); for(x=ix0,y=y0+(ix0-x0)*dy,pp=&pix[z][ix0];x<=ix1;x++,y+=dy,pp++){ if(yy){ pp->rgba=rgba; pp->y=y; } } } void outline(PICFILE *f, unsigned char *p){ register unsigned char *q; static unsigned char *obuf=0; register x; if(obuf==0){ obuf=malloc(nx*8); if(obuf==0){ fprint(2, "cpr: out of space\n"); exits("no mem"); } } if(chan==(R|G|B|A|Z) && sizeof(float)==4){ picwrite(f, (char *)p); return; } q=obuf; for(x=0;x!=nx;x++){ if(chan&R) *q++=*p++; else p++; if(chan&G) *q++=*p++; else p++; if(chan&B) *q++=*p++; else p++; if(chan&A) *q++=*p++; else p++; if(chan&Z){ switch(sizeof(float)){ default: fprintf(stderr, "cpr: sizeof(float) is %d! rewrite outline!\n", sizeof(float)); exits("bad size!"); case 8: *q++=*p++; *q++=*p++; *q++=*p++; *q++=*p++; case 4: *q++=*p++; *q++=*p++; *q++=*p++; *q++=*p++; } } else p+=sizeof(float); } picwrite(f, (char *)obuf); }