Files
RP411/MUNGA/AUDLOC.cpp
T
CydandClaude Opus 4.8 4abbf8879f Initial import of Red Planet v4.10 Win32 source
Imports the current Win32 source for the pod-racing game 'Red Planet',
built on the MUNGA engine and its L4 (Win32/DirectX) platform layer:

- MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend
- RP / RP_L4: Red Planet game logic and Win32 application
- DivLoader, Setup1: asset loader and installer project
- lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies

Removed stale Subversion metadata and added .gitignore/.gitattributes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 07:59:51 -05:00

509 lines
13 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "audloc.h"
#include "objstrm.h"
#include "namelist.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioLocation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//#############################################################################
//#############################################################################
//
AudioLocation::AudioLocation(
PlugStream *stream,
Entity *entity
):
AudioComponent(stream),
vectorToSource(0.0f, 0.0f, 0.0f),
locationOffset(0.0f, 0.0f, 0.0f)
{
AudioFrameCount next_update_delay;
Scalar clipping_scale;
MemoryStream_Read(stream, &locationOffset);
MemoryStream_Read(stream, &next_update_delay);
MemoryStream_Read(stream, &clipping_scale);
AudioLocationX(entity, next_update_delay, clipping_scale);
}
//
//#############################################################################
//#############################################################################
//
AudioLocation::~AudioLocation()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioLocation::AudioLocationX(
Entity *entity,
AudioFrameCount next_update_delay,
Scalar clipping_scale
)
{
Check(entity);
linkedEntity = entity;
entity->AddAudioComponent(this);
clippingScale = clipping_scale;
isHeadSource = False;
distanceToSource = 1.0f;
dopplerCents = 0;
#if 0
angleOffOrientation = 0.0f;
#endif
azimuthOfSource = 0.0f;
distanceVolumeScale = 1.0f;
highFreqCutoffScale = 1.0f;
#if 0
reverbVolumeScale = 0.0f;
#endif
nextUpdateDelay = next_update_delay;
nextUpdateFrame = NullAudioFrameCount;
}
//
//#############################################################################
//#############################################################################
//
Logical
AudioLocation::TestInstance() const
{
Component::TestInstance();
return True;
}
//
//#############################################################################
//#############################################################################
//
void
AudioLocation::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);
MEM_STRM_WRITE_ENTRY(*stream, name_list, Point3D, location_offset);
//
// Read update delay
//
CString update_delay_string("update_delay");
AudioFrameCount update_delay = DefaultAudioFrameDelay;
if (name_list->FindData(update_delay_string) != NULL)
{
Check_Pointer(name_list->FindData(update_delay_string));
Convert_From_Ascii(
(const char *)name_list->FindData(update_delay_string),
&update_delay
);
}
MemoryStream_Write(stream, &update_delay);
//
// Read culling scale
//
CString clipping_scale_string("clipping_scale");
Scalar clipping_scale = 1.0f;
if (name_list->FindData(clipping_scale_string) != NULL)
{
Check_Pointer(name_list->FindData(clipping_scale_string));
Convert_From_Ascii(
(const char *)name_list->FindData(clipping_scale_string),
&clipping_scale
);
}
MemoryStream_Write(stream, &clipping_scale);
}
//
//#############################################################################
//#############################################################################
//
void
AudioLocation::ReceiveControl(
AudioControlID,
AudioControlValue
)
{
}
//
//#############################################################################
//#############################################################################
//
Logical
AudioLocation::IsAudioLocationClipped(AudioHead *audio_head)
{
Check(this);
Check(audio_head);
//
// HACK - This is a rough approximation in that it does not
// take into account the offset of the location from the
// center of the entity. But, this is more efficient and is OK
// as long as the clipping sphere does not exclude the location
// unnaturally
//
Entity
*head_entity;
Vector3D
difference;
Scalar
scaled_radius;
head_entity = audio_head->GetHeadEntity();
Check(head_entity);
Check(linkedEntity);
difference.Subtract(
head_entity->localOrigin.linearPosition,
linkedEntity->localOrigin.linearPosition
);
scaled_radius = audio_head->GetClippingRadius() * clippingScale;
return difference.LengthSquared() > scaled_radius * scaled_radius;
}
//
//#############################################################################
//#############################################################################
//
void
AudioLocation::UpdateSpatialModelImplementation(AudioHead *audio_head)
{
Check(this);
Check(audio_head);
//
//--------------------------------------------------------------------------
// Get head and source entity
//--------------------------------------------------------------------------
//
Entity *source_entity = GetLinkedEntity();
Entity *head_entity = audio_head->GetHeadEntity();
Check(source_entity);
Check(head_entity);
//
//--------------------------------------------------------------------------
// Catch case of head == source at origin
//--------------------------------------------------------------------------
//
if (
head_entity == source_entity &&
locationOffset == Vector3D::Identity
)
{
isHeadSource = True;
vectorToSource = Vector3D::Identity;
distanceToSource = 0.0f;
dopplerCents = 0;
#if 0
angleOffOrientation = 0.0f;
#endif
azimuthOfSource = 0.0f;
distanceVolumeScale = 1.0f;
highFreqCutoffScale = 1.0f;
#if 0
reverbVolumeScale = audio_head->GetReverbToDryRatio();
#endif
return;
}
isHeadSource = False;
//
//--------------------------------------------------------------------------
// Get the ear to world linear matrix
//--------------------------------------------------------------------------
//
LinearMatrix ear_to_world = audio_head->GetEarToWorld();
//
//--------------------------------------------------------------------------
// Calculate vector to source with respect to local coordinate system
// of the ears
//--------------------------------------------------------------------------
//
{
//
// Get the source location offset from the entity position in
// world coordinates
//
Vector3D world_location_offset;
Vector3D world_source_location;
world_location_offset.Multiply(
locationOffset,
source_entity->localToWorld
);
Check(&world_location_offset);
world_source_location.Add(
source_entity->localOrigin.linearPosition,
world_location_offset
);
Check(&world_source_location);
//
// Get the vector from the ear to the source, note that the vector
// is not calculated by the ears offset but from the entities location
//
Vector3D world_ear_to_source_vector;
world_ear_to_source_vector.Subtract(
world_source_location,
head_entity->localOrigin.linearPosition
);
Check(&world_ear_to_source_vector);
//
// Transform the vector into ear coordinates
//
vectorToSource.MultiplyByInverse(
world_ear_to_source_vector,
ear_to_world
);
Check(&vectorToSource);
}
//
//--------------------------------------------------------------------------
// Calculate distance to source
//--------------------------------------------------------------------------
//
distanceToSource = vectorToSource.Length();
//
//--------------------------------------------------------------------------
// Calculate normal to source
//--------------------------------------------------------------------------
//
Vector3D normal_to_source(0.0f, 0.0f, -1.0f);
if (!Small_Enough(distanceToSource))
{
normal_to_source.Normalize(vectorToSource);
Check(&normal_to_source);
}
#if 0
//
//--------------------------------------------------------------------------
// Calculate angle between the orientation of the head and source
//--------------------------------------------------------------------------
//
if (Small_Enough(distanceToSource))
{
angleOffOrientation = 0.0f;
}
else
{
UnitVector direction;
Scalar cosine;
ear_to_world.GetToAxis(Z_Axis, &direction);
Check(&direction);
Check(&normal_to_source);
cosine = normal_to_source * direction;
Clamp(cosine, -1.0f, 1.0f);
angleOffOrientation = Arccos(cosine);
angleOffOrientation.Normalize();
}
Verify(
angleOffOrientation <= 180.0f*RAD_PER_DEG &&
angleOffOrientation >= -180.0f*RAD_PER_DEG
);
#endif
//
//--------------------------------------------------------------------------
// Calculate azimuth
//--------------------------------------------------------------------------
//
if (Small_Enough(vectorToSource.x) && Small_Enough(vectorToSource.z))
{
azimuthOfSource = 0.0f;
}
else
{
Verify(!(Small_Enough(vectorToSource.x) && Small_Enough(vectorToSource.z)));
azimuthOfSource = Arctan(vectorToSource.x, vectorToSource.z);
}
Verify(
azimuthOfSource <= 180.0f*RAD_PER_DEG &&
azimuthOfSource >= -180.0f*RAD_PER_DEG
);
//
//--------------------------------------------------------------------------
// Calculate distance volume scaling
// Apply clipping scale to rolloff distance scale, thereby giving this
// audio location a specifc rolloff characteristic
//--------------------------------------------------------------------------
//
if (distanceToSource > audio_head->GetAmplitudeRollOffKnee())
{
//
// y = 1 / 1 + (K(x - knee))^exp
//
Verify(!Small_Enough(clippingScale));
const Scalar temp =
(audio_head->GetAmplitudeRollOffDistanceScale() / clippingScale) *
(distanceToSource - audio_head->GetAmplitudeRollOffKnee());
const Scalar temp2 =
1.0f + pow(temp, audio_head->GetAmplitudeRollOffExponent());
Verify(!Small_Enough(temp2));
distanceVolumeScale = 1.0f / temp2;
}
else
{
distanceVolumeScale = 1.0f;
}
Verify(distanceVolumeScale >= 0.0f && distanceVolumeScale <= 1.0f);
//
//--------------------------------------------------------------------------
// Calculate high frequency cutoff scaling
// Apply clipping scale to high frequency rolloff distance scale, thereby
// giving this audio location a specifc rolloff characteristic
//--------------------------------------------------------------------------
//
if (distanceToSource > audio_head->GetHighFrequencyRollOffKnee())
{
//
// y = 1 / 1 + (K(x - knee))^exp
//
Verify(!Small_Enough(clippingScale));
const Scalar temp =
(audio_head->GetHighFrequencyRollOffDistanceScale() / clippingScale) *
(distanceToSource - audio_head->GetHighFrequencyRollOffKnee());
const Scalar temp2 =
1.0f + pow(temp, audio_head->GetHighFrequencyRollOffExponent());
Verify(!Small_Enough(temp2));
highFreqCutoffScale = 1.0f / temp2;
}
else
{
highFreqCutoffScale = 1.0f;
}
Verify(highFreqCutoffScale >= 0.0f && highFreqCutoffScale <= 1.0f);
#if 0
//
//--------------------------------------------------------------------------
// Calculate reverb volume scaling
//--------------------------------------------------------------------------
//
if (distanceToSource > audio_head->GetAmplitudeRollOffKnee())
{
const Scalar temp =
distanceToSource - audio_head->GetAmplitudeRollOffKnee() + 1.0f;
reverbVolumeScale =
audio_head->GetReverbToDryRatio() *
pow(temp, audio_head->GetAmplitudeRollOffExponent());
if (reverbVolumeScale > 1.0f)
reverbVolumeScale = 1.0f;
}
else
{
reverbVolumeScale = audio_head->GetReverbToDryRatio();
}
Verify(reverbVolumeScale >= 0.0f && reverbVolumeScale <= 1.0f);
#endif
//
//--------------------------------------------------------------------------
// Calculate relative velocity of source
//--------------------------------------------------------------------------
//
Vector3D relative_velocity_temp;
Vector3D relative_velocity;
relative_velocity_temp.Subtract(
source_entity->GetWorldLinearVelocity(),
head_entity->GetWorldLinearVelocity()
);
Check(&relative_velocity_temp);
relative_velocity.MultiplyByInverse(
relative_velocity_temp,
head_entity->localToWorld
);
Check(&relative_velocity);
//
//--------------------------------------------------------------------------
// Calculate doppler shift in cents
// cents = (c / (c - v)) / ScaleRatio
//--------------------------------------------------------------------------
//
if (!Small_Enough(distanceToSource))
{
AudioPitchCents
cents;
Scalar speed_of_source =
normal_to_source * relative_velocity;
const Scalar ear_radius =
audio_head->GetDistanceBetweenEars() * 0.5f;
if (distanceToSource < ear_radius)
{
Verify(!Small_Enough(ear_radius));
speed_of_source *= (distanceToSource / ear_radius);
}
if (CalculateDoppler(audio_head, speed_of_source, &cents))
{
dopplerCents = cents;
}
}
else
{
dopplerCents = 0;
}
}
//
//#############################################################################
//#############################################################################
//
Logical
AudioLocation::CalculateDoppler(
AudioHead *audio_head,
Scalar speed,
AudioPitchCents *result
)
{
Check(this);
Check(audio_head);
Check_Pointer(result);
Scalar speed_of_sound = audio_head->GetAudioSoundSpeed();
Clamp(speed, -speed_of_sound, speed_of_sound);
if (!Small_Enough(speed_of_sound - speed))
{
*result = audio_head->GetAudioDopplerConstant() *
( 1 - speed_of_sound / (speed_of_sound - speed) );
return True;
}
return False;
}