audio subsystem: openal emitter, minor audio fixes

This commit is contained in:
tmj-fstate
2017-11-19 16:02:12 +01:00
parent 7fc1256b46
commit bc43c21174
24 changed files with 566 additions and 182 deletions

View File

@@ -46,6 +46,7 @@ void TButton::Load(cParser &Parser, TModel3d *pModel1, TModel3d *pModel2) {
else {
// new, block type config
// TODO: rework the base part into yaml-compatible flow style mapping
submodelname = Parser.getToken<std::string>( false );
while( true == Load_mapping( Parser ) ) {
; // all work done by while()
}

View File

@@ -3354,6 +3354,7 @@ bool TDynamicObject::Update(double dt, double dt1)
#ifdef EU07_USE_OLD_SOUNDCODE
rsDoorOpen.Play(1, 0, MechInside, vPosition);
#else
rsDoorClose.stop();
rsDoorOpen.play();
#endif
dDoorMoveL += dt1 * 0.5 * MoverParameters->DoorOpenSpeed;
@@ -3363,6 +3364,7 @@ bool TDynamicObject::Update(double dt, double dt1)
#ifdef EU07_USE_OLD_SOUNDCODE
rsDoorClose.Play(1, 0, MechInside, vPosition);
#else
rsDoorOpen.stop();
rsDoorClose.play();
#endif
dDoorMoveL -= dt1 * MoverParameters->DoorCloseSpeed;
@@ -3374,6 +3376,7 @@ bool TDynamicObject::Update(double dt, double dt1)
#ifdef EU07_USE_OLD_SOUNDCODE
rsDoorOpen.Play(1, 0, MechInside, vPosition);
#else
rsDoorClose.stop();
rsDoorOpen.play();
#endif
dDoorMoveR += dt1 * 0.5 * MoverParameters->DoorOpenSpeed;
@@ -3383,6 +3386,7 @@ bool TDynamicObject::Update(double dt, double dt1)
#ifdef EU07_USE_OLD_SOUNDCODE
rsDoorClose.Play(1, 0, MechInside, vPosition);
#else
rsDoorOpen.stop();
rsDoorClose.play();
#endif
dDoorMoveR -= dt1 * MoverParameters->DoorCloseSpeed;
@@ -3849,16 +3853,70 @@ void TDynamicObject::RenderSounds() {
if (GetVelocity() == 0)
rsDerailment.Stop();
}
#else
// McZapkie! - dzwiek compressor.wav tylko gdy dziala sprezarka
if( MoverParameters->VeselVolume != 0 ) {
if( MoverParameters->TrainType != dt_PseudoDiesel ) {
// NBMX dzwiek przetwornicy
if( MoverParameters->ConverterFlag ) {
sConverter.play();
}
else {
sConverter.stop();
}
}
else {
}
if( MoverParameters->CompressorFlag ) {
sCompressor.play();
}
else {
sCompressor.stop();
}
if( MoverParameters->PantCompFlag ) {
// Winger 160404 - dzwiek malej sprezarki
sSmallCompressor.play();
}
else {
sSmallCompressor.stop();
}
if( TestFlag( MoverParameters->WarningSignal, 1 ) ) {
sHorn1.play();
}
else {
sHorn1.stop();
}
if( TestFlag( MoverParameters->WarningSignal, 2 ) ) {
sHorn2.play();
}
else {
sHorn2.stop();
}
if( MoverParameters->DoorClosureWarning ) {
if( MoverParameters->DepartureSignal ) {
// NBMX sygnal odjazdu
// MC: pod warunkiem ze jest zdefiniowane w chk
sDepartureSignal.play();
}
else {
sDepartureSignal.stop();
}
}
}
#endif
};
// McZapkie-250202
// wczytywanie pliku z danymi multimedialnymi (dzwieki)
void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName,
std::string ReplacableSkin)
{
void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, std::string ReplacableSkin ) {
double dSDist;
// asBaseDir=BaseDir;
Global::asCurrentDynamicPath = BaseDir;
std::string asFileName = BaseDir + TypeName + ".mmd";
std::string asLoadName;
@@ -3898,8 +3956,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName,
}
m_materialdata.multi_textures = clamp( m_materialdata.multi_textures, 0, 1 ); // na razie ustawiamy na 1
}
asModel = BaseDir + asModel; // McZapkie 2002-07-20: dynamics maja swoje
// modele w dynamics/basedir
asModel = BaseDir + asModel; // McZapkie 2002-07-20: dynamics maja swoje modele w dynamics/basedir
Global::asCurrentTexturePath = BaseDir; // biezaca sciezka do tekstur to dynamic/...
mdModel = TModelsManager::GetModel(asModel, true);
assert( mdModel != nullptr ); // TODO: handle this more gracefully than all going to shit
@@ -4757,9 +4814,8 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName,
}
else if( token == "curve:" ) {
double attenuation;
#ifdef EU07_USE_OLD_SOUNDCODE
double attenuation;
parser.getTokens( 2, false );
parser
>> token
@@ -4959,6 +5015,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName,
mdLowPolyInt->Init();
Global::asCurrentTexturePath = szTexturePath; // kiedyś uproszczone wnętrze mieszało tekstury nieba
Global::asCurrentDynamicPath = "";
}
//---------------------------------------------------------------------------

View File

@@ -53,19 +53,15 @@ std::string Global::szTexturesDDS = ".dds"; // lista tekstur od DDS
int Global::iPause = 0; // 0x10; // globalna pauza ruchu
bool Global::bActive = true; // czy jest aktywnym oknem
TWorld *Global::pWorld = NULL;
cParser *Global::pParser = NULL;
TCamera *Global::pCamera = NULL; // parametry kamery
TDynamicObject *Global::pUserDynamic = NULL; // pojazd użytkownika, renderowany bez trzęsienia
TTranscripts Global::tranTexts; // obiekt obsługujący stenogramy dźwięków na ekranie
float4 Global::UITextColor = float4( 225.0 / 255.0f, 225.0f / 255.0f, 225.0f / 255.0f, 1.0f );
// parametry scenerii
vector3 Global::pCameraPosition;
vector3 Global::DebugCameraPosition;
double Global::pCameraRotation;
double Global::pCameraRotationDeg;
std::vector<vector3> Global::FreeCameraInit;
std::vector<vector3> Global::FreeCameraInitAngle;
// parametry scenerii
GLfloat Global::FogColor[] = {0.6f, 0.7f, 0.8f};
double Global::fFogStart = 1700;
double Global::fFogEnd = 2000;
@@ -127,8 +123,6 @@ bool Global::bGlutFont = false; // czy tekst generowany przez GLUT32.DLL
int Global::iConvertModels{ 0 }; // temporary override, to prevent generation of .e3d not compatible with old exe
int Global::iSlowMotionMask = -1; // maska wyłączanych właściwości dla zwiększenia FPS
// bool Global::bTerrainCompact=true; //czy zapisać teren w pliku
TAnimModel *Global::pTerrainCompact = NULL; // do zapisania terenu w pliku
std::string Global::asTerrainModel = ""; // nazwa obiektu terenu do zapisania w pliku
double Global::fFpsAverage = 20.0; // oczekiwana wartosć FPS
double Global::fFpsDeviation = 5.0; // odchylenie standardowe FPS
double Global::fFpsMin = 30.0; // dolna granica FPS, przy której promień scenerii będzie zmniejszany
@@ -965,16 +959,6 @@ void Global::SetCameraPosition(vector3 pNewCameraPosition)
pCameraPosition = pNewCameraPosition;
}
void Global::SetCameraRotation(double Yaw)
{ // ustawienie bezwzględnego kierunku kamery z korekcją do przedziału <-M_PI,M_PI>
pCameraRotation = Yaw;
while (pCameraRotation < -M_PI)
pCameraRotation += 2 * M_PI;
while (pCameraRotation > M_PI)
pCameraRotation -= 2 * M_PI;
pCameraRotationDeg = pCameraRotation * 180.0 / M_PI;
}
void Global::TrainDelete(TDynamicObject *d)
{ // usunięcie pojazdu prowadzonego przez użytkownika
if (pWorld)
@@ -983,20 +967,6 @@ void Global::TrainDelete(TDynamicObject *d)
//---------------------------------------------------------------------------
bool Global::DoEvents()
{ // wywoływać czasem, żeby nie robił wrażenia zawieszonego
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
return FALSE;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return TRUE;
}
//---------------------------------------------------------------------------
TTranscripts::TTranscripts()
{
/*

View File

@@ -10,10 +10,8 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include <string>
#include <Windows.h>
#include "renderer.h"
#include "glfw/glfw3.h"
#include "gl/glew.h"
#include "dumb3d.h"
// definicje klawiszy
@@ -113,16 +111,6 @@ const int k_WalkMode = 73;
int const k_DimHeadlights = 74;
const int MaxKeys = 75;
// klasy dla wskaźników globalnych
/*
class TGround;
class TWorld;
class TCamera;
class TDynamicObject;
class TAnimModel; // obiekt terenu
class cParser; // nowy (powolny!) parser
class TEvent;
*/
class TTranscript
{ // klasa obsługująca linijkę napisu do dźwięku
public:
@@ -159,9 +147,7 @@ public:
static void InitKeys();
inline static Math3D::vector3 GetCameraPosition() { return pCameraPosition; };
static void SetCameraPosition(Math3D::vector3 pNewCameraPosition);
static void SetCameraRotation(double Yaw);
static void TrainDelete(TDynamicObject *d);
static bool DoEvents();
static std::string Bezogonkow(std::string str, bool _ = false);
static double Min0RSpeed(double vel1, double vel2);
@@ -170,8 +156,6 @@ public:
static bool RealisticControlMode; // controls ability to steer the vehicle from outside views
static Math3D::vector3 pCameraPosition; // pozycja kamery w świecie
static Math3D::vector3 DebugCameraPosition; // pozycja kamery w świecie
static double pCameraRotation; // kierunek bezwzględny kamery w świecie: 0=północ, 90°=zachód (-azymut)
static double pCameraRotationDeg; // w stopniach, dla animacji billboard
static std::vector<Math3D::vector3> FreeCameraInit; // pozycje kamery
static std::vector<Math3D::vector3> FreeCameraInitAngle;
static int iWindowWidth;
@@ -272,16 +256,12 @@ public:
static bool bHideConsole; // hunter-271211: ukrywanie konsoli
static TWorld *pWorld; // wskaźnik na świat do usuwania pojazdów
static TAnimModel *pTerrainCompact; // obiekt terenu do ewentualnego zapisania w pliku
static std::string asTerrainModel; // nazwa obiektu terenu do zapisania w pliku
static bool bRollFix; // czy wykonać przeliczanie przechyłki
static cParser *pParser;
static double fFpsAverage; // oczekiwana wartosć FPS
static double fFpsDeviation; // odchylenie standardowe FPS
static double fFpsMin; // dolna granica FPS, przy której promień scenerii będzie zmniejszany
static double fFpsMax; // górna granica FPS, przy której promień scenerii będzie zwiększany
static TCamera *pCamera; // parametry kamery
static TDynamicObject *pUserDynamic; // pojazd użytkownika, renderowany bez trzęsienia
static double fCalibrateIn[6][6]; // parametry kalibracyjne wejść z pulpitu
static double fCalibrateOut[7][6]; // parametry kalibracyjne wyjść dla pulpitu
static double fCalibrateOutMax[7]; // wartości maksymalne wyjść dla pulpitu

View File

@@ -547,18 +547,18 @@ struct TMotorParameters
struct TSecuritySystem
{
int SystemType; /*0: brak, 1: czuwak aktywny, 2: SHP/sygnalizacja kabinowa*/
double AwareDelay; // czas powtarzania czuwaka
int SystemType { 0 }; /*0: brak, 1: czuwak aktywny, 2: SHP/sygnalizacja kabinowa*/
double AwareDelay { -1.0 }; // czas powtarzania czuwaka
double AwareMinSpeed; // minimalna prędkość załączenia czuwaka, normalnie 10% Vmax
double SoundSignalDelay;
double EmergencyBrakeDelay;
int Status; /*0: wylaczony, 1: wlaczony, 2: czuwak, 4: shp, 8: alarm, 16: hamowanie awaryjne*/
double SystemTimer;
double SystemSoundCATimer;
double SystemSoundSHPTimer;
double SystemBrakeCATimer;
double SystemBrakeSHPTimer;
double SystemBrakeCATestTimer; // hunter-091012
double SoundSignalDelay { -1.0 };
double EmergencyBrakeDelay { -1.0 };
int Status { 0 }; /*0: wylaczony, 1: wlaczony, 2: czuwak, 4: shp, 8: alarm, 16: hamowanie awaryjne*/
double SystemTimer { 0.0 };
double SystemSoundCATimer { 0.0 };
double SystemSoundSHPTimer { 0.0 };
double SystemBrakeCATimer { 0.0 };
double SystemBrakeSHPTimer { 0.0 };
double SystemBrakeCATestTimer { 0.0 }; // hunter-091012
int VelocityAllowed;
int NextVelocityAllowed; /*predkosc pokazywana przez sygnalizacje kabinowa*/
bool RadioStop; // czy jest RadioStop

View File

@@ -9,6 +9,8 @@ http://mozilla.org/MPL/2.0/.
#pragma once
int const null_handle = 0;
enum class resource_state {
none,
loading,
@@ -64,6 +66,11 @@ class ResourceManager
};
*/
using resource_timestamp = std::chrono::steady_clock::time_point;
// takes containers providing access to specific element through operator[]
// with elements of std::pair<resource *, resource_timestamp>
// the element should provide method release() freeing resources owned by the element
template <class Container_>
class garbage_collector {

View File

@@ -71,7 +71,6 @@ private:
};
typedef int texture_handle;
int const null_handle = 0;
class texture_manager {
@@ -104,7 +103,7 @@ private:
// types:
typedef std::pair<
opengl_texture *,
std::chrono::steady_clock::time_point > texturetimepoint_pair;
resource_timestamp > texturetimepoint_pair;
typedef std::vector< texturetimepoint_pair > texturetimepointpair_sequence;

View File

@@ -4490,6 +4490,7 @@ bool TTrain::Update( double const Deltatime )
}
else
btLampkaCzuwaka.Turn( false );
btLampkaSHP.Turn(TestFlag(mvOccupied->SecuritySystem.Status, s_active));
}
else // wylaczone
@@ -5604,50 +5605,34 @@ TTrain::update_sounds( double const Deltatime ) {
if( dsbSlipAlarm )
dsbSlipAlarm->Stop();
}
#endif
// McZapkie-141102: SHP i czuwak, TODO: sygnalizacja kabinowa
if (mvOccupied->SecuritySystem.Status > 0)
{
// hunter-091012: rozdzielenie alarmow
if (TestFlag(mvOccupied->SecuritySystem.Status, s_CAalarm) ||
TestFlag(mvOccupied->SecuritySystem.Status, s_SHPalarm))
{
if (dsbBuzzer)
{
dsbBuzzer->GetStatus(&stat);
if (!(stat & DSBSTATUS_PLAYING))
{
play_sound( dsbBuzzer, DSBVOLUME_MAX, DSBPLAY_LOOPING );
Console::BitsSet(1 << 14); // ustawienie bitu 16 na PoKeys
}
}
}
else
{
if (dsbBuzzer)
{
dsbBuzzer->GetStatus(&stat);
if (stat & DSBSTATUS_PLAYING)
{
dsbBuzzer->Stop();
Console::BitsClear(1 << 14); // ustawienie bitu 16 na PoKeys
}
}
}
}
else // wylaczone
{
if (dsbBuzzer)
{
dsbBuzzer->GetStatus(&stat);
if (stat & DSBSTATUS_PLAYING)
{
dsbBuzzer->Stop();
Console::BitsClear(1 << 14); // ustawienie bitu 16 na PoKeys
}
}
}
if( TestFlag( mvOccupied->SecuritySystem.Status, s_CAalarm )
|| TestFlag( mvOccupied->SecuritySystem.Status, s_SHPalarm ) ) {
if( false == dsbBuzzer.is_playing() ) {
dsbBuzzer.play( sound_flags::looping );
Console::BitsSet( 1 << 14 ); // ustawienie bitu 16 na PoKeys
}
}
else {
if( true == dsbBuzzer.is_playing() ) {
dsbBuzzer.stop();
Console::BitsClear( 1 << 14 ); // ustawienie bitu 16 na PoKeys
}
}
}
else {
// wylaczone
if( true == dsbBuzzer.is_playing() ) {
dsbBuzzer.stop();
Console::BitsClear( 1 << 14 ); // ustawienie bitu 16 na PoKeys
}
}
#ifdef EU07_USE_OLD_SOUNDCODE
/* if ((mvControlled->Mains) &&
(mvControlled->EngineType==ElectricSeriesMotor))
{
@@ -5755,7 +5740,6 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName)
if (token == "internaldata:")
{
do
{
token = "";
@@ -5964,7 +5948,7 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName)
rsFadeSound.FM = 1.0;
rsFadeSound.FA = 1.0;
*/
rsFadeSound.deserialize( parser, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency );
rsFadeSound.deserialize( parser, sound_type::single );
}
else if (token == "localbrakesound:")
{

View File

@@ -453,7 +453,7 @@ private:
float fCzuwakTestTimer; // hunter-091012: do testu czuwaka
float fLightsTimer; // yB 150617: timer do swiatel
int CAflag; // hunter-131211: dla osobnego zbijania CA i SHP
bool CAflag { false }; // hunter-131211: dla osobnego zbijania CA i SHP
double fPoslizgTimer;
TTrack *tor;

View File

@@ -77,7 +77,7 @@ simulation_time::init() {
m_time.wSecond = 0;
}
m_yearday = yearday( m_time.wDay, m_time.wMonth, m_time.wYear );
m_yearday = year_day( m_time.wDay, m_time.wMonth, m_time.wYear );
}
void
@@ -125,16 +125,15 @@ simulation_time::update( double const Deltatime ) {
}
int
simulation_time::yearday( int Day, const int Month, const int Year ) {
simulation_time::year_day( int Day, const int Month, const int Year ) const {
char daytab[ 2 ][ 13 ] = {
char const daytab[ 2 ][ 13 ] = {
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
int i, leap;
leap = ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 );
for( i = 1; i < Month; ++i )
int leap { ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ) };
for( int i = 1; i < Month; ++i )
Day += daytab[ leap ][ i ];
return Day;
@@ -215,7 +214,6 @@ void TWorld::TrainDelete(TDynamicObject *d)
Train = NULL;
Controlled = NULL; // tego też już nie ma
mvControlled = NULL;
Global::pUserDynamic = NULL; // tego też nie ma
};
bool TWorld::Init( GLFWwindow *Window ) {
@@ -267,7 +265,6 @@ bool TWorld::Init( GLFWwindow *Window ) {
{
Controlled = Train->Dynamic();
mvControlled = Controlled->ControlledFind()->MoverParameters;
Global::pUserDynamic = Controlled; // renerowanie pojazdu względem kabiny
WriteLog("Player train init OK");
glfwSetWindowTitle( window, ( Global::AppName + " (" + Controlled->MoverParameters->Name + " @ " + Global::SceneryFile + ")" ).c_str() );
@@ -771,7 +768,6 @@ void TWorld::InOutKey( bool const Near )
FreeFlyModeFlag = !FreeFlyModeFlag; // zmiana widoku
if (FreeFlyModeFlag) {
// jeżeli poza kabiną, przestawiamy w jej okolicę - OK
Global::pUserDynamic = NULL; // bez renderowania względem kamery
if (Train) {
// cache current cab position so there's no need to set it all over again after each out-in switch
Train->pMechSittingPosition = Train->pMechOffset;
@@ -787,7 +783,6 @@ void TWorld::InOutKey( bool const Near )
{ // jazda w kabinie
if (Train)
{
Global::pUserDynamic = Controlled; // renerowanie względem kamery
Train->Dynamic()->bDisplayCab = true;
Train->Dynamic()->ABuSetModelShake(
vector3(0, 0, 0)); // zerowanie przesunięcia przed powrotem?
@@ -1061,6 +1056,8 @@ bool TWorld::Update() {
Timer::subsystem.sim_total.stop();
simulation::Region->update_sounds();
audio::renderer.update( dt );
GfxRenderer.Update( dt );
ResourceSweep();
@@ -1136,12 +1133,10 @@ TWorld::Update_Camera( double const Deltatime ) {
else if( Global::shiftState ) {
// patrzenie w bok przez szybę
Camera.LookAt = Camera.Pos - ( lr ? -1 : 1 ) * Train->Dynamic()->VectorLeft() * Train->Dynamic()->MoverParameters->ActiveCab;
Global::SetCameraRotation( -modelrotate );
}
else { // patrzenie w kierunku osi pojazdu, z uwzględnieniem kabiny - jakby z lusterka,
// ale bez odbicia
Camera.LookAt = Camera.Pos - Train->GetDirection() * Train->Dynamic()->MoverParameters->ActiveCab; //-1 albo 1
Global::SetCameraRotation( M_PI - modelrotate ); // tu już trzeba uwzględnić lusterka
}
Camera.Roll = std::atan( Train->pMechShake.x * Train->fMechRoll ); // hustanie kamery na boki
Camera.Pitch = 0.5 * std::atan( Train->vMechVelocity.z * Train->fMechPitch ); // hustanie kamery przod tyl
@@ -1174,11 +1169,10 @@ TWorld::Update_Camera( double const Deltatime ) {
else // patrzenie w kierunku osi pojazdu, z uwzględnieniem kabiny
Camera.LookAt = Train->GetWorldMechPosition() + Train->GetDirection() * 5.0 * Train->Dynamic()->MoverParameters->ActiveCab; //-1 albo 1
Camera.vUp = Train->GetUp();
Global::SetCameraRotation( Camera.Yaw - modelrotate ); // tu już trzeba uwzględnić lusterka
}
}
else { // kamera nieruchoma
Global::SetCameraRotation( Camera.Yaw - M_PI );
else {
// kamera nieruchoma
}
// all done, update camera position to the new value
Global::pCameraPosition = Camera.Pos;
@@ -2118,7 +2112,6 @@ void TWorld::ChangeDynamic() {
Train->Dynamic()->asBaseDir +
Train->Dynamic()->MoverParameters->TypeName + ".mmd" );
if( !FreeFlyModeFlag ) {
Global::pUserDynamic = Controlled; // renerowanie względem kamery
Train->Dynamic()->bDisplayCab = true;
Train->Dynamic()->ABuSetModelShake(
vector3( 0, 0, 0 ) ); // zerowanie przesunięcia przed powrotem?

View File

@@ -39,13 +39,13 @@ public:
second() const { return ( m_time.wMilliseconds * 0.001 + m_time.wSecond ); }
int
year_day() const { return m_yearday; }
// helper, calculates day of year from given date
int
year_day( int Day, int const Month, int const Year ) const;
int
julian_day() const;
private:
// calculates day of year from given date
int
yearday( int Day, int const Month, int const Year );
// calculates day and month from given day of year
void
daymonth( WORD &Day, WORD &Month, WORD const Year, WORD const Yearday );
@@ -115,7 +115,6 @@ TWorld();
// calculates current season of the year based on set simulation date
void compute_season( int const Yearday ) const;
private:
void Update_Environment();
void Update_Camera( const double Deltatime );

View File

@@ -98,7 +98,7 @@ buffer_manager::~buffer_manager() {
}
// creates buffer object out of data stored in specified file. returns: handle to the buffer or null_handle if creation failed
buffer_handle
audio::buffer_handle
buffer_manager::create( std::string const &Filename ) {
auto filename { ToLower( Filename ) };
@@ -114,14 +114,18 @@ buffer_manager::create( std::string const &Filename ) {
std::begin( filename ), std::end( filename ),
'\\', '/' );
// try dynamic-specific sounds first
auto lookup { find_buffer( Global::asCurrentDynamicPath + filename ) };
if( lookup != null_handle ) {
return lookup;
}
std::string filelookup { find_file( Global::asCurrentDynamicPath + filename ) };
if( false == filelookup.empty() ) {
return emplace( filelookup );
audio::buffer_handle lookup { null_handle };
std::string filelookup;
if( false == Global::asCurrentDynamicPath.empty() ) {
// try dynamic-specific sounds first
lookup = find_buffer( Global::asCurrentDynamicPath + filename );
if( lookup != null_handle ) {
return lookup;
}
filelookup = find_file( Global::asCurrentDynamicPath + filename );
if( false == filelookup.empty() ) {
return emplace( filelookup );
}
}
// if dynamic-specific lookup finds nothing, try the default sound folder
lookup = find_buffer( szSoundPath + filename );
@@ -136,8 +140,15 @@ buffer_manager::create( std::string const &Filename ) {
return null_handle;
}
// provides direct access to a specified buffer
audio::openal_buffer const &
buffer_manager::buffer( audio::buffer_handle const Buffer ) const {
return m_buffers[ Buffer ];
}
// places in the bank a buffer containing data stored in specified file. returns: handle to the buffer
buffer_handle
audio::buffer_handle
buffer_manager::emplace( std::string Filename ) {
buffer_handle const handle { m_buffers.size() };
@@ -151,7 +162,7 @@ buffer_manager::emplace( std::string Filename ) {
return handle;
}
buffer_handle
audio::buffer_handle
buffer_manager::find_buffer( std::string const &Buffername ) const {
auto lookup = m_buffermappings.find( Buffername );

View File

@@ -16,6 +16,7 @@ namespace audio {
ALuint const null_resource { ~ALuint{ 0 } };
// wrapper for audio sample
struct openal_buffer {
// members
ALuint id { null_resource }; // associated AL resource
@@ -36,6 +37,8 @@ private:
using buffer_handle = std::size_t;
//
class buffer_manager {
public:
@@ -47,6 +50,9 @@ public:
// creates buffer object out of data stored in specified file. returns: handle to the buffer or null_handle if creation failed
buffer_handle
create( std::string const &Filename );
// provides direct access to a specified buffer
audio::openal_buffer const &
buffer( audio::buffer_handle const Buffer ) const;
private:
// types

View File

@@ -10,12 +10,88 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "audiorenderer.h"
#include "sound.h"
#include "logs.h"
namespace audio {
openal_renderer renderer;
// starts playback of queued buffers
void
openal_source::play() {
::alSourcePlay( id );
is_playing = true;
}
// stops the playback
void
openal_source::stop() {
::alSourcei( id, AL_LOOPING, AL_FALSE );
::alSourceStop( id );
// NOTE: we don't update the is_playing flag
// this way the state will change only on next update loop,
// giving the controller a chance to properly change state from cease to none
// even with multiple active sounds under control
}
// updates state of the source
void
openal_source::update( int const Deltatime ) {
// TODO: test whether the emitter was within range during the last tick, potentially update the counter and flag it for timeout
::alGetSourcei( id, AL_BUFFERS_PROCESSED, &buffer_index );
// for multipart sounds trim away processed sources until only one remains, the last one may be set to looping by the controller
ALuint bufferid;
while( ( buffer_index > 0 )
&& ( buffers.size() > 1 ) ) {
::alSourceUnqueueBuffers( id, 1, &bufferid );
buffers.erase( std::begin( buffers ) );
--buffer_index;
}
int state;
::alGetSourcei( id, AL_SOURCE_STATE, &state );
is_playing = ( state == AL_PLAYING );
// request instructions from the controller
controller->update( *this );
}
// toggles looping of the sound emitted by the source
void
openal_source::loop( bool const State ) {
::alSourcei(
id,
AL_LOOPING,
( State ?
AL_TRUE :
AL_FALSE ) );
}
// releases bound buffers and resets state of the class variables
// NOTE: doesn't release allocated implementation-side source
void
openal_source::clear() {
controller = nullptr;
// unqueue bound buffers:
// ensure no buffer is in use...
::alSourcei( id, AL_LOOPING, AL_FALSE );
::alSourceStop( id );
is_playing = false;
// ...prepare space for returned ids of unqueued buffers (not that we need that info)...
std::vector<ALuint> bufferids;
bufferids.resize( buffers.size() );
// ...release the buffers and update source data to match
::alSourceUnqueueBuffers( id, bufferids.size(), bufferids.data() );
buffers.clear();
buffer_index = 0;
}
openal_renderer::~openal_renderer() {
::alcMakeContextCurrent( nullptr );
@@ -24,12 +100,19 @@ openal_renderer::~openal_renderer() {
if( m_device != nullptr ) { ::alcCloseDevice( m_device ); }
}
buffer_handle
audio::buffer_handle
openal_renderer::fetch_buffer( std::string const &Filename ) {
return m_buffers.create( Filename );
}
// provides direct access to a specified buffer
audio::openal_buffer const &
openal_renderer::buffer( audio::buffer_handle const Buffer ) const {
return m_buffers.buffer( Buffer );
}
// initializes the service
bool
openal_renderer::init() {
@@ -42,11 +125,56 @@ openal_renderer::init() {
// basic initialization failed
return false;
}
// all done
m_ready = true;
return true;
}
// schedules playback of specified sample, under control of the specified emitter
void
openal_renderer::insert( sound_source *Controller, audio::buffer_handle const Sound ) {
audio::openal_source::buffer_sequence buffers { Sound };
return
insert(
Controller,
std::begin( buffers ), std::end( buffers ) );
}
// updates state of all active emitters
void
openal_renderer::update( int const Deltatime ) {
auto source { std::begin( m_sources ) };
while( source != std::end( m_sources ) ) {
// update each source
source->update( Deltatime );
// if after the update the source isn't playing, put it away on the spare stack, it's done
if( false == source->is_playing ) {
source->clear();
m_sourcespares.push( *source );
source = m_sources.erase( source );
}
else {
// otherwise proceed through the list normally
++source;
}
}
}
// returns an instance of implementation-side part of the sound emitter
audio::openal_source
openal_renderer::fetch_source() {
audio::openal_source soundsource;
if( false == m_sourcespares.empty() ) {
// reuse (a copy of) already allocated source
soundsource = m_sourcespares.top();
m_sourcespares.pop();
}
return soundsource;
}
bool
openal_renderer::init_caps() {

View File

@@ -10,9 +10,60 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include "audio.h"
#include "resourcemanager.h"
class sound_source;
namespace audio {
// implementation part of the sound emitter
// TODO: generic interface base, for implementations other than openAL
struct openal_source {
// types
using buffer_sequence = std::vector<audio::buffer_handle>;
// members
ALuint id { audio::null_resource }; // associated AL resource
sound_source *controller { nullptr }; // source controller
buffer_sequence buffers; // sequence of samples the source will emit
int buffer_index; // currently queued sample from the buffer sequence
bool is_playing { false };
// methods
template <class Iterator_>
openal_source &
bind( sound_source *Controller, Iterator_ First, Iterator_ Last ) {
controller = Controller;
buffers.insert( std::end( buffers ), First, Last );
if( id == audio::null_resource ) {
::alGenSources( 1, &id ); }
// look up and queue assigned buffers
std::vector<ALuint> bufferids;
for( auto const buffer : buffers ) {
bufferids.emplace_back( audio::renderer.buffer( buffer ).id ); }
::alSourceQueueBuffers( id, bufferids.size(), bufferids.data() );
return *this; }
// starts playback of queued buffers
void
play();
// stops the playback
void
stop();
// updates state of the source
void
update( int const Deltatime );
// toggles looping of the sound emitted by the source
void
loop( bool const State );
// releases bound buffers and resets state of the class variables
// NOTE: doesn't release allocated implementation-side source
void
clear();
};
class openal_renderer {
public:
@@ -21,23 +72,46 @@ public:
// methods
// buffer methods
// returns handle to a buffer containing audio data from specified file
buffer_handle
audio::buffer_handle
fetch_buffer( std::string const &Filename );
// provides direct access to a specified buffer
audio::openal_buffer const &
buffer( audio::buffer_handle const Buffer ) const;
// core methods
// initializes the service
bool
init();
// schedules playback of provided range of samples, under control of the specified sound emitter
template <class Iterator_>
void
insert( sound_source *Controller, Iterator_ First, Iterator_ Last ) {
m_sources.emplace_back( fetch_source().bind( Controller, First, Last ) ); }
// schedules playback of specified sample, under control of the specified sound emitter
void
insert( sound_source *Controller, audio::buffer_handle const Sound );
// updates state of all active emitters
void
update( int const Deltatime );
private:
// types
using source_list = std::list<audio::openal_source>;
using source_sequence = std::stack<audio::openal_source>;
// methods
bool
init_caps();
// returns an instance of implementation-side part of the sound emitter
audio::openal_source
fetch_source();
// members
ALCdevice * m_device { nullptr };
ALCcontext * m_context { nullptr };
bool m_ready { false }; // renderer is initialized and functional
buffer_manager m_buffers;
// TBD: list of sources as vector, sorted by distance, for openal implementations with limited number of active sources?
source_list m_sources;
source_sequence m_sourcespares; // already created and currently unused sound sources
};
extern openal_renderer renderer;

View File

@@ -225,15 +225,15 @@
<ClCompile Include="messaging.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="sound.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="audiorenderer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="audio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="sound.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Globals.h">
@@ -449,13 +449,13 @@
<ClInclude Include="messaging.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="audiorenderer.h">
<ClInclude Include="sound.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="audio.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="sound.h">
<ClInclude Include="audiorenderer.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>

View File

@@ -395,7 +395,7 @@ geometrybank_manager::create_bank() {
if( true == Global::bUseVBO ) { m_geometrybanks.emplace_back( std::make_shared<opengl_vbogeometrybank>(), std::chrono::steady_clock::time_point() ); }
else { m_geometrybanks.emplace_back( std::make_shared<opengl_dlgeometrybank>(), std::chrono::steady_clock::time_point() ); }
// NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication
return { m_geometrybanks.size(), 0 };
return { static_cast<std::uint32_t>( m_geometrybanks.size() ), 0 };
}
// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL

View File

@@ -302,7 +302,7 @@ private:
// types:
typedef std::pair<
std::shared_ptr<geometry_bank>,
std::chrono::steady_clock::time_point > geometrybanktimepoint_pair;
resource_timestamp > geometrybanktimepoint_pair;
typedef std::deque< geometrybanktimepoint_pair > geometrybanktimepointpair_sequence;

View File

@@ -696,9 +696,10 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f
point = lightviewmatrix * point;
}
bounding_box( frustumchunkmin, frustumchunkmax, std::begin( frustumchunkshapepoints ), std::end( frustumchunkshapepoints ) );
// quantize the frustum points with 50 m resolution, to reduce shadow shimmer on scale/orientation changes
frustumchunkmin = 50.f * glm::floor( frustumchunkmin * ( 1.f / 50.f ) );
frustumchunkmax = 50.f * glm::ceil( frustumchunkmax * ( 1.f / 50.f ) );
// quantize the frustum points and add some padding, to reduce shadow shimmer on scale changes
auto const quantizationstep{ std::min( Global::shadowtune.depth, 50.f ) };
frustumchunkmin = quantizationstep * glm::floor( frustumchunkmin * ( 1.f / quantizationstep ) );
frustumchunkmax = quantizationstep * glm::ceil( frustumchunkmax * ( 1.f / quantizationstep ) );
// ...use the dimensions to set up light projection boundaries...
// NOTE: since we only have one cascade map stage, we extend the chunk forward/back to catch areas normally covered by other stages
camera.projection() *=

View File

@@ -21,7 +21,7 @@ http://mozilla.org/MPL/2.0/.
#define EU07_USE_PICKING_FRAMEBUFFER
//#define EU07_USE_DEBUG_SHADOWMAP
#define EU07_USE_DEBUG_CAMERA
//#define EU07_USE_DEBUG_CAMERA
//#define EU07_USE_DEBUG_CULLING
struct opengl_light {

View File

@@ -460,10 +460,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad
auto *sound { deserialize_sound( Input, Scratchpad, nodedata ) };
if( false == simulation::Sounds.insert( sound ) ) {
#ifdef EU07_USE_OLD_SOUNDCODE
ErrorLog( "Bad scenario: sound node with duplicate name \"" + sound->m_name + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
#else
#endif
ErrorLog( "Bad scenario: sound node with duplicate name \"" + sound->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
simulation::Region->insert_sound( sound, Scratchpad );
}

140
sound.cpp
View File

@@ -10,6 +10,8 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "sound.h"
#include "parser.h"
#include "globals.h"
// restores state of the class from provided data stream
sound_source &
@@ -25,12 +27,14 @@ sound_source::deserialize( cParser &Input, sound_type const Legacytype, int cons
switch( Legacytype ) {
case sound_type::single: {
// single sample only
Input.getTokens( 1, true, "\n\r\t ,;" );
m_soundmain = audio::renderer.fetch_buffer( Input.getToken<std::string>( true, "\n\r\t ,;" ) );
break;
}
case sound_type::multipart: {
// three samples: start, middle, stop
Input.getTokens( 3, true, "\n\r\t ,;" );
m_soundbegin = audio::renderer.fetch_buffer( Input.getToken<std::string>( true, "\n\r\t ,;" ) );
m_soundmain = audio::renderer.fetch_buffer( Input.getToken<std::string>( true, "\n\r\t ,;" ) );
m_soundend = audio::renderer.fetch_buffer( Input.getToken<std::string>( true, "\n\r\t ,;" ) );
break;
}
default: {
@@ -53,14 +57,142 @@ sound_source::deserialize( cParser &Input, sound_type const Legacytype, int cons
// issues contextual play commands for the audio renderer
void
sound_source::play() {
sound_source::play( int const Flags ) {
if( ( false == Global::bSoundEnabled )
|| ( true == empty() ) ) {
// if the sound is disabled altogether or nothing can be emitted from this source, no point wasting time
return;
}
m_flags = Flags;
switch( m_stage ) {
case stage::none:
case stage::restart: {
if( m_soundbegin != null_handle ) {
std::vector<audio::buffer_handle> bufferlist{ m_soundbegin, m_soundmain };
audio::renderer.insert( this, std::begin( bufferlist ), std::end( bufferlist ) );
}
else {
audio::renderer.insert( this, m_soundmain );
}
break;
}
case stage::main: {
// TODO: schedule another main sample playback, or a suitable sample from the table for combined sources
break;
}
case stage::end: {
// schedule stop of current sequence end sample...
m_stage = stage::restart;
// ... and queue startup or main sound again
return play( Flags );
break;
}
default: {
break;
}
}
}
// stops currently active play commands controlled by this emitter
void
sound_source::stop() {
if( ( m_stage == stage::none )
|| ( m_stage == stage::end ) ) {
return;
}
switch( m_stage ) {
case stage::begin:
case stage::main: {
// set the source to kill any currently active sounds
m_stage = stage::cease;
if( m_soundend != null_handle ) {
// and if there's defined sample for the sound end, play it instead
audio::renderer.insert( this, m_soundend );
}
break;
}
case stage::end: {
// set the source to kill any currently active sounds
m_stage = stage::cease;
break;
}
default: {
break;
}
}
}
// adjusts parameters of provided implementation-side sound source
void
sound_source::update( audio::openal_source &Source ) {
if( true == Source.is_playing ) {
// kill the sound if requested
if( m_stage == stage::cease ) {
Source.stop();
return;
}
// TODO: positional update
if( m_soundbegin != null_handle ) {
// multipart sound
// detect the moment when the sound moves from startup sample to the main
if( ( m_stage == stage::begin )
&& ( Source.buffers[ Source.buffer_index ] == m_soundmain ) ) {
// when it happens update active sample flags, and activate the looping
Source.loop( true );
m_stage = stage::main;
}
}
}
else {
// if the emitter isn't playing it's either done or wasn't yet started
// we can determine this from number of processed buffers
if( Source.buffer_index != Source.buffers.size() ) {
auto const buffer { Source.buffers[ Source.buffer_index ] };
if( buffer == m_soundbegin ) { m_stage = stage::begin; }
// TODO: take ito accound sample table for combined sounds
else if( buffer == m_soundmain ) { m_stage = stage::main; }
else if( buffer == m_soundend ) { m_stage = stage::end; }
// TODO: emitter initialization
if( ( buffer == m_soundmain )
&& ( true == TestFlag( m_flags, sound_flags::looping ) ) ) {
// main sample can be optionally set to loop
Source.loop( true );
}
// all set, start playback
Source.play();
}
else {
//
auto const buffer { Source.buffers[ Source.buffer_index - 1 ] };
if( ( buffer == m_soundend )
|| ( ( m_soundend == null_handle )
&& ( buffer == m_soundmain ) ) ) {
m_stage = stage::none;
}
}
}
}
bool
sound_source::empty() const {
return true;
// NOTE: we test only the main sound, won't bother playing potential bookends if this is missing
// TODO: take into account presence of sample table, for combined sounds
return ( m_soundmain == null_handle );
}
// returns true if the source is emitting any sound
bool
sound_source::is_playing() const {
return ( m_stage != stage::none );
}
//---------------------------------------------------------------------------

48
sound.h
View File

@@ -10,7 +10,7 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include "audiorenderer.h"
#include "parser.h"
#include "classes.h"
#include "names.h"
enum sound_type {
@@ -19,14 +19,18 @@ enum sound_type {
};
enum sound_parameters {
none,
range = 0x1,
range = 0x1,
amplitude = 0x2,
frequency = 0x4
};
enum sound_flags {
looping = 0x1, // the main sample will be looping; implied for multi-sounds
exclusive = 0x2 // the source won't dispatch more than one active instance of the sound; implied for multi-sounds
};
// mini controller and audio dispatcher; issues play commands for the audio renderer,
// updates parameters of assigned audio sources for the playback duration
// updates parameters of created audio emitters for the playback duration
// TODO: move to simulation namespace after clean up of owner classes
class sound_source {
@@ -34,15 +38,18 @@ public:
// methods
// restores state of the class from provided data stream
sound_source &
deserialize( cParser &Input, sound_type const Legacytype, int const Legacyparameters = sound_parameters::none );
deserialize( cParser &Input, sound_type const Legacytype, int const Legacyparameters = NULL );
sound_source &
deserialize( std::string const &Input, sound_type const Legacytype, int const Legacyparameters = sound_parameters::none );
deserialize( std::string const &Input, sound_type const Legacytype, int const Legacyparameters = NULL );
// issues contextual play commands for the audio renderer
void
play();
// sets volume of audio streams associated with this source
sound_source &
volume( float const Volume );
play( int const Flags = NULL );
// stops currently active play commands controlled by this emitter
void
stop();
// adjusts parameters of provided implementation-side sound source
void
update( audio::openal_source &Source );
// sound source name setter/getter
void
name( std::string Name );
@@ -51,10 +58,31 @@ public:
// returns true if there isn't any sound buffer associated with the object, false otherwise
bool
empty() const;
// returns true if the source is emitting any sound
bool
is_playing() const;
private:
// types
enum stage {
none,
begin,
main,
end,
cease,
restart
};
// members
TDynamicObject * m_owner { nullptr }; // optional, the vehicle carrying this sound source
glm::vec3 m_offset; // relative position of the source, either from the owner or the region centre
std::string m_name;
stage m_stage{ stage::none };
int m_flags{ NULL };
audio::buffer_handle m_soundmain { null_handle }; // main sound emitted by the source
audio::buffer_handle m_soundbegin { null_handle }; // optional, sound emitted before the main sound
audio::buffer_handle m_soundend { null_handle }; // optional, sound emitted after the main sound
// TODO: table of samples with associated values, activated when controlling variable matches the value
};
// sound source name setter/getter

25
sun.cpp
View File

@@ -14,10 +14,6 @@ cSun::cSun() {
setLocation( 19.00f, 52.00f ); // default location roughly in centre of Poland
m_observer.press = 1013.0; // surface pressure, millibars
m_observer.temp = 15.0; // ambient dry-bulb temperature, degrees C
TIME_ZONE_INFORMATION timezoneinfo; // TODO: timezone dependant on geographic location
::GetTimeZoneInformation( &timezoneinfo );
m_observer.timezone = -timezoneinfo.Bias / 60.0f;
}
cSun::~cSun() { gluDeleteQuadric( sunsphere ); }
@@ -27,6 +23,27 @@ cSun::init() {
sunsphere = gluNewQuadric();
gluQuadricNormals( sunsphere, GLU_SMOOTH );
// calculate and set timezone for the current date of the simulation
// TODO: timezone dependant on geographic location
TIME_ZONE_INFORMATION timezoneinfo;
::GetTimeZoneInformation( &timezoneinfo );
// account for potential daylight/normal time bias
// NOTE: we're using xp-compatible time zone information and current year, instead of 'historically proper' values
auto zonebias { timezoneinfo.Bias };
auto const year { simulation::Time.data().wYear };
auto const yearday { simulation::Time.year_day() };
if( yearday < simulation::Time.year_day( timezoneinfo.DaylightDate.wDay, timezoneinfo.DaylightDate.wMonth, year ) ) {
zonebias += timezoneinfo.StandardBias;
}
else if( yearday < simulation::Time.year_day( timezoneinfo.StandardDate.wDay, timezoneinfo.StandardDate.wMonth, year ) ) {
zonebias += timezoneinfo.DaylightBias;
}
else {
zonebias += timezoneinfo.StandardBias;
}
m_observer.timezone = -zonebias / 60.0f;
}
void