MuckyFoot-UrbanChaos/MuckyBasic/os.cpp

5946 lines
104 KiB
C++
Raw Normal View History

2017-05-20 03:14:17 +02:00
#define _OS_CPP_
#include <windows.h>
#include <windowsx.h>
#include <zmouse.h>
#include <ddraw.h>
#include <d3d.h>
#include <d3dtypes.h>
#include <dinput.h>
#include <dsound.h>
#include <stdarg.h>
#include <string.h>
#include "d3denum.h" // Direct 3D sample library
#include "d3dframe.h"
#include "resource.h"
#include "always.h"
#include "clip.h"
#include "key.h"
#include "os.h"
#include "tga.h"
#include "matrix.h"
#include "wave.h"
#include <mmreg.h>
#include <msacm.h>
//
// The entrypoint into the actual game.
//
extern void MAIN_main(void);
HINSTANCE OS_this_instance;
HINSTANCE OS_last_instance;
LPSTR OS_command_line;
int OS_start_show_state;
CBYTE *OS_application_name = "muckyBASIC Virtual Machine";
//
// Our window class.
//
WNDCLASSEX OS_wcl;
//
// Our window handle and menu handle.
//
HWND OS_window_handle;
//
// The DirectX 6 framework library class.
//
UBYTE OS_frame_is_fullscreen;
UBYTE OS_frame_is_hardware;
CD3DFramework OS_frame;
//
// The number of poly's drawn last frame.
//
SLONG OS_poly_count_transformed;
SLONG OS_poly_count_drawn;
// ========================================================
//
// THE SCREEN
//
// ========================================================
float OS_screen_width;
float OS_screen_height;
// ========================================================
//
// KEY HANDLING STUFF
//
// ========================================================
//
// The keys that are held down.
//
volatile UBYTE KEY_on[256];
volatile SLONG KEY_inkey;
volatile SLONG KEY_shift;
// ========================================================
//
// JOYSTICK STUFF
//
// ========================================================
IDirectInput *OS_joy_direct_input;
IDirectInputDevice *OS_joy_input_device;
IDirectInputDevice2 *OS_joy_input_device2; // We need this newer interface to poll the joystick.
float OS_joy_x;
float OS_joy_y;
SLONG OS_joy_x_range_min;
SLONG OS_joy_x_range_max;
SLONG OS_joy_y_range_min;
SLONG OS_joy_y_range_max;
ULONG OS_joy_button; // The buttons that are currently down
ULONG OS_joy_button_down; // The buttons that have just been pressed
ULONG OS_joy_button_up; // The buttons that have just been released
//
// The callback function for enumerating joysticks.
//
BOOL CALLBACK OS_joy_enum(
LPCDIDEVICEINSTANCE instance,
LPVOID context )
{
HRESULT hr;
LPDIRECTINPUTDEVICE pDevice;
//
// Get an interface to the joystick.
//
hr = OS_joy_direct_input->CreateDevice(
instance->guidInstance,
&OS_joy_input_device,
NULL);
if (FAILED(hr))
{
//
// Cant use this joystick for some reason!
//
OS_joy_input_device = NULL;
OS_joy_input_device2 = NULL;
return DIENUM_CONTINUE;
}
//
// Query for the IDirectInputDevice2 interface.
// We need this to poll the joystick.
//
OS_joy_input_device->QueryInterface(
IID_IDirectInputDevice2,
(LPVOID *) &OS_joy_input_device2);
//
// No need to find another joystick!
//
return DIENUM_STOP;
}
//
// Initialises the joystick.
//
void OS_joy_init(void)
{
HRESULT hr;
//
// Initialise everything.
//
OS_joy_direct_input = NULL;
OS_joy_input_device = NULL;
OS_joy_input_device2 = NULL;
//
// Create the direct input object.
//
CoInitialize(NULL);
CoCreateInstance(
CLSID_DirectInput8,
NULL,
CLSCTX_INPROC_SERVER,
IID_IDirectInput8W,
(void **) &OS_joy_direct_input);
hr = OS_joy_direct_input->Initialize(OS_this_instance, DIRECTINPUT_VERSION);
if (hr != DI_OK)
{
if (hr == DIERR_BETADIRECTINPUTVERSION)
{
return;
}
if (hr == DIERR_OLDDIRECTINPUTVERSION)
{
return;
}
return;
}
/*
hr = DirectInputCreateEx(
OS_this_instance,
DIRECTINPUT_VERSION,
&OS_joy_direct_input,
NULL);
if (FAILED(hr))
{
//
// No direct input!
//
return;
}
*/
//
// Find a joystick.
//
hr = OS_joy_direct_input->EnumDevices(
DI8DEVTYPE_JOYSTICK,
OS_joy_enum,
NULL,
DIEDFL_ATTACHEDONLY);
if (OS_joy_input_device == NULL ||
OS_joy_input_device2 == NULL)
{
//
// The joystick wasn't properly found.
//
OS_joy_input_device = NULL;
OS_joy_input_device2 = NULL;
return;
}
//
// So we can get the nice 'n' simple joystick data format.
//
OS_joy_input_device->SetDataFormat(&c_dfDIJoystick);
//
// Grab the joystick exclusively when our window in the foreground.
//
OS_joy_input_device->SetCooperativeLevel(
OS_window_handle,
DISCL_EXCLUSIVE | DISCL_FOREGROUND);
//
// What is the range of the joystick?
//
DIPROPRANGE diprg;
//
// In x...
//
diprg.diph.dwSize = sizeof(DIPROPRANGE);
diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
diprg.diph.dwHow = DIPH_BYOFFSET;
diprg.diph.dwObj = DIJOFS_X;
diprg.lMin = 0;
diprg.lMax = 0;
OS_joy_input_device->GetProperty(
DIPROP_RANGE,
&diprg.diph);
OS_joy_x_range_min = diprg.lMin;
OS_joy_x_range_max = diprg.lMax;
//
// In y...
//
diprg.diph.dwSize = sizeof(DIPROPRANGE);
diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
diprg.diph.dwHow = DIPH_BYOFFSET;
diprg.diph.dwObj = DIJOFS_Y;
diprg.lMin = 0;
diprg.lMax = 0;
OS_joy_input_device->GetProperty(
DIPROP_RANGE,
&diprg.diph);
OS_joy_y_range_min = diprg.lMin;
OS_joy_y_range_max = diprg.lMax;
}
//
// Polls the joystick.
//
void OS_joy_poll(void)
{
HRESULT hr;
if (OS_joy_direct_input == NULL ||
OS_joy_input_device == NULL ||
OS_joy_input_device2 == NULL)
{
//
// No joystick detected.
//
OS_joy_x = 0.0F;
OS_joy_y = 0.0F;
OS_joy_button = 0;
OS_joy_button_down = 0;
OS_joy_button_up = 0;
return;
}
SLONG acquired_already = FALSE;
try_again_after_acquiring:;
{
DIJOYSTATE js;
//
// We acquired the joystick okay. Poll the joystick to
// update its state.
//
OS_joy_input_device2->Poll();
//
// Finally get the state of the joystick.
//
hr = OS_joy_input_device->GetDeviceState(sizeof(js), &js);
if (hr == DIERR_NOTACQUIRED ||
hr == DIERR_INPUTLOST)
{
if (acquired_already)
{
//
// Oh dear!
//
OS_joy_x = 0.0F;
OS_joy_y = 0.0F;
OS_joy_button = 0;
OS_joy_button_down = 0;
OS_joy_button_up = 0;
return;
}
else
{
hr = OS_joy_input_device->Acquire();
if (hr == DI_OK)
{
acquired_already = TRUE;
goto try_again_after_acquiring;
}
else
{
OS_joy_x = 0.0F;
OS_joy_y = 0.0F;
OS_joy_button = 0;
OS_joy_button_down = 0;
OS_joy_button_up = 0;
return;
}
}
}
if (!FAILED(hr))
{
//
// Axis movment normalised to between -1.0F and +1.0F
//
SLONG dx = OS_joy_x_range_max - OS_joy_x_range_min;
SLONG dy = OS_joy_y_range_max - OS_joy_y_range_min;
OS_joy_x = 0.0F;
OS_joy_y = 0.0F;
if (dx) {OS_joy_x = float(js.lX - OS_joy_x_range_min) * 2.0F / float(dx) - 1.0F;}
if (dy) {OS_joy_y = float(js.lY - OS_joy_y_range_min) * 2.0F / float(dy) - 1.0F;}
//
// The buttons.
//
SLONG i;
ULONG last = OS_joy_button;
ULONG now = 0;
for (i = 0; i < 32; i++)
{
if (js.rgbButtons[i] & 0x80)
{
now |= 1 << i;
}
}
OS_joy_button = now;
OS_joy_button_down = now & ~last;
OS_joy_button_up = last & ~now;
}
}
}
// ========================================================
//
// WAVEFORM CONVERSION
//
// ========================================================
//
// Returns the converted memory, allocated with malloc().
//
void OS_decompress_sound(
WAVEFORMATEX *source_format,
void *source_data,
SLONG source_num_bytes,
WAVEFORMATEX *dest_format,
void **dest_data,
ULONG *dest_num_bytes)
{
//
// Open the conversion stream.
//
HACMSTREAM has;
switch(acmStreamOpen(
&has,
NULL,
source_format,
dest_format,
NULL,
NULL,
0,
ACM_STREAMOPENF_NONREALTIME))
{
case ACMERR_NOTPOSSIBLE: TRACE("The requested operation cannot be performed.\n"); break;
case MMSYSERR_INVALFLAG: TRACE("At least one flag is invalid. \n"); break;
case MMSYSERR_INVALHANDLE: TRACE("The specified handle is invalid. \n"); break;
case MMSYSERR_INVALPARAM: TRACE("At least one parameter is invalid. \n"); break;
case MMSYSERR_NOMEM: TRACE("The system is unable to allocate resources. \n"); break;
}
//
// Work out how many bytes we need.
//
if (acmStreamSize(
has,
source_num_bytes,
dest_num_bytes,
ACM_STREAMSIZEF_SOURCE))
{
return;
}
//
// Allocate the memory.
//
*dest_data = (void *) malloc(*dest_num_bytes);
if (!*dest_data)
{
return;
}
memset(*dest_data, 0, *dest_num_bytes);
//
// Prepare the source and destination buffers.
//
ACMSTREAMHEADER ash;
memset(&ash, 0, sizeof(ash));
ash.cbStruct = sizeof(ash);
ash.pbSrc = (LPBYTE) source_data;
ash.cbSrcLength = source_num_bytes;
ash.pbDst = (LPBYTE) *dest_data;
ash.cbDstLength = *dest_num_bytes;
if (acmStreamPrepareHeader(
has,
&ash,
0))
{
return;
}
//
// Do the conversion.
//
if (acmStreamConvert(
has,
&ash,
0))
{
return;
}
//
// Unprepare the header and close the stream.
//
if (acmStreamUnprepareHeader(
has,
&ash,
0))
{
return;
}
*dest_num_bytes = ash.cbDstLengthUsed;
if (acmStreamClose(has, 0))
{
return;
}
}
// ========================================================
//
// SOUND STUFF
//
// ========================================================
//
// Dsound globals.
//
SLONG OS_sound_valid;
LPDIRECTSOUND OS_sound_dsound;
LPDIRECTSOUNDBUFFER OS_sound_primary;
LPDIRECTSOUND3DLISTENER OS_sound_listener;
SLONG OS_sound_changed; // TRUE => we need to commit deferred settings.
//
// Each dsound sound.
//
typedef struct os_sound
{
SLONG type;
CBYTE fname[256];
LPDIRECTSOUNDBUFFER buffer;
LPDIRECTSOUND3DBUFFER buffer3d;
} OS_Sound;
#define OS_MAX_SOUNDS 256
OS_Sound OS_sound[OS_MAX_SOUNDS];
void OS_sound_init(void)
{
//
// Create direct sound.
//
if (DirectSoundCreate(NULL, &OS_sound_dsound, NULL) != DS_OK)
{
return;
}
//
// Set a co-op level.
//
if (OS_sound_dsound->SetCooperativeLevel(OS_window_handle, DSSCL_PRIORITY) != DS_OK)
{
return;
}
//
// Get our primary buffer.
//
DSBUFFERDESC dsbd;
memset(&dsbd, 0, sizeof(dsbd));
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D;
if (OS_sound_dsound->CreateSoundBuffer(&dsbd, &OS_sound_primary, NULL) != DS_OK)
{
return;
}
//
// Setup the format to 16-bit 22khz.
//
WAVEFORMATEX wfx;
memset(&wfx, 0, sizeof(wfx));
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = 22050;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
if (OS_sound_primary->SetFormat(&wfx) != DS_OK)
{
return;
}
//
// Create the listener for 3D sounds.
//
if (OS_sound_primary->QueryInterface(IID_IDirectSound3DListener, (void ** ) &OS_sound_listener) != DS_OK)
{
return;
}
//
// Set listener defaults...
//
OS_sound_listener->SetDopplerFactor(2.0F, DS3D_DEFERRED);
OS_sound_listener->SetRolloffFactor(2.0F, DS3D_DEFERRED);
OS_sound_changed = TRUE;
//
// Make sure that the primary buffer is always active. That way we don't
// get nasty clicks when we go in or out of silence.
//
OS_sound_primary->Play(0, 0, DSBPLAY_LOOPING);
//
// DSound is initialised ok.
//
OS_sound_valid = TRUE;
}
OS_Sound *OS_sound_create(CBYTE *fname, SLONG type)
{
SLONG i;
unsigned int bytes_read;
OS_Sound *os;
WAVEFORMATEX *pwfx; // Wave format info
HMMIO hmmio; // File handle
MMCKINFO mmckinfoData; // Chunk info
MMCKINFO mmckinfoParent; // Parent chunk info
if (!OS_sound_valid)
{
return NULL;
}
//
// Do we already have this sound?
//
for (i = 0; i < OS_MAX_SOUNDS; i++)
{
os = &OS_sound[i];
if (stricmp(fname, os->fname) == 0)
{
//
// Return this sound!
//
return os;
}
}
//
// Get a new sample structure.
//
for (i = 0; i < OS_MAX_SOUNDS; i++)
{
os = &OS_sound[i];
if (os->type == OS_SOUND_TYPE_UNUSED)
{
goto found_spare_sound;
}
}
//
// No more sounds available.
//
return NULL;
found_spare_sound:;
strcpy(os->fname, fname);
os->type = type;
//
// Open the wave file.
//
if (WaveOpenFile(fname, &hmmio, &pwfx, &mmckinfoParent) != 0)
{
return NULL;
}
if (WaveStartDataRead(&hmmio, &mmckinfoData, &mmckinfoParent) != 0)
{
WaveCloseReadFile(&hmmio, &pwfx);
return NULL;
}
//
// If the format is not PCM, then convert to PCM format.
//
if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
{
SLONG num_source_bytes = mmckinfoData.cksize;
//
// Read the data into a temporary buffer.
//
void *src = (void *) malloc(mmckinfoData.cksize);
if (WaveReadFile(
hmmio, // file handle
mmckinfoData.cksize,// no. of bytes to read
(UBYTE *) src, // destination
&mmckinfoData, // file chunk info
&bytes_read)) // actual no. of bytes read
{
WaveCloseReadFile(&hmmio, &pwfx);
return NULL;
}
//
// Convert!
//
WAVEFORMATEX destwfx;
destwfx.wFormatTag = WAVE_FORMAT_PCM;
destwfx.wBitsPerSample = 16;
destwfx.nChannels = pwfx->nChannels;
destwfx.nSamplesPerSec = pwfx->nSamplesPerSec;
destwfx.nBlockAlign = destwfx.wBitsPerSample / 8 * destwfx.nChannels;
destwfx.nAvgBytesPerSec = destwfx.nSamplesPerSec * destwfx.nBlockAlign;
destwfx.cbSize = 0;
void *dest_data = NULL;
ULONG dest_num_bytes;
OS_decompress_sound(
pwfx,
src,
num_source_bytes,
&destwfx,
&dest_data,
&dest_num_bytes);
if (!dest_data)
{
return NULL;
}
//
// Create the sound buffer with the destination format.
//
DSBUFFERDESC dsbdesc;
memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_LOCSOFTWARE;
dsbdesc.dwBufferBytes = dest_num_bytes;
dsbdesc.lpwfxFormat = &destwfx;
switch(type)
{
case OS_SOUND_TYPE_2D:
dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME;
break;
case OS_SOUND_TYPE_3D:
dsbdesc.dwFlags |= DSBCAPS_CTRL3D;
dsbdesc.dwFlags |= DSBCAPS_MUTE3DATMAXDISTANCE;
break;
default:
ASSERT(0);
break;
}
if (OS_sound_dsound->CreateSoundBuffer(&dsbdesc, &os->buffer, NULL) != DS_OK)
{
WaveCloseReadFile(&hmmio, &pwfx);
return NULL;
}
//
// Lock the sound buffer.
//
void *data;
ULONG num_bytes;
if (os->buffer->Lock(
0, // Offset of lock start
0, // Size of lock; ignored in this case
&data, // Address of lock start
&num_bytes, // Number of bytes locked
NULL, // Wraparound start; not used
NULL, // Wraparound size; not used
DSBLOCK_ENTIREBUFFER) != DS_OK)
{
WaveCloseReadFile(&hmmio, &pwfx);
return NULL;
}
ASSERT(num_bytes == dest_num_bytes);
//
// Copy over the converted data.
//
memcpy(data, dest_data, dest_num_bytes);
//
// Free up memory.
//
free(src);
free(dest_data);
//
// Unlock the buffer and close the wave file.
//
os->buffer->Unlock(data, num_bytes, NULL, 0);
WaveCloseReadFile(&hmmio, &pwfx);
}
else
{
//
// Create a sound buffer- now that we know the format of the wave.
//
DSBUFFERDESC dsbdesc;
memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_STATIC;
dsbdesc.dwBufferBytes = mmckinfoData.cksize;
dsbdesc.lpwfxFormat = pwfx;
switch(type)
{
case OS_SOUND_TYPE_2D:
dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME;
break;
case OS_SOUND_TYPE_3D:
dsbdesc.dwFlags |= DSBCAPS_CTRL3D;
dsbdesc.dwFlags |= DSBCAPS_MUTE3DATMAXDISTANCE;
break;
default:
ASSERT(0);
break;
}
if (OS_sound_dsound->CreateSoundBuffer(&dsbdesc, &os->buffer, NULL) != DS_OK)
{
WaveCloseReadFile(&hmmio, &pwfx);
return NULL;
}
//
// Lock the sound buffer.
//
void *data;
ULONG num_bytes;
if (os->buffer->Lock(
0, // Offset of lock start
0, // Size of lock; ignored in this case
&data, // Address of lock start
&num_bytes, // Number of bytes locked
NULL, // Wraparound start; not used
NULL, // Wraparound size; not used
DSBLOCK_ENTIREBUFFER) != DS_OK)
{
WaveCloseReadFile(&hmmio, &pwfx);
return NULL;
}
//
// Read in the sound data.
//
if (WaveReadFile(
hmmio, // file handle
num_bytes, // no. of bytes to read
(UBYTE *) data, // destination
&mmckinfoData, // file chunk info
&bytes_read)) // actual no. of bytes read
{
WaveCloseReadFile(&hmmio, &pwfx);
return NULL;
}
//
// Unlock the buffer and close the wave file.
//
os->buffer->Unlock(data, num_bytes, NULL, 0);
WaveCloseReadFile(&hmmio, &pwfx);
}
//
// If this buffer is 3D, then get the 3D interface.
//
os->buffer3d = NULL;
if (os->type == OS_SOUND_TYPE_3D)
{
if (FAILED(os->buffer->QueryInterface(IID_IDirectSound3DBuffer, (void **) &os->buffer3d)))
{
return NULL;
}
os->buffer3d->SetMinDistance(4.0F, DS3D_DEFERRED);
}
return os;
}
OS_Sound *OS_sound_create(UWORD *data, SLONG num_samples, SLONG type)
{
SLONG i;
OS_Sound *os;
if (!OS_sound_valid)
{
return NULL;
}
//
// Get a new sample structure.
//
for (i = 0; i < OS_MAX_SOUNDS; i++)
{
os = &OS_sound[i];
if (os->type == OS_SOUND_TYPE_UNUSED)
{
goto found_spare_sound;
}
}
//
// No more sounds available.
//
return NULL;
found_spare_sound:;
strcpy(os->fname, "Generated");
os->type = OS_SOUND_TYPE_2D;
//
// Create a sound buffer.
//
DSBUFFERDESC dsbdesc;
WAVEFORMATEX pwfx; // Wave format info
//
// Setup the wave format
//
memset(&pwfx, 0, sizeof(pwfx));
pwfx.cbSize = sizeof(pwfx);
pwfx.wFormatTag = WAVE_FORMAT_PCM;
pwfx.nChannels = 1;
pwfx.nSamplesPerSec = 22050;
pwfx.nAvgBytesPerSec = 22050 * sizeof(UWORD);
pwfx.nBlockAlign = 2;
pwfx.wBitsPerSample = 16;
memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_STATIC;
dsbdesc.dwBufferBytes = num_samples * sizeof(UWORD);
dsbdesc.lpwfxFormat = &pwfx;
switch(type)
{
case OS_SOUND_TYPE_2D:
dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME;
break;
case OS_SOUND_TYPE_3D:
dsbdesc.dwFlags |= DSBCAPS_CTRL3D;
dsbdesc.dwFlags |= DSBCAPS_MUTE3DATMAXDISTANCE;
break;
default:
ASSERT(0);
break;
}
if (OS_sound_dsound->CreateSoundBuffer(&dsbdesc, &os->buffer, NULL) != DS_OK)
{
return NULL;
}
//
// Lock the sound buffer.
//
void *locked_data;
ULONG locked_bytes;
if (os->buffer->Lock(
0, // Offset of lock start
0, // Size of lock; ignored in this case
&locked_data, // Address of lock start
&locked_bytes, // Number of bytes locked
NULL, // Wraparound start; not used
NULL, // Wraparound size; not used
DSBLOCK_ENTIREBUFFER) != DS_OK)
{
return NULL;
}
//
// Copy over the sound data.
//
memcpy(locked_data, data, locked_bytes);
//
// Unlock the buffer and close the wave file.
//
os->buffer->Unlock(locked_data, locked_bytes, NULL, 0);
//
// If this buffer is 3D, then get the 3D interface.
//
os->buffer3d = NULL;
if (os->type == OS_SOUND_TYPE_3D)
{
if (FAILED(os->buffer->QueryInterface(IID_IDirectSound3DBuffer, (void **) &os->buffer3d)))
{
return NULL;
}
os->buffer3d->SetMinDistance(4.0F, DS3D_DEFERRED);
}
return os;
}
void OS_sound_play(OS_Sound *os, SLONG flag)
{
if (!OS_sound_valid || os == NULL || os->buffer == NULL)
{
return;
}
//
// What's this buffer doing now?
//
ULONG status;
os->buffer->GetStatus(&status);
if (status == DSBSTATUS_PLAYING ||
status == DSBSTATUS_LOOPING)
{
if (flag & OS_SOUND_FLAG_INTERRUPT)
{
os->buffer->SetCurrentPosition(0);
}
}
else
{
os->buffer->Play(0, 0, (flag & OS_SOUND_FLAG_LOOP) ? DSBPLAY_LOOPING : 0);
}
}
void OS_sound_stop(OS_Sound *os)
{
if (!OS_sound_valid || os == NULL || os->buffer == NULL)
{
return;
}
os->buffer->Stop();
}
void OS_sound_finish()
{
}
void OS_sound_2d_set_volume(OS_Sound *os, float volume)
{
if (!OS_sound_valid || os == NULL || os->buffer == NULL)
{
return;
}
volume = sqrt(volume);
volume = sqrt(volume);
volume = sqrt(volume);
volume = sqrt(volume);
SLONG long_volume = SLONG(DSBVOLUME_MIN + volume * (DSBVOLUME_MAX - DSBVOLUME_MIN));
os->buffer->SetVolume(long_volume);
}
void OS_sound_3d_set_range(OS_Sound *os, float min, float max)
{
if (!OS_sound_valid || os == NULL || os->buffer == NULL)
{
return;
}
os->buffer3d->SetMinDistance(min, DS3D_DEFERRED);
os->buffer3d->SetMaxDistance(max, DS3D_DEFERRED);
OS_sound_changed = TRUE;
}
void OS_sound_3d_set_position(
OS_Sound *os,
float x,
float y,
float z,
float dx,
float dy,
float dz)
{
if (!OS_sound_valid || os == NULL || os->buffer == NULL)
{
return;
}
os->buffer3d->SetPosition( x, y, z, DS3D_DEFERRED);
os->buffer3d->SetVelocity(dx,dy,dz, DS3D_DEFERRED);
OS_sound_changed = TRUE;
}
void OS_sound_listener_set(
float x,
float y,
float z,
float dx,
float dy,
float dz,
float yaw,
float pitch,
float roll)
{
float matrix[9];
MATRIX_calc(
matrix,
yaw,
pitch,
roll);
OS_sound_listener->SetPosition( x, y, z, DS3D_DEFERRED);
OS_sound_listener->SetVelocity(dx, dy, dz, DS3D_DEFERRED);
OS_sound_listener->SetOrientation(
matrix[6],
matrix[7],
matrix[8],
matrix[3],
matrix[4],
matrix[5],
DS3D_DEFERRED);
OS_sound_changed = TRUE;
}
void OS_sound_update_changes()
{
if (OS_sound_changed)
{
OS_sound_listener->CommitDeferredSettings();
OS_sound_changed = FALSE;
}
}
// ========================================================
//
// TEXTURE STUFF
//
// ========================================================
//
// The directories where we load textures from.
//
#define OS_MAX_TEXPATHS 256
CBYTE OS_texpath[OS_MAX_TEXPATHS][64];
SLONG OS_texpath_upto;
SLONG OS_texpath_last; // The last texture path we used successfully.
//
// All the textures available and the path we found them in.
//
typedef struct
{
SLONG path; // -1 => Not used.
CBYTE fname[28];
} OS_Texname;
#define OS_MAX_TEXNAMES 1024
OS_Texname OS_texname[OS_MAX_TEXNAMES];
SLONG OS_texname_upto;
//
// The pixel formats for each of our OS_TEXTURE_FORMATs
//
typedef struct
{
SLONG valid;
DDPIXELFORMAT ddpf;
SLONG mask_r;
SLONG mask_g;
SLONG mask_b;
SLONG mask_a;
SLONG shift_r;
SLONG shift_g;
SLONG shift_b;
SLONG shift_a;
} OS_Tformat;
OS_Tformat OS_tformat[OS_TEXTURE_FORMAT_NUMBER];
//
// Our texture pages.
//
typedef struct os_texture
{
CBYTE name[_MAX_PATH];
UBYTE format;
UBYTE inverted;
UWORD padding;
UWORD width;
UWORD height;
DDSURFACEDESC2 ddsd;
LPDIRECTDRAWSURFACE4 ddsurface;
LPDIRECT3DTEXTURE2 ddtx;
OS_Texture *next;
} OS_Texture;
//
// They are stored in a linked list and dynamically allocated.
//
OS_Texture *OS_texture_head;
//
// Fills in the texture path info from the given file.
//
#define OS_BASE_TEXTURE_PATH "Textures\\"
void OS_init_texpaths()
{
SLONG i;
WIN32_FIND_DATA fd;
HANDLE handle;
CBYTE search_path[256];
OS_Texname *otn;
CBYTE *ch;
//
// We always have "Textures\\" in our path.
//
strcpy(OS_texpath[0], OS_BASE_TEXTURE_PATH);
OS_texpath_upto = 1;
//
// Use any additional directories inside the base directory.
//
for (i = 0; i < OS_texpath_upto; i++)
{
sprintf(search_path, "%s*.*", OS_texpath[i]);
handle = FindFirstFile(
search_path,
&fd);
if (handle != INVALID_HANDLE_VALUE)
{
while(1)
{
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
//
// Found another directory.
//
if (fd.cFileName[0] == '.')
{
//
// Ignore the crappy "." and ".." directories
//
}
else
{
ASSERT(WITHIN(OS_texpath_upto, 0, OS_MAX_TEXPATHS - 1));
sprintf(OS_texpath[OS_texpath_upto], "%s%s\\", OS_texpath[i], fd.cFileName);
OS_texpath_upto += 1;
}
}
if (!FindNextFile(handle, &fd))
{
//
// Nothing more in the "Textures\\" directory.
//
FindClose(handle);
break;
}
}
}
}
//
// Find all the texture filenames.
//
for (i = 0; i < OS_texpath_upto; i++)
{
sprintf(search_path, "%s\\*.tga", OS_texpath[i]);
handle = FindFirstFile(search_path, &fd);
if (handle != INVALID_HANDLE_VALUE)
{
while(1)
{
ASSERT(WITHIN(OS_texname_upto, 0, OS_MAX_TEXNAMES - 1));
otn = &OS_texname[OS_texname_upto++];
otn->path = i;
//
// Copy over just the filename.
//
strncpy(otn->fname, fd.cFileName, 27);
if (!FindNextFile(handle, &fd))
{
//
// Nothing more in this path.
//
FindClose(handle);
break;
}
}
}
}
}
//
// Returns the number of bits set in 'mask' with a rather cunning algorithm.
//
SLONG OS_bit_count(ULONG mask)
{
SLONG ans;
for (ans = 0; mask; mask &= mask - 1, ans += 1);
return ans;
}
//
// The texture enumeration function.
//
HRESULT CALLBACK OS_texture_enumerate_pixel_formats(
LPDDPIXELFORMAT lpddpf,
LPVOID context)
{
SLONG format;
OS_Tformat *otf = (OS_Tformat *) malloc(sizeof(OS_Tformat));
if (otf == NULL)
{
//
// Oh dear!
//
return D3DENUMRET_CANCEL;
}
//
// Is this one of the formats we are interested in?
//
if (lpddpf->dwFlags & DDPF_RGB)
{
//
// We are only interested in 16-bpp RGB modes.
//
if (lpddpf->dwRGBBitCount == 16)
{
if (lpddpf->dwFlags & DDPF_ALPHAPIXELS)
{
SLONG alphabits;
//
// Could be 1555 or 4444
//
alphabits = OS_bit_count(lpddpf->dwRGBAlphaBitMask);
if (alphabits == 1)
{
//
// Must be 1555
//
OS_tformat[OS_TEXTURE_FORMAT_1555].valid = TRUE;
OS_tformat[OS_TEXTURE_FORMAT_1555].ddpf = *lpddpf;
}
else
if (alphabits == 4)
{
//
// Must be 4444
//
OS_tformat[OS_TEXTURE_FORMAT_4444].valid = TRUE;
OS_tformat[OS_TEXTURE_FORMAT_4444].ddpf = *lpddpf;
}
}
else
{
//
// This is a good RGB pixel format.
//
OS_tformat[OS_TEXTURE_FORMAT_RGB].valid = TRUE;
OS_tformat[OS_TEXTURE_FORMAT_RGB].ddpf = *lpddpf;
}
}
}
else
if (lpddpf->dwFlags & DDPF_LUMINANCE)
{
if (lpddpf->dwFlags & DDPF_ALPHAPIXELS)
{
//
// We only want luminance- not luminance and alpha.
//
}
else
{
if (lpddpf->dwLuminanceBitCount == 8)
{
//
// This is what we want. An 8-bit luminance format.
//
OS_tformat[OS_TEXTURE_FORMAT_8].valid = TRUE;
OS_tformat[OS_TEXTURE_FORMAT_8].ddpf = *lpddpf;
}
}
}
//
// Ask for another texture format.
//
return D3DENUMRET_OK;
}
//
// Given the bitmask for a colour in a pixel format, it calculates the mask and
// shift so that you can construct a pixel in pixel format given its RGB values.
// The formula is...
//
// PIXEL(r,g,b) = ((r >> mask) << shift) | ((g >> mask) << shift) | ((b >> mask) << shift);
//
// THIS ASSUMES that r,g,b are 8-bit values.
//
void OS_calculate_mask_and_shift(
ULONG bitmask,
SLONG *mask,
SLONG *shift)
{
SLONG i;
SLONG b;
SLONG num_bits = 0;
SLONG first_bit = -1;
for (i = 0, b = 1; i < 32; i++, b <<= 1)
{
if (bitmask & b)
{
num_bits += 1;
if (first_bit == -1)
{
//
// We have found the first bit.
//
first_bit = i;
}
}
}
ASSERT(first_bit != -1 && num_bits != 0);
*mask = 8 - num_bits;
*shift = first_bit;
if (*mask < 0)
{
//
// More than 8 bits per colour component? May
// as well support it!
//
*shift -= *mask;
*mask = 0;
}
}
CBYTE *OS_texture_full_path(CBYTE *fname)
{
SLONG i;
static CBYTE fullpath[MAX_PATH];
//sprintf(fullpath, "Textures\\qmark.tga");
//return fullpath;
for (i = 0; i < OS_texname_upto; i++)
{
if (_stricmp(OS_texname[i].fname, fname) == 0)
{
//
// Found the texture!
//
sprintf(fullpath, "%s\\%s", OS_texpath[OS_texname[i].path], fname);
return fullpath;
}
}
//
// No such texture.
//
return NULL;
}
//
// The buffer we use to load TGAs into.
//
TGA_Pixel OS_texture_tga[OS_TEXTURE_MAX_SIZE * OS_TEXTURE_MAX_SIZE];
OS_Texture *OS_texture_create(CBYTE *fname, SLONG invert)
{
SLONG format;
OS_Texture *ot;
OS_Tformat *best_otf;
TGA_Info ti;
CBYTE *fullpath;
//
// Do we already have this texture?
//
for (ot = OS_texture_head; ot; ot = ot->next)
{
if (strcmp(fname, ot->name) == 0)
{
if (ot->inverted == invert)
{
return ot;
}
}
}
//
// Find the filename.
//
fullpath = OS_texture_full_path(fname);
if (!fullpath)
{
OS_error("Could not find the texture file: \"%s\"\nanywhere inside the Textures directory.\nUsing texture \"qmark.tga\" instead.", fname);
fullpath = OS_BASE_TEXTURE_PATH"qmark.tga";
}
//
// Try to load in the TGA.
//
ti = TGA_load(fullpath, OS_TEXTURE_MAX_SIZE, OS_TEXTURE_MAX_SIZE, OS_texture_tga);
if (!ti.valid ||
OS_bit_count(ti.width) != 1 ||
OS_bit_count(ti.height) != 1)
{
if (!ti.valid)
{
OS_error("Error loading tga file \"%s\".\n", fname);
}
else
{
OS_error("Cannot load \"%s\"\nBoth the width and height of a texture must be a power of 2.\ni.e. 64,128,256,512,...\nUsing qmark.tga instead.", fname);
}
fullpath = OS_BASE_TEXTURE_PATH"qmark.tga";
ti = TGA_load(fullpath, OS_TEXTURE_MAX_SIZE, OS_TEXTURE_MAX_SIZE, OS_texture_tga);
if (!ti.valid ||
OS_bit_count(ti.width) != 1 ||
OS_bit_count(ti.height) != 1)
{
//
// Error loading qmark.tga!
//
return NULL;
}
}
//
// Find the best texture format.
//
if (ti.flag & TGA_FLAG_CONTAINS_ALPHA)
{
if (ti.flag & TGA_FLAG_ONE_BIT_ALPHA)
{
format = OS_TEXTURE_FORMAT_1555;
}
else
{
format = OS_TEXTURE_FORMAT_4444;
}
}
else
if (ti.flag & TGA_FLAG_GRAYSCALE)
{
if (OS_tformat[OS_TEXTURE_FORMAT_8].valid)
{
//
// This card has a luminance only texture format.
//
format = OS_TEXTURE_FORMAT_8;
}
else
{
//
// Use the RGB format as the next-best thing.
//
format = OS_TEXTURE_FORMAT_RGB;
}
}
else
{
//
// A normal RGB texture
//
format = OS_TEXTURE_FORMAT_RGB;
}
best_otf = &OS_tformat[format];
if (!best_otf->valid)
{
//
// No good texture format.
//
return NULL;
}
//
// Create a new texture.
//
ot = (OS_Texture *) malloc(sizeof(OS_Texture));
if (ot == NULL)
{
//
// It's really not worth checking for this... but anyway!
//
return NULL;
}
strncpy(ot->name, fname, _MAX_PATH);
ot->format = format;
ot->inverted = invert;
//
// Create a managed texture surface.
//
memset(&ot->ddsd, 0, sizeof(ot->ddsd));
ot->ddsd.dwSize = sizeof(DDSURFACEDESC2);
ot->ddsd.dwFlags =
DDSD_CAPS |
DDSD_HEIGHT |
DDSD_WIDTH |
DDSD_MIPMAPCOUNT |
DDSD_PIXELFORMAT;
ot->ddsd.dwWidth = ti.width;
ot->ddsd.dwHeight = ti.height;
ot->ddsd.dwMipMapCount = 1;
ot->ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
ot->ddsd.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE | DDSCAPS2_HINTSTATIC;
ot->ddsd.ddpfPixelFormat = best_otf->ddpf;
VERIFY(OS_frame.GetDirectDraw()->CreateSurface(
&ot->ddsd,
&ot->ddsurface,
NULL) == DD_OK);
if (invert)
{
SLONG i;
SLONG j;
TGA_Pixel *tp;
//
// Invert the texture.
//
tp = OS_texture_tga;
for (i = 0; i < ti.width; i++)
{
for (j = 0; j < ti.height; j++)
{
tp->alpha = 255 - tp->alpha;
tp->red = 255 - tp->red;
tp->green = 255 - tp->green;
tp->blue = 255 - tp->blue;
tp += 1;
}
}
}
//
// Lock the surface.
//
DDSURFACEDESC2 ddsd;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
VERIFY(ot->ddsurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL) == DD_OK);
//
// Copy the tga data into the surface.
//
if (format != OS_TEXTURE_FORMAT_8)
{
SLONG i;
SLONG j;
UWORD pixel_our;
TGA_Pixel pixel_tga;
UWORD *wscreen = (UWORD *) ddsd.lpSurface;
//
// 16 bits per pixel.
//
for (i = 0; i < ti.width; i++)
{
for (j = 0; j < ti.height; j++)
{
pixel_tga = OS_texture_tga[i + j * ti.width];
pixel_our = 0;
pixel_our |= (pixel_tga.red >> best_otf->mask_r) << best_otf->shift_r;
pixel_our |= (pixel_tga.green >> best_otf->mask_g) << best_otf->shift_g;
pixel_our |= (pixel_tga.blue >> best_otf->mask_b) << best_otf->shift_b;
if (best_otf->ddpf.dwFlags & DDPF_ALPHAPIXELS)
{
pixel_our |= (pixel_tga.alpha >> best_otf->mask_a) << best_otf->shift_a;
}
wscreen[i + j * (ddsd.lPitch >> 1)] = pixel_our;
}
}
}
else
{
SLONG i;
SLONG j;
UBYTE *wscreen = (UBYTE *) ddsd.lpSurface;
//
// 8 bits per pixel.
//
for (i = 0; i < ti.width; i++)
{
for (j = 0; j < ti.height; j++)
{
wscreen[i + j * ddsd.lPitch] = OS_texture_tga[i + j * ti.width].red;
}
}
}
//
// Unlock the surface.
//
ot->ddsurface->Unlock(NULL);
//
// Query the texture interface from the surface.
//
VERIFY(ot->ddsurface->QueryInterface(IID_IDirect3DTexture2, (void **) &ot->ddtx) == DD_OK);
//
// Insert this texture into the array.
//
ot->next = OS_texture_head;
OS_texture_head = ot;
//
// Remember the size!
//
ot->width = ti.width;
ot->height = ti.height;
return ot;
}
OS_Texture *OS_texture_create(SLONG width, SLONG height, SLONG format)
{
OS_Texture *ot;
OS_Tformat *otf;
//
// Powers of two?
//
if (OS_bit_count(width ) > 1 ||
OS_bit_count(height) > 1)
{
return NULL;
}
//
// Make sure this texture is not too big.
//
{
D3DDEVICEDESC dh;
D3DDEVICEDESC ds;
memset(&dh, 0, sizeof(dh));
memset(&ds, 0, sizeof(ds));
dh.dwSize = sizeof(dh);
ds.dwSize = sizeof(ds);
VERIFY(OS_frame.GetD3DDevice()->GetCaps(&dh, &ds) == D3D_OK);
if (width > dh.dwMaxTextureWidth ||
height > dh.dwMaxTextureHeight)
{
return NULL;
}
}
if (!OS_tformat[format].valid)
{
//
// The requested texture format does not exist. Is there
// another one we can try?
//
switch(format)
{
case OS_TEXTURE_FORMAT_8: format = OS_TEXTURE_FORMAT_RGB; break;
case OS_TEXTURE_FORMAT_1555: format = OS_TEXTURE_FORMAT_4444; break;
case OS_TEXTURE_FORMAT_4444: format = OS_TEXTURE_FORMAT_1555; break;
}
if (!OS_tformat[format].valid)
{
//
// We have no suitable texture format.
//
return NULL;
}
}
//
// The texture format we are going to use.
//
otf = &OS_tformat[format];
//
// Create a new texture.
//
ot = (OS_Texture *) malloc(sizeof(OS_Texture));
if (ot == NULL)
{
//
// It's really not worth checking for this... but anyway!
//
return NULL;
}
sprintf(ot->name, "Generated");
ot->format = format;
ot->width = width;
ot->height = height;
//
// Create a managed texture surface.
//
memset(&ot->ddsd, 0, sizeof(ot->ddsd));
ot->ddsd.dwSize = sizeof(DDSURFACEDESC2);
ot->ddsd.dwFlags =
DDSD_CAPS |
DDSD_HEIGHT |
DDSD_WIDTH |
DDSD_MIPMAPCOUNT |
DDSD_PIXELFORMAT;
ot->ddsd.dwWidth = width;
ot->ddsd.dwHeight = height;
ot->ddsd.dwMipMapCount = 1;
ot->ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
ot->ddsd.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE | DDSCAPS2_HINTDYNAMIC;
ot->ddsd.ddpfPixelFormat = otf->ddpf;
if (OS_frame.GetDirectDraw()->CreateSurface(
&ot->ddsd,
&ot->ddsurface,
NULL) != DD_OK)
{
//
// Oh dear...
//
free(ot);
return NULL;
}
//
// The surface probably contains junk...
//
//
// Query the texture interface from the surface.
//
VERIFY(ot->ddsurface->QueryInterface(IID_IDirect3DTexture2, (void **) &ot->ddtx) == DD_OK);
//
// Insert this texture into the array.
//
ot->next = OS_texture_head;
OS_texture_head = ot;
return ot;
}
void OS_texture_finished_creating()
{
/*
SLONG i;
OS_Texture *ot;
OS_Point op;
UWORD index[3];
//
// Go through all the textures and draw something with them.
//
OS_scene_begin();
OS_init_renderstates();
for (ot = OS_texture_head; ot; ot = ot->next)
{
OS_page_lock(ot);
for (i = 0; i < 3; i++)
{
op.x = frand() * OS_screen_width;
op.y = frand() * OS_screen_height;
op.z = 0.5F;
op.rhw = 0.5F;
op.clip = OS_CLIP_TRANSFORMED;
index[i] = OS_page_add_point(ot, &op, 0x00000000, 0x00000000, frand(), frand(), 0.0F);
}
OS_page_add_triangle(ot, index[0], index[1], index[2]);
OS_page_unlock(ot);
OS_page_draw(ot, OS_TEXTURE_TYPE_DOUBLESIDED | OS_TEXTURE_TYPE_ZALWAYS);
}
OS_scene_end();
OS_show();
*/
}
SLONG OS_texture_width(OS_Texture *ot)
{
if (ot == NULL)
{
return 0;
}
return ot->width;
}
SLONG OS_texture_height(OS_Texture *ot)
{
if (ot == NULL)
{
return 0;
}
return ot->height;
}
SLONG OS_bitmap_format; // OS_TEXTURE_FORMAT_*
UWORD *OS_bitmap_uword_screen; // For 16-bit formats.
SLONG OS_bitmap_uword_pitch; // Pitch in UWORDS
UBYTE *OS_bitmap_ubyte_screen; // For the grayscale format.
SLONG OS_bitmap_ubyte_pitch; // Pitch in UBYTES
SLONG OS_bitmap_width;
SLONG OS_bitmap_height;
SLONG OS_bitmap_mask_r;
SLONG OS_bitmap_mask_g;
SLONG OS_bitmap_mask_b;
SLONG OS_bitmap_mask_a;
SLONG OS_bitmap_shift_r;
SLONG OS_bitmap_shift_g;
SLONG OS_bitmap_shift_b;
SLONG OS_bitmap_shift_a;
void OS_texture_lock(OS_Texture *ot)
{
OS_Tformat *otf;
HRESULT res;
DDSURFACEDESC2 ddsd;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
VERIFY((res = ot->ddsurface->Lock(
NULL,
&ddsd,
DDLOCK_WAIT |
DDLOCK_WRITEONLY |
DDLOCK_NOSYSLOCK,
NULL)) == DD_OK);
ASSERT(WITHIN(ot->format, 0, OS_TEXTURE_FORMAT_NUMBER - 1));
otf = &OS_tformat[ot->format];
if (ot->format == OS_TEXTURE_FORMAT_8)
{
//
// 8-bits per pixel.
//
OS_bitmap_ubyte_screen = (UBYTE *) ddsd.lpSurface;
OS_bitmap_ubyte_pitch = ddsd.lPitch;
OS_bitmap_uword_screen = NULL;
OS_bitmap_uword_pitch = 0;
}
else
{
OS_bitmap_ubyte_screen = NULL;
OS_bitmap_ubyte_pitch = 0;
OS_bitmap_uword_screen = (UWORD *) ddsd.lpSurface;
OS_bitmap_uword_pitch = ddsd.lPitch >> 1;
}
OS_bitmap_format = ot->format;
OS_bitmap_width = ddsd.dwWidth;
OS_bitmap_height = ddsd.dwHeight;
OS_bitmap_mask_r = otf->mask_r;
OS_bitmap_mask_g = otf->mask_g;
OS_bitmap_mask_b = otf->mask_b;
OS_bitmap_mask_a = otf->mask_a;
OS_bitmap_shift_r = otf->shift_r;
OS_bitmap_shift_g = otf->shift_g;
OS_bitmap_shift_b = otf->shift_b;
OS_bitmap_shift_a = otf->shift_a;
}
void OS_texture_unlock(OS_Texture *ot)
{
//
// Unlock the surface.
//
ot->ddsurface->Unlock(NULL);
}
SLONG OS_texture_blit_from_backbuffer(OS_Texture *ot, SLONG x, SLONG y)
{
RECT src_rect;
src_rect.top = y;
src_rect.left = x;
src_rect.bottom = y + ot->height;
src_rect.right = x + ot->width;
if (ot->ddsurface->Blt(NULL, OS_frame.GetBackBuffer(), &src_rect, DDBLT_WAIT, NULL) == DD_OK)
{
return TRUE;
}
else
{
return FALSE;
}
}
// ========================================================
//
// PIPELINE SETUP AND VALIDATION
//
// ========================================================
void OS_init_renderstates()
{
LPDIRECT3DDEVICE3 d3d = OS_frame.GetD3DDevice();
//
// Setup renderstates.
//
d3d->SetRenderState(D3DRENDERSTATE_SHADEMODE, D3DSHADE_GOURAUD);
d3d->SetRenderState(D3DRENDERSTATE_TEXTUREPERSPECTIVE, TRUE);
d3d->SetRenderState(D3DRENDERSTATE_DITHERENABLE, TRUE);
d3d->SetRenderState(D3DRENDERSTATE_SPECULARENABLE, TRUE);
d3d->SetRenderState(D3DRENDERSTATE_SUBPIXEL, TRUE);
d3d->SetRenderState(D3DRENDERSTATE_ZENABLE, TRUE);
d3d->SetRenderState(D3DRENDERSTATE_ZFUNC, D3DCMP_LESSEQUAL);
d3d->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, TRUE);
d3d->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_CCW);
d3d->SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE);
d3d->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, FALSE);
d3d->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, FALSE);
if (KEY_on[KEY_A])
{
d3d->SetRenderState(D3DRENDERSTATE_ANTIALIAS, D3DANTIALIAS_SORTINDEPENDENT);
}
else
{
d3d->SetRenderState(D3DRENDERSTATE_ANTIALIAS, D3DANTIALIAS_NONE);
}
//
// Setup pipeline for one-texture gouraud shaded.
//
d3d->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
d3d->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
d3d->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
d3d->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
d3d->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
d3d->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1);
d3d->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE);
d3d->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFG_LINEAR);
d3d->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_LINEAR);
d3d->SetTextureStageState(0, D3DTSS_ADDRESS, D3DTADDRESS_WRAP);
d3d->SetTextureStageState(1, D3DTSS_MINFILTER, D3DTFG_LINEAR);
d3d->SetTextureStageState(1, D3DTSS_MAGFILTER, D3DTFG_LINEAR);
d3d->SetTextureStageState(1, D3DTSS_ADDRESS, D3DTADDRESS_WRAP);
//
// No alpha.
//
d3d->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
d3d->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
d3d->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
}
//
// Works out how to setup the pipeline for additive and multiplicitive
// multi-texturing.
//
#define OS_METHOD_NUMBER_MUL 2
SLONG OS_pipeline_method_mul;
void OS_pipeline_calculate()
{
ULONG num_passes;
LPDIRECT3DDEVICE3 d3d = OS_frame.GetD3DDevice();
OS_pipeline_method_mul = 0;
OS_Texture *ot1 = OS_texture_create(32, 32, OS_TEXTURE_FORMAT_RGB);
OS_Texture *ot2 = OS_texture_create(32, 32, OS_TEXTURE_FORMAT_RGB);
while(1)
{
OS_init_renderstates();
d3d->SetTexture(0, ot1->ddtx);
d3d->SetTexture(1, ot2->ddtx);
switch(OS_pipeline_method_mul)
{
case 1:
d3d->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
d3d->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
d3d->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
d3d->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
break;
case 0:
d3d->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
d3d->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
d3d->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
d3d->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
d3d->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_MODULATE);
d3d->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
d3d->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_CURRENT);
break;
default:
//
// Didn't find any way to do mulitexturing!
//
break;
}
if (OS_pipeline_method_mul == OS_METHOD_NUMBER_MUL)
{
//
// No multitexturing! :(
//
break;
}
if (d3d->ValidateDevice(&num_passes) == D3D_OK)
{
if (num_passes != 0)
{
//
// Found a methed for doing additive multi-texturing.
//
OS_string("Validated %d with %d passes\n", OS_pipeline_method_mul, num_passes);
break;
}
}
OS_pipeline_method_mul += 1;
}
OS_string("Multitexture method %d\n", OS_pipeline_method_mul);
}
void OS_change_renderstate_for_type(ULONG draw)
{
LPDIRECT3DDEVICE3 d3d = OS_frame.GetD3DDevice();
if (draw & OS_DRAW_ADD)
{
d3d->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE);
d3d->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_ONE);
d3d->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_ONE);
}
if (draw & OS_DRAW_MULTIPLY)
{
d3d->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE);
d3d->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_DESTCOLOR);
d3d->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_SRCCOLOR);
}
if (draw & OS_DRAW_MULBYONE)
{
d3d->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE);
d3d->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_DESTCOLOR);
d3d->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_ZERO);
}
if (draw & OS_DRAW_CLAMP)
{
d3d->SetTextureStageState(0, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP);
}
if (draw & OS_DRAW_DECAL)
{
d3d->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
}
if (draw & OS_DRAW_TRANSPARENT)
{
d3d->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE);
d3d->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_ZERO);
d3d->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_ONE);
}
if (draw & OS_DRAW_DOUBLESIDED)
{
d3d->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
}
if (draw & OS_DRAW_NOZWRITE)
{
d3d->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE);
}
if (draw & OS_DRAW_ALPHAREF)
{
d3d->SetRenderState(D3DRENDERSTATE_ALPHAFUNC,D3DCMP_NOTEQUAL);
d3d->SetRenderState(D3DRENDERSTATE_ALPHAREF,0);
d3d->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE,TRUE);
//
// Make sure the alpha from the texture gets through.
//
d3d->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
}
if (draw & OS_DRAW_ZREVERSE)
{
d3d->SetRenderState(D3DRENDERSTATE_ZFUNC, D3DCMP_GREATEREQUAL);
}
if (draw & OS_DRAW_ZALWAYS)
{
d3d->SetRenderState(D3DRENDERSTATE_ZFUNC, D3DCMP_ALWAYS);
}
if (draw & OS_DRAW_CULLREVERSE)
{
d3d->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_CW);
}
if (draw & OS_DRAW_NODITHER)
{
d3d->SetRenderState(D3DRENDERSTATE_DITHERENABLE, FALSE);
}
if (draw & OS_DRAW_ALPHABLEND)
{
d3d->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE);
d3d->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA);
d3d->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA);
d3d->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
d3d->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
}
if (draw & OS_DRAW_TEX_NONE)
{
d3d->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
d3d->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
}
if (draw & OS_DRAW_TEX_MUL)
{
switch(OS_pipeline_method_mul)
{
case 1:
if (draw & OS_DRAW_DECAL)
{
//
// Don't multiply by diffuse colour...
//
d3d->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
d3d->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
}
else
{
d3d->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
d3d->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
}
d3d->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
d3d->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
break;
case 0:
d3d->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
d3d->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
d3d->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
d3d->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
if (draw & OS_DRAW_DECAL)
{
//
// Don't multiply by diffuse colour...
//
}
else
{
d3d->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_MODULATE);
d3d->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
d3d->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_CURRENT);
}
break;
default:
break;
}
}
if (draw & OS_DRAW_NOFILTER)
{
d3d->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFG_POINT);
d3d->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_POINT);
d3d->SetTextureStageState(1, D3DTSS_MINFILTER, D3DTFG_POINT);
d3d->SetTextureStageState(1, D3DTSS_MAGFILTER, D3DTFG_POINT);
}
}
void OS_undo_renderstate_type_changes(void)
{
LPDIRECT3DDEVICE3 d3d = OS_frame.GetD3DDevice();
d3d->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
d3d->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
d3d->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
d3d->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
d3d->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
d3d->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE);
d3d->SetTextureStageState(0, D3DTSS_ADDRESS, D3DTADDRESS_WRAP);
d3d->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, FALSE);
d3d->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_CCW);
d3d->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, TRUE);
d3d->SetRenderState(D3DRENDERSTATE_ZFUNC, D3DCMP_LESSEQUAL);
d3d->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, FALSE);
d3d->SetRenderState(D3DRENDERSTATE_DITHERENABLE, TRUE);
d3d->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFG_LINEAR);
d3d->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_LINEAR);
d3d->SetTextureStageState(1, D3DTSS_MINFILTER, D3DTFG_LINEAR);
d3d->SetTextureStageState(1, D3DTSS_MAGFILTER, D3DTFG_LINEAR);
}
// ========================================================
//
// WINDOWS STUFF
//
// ========================================================
void OS_string(CBYTE *fmt, ...)
{
//
// Work out the real message.
//
CBYTE message[512];
va_list ap;
va_start(ap, fmt);
vsprintf(message, fmt, ap);
va_end (ap);
OutputDebugString(message);
}
SLONG OS_game_start_tick_count;
SLONG OS_ticks(void)
{
return GetTickCount() - OS_game_start_tick_count;
}
void OS_ticks_reset()
{
OS_game_start_tick_count = GetTickCount();
}
SLONG OS_mhz;
//
// Returns TRUE if the processor has support for the RDTSC instruction.
//
SLONG OS_has_rdtsc(void)
{
SLONG res;
_asm
{
mov eax, 0
cpuid
mov res, eax
}
if (res == 0)
{
//
// This is an old 486!
//
return FALSE;
}
//
// Check the processor feature info.
//
_asm
{
mov eax, 1
cpuid
mov res, edx
}
if (res & (1 << 4))
{
return TRUE;
}
else
{
return FALSE;
}
}
//
// Returns the number of processor ticks since the processor was reset / 65536
//
ULONG OS_rdtsc(void)
{
ULONG hi;
ULONG lo;
_asm
{
rdtsc
mov hi, edx
mov lo, eax
}
ULONG ans;
ans = lo >> 16;
ans |= hi << 16;
return ans;
}
void OS_work_out_mhz(void)
{
if (OS_has_rdtsc())
{
SLONG tick;
ULONG tsc1 = OS_rdtsc();
//
// Wait a second.
//
tick = OS_ticks();
while(OS_ticks() < tick + 1000);
ULONG tsc2 = OS_rdtsc();
float persec = float(tsc2 - tsc1);
persec *= 65536.0F / 1000000.0F;
OS_mhz = SLONG(persec + 0.5F);
}
else
{
//
// It must be a 486... lets say its 66Mhz and be hopeful.
//
OS_mhz = 66;
}
}
SLONG OS_processor_mhz(void)
{
return OS_mhz;
}
void OS_error(CBYTE *fmt, ...)
{
if (!OS_frame_is_fullscreen)
{
//
// Work out the real message.
//
CBYTE message[512];
va_list ap;
va_start(ap, fmt);
vsprintf(message, fmt, ap);
va_end (ap);
MessageBox(
OS_window_handle,
message,
"Error",
MB_ICONERROR | MB_OK);
}
}
SLONG OS_is_archive_bit_set(CBYTE *fname)
{
SLONG attribs;
attribs = GetFileAttributes(fname);
if (attribs == -1)
{
//
// Error!
//
return FALSE;
}
else
{
if (attribs & FILE_ATTRIBUTE_ARCHIVE)
{
return TRUE;
}
else
{
return FALSE;
}
}
}
void OS_clear_archive_bit(CBYTE *fname)
{
SLONG attribs;
attribs = GetFileAttributes(fname);
if (attribs == -1)
{
//
// Error!
//
return;
}
else
{
attribs &= ~FILE_ATTRIBUTE_ARCHIVE;
SetFileAttributes(fname, attribs);
}
}
// ========================================================
//
// MOUSE STUFF
//
// ========================================================
void OS_mouse_get(SLONG *x, SLONG *y)
{
POINT point;
GetCursorPos(&point);
if (!OS_frame_is_fullscreen)
{
RECT rect;
//
// (point.x,point.y) is in screen coordinates. We want it to
// be relative to the client rectangle of our window.
//
ScreenToClient(
OS_window_handle,
&point);
}
*x = point.x;
*y = point.y;
}
void OS_mouse_set(SLONG x, SLONG y)
{
if (!OS_frame_is_fullscreen)
{
POINT point;
point.x = x;
point.y = y;
//
// (x,y) is in relative to the client rectangle of our window
// but SetCurorPos wandt screen coordinates.
//
ClientToScreen(
OS_window_handle,
&point);
}
SetCursorPos(x, y);
}
SLONG OS_message_handler_request_quit;
LRESULT CALLBACK OS_message_handler(
HWND window_handle,
UINT message_type,
WPARAM param_w,
LPARAM param_l)
{
UBYTE scancode;
switch(message_type)
{
case WM_PAINT:
//
// The user callback function does all the screen drawing.
// Do enough to appease windows.
//
HDC device_context_handle;
PAINTSTRUCT paintstruct;
device_context_handle = BeginPaint(window_handle, &paintstruct);
EndPaint(window_handle, &paintstruct);
return 0;
case WM_CHAR:
KEY_inkey = param_w;
break;
case WM_KEYDOWN:
case WM_KEYUP:
//
// Keyboard stuff.
//
scancode = (param_l >> 16) & 0xff;
scancode |= (param_l >> 17) & 0x80;
if (message_type == WM_KEYDOWN)
{
KEY_on[scancode] = 1;
}
else
{
KEY_on[scancode] = 0;
}
//
// Alt keys don't work.
//
KEY_on[KEY_LALT] = 0;
KEY_on[KEY_RALT] = 0;
//
// Check for shift/alt/control keys.
//
KEY_shift = 0;
if (KEY_on[KEY_LSHIFT ] || KEY_on[KEY_RSHIFT ]) KEY_shift |= KEY_SHIFT;
if (KEY_on[KEY_LCONTROL] || KEY_on[KEY_RCONTROL]) KEY_shift |= KEY_CONTROL;
if (KEY_on[KEY_LALT ] || KEY_on[KEY_RALT ]) KEY_shift |= KEY_ALT;
if (KEY_on[KEY_ESCAPE])
{
OS_message_handler_request_quit = TRUE;
}
break;
case WM_MOVE:
//
// Tell the frame about the new position of the window.
//
OS_frame.Move(LOWORD(param_l),HIWORD(param_l));
//
// Fall through to the default handling.
//
break;
case WM_CLOSE:
OS_message_handler_request_quit = TRUE;
return 0;
default:
break;
}
//
// Just let windows do its normal thing.
//
return DefWindowProc(
window_handle,
message_type,
param_w,
param_l);
}
void OS_process_messages()
{
MSG msg;
int ret;
while(1)
{
//
// Poll the joystick.
//
OS_joy_poll();
//
// Activate the changes made to the direct sound settings...
//
OS_sound_update_changes();
//
// Wait for a message to be available.
//
WaitMessage();
while(PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
{
ret = GetMessage(&msg, NULL, 0, 0);
if (ret == 0 || ret == -1)
{
//
// Kill the game!
//
return;
}
TranslateMessage(&msg);
DispatchMessage (&msg);
}
if (OS_message_handler_request_quit)
{
OS_message_handler_request_quit = FALSE;
//
// The window has been told to close!
//
return;
}
}
}
//
// Valid devices.
//
typedef struct
{
D3DEnum_DriverInfo *driver;
D3DEnum_DeviceInfo *device;
D3DEnum_ModeInfo *mode; // NULL => Use windowed mode.
} OS_Mode;
#define OS_MAX_MODES 16
OS_Mode OS_mode[OS_MAX_MODES];
SLONG OS_mode_upto;
SLONG OS_mode_sel;
//
// Finds the valid devices from the D3DEnumerated choice and set
// the OS_mode_sel to the default.
//
void OS_mode_init()
{
SLONG i;
D3DEnum_DriverInfo *vi;
D3DEnum_DeviceInfo *ci;
D3DEnum_ModeInfo *mi; // NULL => Use windowed mode.
OS_mode_upto = 0;
OS_mode_sel = 0;
SLONG lookfor640x480;
SLONG lookfor512x384;
//
// Find all valid modes.
//
for (vi = D3DEnum_GetFirstDriver(); vi; vi = vi->pNext)
{
for (ci = vi->pFirstDevice; ci; ci = ci->pNext)
{
if (ci->bIsHardware)
{
//
// Found a hardware device.
//
if (WITHIN(OS_mode_upto, 0, OS_MAX_MODES - 1))
{
OS_mode[OS_mode_upto].driver = vi;
OS_mode[OS_mode_upto].device = ci;
OS_mode[OS_mode_upto].mode = NULL;
#ifdef NDEBUG
lookfor512x384 = FALSE;
lookfor640x480 = TRUE;
#else
lookfor512x384 = FALSE;
lookfor640x480 = !ci->bWindowed;
#endif
if (lookfor512x384)
{
//
// Look for the first 512x384 mode.
//
for (mi = ci->pFirstMode; mi; mi = mi->pNext)
{
if (mi->ddsd.dwWidth == 512 &&
mi->ddsd.dwHeight == 384)
{
OS_mode[OS_mode_upto].mode = mi;
//
// We already have our mode.
//
lookfor640x480 = FALSE;
break;
}
}
}
if (lookfor640x480)
{
//
// Look for the first 640x480 mode.
//
for (mi = ci->pFirstMode; mi; mi = mi->pNext)
{
if (mi->ddsd.dwWidth == 640 &&
mi->ddsd.dwHeight == 480)
{
OS_mode[OS_mode_upto].mode = mi;
break;
}
}
}
if (OS_mode[OS_mode_upto].mode == NULL)
{
//
// Make sure this device support windowed mode!
//
if (OS_mode[OS_mode_upto].device->bWindowed)
{
//
// We are ok.
//
}
else
{
//
// Use the first available mode.
//
OS_mode[OS_mode_upto].mode = OS_mode[OS_mode_upto].device->pFirstMode;
}
}
OS_mode_upto += 1;
}
}
}
}
#ifndef NDEBUG
//
// In debug build choose the first windowed mode.
//
for (i = 0; i < OS_mode_upto; i++)
{
if (OS_mode[i].device->bWindowed)
{
OS_mode_sel = i;
}
}
#else
//
// In release build choose the last mode.
//
OS_mode_sel = OS_mode_upto - 1;
#endif
}
//
// Adds the modes for the current selection to the combo box.
//
void OS_mydemo_setup_mode_combo(HWND combo_handle, SLONG mode)
{
SLONG index;
ASSERT(WITHIN(mode, 0, OS_mode_upto - 1));
//
// Clear all old modes.
//
SendMessage(combo_handle, CB_RESETCONTENT, 0, 0);
//
// Add each mode.
//
D3DEnum_ModeInfo *mi;
if (OS_mode[mode].device->bWindowed)
{
index = SendMessage(combo_handle, CB_ADDSTRING, 0, (LPARAM) "In a window");
SendMessage(combo_handle, CB_SETITEMDATA, (WPARAM) index, (LPARAM) NULL);
if (NULL == OS_mode[mode].mode)
{
//
// This is the current selection.
//
SendMessage(combo_handle, CB_SETCURSEL, index, 0);
}
}
for (mi = OS_mode[mode].device->pFirstMode; mi; mi = mi->pNext)
{
index = SendMessage(combo_handle, CB_ADDSTRING, 0, (LPARAM) mi->strDesc);
SendMessage(combo_handle, CB_SETITEMDATA, (WPARAM) index, (LPARAM) mi);
if (mi == OS_mode[mode].mode)
{
//
// This is the current selection.
//
SendMessage(combo_handle, CB_SETCURSEL, index, 0);
}
}
}
//
// The callback function for the MyDemo dialog box.
//
#define OS_MYDEMO_RUN 1
#define OS_MYDEMO_EXIT 2
BOOL CALLBACK OS_mydemo_proc(
HWND dialog_handle,
UINT message_type,
WPARAM param_w,
LPARAM param_l)
{
SLONG i;
SLONG d;
SLONG res;
SLONG index;
RECT rect;
HWND combo_handle;
D3DEnum_DriverInfo *vi;
D3DEnum_DeviceInfo *ci;
switch(message_type)
{
case WM_INITDIALOG:
//
// Fill out the list boxes with the correct values. First find
// all compatible
//
combo_handle = GetDlgItem(dialog_handle, IDC_COMBO_DRIVER);
for (i = 0; i < OS_mode_upto; i++)
{
SendMessage(combo_handle, CB_ADDSTRING, 0, (LPARAM) OS_mode[i].driver->strDesc);
}
//
// Set the current selection.
//
SendMessage(combo_handle, CB_SETCURSEL, OS_mode_sel, 0);
//
// Add the modes for the current selection.
//
combo_handle = GetDlgItem(dialog_handle, IDC_COMBO_MODE);
OS_mydemo_setup_mode_combo(combo_handle, OS_mode_sel);
{
CBYTE mhz[64];
sprintf(mhz, "Detected a %dMhz processor", OS_mhz);
//
// What speed processor have we detected?
//
SetWindowText(GetDlgItem(dialog_handle, IDC_PROCESSOR), mhz);
}
//
// Run the egg physics by default.
//
CheckDlgButton(dialog_handle, IDC_RUN_PATCH_TEST, BST_CHECKED);
return TRUE;
case WM_COMMAND:
switch(LOWORD(param_w))
{
case IDOK:
EndDialog(dialog_handle, OS_MYDEMO_RUN);
return TRUE;
case IDCANCEL:
EndDialog(dialog_handle, OS_MYDEMO_EXIT);
return TRUE;
case IDC_COMBO_DRIVER:
switch(HIWORD(param_w))
{
case CBN_SELCHANGE:
//
// Change the list of modes.
//
OS_mode_sel = SendMessage((HWND) param_l, CB_GETCURSEL, 0, 0);
ASSERT(WITHIN(OS_mode_sel, 0, OS_mode_upto - 1));
OS_mydemo_setup_mode_combo(
GetDlgItem(dialog_handle, IDC_COMBO_MODE),
OS_mode_sel);
break;
}
break;
case IDC_COMBO_MODE:
switch(HIWORD(param_w))
{
case CBN_SELCHANGE:
//
// Update the current mode.
//
index = SendMessage((HWND) param_l, CB_GETCURSEL, 0, 0);
ASSERT(WITHIN(OS_mode_sel, 0, OS_mode_upto - 1));
OS_mode[OS_mode_sel].mode = (D3DEnum_ModeInfo * /* We hope */) SendMessage((HWND) param_l, CB_GETITEMDATA, (WPARAM) index, 0);
break;
}
break;
}
break;
case WM_CLOSE:
EndDialog(dialog_handle, OS_MYDEMO_EXIT);
return TRUE;
}
return FALSE;
}
//
// The game thread's function and HANDLE.
//
HANDLE OS_game_thread_handle;
DWORD OS_game_thread_id;
DWORD WINAPI OS_game_thread_function(void *data)
{
MAIN_main();
OS_message_handler_request_quit = TRUE;
OS_game_thread_handle = FALSE;
OS_game_thread_id = FALSE;
//
// Send a windows message to our window to wake it up so it'll exit.
//
PostMessage(OS_window_handle, WM_CLOSE, 0, 0);
return 0;
}
//
// The entry point of the program.
//
int WINAPI WinMain(
HINSTANCE this_instance,
HINSTANCE last_instance,
LPSTR command_line,
int start_show_state)
{
HRESULT res;
//
// Remember the arguments passed to this function.
//
OS_this_instance = this_instance;
OS_last_instance = last_instance;
OS_command_line = command_line;
OS_start_show_state = start_show_state;
OS_wcl.hInstance = this_instance;
OS_wcl.lpszClassName = OS_application_name;
OS_wcl.lpfnWndProc = OS_message_handler;
OS_wcl.style = 0;
OS_wcl.cbSize = sizeof(WNDCLASSEX);
OS_wcl.cbClsExtra = 0;
OS_wcl.cbWndExtra = 0;
OS_wcl.lpszMenuName = NULL;
OS_wcl.hIcon = LoadIcon(this_instance, MAKEINTRESOURCE(IDI_ICON1));
OS_wcl.hIconSm = NULL;//LoadIcon(this_instance, MAKEINTRESOURCE(IDI_ICON1));
OS_wcl.hCursor = LoadCursor(NULL, IDC_ARROW);
OS_wcl.hbrBackground = (HBRUSH) GetStockObject(NULL_BRUSH);
//
// Register the window class.
//
if (RegisterClassEx(&OS_wcl) == 0)
{
//
// Could not register the window class!
//
return 0;
}
//
// The window size in windowed mode.
//
#define OS_WINDOW_WIDTH 640
#define OS_WINDOW_HEIGHT 480
//
// Create a window
//
RECT rect;
rect.left = 100 + 0;
rect.right = 100 + OS_WINDOW_WIDTH;
rect.top = 100 + 0;
rect.bottom = 100 + OS_WINDOW_HEIGHT;
AdjustWindowRect(
&rect,
WS_CAPTION,
FALSE);
OS_window_handle = CreateWindow(
OS_application_name,
OS_application_name,
WS_CAPTION | WS_SYSMENU,
50,
50,
rect.right - rect.left,
rect.bottom - rect.top,
NULL,
NULL,
this_instance,
NULL);
//
// Make sure it worked.
//
if (OS_window_handle == 0)
{
return 0;
}
//
// Initialise joystick control.
//
OS_joy_init();
//
// Find out the speed of the machine we are running on.
//
OS_work_out_mhz();
//
// Enumerate the devices.
//
D3DEnum_EnumerateDevices(NULL);
D3DEnum_DriverInfo *di = D3DEnum_GetFirstDriver();
//
// Find valid modes.
//
OS_mode_init();
if (OS_mode_upto == 0)
{
//
// No valid screen mode!
//
MessageBox(
OS_window_handle,
"Could not find a 3d accelerator card. Make sure that you have DirectX 6.0 or higher installed.", "Beat", MB_ICONERROR | MB_OK);
exit(1);
}
have_another_go:;
//
// Promt the user to choose a mode.
//
switch(DialogBox(
OS_this_instance,
MAKEINTRESOURCE(IDD_DRIVERS),
OS_window_handle,
OS_mydemo_proc))
{
case OS_MYDEMO_RUN:
break;
case OS_MYDEMO_EXIT:
//
// Close gracefully...
//
D3DEnum_FreeResources();
return 0;
default:
exit(1);
break;
}
{
GUID *driver;
GUID *device;
DDSURFACEDESC2 *display_mode;
BOOL is_windowed;
BOOL is_hardware;
#if WE_USE_THE_DEFAULT_DIALOG_BOX
//
// Prompt the user for a device and a driver.
//
INT ret = D3DEnum_UserDlgSelectDriver(OS_window_handle, TRUE);
D3DEnum_GetSelectedDriver(
&driver,
&device,
&display_mode,
&is_windowed,
&is_hardware);
OS_frame_is_fullscreen = !is_windowed;
OS_frame_is_hardware = is_hardware;
#else
driver = OS_mode[OS_mode_sel].driver->pGUID;
device = OS_mode[OS_mode_sel].device->pGUID;
if (OS_mode[OS_mode_sel].mode)
{
display_mode = &OS_mode[OS_mode_sel].mode->ddsd;
OS_frame_is_fullscreen = TRUE;
OS_frame_is_hardware = TRUE;
}
else
{
display_mode = NULL;
OS_frame_is_fullscreen = FALSE;
OS_frame_is_hardware = TRUE;
}
#endif
//
// Initialise the framework.
//
DWORD flags;
flags = 0;
flags |= D3DFW_BACKBUFFER;
flags |= D3DFW_ZBUFFER;
if (OS_frame_is_fullscreen)
{
flags |= D3DFW_FULLSCREEN;
}
if (OS_frame_is_fullscreen)
{
//
// Hide the mouse.
//
ShowCursor(FALSE);
//
// Makes the window not redraw itself when we go fullscreen.
//
SetWindowLong(OS_window_handle, GWL_STYLE, WS_POPUP);
}
res = OS_frame.Initialize(
OS_window_handle,
driver,
device,
display_mode,
flags);
if (res == S_OK)
{
if (!OS_frame_is_fullscreen)
{
//
// Show our window!
//
ShowWindow(OS_window_handle, SW_SHOW);
}
//
// Enumerate texture formats.
//
{
int i;
OS_Tformat *otf;
//
// Find the texture formats.
//
OS_frame.GetD3DDevice()->EnumTextureFormats(OS_texture_enumerate_pixel_formats, NULL);
//
// The pixel format of the screen.
//
{
DDSURFACEDESC2 ddsd;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
if (OS_frame.GetBackBuffer()->GetSurfaceDesc(&ddsd) == DD_OK)
{
OS_tformat[OS_TEXTURE_FORMAT_SCREEN].valid = TRUE;
OS_tformat[OS_TEXTURE_FORMAT_SCREEN].ddpf = ddsd.ddpfPixelFormat;
}
}
//
// Set the masks and shifts for each texture format.
//
for (i = 0; i < OS_TEXTURE_FORMAT_NUMBER; i++)
{
otf = &OS_tformat[i];
if (i == OS_TEXTURE_FORMAT_8)
{
//
// We don't have to set the masks and shifts for grayscale textures.
//
continue;
}
if (otf->valid)
{
//
// Calculate the masks and shifts.
//
OS_calculate_mask_and_shift(otf->ddpf.dwRBitMask, &otf->mask_r, &otf->shift_r);
OS_calculate_mask_and_shift(otf->ddpf.dwGBitMask, &otf->mask_g, &otf->shift_g);
OS_calculate_mask_and_shift(otf->ddpf.dwBBitMask, &otf->mask_b, &otf->shift_b);
if (otf->ddpf.dwFlags & DDPF_ALPHAPIXELS)
{
OS_calculate_mask_and_shift(otf->ddpf.dwRGBAlphaBitMask, &otf->mask_a, &otf->shift_a);
}
}
}
}
//
// What is the screen-res?
//
RECT *dimensions = OS_frame.GetViewportRect();
OS_screen_width = float(dimensions->right);
OS_screen_height = float(dimensions->bottom);
//
// Work out how to multi-texture, find all the texture directories,
// initailise the sound system and setup the floating point unit.
//
ftol_init();
OS_pipeline_calculate();
OS_init_texpaths();
OS_sound_init();
//
// Time relative to the beginning of the program.
//
OS_game_start_tick_count = GetTickCount();
//
// Start the game.
//
OS_game_thread_handle = CreateThread(
NULL,
0,
OS_game_thread_function,
NULL,
0,
&OS_game_thread_id);
if (OS_game_thread_handle)
{
//
// Make the thread get less time than the message loop!
//
SetThreadPriority(
OS_game_thread_handle,
THREAD_PRIORITY_BELOW_NORMAL);
//
// This thread processes messages while the game
// thread runs the game.
//
OS_process_messages();
if (OS_game_thread_handle)
{
//
// Exit the game thread.
//
TerminateThread(
OS_game_thread_handle,
0);
}
}
//
// Clean up.
//
OS_frame.DestroyObjects();
OS_sound_finish();
}
else
{
if (OS_frame_is_fullscreen)
{
//
// Show the mouse.
//
ShowCursor(TRUE);
}
//
// Could not set that mode!
//
CBYTE *err;
if (res == D3DFWERR_NOZBUFFER)
{
err = "There was not enough memory to create the zbuffer. Try using a lower resolution mode.";
}
else
{
err = "Could not setup the display using those settings. Try changing driver or using another mode.";
}
MessageBox(
OS_window_handle,
err,
"Beat",
MB_ICONERROR | MB_OK);
//
// Have another go...
//
goto have_another_go;
}
}
//
// Free all enumeration resources.
//
D3DEnum_FreeResources();
return 0;
}
// ========================================================
//
// ROTATING POINTS
//
// ========================================================
//
// The camera and the screen.
//
float OS_cam_x;
float OS_cam_y;
float OS_cam_z;
float OS_cam_aspect;
float OS_cam_lens;
float OS_cam_view_dist;
float OS_cam_over_view_dist;
float OS_cam_matrix[9];
float OS_cam_view_matrix[9];
float OS_cam_view_matrix_plus_local_rot[9];
float OS_cam_screen_x1;
float OS_cam_screen_y1;
float OS_cam_screen_x2;
float OS_cam_screen_y2;
float OS_cam_screen_width;
float OS_cam_screen_height;
float OS_cam_screen_mid_x;
float OS_cam_screen_mid_y;
float OS_cam_screen_mul_x;
float OS_cam_screen_mul_y;
void OS_camera_set(
float world_x,
float world_y,
float world_z,
float view_dist,
float yaw,
float pitch,
float roll,
float lens,
float screen_x1,
float screen_y1,
float screen_x2,
float screen_y2)
{
float matrix[9];
MATRIX_calc(
matrix,
yaw,
pitch,
roll);
OS_camera_set(
world_x,
world_y,
world_z,
view_dist,
matrix,
lens,
screen_x1,
screen_y1,
screen_x2,
screen_y2);
}
void OS_camera_set(
float world_x,
float world_y,
float world_z,
float view_dist,
float matrix[9],
float lens,
float screen_x1,
float screen_y1,
float screen_x2,
float screen_y2)
{
OS_cam_screen_x1 = screen_x1 * OS_screen_width;
OS_cam_screen_y1 = screen_y1 * OS_screen_height;
OS_cam_screen_x2 = screen_x2 * OS_screen_width;
OS_cam_screen_y2 = screen_y2 * OS_screen_height;
OS_cam_screen_width = OS_cam_screen_x2 - OS_cam_screen_x1;
OS_cam_screen_height = OS_cam_screen_y2 - OS_cam_screen_y1;
OS_cam_screen_mid_x = OS_cam_screen_x1 + OS_cam_screen_width * 0.5F;
OS_cam_screen_mid_y = OS_cam_screen_y1 + OS_cam_screen_height * 0.5F;
OS_cam_screen_mul_x = OS_cam_screen_width * 0.5F / OS_ZCLIP_PLANE;
OS_cam_screen_mul_y = OS_cam_screen_height * 0.5F / OS_ZCLIP_PLANE;
OS_cam_x = world_x;
OS_cam_y = world_y;
OS_cam_z = world_z;
OS_cam_lens = lens;
OS_cam_view_dist = view_dist;
OS_cam_over_view_dist = 1.0F / view_dist;
OS_cam_aspect = OS_cam_screen_height / OS_cam_screen_width;
memcpy(OS_cam_matrix, matrix, sizeof(OS_cam_matrix));
memcpy(OS_cam_view_matrix, matrix, sizeof(OS_cam_matrix));
MATRIX_skew(
OS_cam_matrix,
OS_cam_aspect,
OS_cam_lens,
OS_cam_over_view_dist); // Shrink the matrix down so the furthest point has a view distance z of 1.0F
}
OS_Trans OS_trans[OS_MAX_TRANS];
SLONG OS_trans_upto;
void OS_transform(
float world_x,
float world_y,
float world_z,
OS_Trans *os)
{
os->x = world_x - OS_cam_x;
os->y = world_y - OS_cam_y;
os->z = world_z - OS_cam_z;
MATRIX_MUL(
OS_cam_matrix,
os->x,
os->y,
os->z);
os->clip = OS_CLIP_ROTATED;
if (os->z < OS_ZCLIP_PLANE)
{
os->clip |= OS_CLIP_NEAR;
return;
}
else
if (os->z > 1.0F)
{
os->clip |= OS_CLIP_FAR;
return;
}
else
{
//
// The z-range of the point is okay.
//
os->Z = OS_ZCLIP_PLANE / os->z;
os->X = OS_cam_screen_mid_x + OS_cam_screen_mul_x * os->x * os->Z;
os->Y = OS_cam_screen_mid_y - OS_cam_screen_mul_y * os->y * os->Z;
//
// Set the clipping flags.
//
os->clip |= OS_CLIP_TRANSFORMED;
if (os->X < 0.0F ) {os->clip |= OS_CLIP_LEFT;}
else if (os->X > OS_screen_width) {os->clip |= OS_CLIP_RIGHT;}
if (os->Y < 0.0F ) {os->clip |= OS_CLIP_TOP;}
else if (os->Y > OS_screen_height) {os->clip |= OS_CLIP_BOTTOM;}
return;
}
}
float OS_local_world_x;
float OS_local_world_y;
float OS_local_world_z;
float OS_local_world_matrix[9];
float OS_local_view_x;
float OS_local_view_y;
float OS_local_view_z;
float OS_local_matrix_plus_camera[9];
void OS_local_position_and_rotation(
float world_x,
float world_y,
float world_z,
float local_rotation_matrix[9])
{
//
// Store the local rotation and position.
//
OS_local_world_x = world_x;
OS_local_world_y = world_y;
OS_local_world_z = world_z;
memcpy(OS_local_world_matrix, local_rotation_matrix, sizeof(OS_local_world_matrix));
MATRIX_TRANSPOSE(OS_local_world_matrix);
//
// Premultiply this matrix by the camera matrix.
//
MATRIX_3x3mul(OS_local_matrix_plus_camera, OS_cam_matrix, OS_local_world_matrix);
MATRIX_3x3mul(OS_cam_view_matrix_plus_local_rot, OS_cam_view_matrix, OS_local_world_matrix);
//
// Put the centre of the object into viewspace.
//
OS_local_view_x = world_x - OS_cam_x;
OS_local_view_y = world_y - OS_cam_y;
OS_local_view_z = world_z - OS_cam_z;
MATRIX_MUL(
OS_cam_matrix,
OS_local_view_x,
OS_local_view_y,
OS_local_view_z);
}
void OS_transform_local(
float local_x,
float local_y,
float local_z,
OS_Trans *os)
{
//
// Work out the posiiton of this point in viewspace.
//
MATRIX_MUL(
OS_local_matrix_plus_camera,
local_x,
local_y,
local_z);
os->x = local_x + OS_local_view_x;
os->y = local_y + OS_local_view_y;
os->z = local_z + OS_local_view_z;
os->clip = OS_CLIP_ROTATED;
if (os->z < OS_ZCLIP_PLANE)
{
os->clip |= OS_CLIP_NEAR;
return;
}
else
if (os->z > 1.0F)
{
os->clip |= OS_CLIP_FAR;
return;
}
else
{
//
// The z-range of the point is okay.
//
os->Z = OS_ZCLIP_PLANE / os->z;
os->X = OS_cam_screen_mid_x + OS_cam_screen_mul_x * os->x * os->Z;
os->Y = OS_cam_screen_mid_y - OS_cam_screen_mul_y * os->y * os->Z;
//
// Set the clipping flags.
//
os->clip |= OS_CLIP_TRANSFORMED;
if (os->X < 0.0F ) {os->clip |= OS_CLIP_LEFT;}
else if (os->X > OS_screen_width) {os->clip |= OS_CLIP_RIGHT;}
if (os->Y < 0.0F ) {os->clip |= OS_CLIP_TOP;}
else if (os->Y > OS_screen_height) {os->clip |= OS_CLIP_BOTTOM;}
return;
}
}
// ========================================================
//
// DRAWING STUFF
//
// ========================================================
void OS_clear_screen(UBYTE r, UBYTE g, UBYTE b, float z)
{
ULONG colour = (r << 16) | (g << 8) | (b << 0);
HRESULT ret = OS_frame.GetViewport()->Clear2(
1,
(D3DRECT *) OS_frame.GetViewportRect(),
D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
colour,
z,
0);
}
SLONG OS_in_scene;
void OS_scene_begin()
{
ASSERT(!OS_in_scene);
OS_frame.GetD3DDevice()->BeginScene();
OS_in_scene = TRUE;
//
// Set the render states to their default values.
//
OS_init_renderstates();
}
void OS_scene_end()
{
ASSERT(OS_in_scene);
OS_frame.GetD3DDevice()->EndScene();
OS_in_scene = FALSE;
}
SLONG OS_fps;
SLONG OS_last_time;
SLONG OS_last_frame_count;
SLONG OS_frame_count;
SLONG OS_fps_get()
{
SLONG now;
now = OS_ticks();
OS_frame_count += 1;
if (now >= OS_last_time + 1000)
{
OS_fps = OS_frame_count - OS_last_frame_count;
OS_last_frame_count = OS_frame_count;
OS_last_time = now;
}
return OS_fps;
}
void OS_fps_draw()
{
SLONG i;
float x1;
float y1;
float x2;
float y2;
float tick;
SLONG now;
now = OS_ticks();
OS_frame_count += 1;
if (now >= OS_last_time + 1000)
{
OS_fps = OS_frame_count - OS_last_frame_count;
OS_last_frame_count = OS_frame_count;
OS_last_time = now;
}
OS_Buffer *ob = OS_buffer_new();
for (i = 0; i < OS_fps; i++)
{
switch((i + 1) % 10)
{
case 0:
tick = 8.0F;
break;
case 5:
tick = 5.0F;
break;
default:
tick = 3.0F;
break;
}
x1 = 5.0F + i * 2.0F;
y1 = 5.0F;
x2 = 5.0F + i * 2.0F + 1.0F;
y2 = 5.0F + tick;
x1 /= OS_screen_width;
y1 /= OS_screen_height;
x2 /= OS_screen_width;
y2 /= OS_screen_height;
OS_buffer_add_sprite(
ob,
x1,
y1,
x2,
y2,
0.0F, 1.0F,
0.0F, 1.0F,
0.0F,
0x00ffffff,
0x00000000,
OS_FADE_BOTTOM);
}
OS_buffer_draw(ob, NULL);
}
void OS_show()
{
if (OS_in_scene)
{
OS_scene_end();
}
if (OS_frame_is_fullscreen)
{
//
// Do we blit or do we flip?
//
if (1)
{
//
// Flip.
//
OS_frame.ShowFrame();
}
else
{
//
// Blit.
//
LPDIRECTDRAWSURFACE4 fb = OS_frame.GetFrontBuffer();
LPDIRECTDRAWSURFACE4 bb = OS_frame.GetBackBuffer();
fb->Blt(NULL, bb, NULL, DDBLT_WAIT, NULL);
}
}
else
{
OS_frame.ShowFrame();
}
OS_poly_count_drawn = 0;
OS_poly_count_transformed = 0;
}
//
// Our flexible vertex format.
//
typedef struct
{
float sx;
float sy;
float sz;
float rhw;
ULONG colour;
ULONG specular;
float tu1;
float tv1;
float tu2;
float tv2;
} OS_Flert;
#define OS_FLERT_FORMAT (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEX2)
// ========================================================
//
// BUFFER STUFF
//
// ========================================================
typedef struct os_buffer
{
SLONG num_flerts;
SLONG num_indices;
SLONG max_flerts;
SLONG max_indices;
OS_Flert *flert;
UWORD *index;
OS_Buffer *next;
} OS_Buffer;
OS_Buffer *OS_buffer_free;
//
// Creates a new buffer.
//
OS_Buffer *OS_buffer_create(void)
{
//
// Allocate the buffer.
//
OS_Buffer *ob = (OS_Buffer *) malloc(sizeof(OS_Buffer));
//
// Initialise the buffer.
//
ob->max_flerts = 256;
ob->max_indices = 1024;
ob->num_flerts = 0;
ob->num_indices = 1;
ob->flert = (OS_Flert *) malloc(sizeof(OS_Flert) * ob->max_flerts );
ob->index = (UWORD *) malloc(sizeof(UWORD ) * ob->max_indices);
memset(ob->flert, 0, sizeof(OS_Flert) * ob->max_flerts );
memset(ob->index, 0, sizeof(UWORD ) * ob->max_indices);
ob->next = NULL;
return ob;
}
//
// Gets a buffer from the linked list of free buffers. If the free list is empty,
// it creates a new one.
//
OS_Buffer *OS_buffer_get(void)
{
OS_Buffer *ans;
if (OS_buffer_free)
{
ans = OS_buffer_free;
OS_buffer_free = OS_buffer_free->next;
ans->next = NULL;
}
else
{
ans = OS_buffer_create();
}
return ans;
}
//
// Returns a buffer to the free list.
//
void OS_buffer_give(OS_Buffer *ob)
{
ob->next = OS_buffer_free;
OS_buffer_free = ob;
}
OS_Buffer *OS_buffer_new(void)
{
OS_Buffer *ob = OS_buffer_get();
ob->num_indices = 0;
ob->num_flerts = 1;
return ob;
}
//
// Grows the size of the flert array.
//
void OS_buffer_grow_flerts(OS_Buffer *ob)
{
ob->max_flerts *= 2;
ob->flert = (OS_Flert *) realloc(ob->flert, ob->max_flerts * sizeof(OS_Flert));
}
void OS_buffer_grow_indices(OS_Buffer *ob)
{
ob->max_indices *= 2;
ob->index = (UWORD *) realloc(ob->index, ob->max_indices * sizeof(UWORD));
}
void OS_buffer_add_vert(OS_Buffer *ob, OS_Vert *ov)
{
OS_Trans *ot;
OS_Flert *of;
//
// Make sure we've got enough room for another vertex.
//
if (ob->num_flerts >= ob->max_flerts)
{
//
// We need a bigger buffer.
//
OS_buffer_grow_flerts(ob);
}
ASSERT(WITHIN(ov->trans, 0, OS_MAX_TRANS - 1));
of = &ob->flert[ob->num_flerts];
ot = &OS_trans [ov->trans];
//
// Create the new tlvertex.
//
of->sx = ot->X;
of->sy = ot->Y;
of->sz = 1.0F - ot->Z; //ot->z;
of->rhw = ot->Z;
of->colour = ov->colour;
of->specular = ov->specular;
of->tu1 = ov->u1;
of->tv1 = ov->v1;
of->tu2 = ov->u2;
of->tv2 = ov->v2;
//
// Store the index of the flert inside the vertex.
//
ov->index = ob->num_flerts++;
}
//
// Returns the colour 'v' along from colour1 to colour2 where 0.0 <= v <= 1.0
//
ULONG OS_interpolate_colour(float v, ULONG colour1, ULONG colour2)
{
SLONG rb1, rb2, drb, rba;
SLONG ga1, ga2, dga, gaa;
union
{
float vfloat;
ULONG vfixed8;
};
SLONG answer;
//
// Early outs.
//
if (colour1 == colour2) {return colour1;}
if (v < 0.01F) {return colour1;}
if (v > 0.99F) {return colour2;}
//
// Work out how much to interpolate along in fixed point 8.
//
vfloat = 32768.0F + v;
vfixed8 &= 0xff;
//
// Red and blue.
//
rb1 = colour1 & (0x00ff00ff);
rb2 = colour2 & (0x00ff00ff);
if (rb1 != rb2)
{
//
// Do interpolation of red and blue simultaneously.
//
drb = rb2 - rb1;
drb *= vfixed8;
drb >>= 8;
rba = rb1 + drb;
rba &= 0x00ff00ff;
}
else
{
//
// No need to interpolated red and blue.
//
rba = rb1;
}
//
// Green and alpha.
//
ga1 = colour1 >> 8;
ga2 = colour2 >> 8;
ga1 &= 0x00ff00ff;
ga2 &= 0x00ff00ff;
if (ga1 != ga2)
{
//
// Do interpolationg of red and blue simultaneously.
//
dga = ga2 - ga1;
dga *= vfixed8;
gaa = (ga1 << 8) + dga;
gaa &= 0xff00ff00;
}
else
{
//
// No need to interpolate green and alpha.
//
gaa = ga1 << 8;
}
answer = rba | gaa;
return answer;
}
// ========================================================
//
// Functions to pass the the CLIP module.
//
void OS_clip_helper_interpolate(void *new_point, void *point1, void *point2, float along)
{
OS_Vert *ov1 = (OS_Vert *) point1;
OS_Vert *ov2 = (OS_Vert *) point2;
OS_Vert *ovn = (OS_Vert *) new_point;
ASSERT(WITHIN(ov1->trans, 0, OS_trans_upto - 1));
ASSERT(WITHIN(ov2->trans, 0, OS_trans_upto - 1));
OS_Trans *ot1 = &OS_trans[ov1->trans];
OS_Trans *ot2 = &OS_trans[ov2->trans];
//
// We need a new OS_Trans for the new point.
//
ASSERT(WITHIN(OS_trans_upto, 0, OS_MAX_TRANS - 1));
OS_Trans *otn = &OS_trans[OS_trans_upto];
otn->x = ot1->x + along * (ot2->x - ot1->x);
otn->y = ot1->y + along * (ot2->y - ot1->y);
otn->z = OS_ZCLIP_PLANE;
otn->Z = 1.0F;
otn->X = OS_cam_screen_mid_x + OS_cam_screen_mul_x * otn->x;
otn->Y = OS_cam_screen_mid_y - OS_cam_screen_mul_y * otn->y;
otn->clip = OS_CLIP_TRANSFORMED | OS_CLIP_ROTATED;
if (otn->X < 0.0F ) {otn->clip |= OS_CLIP_LEFT;}
else if (otn->X > OS_screen_width) {otn->clip |= OS_CLIP_RIGHT;}
if (otn->Y < 0.0F ) {otn->clip |= OS_CLIP_TOP;}
else if (otn->Y > OS_screen_height) {otn->clip |= OS_CLIP_BOTTOM;}
//
// Now build the new OS_Vert.
//
ovn->colour = OS_interpolate_colour(along, ov1->colour, ov2->colour );
ovn->specular = OS_interpolate_colour(along, ov1->specular, ov2->specular);
ovn->index = NULL;
ovn->trans = OS_trans_upto;
ovn->u1 = ov1->u1 + along * (ov2->u1 - ov1->u1);
ovn->v1 = ov1->v1 + along * (ov2->v1 - ov1->v1);
ovn->u2 = ov1->u2 + along * (ov2->u2 - ov1->u2);
ovn->v2 = ov1->v2 + along * (ov2->v2 - ov1->v2);
OS_trans_upto += 1;
}
float OS_clip_helper_signed_distance(void *point)
{
OS_Vert *ov;
OS_Trans *ot;
ov = (OS_Vert *) point;
ASSERT(WITHIN(ov->trans, 0, OS_MAX_TRANS - 1));
ot = &OS_trans[ov->trans];
return ot->z - OS_ZCLIP_PLANE;
}
//
// ========================================================
void OS_buffer_add_triangle(
OS_Buffer *ob,
OS_Vert *ov1,
OS_Vert *ov2,
OS_Vert *ov3)
{
OS_poly_count_transformed += 1;
ULONG clip_and =
OS_trans[ov1->trans].clip &
OS_trans[ov2->trans].clip &
OS_trans[ov3->trans].clip;
if (clip_and & OS_CLIP_TRANSFORMED)
{
if (clip_and & OS_CLIP_OFFSCREEN)
{
//
// The triangle is completely off-screen.
//
return;
}
else
{
if (ov1->index == NULL) {OS_buffer_add_vert(ob, ov1);}
if (ov2->index == NULL) {OS_buffer_add_vert(ob, ov2);}
if (ov3->index == NULL) {OS_buffer_add_vert(ob, ov3);}
//
// Add this triangle. All the points are transformed and at least
// one is on screen.
//
if (ob->num_indices + 3 > ob->max_indices)
{
//
// Need a bigger buffer.
//
OS_buffer_grow_indices(ob);
}
ob->index[ob->num_indices++] = ov1->index;
ob->index[ob->num_indices++] = ov2->index;
ob->index[ob->num_indices++] = ov3->index;
return;
}
}
else
{
ULONG clip_or =
OS_trans[ov1->trans].clip |
OS_trans[ov2->trans].clip |
OS_trans[ov3->trans].clip;
if (clip_or & OS_CLIP_TRANSFORMED)
{
//
// This triangle needs to be zclipped.
//
if (clip_or & OS_CLIP_FAR)
{
//
// Reject triangles on the far clip plane.
//
}
else
{
void *clip_tri[3];
void **clip_ans;
SLONG clip_num;
clip_tri[0] = ov1;
clip_tri[1] = ov2;
clip_tri[2] = ov3;
clip_ans = clip_tri;
clip_num = 3;
CLIP_do(
&clip_ans,
&clip_num,
sizeof(OS_Vert),
OS_clip_helper_interpolate,
OS_clip_helper_signed_distance);
ASSERT(
clip_num == 3 ||
clip_num == 4);
//
// Recurse... lazily!
//
ov1 = (OS_Vert *) ((clip_ans)[0]);
ov2 = (OS_Vert *) ((clip_ans)[1]);
ov3 = (OS_Vert *) ((clip_ans)[2]);
ASSERT(WITHIN(ov1->trans, 0, OS_trans_upto - 1));
ASSERT(WITHIN(ov2->trans, 0, OS_trans_upto - 1));
ASSERT(WITHIN(ov3->trans, 0, OS_trans_upto - 1));
{
SLONG i;
for (i = 0; i < clip_num; i++)
{
OS_Vert *ov = (OS_Vert *) (clip_ans[i]);
OS_Trans *ot = &OS_trans[ov->trans];
ASSERT(ot->clip & OS_CLIP_TRANSFORMED);
ASSERT(ot->z >= OS_ZCLIP_PLANE);
}
}
clip_and =
OS_trans[ov1->trans].clip &
OS_trans[ov2->trans].clip &
OS_trans[ov3->trans].clip;
if (clip_and & OS_CLIP_OFFSCREEN)
{
//
// The triangle is completely off-screen.
//
}
else
{
if (ov1->index == NULL) {OS_buffer_add_vert(ob, ov1);}
if (ov2->index == NULL) {OS_buffer_add_vert(ob, ov2);}
if (ov3->index == NULL) {OS_buffer_add_vert(ob, ov3);}
//
// Add this triangle. All the points are transformed and at least
// one is on screen.
//
if (ob->num_indices + 3 > ob->max_indices)
{
//
// Need a bigger buffer.
//
OS_buffer_grow_indices(ob);
}
ob->index[ob->num_indices++] = ov1->index;
ob->index[ob->num_indices++] = ov2->index;
ob->index[ob->num_indices++] = ov3->index;
}
if (clip_num == 4)
{
ov2 = (OS_Vert *) ((clip_ans)[2]);
ov3 = (OS_Vert *) ((clip_ans)[3]);
clip_and =
OS_trans[ov1->trans].clip &
OS_trans[ov2->trans].clip &
OS_trans[ov3->trans].clip;
if (clip_and & OS_CLIP_OFFSCREEN)
{
//
// The triangle is completely off-screen.
//
}
else
{
if (ov1->index == NULL) {OS_buffer_add_vert(ob, ov1);}
if (ov2->index == NULL) {OS_buffer_add_vert(ob, ov2);}
if (ov3->index == NULL) {OS_buffer_add_vert(ob, ov3);}
//
// Add this triangle. All the points are transformed and at least
// one is on screen.
//
if (ob->num_indices + 3 > ob->max_indices)
{
//
// Need a bigger buffer.
//
OS_buffer_grow_indices(ob);
}
ob->index[ob->num_indices++] = ov1->index;
ob->index[ob->num_indices++] = ov2->index;
ob->index[ob->num_indices++] = ov3->index;
}
}
}
return;
}
else
{
//
// The whole triangle is zclipped one way or another. We assume that
// a single triangle is not going to span both the near and far zclip
// planes...
//
return;
}
}
}
void OS_buffer_add_sprite(
OS_Buffer *ob,
float x1, // Normalised to 0.0F - 1.0F
float y1, // Normalised to 0.0F - 1.0F
float x2, // Normalised to 0.0F - 1.0F
float y2, // Normalised to 0.0F - 1.0F
float u1, float v1,
float u2, float v2,
float z,
ULONG colour,
ULONG specular,
ULONG fade)
{
SLONG i;
OS_Flert *of;
//
// Enough room in our buffer?
//
if (ob->num_indices + 6 > ob->max_indices) {OS_buffer_grow_indices(ob);}
if (ob->num_flerts + 4 > ob->max_flerts ) {OS_buffer_grow_flerts (ob);}
//
// Add four vertices.
//
for (i = 0; i < 4; i++)
{
of = &ob->flert[ob->num_flerts + i];
of->sx = ((i & 1) ? x1 : x2) * OS_screen_width;
of->sy = ((i & 2) ? y1 : y2) * OS_screen_height;
of->sz = z;
of->rhw = 0.5F;
of->colour = colour;
of->specular = specular;
of->tu1 = ((i & 1) ? u1 : u2);
of->tv1 = ((i & 2) ? v1 : v2);
of->tu2 = ((i & 1) ? u1 : u2);
of->tv2 = ((i & 2) ? v1 : v2);
}
if (fade)
{
if (fade & OS_FADE_TOP)
{
ob->flert[ob->num_flerts + 2].colour = 0x00000000;
ob->flert[ob->num_flerts + 3].colour = 0x00000000;
}
if (fade & OS_FADE_BOTTOM)
{
ob->flert[ob->num_flerts + 0].colour = 0x00000000;
ob->flert[ob->num_flerts + 1].colour = 0x00000000;
}
if (fade & OS_FADE_LEFT)
{
ob->flert[ob->num_flerts + 1].colour = 0x00000000;
ob->flert[ob->num_flerts + 3].colour = 0x00000000;
}
if (fade & OS_FADE_RIGHT)
{
ob->flert[ob->num_flerts + 0].colour = 0x00000000;
ob->flert[ob->num_flerts + 2].colour = 0x00000000;
}
}
//
// Add two triangles.
//
ob->index[ob->num_indices + 0] = ob->num_flerts + 0;
ob->index[ob->num_indices + 1] = ob->num_flerts + 1;
ob->index[ob->num_indices + 2] = ob->num_flerts + 3;
ob->index[ob->num_indices + 3] = ob->num_flerts + 0;
ob->index[ob->num_indices + 4] = ob->num_flerts + 3;
ob->index[ob->num_indices + 5] = ob->num_flerts + 2;
ob->num_indices += 6;
ob->num_flerts += 4;
}
void OS_buffer_add_multitex_sprite(
OS_Buffer *ob,
float x1, // Normalised to 0.0F - 1.0F
float y1, // Normalised to 0.0F - 1.0F
float x2, // Normalised to 0.0F - 1.0F
float y2, // Normalised to 0.0F - 1.0F
float u11, float v11,
float u12, float v12,
float u21, float v21,
float u22, float v22,
float z,
ULONG colour,
ULONG specular,
ULONG fade)
{
SLONG i;
OS_Flert *of;
//
// Enough room in our buffer?
//
if (ob->num_indices + 6 > ob->max_indices) {OS_buffer_grow_indices(ob);}
if (ob->num_flerts + 4 > ob->max_flerts ) {OS_buffer_grow_flerts (ob);}
//
// Add four vertices.
//
for (i = 0; i < 4; i++)
{
of = &ob->flert[ob->num_flerts + i];
of->sx = ((i & 1) ? x1 : x2) * OS_screen_width;
of->sy = ((i & 2) ? y1 : y2) * OS_screen_height;
of->sz = z;
of->rhw = 0.5F;
of->colour = colour;
of->specular = specular;
of->tu1 = ((i & 1) ? u11 : u12);
of->tv1 = ((i & 2) ? v11 : v12);
of->tu2 = ((i & 1) ? u21 : u22);
of->tv2 = ((i & 2) ? v21 : v22);
}
if (fade)
{
if (fade & OS_FADE_TOP)
{
ob->flert[ob->num_flerts + 2].colour = 0x00000000;
ob->flert[ob->num_flerts + 3].colour = 0x00000000;
}
if (fade & OS_FADE_BOTTOM)
{
ob->flert[ob->num_flerts + 0].colour = 0x00000000;
ob->flert[ob->num_flerts + 1].colour = 0x00000000;
}
if (fade & OS_FADE_LEFT)
{
ob->flert[ob->num_flerts + 1].colour = 0x00000000;
ob->flert[ob->num_flerts + 3].colour = 0x00000000;
}
if (fade & OS_FADE_RIGHT)
{
ob->flert[ob->num_flerts + 0].colour = 0x00000000;
ob->flert[ob->num_flerts + 2].colour = 0x00000000;
}
}
//
// Add two triangles.
//
ob->index[ob->num_indices + 0] = ob->num_flerts + 0;
ob->index[ob->num_indices + 1] = ob->num_flerts + 1;
ob->index[ob->num_indices + 2] = ob->num_flerts + 3;
ob->index[ob->num_indices + 3] = ob->num_flerts + 0;
ob->index[ob->num_indices + 4] = ob->num_flerts + 3;
ob->index[ob->num_indices + 5] = ob->num_flerts + 2;
ob->num_indices += 6;
ob->num_flerts += 4;
}
void OS_buffer_add_sprite_rot(
OS_Buffer *ob,
float x_mid,
float y_mid,
float size, // As a percentage of the width of the screen.
float angle,
float u1, float v1,
float u2, float v2,
float z,
ULONG colour,
ULONG specular,
float tu1, float tv1,
float tu2, float tv2)
{
SLONG i;
OS_Flert *of;
float dx = sin(angle) * size;
float dy = cos(angle) * size;
float x;
float y;
x_mid *= OS_screen_width;
y_mid *= OS_screen_height;
//
// Enough room in our buffer?
//
if (ob->num_indices + 6 > ob->max_indices) {OS_buffer_grow_indices(ob);}
if (ob->num_flerts + 4 > ob->max_flerts ) {OS_buffer_grow_flerts (ob);}
//
// Add four vertices.
//
for (i = 0; i < 4; i++)
{
of = &ob->flert[ob->num_flerts + i];
x = 0.0F;
y = 0.0F;
if (i & 1)
{
x += dx;
y += dy;
}
else
{
x -= dx;
y -= dy;
}
if (i & 2)
{
x += -dy;
y += +dx;
}
else
{
x -= -dy;
y -= +dx;
}
x *= OS_screen_width;
y *= OS_screen_height * 1.33F;
x += x_mid;
y += y_mid;
of->sx = x;
of->sy = y;
of->sz = z;
of->rhw = 0.5F;
of->colour = colour;
of->specular = specular;
of->tu1 = ((i & 1) ? u1 : u2);
of->tv1 = ((i & 2) ? v1 : v2);
of->tu2 = ((i & 1) ? tu1 : tu2);
of->tv2 = ((i & 2) ? tv1 : tv2);
}
//
// Add two triangles.
//
ob->index[ob->num_indices + 0] = ob->num_flerts + 0;
ob->index[ob->num_indices + 1] = ob->num_flerts + 1;
ob->index[ob->num_indices + 2] = ob->num_flerts + 3;
ob->index[ob->num_indices + 3] = ob->num_flerts + 0;
ob->index[ob->num_indices + 4] = ob->num_flerts + 3;
ob->index[ob->num_indices + 5] = ob->num_flerts + 2;
ob->num_indices += 6;
ob->num_flerts += 4;
}
void OS_buffer_add_sprite_arbitrary(
OS_Buffer *ob,
float x1, // Normalised to 0.0F - 1.0F
float y1, // Normalised to 0.0F - 1.0F
float x2, // Normalised to 0.0F - 1.0F
float y2, // Normalised to 0.0F - 1.0F
float x3, // Normalised to 0.0F - 1.0F
float y3, // Normalised to 0.0F - 1.0F
float x4, // Normalised to 0.0F - 1.0F
float y4, // Normalised to 0.0F - 1.0F
float u1, float v1,
float u2, float v2,
float u3, float v3,
float u4, float v4,
float z,
ULONG colour,
ULONG specular)
{
OS_buffer_add_sprite_arbitrary(
ob,
x1, y1,
x2, y2,
x3, y3,
x4, y4,
u1, v1,
u2, v2,
u3, v3,
u4, v4,
z,z,z,z,
colour,
specular);
}
void OS_buffer_add_sprite_arbitrary(
OS_Buffer *ob,
float x1, // Normalised to 0.0F - 1.0F
float y1, // Normalised to 0.0F - 1.0F
float x2, // Normalised to 0.0F - 1.0F
float y2, // Normalised to 0.0F - 1.0F
float x3, // Normalised to 0.0F - 1.0F
float y3, // Normalised to 0.0F - 1.0F
float x4, // Normalised to 0.0F - 1.0F
float y4, // Normalised to 0.0F - 1.0F
float u1, float v1,
float u2, float v2,
float u3, float v3,
float u4, float v4,
float z1,
float z2,
float z3,
float z4,
ULONG colour,
ULONG specular)
{
SLONG i;
float x;
float y;
float u;
float v;
float z;
OS_Flert *of;
//
// Enough room in our buffer?
//
if (ob->num_indices + 6 > ob->max_indices) {OS_buffer_grow_indices(ob);}
if (ob->num_flerts + 4 > ob->max_flerts ) {OS_buffer_grow_flerts (ob);}
//
// Add four vertices.
//
for (i = 0; i < 4; i++)
{
of = &ob->flert[ob->num_flerts + i];
switch(i)
{
case 0:
x = x1;
y = y1;
u = u1;
v = v1;
z = z1;
break;
case 1:
x = x2;
y = y2;
u = u2;
v = v2;
z = z2;
break;
case 2:
x = x3;
y = y3;
u = u3;
v = v3;
z = z3;
break;
case 3:
x = x4;
y = y4;
u = u4;
v = v4;
z = z4;
break;
default:
ASSERT(0);
break;
}
x *= OS_screen_width;
y *= OS_screen_height;
of->sx = x;
of->sy = y;
of->sz = z;
of->rhw = 0.5F;
of->colour = colour;
of->specular = specular;
of->tu1 = u;
of->tv1 = v;
of->tu2 = u;
of->tv2 = v;
}
//
// Add two triangles.
//
ob->index[ob->num_indices + 0] = ob->num_flerts + 0;
ob->index[ob->num_indices + 1] = ob->num_flerts + 1;
ob->index[ob->num_indices + 2] = ob->num_flerts + 3;
ob->index[ob->num_indices + 3] = ob->num_flerts + 0;
ob->index[ob->num_indices + 4] = ob->num_flerts + 3;
ob->index[ob->num_indices + 5] = ob->num_flerts + 2;
ob->num_indices += 6;
ob->num_flerts += 4;
}
void OS_buffer_add_line_3d(
OS_Buffer *ob,
float X1,
float Y1,
float X2,
float Y2,
float width,
float u1, float v1,
float u2, float v2,
float z1,
float z2,
ULONG colour,
ULONG specular)
{
SLONG i;
OS_Flert *of;
float dx;
float dy;
float len;
float overlen;
float x;
float y;
//
// Enough room in our buffer?
//
if (ob->num_indices + 6 > ob->max_indices) {OS_buffer_grow_indices(ob);}
if (ob->num_flerts + 4 > ob->max_flerts ) {OS_buffer_grow_flerts (ob);}
//
// The width of the line.
//
dx = X2 - X1;
dy = Y2 - Y1;
len = qdist2(fabs(dx),fabs(dy));
overlen = width * OS_screen_width / len;
dx *= overlen;
dy *= overlen;
//
// Add four vertices.
//
for (i = 0; i < 4; i++)
{
of = &ob->flert[ob->num_flerts + i];
x = 0.0F;
y = 0.0F;
if (i & 1)
{
x += -dy;
y += +dx;
}
else
{
x -= -dy;
y -= +dx;
}
if (i & 2)
{
x += X1;
y += Y1;
}
else
{
x += X2;
y += Y2;
}
of->sx = x;
of->sy = y;
of->sz = ((i & 2) ? z1 : z2);
of->rhw = 0.5F;
of->colour = colour;
of->specular = specular;
of->tu1 = ((i & 1) ? u1 : u2);
of->tv1 = ((i & 2) ? v1 : v2);
of->tu2 = ((i & 1) ? u1 : u2);
of->tv2 = ((i & 2) ? v1 : v2);
}
//
// Add two triangles.
//
ob->index[ob->num_indices + 0] = ob->num_flerts + 0;
ob->index[ob->num_indices + 1] = ob->num_flerts + 1;
ob->index[ob->num_indices + 2] = ob->num_flerts + 3;
ob->index[ob->num_indices + 3] = ob->num_flerts + 0;
ob->index[ob->num_indices + 4] = ob->num_flerts + 3;
ob->index[ob->num_indices + 5] = ob->num_flerts + 2;
ob->num_indices += 6;
ob->num_flerts += 4;
}
void OS_buffer_add_line_2d(
OS_Buffer *ob,
float x1,
float y1,
float x2,
float y2,
float width,
float u1, float v1,
float u2, float v2,
float z,
ULONG colour,
ULONG specular)
{
SLONG i;
OS_Flert *of;
float dx;
float dy;
float len;
float overlen;
float x;
float y;
//
// Enough room in our buffer?
//
if (ob->num_indices + 6 > ob->max_indices) {OS_buffer_grow_indices(ob);}
if (ob->num_flerts + 4 > ob->max_flerts ) {OS_buffer_grow_flerts (ob);}
//
// The width of the line.
//
x1 *= OS_screen_width;
y1 *= OS_screen_height;
x2 *= OS_screen_width;
y2 *= OS_screen_height;
dx = x2 - x1;
dy = y2 - y1;
len = qdist2(fabs(dx),fabs(dy));
overlen = width * OS_screen_width / len;
dx *= overlen;
dy *= overlen;
//
// Add four vertices.
//
for (i = 0; i < 4; i++)
{
of = &ob->flert[ob->num_flerts + i];
x = 0.0F;
y = 0.0F;
if (i & 1)
{
x += -dy;
y += +dx;
}
else
{
x -= -dy;
y -= +dx;
}
if (i & 2)
{
x += x1;
y += y1;
}
else
{
x += x2;
y += y2;
}
of->sx = x;
of->sy = y;
of->sz = z;
of->rhw = 0.5F;
of->colour = colour;
of->specular = specular;
of->tu1 = ((i & 1) ? u1 : u2);
of->tv1 = ((i & 2) ? v1 : v2);
of->tu2 = ((i & 1) ? u1 : u2);
of->tv2 = ((i & 2) ? v1 : v2);
}
//
// Add two triangles.
//
ob->index[ob->num_indices + 0] = ob->num_flerts + 0;
ob->index[ob->num_indices + 1] = ob->num_flerts + 1;
ob->index[ob->num_indices + 2] = ob->num_flerts + 3;
ob->index[ob->num_indices + 3] = ob->num_flerts + 0;
ob->index[ob->num_indices + 4] = ob->num_flerts + 3;
ob->index[ob->num_indices + 5] = ob->num_flerts + 2;
ob->num_indices += 6;
ob->num_flerts += 4;
}
void OS_buffer_draw(
OS_Buffer *ob,
OS_Texture *ot1,
OS_Texture *ot2,
ULONG draw)
{
LPDIRECT3DDEVICE3 d3d = OS_frame.GetD3DDevice();
if (ob->num_flerts == 0)
{
//
// Empty buffer!
//
OS_buffer_give(ob);
return;
}
if (!OS_in_scene)
{
OS_scene_begin();
}
if (ot1 == NULL)
{
//
// No texturing.
//
draw |= OS_DRAW_TEX_NONE;
}
else
{
//
// Make this texture the input into the pipeline.
//
d3d->SetTexture(0, ot1->ddtx);
}
if (ot2)
{
//
// Make this texture the input into the second stage of the pipeline.
//
d3d->SetTexture(1, ot2->ddtx);
}
//
// Update renderstates.
//
if (draw != OS_DRAW_NORMAL)
{
OS_change_renderstate_for_type(draw);
}
{
//
// Check that this will be okay.
//
ULONG num_passes;
if (d3d->ValidateDevice(&num_passes) != D3D_OK)
{
OS_string("Validation failed: draw = 0x%x\n", draw);
}
}
//
// The number of polys actually drawn.
//
OS_poly_count_drawn += ob->num_indices / 3;
//
// Draw the triangles.
//
d3d->DrawIndexedPrimitive(
D3DPT_TRIANGLELIST,
OS_FLERT_FORMAT,
ob->flert,
ob->num_flerts,
ob->index,
ob->num_indices,
D3DDP_DONOTUPDATEEXTENTS);
//
// Put state back to normal.
//
if (draw != OS_DRAW_NORMAL)
{
OS_undo_renderstate_type_changes();
}
//
// Returns the buffer to the free list.
//
OS_buffer_give(ob);
}
void OS_screenshot(CBYTE *fname)
{
SLONG r;
SLONG g;
SLONG b;
SLONG x;
SLONG y;
ULONG pixel;
UWORD *u_screen;
SLONG pitch;
ULONG *l_screen;
CBYTE ourname[128];
SLONG width = ftol(OS_screen_width );
SLONG height = ftol(OS_screen_height);
TGA_Pixel *tga;
DDSURFACEDESC2 ddsd;
tga = (TGA_Pixel *) malloc(sizeof(TGA_Pixel) * width * height);
if (!tga)
{
return;
}
//
// Lock the backbuffer.
//
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
OS_frame.GetBackBuffer()->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
//
// Screen format.
//
SLONG mask_r;
SLONG mask_g;
SLONG mask_b;
SLONG shift_r;
SLONG shift_g;
SLONG shift_b;
OS_calculate_mask_and_shift(ddsd.ddpfPixelFormat.dwRBitMask, &mask_r, &shift_r);
OS_calculate_mask_and_shift(ddsd.ddpfPixelFormat.dwGBitMask, &mask_g, &shift_g);
OS_calculate_mask_and_shift(ddsd.ddpfPixelFormat.dwBBitMask, &mask_b, &shift_b);
//
// How many bpp?
//
if (ddsd.ddpfPixelFormat.dwRGBBitCount == 16)
{
u_screen = (UWORD *) ddsd.lpSurface;
l_screen = NULL;
pitch = ddsd.lPitch >> 1;
}
else
if (ddsd.ddpfPixelFormat.dwRGBBitCount == 32)
{
u_screen = NULL;
l_screen = (ULONG *) ddsd.lpSurface;
pitch = ddsd.lPitch >> 2;
}
else
{
return;
}
//
// Transfer pixels to buffer.
//
for (x = 0; x < width; x++)
for (y = 0; y < height; y++)
{
if (u_screen)
{
pixel = u_screen[x + y * pitch];
}
else
{
pixel = l_screen[x + y * pitch];
}
r = ((pixel >> shift_r) << mask_r) & 0xff;
g = ((pixel >> shift_g) << mask_g) & 0xff;
b = ((pixel >> shift_b) << mask_b) & 0xff;
tga[x + y * width].red = r;
tga[x + y * width].green = g;
tga[x + y * width].blue = b;
tga[x + y * width].alpha = 0;
}
//
// Unlock screen.
//
OS_frame.GetBackBuffer()->Unlock(NULL);
//
// Get the filename.
//
if (!fname)
{
SLONG i;
for (i = 1; i < 1000; i++)
{
sprintf(ourname, "ScreenShot\\shot%03d.tga", i);
if (fopen(ourname, "rb") == NULL)
{
//
// Found an unused filename.
//
fname = ourname;
goto found_name;
}
}
//
// Could not get a filename.
//
return;
found_name:;
}
TGA_save(
fname,
width,
height,
tga,
FALSE);
//
// Free up tga memory.
//
free(tga);
}