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_VRML97
38 
39 /*!
40   \class SoVRMLProximitySensor SoVRMLProximitySensor.h Inventor/VRMLnodes/SoVRMLProximitySensor.h
41   \brief The SoVRMLProximitySensor class is used to generate events when the viewer enters or exits a region.
42 
43   \ingroup VRMLnodes
44 
45   \WEB3DCOPYRIGHT
46 
47   \verbatim
48   ProximitySensor {
49     exposedField SFVec3f    center      0 0 0    # (-,)
50     exposedField SFVec3f    size        0 0 0    # [0,)
51     exposedField SFBool     enabled     TRUE
52     eventOut     SFBool     isActive
53     eventOut     SFVec3f    position_changed
54     eventOut     SFRotation orientation_changed
55     eventOut     SFTime     enterTime
56     eventOut     SFTime     exitTime
57   }
58   \endverbatim
59 
60   The ProximitySensor node generates events when the viewer enters,
61   exits, and moves within a region in space (defined by a box).
62 
63   A proximity sensor is enabled or disabled by sending it an enabled
64   event with a value of TRUE or FALSE. A disabled sensor does not send
65   events.
66 
67   A ProximitySensor node generates isActive TRUE/FALSE events as the
68   viewer enters and exits the rectangular box defined by its center
69   and size fields. Browsers shall interpolate viewer positions and
70   timestamp the isActive events with the exact time the viewer first
71   intersected the proximity region.
72 
73   The \e center field defines the centre point of the proximity region
74   in object space. The \e size field specifies a vector which defines
75   the width (x), height (y), and depth (z) of the box bounding the
76   region. The components of the size field shall be greater than or
77   equal to zero. ProximitySensor nodes are affected by the
78   hierarchical transformations of their parents.
79 
80   The \e enterTime event is generated whenever the isActive TRUE event
81   is generated (user enters the box), and \e exitTime events are
82   generated whenever an isActive FALSE event is generated (user exits
83   the box).
84 
85   The \e position_changed and \e orientation_changed eventOuts send
86   events whenever the user is contained within the proximity region
87   and the position and orientation of the viewer changes with respect
88   to the ProximitySensor node's coordinate system including enter and
89   exit times.
90 
91   The viewer movement may be a result of a variety of circumstances
92   resulting from browser navigation, ProximitySensor node's coordinate
93   system changes, or bound Viewpoint node's position or orientation
94   changes.
95 
96   Each ProximitySensor node behaves independently of all other
97   ProximitySensor nodes. Every enabled ProximitySensor node that is
98   affected by the viewer's movement receives and sends events,
99   possibly resulting in multiple
100 
101   ProximitySensor nodes receiving and sending events
102   simultaneously. Unlike TouchSensor nodes, there is no notion of a
103   ProximitySensor node lower in the scene graph "grabbing" events.
104 
105   Instanced (DEF/USE) ProximitySensor nodes use the union of all the
106   boxes to check for enter and exit. A multiply instanced
107   ProximitySensor node will detect enter and exit for all instances of
108   the box and send enter/exit events appropriately. However, the
109   results are undefined if the any of the boxes of a multiply
110   instanced ProximitySensor node overlap.
111 
112   A ProximitySensor node that surrounds the entire world has an
113   enterTime equal to the time that the world was entered and can be
114   used to start up animations or behaviours as soon as a world is
115   loaded.
116 
117   A ProximitySensor node with a box containing zero volume (i.e., any
118   size field element of 0.0) cannot generate events. This is
119   equivalent to setting the enabled field to FALSE.
120 
121   A ProximitySensor read from a VRML file shall generate isActive
122   TRUE, position_changed, orientation_changed and enterTime events if
123   the sensor is enabled and the viewer is inside the proximity region.
124 
125   A ProximitySensor inserted into the transformation hierarchy shall
126   generate isActive TRUE, position_changed, orientation_changed and
127   enterTime events if the sensor is enabled and the viewer is inside
128   the proximity region.
129 
130   A ProximitySensor removed from the transformation hierarchy shall
131   generate isActive FALSE, position_changed, orientation_changed and
132   exitTime events if the sensor is enabled and the viewer is inside
133   the proximity region.
134 
135 */
136 
137 /*!
138   \var SoSFVec3f SoVRMLProximitySensor::center
139   The center of the region. Default value is (0,0,0).
140 */
141 
142 /*!
143   \var SoSFVec3f SoVRMLProximitySensor::size
144   The region size. Default value is (0, 0, 0).
145 */
146 
147 /*!
148   \var SoSFBool SoVRMLProximitySensor::enabled
149   Enable/disable sensor. Default value is TRUE.
150 */
151 
152 
153 /*!
154   \var SoSFBool SoVRMLProximitySensor::isActive
155   An eventOut that is sent every time the sensor changes state.
156 */
157 
158 /*!
159   \var SoSFVec3f SoVRMLProximitySensor::position_changed
160 
161   An eventOut that is sent when the viewer is inside the region and
162   the viewer position or orientation is changed.
163 
164 */
165 
166 /*!
167   \var SoSFRotation SoVRMLProximitySensor::orientation_changed
168 
169   An eventOut that is sent when the viewer is inside the region and
170   the viewer position or orientation is changed.
171 
172 */
173 
174 /*!
175   \var SoSFTime SoVRMLProximitySensor::enterTime
176   An eventOut that is sent when the viewer enters the region.
177 */
178 
179 /*!
180   \var SoSFTime SoVRMLProximitySensor::exitTime
181   An eventOut that is sent when the viewer exits the region.
182 */
183 
184 #include <Inventor/VRMLnodes/SoVRMLProximitySensor.h>
185 #include "coindefs.h"
186 
187 #include <Inventor/VRMLnodes/SoVRMLMacros.h>
188 #include <Inventor/actions/SoAction.h>
189 #include <Inventor/elements/SoViewVolumeElement.h>
190 #include <Inventor/elements/SoViewingMatrixElement.h>
191 #include <Inventor/elements/SoModelMatrixElement.h>
192 #include <Inventor/SoDB.h>
193 #include <Inventor/SbBox3f.h>
194 #include <Inventor/misc/SoState.h>
195 
196 #include "nodes/SoSubNodeP.h"
197 
198 SO_NODE_SOURCE(SoVRMLProximitySensor);
199 
200 //
201 // returns the current time. First tries the realTime field, then
202 // SbTime::getTimeOfDay() if field is not found.
203 //
204 static SbTime
prox_get_current_time(void)205 prox_get_current_time(void)
206 {
207   SoField * realtime = SoDB::getGlobalField("realTime");
208   if (realtime && realtime->isOfType(SoSFTime::getClassTypeId())) {
209     return ((SoSFTime*)realtime)->getValue();
210   }
211   return SbTime::getTimeOfDay();
212 }
213 
214 // Doc in parent
215 void
initClass(void)216 SoVRMLProximitySensor::initClass(void)
217 {
218   SO_NODE_INTERNAL_INIT_CLASS(SoVRMLProximitySensor, SO_VRML97_NODE_TYPE);
219 }
220 
221 /*!
222   Constructor.
223 */
SoVRMLProximitySensor(void)224 SoVRMLProximitySensor::SoVRMLProximitySensor(void)
225 {
226   SO_VRMLNODE_INTERNAL_CONSTRUCTOR(SoVRMLProximitySensor);
227 
228   SO_VRMLNODE_ADD_EXPOSED_FIELD(center, (0.0f, 0.0f, 0.0f));
229   SO_VRMLNODE_ADD_EXPOSED_FIELD(size, (0.0f, 0.0f, 0.0f));
230   SO_VRMLNODE_ADD_EXPOSED_FIELD(enabled, (TRUE));
231 
232   SO_VRMLNODE_ADD_EVENT_OUT(isActive);
233   SO_VRMLNODE_ADD_EVENT_OUT(position_changed);
234   SO_VRMLNODE_ADD_EVENT_OUT(orientation_changed);
235   SO_VRMLNODE_ADD_EVENT_OUT(enterTime);
236   SO_VRMLNODE_ADD_EVENT_OUT(exitTime);
237 
238   // initialize eventOut values that we might read
239   this->isActive = FALSE;
240   this->position_changed = SbVec3f(0.0f, 0.0f, 0.0f);
241   this->orientation_changed = SbRotation();
242 }
243 
244 /*!
245   Destructor.
246 */
~SoVRMLProximitySensor()247 SoVRMLProximitySensor::~SoVRMLProximitySensor()
248 {
249 }
250 
251 // Doc in parent
252 SbBool
affectsState(void) const253 SoVRMLProximitySensor::affectsState(void) const
254 {
255   return FALSE;
256 }
257 
258 // Doc in parent
259 void
doAction(SoAction * action)260 SoVRMLProximitySensor::doAction(SoAction * action)
261 {
262   if (!this->enabled.getValue()) return;
263 
264   SbBool wasactive = this->isActive.getValue();
265   SbVec3f s = this->size.getValue() * 0.5f;
266   SbTime currtime = prox_get_current_time();
267 
268   if (s[0] <= 0.0f || s[1] <= 0.0f || s[2] <= 0.0f) {
269     if (wasactive) {
270       this->isActive = FALSE;
271       this->exitTime = currtime;
272     }
273     return;
274   }
275 
276   SoState * state = action->getState();
277   const SbViewVolume & vv = SoViewVolumeElement::get(state);
278   const SbMatrix & mm = SoModelMatrixElement::get(state);
279   const SbMatrix & vm = SoViewingMatrixElement::get(state);
280 
281   // FIXME: if it's not possible to invert the matrix, move proximity
282   // area to world space and do the testing there.
283 
284   SbVec3f viewer = vv.getProjectionPoint(); // world space
285   mm.inverse().multVecMatrix(viewer, viewer); // object space
286 
287   SbVec3f c = this->center.getValue();
288   SbBox3f box(c[0]-s[0], c[1]-s[1], c[2]-s[2],
289               c[0]+s[0], c[1]+s[1], c[2]+s[2]);
290   SbBool inside = box.intersect(viewer);
291 
292   SbRotation oldrot = this->orientation_changed.getValue();
293   SbVec3f oldpos = this->position_changed.getValue();
294 
295   if (inside) {
296     if (!wasactive) {
297       this->isActive = TRUE;
298       this->enterTime = currtime;
299     }
300     SbRotation newrot = SbRotation(vm).inverse();
301     if (!wasactive || newrot != oldrot) {
302       this->orientation_changed = newrot;
303     }
304     if (!wasactive || viewer != oldpos) {
305       this->position_changed = viewer;
306     }
307   }
308   else if (!inside && wasactive) {
309     this->isActive = FALSE;
310     this->exitTime = currtime;
311   }
312 }
313 
314 // Doc in parent
315 void
GLRender(SoGLRenderAction * action)316 SoVRMLProximitySensor::GLRender(SoGLRenderAction * action)
317 {
318   SoVRMLProximitySensor::doAction((SoAction*) action);
319 }
320 
321 // Doc in parent
322 void
callback(SoCallbackAction * COIN_UNUSED_ARG (action))323 SoVRMLProximitySensor::callback(SoCallbackAction * COIN_UNUSED_ARG(action))
324 {
325 }
326 
327 // Doc in parent
328 void
rayPick(SoRayPickAction * COIN_UNUSED_ARG (action))329 SoVRMLProximitySensor::rayPick(SoRayPickAction * COIN_UNUSED_ARG(action))
330 {
331 }
332 
333 // Doc in parent
334 void
getBoundingBox(SoGetBoundingBoxAction * COIN_UNUSED_ARG (action))335 SoVRMLProximitySensor::getBoundingBox(SoGetBoundingBoxAction * COIN_UNUSED_ARG(action))
336 {
337 }
338 
339 // Doc in parent
340 void
notify(SoNotList * list)341 SoVRMLProximitySensor::notify(SoNotList * list)
342 {
343   inherited::notify(list);
344 }
345 
346 #endif // HAVE_VRML97
347