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