1 /** @file lumobj.cpp  Luminous object.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2006-2015 Daniel Swanson <danij@dengine.net>
5  *
6  * @par License
7  * GPL: http://www.gnu.org/licenses/gpl.html
8  *
9  * <small>This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; either version 2 of the License, or (at your
12  * option) any later version. This program is distributed in the hope that it
13  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15  * Public License for more details. You should have received a copy of the GNU
16  * General Public License along with this program; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18  * 02110-1301 USA</small>
19  */
20 
21 #include "de_platform.h"
22 #include "render/lumobj.h"
23 
24 #include <de/vector1.h>
25 #include <doomsday/console/var.h>
26 
27 #include "render/rend_halo.h"
28 #include "render/vissprite.h"
29 
30 #include "world/map.h"
31 
32 using namespace de;
33 
34 static dint radiusMax     = 320;    ///< Absolute maximum lumobj radius (cvar).
35 static dfloat radiusScale = 5.2f;  ///< Radius scale factor (cvar).
36 
occlusion(Vector3d const &) const37 dfloat Lumobj::Source::occlusion(Vector3d const & /*eye*/) const
38 {
39     return 1;  // Fully visible.
40 }
41 
DENG2_PIMPL_NOREF(Lumobj)42 DENG2_PIMPL_NOREF(Lumobj)
43 {
44     Source const *source = nullptr;      ///< Source of the lumobj (if any, not owned).
45     mobj_t const *sourceMobj = nullptr;  ///< Mobj associated with the lumobj (if any).
46     ddouble maxDistance = 0;             ///< Used when rendering to limit the number drawn lumobjs.
47     Vector3f color = Vector3f(1, 1, 1);  ///< Light color/intensity.
48     ddouble radius = 256;                ///< Radius in map space units.
49     ddouble zOffset = 0;                 ///< Z-axis origin offset in map space units.
50     dfloat flareSize = 0;                ///< Scale factor.
51     DGLuint flareTex = 0;                ///< Custom flare texture (@todo should be Texture ptr).
52 
53     /// Custom lightmaps (if any, not owned):
54     ClientTexture *sideTex = nullptr;
55     ClientTexture *downTex = nullptr;
56     ClientTexture *upTex   = nullptr;
57 
58     Impl() {}
59 
60     Impl(Impl const &other)
61         : source     (other.source)
62         , sourceMobj (other.sourceMobj)
63         , maxDistance(other.maxDistance)
64         , color      (other.color)
65         , radius     (other.radius)
66         , zOffset    (other.zOffset)
67         , flareSize  (other.flareSize)
68         , flareTex   (other.flareTex)
69         , sideTex    (other.sideTex)
70         , downTex    (other.downTex)
71         , upTex      (other.upTex)
72     {}
73 };
74 
Lumobj(Vector3d const & origin,ddouble radius,Vector3f const & color)75 Lumobj::Lumobj(Vector3d const &origin, ddouble radius, Vector3f const &color)
76     : MapObject(origin), d(new Impl())
77 {
78     setRadius(radius);
79     setColor(color);
80 }
81 
Lumobj(Lumobj const & other)82 Lumobj::Lumobj(Lumobj const &other)
83     : MapObject(other.origin()), d(new Impl(*other.d))
84 {}
85 
setSource(Source const * newSource)86 void Lumobj::setSource(Source const *newSource)
87 {
88     d->source = newSource;
89 }
90 
setSourceMobj(mobj_t const * mo)91 void Lumobj::setSourceMobj(mobj_t const *mo)
92 {
93     d->sourceMobj = mo;
94 }
95 
sourceMobj() const96 mobj_t const *Lumobj::sourceMobj() const
97 {
98     return d->sourceMobj;
99 }
100 
color() const101 Vector3f const &Lumobj::color() const
102 {
103     return d->color;
104 }
105 
setColor(Vector3f const & newColor)106 Lumobj &Lumobj::setColor(Vector3f const &newColor)
107 {
108     if(d->color != newColor)
109     {
110         d->color = newColor;
111     }
112     return *this;
113 }
114 
radius() const115 ddouble Lumobj::radius() const
116 {
117     return d->radius;
118 }
119 
setRadius(ddouble newRadius)120 Lumobj &Lumobj::setRadius(ddouble newRadius)
121 {
122     // Apply the global scale factor.
123     newRadius *= 40 * ::radiusScale;
124 
125     // Normalize.
126     newRadius = de::clamp<ddouble>(.0001, de::abs(newRadius), ::radiusMax);
127 
128     if(d->radius != newRadius)
129     {
130         d->radius = newRadius;
131     }
132     return *this;
133 }
134 
bounds() const135 AABoxd Lumobj::bounds() const
136 {
137     return AABoxd(origin().x - d->radius, origin().y - d->radius,
138                   origin().x + d->radius, origin().y + d->radius);
139 }
140 
zOffset() const141 ddouble Lumobj::zOffset() const
142 {
143     return d->zOffset;
144 }
145 
setZOffset(ddouble newZOffset)146 Lumobj &Lumobj::setZOffset(ddouble newZOffset)
147 {
148     d->zOffset = newZOffset;
149     return *this;
150 }
151 
maxDistance() const152 ddouble Lumobj::maxDistance() const
153 {
154     return d->maxDistance;
155 }
156 
setMaxDistance(ddouble newMaxDistance)157 Lumobj &Lumobj::setMaxDistance(ddouble newMaxDistance)
158 {
159     d->maxDistance = newMaxDistance;
160     return *this;
161 }
162 
lightmap(LightmapSemantic semantic) const163 ClientTexture *Lumobj::lightmap(LightmapSemantic semantic) const
164 {
165     switch(semantic)
166     {
167     case Side: return d->sideTex;
168     case Down: return d->downTex;
169     case Up:   return d->upTex;
170     };
171     DENG2_ASSERT(false);
172     return d->sideTex;
173 }
174 
setLightmap(LightmapSemantic semantic,ClientTexture * newTexture)175 Lumobj &Lumobj::setLightmap(LightmapSemantic semantic, ClientTexture *newTexture)
176 {
177     switch(semantic)
178     {
179     case Side: d->sideTex = newTexture; break;
180     case Down: d->downTex = newTexture; break;
181     case Up:   d->upTex   = newTexture; break;
182     };
183     return *this;
184 }
185 
flareSize() const186 dfloat Lumobj::flareSize() const
187 {
188     return d->flareSize;
189 }
190 
setFlareSize(dfloat newFlareSize)191 Lumobj &Lumobj::setFlareSize(dfloat newFlareSize)
192 {
193     d->flareSize = de::max(0.f, newFlareSize);
194     return *this;
195 }
196 
flareTexture() const197 DGLuint Lumobj::flareTexture() const
198 {
199     return d->flareTex;
200 }
201 
setFlareTexture(DGLuint newTexture)202 Lumobj &Lumobj::setFlareTexture(DGLuint newTexture)
203 {
204     d->flareTex = newTexture;
205     return *this;
206 }
207 
attenuation(ddouble distFromEye) const208 dfloat Lumobj::attenuation(ddouble distFromEye) const
209 {
210     if(distFromEye > 0 && d->maxDistance > 0)
211     {
212         if(distFromEye > d->maxDistance) return 0;
213         if(distFromEye > .67 * d->maxDistance)
214             return (d->maxDistance - distFromEye) / (.33 * d->maxDistance);
215     }
216     return 1;
217 }
218 
generateFlare(Vector3d const & eye,ddouble distFromEye)219 void Lumobj::generateFlare(Vector3d const &eye, ddouble distFromEye)
220 {
221     // Is the point in range?
222     if(d->maxDistance > 0 && distFromEye > d->maxDistance)
223         return;
224 
225     /// @todo Remove this limitation.
226     if(!d->source) return;
227 
228     vissprite_t *vis = R_NewVisSprite(VSPR_FLARE);
229 
230     vis->pose.origin             = origin();
231     vis->pose.distance           = distFromEye;
232     V3f_Set(vis->data.flare.color, d->color.x, d->color.y, d->color.z);
233     vis->data.flare.mul          = d->source->occlusion(eye) * attenuation(distFromEye);
234     vis->data.flare.size         = d->flareSize > 0? de::max(1.f, d->flareSize * 60 * (50 + haloSize) / 100.0f) : 0;
235     vis->data.flare.tex          = d->flareTex;
236     vis->data.flare.lumIdx       = indexInMap();
237     vis->data.flare.isDecoration = true;
238 }
239 
radiusMax()240 dint Lumobj::radiusMax()  // static
241 {
242     return ::radiusMax;
243 }
244 
radiusFactor()245 dfloat Lumobj::radiusFactor()  // static
246 {
247     return ::radiusScale;
248 }
249 
consoleRegister()250 void Lumobj::consoleRegister()  // static
251 {
252     C_VAR_INT  ("rend-light-radius-max",   &::radiusMax,   0, 64,  512);
253     C_VAR_FLOAT("rend-light-radius-scale", &::radiusScale, 0, .1f, 10);
254 }
255