Game client codebase including: - CharacterActionControl: Character and creature management - GlobalScript: Network, items, skills, quests, utilities - RYLClient: Main client application with GUI and event handlers - Engine: 3D rendering engine (RYLGL) - MemoryManager: Custom memory allocation - Library: Third-party dependencies (DirectX, boost, etc.) - Tools: Development utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
429 lines
14 KiB
C++
429 lines
14 KiB
C++
//------------------------------------------------------------------------------
|
|
// File: Demonstration.CPP
|
|
//
|
|
// Desc: DirectShow sample code
|
|
// Implementation of CDemonstration,
|
|
// "special effects" module to represent capabilities of VMR.
|
|
// This class is called from CVMRMixDlg by "Play" button.
|
|
// CDemonstration contains CVMRCore member (VMR engine) through which
|
|
// it performs initialization of the graph builder and presentation.
|
|
//
|
|
// Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "stdafx.h"
|
|
#include "Demonstration.h"
|
|
#include <math.h>
|
|
|
|
#define DO_NOT_RUN_YET true
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Perform
|
|
// Desc: Performs presentation. Just call it from the 'parent' dialog
|
|
// Return HRESULT
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CDemonstration::Perform()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
clock_t tStart;
|
|
clock_t tCurrent;
|
|
clock_t tUpdate;
|
|
|
|
if( false == m_bInitialized )
|
|
{
|
|
hr = Initialize();
|
|
}
|
|
if( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
try
|
|
{
|
|
hr = m_pCore->Play();
|
|
if( FAILED(hr) )
|
|
{
|
|
sprintf( m_szMsg, "Failed to run VMR, method returned %s\n", hresultNameLookup(hr));
|
|
OutputDebugString(m_szMsg);
|
|
return hr;
|
|
}
|
|
}
|
|
catch(...)
|
|
{
|
|
OutputDebugString("Unhandled exception in CDemonstration::Perform()\n");
|
|
ShellAbort(m_pCore);
|
|
return E_POINTER;
|
|
}
|
|
|
|
tStart = clock();
|
|
tCurrent = clock();
|
|
tUpdate = clock();
|
|
|
|
|
|
// presentation loop: wait until presentation is over
|
|
// or user closed the window and VMRCore is inactive
|
|
while( (tCurrent - tStart) / CLOCKS_PER_SEC < m_MList.GetAvgDuration() / 10000000L &&
|
|
m_pCore->IsActive() )
|
|
{
|
|
Sleep(10);
|
|
UpdateStreams(tStart);
|
|
tCurrent = clock();
|
|
}// while
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Initialize()
|
|
// Desc: Initializes demonstration; fills start parameters,
|
|
// creates the graph for VMR; renders files
|
|
// Return: HRESULT
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CDemonstration::Initialize()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LONGLONG llCurrent = 0L;
|
|
LONGLONG llStop = 0L;
|
|
LONGLONG llDelay = 0L;
|
|
clock_t tStart;
|
|
|
|
// calculate playback time
|
|
m_MList.AdjustDuration();
|
|
|
|
m_MList.SetInitialParameters();
|
|
|
|
// create VMRCore
|
|
m_pCore = new CVMRCore(m_pDlg, &m_MList );
|
|
|
|
tStart = clock();
|
|
|
|
if( !m_pCore)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// here, VMRCore creates the graph and if successfully, runs IMediaControl
|
|
// for the first file from the media list
|
|
hr = m_pCore->Play(DO_NOT_RUN_YET);
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// if user selected "show bitmap" option, load the bitmap
|
|
// from the resource and apply color key
|
|
if( m_pDlg->IsBitmapToUse())
|
|
{
|
|
hr = SetAlphaBitmapColorKey(IDB_BITMAP_LOGO);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
OutputDebugString("Failed to SetAlphaBitmapColorKey() in CDemonstration::Initialize()\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
m_bInitialized = true;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// SetAlphaBitmapColorKey
|
|
// Desc: initializes proper members of VMRALPHABITMAP for solor key support
|
|
// Parameters: [in] UINT ImageID - resource ID
|
|
// Return: HRESULT
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CDemonstration::SetAlphaBitmapColorKey(UINT ImageID)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if( ! m_pCore )
|
|
{
|
|
OutputDebugString("Function SetAlphaBitmapColorKey() is called before initializing m_pCore\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
// prepare the valid default bitmap
|
|
if (GetValidVMRBitmap(ImageID) != FNS_PASS)
|
|
{
|
|
OutputDebugString("Unable to get a valid VMRALPHABITMAP\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
m_sBmpParams.fAlpha = 1.0f;
|
|
m_sBmpParams.clrSrcKey = RGB(255,255,255);
|
|
m_sBmpParams.dwFlags |= VMRBITMAP_SRCCOLORKEY;
|
|
|
|
try
|
|
{
|
|
hr = m_pCore->GetMixerBitmap()->SetAlphaBitmap(&m_sBmpParams);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
} // try
|
|
catch(...)
|
|
{
|
|
return ShellAbort(m_pCore);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// GetValidVMRBitmap
|
|
// Desc: creates bitmap compatible with VMR
|
|
// Parameters: [in] UINT ImageID - resource ID
|
|
// [out] VMRALPHABITMAP * vmrBmp - what IVMRMixerBitmap uses
|
|
// Return: HRESULT
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CDemonstration::GetValidVMRBitmap(UINT ImageID)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
long cx, cy;
|
|
HDC hdc;
|
|
HBITMAP hbmpVmr;
|
|
BITMAP bmp;
|
|
|
|
hr = m_pCore->GetVMRWndless()->GetNativeVideoSize(&cx, &cy, NULL, NULL);
|
|
if( FAILED(hr) )
|
|
{
|
|
OutputDebugString("Failed in GetNativeVideoSize()\n");
|
|
return hr;
|
|
}
|
|
|
|
// get compatible DC
|
|
hdc = GetDC(m_pCore->GetClientHwnd());
|
|
|
|
m_hbmp = CreateCompatibleBitmap(hdc, cx, cy); /*** RELEASE ***/
|
|
|
|
hbmpVmr = (HBITMAP)LoadImage(AfxGetInstanceHandle(),MAKEINTRESOURCE(ImageID),IMAGE_BITMAP,0,0,/*LR_LOADFROMFILE|*/LR_CREATEDIBSECTION);
|
|
if( !hbmpVmr )
|
|
{
|
|
OutputDebugString("Failed to load resource\n");
|
|
DeleteObject(m_hbmp);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Get size of the bitmap
|
|
GetObject( hbmpVmr, sizeof(bmp), &bmp );
|
|
|
|
HDC hdcBmp = CreateCompatibleDC(hdc); /*** RELEASE ***/
|
|
HDC hdcVMR = CreateCompatibleDC(hdc);
|
|
|
|
ReleaseDC(m_pCore->GetClientHwnd(), hdc);
|
|
|
|
HBITMAP hbmpold = (HBITMAP)SelectObject(hdcBmp, m_hbmp);// in hdcBmp, select hbmp
|
|
hbmpVmr = (HBITMAP)SelectObject(hdcVMR, hbmpVmr);// in hdcVmr, select hbmpVmr (the pic we loaded)
|
|
|
|
BitBlt(hdcBmp, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcVMR, 0, 0, SRCPAINT);// put loaded pic from hdcVmr to hdcBmp
|
|
DeleteObject(SelectObject(hdcVMR, hbmpVmr));// ok, we do not need hbmpVmr any more
|
|
DeleteDC(hdcVMR);
|
|
|
|
RECT rc;
|
|
|
|
ZeroMemory(&m_sBmpParams, sizeof(VMRALPHABITMAP) );
|
|
m_sBmpParams.dwFlags = VMRBITMAP_HDC;
|
|
m_sBmpParams.hdc = hdcBmp;
|
|
|
|
// set source rectangle (entire original bitmap)
|
|
SetRect(&rc, 0, 0, bmp.bmWidth, bmp.bmHeight);
|
|
m_sBmpParams.rSrc = rc;
|
|
|
|
float fCoeff = 0.2f + 1.7f * (float)rand()/RAND_MAX;
|
|
// set destination rectangle (keeping aspect ratio of the original image)
|
|
// please note that normalized rect is always in [0.0, 1.0] range for
|
|
// all its members
|
|
m_sBmpParams.rDest.left = 0.f;
|
|
m_sBmpParams.rDest.top = 0.f;
|
|
m_sBmpParams.rDest.right = 0.9f*(float)bmp.bmWidth / (float)cx;
|
|
m_sBmpParams.rDest.bottom = 0.9f*(float)bmp.bmHeight / (float)cy;
|
|
m_sBmpParams.fAlpha = 0.5f;
|
|
|
|
// quite important, otherwise VMR would give error
|
|
m_sBmpParams.pDDS = NULL;
|
|
|
|
// Note: this demo uses bitmap directly, but often it is better to create
|
|
// DirectDrawSurface of appropriate format and set m_sBmpParams.pDDS to it
|
|
// (especially if you experience performance issues)
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// UpdateStreams
|
|
// Desc: updates presentation parameters (destination rectangles and alpha level)
|
|
// for each media file
|
|
// Parameters: clock_t tStart -- presentation start; used as a 'time' variable
|
|
// in calculations
|
|
// Return: HRESULT
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CDemonstration::UpdateStreams(clock_t tStart)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int i;
|
|
clock_t tCurrent;
|
|
NORMALIZEDRECT rectD0;
|
|
NORMALIZEDRECT rectD;
|
|
NORMALIZEDRECT rectDRes;
|
|
double Alpha0;
|
|
double Alpha;
|
|
|
|
ASSERT( tStart >0 );
|
|
|
|
for( i=0; i< this->m_MList.Size(); i++)
|
|
{
|
|
if( false == m_MList.GetItem(i)->m_bInUse)
|
|
continue;
|
|
|
|
tCurrent = clock() - tStart;
|
|
|
|
rectD0 = m_MList.GetItem(i)->m_rD;
|
|
Alpha0 = m_MList.GetItem(i)->m_fAlpha;
|
|
|
|
Alpha = 1.f;
|
|
rectD.left = rectD.top = 0.f;
|
|
rectD.right = rectD.bottom = 1.f;
|
|
|
|
FountainPath( tCurrent,
|
|
(long)(m_MList.GetAvgDuration() / 10000000 * CLOCKS_PER_SEC),
|
|
i,
|
|
rectD0,
|
|
Alpha0,
|
|
&rectD,
|
|
&Alpha);
|
|
|
|
hr = m_pCore->GetMixerControl()->SetAlpha(i, (float)Alpha);
|
|
if( !SUCCEEDED(hr))
|
|
{
|
|
sprintf( m_szMsg, "Failure in CDemonstration::UpdateStreams, GetMixerControl()->SetAlpha, method returned %s\n",
|
|
hresultNameLookup(hr));
|
|
OutputDebugString(m_szMsg);
|
|
}
|
|
hr = m_pCore->GetMixerControl()->SetOutputRect(i, &rectD);
|
|
if( !SUCCEEDED(hr))
|
|
{
|
|
sprintf( m_szMsg, "Failure in CDemonstration::UpdateStreams, GetMixerControl()->SetOutputRect, method returned %s\n",
|
|
hresultNameLookup(hr));
|
|
OutputDebugString(m_szMsg);
|
|
}
|
|
|
|
hr = m_pCore->GetMixerControl()->GetOutputRect(i, &rectDRes);
|
|
if( !SUCCEEDED(hr))
|
|
{
|
|
sprintf( m_szMsg, "Failure in CDemonstration::UpdateStreams, GetMixerControl()->GetOutputRect, method returned %s\n",
|
|
hresultNameLookup(hr));
|
|
OutputDebugString(m_szMsg);
|
|
}
|
|
else
|
|
{
|
|
if( fabs(rectD.top - rectDRes.top) > 0.01 ||
|
|
fabs(rectD.bottom - rectDRes.bottom) > 0.01 ||
|
|
fabs(rectD.left - rectDRes.left) > 0.01 ||
|
|
fabs(rectD.right - rectDRes.right) > 0.01 )
|
|
{
|
|
sprintf( m_szMsg, "Failed invalidation of SetOutputRect(): required [l,t,r,b] = [%f,%f,%f,%f] and returned [%f,%f,%f,%f]\n",
|
|
rectD.left, rectD.top, rectD.right, rectD.bottom,
|
|
rectDRes.left, rectDRes.top, rectDRes.right, rectDRes.bottom );
|
|
OutputDebugString(m_szMsg);
|
|
}
|
|
}// else
|
|
|
|
}// for
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: FountainPath
|
|
// Purpose: creates 'movie fountain' effect; for each stream, center point
|
|
// moves by a random ellipse around the center of a playback window;
|
|
// size and alpha-level of stream's output rect changes by cosine with
|
|
// some random initial delay (to desynchronize streams)
|
|
// Parameters:
|
|
// [IN] long t - current timestamp (any units, we use relative time)
|
|
// [IN] long T - expected total playback time (in the same units as t)
|
|
// [IN] int n - id of particular stream we want to set
|
|
// [IN] NORMALIZEDRECT r0 - original OutputRect of the stream
|
|
// [IN] double A0 - original alpha level (used as a parameter for smoothly
|
|
// changing alpha level)
|
|
// [OUT] NORMALIZEDRECT * pR - dest. part of playback window
|
|
// [OUT] double * pA - new alpha level
|
|
//------------------------------------------------------------------------------
|
|
void CDemonstration::FountainPath( long t,
|
|
long T,
|
|
int n,
|
|
NORMALIZEDRECT r0,
|
|
double A0,
|
|
NORMALIZEDRECT * pR,
|
|
double * pA)
|
|
{
|
|
double cx0; // original center of the output rect, in normalized coordinates
|
|
double cy0;
|
|
double cx; // new center of the output rect, in normalized coordinates
|
|
double cy;
|
|
double cx1; // auxiliary center of the output rect, in normalized coordinates
|
|
double cy1;
|
|
double L0; // original half-diagonal measure of the rectangle
|
|
double w; // orig. rect width
|
|
double h; // orig. rect height
|
|
double beta; // shift in cosine for L(t). see below
|
|
double L; // new half-diagonal measure of the rectangle
|
|
double tau; // time in relative units
|
|
double gamma; // shift in sine for A(t). see below
|
|
double coeff; // coefficient that is used to create 'mirror-flip' effect
|
|
double dx; // new half-width
|
|
double dy; // new half-heights
|
|
|
|
// relative time, to have about 3 periods over the total playtime
|
|
tau = 18.0 * (double)t / T;
|
|
|
|
// alpha level, A = 0.2 + 0.8 sin( tau + gamma) where gamma is such that A(0)=A0
|
|
gamma = (A0 - 0.2)/0.8;
|
|
gamma = (gamma < -1.) ? -1. : gamma;
|
|
gamma = (gamma > 1. ) ? 1. : gamma;
|
|
gamma = asin( gamma);
|
|
*pA = 0.6 + 0.4 * sin(tau + gamma + A0);
|
|
|
|
cx0 = (r0.left + r0.right)/2.;
|
|
cy0 = (r0.top + r0.bottom)/2.;
|
|
w = r0.right - r0.left;
|
|
h = r0.bottom - r0.top;
|
|
L0 = 0.5 * sqrt(w*w + h*h);
|
|
|
|
L0 = (L0 < 0.0001) ? 0.1 : L0;
|
|
// now rectangle. Its half-diagonal measure L = 0.1 + 0.7 cos( tau + beta)
|
|
// where beta is such that L(0) = LO;
|
|
beta = (L0 - 0.1)/0.7;
|
|
beta = (beta < -1.) ? -1. : beta;
|
|
beta = (beta > 1.) ? 1. : beta;
|
|
beta = acos( beta);
|
|
L = 0.35 + 0.45 * cos( tau + beta + 3.*A0);
|
|
|
|
// center of the rectangle is moving by ellips
|
|
// cx = cx0 + (-1)^n 0.1 sin(tau);
|
|
// cy = cy0 - 0.2 + 0.2 cos( tau);
|
|
// and turn it buy... say, theta = 7.85 A0 - 1.57;
|
|
coeff = (n%2) ? -1. : 1.;
|
|
cx1 = cx0 + coeff * 0.2 * sin(tau + A0);
|
|
cy1 = cy0 - 0.05 + 0.2 * cos( tau + A0);
|
|
|
|
// the lines below are unnecessary, but we could want some
|
|
// additional transformation here, like turn trajectory ellipse
|
|
// by some angle
|
|
cx = cx1;
|
|
cy = cy1;
|
|
|
|
dx = L * w / L0;
|
|
dy = L * h / L0;
|
|
|
|
pR->left = (float)(cx - dx);
|
|
pR->right = (float)(cx + dx);
|
|
pR->top = (float)(cy - dy);
|
|
pR->bottom = (float)(cy + dy);
|
|
}
|
|
|
|
|