ported PrtView plugin
[xonotic/netradiant.git] / contrib / prtview / portals.cpp
1 /*
2 PrtView plugin for GtkRadiant
3 Copyright (C) 2001 Geoffrey Dewan, Loki software and qeradiant.com
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library 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 GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #include "Portals.h"
21 #include <string.h>
22 #include <stdlib.h>
23 #ifndef __APPLE__
24 #include <search.h>
25 #endif
26 #include <stdio.h>
27
28 #include "iglrender.h"
29 #include "cullable.h"
30
31 #include "PrtView.h"
32
33 #define LINE_BUF 1000
34
35 CPortals portals;
36 CPortalsRender render;
37
38 int compare( const void *arg1, const void *arg2 )
39 {
40
41         if(portals.portal[*((int *)arg1)].dist > portals.portal[*((int *)arg2)].dist)
42                 return -1;
43         else if(portals.portal[*((int *)arg1)].dist < portals.portal[*((int *)arg2)].dist)
44                 return 1;
45
46         return 0;
47 }
48
49
50 CBspPortal::CBspPortal()
51 {
52         memset(this, 0, sizeof(CBspPortal));
53 }
54
55 CBspPortal::~CBspPortal()
56 {
57         delete[] point;
58         delete[] inner_point;
59 }
60
61 bool CBspPortal::Build(char *def)
62 {
63         char *c = def;
64         unsigned int n;
65         int dummy1, dummy2;
66         int res_cnt, i;
67
68         if(portals.hint_flags)
69         {
70                 res_cnt = sscanf(def, "%u %d %d %d", &point_count, &dummy1, &dummy2, (int *)&hint);
71         }
72         else
73         {
74                 sscanf(def, "%u", &point_count);
75                 hint = false;
76         }
77
78         if(point_count < 3 || (portals.hint_flags && res_cnt < 4))
79                 return false;
80
81         point = new CBspPoint[point_count];
82         inner_point = new CBspPoint[point_count];
83
84         for(n = 0; n < point_count; n++)
85         {
86                 for(; *c != 0 && *c != '('; c++);
87
88                 if(*c == 0)
89                         return false;
90
91                 c++;
92
93                 sscanf(c, "%f %f %f", point[n].p, point[n].p+1, point[n].p+2);
94
95                 center.p[0] += point[n].p[0];
96                 center.p[1] += point[n].p[1];
97                 center.p[2] += point[n].p[2];
98
99                 if(n == 0)
100                 {
101                         for(i = 0; i < 3; i++)
102                         {
103                                 min[i] = point[n].p[i];
104                                 max[i] = point[n].p[i];
105                         }
106                 }
107                 else
108                 {
109                         for(i = 0; i < 3; i++)
110                         {
111                                 if(min[i] > point[n].p[i])
112                                         min[i] = point[n].p[i];
113                                 if(max[i] < point[n].p[i])
114                                         max[i] = point[n].p[i];
115                         }
116                 }
117         }
118
119         center.p[0] /= (float)point_count;
120         center.p[1] /= (float)point_count;
121         center.p[2] /= (float)point_count;
122
123         for(n = 0; n < point_count; n++)
124         {
125                 inner_point[n].p[0] = (0.01f * center.p[0]) + (0.99f * point[n].p[0]);
126                 inner_point[n].p[1] = (0.01f * center.p[1]) + (0.99f * point[n].p[1]);
127                 inner_point[n].p[2] = (0.01f * center.p[2]) + (0.99f * point[n].p[2]);
128         }
129
130         fp_color_random[0] = (float)(rand() & 0xff) / 255.0f;
131         fp_color_random[1] = (float)(rand() & 0xff) / 255.0f;
132         fp_color_random[2] = (float)(rand() & 0xff) / 255.0f;
133         fp_color_random[3] = 1.0f;
134
135         return true;
136 }
137
138 CPortals::CPortals()
139 {
140         memset(this, 0, sizeof(CPortals));
141 }
142
143 CPortals::~CPortals()
144 {
145         Purge();
146 }
147
148 void CPortals::Purge()
149 {
150         delete[] portal;
151         delete[] portal_sort;
152         portal = NULL;
153         portal_sort = NULL;
154         portal_count = 0;
155
156         /*
157         delete[] node;
158         node = NULL;
159         node_count = 0;
160         */
161 }
162
163 void CPortals::Load()
164 {
165         char buf[LINE_BUF+1];
166
167         memset(buf, 0, LINE_BUF + 1);
168         
169         Purge();
170
171         globalOutputStream() << MSG_PREFIX "Loading portal file " << fn << ".\n";
172
173         FILE *in;
174
175         in = fopen(fn, "rt");
176
177         if(in == NULL)
178         {
179                 globalOutputStream() << "  ERROR - could not open file.\n";
180
181                 return;
182         }
183
184         if(!fgets(buf, LINE_BUF, in))
185         {
186                 fclose(in);
187
188                 globalOutputStream() << "  ERROR - File ended prematurely.\n";
189
190                 return;
191         }
192
193         if(strncmp("PRT1", buf, 4) != 0)
194         {
195                 fclose(in);
196
197                 globalOutputStream() << "  ERROR - File header indicates wrong file type (should be \"PRT1\").\n";
198
199                 return;
200         }
201
202         if(!fgets(buf, LINE_BUF, in))
203         {
204                 fclose(in);
205
206                 globalOutputStream() << "  ERROR - File ended prematurely.\n";
207
208                 return;
209         }
210
211         sscanf(buf, "%u", &node_count);
212 /*
213         if(node_count > 0xFFFF)
214         {
215                 fclose(in);
216
217                 node_count = 0;
218
219                 globalOutputStream() << "  ERROR - Extreme number of nodes, aborting.\n";
220
221                 return;
222         }
223         */
224
225         if(!fgets(buf, LINE_BUF, in))
226         {
227                 fclose(in);
228
229                 node_count = 0;
230
231                 globalOutputStream() << "  ERROR - File ended prematurely.\n";
232
233                 return;
234         }
235
236         sscanf(buf, "%u", &portal_count);
237
238         if(portal_count > 0xFFFF)
239         {
240                 fclose(in);
241
242                 portal_count = 0;
243                 node_count = 0;
244
245                 globalOutputStream() << "  ERROR - Extreme number of portals, aborting.\n";
246
247                 return;
248         }
249
250         if(portal_count < 0)
251         {
252                 fclose(in);
253
254                 portal_count = 0;
255                 node_count = 0;
256
257                 globalOutputStream() << "  ERROR - number of portals equals 0, aborting.\n";
258
259                 return;
260         }
261
262 //      node = new CBspNode[node_count];
263         portal = new CBspPortal[portal_count];
264         portal_sort = new int[portal_count];
265
266         unsigned int n;
267         bool first = true;
268         unsigned test_vals_1, test_vals_2;
269
270         hint_flags = false;
271
272         for(n = 0; n < portal_count; )
273         {
274                 if(!fgets(buf, LINE_BUF, in))
275                 {
276                         fclose(in);
277
278                         Purge();
279
280                         globalOutputStream() << "  ERROR - Could not find information for portal number " << n + 1 << " of " << portal_count << ".\n";
281
282                         return;
283                 }
284
285                 if(!portal[n].Build(buf))
286                 {
287                         if(first && sscanf(buf, "%d %d", &test_vals_1, &test_vals_2) == 1) // skip additional counts of later data, not needed
288                         {
289                                 // We can count on hint flags being in the file
290                                 hint_flags = true;
291                                 continue;
292                         }
293
294                         first = false;
295
296                         fclose(in);
297
298                         Purge();
299
300                         globalOutputStream() << "  ERROR - Information for portal number " << n + 1 << " of " << portal_count << " is not formatted correctly.\n";
301
302                         return;
303                 }
304
305                 n++;
306         }
307
308         fclose(in);
309
310         globalOutputStream() << "  " << node_count << " portals read in.\n";
311 }
312
313 #include "math/matrix.h"
314
315 const char* g_state_solid = "$plugins/prtview/solid";
316 const char* g_state_solid_outline = "$plugins/prtview/solid_outline";
317 const char* g_state_wireframe = "$plugins/prtview/wireframe";
318 Shader* g_shader_solid = 0;
319 Shader* g_shader_solid_outline = 0;
320 Shader* g_shader_wireframe = 0;
321
322 void Portals_constructShaders()
323 {
324   OpenGLState state;
325   GlobalOpenGLStateLibrary().getDefaultState(state);
326   state.m_state = RENDER_COLOURWRITE|RENDER_DEPTHWRITE;
327   state.m_sort = OpenGLState::eSortOverlayFirst;
328   state.m_linewidth = portals.width_2d * 0.5f;
329   state.m_colour[0] = portals.fp_color_2d[0];
330   state.m_colour[1] = portals.fp_color_2d[1];
331   state.m_colour[2] = portals.fp_color_2d[2];
332   state.m_colour[3] = portals.fp_color_2d[3];
333   if(portals.aa_2d)
334   {
335     state.m_state |= RENDER_BLEND|RENDER_LINESMOOTH;
336   }
337   GlobalOpenGLStateLibrary().insert(g_state_wireframe, state);
338
339   GlobalOpenGLStateLibrary().getDefaultState(state);
340   state.m_state = RENDER_FILL|RENDER_BLEND|RENDER_COLOURWRITE|RENDER_COLOURCHANGE|RENDER_SMOOTH|RENDER_POLYGONSMOOTH;
341
342         if(portals.aa_3d)
343   {
344                 state.m_state |= RENDER_POLYGONSMOOTH;
345   }
346
347         switch(portals.zbuffer)
348         {
349         case 1:
350                 state.m_state |= RENDER_DEPTHTEST;
351                 break;
352         case 2:
353                 break;
354         default:
355                 state.m_state |= RENDER_DEPTHTEST;
356                 state.m_state |= RENDER_DEPTHWRITE;
357         }
358
359         if(portals.fog)
360         {
361                 state.m_state |= RENDER_FOG;
362
363                 state.m_fog.mode = GL_EXP;
364                 state.m_fog.density = 0.001f;
365                 state.m_fog.start = 10.0f;
366                 state.m_fog.end = 10000.0f;
367                 state.m_fog.index = 0;
368                 state.m_fog.colour[0] = portals.fp_color_fog[0];
369                 state.m_fog.colour[1] = portals.fp_color_fog[1];
370                 state.m_fog.colour[2] = portals.fp_color_fog[2];
371                 state.m_fog.colour[3] = portals.fp_color_fog[3];
372         }
373
374   GlobalOpenGLStateLibrary().insert(g_state_solid, state);
375
376   GlobalOpenGLStateLibrary().getDefaultState(state);
377   state.m_state = RENDER_COLOURWRITE|RENDER_DEPTHWRITE;
378   state.m_sort = OpenGLState::eSortOverlayFirst;
379         state.m_linewidth = portals.width_3d * 0.5f;
380   state.m_colour[0] = portals.fp_color_3d[0];
381   state.m_colour[1] = portals.fp_color_3d[1];
382   state.m_colour[2] = portals.fp_color_3d[2];
383   state.m_colour[3] = portals.fp_color_3d[3];
384
385         if(portals.aa_3d)
386   {
387                 state.m_state |= RENDER_LINESMOOTH;
388   }
389
390         switch(portals.zbuffer)
391         {
392         case 1:
393                 state.m_state |= RENDER_DEPTHTEST;
394                 break;
395         case 2:
396                 break;
397         default:
398                 state.m_state |= RENDER_DEPTHTEST;
399                 state.m_state |= RENDER_DEPTHWRITE;
400         }
401
402         if(portals.fog)
403         {
404                 state.m_state |= RENDER_FOG;
405
406                 state.m_fog.mode = GL_EXP;
407                 state.m_fog.density = 0.001f;
408                 state.m_fog.start = 10.0f;
409                 state.m_fog.end = 10000.0f;
410                 state.m_fog.index = 0;
411                 state.m_fog.colour[0] = portals.fp_color_fog[0];
412                 state.m_fog.colour[1] = portals.fp_color_fog[1];
413                 state.m_fog.colour[2] = portals.fp_color_fog[2];
414                 state.m_fog.colour[3] = portals.fp_color_fog[3];
415         }
416
417   GlobalOpenGLStateLibrary().insert(g_state_solid_outline, state);
418
419   g_shader_solid = GlobalShaderCache().capture(g_state_solid);
420   g_shader_solid_outline = GlobalShaderCache().capture(g_state_solid_outline);
421   g_shader_wireframe = GlobalShaderCache().capture(g_state_wireframe);
422 }
423
424 void Portals_destroyShaders()
425 {
426   GlobalShaderCache().release(g_state_solid);
427   GlobalShaderCache().release(g_state_solid_outline);
428   GlobalShaderCache().release(g_state_wireframe);
429   GlobalOpenGLStateLibrary().erase(g_state_solid);
430   GlobalOpenGLStateLibrary().erase(g_state_solid_outline);
431   GlobalOpenGLStateLibrary().erase(g_state_wireframe);
432 }
433
434 void Portals_shadersChanged()
435 {
436   Portals_destroyShaders();
437   portals.FixColors();
438   Portals_constructShaders();
439 }
440
441 void CPortals::FixColors()
442 {
443         fp_color_2d[0] = (float)GetRValue(color_2d) / 255.0f;
444         fp_color_2d[1] = (float)GetGValue(color_2d) / 255.0f;
445         fp_color_2d[2] = (float)GetBValue(color_2d) / 255.0f;
446         fp_color_2d[3] = 1.0f;
447
448         fp_color_3d[0] = (float)GetRValue(color_3d) / 255.0f;
449         fp_color_3d[1] = (float)GetGValue(color_3d) / 255.0f;
450         fp_color_3d[2] = (float)GetBValue(color_3d) / 255.0f;
451         fp_color_3d[3] = 1.0f;
452
453         fp_color_fog[0] = 0.0f;//(float)GetRValue(color_fog) / 255.0f;
454         fp_color_fog[1] = 0.0f;//(float)GetGValue(color_fog) / 255.0f;
455         fp_color_fog[2] = 0.0f;//(float)GetBValue(color_fog) / 255.0f;
456         fp_color_fog[3] = 1.0f;
457 }
458
459 void CPortalsRender::renderWireframe(Renderer& renderer, const VolumeTest& volume) const
460 {
461         if(!portals.show_2d || portals.portal_count < 1)
462                 return;
463
464   renderer.SetState(g_shader_wireframe, Renderer::eWireframeOnly);
465
466   renderer.addRenderable(m_drawWireframe, g_matrix4_identity);
467 }
468
469 void CPortalsDrawWireframe::render(RenderStateFlags state) const
470 {
471         unsigned int n, p;
472
473         for(n = 0; n < portals.portal_count; n++)
474         {
475                 glBegin(GL_LINE_LOOP);
476
477                 for(p = 0; p < portals.portal[n].point_count; p++)
478                         glVertex3fv(portals.portal[n].point[p].p);
479
480                 glEnd();
481         }
482 }
483
484 CubicClipVolume calculateCubicClipVolume(const Matrix4& viewproj)
485 {
486   CubicClipVolume clip;
487   clip.cam = vector4_projected(
488     matrix4_transformed_vector4(
489       matrix4_full_inverse(viewproj),
490       Vector4(0, 0, -1, 1)
491     )
492   );
493         clip.min[0] = clip.cam[0] + (portals.clip_range * 64.0f);
494         clip.min[1] = clip.cam[1] + (portals.clip_range * 64.0f);
495         clip.min[2] = clip.cam[2] + (portals.clip_range * 64.0f);
496         clip.max[0] = clip.cam[0] - (portals.clip_range * 64.0f);
497         clip.max[1] = clip.cam[1] - (portals.clip_range * 64.0f);
498         clip.max[2] = clip.cam[2] - (portals.clip_range * 64.0f);
499   return clip;
500 }
501
502 void CPortalsRender::renderSolid(Renderer& renderer, const VolumeTest& volume) const
503 {
504         if(!portals.show_3d || portals.portal_count < 1)
505                 return;
506
507   CubicClipVolume clip = calculateCubicClipVolume(matrix4_multiplied_by_matrix4(volume.GetProjection(), volume.GetModelview()));
508
509         if(portals.polygons)
510         {
511     renderer.SetState(g_shader_solid, Renderer::eWireframeOnly);
512     renderer.SetState(g_shader_solid, Renderer::eFullMaterials);
513
514     m_drawSolid.clip = clip;
515     renderer.addRenderable(m_drawSolid, g_matrix4_identity);
516   }
517
518         if(portals.lines)
519         {
520     renderer.SetState(g_shader_solid_outline, Renderer::eWireframeOnly);
521     renderer.SetState(g_shader_solid_outline, Renderer::eFullMaterials);
522
523     m_drawSolidOutline.clip = clip;
524     renderer.addRenderable(m_drawSolidOutline, g_matrix4_identity);
525   }
526 }
527
528 void CPortalsDrawSolid::render(RenderStateFlags state) const
529 {
530         float trans = (100.0f - portals.trans_3d) / 100.0f;
531
532         unsigned int n, p;
533
534         if(portals.zbuffer != 0)
535         {
536                 float d;
537
538                 for(n = 0; n < portals.portal_count; n++)
539                 {
540                         d = (float)clip.cam[0] - portals.portal[n].center.p[0];
541                         portals.portal[n].dist = d * d;
542
543                         d = (float)clip.cam[1] - portals.portal[n].center.p[1];
544                         portals.portal[n].dist += d * d;
545
546                         d = (float)clip.cam[2] - portals.portal[n].center.p[2];
547                         portals.portal[n].dist += d * d;
548
549                         portals.portal_sort[n] = n;
550                 }
551
552                 qsort(portals.portal_sort, portals.portal_count, 4, compare);
553                                 
554                 for(n = 0; n < portals.portal_count; n++)
555                 {
556                         if(portals.polygons == 2 && !portals.portal[portals.portal_sort[n]].hint)
557                                 continue;
558
559                         if(portals.clip)
560                         {
561                                 if(clip.min[0] < portals.portal[portals.portal_sort[n]].min[0])
562                                         continue;
563                                 else if(clip.min[1] < portals.portal[portals.portal_sort[n]].min[1])
564                                         continue;
565                                 else if(clip.min[2] < portals.portal[portals.portal_sort[n]].min[2])
566                                         continue;
567                                 else if(clip.max[0] > portals.portal[portals.portal_sort[n]].max[0])
568                                         continue;
569                                 else if(clip.max[1] > portals.portal[portals.portal_sort[n]].max[1])
570                                         continue;
571                                 else if(clip.max[2] > portals.portal[portals.portal_sort[n]].max[2])
572                                         continue;
573                         }
574
575                         glColor4f(portals.portal[portals.portal_sort[n]].fp_color_random[0], portals.portal[portals.portal_sort[n]].fp_color_random[1],
576                                 portals.portal[portals.portal_sort[n]].fp_color_random[2], trans);
577
578                         glBegin(GL_POLYGON);
579
580                                 for(p = 0; p < portals.portal[portals.portal_sort[n]].point_count; p++)
581                                         glVertex3fv(portals.portal[portals.portal_sort[n]].point[p].p);
582
583                         glEnd();
584                 }
585         }
586         else
587         {
588                 for(n = 0; n < portals.portal_count; n++)
589                 {
590                         if(portals.polygons == 2 && !portals.portal[n].hint)
591                                 continue;
592
593                         if(portals.clip)
594                         {
595                                 if(clip.min[0] < portals.portal[n].min[0])
596                                         continue;
597                                 else if(clip.min[1] < portals.portal[n].min[1])
598                                         continue;
599                                 else if(clip.min[2] < portals.portal[n].min[2])
600                                         continue;
601                                 else if(clip.max[0] > portals.portal[n].max[0])
602                                         continue;
603                                 else if(clip.max[1] > portals.portal[n].max[1])
604                                         continue;
605                                 else if(clip.max[2] > portals.portal[n].max[2])
606                                         continue;
607                         }
608
609                         glColor4f(portals.portal[n].fp_color_random[0], portals.portal[n].fp_color_random[1],
610                                 portals.portal[n].fp_color_random[2], trans);
611
612                         glBegin(GL_POLYGON);
613
614                                 for(p = 0; p < portals.portal[n].point_count; p++)
615                                         glVertex3fv(portals.portal[n].point[p].p);
616
617                         glEnd();
618                 }
619         }
620 }
621
622 void CPortalsDrawSolidOutline::render(RenderStateFlags state) const
623 {
624   for(int n = 0; n < portals.portal_count; n++)
625         {
626                 if(portals.lines == 2 && !portals.portal[n].hint)
627                         continue;
628
629                 if(portals.clip)
630                 {
631                         if(clip.min[0] < portals.portal[n].min[0])
632                                 continue;
633                         else if(clip.min[1] < portals.portal[n].min[1])
634                                 continue;
635                         else if(clip.min[2] < portals.portal[n].min[2])
636                                 continue;
637                         else if(clip.max[0] > portals.portal[n].max[0])
638                                 continue;
639                         else if(clip.max[1] > portals.portal[n].max[1])
640                                 continue;
641                         else if(clip.max[2] > portals.portal[n].max[2])
642                                 continue;
643                 }
644
645                 glBegin(GL_LINE_LOOP);
646
647                 for(int p = 0; p < portals.portal[n].point_count; p++)
648                         glVertex3fv(portals.portal[n].inner_point[p].p);
649
650                 glEnd();
651         }
652 }