ref: a1c6005292984c6c2c6327cf4f6dfa1b5b2e72cb
parent: f6b64cc388c12bb270d6804e0f712026be366f6c
author: qwx <[email protected]>
date: Thu Aug 27 18:08:00 EDT 2020
fs: improve image files format, storage and handling individual images were stores as individual files as compressed plan 9 images. this results in a combinatorial explosion of files any time a unit is added. one unit typically has 8 team colors, 32 rotations, shadows for each rotation, and potentially multiple frames, meaning even more rotations and shadows. so, for command center, hatchery, scv and drones, with just idle and movement sprites, we exceed 1700 files. an obvious additional consequence is ridiculous loading times at startup. units may have 32 rotations internally, but they actually only have 17 sprites: 8 images [0°,180°[ duplicated once, a single 180° sprite, 7 mirrored images ]180°, and another unique sprite for the 32nd rotation. in addition, images for each of the 8 teams all have the same dimensions and offsets. it would still be nice to have to avoid having special formats or more metadata, so to avoid dealing with offsets, we don't bundle shadow sprites. shadows also may have different dimensions. however, we can bundle team colors and only store 17 rotations, selecting the correct one in the code. we can also concatenate tileset images, all 32x32. this reduces the current file count to about 210. bundling images together in compressed bitmaps reduces space somewhat. it would still be nice to find something better since we'll reach the same problem when we add more and more units and frames.
--- a/dat.h
+++ b/dat.h
@@ -63,9 +63,9 @@
};
enum{
- PFterrain = 0,
- PFidle = 1<<0,
- PFmove = 2<<0,
+ PFterrain = 1<<0,
+ PFidle = 1<<1,
+ PFmove = 1<<2,
PFshadow = 1<<15,
};
struct Pic{
@@ -76,7 +76,7 @@
int dy;
};
struct Pics{
- Pic **p;
+ Pic **pic;
Pic **shadow;
int nf;
int nr;
--- a/drw.c
+++ b/drw.c
@@ -261,16 +261,26 @@
static Pic *
frm(Mobj *mo, int notshadow)
{
+ static int rot17[Nrot] = {
+ 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,
+ 9,9,10,10,11,11,12,12,13,13,14,14,15,15,16
+ };
+ int θ;
Pics *pp;
Pic *p;
pp = mo->pics;
+ switch(pp->nr){
+ case 17: θ = rot17[mo->θ]; break;
+ default: θ = 0; break;
+ }
if(notshadow){
- p = pp->p[tc % pp->nf];
- p += pp->nr * (mo->team-1);
- }else
+ p = pp->pic[tc % pp->nf];
+ p += nteam * θ + mo->team - 1;
+ }else{
p = pp->shadow[tc % pp->nf];
- p += mo->θ / (Nrot / pp->nr);
+ p += θ;
+ }
return p;
}
--- a/fs.c
+++ b/fs.c
@@ -26,7 +26,7 @@
int row;
};
struct Picl{
- int id;
+ int frm;
int type;
char *name;
char iname[64];
@@ -40,6 +40,7 @@
Terrainl *l;
};
static Terrainl terrainl0 = {.l = &terrainl0}, *terrainl = &terrainl0;
+static Pic tilesetpic;
static Picl pic0 = {.l = &pic0}, *pic = &pic0;
static Objp *objp;
static Attack *attack;
@@ -47,6 +48,9 @@
static char *tileset;
static int nattack, nobj, nresource, nobjp;
static u32int bgcol = 0x00ffff;
+static int rot17idx[17] = {
+ 0,2,4,6,8,10,12,14,16,17,19,21,23,25,27,29,31
+};
static void
loadpic(char *name, Pic *pic)
@@ -87,48 +91,99 @@
free(b);
}
+static void
+loadshadpic(Pic *pic, Picl *pl)
+{
+ int i;
+ char path[128];
+
+ for(i=0; i<pl->nr; i++){
+ snprint(path, sizeof path, "%s.%02d.%02d.s.bit",
+ pl->name, pl->frm, rot17idx[i]);
+ loadpic(path, pic++);
+ }
+}
+
+static void
+loadobjpic(Pic *pic, Picl *pl)
+{
+ int n, i, j;
+ char path[128];
+ u32int *p0;
+ Pic pic0;
+
+ for(i=0; i<pl->nr; i++){
+ snprint(path, sizeof path, "%s.%02d.%02d.bit",
+ pl->name, pl->frm, rot17idx[i]);
+ loadpic(path, &pic0);
+ if(pic0.h % Nteam != 0)
+ sysfatal("loadobjpic: obj %s sprite sheet %d,%d: height not multiple of %d\n",
+ pl->name, pic0.w, pic0.h, Nteam);
+ pic0.h /= Nteam;
+ n = pic0.w * pic0.h;
+ /* nteam has been set by now, no point in retaining sprites
+ * for additional teams */
+ for(j=0, p0=pic0.p; j<nteam; j++, p0+=n, pic++){
+ memcpy(pic, &pic0, sizeof *pic);
+ pic->p = emalloc(n * sizeof *pic->p);
+ memcpy(pic->p, p0, n * sizeof *pic->p);
+ }
+ free(pic0.p);
+ }
+}
+
+static void
+loadterpic(Pic *pic, Picl *pl)
+{
+ int id, size;
+ char path[128];
+
+ if(tilesetpic.p == nil){
+ snprint(path, sizeof path, "%s.bit", tileset);
+ loadpic(path, &tilesetpic);
+ if(tilesetpic.h % tilesetpic.w != 0)
+ sysfatal("loadterpic: tiles not squares: tilepic %d,%d\n",
+ tilesetpic.w, tilesetpic.h);
+ }
+ id = pl->frm;
+ size = tilesetpic.w;
+ if(size * id >= tilesetpic.h)
+ sysfatal("loadterpic: terrain tile index %d out of bounds", id);
+ pic->w = size;
+ pic->h = size;
+ size *= size;
+ pic->p = emalloc(size * sizeof *pic->p);
+ memcpy(pic->p, tilesetpic.p + size * id, size * sizeof *pic->p);
+}
+
void
initimg(void)
{
- int i, r;
- char path[128];
Pic *p;
Picl *pl;
for(pl=pic->l; pl!=pic; pl=pic->l){
p = pl->p;
- if(pl->type == PFterrain){
- snprint(path, sizeof path, "%s.%05d.bit", tileset, pl->id);
- loadpic(path, p);
- }else if(pl->type & PFshadow){
- for(r=0; r<pl->nr; r++){
- snprint(path, sizeof path,
- "%ss.%02d.%02d.bit",
- pl->name, pl->id, r);
- loadpic(path, p++);
- }
- }else{
- for(i=0; i<nteam; i++)
- for(r=0; r<pl->nr; r++){
- snprint(path, sizeof path,
- "%s%d.%02d.%02d.bit",
- pl->name, i+1, pl->id, r);
- loadpic(path, p++);
- }
- }
+ if(pl->type & PFterrain)
+ loadterpic(p, pl);
+ else if(pl->type & PFshadow)
+ loadshadpic(p, pl);
+ else
+ loadobjpic(p, pl);
pic->l = pl->l;
free(pl);
}
+ free(tilesetpic.p);
}
static Pic *
-pushpic(char *name, int id, int type, int nr)
+pushpic(char *name, int frm, int type, int nr)
{
int n;
char iname[64];
Picl *pl;
- snprint(iname, sizeof iname, "%s%d%02ux", name, id, type);
+ snprint(iname, sizeof iname, "%s%02d%02ux", name, frm, type);
for(pl=pic->l; pl!=pic; pl=pl->l)
if(strcmp(iname, pl->iname) == 0)
break;
@@ -135,12 +190,15 @@
if(pl == pic){
pl = emalloc(sizeof *pl);
memcpy(pl->iname, iname, nelem(pl->iname));
- pl->id = id;
+ pl->frm = frm;
pl->type = type;
pl->name = name;
pl->nr = nr;
+ if(nr != 17 && nr != 1)
+ sysfatal("pushpic %s: invalid number of rotations", iname);
n = nr;
- if((type & PFshadow) == 0)
+ /* nteam isn't guaranteed to be set correctly by now */
+ if((type & (PFshadow|PFterrain)) == 0)
n *= Nteam;
pl->p = emalloc(n * sizeof *pl->p);
pl->l = pic->l;
@@ -161,7 +219,7 @@
tl = emalloc(sizeof *tl);
tl->id = id;
tl->t = emalloc(sizeof *tl->t);
- tl->t->p = pushpic(",", id, PFterrain, 1);
+ tl->t->p = pushpic("/tile/", id - 1, PFterrain, 1);
tl->l = terrainl->l;
terrainl->l = tl;
}
@@ -342,34 +400,34 @@
static void
readspr(char **fld, int n, Table *)
{
- int type, id, nr;
+ int type, frm, nr;
Obj *o;
Pics *ps;
Pic ***ppp, **p, **pe;
if(n < 4)
- sysfatal("readspr: %d fields < 4 mandatory columns", n);
+ sysfatal("readspr %s: %d fields < 4 mandatory columns", o->name, n);
unpack(fld, "odd", &o, &type, &nr);
fld += 3;
n -= 3;
ps = nil;
- switch(type & 0x7f){
+ switch(type & 0x7e){
case PFidle: ps = &o->pidle; break;
case PFmove: ps = &o->pmove; break;
- default: sysfatal("readspr: invalid type %#02ux", type & 0x7f);
+ default: sysfatal("readspr %s: invalid type %#02ux", o->name, type & 0x7e);
}
- ppp = type & PFshadow ? &ps->shadow : &ps->p;
+ ppp = type & PFshadow ? &ps->shadow : &ps->pic;
if(*ppp != nil)
- sysfatal("readspr: %s pic type %#ux already allocated", o->name, type);
+ sysfatal("readspr %s: pic type %#ux already allocated", o->name, type);
if(ps->nf != 0 && ps->nf != n || ps->nr != 0 && ps->nr != nr)
- sysfatal("readspr: %s spriteset phase error", o->name);
+ sysfatal("readspr %s: spriteset phase error", o->name);
ps->nf = n;
ps->nr = nr;
p = emalloc(n * sizeof *ppp);
*ppp = p;
for(pe=p+n; p<pe; p++){
- unpack(fld++, "d", &id);
- *p = pushpic(o->name, id, type, nr);
+ unpack(fld++, "d", &frm);
+ *p = pushpic(o->name, frm, type, nr);
}
}
--- a/sce/sce.db
+++ b/sce/sce.db
@@ -13,13 +13,13 @@
# spawn: objname, [obj..]
spawn,control,scv
# spr: objname, flags (PF enum), rotations, [frame..]
-spr,scv,1,32,0
-spr,scv,0x8001,32,0
-spr,control,1,1,0
-spr,control,0x8001,1,0
-spr,drone,1,32,0
-spr,drone,2,32,0,1,2,3,4
-spr,drone,0x8001,32,0
-spr,drone,0x8002,32,0,1,2,3,4
-spr,hatchery,1,1,0,1,2,3,3,2,1,0
-spr,hatchery,0x8001,1,0,0,0,0,0,0,0,0
+spr,scv,2,17,0
+spr,scv,0x8002,17,0
+spr,control,2,1,0
+spr,control,0x8002,1,0
+spr,drone,2,17,0
+spr,drone,4,17,0,1,2,3,4
+spr,drone,0x8002,17,0
+spr,drone,0x8004,17,0,1,2,3,4
+spr,hatchery,2,1,0,1,2,3,3,2,1,0
+spr,hatchery,0x8002,1,0,0,0,0,0,0,0,0
--- a/sim.c
+++ b/sim.c
@@ -108,7 +108,7 @@
}
mo->movingp = linkmobj(moving, mo, mo->movingp);
mo->pathp = mo->paths;
- mo->pics = mo->o->pmove.p != nil ? &mo->o->pmove : &mo->o->pidle;
+ mo->pics = mo->o->pmove.pic != nil ? &mo->o->pmove : &mo->o->pidle;
nextmove(mo);
return 0;
}
--- /dev/null
+++ b/utils/scefix
@@ -1,0 +1,93 @@
+#!/bin/rc
+rfork n
+
+fn terrain{
+ name=$1
+ shift
+ echo $name `{ls $name^.*.bit | wc -l} |\
+ awk '
+{
+ n = int($2)
+ print "!s", 32, 32*n
+ print "r = z == 0 ? 0 : Z"
+ for(i=0; i<n; i++){
+ printf "!r %s.%05d.bit a\n", $1, i+1
+ print "r = y >= 32*" i " && y < 32*" i+1 " ? a[x,y-32*" i "] : r"
+ }
+ print "!w r /tmp/a.bit"
+}
+' >/env/fuckrc
+ $home/p/pico/pico </env/fuckrc
+ iconv -c r8g8b8 /tmp/a.bit >$name^.bit
+ rm /tmp/a.bit $name^.*.bit
+}
+
+fn sprsheet{
+ name=$1
+ frm=$2
+ rot=$3
+ dim=`{read -c 72 $name^1.$frm.$rot.bit | awk 'NR>1{print $2, $3, $4, $5}'}
+ x1=$dim(1)
+ y1=$dim(2)
+ x2=$dim(3)
+ y2=$dim(4)
+ dy=`{echo $y2-$y1 | pc -n}
+ crop -b 0 255 255 -r $x1 $y1 $x2 \
+ `{echo $y1^'+'^$dy^'*8' | pc -n} \
+ $name^1.$frm.$rot.bit >/tmp/a.bit
+ sed 's/NAME/'^$name^'/g;s/FRM/'^$frm^'/g;s/ROT/'^$rot^'/g;s/DY/'^$dy^'/g' \
+ /env/fuckrc >/env/forever
+ </env/forever $home/p/pico/pico
+ iconv -c r8g8b8 /tmp/b.bit |\
+ crop -t $x1 $y1 \
+ >$name.$frm.$rot.bit
+ mv $name^s.$frm.$rot.bit $name.$frm.$rot.s.bit
+ rm /tmp/b.bit
+}
+
+fn gen32{
+ name=$1
+ shift
+ for(frm in $*){
+ for(rot in 00 02 04 06 08 10 12 14 16 17 19 21 23 25 27 29 31)
+ sprsheet $name $frm $rot
+ rm $name^?.$frm.*.bit
+ }
+ rm /tmp/a.bit
+}
+
+fn gen1{
+ name=$1
+ shift
+ for(frm in $*){
+ sprsheet $name $frm 00
+ rm $name^?.$frm.*.bit
+ }
+ rm /tmp/a.bit
+}
+
+cat <<! >/env/fuckrc
+!r /tmp/a.bit a
+r = a
+!r NAME2.FRM.ROT.bit b
+r = y >= DY*1 && y < DY*2 ? b[x,y-DY*1] : r
+!r NAME3.FRM.ROT.bit b
+r = y >= DY*2 && y < DY*3 ? b[x,y-DY*2] : r
+!r NAME4.FRM.ROT.bit b
+r = y >= DY*3 && y < DY*4 ? b[x,y-DY*3] : r
+!r NAME5.FRM.ROT.bit b
+r = y >= DY*4 && y < DY*5 ? b[x,y-DY*4] : r
+!r NAME6.FRM.ROT.bit b
+r = y >= DY*5 && y < DY*6 ? b[x,y-DY*5] : r
+!r NAME7.FRM.ROT.bit b
+r = y >= DY*6 && y < DY*7 ? b[x,y-DY*6] : r
+!r NAME8.FRM.ROT.bit b
+r = y >= DY*7 && y < DY*8 ? b[x,y-DY*7] : r
+!w r /tmp/b.bit
+!
+
+gen32 scv 00
+gen32 drone 00 01 02 03 04
+gen1 control 00
+gen1 hatchery 00 01 02 03
+terrain badlands