annotate native/PhantomOmni/HapticManager.cpp @ 8:ea7885bd9bff tip

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