]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/bkgrnd2d/bkgrnd2d.cpp
Merge branch 'fix-fast' into 'master'
[xonotic/netradiant.git] / contrib / bkgrnd2d / bkgrnd2d.cpp
1 /*
2    Copyright (C) 2003 Reed Mideke.
3
4    This file is part of GtkRadiant.
5
6    GtkRadiant is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    GtkRadiant is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with GtkRadiant; if not, write to the Free Software
18    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 //
22 // bkgrnd2d Plugin
23 //
24 // Code by reyalP aka Reed Mideke
25 //
26 // Based on various other plugins
27 //
28
29 #include "bkgrnd2d.h"
30
31 CBackgroundRender render;
32
33 CBackgroundImage backgroundXY(XY), backgroundXZ(XZ), backgroundYZ(YZ);
34
35 CBackgroundRender::CBackgroundRender()
36 {
37     refCount = 1;
38 }
39
40 CBackgroundRender::~CBackgroundRender()
41 {
42 }
43
44 void CBackgroundRender::Register()
45 {
46     g_QglTable.m_pfnHookGL2DWindow(this);
47 }
48
49 void CBackgroundRender::Draw2D(VIEWTYPE vt)
50 {
51     switch (vt) {
52         case XY:
53             backgroundXY.Render();
54             break;
55         case XZ:
56             backgroundXZ.Render();
57             break;
58         case YZ:
59             backgroundYZ.Render();
60             break;
61     }
62 }
63
64
65 CBackgroundImage::CBackgroundImage(VIEWTYPE vt)
66 {
67     m_tex = NULL;
68     m_alpha = 0.5;
69
70     // TODO, sensible defaults ? Or not show until we have extents ?
71     m_xmin = m_ymin = 0.0f;
72     m_xmax = m_ymax = 0.0f;
73
74     m_bActive = false;
75
76     m_vt = vt;
77
78     switch (m_vt) {
79         case XY:
80             m_ix = 0;
81             m_iy = 1;
82             break;
83         case XZ:
84             m_ix = 0;
85             m_iy = 2;
86             break;
87         case YZ:
88             m_ix = 1;
89             m_iy = 2;
90             break;
91     }
92 }
93
94 /*
95  * should cleanup, but I don't think we can be sure it happens before our
96  * interfaces are gone
97    CBackgroundImage::~CBackgroundImage()
98    {
99    }
100  */
101
102 void CBackgroundImage::Cleanup()
103 {
104     if (m_tex) {
105         g_QglTable.m_pfn_qglDeleteTextures(1, &m_tex->texture_number);
106         g_free(m_tex);
107         m_tex = NULL;
108     }
109 }
110
111 void CBackgroundImage::Render()
112 {
113     if (!m_bActive || !Valid()) {
114         return;
115     }
116     g_QglTable.m_pfn_qglPushAttrib(GL_ALL_ATTRIB_BITS);
117
118     g_QglTable.m_pfn_qglEnable(GL_TEXTURE_2D);
119     g_QglTable.m_pfn_qglEnable(GL_BLEND);
120     g_QglTable.m_pfn_qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
121     g_QglTable.m_pfn_qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
122     g_QglTable.m_pfn_qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
123     g_QglTable.m_pfn_qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
124
125     g_QglTable.m_pfn_qglPolygonMode(GL_FRONT, GL_FILL);
126     // TODO, just so we can tell if we end up going the wrong way
127     // g_QglTable.m_pfn_qglPolygonMode(GL_BACK,GL_LINE);
128     // TODO any other state we should not assume ?
129
130     g_QglTable.m_pfn_qglBindTexture(GL_TEXTURE_2D, m_tex->texture_number);
131     g_QglTable.m_pfn_qglBegin(GL_QUADS);
132
133     g_QglTable.m_pfn_qglColor4f(1.0, 1.0, 1.0, m_alpha);
134     g_QglTable.m_pfn_qglTexCoord2f(0.0, 1.0);
135     g_QglTable.m_pfn_qglVertex2f(m_xmin, m_ymin);
136
137     g_QglTable.m_pfn_qglTexCoord2f(1.0, 1.0);
138     g_QglTable.m_pfn_qglVertex2f(m_xmax, m_ymin);
139
140     g_QglTable.m_pfn_qglTexCoord2f(1.0, 0.0);
141     g_QglTable.m_pfn_qglVertex2f(m_xmax, m_ymax);
142
143     g_QglTable.m_pfn_qglTexCoord2f(0.0, 0.0);
144     g_QglTable.m_pfn_qglVertex2f(m_xmin, m_ymax);
145
146     g_QglTable.m_pfn_qglEnd();
147     g_QglTable.m_pfn_qglBindTexture(GL_TEXTURE_2D, 0);
148
149     g_QglTable.m_pfn_qglPopAttrib();
150 }
151
152 bool CBackgroundImage::Load(const char *filename)
153 {
154     qtexture_t *newtex;
155
156     unsigned char *image = NULL; // gets allocated with what ? g_malloc
157     int width = 0, height = 0;
158
159     g_FuncTable.m_pfnLoadImage(filename, &image, &width, &height);
160
161     if (!image) {
162         Syn_Printf(MSG_WARN "load %s failed\n", filename);
163         return false;
164     }
165
166 // just in case we want to build for an old version
167 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=900
168 #ifdef BKGRND2D_JPG_WORKAROUND
169     if ( strlen( filename ) > 4 && !strcmp( ".jpg",filename + strlen( filename ) - 4 ) ) {
170         Syn_Printf( MSG_PREFIX ".jpg workaround, clearing alpha channel\n" );
171         int size = width * height * 4;
172         int i;
173         for ( i = 3; i < size; i += 4 ) {
174             image[i] = 255;
175         }
176     }
177 #endif
178
179     //TODO bug for stored texture size
180     //TODO whose gl context are we in, anyway ?
181     newtex = g_FuncTable.m_pfnLoadTextureRGBA(image, width, height);
182
183     g_free(image);
184
185     if (!newtex) {
186         Syn_Printf(MSG_WARN "image to texture failed\n");
187         return false;
188     }
189
190     Cleanup();
191     m_tex = newtex;
192
193     g_FuncTable.m_pfnSysUpdateWindows(W_XY);
194
195     return true;
196 }
197
198 bool CBackgroundImage::SetExtentsMM()
199 {
200     entity_s *worldentity;
201     const char *val;
202     int xmin = 0, ymin = 0, xmax = 0, ymax = 0;
203
204     worldentity = (entity_s *) g_FuncTable.m_pfnGetEntityHandle(0);
205     if (!worldentity) {
206         Syn_Printf(MSG_WARN "SetExtentsMM worldspawn not found\n");
207         return false;
208     }
209     //TODO val is not NULL even if key does not exist
210     val = g_EntityTable.m_pfnValueForKey(worldentity, "mapcoordsmins");
211     if (!val || !val[0]) {
212         Syn_Printf(MSG_WARN "SetExtentsMM mapcoordsmins not found\n");
213         return false;
214     }
215 // we could be more robust
216 // note contortions due to splashs strange idea of min and max
217     if (sscanf(val, "%d %d", &xmin, &ymax) != 2) {
218         Syn_Printf(MSG_WARN "SetExtentsMM mapcoordsmins malformed\n");
219         return false;
220     }
221
222     val = g_EntityTable.m_pfnValueForKey(worldentity, "mapcoordsmaxs");
223     if (!val || !val[0]) {
224         Syn_Printf(MSG_WARN "SetExtentsMM mapcoordsmaxs not found\n");
225         return false;
226     }
227     if (sscanf(val, "%d %d", &xmax, &ymin) != 2) {
228         Syn_Printf(MSG_WARN "SetExtentsMM mapcoordsmaxs malformed\n");
229         return false;
230     }
231     //might do sanity check before we commit
232     m_xmin = (float) xmin;
233     m_ymin = (float) ymin;
234     m_xmax = (float) xmax;
235     m_ymax = (float) ymax;
236
237     g_FuncTable.m_pfnSysUpdateWindows(W_XY);
238     return true;
239 }
240
241 // TODO, this should just be exported from core
242 // ripped directly from radiant/select.cpp:Select_GetBounds
243 //
244 static bool get_selection_bounds(vec3_t mins, vec3_t maxs)
245 {
246     brush_t *b;
247     int i;
248     brush_t *selected_brushes = g_DataTable.m_pfnSelectedBrushes();
249     //TODO should never happen
250     if (!selected_brushes) {
251         Sys_Printf(MSG_PREFIX "selected_brushes = NULL\n");
252         return false;
253     }
254     // this should mean no selection
255     if (selected_brushes == selected_brushes->next) {
256         Sys_Printf(MSG_PREFIX "nothing selected\n");
257
258         return false;
259     }
260
261     for (i = 0; i < 3; i++) {
262         mins[i] = 99999;
263         maxs[i] = -99999;
264     }
265
266     for (b = selected_brushes->next; b != selected_brushes; b = b->next) {
267         if (b->owner->eclass->fixedsize) {
268             for (i = 0; i < 3; i++) {
269                 if (b->owner->origin[i] < mins[i]) {
270                     mins[i] = b->owner->origin[i];
271                 }
272                 if (b->owner->origin[i] > maxs[i]) {
273                     maxs[i] = b->owner->origin[i];
274                 }
275             }
276         } else {
277             for (i = 0; i < 3; i++) {
278                 if (b->mins[i] < mins[i]) {
279                     mins[i] = b->mins[i];
280                 }
281                 if (b->maxs[i] > maxs[i]) {
282                     maxs[i] = b->maxs[i];
283                 }
284             }
285         }
286     }
287     return true;
288 }
289
290 bool CBackgroundImage::SetExtentsSel()
291 {
292     vec3_t mins, maxs;
293
294     if (!get_selection_bounds(mins, maxs)) {
295         return false;
296     }
297
298     if (((int) mins[m_ix] == (int) maxs[m_ix]) ||
299         ((int) mins[m_iy] == (int) maxs[m_iy])) {
300         Syn_Printf(MSG_PREFIX "tiny selection\n");
301         return false;
302     }
303
304     m_xmin = mins[m_ix];
305     m_ymin = mins[m_iy];
306     m_xmax = maxs[m_ix];
307     m_ymax = maxs[m_iy];
308
309     g_FuncTable.m_pfnSysUpdateWindows(W_XY);
310
311     return true;
312 }