mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-08 06:06:51 +02:00
#9 fading & panning of sound sources
This commit is contained in:
BIN
bin/OpenLara.exe
BIN
bin/OpenLara.exe
Binary file not shown.
48
src/camera.h
48
src/camera.h
@@ -160,6 +160,7 @@ struct Camera : Controller {
|
|||||||
|
|
||||||
float fov, znear, zfar;
|
float fov, znear, zfar;
|
||||||
vec3 target, destPos, lastDest, angleAdv;
|
vec3 target, destPos, lastDest, angleAdv;
|
||||||
|
mat4 mViewInv;
|
||||||
int room;
|
int room;
|
||||||
|
|
||||||
float timer;
|
float timer;
|
||||||
@@ -266,29 +267,32 @@ struct Camera : Controller {
|
|||||||
|
|
||||||
pos = pos.lerp(destPos, Core::deltaTime * lerpFactor);
|
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;
|
if (pos.y > info.floor) {
|
||||||
level->getFloorInfo(room, (int)pos.x, (int)pos.z, info);
|
if (info.roomBelow != 255)
|
||||||
|
room = info.roomBelow;
|
||||||
if (info.roomNext != 255)
|
else
|
||||||
room = info.roomNext;
|
if (info.floor != 0xffff8100)
|
||||||
|
pos.y = info.floor;
|
||||||
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) {
|
mViewInv = mat4(pos, target, vec3(0, -1, 0));
|
||||||
if (info.roomBelow != 255)
|
Sound::listener.matrix = mViewInv;
|
||||||
room = info.roomBelow;
|
|
||||||
else
|
|
||||||
if (info.floor != 0xffff8100)
|
|
||||||
pos.y = info.floor;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 trace(int fromRoom, const vec3 &from, const vec3 &to) { // TODO: use Bresenham
|
vec3 trace(int fromRoom, const vec3 &from, const vec3 &to) { // TODO: use Bresenham
|
||||||
@@ -352,7 +356,7 @@ struct Camera : Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void setup() {
|
virtual void setup() {
|
||||||
Core::mViewInv = mat4(pos, target, vec3(0, -1, 0));
|
Core::mViewInv = mViewInv;
|
||||||
Core::mView = Core::mViewInv.inverse();
|
Core::mView = Core::mViewInv.inverse();
|
||||||
Core::mProj = mat4(fov, (float)Core::width / (float)Core::height, znear, zfar);
|
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;
|
Core::viewPos = Core::mViewInv.offset.xyz;
|
||||||
|
|
||||||
frustum->pos = Core::viewPos;
|
frustum->pos = Core::viewPos;
|
||||||
frustum->calcPlanes(Core::mViewProj);
|
frustum->calcPlanes(Core::mViewProj);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -166,7 +166,7 @@ struct Controller {
|
|||||||
return b.floor - floor;
|
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);
|
// LOG("play sound %d\n", id);
|
||||||
|
|
||||||
int16 a = level->soundsMap[id];
|
int16 a = level->soundsMap[id];
|
||||||
@@ -175,7 +175,7 @@ struct Controller {
|
|||||||
if (b.chance == 0 || (rand() & 0x7fff) <= b.chance) {
|
if (b.chance == 0 || (rand() & 0x7fff) <= b.chance) {
|
||||||
uint32 c = level->soundOffsets[b.offset + rand() % ((b.flags & 0xFF) >> 2)];
|
uint32 c = level->soundOffsets[b.offset + rand() % ((b.flags & 0xFF) >> 2)];
|
||||||
void *p = &level->soundData[c];
|
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
|
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() {
|
void collide() {
|
||||||
TR::Entity &entity = getEntity();
|
TR::Entity &entity = getEntity();
|
||||||
|
|
||||||
@@ -255,7 +276,7 @@ struct Controller {
|
|||||||
case TR::Action::SECRET :
|
case TR::Action::SECRET :
|
||||||
if (!level->secrets[next->value]) {
|
if (!level->secrets[next->value]) {
|
||||||
level->secrets[next->value] = true;
|
level->secrets[next->value] = true;
|
||||||
playSound(TR::SND_SECRET);
|
playSound(TR::SND_SECRET, pos, 0);
|
||||||
}
|
}
|
||||||
actionCommand = next;
|
actionCommand = next;
|
||||||
activateNext();
|
activateNext();
|
||||||
@@ -411,12 +432,12 @@ struct Controller {
|
|||||||
if (cmd == TR::ANIM_CMD_EFFECT) {
|
if (cmd == TR::ANIM_CMD_EFFECT) {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case TR::EFFECT_ROTATE_180 : angle.y = angle.y + PI; break;
|
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;
|
case TR::EFFECT_LARA_HANDSFREE : break;
|
||||||
default : LOG("unknown special cmd %d (anim %d)\n", id, animIndex);
|
default : LOG("unknown special cmd %d (anim %d)\n", id, animIndex);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
playSound(id);
|
playSound(id, pos, Sound::Flags::PAN);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
13
src/debug.h
13
src/debug.h
@@ -295,7 +295,7 @@ namespace Debug {
|
|||||||
void debugSectors(const TR::Level &level, const vec3 &pos, int roomIndex) {
|
void debugSectors(const TR::Level &level, const vec3 &pos, int roomIndex) {
|
||||||
TR::Room &room = level.rooms[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);
|
glDisable(GL_DEPTH_TEST);
|
||||||
for (int z = 0; z < room.zSectors; z++)
|
for (int z = 0; z < room.zSectors; z++)
|
||||||
@@ -409,14 +409,15 @@ namespace Debug {
|
|||||||
TR::StaticMesh *sm = level.getMeshByID(m.meshID);
|
TR::StaticMesh *sm = level.getMeshByID(m.meshID);
|
||||||
ASSERT(sm != NULL);
|
ASSERT(sm != NULL);
|
||||||
|
|
||||||
vec3 min, max, offset = vec3(m.x, m.y, m.z);
|
Box box;
|
||||||
sm->getBox(false, m.rotation, min, max); // visible 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
|
if (sm->flags == 2) { // collision box
|
||||||
sm->getBox(true, m.rotation, min, max);
|
sm->getBox(true, m.rotation, box);
|
||||||
Debug::Draw::box(offset + min - vec3(10.0f), offset + max + vec3(10.0f), vec4(1, 0, 0, 0.50));
|
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];
|
TR::Mesh *mesh = (TR::Mesh*)&level.meshData[level.meshOffsets[sm->mesh] / 2];
|
||||||
|
28
src/format.h
28
src/format.h
@@ -75,6 +75,7 @@ namespace TR {
|
|||||||
SND_NO = 2,
|
SND_NO = 2,
|
||||||
SND_LANDING = 4,
|
SND_LANDING = 4,
|
||||||
SND_BUBBLE = 37,
|
SND_BUBBLE = 37,
|
||||||
|
SND_DART = 151,
|
||||||
SND_SECRET = 173,
|
SND_SECRET = 173,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -472,34 +473,17 @@ namespace TR {
|
|||||||
MinMax cbox;
|
MinMax cbox;
|
||||||
uint16 flags;
|
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;
|
int k = rotation.value / 0x4000;
|
||||||
|
|
||||||
MinMax &m = collision ? cbox : vbox;
|
MinMax &m = collision ? cbox : vbox;
|
||||||
|
|
||||||
ASSERT(m.minX <= m.maxX && m.minY <= m.maxY && m.minZ <= m.maxZ);
|
ASSERT(m.minX <= m.maxX && m.minY <= m.maxY && m.minZ <= m.maxZ);
|
||||||
|
|
||||||
switch (k) {
|
box = ::Box(m.min(), m.max());
|
||||||
case 0 :
|
box.rotate90(k);
|
||||||
min = vec3(m.minX, m.minY, m.minZ);
|
|
||||||
max = vec3(m.maxX, m.maxY, m.maxZ);
|
ASSERT(box.min.x <= box.max.x && box.min.y <= box.max.y && box.min.z <= box.max.z);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@ namespace Game {
|
|||||||
|
|
||||||
#ifndef __EMSCRIPTEN__
|
#ifndef __EMSCRIPTEN__
|
||||||
//Sound::play(Sound::openWAD("05_Lara's_Themes.wav"), 1, 1, 0);
|
//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);
|
//Sound::play(new Stream("03.mp3"), 1, 1, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
47
src/lara.h
47
src/lara.h
@@ -144,11 +144,16 @@ struct Lara : Controller {
|
|||||||
angle = vec3(0.0f, PI * 0.5f, 0.0f);
|
angle = vec3(0.0f, PI * 0.5f, 0.0f);
|
||||||
getEntity().room = 12;
|
getEntity().room = 12;
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
// level 2 (pool)
|
// level 2 (pool)
|
||||||
pos = vec3(70067, -256, 29104);
|
pos = vec3(70067, -256, 29104);
|
||||||
angle = vec3(0.0f, -0.68f, 0.0f);
|
angle = vec3(0.0f, -0.68f, 0.0f);
|
||||||
getEntity().room = 15;
|
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)
|
// level 2 (wolf)
|
||||||
@@ -323,7 +328,7 @@ struct Lara : Controller {
|
|||||||
for (int i = 0; i < info.trigCmdCount; i++) {
|
for (int i = 0; i < info.trigCmdCount; i++) {
|
||||||
if (!controller) {
|
if (!controller) {
|
||||||
LOG("! next activation entity %d has no controller\n", level->entities[info.trigCmd[i].args].type);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,7 +425,7 @@ struct Lara : Controller {
|
|||||||
if (stand == STAND_SLIDE || (stand == STAND_AIR && velocity.y > 0) || stand == STAND_GROUND) {
|
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 (e.y + 8 >= info.floor && (abs(info.slantX) > 2 || abs(info.slantZ) > 2)) {
|
||||||
if (stand == STAND_AIR)
|
if (stand == STAND_AIR)
|
||||||
playSound(TR::SND_LANDING);
|
playSound(TR::SND_LANDING, pos, Sound::Flags::PAN);
|
||||||
pos.y = info.floor;
|
pos.y = info.floor;
|
||||||
updateEntity();
|
updateEntity();
|
||||||
|
|
||||||
@@ -928,8 +933,9 @@ struct Lara : Controller {
|
|||||||
vec3 p = pos;
|
vec3 p = pos;
|
||||||
pos = pos + offset;
|
pos = pos + offset;
|
||||||
|
|
||||||
|
TR::Entity &e = getEntity();
|
||||||
TR::Level::FloorInfo info;
|
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
|
// get frame to get height
|
||||||
TR::Animation *anim = &level->anims[animIndex];
|
TR::Animation *anim = &level->anims[animIndex];
|
||||||
@@ -937,7 +943,40 @@ struct Lara : Controller {
|
|||||||
bool canPassGap = (info.floor - info.ceiling) >= (stand == STAND_GROUND ? 768 : 512);
|
bool canPassGap = (info.floor - info.ceiling) >= (stand == STAND_GROUND ? 768 : 512);
|
||||||
float f = info.floor - pos.y;
|
float f = info.floor - pos.y;
|
||||||
float c = pos.y - info.ceiling;
|
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)
|
if (canPassGap)
|
||||||
switch (stand) {
|
switch (stand) {
|
||||||
case STAND_AIR : {
|
case STAND_AIR : {
|
||||||
|
11
src/level.h
11
src/level.h
@@ -238,10 +238,11 @@ struct Level {
|
|||||||
ASSERT(sMesh != NULL);
|
ASSERT(sMesh != NULL);
|
||||||
|
|
||||||
// check visibility
|
// check visibility
|
||||||
vec3 min, max, offset = vec3(rMesh.x, rMesh.y, rMesh.z);
|
Box box;
|
||||||
sMesh->getBox(false, rMesh.rotation, min, max);
|
vec3 offset = vec3(rMesh.x, rMesh.y, rMesh.z);
|
||||||
if (!camera->frustum->isVisible(offset + min, offset + max))
|
sMesh->getBox(false, rMesh.rotation, box);
|
||||||
continue;
|
if (!camera->frustum->isVisible(offset + box.min, offset + box.max))
|
||||||
|
continue;
|
||||||
rMesh.flags.rendered = true;
|
rMesh.flags.rendered = true;
|
||||||
|
|
||||||
// set light parameters
|
// set light parameters
|
||||||
@@ -362,7 +363,7 @@ struct Level {
|
|||||||
m.translate(pos);
|
m.translate(pos);
|
||||||
m.rotateY(angle);
|
m.rotateY(angle);
|
||||||
m.translate(vec3(offset.x, 0.0f, offset.z));
|
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(uModel, m);
|
||||||
Core::active.shader->setParam(uColor, vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
Core::active.shader->setParam(uColor, vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||||
|
48
src/sound.h
48
src/sound.h
@@ -20,6 +20,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SND_CHANNELS_MAX 32
|
#define SND_CHANNELS_MAX 32
|
||||||
|
#define SND_FADEOFF_DIST (1024.0f * 5.0f)
|
||||||
|
|
||||||
namespace Sound {
|
namespace Sound {
|
||||||
|
|
||||||
@@ -216,7 +217,7 @@ namespace Sound {
|
|||||||
|
|
||||||
struct Listener {
|
struct Listener {
|
||||||
mat4 matrix;
|
mat4 matrix;
|
||||||
vec3 velocity;
|
// vec3 velocity;
|
||||||
} listener;
|
} listener;
|
||||||
|
|
||||||
enum Flags {
|
enum Flags {
|
||||||
@@ -229,12 +230,14 @@ namespace Sound {
|
|||||||
|
|
||||||
struct Sample {
|
struct Sample {
|
||||||
Decoder *decoder;
|
Decoder *decoder;
|
||||||
|
vec3 pos;
|
||||||
|
vec3 velocity;
|
||||||
float volume;
|
float volume;
|
||||||
float pitch;
|
float pitch;
|
||||||
int flags;
|
int flags;
|
||||||
bool isPlaying;
|
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;
|
uint32 fourcc;
|
||||||
stream->read(fourcc);
|
stream->read(fourcc);
|
||||||
if (fourcc == FOURCC("RIFF")) { // wav
|
if (fourcc == FOURCC("RIFF")) { // wav
|
||||||
@@ -286,6 +289,21 @@ namespace Sound {
|
|||||||
delete decoder;
|
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) {
|
bool render(Frame *frames, int count) {
|
||||||
if (!isPlaying) return 0;
|
if (!isPlaying) return 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -298,14 +316,15 @@ namespace Sound {
|
|||||||
i += res;
|
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++) {
|
for (int j = 0; j < i; j++) {
|
||||||
frames[j].L = int(frames[j].L * volume);
|
frames[j].L = int(frames[j].L * pan.x);
|
||||||
frames[j].R = int(frames[j].R * volume);
|
frames[j].R = int(frames[j].R * pan.y);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} *channels[SND_CHANNELS_MAX];
|
} *channels[SND_CHANNELS_MAX];
|
||||||
int channelsCount;
|
int channelsCount;
|
||||||
|
|
||||||
@@ -338,7 +357,7 @@ namespace Sound {
|
|||||||
|
|
||||||
memset(buffer, 0, sizeof(Frame) * count);
|
memset(buffer, 0, sizeof(Frame) * count);
|
||||||
channels[i]->render(buffer, count);
|
channels[i]->render(buffer, count);
|
||||||
|
|
||||||
for (int j = 0; j < count; j++) {
|
for (int j = 0; j < count; j++) {
|
||||||
result[j].L += buffer[j].L;
|
result[j].L += buffer[j].L;
|
||||||
result[j].R += buffer[j].R;
|
result[j].R += buffer[j].R;
|
||||||
@@ -382,14 +401,15 @@ namespace Sound {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void play(Stream *stream, float volume, float pitch, int flags) {
|
Sample* play(Stream *stream, const vec3 &pos, float volume, float pitch, int flags) {
|
||||||
if (!stream) return;
|
if (!stream) return NULL;
|
||||||
|
|
||||||
if (channelsCount < SND_CHANNELS_MAX)
|
if (channelsCount < SND_CHANNELS_MAX)
|
||||||
channels[channelsCount++] = new Sample(stream, volume, pitch, flags);
|
return channels[channelsCount++] = new Sample(stream, pos, volume, pitch, flags);
|
||||||
else {
|
|
||||||
LOG("! no free channels\n");
|
LOG("! no free channels\n");
|
||||||
delete stream;
|
delete stream;
|
||||||
}
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -107,8 +107,8 @@ struct Dartgun : Trigger {
|
|||||||
level->entities[dartIndex].controller = new Dart(level, dartIndex);
|
level->entities[dartIndex].controller = new Dart(level, dartIndex);
|
||||||
|
|
||||||
addSprite(level, TR::Entity::SMOKE, entity.room, (int)pos.x, (int)pos.y, (int)pos.z);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
53
src/utils.h
53
src/utils.h
@@ -76,12 +76,12 @@ inline const int sign(const T &x) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float clampAngle(float a) {
|
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 shortAngle(float a, float b) {
|
||||||
float n = clampAngle(b) - clampAngle(a);
|
float n = clampAngle(b) - clampAngle(a);
|
||||||
return clampAngle(n - int(n / PI2) * PI2);
|
return clampAngle(n - int(n / PI2) * PI2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -111,10 +111,14 @@ struct vec3 {
|
|||||||
|
|
||||||
float& operator [] (int index) const { return ((float*)this)[index]; }
|
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 * (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); }
|
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; }
|
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 {
|
struct Stream {
|
||||||
FILE *f;
|
FILE *f;
|
||||||
const char *data;
|
const char *data;
|
||||||
int size, pos;
|
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) {
|
Stream(const char *name) : data(NULL), size(-1), pos(0) {
|
||||||
f = fopen(name, "rb");
|
f = fopen(name, "rb");
|
||||||
@@ -500,12 +525,12 @@ struct Stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
~Stream() {
|
~Stream() {
|
||||||
if (f) fclose(f);
|
if (f) fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPos(int pos) {
|
void setPos(int pos) {
|
||||||
this->pos = pos;
|
this->pos = pos;
|
||||||
if (f) fseek(f, pos, SEEK_SET);
|
if (f) fseek(f, pos, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
void seek(int offset) {
|
void seek(int offset) {
|
||||||
@@ -515,11 +540,11 @@ struct Stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void raw(void *data, int count) {
|
void raw(void *data, int count) {
|
||||||
if (f)
|
if (f)
|
||||||
fread(data, 1, count, f);
|
fread(data, 1, count, f);
|
||||||
else
|
else
|
||||||
memcpy(data, this->data + pos, count);
|
memcpy(data, this->data + pos, count);
|
||||||
pos += count;
|
pos += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
Reference in New Issue
Block a user