1 /**************************************************************************
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 3
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 **************************************************************************/
12 class BackTileImage : Object transient;
19 int tileWidth, tileHeight;
23 override void Destroy () {
29 static final int roundToPOW (int n) {
41 final void blitAt (int x, int y, int scale, optional float angle) {
43 tex.blitAt(x, y, scale, angle:angle!optional);
46 tex.blitExt(x, y, x+w*scale, y+h*scale, 0, 0, w, h, angle:angle!optional);
51 static final BackTileImage Load (string fname) {
52 bool allowNPot = GLVideo.glHasNPOT;
53 //if (allowNPot) writeln("*** NPOT textures allowed");
56 auto fl = TextReader.Open(fname);
59 string sign = fl.readBuf(4, true); // exact
60 if (sign != "TIL0" || fl.error) { delete fl; return none; }
62 int nlen = fl.readU8();
63 if (fl.error) { delete fl; return none; }
66 btName = fl.readBuf(nlen, true); // exact
67 if (fl.error) { delete fl; return none; }
70 int xofs = fl.readI16();
71 int yofs = fl.readI16();
73 int xsep = fl.readI16();
74 int ysep = fl.readI16();
76 int tileW = fl.readU16();
77 int tileH = fl.readU16();
79 int imageW = fl.readU16();
80 int imageH = fl.readU16();
81 if (fl.error) { delete fl; return none; }
82 if (imageW < 1 || imageH < 1 || imageW > 8192 || imageH > 8192) { delete fl; return none; }
83 if (tileW < 1 || tileH < 1 || tileW > imageW || tileH > imageH || imageW%tileW || imageH%tileH) { delete fl; return none; }
85 int txw = (allowNPot ? imageW : roundToPOW(imageW));
86 int txh = (allowNPot ? imageH : roundToPOW(imageH));
88 auto bgimg = SpawnObject(BackTileImage);
89 bgimg.Name = name(btName);
92 bgimg.goodSize = (txw == imageW && txh == imageH);
97 bgimg.tileWidth = tileW;
98 bgimg.tileHeight = tileH;
101 auto tex = GLTexture.CreateEmpty(txw, txh, name(btName));
102 if (!tex) { delete fl; delete bgimg; return none; }
104 foreach (int y; 0..imageH) {
105 foreach (int x; 0..imageW) {
106 ubyte r = fl.readU8();
107 ubyte g = fl.readU8();
108 ubyte b = fl.readU8();
109 ubyte a = 255-fl.readU8();
110 if (fl.error) { delete fl; delete bgimg; return none; }
111 //write("(", a, ",", r, ",", g, ",", b, ") ");
112 tex.setPixel(x, y, (a<<24)|(r<<16)|(g<<8)|b);
114 foreach (int x; imageW..txw) tex.setPixel(x, y, 0xff_00_00_00);
117 foreach (int y; imageH..txh) foreach (int x; imageW..txw) tex.setPixel(x, y, 0xff_00_00_00);
118 // update texture image
126 #define ALT_LIGHT_ATT_FORMULA
127 // color doesn't matter (and it is white for no reason); only alpha matters
128 static final BackTileImage buildLightTexture (name aname, int radius) {
129 auto tex = GLTexture.CreateEmpty(radius*2, radius*2, aname);
130 float sqrad = radius*radius;
131 #ifndef ALT_LIGHT_ATT_FORMULA
132 float minLight = 0.3;
133 float b = 1.0/(sqrad*minLight);
135 foreach (int y; 0..radius+1) {
136 foreach (int x; 0..radius+1) {
137 float dist = sqrt(x*x+y*y);
138 #ifdef ALT_LIGHT_ATT_FORMULA
139 float att = fclamp(1.0-dist*dist/sqrad, 0.0, 1.0);
142 float att = 1.0/(1.0+b*dist*dist);
144 if (att > 0.6) att += att/2.0;
145 else if (att < 0.6) att -= att/2.0;
147 //float att = 1.0-(dist/radius);
149 ubyte a = 255-int(fclamp(255.0*att, 0, 255));
150 int color = (a<<24)|0xff_ff_ff;
151 if (dist >= radius-8) color = 0xff_00_00_00;
152 tex.setPixel(radius-x, radius-y, color);
153 tex.setPixel(x+radius, radius-y, color);
154 tex.setPixel(radius-x, y+radius, color);
155 tex.setPixel(x+radius, y+radius, color);
159 auto bgimg = SpawnObject(BackTileImage);
165 bgimg.tileWidth = radius*2;
166 bgimg.tileHeight = radius*2;
172 static final BackTileImage buildCircleTexture (name aname, int radius) {
173 auto tex = GLTexture.CreateEmpty(radius*2, radius*2, aname);
174 float sqradius = radius*radius;
175 foreach (int y; 0..radius+1) {
176 foreach (int x; 0..radius+1) {
177 float dist = x*x+y*y;
178 int color = (dist < sqradius ? 0x00_ff_ff_ff : 0xff_00_00_00);
179 tex.setPixel(radius-x, radius-y, color);
180 tex.setPixel(x+radius, radius-y, color);
181 tex.setPixel(radius-x, y+radius, color);
182 tex.setPixel(x+radius, y+radius, color);
186 auto bgimg = SpawnObject(BackTileImage);
192 bgimg.tileWidth = radius*2;
193 bgimg.tileHeight = radius*2;
199 // ////////////////////////////////////////////////////////////////////////// //
200 class BackTileStore : Object transient;
202 array!BackTileImage bgbyid;
207 private final void loadTile (name Name) {
208 if (!Name) FatalError("cannot load namelss sprite");
209 auto bgtile = BackTileImage.Load(va("%s/%n.tile", tilePath, Name));
210 if (!bgtile) FatalError("cannot load sprite '%n'", Name);
212 bgbyid[NameToInt(bgtile.Name)] = bgtile;
215 writeln("bgtile: <", bgtile.Name, ">, id=", NameToInt(bgtile.Name), "; size=", bgtile.tex.width, "x", bgtile.tex.height);
220 final BackTileImage opIndex (name Name) {
221 int id = NameToInt(Name);
222 if (id <= 0) return none; // just in case
223 if (id >= bgbyid.length || !bgbyid[id]) loadTile(Name);
224 return (id >= 0 && id < bgbyid.length ? bgbyid[id] : none);
228 final BackTileImage lightTexture (name aname, int radius) {
229 if (!aname || radius < 8) return none;
230 int id = NameToInt(aname);
231 if (id <= 0) return none; // just in case
232 if (id >= bgbyid.length || !bgbyid[id]) {
233 writeln("creating light texture with raduis ", radius);
234 auto bi = BackTileImage.buildLightTexture(aname, radius);
237 return (id >= 0 && id < bgbyid.length ? bgbyid[id] : none);
241 final BackTileImage circleTexture (name aname, int radius) {
242 if (!aname || radius < 8) return none;
243 int id = NameToInt(aname);
244 if (id <= 0) return none; // just in case
245 if (id >= bgbyid.length || !bgbyid[id]) {
246 auto bi = BackTileImage.buildCircleTexture(aname, radius);
249 return (id >= 0 && id < bgbyid.length ? bgbyid[id] : none);
254 //tilePath = "data/gfx/tiles";