From df942b2893ed8e5be1bbe024fb23b3c99682a931 Mon Sep 17 00:00:00 2001 From: XProger Date: Fri, 21 Oct 2016 00:58:15 +0300 Subject: [PATCH] #9 fading & panning of sound sources --- bin/OpenLara.exe | Bin 89600 -> 91136 bytes src/camera.h | 48 ++++++++++++++++++++++-------------------- src/controller.h | 31 ++++++++++++++++++++++----- src/debug.h | 13 ++++++------ src/format.h | 28 ++++++------------------- src/game.h | 2 +- src/lara.h | 47 +++++++++++++++++++++++++++++++++++++---- src/level.h | 11 +++++----- src/sound.h | 48 +++++++++++++++++++++++++++++------------- src/trigger.h | 4 ++-- src/utils.h | 53 ++++++++++++++++++++++++++++++++++------------- 11 files changed, 190 insertions(+), 95 deletions(-) diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index 6b08a4ed61feba9d8e0f5f80e456753e83f66a58..e32bf616965c2be77c64aa95bb538a189d01d503 100644 GIT binary patch delta 18589 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 delta 16999 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 diff --git a/src/camera.h b/src/camera.h index e3fcd0e..0e5f114 100644 --- a/src/camera.h +++ b/src/camera.h @@ -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); } }; diff --git a/src/controller.h b/src/controller.h index c218f8b..1a808a6 100644 --- a/src/controller.h +++ b/src/controller.h @@ -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; } diff --git a/src/debug.h b/src/debug.h index 73fa0e5..bf86d05 100644 --- a/src/debug.h +++ b/src/debug.h @@ -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]; diff --git a/src/format.h b/src/format.h index 023f3cb..4bfa154 100644 --- a/src/format.h +++ b/src/format.h @@ -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); } }; diff --git a/src/game.h b/src/game.h index 64bc0c1..8dc0bd4 100644 --- a/src/game.h +++ b/src/game.h @@ -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 } diff --git a/src/lara.h b/src/lara.h index e582005..ba00cb6 100644 --- a/src/lara.h +++ b/src/lara.h @@ -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 : { diff --git a/src/level.h b/src/level.h index 3001fc3..f726513 100644 --- a/src/level.h +++ b/src/level.h @@ -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)); diff --git a/src/sound.h b/src/sound.h index 071915f..04b4b3c 100644 --- a/src/sound.h +++ b/src/sound.h @@ -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; } } diff --git a/src/trigger.h b/src/trigger.h index 78a0f2b..e8581da 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -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; } diff --git a/src/utils.h b/src/utils.h index f45f857..929fb71 100644 --- a/src/utils.h +++ b/src/utils.h @@ -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