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"
55 #include "dragplanes.h"
57 #include "targetable.h"
61 #include "namedentity.h"
62 #include "keyobservers.h"
68 extern bool g_newLightDraw;
71 void sphere_draw_fill(const Vector3 &origin, float radius, int sides)
77 const double dt = c_2pi / static_cast<double>( sides );
78 const double dp = c_pi / static_cast<double>( sides );
80 glBegin(GL_TRIANGLES);
81 for (int i = 0; i <= sides - 1; ++i) {
82 for (int j = 0; j <= sides - 2; ++j) {
83 const double t = i * dt;
84 const double p = (j * dp) - (c_pi / 2.0);
87 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p), radius)));
88 glVertex3fv(vector3_to_array(v));
92 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p + dp), radius)));
93 glVertex3fv(vector3_to_array(v));
97 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p + dp), radius)));
98 glVertex3fv(vector3_to_array(v));
102 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p), radius)));
103 glVertex3fv(vector3_to_array(v));
107 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p + dp), radius)));
108 glVertex3fv(vector3_to_array(v));
112 Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p), radius)));
113 glVertex3fv(vector3_to_array(v));
119 const double p = (sides - 1) * dp - (c_pi / 2.0);
120 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++) {
148 double ds = sin((i * 2 * c_pi) / sides);
149 double dc = cos((i * 2 * c_pi) / sides);
152 static_cast<float>( origin[0] + radius * dc ),
153 static_cast<float>( origin[1] + radius * ds ),
162 glBegin(GL_LINE_LOOP);
164 for (int i = 0; i <= sides; i++) {
165 double ds = sin((i * 2 * c_pi) / sides);
166 double dc = cos((i * 2 * c_pi) / sides);
169 static_cast<float>( origin[0] + radius * dc ),
171 static_cast<float>( origin[2] + radius * ds )
179 glBegin(GL_LINE_LOOP);
181 for (int i = 0; i <= sides; i++) {
182 double ds = sin((i * 2 * c_pi) / sides);
183 double dc = cos((i * 2 * c_pi) / sides);
187 static_cast<float>( origin[1] + radius * dc ),
188 static_cast<float>( origin[2] + radius * ds )
196 void light_draw_box_lines(const Vector3 &origin, const Vector3 points[8])
198 //draw lines from the center of the bbox to the corners
201 glVertex3fv(vector3_to_array(origin));
202 glVertex3fv(vector3_to_array(points[1]));
204 glVertex3fv(vector3_to_array(origin));
205 glVertex3fv(vector3_to_array(points[5]));
207 glVertex3fv(vector3_to_array(origin));
208 glVertex3fv(vector3_to_array(points[2]));
210 glVertex3fv(vector3_to_array(origin));
211 glVertex3fv(vector3_to_array(points[6]));
213 glVertex3fv(vector3_to_array(origin));
214 glVertex3fv(vector3_to_array(points[0]));
216 glVertex3fv(vector3_to_array(origin));
217 glVertex3fv(vector3_to_array(points[4]));
219 glVertex3fv(vector3_to_array(origin));
220 glVertex3fv(vector3_to_array(points[3]));
222 glVertex3fv(vector3_to_array(origin));
223 glVertex3fv(vector3_to_array(points[7]));
228 void light_draw_radius_wire(const Vector3 &origin, const float envelope[3])
230 if (envelope[0] > 0) {
231 sphere_draw_wire(origin, envelope[0], 24);
233 if (envelope[1] > 0) {
234 sphere_draw_wire(origin, envelope[1], 24);
236 if (envelope[2] > 0) {
237 sphere_draw_wire(origin, envelope[2], 24);
241 void light_draw_radius_fill(const Vector3 &origin, const float envelope[3])
243 if (envelope[0] > 0) {
244 sphere_draw_fill(origin, envelope[0], 16);
246 if (envelope[1] > 0) {
247 sphere_draw_fill(origin, envelope[1], 16);
249 if (envelope[2] > 0) {
250 sphere_draw_fill(origin, envelope[2], 16);
254 void light_vertices(const AABB &aabb_light, Vector3 points[6])
256 Vector3 max(vector3_added(aabb_light.origin, aabb_light.extents));
257 Vector3 min(vector3_subtracted(aabb_light.origin, aabb_light.extents));
258 Vector3 mid(aabb_light.origin);
260 // top, bottom, middle-up, middle-right, middle-down, middle-left
261 points[0] = Vector3(mid[0], mid[1], max[2]);
262 points[1] = Vector3(mid[0], mid[1], min[2]);
263 points[2] = Vector3(mid[0], max[1], mid[2]);
264 points[3] = Vector3(max[0], mid[1], mid[2]);
265 points[4] = Vector3(mid[0], min[1], mid[2]);
266 points[5] = Vector3(min[0], mid[1], mid[2]);
269 void light_draw(const AABB &aabb_light, RenderStateFlags state)
272 light_vertices(aabb_light, points);
274 if (state & RENDER_LIGHTING) {
275 const float f = 0.70710678f;
276 // North, East, South, West
277 const Vector3 normals[8] = {
288 #if !defined( USE_TRIANGLE_FAN )
289 glBegin(GL_TRIANGLES);
291 glBegin( GL_TRIANGLE_FAN );
293 glVertex3fv(vector3_to_array(points[0]));
294 glVertex3fv(vector3_to_array(points[2]));
295 glNormal3fv(vector3_to_array(normals[0]));
296 glVertex3fv(vector3_to_array(points[3]));
298 #if !defined( USE_TRIANGLE_FAN )
299 glVertex3fv(vector3_to_array(points[0]));
300 glVertex3fv(vector3_to_array(points[3]));
302 glNormal3fv(vector3_to_array(normals[1]));
303 glVertex3fv(vector3_to_array(points[4]));
305 #if !defined( USE_TRIANGLE_FAN )
306 glVertex3fv(vector3_to_array(points[0]));
307 glVertex3fv(vector3_to_array(points[4]));
309 glNormal3fv(vector3_to_array(normals[2]));
310 glVertex3fv(vector3_to_array(points[5]));
311 #if !defined( USE_TRIANGLE_FAN )
312 glVertex3fv(vector3_to_array(points[0]));
313 glVertex3fv(vector3_to_array(points[5]));
315 glNormal3fv(vector3_to_array(normals[3]));
316 glVertex3fv(vector3_to_array(points[2]));
317 #if defined( USE_TRIANGLE_FAN )
319 glBegin( GL_TRIANGLE_FAN );
322 glVertex3fv(vector3_to_array(points[1]));
323 glVertex3fv(vector3_to_array(points[2]));
324 glNormal3fv(vector3_to_array(normals[7]));
325 glVertex3fv(vector3_to_array(points[5]));
327 #if !defined( USE_TRIANGLE_FAN )
328 glVertex3fv(vector3_to_array(points[1]));
329 glVertex3fv(vector3_to_array(points[5]));
331 glNormal3fv(vector3_to_array(normals[6]));
332 glVertex3fv(vector3_to_array(points[4]));
334 #if !defined( USE_TRIANGLE_FAN )
335 glVertex3fv(vector3_to_array(points[1]));
336 glVertex3fv(vector3_to_array(points[4]));
338 glNormal3fv(vector3_to_array(normals[5]));
339 glVertex3fv(vector3_to_array(points[3]));
341 #if !defined( USE_TRIANGLE_FAN )
342 glVertex3fv(vector3_to_array(points[1]));
343 glVertex3fv(vector3_to_array(points[3]));
345 glNormal3fv(vector3_to_array(normals[4]));
346 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 );
384 glColor3f( 0, 1, 0 );
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 );
390 glVertex3fv( vTemp );
391 VectorAdd( vTarget, e->origin, vTemp );
392 VectorAdd( vTemp, vUp, vTemp );
393 VectorSubtract( vTemp, vRight, vTemp );
394 glVertex3fv( e->origin );
395 glVertex3fv( vTemp );
396 VectorAdd( vTarget, e->origin, vTemp );
397 VectorAdd( vTemp, vRight, vTemp );
398 VectorSubtract( vTemp, vUp, vTemp );
399 glVertex3fv( e->origin );
400 glVertex3fv( vTemp );
401 VectorAdd( vTarget, e->origin, vTemp );
402 VectorSubtract( vTemp, vUp, vTemp );
403 VectorSubtract( vTemp, vRight, vTemp );
404 glVertex3fv( e->origin );
405 glVertex3fv( vTemp );
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) {
454 float m_primaryIntensity;
455 float m_secondaryIntensity;
460 void calculateRadii()
462 float intensity = 300.0f;
464 if (m_primaryIntensity != 0.0f) {
465 intensity = m_primaryIntensity;
466 } else if (m_secondaryIntensity != 0.0f) {
467 intensity = m_secondaryIntensity;
470 intensity *= m_scale;
472 if (spawnflags_linear(m_flags)) {
473 m_radii[0] = light_radius_linear(intensity, 1.0f) / m_fade;
474 m_radii[1] = light_radius_linear(intensity, 48.0f) / m_fade;
475 m_radii[2] = light_radius_linear(intensity, 255.0f) / m_fade;
477 m_radii[0] = light_radius(intensity, 1.0f);
478 m_radii[1] = light_radius(intensity, 48.0f);
479 m_radii[2] = light_radius(intensity, 255.0f);
484 LightRadii() : m_primaryIntensity(0), m_secondaryIntensity(0), m_flags(0), m_fade(1), m_scale(1)
489 void primaryIntensityChanged(const char *value)
491 m_primaryIntensity = string_read_float(value);
495 typedef MemberCaller<LightRadii, void(
496 const char *), &LightRadii::primaryIntensityChanged> PrimaryIntensityChangedCaller;
498 void secondaryIntensityChanged(const char *value)
500 m_secondaryIntensity = string_read_float(value);
504 typedef MemberCaller<LightRadii, void(
505 const char *), &LightRadii::secondaryIntensityChanged> SecondaryIntensityChangedCaller;
507 void scaleChanged(const char *value)
509 m_scale = string_read_float(value);
510 if (m_scale <= 0.0f) {
516 typedef MemberCaller<LightRadii, void(const char *), &LightRadii::scaleChanged> ScaleChangedCaller;
518 void fadeChanged(const char *value)
520 m_fade = string_read_float(value);
521 if (m_fade <= 0.0f) {
527 typedef MemberCaller<LightRadii, void(const char *), &LightRadii::fadeChanged> FadeChangedCaller;
529 void flagsChanged(const char *value)
531 m_flags = string_read_int(value);
535 typedef MemberCaller<LightRadii, void(const char *), &LightRadii::flagsChanged> FlagsChangedCaller;
538 class Doom3LightRadius {
540 Vector3 m_defaultRadius;
542 Vector3 m_radiusTransformed;
544 Callback<void()> m_changed;
547 Doom3LightRadius(const char *defaultRadius) : m_defaultRadius(300, 300, 300), m_center(0, 0, 0),
548 m_useCenterKey(false)
550 if (!string_parse_vector3(defaultRadius, m_defaultRadius)) {
551 globalErrorStream() << "Doom3LightRadius: failed to parse default light radius\n";
553 m_radius = m_defaultRadius;
556 void lightRadiusChanged(const char *value)
558 if (!string_parse_vector3(value, m_radius)) {
559 m_radius = m_defaultRadius;
561 m_radiusTransformed = m_radius;
566 typedef MemberCaller<Doom3LightRadius, void(
567 const char *), &Doom3LightRadius::lightRadiusChanged> LightRadiusChangedCaller;
569 void lightCenterChanged(const char *value)
571 m_useCenterKey = string_parse_vector3(value, m_center);
572 if (!m_useCenterKey) {
573 m_center = Vector3(0, 0, 0);
578 typedef MemberCaller<Doom3LightRadius, void(
579 const char *), &Doom3LightRadius::lightCenterChanged> LightCenterChangedCaller;
582 class RenderLightRadiiWire : public OpenGLRenderable {
584 const Vector3 &m_origin;
586 RenderLightRadiiWire(LightRadii &radii, const Vector3 &origin) : m_radii(radii), m_origin(origin)
590 void render(RenderStateFlags state) const
592 light_draw_radius_wire(m_origin, m_radii.m_radii);
596 class RenderLightRadiiFill : public OpenGLRenderable {
598 const Vector3 &m_origin;
600 static Shader *m_state;
602 RenderLightRadiiFill(LightRadii &radii, const Vector3 &origin) : m_radii(radii), m_origin(origin)
606 void render(RenderStateFlags state) const
608 light_draw_radius_fill(m_origin, m_radii.m_radii);
612 class RenderLightRadiiBox : public OpenGLRenderable {
613 const Vector3 &m_origin;
615 mutable Vector3 m_points[8];
616 static Shader *m_state;
618 RenderLightRadiiBox(const Vector3 &origin) : m_origin(origin)
622 void render(RenderStateFlags state) const
624 //draw the bounding box of light based on light_radius key
625 if ((state & RENDER_FILL) != 0) {
626 aabb_draw_flatshade(m_points);
628 aabb_draw_wire(m_points);
631 #if 1 //disable if you dont want lines going from the center of the light bbox to the corners
632 light_draw_box_lines(m_origin, m_points);
637 Shader *RenderLightRadiiFill::m_state = 0;
639 class RenderLightCenter : public OpenGLRenderable {
640 const Vector3 &m_center;
641 EntityClass &m_eclass;
643 static Shader *m_state;
645 RenderLightCenter(const Vector3 ¢er, EntityClass &eclass) : m_center(center), m_eclass(eclass)
649 void render(RenderStateFlags state) const
652 glColor3fv(vector3_to_array(m_eclass.color));
653 glVertex3fv(vector3_to_array(m_center));
658 Shader *RenderLightCenter::m_state = 0;
660 class RenderLightProjection : public OpenGLRenderable {
661 const Matrix4 &m_projection;
664 RenderLightProjection(const Matrix4 &projection) : m_projection(projection)
668 void render(RenderStateFlags state) const
670 Matrix4 unproject(matrix4_full_inverse(m_projection));
672 aabb_corners(AABB(Vector3(0.5f, 0.5f, 0.5f), Vector3(0.5f, 0.5f, 0.5f)), points);
673 points[0] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[0], 1)));
674 points[1] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[1], 1)));
675 points[2] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[2], 1)));
676 points[3] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[3], 1)));
677 points[4] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[4], 1)));
678 points[5] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[5], 1)));
679 points[6] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[6], 1)));
680 points[7] = vector4_projected(matrix4_transformed_vector4(unproject, Vector4(points[7], 1)));
681 // Vector4 test1 = matrix4_transformed_vector4( unproject, Vector4( 0.5f, 0.5f, 0.5f, 1 ) );
682 // Vector3 test2 = vector4_projected( test1 );
683 aabb_draw_wire(points);
687 inline void default_extents(Vector3 &extents)
689 extents = Vector3(8, 8, 8);
698 m_shader = GlobalShaderCache().capture(m_name.c_str());
703 GlobalShaderCache().release(m_name.c_str());
717 void setName(const char *name)
735 m_shader.setName(m_defaultShader);
739 static const char *m_defaultShader;
746 void valueChanged(const char *value)
748 if (string_empty(value)) {
751 m_shader.setName(value);
756 typedef MemberCaller<LightShader, void(const char *), &LightShader::valueChanged> ValueChangedCaller;
760 return m_shader.get();
764 const char *LightShader::m_defaultShader = "";
766 inline const BasicVector4<double> &plane3_to_vector4(const Plane3 &self)
768 return reinterpret_cast<const BasicVector4<double> &>( self );
771 inline BasicVector4<double> &plane3_to_vector4(Plane3 &self)
773 return reinterpret_cast<BasicVector4<double> &>( self );
776 inline Matrix4 matrix4_from_planes(const Plane3 &left, const Plane3 &right, const Plane3 &bottom, const Plane3 &top,
777 const Plane3 &front, const Plane3 &back)
780 (right.a - left.a) / 2,
781 (top.a - bottom.a) / 2,
782 (back.a - front.a) / 2,
783 right.a - (right.a - left.a) / 2,
784 (right.b - left.b) / 2,
785 (top.b - bottom.b) / 2,
786 (back.b - front.b) / 2,
787 right.b - (right.b - left.b) / 2,
788 (right.c - left.c) / 2,
789 (top.c - bottom.c) / 2,
790 (back.c - front.c) / 2,
791 right.c - (right.c - left.c) / 2,
792 (right.d - left.d) / 2,
793 (top.d - bottom.d) / 2,
794 (back.d - front.d) / 2,
795 right.d - (right.d - left.d) / 2
800 public OpenGLRenderable,
805 EntityKeyValues m_entity;
806 KeyObserverMap m_keyObservers;
807 TraversableNodeSet m_traverse;
808 IdentityTransform m_transform;
810 OriginKey m_originKey;
811 RotationKey m_rotationKey;
815 ClassnameFilter m_filter;
818 TraversableObserverPairRelay m_traverseObservers;
819 Doom3GroupOrigin m_funcStaticOrigin;
822 Doom3LightRadius m_doom3Radius;
824 RenderLightRadiiWire m_radii_wire;
825 RenderLightRadiiFill m_radii_fill;
826 RenderLightRadiiBox m_radii_box;
827 RenderLightCenter m_render_center;
828 RenderableNamedEntity m_renderName;
830 Vector3 m_lightOrigin;
831 bool m_useLightOrigin;
832 Float9 m_lightRotation;
833 bool m_useLightRotation;
835 Vector3 m_lightTarget;
836 bool m_useLightTarget;
839 Vector3 m_lightRight;
840 bool m_useLightRight;
841 Vector3 m_lightStart;
842 bool m_useLightStart;
846 mutable AABB m_doom3AABB;
847 mutable Matrix4 m_doom3Rotation;
848 mutable Matrix4 m_doom3Projection;
849 mutable Frustum m_doom3Frustum;
850 mutable bool m_doom3ProjectionChanged;
852 RenderLightProjection m_renderProjection;
854 LightShader m_shader;
858 Callback<void()> m_transformChanged;
859 Callback<void()> m_boundsChanged;
860 Callback<void()> m_evaluateTransform;
864 default_rotation(m_rotation);
865 m_aabb_light.origin = Vector3(0, 0, 0);
866 default_extents(m_aabb_light.extents);
868 m_keyObservers.insert("classname", ClassnameFilter::ClassnameChangedCaller(m_filter));
869 m_keyObservers.insert(Static<KeyIsName>::instance().m_nameKey, NamedEntity::IdentifierChangedCaller(m_named));
870 m_keyObservers.insert("_color", Colour::ColourChangedCaller(m_colour));
871 m_keyObservers.insert("origin", OriginKey::OriginChangedCaller(m_originKey));
872 m_keyObservers.insert("_light", LightRadii::PrimaryIntensityChangedCaller(m_radii));
873 m_keyObservers.insert("light", LightRadii::SecondaryIntensityChangedCaller(m_radii));
874 m_keyObservers.insert("fade", LightRadii::FadeChangedCaller(m_radii));
875 m_keyObservers.insert("scale", LightRadii::ScaleChangedCaller(m_radii));
876 m_keyObservers.insert("spawnflags", LightRadii::FlagsChangedCaller(m_radii));
878 if (g_lightType == LIGHTTYPE_DOOM3) {
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;
895 if (g_lightType == LIGHTTYPE_DOOM3) {
896 m_traverse.attach(&m_traverseObservers);
897 m_traverseObservers.attach(m_funcStaticOrigin);
899 m_entity.m_isContainer = true;
905 if (g_lightType == LIGHTTYPE_DOOM3) {
906 m_traverseObservers.detach(m_funcStaticOrigin);
907 m_traverse.detach(&m_traverseObservers);
911 // vc 2k5 compiler fix
920 if (g_lightType == LIGHTTYPE_DOOM3) {
921 m_funcStaticOrigin.originChanged();
924 m_doom3Radius.m_changed();
926 GlobalSelectionSystem().pivotChanged();
931 m_aabb_light.origin = m_useLightOrigin ? m_lightOrigin : m_originKey.m_origin;
935 typedef MemberCaller<Light, void(), &Light::originChanged> OriginChangedCaller;
937 void lightOriginChanged(const char *value)
939 m_useLightOrigin = !string_empty(value);
940 if (m_useLightOrigin) {
941 read_origin(m_lightOrigin, value);
946 typedef MemberCaller<Light, void(const char *), &Light::lightOriginChanged> LightOriginChangedCaller;
948 void lightTargetChanged(const char *value)
950 m_useLightTarget = !string_empty(value);
951 if (m_useLightTarget) {
952 read_origin(m_lightTarget, value);
957 typedef MemberCaller<Light, void(const char *), &Light::lightTargetChanged> LightTargetChangedCaller;
959 void lightUpChanged(const char *value)
961 m_useLightUp = !string_empty(value);
963 read_origin(m_lightUp, value);
968 typedef MemberCaller<Light, void(const char *), &Light::lightUpChanged> LightUpChangedCaller;
970 void lightRightChanged(const char *value)
972 m_useLightRight = !string_empty(value);
973 if (m_useLightRight) {
974 read_origin(m_lightRight, value);
979 typedef MemberCaller<Light, void(const char *), &Light::lightRightChanged> LightRightChangedCaller;
981 void lightStartChanged(const char *value)
983 m_useLightStart = !string_empty(value);
984 if (m_useLightStart) {
985 read_origin(m_lightStart, value);
990 typedef MemberCaller<Light, void(const char *), &Light::lightStartChanged> LightStartChangedCaller;
992 void lightEndChanged(const char *value)
994 m_useLightEnd = !string_empty(value);
996 read_origin(m_lightEnd, value);
1001 typedef MemberCaller<Light, void(const char *), &Light::lightEndChanged> LightEndChangedCaller;
1003 void writeLightOrigin()
1005 write_origin(m_lightOrigin, &m_entity, "light_origin");
1008 void updateLightRadiiBox() const
1010 const Matrix4 &rotation = rotation_toMatrix(m_rotation);
1011 aabb_corners(AABB(Vector3(0, 0, 0), m_doom3Radius.m_radiusTransformed), m_radii_box.m_points);
1012 matrix4_transform_point(rotation, m_radii_box.m_points[0]);
1013 vector3_add(m_radii_box.m_points[0], m_aabb_light.origin);
1014 matrix4_transform_point(rotation, m_radii_box.m_points[1]);
1015 vector3_add(m_radii_box.m_points[1], m_aabb_light.origin);
1016 matrix4_transform_point(rotation, m_radii_box.m_points[2]);
1017 vector3_add(m_radii_box.m_points[2], m_aabb_light.origin);
1018 matrix4_transform_point(rotation, m_radii_box.m_points[3]);
1019 vector3_add(m_radii_box.m_points[3], m_aabb_light.origin);
1020 matrix4_transform_point(rotation, m_radii_box.m_points[4]);
1021 vector3_add(m_radii_box.m_points[4], m_aabb_light.origin);
1022 matrix4_transform_point(rotation, m_radii_box.m_points[5]);
1023 vector3_add(m_radii_box.m_points[5], m_aabb_light.origin);
1024 matrix4_transform_point(rotation, m_radii_box.m_points[6]);
1025 vector3_add(m_radii_box.m_points[6], m_aabb_light.origin);
1026 matrix4_transform_point(rotation, m_radii_box.m_points[7]);
1027 vector3_add(m_radii_box.m_points[7], m_aabb_light.origin);
1030 void rotationChanged()
1032 rotation_assign(m_rotation, m_useLightRotation ? m_lightRotation : m_rotationKey.m_rotation);
1033 GlobalSelectionSystem().pivotChanged();
1036 typedef MemberCaller<Light, void(), &Light::rotationChanged> RotationChangedCaller;
1038 void lightRotationChanged(const char *value)
1040 m_useLightRotation = !string_empty(value);
1041 if (m_useLightRotation) {
1042 read_rotation(m_lightRotation, value);
1047 typedef MemberCaller<Light, void(const char *), &Light::lightRotationChanged> LightRotationChangedCaller;
1051 Light(EntityClass *eclass, scene::Node &node, const Callback<void()> &transformChanged,
1052 const Callback<void()> &boundsChanged, const Callback<void()> &evaluateTransform) :
1054 m_originKey(OriginChangedCaller(*this)),
1055 m_rotationKey(RotationChangedCaller(*this)),
1056 m_colour(Callback<void()>()),
1057 m_filter(m_entity, node),
1059 m_nameKeys(m_entity),
1060 m_funcStaticOrigin(m_traverse, m_originKey.m_origin),
1061 m_doom3Radius(EntityClass_valueForKey(m_entity.getEntityClass(), "light_radius")),
1062 m_radii_wire(m_radii, m_aabb_light.origin),
1063 m_radii_fill(m_radii, m_aabb_light.origin),
1064 m_radii_box(m_aabb_light.origin),
1065 m_render_center(m_doom3Radius.m_center, m_entity.getEntityClass()),
1066 m_renderName(m_named, m_aabb_light.origin),
1067 m_useLightOrigin(false),
1068 m_useLightRotation(false),
1069 m_renderProjection(m_doom3Projection),
1070 m_transformChanged(transformChanged),
1071 m_boundsChanged(boundsChanged),
1072 m_evaluateTransform(evaluateTransform)
1077 Light(const Light &other, scene::Node &node, const Callback<void()> &transformChanged,
1078 const Callback<void()> &boundsChanged, const Callback<void()> &evaluateTransform) :
1079 m_entity(other.m_entity),
1080 m_originKey(OriginChangedCaller(*this)),
1081 m_rotationKey(RotationChangedCaller(*this)),
1082 m_colour(Callback<void()>()),
1083 m_filter(m_entity, node),
1085 m_nameKeys(m_entity),
1086 m_funcStaticOrigin(m_traverse, m_originKey.m_origin),
1087 m_doom3Radius(EntityClass_valueForKey(m_entity.getEntityClass(), "light_radius")),
1088 m_radii_wire(m_radii, m_aabb_light.origin),
1089 m_radii_fill(m_radii, m_aabb_light.origin),
1090 m_radii_box(m_aabb_light.origin),
1091 m_render_center(m_doom3Radius.m_center, m_entity.getEntityClass()),
1092 m_renderName(m_named, m_aabb_light.origin),
1093 m_useLightOrigin(false),
1094 m_useLightRotation(false),
1095 m_renderProjection(m_doom3Projection),
1096 m_transformChanged(transformChanged),
1097 m_boundsChanged(boundsChanged),
1098 m_evaluateTransform(evaluateTransform)
1108 InstanceCounter m_instanceCounter;
1110 void instanceAttach(const scene::Path &path)
1112 if (++m_instanceCounter.m_count == 1) {
1113 m_filter.instanceAttach();
1114 m_entity.instanceAttach(path_find_mapfile(path.begin(), path.end()));
1115 if (g_lightType == LIGHTTYPE_DOOM3) {
1116 m_traverse.instanceAttach(path_find_mapfile(path.begin(), path.end()));
1118 m_entity.attach(m_keyObservers);
1120 if (g_lightType == LIGHTTYPE_DOOM3) {
1121 m_funcStaticOrigin.enable();
1126 void instanceDetach(const scene::Path &path)
1128 if (--m_instanceCounter.m_count == 0) {
1129 if (g_lightType == LIGHTTYPE_DOOM3) {
1130 m_funcStaticOrigin.disable();
1133 m_entity.detach(m_keyObservers);
1134 if (g_lightType == LIGHTTYPE_DOOM3) {
1135 m_traverse.instanceDetach(path_find_mapfile(path.begin(), path.end()));
1137 m_entity.instanceDetach(path_find_mapfile(path.begin(), path.end()));
1138 m_filter.instanceDetach();
1142 EntityKeyValues &getEntity()
1147 const EntityKeyValues &getEntity() const
1152 scene::Traversable &getTraversable()
1157 Namespaced &getNamespaced()
1162 Nameable &getNameable()
1167 TransformNode &getTransformNode()
1172 void attach(scene::Traversable::Observer *observer)
1174 m_traverseObservers.attach(*observer);
1177 void detach(scene::Traversable::Observer *observer)
1179 m_traverseObservers.detach(*observer);
1182 void render(RenderStateFlags state) const
1184 if (!g_newLightDraw) {
1185 aabb_draw(m_aabb_light, state);
1187 light_draw(m_aabb_light, state);
1191 VolumeIntersectionValue intersectVolume(const VolumeTest &volume, const Matrix4 &localToWorld) const
1193 return volume.TestAABB(m_aabb_light, localToWorld);
1197 const AABB &localAABB() const
1199 return m_aabb_light;
1203 mutable Matrix4 m_projectionOrientation;
1205 void renderSolid(Renderer &renderer, const VolumeTest &volume, const Matrix4 &localToWorld, bool selected) const
1207 renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly);
1208 renderer.SetState(m_colour.state(), Renderer::eFullMaterials);
1209 renderer.addRenderable(*this, localToWorld);
1211 if (selected && g_lightRadii && string_empty(m_entity.getKeyValue("target"))) {
1212 if (renderer.getStyle() == Renderer::eFullMaterials) {
1213 renderer.SetState(RenderLightRadiiFill::m_state, Renderer::eFullMaterials);
1214 renderer.Highlight(Renderer::ePrimitive, false);
1215 renderer.addRenderable(m_radii_fill, localToWorld);
1217 renderer.addRenderable(m_radii_wire, localToWorld);
1221 renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eFullMaterials);
1223 if (g_lightType == LIGHTTYPE_DOOM3 && selected) {
1224 if (isProjected()) {
1226 m_projectionOrientation = rotation();
1227 vector4_to_vector3(m_projectionOrientation.t()) = localAABB().origin;
1228 renderer.addRenderable(m_renderProjection, m_projectionOrientation);
1230 updateLightRadiiBox();
1231 renderer.addRenderable(m_radii_box, localToWorld);
1234 //draw the center of the light
1235 if (m_doom3Radius.m_useCenterKey) {
1236 renderer.Highlight(Renderer::ePrimitive, false);
1237 renderer.Highlight(Renderer::eFace, false);
1238 renderer.SetState(m_render_center.m_state, Renderer::eFullMaterials);
1239 renderer.SetState(m_render_center.m_state, Renderer::eWireframeOnly);
1240 renderer.addRenderable(m_render_center, localToWorld);
1245 void renderWireframe(Renderer &renderer, const VolumeTest &volume, const Matrix4 &localToWorld, bool selected) const
1247 renderSolid(renderer, volume, localToWorld, selected);
1249 renderer.addRenderable(m_renderName, localToWorld);
1253 void testSelect(Selector &selector, SelectionTest &test, const Matrix4 &localToWorld)
1255 test.BeginMesh(localToWorld);
1257 SelectionIntersection best;
1258 aabb_testselect(m_aabb_light, test, best);
1260 selector.addIntersection(best);
1264 void translate(const Vector3 &translation)
1266 m_aabb_light.origin = origin_translated(m_aabb_light.origin, translation);
1269 void rotate(const Quaternion &rotation)
1271 rotation_rotate(m_rotation, rotation);
1274 void snapto(float snap)
1276 if (g_lightType == LIGHTTYPE_DOOM3 && !m_useLightOrigin && !m_traverse.empty()) {
1277 m_useLightOrigin = true;
1278 m_lightOrigin = m_originKey.m_origin;
1281 if (m_useLightOrigin) {
1282 m_lightOrigin = origin_snapped(m_lightOrigin, snap);
1285 m_originKey.m_origin = origin_snapped(m_originKey.m_origin, snap);
1286 m_originKey.write(&m_entity);
1290 void setLightRadius(const AABB &aabb)
1292 m_aabb_light.origin = aabb.origin;
1293 m_doom3Radius.m_radiusTransformed = aabb.extents;
1296 void transformLightRadius(const Matrix4 &transform)
1298 matrix4_transform_point(transform, m_aabb_light.origin);
1301 void revertTransform()
1303 m_aabb_light.origin = m_useLightOrigin ? m_lightOrigin : m_originKey.m_origin;
1304 rotation_assign(m_rotation, m_useLightRotation ? m_lightRotation : m_rotationKey.m_rotation);
1305 m_doom3Radius.m_radiusTransformed = m_doom3Radius.m_radius;
1308 void freezeTransform()
1310 if (g_lightType == LIGHTTYPE_DOOM3 && !m_useLightOrigin && !m_traverse.empty()) {
1311 m_useLightOrigin = true;
1314 if (m_useLightOrigin) {
1315 m_lightOrigin = m_aabb_light.origin;
1318 m_originKey.m_origin = m_aabb_light.origin;
1319 m_originKey.write(&m_entity);
1322 if (g_lightType == LIGHTTYPE_DOOM3) {
1323 if (!m_useLightRotation && !m_traverse.empty()) {
1324 m_useLightRotation = true;
1327 if (m_useLightRotation) {
1328 rotation_assign(m_lightRotation, m_rotation);
1329 write_rotation(m_lightRotation, &m_entity, "light_rotation");
1332 rotation_assign(m_rotationKey.m_rotation, m_rotation);
1333 write_rotation(m_rotationKey.m_rotation, &m_entity);
1335 m_doom3Radius.m_radius = m_doom3Radius.m_radiusTransformed;
1336 write_origin(m_doom3Radius.m_radius, &m_entity, "light_radius");
1340 void transformChanged()
1343 m_evaluateTransform();
1347 typedef MemberCaller<Light, void(), &Light::transformChanged> TransformChangedCaller;
1349 mutable Matrix4 m_localPivot;
1351 const Matrix4 &getLocalPivot() const
1353 m_localPivot = rotation_toMatrix(m_rotation);
1354 vector4_to_vector3(m_localPivot.t()) = m_aabb_light.origin;
1355 return m_localPivot;
1358 void setLightChangedCallback(const Callback<void()> &callback)
1360 m_doom3Radius.m_changed = callback;
1363 const AABB &aabb() const
1365 m_doom3AABB = AABB(m_aabb_light.origin, m_doom3Radius.m_radiusTransformed);
1369 bool testAABB(const AABB &other) const
1371 if (isProjected()) {
1372 Matrix4 transform = rotation();
1373 vector4_to_vector3(transform.t()) = localAABB().origin;
1375 Frustum frustum(frustum_transformed(m_doom3Frustum, transform));
1376 return frustum_test_aabb(frustum, other) != c_volumeOutside;
1378 // test against an AABB which contains the rotated bounds of this light.
1379 const AABB &bounds = aabb();
1380 return aabb_intersects_aabb(other, AABB(
1383 static_cast<float>( fabs(m_rotation[0] * bounds.extents[0])
1384 + fabs(m_rotation[3] * bounds.extents[1])
1385 + fabs(m_rotation[6] * bounds.extents[2])),
1386 static_cast<float>( fabs(m_rotation[1] * bounds.extents[0])
1387 + fabs(m_rotation[4] * bounds.extents[1])
1388 + fabs(m_rotation[7] * bounds.extents[2])),
1389 static_cast<float>( fabs(m_rotation[2] * bounds.extents[0])
1390 + fabs(m_rotation[5] * bounds.extents[1])
1391 + fabs(m_rotation[8] * bounds.extents[2]))
1396 const Matrix4 &rotation() const
1398 m_doom3Rotation = rotation_toMatrix(m_rotation);
1399 return m_doom3Rotation;
1402 const Vector3 &offset() const
1404 return m_doom3Radius.m_center;
1407 const Vector3 &colour() const
1409 return m_colour.m_colour;
1412 bool isProjected() const
1414 return m_useLightTarget && m_useLightUp && m_useLightRight;
1417 void projectionChanged()
1419 m_doom3ProjectionChanged = true;
1420 m_doom3Radius.m_changed();
1421 SceneChangeNotify();
1424 const Matrix4 &projection() const
1426 if (!m_doom3ProjectionChanged) {
1427 return m_doom3Projection;
1429 m_doom3ProjectionChanged = false;
1430 m_doom3Projection = g_matrix4_identity;
1431 matrix4_translate_by_vec3(m_doom3Projection, Vector3(0.5f, 0.5f, 0));
1432 matrix4_scale_by_vec3(m_doom3Projection, Vector3(0.5f, 0.5f, 1));
1435 Vector3 right = vector3_cross( m_lightUp, vector3_normalised( m_lightTarget ) );
1436 Vector3 up = vector3_cross( vector3_normalised( m_lightTarget ), m_lightRight );
1437 Vector3 target = m_lightTarget;
1439 -right.x(), -right.y(), -right.z(), 0,
1440 -up.x(), -up.y(), -up.z(), 0,
1441 -target.x(), -target.y(), -target.z(), 0,
1444 Matrix4 frustum = matrix4_frustum( -0.01, 0.01, -0.01, 0.01, 0.01, 1.0 );
1445 test = matrix4_full_inverse( test );
1446 matrix4_premultiply_by_matrix4( test, frustum );
1447 matrix4_multiply_by_matrix4( m_doom3Projection, test );
1449 const float nearFar = 1 / 49.5f;
1450 Vector3 right = vector3_cross( m_lightUp, vector3_normalised( m_lightTarget + m_lightRight ) );
1451 Vector3 up = vector3_cross( vector3_normalised( m_lightTarget + m_lightUp ), m_lightRight );
1452 Vector3 target = vector3_negated( m_lightTarget * ( 1 + nearFar ) );
1453 float scale = -1 / vector3_length( m_lightTarget );
1455 -inverse( right.x() ), -inverse( up.x() ), -inverse( target.x() ), 0,
1456 -inverse( right.y() ), -inverse( up.y() ), -inverse( target.y() ), 0,
1457 -inverse( right.z() ), -inverse( up.z() ), -inverse( target.z() ), scale,
1460 matrix4_multiply_by_matrix4( m_doom3Projection, test );
1462 Vector3 leftA( m_lightTarget - m_lightRight );
1463 Vector3 leftB( m_lightRight + m_lightUp );
1464 Plane3 left( vector3_normalised( vector3_cross( leftA, leftB ) ) * ( 1.0 / 128 ), 0 );
1465 Vector3 rightA( m_lightTarget + m_lightRight );
1466 Vector3 rightB( vector3_cross( rightA, m_lightTarget ) );
1467 Plane3 right( vector3_normalised( vector3_cross( rightA, rightB ) ) * ( 1.0 / 128 ), 0 );
1468 Vector3 bottomA( m_lightTarget - m_lightUp );
1469 Vector3 bottomB( vector3_cross( bottomA, m_lightTarget ) );
1470 Plane3 bottom( vector3_normalised( vector3_cross( bottomA, bottomB ) ) * ( 1.0 / 128 ), 0 );
1471 Vector3 topA( m_lightTarget + m_lightUp );
1472 Vector3 topB( vector3_cross( topA, m_lightTarget ) );
1473 Plane3 top( vector3_normalised( vector3_cross( topA, topB ) ) * ( 1.0 / 128 ), 0 );
1474 Plane3 front( vector3_normalised( m_lightTarget ) * ( 1.0 / 128 ), 1 );
1475 Plane3 back( vector3_normalised( vector3_negated( m_lightTarget ) ) * ( 1.0 / 128 ), 0 );
1476 Matrix4 test( matrix4_from_planes( plane3_flipped( left ), plane3_flipped( right ), plane3_flipped( bottom ), plane3_flipped( top ), plane3_flipped( front ), plane3_flipped( back ) ) );
1477 matrix4_multiply_by_matrix4( m_doom3Projection, test );
1480 Plane3 lightProject[4];
1482 Vector3 start = m_useLightStart && m_useLightEnd ? m_lightStart : vector3_normalised(m_lightTarget);
1483 Vector3 stop = m_useLightStart && m_useLightEnd ? m_lightEnd : m_lightTarget;
1485 float rLen = vector3_length(m_lightRight);
1486 Vector3 right = vector3_divided(m_lightRight, rLen);
1487 float uLen = vector3_length(m_lightUp);
1488 Vector3 up = vector3_divided(m_lightUp, uLen);
1489 Vector3 normal = vector3_normalised(vector3_cross(up, right));
1491 float dist = vector3_dot(m_lightTarget, normal);
1494 normal = vector3_negated(normal);
1497 right *= (0.5f * dist) / rLen;
1498 up *= -(0.5f * dist) / uLen;
1500 lightProject[2] = Plane3(normal, 0);
1501 lightProject[0] = Plane3(right, 0);
1502 lightProject[1] = Plane3(up, 0);
1504 // now offset to center
1505 Vector4 targetGlobal(m_lightTarget, 1);
1507 float a = vector4_dot(targetGlobal, plane3_to_vector4(lightProject[0]));
1508 float b = vector4_dot(targetGlobal, plane3_to_vector4(lightProject[2]));
1509 float ofs = 0.5f - a / b;
1510 plane3_to_vector4(lightProject[0]) += plane3_to_vector4(lightProject[2]) * ofs;
1513 float a = vector4_dot(targetGlobal, plane3_to_vector4(lightProject[1]));
1514 float b = vector4_dot(targetGlobal, plane3_to_vector4(lightProject[2]));
1515 float ofs = 0.5f - a / b;
1516 plane3_to_vector4(lightProject[1]) += plane3_to_vector4(lightProject[2]) * ofs;
1519 // set the falloff vector
1520 Vector3 falloff = stop - start;
1521 float length = vector3_length(falloff);
1522 falloff = vector3_divided(falloff, length);
1526 falloff *= (1.0f / length);
1527 lightProject[3] = Plane3(falloff, -vector3_dot(start, falloff));
1529 // we want the planes of s=0, s=q, t=0, and t=q
1530 m_doom3Frustum.left = lightProject[0];
1531 m_doom3Frustum.bottom = lightProject[1];
1532 m_doom3Frustum.right = Plane3(lightProject[2].normal() - lightProject[0].normal(),
1533 lightProject[2].dist() - lightProject[0].dist());
1534 m_doom3Frustum.top = Plane3(lightProject[2].normal() - lightProject[1].normal(),
1535 lightProject[2].dist() - lightProject[1].dist());
1537 // we want the planes of s=0 and s=1 for front and rear clipping planes
1538 m_doom3Frustum.front = lightProject[3];
1540 m_doom3Frustum.back = lightProject[3];
1541 m_doom3Frustum.back.dist() -= 1.0f;
1542 m_doom3Frustum.back = plane3_flipped(m_doom3Frustum.back);
1544 Matrix4 test(matrix4_from_planes(m_doom3Frustum.left, m_doom3Frustum.right, m_doom3Frustum.bottom,
1545 m_doom3Frustum.top, m_doom3Frustum.front, m_doom3Frustum.back));
1546 matrix4_multiply_by_matrix4(m_doom3Projection, test);
1548 m_doom3Frustum.left = plane3_normalised(m_doom3Frustum.left);
1549 m_doom3Frustum.right = plane3_normalised(m_doom3Frustum.right);
1550 m_doom3Frustum.bottom = plane3_normalised(m_doom3Frustum.bottom);
1551 m_doom3Frustum.top = plane3_normalised(m_doom3Frustum.top);
1552 m_doom3Frustum.back = plane3_normalised(m_doom3Frustum.back);
1553 m_doom3Frustum.front = plane3_normalised(m_doom3Frustum.front);
1555 //matrix4_scale_by_vec3(m_doom3Projection, Vector3(1.0 / 128, 1.0 / 128, 1.0 / 128));
1556 return m_doom3Projection;
1559 Shader *getShader() const
1561 return m_shader.get();
1565 class LightInstance :
1566 public TargetableInstance,
1567 public TransformModifier,
1569 public SelectionTestable,
1570 public RendererLight,
1571 public PlaneSelectable,
1572 public ComponentSelectionTestable {
1574 InstanceTypeCastTable m_casts;
1578 m_casts = TargetableInstance::StaticTypeCasts::instance().get();
1579 InstanceContainedCast<LightInstance, Bounded>::install(m_casts);
1580 //InstanceContainedCast<LightInstance, Cullable>::install(m_casts);
1581 InstanceStaticCast<LightInstance, Renderable>::install(m_casts);
1582 InstanceStaticCast<LightInstance, SelectionTestable>::install(m_casts);
1583 InstanceStaticCast<LightInstance, Transformable>::install(m_casts);
1584 InstanceStaticCast<LightInstance, PlaneSelectable>::install(m_casts);
1585 InstanceStaticCast<LightInstance, ComponentSelectionTestable>::install(m_casts);
1586 InstanceIdentityCast<LightInstance>::install(m_casts);
1589 InstanceTypeCastTable &get()
1596 DragPlanes m_dragPlanes; // dragplanes for lightresizing using mousedrag
1598 typedef LazyStatic<TypeCasts> StaticTypeCasts;
1600 Bounded &get(NullType<Bounded>)
1605 STRING_CONSTANT(Name, "LightInstance");
1607 LightInstance(const scene::Path &path, scene::Instance *parent, Light &contained) :
1608 TargetableInstance(path, parent, this, StaticTypeCasts::instance().get(), contained.getEntity(), *this),
1609 TransformModifier(Light::TransformChangedCaller(contained), ApplyTransformCaller(*this)),
1610 m_contained(contained),
1611 m_dragPlanes(SelectedChangedComponentCaller(*this))
1613 m_contained.instanceAttach(Instance::path());
1615 if (g_lightType == LIGHTTYPE_DOOM3) {
1616 GlobalShaderCache().attach(*this);
1617 m_contained.setLightChangedCallback(LightChangedCaller(*this));
1620 StaticRenderableConnectionLines::instance().attach(*this);
1625 StaticRenderableConnectionLines::instance().detach(*this);
1627 if (g_lightType == LIGHTTYPE_DOOM3) {
1628 m_contained.setLightChangedCallback(Callback<void()>());
1629 GlobalShaderCache().detach(*this);
1632 m_contained.instanceDetach(Instance::path());
1635 void renderSolid(Renderer &renderer, const VolumeTest &volume) const
1637 m_contained.renderSolid(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
1640 void renderWireframe(Renderer &renderer, const VolumeTest &volume) const
1642 m_contained.renderWireframe(renderer, volume, Instance::localToWorld(), getSelectable().isSelected());
1645 void testSelect(Selector &selector, SelectionTest &test)
1647 m_contained.testSelect(selector, test, Instance::localToWorld());
1650 void selectPlanes(Selector &selector, SelectionTest &test, const PlaneCallback &selectedPlaneCallback)
1652 test.BeginMesh(localToWorld());
1653 m_dragPlanes.selectPlanes(m_contained.aabb(), selector, test, selectedPlaneCallback, rotation());
1656 void selectReversedPlanes(Selector &selector, const SelectedPlanes &selectedPlanes)
1658 m_dragPlanes.selectReversedPlanes(m_contained.aabb(), selector, selectedPlanes, rotation());
1661 bool isSelectedComponents() const
1663 return m_dragPlanes.isSelected();
1666 void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode)
1668 if (mode == SelectionSystem::eFace) {
1669 m_dragPlanes.setSelected(false);
1673 void testSelectComponents(Selector &selector, SelectionTest &test, SelectionSystem::EComponentMode mode)
1677 void selectedChangedComponent(const Selectable &selectable)
1679 GlobalSelectionSystem().getObserver(SelectionSystem::eComponent)(selectable);
1680 GlobalSelectionSystem().onComponentSelection(*this, selectable);
1683 typedef MemberCaller<LightInstance, void(
1684 const Selectable &), &LightInstance::selectedChangedComponent> SelectedChangedComponentCaller;
1686 void evaluateTransform()
1688 if (getType() == TRANSFORM_PRIMITIVE) {
1689 m_contained.translate(getTranslation());
1690 m_contained.rotate(getRotation());
1692 //globalOutputStream() << getTranslation() << "\n";
1694 m_dragPlanes.m_bounds = m_contained.aabb();
1695 m_contained.setLightRadius(m_dragPlanes.evaluateResize(getTranslation(), rotation()));
1699 void applyTransform()
1701 m_contained.revertTransform();
1702 evaluateTransform();
1703 m_contained.freezeTransform();
1706 typedef MemberCaller<LightInstance, void(), &LightInstance::applyTransform> ApplyTransformCaller;
1710 GlobalShaderCache().changed(*this);
1713 typedef MemberCaller<LightInstance, void(), &LightInstance::lightChanged> LightChangedCaller;
1715 Shader *getShader() const
1717 return m_contained.getShader();
1720 const AABB &aabb() const
1722 return m_contained.aabb();
1725 bool testAABB(const AABB &other) const
1727 return m_contained.testAABB(other);
1730 const Matrix4 &rotation() const
1732 return m_contained.rotation();
1735 const Vector3 &offset() const
1737 return m_contained.offset();
1740 const Vector3 &colour() const
1742 return m_contained.colour();
1745 bool isProjected() const
1747 return m_contained.isProjected();
1750 const Matrix4 &projection() const
1752 return m_contained.projection();
1757 public scene::Node::Symbiot,
1758 public scene::Instantiable,
1759 public scene::Cloneable,
1760 public scene::Traversable::Observer {
1762 NodeTypeCastTable m_casts;
1766 NodeStaticCast<LightNode, scene::Instantiable>::install(m_casts);
1767 NodeStaticCast<LightNode, scene::Cloneable>::install(m_casts);
1768 if (g_lightType == LIGHTTYPE_DOOM3) {
1769 NodeContainedCast<LightNode, scene::Traversable>::install(m_casts);
1771 NodeContainedCast<LightNode, Editable>::install(m_casts);
1772 NodeContainedCast<LightNode, Snappable>::install(m_casts);
1773 NodeContainedCast<LightNode, TransformNode>::install(m_casts);
1774 NodeContainedCast<LightNode, Entity>::install(m_casts);
1775 NodeContainedCast<LightNode, Nameable>::install(m_casts);
1776 NodeContainedCast<LightNode, Namespaced>::install(m_casts);
1779 NodeTypeCastTable &get()
1787 InstanceSet m_instances;
1792 if (g_lightType == LIGHTTYPE_DOOM3) {
1793 m_contained.attach(this);
1799 if (g_lightType == LIGHTTYPE_DOOM3) {
1800 m_contained.detach(this);
1805 typedef LazyStatic<TypeCasts> StaticTypeCasts;
1807 scene::Traversable &get(NullType<scene::Traversable>)
1809 return m_contained.getTraversable();
1812 Editable &get(NullType<Editable>)
1817 Snappable &get(NullType<Snappable>)
1822 TransformNode &get(NullType<TransformNode>)
1824 return m_contained.getTransformNode();
1827 Entity &get(NullType<Entity>)
1829 return m_contained.getEntity();
1832 Nameable &get(NullType<Nameable>)
1834 return m_contained.getNameable();
1837 Namespaced &get(NullType<Namespaced>)
1839 return m_contained.getNamespaced();
1842 LightNode(EntityClass *eclass) :
1843 m_node(this, this, StaticTypeCasts::instance().get()),
1844 m_contained(eclass, m_node, InstanceSet::TransformChangedCaller(m_instances),
1845 InstanceSet::BoundsChangedCaller(m_instances),
1846 InstanceSetEvaluateTransform<LightInstance>::Caller(m_instances))
1851 LightNode(const LightNode &other) :
1852 scene::Node::Symbiot(other),
1853 scene::Instantiable(other),
1854 scene::Cloneable(other),
1855 scene::Traversable::Observer(other),
1856 m_node(this, this, StaticTypeCasts::instance().get()),
1857 m_contained(other.m_contained, m_node, InstanceSet::TransformChangedCaller(m_instances),
1858 InstanceSet::BoundsChangedCaller(m_instances),
1859 InstanceSetEvaluateTransform<LightInstance>::Caller(m_instances))
1879 scene::Node &clone() const
1881 return (new LightNode(*this))->node();
1884 void insert(scene::Node &child)
1886 m_instances.insert(child);
1889 void erase(scene::Node &child)
1891 m_instances.erase(child);
1894 scene::Instance *create(const scene::Path &path, scene::Instance *parent)
1896 return new LightInstance(path, parent, m_contained);
1899 void forEachInstance(const scene::Instantiable::Visitor &visitor)
1901 m_instances.forEachInstance(visitor);
1904 void insert(scene::Instantiable::Observer *observer, const scene::Path &path, scene::Instance *instance)
1906 m_instances.insert(observer, path, instance);
1909 scene::Instance *erase(scene::Instantiable::Observer *observer, const scene::Path &path)
1911 return m_instances.erase(observer, path);
1915 void Light_Construct(LightType lightType)
1917 g_lightType = lightType;
1918 if (g_lightType == LIGHTTYPE_DOOM3) {
1919 LightShader::m_defaultShader = "lights/defaultPointLight";
1921 LightShader::m_defaultShader = "lights/defaultProjectedLight";
1924 RenderLightRadiiFill::m_state = GlobalShaderCache().capture("$Q3MAP2_LIGHT_SPHERE");
1925 RenderLightCenter::m_state = GlobalShaderCache().capture("$BIGPOINT");
1928 void Light_Destroy()
1930 GlobalShaderCache().release("$Q3MAP2_LIGHT_SPHERE");
1931 GlobalShaderCache().release("$BIGPOINT");
1934 scene::Node &New_Light(EntityClass *eclass)
1936 return (new LightNode(eclass))->node();