1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif // HAVE_CONFIG_H
36
37 #ifdef HAVE_DRAGGERS
38
39 /*!
40 \class SoSpotLightDragger SoSpotLightDragger.h Inventor/draggers/SoSpotLightDragger.h
41 \brief The SoSpotLightDragger class provides interactive geometry for manipulating a spotlight.
42
43 \ingroup draggers
44
45 \DRAGGER_DEFAULT_SCREENSHOT
46
47 <center>
48 \image html spotlight.png "Screen Shot of Default Dragger"
49 </center>
50
51 This dragger is well suited for setting up the fields of a
52 SoSpotLight node, as it provides geometry for the end-user to
53 interact with a directional vector for the spotlight, to set up it's
54 position and to control the cut-off angle for the "lampshade" around
55 the lightsource.
56
57 Note that there is one aspect of SoSpotLight nodes that can not be
58 controlled with this dragger: the SoSpotLight::dropOffRate field.
59
60 The Coin library includes a manipulator class, SoSpotLightManip,
61 which wraps the functionality provided by this class inside the
62 necessary mechanisms for connecting it to SoSpotLight node instances
63 in a scenegraph.
64
65 \sa SoSpotLightManip, SoSpotLight
66 \sa SoDirectionalLightDragger, SoPointLightDragger
67 */
68
69 #include <Inventor/draggers/SoSpotLightDragger.h>
70
71 #include <cmath>
72 #include <cstring>
73
74 #include <Inventor/draggers/SoDragPointDragger.h>
75 #include <Inventor/draggers/SoRotateSphericalDragger.h>
76 #include <Inventor/nodes/SoMaterial.h>
77 #include <Inventor/nodes/SoRotation.h>
78 #include <Inventor/nodes/SoScale.h>
79 #include <Inventor/nodes/SoSeparator.h>
80 #include <Inventor/nodes/SoSwitch.h>
81 #include <Inventor/nodes/SoTranslation.h>
82 #include <Inventor/sensors/SoFieldSensor.h>
83 #include <Inventor/projectors/SbPlaneProjector.h>
84
85 #include <data/draggerDefaults/spotLightDragger.h>
86
87 #include "nodekits/SoSubKitP.h"
88 #include "SbBasicP.h"
89
90 /*!
91 \var SoSFRotation SoSpotLightDragger::rotation
92
93 This field is continuously updated to contain the rotation of the
94 current direction vector. The application programmer will typically
95 connect this to the rotation field of a SoSpotLight node (unless
96 using the SoSpotLightManip class, where this is taken care of
97 automatically).
98
99 It may also of course be connected to any other rotation field
100 controlling the direction of scenegraph geometry, it does not have
101 to part of a SoSpotLight node specifically.
102 */
103
104 /*!
105 \var SoSFVec3f SoSpotLightDragger::translation
106
107 Continuously updated to contain the current translation from the
108 dragger's local origo position.
109 */
110
111 /*!
112 \var SoSFFloat SoSpotLightDragger::angle
113
114 The cut-off angle for the "lampshade" around the lightsource.
115
116 Typically connected to a SoSpotLight::cutOffAngle field.
117 */
118
119 /*!
120 \var SoFieldSensor * SoSpotLightDragger::rotFieldSensor
121 \COININTERNAL
122 */
123 /*!
124 \var SoFieldSensor * SoSpotLightDragger::translFieldSensor
125 \COININTERNAL
126 */
127 /*!
128 \var SoFieldSensor * SoSpotLightDragger::angleFieldSensor
129 \COININTERNAL
130 */
131 /*!
132 \var SbPlaneProjector * SoSpotLightDragger::planeProj
133 \COININTERNAL
134 */
135
136 #define THISP(d) static_cast<SoSpotLightDragger *>(d)
137
138 class SoSpotLightDraggerP {
139 public:
140 };
141
142 SO_KIT_SOURCE(SoSpotLightDragger);
143
144 // doc in superclass
145 void
initClass(void)146 SoSpotLightDragger::initClass(void)
147 {
148 SO_KIT_INTERNAL_INIT_CLASS(SoSpotLightDragger, SO_FROM_INVENTOR_1);
149 }
150
151 // FIXME: document which parts need to be present in the geometry
152 // scenegraph, and what role they play in the dragger. 20010913 mortene.
153 /*!
154 \DRAGGER_CONSTRUCTOR
155
156 \NODEKIT_PRE_DIAGRAM
157
158 \verbatim
159 CLASS SoSpotLightDragger
160 -->"this"
161 "callbackList"
162 "topSeparator"
163 "motionMatrix"
164 --> "material"
165 --> "translatorSep"
166 --> "translatorRotInv"
167 --> "translator"
168 --> "rotator"
169 --> "beamSep"
170 --> "beamPlacement"
171 --> "beamScale"
172 --> "beamSwitch"
173 --> "beam"
174 --> "beamActive"
175 "geomSeparator"
176 \endverbatim
177
178 \NODEKIT_POST_DIAGRAM
179
180
181 \NODEKIT_PRE_TABLE
182
183 \verbatim
184 CLASS SoSpotLightDragger
185 PVT "this", SoSpotLightDragger ---
186 "callbackList", SoNodeKitListPart [ SoCallback, SoEventCallback ]
187 PVT "topSeparator", SoSeparator ---
188 PVT "motionMatrix", SoMatrixTransform ---
189 "material", SoMaterial ---
190 PVT "translatorSep", SoSeparator ---
191 PVT "translatorRotInv", SoRotation ---
192 "translator", SoDragPointDragger ---
193 "rotator", SoRotateSphericalDragger ---
194 PVT "beamSep", SoSeparator ---
195 "beamPlacement", SoTranslation ---
196 "beamScale", SoScale ---
197 PVT "beamSwitch", SoSwitch ---
198 "beam", SoSeparator ---
199 "beamActive", SoSeparator ---
200 PVT "geomSeparator", SoSeparator ---
201 \endverbatim
202
203 \NODEKIT_POST_TABLE
204 */
SoSpotLightDragger(void)205 SoSpotLightDragger::SoSpotLightDragger(void)
206 {
207 SO_KIT_INTERNAL_CONSTRUCTOR(SoSpotLightDragger);
208
209 SO_KIT_ADD_CATALOG_ENTRY(material, SoMaterial, TRUE, topSeparator, translatorSep, TRUE);
210 SO_KIT_ADD_CATALOG_ENTRY(translatorSep, SoSeparator, TRUE, topSeparator, rotator, FALSE);
211 SO_KIT_ADD_CATALOG_ENTRY(translatorRotInv, SoRotation, TRUE, translatorSep, translator, FALSE);
212 SO_KIT_ADD_CATALOG_ENTRY(translator, SoDragPointDragger, TRUE, translatorSep, "", TRUE);
213 SO_KIT_ADD_CATALOG_ENTRY(rotator, SoRotateSphericalDragger, TRUE, topSeparator, beamSep, TRUE);
214 SO_KIT_ADD_CATALOG_ENTRY(beamSep, SoSeparator, TRUE, topSeparator, geomSeparator, FALSE);
215 SO_KIT_ADD_CATALOG_ENTRY(beamPlacement, SoTranslation, TRUE, beamSep, beamScale, TRUE);
216 SO_KIT_ADD_CATALOG_ENTRY(beamScale, SoScale, TRUE, beamSep, beamSwitch, TRUE);
217 SO_KIT_ADD_CATALOG_ENTRY(beamSwitch, SoSwitch, TRUE, beamSep, "", FALSE);
218 SO_KIT_ADD_CATALOG_ENTRY(beam, SoSeparator, TRUE, beamSwitch, beamActive, TRUE);
219 SO_KIT_ADD_CATALOG_ENTRY(beamActive, SoSeparator, TRUE, beamSwitch, "", TRUE);
220
221 if (SO_KIT_IS_FIRST_INSTANCE()) {
222 SoInteractionKit::readDefaultParts("spotLightDragger.iv",
223 SPOTLIGHTDRAGGER_draggergeometry,
224 static_cast<int>(strlen(SPOTLIGHTDRAGGER_draggergeometry)));
225 }
226
227 SO_KIT_ADD_FIELD(rotation, (SbRotation(SbVec3f(0.0f, 0.0f, 1.0f), 0.0f)));
228 SO_KIT_ADD_FIELD(translation, (0.0f, 0.0f, 0.0f));
229 SO_KIT_ADD_FIELD(angle, (1.0f));
230 SO_KIT_INIT_INSTANCE();
231
232 SoDragger *pdragger = SO_GET_ANY_PART(this, "translator", SoDragPointDragger);
233 assert(pdragger);
234 SoDragger *sdragger = SO_GET_ANY_PART(this, "rotator", SoDragPointDragger);
235 assert(sdragger);
236
237 this->setPartAsDefault("beam", "spotLightBeam");
238 this->setPartAsDefault("beamActive", "spotLightBeamActive");
239 this->setPartAsDefault("beamPlacement", "spotLightBeamPlacement");
240 this->setPartAsDefault("material", "spotLightOverallMaterial");
241
242 // create this part here so that we don't add this node to the scene
243 // graph during handleEvent() (causes changes to the scene graph
244 // during traversal)
245 (void)SO_GET_ANY_PART(this, "translatorRotInv", SoRotation);
246
247 SoSwitch * sw = SO_GET_ANY_PART(this, "beamSwitch", SoSwitch);
248 SoInteractionKit::setSwitchValue(sw, 0);
249
250 this->setBeamScaleFromAngle(1.0f);
251
252 this->planeProj = new SbPlaneProjector();
253 this->addStartCallback(SoSpotLightDragger::startCB);
254 this->addMotionCallback(SoSpotLightDragger::motionCB);
255 this->addFinishCallback(SoSpotLightDragger::doneCB);
256 this->addValueChangedCallback(SoSpotLightDragger::valueChangedCB);
257
258 this->rotFieldSensor = new SoFieldSensor(SoSpotLightDragger::fieldSensorCB, this);
259 this->rotFieldSensor->setPriority(0);
260 this->translFieldSensor = new SoFieldSensor(SoSpotLightDragger::fieldSensorCB, this);
261 this->translFieldSensor->setPriority(0);
262 this->angleFieldSensor = new SoFieldSensor(SoSpotLightDragger::fieldSensorCB, this);
263 this->angleFieldSensor->setPriority(0);
264
265 this->translatorSep.setDefault(TRUE);
266 this->beamSep.setDefault(TRUE);
267
268 this->setUpConnections(TRUE, TRUE);
269 }
270
271 /*!
272 Protected destructor.
273
274 (Dragger classes are derived from SoBase, so they are reference
275 counted and automatically destroyed when their reference count goes
276 to 0.)
277 */
~SoSpotLightDragger()278 SoSpotLightDragger::~SoSpotLightDragger()
279 {
280 delete this->angleFieldSensor;
281 delete this->translFieldSensor;
282 delete this->rotFieldSensor;
283 delete this->planeProj;
284 }
285
286 // Doc in superclass.
287 SbBool
setUpConnections(SbBool onoff,SbBool doitalways)288 SoSpotLightDragger::setUpConnections(SbBool onoff, SbBool doitalways)
289 {
290 if (!doitalways && this->connectionsSetUp == onoff) return onoff;
291
292 if (onoff) {
293 inherited::setUpConnections(onoff, doitalways);
294 SoDragger * therotator = coin_assert_cast<SoDragger *>(this->getAnyPart("rotator", FALSE));
295 therotator->setPartAsDefault("rotator", "spotLightRotatorRotator");
296 therotator->setPartAsDefault("rotatorActive",
297 "spotLightRotatorRotatorActive");
298 therotator->setPartAsDefault("feedback",
299 "spotLightRotatorFeedback");
300 therotator->setPartAsDefault("feedbackActive",
301 "spotLightRotatorFeedbackActive");
302
303 SoDragger *thetranslator = coin_assert_cast<SoDragger *>(this->getAnyPart("translator", FALSE));
304 thetranslator->setPartAsDefault("yzTranslator.translator",
305 "spotLightTranslatorPlaneTranslator");
306 thetranslator->setPartAsDefault("xzTranslator.translator",
307 "spotLightTranslatorPlaneTranslator");
308 thetranslator->setPartAsDefault("xyTranslator.translator",
309 "spotLightTranslatorPlaneTranslator");
310 thetranslator->setPartAsDefault("yzTranslator.translatorActive",
311 "spotLightTranslatorPlaneTranslatorActive");
312 thetranslator->setPartAsDefault("xzTranslator.translatorActive",
313 "spotLightTranslatorPlaneTranslatorActive");
314 thetranslator->setPartAsDefault("xyTranslator.translatorActive",
315 "spotLightTranslatorPlaneTranslatorActive");
316 thetranslator->setPartAsDefault("xTranslator.translator",
317 "spotLightTranslatorLineTranslator");
318 thetranslator->setPartAsDefault("yTranslator.translator",
319 "spotLightTranslatorLineTranslator");
320 thetranslator->setPartAsDefault("zTranslator.translator",
321 "spotLightTranslatorLineTranslator");
322 thetranslator->setPartAsDefault("xTranslator.translatorActive",
323 "spotLightTranslatorLineTranslatorActive");
324 thetranslator->setPartAsDefault("yTranslator.translatorActive",
325 "spotLightTranslatorLineTranslatorActive");
326 thetranslator->setPartAsDefault("zTranslator.translatorActive",
327 "spotLightTranslatorLineTranslatorActive");
328
329 this->registerChildDragger(therotator);
330 this->registerChildDragger(thetranslator);
331
332 if (this->angleFieldSensor->getAttachedField() != &this->angle)
333 this->angleFieldSensor->attach(&this->angle);
334 if (this->translFieldSensor->getAttachedField() != &this->translation)
335 this->translFieldSensor->attach(&this->translation);
336 if (this->rotFieldSensor->getAttachedField() != &this->rotation)
337 this->rotFieldSensor->attach(&this->rotation);
338 }
339 else {
340 SoDragger *thetranslator = coin_assert_cast<SoDragger *>(this->getAnyPart("translator", FALSE));
341 this->unregisterChildDragger(thetranslator);
342 SoDragger * therotator = coin_assert_cast<SoDragger *>(this->getAnyPart("rotator", FALSE));
343 this->unregisterChildDragger(therotator);
344
345 if (this->angleFieldSensor->getAttachedField() != NULL)
346 this->angleFieldSensor->detach();
347 if (this->rotFieldSensor->getAttachedField() != NULL)
348 this->rotFieldSensor->detach();
349 if (this->translFieldSensor->getAttachedField() != NULL)
350 this->translFieldSensor->detach();
351
352 inherited::setUpConnections(onoff, doitalways);
353 }
354 return !(this->connectionsSetUp = onoff);
355 }
356
357 // Doc in superclass.
358 void
setDefaultOnNonWritingFields(void)359 SoSpotLightDragger::setDefaultOnNonWritingFields(void)
360 {
361 if (!(this->angle.isConnectionEnabled() && this->angle.isConnected()) &&
362 this->angle.getValue() == 1.0f) this->angle.setDefault(TRUE);
363
364 this->translator.setDefault(TRUE);
365 this->rotator.setDefault(TRUE);
366
367 this->translatorRotInv.setDefault(TRUE);
368 this->beamScale.setDefault(TRUE);
369
370 inherited::setDefaultOnNonWritingFields();
371 }
372
373 /*! \COININTERNAL */
374 void
fieldSensorCB(void * d,SoSensor * s)375 SoSpotLightDragger::fieldSensorCB(void * d, SoSensor * s)
376 {
377 SoSpotLightDragger * thisp = THISP(d);
378
379 if (s == static_cast<SoSensor *>(thisp->angleFieldSensor)) {
380 // need to update the beamScale geometry
381 SoScale * scale = SO_GET_ANY_PART(thisp, "beamScale", SoScale);
382 SbVec3f scaleFactor;
383 scaleFactor[0] = scaleFactor[1] = static_cast<float>(tan(thisp->angle.getValue()));
384 scaleFactor[2] = 1.0f;
385 scale->scaleFactor = scaleFactor;
386 thisp->valueChanged();
387 }
388 else {
389 SbMatrix matrix = thisp->getMotionMatrix();
390 thisp->workFieldsIntoTransform(matrix);
391 thisp->setMotionMatrix(matrix);
392 }
393 }
394
395 /*! \COININTERNAL */
396 void
valueChangedCB(void *,SoDragger * d)397 SoSpotLightDragger::valueChangedCB(void *, SoDragger * d)
398 {
399 SoSpotLightDragger * thisp = THISP(d);
400 SbMatrix matrix = thisp->getMotionMatrix();
401 SbVec3f trans, scale;
402 SbRotation rot, scaleOrient;
403 matrix.getTransform(trans, rot, scale, scaleOrient);
404
405 thisp->translFieldSensor->detach();
406 if (thisp->translation.getValue() != trans)
407 thisp->translation = trans;
408 thisp->translFieldSensor->attach(&thisp->translation);
409
410 thisp->rotFieldSensor->detach();
411 if (thisp->rotation.getValue() != rot)
412 thisp->rotation = rot;
413 thisp->rotFieldSensor->attach(&thisp->rotation);
414
415 SoRotation *invRot = SO_GET_ANY_PART(thisp, "translatorRotInv", SoRotation);
416 invRot->rotation = rot.inverse();
417 }
418
419 /*! \COININTERNAL */
420 void
startCB(void *,SoDragger * d)421 SoSpotLightDragger::startCB(void *, SoDragger * d)
422 {
423 SoSpotLightDragger * thisp = THISP(d);
424 thisp->dragStart();
425 }
426
427 /*! \COININTERNAL */
428 void
motionCB(void *,SoDragger * d)429 SoSpotLightDragger::motionCB(void *, SoDragger * d)
430 {
431 SoSpotLightDragger * thisp = THISP(d);
432 thisp->drag();
433 }
434
435 /*! \COININTERNAL */
436 void
doneCB(void *,SoDragger * d)437 SoSpotLightDragger::doneCB(void *, SoDragger * d)
438 {
439 SoSpotLightDragger * thisp = THISP(d);
440 thisp->dragFinish();
441 }
442
443 /*! \COININTERNAL
444 Called when dragger is selected (picked) by the user.
445 */
446 void
dragStart(void)447 SoSpotLightDragger::dragStart(void)
448 {
449 if (this->getActiveChildDragger()) return;
450 SoSwitch *sw;
451 sw = SO_GET_ANY_PART(this, "beamSwitch", SoSwitch);
452 SoInteractionKit::setSwitchValue(sw, 1);
453
454 SbVec3f hitPt = this->getLocalStartingPoint();
455 SbVec3f apex = SO_GET_ANY_PART(this, "beamPlacement", SoTranslation)->translation.getValue();
456 apex[2] += 1.0f; // FIXME: This should probably be handled another way. 20020814 kristian.
457
458 this->planeProj->setPlane(SbPlane(apex, apex+SbVec3f(0.0f, 0.0f, -1.0f),
459 hitPt));
460 }
461
462 /*! \COININTERNAL
463 Called when user drags the mouse after picking the dragger.
464 */
465 void
drag(void)466 SoSpotLightDragger::drag(void)
467 {
468 if (this->getActiveChildDragger()) return;
469
470 SbVec3f apex = SO_GET_ANY_PART(this, "beamPlacement", SoTranslation)->translation.getValue();
471 apex[2] += 1.0f; // FIXME: This should probably be handled another way. 20020814 kristian.
472 this->planeProj->setViewVolume(this->getViewVolume());
473 this->planeProj->setWorkingSpace(this->getLocalToWorldMatrix());
474 SbVec3f projPt = planeProj->project(this->getNormalizedLocaterPosition());
475
476 SbVec3f vec = projPt - apex;
477 float dot = SbVec3f(0.0f, 0.0f, -1.0f).dot(vec) / vec.length();
478 if (dot < 0.0f) dot = 0.01f; // FIXME: pederb, 20000209
479 this->setBeamScaleFromAngle(static_cast<float>(acos(dot)));
480 }
481
482 /*! \COININTERNAL
483 Called when mouse button is released after picking and interacting
484 with the dragger.
485 */
486 void
dragFinish(void)487 SoSpotLightDragger::dragFinish(void)
488 {
489 if (this->getActiveChildDragger()) return;
490 SoSwitch *sw;
491 sw = SO_GET_ANY_PART(this, "beamSwitch", SoSwitch);
492 SoInteractionKit::setSwitchValue(sw, 0);
493 }
494
495 /*!
496 Scales the geometry representing the "lampshade" around the
497 lightsource to represent the given \a beamangle.
498 */
499 void
setBeamScaleFromAngle(float beamangle)500 SoSpotLightDragger::setBeamScaleFromAngle(float beamangle)
501 {
502 SoScale *scale = SO_GET_ANY_PART(this, "beamScale", SoScale);
503 SbVec3f scaleFactor;
504 scaleFactor[0] = scaleFactor[1] = static_cast<float>(tan(beamangle));
505 scaleFactor[2] = 1.0f;
506 scale->scaleFactor = scaleFactor;
507 this->angle = beamangle;
508 }
509
510 #undef THISP
511 #endif // HAVE_DRAGGERS
512