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 #include <Inventor/scxml/SoScXMLStateMachine.h>
34
35 /*!
36 \class SoScXMLStateMachine SoScXMLStateMachine.h Inventor/scxml/SoScXMLStateMachine.h
37 \brief Integration level for using Coin events with SCXML-based state machines.
38
39 This class is part of integrating the Coin types with the SCXML
40 subsystem. SoScXMLStateMachines adds convenience for using SCXML
41 state machines together with Coin events.
42
43 \since Coin 3.0
44 \ingroup soscxml
45 */
46
47 #include <boost/scoped_ptr.hpp>
48 #include <boost/intrusive_ptr.hpp>
49
50 #include <Inventor/SbString.h>
51 #include <Inventor/SbViewportRegion.h>
52 #include <Inventor/errors/SoDebugError.h>
53 #include <Inventor/actions/SoRayPickAction.h>
54 #include <Inventor/SoPickedPoint.h>
55 #include <Inventor/nodes/SoCamera.h>
56 #include <Inventor/events/SoEvents.h>
57 #include <Inventor/scxml/ScXML.h>
58 #include <Inventor/scxml/SoScXMLEvent.h>
59 #include "base/coinString.h"
60 #include "scxml/SbStringConvert.h"
61 #include "SbBasicP.h"
62
63 // *************************************************************************
64
65 class SoScXMLStateMachine::PImpl {
66 public:
PImpl(void)67 PImpl(void)
68 : scenegraphroot(NULL), activecamera(NULL), viewport(100, 100)
69 { }
~PImpl(void)70 ~PImpl(void) { }
71
72 // hold a couple of custom non-SoEvent-based events
73 boost::scoped_ptr<ScXMLEvent> preGLRenderEvent;
74 boost::scoped_ptr<ScXMLEvent> postGLRenderEvent;
75
76 boost::intrusive_ptr<SoNode> scenegraphroot;
77 boost::intrusive_ptr<SoCamera> activecamera;
78 SbViewportRegion viewport;
79
80 mutable SbString varstring;
81 };
82
83 #define PRIVATE(obj) ((obj)->pimpl)
84
85 SCXML_OBJECT_SOURCE(SoScXMLStateMachine);
86
87 void
initClass(void)88 SoScXMLStateMachine::initClass(void)
89 {
90 SCXML_OBJECT_INIT_CLASS(SoScXMLStateMachine, ScXMLStateMachine, "ScXMLStateMachine");
91 }
92
93 void
cleanClass(void)94 SoScXMLStateMachine::cleanClass(void)
95 {
96 SoScXMLStateMachine::classTypeId = SoType::badType();
97 }
98
SoScXMLStateMachine(void)99 SoScXMLStateMachine::SoScXMLStateMachine(void)
100 {
101 // initialize custom events
102 PRIVATE(this)->preGLRenderEvent.reset(new ScXMLEvent);
103 PRIVATE(this)->preGLRenderEvent->setEventName("sim.coin3d.coin.GLRender.PRE_RENDER");
104 PRIVATE(this)->postGLRenderEvent.reset(new ScXMLEvent);
105 PRIVATE(this)->postGLRenderEvent->setEventName("sim.coin3d.coin.GLRender.POST_RENDER");
106 }
107
~SoScXMLStateMachine(void)108 SoScXMLStateMachine::~SoScXMLStateMachine(void)
109 {
110 }
111
112 void
setSceneGraphRoot(SoNode * root)113 SoScXMLStateMachine::setSceneGraphRoot(SoNode * root)
114 {
115 PRIVATE(this)->scenegraphroot = root;
116 }
117
118 SoNode *
getSceneGraphRoot(void) const119 SoScXMLStateMachine::getSceneGraphRoot(void) const
120 {
121 return PRIVATE(this)->scenegraphroot.get();
122 }
123
124 void
setActiveCamera(SoCamera * camera)125 SoScXMLStateMachine::setActiveCamera(SoCamera * camera)
126 {
127 PRIVATE(this)->activecamera = camera;
128 }
129
130 SoCamera *
getActiveCamera(void) const131 SoScXMLStateMachine::getActiveCamera(void) const
132 {
133 return PRIVATE(this)->activecamera.get();
134 }
135
136 void
setViewportRegion(const SbViewportRegion & vp)137 SoScXMLStateMachine::setViewportRegion(const SbViewportRegion & vp)
138 {
139 PRIVATE(this)->viewport = vp;
140 }
141
142 const SbViewportRegion &
getViewportRegion(void) const143 SoScXMLStateMachine::getViewportRegion(void) const
144 {
145 return PRIVATE(this)->viewport;
146 }
147
148 void
preGLRender(void)149 SoScXMLStateMachine::preGLRender(void)
150 {
151 this->queueEvent(PRIVATE(this)->preGLRenderEvent.get());
152 this->processEventQueue();
153 }
154
155 void
postGLRender(void)156 SoScXMLStateMachine::postGLRender(void)
157 {
158 this->queueEvent(PRIVATE(this)->postGLRenderEvent.get());
159 this->processEventQueue();
160 }
161
162 /*!
163 This function makes the state machine process an SoEvent.
164 */
165 SbBool
processSoEvent(const SoEvent * event)166 SoScXMLStateMachine::processSoEvent(const SoEvent * event)
167 {
168 // FIXME: Not sure if this check should be here and not somewhere else,
169 // but removing this again makes us crash on NULL scenegraphs. kintel 20080729.
170 if (PRIVATE(this)->scenegraphroot.get()) {
171 boost::scoped_ptr<SoScXMLEvent> wrapperevent;
172 wrapperevent.reset(new SoScXMLEvent);
173 wrapperevent->setSoEvent(event);
174 wrapperevent->setUpIdentifier();
175 this->queueEvent(wrapperevent.get());
176 return this->processEventQueue();
177 }
178 else {
179 return false;
180 }
181 }
182
183 /*!
184 The string returned from this function is only valid until the next variable is
185 requested.
186 */
187 const char *
getVariable(const char * key) const188 SoScXMLStateMachine::getVariable(const char * key) const
189 {
190 if (strncmp(key, "_event.", 7) == 0) {
191 // printf("scan for key '%s'\n", key);
192 const char * subkey = key + 7;
193 const ScXMLEvent * ev = this->getCurrentEvent();
194 if (ev->isOfType(SoScXMLEvent::getClassTypeId())) {
195 const SoScXMLEvent * soev = static_cast<const SoScXMLEvent *>(ev);
196 const SoEvent * coinev = soev->getSoEvent();
197
198 if (strcmp(subkey, "getTime()") == 0) {
199 SbTime timeval = coinev->getTime();
200 double doubletime = timeval.getValue();
201 PRIVATE(this)->varstring = SbStringConvert::toString(doubletime);
202 return PRIVATE(this)->varstring.getString();
203 }
204
205 else if (strcmp(subkey, "getPosition().x") == 0) {
206 SbVec2s pos = coinev->getPosition();
207 PRIVATE(this)->varstring = SbStringConvert::toString(static_cast<double>(pos[0]));
208 return PRIVATE(this)->varstring.getString();
209 }
210 else if (strcmp(subkey, "getPosition().y") == 0) {
211 SbVec2s pos = coinev->getPosition();
212 PRIVATE(this)->varstring = SbStringConvert::toString(static_cast<double>(pos[1]));
213 return PRIVATE(this)->varstring.getString();
214 }
215 else if (strcmp(subkey, "getPosition()") == 0) {
216 SbVec2s pos = coinev->getPosition();
217 PRIVATE(this)->varstring = SbStringConvert::toString(pos);
218 return PRIVATE(this)->varstring.getString();
219 }
220
221 else if (strcmp(subkey, "getNormalizedPosition().x") == 0) {
222 SbVec2f pos = coinev->getNormalizedPosition(this->getViewportRegion());
223 PRIVATE(this)->varstring = SbStringConvert::toString(static_cast<double>(pos[0]));
224 return PRIVATE(this)->varstring.getString();
225 }
226 else if (strcmp(subkey, "getNormalizedPosition().y") == 0) {
227 SbVec2f pos = coinev->getNormalizedPosition(this->getViewportRegion());
228 PRIVATE(this)->varstring = SbStringConvert::toString(static_cast<double>(pos[1]));
229 return PRIVATE(this)->varstring.getString();
230 }
231 else if (strcmp(subkey, "getNormalizedPosition()") == 0) {
232 SbVec2f pos = coinev->getNormalizedPosition(this->getViewportRegion());
233 PRIVATE(this)->varstring = SbStringConvert::toString(pos);
234 return PRIVATE(this)->varstring.getString();
235 }
236
237 else if (strcmp(subkey, "wasShiftDown()") == 0) {
238 SbBool wasdown = coinev->wasShiftDown();
239 PRIVATE(this)->varstring = SbStringConvert::toString<bool>(wasdown);
240 return PRIVATE(this)->varstring.getString();
241 }
242
243 else if (strcmp(subkey, "wasCtrlDown()") == 0) {
244 SbBool wasdown = coinev->wasCtrlDown();
245 PRIVATE(this)->varstring = SbStringConvert::toString<bool>(wasdown);
246 return PRIVATE(this)->varstring.getString();
247 }
248
249 else if (strcmp(subkey, "wasAltDown()") == 0) {
250 SbBool wasdown = coinev->wasAltDown();
251 PRIVATE(this)->varstring = SbStringConvert::toString<bool>(wasdown);
252 return PRIVATE(this)->varstring.getString();
253 }
254
255 else if (strcmp(subkey, "getState()") == 0 && coinev->isOfType(SoButtonEvent::getClassTypeId())) {
256 const SoButtonEvent * bevent = coin_assert_cast<const SoButtonEvent *>(coinev);
257 SbString enumname;
258 SoButtonEvent::enumToString(bevent->getState(), enumname);
259 PRIVATE(this)->varstring.sprintf("'%s'", enumname.getString());
260 return PRIVATE(this)->varstring.getString();
261 }
262
263 else if (strcmp(subkey, "getKey()") == 0 && coinev->isOfType(SoKeyboardEvent::getClassTypeId())) {
264 const SoKeyboardEvent * kbevent = coin_assert_cast<const SoKeyboardEvent *>(coinev);
265 SbString enumname;
266 SoKeyboardEvent::enumToString(kbevent->getKey(), enumname);
267 PRIVATE(this)->varstring.sprintf("'%s'", enumname.getString());
268 return PRIVATE(this)->varstring.getString();
269 }
270
271 else if (strcmp(subkey, "getPrintableCharacter()") == 0 && coinev->isOfType(SoKeyboardEvent::getClassTypeId())) {
272 const SoKeyboardEvent * kbevent = coin_assert_cast<const SoKeyboardEvent *>(coinev);
273 char printable = kbevent->getPrintableCharacter();
274 PRIVATE(this)->varstring.sprintf("'%c'", printable);
275 return PRIVATE(this)->varstring.getString();
276 }
277
278 else if (strcmp(subkey, "getButton()") == 0 && coinev->isOfType(SoMouseButtonEvent::getClassTypeId())) {
279 const SoMouseButtonEvent * mbevent = coin_assert_cast<const SoMouseButtonEvent *>(coinev);
280 SbString enumname;
281 SoMouseButtonEvent::enumToString(mbevent->getButton(), enumname);
282 PRIVATE(this)->varstring.sprintf("'%s'", enumname.getString());
283 return PRIVATE(this)->varstring.getString();
284 }
285
286 else if (strcmp(subkey, "getButton()") == 0 && coinev->isOfType(SoSpaceballButtonEvent::getClassTypeId())) {
287 const SoSpaceballButtonEvent * mbevent = coin_assert_cast<const SoSpaceballButtonEvent *>(coinev);
288 SbString enumname;
289 SoSpaceballButtonEvent::enumToString(mbevent->getButton(), enumname);
290 PRIVATE(this)->varstring.sprintf("'%s'", enumname.getString());
291 return PRIVATE(this)->varstring.getString();
292 }
293
294 // FIXME: x., .y, .z
295 else if (strcmp(subkey, "getTranslation()") == 0 && coinev->isOfType(SoMotion3Event::getClassTypeId())) {
296 const SoMotion3Event * m3event = coin_assert_cast<const SoMotion3Event *>(coinev);
297 SbVec3f translation = m3event->getTranslation();
298 PRIVATE(this)->varstring = SbStringConvert::toString(translation);
299 return PRIVATE(this)->varstring.getString();
300 }
301
302 // FIXME: .angle, .axis
303 else if (strcmp(subkey, "getRotation()") == 0 && coinev->isOfType(SoMotion3Event::getClassTypeId())) {
304 const SoMotion3Event * m3event = coin_assert_cast<const SoMotion3Event *>(coinev);
305 SbRotation rotation = m3event->getRotation();
306 PRIVATE(this)->varstring = SbStringConvert::toString(rotation);
307 return PRIVATE(this)->varstring.getString();
308 }
309
310 // FIXME: make this into a evaluator-level RayPick(SbVec2f) function instead
311 else if (strcmp(key + 7, "pickposition3") == 0) {
312 SbVec2s location2 = coinev->getPosition();
313 SoRayPickAction rpa(this->getViewportRegion());
314 rpa.setPoint(location2);
315 rpa.apply(this->getSceneGraphRoot());
316 SoPickedPoint * pp = rpa.getPickedPoint();
317 if (pp) {
318 SbVec3f pickpos = pp->getPoint();
319 PRIVATE(this)->varstring = SbStringConvert::toString(pickpos);
320 } else {
321 PRIVATE(this)->varstring.sprintf("FALSE"); // need a valid undefined-value
322 }
323 return PRIVATE(this)->varstring.getString();
324 }
325 }
326 }
327
328 else if (strncmp(key, "coin:", 5) == 0) {
329 const char * subkey = key + 5;
330 if (strncmp(subkey, "camera.", 7) == 0) {
331 SoCamera * camera = this->getActiveCamera();
332 if (!camera) {
333 SoDebugError::post("SoScXMLStateMachine::getVariable",
334 "queried for camera, but no camera is set.");
335 return NULL;
336 }
337 const char * detail = subkey + 7;
338 if (strcmp(detail, "getTypeId()") == 0) {
339 PRIVATE(this)->varstring.sprintf("'%s'", camera->getTypeId().getName().getString());
340 return PRIVATE(this)->varstring.getString();
341 }
342 }
343
344 // get generic field access working and intercept for more So-specific stuff
345 // coin:viewport
346 // coin:camera
347 // coin:scene
348 }
349
350 //else {
351 //}
352
353 // couldn't resolve the symbol - try parent class to get '_data' and other '_event'
354 // locations resolved
355 return inherited::getVariable(key);
356 }
357
358 #undef PRIVATE
359