annotate native/Falcon/uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics.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@5 1 /*
fiore@5 2 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
fiore@5 3
fiore@5 4 Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
fiore@5 5
fiore@5 6 This program is free software: you can redistribute it and/or modify
fiore@5 7 it under the terms of the GNU General Public License as published by
fiore@5 8 the Free Software Foundation, either version 3 of the License, or
fiore@5 9 (at your option) any later version.
fiore@5 10
fiore@5 11 This program is distributed in the hope that it will be useful,
fiore@5 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
fiore@5 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
fiore@5 14 GNU General Public License for more details.
fiore@5 15
fiore@5 16 You should have received a copy of the GNU General Public License
fiore@5 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
fiore@5 18 */
fiore@5 19
fiore@5 20 #include "uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics.h"
fiore@5 21 #include "HapticManager.h"
fiore@5 22 #include <stdlib.h>
fiore@5 23 #include <math.h>
fiore@5 24 #include <assert.h>
fiore@5 25 #include <setjmp.h>
fiore@5 26 #include <GL/glut.h>
fiore@5 27 #ifdef FREEGLUT
fiore@5 28 #include <GL/freeglut.h>
fiore@5 29 #endif
fiore@5 30 #include "utils.h"
fiore@5 31
fiore@5 32 /* enum to match against when checking for the jump result */
fiore@5 33 enum {JMP_OK =0, JMP_EXIT };
fiore@5 34
fiore@5 35 /* Functions prototypes. */
fiore@5 36 /* callbacks */
fiore@5 37 void display(void);
fiore@5 38 void reshape(int width, int height);
fiore@5 39 void idle(void);
fiore@5 40 void exitProcedure(void);
fiore@5 41 void commandCallback(const jchar cmd, const jint ID, const jdouble x, const jdouble y,const jdouble startX, const jdouble startY);
fiore@5 42
fiore@5 43 void initJniVariables(void);
fiore@5 44
fiore@5 45 /* variables */
fiore@5 46 static HapticManager *hManager;
fiore@5 47 static CollectionsManager *cManager;
fiore@5 48 static JNIEnv *env;
fiore@5 49 static jobject *lock;
fiore@5 50 static jmp_buf jmpenv;
fiore@5 51 static int jmpval = 0;
fiore@5 52 static bool reshaped = false;
fiore@5 53 /* jni variables */
fiore@5 54 static jclass hapticClass;
fiore@5 55 static jclass hapticListenerClass;
fiore@5 56 static jfieldID shutdownFieldId;
fiore@5 57 static jfieldID hapticInitFailedfieldId;
fiore@5 58 static jfieldID collectionsChangedFieldId;
fiore@5 59 static jfieldID pickUpFieldId;
fiore@5 60 static jfieldID attractToFieldId;
fiore@5 61 /* hapticListener jni variables */
fiore@5 62 static jfieldID hapticListenerFieldId;
fiore@5 63 static jfieldID cmdFieldId; // belongs to the haptic listener
fiore@5 64 static jfieldID diagramElementFieldId; // belongs to the haptic listener
fiore@5 65 static jfieldID xFieldId; // belongs to the haptic listener
fiore@5 66 static jfieldID yFieldId; // belongs to the haptic listener
fiore@5 67 static jfieldID startXFieldId; // belongs to the haptic listener
fiore@5 68 static jfieldID startYFieldId; // belongs to the haptic listener
fiore@5 69 static jobject hapticListener;
fiore@5 70 /* synchronization methods ids */
fiore@5 71 static jmethodID notifyMethodId;
fiore@5 72 static jmethodID notifyListenerMethodId;
fiore@5 73 static jmethodID waitMethodId;
fiore@5 74
fiore@5 75 /* Native initialization method for the Falcon Haptic device. Upon successful initialization a loop *
fiore@5 76 * is started that communicates with the Java program in order to represent the diagram haptically */
fiore@5 77 JNIEXPORT jint JNICALL Java_uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics_initFalcon
fiore@5 78 (JNIEnv *environment, jobject obj, jint w, jint h){
fiore@5 79
fiore@5 80 env = environment;
fiore@5 81 lock = &obj;
fiore@5 82 /* fake main argv and argc as this is a dll */
fiore@5 83 char *argv[1] = {"FalconHaptics"};
fiore@5 84 int argc = 1;
fiore@5 85 initJniVariables();
fiore@5 86
fiore@5 87 cManager = new CollectionsManager(env,lock);
fiore@5 88 cManager->init();
fiore@5 89 hManager = new HapticManager(cManager,commandCallback);
fiore@5 90
fiore@5 91 /* try to initialize the haptic, tell the java thread about the success/failure. The endeavour *
fiore@5 92 * takes the lock on the haptics java object in order to access the nodes and edges concurrently */
fiore@5 93 if(env->MonitorEnter(*lock) != JNI_OK){
fiore@5 94 stopExecution("Could not allocate memory for haptic thread monitor");
fiore@5 95 }
fiore@5 96 checkExceptions(env, "Could not enter monitor on Haptics");
fiore@5 97
fiore@5 98 bool mustSayGoodbye = false;
fiore@5 99
fiore@5 100 if(!hManager->init()){
fiore@5 101 /* set the field in the java class to comunicate the main thread (waiting *
fiore@5 102 * for the initialization to complete) that the initialization failed */
fiore@5 103 env->SetBooleanField(*lock,hapticInitFailedfieldId,JNI_TRUE);
fiore@5 104 mustSayGoodbye = true;
fiore@5 105 }
fiore@5 106
fiore@5 107 if(mustSayGoodbye)
fiore@5 108 std::cout << "Failed to initialize Falcon haptic device" << std::endl;
fiore@5 109 else
fiore@5 110 std::cout << "Falcon haptic device successfully initialized" << std::endl;
fiore@5 111
fiore@5 112 /* notify the java thread */
fiore@5 113 env->CallVoidMethod(*lock,notifyMethodId);
fiore@5 114 checkExceptions(env,"Could not call notify() on Haptics");
fiore@5 115
fiore@5 116 /* release the lock */
fiore@5 117 if(env->MonitorExit(*lock) != JNI_OK){
fiore@5 118 std::cerr << "Could not release memory for haptic thread monitor" << std::endl;
fiore@5 119 exit(-1);
fiore@5 120 }
fiore@5 121
fiore@5 122 if(mustSayGoodbye)
fiore@5 123 /* initialization failed: return */
fiore@5 124 return -1;
fiore@5 125
fiore@5 126 /* haptic device is initialized, now build the openGL window */
fiore@5 127
fiore@5 128 /* glut initialization */
fiore@5 129 glutInit(&argc, argv);
fiore@5 130
fiore@5 131 glutInitWindowSize( w, h );
fiore@5 132 glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
fiore@5 133 glutCreateWindow( "Falcon Haptics Window" );
fiore@5 134
fiore@5 135 /* set up GLUT callback functions */
fiore@5 136 glutDisplayFunc ( display );
fiore@5 137 glutReshapeFunc ( reshape );
fiore@5 138 glutIdleFunc( idle );
fiore@5 139
fiore@5 140 /* use setjmp to be able to jump off the glutMainLoop when the user shuts the program down */
fiore@5 141 jmpval = setjmp(jmpenv);
fiore@5 142
fiore@5 143 /* start the loop*/
fiore@5 144 if(jmpval == JMP_OK){
fiore@5 145 glutMainLoop();
fiore@5 146 }
fiore@5 147
fiore@5 148 /* jmpval = JMP_EXIT, we're coming from the glut loop */
fiore@5 149 exitProcedure();
fiore@5 150
fiore@5 151 return 0;
fiore@5 152 }
fiore@5 153
fiore@5 154 /* GLUT display callback */
fiore@5 155 void display(){
fiore@5 156 /* takes the lock on the haptics java obejct in order to access the nodes and edges concurrently */
fiore@5 157 if(env->MonitorEnter(*lock) != JNI_OK){
fiore@5 158 stopExecution("Could not allocate memory for haptic thread monitor");
fiore@5 159 }
fiore@5 160 checkExceptions(env,"Could not enter monitor on Haptics");
fiore@5 161
fiore@5 162 /* check if there is a shutdown request */
fiore@5 163 if( env->GetBooleanField(*lock,shutdownFieldId) == JNI_TRUE ){
fiore@5 164 // notify the other thread that this thread is about to die
fiore@5 165 env->CallVoidMethod(*lock,notifyMethodId);
fiore@5 166 checkExceptions(env, "Could not call notify() on Haptics");
fiore@5 167 // release the lock
fiore@5 168 if(env->MonitorExit(*lock) != JNI_OK){
fiore@5 169 std::cerr << "Could not release memory for haptic thread monitor" << std::endl;
fiore@5 170 }
fiore@5 171 longjmp(jmpenv,JMP_EXIT);
fiore@5 172 }
fiore@5 173
fiore@5 174 /* check if the node and edges collections have changed since the last frame or if *
fiore@5 175 * the window has been reshaped. In either case the haptic scene must be rendered again */
fiore@5 176 bool collectionsChanged = false;
fiore@5 177 if(env->GetBooleanField(*lock,collectionsChangedFieldId) == JNI_TRUE || reshaped){
fiore@5 178 if(reshaped)
fiore@5 179 reshaped = false;
fiore@5 180 /* remember that collections have changed (will be used later in hManager->drawDiagram) */
fiore@5 181 collectionsChanged = true;
fiore@5 182 /* set the Java boolean variable back to false */
fiore@5 183 env->SetBooleanField(*lock,collectionsChangedFieldId,JNI_FALSE);
fiore@5 184 }
fiore@5 185
fiore@5 186 /* check if the user picked up an object */
fiore@5 187 jboolean picked_up = env->GetBooleanField(*lock,pickUpFieldId);
fiore@5 188 if(picked_up == JNI_TRUE){
fiore@5 189 env->SetBooleanField(*lock,pickUpFieldId,JNI_FALSE);
fiore@5 190 }
fiore@5 191
fiore@5 192 /* check if the user asked to be attracted to a node */
fiore@5 193 jint attract_to = env->GetIntField(*lock,attractToFieldId);
fiore@5 194 if(attract_to != HapticManager::NO_ID){
fiore@5 195 env->SetIntField(*lock,attractToFieldId,HapticManager::NO_ID);
fiore@5 196 }
fiore@5 197
fiore@5 198 /* --- start drawing --- */
fiore@5 199 /* Set up OpenGL state. */
fiore@5 200 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
fiore@5 201 glEnable(GL_DEPTH_TEST);
fiore@5 202 glDepthFunc(GL_LESS);
fiore@5 203 glDepthMask(GL_TRUE);
fiore@5 204 glEnable(GL_CULL_FACE);
fiore@5 205 glShadeModel(GL_SMOOTH);
fiore@5 206 glEnable( GL_LIGHTING );
fiore@5 207 glEnable(GL_LIGHT0);
fiore@5 208 glLoadIdentity();
fiore@5 209
fiore@5 210 gluLookAt(0, 0, 0.1, // eye position .05
fiore@5 211 0, 0, 0, // center position
fiore@5 212 0, 1, 0); // which direction is up
fiore@5 213 glEnable( GL_NORMALIZE );
fiore@5 214
fiore@5 215 /* draw the diagram graphicly and, if the collection has changed, haptically too */
fiore@5 216 hManager->drawCursor();
fiore@5 217 hManager->drawDiagram(collectionsChanged,(picked_up == JNI_TRUE),attract_to);
fiore@5 218
fiore@5 219
fiore@5 220 /* release lock. HapticManager methods from now on can execute commands without deadlock risk */
fiore@5 221 if(env->MonitorExit(*lock) != JNI_OK){
fiore@5 222 stopExecution("Could not release memory for haptic thread monitor");
fiore@5 223 }
fiore@5 224 /* check button status */
fiore@5 225 hManager->checkButtons();
fiore@5 226
fiore@5 227 /* check if un unselect command is to be issued, for an object being touched has been deleted */
fiore@5 228 hManager->checkUnselection();
fiore@5 229
fiore@5 230 /* check collisions. priority to nodes */
fiore@5 231 bool node_collision = hManager->checkNodeCollision();
fiore@5 232 if(!node_collision) // only one object at time can be touched
fiore@5 233 hManager->checkEdgeCollision();
fiore@5 234 /* check motion feedback */
fiore@5 235 hManager->checkMotion();
fiore@5 236
fiore@5 237 /* swap graphic buffers. */
fiore@5 238 glutSwapBuffers();
fiore@5 239 }
fiore@5 240
fiore@5 241 /* GLUT reshape callback */
fiore@5 242 void reshape (int w, int h){
fiore@5 243 cManager->setScreenHeight(h);
fiore@5 244 cManager->setScreenWidth(w);
fiore@5 245 reshaped = true;
fiore@5 246
fiore@5 247 static const double ANGLE = 100;
fiore@5 248 glViewport(0, 0, w, h);
fiore@5 249 glMatrixMode(GL_PROJECTION);
fiore@5 250 glLoadIdentity();
fiore@5 251 gluPerspective(ANGLE, // angle
fiore@5 252 (float)w / h, // aspect ratio
fiore@5 253 0.01, // near clipping plane .01
fiore@5 254 500);// far clipping plane 1000
fiore@5 255 glMatrixMode(GL_MODELVIEW);
fiore@5 256 }
fiore@5 257
fiore@5 258 /* GLUT idle callback */
fiore@5 259 void idle(){
fiore@5 260 glutPostRedisplay();
fiore@5 261 }
fiore@5 262
fiore@5 263 /* called before exit */
fiore@5 264 void exitProcedure(){
fiore@5 265 hManager->dispose();
fiore@5 266 delete hManager;
fiore@5 267 delete cManager;
fiore@5 268 }
fiore@5 269
fiore@5 270 /* initialize all the variables needed for the jni access to the Falcon Haptics class by the Haptic thread*/
fiore@5 271 void initJniVariables(void){
fiore@5 272 /* --- CLASSES --- */
fiore@5 273 //this class
fiore@5 274 hapticClass = env->GetObjectClass(*lock);
fiore@5 275 if(hapticClass == NULL){
fiore@5 276 stopExecution("Could not find the Haptics class");
fiore@5 277 }
fiore@5 278 // the haptic listener, member of this class
fiore@5 279 hapticListenerClass = env->FindClass("Luk/ac/qmul/eecs/ccmi/haptics/HapticListenerThread;");
fiore@5 280 if(hapticListenerClass == NULL){
fiore@5 281 stopExecution("Could not find the haptic listener class");
fiore@5 282 }
fiore@5 283
fiore@5 284 /* --- FIELD IDS --- */
fiore@5 285
fiore@5 286 // boolean set to this thread to notify the java thread the unsuccessful initialization of haptic device
fiore@5 287 hapticInitFailedfieldId = env->GetFieldID(hapticClass,"initFailed", "Z");
fiore@5 288 if(hapticInitFailedfieldId == NULL){
fiore@5 289 stopExecution("failed to find the initFailed field id");
fiore@5 290 }
fiore@5 291
fiore@5 292 // boolean set by the java thread to notify this thread the program has been shut down
fiore@5 293 shutdownFieldId = env->GetFieldID(hapticClass, "shutdown", "Z");
fiore@5 294 if(shutdownFieldId == NULL){
fiore@5 295 stopExecution("failed to find the shutdownfieldId field id");
fiore@5 296 }
fiore@5 297
fiore@5 298 collectionsChangedFieldId = env->GetFieldID(hapticClass, "collectionsChanged", "Z");
fiore@5 299 if(collectionsChangedFieldId == NULL){
fiore@5 300 stopExecution("failed to find the collectionsChangedFieldId field id");
fiore@5 301 }
fiore@5 302
fiore@5 303 attractToFieldId = env->GetFieldID(hapticClass, "attractTo" , "I");
fiore@5 304 if(attractToFieldId == NULL){
fiore@5 305 stopExecution("failed to find the attractTo field id");
fiore@5 306 }
fiore@5 307
fiore@5 308 pickUpFieldId = env->GetFieldID(hapticClass,"pickUp","Z");
fiore@5 309 if(pickUpFieldId == NULL){
fiore@5 310 stopExecution("failed to find the pickUp field id");
fiore@5 311 }
fiore@5 312
fiore@5 313 hapticListenerFieldId = env->GetFieldID(hapticClass, "hapticListener", "Luk/ac/qmul/eecs/ccmi/haptics/HapticListenerThread;");
fiore@5 314 if(hapticListenerFieldId == NULL){
fiore@5 315 stopExecution("failed to find the hapticListenerThread field id");
fiore@5 316 }
fiore@5 317
fiore@5 318 cmdFieldId = env->GetFieldID(hapticListenerClass,"cmd", "C");
fiore@5 319 if(cmdFieldId == NULL){
fiore@5 320 stopExecution("failed to find the cmd field id of the hapticListener class");
fiore@5 321 }
fiore@5 322
fiore@5 323 diagramElementFieldId = env->GetFieldID(hapticListenerClass, "diagramElementID", "I");
fiore@5 324 if(diagramElementFieldId == NULL){
fiore@5 325 stopExecution("failed to find the diagramElement field id of the hapticListener class");
fiore@5 326 }
fiore@5 327
fiore@5 328 xFieldId = env->GetFieldID(hapticListenerClass, "x", "D");
fiore@5 329 if(xFieldId == NULL){
fiore@5 330 stopExecution("failed to find the x field id of the hapticListener class");
fiore@5 331 }
fiore@5 332
fiore@5 333 yFieldId = env->GetFieldID(hapticListenerClass, "y", "D");
fiore@5 334 if(yFieldId == NULL){
fiore@5 335 stopExecution("failed to find the y field id of the hapticListener class");
fiore@5 336 }
fiore@5 337
fiore@5 338 startXFieldId = env->GetFieldID(hapticListenerClass, "startX", "D");
fiore@5 339 if(startXFieldId == NULL){
fiore@5 340 stopExecution("failed to find the x field id of the hapticListener class");
fiore@5 341 }
fiore@5 342
fiore@5 343 startYFieldId = env->GetFieldID(hapticListenerClass, "startY", "D");
fiore@5 344 if(startYFieldId == NULL){
fiore@5 345 stopExecution("failed to find the y field id of the hapticListener class");
fiore@5 346 }
fiore@5 347
fiore@5 348 hapticListener = env->GetObjectField(*lock,hapticListenerFieldId);
fiore@5 349 /* --- SYNCHRONIZATION METHODS ID --- */
fiore@5 350 // notify()
fiore@5 351 notifyMethodId = env->GetMethodID(hapticClass,"notify","()V");
fiore@5 352 if(notifyMethodId == NULL){
fiore@5 353 stopExecution("failed to find the notify method id");
fiore@5 354 }
fiore@5 355
fiore@5 356 notifyListenerMethodId = env->GetMethodID(hapticListenerClass,"notify","()V");
fiore@5 357 if(notifyListenerMethodId == NULL){
fiore@5 358 stopExecution("failed to find the notify method id");
fiore@5 359 }
fiore@5 360 // wait()
fiore@5 361 waitMethodId = env->GetMethodID(hapticListenerClass,"wait","()V");
fiore@5 362 if(waitMethodId == NULL){
fiore@5 363 stopExecution("failed to find the wait method id");
fiore@5 364 }
fiore@5 365 }
fiore@5 366
fiore@5 367 void commandCallback(const jchar cmd, const jint ID, const jdouble x, const jdouble y, const jdouble startX, const jdouble startY){
fiore@5 368 /* the haptic listener java thread is waiting for commands
fiore@5 369 first set the variable the Haptic Listener java thread will read after being notified,
fiore@5 370 then notify and get it awake. Thus wait for the java thread to notify that the command
fiore@5 371 has been accomplished. This is done as otherwise some commands might be neglected. as if the thread
fiore@5 372 scheduler decides to execute twice this routine without executing the java thread in the middle then
fiore@5 373 the former command gets overwritten by the latter.
fiore@5 374 Note the monitor is hapticListener and not haptics as for the draw function.
fiore@5 375 When in this routine, this thread does not hold the lock on haptics as if a command results in changing
fiore@5 376 the elements collections (e.g. moveNode) all those methods are synchronized and require to acquire the lock on haptics.
fiore@5 377 Since this thread would wait for the command to be executed by the java thread, which in turns would wait for this
fiore@5 378 thread to release the lock on haptics, that would result in a deadlock.
fiore@5 379 */
fiore@5 380 if(env->MonitorEnter(hapticListener) != JNI_OK){
fiore@5 381 stopExecution("Could not allocate memory for haptic listener thread monitor");
fiore@5 382 }
fiore@5 383 checkExceptions(env,"Could not enter monitor on the haptic listener");
fiore@5 384 /* Fill all the shared variables for informaton for the command to be executed */
fiore@5 385 env->SetCharField(hapticListener,cmdFieldId,cmd);
fiore@5 386 env->SetIntField(hapticListener,diagramElementFieldId,ID);
fiore@5 387 env->SetDoubleField(hapticListener,xFieldId,x);
fiore@5 388 env->SetDoubleField(hapticListener,yFieldId,y);
fiore@5 389 env->SetDoubleField(hapticListener,startXFieldId,startX);
fiore@5 390 env->SetDoubleField(hapticListener,startYFieldId,startY);
fiore@5 391 // wake the java thread up to execute the command
fiore@5 392 env->CallVoidMethod(hapticListener,notifyListenerMethodId);
fiore@5 393 checkExceptions(env, "Could not call notify() on HapticListener");
fiore@5 394 /* wait for the commands to be executed. Here is actually where the monitor is *
fiore@5 395 * freed and the java thread starts to execute the command, having been notified */
fiore@5 396 env->CallVoidMethod(hapticListener,waitMethodId);
fiore@5 397 checkExceptions(env, "Could not call wait() on HapticListener");
fiore@5 398 if(env->MonitorExit(hapticListener) != JNI_OK){
fiore@5 399 stopExecution("Could not release memory for haptic listener thread monitor");
fiore@5 400 }
fiore@5 401 }