|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt3D module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "light.h" |
|
43 #include <QtCore/qmath.h> |
|
44 #include <QtGui/qmatrix4x4.h> |
|
45 |
|
46 /*! |
|
47 \class Light |
|
48 \brief The Light class represents the parameters of a light in a 3D scene. |
|
49 \since 4.7 |
|
50 \ingroup qt3d |
|
51 \ingroup qt3d::painting |
|
52 |
|
53 The functions in this class are a convenience wrapper for the OpenGL enumerations |
|
54 which control each light source in a 3D scene. For the general ambient light in a |
|
55 scene, not from a source refer to QGLLightModel. |
|
56 |
|
57 The ambient, diffuse and specular components of the light can be controlled for by |
|
58 colour, and are set by the following functions: |
|
59 \list |
|
60 \o setAmbientColor() |
|
61 \o setDiffuseColor() |
|
62 \o setSpecularColor() |
|
63 \endlist |
|
64 Other than changing intensity by using darker color values, see below for how to |
|
65 change the intensity of the light with distance from the lit object. |
|
66 |
|
67 Light sources are of two types, directional and positional, described by the |
|
68 enumeration Light::LightType. By default a light source is directional. |
|
69 |
|
70 A directional light source is infinitely distant, such that its rays are all |
|
71 parallel, to a direction \bold vector. This vector is set by the setDirection() |
|
72 function. If the light source is not directional when setDirection() is called, its |
|
73 type is changed to Directional. Directional light sources model real world light |
|
74 sources like the sun. Such light sources are not located at any particular point |
|
75 and affect the scene evenly. |
|
76 |
|
77 In the OpenGL model-view, a directional light is represented by a 4d vector, with |
|
78 the 4th value, \c w, set to 0.0f. This means that the spatial data of the light |
|
79 is not changed during certain transformations, for example translation. |
|
80 See \l {3D Math Basis} for a fuller explanation of this. |
|
81 |
|
82 Calling the setPosition() function defines the light source to be located at a given |
|
83 \bold point, a finite distance from the lit object. If the light source is not |
|
84 positional when this function is called its type is changed to Positional. |
|
85 |
|
86 A positional light source models a real world lamp, such as a light-bulb or |
|
87 car headlight. Since it is a finite distance from the lit object the rays diverge |
|
88 and lighting calculations are thus more complex. |
|
89 |
|
90 In OpenGL the light has its spatial data \c w value set to 1.0f, resulting in the |
|
91 lights position being changed along with other objects in the scene by transformations |
|
92 such as translation. See \l {3D Math Basis} for more. |
|
93 |
|
94 Positional lights are by default point sources, like the light-bulb where light |
|
95 radiates in all directions. Calling the spotAngle() function sets an angle to |
|
96 restrict the light from a positional source to a cone like the car headlight. This |
|
97 makes the light behave like a spotlight, where objects outside of the cone of the |
|
98 light do not receive any light from that source. |
|
99 |
|
100 The spotlight may be further specified by its spotExponent() which dictates the |
|
101 behaviour of the cutoff at the lighting cone; and by the attenuation functions |
|
102 which are the terms in the following equation: |
|
103 \image attenuation.png |
|
104 here \c d is the distance of the light from the lit object, and A is the attenuation |
|
105 factor which is a multiplier of the light source, determining its intensity at the |
|
106 lit object. The terms in the denominator are: |
|
107 \list |
|
108 \o constantAttenuation() - default 1.0 |
|
109 \raw HTML |
|
110 k<sub>c</sub> |
|
111 \endraw |
|
112 \o linearAttenuation() - default 0.0 |
|
113 \raw HTML |
|
114 k<sub>l</sub> |
|
115 \endraw |
|
116 \o quadraticAttenuation() - default 0.0 |
|
117 \raw HTML |
|
118 k<sub>q</sub> |
|
119 \endraw |
|
120 \endlist |
|
121 When these terms are large the light contributed by the source becomes much weaker |
|
122 with distance from the object. |
|
123 */ |
|
124 |
|
125 class LightPrivate |
|
126 { |
|
127 public: |
|
128 LightPrivate() : |
|
129 type(Light::Directional), |
|
130 position(0.0f, 0.0f, 1.0f), |
|
131 ambientColor(0, 0, 0, 255), |
|
132 diffuseColor(255, 255, 255, 255), |
|
133 specularColor(255, 255, 255, 255), |
|
134 spotDirection(0.0f, 0.0f, -1.0f), |
|
135 spotExponent(0.0f), |
|
136 spotAngle(180.0f), |
|
137 spotCosAngle(-1.0f), |
|
138 constantAttenuation(1.0f), |
|
139 linearAttenuation(0.0f), |
|
140 quadraticAttenuation(0.0f) |
|
141 { |
|
142 } |
|
143 |
|
144 Light::LightType type; |
|
145 QVector3D position; |
|
146 QColor ambientColor; |
|
147 QColor diffuseColor; |
|
148 QColor specularColor; |
|
149 QVector3D spotDirection; |
|
150 qreal spotExponent; |
|
151 qreal spotAngle; |
|
152 qreal spotCosAngle; |
|
153 qreal constantAttenuation; |
|
154 qreal linearAttenuation; |
|
155 qreal quadraticAttenuation; |
|
156 }; |
|
157 |
|
158 |
|
159 /*! |
|
160 \enum Light::LightType |
|
161 This enum defines the possible types of light. |
|
162 |
|
163 \value Directional The default. Directional lights are infinitely |
|
164 distant from the lit object, and its rays are parallel to a |
|
165 direction vector. Use setDirection() to make a light Directional. |
|
166 \value Positional Positional lights are a finite distance from the |
|
167 lit object. Complex lighting with spot-lights, and attenuation |
|
168 are enabled with Positional lighting. Use setPosition() to |
|
169 make a light Positional. |
|
170 */ |
|
171 |
|
172 /*! |
|
173 Constructs a new light parameters object with default values |
|
174 and attaches it to \a parent. |
|
175 */ |
|
176 Light::Light(QObject *parent) |
|
177 : QObject(parent), d_ptr(new LightPrivate) |
|
178 { |
|
179 } |
|
180 |
|
181 /*! |
|
182 Destroys this light parameters object. |
|
183 */ |
|
184 Light::~Light() |
|
185 { |
|
186 } |
|
187 |
|
188 /*! |
|
189 \property Light::type |
|
190 \brief the type of this light; Positional or Directional. |
|
191 |
|
192 \sa position(), direction() |
|
193 */ |
|
194 Light::LightType Light::type() const |
|
195 { |
|
196 Q_D(const Light); |
|
197 return d->type; |
|
198 } |
|
199 |
|
200 /*! |
|
201 \property Light::position |
|
202 \brief the position of this light if it is Positional; or a null |
|
203 QVector3D if it is Directional. By default, the light is Directional. |
|
204 |
|
205 For a Positional light, the return value is the location in model space |
|
206 of the light, at some finite distance from the origin. The value is |
|
207 transformed by the model-view transformation when the light |
|
208 parameters are applied. |
|
209 |
|
210 When the light is Positional OpenGL will obey other parameters relating |
|
211 to the light's position, such as attenuation and spot angle. |
|
212 |
|
213 Setting the position converts the light from Directional to Positional. |
|
214 |
|
215 \sa direction(), type(), positionChanged() |
|
216 */ |
|
217 QVector3D Light::position() const |
|
218 { |
|
219 Q_D(const Light); |
|
220 if (d->type == Positional) |
|
221 return d->position; |
|
222 else |
|
223 return QVector3D(); |
|
224 } |
|
225 |
|
226 void Light::setPosition(const QVector3D& point) |
|
227 { |
|
228 Q_D(Light); |
|
229 if (d->type == Positional) { |
|
230 if (d->position != point) { |
|
231 // Only the position() has changed. |
|
232 d->position = point; |
|
233 emit positionChanged(); |
|
234 emit lightChanged(); |
|
235 } |
|
236 } else { |
|
237 // Both the position() and direction() are changed. |
|
238 d->type = Positional; |
|
239 d->position = point; |
|
240 emit positionChanged(); |
|
241 emit directionChanged(); |
|
242 emit lightChanged(); |
|
243 } |
|
244 } |
|
245 |
|
246 /*! |
|
247 \property Light::direction |
|
248 \brief the direction of this light if it is Directional; or a null |
|
249 QVector3D if it is Positional. By default, the light is Directional. |
|
250 |
|
251 For a Directional light, the return value is the direction vector of |
|
252 an infinitely distant directional light, like the sun. |
|
253 |
|
254 The default value is (0, 0, 1), for a directional light pointing along |
|
255 the positive z-axis. |
|
256 |
|
257 OpenGL will ignore other parameters such as attenuation and spot angle |
|
258 when this value is set, since a directional light has no location. |
|
259 In order to set the direction of a spot light, use the setSpotDirection() |
|
260 function. |
|
261 |
|
262 Setting the direction converts the light from Positional to Directional. |
|
263 |
|
264 \sa position(), type(), directionChanged() |
|
265 */ |
|
266 QVector3D Light::direction() const |
|
267 { |
|
268 Q_D(const Light); |
|
269 if (d->type == Directional) |
|
270 return d->position; |
|
271 else |
|
272 return QVector3D(); |
|
273 } |
|
274 |
|
275 void Light::setDirection(const QVector3D& value) |
|
276 { |
|
277 Q_D(Light); |
|
278 if (d->type == Directional) { |
|
279 if (d->position != value) { |
|
280 // Only the direction() has changed. |
|
281 d->position = value; |
|
282 emit directionChanged(); |
|
283 emit lightChanged(); |
|
284 } |
|
285 } else { |
|
286 // Both the position() and direction() are changed. |
|
287 d->type = Directional; |
|
288 d->position = value; |
|
289 emit positionChanged(); |
|
290 emit directionChanged(); |
|
291 emit lightChanged(); |
|
292 } |
|
293 } |
|
294 |
|
295 /*! |
|
296 \property Light::ambientColor |
|
297 \brief the ambient color of this light. The default value is black. |
|
298 |
|
299 \sa diffuseColor(), specularColor(), ambientColorChanged() |
|
300 */ |
|
301 QColor Light::ambientColor() const |
|
302 { |
|
303 Q_D(const Light); |
|
304 return d->ambientColor; |
|
305 } |
|
306 |
|
307 void Light::setAmbientColor(const QColor& value) |
|
308 { |
|
309 Q_D(Light); |
|
310 if (d->ambientColor != value) { |
|
311 d->ambientColor = value; |
|
312 emit ambientColorChanged(); |
|
313 emit lightChanged(); |
|
314 } |
|
315 } |
|
316 |
|
317 /*! |
|
318 \property Light::diffuseColor |
|
319 \brief the diffuse color of this light. The default value is white. |
|
320 |
|
321 \sa ambientColor(), specularColor(), diffuseColorChanged() |
|
322 */ |
|
323 QColor Light::diffuseColor() const |
|
324 { |
|
325 Q_D(const Light); |
|
326 return d->diffuseColor; |
|
327 } |
|
328 |
|
329 void Light::setDiffuseColor(const QColor& value) |
|
330 { |
|
331 Q_D(Light); |
|
332 if (d->diffuseColor != value) { |
|
333 d->diffuseColor = value; |
|
334 emit diffuseColorChanged(); |
|
335 emit lightChanged(); |
|
336 } |
|
337 } |
|
338 |
|
339 /*! |
|
340 \property Light::specularColor |
|
341 \brief the specular color of this light. The default value is white. |
|
342 |
|
343 \sa ambientColor(), diffuseColor(), specularColorChanged() |
|
344 */ |
|
345 QColor Light::specularColor() const |
|
346 { |
|
347 Q_D(const Light); |
|
348 return d->specularColor; |
|
349 } |
|
350 |
|
351 void Light::setSpecularColor(const QColor& value) |
|
352 { |
|
353 Q_D(Light); |
|
354 if (d->specularColor != value) { |
|
355 d->specularColor = value; |
|
356 emit specularColorChanged(); |
|
357 emit lightChanged(); |
|
358 } |
|
359 } |
|
360 |
|
361 /*! |
|
362 \property Light::spotDirection |
|
363 \brief the direction that a spot-light is shining in. |
|
364 |
|
365 A spotlight is a positional light that has a value other than the default |
|
366 for spot angle. The default value is (0, 0, -1). |
|
367 |
|
368 This parameter has no effect on a Directional light. |
|
369 |
|
370 \sa spotExponent(), spotDirectionChanged() |
|
371 */ |
|
372 QVector3D Light::spotDirection() const |
|
373 { |
|
374 Q_D(const Light); |
|
375 return d->spotDirection; |
|
376 } |
|
377 |
|
378 void Light::setSpotDirection(const QVector3D& vector) |
|
379 { |
|
380 Q_D(Light); |
|
381 if (d->spotDirection != vector) { |
|
382 d->spotDirection = vector; |
|
383 emit spotDirectionChanged(); |
|
384 emit lightChanged(); |
|
385 } |
|
386 } |
|
387 |
|
388 /*! |
|
389 \property Light::spotExponent |
|
390 \brief the exponent value between 0 and 128 that indicates the |
|
391 intensity distribution of a spot-light. The default value is 0, |
|
392 indicating uniform light distribution. |
|
393 |
|
394 This parameter has no effect on a Directional light. |
|
395 |
|
396 \sa spotAngle(), setPosition(), spotExponentChanged() |
|
397 */ |
|
398 qreal Light::spotExponent() const |
|
399 { |
|
400 Q_D(const Light); |
|
401 return d->spotExponent; |
|
402 } |
|
403 |
|
404 void Light::setSpotExponent(qreal value) |
|
405 { |
|
406 Q_D(Light); |
|
407 if (d->spotExponent != value) { |
|
408 d->spotExponent = value; |
|
409 emit spotExponentChanged(); |
|
410 emit lightChanged(); |
|
411 } |
|
412 } |
|
413 |
|
414 /*! |
|
415 \property Light::spotAngle |
|
416 \brief the angle over which light is spread from this light. |
|
417 It should be between 0 and 90 for spot lights, and 180 for |
|
418 uniform light distribution from a remote light source. |
|
419 The default value is 180. |
|
420 |
|
421 This parameter has no effect on a Directional light. |
|
422 |
|
423 \sa spotCosAngle(), spotAngleChanged() |
|
424 */ |
|
425 qreal Light::spotAngle() const |
|
426 { |
|
427 Q_D(const Light); |
|
428 return d->spotAngle; |
|
429 } |
|
430 |
|
431 #ifndef M_PI |
|
432 #define M_PI 3.14159265358979323846 |
|
433 #endif |
|
434 |
|
435 void Light::setSpotAngle(qreal value) |
|
436 { |
|
437 Q_D(Light); |
|
438 if (d->spotAngle != value) { |
|
439 d->spotAngle = value; |
|
440 if (value != 180.0f) |
|
441 d->spotCosAngle = qCos(value * M_PI / 180.0f); |
|
442 else |
|
443 d->spotCosAngle = -1.0f; |
|
444 emit spotAngleChanged(); |
|
445 emit lightChanged(); |
|
446 } |
|
447 } |
|
448 |
|
449 /*! |
|
450 Returns the cosine of spotAngle(), or -1 if spotAngle() is 180. |
|
451 |
|
452 The cosine of spotAngle() is useful in lighting algorithms. |
|
453 This function returns a cached copy of the cosine so that it |
|
454 does not need to be computed every time the lighting parameters |
|
455 are read. |
|
456 |
|
457 \sa spotAngle() |
|
458 */ |
|
459 qreal Light::spotCosAngle() const |
|
460 { |
|
461 Q_D(const Light); |
|
462 return d->spotCosAngle; |
|
463 } |
|
464 |
|
465 /*! |
|
466 \property Light::constantAttenuation |
|
467 \brief the constant light attenuation factor. The default value is 1. |
|
468 |
|
469 This parameter has no effect on a Directional light. |
|
470 |
|
471 \sa linearAttenuation(), quadraticAttenuation() |
|
472 \sa constantAttenuationChanged() |
|
473 */ |
|
474 qreal Light::constantAttenuation() const |
|
475 { |
|
476 Q_D(const Light); |
|
477 return d->constantAttenuation; |
|
478 } |
|
479 |
|
480 void Light::setConstantAttenuation(qreal value) |
|
481 { |
|
482 Q_D(Light); |
|
483 if (d->constantAttenuation != value) { |
|
484 d->constantAttenuation = value; |
|
485 emit constantAttenuationChanged(); |
|
486 emit lightChanged(); |
|
487 } |
|
488 } |
|
489 |
|
490 /*! |
|
491 \property Light::linearAttenuation |
|
492 \brief the linear light attenuation factor. The default value is 0. |
|
493 |
|
494 This parameter has no effect on a Directional light. |
|
495 |
|
496 \sa constantAttenuation(), quadraticAttenuation() |
|
497 \sa linearAttenuationChanged() |
|
498 */ |
|
499 qreal Light::linearAttenuation() const |
|
500 { |
|
501 Q_D(const Light); |
|
502 return d->linearAttenuation; |
|
503 } |
|
504 |
|
505 void Light::setLinearAttenuation(qreal value) |
|
506 { |
|
507 Q_D(Light); |
|
508 if (d->linearAttenuation != value) { |
|
509 d->linearAttenuation = value; |
|
510 emit linearAttenuationChanged(); |
|
511 emit lightChanged(); |
|
512 } |
|
513 } |
|
514 |
|
515 /*! |
|
516 \property Light::quadraticAttenuation |
|
517 \brief the quadratic light attenuation factor. The default value is 0. |
|
518 |
|
519 This parameter has no effect on a Directional light. |
|
520 |
|
521 \sa constantAttenuation(), linearAttenuation() |
|
522 \sa quadraticAttenuationChanged() |
|
523 */ |
|
524 qreal Light::quadraticAttenuation() const |
|
525 { |
|
526 Q_D(const Light); |
|
527 return d->quadraticAttenuation; |
|
528 } |
|
529 |
|
530 void Light::setQuadraticAttenuation(qreal value) |
|
531 { |
|
532 Q_D(Light); |
|
533 if (d->quadraticAttenuation != value) { |
|
534 d->quadraticAttenuation = value; |
|
535 emit quadraticAttenuationChanged(); |
|
536 emit lightChanged(); |
|
537 } |
|
538 } |
|
539 |
|
540 /*! |
|
541 Returns the position of this light after transforming it from |
|
542 world co-ordinates to eye co-ordinates using \a transform. |
|
543 |
|
544 If the light is Directional, then direction() will be transformed, |
|
545 after setting the w co-ordinate to 0. If the light is Positional, |
|
546 then position() will be transformed after setting the w co-ordinate |
|
547 to 1. |
|
548 |
|
549 The returned result is suitable to be applied to the GL_POSITION |
|
550 property of \c{glLight()}, assuming that the modelview transformation |
|
551 in the GL context is set to the identity. |
|
552 |
|
553 \sa eyeSpotDirection() |
|
554 */ |
|
555 QVector4D Light::eyePosition(const QMatrix4x4& transform) const |
|
556 { |
|
557 Q_D(const Light); |
|
558 if (d->type == Directional) |
|
559 return transform * QVector4D(d->position, 0.0f); |
|
560 else |
|
561 return transform * QVector4D(d->position, 1.0f); |
|
562 } |
|
563 |
|
564 /*! |
|
565 Returns the spotDirection() for this light after transforming it |
|
566 from world co-ordinates to eye co-ordinates using the top-left |
|
567 3x3 submatrix within \a transform. |
|
568 |
|
569 The returned result is suitable to be applied to the GL_SPOT_DIRECTION |
|
570 property of \c{glLight()}, assuming that the modelview transformation |
|
571 in the GL context is set to the identity. |
|
572 |
|
573 \sa eyePosition() |
|
574 */ |
|
575 QVector3D Light::eyeSpotDirection |
|
576 (const QMatrix4x4& transform) const |
|
577 { |
|
578 Q_D(const Light); |
|
579 return transform.mapVector(d->spotDirection); |
|
580 } |
|
581 |
|
582 /*! |
|
583 \fn void Light::positionChanged() |
|
584 |
|
585 This signal is emitted when position() changes, or when the type() |
|
586 changes between Directional and Positional. |
|
587 |
|
588 \sa position(), lightChanged() |
|
589 */ |
|
590 |
|
591 /*! |
|
592 \fn void Light::directionChanged() |
|
593 |
|
594 This signal is emitted when direction() changes, or when the type() |
|
595 changes between Directional and Positional. |
|
596 |
|
597 \sa direction(), lightChanged() |
|
598 */ |
|
599 |
|
600 /*! |
|
601 \fn void Light::ambientColorChanged() |
|
602 |
|
603 This signal is emitted when ambientColor() changes. |
|
604 |
|
605 \sa ambientColor(), lightChanged() |
|
606 */ |
|
607 |
|
608 /*! |
|
609 \fn void Light::diffuseColorChanged() |
|
610 |
|
611 This signal is emitted when diffuseColor() changes. |
|
612 |
|
613 \sa diffuseColor(), lightChanged() |
|
614 */ |
|
615 |
|
616 /*! |
|
617 \fn void Light::specularColorChanged() |
|
618 |
|
619 This signal is emitted when specularColor() changes. |
|
620 |
|
621 \sa specularColor(), lightChanged() |
|
622 */ |
|
623 |
|
624 /*! |
|
625 \fn void Light::spotDirectionChanged() |
|
626 |
|
627 This signal is emitted when spotDirection() changes. |
|
628 |
|
629 \sa spotDirection(), lightChanged() |
|
630 */ |
|
631 |
|
632 /*! |
|
633 \fn void Light::spotExponentChanged() |
|
634 |
|
635 This signal is emitted when spotExponent() changes. |
|
636 |
|
637 \sa spotExponent(), lightChanged() |
|
638 */ |
|
639 |
|
640 /*! |
|
641 \fn void Light::spotAngleChanged() |
|
642 |
|
643 This signal is emitted when spotAngle() changes. |
|
644 |
|
645 \sa spotAngle(), lightChanged() |
|
646 */ |
|
647 |
|
648 /*! |
|
649 \fn void Light::constantAttenuationChanged() |
|
650 |
|
651 This signal is emitted when constantAttenuation() changes. |
|
652 |
|
653 \sa constantAttenuation(), lightChanged() |
|
654 */ |
|
655 |
|
656 /*! |
|
657 \fn void Light::linearAttenuationChanged() |
|
658 |
|
659 This signal is emitted when linearAttenuation() changes. |
|
660 |
|
661 \sa linearAttenuation(), lightChanged() |
|
662 */ |
|
663 |
|
664 /*! |
|
665 \fn void Light::quadraticAttenuationChanged() |
|
666 |
|
667 This signal is emitted when quadraticAttenuation() changes. |
|
668 |
|
669 \sa quadraticAttenuation(), lightChanged() |
|
670 */ |
|
671 |
|
672 /*! |
|
673 \fn void Light::lightChanged() |
|
674 |
|
675 This signal is emitted when any of the properties on the light changes. |
|
676 */ |