Update UDMFvis, now properly groups by sectors and sorts within them, and doesn't redraw already drawn lines. Default colorscheme was also changed for better visibility.

This commit is contained in:
Marisa the Magician 2018-09-04 17:02:51 +02:00
commit 250d73a03d

255
udmfvis.c
View file

@ -16,9 +16,7 @@
Things left to do: Things left to do:
- Figure out why output is bugged. - Figure out why output is bugged.
- Properly sort lines like dmvis. - Full-sector drawing option like dmvis.
- Full-sector drawing like dmvis.
- Skipping over already drawn lines like dmvis.
- Support doom/hexen map format and reading from wads. - Support doom/hexen map format and reading from wads.
- Rename project to "cdmvis" when the previous point is done. - Rename project to "cdmvis" when the previous point is done.
- Customizable colorschemes. - Customizable colorschemes.
@ -36,34 +34,67 @@ typedef struct
{ {
int v1, v2, type; int v1, v2, type;
int fs, bs; int fs, bs;
int done;
} line_t; } line_t;
typedef struct typedef struct
{ {
int s; int s, l;
int v1, v2, type; int v1, v2, type;
} side_t; } side_t;
typedef struct
{
int *s;
int ns;
} sector_t;
vertex_t *verts = 0; vertex_t *verts = 0;
line_t *lines = 0; line_t *lines = 0;
side_t *sides = 0; side_t *sides = 0;
int nverts = 0, nlines = 0, nsides = 0; sector_t *sectors = 0;
int nverts = 0, nlines = 0, nsides = 0, nsectors = 0;
#define LN_ONESIDED 0 #define LN_ONESIDED 0
#define LN_TWOSIDED 1 #define LN_TWOSIDED 1
#define LN_ASPECIAL 2 #define LN_ASPECIAL 2
int cmp_sides_by_vertex( const void *a, const void *b ) static void sort_sector( sector_t *s )
{ {
side_t *sa = (side_t*)a; // rearrange line indices in sector based on connected vertices
side_t *sb = (side_t*)b; // find initial line (lowest v1)
return sa->v2 - sb->v1; int lowest = nverts;
} int lowests = -1;
int cmp_sides_by_sector( const void *a, const void *b ) for ( int i=0; i<s->ns; i++ )
{ {
side_t *sa = (side_t*)a; int ss = s->s[i];
side_t *sb = (side_t*)b; if ( sides[ss].v1 < lowest )
return sa->s - sb->s; {
lowest = sides[ss].v1;
lowests = ss;
}
}
// allocate new sides array
int *news = malloc(sizeof(int)*s->ns);
// place lowest first
news[0] = lowests;
// loop until filling
for ( int i=0; i<(s->ns-1); i++ )
{
// search for next connected line (v2 equal to current v1)
int next = -1;
for ( int j=0; j<s->ns; j++ )
{
if ( s->s[j] == news[i] ) continue; // skip self
if ( sides[s->s[j]].v1 != sides[news[i]].v2 ) continue;
next = s->s[j];
break;
}
news[i+1] = next;
}
// delete old array and replace with this
free(s->s);
s->s = news;
} }
#define LAST_NONE 0 #define LAST_NONE 0
@ -113,37 +144,37 @@ static void bresenham( int col, int x0, int y0, int x1, int y1 )
} }
} }
void getsidebounds( int i, int *x, int *y, int *w, int *h ) void getsidebounds( int i, int prev, int *x, int *y, int *w, int *h )
{ {
int minx = 0, miny = 0, maxx = 0, maxy = 0; int minx = 0, miny = 0, maxx = 0, maxy = 0;
if ( i < nsides ) if ( i != -1 )
{ {
if ( i > 0 ) if ( prev != -1 )
{ {
minx = verts[sides[i-1].v1].x; minx = verts[sides[prev].v1].x;
if ( verts[sides[i-1].v2].x < minx ) if ( verts[sides[prev].v2].x < minx )
minx = verts[sides[i-1].v2].x; minx = verts[sides[prev].v2].x;
if ( verts[sides[i].v1].x < minx ) if ( verts[sides[i].v1].x < minx )
minx = verts[sides[i].v1].x; minx = verts[sides[i].v1].x;
if ( verts[sides[i].v2].x < minx ) if ( verts[sides[i].v2].x < minx )
minx = verts[sides[i].v2].x; minx = verts[sides[i].v2].x;
maxx = verts[sides[i-1].v1].x; maxx = verts[sides[prev].v1].x;
if ( verts[sides[i-1].v2].x > maxx ) if ( verts[sides[prev].v2].x > maxx )
maxx = verts[sides[i-1].v2].x; maxx = verts[sides[prev].v2].x;
if ( verts[sides[i].v1].x > maxx ) if ( verts[sides[i].v1].x > maxx )
maxx = verts[sides[i].v1].x; maxx = verts[sides[i].v1].x;
if ( verts[sides[i].v2].x > maxx ) if ( verts[sides[i].v2].x > maxx )
maxx = verts[sides[i].v2].x; maxx = verts[sides[i].v2].x;
miny = verts[sides[i-1].v1].y; miny = verts[sides[prev].v1].y;
if ( verts[sides[i-1].v2].y < miny ) if ( verts[sides[prev].v2].y < miny )
miny = verts[sides[i-1].v2].y; miny = verts[sides[prev].v2].y;
if ( verts[sides[i].v1].y < miny ) if ( verts[sides[i].v1].y < miny )
miny = verts[sides[i].v1].y; miny = verts[sides[i].v1].y;
if ( verts[sides[i].v2].y < miny ) if ( verts[sides[i].v2].y < miny )
miny = verts[sides[i].v2].y; miny = verts[sides[i].v2].y;
maxy = verts[sides[i-1].v1].y; maxy = verts[sides[prev].v1].y;
if ( verts[sides[i-1].v2].y > maxy ) if ( verts[sides[prev].v2].y > maxy )
maxy = verts[sides[i-1].v2].y; maxy = verts[sides[prev].v2].y;
if ( verts[sides[i].v1].y > maxy ) if ( verts[sides[i].v1].y > maxy )
maxy = verts[sides[i].v1].y; maxy = verts[sides[i].v1].y;
if ( verts[sides[i].v2].y > maxy ) if ( verts[sides[i].v2].y > maxy )
@ -165,20 +196,20 @@ void getsidebounds( int i, int *x, int *y, int *w, int *h )
maxy = verts[sides[i].v2].y; maxy = verts[sides[i].v2].y;
} }
} }
else if ( i > 0 ) else if ( prev != -1 )
{ {
minx = verts[sides[i-1].v1].x; minx = verts[sides[prev].v1].x;
if ( verts[sides[i-1].v2].x < minx ) if ( verts[sides[prev].v2].x < minx )
minx = verts[sides[i-1].v2].x; minx = verts[sides[prev].v2].x;
maxx = verts[sides[i-1].v1].x; maxx = verts[sides[prev].v1].x;
if ( verts[sides[i-1].v2].x > maxx ) if ( verts[sides[prev].v2].x > maxx )
maxx = verts[sides[i-1].v2].x; maxx = verts[sides[prev].v2].x;
miny = verts[sides[i-1].v1].y; miny = verts[sides[prev].v1].y;
if ( verts[sides[i-1].v2].y < miny ) if ( verts[sides[prev].v2].y < miny )
miny = verts[sides[i-1].v2].y; miny = verts[sides[prev].v2].y;
maxy = verts[sides[i-1].v1].y; maxy = verts[sides[prev].v1].y;
if ( verts[sides[i-1].v2].y > maxy ) if ( verts[sides[prev].v2].y > maxy )
maxy = verts[sides[i-1].v2].y; maxy = verts[sides[prev].v2].y;
} }
*x = minx; *x = minx;
*y = miny; *y = miny;
@ -186,32 +217,33 @@ void getsidebounds( int i, int *x, int *y, int *w, int *h )
*h = (maxy-miny) + 1; *h = (maxy-miny) + 1;
} }
void drawside( int i ) void drawside( int i, int prev )
{ {
// draw previous side normally (if any) // draw previous side normally (if any)
if ( i > 0 ) if ( prev >= 0 )
{ {
int c = 3; int c = 3;
if ( sides[i-1].type&LN_ASPECIAL ) c = 2; if ( sides[prev].type&LN_ASPECIAL ) c = 2;
else if ( sides[i-1].type&LN_TWOSIDED ) c = 4; else if ( sides[prev].type&LN_TWOSIDED ) c = 4;
bresenham(c,verts[sides[i-1].v1].x,verts[sides[i-1].v1].y, bresenham(c,verts[sides[prev].v1].x,verts[sides[prev].v1].y,
verts[sides[i-1].v2].x,verts[sides[i-1].v2].y); verts[sides[prev].v2].x,verts[sides[prev].v2].y);
} }
// draw current line highlighted (if any) // draw current line highlighted (if any)
if ( i < nsides ) if ( i >= 0 )
{ {
bresenham(1,verts[sides[i].v1].x,verts[sides[i].v1].y, bresenham(1,verts[sides[i].v1].x,verts[sides[i].v1].y,
verts[sides[i].v2].x,verts[sides[i].v2].y); verts[sides[i].v2].x,verts[sides[i].v2].y);
lines[sides[i].l].done = 1;
} }
} }
uint8_t gifpal[8][3] = uint8_t gifpal[8][3] =
{ {
{255,255,255}, // background {0,0,0}, // background
{220,0,0}, // cursor {0,255,0}, // cursor
{220,130,50}, // special line {255,255,0}, // special line
{0,0,0}, // one-sided line {255,0,0}, // one-sided line
{144,144,144}, // two-sided line {64,64,64}, // two-sided line
{0,0,0}, {0,0,0},
{0,0,0}, {0,0,0},
{0,0,0}, {0,0,0},
@ -354,6 +386,39 @@ void writelzw( uint8_t *data, int siz, int bits, FILE *f )
drainlzw(f,buffer,1); drainlzw(f,buffer,1);
} }
void writeframe( int side, int prev, FILE *gif )
{
int delay = (side!=-1)?2:3000;
uint8_t gce[] =
{
0x21,0xf9,0x04,0x05,
delay&0xff,delay>>8,
0x00,0x00,
};
fwrite(gce,sizeof(gce),1,gif);
/* calculate bounds */
int x, y, w, h;
getsidebounds(side,prev,&x,&y,&w,&h);
bx = x;
by = y;
bpitch = w;
uint8_t ihead[] =
{
0x2c,
x&0xff,x>>8,
y&0xff,y>>8,
w&0xff,w>>8,
h&0xff,h>>8,
0x00,
};
fwrite(ihead,sizeof(ihead),1,gif);
/* render */
memset(b,0,w*h);
drawside(side,prev);
/* write frame */
writelzw(b,w*h,3,gif);
}
void mkanim( int imgw ) void mkanim( int imgw )
{ {
/* calculate bounds, adjust scaling */ /* calculate bounds, adjust scaling */
@ -430,39 +495,21 @@ void mkanim( int imgw )
}; };
fwrite(ihead,sizeof(ihead),1,gif); fwrite(ihead,sizeof(ihead),1,gif);
writelzw(b,imgw*imgh,3,gif); writelzw(b,imgw*imgh,3,gif);
for ( int i=0; i<=nsides; i++ ) int k = 0;
int prev = -1;
for ( int i=0; i<nsectors; i++ )
{ {
printf("\rwriting frame %d of %d",i,nsides); for ( int j=0; j<sectors[i].ns; j++ )
int delay = (i<nsides)?2:3000;
uint8_t gce[] =
{ {
0x21,0xf9,0x04,0x05, printf("\r progress: %d / %d",k,nsides);
delay&0xff,delay>>8, k++;
0x00,0x00, if ( lines[sides[sectors[i].s[j]].l].done ) continue;
}; writeframe(sectors[i].s[j],prev,gif);
fwrite(gce,sizeof(gce),1,gif); prev = sectors[i].s[j];
/* calculate bounds */ }
int x, y, w, h;
getsidebounds(i,&x,&y,&w,&h);
bx = x;
by = y;
bpitch = w;
uint8_t ihead[] =
{
0x2c,
x&0xff,x>>8,
y&0xff,y>>8,
w&0xff,w>>8,
h&0xff,h>>8,
0x00,
};
fwrite(ihead,sizeof(ihead),1,gif);
/* render */
memset(b,0,w*h);
drawside(i);
/* write frame */
writelzw(b,w*h,3,gif);
} }
printf("\r progress: %d / %d",k,nsides);
writeframe(-1,prev,gif);
free(b); free(b);
putchar('\n'); putchar('\n');
fputc(0x3b,gif); fputc(0x3b,gif);
@ -490,6 +537,12 @@ void process_startblock( char *blk )
else sides = realloc(sides,sizeof(side_t)*(nsides+1)); else sides = realloc(sides,sizeof(side_t)*(nsides+1));
memset(sides+nsides,0,sizeof(side_t)); memset(sides+nsides,0,sizeof(side_t));
} }
else if ( !strcasecmp(blk,"sector") )
{
if ( !sectors ) sectors = malloc(sizeof(sector_t));
else sectors = realloc(sectors,sizeof(sector_t)*(nsectors+1));
memset(sectors+nsectors,0,sizeof(sector_t));
}
} }
void process_assignment( char *blk, char *id, char *val ) void process_assignment( char *blk, char *id, char *val )
@ -527,17 +580,13 @@ void process_assignment( char *blk, char *id, char *val )
void process_endblock( char *blk ) void process_endblock( char *blk )
{ {
if ( !strcasecmp(blk,"vertex") ) if ( !strcasecmp(blk,"vertex") )
{
nverts++; nverts++;
}
else if ( !strcasecmp(blk,"linedef") ) else if ( !strcasecmp(blk,"linedef") )
{
nlines++; nlines++;
}
else if ( !strcasecmp(blk,"sidedef") ) else if ( !strcasecmp(blk,"sidedef") )
{
nsides++; nsides++;
} else if ( !strcasecmp(blk,"sector") )
nsectors++;
} }
int main( int argc, char **argv ) int main( int argc, char **argv )
@ -629,16 +678,27 @@ int main( int argc, char **argv )
sides[lines[i].fs].v1 = lines[i].v1; sides[lines[i].fs].v1 = lines[i].v1;
sides[lines[i].fs].v2 = lines[i].v2; sides[lines[i].fs].v2 = lines[i].v2;
sides[lines[i].fs].type = lines[i].type; sides[lines[i].fs].type = lines[i].type;
sides[lines[i].fs].l = i;
if ( lines[i].bs != -1 ) if ( lines[i].bs != -1 )
{ {
sides[lines[i].bs].v1 = lines[i].v2; sides[lines[i].bs].v1 = lines[i].v2;
sides[lines[i].bs].v2 = lines[i].v1; sides[lines[i].bs].v2 = lines[i].v1;
sides[lines[i].bs].type = lines[i].type; sides[lines[i].bs].type = lines[i].type;
sides[lines[i].bs].l = i;
} }
} }
/* sort sides */ /* populate sectors */
qsort(sides,nsides,sizeof(side_t),cmp_sides_by_vertex); for ( int i=0; i<nsides; i++ )
qsort(sides,nsides,sizeof(side_t),cmp_sides_by_sector); {
int s = sides[i].s;
if ( !sectors[s].s ) sectors[s].s = malloc(sizeof(int));
else sectors[s].s = realloc(sectors[s].s,sizeof(int)
*(sectors[s].ns+1));
sectors[s].s[sectors[s].ns++] = i;
}
/* sort sides on each sector */
for ( int i=0; i<nsectors; i++ ) sort_sector(sectors+i);
/* TODO sort sectors based on shared vertices */
/* do the animation! */ /* do the animation! */
int iw = 1024; int iw = 1024;
if ( argc > 2 ) sscanf(argv[2],"%d",&iw); if ( argc > 2 ) sscanf(argv[2],"%d",&iw);
@ -646,5 +706,12 @@ int main( int argc, char **argv )
if ( verts ) free(verts); if ( verts ) free(verts);
if ( lines ) free(lines); if ( lines ) free(lines);
if ( sides ) free(sides); if ( sides ) free(sides);
if ( sectors )
{
for ( int i=0; i<nsectors; i++ )
if ( sectors[i].s )
free(sectors[i].s);
free(sectors);
}
return 0; return 0;
} }