]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/entity/light.cpp
updated libxml2 win32 package to 2.6.24
[xonotic/netradiant.git] / plugins / entity / light.cpp
1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
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 ///\file
23 ///\brief Represents any light entity (e.g. light).
24 ///
25 /// This entity dislays a special 'light' model.
26 /// The "origin" key directly controls the position of the light model in local space.
27 /// The "_color" key controls the colour of the light model.
28 /// The "light" key is visualised with a sphere representing the approximate coverage of the light (except Doom3).
29 /// Doom3 special behaviour:
30 /// The entity behaves as a group.
31 /// The "origin" key is the translation to be applied to all brushes (not patches) grouped under this entity.
32 /// The "light_center" and "light_radius" keys are visualised with a point and a box when the light is selected.
33 /// The "rotation" key directly controls the orientation of the light bounding box in local space.
34 /// The "light_origin" key controls the position of the light independently of the "origin" key if it is specified.
35 /// The "light_rotation" key duplicates the behaviour of the "rotation" key if it is specified. This appears to be an unfinished feature in Doom3.
36
37 #include "light.h"
38
39 #include <stdlib.h>
40
41 #include "cullable.h"
42 #include "renderable.h"
43 #include "editable.h"
44
45 #include "math/frustum.h"
46 #include "selectionlib.h"
47 #include "instancelib.h"
48 #include "transformlib.h"
49 #include "entitylib.h"
50 #include "render.h"
51 #include "eclasslib.h"
52 #include "render.h"
53 #include "stringio.h"
54 #include "traverselib.h"
55 #include "dragplanes.h"
56
57 #include "targetable.h"
58 #include "origin.h"
59 #include "colour.h"
60 #include "filters.h"
61 #include "namedentity.h"
62 #include "keyobservers.h"
63 #include "namekeys.h"
64 #include "rotation.h"
65
66 #include "entity.h"
67 extern bool g_newLightDraw;
68
69
70 void sphere_draw_fill(const Vector3& origin, float radius, int sides)
71 {
72   if (radius <= 0)
73     return;
74
75   const double dt = c_2pi / static_cast<double>(sides);
76   const double dp = c_pi / static_cast<double>(sides);
77
78   glBegin(GL_TRIANGLES);
79   for (int i = 0; i <= sides - 1; ++i)
80   {
81     for (int j = 0; j <= sides - 2; ++j)
82     {
83       const double t = i * dt;
84       const double p = (j * dp) - (c_pi / 2.0);
85
86       {
87         Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p), radius)));
88         glVertex3fv(vector3_to_array(v));
89       }
90
91       {
92         Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p + dp), radius)));
93         glVertex3fv(vector3_to_array(v));
94       }
95
96       {
97         Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p + dp), radius)));
98         glVertex3fv(vector3_to_array(v));
99       }
100
101       {
102         Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p), radius)));
103         glVertex3fv(vector3_to_array(v));
104       }
105
106       {
107         Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p + dp), radius)));
108         glVertex3fv(vector3_to_array(v));
109       }
110
111       {
112         Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p), radius)));
113         glVertex3fv(vector3_to_array(v));
114       }
115     }
116   }
117
118   {
119     const double p = (sides - 1) * dp - (c_pi / 2.0);
120     for (int i = 0; i <= sides - 1; ++i)
121     {
122       const double t = i * dt;
123
124       {
125         Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p), radius)));
126         glVertex3fv(vector3_to_array(v));
127       }
128
129       {
130         Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p + dp), radius)));
131         glVertex3fv(vector3_to_array(v));
132       }
133
134       {
135         Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p), radius)));
136         glVertex3fv(vector3_to_array(v));
137       }
138     }
139   }
140   glEnd();
141 }
142
143 void sphere_draw_wire(const Vector3& origin, float radius, int sides)
144 {
145   {
146     glBegin(GL_LINE_LOOP);
147
148     for (int i = 0; i <= sides; i++)
149     {
150       double ds = sin((i * 2 * c_pi) / sides);
151       double dc = cos((i * 2 * c_pi) / sides);
152
153       glVertex3f(
154         static_cast<float>(origin[0] + radius * dc),
155         static_cast<float>(origin[1] + radius * ds),
156         origin[2]
157       );
158     }
159
160     glEnd();
161   }
162
163   {
164     glBegin(GL_LINE_LOOP);
165
166     for (int i = 0; i <= sides; i++)
167     {
168       double ds = sin((i * 2 * c_pi) / sides);
169       double dc = cos((i * 2 * c_pi) / sides);
170
171       glVertex3f(
172         static_cast<float>(origin[0] + radius * dc),
173         origin[1],
174         static_cast<float>(origin[2] + radius * ds)
175       );
176     }
177
178     glEnd();
179   }
180
181   {
182     glBegin(GL_LINE_LOOP);
183
184     for (int i = 0; i <= sides; i++)
185     {
186       double ds = sin((i * 2 * c_pi) / sides);
187       double dc = cos((i * 2 * c_pi) / sides);
188
189       glVertex3f(
190         origin[0],
191         static_cast<float>(origin[1] + radius * dc),
192         static_cast<float>(origin[2] + radius * ds)
193       );
194     }
195
196     glEnd();
197   }
198 }
199
200 void light_draw_box_lines(const Vector3& origin, const Vector3 points[8])
201 {
202   //draw lines from the center of the bbox to the corners
203   glBegin(GL_LINES);
204
205   glVertex3fv(vector3_to_array(origin));
206   glVertex3fv(vector3_to_array(points[1]));
207
208   glVertex3fv(vector3_to_array(origin));
209   glVertex3fv(vector3_to_array(points[5]));
210
211   glVertex3fv(vector3_to_array(origin));
212   glVertex3fv(vector3_to_array(points[2]));
213
214   glVertex3fv(vector3_to_array(origin));
215   glVertex3fv(vector3_to_array(points[6]));
216
217   glVertex3fv(vector3_to_array(origin));
218   glVertex3fv(vector3_to_array(points[0]));
219
220   glVertex3fv(vector3_to_array(origin));
221   glVertex3fv(vector3_to_array(points[4]));
222
223   glVertex3fv(vector3_to_array(origin));
224   glVertex3fv(vector3_to_array(points[3]));
225
226   glVertex3fv(vector3_to_array(origin));
227   glVertex3fv(vector3_to_array(points[7]));
228
229   glEnd();
230 }
231
232 void light_draw_radius_wire(const Vector3& origin, const float envelope[3])
233 {
234   if(envelope[0] > 0)
235     sphere_draw_wire(origin, envelope[0], 24);
236   if(envelope[1] > 0)
237     sphere_draw_wire(origin, envelope[1], 24);
238   if(envelope[2] > 0)
239     sphere_draw_wire(origin, envelope[2], 24);
240 }
241
242 void light_draw_radius_fill(const Vector3& origin, const float envelope[3])
243 {
244   if(envelope[0] > 0)
245     sphere_draw_fill(origin, envelope[0], 16);
246   if(envelope[1] > 0)
247     sphere_draw_fill(origin, envelope[1], 16);
248   if(envelope[2] > 0)
249     sphere_draw_fill(origin, envelope[2], 16);
250 }
251
252 void light_vertices(const AABB& aabb_light, Vector3 points[6])
253 {
254   Vector3 max(vector3_added(aabb_light.origin, aabb_light.extents));
255   Vector3 min(vector3_subtracted(aabb_light.origin, aabb_light.extents));
256   Vector3 mid(aabb_light.origin);
257
258   // top, bottom, tleft, tright, bright, bleft
259   points[0] = Vector3(mid[0], mid[1], max[2]);
260   points[1] = Vector3(mid[0], mid[1], min[2]);
261   points[2] = Vector3(min[0], max[1], mid[2]);
262   points[3] = Vector3(max[0], max[1], mid[2]);
263   points[4] = Vector3(max[0], min[1], mid[2]);
264   points[5] = Vector3(min[0], min[1], mid[2]);
265 }
266
267 void light_draw(const AABB& aabb_light, RenderStateFlags state)
268 {
269   Vector3 points[6];
270   light_vertices(aabb_light, points);
271
272   if(state & RENDER_LIGHTING)
273   {
274     const float f = 0.70710678f;
275     // North, East, South, West
276     const Vector3 normals[8] = {
277       Vector3( 0, f, f ),
278       Vector3( f, 0, f ),
279       Vector3( 0,-f, f ),
280       Vector3(-f, 0, f ),
281       Vector3( 0, f,-f ),
282       Vector3( f, 0,-f ),
283       Vector3( 0,-f,-f ),
284       Vector3(-f, 0,-f ),
285     };
286
287 #if !defined(USE_TRIANGLE_FAN)
288     glBegin(GL_TRIANGLES);
289 #else
290     glBegin(GL_TRIANGLE_FAN);
291 #endif
292     glVertex3fv(vector3_to_array(points[0]));
293     glVertex3fv(vector3_to_array(points[2]));
294     glNormal3fv(vector3_to_array(normals[0]));
295     glVertex3fv(vector3_to_array(points[3]));
296
297 #if !defined(USE_TRIANGLE_FAN)
298     glVertex3fv(vector3_to_array(points[0]));
299     glVertex3fv(vector3_to_array(points[3]));
300 #endif
301     glNormal3fv(vector3_to_array(normals[1]));
302     glVertex3fv(vector3_to_array(points[4]));
303
304 #if !defined(USE_TRIANGLE_FAN)
305     glVertex3fv(vector3_to_array(points[0]));
306     glVertex3fv(vector3_to_array(points[4]));
307 #endif
308     glNormal3fv(vector3_to_array(normals[2]));
309     glVertex3fv(vector3_to_array(points[5]));
310 #if !defined(USE_TRIANGLE_FAN)
311     glVertex3fv(vector3_to_array(points[0]));
312     glVertex3fv(vector3_to_array(points[5]));
313 #endif
314     glNormal3fv(vector3_to_array(normals[3]));
315     glVertex3fv(vector3_to_array(points[2]));
316 #if defined(USE_TRIANGLE_FAN)
317     glEnd();
318     glBegin(GL_TRIANGLE_FAN);
319 #endif
320
321     glVertex3fv(vector3_to_array(points[1]));
322     glVertex3fv(vector3_to_array(points[2]));
323     glNormal3fv(vector3_to_array(normals[7]));
324     glVertex3fv(vector3_to_array(points[5]));
325
326 #if !defined(USE_TRIANGLE_FAN)
327     glVertex3fv(vector3_to_array(points[1]));
328     glVertex3fv(vector3_to_array(points[5]));
329 #endif
330     glNormal3fv(vector3_to_array(normals[6]));
331     glVertex3fv(vector3_to_array(points[4]));
332
333 #if !defined(USE_TRIANGLE_FAN)
334     glVertex3fv(vector3_to_array(points[1]));
335     glVertex3fv(vector3_to_array(points[4]));
336 #endif
337     glNormal3fv(vector3_to_array(normals[5]));
338     glVertex3fv(vector3_to_array(points[3]));
339
340 #if !defined(USE_TRIANGLE_FAN)
341     glVertex3fv(vector3_to_array(points[1]));
342     glVertex3fv(vector3_to_array(points[3]));
343 #endif
344     glNormal3fv(vector3_to_array(normals[4]));
345     glVertex3fv(vector3_to_array(points[2]));
346
347     glEnd();
348   }
349   else
350   {
351     typedef unsigned int index_t;
352     const index_t indices[24] = {
353       0, 2, 3,
354       0, 3, 4,
355       0, 4, 5,
356       0, 5, 2,
357       1, 2, 5,
358       1, 5, 4,
359       1, 4, 3,
360       1, 3, 2
361     };
362 #if 1
363     glVertexPointer(3, GL_FLOAT, 0, points);
364     glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(index_t), RenderIndexTypeID, indices);
365 #else
366     glBegin(GL_TRIANGLES);
367     for(unsigned int i = 0; i < sizeof(indices)/sizeof(index_t); ++i)
368     {
369       glVertex3fv(points[indices[i]]);
370     }
371     glEnd();
372 #endif
373   }
374
375
376   // NOTE: prolly not relevant until some time..
377   // check for DOOM lights
378 #if 0
379   if (strlen(ValueForKey(e, "light_right")) > 0) {
380     vec3_t vRight, vUp, vTarget, vTemp;
381     GetVectorForKey (e, "light_right", vRight);
382     GetVectorForKey (e, "light_up", vUp);
383     GetVectorForKey (e, "light_target", vTarget);
384
385     glColor3f(0, 1, 0);
386     glBegin(GL_LINE_LOOP);
387     VectorAdd(vTarget, e->origin, vTemp);
388     VectorAdd(vTemp, vRight, vTemp);
389     VectorAdd(vTemp, vUp, vTemp);
390     glVertex3fv(e->origin);
391     glVertex3fv(vTemp);
392     VectorAdd(vTarget, e->origin, vTemp);
393     VectorAdd(vTemp, vUp, vTemp);
394     VectorSubtract(vTemp, vRight, vTemp);
395     glVertex3fv(e->origin);
396     glVertex3fv(vTemp);
397     VectorAdd(vTarget, e->origin, vTemp);
398     VectorAdd(vTemp, vRight, vTemp);
399     VectorSubtract(vTemp, vUp, vTemp);
400     glVertex3fv(e->origin);
401     glVertex3fv(vTemp);
402     VectorAdd(vTarget, e->origin, vTemp);
403     VectorSubtract(vTemp, vUp, vTemp);
404     VectorSubtract(vTemp, vRight, vTemp);
405     glVertex3fv(e->origin);
406     glVertex3fv(vTemp);
407     glEnd();
408
409   }
410 #endif
411 }
412
413 // These variables are tweakable on the q3map2 console, setting to q3map2
414 // default here as there is no way to find out what the user actually uses
415 // right now. Maybe move them to worldspawn?
416 float fPointScale = 7500.f;
417 float fLinearScale = 1.f / 8000.f;
418
419 float light_radius_linear(float fIntensity, float fFalloffTolerance)
420 {
421   return ((fIntensity * fPointScale * fLinearScale) - fFalloffTolerance);
422 }
423
424 float light_radius(float fIntensity, float fFalloffTolerance)
425 {
426   return sqrt(fIntensity * fPointScale / fFalloffTolerance);
427 }
428
429
430 LightType g_lightType = LIGHTTYPE_DEFAULT;
431
432
433 bool spawnflags_linear(int flags)
434 {
435   if( g_lightType == LIGHTTYPE_RTCW )
436   {
437     // Spawnflags :
438     // 1: nonlinear
439     // 2: angle
440
441     return !(flags & 1);
442   }
443   else
444   {
445     // Spawnflags :
446     // 1: linear
447     // 2: no angle
448
449     return (flags & 1);
450   }
451 }
452
453 class LightRadii
454 {
455 public:
456   float m_radii[3];
457
458 private:
459   float m_primaryIntensity;
460   float m_secondaryIntensity;
461   int m_flags;
462   float m_fade;
463   float m_scale;
464
465   void calculateRadii()
466   {
467     float intensity = 300.0f;
468
469     if(m_primaryIntensity != 0.0f)
470     {
471       intensity = m_primaryIntensity;
472     }
473     else if(m_secondaryIntensity != 0.0f)
474     {
475       intensity = m_secondaryIntensity;
476     }
477
478     intensity *= m_scale;
479
480     if(spawnflags_linear(m_flags))
481     {
482       m_radii[0] = light_radius_linear(intensity, 1.0f) / m_fade;
483       m_radii[1] = light_radius_linear(intensity, 48.0f) / m_fade;
484       m_radii[2] = light_radius_linear(intensity, 255.0f) / m_fade;
485     }
486     else
487     {
488       m_radii[0] = light_radius(intensity, 1.0f);
489       m_radii[1] = light_radius(intensity, 48.0f);
490       m_radii[2] = light_radius(intensity, 255.0f);
491     }
492   }
493
494 public:
495   LightRadii() : m_primaryIntensity(0), m_secondaryIntensity(0), m_flags(0), m_fade(1), m_scale(1)
496   {
497   }
498
499  
500   void primaryIntensityChanged(const char* value)
501   {
502     m_primaryIntensity = string_read_float(value);
503     calculateRadii();
504   }
505   typedef MemberCaller1<LightRadii, const char*, &LightRadii::primaryIntensityChanged> PrimaryIntensityChangedCaller;
506   void secondaryIntensityChanged(const char* value)
507   {
508     m_secondaryIntensity = string_read_float(value);
509     calculateRadii();
510   }
511   typedef MemberCaller1<LightRadii, const char*, &LightRadii::secondaryIntensityChanged> SecondaryIntensityChangedCaller;
512   void scaleChanged(const char* value)
513   {
514     m_scale = string_read_float(value);
515     if(m_scale <= 0.0f)
516     {
517       m_scale = 1.0f;
518     }
519     calculateRadii();
520   }
521   typedef MemberCaller1<LightRadii, const char*, &LightRadii::scaleChanged> ScaleChangedCaller;
522   void fadeChanged(const char* value)
523   {
524     m_fade = string_read_float(value);
525     if(m_fade <= 0.0f)
526     {
527       m_fade = 1.0f;
528     }
529     calculateRadii();
530   }
531   typedef MemberCaller1<LightRadii, const char*, &LightRadii::fadeChanged> FadeChangedCaller;
532   void flagsChanged(const char* value)
533   {
534     m_flags = string_read_int(value);
535     calculateRadii();
536   }
537   typedef MemberCaller1<LightRadii, const char*, &LightRadii::flagsChanged> FlagsChangedCaller;
538 };
539
540 class Doom3LightRadius
541 {
542 public:
543   Vector3 m_defaultRadius;
544   Vector3 m_radius;
545   Vector3 m_radiusTransformed;
546   Vector3 m_center;
547   Callback m_changed;
548   bool m_useCenterKey;
549
550   Doom3LightRadius(const char* defaultRadius) : m_defaultRadius(300, 300, 300), m_center(0, 0, 0), m_useCenterKey(false)
551   {
552     if(!string_parse_vector3(defaultRadius, m_defaultRadius))
553     {
554       globalErrorStream() << "Doom3LightRadius: failed to parse default light radius\n";
555     }
556     m_radius = m_defaultRadius;
557   }
558
559   void lightRadiusChanged(const char* value)
560   {
561     if(!string_parse_vector3(value, m_radius))
562     {
563       m_radius = m_defaultRadius;
564     }
565     m_radiusTransformed = m_radius;
566     m_changed();
567     SceneChangeNotify();
568   }
569   typedef MemberCaller1<Doom3LightRadius, const char*, &Doom3LightRadius::lightRadiusChanged> LightRadiusChangedCaller;
570
571   void lightCenterChanged(const char* value)
572   {
573     m_useCenterKey = string_parse_vector3(value, m_center);
574     if(!m_useCenterKey)
575     {
576       m_center = Vector3(0, 0, 0);
577     }
578     SceneChangeNotify();
579   }
580   typedef MemberCaller1<Doom3LightRadius, const char*, &Doom3LightRadius::lightCenterChanged> LightCenterChangedCaller;
581 };
582
583 class RenderLightRadiiWire : public OpenGLRenderable
584 {
585   LightRadii& m_radii;
586   const Vector3& m_origin;
587 public:
588   RenderLightRadiiWire(LightRadii& radii, const Vector3& origin) : m_radii(radii), m_origin(origin)
589   {
590   }
591   void render(RenderStateFlags state) const
592   {
593     light_draw_radius_wire(m_origin, m_radii.m_radii);
594   }
595 };
596
597 class RenderLightRadiiFill : public OpenGLRenderable
598 {
599   LightRadii& m_radii;
600   const Vector3& m_origin;
601 public:
602   static Shader* m_state;
603
604   RenderLightRadiiFill(LightRadii& radii, const Vector3& origin) : m_radii(radii), m_origin(origin)
605   {
606   }
607   void render(RenderStateFlags state) const
608   {
609     light_draw_radius_fill(m_origin, m_radii.m_radii);
610   }
611 };
612
613 class RenderLightRadiiBox : public OpenGLRenderable
614 {
615   const Vector3& m_origin;
616 public:
617   mutable Vector3 m_points[8];
618   static Shader* m_state;
619
620   RenderLightRadiiBox(const Vector3& origin) : m_origin(origin)
621   {
622   }
623   void render(RenderStateFlags state) const
624   {
625     //draw the bounding box of light based on light_radius key
626     if((state & RENDER_FILL) != 0)
627     {
628       aabb_draw_flatshade(m_points);
629     }
630     else
631     {
632       aabb_draw_wire(m_points);
633     }
634
635   #if 1    //disable if you dont want lines going from the center of the light bbox to the corners
636     light_draw_box_lines(m_origin, m_points);
637   #endif
638   }
639 };
640
641 Shader* RenderLightRadiiFill::m_state = 0;
642
643 class RenderLightCenter : public OpenGLRenderable
644 {
645   const Vector3& m_center;
646   EntityClass& m_eclass;
647 public:
648   static Shader* m_state;
649
650   RenderLightCenter(const Vector3& center, EntityClass& eclass) : m_center(center), m_eclass(eclass)
651   {
652   }
653   void render(RenderStateFlags state) const
654   {
655     glBegin(GL_POINTS);
656     glColor3fv(vector3_to_array(m_eclass.color));
657     glVertex3fv(vector3_to_array(m_center));
658     glEnd();
659   }
660 };
661
662 Shader* RenderLightCenter::m_state = 0;
663
664 class RenderLightProjection : public OpenGLRenderable
665 {
666   const Matrix4& m_projection;
667 public:
668
669   RenderLightProjection(const Matrix4& projection) : m_projection(projection)
670   {
671   }
672   void render(RenderStateFlags state) const
673   {
674     Matrix4 unproject(matrix4_full_inverse(m_projection));
675     Vector3 points[8];
676     aabb_corners(AABB(Vector3(0.5f, 0.5f, 0.5f), Vector3(0.5f, 0.5f, 0.5f)), points);
677     points[0] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[0], 1)));
678     points[1] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[1], 1)));
679     points[2] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[2], 1)));
680     points[3] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[3], 1)));
681     points[4] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[4], 1)));
682     points[5] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[5], 1)));
683     points[6] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[6], 1)));
684     points[7] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[7], 1)));
685     Vector4 test1 = matrix4_transformed_vector4(unproject, Vector4(0.5f, 0.5f, 0.5f, 1));
686     Vector3 test2 = vector4_projected(test1);
687     aabb_draw_wire(points);
688   }
689 };
690
691 inline void default_extents(Vector3& extents)
692 {
693   extents = Vector3(8, 8, 8);
694 }
695
696 class ShaderRef
697 {
698   CopiedString m_name;
699   Shader* m_shader;
700   void capture()
701   {
702     m_shader = GlobalShaderCache().capture(m_name.c_str());
703   }
704   void release()
705   {
706     GlobalShaderCache().release(m_name.c_str());
707   }
708 public:
709   ShaderRef()
710   {
711     capture();
712   }
713   ~ShaderRef()
714   {
715     release();
716   }
717   void setName(const char* name)
718   {
719     release();
720     m_name = name;
721     capture();
722   }
723   Shader* get() const
724   {
725     return m_shader;
726   }
727 };
728
729 class LightShader
730 {
731   ShaderRef m_shader;
732   void setDefault()
733   {
734     m_shader.setName(m_defaultShader);
735   }
736 public:
737   static const char* m_defaultShader;
738
739   LightShader()
740   {
741     setDefault();
742   }
743   void valueChanged(const char* value)
744   {
745     if(string_empty(value))
746     {
747       setDefault();
748     }
749     else
750     {
751       m_shader.setName(value);
752     }
753     SceneChangeNotify();
754   }
755   typedef MemberCaller1<LightShader, const char*, &LightShader::valueChanged> ValueChangedCaller;
756
757   Shader* get() const
758   {
759     return m_shader.get();
760   }
761 };
762
763 const char* LightShader::m_defaultShader = "";
764
765 inline const BasicVector4<double>& plane3_to_vector4(const Plane3& self)
766 {
767   return reinterpret_cast<const BasicVector4<double>&>(self);
768 }
769
770 inline BasicVector4<double>& plane3_to_vector4(Plane3& self)
771 {
772   return reinterpret_cast<BasicVector4<double>&>(self);
773 }
774
775 inline Matrix4 matrix4_from_planes(const Plane3& left, const Plane3& right, const Plane3& bottom, const Plane3& top, const Plane3& front, const Plane3& back)
776 {
777   return Matrix4(
778     (right.a - left.a) / 2,
779     (top.a - bottom.a) / 2,
780     (back.a - front.a) / 2,
781     right.a - (right.a - left.a) / 2,
782     (right.b - left.b) / 2,
783     (top.b - bottom.b) / 2,
784     (back.b - front.b) / 2,
785     right.b - (right.b - left.b) / 2,
786     (right.c - left.c) / 2,
787     (top.c - bottom.c) / 2,
788     (back.c - front.c) / 2,
789     right.c - (right.c - left.c) / 2,
790     (right.d - left.d) / 2,
791     (top.d - bottom.d) / 2,
792     (back.d - front.d) / 2,
793     right.d - (right.d - left.d) / 2
794   );
795 }
796
797 class Light :
798   public OpenGLRenderable,
799   public Cullable,
800   public Bounded,
801   public Editable,
802   public Snappable
803 {
804   EntityKeyValues m_entity;
805   KeyObserverMap m_keyObservers;
806   TraversableNodeSet m_traverse;
807   IdentityTransform m_transform;
808
809   OriginKey m_originKey;
810   RotationKey m_rotationKey;
811   Float9 m_rotation;
812   Colour m_colour;
813
814   ClassnameFilter m_filter;
815   NamedEntity m_named;
816   NameKeys m_nameKeys;
817   TraversableObserverPairRelay m_traverseObservers;
818   Doom3GroupOrigin m_funcStaticOrigin;
819
820   LightRadii m_radii;
821   Doom3LightRadius m_doom3Radius;
822
823   RenderLightRadiiWire m_radii_wire;
824   RenderLightRadiiFill m_radii_fill;
825   RenderLightRadiiBox m_radii_box;
826   RenderLightCenter m_render_center;
827   RenderableNamedEntity m_renderName;
828
829   Vector3 m_lightOrigin;
830   bool m_useLightOrigin;
831   Float9 m_lightRotation;
832   bool m_useLightRotation;
833
834   Vector3 m_lightTarget;
835   bool m_useLightTarget;
836   Vector3 m_lightUp;
837   bool m_useLightUp;
838   Vector3 m_lightRight;
839   bool m_useLightRight;
840   Vector3 m_lightStart;
841   bool m_useLightStart;
842   Vector3 m_lightEnd;
843   bool m_useLightEnd;
844
845   mutable AABB m_doom3AABB;
846   mutable Matrix4 m_doom3Rotation;
847   mutable Matrix4 m_doom3Projection;
848   mutable Frustum m_doom3Frustum;
849   mutable bool m_doom3ProjectionChanged;
850
851   RenderLightProjection m_renderProjection;
852
853   LightShader m_shader;
854
855   AABB m_aabb_light;
856
857   Callback m_transformChanged;
858   Callback m_boundsChanged;
859   Callback m_evaluateTransform;
860
861   void construct()
862   {
863     default_rotation(m_rotation);
864     m_aabb_light.origin = Vector3(0, 0, 0);
865     default_extents(m_aabb_light.extents);
866
867     m_keyObservers.insert("classname", ClassnameFilter::ClassnameChangedCaller(m_filter));
868     m_keyObservers.insert(Static<KeyIsName>::instance().m_nameKey, NamedEntity::IdentifierChangedCaller(m_named));
869     m_keyObservers.insert("_color", Colour::ColourChangedCaller(m_colour));
870     m_keyObservers.insert("origin", OriginKey::OriginChangedCaller(m_originKey));
871     m_keyObservers.insert("_light", LightRadii::PrimaryIntensityChangedCaller(m_radii));
872     m_keyObservers.insert("light", LightRadii::SecondaryIntensityChangedCaller(m_radii));
873     m_keyObservers.insert("fade", LightRadii::FadeChangedCaller(m_radii));
874     m_keyObservers.insert("scale", LightRadii::ScaleChangedCaller(m_radii));
875     m_keyObservers.insert("spawnflags", LightRadii::FlagsChangedCaller(m_radii));
876
877     if(g_lightType == LIGHTTYPE_DOOM3)
878     {
879       m_keyObservers.insert("angle", RotationKey::AngleChangedCaller(m_rotationKey));
880       m_keyObservers.insert("rotation", RotationKey::RotationChangedCaller(m_rotationKey));
881       m_keyObservers.insert("light_radius", Doom3LightRadius::LightRadiusChangedCaller(m_doom3Radius));
882       m_keyObservers.insert("light_center", Doom3LightRadius::LightCenterChangedCaller(m_doom3Radius));
883       m_keyObservers.insert("light_origin", Light::LightOriginChangedCaller(*this));
884       m_keyObservers.insert("light_rotation", Light::LightRotationChangedCaller(*this));
885       m_keyObservers.insert("light_target", Light::LightTargetChangedCaller(*this));
886       m_keyObservers.insert("light_up", Light::LightUpChangedCaller(*this));
887       m_keyObservers.insert("light_right", Light::LightRightChangedCaller(*this));
888       m_keyObservers.insert("light_start", Light::LightStartChangedCaller(*this));
889       m_keyObservers.insert("light_end", Light::LightEndChangedCaller(*this));
890       m_keyObservers.insert("texture", LightShader::ValueChangedCaller(m_shader));
891       m_useLightTarget = m_useLightUp = m_useLightRight = m_useLightStart = m_useLightEnd = false;
892       m_doom3ProjectionChanged = true;
893     }
894
895     if(g_lightType == LIGHTTYPE_DOOM3)
896     {
897       m_traverse.attach(&m_traverseObservers);
898       m_traverseObservers.attach(m_funcStaticOrigin);
899
900       m_entity.m_isContainer = true;
901     }
902   }
903   void destroy()
904   {
905     if(g_lightType == LIGHTTYPE_DOOM3)
906     {
907       m_traverseObservers.detach(m_funcStaticOrigin);
908       m_traverse.detach(&m_traverseObservers);
909     }
910   }
911
912   void updateOrigin()
913   {
914     m_boundsChanged();
915
916     if(g_lightType == LIGHTTYPE_DOOM3)
917     {
918       m_funcStaticOrigin.originChanged();
919     }
920
921     m_doom3Radius.m_changed();
922
923     GlobalSelectionSystem().pivotChanged();
924   }
925
926   void originChanged()
927   {
928     m_aabb_light.origin = m_useLightOrigin ? m_lightOrigin : m_originKey.m_origin;
929     updateOrigin();
930   }
931   typedef MemberCaller<Light, &Light::originChanged> OriginChangedCaller;
932
933   void lightOriginChanged(const char* value)
934   {
935     m_useLightOrigin = !string_empty(value);
936     if(m_useLightOrigin)
937     {
938       read_origin(m_lightOrigin, value);
939     }
940     originChanged();
941   }
942   typedef MemberCaller1<Light, const char*, &Light::lightOriginChanged> LightOriginChangedCaller;
943
944   void lightTargetChanged(const char* value)
945   {
946     m_useLightTarget = !string_empty(value);
947     if(m_useLightTarget)
948     {
949       read_origin(m_lightTarget, value);
950     }
951     projectionChanged();
952   }
953   typedef MemberCaller1<Light, const char*, &Light::lightTargetChanged> LightTargetChangedCaller;
954   void lightUpChanged(const char* value)
955   {
956     m_useLightUp = !string_empty(value);
957     if(m_useLightUp)
958     {
959       read_origin(m_lightUp, value);
960     }
961     projectionChanged();
962   }
963   typedef MemberCaller1<Light, const char*, &Light::lightUpChanged> LightUpChangedCaller;
964   void lightRightChanged(const char* value)
965   {
966     m_useLightRight = !string_empty(value);
967     if(m_useLightRight)
968     {
969       read_origin(m_lightRight, value);
970     }
971     projectionChanged();
972   }
973   typedef MemberCaller1<Light, const char*, &Light::lightRightChanged> LightRightChangedCaller;
974   void lightStartChanged(const char* value)
975   {
976     m_useLightStart = !string_empty(value);
977     if(m_useLightStart)
978     {
979       read_origin(m_lightStart, value);
980     }
981     projectionChanged();
982   }
983   typedef MemberCaller1<Light, const char*, &Light::lightStartChanged> LightStartChangedCaller;
984   void lightEndChanged(const char* value)
985   {
986     m_useLightEnd = !string_empty(value);
987     if(m_useLightEnd)
988     {
989       read_origin(m_lightEnd, value);
990     }
991     projectionChanged();
992   }
993   typedef MemberCaller1<Light, const char*, &Light::lightEndChanged> LightEndChangedCaller;
994
995   void writeLightOrigin()
996   {
997     write_origin(m_lightOrigin, &m_entity, "light_origin");
998   }
999
1000   void updateLightRadiiBox() const
1001   {
1002     const Matrix4& rotation = rotation_toMatrix(m_rotation);
1003     aabb_corners(AABB(Vector3(0, 0, 0), m_doom3Radius.m_radiusTransformed), m_radii_box.m_points);
1004     matrix4_transform_point(rotation, m_radii_box.m_points[0]);
1005     vector3_add(m_radii_box.m_points[0], m_aabb_light.origin);
1006     matrix4_transform_point(rotation, m_radii_box.m_points[1]);
1007     vector3_add(m_radii_box.m_points[1], m_aabb_light.origin);
1008     matrix4_transform_point(rotation, m_radii_box.m_points[2]);
1009     vector3_add(m_radii_box.m_points[2], m_aabb_light.origin);
1010     matrix4_transform_point(rotation, m_radii_box.m_points[3]);
1011     vector3_add(m_radii_box.m_points[3], m_aabb_light.origin);
1012     matrix4_transform_point(rotation, m_radii_box.m_points[4]);
1013     vector3_add(m_radii_box.m_points[4], m_aabb_light.origin);
1014     matrix4_transform_point(rotation, m_radii_box.m_points[5]);
1015     vector3_add(m_radii_box.m_points[5], m_aabb_light.origin);
1016     matrix4_transform_point(rotation, m_radii_box.m_points[6]);
1017     vector3_add(m_radii_box.m_points[6], m_aabb_light.origin);
1018     matrix4_transform_point(rotation, m_radii_box.m_points[7]);
1019     vector3_add(m_radii_box.m_points[7], m_aabb_light.origin);
1020   }
1021
1022   void rotationChanged()
1023   {
1024     rotation_assign(m_rotation, m_useLightRotation ? m_lightRotation : m_rotationKey.m_rotation);
1025     GlobalSelectionSystem().pivotChanged();
1026   }
1027   typedef MemberCaller<Light, &Light::rotationChanged> RotationChangedCaller;
1028
1029   void lightRotationChanged(const char* value)
1030   {
1031     m_useLightRotation = !string_empty(value);
1032     if(m_useLightRotation)
1033     {
1034       read_rotation(m_lightRotation, value);
1035     }
1036     rotationChanged();
1037   }
1038   typedef MemberCaller1<Light, const char*, &Light::lightRotationChanged> LightRotationChangedCaller;
1039
1040 public:
1041
1042   Light(EntityClass* eclass, scene::Node& node, const Callback& transformChanged, const Callback& boundsChanged, const Callback& evaluateTransform) :
1043     m_entity(eclass),
1044     m_originKey(OriginChangedCaller(*this)),
1045     m_rotationKey(RotationChangedCaller(*this)),
1046     m_colour(Callback()),
1047     m_filter(m_entity, node),
1048     m_named(m_entity),
1049     m_nameKeys(m_entity),
1050     m_funcStaticOrigin(m_traverse, m_originKey.m_origin),
1051     m_doom3Radius(EntityClass_valueForKey(m_entity.getEntityClass(), "light_radius")),
1052     m_radii_wire(m_radii, m_aabb_light.origin),
1053     m_radii_fill(m_radii, m_aabb_light.origin),
1054     m_radii_box(m_aabb_light.origin),
1055     m_render_center(m_doom3Radius.m_center, m_entity.getEntityClass()), 
1056     m_renderName(m_named, m_aabb_light.origin),
1057     m_useLightOrigin(false),
1058     m_useLightRotation(false),
1059     m_renderProjection(m_doom3Projection),
1060     m_transformChanged(transformChanged),
1061     m_boundsChanged(boundsChanged),
1062     m_evaluateTransform(evaluateTransform)
1063   {
1064     construct();
1065   }
1066   Light(const Light& other, scene::Node& node, const Callback& transformChanged, const Callback& boundsChanged, const Callback& evaluateTransform) :
1067     m_entity(other.m_entity),
1068     m_originKey(OriginChangedCaller(*this)),
1069     m_rotationKey(RotationChangedCaller(*this)),
1070     m_colour(Callback()),
1071     m_filter(m_entity, node),
1072     m_named(m_entity),
1073     m_nameKeys(m_entity),
1074     m_funcStaticOrigin(m_traverse, m_originKey.m_origin),
1075     m_doom3Radius(EntityClass_valueForKey(m_entity.getEntityClass(), "light_radius")),
1076     m_radii_wire(m_radii, m_aabb_light.origin),
1077     m_radii_fill(m_radii, m_aabb_light.origin),
1078     m_radii_box(m_aabb_light.origin),
1079     m_render_center(m_doom3Radius.m_center, m_entity.getEntityClass()), 
1080     m_renderName(m_named, m_aabb_light.origin),
1081     m_useLightOrigin(false),
1082     m_useLightRotation(false),
1083     m_renderProjection(m_doom3Projection),
1084     m_transformChanged(transformChanged),
1085     m_boundsChanged(boundsChanged),
1086     m_evaluateTransform(evaluateTransform)
1087   {
1088     construct();
1089   }
1090   ~Light()
1091   {
1092     destroy();
1093   }
1094
1095   InstanceCounter m_instanceCounter;
1096   void instanceAttach(const scene::Path& path)
1097   {
1098     if(++m_instanceCounter.m_count == 1)
1099     {
1100       m_filter.instanceAttach();
1101       m_entity.instanceAttach(path_find_mapfile(path.begin(), path.end()));
1102       if(g_lightType == LIGHTTYPE_DOOM3)
1103       {
1104         m_traverse.instanceAttach(path_find_mapfile(path.begin(), path.end()));
1105       }
1106       m_entity.attach(m_keyObservers);
1107
1108       if(g_lightType == LIGHTTYPE_DOOM3)
1109       {
1110         m_funcStaticOrigin.enable();
1111       }
1112     }
1113   }
1114   void instanceDetach(const scene::Path& path)
1115   {
1116     if(--m_instanceCounter.m_count == 0)
1117     {
1118       if(g_lightType == LIGHTTYPE_DOOM3)
1119       {
1120         m_funcStaticOrigin.disable();
1121       }
1122
1123       m_entity.detach(m_keyObservers);
1124       if(g_lightType == LIGHTTYPE_DOOM3)
1125       {
1126         m_traverse.instanceDetach(path_find_mapfile(path.begin(), path.end()));
1127       }
1128       m_entity.instanceDetach(path_find_mapfile(path.begin(), path.end()));
1129       m_filter.instanceDetach();
1130     }
1131   }
1132
1133   EntityKeyValues& getEntity()
1134   {
1135     return m_entity;
1136   }
1137   const EntityKeyValues& getEntity() const
1138   {
1139     return m_entity;
1140   }
1141
1142   scene::Traversable& getTraversable()
1143   {
1144     return m_traverse;
1145   }
1146   Namespaced& getNamespaced()
1147   {
1148     return m_nameKeys;
1149   }
1150   Nameable& getNameable()
1151   {
1152     return m_named;
1153   }
1154   TransformNode& getTransformNode()
1155   {
1156     return m_transform;
1157   }
1158
1159   void attach(scene::Traversable::Observer* observer)
1160   {
1161     m_traverseObservers.attach(*observer);
1162   }
1163   void detach(scene::Traversable::Observer* observer)
1164   {
1165     m_traverseObservers.detach(*observer);
1166   }
1167
1168   void render(RenderStateFlags state) const
1169   {
1170     if(!g_newLightDraw)
1171     {
1172       aabb_draw(m_aabb_light, state);
1173     }
1174     else
1175     {
1176       light_draw(m_aabb_light, state);
1177     }
1178   }
1179
1180   VolumeIntersectionValue intersectVolume(const VolumeTest& volume, const Matrix4& localToWorld) const
1181   {
1182     return volume.TestAABB(m_aabb_light, localToWorld);
1183   }
1184
1185   // cache
1186   const AABB& localAABB() const
1187   {
1188     return m_aabb_light;
1189   }
1190
1191
1192   mutable Matrix4 m_projectionOrientation;
1193
1194   void renderSolid(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
1195   {
1196     renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly);
1197     renderer.SetState(m_colour.state(), Renderer::eFullMaterials);
1198     renderer.addRenderable(*this, localToWorld);
1199
1200     if(selected && g_lightRadii && string_empty(m_entity.getKeyValue("target")))
1201     {
1202       if(renderer.getStyle() == Renderer::eFullMaterials)
1203       {
1204         renderer.SetState(RenderLightRadiiFill::m_state, Renderer::eFullMaterials);
1205         renderer.Highlight(Renderer::ePrimitive, false);
1206         renderer.addRenderable(m_radii_fill, localToWorld);
1207       }
1208       else
1209       {
1210         renderer.addRenderable(m_radii_wire, localToWorld);
1211       }
1212     }
1213
1214     renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eFullMaterials);
1215
1216     if(g_lightType == LIGHTTYPE_DOOM3 && selected)
1217     {
1218       if(isProjected())
1219       {
1220         projection();
1221         m_projectionOrientation = rotation();
1222         vector4_to_vector3(m_projectionOrientation.t()) = localAABB().origin;
1223         renderer.addRenderable(m_renderProjection, m_projectionOrientation);
1224       }
1225       else
1226       {
1227         updateLightRadiiBox();
1228         renderer.addRenderable(m_radii_box, localToWorld);
1229       }
1230
1231       //draw the center of the light
1232       if(m_doom3Radius.m_useCenterKey)
1233       {
1234         renderer.Highlight(Renderer::ePrimitive, false);
1235         renderer.Highlight(Renderer::eFace, false);
1236         renderer.SetState(m_render_center.m_state, Renderer::eFullMaterials);
1237         renderer.SetState(m_render_center.m_state, Renderer::eWireframeOnly);
1238         renderer.addRenderable(m_render_center, localToWorld);
1239       }
1240     }
1241   }
1242   void renderWireframe(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
1243   {
1244     renderSolid(renderer, volume, localToWorld, selected);
1245     if(g_showNames)
1246     {
1247       renderer.addRenderable(m_renderName, localToWorld);
1248     }
1249   }
1250
1251   void testSelect(Selector& selector, SelectionTest& test, const Matrix4& localToWorld)
1252   {
1253     test.BeginMesh(localToWorld);
1254
1255     SelectionIntersection best;
1256     aabb_testselect(m_aabb_light, test, best);
1257     if(best.valid())
1258     {
1259       selector.addIntersection(best);
1260     }
1261   }
1262   
1263   void translate(const Vector3& translation)
1264   {
1265     m_aabb_light.origin = origin_translated(m_aabb_light.origin, translation);
1266   }
1267   void rotate(const Quaternion& rotation)
1268   {
1269     rotation_rotate(m_rotation, rotation);
1270   }
1271   void snapto(float snap)
1272   {
1273     if(g_lightType == LIGHTTYPE_DOOM3 && !m_useLightOrigin && !m_traverse.empty())
1274     {
1275       m_useLightOrigin = true;
1276       m_lightOrigin = m_originKey.m_origin;
1277     }
1278
1279     if(m_useLightOrigin)
1280     {
1281       m_lightOrigin = origin_snapped(m_lightOrigin, snap);
1282       writeLightOrigin();
1283     }
1284     else
1285     {
1286       m_originKey.m_origin = origin_snapped(m_originKey.m_origin, snap);
1287       m_originKey.write(&m_entity);
1288     }
1289   }
1290   void setLightRadius(const AABB& aabb)
1291   {
1292     m_aabb_light.origin = aabb.origin;
1293     m_doom3Radius.m_radiusTransformed = aabb.extents;
1294   }
1295   void transformLightRadius(const Matrix4& transform)
1296   {
1297     matrix4_transform_point(transform, m_aabb_light.origin);
1298   }
1299   void revertTransform()
1300   {
1301     m_aabb_light.origin = m_useLightOrigin ? m_lightOrigin : m_originKey.m_origin;
1302     rotation_assign(m_rotation, m_useLightRotation ? m_lightRotation : m_rotationKey.m_rotation);
1303     m_doom3Radius.m_radiusTransformed = m_doom3Radius.m_radius;
1304   }
1305   void freezeTransform()
1306   {
1307     if(g_lightType == LIGHTTYPE_DOOM3 && !m_useLightOrigin && !m_traverse.empty())
1308     {
1309       m_useLightOrigin = true;
1310     }
1311
1312     if(m_useLightOrigin)
1313     {
1314       m_lightOrigin = m_aabb_light.origin;
1315       writeLightOrigin();
1316     }
1317     else
1318     {
1319       m_originKey.m_origin = m_aabb_light.origin;
1320       m_originKey.write(&m_entity);
1321     }
1322
1323     if(g_lightType == LIGHTTYPE_DOOM3)
1324     {
1325       if(!m_useLightRotation && !m_traverse.empty())
1326       {
1327         m_useLightRotation = true;
1328       }
1329
1330       if(m_useLightRotation)
1331       {
1332         rotation_assign(m_lightRotation, m_rotation);
1333         write_rotation(m_lightRotation, &m_entity, "light_rotation");
1334       }
1335
1336       rotation_assign(m_rotationKey.m_rotation, m_rotation);
1337       write_rotation(m_rotationKey.m_rotation, &m_entity);
1338
1339       m_doom3Radius.m_radius = m_doom3Radius.m_radiusTransformed;
1340       if(m_doom3Radius.m_radius == m_doom3Radius.m_defaultRadius)
1341       {
1342         m_entity.setKeyValue("light_radius", "");
1343       }
1344       else
1345       {
1346         write_origin(m_doom3Radius.m_radius, &m_entity, "light_radius");
1347       }
1348     }
1349   }
1350   void transformChanged()
1351   {
1352     revertTransform();
1353     m_evaluateTransform();
1354     updateOrigin();
1355   }
1356   typedef MemberCaller<Light, &Light::transformChanged> TransformChangedCaller;
1357
1358   mutable Matrix4 m_localPivot;
1359   const Matrix4& getLocalPivot() const
1360   {
1361     m_localPivot = rotation_toMatrix(m_rotation);
1362     vector4_to_vector3(m_localPivot.t()) = m_aabb_light.origin;
1363     return m_localPivot;
1364   }
1365
1366   void setLightChangedCallback(const Callback& callback)
1367   {
1368     m_doom3Radius.m_changed = callback;
1369   }
1370
1371   const AABB& aabb() const
1372   {
1373     m_doom3AABB = AABB(m_aabb_light.origin, m_doom3Radius.m_radiusTransformed);
1374     return m_doom3AABB;
1375   }
1376   bool testAABB(const AABB& other) const
1377   {
1378     if(isProjected())
1379     {
1380       Matrix4 transform = rotation();
1381       vector4_to_vector3(transform.t()) = localAABB().origin;
1382       projection();
1383       Frustum frustum(frustum_transformed(m_doom3Frustum, transform));
1384       return frustum_test_aabb(frustum, other) != c_volumeOutside;
1385     }
1386     // test against an AABB which contains the rotated bounds of this light.
1387     const AABB& bounds = aabb();
1388     return aabb_intersects_aabb(other, AABB(
1389       bounds.origin,
1390       Vector3(
1391         static_cast<float>(fabs(m_rotation[0] * bounds.extents[0])
1392                             + fabs(m_rotation[3] * bounds.extents[1])
1393                             + fabs(m_rotation[6] * bounds.extents[2])),
1394         static_cast<float>(fabs(m_rotation[1] * bounds.extents[0])
1395                             + fabs(m_rotation[4] * bounds.extents[1])
1396                             + fabs(m_rotation[7] * bounds.extents[2])),
1397         static_cast<float>(fabs(m_rotation[2] * bounds.extents[0])
1398                             + fabs(m_rotation[5] * bounds.extents[1])
1399                             + fabs(m_rotation[8] * bounds.extents[2]))
1400       )
1401     ));
1402   }
1403
1404   const Matrix4& rotation() const
1405   {
1406     m_doom3Rotation = rotation_toMatrix(m_rotation);
1407     return m_doom3Rotation;
1408   }
1409   const Vector3& offset() const
1410   {
1411     return m_doom3Radius.m_center;
1412   }
1413   const Vector3& colour() const
1414   {
1415     return m_colour.m_colour;
1416   }
1417
1418   bool isProjected() const
1419   {
1420     return m_useLightTarget && m_useLightUp && m_useLightRight;
1421   }
1422   void projectionChanged()
1423   {
1424     m_doom3ProjectionChanged = true;
1425     m_doom3Radius.m_changed();
1426     SceneChangeNotify();
1427   }
1428
1429   const Matrix4& projection() const
1430   {
1431     if(!m_doom3ProjectionChanged)
1432     {
1433       return m_doom3Projection;
1434     }
1435     m_doom3ProjectionChanged = false;
1436     m_doom3Projection = g_matrix4_identity;
1437     matrix4_translate_by_vec3(m_doom3Projection, Vector3(0.5f, 0.5f, 0));
1438     matrix4_scale_by_vec3(m_doom3Projection, Vector3(0.5f, 0.5f, 1));
1439
1440 #if 0
1441     Vector3 right = vector3_cross(m_lightUp, vector3_normalised(m_lightTarget));
1442     Vector3 up = vector3_cross(vector3_normalised(m_lightTarget), m_lightRight);
1443     Vector3 target = m_lightTarget;
1444     Matrix4 test(
1445       -right.x(), -right.y(), -right.z(), 0,
1446       -up.x(), -up.y(), -up.z(), 0,
1447       -target.x(), -target.y(), -target.z(), 0,
1448       0, 0, 0, 1
1449     );
1450     Matrix4 frustum = matrix4_frustum(-0.01, 0.01, -0.01, 0.01, 0.01, 1.0);
1451     test = matrix4_full_inverse(test);
1452     matrix4_premultiply_by_matrix4(test, frustum);
1453     matrix4_multiply_by_matrix4(m_doom3Projection, test);
1454 #elif 0
1455     const float nearFar = 1 / 49.5f;
1456     Vector3 right = vector3_cross(m_lightUp, vector3_normalised(m_lightTarget + m_lightRight));
1457     Vector3 up = vector3_cross(vector3_normalised(m_lightTarget + m_lightUp), m_lightRight);
1458     Vector3 target = vector3_negated(m_lightTarget * (1 + nearFar));
1459     float scale = -1 / vector3_length(m_lightTarget);
1460     Matrix4 test(
1461       -inverse(right.x()), -inverse(up.x()), -inverse(target.x()), 0,
1462       -inverse(right.y()), -inverse(up.y()), -inverse(target.y()), 0,
1463       -inverse(right.z()), -inverse(up.z()), -inverse(target.z()), scale,
1464       0, 0, -nearFar, 0
1465     );
1466     matrix4_multiply_by_matrix4(m_doom3Projection, test);
1467 #elif 0
1468     Vector3 leftA(m_lightTarget - m_lightRight);
1469     Vector3 leftB(m_lightRight + m_lightUp);
1470     Plane3 left(vector3_normalised(vector3_cross(leftA, leftB)) * (1.0 / 128), 0);
1471     Vector3 rightA(m_lightTarget + m_lightRight);
1472     Vector3 rightB(vector3_cross(rightA, m_lightTarget));
1473     Plane3 right(vector3_normalised(vector3_cross(rightA, rightB)) * (1.0 / 128), 0);
1474     Vector3 bottomA(m_lightTarget - m_lightUp);
1475     Vector3 bottomB(vector3_cross(bottomA, m_lightTarget));
1476     Plane3 bottom(vector3_normalised(vector3_cross(bottomA, bottomB)) * (1.0 / 128), 0);
1477     Vector3 topA(m_lightTarget + m_lightUp);
1478     Vector3 topB(vector3_cross(topA, m_lightTarget));
1479     Plane3 top(vector3_normalised(vector3_cross(topA, topB)) * (1.0 / 128), 0);
1480     Plane3 front(vector3_normalised(m_lightTarget) * (1.0 / 128), 1);
1481     Plane3 back(vector3_normalised(vector3_negated(m_lightTarget)) * (1.0 / 128), 0);
1482     Matrix4 test(matrix4_from_planes(plane3_flipped(left), plane3_flipped(right), plane3_flipped(bottom), plane3_flipped(top), plane3_flipped(front), plane3_flipped(back)));
1483     matrix4_multiply_by_matrix4(m_doom3Projection, test);
1484 #else
1485
1486     Plane3 lightProject[4];
1487
1488     Vector3 start = m_useLightStart && m_useLightEnd ? m_lightStart : vector3_normalised(m_lightTarget);
1489     Vector3 stop = m_useLightStart && m_useLightEnd ? m_lightEnd : m_lightTarget;
1490
1491           float rLen = vector3_length(m_lightRight);
1492           Vector3 right = vector3_divided(m_lightRight, rLen);
1493           float uLen = vector3_length(m_lightUp);
1494           Vector3 up = vector3_divided(m_lightUp, uLen);
1495           Vector3 normal = vector3_normalised(vector3_cross(up, right));
1496
1497           float dist = vector3_dot(m_lightTarget, normal);
1498           if ( dist < 0 ) {
1499                   dist = -dist;
1500                   normal = vector3_negated(normal);
1501           }
1502
1503           right *= ( 0.5f * dist ) / rLen;
1504           up *= -( 0.5f * dist ) / uLen;
1505
1506           lightProject[2] = Plane3(normal, 0);
1507           lightProject[0] = Plane3(right, 0);
1508           lightProject[1] = Plane3(up, 0);
1509
1510           // now offset to center
1511           Vector4 targetGlobal(m_lightTarget, 1);
1512     {
1513       float a = vector4_dot(targetGlobal, plane3_to_vector4(lightProject[0]));
1514       float b = vector4_dot(targetGlobal, plane3_to_vector4(lightProject[2]));
1515             float ofs = 0.5f - a / b;
1516             plane3_to_vector4(lightProject[0]) += plane3_to_vector4(lightProject[2]) * ofs;
1517     }
1518     {
1519       float a = vector4_dot(targetGlobal, plane3_to_vector4(lightProject[1]));
1520       float b = vector4_dot(targetGlobal, plane3_to_vector4(lightProject[2]));
1521             float ofs = 0.5f - a / b;
1522             plane3_to_vector4(lightProject[1]) += plane3_to_vector4(lightProject[2]) * ofs;
1523     }
1524
1525           // set the falloff vector
1526           Vector3 falloff = stop - start;
1527           float length = vector3_length(falloff);
1528     falloff = vector3_divided(falloff, length);
1529           if ( length <= 0 ) {
1530                   length = 1;
1531           }
1532     falloff *= (1.0f / length);
1533           lightProject[3] = Plane3(falloff, -vector3_dot(start, falloff));
1534
1535           // we want the planes of s=0, s=q, t=0, and t=q
1536           m_doom3Frustum.left = lightProject[0];
1537           m_doom3Frustum.bottom = lightProject[1];
1538           m_doom3Frustum.right = Plane3(lightProject[2].normal() - lightProject[0].normal(), lightProject[2].dist() - lightProject[0].dist());
1539           m_doom3Frustum.top = Plane3(lightProject[2].normal() - lightProject[1].normal(), lightProject[2].dist() - lightProject[1].dist());
1540
1541           // we want the planes of s=0 and s=1 for front and rear clipping planes
1542           m_doom3Frustum.front = lightProject[3];
1543
1544           m_doom3Frustum.back = lightProject[3];
1545           m_doom3Frustum.back.dist() -= 1.0f;
1546           m_doom3Frustum.back = plane3_flipped(m_doom3Frustum.back);
1547
1548     Matrix4 test(matrix4_from_planes(m_doom3Frustum.left, m_doom3Frustum.right, m_doom3Frustum.bottom, m_doom3Frustum.top, m_doom3Frustum.front, m_doom3Frustum.back));
1549     matrix4_multiply_by_matrix4(m_doom3Projection, test);
1550
1551     m_doom3Frustum.left = plane3_normalised(m_doom3Frustum.left);
1552     m_doom3Frustum.right = plane3_normalised(m_doom3Frustum.right);
1553     m_doom3Frustum.bottom = plane3_normalised(m_doom3Frustum.bottom);
1554     m_doom3Frustum.top = plane3_normalised(m_doom3Frustum.top);
1555     m_doom3Frustum.back = plane3_normalised(m_doom3Frustum.back);
1556     m_doom3Frustum.front = plane3_normalised(m_doom3Frustum.front);
1557 #endif
1558     //matrix4_scale_by_vec3(m_doom3Projection, Vector3(1.0 / 128, 1.0 / 128, 1.0 / 128));
1559     return m_doom3Projection;
1560   }
1561
1562   Shader* getShader() const
1563   {
1564     return m_shader.get();
1565   }
1566 };
1567
1568 class LightInstance :
1569   public TargetableInstance,
1570   public TransformModifier,
1571   public Renderable,
1572   public SelectionTestable,
1573   public RendererLight,
1574   public PlaneSelectable,
1575   public ComponentSelectionTestable
1576 {
1577   class TypeCasts
1578   {
1579     InstanceTypeCastTable m_casts;
1580   public:
1581     TypeCasts()
1582     {
1583       m_casts = TargetableInstance::StaticTypeCasts::instance().get();
1584       InstanceContainedCast<LightInstance, Bounded>::install(m_casts);
1585       //InstanceContainedCast<LightInstance, Cullable>::install(m_casts);
1586       InstanceStaticCast<LightInstance, Renderable>::install(m_casts);
1587       InstanceStaticCast<LightInstance, SelectionTestable>::install(m_casts);
1588       InstanceStaticCast<LightInstance, Transformable>::install(m_casts);
1589       InstanceStaticCast<LightInstance, PlaneSelectable>::install(m_casts);
1590       InstanceStaticCast<LightInstance, ComponentSelectionTestable>::install(m_casts);
1591       InstanceIdentityCast<LightInstance>::install(m_casts);
1592     }
1593     InstanceTypeCastTable& get()
1594     {
1595       return m_casts;
1596     }
1597   };
1598
1599   Light& m_contained;
1600   DragPlanes m_dragPlanes;// dragplanes for lightresizing using mousedrag
1601 public:
1602   typedef LazyStatic<TypeCasts> StaticTypeCasts;
1603
1604   Bounded& get(NullType<Bounded>)
1605   {
1606     return m_contained;
1607   }
1608
1609   STRING_CONSTANT(Name, "LightInstance");
1610
1611   LightInstance(const scene::Path& path, scene::Instance* parent, Light& contained) :
1612     TargetableInstance(path, parent, this, StaticTypeCasts::instance().get(), contained.getEntity(), *this),
1613     TransformModifier(Light::TransformChangedCaller(contained), ApplyTransformCaller(*this)),
1614     m_contained(contained),
1615     m_dragPlanes(SelectedChangedComponentCaller(*this))
1616   {
1617     m_contained.instanceAttach(Instance::path());
1618
1619     if(g_lightType == LIGHTTYPE_DOOM3)
1620     {
1621       GlobalShaderCache().attach(*this);
1622       m_contained.setLightChangedCallback(LightChangedCaller(*this));
1623     }
1624
1625     StaticRenderableConnectionLines::instance().attach(*this);
1626   }
1627   ~LightInstance()
1628   {
1629     StaticRenderableConnectionLines::instance().detach(*this);
1630
1631     if(g_lightType == LIGHTTYPE_DOOM3)
1632     {
1633       m_contained.setLightChangedCallback(Callback());
1634       GlobalShaderCache().detach(*this);
1635     }
1636
1637     m_contained.instanceDetach(Instance::path());
1638   }
1639   void renderSolid(Renderer& renderer, const VolumeTest& volume) const
1640   {
1641     m_contained.renderSolid(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
1642   }
1643   void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
1644   {
1645     m_contained.renderWireframe(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
1646   }
1647   void testSelect(Selector& selector, SelectionTest& test)
1648   {
1649     m_contained.testSelect(selector, test, Instance::localToWorld());
1650   }
1651
1652   void selectPlanes(Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback)
1653   {
1654     test.BeginMesh(localToWorld());
1655     m_dragPlanes.selectPlanes(m_contained.aabb(), selector, test, selectedPlaneCallback, rotation());
1656   }
1657   void selectReversedPlanes(Selector& selector, const SelectedPlanes& selectedPlanes)
1658   {
1659     m_dragPlanes.selectReversedPlanes(m_contained.aabb(), selector, selectedPlanes, rotation());
1660   }
1661   
1662   bool isSelectedComponents() const
1663   {
1664     return m_dragPlanes.isSelected();
1665   }
1666   void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode)
1667   {
1668     if(mode == SelectionSystem::eFace)
1669     {
1670       m_dragPlanes.setSelected(false);
1671     }
1672   }
1673   void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode)
1674   {
1675   }
1676
1677   void selectedChangedComponent(const Selectable& selectable)
1678   {
1679     GlobalSelectionSystem().getObserver(SelectionSystem::eComponent)(selectable);
1680     GlobalSelectionSystem().onComponentSelection(*this, selectable);
1681   }
1682   typedef MemberCaller1<LightInstance, const Selectable&, &LightInstance::selectedChangedComponent> SelectedChangedComponentCaller;
1683
1684   void evaluateTransform()
1685   {
1686     if(getType() == TRANSFORM_PRIMITIVE)
1687     {
1688       m_contained.translate(getTranslation());
1689       m_contained.rotate(getRotation());
1690     }
1691     else
1692     {
1693       //globalOutputStream() << getTranslation() << "\n";
1694
1695       m_dragPlanes.m_bounds = m_contained.aabb();
1696       m_contained.setLightRadius(m_dragPlanes.evaluateResize(getTranslation(), rotation()));
1697     }
1698   }
1699   void applyTransform()
1700   {
1701     m_contained.revertTransform();
1702     evaluateTransform();
1703     m_contained.freezeTransform();
1704   }
1705   typedef MemberCaller<LightInstance, &LightInstance::applyTransform> ApplyTransformCaller;
1706
1707   void lightChanged()
1708   {
1709     GlobalShaderCache().changed(*this);
1710   }
1711   typedef MemberCaller<LightInstance, &LightInstance::lightChanged> LightChangedCaller;
1712
1713   Shader* getShader() const
1714   {
1715     return m_contained.getShader();
1716   }
1717   const AABB& aabb() const
1718   {
1719     return m_contained.aabb();
1720   }
1721   bool testAABB(const AABB& other) const
1722   {
1723     return m_contained.testAABB(other);
1724   }
1725   const Matrix4& rotation() const
1726   {
1727     return m_contained.rotation();
1728   }
1729   const Vector3& offset() const
1730   {
1731     return m_contained.offset();
1732   }
1733   const Vector3& colour() const
1734   {
1735     return m_contained.colour();
1736   }
1737
1738   bool isProjected() const
1739   {
1740     return m_contained.isProjected();
1741   }
1742   const Matrix4& projection() const
1743   {
1744     return m_contained.projection();
1745   }
1746 };
1747
1748 class LightNode :
1749   public scene::Node::Symbiot,
1750   public scene::Instantiable,
1751   public scene::Cloneable,
1752   public scene::Traversable::Observer
1753 {
1754   class TypeCasts
1755   {
1756     NodeTypeCastTable m_casts;
1757   public:
1758     TypeCasts()
1759     {
1760       NodeStaticCast<LightNode, scene::Instantiable>::install(m_casts);
1761       NodeStaticCast<LightNode, scene::Cloneable>::install(m_casts);
1762       if(g_lightType == LIGHTTYPE_DOOM3)
1763       {
1764         NodeContainedCast<LightNode, scene::Traversable>::install(m_casts);
1765       }
1766       NodeContainedCast<LightNode, Editable>::install(m_casts);
1767       NodeContainedCast<LightNode, Snappable>::install(m_casts);
1768       NodeContainedCast<LightNode, TransformNode>::install(m_casts);
1769       NodeContainedCast<LightNode, Entity>::install(m_casts);
1770       NodeContainedCast<LightNode, Nameable>::install(m_casts);
1771       NodeContainedCast<LightNode, Namespaced>::install(m_casts);
1772     }
1773     NodeTypeCastTable& get()
1774     {
1775       return m_casts;
1776     }
1777   };
1778
1779
1780   scene::Node m_node;
1781   InstanceSet m_instances;
1782   Light m_contained;
1783
1784   void construct()
1785   {
1786     if(g_lightType == LIGHTTYPE_DOOM3)
1787     {
1788       m_contained.attach(this);
1789     }
1790   }
1791   void destroy()
1792   {
1793     if(g_lightType == LIGHTTYPE_DOOM3)
1794     {
1795       m_contained.detach(this);
1796     }
1797   }
1798 public:
1799   typedef LazyStatic<TypeCasts> StaticTypeCasts;
1800
1801   scene::Traversable& get(NullType<scene::Traversable>)
1802   {
1803     return m_contained.getTraversable();
1804   }
1805   Editable& get(NullType<Editable>)
1806   {
1807     return m_contained;
1808   }
1809   Snappable& get(NullType<Snappable>)
1810   {
1811     return m_contained;
1812   }
1813   TransformNode& get(NullType<TransformNode>)
1814   {
1815     return m_contained.getTransformNode();
1816   }
1817   Entity& get(NullType<Entity>)
1818   {
1819     return m_contained.getEntity();
1820   }
1821   Nameable& get(NullType<Nameable>)
1822   {
1823     return m_contained.getNameable();
1824   }
1825   Namespaced& get(NullType<Namespaced>)
1826   {
1827     return m_contained.getNamespaced();
1828   }
1829
1830   LightNode(EntityClass* eclass) :
1831     m_node(this, this, StaticTypeCasts::instance().get()),
1832     m_contained(eclass, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSet::BoundsChangedCaller(m_instances), InstanceSetEvaluateTransform<LightInstance>::Caller(m_instances))
1833   {
1834     construct();
1835   }
1836   LightNode(const LightNode& other) :
1837     scene::Node::Symbiot(other),
1838     scene::Instantiable(other),
1839     scene::Cloneable(other),
1840     scene::Traversable::Observer(other),
1841     m_node(this, this, StaticTypeCasts::instance().get()),
1842     m_contained(other.m_contained, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSet::BoundsChangedCaller(m_instances), InstanceSetEvaluateTransform<LightInstance>::Caller(m_instances))
1843   {
1844     construct();
1845   }
1846   ~LightNode()
1847   {
1848     destroy();
1849   }
1850
1851   void release()
1852   {
1853     delete this;
1854   }
1855   scene::Node& node()
1856   {
1857     return m_node;
1858   }
1859
1860   scene::Node& clone() const
1861   {
1862     return (new LightNode(*this))->node();
1863   }
1864
1865   void insert(scene::Node& child)
1866   {
1867     m_instances.insert(child);
1868   }
1869   void erase(scene::Node& child)
1870   {
1871     m_instances.erase(child);
1872   }
1873
1874   scene::Instance* create(const scene::Path& path, scene::Instance* parent)
1875   {
1876     return new LightInstance(path, parent, m_contained);
1877   }
1878   void forEachInstance(const scene::Instantiable::Visitor& visitor)
1879   {
1880     m_instances.forEachInstance(visitor);
1881   }
1882   void insert(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance)
1883   {
1884     m_instances.insert(observer, path, instance);
1885   }
1886   scene::Instance* erase(scene::Instantiable::Observer* observer, const scene::Path& path)
1887   {
1888     return m_instances.erase(observer, path);
1889   }
1890 };
1891
1892 void Light_Construct(LightType lightType)
1893 {
1894   g_lightType = lightType;
1895   if(g_lightType == LIGHTTYPE_DOOM3)
1896   {
1897     LightShader::m_defaultShader = "lights/defaultPointLight";
1898 #if 0
1899     LightShader::m_defaultShader = "lights/defaultProjectedLight";
1900 #endif
1901   }
1902   RenderLightRadiiFill::m_state = GlobalShaderCache().capture("$Q3MAP2_LIGHT_SPHERE");
1903   RenderLightCenter::m_state = GlobalShaderCache().capture("$BIGPOINT");
1904 }
1905 void Light_Destroy()
1906 {
1907   GlobalShaderCache().release("$Q3MAP2_LIGHT_SPHERE");
1908   GlobalShaderCache().release("$BIGPOINT");
1909 }
1910
1911 scene::Node& New_Light(EntityClass* eclass)
1912 {
1913   return (new LightNode(eclass))->node();
1914 }