Mercurial > hg > ccmieditor
view native/PhantomOmni/HapticManager.cpp @ 5:d66dd5880081
Added support for Falcon Haptic device and Tablet/Mouse as haptic device
author | Fiore Martin <fiore@eecs.qmul.ac.uk> |
---|---|
date | Tue, 10 Jul 2012 22:39:37 +0100 |
parents | 9e67171477bc |
children | ea7885bd9bff |
line wrap: on
line source
#include "HapticManager.h" #define NO_ERROR_SIMUL void checkExceptions(void); void stopExecution(char* msg); bool movedNode = false; void HLCALLBACK HapticManager::hlButton1CB(HLenum event, HLuint object, HLenum thread, HLcache *cache, void *userdata) { HapticManager *pThis = static_cast<HapticManager*>(userdata); if(object == pThis->wall1Id||object == pThis->wall2Id) // no event is associated to the wall return; if(event != HL_EVENT_1BUTTONDOWN) return; /* check if it was a double click or not */ clock_t time = clock(); if(pThis->clickStart == 0) // first click pThis->clickStart = time; else if(time < (pThis->clickStart + CLICK_INTERVAL)) pThis->doubleClick = true; } void HLCALLBACK HapticManager::hlButton2CB(HLenum event, HLuint object, HLenum thread, HLcache *cache, void *userdata){ HapticManager *pThis = static_cast<HapticManager*>(userdata); if(event == HL_EVENT_2BUTTONDOWN){ pThis->stickyMode = !pThis->stickyMode; pThis->executeCommand('g', pThis->stickyMode ? 1 : 0,0,0,0,0); /* we change mode to stickyMode, thus dragging becomes not active */ if(pThis->stickyMode && pThis->springStatus == DRAGGING){ pThis->springStatus = STOP_DRAGGING; pThis->secondClick = false; } #ifdef VIBRATION // vibrate if(pThis->vibrationStatus == NO_VIBRATION){ pThis->vibrationStatus = START_VIBRATION; } #endif } } void HLCALLBACK HapticManager::hlTouchCB(HLenum event, HLuint object, HLenum thread, HLcache *cache, void *userdata) { HapticManager *pThis = static_cast<HapticManager*>(userdata); if(object == pThis->wall1Id||object == pThis->wall2Id) // no event is associated to the wall return; if(pThis->lastMovedElement == object){ pThis->stillTouchingLastMovedElement = true; } try{ // check if the touched object is an edge if(pThis->collectionsManager->isEdge(object)){ CollectionsManager::EdgeData & ed = pThis->collectionsManager->getEdgeDataFromID(object); pThis->lastTouchedEdgeID = object; pThis->lastTouchedEdgeIsUntouched = false; pThis->lastTouchedEdgeStipplePattern = ed.stipplePattern; // check if the proxy is still touching the edge's node, in that case only play the sound HLboolean isTouching = HL_FALSE; if(pThis->lastTouchedNodeID != HL_OBJECT_ANY){ hlGetShapeBooleanv(pThis->lastTouchedNodeID,HL_PROXY_IS_TOUCHING,&isTouching); } /* play the sound and speech associated to the edge */ if((isTouching == HL_TRUE||pThis->stillTouchingLastMovedElement) && (object != pThis->attractToHapticId)){ if(!pThis->stickyMode) pThis->executeCommand('p',ed.diagramId,0,0,0,0); // just plays the sound }else{ pThis->executeCommand('s',ed.diagramId,0,0,0,0); pThis->lastHighlightElementID = object; pThis->executeCommand('t',ed.diagramId,0,0,0,0); // speaks the edge's name } }else{ // it's a node pThis->lastTouchedNodeID = object; CollectionsManager::NodeData & nd = pThis->collectionsManager->getNodeDataFromID(object); pThis->lastTouchedNodePosition[0] = nd.x; pThis->lastTouchedNodePosition[1] = nd.y; pThis->lastTouchedNodePosition[2] = 0; pThis->frictionDisabled = true; pThis->executeCommand('s',nd.diagramId,0,0,0,0); pThis->lastHighlightElementID = object; pThis->executeCommand('t',nd.diagramId,0,0,0,0); } }catch(int){ stopExecution("Touch Call Back: Could not retrieve element from ID"); } } void HLCALLBACK HapticManager::hlUntouchCB(HLenum event, HLuint object, HLenum thread, HLcache *cache, void *userdata) { HapticManager *pThis = static_cast<HapticManager*>(userdata); if(object == pThis->wall1Id||object == pThis->wall2Id) // no event is associated to the wall return; if(object == pThis->lastMovedElement){ pThis->lastMovedElement = HL_OBJECT_ANY; pThis->stillTouchingLastMovedElement = false; } if(pThis->lastHighlightElementID == object){ pThis->lastHighlightElementID = HL_OBJECT_ANY; pThis->executeCommand('u',0,0,0,0,0); } if(pThis->lastTouchedEdgeID == object){ pThis->lastTouchedEdgeIsUntouched = true; } } void HLCALLBACK HapticManager::hlMotionCB(HLenum event, HLuint object, HLenum thread, HLcache *cache, void *userdata) { HapticManager *pThis = static_cast<HapticManager*>(userdata); /* plays the sound to acknowledge the user they're dragging an element around */ static int onceinawhile = 0; if(pThis->springStatus == DRAGGING){ onceinawhile = (onceinawhile+1)%5; if(!onceinawhile) pThis->executeCommand('g',3,0,0,0,0); } /* the following code is to play element speech when taking a path through an edge starting from a connected node */ /* friction is disabled when we land on a node. If proxy ain't on a node, then just return */ /* else it would play every time we move along the edge even if not detouching from a node */ if(!pThis->frictionDisabled){ return; } /* calculate the distance between the node, the stylus was on, and the current proxy position after moving out from it*/ HLdouble proxyPosition[3]; hlGetDoublev(HL_DEVICE_POSITION,proxyPosition); proxyPosition[0] -= pThis->lastTouchedNodePosition[0]; proxyPosition[1] -= pThis->lastTouchedNodePosition[1]; proxyPosition[2] -= pThis->lastTouchedNodePosition[2]; if(pThis->lastTouchedEdgeID != HL_OBJECT_ANY){ if(norm(proxyPosition) > EDGE_SPEECH_DISTANCE_WHEN_LEAVING_NODE){ pThis->frictionDisabled = false; HLboolean isTouching; hlGetShapeBooleanv(pThis->lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching); if(isTouching == HL_TRUE){ try{ CollectionsManager::EdgeData & ed = pThis->collectionsManager->getEdgeDataFromID(pThis->lastTouchedEdgeID); pThis->executeCommand('t',ed.diagramId,0,0,0,0); pThis->lastTouchedEdgeID = ed.hapticId; pThis->lastHighlightElementID = ed.hapticId; pThis->executeCommand('s',ed.diagramId,0,0,0,0); }catch(int){ stopExecution("Button 1 callback: Could not retrieve edge from ID"); } } } } } #ifdef VIBRATION void HLCALLBACK HapticManager::vibrationCB(HDdouble force[3], HLcache *cache, void *userdata){ static const hduVector3Dd direction( 0, 1, 0 ); HDdouble instRate; static HDdouble timer = 0; HapticManager *pThis = static_cast<HapticManager*>(userdata); /* Use the reciprocal of the instantaneous rate as a timer. */ hdGetDoublev(HD_INSTANTANEOUS_UPDATE_RATE, &instRate); timer += 1.0 / instRate; /* Apply a sinusoidal force in the direction of motion. */ hduVecScale(force, direction, VIBRATION_AMPLITUDE * sin(timer * VIBRATION_FREQUENCY)); hdSetDoublev(HD_CURRENT_FORCE, force); } void HLCALLBACK HapticManager::beforeVibrationCB(HLcache *cache, void *userdata){} void HLCALLBACK HapticManager::afterVibrationCB(HLcache *cache, void *userdata){} #endif void HapticManager::init(void) throw(HapticException){ HDErrorInfo error; ghHD = hdInitDevice(HD_DEFAULT_DEVICE); if (HD_DEVICE_ERROR(error = hdGetError())){ throw HapticException(); } ghHLRC = hlCreateContext(ghHD); hlMakeCurrent(ghHLRC); // Enable optimization of the viewing parameters when rendering geometry for OpenHaptics. hlEnable(HL_HAPTIC_CAMERA_VIEW); hlTouchableFace(HL_FRONT); wall1Id = hlGenShapes(1); wall2Id = hlGenShapes(1); attractToHapticId = HL_OBJECT_ANY; lastTouchedNodeID = HL_OBJECT_ANY; lastTouchedEdgeID = HL_OBJECT_ANY; draggedElementID = HL_OBJECT_ANY; lastHighlightElementID = HL_OBJECT_ANY; // initialize effects frictionFX = hlGenEffects(1); vibrationFX = hlGenEffects(1); attractionFX = hlGenEffects(1); springFX = hlGenEffects(1); displayList1 = 0; displayList2 = 0; hlEventd(HL_EVENT_MOTION_LINEAR_TOLERANCE,5); hlEventd(HL_EVENT_MOTION_ANGULAR_TOLERANCE,5); // add callbacks hlAddEventCallback(HL_EVENT_2BUTTONDOWN,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton2CB,this); hlAddEventCallback(HL_EVENT_2BUTTONUP,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton2CB,this); hlAddEventCallback(HL_EVENT_1BUTTONUP,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton1CB,this); hlAddEventCallback(HL_EVENT_1BUTTONDOWN,HL_OBJECT_ANY,HL_CLIENT_THREAD,hlButton1CB,this); hlAddEventCallback(HL_EVENT_TOUCH, HL_OBJECT_ANY, HL_CLIENT_THREAD, hlTouchCB, this); hlAddEventCallback(HL_EVENT_UNTOUCH, HL_OBJECT_ANY, HL_CLIENT_THREAD, hlUntouchCB, this); hlAddEventCallback(HL_EVENT_MOTION, HL_OBJECT_ANY, HL_CLIENT_THREAD, hlMotionCB, this); #ifdef VIBRATION hlBeginFrame(); hlCallback(HL_EFFECT_START,(HLcallbackProc) beforeVibrationCB, this); hlCallback(HL_EFFECT_STOP,(HLcallbackProc) afterVibrationCB, this); hlCallback(HL_EFFECT_COMPUTE_FORCE, (HLcallbackProc) vibrationCB, this); hlEndFrame(); #endif } HapticManager::ClickStatus HapticManager::getButton1Status(){ if(clickStart == 0){ return NO_CLICK; }else if(doubleClick){ /* double click: set everything back to default value and return DOUBLE_CLICK */ clickStart = 0; doubleClick = false; return TWO_CLICK; }else if(clock() >= clickStart + CLICK_INTERVAL || secondClick) { /* one click and enough time elapsed, so that it cannot be a double click */ clickStart = 0; return ONE_CLICK; } return NO_CLICK; } void HapticManager::doButton1Click(bool doubleClick){ /* DOUBLE CLICK*/ if(doubleClick){ if(lastTouchedNodeID != HL_OBJECT_ANY){ HLboolean isTouching = HL_FALSE; hlGetShapeBooleanv(lastTouchedNodeID,HL_PROXY_IS_TOUCHING,&isTouching); // priority to nodes if(isTouching == HL_TRUE||lastTouchedNodeID == lastMovedElement){ try{ CollectionsManager::NodeData & nd = collectionsManager->getNodeDataFromID(lastTouchedNodeID); executeCommand('i',nd.diagramId, 0,0,0,0); }catch(int){ stopExecution("Button 1 callback: Could not retrieve node from ID"); } }else{ if(lastTouchedEdgeID != HL_OBJECT_ANY){ hlGetShapeBooleanv(lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching); if(isTouching == HL_TRUE||lastTouchedNodeID == lastMovedElement){ try{ CollectionsManager::EdgeData & ed = collectionsManager->getEdgeDataFromID(lastTouchedEdgeID); executeCommand('i',ed.diagramId, 0,0,0,0); }catch(int){ stopExecution("Button 1 callback: Could not retrieve edge from ID"); } } } } } // this was a double click, thus clean up the data about the dragging if(springStatus == DRAGGING){ springStatus = STOP_DRAGGING; secondClick = false; } return; } /* - SINLGE CLICK - */ if(!secondClick){ // button 1 pressed for the first time HLboolean isTouching; /* if the stylus is touching a node then pick it up */ /* give priority to nodes */ if(lastTouchedNodeID != HL_OBJECT_ANY){ hlGetShapeBooleanv(lastTouchedNodeID,HL_PROXY_IS_TOUCHING,&isTouching); if(isTouching == HL_TRUE || (stillTouchingLastMovedElement&&lastMovedElement == lastTouchedNodeID)){ draggedElementID = lastTouchedNodeID; try{ CollectionsManager::NodeData & nd = collectionsManager->getNodeDataFromID(lastTouchedNodeID); executeCommand('c',nd.diagramId,0,0,0,0); // pick up command }catch(int){ stopExecution("Button 1 callback: Could not retrieve node from ID"); } return; } } /* no node is currently touched, check the edges now */ if(collectionsManager->isEdge(lastTouchedEdgeID)){ hlGetShapeBooleanv(lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching); if(isTouching == HL_TRUE||(stillTouchingLastMovedElement&&lastMovedElement == lastTouchedEdgeID)){ draggedElementID = lastTouchedEdgeID; try{ CollectionsManager::EdgeData & ed = collectionsManager->getEdgeDataFromID(lastTouchedEdgeID); executeCommand('c',ed.diagramId,0,0,0,0); // pick up command }catch(int){ stopExecution("Button 1 callback: Could not retrieve edge from ID"); } } } }else{ // button 1 pressed for the second time if(springStatus != DRAGGING) return; springStatus = STOP_DRAGGING; HLdouble leanPoint[3]; hlGetDoublev(HL_PROXY_POSITION,leanPoint); leanPoint[2] = 1; hduVector3Dd winCoordPoint; toScreen(hduVector3Dd(leanPoint),winCoordPoint); try{ if(collectionsManager->isNode(draggedElementID)){ CollectionsManager::NodeData & nd = collectionsManager->getNodeDataFromID(draggedElementID); movedNode = true; executeCommand('m',nd.diagramId,winCoordPoint[0],collectionsManager->getScreenHeight() - winCoordPoint[1],0,0); }else if(collectionsManager->isEdge(draggedElementID)){ // double check necessary for the user might have changed the tab or closed the window before releasing the spring springStart[2] = 1; hduVector3Dd startWinCoordPoint; toScreen(hduVector3Dd(springStart),startWinCoordPoint);//it's an edge, we also need the point where the motion has started CollectionsManager::EdgeData & ed = collectionsManager->getEdgeDataFromID(draggedElementID); executeCommand('m', ed.diagramId, winCoordPoint[0], collectionsManager->getScreenHeight() - winCoordPoint[1], startWinCoordPoint[0], collectionsManager->getScreenHeight() - startWinCoordPoint[1] ); } }catch(int){ stopExecution("Button 1 callback: Could not retrieve element from ID"); } /* with second click we place the element and it becomes the last moved element */ lastMovedElement = draggedElementID; secondClick = false; } } void HapticManager::setAttractTo(jint hapticId){ if(attractionStatus == NO_ATTRACTION) // starts only if there is no previous attraction being overwritten attractionStatus = START_ATTRACTION; attractToHapticId = hapticId; alreadyTouchingAttractingElement = false; } void HapticManager::pickUp(jint hapticId){ springStatus = START_DRAGGING; secondClick = true; hlGetDoublev(HL_PROXY_POSITION,springStart); } bool HapticManager::wasAlreadyTouchingAttractingElement(){ return alreadyTouchingAttractingElement; } void HapticManager::draw(void){ // Start haptic frame. (Must do this before rendering any haptic shapes.) hlBeginFrame(); if(!doneOnce){ hlStartEffect(HL_EFFECT_FRICTION, frictionFX); doneOnce = true; } if(wall){ // Plane shape. hlPushAttrib(HL_MATERIAL_BIT|HL_TOUCH_BIT); hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, wall1Id); hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1); hlMaterialf(HL_FRONT, HL_POPTHROUGH, 0 ); hlMaterialf(HL_FRONT, HL_STATIC_FRICTION, 0); hlMaterialf(HL_FRONT, HL_DYNAMIC_FRICTION, 0); hlTouchModel(HL_CONTACT); hlTouchableFace(HL_FRONT); drawPlane(-0.005); hlEndShape(); #define SECOND_PLANE #ifdef SECOND_PLANE hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, wall2Id); hlMaterialf(HL_BACK, HL_STIFFNESS, 1); hlTouchableFace(HL_BACK); hlMaterialf(HL_FRONT, HL_POPTHROUGH, 0 ); hlMaterialf(HL_FRONT, HL_STATIC_FRICTION, 0); hlMaterialf(HL_FRONT, HL_DYNAMIC_FRICTION, 0); drawPlane(0.005); hlEndShape(); #endif hlPopAttrib(); } // Start a new haptic shape. Use the feedback buffer to capture OpenGL // geometry for haptic rendering. // draw nodes int size = collectionsManager->getNodesNum(); for(int i=0; i< size;i++){ CollectionsManager::NodeData & nd = collectionsManager->getNodeData(i); hlPushAttrib(HL_HINT_BIT); hlHinti(HL_SHAPE_FEEDBACK_BUFFER_VERTICES, 3); hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, nd.hapticId); hlTouchModel(HL_CONSTRAINT); hlTouchModelf(HL_SNAP_DISTANCE , 4.0f ); hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1.0); hlMaterialf(HL_FRONT_AND_BACK, HL_DAMPING, 0); hlMaterialf(HL_FRONT_AND_BACK, HL_STATIC_FRICTION, 0); hlMaterialf(HL_FRONT_AND_BACK, HL_DYNAMIC_FRICTION, 0); //draw the point glBegin(GL_POINTS); glVertex3d(nd.x,nd.y,0); glEnd(); // End the shape. hlEndShape(); hlPopAttrib(); // saves the coorinates for a later use so that for the effect we don't need to cycle again if(attractionStatus == START_ATTRACTION || attractionStatus == DOING_ATTRACTION){ if(nd.hapticId == attractToHapticId){ attractToCoord[0] = nd.x; attractToCoord[1] = nd.y; attractToCoord[2] = 0; } } } /* draw lines. When a node is moved, edges are not drawn for the very first haptic frame after the shift. * * This is to address the behaviour of the device which, after a n ode is moved cannot see the styilus * * touching the edge. As a consequence of that in hlMotionCB() when moving away from a node along an edge * * no edge name will be spoken and furthermore the pick up botton won't work as the device thinks it's not * * touching anything. Not drawing the edge on the first frame, seems to address this issue. */ if(!movedNode) { size = collectionsManager->getEdgesNum(); for(int i = 0; i<size; i++){ CollectionsManager::EdgeData & ed = collectionsManager->getEdgeData(i); hlPushAttrib(HL_HINT_BIT|HL_MATERIAL_BIT); hlHinti(HL_SHAPE_FEEDBACK_BUFFER_VERTICES, ed.getSize()); hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, ed.hapticId); hlTouchModel(HL_CONSTRAINT); hlTouchModelf(HL_SNAP_DISTANCE, ( (stickyMode && springStatus != DRAGGING && springStatus != START_DRAGGING) ? 20.0f : 3.5f )); hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1); if(ed.stipplePattern == 0xFFFF){ hlMaterialf(HL_FRONT_AND_BACK, HL_STATIC_FRICTION, 0.5f); } glLineWidth(1.0); glBegin(GL_LINES); for(unsigned int j = 0; j < ed.getSize(); j++){ for(unsigned int k = j; k < ed.getSize(); k++){ if(ed.adjMatrix[j][k]){ if( k >= ed.nodeStart && (!stickyMode || (springStatus != RELEASED && springStatus != STOP_DRAGGING)) && attractionStatus != DOING_ATTRACTION && attractionStatus != START_ATTRACTION){ // not a line connecting two edges and stickyMode not enabled /* it's drawing a line from a point to a node, thus it draws it a little shorter*/ 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])); if(hypotenuse == 0) continue; double mySin = (ed.y[j] - ed.y[k])/hypotenuse; double myCos = (ed.x[j] - ed.x[k])/hypotenuse; glVertex3d(ed.x[k]+(EDGE_SHORTEND_VAL * myCos) , ed.y[k]+(EDGE_SHORTEND_VAL*mySin),0); if(j >= ed.nodeStart) //its a direct edge connecting two nodes glVertex3d(ed.x[j]+(EDGE_SHORTEND_VAL * -myCos),ed.y[j]+(EDGE_SHORTEND_VAL*-mySin),0); else glVertex3d(ed.x[j],ed.y[j],0); }else{ glVertex3d(ed.x[j],ed.y[j],0); glVertex3d(ed.x[k],ed.y[k],0); } } } } glEnd(); hlEndShape(); hlPopAttrib(); // saves the coorinates for a later use so that for the effect we don't need to cycle again if(attractionStatus == START_ATTRACTION || attractionStatus == DOING_ATTRACTION){ if(ed.hapticId == attractToHapticId){ attractToCoord[0] = ed.attractPoint[0]; attractToCoord[1] = ed.attractPoint[1]; attractToCoord[2] = 0; } } } }else { movedNode = false; } /* --- FORCES --- */ /* friction */ if(lastTouchedEdgeID != HL_OBJECT_ANY){ /* check if the edge is still there, it might not as the tab could be changed */ if(collectionsManager->isEdge(lastTouchedEdgeID)){ //hlGetShapeBooleanv(lastTouchedEdgeID,HL_PROXY_IS_TOUCHING,&isTouching); FIXME to remove if(frictionDisabled||lastTouchedEdgeIsUntouched/*(isTouching == HL_FALSE)*/){ hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0); hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0); }else{ if(lastTouchedEdgeStipplePattern == 0xAAAA){ // dotted hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.4f); hlEffectd(HL_EFFECT_PROPERTY_GAIN, 1); }else if(lastTouchedEdgeStipplePattern == 0xF0F0){ // dashed hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.6); hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0.6); } } }else{ lastTouchedEdgeID = HL_OBJECT_ANY; hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0); hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0); } hlUpdateEffect(frictionFX); } // spring if(springStatus == START_DRAGGING){ hlPushAttrib(HL_EFFECT_BIT); hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0.3); hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.5); hduMatrix worldToView; hduMatrix graphicToTouch; hduMatrix touchToWorkspace; hduMatrix WorldToDevice; glMatrixMode(GL_MODELVIEW); glGetDoublev(GL_MODELVIEW_MATRIX,worldToView); hlGetDoublev(HL_VIEWTOUCH_MATRIX, graphicToTouch ); hlGetDoublev(HL_TOUCHWORKSPACE_MATRIX, touchToWorkspace ); WorldToDevice = worldToView * graphicToTouch * touchToWorkspace; hduVector3Dd dst; WorldToDevice.multVecMatrix(hduVector3Dd(springStart[0],springStart[1],0),dst); hlEffectdv(HL_EFFECT_PROPERTY_POSITION, dst); hlStartEffect(HL_EFFECT_SPRING, springFX); hlPopAttrib(); springStatus = DRAGGING; }else if(springStatus == STOP_DRAGGING){ hlStopEffect(springFX); springStatus = RELEASED; } // attraction HLboolean isTouching = HL_FALSE; if(attractionStatus == DOING_ATTRACTION || attractionStatus == START_ATTRACTION){ /* we need to be sure we're currently touching the element */ if(attractToHapticId != HL_OBJECT_ANY){ hlGetShapeBooleanv(attractToHapticId,HL_PROXY_IS_TOUCHING,&isTouching); } // when touching the element we're getting attracted to, then shut the force down if(isTouching == HL_TRUE){ if(attractionStatus == DOING_ATTRACTION){ // we finally reached the attracting node attractionStatus = STOP_ATTRACTION; }else{ attractionStatus = NO_ATTRACTION; // we were already touching the node we wanted to get attracted to alreadyTouchingAttractingElement = true; } } } if(attractionStatus == STOP_ATTRACTION){ attractionStatus = NO_ATTRACTION; hlStopEffect(attractionFX); } else if(attractionStatus == START_ATTRACTION || attractionStatus == DOING_ATTRACTION){ HLdouble devicePosition[3]; HDdouble direction[3]; hlGetDoublev(HL_DEVICE_POSITION,devicePosition); direction[0] = attractToCoord[0] - devicePosition[0]; direction[1] = attractToCoord[1] - devicePosition[1]; direction[2] = attractToCoord[2] - devicePosition[2]; HDdouble directionNorm = norm(direction); if(directionNorm != 0 ){ direction[0] /= directionNorm; direction[1] /= directionNorm; direction[2] /= directionNorm; } hlPushAttrib(HL_EFFECT_BIT); hlEffectdv(HL_EFFECT_PROPERTY_DIRECTION, direction); hlEffectd(HL_EFFECT_PROPERTY_GAIN, 0.5f); hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, 0.5f); if(attractionStatus == START_ATTRACTION){ hlStartEffect(HL_EFFECT_CONSTANT, attractionFX); attractionStatus = DOING_ATTRACTION; }else{ hlUpdateEffect(attractionFX); } hlPopAttrib(); } #ifdef VIBRATION if(vibrationStatus == START_VIBRATION){ hlStartEffect(HL_EFFECT_CALLBACK, vibrationFX); vibrationEnd = clock() + VIBRATION_DURATION; vibrationStatus = DOING_VIBRATION; }else if(vibrationStatus == DOING_VIBRATION){ if(clock() >= vibrationEnd){ hlStopEffect(vibrationFX); vibrationStatus = NO_VIBRATION; } } #endif // End the haptic frame. hlEndFrame(); } void HapticManager::drawPlane(float zoffset){ GLuint displayList = (zoffset < 0) ? displayList1 : displayList2; if (displayList){ glCallList(displayList); } else{ if(zoffset < 0){ displayList1 = glGenLists(1); glNewList(displayList1, GL_COMPILE_AND_EXECUTE); }else{ displayList2 = glGenLists(1); glNewList(displayList2, GL_COMPILE_AND_EXECUTE); } glNewList(displayList, GL_COMPILE_AND_EXECUTE); glPushAttrib(GL_ENABLE_BIT); glPolygonOffset(1,1); glEnable(GL_POLYGON_OFFSET_FILL); glBegin(GL_QUADS); glVertex3f(-5, -5, zoffset); glVertex3f(5, -5, zoffset); glVertex3f(5, 5, zoffset); glVertex3f(-5, 5, zoffset); glEnd(); glDisable(GL_POLYGON_OFFSET_FILL); glPopAttrib(); glEndList(); } } HHD HapticManager::ghHD = HD_INVALID_HANDLE; HHLRC HapticManager::ghHLRC = 0; const double HapticManager::EDGE_SHORTEND_VAL = 0.06; const unsigned int HapticManager::CLICK_INTERVAL = CLOCKS_PER_SEC/5; const double HapticManager::EDGE_SPEECH_DISTANCE_WHEN_LEAVING_NODE = 0.03; #ifdef VIBRATION const HDdouble HapticManager::VIBRATION_AMPLITUDE = 0.88;//0.88; const HDint HapticManager::VIBRATION_FREQUENCY = 170;//160; const unsigned int HapticManager::VIBRATION_DURATION = CLOCKS_PER_SEC/10; // = 100 ms #endif