/* simgraph.c * * Copyright (c) 2001 Hansjörg Malthaner * hansjoerg.malthaner@gmx.de * * This file is part of the Simugraph graphics engine. * * * This file may be copied and modified freely so long as the above credits, * this paragraph, and the below disclaimer of warranty are retained; no * financial profit is derived from said modification or copying; and all * licensing rights to any modifications are granted to the original author, * Hansjörg Malthaner. * * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ /* simgraph.c * * Versuch einer Graphic fuer Simulationsspiele * Hj. Malthaner, Aug. 1997 * * A try to create a graphics engine for simulation games. * * 3D, isometrische Darstellung * * 3D, isometric display * * * 18.11.97 lineare Speicherung fuer Images -> hoehere Performance * 22.03.00 run längen Speicherung fuer Images -> hoehere Performance * 15.08.00 dirty tile verwaltung fuer effizientere updates */ //#define DEBUG 1 #if defined(MSDOS) || defined(__MINGW32__) #define USE_SOFTPOINTER #endif #include #include #include #include #include "simgraph.h" #include "simsys.h" extern unsigned int class; extern unsigned int code; extern int mx,my; /* es sind negative Koodinaten mgl */ #ifdef USE_SOFTPOINTER static int softpointer = 261; static int old_my = -1; // die icon leiste muss neu gezeichnet werden wenn der // Mauszeiger darueber schwebt #endif static unsigned char dr_fonttab[2048]; /* Unser Zeichensatz sitzt hier */ /* * pixel value type, RGB 555 */ typedef unsigned short PIXVAL; struct imd { int y; // offset top int h; // image height int len; // data length in entities of PIXVAL size PIXVAL * data; // iamge data }; static int anz_images = 0; static int disp_width = 640; static int disp_height = 480; /** * Tile size in pixels * * @author Hj. Malthaner */ static int tile_size = 32; static struct imd *images = NULL; static struct imd *images1 = NULL; static struct imd *images2 = NULL; static PIXVAL *textur = NULL; static struct clip_dimension clip_rect; // dirty tile management strcutures #define DIRTY_TILE_SIZE 32 #define DIRTY_TILE_SHIFT 5 static char *tile_dirty = NULL; static char *tile_dirty_old = NULL; static int tiles_per_line = 0; static int tile_lines = 0; // colormap management structures static unsigned char day_pal[256*3]; static unsigned char night_pal[256*3]; static unsigned char base_pal[256*3]; static int light_level = 0; static int color_level = 1; // ------------ procedure like defines -------------------- #define mark_tile_dirty(x,y) tile_dirty[(x) + (y)*tiles_per_line] = 1 #define is_tile_dirty(x,y) ((tile_dirty[(x) + (y)*tiles_per_line]) || (tile_dirty_old[(x) + (y)*tiles_per_line]) ) // ----------------- procedures --------------------------- /** * Markiert ein Tile as schmutzig, ohne Clipping * @author Hj. Malthaner */ static void mark_rect_dirty_nc(int x1, int y1, int x2, int y2) { int x, y; // floor to tile size x1 >>= DIRTY_TILE_SHIFT; y1 >>= DIRTY_TILE_SHIFT; x2 >>= DIRTY_TILE_SHIFT; y2 >>= DIRTY_TILE_SHIFT; for(y=y1; y<=y2; y++) { for(x=x1; x<=x2; x++) { mark_tile_dirty(x, y); } } } /** * Markiert ein Tile as schmutzig, mit Clipping * @author Hj. Malthaner */ void mark_rect_dirty_wc(int x1, int y1, int x2, int y2) { if(x1 < 0) x1 = 0; if(x1 >= disp_width) x1 = disp_width-1; if(y1 < 0) y1 = 0; if(y1 >= disp_height) y1 = disp_height-1; if(x2 < 0) x2 = 0; if(x2 >= disp_width) x2 = disp_width-1; if(y2 < 0) y2 = 0; if(y2 >= disp_height) y2 = disp_height-1; mark_rect_dirty_nc(x1, y1, x2, y2); } /** * Clipped einen Wert in Intervall * @author Hj. Malthaner */ static int clip(int w, int u, int o) { return w < u ? u : w > o ? o : w; } /** * Laedt den Font * @author Hj. Malthaner */ static void init_font() { FILE *f = NULL; // suche in ./draw.fnt if(f==NULL ) { f=fopen("./draw.fnt","rb"); } if(f==NULL) { printf("Cannot open draw.fnt!\n"); exit(1); } else { int i; for(i=0;i<2048;i++) dr_fonttab[i]=getc(f); fclose(f); } } /** * Laedt die Palette * @author Hj. Malthaner */ static int load_palette(const char *fname, unsigned char *palette) { FILE *file = fopen(fname,"rb"); if(file) { int x; int anzahl=256; int r,g,b; fscanf(file, "%d", &anzahl); for(x=0; x 3) { color_level = 3; } switch(color_level) { case 0: load_palette("./simud70.pal", day_pal); load_palette("./simun70.pal", night_pal); break; case 1: load_palette("./simud80.pal", day_pal); load_palette("./simun80.pal", night_pal); break; case 2: load_palette("./simud90.pal", day_pal); load_palette("./simun90.pal", night_pal); break; case 3: load_palette("./simud100.pal", day_pal); load_palette("./simun100.pal", night_pal); break; } memcpy(base_pal, day_pal, 256*3); display_set_light(display_get_light()); } static int night_shift = -1; static void calc_base_pal_from_night_shift(const int night) { const int day = 4 - night; int i; for(i=0; i<256; i++) { base_pal[i*3+0] = (day_pal[i*3+0] * day + night_pal[i*3+0] * night) >> 2; base_pal[i*3+1] = (day_pal[i*3+1] * day + night_pal[i*3+1] * night) >> 2; base_pal[i*3+2] = (day_pal[i*3+2] * day + night_pal[i*3+2] * night) >> 2; } } void display_day_night_shift(int night) { if(night != night_shift) { night_shift = night; calc_base_pal_from_night_shift(night); display_set_light(light_level); mark_rect_dirty_nc(0,0, disp_width-1, disp_height-1); } } /** * Setzt Farbeintrag * @author Hj. Malthaner */ void display_set_player_colors(const unsigned char *day, const unsigned char *night) { memcpy(day_pal, day, 12); memcpy(night_pal, night, 12); calc_base_pal_from_night_shift(night_shift); display_set_light(light_level); mark_rect_dirty_nc(0,0, disp_width-1, disp_height-1); } /** * Liest 32Bit wert Plattfromunabhängig * @author Hj. Malthaner */ static int fread_int(FILE *f) { int i = 0; i += fgetc(f); i += fgetc(f) << 8; i += fgetc(f) << 16; i += fgetc(f) << 24; return i; } /** * Laedt daten.pak * @author Hj. Malthaner */ static struct imd * init_images(const char *filename) { FILE *f = fopen(filename, "rb"); struct imd * images = NULL; if( f ) { int i; anz_images = fread_int(f); images = malloc(sizeof(struct imd)*anz_images); for(i=0; i 0) { images[i].data = malloc(images[i].len*sizeof(PIXVAL)); fread(images[i].data, images[i].len*sizeof(PIXVAL), 1, f); } else { images[i].data = NULL; } } fclose(f); } else { printf("Kann '%s' nicht lesen.\n", filename); exit(1); } return images; } /** * Holt Maus X-Position * @author Hj. Malthaner */ int gib_maus_x() { return mx; } /** * Holt Maus y-Position * @author Hj. Malthaner */ int gib_maus_y() { return my; } /** * this sets width < 0 if the range is out of bounds * so check the value after calling and before displaying * @author Hj. Malthaner */ static int clip_wh(int *x, int *width, const int min_width, const int max_width) { // doesnt check "wider than image" case if(*x < min_width) { const int xoff = min_width - (*x); *width += *x; *x = min_width; return xoff; } else if(*x + *width >= max_width) { *width = max_width - *x; } return 0; } /** * Ermittelt Clipping Rechteck * @author Hj. Malthaner */ struct clip_dimension display_gib_clip_wh(void) { return clip_rect; } /** * Setzt Clipping Rechteck * @author Hj. Malthaner */ void display_setze_clip_wh(int x, int y, int w, int h) { clip_wh(&x, &w, 0, disp_width); clip_wh(&y, &h, 0, disp_height); clip_rect.x = x; clip_rect.y = y; clip_rect.w = w; clip_rect.h = h; clip_rect.xx = x+w-1; clip_rect.yy = y+h-1; } // ----------------- basic painting procedures ---------------- /** * Kopiert Pixel von src nach dest * @author Hj. Malthaner */ static void pixcopy(PIXVAL *dest, const PIXVAL *src, int len) { memcpy(dest, src, len*sizeof(PIXVAL)); } /** * Zeichnet Bild mit Clipping * @author Hj. Malthaner */ static void display_img_wc(const int n, const int xp, const int yp, const int dirty) { if(n >= 0 && n < anz_images) { int h = images[n].h; int y = yp + images[n].y; int yoff = clip_wh(&y, &h, 0, clip_rect.yy); if(h > 0) { const int width = disp_width; const PIXVAL *sp = images[n].data; PIXVAL *tp = textur + y*width; if(dirty) { mark_rect_dirty_wc(xp, y, xp+tile_size-1, y+h+1); } // oben clippen while(yoff) { yoff --; do { if(*(++sp)) { sp += *sp + 1; } } while(*sp); sp ++; } do { // zeilen dekodieren int xpos = xp; // bild darstellen int runlen = *sp++; do { // wir starten mit einem clear run xpos += runlen; // jetzt kommen farbige pixel runlen = *sp++; if(runlen) { if(xpos >= 0 && runlen+xpos < width) { // pixcopy(tp+xpos, sp, runlen); memcpy(tp+xpos, sp, runlen*sizeof(PIXVAL)); } else if(xpos < 0) { if(runlen+xpos > 0) { // pixcopy(tp, sp-xpos, runlen+xpos); memcpy(tp, sp-xpos, (runlen+xpos)*sizeof(PIXVAL)); } } else if(width > xpos) { // pixcopy(tp+xpos, sp, width-xpos); memcpy(tp+xpos, sp, (width-xpos)*sizeof(PIXVAL)); } sp += runlen; xpos += runlen; runlen = *sp ++; } } while(runlen); tp += width; } while(--h > 0); } } } /** * Zeichnet Bild ohne Clipping * @author Hj. Malthaner */ static void display_img_nc(const int n, const int xp, const int yp, const int dirty) { if(n >= 0 && n < anz_images) { int h = images[n].h; if(h > 0) { const PIXVAL *sp = images[n].data; PIXVAL *tp = textur + (yp + images[n].y)*disp_width + xp; if(dirty) { mark_rect_dirty_nc(xp, yp+images[n].y, xp+tile_size-1, yp+images[n].y+h-1); } do { // zeilen dekodieren // bild darstellen int runlen = *sp++; do { // wir starten mit einem clear run tp += runlen; // jetzt kommen farbige pixel runlen = *sp++; if(runlen) { // pixcopy(tp, sp, runlen); memcpy(tp, sp, runlen*sizeof(PIXVAL)); sp += runlen; tp += runlen; runlen = *sp++; } } while(runlen); tp += (disp_width-tile_size); } while(--h); } } } /** * Zeichnet Bild * @author Hj. Malthaner */ void display_img(const int n, const int xp, const int yp, const int dirty) { if(xp>=0 && yp>=0 && xp < disp_width-tile_size-1 && yp < disp_height-tile_size-1) { display_img_nc(n, xp, yp, dirty); } else { if(xp>-tile_size && yp>-tile_size && xp < disp_width && yp < disp_height) { display_img_wc(n, xp, yp, dirty); } } } /** * Copies and shades colors * @param shade the amount to darken the color * @author Hj. Malthaner */ static void colorpixcpy(PIXVAL *dest, const PIXVAL *src, const PIXVAL * const end, const PIXVAL shade) { while(src < end) { *dest++ = darken(shade, *src++); } } /** * Zeichnet Bild, ersetzt Spielerfarben * @author Hj. Malthaner */ static void display_color_img_aux(const int n, const int xp, const int yp, const int color, const int dirty) { if(n >= 0 && n < anz_images) { int h = images[n].h; int y = yp + images[n].y; int yoff = clip_wh(&y, &h, 0, clip_rect.yy); if(h > 0) { const int width = disp_width; const PIXVAL *sp = images[n].data; PIXVAL *tp = textur + y*width; // printf("textur = %p tp = %p\n", textur, tp); if(dirty) { mark_rect_dirty_wc(xp, y+yoff, xp+tile_size-1, y+yoff+h-1); } // oben clippen while(yoff) { yoff --; do { if(*(++sp)) { sp += *sp + 1; } } while(*sp); sp ++; } do { // zeilen dekodieren int xpos = xp; // bild darstellen do { // wir starten mit einem clear run xpos += *sp ++; // jetzt kommen farbige pixel if(*sp) { const int runlen = *sp++; if(xpos >= 0 && runlen+xpos < width) { colorpixcpy(tp+xpos, sp, sp+runlen, color); } else if(xpos < 0) { if(runlen+xpos > 0) { colorpixcpy(tp, sp-xpos, sp+runlen, color); } } else if(width > xpos) { colorpixcpy(tp+xpos, sp, sp+width-xpos, color); } sp += runlen; xpos += runlen; } } while(*sp); tp += width; sp ++; } while(--h); } } } /** * Zeichnet Bild, ersetzt Farben * @author Hj. Malthaner */ void display_color_img(const int n, const int xp, const int yp, const int color, const int dirty) { // since the colors for player 0 are already right, // only use the expensive replacement routine for colored images // of other players // printf("color=%d\n", color); if(color) { display_color_img_aux(n, xp, yp, color, dirty); } else { display_img_wc(n, xp, yp, dirty); } } /** * Zeichnet ein Pixel * @author Hj. Malthaner */ void display_pixel(int x, int y, const int color) { if(x >= clip_rect.x && x<=clip_rect.xx && y >= clip_rect.y && y> DIRTY_TILE_SHIFT, y >> DIRTY_TILE_SHIFT); } } /** * Zeichnet einen Text, lowlevel Funktion * @author Hj. Malthaner */ static void dr_textur_text(PIXVAL *textur,int x,int y,const char *txt, const int chars, const int fgpen, const int dirty) { int p; y+=4; /* Korektu amiga <-> pc */ if(y < 0 || y+8 >= disp_height) return; /* out of clip */ if(dirty) { mark_rect_dirty_nc(x, y, x+chars*8-1, y+8-1); } for(p=0; p> b)) { textur[screen_pos+b] = fgpen; } } screen_pos += disp_width; }while(base < end); } } /** * Zeichnet Text, highlevel Funktion * @author Hj. Malthaner */ void display_text(int x, int y, const char *txt, const int color, int dirty) { const int chars = strlen(txt); const int text_width = chars*8; if(y >= 8 && y < disp_height-12) { if(x >= 0 && x+text_width < disp_width) { dr_textur_text(textur, x, y, txt, chars, color, dirty); } else { if(x < 0 && x+text_width > 8) { const int left_chars = (-x+7)/8; dr_textur_text(textur, (x & 7), y, txt+left_chars, chars-left_chars, color, dirty); } else if(x > 0 && x < disp_width-7) { const int rest_chars = (disp_width-x-1) / 8; dr_textur_text(textur, x, y, txt, rest_chars, color, dirty); } } } } /** * Zeichnet gefuelltes Rechteck, ohne clipping * @author Hj. Malthaner */ void display_fb_internal(int xp, int yp, int w, int h, const int color, const int dirty, int cL, int cR, int cT, int cB) { clip_wh(&xp, &w, cL, cR); clip_wh(&yp, &h, cT, cB); if(w > 0 && h > 0) { PIXVAL *p = textur + xp + yp*disp_width; if(dirty) { mark_rect_dirty_nc(xp, yp, xp+w-1, yp+h-1); } do { memset(p, color, w*sizeof(PIXVAL)); p += disp_width; } while(--h); } } void display_fillbox_wh(int xp, int yp, int w, int h, const int color, const int dirty) { display_fb_internal(xp,yp,w,h,color,dirty, 0,disp_width-1,0,disp_height-1); } void display_fillbox_wh_clip(int xp, int yp, int w, int h, const int color, const int dirty) { display_fb_internal(xp,yp,w,h,color,dirty, clip_rect.x, clip_rect.xx, clip_rect.y, clip_rect.yy); } /** * Zeichnet vertikale Linie * @author Hj. Malthaner */ void display_vl_internal(const int xp, int yp, int h, const int color, int dirty, int cL, int cR, int cT, int cB) { clip_wh(&yp, &h, cT, cB); if(xp >= cL && xp <= cR && h > 0) { PIXVAL *p = textur + xp + yp*disp_width; if (dirty) { mark_rect_dirty_nc(xp, yp, xp, yp+h-1); } do { *p = color; p += disp_width; } while(--h); } } void display_vline_wh(int xp, int yp, int h, const int color, const int dirty) { display_vl_internal(xp,yp,h,color,dirty, 0,disp_width-1,0,disp_height-1); } void display_vline_wh_clip(int xp, int yp, int h, const int color, const int dirty) { display_vl_internal(xp,yp,h,color,dirty, clip_rect.x, clip_rect.xx, clip_rect.y, clip_rect.yy); } /** * Zeichnet rohe Pixeldaten * @author Hj. Malthaner */ void display_array_wh(int xp, int yp, int w, int h, const unsigned char *arr) { const int arr_w = w; clip_wh(&xp, &w, 0, disp_width); clip_wh(&yp, &h, 0, disp_height); if(w > 0 && h > 0) { PIXVAL *p = textur + xp + yp*disp_width; mark_rect_dirty_nc(xp, yp, xp+w-1, yp+h-1); if(xp == 0) { arr += arr_w - w; } do { // FIXME!!! memcpy(p, arr, w); p += disp_width; arr += arr_w; } while(--h); } } // --------------- compound painting procedures --------------- /** * Zeichnet schattiertes Rechteck * @author Hj. Malthaner */ void display_ddd_box(int x1, int y1, int w, int h, int tl_color, int rd_color) { display_fillbox_wh(x1, y1, w, 1, tl_color, TRUE); display_fillbox_wh(x1, y1+h-1, w, 1, rd_color, TRUE); h-=2; display_vline_wh(x1, y1+1, h, tl_color, TRUE); display_vline_wh(x1+w-1, y1+1, h, rd_color, TRUE); } /** * Zeichnet schattierten Text * @author Hj. Malthaner */ void display_ddd_text(int xpos, int ypos, int hgt, int ddd_farbe, int text_farbe, const char *text, int dirty) { const int len = strlen(text)*4; display_fillbox_wh(xpos-2-len, ypos-hgt-6, 4 + len*2, 1, ddd_farbe+1, dirty); display_fillbox_wh(xpos-2-len, ypos-hgt-5, 4 + len*2, 8, ddd_farbe, dirty); display_fillbox_wh(xpos-2-len, ypos-hgt+3, 4 + len*2, 1, ddd_farbe-1, dirty); display_text(xpos - len, ypos-hgt-9, text, text_farbe, dirty); } /** * Zaehlt Vorkommen eines Buchstabens in einem String * @author Hj. Malthaner */ int count_char(const char *str, const char c) { int count = 0; while(*str) { count += (*str++ == c); } return count; } /** * Zeichnet einen mehrzeiligen Text * @author Hj. Malthaner */ void display_multiline_text(int x, int y, const char *inbuf, int color) { char tmp[4096]; char *buf = tmp; char *next; int y_off = 0; // be sure not to copy more than buffer size strncpy(buf, inbuf, 4095); // always close with a 0 byte buf[4095] = 0; while( (*buf != 0) && (next = strchr(buf,'\n')) ) { *next = 0; display_text(x,y+y_off, buf, color, TRUE); buf = next+1; y_off += LINESPACE; } } /** * Loescht den Bildschirm * @author Hj. Malthaner */ void display_clear() { memset(textur+32*disp_width, 32, disp_width*(disp_height-17-32)); mark_rect_dirty_nc(0, 0, disp_width-1, disp_height-1); } #if 0 void display_flush_buffer() { int x, y; char * tmp; #ifdef USE_SOFTPOINTER display_img(softpointer, mx, my, TRUE); old_my = my; #endif #ifdef DEBUG // just for debugging int tile_count = 0; #endif for(y=0; y