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>
384 lines
13 KiB
C++
384 lines
13 KiB
C++
//-----------------------------------------------------------------------------
|
|
// File: DShowTextures.cpp
|
|
//
|
|
// Desc: DirectShow sample code - adds support for DirectShow videos playing
|
|
// on a DirectX 8.0 texture surface. Turns the D3D texture tutorial into
|
|
// a recreation of the VideoTex sample from previous versions of DirectX.
|
|
//
|
|
// Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
#include "Textures.h"
|
|
#include "DShowTextures.h"
|
|
#include "DXUtil.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Global Constants
|
|
//-----------------------------------------------------------------------------
|
|
#define SOURCE_FILE TEXT("skiing.avi")
|
|
|
|
// An application can advertise the existence of its filter graph
|
|
// by registering the graph with a global Running Object Table (ROT).
|
|
// The GraphEdit application can detect and remotely view the running
|
|
// filter graph, allowing you to 'spy' on the graph with GraphEdit.
|
|
//
|
|
// To enable registration in this sample, define REGISTER_FILTERGRAPH.
|
|
//
|
|
#define REGISTER_FILTERGRAPH
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Global DirectShow pointers
|
|
//-----------------------------------------------------------------------------
|
|
CComPtr<IGraphBuilder> g_pGB; // GraphBuilder
|
|
CComPtr<IMediaControl> g_pMC; // Media Control
|
|
CComPtr<IMediaPosition> g_pMP; // Media Postion
|
|
CComPtr<IMediaEvent> g_pME; // Media Event
|
|
|
|
D3DFORMAT g_TextureFormat; // Texture format
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// InitDShowTextureRenderer : Create DirectShow filter graph and run the graph
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT InitDShowTextureRenderer(LPDIRECT3DTEXTURE8 pTexture)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr<IBaseFilter> pFTR; // Texture Renderer Filter
|
|
CComPtr<IPin> pFTRPinIn; // Texture Renderer Input Pin
|
|
CComPtr<IBaseFilter> pFSrc; // Source Filter
|
|
CComPtr<IPin> pFSrcPinOut; // Source Filter Output Pin
|
|
CTextureRenderer *pCTR; // DShow Texture renderer
|
|
|
|
// Create the filter graph
|
|
if (FAILED(g_pGB.CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC)))
|
|
return E_FAIL;
|
|
|
|
#ifdef REGISTER_FILTERGRAPH
|
|
// Register the graph in the Running Object Table (for debug purposes)
|
|
AddToROT(g_pGB);
|
|
#endif
|
|
|
|
// Create the Texture Renderer object
|
|
pCTR = new CTextureRenderer(NULL, &hr);
|
|
if (FAILED(hr))
|
|
{
|
|
Msg(TEXT("Could not create texture renderer object! hr=0x%x"), hr);
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Get a pointer to the IBaseFilter on the TextureRenderer, add it to graph
|
|
pFTR = pCTR;
|
|
if (FAILED(hr = g_pGB->AddFilter(pFTR, L"TEXTURERENDERER")))
|
|
{
|
|
Msg(TEXT("Could not add renderer filter to graph! hr=0x%x"), hr);
|
|
return hr;
|
|
}
|
|
|
|
// Determine the file to load based on DirectX Media path (from SDK)
|
|
// Use a helper function included in DXUtils.cpp
|
|
TCHAR strFileName[MAX_PATH];
|
|
WCHAR wFileName[MAX_PATH];
|
|
lstrcpy( strFileName, DXUtil_GetDXSDKMediaPath() );
|
|
lstrcat( strFileName, SOURCE_FILE );
|
|
|
|
#ifndef UNICODE
|
|
MultiByteToWideChar(CP_ACP, 0, strFileName, -1, wFileName, MAX_PATH);
|
|
#else
|
|
lstrcpy(wFileName, strFileName);
|
|
#endif
|
|
|
|
// Add the source filter
|
|
if (FAILED(hr = g_pGB->AddSourceFilter (wFileName, L"SOURCE", &pFSrc)))
|
|
{
|
|
Msg(TEXT("Could not create source filter to graph! hr=0x%x"), hr);
|
|
return hr;
|
|
}
|
|
|
|
// Find the source's output pin and the renderer's input pin
|
|
if (FAILED(hr = pFTR->FindPin(L"In", &pFTRPinIn)))
|
|
{
|
|
Msg(TEXT("Could not find input pin! hr=0x%x"), hr);
|
|
return hr;
|
|
}
|
|
|
|
if (FAILED(hr = pFSrc->FindPin(L"Output", &pFSrcPinOut)))
|
|
{
|
|
Msg(TEXT("Could not find output pin! hr=0x%x"), hr);
|
|
return hr;
|
|
}
|
|
|
|
// Connect these two filters
|
|
if (FAILED(hr = g_pGB->Connect(pFSrcPinOut, pFTRPinIn)))
|
|
{
|
|
Msg(TEXT("Could not connect pins! hr=0x%x"), hr);
|
|
return hr;
|
|
}
|
|
|
|
// Get the graph's media control, event & position interfaces
|
|
g_pGB.QueryInterface(&g_pMC);
|
|
g_pGB.QueryInterface(&g_pMP);
|
|
g_pGB.QueryInterface(&g_pME);
|
|
|
|
// Start the graph running;
|
|
if (FAILED(hr = g_pMC->Run()))
|
|
{
|
|
Msg(TEXT("Could not run the DirectShow graph! hr=0x%x"), hr);
|
|
return hr;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CheckMovieStatus: If the movie has ended, rewind to beginning
|
|
//-----------------------------------------------------------------------------
|
|
void CheckMovieStatus(void)
|
|
{
|
|
long lEventCode;
|
|
long lParam1;
|
|
long lParam2;
|
|
HRESULT hr;
|
|
|
|
// Check for completion events
|
|
hr = g_pME->GetEvent(&lEventCode, (LONG_PTR *) &lParam1, (LONG_PTR *) &lParam2, 0);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (EC_COMPLETE == lEventCode)
|
|
{
|
|
hr = g_pMP->put_CurrentPosition(0);
|
|
}
|
|
|
|
// Free any memory associated with this event
|
|
hr = g_pME->FreeEventParams(lEventCode, lParam1, lParam2);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CleanupDShow
|
|
//-----------------------------------------------------------------------------
|
|
void CleanupDShow(void)
|
|
{
|
|
#ifdef REGISTER_FILTERGRAPH
|
|
// Pull graph from Running Object Table (Debug)
|
|
RemoveFromROT();
|
|
#endif
|
|
|
|
// Shut down the graph
|
|
if (!(!g_pMC)) g_pMC->Stop();
|
|
if (!(!g_pMC)) g_pMC.Release();
|
|
if (!(!g_pME)) g_pME.Release();
|
|
if (!(!g_pMP)) g_pMP.Release();
|
|
if (!(!g_pGB)) g_pGB.Release();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CTextureRenderer constructor
|
|
//-----------------------------------------------------------------------------
|
|
CTextureRenderer::CTextureRenderer( LPUNKNOWN pUnk, HRESULT *phr )
|
|
: CBaseVideoRenderer(__uuidof(CLSID_TextureRenderer),
|
|
NAME("Texture Renderer"), pUnk, phr)
|
|
{
|
|
// Store and AddRef the texture for our use.
|
|
*phr = S_OK;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CTextureRenderer destructor
|
|
//-----------------------------------------------------------------------------
|
|
CTextureRenderer::~CTextureRenderer()
|
|
{
|
|
// Do nothing
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CheckMediaType: This method forces the graph to give us an R8G8B8 video
|
|
// type, making our copy to texture memory trivial.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CTextureRenderer::CheckMediaType(const CMediaType *pmt)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
VIDEOINFO *pvi;
|
|
|
|
// Reject the connection if this is not a video type
|
|
if( *pmt->FormatType() != FORMAT_VideoInfo ) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Only accept RGB24
|
|
pvi = (VIDEOINFO *)pmt->Format();
|
|
if(IsEqualGUID( *pmt->Type(), MEDIATYPE_Video) &&
|
|
IsEqualGUID( *pmt->Subtype(), MEDIASUBTYPE_RGB24))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// SetMediaType: Graph connection has been made.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CTextureRenderer::SetMediaType(const CMediaType *pmt)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Retrive the size of this media type
|
|
VIDEOINFO *pviBmp; // Bitmap info header
|
|
pviBmp = (VIDEOINFO *)pmt->Format();
|
|
m_lVidWidth = pviBmp->bmiHeader.biWidth;
|
|
m_lVidHeight = abs(pviBmp->bmiHeader.biHeight);
|
|
m_lVidPitch = (m_lVidWidth * 3 + 3) & ~(3); // We are forcing RGB24
|
|
|
|
// Create the texture that maps to this media type
|
|
if( FAILED( hr = D3DXCreateTexture(g_pd3dDevice,
|
|
m_lVidWidth, m_lVidHeight,
|
|
1, 0,
|
|
D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &g_pTexture ) ) )
|
|
{
|
|
Msg(TEXT("Could not create the D3DX texture! hr=0x%x"), hr);
|
|
return hr;
|
|
}
|
|
|
|
// D3DXCreateTexture can silently change the parameters on us
|
|
D3DSURFACE_DESC ddsd;
|
|
if ( FAILED( hr = g_pTexture->GetLevelDesc( 0, &ddsd ) ) ) {
|
|
Msg(TEXT("Could not get level Description of D3DX texture! hr = 0x%x"), hr);
|
|
return hr;
|
|
}
|
|
g_TextureFormat = ddsd.Format;
|
|
if (g_TextureFormat != D3DFMT_A8R8G8B8 &&
|
|
g_TextureFormat != D3DFMT_A1R5G5B5) {
|
|
Msg(TEXT("Texture is format we can't handle! Format = 0x%x"), g_TextureFormat);
|
|
return VFW_E_TYPE_NOT_ACCEPTED;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// DoRenderSample: A sample has been delivered. Copy it to the texture.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CTextureRenderer::DoRenderSample( IMediaSample * pSample )
|
|
{
|
|
BYTE *pBmpBuffer, *pTxtBuffer; // Bitmap buffer, texture buffer
|
|
LONG lTxtPitch; // Pitch of bitmap, texture
|
|
|
|
// Get the video bitmap buffer
|
|
pSample->GetPointer( &pBmpBuffer );
|
|
|
|
// Lock the Texture
|
|
D3DLOCKED_RECT d3dlr;
|
|
if (FAILED(g_pTexture->LockRect(0, &d3dlr, 0, 0)))
|
|
return E_FAIL;
|
|
|
|
// Get the texture buffer & pitch
|
|
pTxtBuffer = static_cast<byte *>(d3dlr.pBits);
|
|
lTxtPitch = d3dlr.Pitch;
|
|
|
|
// Copy the bits
|
|
// OPTIMIZATION OPPORTUNITY: Use a video and texture
|
|
// format that allows a simpler copy than this one.
|
|
if (g_TextureFormat == D3DFMT_A8R8G8B8) {
|
|
for(int y = 0; y < m_lVidHeight; y++ ) {
|
|
BYTE *pBmpBufferOld = pBmpBuffer;
|
|
BYTE *pTxtBufferOld = pTxtBuffer;
|
|
for (int x = 0; x < m_lVidWidth; x++) {
|
|
pTxtBuffer[0] = pBmpBuffer[0];
|
|
pTxtBuffer[1] = pBmpBuffer[1];
|
|
pTxtBuffer[2] = pBmpBuffer[2];
|
|
pTxtBuffer[3] = 0xff;
|
|
pTxtBuffer += 4;
|
|
pBmpBuffer += 3;
|
|
}
|
|
pBmpBuffer = pBmpBufferOld + m_lVidPitch;
|
|
pTxtBuffer = pTxtBufferOld + lTxtPitch;
|
|
}
|
|
}
|
|
|
|
if (g_TextureFormat == D3DFMT_A1R5G5B5) {
|
|
for(int y = 0; y < m_lVidHeight; y++ ) {
|
|
BYTE *pBmpBufferOld = pBmpBuffer;
|
|
BYTE *pTxtBufferOld = pTxtBuffer;
|
|
for (int x = 0; x < m_lVidWidth; x++) {
|
|
*(WORD *)pTxtBuffer = (WORD)
|
|
(0x8000 +
|
|
((pBmpBuffer[2] & 0xF8) << 7) +
|
|
((pBmpBuffer[1] & 0xF8) << 2) +
|
|
(pBmpBuffer[0] >> 3));
|
|
pTxtBuffer += 2;
|
|
pBmpBuffer += 3;
|
|
}
|
|
pBmpBuffer = pBmpBufferOld + m_lVidPitch;
|
|
pTxtBuffer = pTxtBufferOld + lTxtPitch;
|
|
}
|
|
}
|
|
|
|
|
|
// Unlock the Texture
|
|
if (FAILED(g_pTexture->UnlockRect(0)))
|
|
return E_FAIL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
#ifdef REGISTER_FILTERGRAPH
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Running Object Table functions: Used to debug. By registering the graph
|
|
// in the running object table, GraphEdit is able to connect to the running
|
|
// graph. This code should be removed before the application is shipped in
|
|
// order to avoid third parties from spying on your graph.
|
|
//-----------------------------------------------------------------------------
|
|
DWORD dwROTReg = 0xfedcba98;
|
|
|
|
HRESULT AddToROT(IUnknown *pUnkGraph)
|
|
{
|
|
IMoniker * pmk;
|
|
IRunningObjectTable *pirot;
|
|
if (FAILED(GetRunningObjectTable(0, &pirot))) {
|
|
return E_FAIL;
|
|
}
|
|
WCHAR wsz[256];
|
|
wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR) 0, GetCurrentProcessId());
|
|
HRESULT hr = CreateItemMoniker(L"!", wsz, &pmk);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = pirot->Register(0, pUnkGraph, pmk, &dwROTReg);
|
|
pmk->Release();
|
|
}
|
|
pirot->Release();
|
|
return hr;
|
|
}
|
|
|
|
void RemoveFromROT(void)
|
|
{
|
|
IRunningObjectTable *pirot;
|
|
if (SUCCEEDED(GetRunningObjectTable(0, &pirot))) {
|
|
pirot->Revoke(dwROTReg);
|
|
pirot->Release();
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Msg: Display an error message box if needed
|
|
//-----------------------------------------------------------------------------
|
|
void Msg(TCHAR *szFormat, ...)
|
|
{
|
|
TCHAR szBuffer[512];
|
|
|
|
va_list pArgs;
|
|
va_start(pArgs, szFormat);
|
|
_vstprintf(szBuffer, szFormat, pArgs);
|
|
va_end(pArgs);
|
|
|
|
MessageBox(NULL, szBuffer, TEXT("DirectShow Texture3D Sample"),
|
|
MB_OK | MB_ICONERROR);
|
|
}
|