Descent3/dd_sndlib/aureal3d.cpp

1263 lines
27 KiB
C++

/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* $Logfile: /DescentIII/Main/dd_sndlib/aureal3d.cpp $
* $Revision: 18 $
* $Date: 8/19/99 10:25a $
* $Author: Samir $
*
* Aureal enhancements to sound library.
*
* $Log: /DescentIII/Main/dd_sndlib/aureal3d.cpp $
*
* 18 8/19/99 10:25a Samir
* tweaks for Aureal as requested by Aureal.
*
* 17 8/13/99 2:00p Samir
* more aureal and geometry fixes.
*
* 16 8/11/99 3:12p Samir
* fixes for aureal support.
*
* 15 7/30/99 5:03p Samir
* an attempt to get some aureal reflection support working.
*
* 14 7/20/99 4:09p Samir
* fixed reflection support. tag polygons for reuse.
*
* 13 5/03/99 12:35p Samir
* added config file support for aureal features.
*
* 12 5/03/99 3:12a Samir
* added event management routines.
*
* 11 4/29/99 3:01p Samir
* added some more functions.
*
* 10 4/23/99 7:51p Samir
* added flush function
*
* 9 4/13/99 4:09p Samir
* more priority stuff.
*
* 8 4/06/99 8:30p Samir
* added reflection support.
*
* 7 4/01/99 4:28p Samir
* added some error checking.
*
* 6 3/29/99 10:53a Samir
* added new Aureal 2.0 sdk support.
*
* 5 1/14/99 6:10p Samir
* added DirectSound buffer duplication code.
*
* 4 1/08/99 6:31p Samir
* added reverb
*
* 3 1/08/99 11:36a Samir
* implemented basic Aureal 2.0 support.
*
* 2 12/23/98 11:48a Samir
* basic functionality.
*
* 1 7/02/98 4:38p Samir
* Initial revision.
*
* $NoKeywords: $
*/
#if 0
#include "auddev.h"
#include <objbase.h>
#include <initguid.h>
#include <stdio.h>
#include <stdlib.h>
#include "ia3dutil.h"
#include "ia3dapi.h"
#include "pserror.h"
#include "logfile.h"
#include "Macros.h"
#include "inffile.h"
typedef struct tA3D
{
IA3d4 *a3d;
IA3dListener *listener;
IA3dGeom *geom; // Geometry object.
IA3dList *list; // list of geometry commands for a room.
DWORD geom_mode_flags; // active elements in A3d geometry engine.
float global_gain;
float reflect_time;
float reflect_scale; // reflection scale
float reflect_gain; // reflection game
bool do_occlusions;
bool do_reflections;
sbyte reflection_update_interval;
}
tA3D;
static tA3D A3D = { NULL, NULL, NULL, NULL, 0, };
static logfile a3dlog;
static bool A3D_first_init = false;
const DWORD A3D_FEATURES = A3D_OCCLUSIONS | A3D_1ST_REFLECTIONS | A3D_DISABLE_SPLASHSCREEN;
const A3DVAL A3D_PRIORITY_BIAS = 0.60f;
const DWORD A3D_FALLBACK_SOURCES = 16;
int A3D_buffers_allocated=0;
void A3D_FinalShutdown();
#define A3DCMD_NUM 7
#define A3DCMD_GAIN 0
#define A3DCMD_OCC 1
#define A3DCMD_REF 2
#define A3DCMD_REFINT 3
#define A3DCMD_REFTIME 4
#define A3DCMD_REFSCALE 5
#define A3DCMD_REFGAIN 6
const char *A3DCommands[A3DCMD_NUM] = {
"gain",
"occlusions",
"reflections",
"reflectinterval",
"reflecttime",
"reflectscale",
"reflectgain"
};
int A3DLex(const char *command)
{
for (int i = 0; i < A3DCMD_NUM; i++)
{
if (strcmp(A3DCommands[i], command) == 0)
return i;
}
return INFFILE_ERROR;
}
//////////////////////////////////////////////////////////////////////////////
// Initialize object
bool A3D_Init(HWND hwnd)
{
InfFile cfgfile;
int hr;
if (!A3D_first_init) {
a3dlog.start("a3d.log", "Aureal 3D 2.0 Interface");
atexit(A3D_FinalShutdown);
A3D_first_init = true;
}
a3dlog.puts("initializing\n");
// Core Initialization
hr = A3dInitialize();
if (SUCCEEDED(hr)) {
hr = A3dCreate(NULL, (void **)&A3D.a3d, NULL, A3D_FEATURES);
if (hr != S_OK) {
a3dlog.printf("failed to create a3d object (%x).\n", hr);
A3dUninitialize();
return false;
}
}
// okay read in config file.
A3D.global_gain = 1.0f;
A3D.do_occlusions = true;
A3D.do_reflections = true;
A3D.reflection_update_interval = 1;
A3D.reflect_time = 1.0f;
A3D.reflect_scale = 0.95f;
A3D.reflect_gain = A3D.global_gain;
if (cfgfile.Open("a3d.inf", NULL, A3DLex)) {
a3dlog.puts("a3d.inf found:\n");
while (cfgfile.ReadLine())
{
char operand[32];
int cmd;
while ((cmd = cfgfile.ParseLine(operand, INFFILE_LINELEN)) > INFFILE_ERROR)
{
switch (cmd)
{
float fval;
bool bval;
int ival;
case A3DCMD_GAIN:
fval = atof(operand);
if (fval < 0) fval = 0;
else if (fval > 1.0f) fval = 1.0f;
A3D.global_gain = fval;
a3dlog.printf("gain = %.2f\n", fval);
break;
case A3DCMD_OCC:
bval = (strcmpi(operand, "true")==0) ? true : false;
A3D.do_occlusions = bval;
a3dlog.printf("occlutions = %s\n", operand);
break;
case A3DCMD_REF:
bval = (strcmpi(operand, "true")==0) ? true : false;
A3D.do_reflections = bval;
a3dlog.printf("reflections = %s\n", operand);
break;
case A3DCMD_REFINT:
ival = atoi(operand);
if (ival < 1) ival = 1;
else if (ival > 6) ival = 6;
A3D.reflection_update_interval = ival;
a3dlog.printf("update interval = %d\n", ival);
break;
case A3DCMD_REFTIME:
fval = atof(operand);
if (fval < 0.0f) fval = 0.0f;
A3D.reflect_time = fval;
a3dlog.printf("max reflect time = %.2f", fval);
break;
case A3DCMD_REFSCALE:
fval = atof(operand);
if (fval < 0.0f) fval = 0.0f;
A3D.reflect_scale = fval;
a3dlog.printf("max reflect scale = %.2f", fval);
break;
case A3DCMD_REFGAIN:
fval = atof(operand);
if (fval < 0.0f) fval = 0.0f;
A3D.reflect_gain = fval;
a3dlog.printf("max reflect gain = %.2f", fval);
break;
}
}
}
cfgfile.Close();
}
else {
a3dlog.puts("no a3d.inf, using defaults.\n");
}
A3DCAPS_HARDWARE hwcaps;
memset(&hwcaps, 0 , sizeof(hwcaps));
hwcaps.dwSize = sizeof(hwcaps);
hr = A3D.a3d->GetHardwareCaps(&hwcaps);
if (hr != S_OK) {
// failed???
a3dlog.printf("failed hardware.\n");
A3D.a3d->Release();
A3D.a3d = NULL;
A3dUninitialize();
return false;
}
if (hwcaps.dwFlags & A3D_DIRECT_PATH_A3D) {
bool occlusions = A3D.a3d->IsFeatureAvailable(A3D_OCCLUSIONS) == TRUE;
bool reflections = A3D.a3d->IsFeatureAvailable(A3D_1ST_REFLECTIONS) == TRUE;
if ((!occlusions && A3D.do_occlusions)) {
a3dlog.printf("failed hardware occlusion test.\n");
A3D.a3d->Release();
A3D.a3d = NULL;
A3dUninitialize();
return false;
}
if (!reflections && A3D.do_reflections) {
a3dlog.printf("failed hardware reflections test.\n");
A3D.do_reflections = false;
}
}
else {
a3dlog.printf("aureal vortex support failed..\n");
A3D.a3d->Release();
A3D.a3d = NULL;
A3dUninitialize();
return false;
}
hr = A3D.a3d->SetCooperativeLevel(hwnd, A3D_CL_NORMAL);
if (FAILED(hr)) {
a3dlog.printf("failed to obtain access to device (%x).\n", hr);
A3D.a3d->Release();
A3D.a3d = NULL;
A3dUninitialize();
return false;
}
// set number of fallback a2d sources, say to 20ish?
hr = A3D.a3d->SetNumFallbackSources(A3D_FALLBACK_SOURCES);
if (FAILED(hr)) {
a3dlog.printf("failed to set fallback source count (%x).\n", hr);
}
// we'll tend to lean towards priority since our code will more likely determine audibility.
hr = A3D.a3d->SetRMPriorityBias(A3D_PRIORITY_BIAS);
if (FAILED(hr)) {
a3dlog.printf("failed to set priority bias (%x).\n", hr);
A3D.a3d->Release();
A3D.a3d = NULL;
A3dUninitialize();
return false;
}
// we use left handed coordinate system!
hr = A3D.a3d->SetCoordinateSystem(A3D_LEFT_HANDED_CS);
if (hr != S_OK) {
a3dlog.printf("couldn't set left handed coord system (%x).\n", hr);
A3D.a3d->Release();
A3D.a3d = NULL;
A3dUninitialize();
return false;
}
hr = A3D.a3d->SetOutputGain(A3D.global_gain);
if (hr != S_OK) {
a3dlog.printf("couldn't set gain (%x).\n", hr);
}
hr = A3D.a3d->SetMaxReflectionDelayTime(A3D.reflect_time);
if (hr != S_OK) {
a3dlog.printf("couldn't set reflect time (%x).\n", hr);
}
A3D.geom_mode_flags = 0;
A3D_buffers_allocated = 0;
A3D.a3d->Clear();
a3dlog.update();
return true;
}
// create or destroy geometry object
bool A3D_CreateGeometryInterface()
{
// Get geometry interface object. We'll need this.
if (!A3D.do_occlusions) {
A3D.geom = NULL;
return false;
}
HRESULT hr = A3D.a3d->QueryInterface(IID_IA3dGeom, (void **)&A3D.geom);
if (hr != S_OK) {
a3dlog.printf("failed to query geometry interface (%x).\n", hr);
A3D.geom = NULL;
return false;
}
A3D.geom->Enable(A3D_OCCLUSIONS);
if (A3D.do_reflections) {
A3D.geom->Enable(A3D_1ST_REFLECTIONS);
A3D.geom->SetReflectionDelayScale(A3D.reflect_scale);
A3D.geom->SetReflectionGainScale(A3D.reflect_gain);
A3D.geom->SetReflectionUpdateInterval(A3D.reflection_update_interval);
}
// obtain a new list.
hr = A3D.geom->NewList(&A3D.list);
if (hr != S_OK) {
a3dlog.printf("failed to create a list interface (%x).\n", hr);
A3D.geom->Release();
A3D.geom = NULL;
return false;
}
A3D.geom_mode_flags = A3DPOLY_RENDERMODE_OCCLUSIONS;
A3D.a3d->Clear();
return true;
}
void A3D_DestroyGeometryInterface()
{
if (A3D.list) {
A3D.list->Release();
A3D.list = NULL;
}
if (A3D.geom) {
A3D.geom->Release();
A3D.geom = NULL;
}
}
// destroy object
void A3D_Destroy()
{
A3D_DestroyGeometryInterface();
if (A3D.geom) A3D.geom->Release();
if (A3D.listener) A3D.listener->Release();
if (A3D.a3d) {
A3D.a3d->Release();
A3D.listener = NULL;
A3D.a3d = NULL;
A3dUninitialize();
a3dlog.puts("shutdown\n\n");
a3dlog.update();
}
}
void A3D_FinalShutdown()
{
a3dlog.puts("final shutdown\n");
a3dlog.end();
}
// sets rolloff for all source objects
void A3D_SetRolloffFactor(float scalar)
{
HRESULT hr;
hr = A3D.a3d->SetDistanceModelScale(scalar);
if (hr != S_OK) {
a3dlog.printf("set distance model scalar failed (%x)\n", hr);
}
}
// sets doppler for all source objects (from 0.0f to ???) (1.0f is default, 0.0f is none)
void A3D_SetDopplerFactor(float scalar)
{
HRESULT hr = A3D.a3d->SetDopplerScale(scalar);
if (hr != S_OK) {
a3dlog.printf("set doppler scalar failed (%x)\n", hr);
}
}
// sets units of measurement
void A3D_SetUnitsPerMeter(float units)
{
HRESULT hr = A3D.a3d->SetUnitsPerMeter(units);
if (hr != S_OK) {
a3dlog.printf("couldn't set distance units (%x).\n", hr);
}
}
// create listener
bool A3D_CreateListener()
{
/* query for the listener */
int hr = A3D.a3d->QueryInterface(IID_IA3dListener, (void **)&A3D.listener);
if (hr != S_OK) {
a3dlog.printf("create listener failed (%x)!\n", hr);
return false;
}
a3dlog.puts("listener created.\n");
return true;
}
// create source
void *A3D_CreateSSource(int size, int bps, bool stereo, bool streaming, ushort freq, bool is3d)
{
/* make a new source. at this stage the source is empty - no wave data */
IA3dSource *a3dsrc=NULL;
DWORD flags=0;
if (is3d) {
flags = A3DSOURCE_TYPEDEFAULT;
}
else {
flags = A3DSOURCE_INITIAL_RENDERMODE_NATIVE;
}
if (streaming) {
flags |= (A3DSOURCE_TYPESTREAMED);
// a3dlog.printf("new streamed source 3d:%c\n", is3d ? 'y' : 'n');
}
else {
// a3dlog.printf("new source type:%s\n", is3d ? "default[3d]" : "native[2d]");
}
int hr = A3D.a3d->NewSource( flags, &a3dsrc);
if (hr != S_OK) {
a3dlog.printf("failed to create for source (%x)\n", hr);
return NULL;
}
/* set wave format */
WAVEFORMATEX fmt;
fmt.cbSize = sizeof(fmt);
fmt.wFormatTag = WAVE_FORMAT_PCM;
fmt.nChannels = (stereo)?2:1;
fmt.wBitsPerSample = bps;
fmt.nSamplesPerSec = ((DWORD)freq);
fmt.nBlockAlign = fmt.nChannels*(fmt.wBitsPerSample/8);
fmt.nAvgBytesPerSec = ((DWORD)fmt.nSamplesPerSec)*((DWORD)fmt.nBlockAlign);
hr = a3dsrc->SetWaveFormat(&fmt);
if (hr != S_OK) {
a3dlog.printf("failed to set format for source (%x)\n", hr);
a3dsrc->Release();
return NULL;
}
hr = a3dsrc->AllocateWaveData(size);
if (hr != S_OK) {
a3dlog.printf("failed to allocate wavedata for source [%d.%d.%c.%c.%c.%08x] (%x).\n",
size,bps,stereo ? 'S' : 'M', streaming ? 'Y' : 'N', is3d ? 'Y' : 'N', hr);
a3dlog.update();
a3dsrc->Release();
return NULL;
}
// if (!CHECK_FLAG(flags, A3DSOURCE_TYPEUNMANAGED)) {
// a3dsrc->SetPriority(0.5f);
// }
A3D_buffers_allocated++;
return (void *)a3dsrc;
}
// duplicates source
void *A3D_DuplicateSource(void *src)
{
IA3dSource *a3dsrc = (IA3dSource *)src;
IA3dSource *new_src;
int hr = A3D.a3d->DuplicateSource(a3dsrc, &new_src);
if (hr != S_OK) {
a3dlog.printf("duplicate source failed (%x)\n", hr);
return NULL;
}
A3D_buffers_allocated++;
return (void *)new_src;
}
// frees source
void A3D_FreeSSource(void *obj)
{
if (obj) {
IA3dSource *a3dsrc = (IA3dSource *)obj;
a3dsrc->FreeWaveData();
a3dsrc->Release();
A3D_buffers_allocated--;
}
}
// loads data into source
bool A3D_LoadSample(void *obj, void *src_data, int src_length)
{
int hr=0;
if (obj) {
void *srcptr1=0, *srcptr2=0;
DWORD len1, len2;
// IA3dSource *a3dsrc = (IA3dSource *)obj;
// hr = a3dsrc->LoadWaveData(src_data, (DWORD)src_length);
// if (SUCCEEDED(hr)) {
// return true;
// }
if (A3D_Lock(obj, 0, src_length, &srcptr1, &len1, &srcptr2, &len2)) {
memcpy(srcptr1, src_data, len1);
if(srcptr2) {
memcpy(srcptr2, ((char *)src_data) + len1, len2);
}
A3D_Unlock(obj, srcptr1, len1, srcptr2, len2);
return true;
}
}
a3dlog.printf("lock on load source failed (%x)\n", hr);
return false;
}
// plays a sound
void A3D_Play(void *obj, bool looping)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return;
hr = a3dsrc->Play(looping ? A3D_LOOPED : A3D_SINGLE);
if (hr == A3DERROR_NO_WAVE_DATA) {
a3dlog.printf("play failed because no wave data associated with source!\n");
}
}
// returns current position in buffer of playback
void A3D_GetCurrentPosition(void *obj,uint *playp)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
DWORD pos=0;
if (!a3dsrc) return;
hr = a3dsrc->GetWavePosition(&pos);
if (hr != S_OK) {
*playp = (uint)(-1);
a3dlog.printf("get current position failed (%x)\n", hr);
}
else {
*playp = (uint)pos;
}
}
// sets current position in buffer
void A3D_SetCurrentPosition(void *obj, uint pos)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return;
hr = a3dsrc->SetWavePosition(pos);
if (hr != S_OK) {
a3dlog.printf("set wave position failed (%x)\n", hr);
}
}
// stops a sound
void A3D_Stop(void *obj)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
DWORD pos=0;
if (!a3dsrc) return;
hr = a3dsrc->Stop();
}
// priority is from 0 to 1.0
void A3D_SetSourcePriority(void *obj, float priority)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return;
hr = a3dsrc->SetPriority((A3DVAL)priority);
}
// sets properties for source object
void A3D_SetSourceVolume(void *obj, float volume)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return;
hr = a3dsrc->SetGain((A3DVAL)volume);
}
float A3D_GetSourceVolume(void *obj)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
float gain;
if (!a3dsrc) return 0.0f;
hr = a3dsrc->GetGain(&gain);
if (hr != S_OK) {
a3dlog.printf("get gain failed (%x)\n",hr);
return 0.0f;
}
return gain;
}
void A3D_SetSourceVelocity(void *obj, float x, float y, float z)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return;
hr = a3dsrc->SetVelocity3f(x,y,z);
if (hr != S_OK) {
a3dlog.printf("set source velocity failed (%x)\n", hr);
}
}
void A3D_GetSourceVelocity(void *obj, vector *vel)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return;
hr = a3dsrc->GetVelocity3f(&vel->x,&vel->y,&vel->z);
if (hr != S_OK) {
a3dlog.printf("get source velocity failed (%x)\n", hr);
vel->x = vel->y = vel->z = 0.0f;
}
}
void A3D_SetSourceCone(void *obj, float x, float y, float z)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return;
hr = a3dsrc->SetCone(x,y,z);
}
void A3D_SetSourcePosition(void *obj, float x, float y, float z)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return;
hr = a3dsrc->SetPosition3f(x,y,z);
if (hr != S_OK) {
a3dlog.printf("set source position failed (%x)\n", hr);
}
}
bool A3D_SetSourceWaveEvent(void *obj, DWORD offset, HANDLE hEvent)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return false;
hr = a3dsrc->SetWaveEvent(offset, hEvent);;
if (hr != S_OK) {
a3dlog.printf("set source waveevent failed (%x)\n", hr);
return false;
}
return true;
}
bool A3D_ClearSourceWaveEvents(void *obj)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return false;
hr = a3dsrc->ClearWaveEvents();
if (hr != S_OK) {
a3dlog.printf("clear source waveevent failed (%x)\n", hr);
return false;
}
return true;
}
void A3D_GetSourcePosition(void *obj, vector *pos)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return;
hr = a3dsrc->GetPosition3f(&pos->x,&pos->y,&pos->z);
if (hr != S_OK) {
a3dlog.printf("get source position failed (%x)\n", hr);
}
}
void A3D_SetSourceOrientation(void *obj, const vector *fvec, const vector *uvec)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return;
hr = a3dsrc->SetOrientation6f(fvec->x, fvec->y, fvec->z, uvec->x, uvec->y, uvec->z);
if (hr != S_OK) {
a3dlog.printf("set source orientation failed (%x)\n", hr);
}
}
void A3D_GetSourceOrientation(void *obj, matrix *orient)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
vector fvec, uvec;
HRESULT hr;
if (!a3dsrc) return;
hr = a3dsrc->GetOrientation6f(&fvec.x, &fvec.y, &fvec.z, &uvec.x, &uvec.y, &uvec.z);
if (hr == S_OK) {
vm_VectorToMatrix(orient, &fvec, &uvec, NULL);
}
else {
vm_MakeIdentity(orient);
a3dlog.printf("get source orientation failed (%x)\n", hr);
}
}
void A3D_SetMinMaxDistance(void *obj, float min, float max, bool mute_max)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return;
hr = a3dsrc->SetMinMaxDistance(min, max, mute_max ? A3D_MUTE : A3D_AUDIBLE);
if (hr != S_OK) {
a3dlog.printf("set minmaxdist failed (%x)\n", hr);
}
}
void A3D_SetSourcePan(void *obj, float lpan, float rpan)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
A3DVAL pan[2] = { lpan, rpan };
if (!a3dsrc) return;
hr = a3dsrc->SetPanValues(2, pan);
if (hr != S_OK) {
a3dlog.printf("set pan failed (%x)\n", hr);
}
}
void A3D_GetSourcePan(void *obj, float *lpan, float *rpan)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
A3DVAL pan[2];
if (!a3dsrc) return;
hr = a3dsrc->GetPanValues(2, pan);
if (hr != S_OK) {
a3dlog.printf("set pan failed (%x)\n", hr);
*lpan = 0.0f;
*rpan = 0.0f;
}
else {
*lpan = pan[0];
*rpan = pan[1];
}
}
// sets properties for listener
void A3D_SetListenerVelocity(float x, float y, float z)
{
HRESULT hr;
if (!A3D.listener) return;
hr = A3D.listener->SetVelocity3f(x,y,z);
if (hr != S_OK) {
a3dlog.printf("set listener velocity failed (%x)\n", hr);
}
}
void A3D_SetListenerOrient(vector *fvec, vector *uvec)
{
HRESULT hr;
if (!A3D.listener) return;
hr = A3D.listener->SetOrientation6f(fvec->x,fvec->y,fvec->z, uvec->x, uvec->y, uvec->z);
if (hr != S_OK) {
a3dlog.printf("set listener orient failed (%x)\n", hr);
}
}
void A3D_SetListenerPosition(float x, float y, float z)
{
HRESULT hr;
if (!A3D.listener) return;
hr = A3D.listener->SetPosition3f(x,y,z);
if (hr != S_OK) {
a3dlog.printf("set listener position failed (%x)\n", hr);
}
}
// call at start of sound frame
void A3D_StartFrame()
{
if (!A3D.a3d) return;
// mprintf_at((1,5,20,"A3D:%2d", A3D_buffers_allocated));
// maybe we shouldn't clear here. flush should do the trick.
// A3D.a3d->Clear();
}
void A3D_EndFrame()
{
if (!A3D.a3d) return;
A3D.geom_mode_flags = A3D_OCCLUSIONS;
A3D.a3d->Flush();
a3dlog.update();
}
void A3D_Flush()
{
if (!A3D.a3d) return;
A3D.a3d->Flush();
}
// returns status of source buffer
int A3D_GetSourceStatus(void *obj)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
DWORD status=0;
int retval = 0;
if (!a3dsrc) return 0;
hr = a3dsrc->GetStatus(&status);
if (SUCCEEDED(hr)) {
if (status & A3DSTATUS_PLAYING) {
retval |= A3D_STATUS_PLAYING;
}
if (status & A3DSTATUS_LOOPING) {
retval |= A3D_STATUS_PLAYING;
}
if (status & A3DSTATUS_WAITING_FOR_FLUSH) {
retval |= A3D_STATUS_PLAYING;
}
// if (status & A3DSTATUS_BUFFERLOST) {
// mprintf((0, "A3D:buffer lost!\n"));
// }
// if (status & A3DSTATUS_LOOPING) {
// mprintf((0, "A3D:looping!\n"));
// }
// if (status & A3DSTATUS_WAITING_FOR_FLUSH) {
// mprintf((0, "A3D:flush wait!\n"));
// }
}
return retval;
}
bool A3D_Lock(void *obj, DWORD dwWriteCursor, DWORD dwWriteBytes, void **ptr1, LPDWORD plen1, void **ptr2, LPDWORD plen2)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return false;
hr = a3dsrc->Lock(dwWriteCursor, dwWriteBytes,ptr1,plen1,ptr2,plen2,0);
if (hr != S_OK) {
return false;
}
return true;
}
bool A3D_Unlock(void *obj, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
HRESULT hr;
if (!a3dsrc) return false;
hr = a3dsrc->Unlock(ptr1,len1,ptr2,len2);
if (hr != S_OK) {
return false;
}
return true;
}
// sets up current global environment reverb
bool A3D_SetEnvironmentalReverb(float volume, float damping, float decay)
{
// geometry interface is on? then lets emulate this function by setting reflection scalar.
if (!A3D.geom) {
return false;
}
// here, decay = seconds. since a hangar environment takes 10.0f seconds to decay, this is the most obvious
// a padded cell is 0.1 seconds, to the delay scale is minimal.
float time = decay*4.0f/3.0f;
A3D.a3d->SetMaxReflectionDelayTime(time);
// A3D.geom->SetReflectionDelayScale(0.5f+decay*2.0f);
// here the volumes passed into our function seem a bit small. we don't really want to diminish gain volumes,
// so we'll scale up of gain a bit. at most it'll be around 1.5 to 2.0.
// A3D.geom->SetReflectionGainScale(volume*3.0f/2.0f);
return true;
}
// list = list id. 0 is only valid.
void A3D_GeomListStart(int list)
{
if (!A3D.list) return;
A3D.a3d->Clear();
// free old list, start new list.
if (A3D.list) {
A3D.list->Release();
A3D.list = NULL;
HRESULT hr = A3D.geom->NewList(&A3D.list);
if (hr != S_OK) {
a3dlog.printf("failed to create a list interface (%x).\n", hr);
return;
}
}
A3D.list->Begin();
// this will be used by geometry engine if on.
if (A3D.geom) {
A3D.geom->LoadIdentity();
A3D.geom->SetRenderMode(A3D.geom_mode_flags);
}
}
void A3D_GeomListEnd(int list)
{
if (!A3D.list) return;
A3D.list->End();
}
void A3D_GeomListExec(int list)
{
if (!A3D.list) return;
A3D.list->Call();
}
void A3D_GeomBindMaterial (void *obj)
{
if (A3D.geom) {
if (!obj) {
if (A3D.geom_mode_flags & A3DPOLY_RENDERMODE_1ST_REFLECTIONS) {
A3D.geom_mode_flags &= (~A3DPOLY_RENDERMODE_1ST_REFLECTIONS);
A3D.geom->SetRenderMode(A3D.geom_mode_flags);
}
}
else {
if (A3D.do_reflections) {
IA3dMaterial *matp = (IA3dMaterial *)obj;
if (!(A3D.geom_mode_flags & A3DPOLY_RENDERMODE_1ST_REFLECTIONS)) {
A3D.geom_mode_flags |= A3DPOLY_RENDERMODE_1ST_REFLECTIONS;
A3D.geom->SetRenderMode(A3D.geom_mode_flags);
}
A3D.geom->BindMaterial(matp);
}
}
}
}
// add primatives to geometry engine
void A3D_GeomAddTriangle(unsigned tag, vector **verts)
{
HRESULT hr;
if (!A3D.geom) return;
// A3D.geom->PushMatrix();
A3D.geom->Begin(A3D_TRIANGLES);
A3D.geom->Tag(tag);
hr = A3D.geom->Vertex3f(verts[0]->x, verts[0]->y, verts[0]->z);
hr = A3D.geom->Vertex3f(verts[1]->x, verts[1]->y, verts[1]->z);
hr = A3D.geom->Vertex3f(verts[2]->x, verts[2]->y, verts[2]->z);
A3D.geom->End();
// A3D.geom->PopMatrix();
}
void A3D_GeomAddQuad(unsigned tag, vector **verts)
{
HRESULT hr;
if (!A3D.geom) return;
// A3D.geom->PushMatrix();
A3D.geom->Begin(A3D_QUADS);
A3D.geom->Tag(tag);
hr = A3D.geom->Vertex3f(verts[0]->x, verts[0]->y, verts[0]->z);
hr = A3D.geom->Vertex3f(verts[1]->x, verts[1]->y, verts[1]->z);
hr = A3D.geom->Vertex3f(verts[2]->x, verts[2]->y, verts[2]->z);
hr = A3D.geom->Vertex3f(verts[3]->x, verts[3]->y, verts[3]->z);
A3D.geom->End();
// A3D.geom->PopMatrix();
}
void A3D_GeomAddListener(vector *pos, matrix *orient)
{
A3DMATRIX a3dorient;
if (!A3D.geom) return;
A3D.geom->PushMatrix();
/* translate to position of listener (happens to be 0,0,0 here) */
A3D.geom->Translate3f(pos->x, pos->y, pos->z);
/* rotate by 'heading' degrees about the Y (up) axis */
a3dorient[0] = orient->rvec.x;
a3dorient[1] = orient->rvec.y;
a3dorient[2] = orient->rvec.z;
a3dorient[3] = 0;
a3dorient[4] = orient->uvec.x;
a3dorient[5] = orient->uvec.y;
a3dorient[6] = orient->uvec.z;
a3dorient[7] = 0;
a3dorient[8] = orient->fvec.x;
a3dorient[9] = orient->fvec.y;
a3dorient[10] = orient->fvec.z;
a3dorient[11] = 0;
a3dorient[12] = 0;
a3dorient[13] = 0;
a3dorient[14] = 0;
a3dorient[15] = 1;
A3D.geom->MultMatrix(a3dorient);
/* make these transformations apply to the listener */
A3D.geom->BindListener();
A3D.geom->PopMatrix();
}
void A3D_GeomAddSource(void *obj, vector *pos, matrix *orient)
{
IA3dSource *a3dsrc = (IA3dSource *)obj;
A3DMATRIX a3dorient;
if (!A3D.geom) return;
A3D.geom->PushMatrix();
/* translate to position of listener (happens to be 0,0,0 here) */
A3D.geom->Translate3f(pos->x, pos->y, pos->z);
/* rotate by 'heading' degrees about the Y (up) axis */
a3dorient[0] = orient->rvec.x;
a3dorient[1] = orient->rvec.y;
a3dorient[2] = orient->rvec.z;
a3dorient[3] = 0;
a3dorient[4] = orient->uvec.x;
a3dorient[5] = orient->uvec.y;
a3dorient[6] = orient->uvec.z;
a3dorient[7] = 0;
a3dorient[8] = orient->fvec.x;
a3dorient[9] = orient->fvec.y;
a3dorient[10] = orient->fvec.z;
a3dorient[11] = 0;
a3dorient[12] = 0;
a3dorient[13] = 0;
a3dorient[14] = 0;
a3dorient[15] = 1;
A3D.geom->MultMatrix(a3dorient);
/* make these transformations apply to the listener */
A3D.geom->BindSource(a3dsrc);
A3D.geom->PopMatrix();
}
// used to create aureal 2.0 materials.
void *A3D_CreateMaterial(float xmit_gain, float xmit_highfreq, float reflect_gain, float reflect_highfreq)
{
IA3dMaterial *matp;
if (!A3D.geom) return NULL;
A3D.geom->NewMaterial(&matp);
matp->SetTransmittance(xmit_gain, xmit_highfreq);
matp->SetReflectance(reflect_gain, reflect_highfreq);
return (void *)matp;
}
void A3D_DestroyMaterial(void *obj)
{
IA3dMaterial *matp = (IA3dMaterial *)obj;
matp->Release();
}
void A3D_Clear() // clears geometry info
{
if (!A3D.a3d) return;
A3D.a3d->Clear();
}
#endif