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 SoVRMLTouchSensor SoVRMLTouchSensor.h Inventor/VRMLnodes/SoVRMLTouchSensor.h
41   \brief The SoVRMLTouchSensor class tracks to pointer position and sends events based on user interaction.
42 
43   \ingroup VRMLnodes
44 
45   \WEB3DCOPYRIGHT
46 
47   \verbatim
48   TouchSensor {
49     exposedField SFBool  enabled TRUE
50     eventOut     SFVec3f hitNormal_changed
51     eventOut     SFVec3f hitPoint_changed
52     eventOut     SFVec2f hitTexCoord_changed
53     eventOut     SFBool  isActive
54     eventOut     SFBool  isOver
55     eventOut     SFTime  touchTime
56   }
57   \endverbatim
58 
59   A TouchSensor node tracks the location and state of the pointing
60   device and detects when the user points at geometry contained by the
61   TouchSensor node's parent group. A TouchSensor node can be enabled
62   or disabled by sending it an enabled event with a value of TRUE or
63   FALSE. If the TouchSensor node is disabled, it does not track user
64   input or send events.
65 
66   The TouchSensor generates events when the pointing device points toward
67   any geometry nodes that are descendants of the TouchSensor's parent group.
68   See 4.6.7.5, Activating and manipulating sensors
69   (<http://www.web3d.org/documents/specifications/14772/V2.0/part1/concepts.html#4.6.7.5>),
70   for more details on using the pointing device to activate the TouchSensor.
71 
72   The \e isOver eventOut reflects the state of the pointing device
73   with regard to whether it is pointing towards the TouchSensor node's
74   geometry or not. When the pointing device changes state from a
75   position such that its bearing does not intersect any of the
76   TouchSensor node's geometry to one in which it does intersect
77   geometry, an isOver TRUE event is generated. When the pointing
78   device moves from a position such that its bearing intersects
79   geometry to one in which it no longer intersects the geometry, or
80   some other geometry is obstructing the TouchSensor node's geometry,
81   an isOver FALSE event is generated. These events are generated only
82   when the pointing device has moved and changed `over' state. Events
83   are not generated if the geometry itself is animating and moving
84   underneath the pointing device.
85 
86   As the user moves the bearing over the TouchSensor node's geometry,
87   the point of intersection (if any) between the bearing and the
88   geometry is determined.  Each movement of the pointing device, while
89   isOver is TRUE, generates hitPoint_changed, hitNormal_changed and
90   hitTexCoord_changed events. hitPoint_changed events contain the 3D
91   point on the surface of the underlying geometry, given in the
92   TouchSensor node's coordinate system.  hitNormal_changed events
93   contain the surface normal vector at the
94   hitPoint. hitTexCoord_changed events contain the texture coordinates
95   of that surface at the hitPoint. The values of hitTexCoord_changed
96   and hitNormal_changed events are computed as appropriate for the
97   associated shape.
98 
99   If isOver is TRUE, the user may activate the pointing device to
100   cause the TouchSensor node to generate isActive events (e.g., by
101   pressing the primary mouse button). When the TouchSensor node
102   generates an isActive TRUE event, it grabs all further motion events
103   from the pointing device until it is released and generates an
104   isActive FALSE event (other pointing-device sensors will not
105   generate events during this time). Motion of the pointing device
106   while isActive is TRUE is termed a "drag." If a 2D pointing device
107   is in use, isActive events reflect the state of the primary button
108   associated with the device (i.e., isActive is TRUE when the primary
109   button is pressed and FALSE when it is released).  If a 3D pointing
110   device is in use, isActive events will typically reflect whether the
111   pointing device is within (or in contact with) the TouchSensor
112   node's geometry.
113 
114   The eventOut field touchTime is generated when all three of the
115   following conditions are true:
116 
117   - The pointing device was pointing towards the geometry when it
118     was initially activated (isActive is TRUE).
119 
120   - The pointing device is currently pointing towards the geometry (isOver is TRUE).
121 
122   - The pointing device is deactivated (isActive FALSE event
123     is also generated).
124 
125   More information about this behaviour is described in 4.6.7.3,
126   Pointing-device sensors
127   (<http://www.web3d.org/documents/specifications/14772/V2.0/part1/concepts.html#4.6.7.3>),
128   4.6.7.4, Drag sensors
129   (<http://www.web3d.org/documents/specifications/14772/V2.0/part1/concepts.html#4.6.7.4>),
130   and 4.6.7.5, Activating and manipulating sensors
131   (<http://www.web3d.org/documents/specifications/14772/V2.0/part1/concepts.html#4.6.7.5>).
132 
133   \ENDWEB3D
134 
135   Here is a small example which demonstrates use of the
136   SoVRMLTouchSensor. Click on the SoVRMLSphere to turn the
137   SoVRMLPointLight on:
138 
139   \code
140   #VRML V2.0 utf8
141 
142   Group {
143      children [
144         Transform {
145            children [
146               DEF light PointLight {
147                  intensity 1
148                  on FALSE
149               }
150 
151               Transform {
152                  translation -2 0 -2
153                  children [
154                     Shape {
155                        appearance Appearance {
156                           material Material {
157                              diffuseColor 1 0 1
158                              specularColor 1 1 1
159                              shininess 0.9
160                           }
161                        }
162                        geometry Sphere { }
163                     }
164                     DEF touchsensor TouchSensor { }
165                  ]
166               }
167            ]
168         }
169      ]
170      ROUTE touchsensor.isActive TO light.set_on
171   }
172   \endcode
173 */
174 
175 /*!
176   \var SoSFBool SoVRMLTouchSensor::enabled
177   TRUE is enabled. Default value is TRUE.
178 */
179 
180 /*!
181   \var SoSFVec3f SoVRMLTouchSensor::hitNormal_changed
182 
183   An eventOut that is sent when the pointer is over some child
184   geometry. Contains the object space normal.
185 
186 */
187 
188 /*!
189   \var SoSFVec3f SoVRMLTouchSensor::hitPoint_changed
190 
191   An eventOut that is sent when the pointer is over some child
192   geometry. Contains the object space point.
193 
194 */
195 
196 /*!
197   \var SoSFVec2f SoVRMLTouchSensor::hitTexCoord_changed
198 
199   An eventOut that is sent when the pointer is over some child
200   geometry. Contains the object space texture coordinates.
201 
202 */
203 
204 /*!
205   \var SoSFBool SoVRMLTouchSensor::isActive
206 
207   An event out that is sent when the active state changes. The sensor
208   goes active when the user clicks on some child, and is inactive
209   again when the mouse button is release.
210 
211 */
212 
213 /*!
214   \var SoSFBool SoVRMLTouchSensor::isOver
215 
216   An event out that is sent when the pointer is moved onto or away from
217   a child object.
218 
219 */
220 
221 /*!
222   \var SoSFTime SoVRMLTouchSensor::touchTime
223 
224   An event out that is generated when the sensor is active and is
225   currently pointing on some child geometry.
226 */
227 
228 
229 
230 #include <Inventor/VRMLnodes/SoVRMLTouchSensor.h>
231 
232 #include <Inventor/VRMLnodes/SoVRMLMacros.h>
233 #include <Inventor/actions/SoHandleEventAction.h>
234 #include <Inventor/events/SoLocation2Event.h>
235 #include <Inventor/events/SoMouseButtonEvent.h>
236 #include <Inventor/SoPickedPoint.h>
237 #include <Inventor/SoDB.h>
238 #include <Inventor/fields/SoSFTime.h>
239 #include <Inventor/SbMatrix.h>
240 
241 #include "nodes/SoSubNodeP.h"
242 
243 //
244 // returns the current time. First tries the realTime field, then
245 // SbTime::getTimeOfDay() if field is not found.
246 //
247 static SbTime
touchsensor_get_current_time(void)248 touchsensor_get_current_time(void)
249 {
250   SoField * realtime = SoDB::getGlobalField("realTime");
251   if (realtime && realtime->isOfType(SoSFTime::getClassTypeId())) {
252     return ((SoSFTime*)realtime)->getValue();
253   }
254   return SbTime::getTimeOfDay();
255 }
256 
257 SO_NODE_SOURCE(SoVRMLTouchSensor);
258 
259 // Doc in parent
260 void
initClass(void)261 SoVRMLTouchSensor::initClass(void)
262 {
263   SO_NODE_INTERNAL_INIT_CLASS(SoVRMLTouchSensor, SO_VRML97_NODE_TYPE);
264 }
265 
266 /*!
267   Constructor.
268 */
SoVRMLTouchSensor(void)269 SoVRMLTouchSensor::SoVRMLTouchSensor(void)
270 {
271   SO_VRMLNODE_INTERNAL_CONSTRUCTOR(SoVRMLTouchSensor);
272 
273   SO_VRMLNODE_ADD_EXPOSED_FIELD(enabled, (TRUE));
274 
275   SO_VRMLNODE_ADD_EVENT_OUT(hitNormal_changed);
276   SO_VRMLNODE_ADD_EVENT_OUT(hitPoint_changed);
277   SO_VRMLNODE_ADD_EVENT_OUT(hitTexCoord_changed);
278   SO_VRMLNODE_ADD_EVENT_OUT(isActive);
279   SO_VRMLNODE_ADD_EVENT_OUT(isOver);
280   SO_VRMLNODE_ADD_EVENT_OUT(touchTime);
281 
282   this->isactive = FALSE;
283 }
284 
285 /*!
286   Destructor.
287 */
~SoVRMLTouchSensor()288 SoVRMLTouchSensor::~SoVRMLTouchSensor()
289 {
290 }
291 
292 // Doc in parent
293 SbBool
affectsState(void) const294 SoVRMLTouchSensor::affectsState(void) const // virtual
295 {
296   return TRUE;
297 }
298 
299 // Doc in parent
300 void
handleEvent(SoHandleEventAction * action)301 SoVRMLTouchSensor::handleEvent(SoHandleEventAction * action)
302 {
303   const SoEvent * event = action->getEvent();
304 
305   SbBool buttondown = SO_MOUSE_PRESS_EVENT(event, BUTTON1);
306   SbBool buttonup = SO_MOUSE_RELEASE_EVENT(event, BUTTON1);
307   SbBool mousemove = event->isOfType(SoLocation2Event::getClassTypeId());
308 
309   SbBool wasover = this->isOver.getValue();
310 
311   if (mousemove || buttondown || buttonup) {
312     SbBool isover = FALSE;
313     const SoPickedPoint * pp = action->getPickedPoint();
314     SoNode * parentnode = NULL;
315     if (pp) {
316       const SoFullPath * currpath = (const SoFullPath*) action->getCurPath();
317       SoFullPath * parentpath = (SoFullPath*) currpath->copy(0, currpath->getLength()-1);
318       parentnode = parentpath->getTail();
319       parentpath->ref();
320       isover = pp->getPath()->containsPath(parentpath);
321       parentpath->unref();
322     }
323     SbBool active = this->isactive;
324     if (active && buttonup) {
325       this->isActive.setValue(FALSE);
326       this->isactive = FALSE;
327     }
328     if (!active && buttondown && isover) {
329       this->isActive.setValue(TRUE);
330       this->isactive = TRUE;
331       active = TRUE;
332     }
333     if (wasover != isover) {
334       this->isOver.setValue(isover);
335     }
336     if (isover) {
337       if (active) {
338           this->touchTime.setValue(touchsensor_get_current_time());
339       }
340       SbMatrix transform = pp->getWorldToObject(parentnode);
341       SbVec3f normal = pp->getNormal();
342       transform.multDirMatrix(normal, normal);
343       this->hitNormal_changed = normal;
344       SbVec3f pt = pp->getPoint();
345       transform.multVecMatrix(pt, pt);
346       this->hitPoint_changed = pt;
347 
348       transform = pp->getImageToObject(parentnode);
349       SbVec4f tc = pp->getTextureCoords();
350       transform.multVecMatrix(tc, tc);
351       float div = tc[3] ? 1.0f / tc[3] : 1.0f;
352       SbVec2f tc2(tc[0] * div, tc[1] * div);
353       this->hitTexCoord_changed = tc2;
354     }
355   }
356   inherited::handleEvent(action);
357 }
358 
359 // Doc in parent
360 void
notify(SoNotList * list)361 SoVRMLTouchSensor::notify(SoNotList * list)
362 {
363   inherited::notify(list);
364 }
365 
366 #endif // HAVE_VRML97
367