1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-06 13:16:52 +02:00

#9 fading & panning of sound sources

This commit is contained in:
XProger
2016-10-21 00:58:15 +03:00
parent 8bbd26a9ef
commit df942b2893
11 changed files with 190 additions and 95 deletions

Binary file not shown.

View File

@@ -160,6 +160,7 @@ struct Camera : Controller {
float fov, znear, zfar;
vec3 target, destPos, lastDest, angleAdv;
mat4 mViewInv;
int room;
float timer;
@@ -266,29 +267,32 @@ struct Camera : Controller {
pos = pos.lerp(destPos, Core::deltaTime * lerpFactor);
if (actCamera > -1) return;
if (actCamera <= -1) {
TR::Level::FloorInfo info;
level->getFloorInfo(room, (int)pos.x, (int)pos.z, info);
if (info.roomNext != 255)
room = info.roomNext;
if (pos.y < info.ceiling) {
if (info.roomAbove != 255)
room = info.roomAbove;
else
if (info.ceiling != 0xffff8100)
pos.y = info.ceiling;
}
TR::Level::FloorInfo info;
level->getFloorInfo(room, (int)pos.x, (int)pos.z, info);
if (info.roomNext != 255)
room = info.roomNext;
if (pos.y < info.ceiling) {
if (info.roomAbove != 255)
room = info.roomAbove;
else
if (info.ceiling != 0xffff8100)
pos.y = info.ceiling;
if (pos.y > info.floor) {
if (info.roomBelow != 255)
room = info.roomBelow;
else
if (info.floor != 0xffff8100)
pos.y = info.floor;
}
}
if (pos.y > info.floor) {
if (info.roomBelow != 255)
room = info.roomBelow;
else
if (info.floor != 0xffff8100)
pos.y = info.floor;
}
mViewInv = mat4(pos, target, vec3(0, -1, 0));
Sound::listener.matrix = mViewInv;
}
vec3 trace(int fromRoom, const vec3 &from, const vec3 &to) { // TODO: use Bresenham
@@ -352,7 +356,7 @@ struct Camera : Controller {
}
virtual void setup() {
Core::mViewInv = mat4(pos, target, vec3(0, -1, 0));
Core::mViewInv = mViewInv;
Core::mView = Core::mViewInv.inverse();
Core::mProj = mat4(fov, (float)Core::width / (float)Core::height, znear, zfar);
@@ -360,7 +364,7 @@ struct Camera : Controller {
Core::viewPos = Core::mViewInv.offset.xyz;
frustum->pos = Core::viewPos;
frustum->calcPlanes(Core::mViewProj);
frustum->calcPlanes(Core::mViewProj);
}
};

View File

@@ -166,7 +166,7 @@ struct Controller {
return b.floor - floor;
}
void playSound(int id) const {
void playSound(int id, const vec3 &pos, int flags) const {
// LOG("play sound %d\n", id);
int16 a = level->soundsMap[id];
@@ -175,7 +175,7 @@ struct Controller {
if (b.chance == 0 || (rand() & 0x7fff) <= b.chance) {
uint32 c = level->soundOffsets[b.offset + rand() % ((b.flags & 0xFF) >> 2)];
void *p = &level->soundData[c];
Sound::play(new Stream(p, 1024 * 1024), (float)b.volume / 0xFFFF, 0.0f, Sound::Flags::PAN);
Sound::play(new Stream(p, 1024 * 1024), pos, (float)b.volume / 0xFFFF, 0.0f, flags);
}
}
@@ -198,6 +198,27 @@ struct Controller {
angle.y = k * PI * 0.5f; // clamp angle to n*PI/2
}
virtual Box getBoundingBox() {
TR::Animation *anim = &level->anims[animIndex];
TR::Model &model = getModel();
float k = animTime * 30.0f / anim->frameRate;
int fIndex = (int)k;
int fCount = (anim->frameEnd - anim->frameStart) / anim->frameRate + 1;
int fSize = sizeof(TR::AnimFrame) + model.mCount * sizeof(uint16) * 2;
k = k - fIndex;
int fIndexA = fIndex % fCount, fIndexB = (fIndex + 1) % fCount;
TR::AnimFrame *fA = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + fIndexA * fSize) >> 1];
TR::AnimFrame *fB = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + fIndexB * fSize) >> 1];
Box box(fA->box.min().lerp(fB->box.min(), k), fA->box.max().lerp(fB->box.max(), k));
box.rotate90(getEntity().rotation.value / 0x4000);
box.min += pos;
box.max += pos;
return box;
}
void collide() {
TR::Entity &entity = getEntity();
@@ -255,7 +276,7 @@ struct Controller {
case TR::Action::SECRET :
if (!level->secrets[next->value]) {
level->secrets[next->value] = true;
playSound(TR::SND_SECRET);
playSound(TR::SND_SECRET, pos, 0);
}
actionCommand = next;
activateNext();
@@ -411,12 +432,12 @@ struct Controller {
if (cmd == TR::ANIM_CMD_EFFECT) {
switch (id) {
case TR::EFFECT_ROTATE_180 : angle.y = angle.y + PI; break;
case TR::EFFECT_LARA_BUBBLES : if (rand() % 10 > 6) playSound(TR::SND_BUBBLE); break;
case TR::EFFECT_LARA_BUBBLES : if (rand() % 10 > 6) playSound(TR::SND_BUBBLE, pos, Sound::Flags::PAN); break;
case TR::EFFECT_LARA_HANDSFREE : break;
default : LOG("unknown special cmd %d (anim %d)\n", id, animIndex);
}
} else
playSound(id);
playSound(id, pos, Sound::Flags::PAN);
}
break;
}

View File

@@ -295,7 +295,7 @@ namespace Debug {
void debugSectors(const TR::Level &level, const vec3 &pos, int roomIndex) {
TR::Room &room = level.rooms[roomIndex];
vec3 p = (pos - vec3(room.info.x, 0, room.info.z)) / vec3(1024, 1, 1024);
vec3 p = (pos - vec3(room.info.x, 0, room.info.z)) * vec3(1.0f / 1024.0f, 1, 1.0f / 1024.0f);
glDisable(GL_DEPTH_TEST);
for (int z = 0; z < room.zSectors; z++)
@@ -409,14 +409,15 @@ namespace Debug {
TR::StaticMesh *sm = level.getMeshByID(m.meshID);
ASSERT(sm != NULL);
vec3 min, max, offset = vec3(m.x, m.y, m.z);
sm->getBox(false, m.rotation, min, max); // visible box
Box box;
vec3 offset = vec3(m.x, m.y, m.z);
sm->getBox(false, m.rotation, box); // visible box
Debug::Draw::box(offset + min, offset + max, vec4(1, 1, 0, 0.25));
Debug::Draw::box(offset + box.min, offset + box.max, vec4(1, 1, 0, 0.25));
if (sm->flags == 2) { // collision box
sm->getBox(true, m.rotation, min, max);
Debug::Draw::box(offset + min - vec3(10.0f), offset + max + vec3(10.0f), vec4(1, 0, 0, 0.50));
sm->getBox(true, m.rotation, box);
Debug::Draw::box(offset + box.min - vec3(10.0f), offset + box.max + vec3(10.0f), vec4(1, 0, 0, 0.50));
}
/*
TR::Mesh *mesh = (TR::Mesh*)&level.meshData[level.meshOffsets[sm->mesh] / 2];

View File

@@ -75,6 +75,7 @@ namespace TR {
SND_NO = 2,
SND_LANDING = 4,
SND_BUBBLE = 37,
SND_DART = 151,
SND_SECRET = 173,
};
@@ -472,34 +473,17 @@ namespace TR {
MinMax cbox;
uint16 flags;
void getBox(bool collision, angle rotation, vec3 &min, vec3 &max) {
void getBox(bool collision, angle rotation, ::Box &box) {
int k = rotation.value / 0x4000;
MinMax &m = collision ? cbox : vbox;
ASSERT(m.minX <= m.maxX && m.minY <= m.maxY && m.minZ <= m.maxZ);
switch (k) {
case 0 :
min = vec3(m.minX, m.minY, m.minZ);
max = vec3(m.maxX, m.maxY, m.maxZ);
break;
case 1 :
min = vec3(m.minZ, m.minY, -m.maxX);
max = vec3(m.maxZ, m.maxY, -m.minX);
break;
case 2 :
min = vec3(-m.maxX, m.minY, -m.maxZ);
max = vec3(-m.minX, m.maxY, -m.minZ);
break;
case 3 :
min = vec3(-m.maxZ, m.minY, m.minX);
max = vec3(-m.minZ, m.maxY, m.maxX);
break;
default :
ASSERT(false);
}
ASSERT(min.x <= max.x && min.y <= max.y && min.z <= max.z);
box = ::Box(m.min(), m.max());
box.rotate90(k);
ASSERT(box.min.x <= box.max.x && box.min.y <= box.max.y && box.min.z <= box.max.z);
}
};

View File

@@ -17,7 +17,7 @@ namespace Game {
#ifndef __EMSCRIPTEN__
//Sound::play(Sound::openWAD("05_Lara's_Themes.wav"), 1, 1, 0);
Sound::play(new Stream("05.ogg"), 1, 1, 0);
Sound::play(new Stream("05.ogg"), vec3(0.0f), 1, 1, Sound::Flags::LOOP);
//Sound::play(new Stream("03.mp3"), 1, 1, 0);
#endif
}

View File

@@ -144,11 +144,16 @@ struct Lara : Controller {
angle = vec3(0.0f, PI * 0.5f, 0.0f);
getEntity().room = 12;
*/
/*
// level 2 (pool)
pos = vec3(70067, -256, 29104);
angle = vec3(0.0f, -0.68f, 0.0f);
getEntity().room = 15;
*/
// level 2 (blade)
pos = vec3(27221, -1024, 29205);
angle = vec3(0.0f, PI * 0.5f, 0.0f);
getEntity().room = 61;
/*
// level 2 (wolf)
@@ -323,7 +328,7 @@ struct Lara : Controller {
for (int i = 0; i < info.trigCmdCount; i++) {
if (!controller) {
LOG("! next activation entity %d has no controller\n", level->entities[info.trigCmd[i].args].type);
playSound(TR::SND_NO);
playSound(TR::SND_NO, pos, 0);
return;
}
@@ -420,7 +425,7 @@ struct Lara : Controller {
if (stand == STAND_SLIDE || (stand == STAND_AIR && velocity.y > 0) || stand == STAND_GROUND) {
if (e.y + 8 >= info.floor && (abs(info.slantX) > 2 || abs(info.slantZ) > 2)) {
if (stand == STAND_AIR)
playSound(TR::SND_LANDING);
playSound(TR::SND_LANDING, pos, Sound::Flags::PAN);
pos.y = info.floor;
updateEntity();
@@ -928,8 +933,9 @@ struct Lara : Controller {
vec3 p = pos;
pos = pos + offset;
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
level->getFloorInfo(getEntity().room, (int)pos.x, (int)pos.z, info, true);
level->getFloorInfo(e.room, (int)pos.x, (int)pos.z, info, true);
// get frame to get height
TR::Animation *anim = &level->anims[animIndex];
@@ -937,7 +943,40 @@ struct Lara : Controller {
bool canPassGap = (info.floor - info.ceiling) >= (stand == STAND_GROUND ? 768 : 512);
float f = info.floor - pos.y;
float c = pos.y - info.ceiling;
/*
Box eBox = Box(pos - vec3(128.0f, 0.0f, 128.0f), pos + vec3(128.0, getHeight(), 128.0f)); // getBoundingBox();
// check static meshes in the room
if (canPassGap) {
TR::Room &r = level->rooms[e.room];
for (int i = 0; i < r.meshesCount; i++) {
TR::Room::Mesh &m = r.meshes[i];
TR::StaticMesh *sm = level->getMeshByID(m.meshID);
if (sm->flags != 2) continue; // no have collision box
Box mBox;
vec3 offset(m.x, m.y, m.z);
sm->getBox(true, m.rotation, mBox);
mBox.min += offset;
mBox.max += offset;
if (eBox.intersect(mBox)) {
canPassGap = false;
break;
}
}
}
// check entities in the room
if (canPassGap)
for (int i = 0; i < level->entitiesCount; i++)
if (i != entity && level->entities[i].room == e.room && level->entities[i].controller) {
Box mBox = ((Controller*)level->entities[i].controller)->getBoundingBox();
if (eBox.intersect(mBox)) {
canPassGap = false;
break;
}
}
*/
if (canPassGap)
switch (stand) {
case STAND_AIR : {

View File

@@ -238,10 +238,11 @@ struct Level {
ASSERT(sMesh != NULL);
// check visibility
vec3 min, max, offset = vec3(rMesh.x, rMesh.y, rMesh.z);
sMesh->getBox(false, rMesh.rotation, min, max);
if (!camera->frustum->isVisible(offset + min, offset + max))
continue;
Box box;
vec3 offset = vec3(rMesh.x, rMesh.y, rMesh.z);
sMesh->getBox(false, rMesh.rotation, box);
if (!camera->frustum->isVisible(offset + box.min, offset + box.max))
continue;
rMesh.flags.rendered = true;
// set light parameters
@@ -362,7 +363,7 @@ struct Level {
m.translate(pos);
m.rotateY(angle);
m.translate(vec3(offset.x, 0.0f, offset.z));
m.scale(vec3(size.x, 0.0f, size.z) / 1024.0f);
m.scale(vec3(size.x, 0.0f, size.z) * (1.0f / 1024.0f));
Core::active.shader->setParam(uModel, m);
Core::active.shader->setParam(uColor, vec4(0.0f, 0.0f, 0.0f, 0.5f));

View File

@@ -20,6 +20,7 @@
#endif
#define SND_CHANNELS_MAX 32
#define SND_FADEOFF_DIST (1024.0f * 5.0f)
namespace Sound {
@@ -216,7 +217,7 @@ namespace Sound {
struct Listener {
mat4 matrix;
vec3 velocity;
// vec3 velocity;
} listener;
enum Flags {
@@ -229,12 +230,14 @@ namespace Sound {
struct Sample {
Decoder *decoder;
vec3 pos;
vec3 velocity;
float volume;
float pitch;
int flags;
bool isPlaying;
Sample(Stream *stream, float volume, float pitch, int flags) : decoder(NULL), volume(volume), pitch(pitch), flags(flags) {
Sample(Stream *stream, const vec3 &pos, float volume, float pitch, int flags) : decoder(NULL), pos(pos), volume(volume), pitch(pitch), flags(flags) {
uint32 fourcc;
stream->read(fourcc);
if (fourcc == FOURCC("RIFF")) { // wav
@@ -286,6 +289,21 @@ namespace Sound {
delete decoder;
}
vec3 getPan() {
if (!(flags & PAN))
return vec3(1.0f);
mat4 m = Sound::listener.matrix;
vec3 v = pos - m.offset.xyz;
float dist = max(0.0f, 1.0f - (v.length2() / (SND_FADEOFF_DIST * SND_FADEOFF_DIST)));
float pan = m.right.xyz.dot(v.normal());
float l = min(1.0f, 1.0f - pan);
float r = min(1.0f, 1.0f + pan);
return vec3(l, r, 1.0f) * dist;
}
bool render(Frame *frames, int count) {
if (!isPlaying) return 0;
int i = 0;
@@ -298,14 +316,15 @@ namespace Sound {
i += res;
}
if (volume != 1.0f)
vec3 pan = getPan() * volume;
if (pan.x < 1.0f || pan.y < 1.0f)
for (int j = 0; j < i; j++) {
frames[j].L = int(frames[j].L * volume);
frames[j].R = int(frames[j].R * volume);
frames[j].L = int(frames[j].L * pan.x);
frames[j].R = int(frames[j].R * pan.y);
}
return true;
}
} *channels[SND_CHANNELS_MAX];
int channelsCount;
@@ -338,7 +357,7 @@ namespace Sound {
memset(buffer, 0, sizeof(Frame) * count);
channels[i]->render(buffer, count);
for (int j = 0; j < count; j++) {
result[j].L += buffer[j].L;
result[j].R += buffer[j].R;
@@ -382,14 +401,15 @@ namespace Sound {
return NULL;
}
void play(Stream *stream, float volume, float pitch, int flags) {
if (!stream) return;
Sample* play(Stream *stream, const vec3 &pos, float volume, float pitch, int flags) {
if (!stream) return NULL;
if (channelsCount < SND_CHANNELS_MAX)
channels[channelsCount++] = new Sample(stream, volume, pitch, flags);
else {
LOG("! no free channels\n");
delete stream;
}
return channels[channelsCount++] = new Sample(stream, pos, volume, pitch, flags);
LOG("! no free channels\n");
delete stream;
return NULL;
}
}

View File

@@ -107,8 +107,8 @@ struct Dartgun : Trigger {
level->entities[dartIndex].controller = new Dart(level, dartIndex);
addSprite(level, TR::Entity::SMOKE, entity.room, (int)pos.x, (int)pos.y, (int)pos.z);
playSound(151);
playSound(TR::SND_DART, pos, Sound::Flags::PAN);
return true;
}

View File

@@ -76,12 +76,12 @@ inline const int sign(const T &x) {
}
float clampAngle(float a) {
return a < -PI ? a + PI2 : (a >= PI ? a - PI2 : a);
return a < -PI ? a + PI2 : (a >= PI ? a - PI2 : a);
}
float shortAngle(float a, float b) {
float n = clampAngle(b) - clampAngle(a);
return clampAngle(n - int(n / PI2) * PI2);
float n = clampAngle(b) - clampAngle(a);
return clampAngle(n - int(n / PI2) * PI2);
}
@@ -111,10 +111,14 @@ struct vec3 {
float& operator [] (int index) const { return ((float*)this)[index]; }
vec3& operator += (const vec3 &v) { x += v.x; y += v.y; z += v.z; return *this; }
vec3& operator -= (const vec3 &v) { x -= v.x; y -= v.y; z -= v.z; return *this; }
vec3& operator *= (const vec3 &v) { x *= v.x; y *= v.y; z *= v.z; return *this; }
vec3& operator *= (float s) { x *= s; y *= s; z *= s; return *this; }
vec3 operator + (const vec3 &v) const { return vec3(x+v.x, y+v.y, z+v.z); }
vec3 operator - (const vec3 &v) const { return vec3(x-v.x, y-v.y, z-v.z); }
vec3 operator * (const vec3 &v) const { return vec3(x*v.x, y*v.y, z*v.z); }
vec3 operator / (const vec3 &v) const { return vec3(x/v.x, y/v.y, z/v.z); }
vec3 operator * (float s) const { return vec3(x*s, y*s, z*s); }
float dot(const vec3 &v) const { return x*v.x + y*v.y + z*v.z; }
@@ -484,12 +488,33 @@ struct mat4 {
}
};
struct Box {
vec3 min, max;
Box() {}
Box(const vec3 &min, const vec3 &max) : min(min), max(max) {}
void rotate90(int n) {
switch (n) {
case 0 : break;
case 1 : *this = Box(vec3( min.z, min.y, -max.x), vec3( max.z, max.y, -min.x)); break;
case 2 : *this = Box(vec3(-max.x, min.y, -max.z), vec3(-min.x, max.y, -min.z)); break;
case 3 : *this = Box(vec3(-max.z, min.y, min.x), vec3(-min.z, max.y, max.x)); break;
default : ASSERT(false);
}
}
bool intersect(const Box &box) const {
return !((max.x < box.min.x || min.x > box.max.x) || (max.y < box.min.y || min.y > box.max.y) || (max.z < box.min.z || min.z > box.max.z));
}
};
struct Stream {
FILE *f;
const char *data;
int size, pos;
const char *data;
int size, pos;
Stream(const void *data, int size) : f(NULL), data((char*)data), size(size), pos(0) {}
Stream(const void *data, int size) : f(NULL), data((char*)data), size(size), pos(0) {}
Stream(const char *name) : data(NULL), size(-1), pos(0) {
f = fopen(name, "rb");
@@ -500,12 +525,12 @@ struct Stream {
}
~Stream() {
if (f) fclose(f);
if (f) fclose(f);
}
void setPos(int pos) {
this->pos = pos;
if (f) fseek(f, pos, SEEK_SET);
if (f) fseek(f, pos, SEEK_SET);
}
void seek(int offset) {
@@ -515,11 +540,11 @@ struct Stream {
}
void raw(void *data, int count) {
if (f)
fread(data, 1, count, f);
else
memcpy(data, this->data + pos, count);
pos += count;
if (f)
fread(data, 1, count, f);
else
memcpy(data, this->data + pos, count);
pos += count;
}
template <typename T>