--- deliantra/Deliantra-Client/Client.xs 2007/04/06 08:09:42 1.175 +++ deliantra/Deliantra-Client/Client.xs 2007/04/10 09:39:48 1.176 @@ -165,13 +165,15 @@ int w, h; float s, t; uint8_t r, g, b, a; + tileid smoothtile; + uint8_t smoothlevel; } maptex; typedef struct { uint32_t player; tileid tile[3]; uint16_t darkness; - uint8_t stat_width, stat_hp, flags; + uint8_t stat_width, stat_hp, flags, smoothmax; } mapcell; typedef struct { @@ -337,6 +339,22 @@ } } +typedef struct { + tileid tile; + uint8_t x, y, level; +} smooth_key; + +static void +smooth_or_bits (HV *hv, smooth_key *key, IV bits) +{ + SV **sv = hv_fetch (hv, (char *)key, sizeof (key), 1); + + if (SvIOK (*sv)) + SvIV_set (*sv, SvIVX (*sv) | bits); + else + sv_setiv (*sv, bits); +} + static void music_finished (void) { @@ -1297,6 +1315,29 @@ } void +set_smooth (CFPlus::Map self, int face, int smooth, int level) + CODE: +{ + tileid texid; + maptex *tex; + + if (face < 0 || face >= self->faces) + return; + + if (smooth < 0 || smooth >= self->faces) + return; + + texid = self->face2tile [face]; + + if (!texid) + return; + + tex = self->tex + texid; + tex->smoothtile = self->face2tile [smooth]; + tex->smoothlevel = level; +} + +void set_texture (CFPlus::Map self, int texid, int name, int w, int h, float s, float t, int r, int g, int b, int a) CODE: { @@ -1526,9 +1567,20 @@ draw (CFPlus::Map self, int mx, int my, int sw, int sh, int T) CODE: { + HV *smooth = (HV *)sv_2mortal ((SV *)newHV ()); + uint32_t smooth_level[256 / 32]; // one bit for every possible smooth level + uint8_t smooth_max[256][256]; + smooth_key skey; int x, y, z; int last_name; + // thats current max. sorry. + if (sw > 255) sw = 255; + if (sh > 255) sh = 255; + + // clear key, in case of extra padding + memset (&skey, 0, sizeof (skey)); + glColor4ub (255, 255, 255, 255); glEnable (GL_BLEND); @@ -1543,60 +1595,195 @@ mx += self->x; my += self->y; - for (z = 0; z < 3; z++) - for (y = 0; y < sh; y++) - if (0 <= y + my && y + my < self->rows) - { - maprow *row = self->row + (y + my); + // first pass: determine smooth_max + // rather ugly, if you ask me + // could also be stored inside mapcell and updated on change + memset (smooth_max, 0, sizeof (smooth_max)); - for (x = 0; x < sw; x++) - if (row->c0 <= x + mx && x + mx < row->c1) - { - mapcell *cell = row->col + (x + mx - row->c0); - tileid tile = cell->tile [z]; - - if (tile) - { - maptex tex = self->tex [tile]; - int px = (x + 1) * T - tex.w; - int py = (y + 1) * T - tex.h; + for (y = 0; y < sh; y++) + if (0 <= y + my && y + my < self->rows) + { + maprow *row = self->row + (y + my); - if (last_name != tex.name) - { - if (!tex.name) - tex = self->tex [2]; /* missing, replace by noface */ + for (x = 0; x < sw; x++) + if (row->c0 <= x + mx && x + mx < row->c1) + { + mapcell *cell = row->col + (x + mx - row->c0); - glEnd (); - glBindTexture (GL_TEXTURE_2D, last_name = tex.name); - glBegin (GL_QUADS); - } + for (z = 0; z <= 2; z++) + { + uint8_t level = self->tex [cell->tile [z]].smoothlevel; + if (level > smooth_max [x + 1][y + 1]) + smooth_max [x + 1][y + 1] = level; + } + } + } - glTexCoord2f (0 , 0 ); glVertex2f (px , py ); - glTexCoord2f (0 , tex.t); glVertex2f (px , py + tex.h); - glTexCoord2f (tex.s, tex.t); glVertex2f (px + tex.w, py + tex.h); - glTexCoord2f (tex.s, 0 ); glVertex2f (px + tex.w, py ); + for (z = 0; z <= 2; z++) + { + memset (smooth_level, 0, sizeof (smooth_level)); + + for (y = 0; y < sh; y++) + if (0 <= y + my && y + my < self->rows) + { + maprow *row = self->row + (y + my); - if (cell->flags && z == 2) + for (x = 0; x < sw; x++) + if (row->c0 <= x + mx && x + mx < row->c1) + { + mapcell *cell = row->col + (x + mx - row->c0); + tileid tile = cell->tile [z]; + + if (tile) + { + maptex tex = self->tex [tile]; + int px = (x + 1) * T - tex.w; + int py = (y + 1) * T - tex.h; + + // suppressing texture state switches here + // is only moderately effective, but worth the extra effort + if (last_name != tex.name) + { + if (!tex.name) + tex = self->tex [2]; /* missing, replace by noface */ + + glEnd (); + glBindTexture (GL_TEXTURE_2D, last_name = tex.name); + glBegin (GL_QUADS); + } + + glTexCoord2f (0 , 0 ); glVertex2f (px , py ); + glTexCoord2f (0 , tex.t); glVertex2f (px , py + tex.h); + glTexCoord2f (tex.s, tex.t); glVertex2f (px + tex.w, py + tex.h); + glTexCoord2f (tex.s, 0 ); glVertex2f (px + tex.w, py ); + + if (cell->flags && z == 2) + { + if (cell->flags & 1) + { + maptex tex = self->tex [1]; + int px = x * T + T * 2 / 32; + int py = y * T - T * 6 / 32; + + glEnd (); + glBindTexture (GL_TEXTURE_2D, last_name = tex.name); + glBegin (GL_QUADS); + + glTexCoord2f (0 , 0 ); glVertex2f (px , py ); + glTexCoord2f (0 , tex.t); glVertex2f (px , py + T); + glTexCoord2f (tex.s, tex.t); glVertex2f (px + T, py + T); + glTexCoord2f (tex.s, 0 ); glVertex2f (px + T, py ); + } + } + + // update smooth hash + if (tex.smoothtile) + { + skey.tile = tex.smoothtile; + skey.level = tex.smoothlevel; + + smooth_level [tex.smoothlevel >> 5] |= ((uint32_t)1) << (tex.smoothlevel & 31); + + // add bits to current tile and all neighbours. skey.x|y is + // shifted +1|+1 so we always stay positive. + + // full tile + skey.x = x + 1; skey.y = y + 1; smooth_or_bits (smooth, &skey, 0x1000); + + // borders + skey.x = x + 2; skey.y = y + 1; smooth_or_bits (smooth, &skey, 0x0031); + skey.x = x + 1; skey.y = y + 2; smooth_or_bits (smooth, &skey, 0x0092); + skey.x = x ; skey.y = y + 1; smooth_or_bits (smooth, &skey, 0x0064); + skey.x = x + 1; skey.y = y ; smooth_or_bits (smooth, &skey, 0x00c8); + + // corners + skey.x = x + 2; skey.y = y + 2; smooth_or_bits (smooth, &skey, 0x0100); + skey.x = x ; skey.y = y + 2; smooth_or_bits (smooth, &skey, 0x0200); + skey.x = x ; skey.y = y ; smooth_or_bits (smooth, &skey, 0x0400); + skey.x = x + 2; skey.y = y ; smooth_or_bits (smooth, &skey, 0x0800); + } + } + } + } + + // go through all smoothlevels, lowest to highest, then draw + // this is basically counting sort + { + int w, b; + + for (w = 0; w < 256 / 32; ++w) + { + uint32_t smask = smooth_level [w]; + if (smask) + for (b = 0; b < 32; ++b) + if (smask & (((uint32_t)1) << b)) + { + int level = (w << 5) | b; + HE *he; + + hv_iterinit (smooth); + while ((he = hv_iternext (smooth))) { - if (cell->flags & 1) + smooth_key *skey = (smooth_key *)HeKEY (he); + IV bits = SvIVX (HeVAL (he)); + + // bits is ___n cccc CCCC bbbb + // n do not draw borders&corners + // c draw these corners, but... + // C ... not these + // b draw these borders + + if (!(bits & 0x1000) + && skey->level == level + && level >= smooth_max [skey->x][skey->y]) { - maptex tex = self->tex [1]; - int px = x * T + T * 2 / 32; - int py = y * T - T * 6 / 32; - - glEnd (); - glBindTexture (GL_TEXTURE_2D, last_name = tex.name); - glBegin (GL_QUADS); - - glTexCoord2f (0 , 0 ); glVertex2f (px , py ); - glTexCoord2f (0 , tex.t); glVertex2f (px , py + T); - glTexCoord2f (tex.s, tex.t); glVertex2f (px + T, py + T); - glTexCoord2f (tex.s, 0 ); glVertex2f (px + T, py ); + maptex tex = self->tex [skey->tile]; + int px = (((int)skey->x) - 1) * T; + int py = (((int)skey->y) - 1) * T; + int border = bits & 15; + int corner = (bits >> 8) & ~(bits >> 4) & 15; + float dx = tex.s * .0625f; // 16 images/row + float dy = tex.t * .5f ; // 2 images/column + + // this time naively avoiding texture state changes + // save gobs of state changes. + if (last_name != tex.name) + { + if (!tex.name) + continue; // smoothing not yet available + + glEnd (); + glBindTexture (GL_TEXTURE_2D, last_name = tex.name); + glBegin (GL_QUADS); + } + + if (border) + { + float ox = border * dx; + + glTexCoord2f (ox , 0.f ); glVertex2f (px , py ); + glTexCoord2f (ox , dy ); glVertex2f (px , py + T); + glTexCoord2f (ox + dx, dy ); glVertex2f (px + T, py + T); + glTexCoord2f (ox + dx, 0.f ); glVertex2f (px + T, py ); + } + + if (corner) + { + float ox = corner * dx; + + glTexCoord2f (ox , dy ); glVertex2f (px , py ); + glTexCoord2f (ox , dy * 2.f); glVertex2f (px , py + T); + glTexCoord2f (ox + dx, dy * 2.f); glVertex2f (px + T, py + T); + glTexCoord2f (ox + dx, dy ); glVertex2f (px + T, py ); + } } } } - } - } + } + } + + hv_clear (smooth); + } glEnd ();