/*****************************************************************************\ FILE: CameraMove.cpp DESCRIPTION: The caller can create this object to tell it to move from point a to point b from time t1 to time t2. BryanSt 12/24/2000 Copyright (C) Microsoft Corp 2000-2001. All rights reserved. \*****************************************************************************/ #include "stdafx.h" #include #include #include #include #include #include #include "CameraMove.h" enum eCameraMoveType { cameraMoveLocation = 0, cameraRotate, cameraWait, }; typedef struct { eCameraMoveType type; D3DXVECTOR3 vSourceLoc; // For cameraMoveLocation and cameraRotate D3DXVECTOR3 vSourceTangent; // For cameraMoveLocation and cameraRotate D3DXVECTOR3 vDestLoc; // For cameraMoveLocation D3DXVECTOR3 vDestTangent; // For cameraMoveLocation and cameraRotate float fTime; // For cameraMoveLocation cameraRotate, and cameraWait int nMinFrames; int nMaxFrames; int nBatch; int nPreFetch; } CAMERA_MOVEMENT; CCameraMove::CCameraMove() { m_hdpaMovements = DPA_Create(4); m_fTimeInPreviousMovements = NULL; m_vLookAtLast = m_vUpVec = m_vLocLast = D3DXVECTOR3(0.0f, 0.0f, 0.0f); m_nCurrent = 0; m_fTimeInPreviousMovements = 0.0f; m_nFramesFromCurrent = 1; m_fTimeToLookAtPainting = 1.0f; DWORD dwSpeedSlider = DEFAULT_SPEEDSLIDER; if (g_pConfig) { m_fTimeToLookAtPainting = (float) g_pConfig->GetDWORDSetting(CONFIG_DWORD_VIEWPAINTINGTIME); dwSpeedSlider = g_pConfig->GetDWORDSetting(CONFIG_DWORD_SPEED_SLIDER); } m_fTimeToRotate = s_SpeedSettings[dwSpeedSlider].fTimeToRotate; m_nMinTurnFrames = s_SpeedSettings[dwSpeedSlider].nMinTurnFrames; m_nMaxTurnFrames = s_SpeedSettings[dwSpeedSlider].nMaxTurnFrames; m_fTimeToWalk = s_SpeedSettings[dwSpeedSlider].fTimeToWalk; m_nMinWalkFrames = s_SpeedSettings[dwSpeedSlider].nMinWalkFrames; m_nMaxWalkFrames = s_SpeedSettings[dwSpeedSlider].nMaxWalkFrames; } CCameraMove::~CCameraMove() { DeleteAllMovements(0.0f); } HRESULT CCameraMove::Init(D3DXVECTOR3 vStartLoc, D3DXVECTOR3 vStartTangent, D3DXVECTOR3 vUpVec) { HRESULT hr = S_OK; // Initialize member variables m_vUpVec = vUpVec; m_vLocLast = vStartLoc; m_vLookAtLast = vStartTangent; m_nFramesFromCurrent = 0; return hr; } HRESULT CCameraMove::CreateNextMove(D3DXVECTOR3 vSourceLoc, D3DXVECTOR3 vSourceTangent, D3DXVECTOR3 vDestLoc, D3DXVECTOR3 vDestTangent) { HRESULT hr = E_OUTOFMEMORY; if (m_hdpaMovements) { CAMERA_MOVEMENT * pNew = (CAMERA_MOVEMENT *) LocalAlloc(LPTR, sizeof(*pNew)); if (pNew) { D3DXVECTOR3 vDelta = (vSourceLoc - vDestLoc); float fLen = D3DXVec3Length(&vDelta); // How far are we traveling float fRatio = (fLen / 50.0f); // The speed values are stored per 50.0f distance pNew->type = cameraMoveLocation; pNew->vSourceLoc = vSourceLoc; pNew->vSourceTangent = vSourceTangent; pNew->vDestLoc = vDestLoc; pNew->vDestTangent = vDestTangent; pNew->fTime = (m_fTimeToWalk * fRatio); pNew->nMinFrames = (int) max((m_nMinWalkFrames * fRatio), 1); pNew->nMaxFrames = (int) max((m_nMaxWalkFrames * fRatio), 1); pNew->nBatch = 0; pNew->nPreFetch = 0; if (-1 != DPA_AppendPtr(m_hdpaMovements, pNew)) { hr = S_OK; } else { LocalFree(pNew); } } } return hr; } HRESULT CCameraMove::CreateNextRotate(D3DXVECTOR3 vSourceLoc, D3DXVECTOR3 vSourceTangent, D3DXVECTOR3 vDestTangent) { HRESULT hr = E_OUTOFMEMORY; if (m_hdpaMovements) { CAMERA_MOVEMENT * pNew = (CAMERA_MOVEMENT *) LocalAlloc(LPTR, sizeof(*pNew)); if (pNew) { float fDotProduct = D3DXVec3Dot(&vSourceTangent, &vDestTangent); float fRatio; if (fDotProduct) { float fRads = (float)acos(fDotProduct / max(1, (D3DXVec3Length(&vSourceTangent) * D3DXVec3Length(&vDestTangent)))); // How far are we traveling fRatio = (D3DXToDegree(fRads) / 90.0f); // The speed values are stored per 90.0f distance } else { // Assume a dot product of 0 means 90 degrees. fRatio = 1.0f; // The speed values are stored per 90.0f distance } pNew->type = cameraRotate; pNew->vSourceLoc = vSourceLoc; pNew->vSourceTangent = vSourceTangent; pNew->vDestLoc = vSourceLoc; pNew->vDestTangent = vDestTangent; pNew->fTime = (m_fTimeToRotate * fRatio); pNew->nMinFrames = (int) max((m_nMinTurnFrames * fRatio), 1); pNew->nMaxFrames = (int) max((m_nMaxTurnFrames * fRatio), 1); pNew->nBatch = 0; pNew->nPreFetch = 0; if (-1 != DPA_AppendPtr(m_hdpaMovements, pNew)) { hr = S_OK; } else { LocalFree(pNew); } } } return hr; } HRESULT CCameraMove::CreateNextWait(int nBatch, int nPreFetch, float fTime) { HRESULT hr = E_OUTOFMEMORY; if (-1.0f == fTime) { fTime = m_fTimeToLookAtPainting; } if (m_hdpaMovements) { CAMERA_MOVEMENT * pNew = (CAMERA_MOVEMENT *) LocalAlloc(LPTR, sizeof(*pNew)); if (pNew) { pNew->type = cameraWait; pNew->vSourceLoc = D3DXVECTOR3(0.0f, 0.0f, 0.0f); pNew->vSourceTangent = D3DXVECTOR3(0.0f, 0.0f, 0.0f); pNew->vDestLoc = D3DXVECTOR3(0.0f, 0.0f, 0.0f); pNew->vDestTangent = D3DXVECTOR3(0.0f, 0.0f, 0.0f); pNew->fTime = fTime; pNew->nMinFrames = 1; pNew->nMaxFrames = 1000000; pNew->nBatch = nBatch; pNew->nPreFetch = nPreFetch; if (-1 != DPA_AppendPtr(m_hdpaMovements, pNew)) { hr = S_OK; } else { LocalFree(pNew); } } } return hr; } HRESULT CCameraMove::SetCamera(IDirect3DDevice8 * pD3DDevice, FLOAT fTimeKeyIn) { HRESULT hr = E_INVALIDARG; if (pD3DDevice && m_hdpaMovements) { float fTimeInSegment = 0.0f; CAMERA_MOVEMENT * pCurrent = NULL; if (0 > m_nCurrent) { m_nCurrent = 0; } if (m_nCurrent >= DPA_GetPtrCount(m_hdpaMovements)) { hr = S_FALSE; // This means we left the room. } else { do { pCurrent = (CAMERA_MOVEMENT *) DPA_GetPtr(m_hdpaMovements, m_nCurrent); if (!pCurrent) { // ASSERT(FAILED(hr)); break; } else { float fTimePerFrameMin = (pCurrent->fTime / pCurrent->nMinFrames); fTimeInSegment = (fTimeKeyIn - m_fTimeInPreviousMovements); if (fTimeInSegment < 0) { fTimeInSegment = 0; } // Do we need to warp time in order to have enough frames for the motion so we don't // jump? if ((fTimeInSegment > (fTimePerFrameMin * m_nFramesFromCurrent)) && (m_nFramesFromCurrent <= pCurrent->nMinFrames)) { // Yes. float fTimeWarp = (fTimeInSegment - (fTimePerFrameMin * m_nFramesFromCurrent)); m_fTimeInPreviousMovements += fTimeWarp; fTimeInSegment = (fTimeKeyIn - m_fTimeInPreviousMovements); } if (fTimeInSegment > pCurrent->fTime) { m_fTimeInPreviousMovements += pCurrent->fTime; if (cameraRotate == pCurrent->type) { m_vLocLast = pCurrent->vSourceLoc; m_vLookAtLast = pCurrent->vDestTangent; } else if (cameraMoveLocation == pCurrent->type) { m_vLocLast = pCurrent->vDestLoc; m_vLookAtLast = pCurrent->vDestTangent; } m_nFramesFromCurrent = 0; m_nCurrent++; } else { m_nFramesFromCurrent++; hr = S_OK; break; } } } while (1); } if (S_OK == hr) // S_FALSE means we left the room, so do nothing. { D3DXVECTOR3 vEye = m_vLocLast; D3DXVECTOR3 vLookAt = (m_vLocLast + m_vLookAtLast); float fTimeRatio = (fTimeInSegment / pCurrent->fTime); float fTimeRemainingInSeg = 0.0f; switch (pCurrent->type) { case cameraMoveLocation: D3DXVec3Lerp(&vEye, &pCurrent->vSourceLoc, &pCurrent->vDestLoc, fTimeRatio); D3DXVec3Lerp(&vLookAt, &pCurrent->vSourceTangent, &pCurrent->vDestTangent, fTimeRatio); vLookAt += vEye; break; case cameraRotate: // TODO: Use D3DXVec3Lerp() instead. D3DXVec3Lerp(&vLookAt, &pCurrent->vSourceTangent, &pCurrent->vDestTangent, fTimeRatio); vLookAt += vEye; // vLookAt = (vEye + (pCurrent->vSourceTangent + (fTimeRatio * (pCurrent->vDestTangent - pCurrent->vSourceTangent)))); // How do we rotate? Quaternion. break; case cameraWait: if (m_nFramesFromCurrent > 1) { if ((2 == m_nFramesFromCurrent) && g_pPictureMgr) { DWORD dwMaxPixelSize = ((3 * g_dwHeight) / 4); // Let's take the hit now of converting an image into a texture object since we don't have // any work to do while looking at this painting. This can normally take 1.5 seconds, so // it's big perf hit to do it any other time. hr = g_pPictureMgr->PreFetch(pCurrent->nBatch, pCurrent->nPreFetch); } else { // We don't have any work remaining to do, so sleep so the computer can get some work // done. (Like in background services or let it do any paging that we may have caused) fTimeRemainingInSeg = (pCurrent->fTime - fTimeInSegment); int nSleepTime = 1000 * (int) fTimeRemainingInSeg; Sleep(nSleepTime); } } break; default: // Do nothing. break; }; D3DXMATRIX matView; D3DXMATRIX matIdentity; D3DXMatrixIdentity(&matIdentity); if (g_fOverheadViewTest) { static float s_fHeight = 600.0f; D3DXVECTOR3 vDelta = (vEye - vLookAt); vEye += D3DXVECTOR3(0.0f, s_fHeight, 0.0f); vEye += (4 * vDelta); D3DXMatrixLookAtLH(&matView, &vEye, &vLookAt, &m_vUpVec); } else { D3DXMatrixLookAtLH(&matView, &vEye, &vLookAt, &m_vUpVec); } // PrintLocation(TEXT("Camera angle at: %s and looking at: %s"), vEye, vLookAt); hr = pD3DDevice->SetTransform(D3DTS_VIEW, &matView); m_vEyePrev = vEye; m_vLookAtPrev = vLookAt; } else { D3DXMATRIX matView; D3DXMatrixLookAtLH(&matView, &m_vEyePrev, &m_vLookAtPrev, &m_vUpVec); // PrintLocation(TEXT("xxxxxx Camera angle at: %s and looking at: %s"), m_vEyePrev, m_vLookAtPrev); pD3DDevice->SetTransform(D3DTS_VIEW, &matView); } } else { DXUtil_Trace(TEXT("ERROR: pD3DDevice or m_hdpaMovements is NULL")); } return hr; } HRESULT CCameraMove::DeleteAllMovements(float fCurrentTime) { HRESULT hr = S_OK; if (m_hdpaMovements) { DPA_DestroyCallback(m_hdpaMovements, DPALocalFree_Callback, NULL); m_hdpaMovements = DPA_Create(4); } m_fTimeInPreviousMovements = fCurrentTime; m_nCurrent = 0; return hr; }