2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
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.
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.
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
23 ///\brief Represents any light entity (e.g. light).
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.
42 #include "renderable.h"
45 #include "math/frustum.h"
46 #include "selectionlib.h"
47 #include "instancelib.h"
48 #include "transformlib.h"
49 #include "entitylib.h"
51 #include "eclasslib.h"
54 #include "traverselib.h"
56 #include "targetable.h"
60 #include "namedentity.h"
61 #include "keyobservers.h"
66 extern bool g_newLightDraw;
69 void sphere_draw_fill(const Vector3& origin, float radius, int sides)
74 const double dt = c_2pi / static_cast<double>(sides);
75 const double dp = c_pi / static_cast<double>(sides);
77 glBegin(GL_TRIANGLES);
78 for (int i = 0; i <= sides - 1; ++i)
80 for (int j = 0; j <= sides - 2; ++j)
82 const double t = i * dt;
83 const double p = (j * dp) - (c_pi / 2.0);
86 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p), radius)));
87 glVertex3fv(vector3_to_array(v));
91 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p + dp), radius)));
92 glVertex3fv(vector3_to_array(v));
96 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p + dp), radius)));
97 glVertex3fv(vector3_to_array(v));
101 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p), radius)));
102 glVertex3fv(vector3_to_array(v));
106 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p + dp), radius)));
107 glVertex3fv(vector3_to_array(v));
111 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p), radius)));
112 glVertex3fv(vector3_to_array(v));
118 const double p = (sides - 1) * dp - (c_pi / 2.0);
119 for (int i = 0; i <= sides - 1; ++i)
121 const double t = i * dt;
124 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p), radius)));
125 glVertex3fv(vector3_to_array(v));
129 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p + dp), radius)));
130 glVertex3fv(vector3_to_array(v));
134 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p), radius)));
135 glVertex3fv(vector3_to_array(v));
142 void sphere_draw_wire(const Vector3& origin, float radius, int sides)
145 glBegin(GL_LINE_LOOP);
147 for (int i = 0; i <= sides; i++)
149 double ds = sin((i * 2 * c_pi) / sides);
150 double dc = cos((i * 2 * c_pi) / sides);
153 static_cast<float>(origin[0] + radius * dc),
154 static_cast<float>(origin[1] + radius * ds),
163 glBegin(GL_LINE_LOOP);
165 for (int i = 0; i <= sides; i++)
167 double ds = sin((i * 2 * c_pi) / sides);
168 double dc = cos((i * 2 * c_pi) / sides);
171 static_cast<float>(origin[0] + radius * dc),
173 static_cast<float>(origin[2] + radius * ds)
181 glBegin(GL_LINE_LOOP);
183 for (int i = 0; i <= sides; i++)
185 double ds = sin((i * 2 * c_pi) / sides);
186 double dc = cos((i * 2 * c_pi) / sides);
190 static_cast<float>(origin[1] + radius * dc),
191 static_cast<float>(origin[2] + radius * ds)
199 void light_draw_box_lines(const Vector3& origin, const Vector3 points[8])
201 //draw lines from the center of the bbox to the corners
204 glVertex3fv(vector3_to_array(origin));
205 glVertex3fv(vector3_to_array(points[1]));
207 glVertex3fv(vector3_to_array(origin));
208 glVertex3fv(vector3_to_array(points[5]));
210 glVertex3fv(vector3_to_array(origin));
211 glVertex3fv(vector3_to_array(points[2]));
213 glVertex3fv(vector3_to_array(origin));
214 glVertex3fv(vector3_to_array(points[6]));
216 glVertex3fv(vector3_to_array(origin));
217 glVertex3fv(vector3_to_array(points[0]));
219 glVertex3fv(vector3_to_array(origin));
220 glVertex3fv(vector3_to_array(points[4]));
222 glVertex3fv(vector3_to_array(origin));
223 glVertex3fv(vector3_to_array(points[3]));
225 glVertex3fv(vector3_to_array(origin));
226 glVertex3fv(vector3_to_array(points[7]));
231 void light_draw_radius_wire(const Vector3& origin, const float envelope[3])
234 sphere_draw_wire(origin, envelope[0], 24);
236 sphere_draw_wire(origin, envelope[1], 24);
238 sphere_draw_wire(origin, envelope[2], 24);
241 void light_draw_radius_fill(const Vector3& origin, const float envelope[3])
244 sphere_draw_fill(origin, envelope[0], 16);
246 sphere_draw_fill(origin, envelope[1], 16);
248 sphere_draw_fill(origin, envelope[2], 16);
251 void light_vertices(const AABB& aabb_light, Vector3 points[6])
253 Vector3 max(vector3_added(aabb_light.origin, aabb_light.extents));
254 Vector3 min(vector3_subtracted(aabb_light.origin, aabb_light.extents));
255 Vector3 mid(aabb_light.origin);
257 // top, bottom, tleft, tright, bright, bleft
258 points[0] = Vector3(mid[0], mid[1], max[2]);
259 points[1] = Vector3(mid[0], mid[1], min[2]);
260 points[2] = Vector3(min[0], max[1], mid[2]);
261 points[3] = Vector3(max[0], max[1], mid[2]);
262 points[4] = Vector3(max[0], min[1], mid[2]);
263 points[5] = Vector3(min[0], min[1], mid[2]);
266 void light_draw(const AABB& aabb_light, RenderStateFlags state)
269 light_vertices(aabb_light, points);
271 if(state & RENDER_LIGHTING)
273 const float f = 0.70710678f;
274 // North, East, South, West
275 const Vector3 normals[8] = {
286 #if !defined(USE_TRIANGLE_FAN)
287 glBegin(GL_TRIANGLES);
289 glBegin(GL_TRIANGLE_FAN);
291 glVertex3fv(vector3_to_array(points[0]));
292 glVertex3fv(vector3_to_array(points[2]));
293 glNormal3fv(vector3_to_array(normals[0]));
294 glVertex3fv(vector3_to_array(points[3]));
296 #if !defined(USE_TRIANGLE_FAN)
297 glVertex3fv(vector3_to_array(points[0]));
298 glVertex3fv(vector3_to_array(points[3]));
300 glNormal3fv(vector3_to_array(normals[1]));
301 glVertex3fv(vector3_to_array(points[4]));
303 #if !defined(USE_TRIANGLE_FAN)
304 glVertex3fv(vector3_to_array(points[0]));
305 glVertex3fv(vector3_to_array(points[4]));
307 glNormal3fv(vector3_to_array(normals[2]));
308 glVertex3fv(vector3_to_array(points[5]));
309 #if !defined(USE_TRIANGLE_FAN)
310 glVertex3fv(vector3_to_array(points[0]));
311 glVertex3fv(vector3_to_array(points[5]));
313 glNormal3fv(vector3_to_array(normals[3]));
314 glVertex3fv(vector3_to_array(points[2]));
315 #if defined(USE_TRIANGLE_FAN)
317 glBegin(GL_TRIANGLE_FAN);
320 glVertex3fv(vector3_to_array(points[1]));
321 glVertex3fv(vector3_to_array(points[2]));
322 glNormal3fv(vector3_to_array(normals[7]));
323 glVertex3fv(vector3_to_array(points[5]));
325 #if !defined(USE_TRIANGLE_FAN)
326 glVertex3fv(vector3_to_array(points[1]));
327 glVertex3fv(vector3_to_array(points[5]));
329 glNormal3fv(vector3_to_array(normals[6]));
330 glVertex3fv(vector3_to_array(points[4]));
332 #if !defined(USE_TRIANGLE_FAN)
333 glVertex3fv(vector3_to_array(points[1]));
334 glVertex3fv(vector3_to_array(points[4]));
336 glNormal3fv(vector3_to_array(normals[5]));
337 glVertex3fv(vector3_to_array(points[3]));
339 #if !defined(USE_TRIANGLE_FAN)
340 glVertex3fv(vector3_to_array(points[1]));
341 glVertex3fv(vector3_to_array(points[3]));
343 glNormal3fv(vector3_to_array(normals[4]));
344 glVertex3fv(vector3_to_array(points[2]));
350 typedef unsigned int index_t;
351 const index_t indices[24] = {
362 glVertexPointer(3, GL_FLOAT, 0, points);
363 glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(index_t), RenderIndexTypeID, indices);
365 glBegin(GL_TRIANGLES);
366 for(unsigned int i = 0; i < sizeof(indices)/sizeof(index_t); ++i)
368 glVertex3fv(points[indices[i]]);
375 // NOTE: prolly not relevant until some time..
376 // check for DOOM lights
378 if (strlen(ValueForKey(e, "light_right")) > 0) {
379 vec3_t vRight, vUp, vTarget, vTemp;
380 GetVectorForKey (e, "light_right", vRight);
381 GetVectorForKey (e, "light_up", vUp);
382 GetVectorForKey (e, "light_target", vTarget);
385 glBegin(GL_LINE_LOOP);
386 VectorAdd(vTarget, e->origin, vTemp);
387 VectorAdd(vTemp, vRight, vTemp);
388 VectorAdd(vTemp, vUp, vTemp);
389 glVertex3fv(e->origin);
391 VectorAdd(vTarget, e->origin, vTemp);
392 VectorAdd(vTemp, vUp, vTemp);
393 VectorSubtract(vTemp, vRight, vTemp);
394 glVertex3fv(e->origin);
396 VectorAdd(vTarget, e->origin, vTemp);
397 VectorAdd(vTemp, vRight, vTemp);
398 VectorSubtract(vTemp, vUp, vTemp);
399 glVertex3fv(e->origin);
401 VectorAdd(vTarget, e->origin, vTemp);
402 VectorSubtract(vTemp, vUp, vTemp);
403 VectorSubtract(vTemp, vRight, vTemp);
404 glVertex3fv(e->origin);
412 // These variables are tweakable on the q3map2 console, setting to q3map2
413 // default here as there is no way to find out what the user actually uses
414 // right now. Maybe move them to worldspawn?
415 float fPointScale = 7500.f;
416 float fLinearScale = 1.f / 8000.f;
418 float light_radius_linear(float fIntensity, float fFalloffTolerance)
420 return ((fIntensity * fPointScale * fLinearScale) - fFalloffTolerance);
423 float light_radius(float fIntensity, float fFalloffTolerance)
425 return sqrt(fIntensity * fPointScale / fFalloffTolerance);
429 LightType g_lightType = LIGHTTYPE_DEFAULT;
432 bool spawnflags_linear(int flags)
434 if( g_lightType == LIGHTTYPE_RTCW )
458 float m_primaryIntensity;
459 float m_secondaryIntensity;
464 void calculateRadii()
466 float intensity = 300.0f;
468 if(m_primaryIntensity != 0.0f)
470 intensity = m_primaryIntensity;
472 else if(m_secondaryIntensity != 0.0f)
474 intensity = m_secondaryIntensity;
477 intensity *= m_scale;
479 if(spawnflags_linear(m_flags))
481 m_radii[0] = light_radius_linear(intensity, 1.0f) / m_fade;
482 m_radii[1] = light_radius_linear(intensity, 48.0f) / m_fade;
483 m_radii[2] = light_radius_linear(intensity, 255.0f) / m_fade;
487 m_radii[0] = light_radius(intensity, 1.0f);
488 m_radii[1] = light_radius(intensity, 48.0f);
489 m_radii[2] = light_radius(intensity, 255.0f);
494 LightRadii() : m_primaryIntensity(0), m_secondaryIntensity(0), m_flags(0), m_fade(1), m_scale(1)
499 void primaryIntensityChanged(const char* value)
501 m_primaryIntensity = string_read_float(value);
504 typedef MemberCaller1<LightRadii, const char*, &LightRadii::primaryIntensityChanged> PrimaryIntensityChangedCaller;
505 void secondaryIntensityChanged(const char* value)
507 m_secondaryIntensity = string_read_float(value);
510 typedef MemberCaller1<LightRadii, const char*, &LightRadii::secondaryIntensityChanged> SecondaryIntensityChangedCaller;
511 void scaleChanged(const char* value)
513 m_scale = string_read_float(value);
520 typedef MemberCaller1<LightRadii, const char*, &LightRadii::scaleChanged> ScaleChangedCaller;
521 void fadeChanged(const char* value)
523 m_fade = string_read_float(value);
530 typedef MemberCaller1<LightRadii, const char*, &LightRadii::fadeChanged> FadeChangedCaller;
531 void flagsChanged(const char* value)
533 m_flags = string_read_int(value);
536 typedef MemberCaller1<LightRadii, const char*, &LightRadii::flagsChanged> FlagsChangedCaller;
539 const Vector3 c_defaultDoom3LightRadius = Vector3(300, 300, 300);
540 class Doom3LightRadius
548 Doom3LightRadius() : m_radius(c_defaultDoom3LightRadius), m_center(0, 0, 0), m_useCenterKey(false)
552 void lightRadiusChanged(const char* value)
554 if(!string_parse_vector3(value, m_radius))
556 m_radius = c_defaultDoom3LightRadius;
561 typedef MemberCaller1<Doom3LightRadius, const char*, &Doom3LightRadius::lightRadiusChanged> LightRadiusChangedCaller;
563 void lightCenterChanged(const char* value)
565 m_useCenterKey = string_parse_vector3(value, m_center);
568 m_center = Vector3(0, 0, 0);
572 typedef MemberCaller1<Doom3LightRadius, const char*, &Doom3LightRadius::lightCenterChanged> LightCenterChangedCaller;
575 class RenderLightRadiiWire : public OpenGLRenderable
578 const Vector3& m_origin;
580 RenderLightRadiiWire(LightRadii& radii, const Vector3& origin) : m_radii(radii), m_origin(origin)
583 void render(RenderStateFlags state) const
585 light_draw_radius_wire(m_origin, m_radii.m_radii);
589 class RenderLightRadiiFill : public OpenGLRenderable
592 const Vector3& m_origin;
594 static Shader* m_state;
596 RenderLightRadiiFill(LightRadii& radii, const Vector3& origin) : m_radii(radii), m_origin(origin)
599 void render(RenderStateFlags state) const
601 light_draw_radius_fill(m_origin, m_radii.m_radii);
605 class RenderLightRadiiBox : public OpenGLRenderable
607 const Vector3& m_origin;
609 mutable Vector3 m_points[8];
610 static Shader* m_state;
612 RenderLightRadiiBox(const Vector3& origin) : m_origin(origin)
615 void render(RenderStateFlags state) const
617 //draw the bounding box of light based on light_radius key
618 if((state & RENDER_FILL) != 0)
620 aabb_draw_flatshade(m_points);
624 aabb_draw_wire(m_points);
627 #if 1 //disable if you dont want lines going from the center of the light bbox to the corners
628 light_draw_box_lines(m_origin, m_points);
633 Shader* RenderLightRadiiFill::m_state = 0;
635 class RenderLightCenter : public OpenGLRenderable
637 const Vector3& m_center;
638 EntityClass& m_eclass;
640 static Shader* m_state;
642 RenderLightCenter(const Vector3& center, EntityClass& eclass) : m_center(center), m_eclass(eclass)
645 void render(RenderStateFlags state) const
648 glColor3fv(vector3_to_array(m_eclass.color));
649 glVertex3fv(vector3_to_array(m_center));
654 Shader* RenderLightCenter::m_state = 0;
656 class RenderLightProjection : public OpenGLRenderable
658 const Matrix4& m_projection;
661 RenderLightProjection(const Matrix4& projection) : m_projection(projection)
664 void render(RenderStateFlags state) const
666 Matrix4 unproject(matrix4_full_inverse(m_projection));
668 aabb_corners(AABB(Vector3(0.5f, 0.5f, 0.5f), Vector3(0.5f, 0.5f, 0.5f)), points);
669 points[0] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[0], 1)));
670 points[1] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[1], 1)));
671 points[2] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[2], 1)));
672 points[3] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[3], 1)));
673 points[4] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[4], 1)));
674 points[5] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[5], 1)));
675 points[6] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[6], 1)));
676 points[7] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[7], 1)));
677 Vector4 test1 = matrix4_transformed_vector4(unproject, Vector4(0.5f, 0.5f, 0.5f, 1));
678 Vector3 test2 = vector4_projected(test1);
679 aabb_draw_wire(points);
683 inline void default_extents(Vector3& extents)
685 extents = Vector3(8, 8, 8);
694 m_shader = GlobalShaderCache().capture(m_name.c_str());
698 GlobalShaderCache().release(m_name.c_str());
709 void setName(const char* name)
726 m_shader.setName(m_defaultShader);
729 static const char* m_defaultShader;
735 void valueChanged(const char* value)
737 if(string_empty(value))
743 m_shader.setName(value);
747 typedef MemberCaller1<LightShader, const char*, &LightShader::valueChanged> ValueChangedCaller;
751 return m_shader.get();
755 const char* LightShader::m_defaultShader = "";
757 inline const BasicVector4<double>& plane3_to_vector4(const Plane3& self)
759 return reinterpret_cast<const BasicVector4<double>&>(self);
762 inline BasicVector4<double>& plane3_to_vector4(Plane3& self)
764 return reinterpret_cast<BasicVector4<double>&>(self);
767 inline Matrix4 matrix4_from_planes(const Plane3& left, const Plane3& right, const Plane3& bottom, const Plane3& top, const Plane3& front, const Plane3& back)
770 (right.a - left.a) / 2,
771 (top.a - bottom.a) / 2,
772 (back.a - front.a) / 2,
773 right.a - (right.a - left.a) / 2,
774 (right.b - left.b) / 2,
775 (top.b - bottom.b) / 2,
776 (back.b - front.b) / 2,
777 right.b - (right.b - left.b) / 2,
778 (right.c - left.c) / 2,
779 (top.c - bottom.c) / 2,
780 (back.c - front.c) / 2,
781 right.c - (right.c - left.c) / 2,
782 (right.d - left.d) / 2,
783 (top.d - bottom.d) / 2,
784 (back.d - front.d) / 2,
785 right.d - (right.d - left.d) / 2
790 public OpenGLRenderable,
796 EntityKeyValues m_entity;
797 KeyObserverMap m_keyObservers;
798 TraversableNodeSet m_traverse;
799 IdentityTransform m_transform;
801 OriginKey m_originKey;
802 RotationKey m_rotationKey;
806 ClassnameFilter m_filter;
809 TraversableObserverPairRelay m_traverseObservers;
810 Doom3GroupOrigin m_funcStaticOrigin;
813 Doom3LightRadius m_doom3Radius;
815 RenderLightRadiiWire m_radii_wire;
816 RenderLightRadiiFill m_radii_fill;
817 RenderLightRadiiBox m_radii_box;
818 RenderLightCenter m_render_center;
819 RenderableNamedEntity m_renderName;
821 Vector3 m_lightOrigin;
822 bool m_useLightOrigin;
823 Float9 m_lightRotation;
824 bool m_useLightRotation;
826 Vector3 m_lightTarget;
827 bool m_useLightTarget;
830 Vector3 m_lightRight;
831 bool m_useLightRight;
832 Vector3 m_lightStart;
833 bool m_useLightStart;
837 mutable AABB m_doom3AABB;
838 mutable Matrix4 m_doom3Rotation;
839 mutable Matrix4 m_doom3Projection;
840 mutable Frustum m_doom3Frustum;
841 mutable bool m_doom3ProjectionChanged;
843 RenderLightProjection m_renderProjection;
845 LightShader m_shader;
849 Callback m_transformChanged;
850 Callback m_boundsChanged;
851 Callback m_evaluateTransform;
855 default_rotation(m_rotation);
856 m_aabb_light.origin = Vector3(0, 0, 0);
857 default_extents(m_aabb_light.extents);
859 m_keyObservers.insert("classname", ClassnameFilter::ClassnameChangedCaller(m_filter));
860 m_keyObservers.insert(Static<KeyIsName>::instance().m_nameKey, NamedEntity::IdentifierChangedCaller(m_named));
861 m_keyObservers.insert("_color", Colour::ColourChangedCaller(m_colour));
862 m_keyObservers.insert("origin", OriginKey::OriginChangedCaller(m_originKey));
863 m_keyObservers.insert("_light", LightRadii::PrimaryIntensityChangedCaller(m_radii));
864 m_keyObservers.insert("light", LightRadii::SecondaryIntensityChangedCaller(m_radii));
865 m_keyObservers.insert("fade", LightRadii::FadeChangedCaller(m_radii));
866 m_keyObservers.insert("scale", LightRadii::ScaleChangedCaller(m_radii));
867 m_keyObservers.insert("spawnflags", LightRadii::FlagsChangedCaller(m_radii));
869 if(g_lightType == LIGHTTYPE_DOOM3)
871 m_keyObservers.insert("angle", RotationKey::AngleChangedCaller(m_rotationKey));
872 m_keyObservers.insert("rotation", RotationKey::RotationChangedCaller(m_rotationKey));
873 m_keyObservers.insert("light_radius", Doom3LightRadius::LightRadiusChangedCaller(m_doom3Radius));
874 m_keyObservers.insert("light_center", Doom3LightRadius::LightCenterChangedCaller(m_doom3Radius));
875 m_keyObservers.insert("light_origin", Light::LightOriginChangedCaller(*this));
876 m_keyObservers.insert("light_rotation", Light::LightRotationChangedCaller(*this));
877 m_keyObservers.insert("light_target", Light::LightTargetChangedCaller(*this));
878 m_keyObservers.insert("light_up", Light::LightUpChangedCaller(*this));
879 m_keyObservers.insert("light_right", Light::LightRightChangedCaller(*this));
880 m_keyObservers.insert("light_start", Light::LightStartChangedCaller(*this));
881 m_keyObservers.insert("light_end", Light::LightEndChangedCaller(*this));
882 m_keyObservers.insert("texture", LightShader::ValueChangedCaller(m_shader));
883 m_useLightTarget = m_useLightUp = m_useLightRight = m_useLightStart = m_useLightEnd = false;
884 m_doom3ProjectionChanged = true;
887 if(g_lightType == LIGHTTYPE_DOOM3)
889 m_traverse.attach(&m_traverseObservers);
890 m_traverseObservers.attach(m_funcStaticOrigin);
892 m_entity.m_isContainer = true;
897 if(g_lightType == LIGHTTYPE_DOOM3)
899 m_traverseObservers.detach(m_funcStaticOrigin);
900 m_traverse.detach(&m_traverseObservers);
908 if(g_lightType == LIGHTTYPE_DOOM3)
910 m_funcStaticOrigin.originChanged();
913 m_doom3Radius.m_changed();
915 GlobalSelectionSystem().pivotChanged();
920 m_aabb_light.origin = m_useLightOrigin ? m_lightOrigin : m_originKey.m_origin;
923 typedef MemberCaller<Light, &Light::originChanged> OriginChangedCaller;
925 void lightOriginChanged(const char* value)
927 m_useLightOrigin = !string_empty(value);
930 read_origin(m_lightOrigin, value);
934 typedef MemberCaller1<Light, const char*, &Light::lightOriginChanged> LightOriginChangedCaller;
936 void lightTargetChanged(const char* value)
938 m_useLightTarget = !string_empty(value);
941 read_origin(m_lightTarget, value);
945 typedef MemberCaller1<Light, const char*, &Light::lightTargetChanged> LightTargetChangedCaller;
946 void lightUpChanged(const char* value)
948 m_useLightUp = !string_empty(value);
951 read_origin(m_lightUp, value);
955 typedef MemberCaller1<Light, const char*, &Light::lightUpChanged> LightUpChangedCaller;
956 void lightRightChanged(const char* value)
958 m_useLightRight = !string_empty(value);
961 read_origin(m_lightRight, value);
965 typedef MemberCaller1<Light, const char*, &Light::lightRightChanged> LightRightChangedCaller;
966 void lightStartChanged(const char* value)
968 m_useLightStart = !string_empty(value);
971 read_origin(m_lightStart, value);
975 typedef MemberCaller1<Light, const char*, &Light::lightStartChanged> LightStartChangedCaller;
976 void lightEndChanged(const char* value)
978 m_useLightEnd = !string_empty(value);
981 read_origin(m_lightEnd, value);
985 typedef MemberCaller1<Light, const char*, &Light::lightEndChanged> LightEndChangedCaller;
987 void writeLightOrigin()
989 write_origin(m_lightOrigin, &m_entity, "light_origin");
992 void updateLightRadiiBox() const
994 const Matrix4& rotation = rotation_toMatrix(m_rotation);
995 aabb_corners(AABB(Vector3(0, 0, 0), m_doom3Radius.m_radius), m_radii_box.m_points);
996 matrix4_transform_point(rotation, m_radii_box.m_points[0]);
997 vector3_add(m_radii_box.m_points[0], m_aabb_light.origin);
998 matrix4_transform_point(rotation, m_radii_box.m_points[1]);
999 vector3_add(m_radii_box.m_points[1], m_aabb_light.origin);
1000 matrix4_transform_point(rotation, m_radii_box.m_points[2]);
1001 vector3_add(m_radii_box.m_points[2], m_aabb_light.origin);
1002 matrix4_transform_point(rotation, m_radii_box.m_points[3]);
1003 vector3_add(m_radii_box.m_points[3], m_aabb_light.origin);
1004 matrix4_transform_point(rotation, m_radii_box.m_points[4]);
1005 vector3_add(m_radii_box.m_points[4], m_aabb_light.origin);
1006 matrix4_transform_point(rotation, m_radii_box.m_points[5]);
1007 vector3_add(m_radii_box.m_points[5], m_aabb_light.origin);
1008 matrix4_transform_point(rotation, m_radii_box.m_points[6]);
1009 vector3_add(m_radii_box.m_points[6], m_aabb_light.origin);
1010 matrix4_transform_point(rotation, m_radii_box.m_points[7]);
1011 vector3_add(m_radii_box.m_points[7], m_aabb_light.origin);
1014 void rotationChanged()
1016 rotation_assign(m_rotation, m_useLightRotation ? m_lightRotation : m_rotationKey.m_rotation);
1017 GlobalSelectionSystem().pivotChanged();
1019 typedef MemberCaller<Light, &Light::rotationChanged> RotationChangedCaller;
1021 void lightRotationChanged(const char* value)
1023 m_useLightRotation = !string_empty(value);
1024 if(m_useLightRotation)
1026 read_rotation(m_lightRotation, value);
1030 typedef MemberCaller1<Light, const char*, &Light::lightRotationChanged> LightRotationChangedCaller;
1034 Light(EntityClass* eclass, scene::Node& node, const Callback& transformChanged, const Callback& boundsChanged, const Callback& evaluateTransform) :
1036 m_originKey(OriginChangedCaller(*this)),
1037 m_rotationKey(RotationChangedCaller(*this)),
1038 m_colour(Callback()),
1039 m_filter(m_entity, node),
1041 m_nameKeys(m_entity),
1042 m_funcStaticOrigin(m_traverse, m_originKey.m_origin),
1043 m_radii_wire(m_radii, m_aabb_light.origin),
1044 m_radii_fill(m_radii, m_aabb_light.origin),
1045 m_radii_box(m_aabb_light.origin),
1046 m_render_center(m_doom3Radius.m_center, m_entity.getEntityClass()),
1047 m_renderName(m_named, m_aabb_light.origin),
1048 m_useLightOrigin(false),
1049 m_useLightRotation(false),
1050 m_renderProjection(m_doom3Projection),
1051 m_transformChanged(transformChanged),
1052 m_boundsChanged(boundsChanged),
1053 m_evaluateTransform(evaluateTransform)
1057 Light(const Light& other, scene::Node& node, const Callback& transformChanged, const Callback& boundsChanged, const Callback& evaluateTransform) :
1058 m_entity(other.m_entity),
1059 m_originKey(OriginChangedCaller(*this)),
1060 m_rotationKey(RotationChangedCaller(*this)),
1061 m_colour(Callback()),
1062 m_filter(m_entity, node),
1064 m_nameKeys(m_entity),
1065 m_funcStaticOrigin(m_traverse, m_originKey.m_origin),
1066 m_radii_wire(m_radii, m_aabb_light.origin),
1067 m_radii_fill(m_radii, m_aabb_light.origin),
1068 m_radii_box(m_aabb_light.origin),
1069 m_render_center(m_doom3Radius.m_center, m_entity.getEntityClass()),
1070 m_renderName(m_named, m_aabb_light.origin),
1071 m_useLightOrigin(false),
1072 m_useLightRotation(false),
1073 m_renderProjection(m_doom3Projection),
1074 m_transformChanged(transformChanged),
1075 m_boundsChanged(boundsChanged),
1076 m_evaluateTransform(evaluateTransform)
1085 InstanceCounter m_instanceCounter;
1086 void instanceAttach(const scene::Path& path)
1088 if(++m_instanceCounter.m_count == 1)
1090 m_filter.instanceAttach();
1091 m_entity.instanceAttach(path_find_mapfile(path.begin(), path.end()));
1092 if(g_lightType == LIGHTTYPE_DOOM3)
1094 m_traverse.instanceAttach(path_find_mapfile(path.begin(), path.end()));
1096 m_entity.attach(m_keyObservers);
1098 if(g_lightType == LIGHTTYPE_DOOM3)
1100 m_funcStaticOrigin.enable();
1104 void instanceDetach(const scene::Path& path)
1106 if(--m_instanceCounter.m_count == 0)
1108 if(g_lightType == LIGHTTYPE_DOOM3)
1110 m_funcStaticOrigin.disable();
1113 m_entity.detach(m_keyObservers);
1114 if(g_lightType == LIGHTTYPE_DOOM3)
1116 m_traverse.instanceDetach(path_find_mapfile(path.begin(), path.end()));
1118 m_entity.instanceDetach(path_find_mapfile(path.begin(), path.end()));
1119 m_filter.instanceDetach();
1123 EntityKeyValues& getEntity()
1127 const EntityKeyValues& getEntity() const
1132 scene::Traversable& getTraversable()
1136 Namespaced& getNamespaced()
1140 Nameable& getNameable()
1144 TransformNode& getTransformNode()
1149 void attach(scene::Traversable::Observer* observer)
1151 m_traverseObservers.attach(*observer);
1153 void detach(scene::Traversable::Observer* observer)
1155 m_traverseObservers.detach(*observer);
1158 void render(RenderStateFlags state) const
1162 aabb_draw(m_aabb_light, state);
1166 light_draw(m_aabb_light, state);
1170 VolumeIntersectionValue intersectVolume(const VolumeTest& volume, const Matrix4& localToWorld) const
1172 return volume.TestAABB(m_aabb_light, localToWorld);
1176 const AABB& localAABB() const
1178 return m_aabb_light;
1182 mutable Matrix4 m_projectionOrientation;
1184 void renderSolid(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
1186 renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly);
1187 renderer.SetState(m_colour.state(), Renderer::eFullMaterials);
1188 renderer.addRenderable(*this, localToWorld);
1190 if(selected && g_lightRadii && string_empty(m_entity.getKeyValue("target")))
1192 if(renderer.getStyle() == Renderer::eFullMaterials)
1194 renderer.SetState(RenderLightRadiiFill::m_state, Renderer::eFullMaterials);
1195 renderer.Highlight(Renderer::ePrimitive, false);
1196 renderer.addRenderable(m_radii_fill, localToWorld);
1200 renderer.addRenderable(m_radii_wire, localToWorld);
1204 renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eFullMaterials);
1206 if(g_lightType == LIGHTTYPE_DOOM3 && selected)
1211 m_projectionOrientation = rotation();
1212 vector4_to_vector3(m_projectionOrientation.t()) = localAABB().origin;
1213 renderer.addRenderable(m_renderProjection, m_projectionOrientation);
1217 updateLightRadiiBox();
1218 renderer.addRenderable(m_radii_box, localToWorld);
1221 //draw the center of the light
1222 if(m_doom3Radius.m_useCenterKey)
1224 renderer.Highlight(Renderer::ePrimitive, false);
1225 renderer.Highlight(Renderer::eFace, false);
1226 renderer.SetState(m_render_center.m_state, Renderer::eFullMaterials);
1227 renderer.SetState(m_render_center.m_state, Renderer::eWireframeOnly);
1228 renderer.addRenderable(m_render_center, localToWorld);
1232 void renderWireframe(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld, bool selected) const
1234 renderSolid(renderer, volume, localToWorld, selected);
1237 renderer.addRenderable(m_renderName, localToWorld);
1241 void testSelect(Selector& selector, SelectionTest& test, const Matrix4& localToWorld)
1243 test.BeginMesh(localToWorld);
1245 SelectionIntersection best;
1246 aabb_testselect(m_aabb_light, test, best);
1249 selector.addIntersection(best);
1253 void translate(const Vector3& translation)
1255 m_aabb_light.origin = origin_translated(m_aabb_light.origin, translation);
1257 void rotate(const Quaternion& rotation)
1259 rotation_rotate(m_rotation, rotation);
1261 void snapto(float snap)
1263 if(g_lightType == LIGHTTYPE_DOOM3 && !m_useLightOrigin && !m_traverse.empty())
1265 m_useLightOrigin = true;
1266 m_lightOrigin = m_originKey.m_origin;
1269 if(m_useLightOrigin)
1271 m_lightOrigin = origin_snapped(m_lightOrigin, snap);
1276 m_originKey.m_origin = origin_snapped(m_originKey.m_origin, snap);
1277 m_originKey.write(&m_entity);
1280 void revertTransform()
1282 m_aabb_light.origin = m_useLightOrigin ? m_lightOrigin : m_originKey.m_origin;
1283 rotation_assign(m_rotation, m_useLightRotation ? m_lightRotation : m_rotationKey.m_rotation);
1285 void freezeTransform()
1287 if(g_lightType == LIGHTTYPE_DOOM3 && !m_useLightOrigin && !m_traverse.empty())
1289 m_useLightOrigin = true;
1292 if(m_useLightOrigin)
1294 m_lightOrigin = m_aabb_light.origin;
1299 m_originKey.m_origin = m_aabb_light.origin;
1300 m_originKey.write(&m_entity);
1303 if(g_lightType == LIGHTTYPE_DOOM3)
1305 if(!m_useLightRotation && !m_traverse.empty())
1307 m_useLightRotation = true;
1310 if(m_useLightRotation)
1312 rotation_assign(m_lightRotation, m_rotation);
1313 write_rotation(m_lightRotation, &m_entity, "light_rotation");
1316 rotation_assign(m_rotationKey.m_rotation, m_rotation);
1317 write_rotation(m_rotationKey.m_rotation, &m_entity);
1320 void transformChanged()
1323 m_evaluateTransform();
1326 typedef MemberCaller<Light, &Light::transformChanged> TransformChangedCaller;
1328 mutable Matrix4 m_localPivot;
1329 const Matrix4& getLocalPivot() const
1331 m_localPivot = rotation_toMatrix(m_rotation);
1332 vector4_to_vector3(m_localPivot.t()) = m_aabb_light.origin;
1333 return m_localPivot;
1336 void setLightChangedCallback(const Callback& callback)
1338 m_doom3Radius.m_changed = callback;
1341 const AABB& aabb() const
1343 m_doom3AABB = AABB(m_aabb_light.origin, m_doom3Radius.m_radius);
1346 bool testAABB(const AABB& other) const
1350 Matrix4 transform = rotation();
1351 vector4_to_vector3(transform.t()) = localAABB().origin;
1353 Frustum frustum(frustum_transformed(m_doom3Frustum, transform));
1354 return frustum_test_aabb(frustum, other) != c_volumeOutside;
1356 // test against an AABB which contains the rotated bounds of this light.
1357 const AABB& bounds = aabb();
1358 return aabb_intersects_aabb(other, AABB(
1361 static_cast<float>(fabs(m_rotation[0] * bounds.extents[0])
1362 + fabs(m_rotation[3] * bounds.extents[1])
1363 + fabs(m_rotation[6] * bounds.extents[2])),
1364 static_cast<float>(fabs(m_rotation[1] * bounds.extents[0])
1365 + fabs(m_rotation[4] * bounds.extents[1])
1366 + fabs(m_rotation[7] * bounds.extents[2])),
1367 static_cast<float>(fabs(m_rotation[2] * bounds.extents[0])
1368 + fabs(m_rotation[5] * bounds.extents[1])
1369 + fabs(m_rotation[8] * bounds.extents[2]))
1374 const Matrix4& rotation() const
1376 m_doom3Rotation = rotation_toMatrix(m_rotation);
1377 return m_doom3Rotation;
1379 const Vector3& offset() const
1381 return m_doom3Radius.m_center;
1383 const Vector3& colour() const
1385 return m_colour.m_colour;
1388 bool isProjected() const
1390 return m_useLightTarget && m_useLightUp && m_useLightRight;
1392 void projectionChanged()
1394 m_doom3ProjectionChanged = true;
1395 m_doom3Radius.m_changed();
1396 SceneChangeNotify();
1399 const Matrix4& projection() const
1401 if(!m_doom3ProjectionChanged)
1403 return m_doom3Projection;
1405 m_doom3ProjectionChanged = false;
1406 m_doom3Projection = g_matrix4_identity;
1407 matrix4_translate_by_vec3(m_doom3Projection, Vector3(0.5f, 0.5f, 0));
1408 matrix4_scale_by_vec3(m_doom3Projection, Vector3(0.5f, 0.5f, 1));
1411 Vector3 right = vector3_cross(m_lightUp, vector3_normalised(m_lightTarget));
1412 Vector3 up = vector3_cross(vector3_normalised(m_lightTarget), m_lightRight);
1413 Vector3 target = m_lightTarget;
1415 -right.x(), -right.y(), -right.z(), 0,
1416 -up.x(), -up.y(), -up.z(), 0,
1417 -target.x(), -target.y(), -target.z(), 0,
1420 Matrix4 frustum = matrix4_frustum(-0.01, 0.01, -0.01, 0.01, 0.01, 1.0);
1421 test = matrix4_full_inverse(test);
1422 matrix4_premultiply_by_matrix4(test, frustum);
1423 matrix4_multiply_by_matrix4(m_doom3Projection, test);
1425 const float nearFar = 1 / 49.5f;
1426 Vector3 right = vector3_cross(m_lightUp, vector3_normalised(m_lightTarget + m_lightRight));
1427 Vector3 up = vector3_cross(vector3_normalised(m_lightTarget + m_lightUp), m_lightRight);
1428 Vector3 target = vector3_negated(m_lightTarget * (1 + nearFar));
1429 float scale = -1 / vector3_length(m_lightTarget);
1431 -inverse(right.x()), -inverse(up.x()), -inverse(target.x()), 0,
1432 -inverse(right.y()), -inverse(up.y()), -inverse(target.y()), 0,
1433 -inverse(right.z()), -inverse(up.z()), -inverse(target.z()), scale,
1436 matrix4_multiply_by_matrix4(m_doom3Projection, test);
1438 Vector3 leftA(m_lightTarget - m_lightRight);
1439 Vector3 leftB(m_lightRight + m_lightUp);
1440 Plane3 left(vector3_normalised(vector3_cross(leftA, leftB)) * (1.0 / 128), 0);
1441 Vector3 rightA(m_lightTarget + m_lightRight);
1442 Vector3 rightB(vector3_cross(rightA, m_lightTarget));
1443 Plane3 right(vector3_normalised(vector3_cross(rightA, rightB)) * (1.0 / 128), 0);
1444 Vector3 bottomA(m_lightTarget - m_lightUp);
1445 Vector3 bottomB(vector3_cross(bottomA, m_lightTarget));
1446 Plane3 bottom(vector3_normalised(vector3_cross(bottomA, bottomB)) * (1.0 / 128), 0);
1447 Vector3 topA(m_lightTarget + m_lightUp);
1448 Vector3 topB(vector3_cross(topA, m_lightTarget));
1449 Plane3 top(vector3_normalised(vector3_cross(topA, topB)) * (1.0 / 128), 0);
1450 Plane3 front(vector3_normalised(m_lightTarget) * (1.0 / 128), 1);
1451 Plane3 back(vector3_normalised(vector3_negated(m_lightTarget)) * (1.0 / 128), 0);
1452 Matrix4 test(matrix4_from_planes(plane3_flipped(left), plane3_flipped(right), plane3_flipped(bottom), plane3_flipped(top), plane3_flipped(front), plane3_flipped(back)));
1453 matrix4_multiply_by_matrix4(m_doom3Projection, test);
1456 Plane3 lightProject[4];
1458 Vector3 start = m_useLightStart && m_useLightEnd ? m_lightStart : vector3_normalised(m_lightTarget);
1459 Vector3 stop = m_useLightStart && m_useLightEnd ? m_lightEnd : m_lightTarget;
1461 float rLen = vector3_length(m_lightRight);
1462 Vector3 right = vector3_divided(m_lightRight, rLen);
1463 float uLen = vector3_length(m_lightUp);
1464 Vector3 up = vector3_divided(m_lightUp, uLen);
1465 Vector3 normal = vector3_normalised(vector3_cross(up, right));
1467 float dist = vector3_dot(m_lightTarget, normal);
1470 normal = vector3_negated(normal);
1473 right *= ( 0.5f * dist ) / rLen;
1474 up *= -( 0.5f * dist ) / uLen;
1476 lightProject[2] = Plane3(normal, 0);
1477 lightProject[0] = Plane3(right, 0);
1478 lightProject[1] = Plane3(up, 0);
1480 // now offset to center
1481 Vector4 targetGlobal(m_lightTarget, 1);
1483 float a = vector4_dot(targetGlobal, plane3_to_vector4(lightProject[0]));
1484 float b = vector4_dot(targetGlobal, plane3_to_vector4(lightProject[2]));
1485 float ofs = 0.5f - a / b;
1486 plane3_to_vector4(lightProject[0]) += plane3_to_vector4(lightProject[2]) * ofs;
1489 float a = vector4_dot(targetGlobal, plane3_to_vector4(lightProject[1]));
1490 float b = vector4_dot(targetGlobal, plane3_to_vector4(lightProject[2]));
1491 float ofs = 0.5f - a / b;
1492 plane3_to_vector4(lightProject[1]) += plane3_to_vector4(lightProject[2]) * ofs;
1495 // set the falloff vector
1496 Vector3 falloff = stop - start;
1497 float length = vector3_length(falloff);
1498 falloff = vector3_divided(falloff, length);
1499 if ( length <= 0 ) {
1502 falloff *= (1.0f / length);
1503 lightProject[3] = Plane3(falloff, -vector3_dot(start, falloff));
1505 // we want the planes of s=0, s=q, t=0, and t=q
1506 m_doom3Frustum.left = lightProject[0];
1507 m_doom3Frustum.bottom = lightProject[1];
1508 m_doom3Frustum.right = Plane3(lightProject[2].normal() - lightProject[0].normal(), lightProject[2].dist() - lightProject[0].dist());
1509 m_doom3Frustum.top = Plane3(lightProject[2].normal() - lightProject[1].normal(), lightProject[2].dist() - lightProject[1].dist());
1511 // we want the planes of s=0 and s=1 for front and rear clipping planes
1512 m_doom3Frustum.front = lightProject[3];
1514 m_doom3Frustum.back = lightProject[3];
1515 m_doom3Frustum.back.dist() -= 1.0f;
1516 m_doom3Frustum.back = plane3_flipped(m_doom3Frustum.back);
1518 Matrix4 test(matrix4_from_planes(m_doom3Frustum.left, m_doom3Frustum.right, m_doom3Frustum.bottom, m_doom3Frustum.top, m_doom3Frustum.front, m_doom3Frustum.back));
1519 matrix4_multiply_by_matrix4(m_doom3Projection, test);
1521 m_doom3Frustum.left = plane3_normalised(m_doom3Frustum.left);
1522 m_doom3Frustum.right = plane3_normalised(m_doom3Frustum.right);
1523 m_doom3Frustum.bottom = plane3_normalised(m_doom3Frustum.bottom);
1524 m_doom3Frustum.top = plane3_normalised(m_doom3Frustum.top);
1525 m_doom3Frustum.back = plane3_normalised(m_doom3Frustum.back);
1526 m_doom3Frustum.front = plane3_normalised(m_doom3Frustum.front);
1528 //matrix4_scale_by_vec3(m_doom3Projection, Vector3(1.0 / 128, 1.0 / 128, 1.0 / 128));
1529 return m_doom3Projection;
1532 Shader* getShader() const
1534 return m_shader.get();
1538 class LightInstance :
1539 public TargetableInstance,
1540 public TransformModifier,
1542 public SelectionTestable,
1543 public RendererLight
1547 InstanceTypeCastTable m_casts;
1551 m_casts = TargetableInstance::StaticTypeCasts::instance().get();
1552 InstanceContainedCast<LightInstance, Bounded>::install(m_casts);
1553 //InstanceContainedCast<LightInstance, Cullable>::install(m_casts);
1554 InstanceStaticCast<LightInstance, Renderable>::install(m_casts);
1555 InstanceStaticCast<LightInstance, SelectionTestable>::install(m_casts);
1556 InstanceStaticCast<LightInstance, Transformable>::install(m_casts);
1557 InstanceIdentityCast<LightInstance>::install(m_casts);
1559 InstanceTypeCastTable& get()
1567 typedef LazyStatic<TypeCasts> StaticTypeCasts;
1569 Bounded& get(NullType<Bounded>)
1574 STRING_CONSTANT(Name, "LightInstance");
1576 LightInstance(const scene::Path& path, scene::Instance* parent, Light& contained) :
1577 TargetableInstance(path, parent, this, StaticTypeCasts::instance().get(), contained.getEntity(), *this),
1578 TransformModifier(Light::TransformChangedCaller(contained), ApplyTransformCaller(*this)),
1579 m_contained(contained)
1581 m_contained.instanceAttach(Instance::path());
1583 if(g_lightType == LIGHTTYPE_DOOM3)
1585 GlobalShaderCache().attach(*this);
1586 m_contained.setLightChangedCallback(LightChangedCaller(*this));
1589 StaticRenderableConnectionLines::instance().attach(*this);
1593 StaticRenderableConnectionLines::instance().detach(*this);
1595 if(g_lightType == LIGHTTYPE_DOOM3)
1597 m_contained.setLightChangedCallback(Callback());
1598 GlobalShaderCache().detach(*this);
1601 m_contained.instanceDetach(Instance::path());
1603 void renderSolid(Renderer& renderer, const VolumeTest& volume) const
1605 m_contained.renderSolid(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
1607 void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
1609 m_contained.renderWireframe(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
1611 void testSelect(Selector& selector, SelectionTest& test)
1613 m_contained.testSelect(selector, test, Instance::localToWorld());
1616 void evaluateTransform()
1618 if(getType() == TRANSFORM_PRIMITIVE)
1620 m_contained.translate(getTranslation());
1621 m_contained.rotate(getRotation());
1624 void applyTransform()
1626 m_contained.revertTransform();
1627 evaluateTransform();
1628 m_contained.freezeTransform();
1630 typedef MemberCaller<LightInstance, &LightInstance::applyTransform> ApplyTransformCaller;
1634 GlobalShaderCache().changed(*this);
1636 typedef MemberCaller<LightInstance, &LightInstance::lightChanged> LightChangedCaller;
1638 Shader* getShader() const
1640 return m_contained.getShader();
1642 const AABB& aabb() const
1644 return m_contained.aabb();
1646 bool testAABB(const AABB& other) const
1648 return m_contained.testAABB(other);
1650 const Matrix4& rotation() const
1652 return m_contained.rotation();
1654 const Vector3& offset() const
1656 return m_contained.offset();
1658 const Vector3& colour() const
1660 return m_contained.colour();
1663 bool isProjected() const
1665 return m_contained.isProjected();
1667 const Matrix4& projection() const
1669 return m_contained.projection();
1674 public scene::Node::Symbiot,
1675 public scene::Instantiable,
1676 public scene::Cloneable,
1677 public scene::Traversable::Observer
1681 NodeTypeCastTable m_casts;
1685 NodeStaticCast<LightNode, scene::Instantiable>::install(m_casts);
1686 NodeStaticCast<LightNode, scene::Cloneable>::install(m_casts);
1687 if(g_lightType == LIGHTTYPE_DOOM3)
1689 NodeContainedCast<LightNode, scene::Traversable>::install(m_casts);
1691 NodeContainedCast<LightNode, Editable>::install(m_casts);
1692 NodeContainedCast<LightNode, Snappable>::install(m_casts);
1693 NodeContainedCast<LightNode, TransformNode>::install(m_casts);
1694 NodeContainedCast<LightNode, Entity>::install(m_casts);
1695 NodeContainedCast<LightNode, Nameable>::install(m_casts);
1696 NodeContainedCast<LightNode, Namespaced>::install(m_casts);
1698 NodeTypeCastTable& get()
1706 InstanceSet m_instances;
1711 if(g_lightType == LIGHTTYPE_DOOM3)
1713 m_contained.attach(this);
1718 if(g_lightType == LIGHTTYPE_DOOM3)
1720 m_contained.detach(this);
1724 typedef LazyStatic<TypeCasts> StaticTypeCasts;
1726 scene::Traversable& get(NullType<scene::Traversable>)
1728 return m_contained.getTraversable();
1730 Editable& get(NullType<Editable>)
1734 Snappable& get(NullType<Snappable>)
1738 TransformNode& get(NullType<TransformNode>)
1740 return m_contained.getTransformNode();
1742 Entity& get(NullType<Entity>)
1744 return m_contained.getEntity();
1746 Nameable& get(NullType<Nameable>)
1748 return m_contained.getNameable();
1750 Namespaced& get(NullType<Namespaced>)
1752 return m_contained.getNamespaced();
1755 LightNode(EntityClass* eclass) :
1756 m_node(this, this, StaticTypeCasts::instance().get()),
1757 m_contained(eclass, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSet::BoundsChangedCaller(m_instances), InstanceSetEvaluateTransform<LightInstance>::Caller(m_instances))
1761 LightNode(const LightNode& other) :
1762 scene::Node::Symbiot(other),
1763 scene::Instantiable(other),
1764 scene::Cloneable(other),
1765 scene::Traversable::Observer(other),
1766 m_node(this, this, StaticTypeCasts::instance().get()),
1767 m_contained(other.m_contained, m_node, InstanceSet::TransformChangedCaller(m_instances), InstanceSet::BoundsChangedCaller(m_instances), InstanceSetEvaluateTransform<LightInstance>::Caller(m_instances))
1785 scene::Node& clone() const
1787 return (new LightNode(*this))->node();
1790 void insert(scene::Node& child)
1792 m_instances.insert(child);
1794 void erase(scene::Node& child)
1796 m_instances.erase(child);
1799 scene::Instance* create(const scene::Path& path, scene::Instance* parent)
1801 return new LightInstance(path, parent, m_contained);
1803 void forEachInstance(const scene::Instantiable::Visitor& visitor)
1805 m_instances.forEachInstance(visitor);
1807 void insert(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance)
1809 m_instances.insert(observer, path, instance);
1811 scene::Instance* erase(scene::Instantiable::Observer* observer, const scene::Path& path)
1813 return m_instances.erase(observer, path);
1817 void Light_Construct(LightType lightType)
1819 g_lightType = lightType;
1820 if(g_lightType == LIGHTTYPE_DOOM3)
1822 LightShader::m_defaultShader = "lights/defaultPointLight";
1824 LightShader::m_defaultShader = "lights/defaultProjectedLight";
1827 RenderLightRadiiFill::m_state = GlobalShaderCache().capture("$Q3MAP2_LIGHT_SPHERE");
1828 RenderLightCenter::m_state = GlobalShaderCache().capture("$BIGPOINT");
1830 void Light_Destroy()
1832 GlobalShaderCache().release("$Q3MAP2_LIGHT_SPHERE");
1833 GlobalShaderCache().release("$BIGPOINT");
1836 scene::Node& New_Light(EntityClass* eclass)
1838 return (new LightNode(eclass))->node();