windows-nt/Source/XPSP1/NT/shell/osshell/control/scrnsave/museum/cameramove.cpp
2020-09-26 16:20:57 +08:00

422 lines
13 KiB
C++

/*****************************************************************************\
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 <d3d8.h>
#include <d3dx8.h>
#include <d3dsaver.h>
#include <d3d8rgbrast.h>
#include <dxutil.h>
#include <shlobj.h>
#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;
}