Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1066 lines
38 KiB

  1. //================ Copyright (c) Valve Corporation. All Rights Reserved. ===========================
  2. //
  3. //
  4. //
  5. //==================================================================================================
  6. #include "movecontroller_ps3.h"
  7. #include <cell/camera.h> // PS3 eye camera
  8. #include <pthread.h>
  9. #include <vjobs/root.h>
  10. #include <tier1/convar.h>
  11. #include <tier0/dbg.h>
  12. // NOTE: This has to be the last file included!
  13. #include <tier0/memdbgon.h>
  14. #include "input_device.h"
  15. #include "inputsystem.h"
  16. #include <vectormath/cpp/vectormath_aos.h>
  17. using namespace Vectormath::Aos;
  18. #define MC_MAX_NUM_SAMPLES 30
  19. enum FilterModeType { NONE, LOW_PASS, MOVING_AVG, EXP_MOVING_AVG, GYRO_ATTEN_LOW_PASS, GYRO_PREDICTOR_CORRECTOR, DISTANCE_FALLOFF };
  20. // time in microseconds between gyro samples (gyro samples are updated at approximately 180 Hz = ~5556 usec)
  21. #define GYRO_SAMPLE_SPACING 5625
  22. // [dkorus] lifted this define from the samples/sdk/gem/sharpshooter demo
  23. #define SHARP_SHOOTER_DEVICE_ID 0x8081
  24. extern IVJobs * g_pVJobs;
  25. CMoveController g_moveController;
  26. CMoveController* g_pMoveController = &g_moveController;
  27. ConVar ps3_move_roll_trigger( "ps3_move_roll_trigger", "45.0", FCVAR_ARCHIVE, "amount of roll to trigger R/L shoulder button press in degrees" );
  28. ConVar ps3_move_enabled( "ps3_move_enabled", "1", FCVAR_DEVELOPMENTONLY, "0 => Disabled, 1 => Enabled." );
  29. ConVar ps3_move_filter_method( "ps3_move_filter_method", "6", FCVAR_DEVELOPMENTONLY, "Which filter method to use. 0 to 6 (none, low_pass, moving_average, gyro_atten_low_pass, gyro_corrector, distance_falloff)." );
  30. ConVar ps3_move_cursor_sampling( "ps3_move_cursor_sampling", "0.5", FCVAR_DEVELOPMENTONLY, "0 to 1. Larger numbers = more samples, smoother, more lag.", true, 0.0f, true, 1.0f );
  31. ConVar mc_cursor_sensitivity( "mc_cursor_sensitivity", "0.5", FCVAR_ARCHIVE, "0.0 to 1.0", true, 0.0f, true, 1.0f );
  32. ConVar mc_min_cursor_sensitivity( "mc_min_cursor_sensitivity", "0.25", FCVAR_DEVELOPMENTONLY, "0.0 to 1.0", true, 0.0f, true, 1.0f );
  33. ConVar mc_max_cursor_sensitivity( "mc_max_cursor_sensitivity", "1.25", FCVAR_DEVELOPMENTONLY, "0.0 to 4.0", true, 0.0f, true, 4.0f );
  34. static sys_ppu_thread_t s_gemThread;
  35. static bool s_bGemThreadExit = false;
  36. static sys_memory_container_t s_camContainer;
  37. struct { float left, right, bottom, top; } static s_tracker_plane_extents[MAX_PS3_MOVE_CONTROLLERS]; /**< @brief tracking region extents for each controller */
  38. static int s_nCalibrationStep = 0;
  39. //--------------------------------------------------------------------------------------------------
  40. // GemThreadState contains motion controller state
  41. // written to by Gem update thread
  42. // read by main thread
  43. //--------------------------------------------------------------------------------------------------
  44. struct GemThreadState
  45. {
  46. CellGemInfo m_CellGemInfo;
  47. CellGemState m_aCellGemState[MAX_PS3_MOVE_CONTROLLERS];
  48. int32 m_aStatus[MAX_PS3_MOVE_CONTROLLERS]; // associated getState return values
  49. uint64 m_aStatusFlags[MAX_PS3_MOVE_CONTROLLERS];
  50. int32 m_camStatus;
  51. Vector m_pos[MAX_PS3_MOVE_CONTROLLERS];
  52. Quaternion m_quat[MAX_PS3_MOVE_CONTROLLERS];
  53. float m_posX[MAX_PS3_MOVE_CONTROLLERS];
  54. float m_posY[MAX_PS3_MOVE_CONTROLLERS];
  55. };
  56. static GemThreadState s_gemThreadState;
  57. static void UpdateGemThread(uint64 args);
  58. static float getPitch( vec_float4 q )
  59. {
  60. Vectormath::Aos::Quat quat( q );
  61. Vectormath::Aos::Vector3 home_dir( 0, 0, -1 );
  62. Vectormath::Aos::Vector3 new_dir = Vectormath::Aos::rotate( quat, home_dir );
  63. float x = new_dir[0], y = new_dir[1], z = new_dir[2];
  64. return atan2f( y, sqrtf( x * x + z * z) );
  65. }
  66. // Yaw - Rotation of the controller pointing axis, if it were projected into the x-z plane
  67. // Same as telescope azimuth angle.
  68. static float getYaw( vec_float4 q )
  69. {
  70. Vectormath::Aos::Quat quat( q );
  71. Vectormath::Aos::Vector3 home_dir( 0, 0, -1 );
  72. Vectormath::Aos::Vector3 new_dir = Vectormath::Aos::rotate( quat, home_dir );
  73. new_dir[1] = 0; // portion on x-z plane
  74. return atan2f( -new_dir[0], -new_dir[2] );
  75. }
  76. //Performs a raw ray cast into the tracker plane extents down the -Z axis of the controller
  77. static void MoveKitPointerIntersectWithTrackerPlane(VmathVector3 position, VmathQuat orientation, float* pointerX, float* pointerY)
  78. {
  79. // given the gem position and orientation, form a ray from the ball down the gem -Z axis (direction of pointing)
  80. VmathVector3 rayStart;
  81. VmathVector3 rayDir;
  82. vmathV3Copy(&rayStart, &position);
  83. vmathV3MakeFromElems(&rayDir, 0.0f, 0.0f, -1.0f);
  84. // rotate the direction into the orientation of the controller
  85. vmathQRotate(&rayDir, &orientation, &rayDir);
  86. // intersect the ray with the display plane (at z=0):
  87. // isect.z = rayStart.z + rayDir.z * t, so t = (0 - rayStart.z) / rayDir.z
  88. float t = -vmathV3GetZ(&rayStart) / vmathV3GetZ(&rayDir);
  89. *pointerX = vmathV3GetX(&rayStart) + vmathV3GetX(&rayDir)*t;
  90. *pointerY = vmathV3GetY(&rayStart) + vmathV3GetY(&rayDir)*t;
  91. }
  92. /** @brief Calculates position of cursor in screen space.
  93. *
  94. * Takes the most recent position and orientation of the given controller and converts the pointed at location in the display plane to screen space.
  95. *
  96. * @param[in] gem_num index of controller we're calculating for
  97. * @param[in] position 3D position of controller from most recently queried state
  98. * @param[in] orientation orientation of controller from most recently queried state
  99. * @param[out] pointerX normalized but unclamped X position of pointer in screen space
  100. * @param[out] pointerY normalized but unclamped Y position of pointer in screen space
  101. *
  102. * @post pointerX and pointerY contain normalized (but unclamped) screen positions based on the specified state
  103. *
  104. * @see MoveKitPointerCalcPointerNormalizedRawFromState
  105. */
  106. void MoveKitPointerCalcPointerNormalizedRaw(int gem_num, VmathVector3 position, VmathQuat orientation, float* pointerX, float* pointerY)
  107. {
  108. MoveKitPointerIntersectWithTrackerPlane(position, orientation, pointerX, pointerY);
  109. *pointerX = -1.0f + 2.0f*((*pointerX-s_tracker_plane_extents[gem_num].left)/(s_tracker_plane_extents[gem_num].right-s_tracker_plane_extents[gem_num].left));
  110. *pointerY = -1.0f + 2.0f*((*pointerY-s_tracker_plane_extents[gem_num].bottom)/(s_tracker_plane_extents[gem_num].top-s_tracker_plane_extents[gem_num].bottom));
  111. }
  112. //--------------------------------------------------------------------------------------------------
  113. // ReadCamera
  114. // Init camera if required and read image data for latest frame using cellCameraReadEx
  115. // Returns the cellCameraReadEx return code
  116. //--------------------------------------------------------------------------------------------------
  117. static int32 ReadCamera(CellCameraReadEx *pCamReadEx)
  118. {
  119. pCamReadEx->version=CELL_CAMERA_READ_VER;
  120. int32 camStatus = cellCameraReadEx(0,pCamReadEx);
  121. if (camStatus==CELL_CAMERA_ERROR_NOT_OPEN)
  122. {
  123. CellCameraType type;
  124. cellCameraGetType(0, &type);
  125. if (type == CELL_CAMERA_EYETOY2)
  126. {
  127. sys_memory_container_create(&s_camContainer, 0x100000);
  128. CellCameraInfoEx camera_info;
  129. camera_info.format=CELL_CAMERA_RAW8;
  130. camera_info.framerate=60;
  131. camera_info.resolution=CELL_CAMERA_VGA;
  132. camera_info.container=s_camContainer;
  133. camera_info.info_ver=CELL_CAMERA_INFO_VER_101;
  134. cellCameraOpenEx(0, &camera_info);
  135. camStatus = cellCameraReadEx(0,pCamReadEx);
  136. }
  137. }
  138. if (camStatus==CELL_CAMERA_ERROR_NOT_STARTED)
  139. {
  140. cellCameraReset(0);
  141. cellCameraStart(0);
  142. // Prepare camera using exposure and image quality settings
  143. // for best tracking performance (see PS3 docs)
  144. cellGemPrepareCamera(CELL_GEM_MIN_CAMERA_EXPOSURE,0);
  145. camStatus = cellCameraReadEx(0,pCamReadEx);
  146. }
  147. return camStatus;
  148. }
  149. static void StartUpdateGemThread(GemThreadState *pState)
  150. {
  151. s_bGemThreadExit = false;
  152. sys_ppu_thread_create( &s_gemThread, UpdateGemThread, (uint64)pState,
  153. 1001, // PRIORITY 0-3071, 0 highest
  154. 16*1024, // Stack size, multiple of 4 KB
  155. SYS_PPU_THREAD_CREATE_JOINABLE, "UpdateMoveController" );
  156. }
  157. static inline void FlashSphere(const int controllerNum)
  158. {
  159. const int speed = (1<<18)-1;
  160. float t = M_PI * (float)((int)clock()%speed) / (speed-1); // t in range [0,PI]
  161. float ft = exp(-t*t); // f(t) = 1 / e^(t^2) in range [1,0)
  162. cellGemForceRGB(controllerNum,ft,ft,ft);
  163. }
  164. static bool CheckForSharpshooterConnected( int controllerId )
  165. {
  166. unsigned int ext_id;
  167. unsigned char ext_info[CELL_GEM_EXTERNAL_PORT_DEVICE_INFO_SIZE];
  168. cellGemReadExternalPortDeviceInfo( controllerId, &ext_id, ext_info);
  169. bool sharpshooterConnected = g_pInputSystem->IsInputDeviceConnected( INPUT_DEVICE_SHARPSHOOTER );
  170. if (ext_id == SHARP_SHOOTER_DEVICE_ID &&
  171. !sharpshooterConnected )
  172. {
  173. Msg("Sharp Shooter is connected and ready!\n");
  174. g_pInputSystem->SetInputDeviceConnected( INPUT_DEVICE_SHARPSHOOTER, true );
  175. return true;
  176. }
  177. else if( !sharpshooterConnected )
  178. {
  179. g_pInputSystem->SetInputDeviceConnected( INPUT_DEVICE_SHARPSHOOTER, false );
  180. Msg("Sharp Shooter is disconnected2!\n");
  181. }
  182. return false;
  183. }
  184. bool CheckForMoveConnected( int controllerId )
  185. {
  186. CellGemState gemState;
  187. int32 gemStatus = cellGemGetState(controllerId, CELL_GEM_STATE_FLAG_LATEST_IMAGE_TIME, 0, &gemState);
  188. if(s_gemThreadState.m_CellGemInfo.status[controllerId]==CELL_GEM_STATUS_READY)
  189. {
  190. return true;
  191. }
  192. return false;
  193. }
  194. /** @brief Calculates position of cursor in screen space.
  195. *
  196. * Takes the state of the given controller and converts the pointed at location in the display plane to screen space.
  197. *
  198. * @param[in] gem_num index of controller corresponding to specified state
  199. * @param[in] state the most recently queried gem state for the specified controller
  200. * @param[out] pointerX normalized but unclamped X position of pointer in screen space
  201. * @param[out] pointerY normalized but unclamped Y position of pointer in screen space
  202. *
  203. * @post pointerX and pointerY contain normalized (but unclamped) screen positions based on the specified state
  204. *
  205. * @see MoveKitPointerCalcPointerNormalizedRaw
  206. */
  207. void MoveKitPointerCalcPointerNormalizedRawFromState(int gem_num, CellGemState* state, float* pointerX, float* pointerY)
  208. {
  209. VmathQuat orientation;
  210. vmathQMakeFrom128(&orientation, state->quat);
  211. VmathVector3 position;
  212. vmathV3MakeFrom128(&position, state->pos);
  213. return MoveKitPointerCalcPointerNormalizedRaw(gem_num, position, orientation, pointerX, pointerY);
  214. }
  215. /** @brief Implementation of simple low-pass filter
  216. *
  217. * Blend the current pointer position with previous samples.
  218. * The blend weight controls the amount of lag and smoothness.
  219. * Higher blend weight (alpha) favors the new position while
  220. * lower favors the older (smoother).
  221. *
  222. * @note
  223. * The canonical "sluggish feeling" filter.
  224. *
  225. * @param[out] filteredX X component of filtered pointer position
  226. * @param[out] filteredY Y component of filtered pointer position
  227. * @param screenWidth currently unused
  228. * @param screenHeight currently unused
  229. * @param[in] whichGem index of controller we're filtering [0-3]
  230. * @param[in] numSamples number of consecutive samples to include in filtering calculation, including current sample
  231. * @param[in] alpha blend weight [0.0-1.0]
  232. *
  233. * @returns return code from last \a cellGemGetState call
  234. */
  235. int32_t calcFilteredPointerPos_LowPass(float &filteredX, float &filteredY,
  236. const int whichGem, const int numSamples, const float alpha)
  237. {
  238. assert(numSamples<=MC_MAX_NUM_SAMPLES);
  239. // grab the current gem state time stamp
  240. CellGemState gemState;
  241. int32_t retVal = cellGemGetState(whichGem,CELL_GEM_STATE_FLAG_CURRENT_TIME,CELL_GEM_LATENCY_OFFSET,&gemState);
  242. system_time_t startTimeStamp = gemState.timestamp;
  243. // blend numSamples consecutive samples
  244. retVal = cellGemGetState(whichGem,CELL_GEM_STATE_FLAG_TIMESTAMP,startTimeStamp-GYRO_SAMPLE_SPACING*(numSamples-1),&gemState);
  245. assert(retVal!=CELL_GEM_TIME_OUT_OF_RANGE);
  246. MoveKitPointerCalcPointerNormalizedRawFromState(whichGem, &gemState, &filteredX, &filteredY);
  247. //laserPointerCalcPos(filteredX,filteredY,gemState,screenWidth,screenHeight);
  248. for (int i=numSamples-2; i>=0; i--)
  249. {
  250. float x, y;
  251. retVal = cellGemGetState(whichGem,CELL_GEM_STATE_FLAG_TIMESTAMP,startTimeStamp-GYRO_SAMPLE_SPACING*i,&gemState);
  252. MoveKitPointerCalcPointerNormalizedRawFromState(whichGem, &gemState, &x, &y);
  253. //laserPointerCalcPos(x,y,gemState,screenWidth,screenHeight);
  254. filteredX = (x * alpha) + (filteredX * (1-alpha));
  255. filteredY = (y * alpha) + (filteredY * (1-alpha));
  256. }
  257. return(retVal);
  258. }
  259. /** @brief Implementation of moving average filter
  260. *
  261. * Collect a history of N samples and average them.
  262. *
  263. * @note
  264. * Increasing the number of samples increases the smoothness as well as the latency.
  265. *
  266. * @note
  267. * More responsive than the simple low pass and nearly as stable.
  268. *
  269. * @note
  270. * Optimal at removing "white noise" while preserving sharp transitions. http://www.dspguide.com/ch15/2.htm
  271. *
  272. * @param[out] filteredX X component of filtered pointer position
  273. * @param[out] filteredY Y component of filtered pointer position
  274. * @param screenWidth currently unused
  275. * @param screenHeight currently unused
  276. * @param[in] whichGem index of controller we're filtering [0-3]
  277. * @param[in] numSamples number of consecutive samples to include in filtering calculation, including current sample
  278. *
  279. * @returns return code from last \a cellGemGetState call
  280. */
  281. int32_t calcFilteredPointerPos_MovingAvg(float &filteredX, float &filteredY, const int whichGem, const int numSamples)
  282. {
  283. assert(numSamples<=MC_MAX_NUM_SAMPLES);
  284. // grab the current gem state time stamp
  285. CellGemState gemState;
  286. int32_t retVal = cellGemGetState(whichGem,CELL_GEM_STATE_FLAG_CURRENT_TIME,CELL_GEM_LATENCY_OFFSET,&gemState);
  287. system_time_t startTimeStamp = gemState.timestamp;
  288. // average numSamples consecutive samples
  289. filteredX=0.0f;
  290. filteredY=0.0f;
  291. int numActualSamples = 0;
  292. for (int i=0; i<numSamples; i++)
  293. {
  294. retVal = cellGemGetState(whichGem,CELL_GEM_STATE_FLAG_TIMESTAMP,startTimeStamp-GYRO_SAMPLE_SPACING*i,&gemState);
  295. if ( retVal == 0 )
  296. {
  297. float x=0.0f, y=0.0f;
  298. MoveKitPointerCalcPointerNormalizedRawFromState(whichGem, &gemState, &x, &y);
  299. filteredX +=x;
  300. filteredY +=y;
  301. numActualSamples ++;
  302. }
  303. else
  304. {
  305. assert(retVal!=CELL_GEM_TIME_OUT_OF_RANGE);
  306. }
  307. }
  308. if ( numActualSamples > 1 )
  309. {
  310. filteredX /= (float)numActualSamples;
  311. filteredY /= (float)numActualSamples;
  312. }
  313. return(retVal);
  314. }
  315. /** @brief Implementation of exponential moving average filter
  316. *
  317. * Same as the moving average, but weights newer points in the average higher
  318. * than the older ones. The weights drop off exponentially, so for 4 samples:
  319. * 1, 0.5, 0.25, 0.125
  320. *
  321. * @note
  322. * More responsive than the moving average, but also less smooth
  323. *
  324. * @param[out] filteredX X component of filtered pointer position
  325. * @param[out] filteredY Y component of filtered pointer position
  326. * @param screenWidth currently unused
  327. * @param screenHeight currently unused
  328. * @param[in] whichGem index of controller we're filtering [0-3]
  329. * @param[in] numSamples number of consecutive samples to include in filtering calculation, including current sample
  330. * @param[in] expMovAvgExponent exponential drop-off for samples
  331. *
  332. * @returns return code from last \a cellGemGetState call
  333. */
  334. int32_t calcFilteredPointerPos_ExpMovingAvg(float &filteredX, float &filteredY,
  335. const int whichGem, const int numSamples, const float expMovAvgExponent)
  336. {
  337. assert(numSamples<=MC_MAX_NUM_SAMPLES);
  338. // grab the current gem state time stamp
  339. CellGemState gemState;
  340. int32_t retVal = cellGemGetState(whichGem,CELL_GEM_STATE_FLAG_CURRENT_TIME,CELL_GEM_LATENCY_OFFSET,&gemState);
  341. system_time_t startTimeStamp = gemState.timestamp;
  342. // average numSamples consecutive samples
  343. filteredX=0;
  344. filteredY=0;
  345. float totalWeight=0;
  346. for (int i=0; i<numSamples; i++)
  347. {
  348. float x, y;
  349. retVal = cellGemGetState(whichGem,CELL_GEM_STATE_FLAG_TIMESTAMP,startTimeStamp-GYRO_SAMPLE_SPACING*i,&gemState);
  350. assert(retVal!=CELL_GEM_TIME_OUT_OF_RANGE);
  351. MoveKitPointerCalcPointerNormalizedRawFromState(whichGem, &gemState, &x, &y);
  352. //laserPointerCalcPos(x,y,gemState,screenWidth,screenHeight);
  353. float weight = 1.f / powf(expMovAvgExponent, i);
  354. filteredX += (x * weight);
  355. filteredY += (y * weight);
  356. totalWeight += weight;
  357. }
  358. filteredX /= totalWeight;
  359. filteredY /= totalWeight;
  360. return(retVal);
  361. }
  362. /** @brief Implementation of gyro attentuated low pass filter
  363. *
  364. * Uses the magnitude of the gyro signal to guide a simple low-pass filter.
  365. * Faster gyro motion allows the filter to be more responsive, slower
  366. * motion smooths the signal. The intended behavior is that when the
  367. * user is moving slowly, the response is smoother. While this introduces
  368. * latency, its not as noticeable at slow speeds, where smoothness and
  369. * stability are more important. During fast motions, the gyro magnitude
  370. * is high, which allows the filter to pass the signal through "raw" so
  371. * there is no latency or smooth. Fast motions tend to be imprecise,
  372. * so low latency is more important than smoothness. Slow motions are
  373. * assumed to be "precision" adjustments, which can have some latency
  374. * at the cost of smoothing.
  375. *
  376. * @note
  377. * I think this filter feels best for twitchy precise motions. If the
  378. * user does a lot of fine corrections, or a lot of "medium speed" motions
  379. * it will feel sluggish. But for a precise user, it acts fast when
  380. * twitching and then stabilizes on the local area.
  381. *
  382. * @param[out] filteredX X component of filtered pointer position
  383. * @param[out] filteredY Y component of filtered pointer position
  384. * @param screenWidth currently unused
  385. * @param screenHeight currently unused
  386. * @param[in] whichGem index of controller we're filtering [0-3]
  387. * @param[in] numSamples number of consecutive samples to include in filtering calculation, including current sample
  388. * @param[in] maxGyroAngVel speed at which the blend param = 1, in radians
  389. *
  390. * @returns return code from last \a cellGemGetState call
  391. */
  392. int32_t calcFilteredPointerPos_GyroAttenLowPass(float &filteredX, float &filteredY,
  393. const int whichGem, const int numSamples, const float maxGyroAngVel)
  394. {
  395. assert(numSamples<=MC_MAX_NUM_SAMPLES);
  396. // grab the current gem state time stamp
  397. CellGemState gemState;
  398. int32_t retVal = cellGemGetState(whichGem,CELL_GEM_STATE_FLAG_CURRENT_TIME,CELL_GEM_LATENCY_OFFSET,&gemState);
  399. system_time_t startTimeStamp = gemState.timestamp;
  400. // blend numSamples consecutive samples
  401. retVal = cellGemGetState(whichGem,CELL_GEM_STATE_FLAG_TIMESTAMP,startTimeStamp-GYRO_SAMPLE_SPACING*(numSamples-1),&gemState);
  402. assert(retVal!=CELL_GEM_TIME_OUT_OF_RANGE);
  403. MoveKitPointerCalcPointerNormalizedRawFromState(whichGem, &gemState, &filteredX, &filteredY);
  404. //laserPointerCalcPos(filteredX,filteredY,gemState,screenWidth,screenHeight);
  405. for (int i=numSamples-2; i>=0; i--)
  406. {
  407. float x, y;
  408. retVal = cellGemGetState(whichGem,CELL_GEM_STATE_FLAG_TIMESTAMP,startTimeStamp-GYRO_SAMPLE_SPACING*i,&gemState);
  409. MoveKitPointerCalcPointerNormalizedRawFromState(whichGem, &gemState, &x, &y);
  410. //laserPointerCalcPos(x,y,gemState,screenWidth,screenHeight);
  411. float angVel = sqrtf( gemState.angvel[0]*gemState.angvel[0] + gemState.angvel[1]*gemState.angvel[1] + gemState.angvel[2]*gemState.angvel[2] );
  412. angVel = (angVel>maxGyroAngVel) ? maxGyroAngVel : angVel;
  413. float alpha = angVel / maxGyroAngVel;
  414. filteredX = (x * alpha) + (filteredX * (1-alpha));
  415. filteredY = (y * alpha) + (filteredY * (1-alpha));
  416. }
  417. return(retVal);
  418. }
  419. static inline double rotationAngle(const Quat& u, const Quat& v)
  420. {
  421. double udotv = dot(u, v);
  422. return (fabsf(udotv)>1.0f) ? 0.0f : 2.0f*acosf(udotv);
  423. }
  424. /** @brief Implementation of gyro-based predictor/corrector filter
  425. *
  426. * This filter prevents spurious angular corrections (like from the
  427. * magnetometer or the internal gem filtering system) that are inconsistent
  428. * with the current gyro measurements. Corrections should always be
  429. * proportional to the magnitude of motions read from the gyros, and when
  430. * it is motionless, stray corrections should not occur.
  431. *
  432. * This check is performed as follows:
  433. * 1) Directly integrates the angular velocity from the gyros (the gemState angvel)
  434. * 2) Compare the gyro motion to the "correction" motion (between the current
  435. * state and the integrated result)
  436. * 3) limit the amount of correction proportional to the amount of actual gyro motion.
  437. *
  438. * @note
  439. * This is technically not a motion filter, but rather a spurious motion check
  440. * that could be combined with other filters.
  441. *
  442. * @param[out] filteredX X component of filtered pointer position
  443. * @param[out] filteredY Y component of filtered pointer position
  444. * @param[in] whichGem index of controller we're filtering [0-3]
  445. *
  446. * @returns return code from last \a cellGemGetState call
  447. */
  448. int32_t calcFilteredPointerPos_GyroPredictorCorrector(float &filteredX, float &filteredY,
  449. const int whichGem)
  450. {
  451. static Quat laserQuat[4] = { Quat(0,0,0,1), Quat(0,0,0,1), Quat(0,0,0,1), Quat(0,0,0,1) }; // quat integration accumulators
  452. static system_time_t lastStateTimestamp[4] = { 0,0,0,0 };
  453. CellGemState gemState;
  454. int32_t retVal = cellGemGetState(whichGem,CELL_GEM_STATE_FLAG_CURRENT_TIME,CELL_GEM_LATENCY_OFFSET,&gemState);
  455. float dt = (gemState.timestamp - lastStateTimestamp[whichGem]) * 1e-6f; // in seconds
  456. lastStateTimestamp[whichGem] = gemState.timestamp;
  457. if (dt < 3.0f/60.0f) // update normally if time between last sampling is less than a few frames
  458. {
  459. // this crazy math integrates the world-space angular velocity (which happens to come from the gyros only), and adds it to laserQuat.
  460. Vector3 angVel(gemState.angvel);
  461. Quat angVelQuat(angVel, 0.0f);
  462. Quat quatDot = (0.5f * angVelQuat) * laserQuat[whichGem];
  463. Quat integrationResult = normalize(laserQuat[whichGem] + quatDot*dt);
  464. Quat stateQuat(gemState.quat);
  465. float integrationAngle = fabsf(rotationAngle(laserQuat[whichGem], integrationResult)); // magnitude of the motion-due to gyros
  466. float stateDiffAngle = fabsf(rotationAngle(integrationResult, stateQuat)); // magnitude of the correction that would be needed to match the gyro-based result to libgem's result
  467. // limit the amount of "correction" that occurs to 0.1 * amount of the gyro motion, so as to hide it
  468. // the big benefit of this is that no "correction" occurs if there is no gyro motion, so the pointer
  469. // only moves when the gyros say there is motion occurring
  470. float angleLimit = (stateDiffAngle > 0.1f*integrationAngle)? 0.1f*integrationAngle : stateDiffAngle;
  471. // rotate the integrationResult by the correction amount. this is the new output quat
  472. if (stateDiffAngle > 1e-10f)
  473. laserQuat[whichGem] = slerp(angleLimit/stateDiffAngle, integrationResult, stateQuat);
  474. else
  475. laserQuat[whichGem] = integrationResult;
  476. gemState.quat = laserQuat[whichGem].get128();
  477. }
  478. else // if too long since last sample, set the integration accumulator quat to the current gem state quat
  479. {
  480. laserQuat[whichGem] = Quat(gemState.quat);
  481. }
  482. MoveKitPointerCalcPointerNormalizedRawFromState(whichGem, &gemState, &filteredX, &filteredY);
  483. return(retVal);
  484. }
  485. // Weights samples based on their distance to the latest sample.
  486. int32_t calcFilteredPointerPos_DistanceFalloff(float &filteredX, float &filteredY, const int whichGem)
  487. {
  488. // The higher the sampling (more lag), the smaller we want fRangeScale to be since a smaller range will include more samples and give them higher weights.
  489. // So we use 1.0 - ps3_move_cursor_sampling.
  490. const float fRangeScale = (float)(0x1 << (int)( (1.0f - ps3_move_cursor_sampling.GetFloat()) * 10.0f));
  491. float latestSampleX=0.0f, latestSampleY=0.0f;
  492. // grab the current gem state time stamp
  493. CellGemState gemState;
  494. int32_t retVal = cellGemGetState( whichGem, CELL_GEM_STATE_FLAG_CURRENT_TIME, CELL_GEM_LATENCY_OFFSET, &gemState );
  495. MoveKitPointerCalcPointerNormalizedRawFromState(whichGem, &gemState, &latestSampleX, &latestSampleY);
  496. system_time_t startTimeStamp = gemState.timestamp;
  497. // average numSamples consecutive samples
  498. filteredX=0.0f;
  499. filteredY=0.0f;
  500. float fTotalWeight = 0.0f;
  501. for (int i=0; i<MC_MAX_NUM_SAMPLES; i++)
  502. {
  503. retVal = cellGemGetState( whichGem, CELL_GEM_STATE_FLAG_TIMESTAMP, startTimeStamp-GYRO_SAMPLE_SPACING*i, &gemState );
  504. if ( retVal == 0 )
  505. {
  506. float x=0.0f, y=0.0f;
  507. MoveKitPointerCalcPointerNormalizedRawFromState( whichGem, &gemState, &x, &y );
  508. float dx = x - latestSampleX;
  509. float dy = y - latestSampleY;
  510. float weight = 1.0f - ( sqrtf(dx*dx + dy*dy) * fRangeScale );
  511. weight = clamp(weight, 0.0f, 1.0f);
  512. filteredX += x*weight;
  513. filteredY += y*weight;
  514. fTotalWeight += weight;
  515. }
  516. else
  517. {
  518. assert(retVal!=CELL_GEM_TIME_OUT_OF_RANGE);
  519. }
  520. }
  521. if ( fTotalWeight > 0.0f )
  522. {
  523. filteredX /= (float)fTotalWeight;
  524. filteredY /= (float)fTotalWeight;
  525. }
  526. return(retVal);
  527. }
  528. static void GetFilteredPointerPosition( int gem_num, float *cursorX, float *cursorY )
  529. {
  530. CellGemState gemState;
  531. float filter_lowpass_alpha = 0.1f; //parameter for low pass filter
  532. float filter_exp_moving_avg_exponent = 0.1f; //parameter for exponential moving average filter
  533. float filter_max_gyro_ang_vel = 0.1f; //parameter for gyro attenuated low pass filter
  534. int retVal = -1;
  535. FilterModeType filter_type = (FilterModeType)ps3_move_filter_method.GetInt();
  536. // Higher sampling means use more samples (more smooth and laggy).
  537. int filter_numsamples = (int)( ps3_move_cursor_sampling.GetFloat() * (float)MC_MAX_NUM_SAMPLES);
  538. if (filter_numsamples < 1)
  539. {
  540. filter_numsamples = 1;
  541. }
  542. switch (filter_type)
  543. {
  544. case NONE:
  545. retVal = cellGemGetState(gem_num, CELL_GEM_STATE_FLAG_CURRENT_TIME, CELL_GEM_LATENCY_OFFSET, &gemState);
  546. MoveKitPointerCalcPointerNormalizedRawFromState(gem_num, &gemState, cursorX, cursorY);
  547. break;
  548. case LOW_PASS:
  549. retVal = calcFilteredPointerPos_LowPass(*cursorX, *cursorY, gem_num, filter_numsamples, filter_lowpass_alpha);
  550. break;
  551. case MOVING_AVG:
  552. retVal = calcFilteredPointerPos_MovingAvg(*cursorX, *cursorY, gem_num, filter_numsamples);
  553. break;
  554. case EXP_MOVING_AVG:
  555. retVal = calcFilteredPointerPos_ExpMovingAvg(*cursorX, *cursorY, gem_num, filter_numsamples, filter_exp_moving_avg_exponent);
  556. break;
  557. case GYRO_ATTEN_LOW_PASS:
  558. retVal = calcFilteredPointerPos_GyroAttenLowPass(*cursorX, *cursorY, gem_num, filter_numsamples, filter_max_gyro_ang_vel);
  559. break;
  560. case GYRO_PREDICTOR_CORRECTOR:
  561. retVal = calcFilteredPointerPos_GyroPredictorCorrector(*cursorX, *cursorY, gem_num);
  562. break;
  563. case DISTANCE_FALLOFF:
  564. retVal = calcFilteredPointerPos_DistanceFalloff(*cursorX, *cursorY, gem_num);
  565. break;
  566. default:
  567. *cursorX = 0.0f;
  568. *cursorY = 0.0f;
  569. break;
  570. }
  571. const float fMinCursorSensitivity = mc_min_cursor_sensitivity.GetFloat();
  572. const float fCursorSensitivity = fMinCursorSensitivity + (mc_max_cursor_sensitivity.GetFloat() - fMinCursorSensitivity) * mc_cursor_sensitivity.GetFloat();
  573. (*cursorX) *= fCursorSensitivity;
  574. (*cursorY) *= fCursorSensitivity;
  575. }
  576. // the main gem update function (this can be called in a thread or directly)
  577. static void GemFrameUpdate()
  578. {
  579. // Read move controller state if camera is ok
  580. if (s_gemThreadState.m_camStatus == CELL_OK)
  581. {
  582. bool moveConnected = g_pInputSystem->IsInputDeviceConnected( INPUT_DEVICE_PLAYSTATION_MOVE );
  583. bool sharpshooterConnected = g_pInputSystem->IsInputDeviceConnected( INPUT_DEVICE_SHARPSHOOTER );
  584. if ( !moveConnected && !sharpshooterConnected )
  585. {
  586. g_pInputSystem->SetMotionControllerDeviceStatus( INPUT_DEVICE_MC_STATE_CONTROLLER_NOT_CONNECTED );
  587. }
  588. cellGemGetInfo(&s_gemThreadState.m_CellGemInfo);
  589. for (int ii=0; ii<MAX_PS3_MOVE_CONTROLLERS; ++ii)
  590. {
  591. CellGemState gemState;
  592. int32 gemStatus = cellGemGetState( ii, CELL_GEM_STATE_FLAG_CURRENT_TIME, 0, &gemState );
  593. uint64 gemStatusFlags = 0;
  594. cellGemGetStatusFlags( ii, &gemStatusFlags );
  595. if(s_gemThreadState.m_CellGemInfo.status[ii]==CELL_GEM_STATUS_READY)
  596. {
  597. // check for sharpshooter connection:
  598. if( !sharpshooterConnected && gemState.ext.status != 0 )
  599. {
  600. CheckForSharpshooterConnected( ii );
  601. }
  602. else if ( sharpshooterConnected && gemState.ext.status == 0 )
  603. {
  604. g_pInputSystem->SetInputDeviceConnected( INPUT_DEVICE_SHARPSHOOTER, false );
  605. Msg("Sharp Shooter is disconnected!\n");
  606. }
  607. if( !moveConnected )
  608. g_pInputSystem->SetInputDeviceConnected( INPUT_DEVICE_PLAYSTATION_MOVE, true );
  609. if (gemStatus == CELL_GEM_HUE_NOT_SET)
  610. {
  611. // if the gem needs a color and the sphere calibration is complete
  612. // pick a color (see http://en.wikipedia.org/wiki/Hue for 0-360 hue value description)
  613. #define HUE_BLUE 200
  614. unsigned int hues[] = {HUE_BLUE,HUE_BLUE,HUE_BLUE,HUE_BLUE};
  615. cellGemTrackHues(hues, NULL);
  616. }
  617. if (gemStatus == CELL_GEM_SPHERE_NOT_CALIBRATED )
  618. {
  619. if ( g_pInputSystem->GetMotionControllerDeviceStatus() != INPUT_DEVICE_MC_STATE_CONTROLLER_ERROR )
  620. {
  621. g_pInputSystem->SetMotionControllerDeviceStatus( INPUT_DEVICE_MC_STATE_CONTROLLER_NOT_CALIBRATED );
  622. }
  623. }
  624. if (gemStatus == CELL_GEM_SPHERE_CALIBRATING) { // several frames are needed to finish calibration
  625. // several frames are needed to finish calibration
  626. FlashSphere( ii );
  627. g_pInputSystem->SetMotionControllerDeviceStatus( INPUT_DEVICE_MC_STATE_CONTROLLER_CALIBRATING );
  628. }
  629. if ( gemStatus == CELL_OK )
  630. {
  631. g_pInputSystem->SetMotionControllerDeviceStatus( INPUT_DEVICE_MC_STATE_OK );
  632. }
  633. //// handle buttons ////
  634. uint16 lastpadbuttons = s_gemThreadState.m_aCellGemState[ii].pad.digitalbuttons;
  635. uint16 pressedbuttons = gemState.pad.digitalbuttons & ~lastpadbuttons;
  636. if ( pressedbuttons & CELL_GEM_CTRL_MOVE &&
  637. gemStatus != CELL_OK )
  638. { // Calibrates when pointing at the camera if it's not already calibrated
  639. //DevMsg("gem calibration started...\n");
  640. // dkorus note: we should handle failure conditions on the PS move for PSMove calibration
  641. cellGemCalibrate(0);
  642. }
  643. }
  644. else if( g_pInputSystem->IsInputDeviceConnected( INPUT_DEVICE_PLAYSTATION_MOVE ) )
  645. {
  646. g_pInputSystem->SetInputDeviceConnected( INPUT_DEVICE_PLAYSTATION_MOVE, false );
  647. // [dkorus] without the MOVE, we certainly can't have the sharpshooter connected
  648. g_pInputSystem->SetInputDeviceConnected( INPUT_DEVICE_SHARPSHOOTER, false );
  649. }
  650. if ( g_pInputSystem->GetMotionControllerDeviceStatus() == INPUT_DEVICE_MC_STATE_OK )
  651. {
  652. // Do not display these notices while calibrating
  653. if ( (s_gemThreadState.m_aCellGemState[ii].tracking_flags & CELL_GEM_TRACKING_FLAG_VISIBLE) !=
  654. ( gemState.tracking_flags & CELL_GEM_TRACKING_FLAG_VISIBLE ) )
  655. {
  656. // if status changes, send over a message
  657. InputEvent_t event;
  658. memset( &event, 0, sizeof(event) );
  659. event.m_nTick = g_pInputSystem->GetPollTick();
  660. event.m_nType = IE_PS_Move_OutOfView;
  661. event.m_nData = gemState.tracking_flags & CELL_GEM_TRACKING_FLAG_VISIBLE;
  662. g_pInputSystem->PostUserEvent( event );
  663. // handle the red light if we're using the sharpshooter or move
  664. if ( g_pInputSystem->GetCurrentInputDevice() == INPUT_DEVICE_PLAYSTATION_MOVE ||
  665. g_pInputSystem->GetCurrentInputDevice() == INPUT_DEVICE_SHARPSHOOTER )
  666. {
  667. bool redLightOn = false;
  668. if ( !event.m_nData )
  669. redLightOn = true;
  670. // set m_nData to 1 (TRUE) if in view
  671. cellCameraSetAttribute( 0, CELL_CAMERA_LED, ( int ) redLightOn, 0 );
  672. }
  673. }
  674. }
  675. if ( gemStatusFlags & CELL_GEM_FLAG_CALIBRATION_OCCURRED )
  676. {
  677. if ( gemStatusFlags & CELL_GEM_FLAG_CALIBRATION_FAILED_CANT_FIND_SPHERE ||
  678. gemStatusFlags & CELL_GEM_FLAG_CALIBRATION_FAILED_MOTION_DETECTED ||
  679. gemStatusFlags & CELL_GEM_FLAG_CALIBRATION_FAILED_BRIGHT_LIGHTING )
  680. {
  681. g_pInputSystem->SetMotionControllerDeviceStatus( INPUT_DEVICE_MC_STATE_CONTROLLER_ERROR );
  682. }
  683. }
  684. s_gemThreadState.m_aStatus[ii] = gemStatus;
  685. s_gemThreadState.m_aStatusFlags[ii] = gemStatusFlags;
  686. s_gemThreadState.m_aCellGemState[ii] = gemState;
  687. if ( s_nCalibrationStep == 4 )
  688. {
  689. // We've calibrated the cursor, so now we can filter it.
  690. GetFilteredPointerPosition( ii, &s_gemThreadState.m_posX[ii], &s_gemThreadState.m_posY[ii] );
  691. }
  692. else
  693. {
  694. VmathVector3 position;
  695. VmathQuat orientation;
  696. vmathV3MakeFrom128(&position, s_gemThreadState.m_aCellGemState[ii].pos);
  697. vmathQMakeFrom128(&orientation, s_gemThreadState.m_aCellGemState[ii].quat);
  698. MoveKitPointerIntersectWithTrackerPlane( position, orientation, &s_gemThreadState.m_posX[ii], &s_gemThreadState.m_posY[ii] );
  699. }
  700. }
  701. }
  702. else
  703. {
  704. g_pInputSystem->SetMotionControllerDeviceStatus( INPUT_DEVICE_MC_STATE_CAMERA_NOT_CONNECTED );
  705. }
  706. }
  707. static void UpdateGemThread(uint64 args)
  708. {
  709. sys_ipc_key_t ipckey = 0xabcdefab;
  710. sys_event_queue_t eventQueue;
  711. sys_event_queue_attribute_t evattr;
  712. sys_event_queue_attribute_initialize(evattr);
  713. sys_event_queue_create(&eventQueue, &evattr, ipckey, 10);
  714. cellCameraSetNotifyEventQueue2(ipckey, SYS_EVENT_PORT_NO_NAME, CELL_CAMERA_EFLAG_FRAME_UPDATE);
  715. while(!s_bGemThreadExit)
  716. {
  717. sys_event_t event;
  718. // timeout after 4 frames
  719. int receive_ret = sys_event_queue_receive(eventQueue, &event, 4*1000000/60);
  720. //Assert(receive_ret == CELL_OK);
  721. // if(receive_ret == ETIMEDOUT) {
  722. // Msg("ERROR: UpdateGemThread timeout!\n");
  723. // } else if(receive_ret != CELL_OK) {
  724. // Msg("ERROR: UpdateGemThread failed\n");
  725. // }
  726. if (receive_ret == CELL_OK)
  727. {
  728. // read the camera
  729. CellCameraReadEx camReadEx;
  730. int32 camStatus = ReadCamera(&camReadEx);
  731. if ( s_gemThreadState.m_camStatus != camStatus )
  732. {
  733. // if status changes, send over a message
  734. InputEvent_t event;
  735. memset( &event, 0, sizeof(event) );
  736. event.m_nTick = g_pInputSystem->GetPollTick();
  737. event.m_nType = IE_PS_CameraUnplugged;
  738. event.m_nData = camStatus;
  739. g_pInputSystem->PostUserEvent( event );
  740. }
  741. // analyze the image / update gem (do this regardless of the pseye status)
  742. CellCameraInfoEx camera_info;
  743. camera_info.buffer=0; // set to NULL just in case the camera isn't ready
  744. cellCameraGetBufferInfoEx(0,&camera_info);
  745. cellGemUpdateStart(camera_info.buffer, camReadEx.timestamp);
  746. cellGemUpdateFinish();
  747. s_gemThreadState.m_camStatus = camStatus;
  748. }
  749. }
  750. cellCameraRemoveNotifyEventQueue2(ipckey);
  751. sys_event_queue_destroy(eventQueue, 0);
  752. sys_ppu_thread_exit(0);
  753. }
  754. static void EndUpdateGemThread()
  755. {
  756. s_bGemThreadExit = true;
  757. uint64 threadRet;
  758. sys_ppu_thread_join(s_gemThread, &threadRet);
  759. }
  760. void CMoveController::Init()
  761. {
  762. MEM_ALLOC_CREDIT();
  763. // Init PS Eye lib
  764. cellSysmoduleLoadModule( CELL_SYSMODULE_CAMERA );
  765. cellCameraInit();
  766. // Load Move controller lib and alloc memory for it
  767. // (but can't complete init until SPURS instance is available)
  768. cellSysmoduleLoadModule( CELL_SYSMODULE_GEM );
  769. m_iSizeGemMem = cellGemGetMemorySize(MAX_PS3_MOVE_CONTROLLERS);
  770. m_pGemMem = malloc(m_iSizeGemMem);
  771. // Move controller lib requires a SPURS instance, so register with VJobs
  772. g_pVJobs->Register( this );
  773. }
  774. void CMoveController::Shutdown()
  775. {
  776. g_pVJobs->Unregister( this );
  777. free(m_pGemMem);
  778. m_pGemMem = NULL;
  779. cellCameraEnd();
  780. cellSysmoduleUnloadModule(CELL_SYSMODULE_CAMERA);
  781. }
  782. // note: rumbleVal should represent between 0 and 255 to match cellGemSetRumble
  783. void CMoveController::Rumble( unsigned char rumbleVal )
  784. {
  785. for ( int ii = 0; ii < MAX_PS3_MOVE_CONTROLLERS; ++ii )
  786. {
  787. int32_t result = cellGemSetRumble( ii, rumbleVal );
  788. if ( result == CELL_GEM_ERROR_INVALID_PARAMETER )
  789. {
  790. Warning( "CMoveController::Rumble invalid paramater for rumble \n" );
  791. }
  792. }
  793. }
  794. void CMoveController::OnVjobsInit()
  795. {
  796. // Init move controller lib using VJobs SPURS instance
  797. CellGemAttribute gem_attr;
  798. cellGemAttributeInit(&gem_attr, 1, m_pGemMem, &m_pRoot->m_spurs, ( uint8_t* )&m_pRoot->m_nGemWorkloadPriority);
  799. int res = cellGemInit(&gem_attr);
  800. Assert(res == CELL_OK);
  801. if (res!= CELL_OK) Msg("Error on cellGemInit %d", res);
  802. // Start Gem update thread (must run at 60Hz for accurate tracking performance)
  803. memset(&s_gemThreadState,0,sizeof(s_gemThreadState));
  804. s_gemThreadState.m_camStatus=CELL_CAMERA_ERROR_NOT_OPEN;
  805. s_gemThread = 0;
  806. m_bEnabled = false;
  807. if(ps3_move_enabled.GetBool())
  808. {
  809. StartUpdateGemThread(&s_gemThreadState);
  810. m_bEnabled = true;
  811. }
  812. }
  813. void CMoveController::OnVjobsShutdown()
  814. {
  815. EndUpdateGemThread();
  816. m_bEnabled = false;
  817. // End move controller lib
  818. cellGemEnd();
  819. cellSysmoduleUnloadModule(CELL_SYSMODULE_GEM);
  820. }
  821. void CMoveController::ReadState( MoveControllerState* pState )
  822. {
  823. GemFrameUpdate();
  824. pState->m_CellGemInfo = s_gemThreadState.m_CellGemInfo;
  825. pState->m_camStatus = s_gemThreadState.m_camStatus;
  826. for(int ii=0; ii< MAX_PS3_MOVE_CONTROLLERS; ++ii)
  827. {
  828. pState->m_aCellGemState[ii] = s_gemThreadState.m_aCellGemState[ii];
  829. pState->m_aStatus[ii] = s_gemThreadState.m_aStatus[ii];
  830. pState->m_aStatusFlags[ii] = s_gemThreadState.m_aStatusFlags[ii];
  831. pState->m_pos[ii] = s_gemThreadState.m_pos[ii];
  832. pState->m_quat[ii] = s_gemThreadState.m_quat[ii];
  833. pState->m_posX[ii] = s_gemThreadState.m_posX[ii];
  834. pState->m_posY[ii] = s_gemThreadState.m_posY[ii];
  835. }
  836. }
  837. //--------------------------------------------------------------------------------------------------
  838. // Disable (for debugging)
  839. //--------------------------------------------------------------------------------------------------
  840. void CMoveController::Disable()
  841. {
  842. if(m_bEnabled)
  843. {
  844. EndUpdateGemThread();
  845. m_bEnabled = false;
  846. }
  847. }
  848. //--------------------------------------------------------------------------------------------------
  849. // Enable (for debugging)
  850. //--------------------------------------------------------------------------------------------------
  851. void CMoveController::Enable()
  852. {
  853. if(!m_bEnabled)
  854. {
  855. StartUpdateGemThread(&s_gemThreadState);
  856. m_bEnabled = true;
  857. }
  858. }
  859. void CMoveController::InvalidateCalibration( void )
  860. {
  861. for ( int ii = 0; ii < MAX_PS3_MOVE_CONTROLLERS; ++ii )
  862. {
  863. cellGemClearStatusFlags( ii, CELL_GEM_ALL_FLAGS );
  864. cellGemInvalidateCalibration( ii );
  865. cellGemReset( ii );
  866. g_pInputSystem->SetMotionControllerDeviceStatus( INPUT_DEVICE_MC_STATE_CAMERA_NOT_CONNECTED );
  867. }
  868. ResetMotionControllerScreenCalibration();
  869. }
  870. void CMoveController::ResetMotionControllerScreenCalibration( void )
  871. {
  872. s_nCalibrationStep = 0;
  873. }
  874. void CMoveController::StepMotionControllerCalibration( void )
  875. {
  876. float pointerX = 0.0;
  877. float pointerY = 0.0;
  878. VmathVector3 position;
  879. VmathQuat orientation;
  880. MoveControllerState currentState;
  881. ReadState( &currentState );
  882. switch ( s_nCalibrationStep )
  883. {
  884. case 0: // left
  885. s_tracker_plane_extents[0].left = currentState.m_posX[0];
  886. break;
  887. case 1: // right
  888. s_tracker_plane_extents[0].right = currentState.m_posX[0];
  889. break;
  890. case 2: // bottom
  891. s_tracker_plane_extents[0].bottom = currentState.m_posY[0];
  892. break;
  893. case 3: // top
  894. s_tracker_plane_extents[0].top = currentState.m_posY[0];
  895. break;
  896. default:
  897. AssertMsg( false, "Invalid step.\n" );
  898. break;
  899. }
  900. if ( s_nCalibrationStep <= 3 )
  901. {
  902. s_nCalibrationStep++;
  903. }
  904. }