#include #include #include #include #include #define _USE_MATH_DEFINES #include #include "C_3DObject.h" #include "C_Environment.h" #include "C_Hand.h" #include "C_Block.h" #include "C_Box.h" #include "C_Log.h" #include "bitmap.h" #include "message_queue.h" #include "message_input.h" #include "menu_msg.h" #include "wiimote_utils.h" #include "C_Smoother.h" #include "C_MatchBloxEngine.h" #include "font.h" #include "stereoheadtrackfrustum.h" #include "textfile.h" extern "C" { extern GLuint g_iBase; extern struct BitmapStruct g_sFont; } C_MatchBloxEngine::C_MatchBloxEngine(const char *f_strModelPath, const char *f_strLogFile, GameSettings f_GameSettings) : m_GameSettings(f_GameSettings) { //create logger m_State = ES_INITIALISED; //load and build the grayscale shader program if (!LoadGrayScaleShader()) { m_State = ES_ERROR; } m_GSConvScale[0] = 0.299; //grayscale conversion scale (default 0.299, 0.587, 0.114) m_GSConvScale[1] = 0.587; m_GSConvScale[2] = 0.114; //Load models if (!LoadModels(f_strModelPath)) { //set state to initialised; m_State = ES_ERROR; } //initialise a random seed srand ( time(NULL) ); //init vars m_CurrentBox = BS_LARGE; m_bHeadTrackingEnabled = true; m_bStereoEnabled = true; //init the world bounding box m_WorldBox.m_Min = Vect3D_t(-15.0, -5.0, -15.0); m_WorldBox.m_Max = Vect3D_t(15.0, 15.0, 15.0); //set smoothers m_pWorldZSmoother[0] = new C_Smoother(0.0); m_pWorldZSmoother[1] = new C_Smoother(0.0); m_pWorldZSmoother[0]->SetSimpleMovingAverage(25); //world z coordinate smoother probably lots of lag (but only in z dir) m_pWorldZSmoother[1]->SetExponentialMovingAverage(0.8); } C_MatchBloxEngine::~C_MatchBloxEngine() { //destroy logger //delete models DeleteModels(); delete m_pWorldZSmoother[0]; delete m_pWorldZSmoother[1]; } GameResult C_MatchBloxEngine::ProcessMsgs(void) { struct messageq_s *message; GameResult l_Result = GR_BUSY; //default return value //init current time m_uiCurrentTime = glutGet(GLUT_ELAPSED_TIME); while ((message = messageq_get(MESSAGE_RENDERER)) && l_Result == GR_BUSY) { switch(message->sender) { case MESSAGE_MENU: int l_iAction; menu_payload *l_pMsg1; l_pMsg1 = (menu_payload*)message->payload; l_iAction = l_pMsg1->action; switch (l_iAction) { case ACTION_PAUSE: if (!Pause()) { Resume(); } break; case ACTION_START: Abort(); NewGame(l_pMsg1->gamemode); break; case ACTION_RESUME: Resume(); break; case ACTION_ABORT: Abort(); break; } break; case MESSAGE_INPUT_KEYBOARD: break; case MESSAGE_INPUT_MOUSE: break; case MESSAGE_INPUT_WIIMOTE: input_payload_wiimote *l_pMsg2; l_pMsg2 = (input_payload_wiimote*)message->payload; switch (m_State) { case ES_GET_READY: if (l_pMsg2->btns & WIIMOTE_BUTTON_A && l_pMsg2->btns & WIIMOTE_BUTTON_B && l_pMsg2->posDataValid) { //the 3d mouse position data is valid and A+B are pressed //init the z depth and start the game m_dInitialWiimoteDist = l_pMsg2->Zdist; if (!StartGame()) { l_Result = GR_ERROR; } } break; case ES_FINISHED: if (l_pMsg2->btns & WIIMOTE_BUTTON_ALL) { //any button is pressed //delete session and return GAME_FINISHED delete m_pCurrentSession; l_Result = GR_FINISHED; //set state to initialised m_State = ES_INITIALISED; } break; default: if (l_pMsg2->posDataValid) { m_uiLastPosValid = m_uiCurrentTime; Vect3D_t l_WorldPos; if (l_pMsg2->btns & WIIMOTE_BUTTON_A) { int i = 0; //debug break } if (ConvertWiimoteToWorld(l_pMsg2, &l_WorldPos)) { CursorMove(l_WorldPos); } } break; } break; default: std::cout << "Undefined message! Sender: " << message->sender << std::endl; //l_Result = GR_ERROR; } } return l_Result; } void C_MatchBloxEngine::Render(unsigned int f_uiElapsedTime, input_payload_wiimote &f_HTWiimote, FrustumParms_t &f_FrustParms) { Vect3D_t l_LeftEye, l_CenterEye, l_RightEye; if (f_HTWiimote.posDataValid && m_bHeadTrackingEnabled) { //calculate the eye positions CalcEyePosInMM(&f_HTWiimote, STEREO_LEFT_EYE, f_FrustParms, l_LeftEye); CalcEyePosInMM(&f_HTWiimote, STEREO_RIGHT_EYE, f_FrustParms, l_RightEye); CalcEyePosInMM(&f_HTWiimote, MONO_CENTER, f_FrustParms, l_CenterEye); } else { //set default positions l_LeftEye = l_RightEye = l_CenterEye = Vect3D_t(0.0, 0.0, f_FrustParms.m_dDefHeadDistMM); l_LeftEye.x = f_FrustParms.m_dEyeDistMM * -0.5; l_RightEye.x = f_FrustParms.m_dEyeDistMM * 0.5; } //always render the environment map in color, using the mono head tracked frustum SetFrustum(f_FrustParms, l_CenterEye, true); RenderEnv(f_uiElapsedTime); if (!m_bStereoEnabled) { //render the 3d shit once SetFrustum(f_FrustParms, l_CenterEye); RenderMain(f_uiElapsedTime); } else { //enable grayscale shader glUseProgram(m_GrayScaleShaderProgram); //set the conversion scale glUniform3fv(m_GSConvScaleLocation, 1, m_GSConvScale); for (int CurrentEye=0; CurrentEye<2; CurrentEye++) //STEREO_LEFT_EYE = 0, STEREO_RIGHT_EYE = 1 { //clear depth buffer glClear(GL_DEPTH_BUFFER_BIT); //set colormask if (CurrentEye == STEREO_LEFT_EYE) { glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE); //set frustum SetFrustum(f_FrustParms, l_LeftEye); } else { glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE); //set frustum SetFrustum(f_FrustParms, l_RightEye); } RenderMain(f_uiElapsedTime); } //restore the colormask glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); //disable greyscale shader glUseProgram(0); } RenderOverlay(f_uiElapsedTime); } void C_MatchBloxEngine::RenderEnv(unsigned int f_uiElapsedTime) { glPushMatrix(); //set camera pitch glRotated(20.0, 1.0, 0.0, 0.0); //rotate the environment map about the y axis glRotated((GLdouble)f_uiElapsedTime/1000.0, 0.0, 1.0, 0.0); m_pEnvMap->Render(); glPopMatrix(); } void C_MatchBloxEngine::RenderMain(unsigned int f_uiElapsedTime) { GLint l_iTexShaderLocation = m_bStereoEnabled? m_GSTextureLocation : 0; glPushMatrix(); //move the camera backwards glTranslated(0.0, m_WorldBox.m_Min.y + 1.0 , m_pBox[m_CurrentBox]->GetBoundingBox().m_Min.z - 1.0); glRotated(20.0, 1.0, 0.0, 0.0); //always render the box m_pBox[(int)m_CurrentBox]->Render(l_iTexShaderLocation); switch (m_State) { case ES_PLAYING_GRAB_BLOCK: case ES_PLAYING_PUT_BLOCK: m_pBlock[(int)m_CurrentBlock]->Render(f_uiElapsedTime, l_iTexShaderLocation); m_pHand->Render(f_uiElapsedTime); break; } glPopMatrix(); } void C_MatchBloxEngine::RenderOverlay(unsigned int f_uiElapsedTime) { switch (m_State) { case ES_INITIALISED: case ES_ERROR: break; case ES_GET_READY: //render some GET READY text RenderGetReady(); if (f_uiElapsedTime - m_uiLastPosValid > 5000) { RenderPointHere(); } break; case ES_PLAYING_GRAB_BLOCK: case ES_PLAYING_PUT_BLOCK: //render timer and block count RenderHUD(f_uiElapsedTime); if (f_uiElapsedTime - m_uiLastPosValid > 5000) { RenderPointHere(); } break; case ES_PAUSED: break; case ES_FINISHED: RenderResults(); //render results... break; } } void C_MatchBloxEngine::RenderGetReady() { ColorStruct l_TextCol = {0.0, 0.3, 1.0}; int w = glutGet(GLUT_WINDOW_WIDTH), h = glutGet(GLUT_WINDOW_HEIGHT); RenderText((w/2)-(9*32), (h/2)-16, "Press A+B to begin.", l_TextCol); } void C_MatchBloxEngine::RenderHUD(unsigned int f_uiElapsedTime) { ColorStruct l_TextCol = {0.0, 0.3, 1.0}; std::stringstream l_ss; unsigned l_uiTime = f_uiElapsedTime - m_pCurrentSession->m_uiSessionStart; int l_iMSecs = l_uiTime % 1000; l_uiTime /= 1000; int l_iSecs = l_uiTime % 60; l_uiTime /= 60; int l_iMins = l_uiTime % 60; l_ss << "Block " << m_pCurrentSession->m_iCurrentTurn << "/" << m_pCurrentSession->m_iTotalTurns << " Time: " << setw(2) << setfill('0') << l_iMins << ":" << setw(2) << l_iSecs << "." << l_iMSecs; RenderText(32, 16, (char*)l_ss.str().c_str(), l_TextCol); } void C_MatchBloxEngine::RenderResults() { ColorStruct l_TextCol = {0.0, 0.3, 1.0}; RenderText(32, 64, "YOU SUCK!", l_TextCol); } void C_MatchBloxEngine::RenderPointHere() { ColorStruct l_TextCol = {0.0, 0.3, 1.0}; int w = glutGet(GLUT_WINDOW_WIDTH), h = glutGet(GLUT_WINDOW_HEIGHT); RenderText((w/2)-(6*32), (h/4), "Point >o< here!", l_TextCol); } void C_MatchBloxEngine::RenderText(GLint x, GLint y, char *string, struct ColorStruct f_sColor) { glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, g_sFont.m_iImageId); glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3d(f_sColor.m_dRed, f_sColor.m_dGreen, f_sColor.m_dBlue); glMatrixMode(GL_MODELVIEW); glPushMatrix(); //push modelview matrix glMatrixMode(GL_PROJECTION); glPushMatrix(); //push projection matrix glLoadIdentity(); glOrtho(0, glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT), 0, 0, 1); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTranslated(x, y, 0); glListBase(g_iBase - 32); glCallLists((GLsizei)strlen(string), GL_UNSIGNED_BYTE, string); glMatrixMode(GL_PROJECTION); glPopMatrix(); //pop projection matrix glMatrixMode(GL_MODELVIEW); glPopMatrix(); //pop modelview matrix glPopAttrib(); } bool C_MatchBloxEngine::NewGame(int f_iGameMode) { if(m_State == ES_INITIALISED) { m_bHeadTrackingEnabled = (int)(f_iGameMode & GO_HEADTRACKING); m_bStereoEnabled = (int)(f_iGameMode & GO_STEREOVISION); m_CurrentBox = (BoxSize)((f_iGameMode & (255 - 8)) >> 3); //prepare a session struct for administration m_pCurrentSession = new GameSession(m_GameSettings.m_iNrOfTurns, m_CurrentBox); //randomize the box tiles m_pBox[(int)m_CurrentBox]->RandomizeTiles(); //set state to GET READY m_State = ES_GET_READY; return true; } return false; } bool C_MatchBloxEngine::StartGame() { if (m_State == ES_GET_READY) { //start the game session NewBlock(); return true; } return false; } bool C_MatchBloxEngine::Pause() { //only pause when playing if (m_State == ES_PLAYING_GRAB_BLOCK || m_State == ES_PLAYING_PUT_BLOCK) { //save current state m_SavedState = m_State; m_pCurrentSession->PauseSession(); //set current state to paused m_State = ES_PAUSED; return true; } return false; } bool C_MatchBloxEngine::Resume() { if (m_State == ES_PAUSED) { //restore previous state m_State = m_SavedState; //restore timer m_pCurrentSession->ResumeSession(); return true; } return false; } bool C_MatchBloxEngine::Abort() { //abort when not in error or init state if (m_State != ES_ERROR && m_State != ES_INITIALISED) { //set state to initialised m_State = ES_INITIALISED; //delete session (if there is any) if (m_pCurrentSession) { delete m_pCurrentSession; m_pCurrentSession = NULL; } return true; } return false; } bool C_MatchBloxEngine::LoadGrayScaleShader() { GLint l_bStatus; char *vs_text = NULL,*fs_text = NULL; GLuint vs = glCreateShader(GL_VERTEX_SHADER), fs = glCreateShader(GL_FRAGMENT_SHADER); vs_text = textFileRead("shaders\\grayscale.vert"); if (vs_text == NULL) { return false; } fs_text = textFileRead("shaders\\grayscale.frag"); if (fs_text == NULL) { free(vs_text); return false; } glShaderSource(vs, 1, (const char**)&vs_text,NULL); glShaderSource(fs, 1, (const char**)&fs_text,NULL); free(vs_text); free(fs_text); glCompileShader(vs); // printShaderInfoLog(vs); glGetShaderiv(vs, GL_COMPILE_STATUS, &l_bStatus); if (l_bStatus == GL_FALSE) { return false; } glCompileShader(fs); // printShaderInfoLog(fs); glGetShaderiv(fs, GL_COMPILE_STATUS, &l_bStatus); if (l_bStatus == GL_FALSE) { return false; } m_GrayScaleShaderProgram = glCreateProgram(); glAttachShader(m_GrayScaleShaderProgram,fs); glAttachShader(m_GrayScaleShaderProgram,vs); glLinkProgram(m_GrayScaleShaderProgram); // printProgramInfoLog(g_GameState.m_GreyScaleShaderProgram); glGetProgramiv(m_GrayScaleShaderProgram, GL_LINK_STATUS, &l_bStatus); if (l_bStatus == GL_FALSE) { return false; } m_GSConvScaleLocation = glGetUniformLocation(m_GrayScaleShaderProgram, "g_ConversionWeights"); m_GSTextureLocation = glGetUniformLocation(m_GrayScaleShaderProgram, "g_TexSampler"); return true; } bool C_MatchBloxEngine::LoadModels(const char* f_strModelDir) { MatProps_t l_Mat; std::string l_BaseName = f_strModelDir; //create the environment mapped cube m_pEnvMap = new C_Environment("envmaps/terrain_", 50.0); //load the bitmaps for the textures LoadTexture((l_BaseName + "/wood1.bmp").c_str(), m_uiWood1Tex); LoadTexture((l_BaseName + "/wood2.bmp").c_str(), m_uiWood2Tex); LoadTexture((l_BaseName + "/wood3.bmp").c_str(), m_uiWood3Tex); //load the block models //red squares l_Mat.setAmb(1.0, 0.0, 0.0, 1.0); l_Mat.setDif(1.0, 0.0, 0.0, 1.0); m_pBlock[BT_SQUARE] = new C_Block((l_BaseName + "/square.obj").c_str(), m_uiWood1Tex, l_Mat); if (!m_pBlock[BT_SQUARE]->Initialized()) return false; //yellow cricles l_Mat.setAmb(0.0, 1.0, 1.0, 1.0); l_Mat.setDif(0.0, 1.0, 1.0, 1.0); m_pBlock[BT_CIRCLE] = new C_Block((l_BaseName + "/circle.obj").c_str(), m_uiWood1Tex, l_Mat); if (!m_pBlock[BT_CIRCLE]->Initialized()) return false; //green triangles l_Mat.setAmb(0.0, 1.0, 0.0, 1.0); l_Mat.setDif(0.0, 1.0, 0.0, 1.0); m_pBlock[BT_TRIANGLE] = new C_Block((l_BaseName + "/triangle.obj").c_str(), m_uiWood1Tex, l_Mat); if (!m_pBlock[BT_TRIANGLE]->Initialized()) return false; //blue crosses l_Mat.setAmb(0.0, 0.0, 1.0, 1.0); l_Mat.setDif(0.0, 0.0, 1.0, 1.0); m_pBlock[BT_CROSS] = new C_Block((l_BaseName + "/cross.obj").c_str(), m_uiWood1Tex, l_Mat); if (!m_pBlock[BT_CROSS]->Initialized()) return false; //load the hand??? l_Mat.setAmb(1.0, 1.0, 1.0, 1.0); l_Mat.setDif(1.0, 1.0, 1.0, 1.0); m_pHand = new C_Hand("", 0, l_Mat); //Load the box tiles l_Mat.setAmb(1.0, 1.0, 1.0, 1.0); l_Mat.setDif(1.0, 1.0, 1.0, 1.0); m_pTiles[BT_SQUARE] = new C_3DObject((l_BaseName + "/tile_square.obj").c_str(), m_uiWood3Tex, l_Mat); if (!m_pTiles[BT_SQUARE]->Initialized()) return false; m_pTiles[BT_CIRCLE] = new C_3DObject((l_BaseName + "/tile_circle.obj").c_str(), m_uiWood3Tex, l_Mat); if (!m_pTiles[BT_CIRCLE]->Initialized()) return false; m_pTiles[BT_TRIANGLE] = new C_3DObject((l_BaseName + "/tile_triangle.obj").c_str(), m_uiWood3Tex, l_Mat); if (!m_pTiles[BT_TRIANGLE]->Initialized()) return false; m_pTiles[BT_CROSS] = new C_3DObject((l_BaseName + "/tile_cross.obj").c_str(), m_uiWood3Tex, l_Mat); if (!m_pTiles[BT_CROSS]->Initialized()) return false; m_pTiles[4] = new C_3DObject((l_BaseName + "/tile_no_hole.obj").c_str(), m_uiWood3Tex, l_Mat); if (!m_pTiles[4]->Initialized()) return false; //Load the box models m_pBox[0] = new C_Box((l_BaseName + "/box_small.obj").c_str(), m_uiWood2Tex, l_Mat, 2, 2, m_pTiles); if (!m_pBox[0]->Initialized()) return false; m_pBox[1] = new C_Box((l_BaseName + "/box_med.obj").c_str(), m_uiWood2Tex, l_Mat, 4, 2, m_pTiles); if (!m_pBox[1]->Initialized()) return false; m_pBox[2] = new C_Box((l_BaseName + "/box_large.obj").c_str(), m_uiWood2Tex, l_Mat, 4, 4, m_pTiles); if (!m_pBox[2]->Initialized()) return false; return true; } void C_MatchBloxEngine::DeleteModels() { //delete objects delete m_pEnvMap; delete m_pBlock[0]; delete m_pBlock[1]; delete m_pBlock[2]; delete m_pBlock[3]; //delete m_pHand; delete m_pBox[0]; delete m_pBox[1]; delete m_pBox[2]; delete m_pTiles[0]; delete m_pTiles[1]; delete m_pTiles[2]; delete m_pTiles[3]; delete m_pTiles[4]; //delete textures glDeleteTextures(1, &m_uiWood1Tex); glDeleteTextures(1, &m_uiWood2Tex); glDeleteTextures(1, &m_uiWood3Tex); } void C_MatchBloxEngine::LoadTexture(const char* f_BmpName, GLuint &f_uiTexHandle) { BitmapStruct l_Bmp; l_Bmp = BitmapLoad((char*)f_BmpName); f_uiTexHandle = (GLuint)l_Bmp.m_iImageId; glBindTexture(GL_TEXTURE_2D, f_uiTexHandle); //set the texture paramaters glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } void C_MatchBloxEngine::CursorMove(Vect3D_t &f_NewCursorPos) { //check the state to see what 3d object currently has to be considered to //be the cursor currently is and which bounding box we need to check for overlap bool l_bCollision = false; //indicates a collission has happend switch (m_State) { case ES_PLAYING_PUT_BLOCK: //cursor is the current block figure (that is being held by the player) //translate the bounding box of the block with the cursor position l_bCollision = CursorMove_PutBlock(f_NewCursorPos); break; default: //cursor is the hand object l_bCollision = CursorMove_GrabBlock(f_NewCursorPos); break; } if (l_bCollision) { //send a rumble message and set collision animation state //message_send() } } bool C_MatchBloxEngine::CursorMove_GrabBlock(Vect3D_t &f_NewCursorPos) { BoundingBox_t l_HandBB = m_pHand->GetBoundingBox() + f_NewCursorPos; if (m_WorldBox.Contains(l_HandBB)) { if (m_State == ES_PLAYING_GRAB_BLOCK) { //check for overlap with the current block if (m_pBlock[(int)m_CurrentBlock]->GetAbsBoundBox().Overlap(l_HandBB)) { //grabbing the block m_State = ES_PLAYING_PUT_BLOCK; m_pHand->SetState(HS_GRAB, m_uiCurrentTime); m_pBlock[(int)m_CurrentBlock]->SetPos(f_NewCursorPos); } } //set the new position m_pHand->SetPos(f_NewCursorPos); return false; } else { //the cursor is leaving the world box, which is a collision m_pHand->SetState(HS_COLLIDE, m_uiCurrentTime); return true; //true == collision } } bool C_MatchBloxEngine::CursorMove_PutBlock(Vect3D_t &f_NewCursorPos) { bool l_bCollision; BoundingBox_t l_BlockBB = m_pBlock[(int)m_CurrentBlock]->GetBoundingBox() + f_NewCursorPos, l_TileBB = m_pTiles[(int)m_CurrentBlock]->GetAbsBoundBox() + m_pBox[(int)m_CurrentBox]->GetPos(); if (m_WorldBox.Contains(l_BlockBB)) { //check for overlap with the block box if (m_pBox[(int)m_CurrentBox]->GetAbsBoundBox().Overlap(l_BlockBB)) { //check for overlap with the target hole tile if (l_TileBB.Overlap(l_BlockBB)) { Vect3D_t l_AbsHolePos = m_pBox[m_CurrentBox]->GetPos() + m_pTiles[m_CurrentBlock]->GetPos(); Vect3D_t l_PosDif = l_AbsHolePos - m_pBlock[(int)m_CurrentBlock]->GetPos(); double l_dXZProximity = l_PosDif.x * l_PosDif.x + l_PosDif.y * l_PosDif.y; //cout << "Block proximety: " << l_dXZProximity << endl; if (l_dXZProximity < m_GameSettings.m_dMinProximity) { //we have a winner l_bCollision = false; NewBlock(); } else { //overlap with the correct tile but not close enough to the hole l_bCollision = true; } } else { //overlap with the block box but not with the correct tile l_bCollision = true; } } else { //inside the world box and not overlapping the block box l_bCollision = false; } } else { //leaving the world box l_bCollision = true; } if (l_bCollision) { m_pBlock[(int)m_CurrentBlock]->SetState(BS_COLLIDE, m_uiCurrentTime); } else { if (m_State == ES_PLAYING_PUT_BLOCK) { m_pBlock[(int)m_CurrentBlock]->SetPos(f_NewCursorPos); } } //allways set mouse pointer position m_pHand->SetPos(f_NewCursorPos); return l_bCollision; } bool C_MatchBloxEngine::Check_PutBlock(Vect3D_t &f_CursPos, BoundingBox_t &f_CursBBox) { //check if the block is being put in the right hole //by testing whether the position of the cursor is close //enough to the center of the correct hole //note that the hole positions are relative to the position //of the box Vect3D_t l_AbsHolePos = m_pBox[m_CurrentBox]->GetPos() + m_pTiles[m_CurrentBlock]->GetPos(); Vect3D_t l_PosDif = l_AbsHolePos - f_CursPos; double l_dXZProximity = l_PosDif.x * l_PosDif.x + l_PosDif.y * l_PosDif.y; BoundingBox_t l_HoleBBox = m_pTiles[m_CurrentBlock]->GetAbsBoundBox() + m_pBox[m_CurrentBox]->GetPos(); //first check if the block is being put in the right tile with //the right xz-proximity if (l_HoleBBox.Overlap(f_CursBBox) && l_dXZProximity < m_GameSettings.m_dMinProximity) { //check if the block is deep enough inside the box to count as a point if (l_PosDif.z > m_GameSettings.m_dHoleDepth) { //yipee!! we have got a winner!! if (m_pCurrentSession->NextTurn(m_CurrentBlock)) { NewBlock(); } else { GameFinished(); } return true; } //not a win but also no collison -> set the position m_pBlock[(int)m_CurrentBlock]->SetPos(f_CursPos); return true; } return false; } bool C_MatchBloxEngine::ConvertWiimoteToWorld(input_payload_wiimote *f_pWiimoteMsg, Vect3D_t *f_pWorldPos) { if (!f_pWiimoteMsg->posDataValid) { return false; } Vect3D_t l_RelPos(f_pWiimoteMsg->relX, f_pWiimoteMsg->relY, f_pWiimoteMsg->Zdist); //use the world bounding box dimensions to convert the relative position to world coordinates //z is in mm, cap it to 500mm from the initial wiimote distance //using the initial distance l_RelPos.z = (l_RelPos.z - m_dInitialWiimoteDist + 500.0) / 1000.0; if (l_RelPos.z < 0.0) l_RelPos.z = 0.0; else if (l_RelPos.z > 1.0) l_RelPos.z = 1.0; Vect3D_t l_WorldSize = m_WorldBox.m_Max - m_WorldBox.m_Min; *f_pWorldPos = m_WorldBox.m_Min + (l_RelPos * l_WorldSize); //cout << "Z_world: " << std::fixed << std::setprecision(2) << setw(10) << left << f_pWorldPos->z; //smooth the world z coordinate in two passes (first average the last 25 samples) //and then smooth exponentially over the averages f_pWorldPos->z = m_pWorldZSmoother[0]->Smooth(f_pWorldPos->z); f_pWorldPos->z = m_pWorldZSmoother[1]->Smooth(f_pWorldPos->z); //cout << "Z_world_smoof: " << std::fixed << std::setprecision(2) << left << f_pWorldPos->z << endl; return true; } void C_MatchBloxEngine::NewBlock() { if (m_pCurrentSession->NextTurn(m_CurrentBlock)) { //move a block into the sky m_pBlock[(int)m_CurrentBlock ]->SetPos(0.0, 10.0, 0.0); m_State = ES_PLAYING_GRAB_BLOCK; } else { m_State = ES_FINISHED; } } void C_MatchBloxEngine::GameFinished() { //save session results to database }