Files
FC1/BinkSDK/examalph.c
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

1031 lines
25 KiB
C

//############################################################################
//## ##
//## EXAMALPH.C ##
//## ##
//## Example program that plays a video with alpha transparency. ##
//## Link with glrad3d.c or d3drad3d.c to hook to OpenGL or Direct3D. ##
//## ##
//## Author: Jeff Roberts ##
//## ##
//############################################################################
//## ##
//## Copyright (C) RAD Game Tools, Inc. ##
//## ##
//## For technical support, contact RAD Game Tools at 425 - 893 - 4300. ##
//## ##
//############################################################################
#include <stdio.h>
#include <windows.h>
#include <zmouse.h>
#include "bink.h"
#include "rad3d.h"
//
// Example globals
//
static HBINK Bink_foreground = 0;
static HBINK Bink_background = 0;
static HRAD3D Rad_3d = 0;
static HRAD3DIMAGE Image_foreground = 0;
static HRAD3DIMAGE Image_background = 0;
static U32 Maximum_texture_size = 256;
static F32 Alpha_level = 1.0F;
static U32 Play_foreground_fast = 0;
static U32 Show_texture_lines = 0;
//
// Statistics variables.
//
static U64 Last_timer = 0;
static U32 Frame_count;
static U32 Bink_microseconds;
static U32 Render_microseconds;
//############################################################################
//## ##
//## Ask for a timer count in 64-bit accuracy. ##
//## ##
//############################################################################
static void Start_us_count( U64* out_count )
{
//
// Read the fast timer.
//
QueryPerformanceCounter( ( LARGE_INTEGER* )out_count );
}
//############################################################################
//## ##
//## Delta from a previous call to Start or Delta and return microseconds. ##
//## ##
//############################################################################
static U32 Delta_us_count( U64* last_count )
{
static U64 frequency=1000;
static S32 got_frequency=0;
U64 start;
//
// If this is the first time called, get the high-performance timer count.
//
if ( !got_frequency )
{
got_frequency = 1;
QueryPerformanceFrequency( ( LARGE_INTEGER* )&frequency );
}
start = *last_count;
QueryPerformanceCounter( ( LARGE_INTEGER* )last_count );
return( ( U32 ) ( ( ( *last_count - start ) * ( U64 ) 1000000 ) / frequency ) );
}
//############################################################################
//## ##
//## Simple macros using the above two functions for perfoming timings. ##
//## ##
//############################################################################
#define Start_timer() { U64 __timer; Start_us_count( &__timer );
#define End_and_start_next_timer( count ) count += Delta_us_count( &__timer );
#define End_timer( count ) End_and_start_next_timer( count ) }
//############################################################################
//## ##
//## Setup the array to convert from 3D image formats to Bink formats. ##
//## ##
//############################################################################
static U32 Bink_surface_type [ RAD3DSURFACECOUNT ];
static void Setup_surface_array( void )
{
Bink_surface_type[ RAD3DSURFACE24 ] = BINKSURFACE24;
Bink_surface_type[ RAD3DSURFACE24R ] = BINKSURFACE24R;
Bink_surface_type[ RAD3DSURFACE32 ] = BINKSURFACE32;
Bink_surface_type[ RAD3DSURFACE32R ] = BINKSURFACE32R;
Bink_surface_type[ RAD3DSURFACE32A ] = BINKSURFACE32A;
Bink_surface_type[ RAD3DSURFACE32RA ] = BINKSURFACE32RA;
Bink_surface_type[ RAD3DSURFACE555 ] = BINKSURFACE555;
Bink_surface_type[ RAD3DSURFACE565 ] = BINKSURFACE565;
Bink_surface_type[ RAD3DSURFACE5551 ] = BINKSURFACE5551;
Bink_surface_type[ RAD3DSURFACE4444 ] = BINKSURFACE4444;
}
//############################################################################
//## ##
//## Advance a Bink file by one frame into a 3D image buffer. ##
//## ##
//############################################################################
static void Decompress_frame( HBINK bink, HRAD3DIMAGE image, S32 copy_all )
{
void* pixels;
U32 pixel_pitch;
U32 pixel_format;
U32 src_x, src_y, src_w, src_h;
//
// Decompress the Bink frame.
//
BinkDoFrame( bink );
//
// Lock the 3D image so that we can copy the decompressed frame into it.
//
while ( Lock_RAD_3D_image( image, &pixels, &pixel_pitch, &pixel_format,
&src_x, &src_y, &src_w, &src_h ) )
{
//
// Copy the decompressed frame into the 3D image.
//
BinkCopyToBufferRect( bink,
pixels,
pixel_pitch,
bink->Height,
0,0,
src_x, src_y, src_w, src_h,
Bink_surface_type[ pixel_format ] |
( ( copy_all ) ? BINKCOPYALL : 0 ) );
//
// Unlock the 3D image.
//
Unlock_RAD_3D_image( image );
}
}
//############################################################################
//## ##
//## Show_next_frame - advances to the next Bink frame. ##
//## ##
//############################################################################
static void Show_foreground_frame( HWND window )
{
//
// Keep track of how many frames we've played.
//
++Frame_count;
//
// Decompress the Bink frame into the image buffer.
//
Start_timer( );
Decompress_frame( Bink_foreground, Image_foreground, 0 );
End_and_start_next_timer( Bink_microseconds );
//
// Begin a 3D frame.
//
Start_RAD_3D_frame ( Rad_3d );
//
// Draw the background image on the screen.
//
Blit_RAD_3D_image( Image_background, 0, 0, 1.0F, 1.0F, 1.0F );
//
// Draw the image on the screen.
//
Blit_RAD_3D_image( Image_foreground, 0, 0, 1.0F, 1.0F, Alpha_level );
//
// Draw the texture lines, if requested.
//
if ( Show_texture_lines )
Draw_lines_RAD_3D_image( Image_foreground, 0, 0, 1.0F, 1.0F );
//
// End a 3D frame.
//
End_RAD_3D_frame ( Rad_3d );
End_timer( Render_microseconds );
//
// Keep playing the movie.
//
BinkNextFrame( Bink_foreground );
}
//############################################################################
//## ##
//## Allocate the two image handles that we'll use to show the videos. ##
//## ##
//############################################################################
static S32 Allocate_3D_images( )
{
HRAD3DIMAGE New_foreground;
HRAD3DIMAGE New_background;
//
// Try to open a 3D image for the foreground Bink.
//
New_foreground = Open_RAD_3D_image( Rad_3d,
Bink_foreground->Width,
Bink_foreground->Height,
Bink_foreground->OpenFlags&BINKALPHA,
Maximum_texture_size );
//
// Try to open a 3D image for the background Bink.
//
New_background = Open_RAD_3D_image( Rad_3d,
Bink_background->Width,
Bink_background->Height,
0,
Maximum_texture_size );
//
// Did both of them open?
//
if ( ( New_foreground ) && ( New_background ) )
{
//
// Yup, both opened! Close the old images (if there were any) and replace them.
//
if ( Image_foreground )
Close_RAD_3D_image( Image_foreground );
if ( Image_background )
Close_RAD_3D_image( Image_background );
Image_foreground = New_foreground;
Image_background = New_background;
//
// Advance the two Binks to fill the textures.
//
Decompress_frame( Bink_foreground, Image_foreground, 1 );
BinkNextFrame( Bink_foreground );
Decompress_frame( Bink_background, Image_background, 1 );
BinkNextFrame( Bink_background );
return( 1 );
}
//
// Free the images if any were opened.
//
if ( New_foreground )
Close_RAD_3D_image( New_foreground );
if ( New_background )
Close_RAD_3D_image( New_background );
return( 0 );
}
static void Clear_to_black( HWND window )
{
PAINTSTRUCT ps;
HDC dc;
//
// Get the repaint DC and then fill the window with black.
//
dc = BeginPaint( window, &ps );
PatBlt( dc, 0, 0, 4096, 4096, BLACKNESS );
EndPaint( window, &ps );
}
//############################################################################
//## ##
//## Calc_window_values - calculates the extra width and height to add to ##
//## the windows's size so that the video fits. ##
//## ##
//############################################################################
static void Calc_window_values( HWND window,
S32* out_extra_width,
S32* out_extra_height )
{
RECT r, c;
//
// Get the current window rect (in screen space).
//
GetWindowRect( window, &r );
//
// Get the client rectangle of the window.
//
GetClientRect( window, &c );
*out_extra_width = ( r.right - r.left ) - ( c.right - c.left );
*out_extra_height = ( r.bottom - r.top ) - ( c.bottom - c.top );
}
//############################################################################
//## ##
//## WindowProc - the main window message procedure. ##
//## ##
//############################################################################
void Copy_one_filename( char* out_dest, char* out_src )
{
char* s = out_src;
char* d = out_dest;
int quoted = 0;
//
// Skip initial whitespace.
//
while ( ( *s ) && ( ( *s ) <= ' ' ) )
++s;
//
// Start the file copy.
//
while ( *s )
{
if ( ( *s ) == '"' )
{
//
// If we get a quote, turn on or off quoted mode.
//
quoted = !quoted;
}
else if ( ( ( *s ) <= ' ' ) && ( !quoted ) )
{
//
// If we get a space or other low character, assume white space and exit
// the loop.
//
++s;
break;
}
else
{
//
// Otherwise, just copy the character.
//
*d++ = *s;
}
++s;
}
*d = 0;
//
// Skip middle whitespace.
//
while ( ( *s ) && ( ( *s ) <= ' ' ) )
++s;
//
// Copy up the end of the source string (to remove the input).
//
strcpy( out_src, s );
}
static void Handle_character( HWND window,
U32 ch )
{
switch ( ch )
{
//
// Toggle the "play-fast" flag.
//
case 'f': case 'F':
Play_foreground_fast = !Play_foreground_fast;
break;
//
// Advance the background one frame.
//
case 'b': case 'B':
Decompress_frame( Bink_background, Image_background, 0 );
BinkNextFrame( Bink_background );
break;
//
// Draw lines.
//
case 'l': case 'L':
Show_texture_lines = !Show_texture_lines;
break;
//
// Handle shrinking or enlarging the texture sizes.
//
case '+':
Maximum_texture_size *= 2;
if ( !Allocate_3D_images( ) )
Maximum_texture_size /= 2;
break;
case '-':
if ( Maximum_texture_size >= 32 )
{
Maximum_texture_size /= 2;
if ( !Allocate_3D_images( ) )
Maximum_texture_size *= 2;
}
break;
//
// Close the application.
//
case 27:
DestroyWindow( window );
break;
}
}
//############################################################################
//## ##
//## Update the playback statistics. ##
//## ##
//############################################################################
static void Update_statistics( HWND window )
{
char buffer[256];
U32 delta;
if ( Last_timer == 0 )
{
Start_us_count( &Last_timer );
}
else
{
delta = Delta_us_count ( &Last_timer );
sprintf( buffer,
"Frame rate: %3.1f Bink: %2.1f%% %s Rendering: %2.1f%%",
( ( F32 ) Frame_count * 1000000.0F ) / ( F32 ) delta,
( ( F32 ) Bink_microseconds * 100.0F ) / ( F32 ) delta,
Describe_RAD_3D( ),
( ( F32 ) Render_microseconds * 100.0F ) / ( F32 ) delta );
SetWindowText( window, buffer );
}
Frame_count = 0;
Bink_microseconds = 0;
Render_microseconds = 0;
}
//############################################################################
//## ##
//## WindowProc - the main window message procedure. ##
//## ##
//############################################################################
LONG FAR PASCAL WindowProc( HWND window,
UINT message,
WPARAM wparam,
LPARAM lparam )
{
switch( message )
{
//
// Just close the window if the user hits a key.
//
case WM_CHAR:
Handle_character( window, wparam );
break;
//
// Pause/resume the video when the focus changes.
//
case WM_KILLFOCUS:
BinkPause( Bink_foreground, 1 );
break;
case WM_SETFOCUS:
BinkPause( Bink_foreground, 0 );
break;
//
// Allow the mouse wheel to control the foreground alpha.
//
case WM_MOUSEWHEEL:
if ( ( ( S16 ) HIWORD( wparam ) ) < 0 )
{
Alpha_level -= 0.1F;
if ( Alpha_level < 0.0F )
Alpha_level = 0.0F;
}
else
{
Alpha_level += 0.1F;
if ( Alpha_level > 1.0F )
Alpha_level = 1.0F;
}
break;
//
// Handle the window paint messages.
//
case WM_PAINT:
Clear_to_black( window );
return( 0 );
case WM_ERASEBKGND:
return( 1 );
//
// Handle the window being sized.
//
case WM_SIZE:
if ( Rad_3d )
{
RECT r;
GetClientRect( window, &r);
Resize_RAD_3D( Rad_3d, r.right - r.left, r.bottom - r.top );
}
break;
//
// Use a timer to display periodic playback statistics.
//
case WM_TIMER:
Update_statistics( window );
break;
//
// Post the quit message.
//
case WM_DESTROY:
PostQuitMessage( 0 );
return( 0 );
}
//
// Call the OS default window procedure.
//
return( DefWindowProc( window, message, wparam, lparam ) );
}
//############################################################################
//## ##
//## Build_window_handle - creates a window class and window handle. ##
//## ##
//############################################################################
static HWND Build_window_handle( HINSTANCE instance,
HINSTANCE previous_instance )
{
//
// Create the window class if this is the first instance.
//
if ( !previous_instance )
{
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = instance;
wc.hIcon = LoadIcon( instance, MAKEINTRESOURCE( 101 ) );
wc.hCursor = LoadCursor( 0, IDC_ARROW );;
wc.hbrBackground = 0;
wc.lpszMenuName = 0;
wc.lpszClassName = "BinkExam";
//
// Try to register the class.
//
if ( !RegisterClass( &wc ) )
{
return( 0 );
}
}
//
// Return the new window with a tiny initial default size (it is resized
// later on).
//
return( CreateWindow( "BinkExam",
"Bink Example Player",
WS_CAPTION | WS_POPUP| WS_CLIPCHILDREN |
WS_SYSMENU | WS_MINIMIZEBOX | WS_SIZEBOX,
64, 64,
64, 64,
0, 0, instance,0 ) );
}
//############################################################################
//## ##
//## Good_sleep_us - sleeps for a specified number of MICROseconds. ##
//## The task switcher in Windows has a latency of 15 ms. That means ##
//## you can ask for a Sleep of one millisecond and actually get a ##
//## sleep of 15 ms! In normal applications, this is no big deal, ##
//## however, with a video player at 30 fps, 15 ms is almost half our ##
//## frame time! The Good_sleep_us function times each sleep and keeps ##
//## the average sleep time to what you requested. It also give more ##
//## accuracy than Sleep - Good_sleep_us() uses microseconds instead of ##
//## milliseconds. ##
//## ##
//############################################################################
static void Good_sleep_us( S32 microseconds )
{
static S32 total_sleep=0;
static S32 slept_in_advance=0;
static U64 frequency=1000;
static S32 got_frequency=0;
//
// If this is the first time called, get the high-performance timer count.
//
if ( !got_frequency )
{
got_frequency = 1;
QueryPerformanceFrequency( ( LARGE_INTEGER* )&frequency );
}
total_sleep += microseconds;
//
// Have we exceeded our reserve of slept microseconds?
//
if (( total_sleep - slept_in_advance ) > 1000)
{
U64 start, end;
total_sleep -= slept_in_advance;
//
// Do the timed sleep.
//
QueryPerformanceCounter( ( LARGE_INTEGER* )&start );
Sleep( total_sleep / 1000 );
QueryPerformanceCounter( ( LARGE_INTEGER* )&end );
//
// Calculate delta time in microseconds.
//
end = ( (end - start) * (U64)1000000 ) / frequency;
//
// Keep track of how much extra we slept.
//
slept_in_advance = ( U32 )end - total_sleep;
total_sleep %= 1000;
}
}
//############################################################################
//## ##
//## WinMain - the primary function entry point ##
//## ##
//############################################################################
int PASCAL WinMain( HINSTANCE instance,
HINSTANCE previous_instance,
LPSTR cmd_line,
int cmd_show )
{
//
// Win32 locals.
//
HWND window;
MSG msg;
S32 extra_width, extra_height;
char foreground_name[256];
char background_name[256];
//
// Extract the filenames (copy both into background and then copy the first
// filename out of the background and into the foreground).
//
strcpy( background_name, cmd_line );
Copy_one_filename( foreground_name, background_name );
//
// Try to create our window.
//
window = Build_window_handle( instance,
previous_instance );
if ( !window )
{
MessageBox( 0,
"Error creating window.",
"Windows",
MB_OK | MB_ICONSTOP );
return( 1 );
}
//
// Calculate the extra width and height for the window.
//
Calc_window_values( window,
&extra_width, &extra_height );
//
// Tell Bink to use DirectSound (must be before BinkOpen)!
//
BinkSoundUseDirectSound( 0 );
//
// Try to open the foreground Bink file.
//
Bink_foreground = BinkOpen( foreground_name, BINKALPHA|BINKPRELOADALL );
if ( !Bink_foreground )
{
MessageBox( 0,
BinkGetError( ),
"Error opening foreground file...",
MB_OK | MB_ICONSTOP );
DestroyWindow( window );
return( 3 );
}
//
// Try to open the background Bink file.
//
Bink_background = BinkOpen( background_name, 0 );
if ( !Bink_background )
{
MessageBox( 0,
BinkGetError( ),
"Error opening background file...",
MB_OK | MB_ICONSTOP );
BinkClose( Bink_foreground );
DestroyWindow( window );
return( 3 );
}
//
// Size the window such that its client area exactly fits our Bink movie.
//
SetWindowPos( window, 0,
0, 0,
Bink_foreground->Width+extra_width,
Bink_foreground->Height+extra_height,
SWP_NOMOVE );
//
// Try to open the 3D API.
//
Rad_3d = Open_RAD_3D( window );
if ( !Rad_3d )
{
MessageBox( 0,
"Error opening 3D API.",
"3D Error",
MB_OK | MB_ICONSTOP );
DestroyWindow( window );
BinkClose( Bink_foreground );
BinkClose( Bink_background );
return( 4 );
}
//
// Setup up the array that converts between surface types.
//
Setup_surface_array( );
//
// Try to open the 3D images from the Binks.
//
if ( !Allocate_3D_images( ) )
{
MessageBox( 0,
"Error creating 3D textures.",
"3D Error",
MB_OK | MB_ICONSTOP );
Close_RAD_3D( Rad_3d );
DestroyWindow( window );
BinkClose( Bink_foreground );
BinkClose( Bink_background );
return( 5 );
}
//
// Create a timer to update the statistics.
//
SetTimer( window, 0, 1000, 0 );
//
// Now display the window and start the message loop.
//
ShowWindow( window, cmd_show );
for ( ; ; )
{
//
// Are there any messages to handle?
//
if ( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
{
//
// Yup, handle them.
//
if ( msg.message == WM_QUIT )
break;
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
//
// Is it time for a new Bink frame?
//
if ( ( !BinkWait( Bink_foreground ) ) || ( Play_foreground_fast ) )
{
//
// Yup, draw the next frame.
//
Show_foreground_frame( window );
}
else
{
//
// Nope, give the rest of the system a chance to run (500 MICROseconds).
//
Good_sleep_us( 500 );
}
}
}
//
// Close the Bink file.
//
if ( Bink_foreground )
{
BinkClose( Bink_foreground );
Bink_foreground = 0;
}
//
// Close the Bink file.
//
if ( Bink_background )
{
BinkClose( Bink_background );
Bink_background = 0;
}
//
// Close the 3D image.
//
if ( Image_foreground )
{
Close_RAD_3D_image( Image_foreground );
Image_foreground = 0;
}
//
// Close the 3D image.
//
if ( Image_background )
{
Close_RAD_3D_image( Image_background );
Image_background = 0;
}
//
// Close the 3D API.
//
if ( Rad_3d )
{
Close_RAD_3D( Rad_3d );
Rad_3d = 0;
}
//
// And exit.
//
return( 0 );
}
// some stuff for the RAD build utility
// @cdep pre $DefaultsWinEXE
// @cdep pre $requires( dx8rad3d.cpp )
// @cdep pre $requiresbinary($BuildDir/binkw32.lib)
// @cdep post $BuildWinEXE( , -libpath:\devel\libs\dx81\lib )