d2dimage: trying png and jpeg if there's no vga
[dd2d.git] / d2dsprite.d
blob0d513396326fa68e46f40aaf0501eb3559380c66
1 /* DooM2D: Midnight on the Firing Line
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module d2dsprite is aliced;
20 private:
21 import iv.glbinds;
22 import glutils;
23 import console;
24 import dacs;
25 import d2dimage;
26 import tatlas;
28 //version = tatlas_dump;
31 // ////////////////////////////////////////////////////////////////////////// //
32 // sprite atlases
33 __gshared TexAtlas[] atlases;
34 __gshared ImgSprite[string] sprImages;
37 // ////////////////////////////////////////////////////////////////////////// //
38 public void realiseSpriteAtlases () {
39 foreach (immutable idx, TexAtlas a; atlases) {
40 import arsd.png;
41 import std.string : format;
42 a.updateTexture();
43 version(tatlas_dump) writePng("_za%02s.png".format(idx), a.img);
48 // ////////////////////////////////////////////////////////////////////////// //
49 public class ImgSprite {
50 D2DImage vga;
51 bool mirrored;
52 TexAtlas atlas;
53 TexAtlas.Rect arect;
54 TexAtlas.FRect frect;
55 GLuint listBase; // gl lists (4)
57 private this () {}
59 ~this () { if (/*!mirrored &&*/ listBase) glDeleteLists(listBase, 4); listBase = 0; }
61 final:
62 void putToAtlas () {
63 if (atlas !is null) return;
64 TexAtlas.Rect arc;
65 TexAtlas aa;
66 foreach (TexAtlas a; atlases) {
67 auto rc = a.insert(vga);
68 if (rc.valid) { arc = rc; aa = a; break; }
70 if (!arc.valid) {
71 int w = (vga.width < 1024 ? 1024 : vga.width);
72 int h = (vga.height < 1024 ? 1024 : vga.height);
73 aa = new TexAtlas(w, h);
74 arc = aa.insert(vga);
75 assert(arc.valid);
76 atlases ~= aa;
78 atlas = aa;
79 arect = arc;
80 frect = aa.texCoords(arc);
81 createLists();
84 private void createLists () {
85 listBase = glGenLists(4);
86 foreach (immutable my; 0..2) {
87 foreach (immutable mx; 0..2) {
88 GLuint ln = listBase+(my*2)+mx;
89 glNewList(ln, GL_COMPILE);
90 int x0 = -(mirrored ? vga.width-1-vga.sx : vga.sx);
91 int y0 = -vga.sy;
92 int x1 = x0+vga.width;
93 int y1 = y0+vga.height;
94 if (mx) { int tmp = x0; x0 = x1; x1 = tmp; }
95 if (my) { int tmp = y0; y0 = y1; y1 = tmp; }
96 glBegin(GL_QUADS);
97 glTexCoord2f(frect.x0, frect.y0); glVertex2i(x0, y0); // top-left
98 glTexCoord2f(frect.x1, frect.y0); glVertex2i(x1, y0); // top-right
99 glTexCoord2f(frect.x1, frect.y1); glVertex2i(x1, y1); // bottom-right
100 glTexCoord2f(frect.x0, frect.y1); glVertex2i(x0, y1); // bottom-left
101 glEnd();
102 glEndList();
107 void drawAtXY (int x, int y, bool mirrorX=false, bool mirrorY=false) {
108 if (atlas is null || !atlas.hasTexture) return;
109 //if (listBase == 0) createLists();
110 if (mirrored) mirrorX = !mirrorX;
111 ubyte lnum = (mirrorX ? 0x01 : 0x00)|(mirrorY ? 0x02 : 0x00);
112 bindTexture(atlas.tex.tid);
113 glPushMatrix();
114 scope(exit) glPopMatrix();
115 glTranslatef(x, y, 0);
116 glCallList(listBase+lnum);
121 public final class ImgSpriteMirrored : ImgSprite {
122 private this (ImgSprite aspr) {
123 vga = aspr.vga;
124 mirrored = true;
125 atlas = aspr.atlas;
126 arect = aspr.arect;
127 frect = aspr.frect;
128 assert(atlas !is null);
129 //listBase = aspr.listBase;
130 listBase = 0;
131 createLists();
136 // ////////////////////////////////////////////////////////////////////////// //
137 ImgSprite loadSpriteIntr (string spfilename, bool checkMirror, bool updateAtlas) {
138 if (spfilename.length == 0) return null;
139 ImgSprite im;
140 if (auto imm = spfilename in sprImages) {
141 // cached sprite
142 //conwriteln("cached sprite '", spfilename, "'");
143 im = *imm;
144 } else {
145 // new sprite
146 import std.algorithm : endsWith;
147 string spnameReal = spfilename;
148 if (checkMirror && spnameReal.endsWith("_mirrored.vga")) {
149 // for mirrored, load normal sprite and create mirrored one
150 spnameReal = spnameReal[0..$-13]~".vga";
151 if (auto imm = spnameReal in sprImages) {
152 im = *imm;
153 } else {
154 im = loadSpriteIntr(spnameReal, false, updateAtlas);
155 if (im !is null && updateAtlas) realiseSpriteAtlases();
157 if (im is null) return null;
158 im = new ImgSpriteMirrored(im);
159 assert(im.mirrored);
160 assert(im.listBase);
161 //conwriteln("mirroired sprite '", spfilename, "' [", spnameReal, "]");
162 } else {
163 im = new ImgSprite();
164 //conwriteln("loading sprite '", spfilename, "'");
165 im.vga = new D2DImage(spfilename);
166 im.putToAtlas();
167 if (updateAtlas) realiseSpriteAtlases();
169 sprImages[spfilename] = im;
171 return im;
175 public ImgSprite loadSprite (string spfilename, bool updateAtlas=false) { return loadSpriteIntr(spfilename, true, updateAtlas); }