#include "light.hpp" #include #include "image.hpp" #include "macs.hpp" #include "video.hpp" #include "palette.hpp" #include "timing.hpp" #include "specs.hpp" #include "dprint.hpp" #include "filter.hpp" #include "video.hpp" light_source *first_light_source=NULL; unsigned char *white_light,*green_light; unsigned short min_light_level=0; extern int vmode; int light_detail=MEDIUM_DETAIL; long light_to_number(light_source *l) { if (!l) return 0; int x=1; for (light_source *s=first_light_source;s;s=s->next,x++) if (s==l) return x; return 0; } light_source *number_to_light(long x) { if (x==0) return NULL; x--; for (light_source *s=first_light_source;x && s;x--,s=s->next); return s; } light_source *light_source::copy() { next=new light_source(type,x,y,inner_radius,outer_radius,xshift,yshift,next); return next; } void delete_all_lights() { while (first_light_source) { light_source *p=first_light_source; first_light_source=first_light_source->next; delete p; } } void delete_light(light_source *which) { if (which==first_light_source) { first_light_source=first_light_source->next; delete which; } else { for (light_source *f=first_light_source;f->next!=which && f;f=f->next); if (f) { f->next=which->next; delete which; } } } void light_source::calc_range() { switch (type) { case 0 : { x1=x-(outer_radius>>xshift); y1=y-(outer_radius>>yshift); x2=x+(outer_radius>>xshift); y2=y+(outer_radius>>yshift); } break; case 1 : { x1=x-(outer_radius>>xshift); y1=y-(outer_radius>>yshift); x2=x+(outer_radius>>xshift); y2=y; } break; case 2 : { x1=x-(outer_radius>>xshift); y1=y; x2=x+(outer_radius>>xshift); y2=y+(outer_radius>>yshift); } break; case 3 : { x1=x; y1=y-(outer_radius>>yshift); x2=x+(outer_radius>>xshift); y2=y+(outer_radius>>yshift); } break; case 4 : { x1=x-(outer_radius>>xshift); y1=y-(outer_radius>>yshift); x2=x; y2=y+(outer_radius>>yshift); } break; case 5 : { x1=x; y1=y-(outer_radius>>yshift); x2=x+(outer_radius>>xshift); y2=y; } break; case 6 : { x1=x-(outer_radius>>xshift); y1=y-(outer_radius>>yshift); x2=x; y2=y; } break; case 7 : { x1=x-(outer_radius>>xshift); y1=y; x2=x; y2=y+(outer_radius>>yshift); } break; case 8 : { x1=x; y1=y; x2=x+(outer_radius>>xshift); y2=y+(outer_radius>>yshift); } break; case 9 : { x1=x; y1=y; x2=x+xshift; y2=y+yshift; } break; } mul_div=(1<<16)/(outer_radius-inner_radius)*64; } light_source::light_source(char Type, long X, long Y, long Inner_radius, long Outer_radius, long Xshift, long Yshift, light_source *Next) { type=Type; x=X; y=Y; inner_radius=Inner_radius; outer_radius=Outer_radius; next=Next; known=0; xshift=Xshift; yshift=Yshift; calc_range(); } light_source *add_light_source(char type, long x, long y, long inner, long outer, long xshift, long yshift) { first_light_source=new light_source(type,x,y,inner,outer,xshift,yshift,first_light_source); return first_light_source; } #define TTINTS 9 uchar *tints[TTINTS]; void calc_tint(uchar *tint, int rs, int gs, int bs, int ra, int ga, int ba, palette *pal) { palette npal; memset(npal.addr(),0,256); for (int i=0;i<256;i++) { npal.set(i,(int)rs,(int)gs,(int)bs); rs+=ra; if (rs>255) rs=255; if (rs<0) rs=0; gs+=ga; if (gs>255) gs=255; if (gs<0) gs=0; bs+=ba; if (bs>255) bs=255; if (bs<0) bs=0; } filter f(pal,&npal); filter f2(&npal,pal); for (i=0;i<256;i++,tint++) *tint=f2.get_mapping(f.get_mapping(i)); } void calc_light_table(palette *pal) { white_light=(unsigned char *)jmalloc(256*64,"light table"); green_light=(unsigned char *)jmalloc(256*64,"green light"); for (int i=0;iaddr(),768)) recalc=1; else { fp.read(white_light,256*64); fp.read(green_light,256*64); for (i=0;iget(color,r,g,b); if (color%16==0) fprintf(stderr,"%d ",color); for (int intensity=63;intensity>=0;intensity--) { if (r>0 || g>0 || b>0) white_light[intensity*256+color]=pal->find_closest(r,g,b); else white_light[intensity*256+color]=0; if (r) r--; if (g) g--; if (b) b--; } } fprintf(stderr,"green : "); for (color=0;color<256;color++) { unsigned char r,g,b; pal->get(color,r,g,b); r=r*3/5; b=b*3/5; g+=7; if (g>255) g=255; if (color%16==0) fprintf(stderr,"%d ",color); for (int intensity=63;intensity>=0;intensity--) { if (r>0 || g>0 || b>0) green_light[intensity*256+color]=pal->find_closest(r,g,b); else green_light[intensity*256+color]=0; if (r) r--; if ((intensity&1)==1) if (g) g--; if (b) b--; } } dprintf("\ncalculating tints : \n"); uchar t[TTINTS*6]={0,0,0,0,0,0, // normal 0,0,0,1,0,0, // red 0,0,0,1,1,0, // yellow 0,0,0,1,0,1, // purple 0,0,0,1,1,1, // gray 0,0,0,0,1,0, // green 0,0,0,0,0,1, // blue 0,0,0,0,1,1, // cyan 0,0,0,0,0,0 // reverse green (night vision effect) } ; uchar *ti=t+6; uchar *c; for (i=0,c=tints[0];i<256;i++,c++) *c=i; // make the normal tint (maps everthing to itself) for (i=0,c=tints[TTINTS-1];i<256;i++,c++) // reverse green { int r=pal->red(i)/2,g=255-pal->green(i)-30,b=pal->blue(i)*3/5+50; if (g<0) g=0; if (b>255) b=0; *c=pal->find_closest(r,g,b); } // make the colored tints for (i=1;iaddr(),768)); f.write(white_light,256*64); f.write(green_light,256*64); for (int i=0;itotal=total; if (total) { p->lights=(light_source **)jmalloc(total*sizeof(light_source *),"light patches"); memcpy(p->lights,lights,total*(sizeof(light_source *))); } else p->lights=NULL; return p; } #define MAX_LP 10 void add_light(light_patch *&first, long x1, long y1, long x2, long y2, light_source *who) { light_patch *last=NULL; for (light_patch *p=first;p;p=p->next) { // first see if light patch we are adding is enclosed entirely by another patch if (x1>=p->x1 && y1>=p->y1 && x2<=p->x2 && y2<=p->y2) { if (p->total==MAX_LP) return ; if (x1>p->x1) { first=p->copy(first); first->x2=x1-1; } if (x2x2) { first=p->copy(first); first->x1=x2+1; } if (y1>p->y1) { first=p->copy(first); first->x1=x1; first->x2=x2; first->y2=y1-1; } if (y2y2) { first=p->copy(first); first->x1=x1; first->x2=x2; first->y1=y2+1; } p->x1=x1; p->y1=y1; p->x2=x2; p->y2=y2; p->total++; p->lights=(light_source **)jrealloc(p->lights,sizeof(light_source *)*p->total,"patch_list"); p->lights[p->total-1]=who; return ; } // see if the patch completly covers another patch. if (x1<=p->x1 && y1<=p->y1 && x2>=p->x2 && y2>=p->y2) { if (x1x1) add_light(first,x1,y1,p->x1-1,y2,who); if (x2>p->x2) add_light(first,p->x2+1,y1,x2,y2,who); if (y1y1) add_light(first,p->x1,y1,p->x2,p->y1-1,who); if (y2>p->y2) add_light(first,p->x1,p->y2+1,p->x2,y2,who); if (p->total==MAX_LP) return ; p->total++; p->lights=(light_source **)jrealloc(p->lights,sizeof(light_source *)*p->total,"patch_list"); p->lights[p->total-1]=who; return ; } // see if we intersect another rect if (!(x2x1 || y2y1 || x1>p->x2 || y1>p->y2)) { int ax1,ay1,ax2,ay2; if (x1x1) { add_light(first,x1,max(y1,p->y1),p->x1-1,min(y2,p->y2),who); ax1=p->x1; } else ax1=x1; if (x2>p->x2) { add_light(first,p->x2+1,max(y1,p->y1),x2,min(y2,p->y2),who); ax2=p->x2; } else ax2=x2; if (y1y1) { add_light(first,x1,y1,x2,p->y1-1,who); ay1=p->y1; } else ay1=y1; if (y2>p->y2) { add_light(first,x1,p->y2+1,x2,y2,who); ay2=p->y2; } else ay2=y2; add_light(first,ax1,ay1,ax2,ay2,who); return ; } } } light_patch *find_patch(int screenx, int screeny, light_patch *list) { for (;list;list=list->next) { if (screenx>=list->x1 && screenx<=list->x2 && screeny>=list->y1 && screeny<=list->y2) return list; } return NULL; } int calc_light_value(long x, long y, light_patch *which) { int lv=0; int t=which->total; for (register int i=t-1;i>=0;i--) { light_source *fn=which->lights[i]; if (fn->type==9) { lv=fn->inner_radius; i=0; } else { long dx=abs(fn->x-x)<xshift; long dy=abs(fn->y-y)<yshift; long r2; if (dx>1); else r2=dx+dy-(dy>>1); if (r2>=fn->inner_radius) { if (r2outer_radius) { lv+=((fn->outer_radius-r2)*fn->mul_div)>>16; } } else lv=63; } } if (lv>63) return 63; else return lv; } void reduce_patches(light_patch *f) // find constant valued patches { } light_patch *make_patch_list(int cx1,int cy1,int cx2,int cy2, long screenx, long screeny) { light_patch *first=new light_patch(cx1,cy1,cx2,cy2,NULL); for (light_source *f=first_light_source;f;f=f->next) // determine which lights will have effect { long x1=f->x1-screenx,y1=f->y1-screeny,x2=f->x2-screenx,y2=f->y2-screeny; if (x1cx2) x2=cx2; if (y2>cy2) y2=cy2; if (x1<=x2 && y1<=y2) add_light(first,x1,y1,x2,y2,f); } reduce_patches(first); return first; } void delete_patch_list(light_patch *first) { while (first) { light_patch *p=first; first=first->next; delete p; } } #ifdef __WATCOMC__ extern "C" { extern long MAP_PUT(long pad, long screen_addr, long remap, long w); extern long MAP_PUT2(long dest, long screen_addr, long remap, long w); } ; #else inline long MAP_PUT(long pad, long screen_addr, long remap, long w) { while (w--) { *((uchar *)(screen_addr))=*((uchar *)remap+*((uchar *)screen_addr)); screen_addr++; } return screen_addr; } inline long MAP_PUT2(long dest_addr, long screen_addr, long remap, long w) { while (w--) { *((uchar *)(dest_addr))=*((uchar *)remap+*((uchar *)screen_addr)); screen_addr++; dest_addr++; } return dest_addr; } #endif inline long calc_lv(light_patch *lp, long sx, long sy) { light_source **lon_p=lp->lights; long lv=min_light_level; int light_on; for (light_on=lp->total-1;light_on>=0;light_on--) { light_source *fn=*lon_p; long *dt=&(*lon_p)->type; if (*dt==9) { lv=fn->inner_radius; light_on=0; } else { dt++; int dx=abs(*dt-sx); dt++; dx<<=*dt; dt++; int dy=abs(*dt-sy); dt++; dy<<=*dt; dt++; int r2; if (dx>1); else r2=dx+dy-(dy>>1); if (r2<*dt) { int v=*dt-r2; dt++; lv+=v*(*dt)>>16; } } lon_p++; } if (lv>63) return 63; else return lv; } void light_screen(image *sc, long screenx, long screeny, uchar *light_lookup) { int lx_run,ly_run; switch (light_detail) { case HIGH_DETAIL : { lx_run=2; ly_run=1; } break; case MEDIUM_DETAIL : { lx_run=3; ly_run=2; } break; case LOW_DETAIL : { lx_run=4; ly_run=3; } break; case POOR_DETAIL : // low detail lighting alread taken care of. return ; } short cx1,cy1,cx2,cy2; sc->get_clip(cx1,cy1,cx2,cy2); // sc->add_dirty(cx1,cy1,cx2,cy2); unsigned char *mint=light_lookup+min_light_level*256; screenx-=cx1; screeny-=cy1; light_patch *first=make_patch_list(cx1,cy1,cx2,cy2,screenx,screeny); int ytry=(1<width(); for (light_patch *lp=first;lp;lp=lp->next) { register int x2=lp->x2; if (lp->total==0) // do dark patches really fast. { unsigned char *sl=screen->scan_line(lp->y1)+lp->x1,*dest; #ifdef __WATCOMC__ if (get_vmode()==19) dest=(uchar *)0xa0000+lp->y1*320+lp->x1; else dest=sl; #else dest=sl; #endif int y2=lp->y2; for (int y=lp->y1;y<=y2;y++,sl+=scr_w,dest+=scr_w) { int w=lp->x2-lp->x1+1; MAP_PUT2((long)dest,(long)sl,(long)mint,w); } } else { int todoy; int y2=lp->y2; int xmask=(xtry-1); int ymask=(ytry-1); uchar *yaddr=screen->scan_line(lp->y1)+lp->x1,*dyaddr; #ifdef __WATCOMC__ if (get_vmode()==19) dyaddr=((uchar *)0xa0000)+lp->y1*320+lp->x1; else dyaddr=yaddr; #else dyaddr=yaddr; #endif for (int y=lp->y1;y<=y2;) { long sy=(screeny+y); if (y+ytry>lp->y2) // see how many y lines to do for this block todoy=lp->y2-y+1; else todoy=ytry; int maxy=ytry-(sy&ymask); if (todoy>maxy) todoy=maxy; uchar *xaddr=yaddr,*dxaddr=dyaddr; for (register int x=lp->x1;x<=x2;) { long lv=min_light_level; unsigned char *ct; light_source *f; int todox; if (x+xtry>lp->x2) todox=lp->x2-x+1; else todox=xtry; int sx=(screenx+x); int max=xtry-(sx&xmask); if (todox>max) todox=max; ct=light_lookup+calc_lv(lp,sx,sy)*256; switch (todox) { case 8 : { x+=8; switch (todoy) { case 4 : { uchar *addr=xaddr,*daddr=dxaddr; dxaddr=(uchar *)MAP_PUT2((long)daddr,(long)addr,(long)ct,8); xaddr+=8; addr+=scr_w; daddr+=scr_w; MAP_PUT2((long)daddr,(long)addr,(long)ct,8); addr+=scr_w; daddr+=scr_w; MAP_PUT2((long)daddr,(long)addr,(long)ct,8); addr+=scr_w; daddr+=scr_w; MAP_PUT2((long)daddr,(long)addr,(long)ct,8); } break; case 3 : { uchar *addr=xaddr,*daddr=dxaddr; dxaddr=(uchar *)MAP_PUT2((long)daddr,(long)addr,(long)ct,8); xaddr+=8; addr+=scr_w; daddr+=scr_w; MAP_PUT2((long)daddr,(long)addr,(long)ct,8); addr+=scr_w; daddr+=scr_w; MAP_PUT2((long)daddr,(long)addr,(long)ct,8); } break; case 2 : { uchar *addr=xaddr,*daddr=dxaddr; dxaddr=(uchar *)MAP_PUT2((long)daddr,(long)addr,(long)ct,8); xaddr+=8; addr+=scr_w; daddr+=scr_w; MAP_PUT2((long)daddr,(long)addr,(long)ct,8); } break; case 1 : { dxaddr=(uchar *)MAP_PUT((long)dxaddr,(long)xaddr,(long)ct,8); xaddr+=8; } break; } } break; /* case 4 : { x+=4; switch (todoy) { case 4 : { uchar *addr=xaddr; xaddr=(uchar *)MAP_PUT(0,(long)addr,(long)ct,4); addr+=scr_w; daddr+=scr_w; MAP_PUT(0,(long)addr,(long)ct,4); addr+=scr_w; daddr+=scr_w; MAP_PUT(0,(long)addr,(long)ct,4); addr+=scr_w; daddr+=scr_w; MAP_PUT(0,(long)addr,(long)ct,4); } break; case 3 : { uchar *addr=xaddr; xaddr=(uchar *)MAP_PUT(0,(long)addr,(long)ct,4); addr+=scr_w; daddr+=scr_w; MAP_PUT(0,(long)addr,(long)ct,4); addr+=scr_w; daddr+=scr_w; MAP_PUT(0,(long)addr,(long)ct,4); } break; case 2 : { uchar *addr=xaddr; xaddr=(uchar *)MAP_PUT(0,(long)addr,(long)ct,4); addr+=scr_w; daddr+=scr_w; MAP_PUT(0,(long)addr,(long)ct,4); } break; case 1 : xaddr=(uchar *)MAP_PUT(0,(long)xaddr,(long)ct,4); break; } } break; */ default : { x+=todox; if (todoy==1) { dxaddr=(uchar *)MAP_PUT2((long)dxaddr,(long)xaddr,(long)ct,todox); xaddr+=todox; } else { uchar *addr=xaddr,*daddr=dxaddr; dxaddr=(uchar *)MAP_PUT2((long)dxaddr,(long)xaddr,(long)ct,todox); xaddr+=todox; addr+=scr_w; daddr+=scr_w; int cc=todoy-1; while (cc--) { MAP_PUT2((long)daddr,(long)addr,(long)ct,todox); addr+=scr_w; daddr+=scr_w; } } } } } int yadd=scr_w*todoy; yaddr+=yadd; dyaddr+=yadd; y+=todoy; } } } while (first) { light_patch *p=first; first=first->next; delete p; } } void add_light_spec(spec_directory *sd, char *level_name) { long size=4+4; // number of lights and minimum light levels for (light_source *f=first_light_source;f;f=f->next) size+=6*4+1; sd->add(new spec_entry(SPEC_LIGHT_LIST,"lights",NULL,size,0)); } void write_lights(jFILE *fp) { int t=0; for (light_source *f=first_light_source;f;f=f->next) t++; fp->write_long(t); fp->write_long(min_light_level); for (f=first_light_source;f;f=f->next) { fp->write_long(f->x); fp->write_long(f->y); fp->write_long(f->xshift); fp->write_long(f->yshift); fp->write_long(f->inner_radius); fp->write_long(f->outer_radius); fp->write_byte(f->type); } } int send_lights(net_descriptor *os) { packet pk; int t=0; for (light_source *f=first_light_source;f;f=f->next) t++; pk.write_long(t); pk.write_short(min_light_level); if (!os->send(pk)) return 0; for (f=first_light_source;f;f=f->next) { pk.reset(); pk.write_long(f->x); pk.write_long(f->y); pk.write_long(f->xshift); pk.write_long(f->yshift); pk.write_long(f->inner_radius); pk.write_long(f->outer_radius); pk.write_long(f->type); if (!os->send(pk)) return 0; } return 1; } void read_lights(spec_directory *sd, jFILE *fp, char *level_name) { delete_all_lights(); spec_entry *se=sd->find("lights"); if (se) { fp->seek(se->offset,SEEK_SET); long t=fp->read_long(); min_light_level=fp->read_long(); light_source *last; while (t) { t--; long x=fp->read_long(); long y=fp->read_long(); long xshift=fp->read_long(); long yshift=fp->read_long(); long ir=fp->read_long(); long ora=fp->read_long(); long ty=fp->read_byte(); light_source *p=new light_source(ty,x,y,ir,ora,xshift,yshift,NULL); if (first_light_source) last->next=p; else first_light_source=p; last=p; } } } int receive_lights(net_descriptor *os) { packet pk; if (!os->get(pk)) { printf("net error : light total\n"); return 0; } long t; if (pk.read((uchar *)&t,4)!=4) { printf("net error : (t) packet incomplete\n"); return 0; } t=lltl(t); if (pk.read((uchar *)&min_light_level,2)!=2) { printf("net error : (minl) packet incomplete\n"); return 0; } min_light_level=lstl(min_light_level); light_source *last; printf("Reading %d lights\n",t); while (t) { if (!os->get(pk)) {printf("net error : read light\n"); return 0; } t--; long dt[7]; if (pk.read((uchar *)dt,7*4)!=7*4) return 0; light_source *p=new light_source(lltl(dt[6]), lltl(dt[0]),lltl(dt[1]), lltl(dt[4]),lltl(dt[5]), lltl(dt[2]),lltl(dt[3]),NULL); if (first_light_source) last->next=p; else first_light_source=p; last=p; } return 1; }