BilliardView.cpp
上传用户:owen_mei
上传日期:2022-08-09
资源大小:2263k
文件大小:32k
源码类别:

OpenGL

开发平台:

Visual C++

  1. // BilliardView.cpp : implementation of the CBilliardView class
  2. //
  3. #include "stdafx.h"
  4. #include "Billiard.h"
  5. #include "BilliardDoc.h"
  6. #include "BilliardView.h"
  7. #include "model.h"
  8. #ifdef _DEBUG
  9. #define new DEBUG_NEW
  10. #undef THIS_FILE
  11. static char THIS_FILE[] = __FILE__;
  12. #endif
  13. /////////////////////////////////////////////////////////////////////////////
  14. // CBilliardView
  15. IMPLEMENT_DYNCREATE(CBilliardView, CView)
  16. BEGIN_MESSAGE_MAP(CBilliardView, CView)
  17. //{{AFX_MSG_MAP(CBilliardView)
  18. ON_WM_CREATE()
  19. ON_WM_DESTROY()
  20. ON_WM_SIZE()
  21. ON_WM_TIMER()
  22. ON_WM_LBUTTONDOWN()
  23. ON_WM_LBUTTONUP()
  24. ON_WM_MOUSEMOVE()
  25. ON_WM_RBUTTONDOWN()
  26. ON_WM_RBUTTONUP()
  27. ON_WM_KEYDOWN()
  28. //}}AFX_MSG_MAP
  29. // Standard printing commands
  30. ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
  31. ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
  32. ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
  33. END_MESSAGE_MAP()
  34. /////////////////////////////////////////////////////////////////////////////
  35. // CBilliardView construction/destruction
  36. CBilliardView::CBilliardView()
  37. {
  38. // TODO: add construction code here
  39. g_Dragging = FALSE; //  鼠标的初始状态不是拖动状态
  40. g_DrawingStick = FALSE; //  球杆在初始时不随场景一起绘制
  41. g_LastTime = 0;
  42. g_SimRunning = TRUE; //  初始时即进入游戏
  43. g_TimeIterations = 10;
  44. g_UseFixedTimeStep = FALSE; //  不使用固定的时间步长
  45. g_MaxTimeStep = 0.01f;
  46. g_CollisionRootFinding = FALSE;
  47. g_UseDamping = TRUE; // 初始时应用阻尼
  48. g_UseFriction = TRUE; // 初始时应用摩擦
  49. g_CueHitBall = FALSE; // 初始时球杆没有击中球
  50. g_BallInPlay = FALSE; // 初始时没有球在运动
  51. }
  52. CBilliardView::~CBilliardView()
  53. {
  54. }
  55. BOOL CBilliardView::PreCreateWindow(CREATESTRUCT& cs)
  56. {
  57. // TODO: Modify the Window class or styles here by modifying
  58. //  the CREATESTRUCT cs
  59. ////////////////////////////////////////////////////////////////
  60. //设置窗口类型
  61. cs.style |=WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
  62. ////////////////////////////////////////////////////////////////
  63. return CView::PreCreateWindow(cs);
  64. }
  65. /////////////////////////////////////////////////////////////////////////////
  66. // CBilliardView drawing
  67. void CBilliardView::OnDraw(CDC* pDC)
  68. {
  69. CBilliardDoc* pDoc = GetDocument();
  70. ASSERT_VALID(pDoc);
  71. // TODO: add draw code for native data here
  72. //////////////////////////////////////////////////////////////////
  73. RenderWorld(); //渲染场景
  74. //////////////////////////////////////////////////////////////////
  75. }
  76. /////////////////////////////////////////////////////////////////////////////
  77. // CBilliardView printing
  78. BOOL CBilliardView::OnPreparePrinting(CPrintInfo* pInfo)
  79. {
  80. // default preparation
  81. return DoPreparePrinting(pInfo);
  82. }
  83. void CBilliardView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
  84. {
  85. // TODO: add extra initialization before printing
  86. }
  87. void CBilliardView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
  88. {
  89. // TODO: add cleanup after printing
  90. }
  91. /////////////////////////////////////////////////////////////////////////////
  92. // CBilliardView diagnostics
  93. #ifdef _DEBUG
  94. void CBilliardView::AssertValid() const
  95. {
  96. CView::AssertValid();
  97. }
  98. void CBilliardView::Dump(CDumpContext& dc) const
  99. {
  100. CView::Dump(dc);
  101. }
  102. CBilliardDoc* CBilliardView::GetDocument() // non-debug version is inline
  103. {
  104. ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CBilliardDoc)));
  105. return (CBilliardDoc*)m_pDocument;
  106. }
  107. #endif //_DEBUG
  108. /////////////////////////////////////////////////////////////////////////////
  109. // CBilliardView message handlers
  110. int CBilliardView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  111. {
  112. if (CView::OnCreate(lpCreateStruct) == -1)
  113. return -1;
  114. // TODO: Add your specialized creation code here
  115. //////////////////////////////////////////////////////////////////
  116. //初始化OpenGL和设置定时器
  117. m_pDC = new CClientDC(this);
  118. SetTimer(1, 20, NULL);
  119. InitializeOpenGL(m_pDC);
  120. //////////////////////////////////////////////////////////////////
  121. SetupViewRC();
  122. g_LastTime = GetTime();
  123. InitRender();
  124. if(!InitGame())
  125. return -1;
  126. return 0;
  127. }
  128. void CBilliardView::OnDestroy() 
  129. {
  130. CView::OnDestroy();
  131. // TODO: Add your message handler code here
  132. /////////////////////////////////////////////////////////////////
  133. //删除调色板和渲染上下文、定时器
  134. ::wglMakeCurrent(0,0);
  135. ::wglDeleteContext( m_hRC);
  136. if (m_hPalette)
  137.     DeleteObject(m_hPalette);
  138. if ( m_pDC )
  139. {
  140. delete m_pDC;
  141. }
  142. KillTimer(1);
  143. /////////////////////////////////////////////////////////////////
  144. }
  145. void CBilliardView::OnSize(UINT nType, int cx, int cy) 
  146. {
  147. CView::OnSize(nType, cx, cy);
  148. // TODO: Add your message handler code here
  149. if(cy == 0)   // 避免被0除
  150. cy = 1;
  151. double dAspect = (double)cx/(double)cy;
  152. // 设置视口
  153.     glViewport(0, 0, cx, cy);
  154. // 设置投影矩阵
  155. glMatrixMode(GL_PROJECTION);
  156. glLoadIdentity();
  157. // 设置投影变换
  158. gluPerspective(60.0, dAspect,0.2f, 2000);
  159. //  设置模型矩阵
  160. glMatrixMode(GL_MODELVIEW);
  161. glLoadIdentity();
  162. }
  163. void CBilliardView::OnTimer(UINT nIDEvent) 
  164. {
  165. // TODO: Add your message handler code here and/or call default
  166. /////////////////////////////////////////////////////////////////
  167. //添加定时器响应函数和场景更新函数
  168. Invalidate(FALSE);
  169. /////////////////////////////////////////////////////////////////
  170. CView::OnTimer(nIDEvent);
  171. }
  172. /////////////////////////////////////////////////////////////////////
  173. //                   设置逻辑调色板
  174. //////////////////////////////////////////////////////////////////////
  175. void CBilliardView::SetLogicalPalette(void)
  176. {
  177.     struct
  178.     {
  179.         WORD Version;
  180.         WORD NumberOfEntries;
  181.         PALETTEENTRY aEntries[256];
  182.     } logicalPalette = { 0x300, 256 };
  183. BYTE reds[] = {0, 36, 72, 109, 145, 182, 218, 255};
  184. BYTE greens[] = {0, 36, 72, 109, 145, 182, 218, 255};
  185. BYTE blues[] = {0, 85, 170, 255};
  186.     for (int colorNum=0; colorNum<256; ++colorNum)
  187.     {
  188.         logicalPalette.aEntries[colorNum].peRed =
  189.             reds[colorNum & 0x07];
  190.         logicalPalette.aEntries[colorNum].peGreen =
  191.             greens[(colorNum >> 0x03) & 0x07];
  192.         logicalPalette.aEntries[colorNum].peBlue =
  193.             blues[(colorNum >> 0x06) & 0x03];
  194.         logicalPalette.aEntries[colorNum].peFlags = 0;
  195.     }
  196.     m_hPalette = CreatePalette ((LOGPALETTE*)&logicalPalette);
  197. }
  198. //////////////////////////////////////////////////////////
  199. // 初始化openGL场景
  200. //////////////////////////////////////////////////////////
  201. BOOL CBilliardView::InitializeOpenGL(CDC* pDC)
  202. {
  203. m_pDC = pDC;
  204. SetupPixelFormat();
  205. //生成绘制描述表
  206. m_hRC = ::wglCreateContext(m_pDC->GetSafeHdc());
  207. //置当前绘制描述表
  208. ::wglMakeCurrent(m_pDC->GetSafeHdc(), m_hRC);
  209. return TRUE;
  210. }
  211. //////////////////////////////////////////////////////////
  212. // 设置像素格式
  213. //////////////////////////////////////////////////////////
  214. BOOL CBilliardView::SetupPixelFormat()
  215. {
  216. PIXELFORMATDESCRIPTOR pfd = { 
  217.     sizeof(PIXELFORMATDESCRIPTOR),    // pfd结构的大小 
  218.     1,                                // 版本号 
  219.     PFD_DRAW_TO_WINDOW |              // 支持在窗口中绘图 
  220.     PFD_SUPPORT_OPENGL |              // 支持 OpenGL 
  221.     PFD_DOUBLEBUFFER,                 // 双缓存模式 
  222.     PFD_TYPE_RGBA,                    // RGBA 颜色模式 
  223.     24,                               // 24 位颜色深度 
  224.     0, 0, 0, 0, 0, 0,                 // 忽略颜色位 
  225.     0,                                // 没有非透明度缓存 
  226.     0,                                // 忽略移位位 
  227.     0,                                // 无累加缓存 
  228.     0, 0, 0, 0,                       // 忽略累加位 
  229.     32,                               // 32 位深度缓存     
  230.     0,                                // 无模板缓存 
  231.     0,                                // 无辅助缓存 
  232.     PFD_MAIN_PLANE,                   // 主层 
  233.     0,                                // 保留 
  234.     0, 0, 0                           // 忽略层,可见性和损毁掩模 
  235. }; 
  236. int pixelformat;
  237. pixelformat = ::ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd);//选择像素格式
  238. ::SetPixelFormat(m_pDC->GetSafeHdc(), pixelformat, &pfd); //设置像素格式
  239. if(pfd.dwFlags & PFD_NEED_PALETTE)
  240. SetLogicalPalette(); //设置逻辑调色板
  241. return TRUE;
  242. }
  243. //////////////////////////////////////////////////////////
  244. // 场景绘制与渲染
  245. //////////////////////////////////////////////////////////
  246. BOOL CBilliardView::RenderWorld() 
  247. {
  248. float Time;
  249. float DeltaTime;
  250.     if (g_UseFixedTimeStep) //  如果使用固定步长
  251. Time = g_LastTime + (g_MaxTimeStep * g_TimeIterations);
  252. else
  253. Time = GetTime();
  254. if (g_SimRunning)
  255. {
  256. while(g_LastTime < Time)
  257. {
  258. DeltaTime = Time - g_LastTime;
  259. if(DeltaTime > g_MaxTimeStep)
  260. {
  261. DeltaTime = g_MaxTimeStep;
  262. }
  263.   Simulate(DeltaTime,g_SimRunning);
  264. g_LastTime += DeltaTime;
  265. }
  266. g_LastTime = Time;
  267. }
  268. else
  269. {
  270. DeltaTime = 0;
  271. Simulate(DeltaTime,g_SimRunning);
  272. }
  273. ////////////////////////////////////////////////////////////////
  274. //  清除颜色缓存和深度缓存
  275. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  276. glPushMatrix();
  277. g_CueStick.yaw = -g_POV.rot.y; // 设置球杆始终位于视场中心
  278. //  设置摄像机平移
  279. glTranslatef(-g_POV.trans.x, -g_POV.trans.y, -g_POV.trans.z);
  280. //  设置摄像机旋转
  281. glRotatef(g_POV.rot.z, 0.0f, 0.0f, 1.0f);
  282. glRotatef(g_POV.rot.x, 1.0f, 0.0f, 0.0f); 
  283. glRotatef(g_POV.rot.y, 0.0f, 1.0f, 0.0f);
  284. glTranslatef(-g_CueStick.pos.x, -g_CueStick.pos.y, -g_CueStick.pos.z);
  285. RenderScene(); //  绘制静止的场景
  286. RenderCueAndBalls(); //  绘制球杆和球
  287. glPopMatrix();
  288. ::SwapBuffers(m_pDC->GetSafeHdc()); //交互缓冲区
  289. return TRUE;
  290. }
  291. void CBilliardView::OnLButtonDown(UINT nFlags, CPoint point) 
  292. {
  293. // TODO: Add your message handler code here and/or call default
  294. g_Dragging = TRUE; //  按下鼠标左键,将鼠标设置为拖动状态
  295. g_LastYaw = g_POV.rot.y; //  保存摄像机绕Y轴旋转的位置
  296. g_LastPitch = g_POV.rot.x; //  保存摄像机绕X轴旋转的位置
  297. g_MouseHitX = point.x; //  鼠标单击的X坐标 
  298. g_MouseHitY = point.y; //  鼠标单击的Y坐标 
  299. // 设置球杆的位置
  300. g_CueStick.pos.x = g_CurrentSys->pos.x;
  301. g_CueStick.pos.z = g_CurrentSys->pos.z;
  302. SetCapture();
  303. CView::OnLButtonDown(nFlags, point);
  304. }
  305. void CBilliardView::OnLButtonUp(UINT nFlags, CPoint point) 
  306. {
  307. // TODO: Add your message handler code here and/or call default
  308. g_Dragging = FALSE; //  释放鼠标左键,解除鼠标的拖动状态
  309. ReleaseCapture();
  310. CView::OnLButtonUp(nFlags, point);
  311. }
  312. void CBilliardView::OnMouseMove(UINT nFlags, CPoint point) 
  313. {
  314. // TODO: Add your message handler code here and/or call default
  315. tx = point.x; //  记下当前鼠标的X坐标 
  316. ty = point.y; //  记下当前鼠标的Y坐标 
  317. if (g_Dragging) //  当鼠标处于拖动状态
  318. {
  319. if (tx != g_MouseHitX)
  320. {
  321. //  摄像机绕Y轴旋转
  322. g_POV.rot.y = g_LastYaw + (float)(tx - g_MouseHitX);
  323. Invalidate(FALSE);;
  324. }
  325. if (ty != g_MouseHitY)
  326. {
  327. //  摄像机绕X轴旋转
  328. g_POV.rot.x = g_LastPitch + (float)(ty - g_MouseHitY);
  329. if (g_POV.rot.x < 0.0f) 
  330. g_POV.rot.x = 0.0f;
  331. if (g_POV.rot.x > 90.0f) 
  332. g_POV.rot.x = 90.0f;
  333. Invalidate(FALSE);;
  334. }
  335. }
  336. // 如果球杆处于绘制状态(即拖动是鼠标右键),并且球的运动没有结束
  337. if (g_DrawingStick && !g_BallInPlay)
  338. {
  339. if (ty != g_MouseHitY)
  340. {
  341. g_CueStick.draw = g_LastDraw + ((float)(ty - g_MouseHitY) * 0.1f);
  342. // 向后移动球杆
  343. if (g_LastDraw < g_CueStick.draw)
  344. {
  345. g_CueStick.old_draw = g_CueStick.draw;
  346. g_CueStick.drawtime = GetTime();
  347. }
  348. // 如果球杆接触到球,则击中球
  349. else if (g_CueStick.draw < 0.0f)
  350. {
  351. g_CueHitBall = TRUE; // 设置球击中标志为TRUE
  352. magnitude = -CUE_STICK_FORCE * ((g_CueStick.old_draw - g_CueStick.draw) / (GetTime() - g_CueStick.drawtime));
  353. g_CueForce.x = magnitude * sin(DEGTORAD(g_CueStick.yaw));
  354. g_CueForce.z = magnitude * cos(DEGTORAD(g_CueStick.yaw));
  355. g_CueStick.draw = 0.2f;
  356. g_BallInPlay = TRUE; //  击中球后,球开始运动
  357. g_DrawingStick = FALSE; //  不绘制球杆
  358. }
  359. Invalidate(FALSE);;
  360. }
  361. }
  362. CView::OnMouseMove(nFlags, point);
  363. }
  364. void CBilliardView::OnRButtonDown(UINT nFlags, CPoint point) 
  365. {
  366. // TODO: Add your message handler code here and/or call default
  367. g_DrawingStick = TRUE; //  绘制球杆
  368. g_LastDraw = g_CueStick.draw; //  保存球杆与球之间的距离
  369. g_CueStick.old_draw = g_CueStick.draw; //  保存球杆与球之间的距离
  370. g_MouseHitX = point.x; //  鼠标单击的X坐标 
  371. g_MouseHitY = point.y; //  鼠标单击的Y坐标 
  372. // 重新设置球杆的位置
  373. g_CueStick.pos.x = g_CurrentSys->pos.x;
  374. g_CueStick.pos.z = g_CurrentSys->pos.z;
  375. SetCapture();
  376. CView::OnRButtonDown(nFlags, point);
  377. }
  378. void CBilliardView::OnRButtonUp(UINT nFlags, CPoint point) 
  379. {
  380. // TODO: Add your message handler code here and/or call default
  381. g_DrawingStick = FALSE; //  不绘制球杆
  382. ReleaseCapture();
  383. CView::OnRButtonUp(nFlags, point);
  384. }
  385. void CBilliardView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
  386. {
  387. // TODO: Add your message handler code here and/or call default
  388. switch(nChar)
  389. {
  390. case VK_UP:
  391. {
  392. if (g_POV.rot.x > 0.0f) 
  393. g_POV.rot.x -= 1.0; //  绕X轴负方向旋转
  394. Invalidate(FALSE);;
  395. break;
  396. }
  397. case VK_DOWN: 
  398. {
  399. if (g_POV.rot.x < 90.0f) 
  400. g_POV.rot.x += 1.0; //  绕X轴正方向旋转
  401. Invalidate(FALSE);;
  402. break;
  403. }
  404. case VK_LEFT:
  405. {
  406. g_POV.rot.y += 1.0; //  绕Y轴正方向旋转
  407. Invalidate(FALSE);;
  408. break;
  409. }
  410. case VK_RIGHT:
  411. {
  412. g_POV.rot.y -= 1.0; //  绕Y轴负方向旋转
  413. Invalidate(FALSE);;
  414. break;
  415. }
  416. case VK_PRIOR: // 场景放大
  417. {
  418. if (g_POV.trans.z > 1.0f)
  419. //  摄像机向z轴的负方向平移
  420. g_POV.trans.z -= 1.0;
  421. Invalidate(FALSE);;
  422. break;
  423. }
  424. case VK_NEXT: // 场景缩小
  425. {
  426. if (g_POV.trans.z < 9.0f) 
  427. //  摄像机向z轴方向平移
  428. g_POV.trans.z += 1.0;
  429. Invalidate(FALSE);;
  430. break;
  431. }
  432. }
  433. CView::OnKeyDown(nChar, nRepCnt, nFlags);
  434. }
  435. //  下面的函数的功能是对游戏进行初始化
  436. BOOL CBilliardView::InitGame(void)
  437. {
  438. g_CueStick.draw = g_CueStick.old_draw = 0.2f;
  439. g_CueStick.yaw = 0.0f;
  440. g_CueStick.pos.x = -4.0f;
  441. g_CueStick.pos.y = TABLE_POSITION;
  442. g_CueStick.pos.z = 0.0f;
  443. g_Kd = 0.02f; // 阻尼因子
  444. g_Kr_Bumper = 0.8f; // 球与边界之间碰撞的恢复系数
  445. g_Kr_Ball = 0.2f; // 球与球之间碰撞的恢复系数
  446. g_Csf = 0.2f; // 缺省的静摩擦
  447. g_Ckf = 0.1f; // 缺省的动摩擦
  448. MAKEVECTOR(g_Gravity, 0.0f, -32.0f, 0.0f)
  449. g_IntegratorType = EULER_INTEGRATOR;
  450. g_CollisionRootFinding = FALSE; // ONLY SET WHEN FINDING A COLLISION
  451. g_ContactCnt = 0;
  452. SetupBalls();
  453. g_CurrentSys = g_GameSys[0];
  454. g_TargetSys = g_GameSys[1];
  455. g_CollisionPlaneCnt = 4;
  456. // 左边的碰撞平面
  457. MAKEVECTOR(g_CollisionPlane[0].normal,1.0f, 0.0f, 0.0f)
  458. g_CollisionPlane[0].d = LEFT_BUMPER;
  459. // 右边的碰撞平面
  460. MAKEVECTOR(g_CollisionPlane[1].normal,-1.0f, 0.0f, 0.0f)
  461. g_CollisionPlane[1].d = RIGHT_BUMPER;
  462. // 上边的碰撞平面
  463. MAKEVECTOR(g_CollisionPlane[2].normal, 0.0f, 0.0f, -1.0f)
  464. g_CollisionPlane[2].d = TOP_BUMPER;
  465. // 下边的碰撞平面
  466. MAKEVECTOR(g_CollisionPlane[3].normal, 0.0f, 0.0f, 1.0f)
  467. g_CollisionPlane[3].d = BOTTOM_BUMPER;
  468. return TRUE;
  469. }
  470. void CBilliardView::InitRender(void)
  471. {
  472. int loop;
  473. // 初始化摄像机的位置
  474. g_POV.trans.x = 0.0f;
  475. g_POV.trans.y = 0.0f;
  476. g_POV.trans.z = 8.0f;
  477. g_POV.rot.x = 20.0f;
  478. g_POV.rot.y = 90.0f;
  479. g_POV.rot.z = 0.0f;
  480. //  定义白球的显示列表
  481. glNewList(OGL_WBALL_DLIST,GL_COMPILE);
  482. // 定义数据序列
  483. glInterleavedArrays(WBALLFORMAT,0,(GLvoid *)&WBALLMODEL);
  484. glBegin(GL_TRIANGLES);
  485. for (loop = 0; loop < WBALLPOLYCNT * 3; loop++)
  486. {
  487. glArrayElement(loop);
  488. }
  489. glEnd();
  490. glEndList();
  491. //  定义黄球的显示列表
  492. glNewList(OGL_YBALL_DLIST,GL_COMPILE);
  493. // 定义数据序列
  494. glInterleavedArrays(YBALLFORMAT,0,(GLvoid *)&YBALLMODEL);
  495. glBegin(GL_TRIANGLES);
  496. for (loop = 0; loop < YBALLPOLYCNT * 3; loop++)
  497. {
  498. glArrayElement(loop);
  499. }
  500. glEnd();
  501. glEndList();
  502. //  定义球杆的显示列表
  503. glNewList(OGL_CUE_DLIST,GL_COMPILE);
  504. // 定义数据序列
  505. glInterleavedArrays(CUEFORMAT,0,(GLvoid *)&CUEMODEL);
  506. glBegin(GL_TRIANGLES);
  507. for (loop = 0; loop < CUEPOLYCNT * 3; loop++)
  508. {
  509. glArrayElement(loop);
  510. }
  511. glEnd();
  512. glEndList();
  513. LoadTextures();
  514. LoadSceneFile("Pool.ros"); // 装入场景数据
  515. }
  516. //  下面的函数的功能是将所有的TGA图像数据读入到纹理中
  517. void CBilliardView::LoadTextures()
  518. {
  519. GLubyte *rgb;
  520. char texName[80];
  521. tTGAHeader_s tgaHeader;
  522. HANDLE specHandle;
  523. WIN32_FIND_DATA fileData;
  524. int rv;
  525. g_TextureCnt = 0;
  526. sprintf(texName,"%s*.tga",ART_PATH);
  527. if ((specHandle=FindFirstFile(texName,&fileData))!= INVALID_HANDLE_VALUE)
  528. {
  529. do
  530. {
  531. sprintf(g_TexPool[g_TextureCnt].map,"%s%s",ART_PATH,fileData.cFileName);
  532. glGenTextures(1,&g_TexPool[g_TextureCnt].glTex);
  533. rgb = m_LoadTGA.LoadTGAFile( g_TexPool[g_TextureCnt].map,&tgaHeader);
  534. if (rgb == NULL)
  535. {
  536. MessageBox("Unable to Open File...",g_TexPool[g_TextureCnt].map,MB_OK);
  537. g_TexPool[g_TextureCnt].glTex = 0;
  538. return;
  539. }
  540. glBindTexture(GL_TEXTURE_2D, g_TexPool[g_TextureCnt].glTex);
  541. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  542. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  543. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  544. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  545. //  定义2D纹理像素格式
  546. glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* Force 4-byte alignment */
  547. glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
  548. glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
  549. glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
  550. if (tgaHeader.d_pixel_size == 32)
  551. {
  552. glTexImage2D(GL_TEXTURE_2D, 0, 4, tgaHeader.d_width, tgaHeader.d_height, 0,
  553.  GL_RGBA , GL_UNSIGNED_BYTE, rgb);
  554. rv = gluBuild2DMipmaps( GL_TEXTURE_2D, 4, tgaHeader.d_width, tgaHeader.d_height, 
  555. GL_RGBA, GL_UNSIGNED_BYTE, rgb );
  556. }
  557. else
  558. {
  559. glTexImage2D(GL_TEXTURE_2D, 0, 3, tgaHeader.d_width, tgaHeader.d_height, 0,
  560.  GL_RGB, GL_UNSIGNED_BYTE, rgb);
  561. rv = gluBuild2DMipmaps( GL_TEXTURE_2D, 3, tgaHeader.d_width, tgaHeader.d_height, GL_RGB, 
  562. GL_UNSIGNED_BYTE, rgb );
  563. }
  564. //  释放位图和RGB图像数据
  565. free(rgb);
  566. g_TextureCnt++;
  567. }while (FindNextFile(specHandle,&fileData));
  568. FindClose(specHandle);
  569. }
  570. }
  571. void CBilliardView::LoadSceneFile(char *filename)
  572. {
  573. FILE *fp;
  574. char tempstr[80];
  575. t_Visual *visual;
  576. fp = fopen(filename,"rb");
  577. if (fp != NULL)
  578. {
  579. fread(tempstr,1,4,fp);
  580. if (strncmp(tempstr,"ROSC",4)!= 0)
  581. {
  582. MessageBox("Not a Valid Data File","Load File", MB_OK|MB_ICONEXCLAMATION);
  583. return;
  584. }
  585. visual = &g_Scene;
  586. fread(&visual->vertexCnt,sizeof(long),1,fp);
  587. visual->vertex = (tVector *)malloc(sizeof(tVector) * visual->vertexCnt);
  588. fread(visual->vertex,sizeof(tVector),visual->vertexCnt,fp);
  589. fread(&visual->triCnt,sizeof(long),1,fp);
  590. visual->tri = (tPrimPoly *)malloc(sizeof(tPrimPoly) * (visual->triCnt));
  591. fread(visual->tri,sizeof(tPrimPoly),visual->triCnt,fp);
  592. fread(&visual->quadCnt,sizeof(long),1,fp);
  593. visual->quad = (tPrimPoly *)malloc(sizeof(tPrimPoly) * (visual->quadCnt));
  594. fread(visual->quad,sizeof(tPrimPoly),visual->quadCnt,fp);
  595. fclose(fp);
  596. }
  597. }
  598. //  设置OpenGL绘制环境
  599. void CBilliardView::SetupViewRC(void)
  600. {
  601. // 设置黑色背景
  602. glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
  603. glCullFace(GL_BACK);
  604. glFrontFace(GL_CCW);
  605. glEnable(GL_CULL_FACE);
  606. // 使用深度测试
  607. glEnable(GL_DEPTH_TEST);
  608. glDisable(GL_TEXTURE_2D);
  609. // 无光照
  610. glDisable(GL_LIGHTING);
  611. glDisable(GL_LIGHT0);
  612. glPolygonMode(GL_FRONT,GL_FILL);
  613. glDepthFunc(GL_LESS);
  614. glEnable(GL_BLEND);
  615. glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
  616. glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  617.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  618.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  619. }
  620. void CBilliardView::RenderCueAndBalls()
  621. {
  622. // 绘制白球
  623. glPushMatrix();
  624. glTranslatef(g_CurrentSys[0].pos.x,g_CurrentSys[0].pos.y,g_CurrentSys[0].pos.z);
  625. glCallList(OGL_WBALL_DLIST);
  626. glPopMatrix();
  627. // 绘制黄球
  628. glPushMatrix();
  629. glTranslatef(g_CurrentSys[1].pos.x,g_CurrentSys[1].pos.y,g_CurrentSys[1].pos.z);
  630. glCallList(OGL_YBALL_DLIST);
  631. glPopMatrix();
  632. // 绘制球杆
  633. glPushMatrix();
  634. //  球杆的位置
  635. glTranslatef(g_CueStick.pos.x,g_CueStick.pos.y,g_CueStick.pos.z);
  636. //  绕Y轴旋转
  637. glRotatef(g_CueStick.yaw, 0.0f, 1.0f, 0.0f); 
  638. //  绕X轴旋转一个固定角度,即略向上抬
  639. glRotatef(-5.0f, 1.0f, 0.0f, 0.0f);
  640. //  击球时向后移动球杆
  641. glTranslatef(0,0,g_CueStick.draw);
  642. glCallList(OGL_CUE_DLIST);
  643. glPopMatrix();
  644. }
  645. void CBilliardView::RenderScene()
  646. {
  647.     int loop,loop2;
  648. t_Visual *visual;
  649. tPrimPoly *poly;
  650. glEnable(GL_TEXTURE_2D);
  651. glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  652. visual = &g_Scene;
  653. for (loop2 = 0; loop2 < g_TextureCnt; loop2++)
  654. {
  655. poly = visual->quad;
  656. glBindTexture(GL_TEXTURE_2D, g_TexPool[loop2].glTex);
  657. for (loop = 0; loop < visual->quadCnt; loop++)
  658. {
  659. if ((poly->type & POLY_TEXTURED) > 0 && poly->TexNdx1 == (uint)loop2)
  660. {
  661. glBegin(GL_QUADS);
  662. glTexCoord2fv((float *)&poly->t1[0]);
  663. glColor3ubv((unsigned char *)&poly->color[0]);
  664. glVertex3fv((float *)&visual->vertex[poly->index[0]]);
  665. glTexCoord2fv((float *)&poly->t1[1]);
  666. glColor3ubv((unsigned char *)&poly->color[1]);
  667. glVertex3fv((float *)&visual->vertex[poly->index[1]]);
  668. glTexCoord2fv((float *)&poly->t1[2]);
  669. glColor3ubv((unsigned char *)&poly->color[2]);
  670. glVertex3fv((float *)&visual->vertex[poly->index[2]]);
  671. glTexCoord2fv((float *)&poly->t1[3]);
  672. glColor3ubv((unsigned char *)&poly->color[3]);
  673. glVertex3fv((float *)&visual->vertex[poly->index[3]]);
  674. glEnd();
  675. }
  676. poly++;
  677. }
  678. poly = visual->tri;
  679. for (loop = 0; loop < visual->triCnt; loop++)
  680. {
  681. if ((poly->type & POLY_TEXTURED) > 0 && poly->TexNdx1 == (uint)loop2)
  682. {
  683. glBegin(GL_TRIANGLES);
  684. glTexCoord2fv((float *)&poly->t1[0]);
  685. glColor3ubv((unsigned char *)&poly->color[0]);
  686. glVertex3fv((float *)&visual->vertex[poly->index[0]]);
  687. glTexCoord2fv((float *)&poly->t1[1]);
  688. glColor3ubv((unsigned char *)&poly->color[1]);
  689. glVertex3fv((float *)&visual->vertex[poly->index[1]]);
  690. glTexCoord2fv((float *)&poly->t1[2]);
  691. glColor3ubv((unsigned char *)&poly->color[2]);
  692. glVertex3fv((float *)&visual->vertex[poly->index[2]]);
  693. glEnd();
  694. }
  695. poly++;
  696. }
  697. }
  698. glDisable(GL_TEXTURE_2D);
  699. }
  700. void CBilliardView::SetupBalls()
  701. {
  702. int loop, loop2;
  703. for (loop = 0; loop < SYSTEM_COUNT; loop++)
  704. {
  705. for (loop2 = 0; loop2 < BALL_COUNT; loop2++)
  706. {
  707. switch (loop2)
  708. {
  709. // 球1是球杆需要击中的球
  710. case 0:
  711. MAKEVECTOR(g_GameSys[loop][loop2].pos,-4.0f, TABLE_POSITION, 0.0f)
  712. break;
  713. case 1:
  714. MAKEVECTOR(g_GameSys[loop][loop2].pos,4.0f, TABLE_POSITION, 0.0f)
  715. break;
  716. }
  717. MAKEVECTOR(g_GameSys[loop][loop2].v,0.0f, 0.0f, 0.0f)
  718. MAKEVECTOR(g_GameSys[loop][loop2].f,0.0f, 0.0f, 0.0f)
  719. g_GameSys[loop][loop2].oneOverM = 1.0f / 0.34f;
  720. MAKEVECTOR(g_GameSys[loop][loop2].angMom,0.0f, 0.0f, 0.0f)
  721. MAKEVECTOR(g_GameSys[loop][loop2].torque,0.0f, 0.0f, 0.0f)
  722. // 四元数清零
  723. MAKEVECTOR(g_GameSys[loop][loop2].orientation,0.0f, 0.0f, 0.0f)
  724. g_GameSys[loop][loop2].orientation.w = 1.0f;
  725. g_GameSys[loop][loop2].flags = 0;
  726. g_GameSys[loop][loop2].flags = 0;
  727. }
  728. }
  729. }
  730. #define STATIC_THRESHOLD 0.03f //  速度的阈值,用于判断使用何种摩擦
  731. void CBilliardView::ComputeForces( t_Ball *system)
  732. {
  733. int loop;
  734. t_Ball *curBall;
  735. tVector contactN;
  736. float FdotN,VdotN,Vmag;
  737. tVector Vn,Vt;
  738. curBall = system;
  739. for (loop = 0; loop < BALL_COUNT; loop++)
  740. {
  741. MAKEVECTOR(curBall->f,0.0f,0.0f,0.0f) // 清除力
  742. MAKEVECTOR(curBall->torque,0.0f, 0.0f, 0.0f) // 清除力矩
  743. if (g_UseDamping) //  如果用户定义阻尼
  744. {
  745. curBall->f.x += (-g_Kd * curBall->v.x);
  746. curBall->f.y += (-g_Kd * curBall->v.y);
  747. curBall->f.z += (-g_Kd * curBall->v.z);
  748. }
  749. else //  否则使用缺省阻尼
  750. {
  751. curBall->f.x += (-DEFAULT_DAMPING * curBall->v.x);
  752. curBall->f.y += (-DEFAULT_DAMPING * curBall->v.y);
  753. curBall->f.z += (-DEFAULT_DAMPING * curBall->v.z);
  754. }
  755. //  下面处理球与桌面之间的摩擦问题
  756. if (g_UseFriction)
  757. {
  758. // 计算正压力,即重力
  759. FdotN = g_Gravity.y / curBall->oneOverM;
  760. // 计算垂直于法向平面的速度
  761. MAKEVECTOR(contactN,0.0f, 1.0f, 0.0f)
  762. VdotN = m_MathDefs.DotProduct(&contactN,&curBall->v);
  763. m_MathDefs.ScaleVector(&contactN, VdotN, &Vn);
  764. m_MathDefs.VectorDifference(&curBall->v, &Vn, &Vt);
  765. Vmag = m_MathDefs.VectorSquaredLength(&Vt);
  766. // 判断速度是否大于阈值
  767. if (Vmag > STATIC_THRESHOLD) // 大于阈值,使用动摩擦模型
  768. {
  769. m_MathDefs.NormalizeVector(&Vt);
  770. m_MathDefs.ScaleVector(&Vt, (FdotN * g_Ckf), &Vt);
  771. m_MathDefs.VectorSum(&curBall->f,&Vt,&curBall->f);
  772. }
  773. else // 小于阈值,使用静摩擦模型
  774. {
  775. Vmag = Vmag / STATIC_THRESHOLD;
  776. m_MathDefs.NormalizeVector(&Vt);
  777. m_MathDefs.ScaleVector(&Vt, (FdotN * g_Csf * Vmag), &Vt);  // Scale Friction by Velocity
  778. m_MathDefs.VectorSum(&curBall->f,&Vt,&curBall->f);
  779. if (loop == 0)
  780. {
  781. g_BallInPlay = FALSE;
  782. }
  783. }
  784. }
  785. curBall++;
  786. }
  787. //  用户是否定义了球杆撞击球的力
  788. if (g_CueHitBall)
  789. {
  790. m_MathDefs.VectorSum(&system[0].f,&g_CueForce,&system[0].f);
  791. }
  792. }   
  793. void CBilliardView::IntegrateSysOverTime(t_Ball *initial,t_Ball *source, t_Ball *target, float deltaTime)
  794. {
  795. int loop;
  796. float deltaTimeMass;
  797. for (loop = 0; loop < BALL_COUNT; loop++)
  798. {
  799. deltaTimeMass = deltaTime * initial->oneOverM;
  800. // 获得新的速度
  801. target->v.x = initial->v.x + (source->f.x * deltaTimeMass);
  802. target->v.y = initial->v.y + (source->f.y * deltaTimeMass);
  803. target->v.z = initial->v.z + (source->f.z * deltaTimeMass);
  804. target->oneOverM = initial->oneOverM;
  805. // 获得新的位置坐标
  806. target->pos.x = initial->pos.x + (deltaTime * source->v.x);
  807. target->pos.y = initial->pos.y + (deltaTime * source->v.y);
  808. target->pos.z = initial->pos.z + (deltaTime * source->v.z);
  809. initial++;
  810. source++;
  811. target++;
  812. }
  813. }
  814. //  欧拉积分
  815. void CBilliardView::EulerIntegrate( float DeltaTime)
  816. {
  817. IntegrateSysOverTime(g_CurrentSys,g_CurrentSys, g_TargetSys,DeltaTime);
  818. }
  819. //  中点方法
  820. void CBilliardView::MidPointIntegrate( float DeltaTime)
  821. {
  822. float halfDeltaT;
  823. halfDeltaT = DeltaTime / 2.0f;
  824. //  计算半步长
  825. IntegrateSysOverTime(g_CurrentSys,g_CurrentSys,&g_GameSys[2][0],halfDeltaT);
  826. //  使用新的位置和速度计算力
  827. ComputeForces(&g_GameSys[2][0]);
  828. //  计算全步长
  829. IntegrateSysOverTime(g_CurrentSys,&g_GameSys[2][0],g_TargetSys,DeltaTime);
  830. }
  831. int CBilliardView::CheckForCollisions( t_Ball *system )
  832. {
  833. int collisionState = NOT_COLLIDING;
  834.     float const depthEpsilon = 0.001f;
  835.     float const ballDepthEpsilon = 0.0001f;
  836. int loop,loop2,planeIndex;
  837. t_Ball *curBall,*ball2;
  838. t_CollisionPlane *plane;
  839. float axbyczd,dist,relativeVelocity;
  840. tVector distVect;
  841. g_ContactCnt = 0; // 目前的碰撞次数为0
  842. curBall = system;
  843. for (loop = 0; 
  844. (loop < BALL_COUNT) && (collisionState != PENETRATING); 
  845. loop++,curBall++)
  846. {
  847. //  判断球与边界面之间的碰撞
  848.         for(planeIndex = 0;
  849. (planeIndex < g_CollisionPlaneCnt) && (collisionState != PENETRATING);
  850. planeIndex++)
  851.         {
  852. plane = &g_CollisionPlane[planeIndex];
  853.             axbyczd = m_MathDefs.DotProduct(&curBall->pos,&plane->normal) + plane->d;
  854.             if(axbyczd < -depthEpsilon)
  855.             {
  856. // 任何球穿过边界面,退出循环
  857. collisionState = PENETRATING;
  858.             }
  859.             else
  860.             if(axbyczd < depthEpsilon)
  861.             {
  862.                 relativeVelocity = m_MathDefs.DotProduct(&plane->normal,&curBall->v);
  863. if(relativeVelocity < 0.0f)
  864.                 {
  865.                     collisionState = COLLIDING_WITH_WALL;
  866. g_Contact[g_ContactCnt].type = COLLIDING_WITH_WALL;
  867. g_Contact[g_ContactCnt].ball = loop; 
  868. g_Contact[g_ContactCnt].Kr = g_Kr_Bumper; // Ball to bumper Kr
  869. memcpy(&g_Contact[g_ContactCnt].normal,&plane->normal,sizeof(tVector));
  870. g_ContactCnt++;
  871.                 }
  872.             }
  873.         }
  874. // 现在判断球与球之间的碰撞  
  875. ball2 = system;
  876. for (loop2 = 0; (loop2 < BALL_COUNT) && (collisionState != PENETRATING); 
  877. loop2++,ball2++)
  878. {
  879. if (loop2 == loop) continue; // 不与自身比较
  880. if (curBall->flags) continue; // 如果当前球是碰撞球
  881. m_MathDefs.VectorDifference(&curBall->pos, &ball2->pos, &distVect);
  882. //  获得两球心之间的距离
  883. dist = m_MathDefs.VectorSquaredLength(&distVect);
  884. dist = dist - (BALL_DIAMETER * BALL_DIAMETER);  
  885. //  判断两球之间是否穿过
  886. if(dist < -ballDepthEpsilon)
  887. {
  888. // 如果穿过,则退出
  889. collisionState = PENETRATING;
  890. }
  891. else
  892. if(dist < ballDepthEpsilon)
  893. {
  894. m_MathDefs.NormalizeVector(&distVect);
  895. //  获得相对速度
  896. relativeVelocity = m_MathDefs.DotProduct(&distVect,&curBall->v);
  897. //  如果相对速度小于0
  898. if(relativeVelocity < 0.0f)
  899. {
  900. collisionState = COLLIDING_WITH_BALL;
  901. g_Contact[g_ContactCnt].type = COLLIDING_WITH_BALL;
  902. g_Contact[g_ContactCnt].ball = loop; 
  903. g_Contact[g_ContactCnt].ball2 = loop2; 
  904. g_Contact[g_ContactCnt].Kr = g_Kr_Ball;
  905. memcpy(&g_Contact[g_ContactCnt].normal,&distVect,sizeof(tVector));
  906. ball2->flags = 1;
  907. g_ContactCnt++;
  908. }
  909. }
  910. }
  911. }
  912.     return collisionState;
  913. }
  914. void CBilliardView::ResolveCollisions( t_Ball  *system )
  915. {
  916. t_Contact *contact;
  917. t_Ball *ball,*ball2;
  918. float VdotN;
  919. tVector Vn,Vt,Vn1;
  920. int loop;
  921. contact = g_Contact;
  922. for (loop = 0; loop < g_ContactCnt; loop++,contact++)
  923. {
  924. ball = &system[contact->ball];
  925. // 计算 Vn
  926. VdotN = m_MathDefs.DotProduct(&contact->normal,&ball->v);
  927. m_MathDefs.ScaleVector(&contact->normal, VdotN, &Vn);
  928. // 计算 Vt
  929. m_MathDefs.VectorDifference(&ball->v, &Vn, &Vt);
  930. // 判断碰撞类型
  931. if (contact->type == COLLIDING_WITH_WALL) //  如果球与边界碰撞
  932. {
  933. //  Vn乘以恢复系数
  934. m_MathDefs.ScaleVector(&Vn, contact->Kr, &Vn);
  935. //  设置新的速度
  936. m_MathDefs.VectorDifference(&Vt, &Vn, &ball->v);
  937. }
  938. else // 球与球之间碰撞
  939. {
  940. // Vn乘以恢复系数
  941. m_MathDefs.ScaleVector(&Vn, contact->Kr, &Vn1);
  942. // 设置新的速度
  943. m_MathDefs.VectorDifference(&Vt, &Vn1, &ball->v);
  944. ball2 = &system[contact->ball2];
  945. // 球2的Vn乘以恢复系数
  946. m_MathDefs.ScaleVector(&Vn, 1.0f - contact->Kr, &Vn1);
  947. // 设置球2的速度
  948. m_MathDefs.VectorSum(&Vn1,&ball2->v,&ball2->v);
  949. ball->flags = 0;
  950. ball2->flags = 0;
  951. }
  952. }
  953. }
  954. void CBilliardView::Simulate(float DeltaTime, BOOL running)
  955. {
  956.     float CurrentTime = 0.0f;
  957.     float TargetTime = DeltaTime;
  958. t_Ball *tempSys;
  959. int collisionState;
  960.     while(CurrentTime < DeltaTime)
  961.     {
  962. if (running)
  963. {
  964. //  定义各球的作用力
  965. ComputeForces(g_CurrentSys);
  966. //  如果出现碰撞穿透的现象,强制使用欧拉方法,否则提供选择
  967. if (g_CollisionRootFinding)
  968. {
  969. EulerIntegrate(TargetTime-CurrentTime);
  970. }
  971. else
  972. {
  973. switch (g_IntegratorType)
  974. {
  975. case EULER_INTEGRATOR:
  976. EulerIntegrate(TargetTime-CurrentTime);
  977. break;
  978. case MIDPOINT_INTEGRATOR:
  979. MidPointIntegrate(TargetTime-CurrentTime);
  980. break;
  981. }
  982. }
  983. }
  984. collisionState = CheckForCollisions(g_TargetSys);
  985.         if(collisionState == PENETRATING)
  986.         {
  987. // 将使用欧拉方法
  988. g_CollisionRootFinding = TRUE;
  989.             // 将时间步长缩短
  990.             TargetTime = (CurrentTime + TargetTime) / 2.0f;
  991.             assert(fabs(TargetTime - CurrentTime) > EPSILON);
  992.         }
  993.         else
  994.         {
  995.             if(collisionState == COLLIDING_WITH_WALL || collisionState == COLLIDING_WITH_BALL)
  996.             {
  997.                 int Counter = 0;
  998.                 do
  999.                 {
  1000.                     ResolveCollisions(g_TargetSys);
  1001.                     Counter++;
  1002.                 } while((CheckForCollisions(g_TargetSys) >= COLLIDING_WITH_WALL) && (Counter < 500));
  1003.                 assert(Counter < 500);
  1004. g_CollisionRootFinding = FALSE;
  1005.             }
  1006.   //  进入下一步
  1007. CurrentTime = TargetTime;
  1008. TargetTime = DeltaTime;
  1009. MAKEVECTOR(g_CueForce,0.0f,0.0f,0.0f); // 清除球杆的力
  1010. // SWAP MY TWO SYSTEM BUFFERS SO I CAN DO IT AGAIN
  1011. tempSys = g_CurrentSys;
  1012. g_CurrentSys = g_TargetSys;
  1013. g_TargetSys = tempSys;
  1014.         }
  1015.     }
  1016. }
  1017. float CBilliardView::GetTime( void )
  1018. {
  1019.     static long StartMilliseconds;
  1020. long CurrentMilliseconds;
  1021.     if(!StartMilliseconds)
  1022.     {
  1023.         StartMilliseconds = timeGetTime();
  1024.     }
  1025.     CurrentMilliseconds = timeGetTime();
  1026.     return (float)(CurrentMilliseconds - StartMilliseconds) / 1000.0f;
  1027. }