You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1133 lines
32 KiB
1133 lines
32 KiB
//====== Copyright 2010, Valve Corporation, All rights reserved. ==============
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================
|
|
|
|
#include "filesystem.h"
|
|
#include "tier1/strtools.h"
|
|
#include "tier1/utllinkedlist.h"
|
|
#include "tier1/keyvalues.h"
|
|
#include "materialsystem/imaterial.h"
|
|
#include "materialsystem/imaterialsystem.h"
|
|
#include "materialsystem/materialsystemutil.h"
|
|
#include "materialsystem/itexture.h"
|
|
#include "vtf/vtf.h"
|
|
#include "pixelwriter.h"
|
|
#include "tier3/tier3.h"
|
|
#include "platform.h"
|
|
#include "avi/iquicktime.h"
|
|
#include "quicktime.h"
|
|
|
|
#if defined ( WIN32 )
|
|
#if defined ( QUICKTIME_VIDEO )
|
|
#include <WinDef.h>
|
|
#include <../dx9sdk/include/dsound.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
#define ZeroVar( var ) V_memset( &var, 0, sizeof( var) )
|
|
#define SAFE_DELETE( var ) if ( var != NULL ) { delete var; var = NULL; }
|
|
#define SAFE_DELETE_ARRAY( var ) if ( var != NULL ) { delete[] var; var = NULL; }
|
|
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
|
|
#define AssertExit( _exp ) Assert( _exp )
|
|
#define AssertExitF( _exp ) Assert( _exp )
|
|
|
|
#else
|
|
|
|
#define AssertExit( _exp ) if ( !( _exp ) ) return;
|
|
#define AssertExitF( _exp ) if ( !( _exp ) ) return false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Singleton
|
|
//-----------------------------------------------------------------------------
|
|
static CQuickTime g_QUICKTIME;
|
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CQuickTime, IQuickTime, QUICKTIME_INTERFACE_VERSION, g_QUICKTIME );
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Inherited from ITextureRegenerator
|
|
//-----------------------------------------------------------------------------
|
|
void CQuicktimeMaterialRGBTextureRegenerator::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
|
|
{
|
|
AssertExit( pVTFTexture != NULL );
|
|
|
|
// Error condition
|
|
if ( ( pVTFTexture->FrameCount() > 1 ) || ( pVTFTexture->FaceCount() > 1 ) || ( pVTFTexture->MipCount() > 1 ) || ( pVTFTexture->Depth() > 1 ) )
|
|
{
|
|
memset( pVTFTexture->ImageData(), 0xAA, pVTFTexture->ComputeTotalSize() );
|
|
return;
|
|
}
|
|
|
|
// do we not have a video to install, or a video buffer that is too big?
|
|
if ( m_pQTMaterial->m_BitMapData == NULL /* || m_VideoFrameBufferSize < pVTFTexture->ComputeMipSize( 0 ) */ )
|
|
{
|
|
memset( pVTFTexture->ImageData(), 0xCC, pVTFTexture->ComputeTotalSize() );
|
|
return;
|
|
}
|
|
|
|
// Need to verify we have compatible formats
|
|
Assert( pVTFTexture->Format() == IMAGE_FORMAT_BGRA8888 );
|
|
Assert( pVTFTexture->RowSizeInBytes( 0 ) >= pVTFTexture->Width() * 4 );
|
|
Assert( pVTFTexture->Width() >= m_nSourceWidth );
|
|
Assert( pVTFTexture->Height() >= m_nSourceHeight );
|
|
|
|
// simplest of image copies, one line at a time
|
|
BYTE *ImageData = pVTFTexture->ImageData();
|
|
BYTE *SrcData = (BYTE*) m_pQTMaterial->m_BitMapData;
|
|
int dstStride = pVTFTexture->RowSizeInBytes( 0 );
|
|
int srcStride = m_nSourceWidth * 4;
|
|
int rowSize = m_nSourceWidth * 4;
|
|
|
|
// copy the rows of data
|
|
for ( int y = 0; y < m_nSourceHeight; y++ )
|
|
{
|
|
memcpy( ImageData, SrcData, rowSize);
|
|
ImageData+= dstStride;
|
|
SrcData+= srcStride;
|
|
}
|
|
|
|
}
|
|
|
|
void CQuicktimeMaterialRGBTextureRegenerator::Release()
|
|
{
|
|
// we don't invoke the destructor here, we're not using the no-release extensions
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructor
|
|
//-----------------------------------------------------------------------------
|
|
CQuickTimeMaterial::CQuickTimeMaterial() :
|
|
m_pFileName( NULL )
|
|
#if defined ( QUICKTIME_VIDEO )
|
|
,m_MovieGWorld( NULL ),
|
|
m_QTMovie( NULL ),
|
|
m_AudioContext( NULL ),
|
|
m_BitMapData( NULL )
|
|
#endif
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Destructor
|
|
//-----------------------------------------------------------------------------
|
|
CQuickTimeMaterial::~CQuickTimeMaterial()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
|
|
void CQuickTimeMaterial::SetQTFileName( const char* theQTMovieFileName )
|
|
{
|
|
SAFE_DELETE_ARRAY( m_pFileName );
|
|
|
|
if ( theQTMovieFileName != NULL )
|
|
{
|
|
int sLen = V_strlen( theQTMovieFileName );
|
|
AssertMsg( sLen > 0 && sLen <= cMaxQTFileNameLen, "Bad Movie FileName" );
|
|
m_pFileName = new char[ sLen + 1 ];
|
|
V_strcpy( m_pFileName, theQTMovieFileName );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CQuickTimeMaterial::Reset()
|
|
{
|
|
SetQTFileName( NULL );
|
|
|
|
ZeroVar( m_TextureName );
|
|
ZeroVar( m_MaterialName );
|
|
|
|
DestroyProceduralTexture();
|
|
DestroyProceduralMaterial();
|
|
|
|
SAFE_DELETE_ARRAY( m_BitMapData );
|
|
|
|
m_TexCordU = 0.0f;
|
|
m_TexCordV = 0.0f;
|
|
|
|
m_bActive = false;
|
|
m_bLoopMovie = false;
|
|
|
|
m_bMoviePlaying = false;
|
|
m_MovieBeganPlayingTime = 0.0;
|
|
m_MovieCurrentTime = 0.0;
|
|
|
|
|
|
#if defined ( QUICKTIME_VIDEO )
|
|
|
|
if ( m_AudioContext != NULL )
|
|
{
|
|
QTAudioContextRelease( m_AudioContext );
|
|
m_AudioContext = NULL;
|
|
}
|
|
|
|
if ( m_MovieGWorld != NULL )
|
|
{
|
|
DisposeGWorld( m_MovieGWorld );
|
|
m_MovieGWorld = NULL;
|
|
}
|
|
|
|
if ( m_QTMovie != NULL )
|
|
{
|
|
DisposeMovie( m_QTMovie );
|
|
m_QTMovie = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Initializes the material
|
|
//-----------------------------------------------------------------------------
|
|
bool CQuickTimeMaterial::Init( const char *pMaterialName, const char *pFileName, const char *pPathID )
|
|
{
|
|
// Determine the full path name of the video file
|
|
char pQTFileName[ MAX_PATH ];
|
|
char pFullQTFileName[ MAX_PATH ];
|
|
Q_snprintf( pQTFileName, sizeof( pQTFileName ), "%s", pFileName );
|
|
V_SetExtension( pQTFileName, ".mov", sizeof( pQTFileName ) );
|
|
if ( !g_pFullFileSystem->RelativePathToFullPath( pQTFileName, pPathID, pFullQTFileName, sizeof( pFullQTFileName ) ) )
|
|
{
|
|
// A file by that name was not found
|
|
Assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
OpenQTMovie( pFullQTFileName );
|
|
if ( !m_bActive )
|
|
{
|
|
// The file was unable to be opened
|
|
Assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
// Now we can properly setup out regenerators
|
|
m_TextureRegen.SetParentMaterial( this, m_VideoFrameWidth, m_VideoFrameHeight);
|
|
|
|
CreateProceduralTexture( pMaterialName );
|
|
CreateProceduralMaterial( pMaterialName );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void CQuickTimeMaterial::Shutdown( void )
|
|
{
|
|
CloseQTFile();
|
|
|
|
DestroyProceduralMaterial();
|
|
DestroyProceduralTexture();
|
|
|
|
Reset();
|
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Updates our scene
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CQuickTimeMaterial::Update( void )
|
|
{
|
|
Assert( m_bActive );
|
|
#if defined ( QUICKTIME_VIDEO )
|
|
|
|
OSType qTypes[1] = { VisualMediaCharacteristic };
|
|
|
|
// is this our first frame?
|
|
if ( !m_bMoviePlaying )
|
|
{
|
|
TimeValue startTime = -1;
|
|
short qFlags = nextTimeMediaSample + nextTimeEdgeOK;
|
|
|
|
GetMovieNextInterestingTime( m_QTMovie, qFlags, 1, qTypes, (TimeValue) 0, fixed1, &startTime, NULL);
|
|
Assert( GetMoviesError() == noErr );
|
|
|
|
GetMovieNextInterestingTime( m_QTMovie, nextTimeMediaSample, 1, qTypes, startTime, fixed1, &m_NextInterestingTimeToPlay, NULL);
|
|
Assert( GetMoviesError() == noErr );
|
|
|
|
SetMovieTimeValue( m_QTMovie, startTime );
|
|
Assert( GetMoviesError() == noErr );
|
|
|
|
m_LastInterestingTimePlayed = startTime;
|
|
|
|
m_MovieBeganPlayingTime = Plat_FloatTime();
|
|
|
|
m_bMoviePlaying = true;
|
|
}
|
|
else // we've drawn at least one frame before
|
|
{
|
|
// Get Current Time.. are we done playing the movie?
|
|
double rightNow = Plat_FloatTime();
|
|
m_MovieCurrentTime = rightNow - m_MovieBeganPlayingTime;
|
|
|
|
// did we hit the end of the movie?
|
|
if ( m_MovieCurrentTime >= m_QTMovieDurationinSec || m_NextInterestingTimeToPlay == -2 )
|
|
{
|
|
// If we're not looping, then report that we are done updating
|
|
if ( m_bLoopMovie == false )
|
|
{
|
|
return false;
|
|
}
|
|
// ok we're looping the movie, so....
|
|
|
|
// wrap around the current time
|
|
while ( m_MovieCurrentTime >= m_QTMovieDurationinSec )
|
|
{
|
|
m_MovieBeganPlayingTime+= m_QTMovieDurationinSec;
|
|
m_MovieCurrentTime = rightNow - m_MovieBeganPlayingTime;
|
|
}
|
|
|
|
// the next frame is set the frame 0, so it should trigger wrapping to the beginning
|
|
long currentMovieTime = ( long ) ( m_MovieCurrentTime * m_QTMovieTimeScale );
|
|
m_NextInterestingTimeToPlay = 0;
|
|
|
|
// Reset the movie to the wrapped around time (probably should compute starttime instead of assuming 0)
|
|
SetMovieTimeValue( m_QTMovie, currentMovieTime );
|
|
Assert( GetMoviesError() == noErr );
|
|
|
|
}
|
|
|
|
// where are we in terms of QT media units?
|
|
long currentMovieTime = ( long ) ( m_MovieCurrentTime * m_QTMovieTimeScale );
|
|
|
|
// Enough time passed to get to next frame
|
|
if ( currentMovieTime < m_NextInterestingTimeToPlay )
|
|
{
|
|
// nope.. use the previous frame
|
|
return true;
|
|
}
|
|
|
|
TimeValue nextTimeAfter = -1;
|
|
|
|
// do we need to skip any frames?
|
|
while ( true )
|
|
{
|
|
// look at the sample time after the one we past
|
|
GetMovieNextInterestingTime( m_QTMovie, nextTimeMediaSample, 1, qTypes, m_NextInterestingTimeToPlay, fixed1, &nextTimeAfter, NULL);
|
|
OSErr lastErr = GetMoviesError();
|
|
// hit the end of the movie?
|
|
if ( lastErr == invalidTime )
|
|
{
|
|
nextTimeAfter = -2;
|
|
break;
|
|
}
|
|
Assert( lastErr == noErr );
|
|
|
|
// is there a later frame we should be showing?
|
|
if ( nextTimeAfter <= currentMovieTime)
|
|
{
|
|
m_NextInterestingTimeToPlay = nextTimeAfter;
|
|
nextTimeAfter = -1;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// SetMovieTimeValue( m_QTMovie, m_NextInterestingTimeToPlay );
|
|
Assert( GetMoviesError() == noErr );
|
|
|
|
m_LastInterestingTimePlayed = m_NextInterestingTimeToPlay;
|
|
m_NextInterestingTimeToPlay = nextTimeAfter;
|
|
|
|
}
|
|
|
|
// move the movie along
|
|
UpdateMovie( m_QTMovie );
|
|
Assert( GetMoviesError() == noErr );
|
|
|
|
MoviesTask( m_QTMovie, 10L );
|
|
Assert( GetMoviesError() == noErr );
|
|
|
|
#if defined (WIN32)
|
|
|
|
HDC theHDC = (HDC) GetPortHDC( (GrafPtr) m_MovieGWorld );
|
|
HBITMAP theHBITMAP = (HBITMAP) GetPortHBITMAP( (GrafPtr) m_MovieGWorld );
|
|
|
|
// create the bitmapinfo header information
|
|
BITMAP bmp;
|
|
if ( !GetObject( theHBITMAP, sizeof(BITMAP), (LPSTR)&bmp) )
|
|
{
|
|
Assert( false );
|
|
return false;
|
|
}
|
|
|
|
Assert( bmp.bmWidth == m_QTMovieRect.right );
|
|
Assert( bmp.bmBitsPixel == 32 );
|
|
|
|
BITMAPINFO tempInfo;
|
|
V_memcpy( &tempInfo, &m_BitmapInfo, sizeof( tempInfo ) );
|
|
|
|
// Retrieve the pixel bits (no color table)
|
|
if ( !GetDIBits( theHDC, theHBITMAP, 0, (WORD) bmp.bmHeight, m_BitMapData, &tempInfo, DIB_RGB_COLORS))
|
|
{
|
|
AssertMsg( false, "writeBMP::GetDIB error" );
|
|
return false;
|
|
}
|
|
|
|
#elif defined ( OSX )
|
|
|
|
PixMapHandle thePixMap = GetGWorldPixMap( m_MovieGWorld );
|
|
if ( LockPixels( thePixMap ) )
|
|
{
|
|
void *pPixels = GetPixBaseAddr( thePixMap );
|
|
long rowStride = GetPixRowBytes( thePixMap );
|
|
int rowBytes = m_VideoFrameWidth * 4;
|
|
|
|
for (int y = 0; y < m_VideoFrameHeight; y++ )
|
|
{
|
|
BYTE *src = (BYTE*) pPixels + ( y * rowStride );
|
|
BYTE *dst = (BYTE*) m_BitMapData + ( y * rowBytes );
|
|
memcpy( dst, src, rowBytes );
|
|
}
|
|
|
|
UnlockPixels( thePixMap );
|
|
}
|
|
|
|
#endif
|
|
|
|
// Regenerate our texture
|
|
m_Texture->Download();
|
|
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Checks to see if the video has a new frame ready to download into the
|
|
// texture
|
|
//-----------------------------------------------------------------------------
|
|
bool CQuickTimeMaterial::ReadyForSwap( void )
|
|
{
|
|
AssertExitF( m_bActive );
|
|
|
|
// Waiting to play the first frame? Hell yes we are ready
|
|
if ( !m_bMoviePlaying ) return true;
|
|
|
|
// Get Current Time.. are we done playing the movie?
|
|
double CurrentTime = Plat_FloatTime() - m_MovieBeganPlayingTime;
|
|
|
|
// did we hit the end of the movie?
|
|
if ( CurrentTime >= m_QTMovieDurationinSec || m_NextInterestingTimeToPlay == -2 )
|
|
{
|
|
// if we are looping, we have another frame, otherwise no
|
|
return m_bLoopMovie;
|
|
}
|
|
|
|
// where are we in terms of QT media units?
|
|
long currentMovieTime = ( long ) ( CurrentTime * m_QTMovieTimeScale );
|
|
|
|
// Enough time passed to get to next frame??
|
|
if ( currentMovieTime < m_NextInterestingTimeToPlay )
|
|
{
|
|
// nope.. use the previous frame
|
|
return false;
|
|
}
|
|
|
|
// we have a new frame we want then..
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the material
|
|
//-----------------------------------------------------------------------------
|
|
IMaterial *CQuickTimeMaterial::GetMaterial()
|
|
{
|
|
return m_Material;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the texcoord range
|
|
//-----------------------------------------------------------------------------
|
|
void CQuickTimeMaterial::GetTexCoordRange( float *pMaxU, float *pMaxV )
|
|
{
|
|
// no texture?
|
|
if ( m_Texture == NULL )
|
|
{
|
|
*pMaxU = *pMaxV = 1.0f;
|
|
return;
|
|
}
|
|
|
|
int nTextureWidth = m_Texture->GetActualWidth();
|
|
int nTextureHeight = m_Texture->GetActualHeight();
|
|
*pMaxU = (float) m_VideoFrameWidth / (float) nTextureWidth;
|
|
*pMaxV = (float) m_VideoFrameHeight / (float) nTextureHeight;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the frame size of the QuickTime Video (stored in a subrect of the material itself)
|
|
//-----------------------------------------------------------------------------
|
|
void CQuickTimeMaterial::GetFrameSize( int *pWidth, int *pHeight )
|
|
{
|
|
*pWidth = m_VideoFrameWidth;
|
|
*pHeight = m_VideoFrameHeight;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Computes a power of two at least as big as the passed-in number
|
|
//-----------------------------------------------------------------------------
|
|
static inline int ComputeGreaterPowerOfTwo( int n )
|
|
{
|
|
int i = 1;
|
|
while ( i < n )
|
|
{
|
|
i <<= 1;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the frame rate of the Quicktime Video
|
|
//-----------------------------------------------------------------------------
|
|
int CQuickTimeMaterial::GetFrameRate( )
|
|
{
|
|
#if defined ( QUICKTIME_VIDEO )
|
|
return m_QTMoveFrameRate;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
int CQuickTimeMaterial::GetFrameCount( )
|
|
{
|
|
#if defined ( QUICKTIME_VIDEO )
|
|
return (int) ( m_QTMovieDurationinSec * m_QTMoveFrameRate );
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the frame for an QuickTime Material (use instead of SetTime)
|
|
//-----------------------------------------------------------------------------
|
|
void CQuickTimeMaterial::SetFrame( float flFrame )
|
|
{
|
|
flFrame;
|
|
AssertMsg( false, "method not implemented " );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the movie to loop continously instead of end, or not
|
|
//-----------------------------------------------------------------------------
|
|
void CQuickTimeMaterial::SetLooping( bool loop )
|
|
{
|
|
m_bLoopMovie = loop;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Initializes, shuts down the procedural texture
|
|
//-----------------------------------------------------------------------------
|
|
void CQuickTimeMaterial::CreateProceduralTexture( const char *pTextureName )
|
|
{
|
|
|
|
Assert( m_VideoFrameWidth >= cMinVideoFrameWidth && m_VideoFrameHeight >= cMinVideoFrameHeight &&
|
|
m_VideoFrameWidth <= cMaxVideoFrameWidth && m_VideoFrameHeight <= cMaxVideoFrameHeight);
|
|
Assert( pTextureName );
|
|
|
|
// Choose power-of-two textures which are at least as big as the AVI
|
|
int nWidth = ComputeGreaterPowerOfTwo( m_VideoFrameWidth );
|
|
int nHeight = ComputeGreaterPowerOfTwo( m_VideoFrameHeight );
|
|
|
|
// initialize the procedural texture as 32-it RGBA, w/o mipmaps
|
|
m_Texture.InitProceduralTexture( pTextureName, "VideoCacheTextures", nWidth, nHeight,
|
|
IMAGE_FORMAT_BGRA8888, TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT | TEXTUREFLAGS_NOMIP |
|
|
TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_SINGLECOPY | TEXTUREFLAGS_NOLOD );
|
|
|
|
// Use this to get the updated frame from the remote connection
|
|
m_Texture->SetTextureRegenerator( &m_TextureRegen /* , false */ );
|
|
|
|
// compute the texcoords
|
|
int nTextureWidth = m_Texture->GetActualWidth();
|
|
int nTextureHeight = m_Texture->GetActualHeight();
|
|
|
|
m_TexCordU = ( nTextureWidth > 0 ) ? (float) m_VideoFrameWidth / (float) nTextureWidth : 0.0f;
|
|
m_TexCordV = ( nTextureHeight > 0 ) ? (float) m_VideoFrameHeight / (float) nTextureHeight : 0.0f;
|
|
|
|
|
|
}
|
|
|
|
void CQuickTimeMaterial::DestroyProceduralTexture()
|
|
{
|
|
if ( m_Texture != NULL )
|
|
{
|
|
// DO NOT Call release on the Texture Regenerator, as it will destroy this object! bad bad bad
|
|
// instead we tell it to assign a NULL regenerator and flag it to not call release
|
|
m_Texture->SetTextureRegenerator( NULL /*, false */ );
|
|
// Texture, texture go away...
|
|
m_Texture.Shutdown( true );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Initializes, shuts down the procedural material
|
|
//-----------------------------------------------------------------------------
|
|
void CQuickTimeMaterial::CreateProceduralMaterial( const char *pMaterialName )
|
|
{
|
|
// create keyvalues if necessary
|
|
KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
|
|
{
|
|
pVMTKeyValues->SetString( "$basetexture", m_Texture->GetName() );
|
|
pVMTKeyValues->SetInt( "$nofog", 1 );
|
|
pVMTKeyValues->SetInt( "$spriteorientation", 3 );
|
|
pVMTKeyValues->SetInt( "$translucent", 1 );
|
|
pVMTKeyValues->SetInt( "$nolod", 1 );
|
|
pVMTKeyValues->SetInt( "$nomip", 1 );
|
|
pVMTKeyValues->SetInt( "$gammacolorread", 0 );
|
|
}
|
|
|
|
// FIXME: gak, this is backwards. Why doesn't the material just see that it has a funky basetexture?
|
|
m_Material.Init( pMaterialName, pVMTKeyValues );
|
|
m_Material->Refresh();
|
|
}
|
|
|
|
void CQuickTimeMaterial::DestroyProceduralMaterial()
|
|
{
|
|
// Store the internal material pointer for later use
|
|
IMaterial *pMaterial = m_Material;
|
|
m_Material.Shutdown();
|
|
materials->UncacheUnusedMaterials();
|
|
|
|
// Now be sure to free that material because we don't want to reference it again later, we'll recreate it!
|
|
if ( pMaterial != NULL )
|
|
{
|
|
pMaterial->DeleteIfUnreferenced();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Opens a movie file using quicktime
|
|
//-----------------------------------------------------------------------------
|
|
void CQuickTimeMaterial::OpenQTMovie( const char* theQTMovieFileName )
|
|
{
|
|
|
|
AssertExit( theQTMovieFileName != NULL );
|
|
|
|
#if defined ( QUICKTIME_VIDEO )
|
|
short theFile = 0;
|
|
FSSpec sfFile;
|
|
char fullPath[256];
|
|
OSErr status = 0;
|
|
|
|
// Set graphics port
|
|
#if defined ( WIN32 )
|
|
SetGWorld ( (CGrafPtr) GetNativeWindowPort( nil ), nil );
|
|
#elif defined ( OSX )
|
|
SetGWorld( nil, nil );
|
|
#endif
|
|
|
|
SetQTFileName( theQTMovieFileName );
|
|
|
|
#if defined ( OSX )
|
|
|
|
FSRef dirRef;
|
|
Boolean isDir;
|
|
|
|
status = FSPathMakeRef( (UInt8 *)theQTMovieFileName, &dirRef, &isDir );
|
|
if ( status == noErr )
|
|
{
|
|
status = FSGetCatalogInfo( &dirRef, kFSCatInfoNone, NULL, NULL, &sfFile, NULL );
|
|
Assert( status == noErr );
|
|
}
|
|
|
|
if ( status != noErr )
|
|
{
|
|
Reset();
|
|
return;
|
|
}
|
|
|
|
#elif defined ( WIN32 )
|
|
strcpy ( fullPath, theQTMovieFileName); // Copy full pathname
|
|
c2pstr ( fullPath ); // Convert to Pascal string
|
|
|
|
status = FSMakeFSSpec( 0, 0L, (const unsigned char *)&fullPath[0], &sfFile ); // Make file-system specification record
|
|
AssertExit( status == noErr );
|
|
|
|
#endif
|
|
status = OpenMovieFile( &sfFile, &theFile, fsRdPerm) ; // Open movie file
|
|
Assert( status == noErr );
|
|
if ( status != noErr )
|
|
{
|
|
CloseMovieFile( theFile );
|
|
Reset();
|
|
return;
|
|
}
|
|
|
|
status = NewMovieFromFile ( &m_QTMovie, theFile, nil, nil, newMovieActive, nil); // Get movie from file
|
|
Assert( status == noErr );
|
|
if ( status != noErr )
|
|
{
|
|
CloseMovieFile( theFile );
|
|
Reset();
|
|
return;
|
|
}
|
|
|
|
status = CloseMovieFile (theFile); // Close movie file
|
|
AssertExit( status == noErr );
|
|
|
|
// Now we need to extract the time info from the QT Movie
|
|
// Duration scale = 600 per second...
|
|
m_QTMovieTimeScale = GetMovieTimeScale( m_QTMovie );
|
|
m_QTMovieDuration = GetMovieDuration( m_QTMovie );
|
|
m_QTMovieDurationinSec = float ( m_QTMovieDuration ) / float ( m_QTMovieTimeScale );
|
|
|
|
Fixed movieRate = GetMoviePreferredRate( m_QTMovie );
|
|
m_QTMoveFrameRate = Fix2Long( movieRate );
|
|
|
|
// what size do we set the output rect to?
|
|
GetMovieNaturalBoundsRect(m_QTMovie, &m_QTMovieRect);
|
|
|
|
m_VideoFrameWidth = m_QTMovieRect.right;
|
|
m_VideoFrameHeight = m_QTMovieRect.bottom;
|
|
|
|
// Sanity check...
|
|
AssertExit( m_QTMovieRect.top == 0 && m_QTMovieRect.left == 0 && m_QTMovieRect.right >= 64 && m_QTMovieRect.right <= 1920 &&
|
|
m_QTMovieRect.bottom >= 48 && m_QTMovieRect.bottom <= 1200 && m_QTMovieRect.right % 4 == 0 );
|
|
|
|
// Setup a bitmap to store frames...
|
|
|
|
// compute image buffer size
|
|
m_BitMapDataSize = 4 * m_QTMovieRect.right * m_QTMovieRect.bottom;
|
|
|
|
|
|
#if defined ( WIN32 )
|
|
// Initialize bitmap info
|
|
ZeroVar( m_BitmapInfo );
|
|
m_BitmapInfo.bmiHeader.biSize = sizeof( m_BitmapInfo.bmiHeader );
|
|
m_BitmapInfo.bmiHeader.biWidth = (LONG) m_QTMovieRect.right;
|
|
m_BitmapInfo.bmiHeader.biHeight = -1 * (LONG) m_QTMovieRect.bottom;
|
|
m_BitmapInfo.bmiHeader.biPlanes = 1;
|
|
m_BitmapInfo.bmiHeader.biBitCount = 32;
|
|
m_BitmapInfo.bmiHeader.biCompression = 0; /* BI_RGB */
|
|
m_BitmapInfo.bmiHeader.biSizeImage = m_BitMapDataSize;
|
|
// the rest of the fields should be 0
|
|
|
|
#endif
|
|
|
|
// create buffer to hold a single frame
|
|
m_BitMapData = new byte[ m_BitMapDataSize ];
|
|
|
|
// Setup the QuiuckTime Graphics World for the Movie
|
|
status = QTNewGWorld( &m_MovieGWorld, k32BGRAPixelFormat, &m_QTMovieRect, nil, nil, 0 );
|
|
AssertExit( status == noErr );
|
|
|
|
// perform any needed gamma correction
|
|
// kQTUsePlatformDefaultGammaLevel = 0, /* When decompressing into this PixMap, gamma-correct to the platform's standard gamma. */
|
|
// kQTUseSourceGammaLevel = -1L, /* When decompressing into this PixMap, don't perform gamma-correction. */
|
|
// kQTCCIR601VideoGammaLevel = 0x00023333 /* 2.2, standard television video gamma.*/
|
|
// Fixed cGamma1_8 = 0x0001CCCC; // Gamma 1.8
|
|
// Fixed cGamma2_5 = 0x00028000; // Gamma 2.5
|
|
//
|
|
// On OSX it appears we need to set a gamma of 1.0 or 0x0001000 - the values are interpreted differently?
|
|
|
|
#if defined ( OSX )
|
|
Fixed decodeGamma = 0x00012000;
|
|
#elif defined ( WIN32 )
|
|
Fixed decodeGamma = 0x00023333;
|
|
#endif
|
|
|
|
// Get the pix map for the GWorld and adjust the gamma correction on it
|
|
PixMapHandle thePixMap = GetGWorldPixMap( m_MovieGWorld );
|
|
|
|
OSErr Status = QTSetPixMapHandleGammaLevel( thePixMap, decodeGamma );
|
|
AssertExit( Status == noErr );
|
|
|
|
Status = QTSetPixMapHandleRequestedGammaLevel( thePixMap, decodeGamma );
|
|
AssertExit( Status == noErr );
|
|
|
|
SetMovieGWorld( m_QTMovie, m_MovieGWorld, nil );
|
|
|
|
|
|
#if defined ( WIN32 )
|
|
|
|
WCHAR strGUID[39];
|
|
int numBytes = StringFromGUID2( DSDEVID_DefaultPlayback, (LPOLESTR) strGUID, 39); // CLSID_DirectSound is not what you want here
|
|
|
|
// create the audio context
|
|
CFStringRef deviceNameStrRef = NULL;
|
|
deviceNameStrRef = CFStringCreateWithCharacters(kCFAllocatorDefault,
|
|
(const UniChar*) strGUID,
|
|
(CFIndex) (numBytes -1) );
|
|
|
|
OSStatus result = QTAudioContextCreateForAudioDevice( NULL, deviceNameStrRef, NULL, &m_AudioContext );
|
|
#elif defined ( OSX )
|
|
|
|
OSStatus result = QTAudioContextCreateForAudioDevice( NULL, NULL, NULL, &m_AudioContext );
|
|
|
|
#endif
|
|
|
|
AssertExit( result == noErr );
|
|
|
|
// Set the audio context
|
|
|
|
result = SetMovieAudioContext( m_QTMovie, m_AudioContext );
|
|
AssertExit( result == noErr );
|
|
|
|
// Set the volume
|
|
ConVarRef volumeConVar( "volume" );
|
|
float sysVolume = 1.0f;
|
|
if ( volumeConVar.IsValid() )
|
|
sysVolume = volumeConVar.GetFloat();
|
|
clamp( sysVolume, 0.0f, 1.0f);
|
|
|
|
short movieVolume = (short) ( sysVolume * 256.0 );
|
|
|
|
SetMovieVolume( m_QTMovie, movieVolume );
|
|
|
|
// Start movie playback (get the sound rolling)
|
|
|
|
StartMovie( m_QTMovie );
|
|
|
|
m_bActive = true;
|
|
|
|
#if defined( WIN32 )
|
|
if ( deviceNameStrRef )
|
|
{
|
|
CFRelease( deviceNameStrRef );
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
void CQuickTimeMaterial::CloseQTFile()
|
|
{
|
|
#if defined ( QUICKTIME_VIDEO )
|
|
|
|
StopMovie( m_QTMovie );
|
|
|
|
SAFE_DELETE_ARRAY( m_BitMapData );
|
|
|
|
if ( m_AudioContext != NULL )
|
|
{
|
|
QTAudioContextRelease( m_AudioContext );
|
|
m_AudioContext = NULL;
|
|
}
|
|
|
|
if ( m_MovieGWorld != NULL )
|
|
{
|
|
DisposeGWorld( m_MovieGWorld );
|
|
m_MovieGWorld = NULL;
|
|
}
|
|
|
|
if ( m_QTMovie )
|
|
{
|
|
DisposeMovie( m_QTMovie );
|
|
m_QTMovie = NULL;
|
|
|
|
}
|
|
|
|
SetQTFileName( NULL );
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructor/destructor
|
|
//-----------------------------------------------------------------------------
|
|
CQuickTime::CQuickTime()
|
|
{
|
|
m_bQTInitialized = false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
CQuickTime::~CQuickTime()
|
|
{
|
|
// Make sure we shut down quicktime
|
|
ShutdownQuicktime();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Connect/disconnect
|
|
//-----------------------------------------------------------------------------
|
|
bool CQuickTime::Connect( CreateInterfaceFn factory )
|
|
{
|
|
ConnectTier1Libraries( &factory, 1 );
|
|
ConnectTier2Libraries( &factory, 1 );
|
|
|
|
if ( !( g_pFullFileSystem && materials ) )
|
|
{
|
|
Msg( "Quicktime failed to connect to a required system\n" );
|
|
}
|
|
return ( g_pFullFileSystem && materials );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Connect/disconnect
|
|
//-----------------------------------------------------------------------------
|
|
void CQuickTime::Disconnect( void )
|
|
{
|
|
// Make sure we shut down quicktime
|
|
ShutdownQuicktime();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Query Interface
|
|
//-----------------------------------------------------------------------------
|
|
void *CQuickTime::QueryInterface( const char *pInterfaceName )
|
|
{
|
|
if ( Q_strncmp( pInterfaceName, QUICKTIME_INTERFACE_VERSION, Q_strlen(QUICKTIME_INTERFACE_VERSION) + 1) == 0 )
|
|
{
|
|
return (IQuickTime*)this;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Init/shutdown
|
|
//-----------------------------------------------------------------------------
|
|
InitReturnVal_t CQuickTime::Init()
|
|
{
|
|
return SetupQuicktime() ? INIT_OK : INIT_FAILED;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void CQuickTime::Shutdown()
|
|
{
|
|
ShutdownQuicktime();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Create/destroy an QuickTime material
|
|
//-----------------------------------------------------------------------------
|
|
QUICKTIMEMaterial_t CQuickTime::CreateMaterial( const char *pMaterialName, const char *pFileName, const char *pPathID, int flags )
|
|
{
|
|
if ( ! m_bQTInitialized )
|
|
{
|
|
return QUICKTIMEMATERIAL_INVALID;
|
|
}
|
|
|
|
QUICKTIMEMaterial_t h = m_QTMaterials.AddToTail();
|
|
m_QTMaterials[h] = new CQuickTimeMaterial;
|
|
if ( m_QTMaterials[h]->Init( pMaterialName, pFileName, pPathID ) == false )
|
|
{
|
|
delete m_QTMaterials[h];
|
|
m_QTMaterials.Remove( h );
|
|
return QUICKTIMEMATERIAL_INVALID;
|
|
}
|
|
|
|
m_QTMaterials[h]->SetLooping( ( flags & QUICKTIME_LOOP_MOVIE ) == QUICKTIME_LOOP_MOVIE );
|
|
|
|
return h;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void CQuickTime::DestroyMaterial( QUICKTIMEMaterial_t h )
|
|
{
|
|
if ( h != QUICKTIMEMATERIAL_INVALID )
|
|
{
|
|
m_QTMaterials[h]->Shutdown();
|
|
delete m_QTMaterials[h];
|
|
m_QTMaterials.Remove( h );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Update the QuickTime Video Material
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CQuickTime::Update( QUICKTIMEMaterial_t hMaterial )
|
|
{
|
|
return ( hMaterial == QUICKTIMEMATERIAL_INVALID ) ? false : m_QTMaterials[hMaterial]->Update();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Determine if a new frame of the movie is ready for display
|
|
//-----------------------------------------------------------------------------
|
|
bool CQuickTime::ReadyForSwap( QUICKTIMEMaterial_t hMaterial )
|
|
{
|
|
return ( hMaterial == QUICKTIMEMATERIAL_INVALID ) ? false : m_QTMaterials[hMaterial]->ReadyForSwap();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the IMaterial associated with an QuickTime video material
|
|
//-----------------------------------------------------------------------------
|
|
IMaterial *CQuickTime::GetMaterial( QUICKTIMEMaterial_t h )
|
|
{
|
|
return ( h != QUICKTIMEMATERIAL_INVALID ) ? m_QTMaterials[h]->GetMaterial() : NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the max texture coordinate of the QuickTime Video on the texture
|
|
//-----------------------------------------------------------------------------
|
|
void CQuickTime::GetTexCoordRange( QUICKTIMEMaterial_t h, float *pMaxU, float *pMaxV )
|
|
{
|
|
if ( h != QUICKTIMEMATERIAL_INVALID )
|
|
{
|
|
m_QTMaterials[h]->GetTexCoordRange( pMaxU, pMaxV );
|
|
}
|
|
else
|
|
{
|
|
*pMaxU = *pMaxV = 1.0f;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the frame size of the Quicktime Video (is a subrect of the material itself)
|
|
//-----------------------------------------------------------------------------
|
|
void CQuickTime::GetFrameSize( QUICKTIMEMaterial_t h, int *pWidth, int *pHeight )
|
|
{
|
|
if ( h != QUICKTIMEMATERIAL_INVALID )
|
|
{
|
|
m_QTMaterials[h]->GetFrameSize( pWidth, pHeight );
|
|
}
|
|
else
|
|
{
|
|
*pWidth = *pHeight = 1;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the frame size of the QuickTime Video (is a subrect of the material itself)
|
|
//-----------------------------------------------------------------------------
|
|
int CQuickTime::GetFrameRate( QUICKTIMEMaterial_t h )
|
|
{
|
|
return ( h == QUICKTIMEMATERIAL_INVALID ) ? -1 : m_QTMaterials[h]->GetFrameRate();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the frame for an Quicktime Video material (use instead of SetTime)
|
|
//-----------------------------------------------------------------------------
|
|
void CQuickTime::SetFrame( QUICKTIMEMaterial_t h, float flFrame )
|
|
{
|
|
if ( h != QUICKTIMEMATERIAL_INVALID )
|
|
{
|
|
m_QTMaterials[h]->SetFrame( flFrame );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the frame rate of the Quicktime Video
|
|
//-----------------------------------------------------------------------------
|
|
int CQuickTime::GetFrameCount( QUICKTIMEMaterial_t h )
|
|
{
|
|
return ( h == QUICKTIMEMATERIAL_INVALID ) ? -1 : m_QTMaterials[h]->GetFrameCount();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Hooks up the houtput sound device
|
|
//-----------------------------------------------------------------------------
|
|
bool CQuickTime::SetSoundDevice( void *pDevice )
|
|
{
|
|
pDevice;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// functions to initialize and shut down QuickTime services
|
|
// ----------------------------------------------------------------------------
|
|
bool CQuickTime::SetupQuicktime()
|
|
{
|
|
m_bQTInitialized = false;
|
|
|
|
#if defined ( QUICKTIME_VIDEO )
|
|
#if defined ( WIN32 )
|
|
OSErr status = InitializeQTML( 0 );
|
|
|
|
// if -2903 then quicktime not installed on this system
|
|
if ( status != noErr )
|
|
{
|
|
if ( status == qtmlDllLoadErr )
|
|
{
|
|
//Plat_MessageBox( "VideoCache ERROR", "ERROR: QuickTime is not installed on this system. It is needed in order for the SFM Video Cache service to run" );
|
|
Assert( 0 );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Make sure we have version 7.04 or greater of quicktime
|
|
long version = 0;
|
|
status = Gestalt(gestaltQuickTime, &version);
|
|
if ( (status != noErr) || ( version < 0x07048000) )
|
|
{
|
|
TerminateQTML();
|
|
return false;
|
|
}
|
|
|
|
#endif
|
|
|
|
OSErr status2 = EnterMovies(); // Initialize QuickTime Movie Toolbox
|
|
if ( status2 != noErr )
|
|
{
|
|
Assert( 0 );
|
|
#if defined ( WIN32 )
|
|
TerminateQTML();
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
m_bQTInitialized = true;
|
|
#endif
|
|
|
|
return m_bQTInitialized;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void CQuickTime::ShutdownQuicktime()
|
|
{
|
|
if ( m_bQTInitialized )
|
|
{
|
|
#if defined ( QUICKTIME_VIDEO )
|
|
ExitMovies(); // Terminate QuickTime
|
|
#if defined ( WIN32 )
|
|
TerminateQTML(); // Terminate QTML
|
|
#endif
|
|
#endif
|
|
m_bQTInitialized = false;
|
|
}
|
|
|
|
}
|
|
|
|
|