allow changing the "Alternate texture projection" mode at runtime (requires Map_New...
[divverent/netradiant.git] / radiant / points.cpp
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 /*
23 The following source code is licensed by Id Software and subject to the terms of 
24 its LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with 
25 GtkRadiant. If you did not receive a LIMITED USE SOFTWARE LICENSE AGREEMENT, 
26 please contact Id Software immediately at info@idsoftware.com.
27 */
28
29 #include "points.h"
30
31 #include "debugging/debugging.h"
32
33 #include "irender.h"
34 #include "igl.h"
35 #include "renderable.h"
36
37 #include "stream/stringstream.h"
38 #include "os/path.h"
39 #include "os/file.h"
40 #include "cmdlib.h"
41
42 #include "map.h"
43 #include "qe3.h"
44 #include "camwindow.h"
45 #include "xywindow.h"
46 #include "xmlstuff.h"
47 #include "mainframe.h"
48 #include "watchbsp.h"
49 #include "commands.h"
50
51
52 class CPointfile;
53 void Pointfile_Parse(CPointfile& pointfile);
54
55
56 class CPointfile : public ISAXHandler, public Renderable, public OpenGLRenderable
57 {
58   enum
59   {
60     MAX_POINTFILE = 8192,
61   };
62   Vector3 s_pointvecs[MAX_POINTFILE];
63   std::size_t s_num_points;
64   int m_displaylist;
65   static Shader* m_renderstate;
66   StringOutputStream m_characters;
67 public:
68   CPointfile()
69   {
70   }
71   ~CPointfile()
72   {
73   }
74   void Init();
75   void PushPoint (const Vector3& v);
76   void GenerateDisplayList();
77   // SAX interface
78   void Release()
79   {
80     // blank because not heap-allocated
81   }
82   void saxStartElement (message_info_t *ctx, const xmlChar *name, const xmlChar **attrs);
83   void saxEndElement (message_info_t *ctx, const xmlChar *name);
84   void saxCharacters (message_info_t *ctx, const xmlChar *ch, int len);
85   const char* getName();
86
87   typedef const Vector3* const_iterator;
88
89   const_iterator begin() const
90   {
91     return &s_pointvecs[0];
92   }
93   const_iterator end() const
94   {
95     return &s_pointvecs[s_num_points];
96   }
97
98   bool shown() const
99   {
100     return m_displaylist != 0;
101   }
102   void show(bool show)
103   {
104     if(show && !shown())
105     {
106       Pointfile_Parse(*this);
107             GenerateDisplayList();
108       SceneChangeNotify();
109     }
110     else if(!show && shown())
111     {
112             glDeleteLists (m_displaylist, 1);
113             m_displaylist = 0;
114       SceneChangeNotify();
115     }
116   }
117
118   void render(RenderStateFlags state) const
119   {
120     glCallList(m_displaylist);
121   }
122
123   void renderSolid(Renderer& renderer, const VolumeTest& volume) const
124   {
125     if(shown())
126     {
127       renderer.SetState(m_renderstate, Renderer::eWireframeOnly);
128       renderer.SetState(m_renderstate, Renderer::eFullMaterials);
129       renderer.addRenderable(*this, g_matrix4_identity);
130     }
131   }
132   void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
133   {
134     renderSolid(renderer, volume);
135   }
136
137   static void constructStatic()
138   {
139     m_renderstate = GlobalShaderCache().capture("$POINTFILE");
140   }
141
142   static void destroyStatic()
143   {
144     GlobalShaderCache().release("$POINTFILE");
145   }
146 };
147
148 Shader* CPointfile::m_renderstate = 0;
149
150 namespace
151 {
152   CPointfile s_pointfile;
153 }
154
155 ISAXHandler& g_pointfile = s_pointfile;
156
157 static CPointfile::const_iterator s_check_point;
158
159 void CPointfile::Init()
160 {
161   s_num_points = 0;
162   m_displaylist = 0;
163 }
164
165 void CPointfile::PushPoint(const Vector3& v)
166 {
167         if (s_num_points < MAX_POINTFILE)
168         {
169                 s_pointvecs[s_num_points] = v;
170                 ++s_num_points;
171         }
172 }
173
174 // create the display list at the end
175 void CPointfile::GenerateDisplayList()
176 {
177   m_displaylist = glGenLists(1);
178
179   glNewList (m_displaylist,  GL_COMPILE);
180
181   glBegin(GL_LINE_STRIP);
182         for(std::size_t i=0;i<s_num_points;i++)
183           glVertex3fv (vector3_to_array(s_pointvecs[i]));
184   glEnd();
185   glLineWidth (1);
186         
187   glEndList();
188 }
189
190 // old (but still relevant) pointfile code -------------------------------------
191
192 void Pointfile_Delete (void)
193 {
194   const char* mapname = Map_Name(g_map);
195   StringOutputStream name(256);
196   name << StringRange(mapname, path_get_filename_base_end(mapname)) << ".lin";
197         file_remove(name.c_str());
198 }
199
200 // advance camera to next point
201 void Pointfile_Next (void)
202 {
203   if(!s_pointfile.shown())
204     return;
205
206         if (s_check_point+2 == s_pointfile.end())
207         {
208                 globalOutputStream() << "End of pointfile\n";
209                 return;
210         }
211
212   CPointfile::const_iterator i = ++s_check_point;
213
214
215   CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
216         Camera_setOrigin(camwnd, *i);
217         g_pParentWnd->GetXYWnd()->SetOrigin(*i);
218   {
219           Vector3 dir(vector3_normalised(vector3_subtracted(*(++i), Camera_getOrigin(camwnd))));
220     Vector3 angles(Camera_getAngles(camwnd));
221           angles[CAMERA_YAW] = static_cast<float>(radians_to_degrees(atan2(dir[1], dir[0])));
222           angles[CAMERA_PITCH] = static_cast<float>(radians_to_degrees(asin(dir[2])));
223     Camera_setAngles(camwnd, angles);
224   }
225 }
226
227 // advance camera to previous point
228 void Pointfile_Prev (void)
229 {
230   if(!s_pointfile.shown())
231     return;
232
233         if (s_check_point == s_pointfile.begin())
234         {
235                 globalOutputStream() << "Start of pointfile\n";
236                 return;
237         }
238
239         CPointfile::const_iterator i = --s_check_point;
240
241   CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
242         Camera_setOrigin(camwnd, *i);
243         g_pParentWnd->GetXYWnd()->SetOrigin(*i);
244   {
245           Vector3 dir(vector3_normalised(vector3_subtracted(*(++i), Camera_getOrigin(camwnd))));
246     Vector3 angles(Camera_getAngles(camwnd));
247           angles[CAMERA_YAW] = static_cast<float>(radians_to_degrees(atan2(dir[1], dir[0])));
248           angles[CAMERA_PITCH] = static_cast<float>(radians_to_degrees(asin(dir[2])));
249     Camera_setAngles(camwnd, angles);
250   }
251 }
252
253 int LoadFile (const char *filename, void **bufferptr)
254 {
255   FILE *f;
256   long len;
257
258   f = fopen (filename, "rb");
259   if (f == 0)
260     return -1;
261
262   fseek (f, 0, SEEK_END);
263   len = ftell (f);
264   rewind (f);
265
266   *bufferptr = malloc (len+1);
267   if (*bufferptr == 0)
268     return -1;
269
270   fread (*bufferptr, 1, len, f);
271   fclose (f);
272
273   // we need to end the buffer with a 0
274   ((char*) (*bufferptr))[len] = 0;
275
276   return len;
277 }
278
279 void Pointfile_Parse(CPointfile& pointfile)
280 {
281         int             size;
282         char    *data;
283   char  *text;
284   int   line = 1;
285
286   const char* mapname = Map_Name(g_map);
287   StringOutputStream name(256);
288   name << StringRange(mapname, path_get_filename_base_end(mapname)) << ".lin";
289
290         size = LoadFile (name.c_str(), (void**)&data);
291   if (size == -1)
292   {
293     globalErrorStream() << "Pointfile " << name.c_str() << " not found\n";
294                 return;
295   }
296
297   // store a pointer
298   text = data;
299
300         globalOutputStream() << "Reading pointfile " << name.c_str() << "\n";
301
302   pointfile.Init();
303
304         while (*data)
305         {
306           Vector3 v;
307     if (sscanf(data,"%f %f %f", &v[0], &v[1], &v[2]) != 3)
308     {
309       globalOutputStream() << "Corrupt point file, line " << line << "\n";
310                         break;
311     }
312
313         while (*data && *data != '\n')
314     {
315       if (*(data-1) == ' ' && *(data) == '-' && *(data+1) == ' ')
316         break;
317                   data++;
318     }
319     // deal with zhlt style point files.
320     if (*data == '-')
321     {
322       if (sscanf(data,"- %f %f %f", &v[0], &v[1], &v[2]) != 3)
323       {
324         globalOutputStream() << "Corrupt point file, line " << line << "\n";
325         break;
326       }
327
328       while (*data && *data != '\n')
329                     data++;
330
331     }
332     while (*data == '\n')
333     {
334       data++; // skip the \n
335       line++;
336     }
337                 pointfile.PushPoint (v);
338         }
339
340   g_free(text);
341 }
342
343 void Pointfile_Clear()
344 {
345   s_pointfile.show(false);
346 }
347
348 void Pointfile_Toggle()
349 {
350   s_pointfile.show(!s_pointfile.shown());
351
352   s_check_point = s_pointfile.begin();
353 }
354
355 void Pointfile_Construct()
356 {
357   CPointfile::constructStatic();
358
359   GlobalShaderCache().attachRenderable(s_pointfile);
360
361   GlobalCommands_insert("TogglePointfile", FreeCaller<Pointfile_Toggle>());
362   GlobalCommands_insert("NextLeakSpot", FreeCaller<Pointfile_Next>(), Accelerator('K', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
363   GlobalCommands_insert("PrevLeakSpot", FreeCaller<Pointfile_Prev>(), Accelerator('L', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
364 }
365
366 void Pointfile_Destroy()
367 {
368   GlobalShaderCache().detachRenderable(s_pointfile);
369
370   CPointfile::destroyStatic();
371 }
372
373
374
375 // CPointfile implementation for SAX-specific stuff -------------------------------
376 void CPointfile::saxStartElement (message_info_t *ctx, const xmlChar *name, const xmlChar **attrs)
377 {
378   if(string_equal(reinterpret_cast<const char*>(name), "polyline"))
379   {
380     Init();
381     // there's a prefs setting to avoid stopping on leak
382     if (!g_WatchBSP_LeakStop)
383       ctx->stop_depth = 0;
384   }
385 }
386
387 void CPointfile::saxEndElement (message_info_t *ctx, const xmlChar *name)
388 {
389   if(string_equal(reinterpret_cast<const char*>(name), "polyline"))
390   {
391     // we are done
392     GenerateDisplayList();
393     SceneChangeNotify();
394     s_check_point = begin();
395   }
396   else if(string_equal(reinterpret_cast<const char*>(name), "point"))
397   {
398     Vector3 v;  
399     sscanf(m_characters.c_str(), "%f %f %f\n", &v[0], &v[1], &v[2]);
400     PushPoint(v);
401     m_characters.clear();
402   }
403 }
404
405 // only "point" is expected to have characters around here
406 void CPointfile::saxCharacters (message_info_t *ctx, const xmlChar *ch, int len)
407 {
408   m_characters.write(reinterpret_cast<const char*>(ch), len);
409 }
410
411 const char* CPointfile::getName()
412 {
413   return "Map leaked";
414 }