Mercurial > hg > ccmieditor
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 |