www.pudn.com > Billiard.rar > BilliardView.cpp
// BilliardView.cpp : implementation of the CBilliardView class
//
#include "stdafx.h"
#include "Billiard.h"
#include "BilliardDoc.h"
#include "BilliardView.h"
#include "model.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CBilliardView
IMPLEMENT_DYNCREATE(CBilliardView, CView)
BEGIN_MESSAGE_MAP(CBilliardView, CView)
//{{AFX_MSG_MAP(CBilliardView)
ON_WM_CREATE()
ON_WM_DESTROY()
ON_WM_SIZE()
ON_WM_TIMER()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_RBUTTONDOWN()
ON_WM_RBUTTONUP()
ON_WM_KEYDOWN()
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CBilliardView construction/destruction
CBilliardView::CBilliardView()
{
// TODO: add construction code here
g_Dragging = FALSE; // 鼠标的初始状态不是拖动状态
g_DrawingStick = FALSE; // 球杆在初始时不随场景一起绘制
g_LastTime = 0;
g_SimRunning = TRUE; // 初始时即进入游戏
g_TimeIterations = 10;
g_UseFixedTimeStep = FALSE; // 不使用固定的时间步长
g_MaxTimeStep = 0.01f;
g_CollisionRootFinding = FALSE;
g_UseDamping = TRUE; // 初始时应用阻尼
g_UseFriction = TRUE; // 初始时应用摩擦
g_CueHitBall = FALSE; // 初始时球杆没有击中球
g_BallInPlay = FALSE; // 初始时没有球在运动
}
CBilliardView::~CBilliardView()
{
}
BOOL CBilliardView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
////////////////////////////////////////////////////////////////
//设置窗口类型
cs.style |=WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
////////////////////////////////////////////////////////////////
return CView::PreCreateWindow(cs);
}
/////////////////////////////////////////////////////////////////////////////
// CBilliardView drawing
void CBilliardView::OnDraw(CDC* pDC)
{
CBilliardDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
//////////////////////////////////////////////////////////////////
RenderWorld(); //渲染场景
//////////////////////////////////////////////////////////////////
}
/////////////////////////////////////////////////////////////////////////////
// CBilliardView printing
BOOL CBilliardView::OnPreparePrinting(CPrintInfo* pInfo)
{
// default preparation
return DoPreparePrinting(pInfo);
}
void CBilliardView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add extra initialization before printing
}
void CBilliardView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add cleanup after printing
}
/////////////////////////////////////////////////////////////////////////////
// CBilliardView diagnostics
#ifdef _DEBUG
void CBilliardView::AssertValid() const
{
CView::AssertValid();
}
void CBilliardView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CBilliardDoc* CBilliardView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CBilliardDoc)));
return (CBilliardDoc*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CBilliardView message handlers
int CBilliardView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
//////////////////////////////////////////////////////////////////
//初始化OpenGL和设置定时器
m_pDC = new CClientDC(this);
SetTimer(1, 20, NULL);
InitializeOpenGL(m_pDC);
//////////////////////////////////////////////////////////////////
SetupViewRC();
g_LastTime = GetTime();
InitRender();
if(!InitGame())
return -1;
return 0;
}
void CBilliardView::OnDestroy()
{
CView::OnDestroy();
// TODO: Add your message handler code here
/////////////////////////////////////////////////////////////////
//删除调色板和渲染上下文、定时器
::wglMakeCurrent(0,0);
::wglDeleteContext( m_hRC);
if (m_hPalette)
DeleteObject(m_hPalette);
if ( m_pDC )
{
delete m_pDC;
}
KillTimer(1);
/////////////////////////////////////////////////////////////////
}
void CBilliardView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
if(cy == 0) // 避免被0除
cy = 1;
double dAspect = (double)cx/(double)cy;
// 设置视口
glViewport(0, 0, cx, cy);
// 设置投影矩阵
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// 设置投影变换
gluPerspective(60.0, dAspect,0.2f, 2000);
// 设置模型矩阵
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void CBilliardView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
/////////////////////////////////////////////////////////////////
//添加定时器响应函数和场景更新函数
Invalidate(FALSE);
/////////////////////////////////////////////////////////////////
CView::OnTimer(nIDEvent);
}
/////////////////////////////////////////////////////////////////////
// 设置逻辑调色板
//////////////////////////////////////////////////////////////////////
void CBilliardView::SetLogicalPalette(void)
{
struct
{
WORD Version;
WORD NumberOfEntries;
PALETTEENTRY aEntries[256];
} logicalPalette = { 0x300, 256 };
BYTE reds[] = {0, 36, 72, 109, 145, 182, 218, 255};
BYTE greens[] = {0, 36, 72, 109, 145, 182, 218, 255};
BYTE blues[] = {0, 85, 170, 255};
for (int colorNum=0; colorNum<256; ++colorNum)
{
logicalPalette.aEntries[colorNum].peRed =
reds[colorNum & 0x07];
logicalPalette.aEntries[colorNum].peGreen =
greens[(colorNum >> 0x03) & 0x07];
logicalPalette.aEntries[colorNum].peBlue =
blues[(colorNum >> 0x06) & 0x03];
logicalPalette.aEntries[colorNum].peFlags = 0;
}
m_hPalette = CreatePalette ((LOGPALETTE*)&logicalPalette);
}
//////////////////////////////////////////////////////////
// 初始化openGL场景
//////////////////////////////////////////////////////////
BOOL CBilliardView::InitializeOpenGL(CDC* pDC)
{
m_pDC = pDC;
SetupPixelFormat();
//生成绘制描述表
m_hRC = ::wglCreateContext(m_pDC->GetSafeHdc());
//置当前绘制描述表
::wglMakeCurrent(m_pDC->GetSafeHdc(), m_hRC);
return TRUE;
}
//////////////////////////////////////////////////////////
// 设置像素格式
//////////////////////////////////////////////////////////
BOOL CBilliardView::SetupPixelFormat()
{
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // pfd结构的大小
1, // 版本号
PFD_DRAW_TO_WINDOW | // 支持在窗口中绘图
PFD_SUPPORT_OPENGL | // 支持 OpenGL
PFD_DOUBLEBUFFER, // 双缓存模式
PFD_TYPE_RGBA, // RGBA 颜色模式
24, // 24 位颜色深度
0, 0, 0, 0, 0, 0, // 忽略颜色位
0, // 没有非透明度缓存
0, // 忽略移位位
0, // 无累加缓存
0, 0, 0, 0, // 忽略累加位
32, // 32 位深度缓存
0, // 无模板缓存
0, // 无辅助缓存
PFD_MAIN_PLANE, // 主层
0, // 保留
0, 0, 0 // 忽略层,可见性和损毁掩模
};
int pixelformat;
pixelformat = ::ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd);//选择像素格式
::SetPixelFormat(m_pDC->GetSafeHdc(), pixelformat, &pfd); //设置像素格式
if(pfd.dwFlags & PFD_NEED_PALETTE)
SetLogicalPalette(); //设置逻辑调色板
return TRUE;
}
//////////////////////////////////////////////////////////
// 场景绘制与渲染
//////////////////////////////////////////////////////////
BOOL CBilliardView::RenderWorld()
{
float Time;
float DeltaTime;
if (g_UseFixedTimeStep) // 如果使用固定步长
Time = g_LastTime + (g_MaxTimeStep * g_TimeIterations);
else
Time = GetTime();
if (g_SimRunning)
{
while(g_LastTime < Time)
{
DeltaTime = Time - g_LastTime;
if(DeltaTime > g_MaxTimeStep)
{
DeltaTime = g_MaxTimeStep;
}
Simulate(DeltaTime,g_SimRunning);
g_LastTime += DeltaTime;
}
g_LastTime = Time;
}
else
{
DeltaTime = 0;
Simulate(DeltaTime,g_SimRunning);
}
////////////////////////////////////////////////////////////////
// 清除颜色缓存和深度缓存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
g_CueStick.yaw = -g_POV.rot.y; // 设置球杆始终位于视场中心
// 设置摄像机平移
glTranslatef(-g_POV.trans.x, -g_POV.trans.y, -g_POV.trans.z);
// 设置摄像机旋转
glRotatef(g_POV.rot.z, 0.0f, 0.0f, 1.0f);
glRotatef(g_POV.rot.x, 1.0f, 0.0f, 0.0f);
glRotatef(g_POV.rot.y, 0.0f, 1.0f, 0.0f);
glTranslatef(-g_CueStick.pos.x, -g_CueStick.pos.y, -g_CueStick.pos.z);
RenderScene(); // 绘制静止的场景
RenderCueAndBalls(); // 绘制球杆和球
glPopMatrix();
::SwapBuffers(m_pDC->GetSafeHdc()); //交互缓冲区
return TRUE;
}
void CBilliardView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
g_Dragging = TRUE; // 按下鼠标左键,将鼠标设置为拖动状态
g_LastYaw = g_POV.rot.y; // 保存摄像机绕Y轴旋转的位置
g_LastPitch = g_POV.rot.x; // 保存摄像机绕X轴旋转的位置
g_MouseHitX = point.x; // 鼠标单击的X坐标
g_MouseHitY = point.y; // 鼠标单击的Y坐标
// 设置球杆的位置
g_CueStick.pos.x = g_CurrentSys->pos.x;
g_CueStick.pos.z = g_CurrentSys->pos.z;
SetCapture();
CView::OnLButtonDown(nFlags, point);
}
void CBilliardView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
g_Dragging = FALSE; // 释放鼠标左键,解除鼠标的拖动状态
ReleaseCapture();
CView::OnLButtonUp(nFlags, point);
}
void CBilliardView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
tx = point.x; // 记下当前鼠标的X坐标
ty = point.y; // 记下当前鼠标的Y坐标
if (g_Dragging) // 当鼠标处于拖动状态
{
if (tx != g_MouseHitX)
{
// 摄像机绕Y轴旋转
g_POV.rot.y = g_LastYaw + (float)(tx - g_MouseHitX);
Invalidate(FALSE);;
}
if (ty != g_MouseHitY)
{
// 摄像机绕X轴旋转
g_POV.rot.x = g_LastPitch + (float)(ty - g_MouseHitY);
if (g_POV.rot.x < 0.0f)
g_POV.rot.x = 0.0f;
if (g_POV.rot.x > 90.0f)
g_POV.rot.x = 90.0f;
Invalidate(FALSE);;
}
}
// 如果球杆处于绘制状态(即拖动是鼠标右键),并且球的运动没有结束
if (g_DrawingStick && !g_BallInPlay)
{
if (ty != g_MouseHitY)
{
g_CueStick.draw = g_LastDraw + ((float)(ty - g_MouseHitY) * 0.1f);
// 向后移动球杆
if (g_LastDraw < g_CueStick.draw)
{
g_CueStick.old_draw = g_CueStick.draw;
g_CueStick.drawtime = GetTime();
}
// 如果球杆接触到球,则击中球
else if (g_CueStick.draw < 0.0f)
{
g_CueHitBall = TRUE; // 设置球击中标志为TRUE
magnitude = -CUE_STICK_FORCE * ((g_CueStick.old_draw - g_CueStick.draw) / (GetTime() - g_CueStick.drawtime));
g_CueForce.x = magnitude * sin(DEGTORAD(g_CueStick.yaw));
g_CueForce.z = magnitude * cos(DEGTORAD(g_CueStick.yaw));
g_CueStick.draw = 0.2f;
g_BallInPlay = TRUE; // 击中球后,球开始运动
g_DrawingStick = FALSE; // 不绘制球杆
}
Invalidate(FALSE);;
}
}
CView::OnMouseMove(nFlags, point);
}
void CBilliardView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
g_DrawingStick = TRUE; // 绘制球杆
g_LastDraw = g_CueStick.draw; // 保存球杆与球之间的距离
g_CueStick.old_draw = g_CueStick.draw; // 保存球杆与球之间的距离
g_MouseHitX = point.x; // 鼠标单击的X坐标
g_MouseHitY = point.y; // 鼠标单击的Y坐标
// 重新设置球杆的位置
g_CueStick.pos.x = g_CurrentSys->pos.x;
g_CueStick.pos.z = g_CurrentSys->pos.z;
SetCapture();
CView::OnRButtonDown(nFlags, point);
}
void CBilliardView::OnRButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
g_DrawingStick = FALSE; // 不绘制球杆
ReleaseCapture();
CView::OnRButtonUp(nFlags, point);
}
void CBilliardView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
switch(nChar)
{
case VK_UP:
{
if (g_POV.rot.x > 0.0f)
g_POV.rot.x -= 1.0; // 绕X轴负方向旋转
Invalidate(FALSE);;
break;
}
case VK_DOWN:
{
if (g_POV.rot.x < 90.0f)
g_POV.rot.x += 1.0; // 绕X轴正方向旋转
Invalidate(FALSE);;
break;
}
case VK_LEFT:
{
g_POV.rot.y += 1.0; // 绕Y轴正方向旋转
Invalidate(FALSE);;
break;
}
case VK_RIGHT:
{
g_POV.rot.y -= 1.0; // 绕Y轴负方向旋转
Invalidate(FALSE);;
break;
}
case VK_PRIOR: // 场景放大
{
if (g_POV.trans.z > 1.0f)
// 摄像机向z轴的负方向平移
g_POV.trans.z -= 1.0;
Invalidate(FALSE);;
break;
}
case VK_NEXT: // 场景缩小
{
if (g_POV.trans.z < 9.0f)
// 摄像机向z轴方向平移
g_POV.trans.z += 1.0;
Invalidate(FALSE);;
break;
}
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
// 下面的函数的功能是对游戏进行初始化
BOOL CBilliardView::InitGame(void)
{
g_CueStick.draw = g_CueStick.old_draw = 0.2f;
g_CueStick.yaw = 0.0f;
g_CueStick.pos.x = -4.0f;
g_CueStick.pos.y = TABLE_POSITION;
g_CueStick.pos.z = 0.0f;
g_Kd = 0.02f; // 阻尼因子
g_Kr_Bumper = 0.8f; // 球与边界之间碰撞的恢复系数
g_Kr_Ball = 0.2f; // 球与球之间碰撞的恢复系数
g_Csf = 0.2f; // 缺省的静摩擦
g_Ckf = 0.1f; // 缺省的动摩擦
MAKEVECTOR(g_Gravity, 0.0f, -32.0f, 0.0f)
g_IntegratorType = EULER_INTEGRATOR;
g_CollisionRootFinding = FALSE; // ONLY SET WHEN FINDING A COLLISION
g_ContactCnt = 0;
SetupBalls();
g_CurrentSys = g_GameSys[0];
g_TargetSys = g_GameSys[1];
g_CollisionPlaneCnt = 4;
// 左边的碰撞平面
MAKEVECTOR(g_CollisionPlane[0].normal,1.0f, 0.0f, 0.0f)
g_CollisionPlane[0].d = LEFT_BUMPER;
// 右边的碰撞平面
MAKEVECTOR(g_CollisionPlane[1].normal,-1.0f, 0.0f, 0.0f)
g_CollisionPlane[1].d = RIGHT_BUMPER;
// 上边的碰撞平面
MAKEVECTOR(g_CollisionPlane[2].normal, 0.0f, 0.0f, -1.0f)
g_CollisionPlane[2].d = TOP_BUMPER;
// 下边的碰撞平面
MAKEVECTOR(g_CollisionPlane[3].normal, 0.0f, 0.0f, 1.0f)
g_CollisionPlane[3].d = BOTTOM_BUMPER;
return TRUE;
}
void CBilliardView::InitRender(void)
{
int loop;
// 初始化摄像机的位置
g_POV.trans.x = 0.0f;
g_POV.trans.y = 0.0f;
g_POV.trans.z = 8.0f;
g_POV.rot.x = 20.0f;
g_POV.rot.y = 90.0f;
g_POV.rot.z = 0.0f;
// 定义白球的显示列表
glNewList(OGL_WBALL_DLIST,GL_COMPILE);
// 定义数据序列
glInterleavedArrays(WBALLFORMAT,0,(GLvoid *)&WBALLMODEL);
glBegin(GL_TRIANGLES);
for (loop = 0; loop < WBALLPOLYCNT * 3; loop++)
{
glArrayElement(loop);
}
glEnd();
glEndList();
// 定义黄球的显示列表
glNewList(OGL_YBALL_DLIST,GL_COMPILE);
// 定义数据序列
glInterleavedArrays(YBALLFORMAT,0,(GLvoid *)&YBALLMODEL);
glBegin(GL_TRIANGLES);
for (loop = 0; loop < YBALLPOLYCNT * 3; loop++)
{
glArrayElement(loop);
}
glEnd();
glEndList();
// 定义球杆的显示列表
glNewList(OGL_CUE_DLIST,GL_COMPILE);
// 定义数据序列
glInterleavedArrays(CUEFORMAT,0,(GLvoid *)&CUEMODEL);
glBegin(GL_TRIANGLES);
for (loop = 0; loop < CUEPOLYCNT * 3; loop++)
{
glArrayElement(loop);
}
glEnd();
glEndList();
LoadTextures();
LoadSceneFile("Pool.ros"); // 装入场景数据
}
// 下面的函数的功能是将所有的TGA图像数据读入到纹理中
void CBilliardView::LoadTextures()
{
GLubyte *rgb;
char texName[80];
tTGAHeader_s tgaHeader;
HANDLE specHandle;
WIN32_FIND_DATA fileData;
int rv;
g_TextureCnt = 0;
sprintf(texName,"%s*.tga",ART_PATH);
if ((specHandle=FindFirstFile(texName,&fileData))!= INVALID_HANDLE_VALUE)
{
do
{
sprintf(g_TexPool[g_TextureCnt].map,"%s%s",ART_PATH,fileData.cFileName);
glGenTextures(1,&g_TexPool[g_TextureCnt].glTex);
rgb = m_LoadTGA.LoadTGAFile( g_TexPool[g_TextureCnt].map,&tgaHeader);
if (rgb == NULL)
{
MessageBox("Unable to Open File...",g_TexPool[g_TextureCnt].map,MB_OK);
g_TexPool[g_TextureCnt].glTex = 0;
return;
}
glBindTexture(GL_TEXTURE_2D, g_TexPool[g_TextureCnt].glTex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
// 定义2D纹理像素格式
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* Force 4-byte alignment */
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
if (tgaHeader.d_pixel_size == 32)
{
glTexImage2D(GL_TEXTURE_2D, 0, 4, tgaHeader.d_width, tgaHeader.d_height, 0,
GL_RGBA , GL_UNSIGNED_BYTE, rgb);
rv = gluBuild2DMipmaps( GL_TEXTURE_2D, 4, tgaHeader.d_width, tgaHeader.d_height,
GL_RGBA, GL_UNSIGNED_BYTE, rgb );
}
else
{
glTexImage2D(GL_TEXTURE_2D, 0, 3, tgaHeader.d_width, tgaHeader.d_height, 0,
GL_RGB, GL_UNSIGNED_BYTE, rgb);
rv = gluBuild2DMipmaps( GL_TEXTURE_2D, 3, tgaHeader.d_width, tgaHeader.d_height, GL_RGB,
GL_UNSIGNED_BYTE, rgb );
}
// 释放位图和RGB图像数据
free(rgb);
g_TextureCnt++;
}while (FindNextFile(specHandle,&fileData));
FindClose(specHandle);
}
}
void CBilliardView::LoadSceneFile(char *filename)
{
FILE *fp;
char tempstr[80];
t_Visual *visual;
fp = fopen(filename,"rb");
if (fp != NULL)
{
fread(tempstr,1,4,fp);
if (strncmp(tempstr,"ROSC",4)!= 0)
{
MessageBox("Not a Valid Data File","Load File", MB_OK|MB_ICONEXCLAMATION);
return;
}
visual = &g_Scene;
fread(&visual->vertexCnt,sizeof(long),1,fp);
visual->vertex = (tVector *)malloc(sizeof(tVector) * visual->vertexCnt);
fread(visual->vertex,sizeof(tVector),visual->vertexCnt,fp);
fread(&visual->triCnt,sizeof(long),1,fp);
visual->tri = (tPrimPoly *)malloc(sizeof(tPrimPoly) * (visual->triCnt));
fread(visual->tri,sizeof(tPrimPoly),visual->triCnt,fp);
fread(&visual->quadCnt,sizeof(long),1,fp);
visual->quad = (tPrimPoly *)malloc(sizeof(tPrimPoly) * (visual->quadCnt));
fread(visual->quad,sizeof(tPrimPoly),visual->quadCnt,fp);
fclose(fp);
}
}
// 设置OpenGL绘制环境
void CBilliardView::SetupViewRC(void)
{
// 设置黑色背景
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
// 使用深度测试
glEnable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
// 无光照
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
glPolygonMode(GL_FRONT,GL_FILL);
glDepthFunc(GL_LESS);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
void CBilliardView::RenderCueAndBalls()
{
// 绘制白球
glPushMatrix();
glTranslatef(g_CurrentSys[0].pos.x,g_CurrentSys[0].pos.y,g_CurrentSys[0].pos.z);
glCallList(OGL_WBALL_DLIST);
glPopMatrix();
// 绘制黄球
glPushMatrix();
glTranslatef(g_CurrentSys[1].pos.x,g_CurrentSys[1].pos.y,g_CurrentSys[1].pos.z);
glCallList(OGL_YBALL_DLIST);
glPopMatrix();
// 绘制球杆
glPushMatrix();
// 球杆的位置
glTranslatef(g_CueStick.pos.x,g_CueStick.pos.y,g_CueStick.pos.z);
// 绕Y轴旋转
glRotatef(g_CueStick.yaw, 0.0f, 1.0f, 0.0f);
// 绕X轴旋转一个固定角度,即略向上抬
glRotatef(-5.0f, 1.0f, 0.0f, 0.0f);
// 击球时向后移动球杆
glTranslatef(0,0,g_CueStick.draw);
glCallList(OGL_CUE_DLIST);
glPopMatrix();
}
void CBilliardView::RenderScene()
{
int loop,loop2;
t_Visual *visual;
tPrimPoly *poly;
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
visual = &g_Scene;
for (loop2 = 0; loop2 < g_TextureCnt; loop2++)
{
poly = visual->quad;
glBindTexture(GL_TEXTURE_2D, g_TexPool[loop2].glTex);
for (loop = 0; loop < visual->quadCnt; loop++)
{
if ((poly->type & POLY_TEXTURED) > 0 && poly->TexNdx1 == (uint)loop2)
{
glBegin(GL_QUADS);
glTexCoord2fv((float *)&poly->t1[0]);
glColor3ubv((unsigned char *)&poly->color[0]);
glVertex3fv((float *)&visual->vertex[poly->index[0]]);
glTexCoord2fv((float *)&poly->t1[1]);
glColor3ubv((unsigned char *)&poly->color[1]);
glVertex3fv((float *)&visual->vertex[poly->index[1]]);
glTexCoord2fv((float *)&poly->t1[2]);
glColor3ubv((unsigned char *)&poly->color[2]);
glVertex3fv((float *)&visual->vertex[poly->index[2]]);
glTexCoord2fv((float *)&poly->t1[3]);
glColor3ubv((unsigned char *)&poly->color[3]);
glVertex3fv((float *)&visual->vertex[poly->index[3]]);
glEnd();
}
poly++;
}
poly = visual->tri;
for (loop = 0; loop < visual->triCnt; loop++)
{
if ((poly->type & POLY_TEXTURED) > 0 && poly->TexNdx1 == (uint)loop2)
{
glBegin(GL_TRIANGLES);
glTexCoord2fv((float *)&poly->t1[0]);
glColor3ubv((unsigned char *)&poly->color[0]);
glVertex3fv((float *)&visual->vertex[poly->index[0]]);
glTexCoord2fv((float *)&poly->t1[1]);
glColor3ubv((unsigned char *)&poly->color[1]);
glVertex3fv((float *)&visual->vertex[poly->index[1]]);
glTexCoord2fv((float *)&poly->t1[2]);
glColor3ubv((unsigned char *)&poly->color[2]);
glVertex3fv((float *)&visual->vertex[poly->index[2]]);
glEnd();
}
poly++;
}
}
glDisable(GL_TEXTURE_2D);
}
void CBilliardView::SetupBalls()
{
int loop, loop2;
for (loop = 0; loop < SYSTEM_COUNT; loop++)
{
for (loop2 = 0; loop2 < BALL_COUNT; loop2++)
{
switch (loop2)
{
// 球1是球杆需要击中的球
case 0:
MAKEVECTOR(g_GameSys[loop][loop2].pos,-4.0f, TABLE_POSITION, 0.0f)
break;
case 1:
MAKEVECTOR(g_GameSys[loop][loop2].pos,4.0f, TABLE_POSITION, 0.0f)
break;
}
MAKEVECTOR(g_GameSys[loop][loop2].v,0.0f, 0.0f, 0.0f)
MAKEVECTOR(g_GameSys[loop][loop2].f,0.0f, 0.0f, 0.0f)
g_GameSys[loop][loop2].oneOverM = 1.0f / 0.34f;
MAKEVECTOR(g_GameSys[loop][loop2].angMom,0.0f, 0.0f, 0.0f)
MAKEVECTOR(g_GameSys[loop][loop2].torque,0.0f, 0.0f, 0.0f)
// 四元数清零
MAKEVECTOR(g_GameSys[loop][loop2].orientation,0.0f, 0.0f, 0.0f)
g_GameSys[loop][loop2].orientation.w = 1.0f;
g_GameSys[loop][loop2].flags = 0;
g_GameSys[loop][loop2].flags = 0;
}
}
}
#define STATIC_THRESHOLD 0.03f // 速度的阈值,用于判断使用何种摩擦
void CBilliardView::ComputeForces( t_Ball *system)
{
int loop;
t_Ball *curBall;
tVector contactN;
float FdotN,VdotN,Vmag;
tVector Vn,Vt;
curBall = system;
for (loop = 0; loop < BALL_COUNT; loop++)
{
MAKEVECTOR(curBall->f,0.0f,0.0f,0.0f) // 清除力
MAKEVECTOR(curBall->torque,0.0f, 0.0f, 0.0f) // 清除力矩
if (g_UseDamping) // 如果用户定义阻尼
{
curBall->f.x += (-g_Kd * curBall->v.x);
curBall->f.y += (-g_Kd * curBall->v.y);
curBall->f.z += (-g_Kd * curBall->v.z);
}
else // 否则使用缺省阻尼
{
curBall->f.x += (-DEFAULT_DAMPING * curBall->v.x);
curBall->f.y += (-DEFAULT_DAMPING * curBall->v.y);
curBall->f.z += (-DEFAULT_DAMPING * curBall->v.z);
}
// 下面处理球与桌面之间的摩擦问题
if (g_UseFriction)
{
// 计算正压力,即重力
FdotN = g_Gravity.y / curBall->oneOverM;
// 计算垂直于法向平面的速度
MAKEVECTOR(contactN,0.0f, 1.0f, 0.0f)
VdotN = m_MathDefs.DotProduct(&contactN,&curBall->v);
m_MathDefs.ScaleVector(&contactN, VdotN, &Vn);
m_MathDefs.VectorDifference(&curBall->v, &Vn, &Vt);
Vmag = m_MathDefs.VectorSquaredLength(&Vt);
// 判断速度是否大于阈值
if (Vmag > STATIC_THRESHOLD) // 大于阈值,使用动摩擦模型
{
m_MathDefs.NormalizeVector(&Vt);
m_MathDefs.ScaleVector(&Vt, (FdotN * g_Ckf), &Vt);
m_MathDefs.VectorSum(&curBall->f,&Vt,&curBall->f);
}
else // 小于阈值,使用静摩擦模型
{
Vmag = Vmag / STATIC_THRESHOLD;
m_MathDefs.NormalizeVector(&Vt);
m_MathDefs.ScaleVector(&Vt, (FdotN * g_Csf * Vmag), &Vt); // Scale Friction by Velocity
m_MathDefs.VectorSum(&curBall->f,&Vt,&curBall->f);
if (loop == 0)
{
g_BallInPlay = FALSE;
}
}
}
curBall++;
}
// 用户是否定义了球杆撞击球的力
if (g_CueHitBall)
{
m_MathDefs.VectorSum(&system[0].f,&g_CueForce,&system[0].f);
}
}
void CBilliardView::IntegrateSysOverTime(t_Ball *initial,t_Ball *source, t_Ball *target, float deltaTime)
{
int loop;
float deltaTimeMass;
for (loop = 0; loop < BALL_COUNT; loop++)
{
deltaTimeMass = deltaTime * initial->oneOverM;
// 获得新的速度
target->v.x = initial->v.x + (source->f.x * deltaTimeMass);
target->v.y = initial->v.y + (source->f.y * deltaTimeMass);
target->v.z = initial->v.z + (source->f.z * deltaTimeMass);
target->oneOverM = initial->oneOverM;
// 获得新的位置坐标
target->pos.x = initial->pos.x + (deltaTime * source->v.x);
target->pos.y = initial->pos.y + (deltaTime * source->v.y);
target->pos.z = initial->pos.z + (deltaTime * source->v.z);
initial++;
source++;
target++;
}
}
// 欧拉积分
void CBilliardView::EulerIntegrate( float DeltaTime)
{
IntegrateSysOverTime(g_CurrentSys,g_CurrentSys, g_TargetSys,DeltaTime);
}
// 中点方法
void CBilliardView::MidPointIntegrate( float DeltaTime)
{
float halfDeltaT;
halfDeltaT = DeltaTime / 2.0f;
// 计算半步长
IntegrateSysOverTime(g_CurrentSys,g_CurrentSys,&g_GameSys[2][0],halfDeltaT);
// 使用新的位置和速度计算力
ComputeForces(&g_GameSys[2][0]);
// 计算全步长
IntegrateSysOverTime(g_CurrentSys,&g_GameSys[2][0],g_TargetSys,DeltaTime);
}
int CBilliardView::CheckForCollisions( t_Ball *system )
{
int collisionState = NOT_COLLIDING;
float const depthEpsilon = 0.001f;
float const ballDepthEpsilon = 0.0001f;
int loop,loop2,planeIndex;
t_Ball *curBall,*ball2;
t_CollisionPlane *plane;
float axbyczd,dist,relativeVelocity;
tVector distVect;
g_ContactCnt = 0; // 目前的碰撞次数为0
curBall = system;
for (loop = 0;
(loop < BALL_COUNT) && (collisionState != PENETRATING);
loop++,curBall++)
{
// 判断球与边界面之间的碰撞
for(planeIndex = 0;
(planeIndex < g_CollisionPlaneCnt) && (collisionState != PENETRATING);
planeIndex++)
{
plane = &g_CollisionPlane[planeIndex];
axbyczd = m_MathDefs.DotProduct(&curBall->pos,&plane->normal) + plane->d;
if(axbyczd < -depthEpsilon)
{
// 任何球穿过边界面,退出循环
collisionState = PENETRATING;
}
else
if(axbyczd < depthEpsilon)
{
relativeVelocity = m_MathDefs.DotProduct(&plane->normal,&curBall->v);
if(relativeVelocity < 0.0f)
{
collisionState = COLLIDING_WITH_WALL;
g_Contact[g_ContactCnt].type = COLLIDING_WITH_WALL;
g_Contact[g_ContactCnt].ball = loop;
g_Contact[g_ContactCnt].Kr = g_Kr_Bumper; // Ball to bumper Kr
memcpy(&g_Contact[g_ContactCnt].normal,&plane->normal,sizeof(tVector));
g_ContactCnt++;
}
}
}
// 现在判断球与球之间的碰撞
ball2 = system;
for (loop2 = 0; (loop2 < BALL_COUNT) && (collisionState != PENETRATING);
loop2++,ball2++)
{
if (loop2 == loop) continue; // 不与自身比较
if (curBall->flags) continue; // 如果当前球是碰撞球
m_MathDefs.VectorDifference(&curBall->pos, &ball2->pos, &distVect);
// 获得两球心之间的距离
dist = m_MathDefs.VectorSquaredLength(&distVect);
dist = dist - (BALL_DIAMETER * BALL_DIAMETER);
// 判断两球之间是否穿过
if(dist < -ballDepthEpsilon)
{
// 如果穿过,则退出
collisionState = PENETRATING;
}
else
if(dist < ballDepthEpsilon)
{
m_MathDefs.NormalizeVector(&distVect);
// 获得相对速度
relativeVelocity = m_MathDefs.DotProduct(&distVect,&curBall->v);
// 如果相对速度小于0
if(relativeVelocity < 0.0f)
{
collisionState = COLLIDING_WITH_BALL;
g_Contact[g_ContactCnt].type = COLLIDING_WITH_BALL;
g_Contact[g_ContactCnt].ball = loop;
g_Contact[g_ContactCnt].ball2 = loop2;
g_Contact[g_ContactCnt].Kr = g_Kr_Ball;
memcpy(&g_Contact[g_ContactCnt].normal,&distVect,sizeof(tVector));
ball2->flags = 1;
g_ContactCnt++;
}
}
}
}
return collisionState;
}
void CBilliardView::ResolveCollisions( t_Ball *system )
{
t_Contact *contact;
t_Ball *ball,*ball2;
float VdotN;
tVector Vn,Vt,Vn1;
int loop;
contact = g_Contact;
for (loop = 0; loop < g_ContactCnt; loop++,contact++)
{
ball = &system[contact->ball];
// 计算 Vn
VdotN = m_MathDefs.DotProduct(&contact->normal,&ball->v);
m_MathDefs.ScaleVector(&contact->normal, VdotN, &Vn);
// 计算 Vt
m_MathDefs.VectorDifference(&ball->v, &Vn, &Vt);
// 判断碰撞类型
if (contact->type == COLLIDING_WITH_WALL) // 如果球与边界碰撞
{
// Vn乘以恢复系数
m_MathDefs.ScaleVector(&Vn, contact->Kr, &Vn);
// 设置新的速度
m_MathDefs.VectorDifference(&Vt, &Vn, &ball->v);
}
else // 球与球之间碰撞
{
// Vn乘以恢复系数
m_MathDefs.ScaleVector(&Vn, contact->Kr, &Vn1);
// 设置新的速度
m_MathDefs.VectorDifference(&Vt, &Vn1, &ball->v);
ball2 = &system[contact->ball2];
// 球2的Vn乘以恢复系数
m_MathDefs.ScaleVector(&Vn, 1.0f - contact->Kr, &Vn1);
// 设置球2的速度
m_MathDefs.VectorSum(&Vn1,&ball2->v,&ball2->v);
ball->flags = 0;
ball2->flags = 0;
}
}
}
void CBilliardView::Simulate(float DeltaTime, BOOL running)
{
float CurrentTime = 0.0f;
float TargetTime = DeltaTime;
t_Ball *tempSys;
int collisionState;
while(CurrentTime < DeltaTime)
{
if (running)
{
// 定义各球的作用力
ComputeForces(g_CurrentSys);
// 如果出现碰撞穿透的现象,强制使用欧拉方法,否则提供选择
if (g_CollisionRootFinding)
{
EulerIntegrate(TargetTime-CurrentTime);
}
else
{
switch (g_IntegratorType)
{
case EULER_INTEGRATOR:
EulerIntegrate(TargetTime-CurrentTime);
break;
case MIDPOINT_INTEGRATOR:
MidPointIntegrate(TargetTime-CurrentTime);
break;
}
}
}
collisionState = CheckForCollisions(g_TargetSys);
if(collisionState == PENETRATING)
{
// 将使用欧拉方法
g_CollisionRootFinding = TRUE;
// 将时间步长缩短
TargetTime = (CurrentTime + TargetTime) / 2.0f;
assert(fabs(TargetTime - CurrentTime) > EPSILON);
}
else
{
if(collisionState == COLLIDING_WITH_WALL || collisionState == COLLIDING_WITH_BALL)
{
int Counter = 0;
do
{
ResolveCollisions(g_TargetSys);
Counter++;
} while((CheckForCollisions(g_TargetSys) >= COLLIDING_WITH_WALL) && (Counter < 500));
assert(Counter < 500);
g_CollisionRootFinding = FALSE;
}
// 进入下一步
CurrentTime = TargetTime;
TargetTime = DeltaTime;
MAKEVECTOR(g_CueForce,0.0f,0.0f,0.0f); // 清除球杆的力
// SWAP MY TWO SYSTEM BUFFERS SO I CAN DO IT AGAIN
tempSys = g_CurrentSys;
g_CurrentSys = g_TargetSys;
g_TargetSys = tempSys;
}
}
}
float CBilliardView::GetTime( void )
{
static long StartMilliseconds;
long CurrentMilliseconds;
if(!StartMilliseconds)
{
StartMilliseconds = timeGetTime();
}
CurrentMilliseconds = timeGetTime();
return (float)(CurrentMilliseconds - StartMilliseconds) / 1000.0f;
}