comparison native/PhantomOmni/HapticManager.cpp @ 3:9e67171477bc

PHANTOM Omni Heptic device release
author Fiore Martin <fiore@eecs.qmul.ac.uk>
date Wed, 25 Apr 2012 17:09:09 +0100
parents
children d66dd5880081
comparison
equal deleted inserted replaced
2:4b2f975e35fa 3:9e67171477bc
1 #include "HapticManager.h"
2
3 #define NO_ERROR_SIMUL
4
5
6 void checkExceptions(void);
7 void stopExecution(char* msg);
8
9 bool movedNode = false;
10
11
12 void HLCALLBACK HapticManager::hlButton1CB(HLenum event, HLuint object,
13 HLenum thread, HLcache *cache,
14 void *userdata)
15 {
16 HapticManager *pThis = static_cast<HapticManager*>(userdata);
17 if(object == pThis->wall1Id||object == pThis->wall2Id) // no event is associated to the wall
18 return;
19 if(event != HL_EVENT_1BUTTONDOWN)
20 return;
21
22 /* check if it was a double click or not */
23
24 clock_t time = clock();
25 if(pThis->clickStart == 0) // first click
26 pThis->clickStart = time;
27 else if(time < (pThis->clickStart + CLICK_INTERVAL))
28 pThis->doubleClick = true;
29
30 }
31
32 void HLCALLBACK HapticManager::hlButton2CB(HLenum event, HLuint object,
33 HLenum thread, HLcache *cache,
34 void *userdata){
35 HapticManager *pThis = static_cast<HapticManager*>(userdata);
36 if(event == HL_EVENT_2BUTTONDOWN){
37 pThis->stickyMode = !pThis->stickyMode;
38 pThis->executeCommand('g', pThis->stickyMode ? 1 : 0,0,0,0,0);
39 /* we change mode to stickyMode, thus dragging becomes not active */
40 if(pThis->stickyMode && pThis->springStatus == DRAGGING){
41 pThis->springStatus = STOP_DRAGGING;
42 pThis->secondClick = false;
43 }
44 #ifdef VIBRATION
45 // vibrate
46 if(pThis->vibrationStatus == NO_VIBRATION){
47 pThis->vibrationStatus = START_VIBRATION;
48 }
49 #endif
50 }
51 }
52
53 void HLCALLBACK HapticManager::hlTouchCB(HLenum event, HLuint object, HLenum thread,
54 HLcache *cache, void *userdata)
55 {
56 HapticManager *pThis = static_cast<HapticManager*>(userdata);
57 if(object == pThis->wall1Id||object == pThis->wall2Id) // no event is associated to the wall
58 return;
59 if(pThis->lastMovedElement == object){
60 pThis->stillTouchingLastMovedElement = true;
61 }
62
63 try{
64 // check if the touched object is an edge
65 if(pThis->collectionsManager->isEdge(object)){
66 CollectionsManager::EdgeData & ed = pThis->collectionsManager->getEdgeDataFromID(object);
67 pThis->lastTouchedEdgeID = object;
68 pThis->lastTouchedEdgeIsUntouched = false;
69 pThis->lastTouchedEdgeStipplePattern = ed.stipplePattern;
70 // check if the proxy is still touching the edge's node, in that case only play the sound
71 HLboolean isTouching = HL_FALSE;
72 if(pThis->lastTouchedNodeID != HL_OBJECT_ANY){
73 hlGetShapeBooleanv(pThis->lastTouchedNodeID,HL_PROXY_IS_TOUCHING,&isTouching);
74 }
75 /* play the sound and speech associated to the edge */
76 if((isTouching == HL_TRUE||pThis->stillTouchingLastMovedElement) && (object != pThis->attractToHapticId)){
77 if(!pThis->stickyMode)
78 pThis->executeCommand('p',ed.diagramId,0,0,0,0); // just plays the sound
79 }else{
80 pThis->executeCommand('s',ed.diagramId,0,0,0,0);
81 pThis->lastHighlightElementID = object;
82 pThis->executeCommand('t',ed.diagramId,0,0,0,0); // speaks the edge's name
83 }
84 }else{ // it's a node
85 pThis->lastTouchedNodeID = object;
86 CollectionsManager::NodeData & nd = pThis->collectionsManager->getNodeDataFromID(object);
87 pThis->lastTouchedNodePosition[0] = nd.x;
88 pThis->lastTouchedNodePosition[1] = nd.y;
89 pThis->lastTouchedNodePosition[2] = 0;
90 pThis->frictionDisabled = true;
91 pThis->executeCommand('s',nd.diagramId,0,0,0,0);
92 pThis->lastHighlightElementID = object;
93 pThis->executeCommand('t',nd.diagramId,0,0,0,0);
94 }
95 }catch(int){
96 stopExecution("Touch Call Back: Could not retrieve element from ID");
97 }
98 }
99
100 void HLCALLBACK HapticManager::hlUntouchCB(HLenum event, HLuint object, HLenum thread,
101 HLcache *cache, void *userdata)
102 {
103 HapticManager *pThis = static_cast<HapticManager*>(userdata);
104 if(object == pThis->wall1Id||object == pThis->wall2Id) // no event is associated to the wall
105 return;
106
107 if(object == pThis->lastMovedElement){
108 pThis->lastMovedElement = HL_OBJECT_ANY;
109 pThis->stillTouchingLastMovedElement = false;
110 }
111 if(pThis->lastHighlightElementID == object){
112 pThis->lastHighlightElementID = HL_OBJECT_ANY;
113 pThis->executeCommand('u',0,0,0,0,0);
114 }
115 if(pThis->lastTouchedEdgeID == object){
116 pThis->lastTouchedEdgeIsUntouched = true;
117 }
118 }
119
120
121 void HLCALLBACK HapticManager::hlMotionCB(HLenum event, HLuint object, HLenum thread,
122 HLcache *cache, void *userdata)
123 {
124 HapticManager *pThis = static_cast<HapticManager*>(userdata);
125
126 /* plays the sound to acknowledge the user they're dragging an element around */
127 static int onceinawhile = 0;
128 if(pThis->springStatus == DRAGGING){
129 onceinawhile = (onceinawhile+1)%5;
130 if(!onceinawhile)
131 pThis->executeCommand('g',3,0,0,0,0);
132 }
133
134 /* the following code is to play element speech when taking a path through an edge starting from a connected node */
135
136 /* friction is disabled when we land on a node. If proxy ain't on a node, then just return */
137 /* else it would play every time we move along the edge even if not detouching from a node */
138 if(!pThis->frictionDisabled){
139 return;
140 }
141
142 /* calculate the distance between the node, the stylus was on, and the current proxy position after moving out from it*/
143 HLdouble proxyPosition[3];
144 hlGetDoublev(HL_DEVICE_POSITION,proxyPosition);
145 proxyPosition[0] -= pThis->lastTouchedNodePosition[0];
146 proxyPosition[1] -= pThis->lastTouchedNodePosition[1];
147 proxyPosition[2] -= pThis->lastTouchedNodePosition[2];
148 if(pThis->lastTouchedEdgeID != HL_OBJECT_ANY){
149 if(norm(proxyPosition) > EDGE_SPEECH_DISTANCE_WHEN_LEAVING_NODE){
150 pThis->frictionDisabled = false;
151 HLboolean isTouching;
152 hlGetShapeBooleanv(pThis->lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching);
153 if(isTouching == HL_TRUE){
154 try{
155 CollectionsManager::EdgeData & ed = pThis->collectionsManager->getEdgeDataFromID(pThis->lastTouchedEdgeID);
156 pThis->executeCommand('t',ed.diagramId,0,0,0,0);
157 pThis->lastTouchedEdgeID = ed.hapticId;
158 pThis->lastHighlightElementID = ed.hapticId;
159 pThis->executeCommand('s',ed.diagramId,0,0,0,0);
160 }catch(int){
161 stopExecution("Button 1 callback: Could not retrieve edge from ID");
162 }
163 }
164 }
165 }
166 }
167
168 #ifdef VIBRATION
169 void HLCALLBACK HapticManager::vibrationCB(HDdouble force[3], HLcache *cache, void *userdata){
170 static const hduVector3Dd direction( 0, 1, 0 );
171 HDdouble instRate;
172 static HDdouble timer = 0;
173
174 HapticManager *pThis = static_cast<HapticManager*>(userdata);
175
176 /* Use the reciprocal of the instantaneous rate as a timer. */
177 hdGetDoublev(HD_INSTANTANEOUS_UPDATE_RATE, &instRate);
178 timer += 1.0 / instRate;
179
180 /* Apply a sinusoidal force in the direction of motion. */
181 hduVecScale(force, direction, VIBRATION_AMPLITUDE * sin(timer * VIBRATION_FREQUENCY));
182 hdSetDoublev(HD_CURRENT_FORCE, force);
183 }
184
185 void HLCALLBACK HapticManager::beforeVibrationCB(HLcache *cache, void *userdata){}
186
187 void HLCALLBACK HapticManager::afterVibrationCB(HLcache *cache, void *userdata){}
188 #endif
189
190 void HapticManager::init(void) throw(HapticException){
191 HDErrorInfo error;
192
193 ghHD = hdInitDevice(HD_DEFAULT_DEVICE);
194 if (HD_DEVICE_ERROR(error = hdGetError())){
195 throw HapticException();
196 }
197
198 ghHLRC = hlCreateContext(ghHD);
199 hlMakeCurrent(ghHLRC);
200
201 // Enable optimization of the viewing parameters when rendering geometry for OpenHaptics.
202 hlEnable(HL_HAPTIC_CAMERA_VIEW);
203
204 hlTouchableFace(HL_FRONT);
205 wall1Id = hlGenShapes(1);
206 wall2Id = hlGenShapes(1);
207 attractToHapticId = HL_OBJECT_ANY;
208 lastTouchedNodeID = HL_OBJECT_ANY;
209 lastTouchedEdgeID = HL_OBJECT_ANY;
210 draggedElementID = HL_OBJECT_ANY;
211 lastHighlightElementID = HL_OBJECT_ANY;
212
213 // initialize effects
214 frictionFX = hlGenEffects(1);
215 vibrationFX = hlGenEffects(1);
216 attractionFX = hlGenEffects(1);
217 springFX = hlGenEffects(1);
218 displayList1 = 0;
219 displayList2 = 0;
220 hlEventd(HL_EVENT_MOTION_LINEAR_TOLERANCE,5);
221 hlEventd(HL_EVENT_MOTION_ANGULAR_TOLERANCE,5);
222
223 // add callbacks
224 hlAddEventCallback(HL_EVENT_2BUTTONDOWN,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton2CB,this);
225 hlAddEventCallback(HL_EVENT_2BUTTONUP,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton2CB,this);
226 hlAddEventCallback(HL_EVENT_1BUTTONUP,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton1CB,this);
227 hlAddEventCallback(HL_EVENT_1BUTTONDOWN,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton1CB,this);
228 hlAddEventCallback(HL_EVENT_TOUCH, HL_OBJECT_ANY, HL_CLIENT_THREAD, hlTouchCB, this);
229 hlAddEventCallback(HL_EVENT_UNTOUCH, HL_OBJECT_ANY, HL_CLIENT_THREAD, hlUntouchCB, this);
230 hlAddEventCallback(HL_EVENT_MOTION, HL_OBJECT_ANY, HL_CLIENT_THREAD, hlMotionCB, this);
231
232 #ifdef VIBRATION
233 hlBeginFrame();
234 hlCallback(HL_EFFECT_START,(HLcallbackProc) beforeVibrationCB, this);
235 hlCallback(HL_EFFECT_STOP,(HLcallbackProc) afterVibrationCB, this);
236 hlCallback(HL_EFFECT_COMPUTE_FORCE, (HLcallbackProc) vibrationCB, this);
237 hlEndFrame();
238 #endif
239 }
240
241 HapticManager::ClickStatus HapticManager::getButton1Status(){
242 if(clickStart == 0){
243 return NO_CLICK;
244 }else if(doubleClick){
245 /* double click: set everything back to default value and return DOUBLE_CLICK */
246 clickStart = 0;
247 doubleClick = false;
248 return TWO_CLICK;
249 }else if(clock() >= clickStart + CLICK_INTERVAL || secondClick) {
250 /* one click and enough time elapsed, so that it cannot be a double click */
251 clickStart = 0;
252 return ONE_CLICK;
253 }
254 return NO_CLICK;
255 }
256
257 void HapticManager::doButton1Click(bool doubleClick){
258 /* DOUBLE CLICK*/
259 if(doubleClick){
260 if(lastTouchedNodeID != HL_OBJECT_ANY){
261 HLboolean isTouching = HL_FALSE;
262 hlGetShapeBooleanv(lastTouchedNodeID,HL_PROXY_IS_TOUCHING,&isTouching); // priority to nodes
263 if(isTouching == HL_TRUE||lastTouchedNodeID == lastMovedElement){
264 try{
265 CollectionsManager::NodeData & nd = collectionsManager->getNodeDataFromID(lastTouchedNodeID);
266 executeCommand('i',nd.diagramId, 0,0,0,0);
267 }catch(int){
268 stopExecution("Button 1 callback: Could not retrieve node from ID");
269 }
270 }else{
271 if(lastTouchedEdgeID != HL_OBJECT_ANY){
272 hlGetShapeBooleanv(lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching);
273 if(isTouching == HL_TRUE||lastTouchedNodeID == lastMovedElement){
274 try{
275 CollectionsManager::EdgeData & ed = collectionsManager->getEdgeDataFromID(lastTouchedEdgeID);
276 executeCommand('i',ed.diagramId, 0,0,0,0);
277 }catch(int){
278 stopExecution("Button 1 callback: Could not retrieve edge from ID");
279 }
280 }
281 }
282 }
283 }
284 // this was a double click, thus clean up the data about the dragging
285 if(springStatus == DRAGGING){
286 springStatus = STOP_DRAGGING;
287 secondClick = false;
288 }
289 return;
290 }
291
292 /* - SINLGE CLICK - */
293 if(!secondClick){ // button 1 pressed for the first time
294 HLboolean isTouching;
295 /* if the stylus is touching a node then pick it up */
296 /* give priority to nodes */
297 if(lastTouchedNodeID != HL_OBJECT_ANY){
298 hlGetShapeBooleanv(lastTouchedNodeID,HL_PROXY_IS_TOUCHING,&isTouching);
299 if(isTouching == HL_TRUE || (stillTouchingLastMovedElement&&lastMovedElement == lastTouchedNodeID)){
300 draggedElementID = lastTouchedNodeID;
301 try{
302 CollectionsManager::NodeData & nd = collectionsManager->getNodeDataFromID(lastTouchedNodeID);
303 executeCommand('c',nd.diagramId,0,0,0,0); // pick up command
304 }catch(int){
305 stopExecution("Button 1 callback: Could not retrieve node from ID");
306 }
307 return;
308 }
309 }
310 /* no node is currently touched, check the edges now */
311 if(collectionsManager->isEdge(lastTouchedEdgeID)){
312 hlGetShapeBooleanv(lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching);
313 if(isTouching == HL_TRUE||(stillTouchingLastMovedElement&&lastMovedElement == lastTouchedEdgeID)){
314 draggedElementID = lastTouchedEdgeID;
315 try{
316 CollectionsManager::EdgeData & ed = collectionsManager->getEdgeDataFromID(lastTouchedEdgeID);
317 executeCommand('c',ed.diagramId,0,0,0,0); // pick up command
318 }catch(int){
319 stopExecution("Button 1 callback: Could not retrieve edge from ID");
320 }
321 }
322 }
323 }else{ // button 1 pressed for the second time
324 if(springStatus != DRAGGING)
325 return;
326 springStatus = STOP_DRAGGING;
327 HLdouble leanPoint[3];
328 hlGetDoublev(HL_PROXY_POSITION,leanPoint);
329 leanPoint[2] = 1;
330 hduVector3Dd winCoordPoint;
331 toScreen(hduVector3Dd(leanPoint),winCoordPoint);
332
333 try{
334 if(collectionsManager->isNode(draggedElementID)){
335 CollectionsManager::NodeData & nd = collectionsManager->getNodeDataFromID(draggedElementID);
336 movedNode = true;
337 executeCommand('m',nd.diagramId,winCoordPoint[0],collectionsManager->getScreenHeight() - winCoordPoint[1],0,0);
338 }else if(collectionsManager->isEdge(draggedElementID)){ // double check necessary for the user might have changed the tab or closed the window before releasing the spring
339 springStart[2] = 1;
340 hduVector3Dd startWinCoordPoint;
341 toScreen(hduVector3Dd(springStart),startWinCoordPoint);//it's an edge, we also need the point where the motion has started
342 CollectionsManager::EdgeData & ed = collectionsManager->getEdgeDataFromID(draggedElementID);
343 executeCommand('m', ed.diagramId,
344 winCoordPoint[0],
345 collectionsManager->getScreenHeight() - winCoordPoint[1],
346 startWinCoordPoint[0],
347 collectionsManager->getScreenHeight() - startWinCoordPoint[1]
348 );
349 }
350 }catch(int){
351 stopExecution("Button 1 callback: Could not retrieve element from ID");
352 }
353 /* with second click we place the element and it becomes the last moved element */
354 lastMovedElement = draggedElementID;
355 secondClick = false;
356 }
357 }
358
359 void HapticManager::setAttractTo(jint hapticId){
360 if(attractionStatus == NO_ATTRACTION) // starts only if there is no previous attraction being overwritten
361 attractionStatus = START_ATTRACTION;
362 attractToHapticId = hapticId;
363 alreadyTouchingAttractingElement = false;
364 }
365
366 void HapticManager::pickUp(jint hapticId){
367 springStatus = START_DRAGGING;
368 secondClick = true;
369 hlGetDoublev(HL_PROXY_POSITION,springStart);
370 }
371
372 bool HapticManager::wasAlreadyTouchingAttractingElement(){
373 return alreadyTouchingAttractingElement;
374 }
375
376 void HapticManager::draw(void){
377 // Start haptic frame. (Must do this before rendering any haptic shapes.)
378 hlBeginFrame();
379
380 if(!doneOnce){
381 hlStartEffect(HL_EFFECT_FRICTION, frictionFX);
382 doneOnce = true;
383 }
384
385 if(wall){
386 // Plane shape.
387 hlPushAttrib(HL_MATERIAL_BIT|HL_TOUCH_BIT);
388 hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, wall1Id);
389 hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1);
390 hlMaterialf(HL_FRONT, HL_POPTHROUGH, 0 );
391 hlMaterialf(HL_FRONT, HL_STATIC_FRICTION, 0);
392 hlMaterialf(HL_FRONT, HL_DYNAMIC_FRICTION, 0);
393 hlTouchModel(HL_CONTACT);
394 hlTouchableFace(HL_FRONT);
395 drawPlane(-0.005);
396 hlEndShape();
397 #define SECOND_PLANE
398 #ifdef SECOND_PLANE
399 hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, wall2Id);
400 hlMaterialf(HL_BACK, HL_STIFFNESS, 1);
401 hlTouchableFace(HL_BACK);
402 hlMaterialf(HL_FRONT, HL_POPTHROUGH, 0 );
403 hlMaterialf(HL_FRONT, HL_STATIC_FRICTION, 0);
404 hlMaterialf(HL_FRONT, HL_DYNAMIC_FRICTION, 0);
405 drawPlane(0.005);
406 hlEndShape();
407 #endif
408 hlPopAttrib();
409 }
410
411 // Start a new haptic shape. Use the feedback buffer to capture OpenGL
412 // geometry for haptic rendering.
413
414 // draw nodes
415 int size = collectionsManager->getNodesNum();
416
417 for(int i=0; i< size;i++){
418 CollectionsManager::NodeData & nd = collectionsManager->getNodeData(i);
419
420 hlPushAttrib(HL_HINT_BIT);
421 hlHinti(HL_SHAPE_FEEDBACK_BUFFER_VERTICES, 3);
422 hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, nd.hapticId);
423 hlTouchModel(HL_CONSTRAINT);
424 hlTouchModelf(HL_SNAP_DISTANCE , 4.0f );
425 hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1.0);
426 hlMaterialf(HL_FRONT_AND_BACK, HL_DAMPING, 0);
427
428 hlMaterialf(HL_FRONT_AND_BACK, HL_STATIC_FRICTION, 0);
429 hlMaterialf(HL_FRONT_AND_BACK, HL_DYNAMIC_FRICTION, 0);
430
431 //draw the point
432 glBegin(GL_POINTS);
433 glVertex3d(nd.x,nd.y,0);
434 glEnd();
435
436 // End the shape.
437 hlEndShape();
438 hlPopAttrib();
439
440 // saves the coorinates for a later use so that for the effect we don't need to cycle again
441 if(attractionStatus == START_ATTRACTION || attractionStatus == DOING_ATTRACTION){
442 if(nd.hapticId == attractToHapticId){
443 attractToCoord[0] = nd.x;
444 attractToCoord[1] = nd.y;
445 attractToCoord[2] = 0;
446 }
447 }
448 }
449
450 /* draw lines. When a node is moved, edges are not drawn for the very first haptic frame after the shift. *
451 * This is to address the behaviour of the device which, after a n ode is moved cannot see the styilus *
452 * touching the edge. As a consequence of that in hlMotionCB() when moving away from a node along an edge *
453 * no edge name will be spoken and furthermore the pick up botton won't work as the device thinks it's not *
454 * touching anything. Not drawing the edge on the first frame, seems to address this issue. */
455 if(!movedNode) {
456 size = collectionsManager->getEdgesNum();
457 for(int i = 0; i<size; i++){
458 CollectionsManager::EdgeData & ed = collectionsManager->getEdgeData(i);
459 hlPushAttrib(HL_HINT_BIT|HL_MATERIAL_BIT);
460 hlHinti(HL_SHAPE_FEEDBACK_BUFFER_VERTICES, ed.getSize());
461
462 hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, ed.hapticId);
463 hlTouchModel(HL_CONSTRAINT);
464 hlTouchModelf(HL_SNAP_DISTANCE, ( (stickyMode && springStatus != DRAGGING && springStatus != START_DRAGGING) ? 20.0f : 3.5f ));
465 hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1);
466
467 if(ed.stipplePattern == 0xFFFF){
468 hlMaterialf(HL_FRONT_AND_BACK, HL_STATIC_FRICTION, 0.5f);
469 }
470
471 glLineWidth(1.0);
472
473 glBegin(GL_LINES);
474 for(unsigned int j = 0; j < ed.getSize(); j++){
475 for(unsigned int k = j; k < ed.getSize(); k++){
476 if(ed.adjMatrix[j][k]){
477 if( k >= ed.nodeStart &&
478 (!stickyMode || (springStatus != RELEASED && springStatus != STOP_DRAGGING))
479 && attractionStatus != DOING_ATTRACTION && attractionStatus != START_ATTRACTION){ // not a line connecting two edges and stickyMode not enabled
480 /* it's drawing a line from a point to a node, thus it draws it a little shorter*/
481 double hypotenuse = sqrt( (ed.x[j] - ed.x[k])*(ed.x[j] - ed.x[k]) + (ed.y[j] - ed.y[k])*(ed.y[j] - ed.y[k]));
482 if(hypotenuse == 0)
483 continue;
484 double mySin = (ed.y[j] - ed.y[k])/hypotenuse;
485 double myCos = (ed.x[j] - ed.x[k])/hypotenuse;
486 glVertex3d(ed.x[k]+(EDGE_SHORTEND_VAL * myCos) , ed.y[k]+(EDGE_SHORTEND_VAL*mySin),0);
487 if(j >= ed.nodeStart) //its a direct edge connecting two nodes
488 glVertex3d(ed.x[j]+(EDGE_SHORTEND_VAL * -myCos),ed.y[j]+(EDGE_SHORTEND_VAL*-mySin),0);
489 else
490 glVertex3d(ed.x[j],ed.y[j],0);
491 }else{
492 glVertex3d(ed.x[j],ed.y[j],0);
493 glVertex3d(ed.x[k],ed.y[k],0);
494 }
495 }
496 }
497 }
498 glEnd();
499 hlEndShape();
500 hlPopAttrib();
501
502 // saves the coorinates for a later use so that for the effect we don't need to cycle again
503 if(attractionStatus == START_ATTRACTION || attractionStatus == DOING_ATTRACTION){
504 if(ed.hapticId == attractToHapticId){
505 attractToCoord[0] = ed.attractPoint[0];
506 attractToCoord[1] = ed.attractPoint[1];
507 attractToCoord[2] = 0;
508 }
509 }
510 }
511 }else {
512 movedNode = false;
513 }
514 /* --- FORCES --- */
515 /* friction */
516 if(lastTouchedEdgeID != HL_OBJECT_ANY){
517 /* check if the edge is still there, it might not as the tab could be changed */
518 if(collectionsManager->isEdge(lastTouchedEdgeID)){
519 //hlGetShapeBooleanv(lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching); FIXME to remove
520 if(frictionDisabled||lastTouchedEdgeIsUntouched/*(isTouching == HL_FALSE)*/){
521 hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0);
522 hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0);
523 }else{
524 if(lastTouchedEdgeStipplePattern == 0xAAAA){ // dotted
525 hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.4f);
526 hlEffectd(HL_EFFECT_PROPERTY_GAIN, 1);
527 }else if(lastTouchedEdgeStipplePattern == 0xF0F0){ // dashed
528 hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.6);
529 hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0.6);
530 }
531 }
532 }else{
533 lastTouchedEdgeID = HL_OBJECT_ANY;
534 hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0);
535 hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0);
536 }
537 hlUpdateEffect(frictionFX);
538 }
539
540 // spring
541 if(springStatus == START_DRAGGING){
542 hlPushAttrib(HL_EFFECT_BIT);
543 hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0.3);
544 hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.5);
545
546 hduMatrix worldToView;
547 hduMatrix graphicToTouch;
548 hduMatrix touchToWorkspace;
549 hduMatrix WorldToDevice;
550 glMatrixMode(GL_MODELVIEW);
551 glGetDoublev(GL_MODELVIEW_MATRIX,worldToView);
552 hlGetDoublev(HL_VIEWTOUCH_MATRIX, graphicToTouch );
553 hlGetDoublev(HL_TOUCHWORKSPACE_MATRIX, touchToWorkspace );
554 WorldToDevice = worldToView * graphicToTouch * touchToWorkspace;
555
556 hduVector3Dd dst;
557 WorldToDevice.multVecMatrix(hduVector3Dd(springStart[0],springStart[1],0),dst);
558
559 hlEffectdv(HL_EFFECT_PROPERTY_POSITION, dst);
560 hlStartEffect(HL_EFFECT_SPRING, springFX);
561 hlPopAttrib();
562 springStatus = DRAGGING;
563 }else if(springStatus == STOP_DRAGGING){
564 hlStopEffect(springFX);
565 springStatus = RELEASED;
566 }
567
568 // attraction
569 HLboolean isTouching = HL_FALSE;
570 if(attractionStatus == DOING_ATTRACTION || attractionStatus == START_ATTRACTION){
571 /* we need to be sure we're currently touching the element */
572 if(attractToHapticId != HL_OBJECT_ANY){
573 hlGetShapeBooleanv(attractToHapticId,HL_PROXY_IS_TOUCHING,&isTouching);
574 }
575
576 // when touching the element we're getting attracted to, then shut the force down
577 if(isTouching == HL_TRUE){
578 if(attractionStatus == DOING_ATTRACTION){ // we finally reached the attracting node
579 attractionStatus = STOP_ATTRACTION;
580 }else{
581 attractionStatus = NO_ATTRACTION; // we were already touching the node we wanted to get attracted to
582 alreadyTouchingAttractingElement = true;
583 }
584 }
585 }
586
587 if(attractionStatus == STOP_ATTRACTION){
588 attractionStatus = NO_ATTRACTION;
589 hlStopEffect(attractionFX);
590 } else if(attractionStatus == START_ATTRACTION || attractionStatus == DOING_ATTRACTION){
591 HLdouble devicePosition[3];
592 HDdouble direction[3];
593 hlGetDoublev(HL_DEVICE_POSITION,devicePosition);
594 direction[0] = attractToCoord[0] - devicePosition[0];
595 direction[1] = attractToCoord[1] - devicePosition[1];
596 direction[2] = attractToCoord[2] - devicePosition[2];
597 HDdouble directionNorm = norm(direction);
598 if(directionNorm != 0 ){
599 direction[0] /= directionNorm;
600 direction[1] /= directionNorm;
601 direction[2] /= directionNorm;
602 }
603 hlPushAttrib(HL_EFFECT_BIT);
604 hlEffectdv(HL_EFFECT_PROPERTY_DIRECTION, direction);
605 hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0.5f);
606 hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.5f);
607 if(attractionStatus == START_ATTRACTION){
608 hlStartEffect(HL_EFFECT_CONSTANT, attractionFX);
609 attractionStatus = DOING_ATTRACTION;
610 }else{
611 hlUpdateEffect(attractionFX);
612 }
613 hlPopAttrib();
614 }
615 #ifdef VIBRATION
616 if(vibrationStatus == START_VIBRATION){
617 hlStartEffect(HL_EFFECT_CALLBACK, vibrationFX);
618 vibrationEnd = clock() + VIBRATION_DURATION;
619 vibrationStatus = DOING_VIBRATION;
620 }else if(vibrationStatus == DOING_VIBRATION){
621 if(clock() >= vibrationEnd){
622 hlStopEffect(vibrationFX);
623 vibrationStatus = NO_VIBRATION;
624 }
625 }
626 #endif
627 // End the haptic frame.
628 hlEndFrame();
629 }
630
631 void HapticManager::drawPlane(float zoffset){
632 GLuint displayList = (zoffset < 0) ? displayList1 : displayList2;
633
634 if (displayList){
635 glCallList(displayList);
636 }
637 else{
638 if(zoffset < 0){
639 displayList1 = glGenLists(1);
640 glNewList(displayList1, GL_COMPILE_AND_EXECUTE);
641 }else{
642 displayList2 = glGenLists(1);
643 glNewList(displayList2, GL_COMPILE_AND_EXECUTE);
644 }
645 glNewList(displayList, GL_COMPILE_AND_EXECUTE);
646 glPushAttrib(GL_ENABLE_BIT);
647 glPolygonOffset(1,1);
648 glEnable(GL_POLYGON_OFFSET_FILL);
649
650 glBegin(GL_QUADS);
651 glVertex3f(-5, -5, zoffset);
652 glVertex3f(5, -5, zoffset);
653 glVertex3f(5, 5, zoffset);
654 glVertex3f(-5, 5, zoffset);
655 glEnd();
656 glDisable(GL_POLYGON_OFFSET_FILL);
657 glPopAttrib();
658 glEndList();
659 }
660
661 }
662
663 HHD HapticManager::ghHD = HD_INVALID_HANDLE;
664 HHLRC HapticManager::ghHLRC = 0;
665 const double HapticManager::EDGE_SHORTEND_VAL = 0.06;
666 const unsigned int HapticManager::CLICK_INTERVAL = CLOCKS_PER_SEC/5;
667 const double HapticManager::EDGE_SPEECH_DISTANCE_WHEN_LEAVING_NODE = 0.03;
668 #ifdef VIBRATION
669 const HDdouble HapticManager::VIBRATION_AMPLITUDE = 0.88;//0.88;
670 const HDint HapticManager::VIBRATION_FREQUENCY = 170;//160;
671 const unsigned int HapticManager::VIBRATION_DURATION = CLOCKS_PER_SEC/10; // = 100 ms
672 #endif