#include "snd_local_console.h" #include "../renderer/tr_local.h" #include "BinkVideo.h" #include "RAD.h" bool bvUseGCTexMem = true; #ifdef _XBOX int memMarker = 0; char* binkXboxStartAddr = NULL; char* binkXboxCurrentAddr = NULL; char* binkXboxNextAddr = NULL; #endif static void PTR4* RADEXPLINK AllocWrapper(U32 size) { // Give bink pre-initialized mem on xbox #ifdef _XBOX switch(memMarker) { case 0: memMarker++; binkXboxCurrentAddr = binkXboxStartAddr; binkXboxNextAddr = binkXboxCurrentAddr + size; return (void *)binkXboxStartAddr; case 1: case 2: case 3: case 4: case 5: case 6: case 7: memMarker++; binkXboxCurrentAddr = binkXboxNextAddr; binkXboxNextAddr = binkXboxCurrentAddr + size; return (void *)binkXboxCurrentAddr; case 8: memMarker = -1; binkXboxCurrentAddr = binkXboxNextAddr; binkXboxNextAddr = binkXboxStartAddr; return (void *)binkXboxCurrentAddr; default: return BinkVideo::Allocate(size); } #endif return BinkVideo::Allocate(size); } static void RADEXPLINK FreeWrapper(void PTR4* ptr) { // Don't free the preinitialized mem #ifdef _XBOX if(memMarker < 6) { memMarker++; return; } else if(memMarker == 6) { memMarker = 1; binkXboxNextAddr = binkXboxStartAddr + XBOX_MEM_STAGE_1; return; } #endif BinkVideo::Free(ptr); } /********* BinkVideo *********/ BinkVideo::BinkVideo() { bink = NULL; buffer = NULL; texture = 0; x1 = 0.0f; y1 = 0.0f; x2 = 0.0f; y2 = 0.0f; w = 0.0f; h = 0.0f; status = NS_BV_STOPPED; looping = false; #ifdef _XBOX initialized = false; #endif } /********* ~BinkVideo *********/ BinkVideo::~BinkVideo() { Free(buffer); BinkClose(bink); } /********* AllocateXboxMem Pre-Allocates memory for xbox *********/ #ifdef _XBOX void BinkVideo::AllocateXboxMem(void) { u32 memToAllocate = XBOX_MEM_STAGE_1 + XBOX_MEM_STAGE_2 + XBOX_MEM_STAGE_3 + XBOX_MEM_STAGE_4 + XBOX_MEM_STAGE_5 + XBOX_MEM_STAGE_6 + XBOX_MEM_STAGE_7 + XBOX_MEM_STAGE_8 + XBOX_BUFFER_SIZE; binkXboxStartAddr = (char*)Allocate(memToAllocate); memMarker = 0; initialized = true; } /********* FreeXboxMem *********/ void BinkVideo::FreeXboxMem(void) { initialized = false; Z_Free(binkXboxStartAddr); memMarker = 0; } #endif /********* Start Opens a bink file and gets it ready to play *********/ bool BinkVideo::Start(const char *filename, float xOrigin, float yOrigin, float width, float height) { #ifdef _XBOX assert(initialized); #endif // Check to see if a video is being played. if(status == NS_BV_PLAYING) { // stop this->Stop(); } // Set memory allocation wrapper RADSetMemory(AllocWrapper,FreeWrapper); // Set up sound for consoles #if defined(_XBOX) // If we are on XBox, tell Bink to play all of the 5.1 tracks U32 TrackIDsToPlay[ 4 ] = { 0, 1, 2, 3 }; BinkSetSoundTrack( 4, TrackIDsToPlay ); // Now route the sound tracks to the correct speaker U32 bins[ 2 ]; bins[ 0 ] = DSMIXBIN_FRONT_LEFT; bins[ 1 ] = DSMIXBIN_FRONT_RIGHT; BinkSetMixBins( bink, 0, bins, 2 ); bins[ 0 ] = DSMIXBIN_FRONT_CENTER; BinkSetMixBins( bink, 1, bins, 1 ); bins[ 0 ] = DSMIXBIN_LOW_FREQUENCY; BinkSetMixBins( bink, 2, bins, 1 ); bins[ 0 ] = DSMIXBIN_BACK_LEFT; bins[ 1 ] = DSMIXBIN_BACK_RIGHT; BinkSetMixBins( bink, 3, bins, 2 ); #elif defined(_GAMECUBE) BinkSoundUseNGCSound(); RADMEMALLOC a; alGeti(AL_MEMORY_ALLOCATOR, (ALint*)&a); RADMEMFREE f; alGeti(AL_MEMORY_DEALLOCATOR, (ALint*)&f); RADSetAudioMemory(a, f); #endif // Try to open the Bink file. #ifdef _XBOX bink = BinkOpen( filename, BINKSNDTRACK ); if(!bink) { return false; } #elif defined _GAMECUBE if(bvUseGCTexMem) { extern void GLW_TexCacheLock(void); GLW_TexCacheLock(); } bink = BinkOpen( filename, 0); if(!bink) { extern void GLW_TexCacheUnlock(void); GLW_TexCacheUnlock(); return false; } #endif assert(bink->Width <= MAX_WIDTH && bink->Height <=MAX_HEIGHT); // allocate memory for the frame buffer #ifdef _XBOX buffer = AllocWrapper(XBOX_BUFFER_SIZE); #elif _GAMECUBE buffer = Allocate(XBOX_BUFFER_SIZE); #endif // set the height, width, etc... x1 = xOrigin; y1 = yOrigin; x2 = x1 + width; y2 = y1 + height; w = width; h = height; // flush any background sound reads #if defined (_XBOX)|| (_GAMECUBE) extern void S_DrainRawSoundData(void); S_DrainRawSoundData(); #endif // Create the video texture GLuint tex = (GLuint)texture; if (tex != 0) qglDeleteTextures(1, &tex); qglGenTextures(1, &tex); qglBindTexture(GL_TEXTURE_2D, tex); glState.currenttextures[glState.currenttmu] = tex; qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB5, bink->Width, bink->Height, 0, GL_RGB_SWIZZLE_EXT, GL_UNSIGNED_BYTE, buffer ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); texture = (int)tex; status = NS_BV_PLAYING; return true; } /********* Run Decompresses a frame, renders it to the screen, and advances to the next frame. *********/ bool BinkVideo::Run(void) { if(status == NS_BV_STOPPED) // A movie can't be run if it's not started first { return false; } while(BinkWait(bink)); // Wait DecompressFrame(); // Decompress Draw(); // Render if(status != NS_BV_PAUSED) // Only advance the frame is not paused { BinkNextFrame( bink ); } if(bink->FrameNum == (bink->Frames - 1) && !looping) // The movie is done { Stop(); return false; } return true; } /********* GetBinkData Returns the buffer data for the next frame of the video *********/ void* BinkVideo::GetBinkData(void) { while(BinkWait(bink)); DecompressFrame(); BinkNextFrame(bink); return buffer; } /******** Draw Copies the decompressed frame to a texture to be rendered on the screen. ********/ void BinkVideo::Draw(void) { if(buffer) { qglFlush(); extern void RB_SetGL2D (void); RB_SetGL2D(); GL_SelectTexture(0); // Update the video texture qglBindTexture(GL_TEXTURE_2D, (GLuint)texture); glState.currenttextures[glState.currenttmu] = texture; qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, bink->Width, bink->Height, GL_RGB_SWIZZLE_EXT, GL_UNSIGNED_BYTE, buffer ); // Clear the screen. We use triangles here (instead // of glClear) because we want the back buffer to stick // around... so we can get a nice, cheap fade on Gamecube // reset. qglColor3f(0.f, 0.f, 0.f); #if defined (_XBOX) || (_GAMECUBE) qglBeginEXT (GL_TRIANGLE_STRIP, 4, 0, 0, 4, 0); #else qglBegin(GL_TRIANGLE_STRIP); #endif qglTexCoord2f ( 0, 0 ); qglVertex2f (-10, -10); qglTexCoord2f ( 1 , 0 ); qglVertex2f (650, -10); qglTexCoord2f ( 0, 1 ); qglVertex2f (-10, 490); qglTexCoord2f ( 1, 1 ); qglVertex2f (650, 490); qglEnd (); // Draw the video qglColor3f(1.f, 1.f, 1.f); #if defined (_XBOX) || (_GAMECUBE) qglBeginEXT (GL_TRIANGLE_STRIP, 4, 0, 0, 4, 0); #else qglBegin(GL_TRIANGLE_STRIP); #endif qglTexCoord2f ( 0, 0 ); qglVertex2f (x1, y1); qglTexCoord2f ( 1 , 0 ); qglVertex2f (x2, y1); qglTexCoord2f ( 0, 1 ); qglVertex2f (x1, y2); qglTexCoord2f ( 1, 1 ); qglVertex2f (x2, y2); qglEnd (); } } /********* Stop Stops the current movie, and clears it from memory *********/ void BinkVideo::Stop(void) { BinkClose(bink); bink = NULL; #ifdef _XBOX FreeWrapper(buffer); #elif _GAMECUBE Free(buffer); #endif buffer = NULL; GLuint tex = (GLuint)texture; if (tex != 0) qglDeleteTextures(1, &tex); texture = 0; x1 = 0.0f; y1 = 0.0f; x2 = 0.0f; y2 = 0.0f; w = 0.0f; h = 0.0f; status = NS_BV_STOPPED; #ifdef _XBOX memMarker = 0; #endif #ifdef _GAMECUBE extern void GLW_TexCacheUnlock(void); GLW_TexCacheUnlock(); #endif } /********* Pause Pauses the current movie. Only the current frame is rendered *********/ void BinkVideo::Pause(void) { status = NS_BV_PAUSED; } /********* SetExtends Sets dimmension variables *********/ void BinkVideo::SetExtents(float xOrigin, float yOrigin, float width, float height) { x1 = xOrigin; y1 = yOrigin; x2 = x1 + width; y2 = y1 + height; w = width; h = height; } /********* SetMasterVolume Sets the volume of the specified track *********/ void BinkVideo::SetMasterVolume(s32 volume) { #ifdef _XBOX int i; for(i = 0; i < 4; i++) { BinkSetVolume(bink,i,volume); } #else BinkSetVolume(bink,0,volume); #endif } /********* DecompressFrame Decompresses current frame and copies the data to the buffer *********/ S32 BinkVideo::DecompressFrame() { BinkDoFrame(bink); S32 skip; skip = BinkCopyToBuffer( bink, (void *)buffer, NS_BV_DEFAULT_CIN_BPS * bink->Width, //pitch bink->Height, 0, 0, BINKCOPYALL | BINKSURFACE565); return skip; } /********* Allocate Allocates memory for the frame buffer *********/ void *BinkVideo::Allocate(U32 size) { size = RoundUp(size + 32, 32); char* ptr = NULL; #ifdef _GAMECUBE if (bvUseGCTexMem) { // Try allocating from texture cache extern void* GLW_TexCacheAllocRaw(int size); ptr = (char*)GLW_TexCacheAllocRaw(size); } #endif if (!ptr) { // Did not allocate texture cache memory, fall // back to main memory.. ptr = (char*)Z_Malloc(size, TAG_BINK, qfalse, 32); ptr[0] = 'z'; } else { // Allocated memory from the texture cache ptr[0] = 't'; } return (void*)(ptr + 32); } /********* FreeBuffer Releases the frame buffer memory *********/ void BinkVideo::Free(void* ptr) { char* base = (char*)ptr - 32; switch (*base) { #ifdef _GAMECUBE case 't': { // Free texture cache memory extern void GLW_TexCacheFreeRaw(void* ptr); GLW_TexCacheFreeRaw(base); break; } #endif case 'z': // Free main memory Z_Free(base); break; default: assert(false); } }