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