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