Implement footstep sounds
This commit is contained in:
parent
7a77cabf4c
commit
e3d49fd3b2
19 changed files with 268 additions and 21 deletions
|
@ -289,6 +289,7 @@ set_target_properties(libvideo PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINAR
|
|||
## libscene static library
|
||||
|
||||
set(SCENE_HEADERS
|
||||
src/scene/animation/eventlistener.h
|
||||
src/scene/animation/channel.h
|
||||
src/scene/animation/properties.h
|
||||
src/scene/animation/scenenodeanimator.h
|
||||
|
@ -429,6 +430,7 @@ set(GAME_HEADERS
|
|||
src/game/enginetype/event.h
|
||||
src/game/enginetype/location.h
|
||||
src/game/enginetype/talent.h
|
||||
src/game/footstepsounds.h
|
||||
src/game/game.h
|
||||
src/game/gameidutil.h
|
||||
src/game/gui/barkbubble.h
|
||||
|
@ -535,6 +537,7 @@ set(GAME_SOURCES
|
|||
src/game/d20/classes.cpp
|
||||
src/game/d20/skills.cpp
|
||||
src/game/dialog.cpp
|
||||
src/game/footstepsounds.cpp
|
||||
src/game/game.cpp
|
||||
src/game/game_kotor.cpp
|
||||
src/game/game_save.cpp
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include <functional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "glm/gtx/norm.hpp"
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "../common/log.h"
|
||||
|
@ -34,6 +36,9 @@ namespace reone {
|
|||
|
||||
namespace audio {
|
||||
|
||||
static constexpr float kMaxPositionalSoundDistance = 16.0f;
|
||||
static constexpr float kMaxPositionalSoundDistance2 = kMaxPositionalSoundDistance * kMaxPositionalSoundDistance;
|
||||
|
||||
AudioPlayer &AudioPlayer::instance() {
|
||||
static AudioPlayer instance;
|
||||
return instance;
|
||||
|
@ -155,8 +160,11 @@ void AudioPlayer::enqueue(const shared_ptr<SoundInstance> &sound) {
|
|||
}
|
||||
|
||||
shared_ptr<SoundHandle> AudioPlayer::play(const shared_ptr<AudioStream> &stream, AudioType type, bool loop, float gain, bool positional, glm::vec3 position) {
|
||||
if (positional && glm::distance2(_listenerPosition.load(), position) > kMaxPositionalSoundDistance2) return nullptr;
|
||||
|
||||
auto sound = make_shared<SoundInstance>(stream, loop, getGain(type, gain), positional, move(position));
|
||||
enqueue(sound);
|
||||
|
||||
return sound->handle();
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
namespace reone {
|
||||
|
||||
/**
|
||||
* Utility class for caching objects. Requires a function which computes an object by key.
|
||||
* Utility class for caching objects. Takes a function which computes an object by key.
|
||||
*/
|
||||
template <class K, class V>
|
||||
class MemoryCache : boost::noncopyable {
|
||||
|
|
|
@ -63,12 +63,14 @@ bool CollisionDetector::rayTestRooms(const RaycastProperties &props, RaycastResu
|
|||
shared_ptr<Walkmesh> walkmesh(room.second->walkmesh());
|
||||
if (!walkmesh) continue;
|
||||
|
||||
if (walkmesh->raycast(props.origin, props.direction, props.flags & RaycastFlags::walkable, distance) &&
|
||||
int material = 0;
|
||||
if (walkmesh->raycast(props.origin, props.direction, props.flags & RaycastFlags::walkable, distance, material) &&
|
||||
distance <= props.distance) {
|
||||
|
||||
result.room = room.second.get();
|
||||
result.intersection = props.origin + distance * props.direction;
|
||||
result.distance = distance;
|
||||
result.material = material;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +113,9 @@ bool CollisionDetector::rayTestObjects(const RaycastProperties &props, RaycastRe
|
|||
} else {
|
||||
// Test using a walkmesh
|
||||
shared_ptr<Walkmesh> walkmesh(object->getWalkmesh());
|
||||
if (walkmesh && walkmesh->raycast(objSpaceOrigin, objSpaceDir, props.flags & RaycastFlags::walkable, distance) &&
|
||||
int material = 0;
|
||||
|
||||
if (walkmesh && walkmesh->raycast(objSpaceOrigin, objSpaceDir, props.flags & RaycastFlags::walkable, distance, material) &&
|
||||
distance <= props.distance) {
|
||||
|
||||
collisions.push_back(make_pair(object, distance));
|
||||
|
|
|
@ -56,6 +56,7 @@ struct RaycastResult {
|
|||
std::shared_ptr<SpatialObject> object;
|
||||
glm::vec3 intersection { 0.0f };
|
||||
float distance { 0.0f };
|
||||
int material { -1 };
|
||||
};
|
||||
|
||||
class CollisionDetector : boost::noncopyable {
|
||||
|
|
74
src/game/footstepsounds.cpp
Normal file
74
src/game/footstepsounds.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 The reone project contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "footstepsounds.h"
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include "../audio/files.h"
|
||||
#include "../resource/resources.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace std::placeholders;
|
||||
|
||||
using namespace reone::audio;
|
||||
using namespace reone::resource;
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace game {
|
||||
|
||||
FootstepSounds &FootstepSounds::instance() {
|
||||
static FootstepSounds instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
FootstepSounds::FootstepSounds() : MemoryCache(bind(&FootstepSounds::doGet, this, _1)) {
|
||||
}
|
||||
|
||||
shared_ptr<FootstepTypeSounds> FootstepSounds::doGet(uint32_t type) {
|
||||
shared_ptr<FootstepTypeSounds> result;
|
||||
|
||||
shared_ptr<TwoDA> twoDa(Resources::instance().get2DA("footstepsounds"));
|
||||
if (twoDa) {
|
||||
result = make_shared<FootstepTypeSounds>();
|
||||
map<string, vector<shared_ptr<AudioStream>> &> dict {
|
||||
{ "dirt", result->dirt },
|
||||
{ "grass", result->grass },
|
||||
{ "stone", result->stone },
|
||||
{ "wood", result->wood },
|
||||
{ "water", result->water },
|
||||
{ "carpet", result->carpet },
|
||||
{ "metal", result->metal },
|
||||
{ "leaves", result->leaves }
|
||||
};
|
||||
for (auto &pair : dict) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
string key(str(boost::format("%s%d") % pair.first % i));
|
||||
string resRef(twoDa->getString(static_cast<int>(type), key));
|
||||
shared_ptr<AudioStream> audio(AudioFiles::instance().get(resRef));
|
||||
pair.second.push_back(move(audio));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return move(result);
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
|
||||
} // namespace reone
|
53
src/game/footstepsounds.h
Normal file
53
src/game/footstepsounds.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 The reone project contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "../audio/stream.h"
|
||||
#include "../common/cache.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace game {
|
||||
|
||||
struct FootstepTypeSounds {
|
||||
std::vector<std::shared_ptr<audio::AudioStream>> dirt;
|
||||
std::vector<std::shared_ptr<audio::AudioStream>> grass;
|
||||
std::vector<std::shared_ptr<audio::AudioStream>> stone;
|
||||
std::vector<std::shared_ptr<audio::AudioStream>> wood;
|
||||
std::vector<std::shared_ptr<audio::AudioStream>> water;
|
||||
std::vector<std::shared_ptr<audio::AudioStream>> carpet;
|
||||
std::vector<std::shared_ptr<audio::AudioStream>> metal;
|
||||
std::vector<std::shared_ptr<audio::AudioStream>> leaves;
|
||||
};
|
||||
|
||||
class FootstepSounds : public MemoryCache<uint32_t, FootstepTypeSounds> {
|
||||
public:
|
||||
static FootstepSounds &instance();
|
||||
|
||||
private:
|
||||
FootstepSounds();
|
||||
|
||||
std::shared_ptr<FootstepTypeSounds> doGet(uint32_t type);
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
|
||||
} // namespace reone
|
|
@ -165,8 +165,9 @@ void Area::loadPTH() {
|
|||
const Path::Point &point = points[i];
|
||||
Room *room = nullptr;
|
||||
float z = 0.0f;
|
||||
int material = 0;
|
||||
|
||||
if (!getElevationAt(glm::vec2(point.x, point.y), room, z)) {
|
||||
if (!getElevationAt(glm::vec2(point.x, point.y), room, z, material)) {
|
||||
warn(boost::format("Area: point %d elevation not found") % i);
|
||||
continue;
|
||||
}
|
||||
|
@ -273,8 +274,9 @@ void Area::add(const shared_ptr<SpatialObject> &object) {
|
|||
void Area::determineObjectRoom(SpatialObject &object) {
|
||||
glm::vec3 position(object.position());
|
||||
Room *room = nullptr;
|
||||
int material = 0;
|
||||
|
||||
if (getElevationAt(position, room, position.z)) {
|
||||
if (getElevationAt(position, room, position.z, material)) {
|
||||
object.setRoom(room);
|
||||
}
|
||||
}
|
||||
|
@ -344,8 +346,9 @@ shared_ptr<SpatialObject> Area::getObjectByTag(const string &tag, int nth) const
|
|||
void Area::landObject(SpatialObject &object) {
|
||||
glm::vec3 position(object.position());
|
||||
Room *room = nullptr;
|
||||
int material = 0;
|
||||
|
||||
if (getElevationAt(position, room, position.z, true, &object)) {
|
||||
if (getElevationAt(position, room, position.z, material, true, &object)) {
|
||||
object.setPosition(position);
|
||||
return;
|
||||
}
|
||||
|
@ -353,7 +356,7 @@ void Area::landObject(SpatialObject &object) {
|
|||
float angle = i * glm::half_pi<float>();
|
||||
position = object.position() + glm::vec3(glm::sin(angle), glm::cos(angle), 0.0f);
|
||||
|
||||
if (getElevationAt(position, room, position.z, true, &object)) {
|
||||
if (getElevationAt(position, room, position.z, material, true, &object)) {
|
||||
object.setPosition(position);
|
||||
return;
|
||||
}
|
||||
|
@ -429,7 +432,7 @@ void Area::printDebugInfo(const SpatialObject &object) {
|
|||
debug("Selected object: " + ss.str());
|
||||
}
|
||||
|
||||
bool Area::getElevationAt(const glm::vec2 &position, Room *&room, float &z, bool creatures, const SpatialObject *except) const {
|
||||
bool Area::getElevationAt(const glm::vec2 &position, Room *&room, float &z, int &material, bool creatures, const SpatialObject *except) const {
|
||||
// Test AABB of alive creatures
|
||||
if (creatures) {
|
||||
RaycastProperties props;
|
||||
|
@ -471,6 +474,7 @@ bool Area::getElevationAt(const glm::vec2 &position, Room *&room, float &z, bool
|
|||
if (_collisionDetector.raycast(props, result)) {
|
||||
room = result.room;
|
||||
z = result.intersection.z;
|
||||
material = result.material;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -539,12 +543,14 @@ bool Area::moveCreature(const shared_ptr<Creature> &creature, const glm::vec2 &d
|
|||
bool Area::doMoveCreature(const shared_ptr<Creature> &creature, const glm::vec3 &dest) {
|
||||
float z;
|
||||
Room *room;
|
||||
int material;
|
||||
|
||||
if (getElevationAt(dest, room, z)) {
|
||||
if (getElevationAt(dest, room, z, material)) {
|
||||
const Room *oldRoom = creature->room();
|
||||
|
||||
creature->setRoom(room);
|
||||
creature->setPosition(glm::vec3(dest.x, dest.y, z));
|
||||
creature->setWalkmeshMaterial(material);
|
||||
|
||||
if (creature == _game->party().getLeader()) {
|
||||
onPartyLeaderMoved(room != oldRoom);
|
||||
|
|
|
@ -324,7 +324,7 @@ private:
|
|||
*/
|
||||
bool getCreatureObstacle(const Creature &creature, const glm::vec3 &dest) const;
|
||||
|
||||
bool getElevationAt(const glm::vec2 &position, Room *&room, float &z, bool creatures = false, const SpatialObject *except = nullptr) const;
|
||||
bool getElevationAt(const glm::vec2 &position, Room *&room, float &z, int &material, bool creatures = false, const SpatialObject *except = nullptr) const;
|
||||
|
||||
// END Collision detection
|
||||
};
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "../../audio/player.h"
|
||||
#include "../../common/log.h"
|
||||
#include "../../common/random.h"
|
||||
#include "../../common/streamutil.h"
|
||||
#include "../../common/timer.h"
|
||||
#include "../../render/model/models.h"
|
||||
|
@ -35,7 +36,9 @@
|
|||
|
||||
#include "../action/attack.h"
|
||||
#include "../animationutil.h"
|
||||
#include "../footstepsounds.h"
|
||||
#include "../portraits.h"
|
||||
#include "../surfaces.h"
|
||||
|
||||
#include "objectfactory.h"
|
||||
|
||||
|
@ -83,8 +86,9 @@ void Creature::loadFromBlueprint(const string &resRef) {
|
|||
void Creature::loadAppearance() {
|
||||
shared_ptr<TwoDA> appearances(Resources::instance().get2DA("appearance"));
|
||||
_modelType = parseModelType(appearances->getString(_appearance, "modeltype"));
|
||||
_walkSpeed = appearances->getFloat(_appearance, "walkdist", 0.0f);
|
||||
_runSpeed = appearances->getFloat(_appearance, "rundist", 0.0f);
|
||||
_walkSpeed = appearances->getFloat(_appearance, "walkdist");
|
||||
_runSpeed = appearances->getFloat(_appearance, "rundist");
|
||||
_footstepType = appearances->getInt(_appearance, "footsteptype", -1);
|
||||
|
||||
if (_portraitId > 0) {
|
||||
_portrait = Portraits::instance().getTextureByIndex(_portraitId);
|
||||
|
@ -644,6 +648,40 @@ void Creature::setAppliedForce(glm::vec3 force) {
|
|||
}
|
||||
}
|
||||
|
||||
void Creature::onEventSignalled(const string &name) {
|
||||
if (name == "snd_footstep" && _footstepType != -1 && _walkmeshMaterial != -1) {
|
||||
shared_ptr<FootstepTypeSounds> sounds(FootstepSounds::instance().get(_footstepType));
|
||||
if (sounds) {
|
||||
const Surface &surface = Surfaces::instance().getSurface(_walkmeshMaterial);
|
||||
vector<shared_ptr<AudioStream>> materialSounds;
|
||||
if (surface.sound == "DT") {
|
||||
materialSounds = sounds->dirt;
|
||||
} else if (surface.sound == "GR") {
|
||||
materialSounds = sounds->grass;
|
||||
} else if (surface.sound == "ST") {
|
||||
materialSounds = sounds->stone;
|
||||
} else if (surface.sound == "WD") {
|
||||
materialSounds = sounds->wood;
|
||||
} else if (surface.sound == "WT") {
|
||||
materialSounds = sounds->water;
|
||||
} else if (surface.sound == "CP") {
|
||||
materialSounds = sounds->carpet;
|
||||
} else if (surface.sound == "MT") {
|
||||
materialSounds = sounds->metal;
|
||||
} else if (surface.sound == "LV") {
|
||||
materialSounds = sounds->leaves;
|
||||
}
|
||||
int index = random(0, 3);
|
||||
if (index < static_cast<int>(materialSounds.size())) {
|
||||
shared_ptr<AudioStream> sound(materialSounds[index]);
|
||||
if (sound) {
|
||||
AudioPlayer::instance().play(sound, AudioType::Sound, false, 1.0f, true, _position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
|
||||
} // namespace reone
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "../../resource/format/2dareader.h"
|
||||
#include "../../resource/format/gffreader.h"
|
||||
#include "../../resource/types.h"
|
||||
#include "../../scene/animation/eventlistener.h"
|
||||
#include "../../script/types.h"
|
||||
|
||||
#include "../d20/attributes.h"
|
||||
|
@ -41,7 +42,7 @@ namespace game {
|
|||
|
||||
constexpr float kDefaultAttackRange = 2.0f;
|
||||
|
||||
class Creature : public SpatialObject {
|
||||
class Creature : public SpatialObject, public scene::IAnimationEventListener {
|
||||
public:
|
||||
enum class ModelType {
|
||||
Creature,
|
||||
|
@ -124,6 +125,7 @@ public:
|
|||
RacialType racialType() const { return _race; }
|
||||
Subrace subrace() const { return _subrace; }
|
||||
NPCAIStyle aiStyle() const { return _aiStyle; }
|
||||
int walkmeshMaterial() const { return _walkmeshMaterial; }
|
||||
|
||||
void setGender(Gender gender) { _gender = gender; }
|
||||
void setAppearance(int appearance) { _appearance = appearance; }
|
||||
|
@ -133,6 +135,7 @@ public:
|
|||
void setImmortal(bool immortal) { _immortal = immortal; }
|
||||
void setXP(int xp) { _xp = xp; }
|
||||
void setAIStyle(NPCAIStyle style) { _aiStyle = style; }
|
||||
void setWalkmeshMaterial(int material) { _walkmeshMaterial = material; }
|
||||
|
||||
// Animation
|
||||
|
||||
|
@ -213,6 +216,12 @@ public:
|
|||
|
||||
// END Scripts
|
||||
|
||||
// IAnimationEventListener
|
||||
|
||||
void onEventSignalled(const std::string &name);
|
||||
|
||||
// END IAnimationEventListener
|
||||
|
||||
private:
|
||||
Gender _gender { Gender::Male };
|
||||
int _appearance { 0 };
|
||||
|
@ -255,6 +264,8 @@ private:
|
|||
int _lawfulChaotic { 0 };
|
||||
int _challengeRating { 0 };
|
||||
bool _disarmable { false };
|
||||
uint32_t _footstepType { 0 };
|
||||
int _walkmeshMaterial { -1 };
|
||||
|
||||
// Animation
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ shared_ptr<ModelSceneNode> Creature::buildModel() {
|
|||
shared_ptr<Model> model(Models::instance().get(modelName));
|
||||
if (!model) return nullptr;
|
||||
|
||||
auto modelSceneNode = make_unique<ModelSceneNode>(ModelUsage::Creature, model, _sceneGraph);
|
||||
auto modelSceneNode = make_unique<ModelSceneNode>(ModelUsage::Creature, model, _sceneGraph, set<string>(), this);
|
||||
|
||||
// Body texture
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ struct Surface {
|
|||
std::string label;
|
||||
bool walkable { false };
|
||||
bool grass { false };
|
||||
std::string soundResRef;
|
||||
std::string sound;
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
|
|
|
@ -43,7 +43,7 @@ void Surfaces::init() {
|
|||
surface.label = surfacemat->getString(row, "label");
|
||||
surface.walkable = surfacemat->getBool(row, "walk");
|
||||
surface.grass = surfacemat->getBool(row, "grass");
|
||||
surface.soundResRef = boost::to_lower_copy(surfacemat->getString(row, "sound"));
|
||||
surface.sound = surfacemat->getString(row, "sound");
|
||||
_surfaces.push_back(move(surface));
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ bool Surfaces::isWalkable(int index) const {
|
|||
|
||||
const Surface &Surfaces::getSurface(int index) const {
|
||||
if (index < 0 || index >= static_cast<int>(_surfaces.size())) {
|
||||
throw out_of_range("index is out of range");
|
||||
throw out_of_range("index is out of range: " + to_string(index));
|
||||
}
|
||||
return _surfaces[index];
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ void Walkmesh::computeAABB() {
|
|||
}
|
||||
}
|
||||
|
||||
bool Walkmesh::raycast(const glm::vec3 &origin, const glm::vec3 &dir, bool walkable, float &distance) const {
|
||||
bool Walkmesh::raycast(const glm::vec3 &origin, const glm::vec3 &dir, bool walkable, float &distance, int &material) const {
|
||||
float minDistance = FLT_MAX;
|
||||
|
||||
const vector<Face> &faces = walkable ? _walkableFaces : _nonWalkableFaces;
|
||||
|
@ -55,6 +55,7 @@ bool Walkmesh::raycast(const glm::vec3 &origin, const glm::vec3 &dir, bool walka
|
|||
|
||||
if (glm::intersectRayTriangle(origin, dir, p0, p1, p2, baryPosition, localDistance) && localDistance >= 0.0f && localDistance < minDistance) {
|
||||
minDistance = localDistance;
|
||||
material = static_cast<int>(face.material);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
float area { 0.0f };
|
||||
};
|
||||
|
||||
bool raycast(const glm::vec3 &origin, const glm::vec3 &dir, bool walkable, float &distance) const;
|
||||
bool raycast(const glm::vec3 &origin, const glm::vec3 &dir, bool walkable, float &distance, int &material) const;
|
||||
|
||||
const std::vector<Face> &grassFaces() const { return _grassFaces; }
|
||||
const AABB &aabb() const { return _aabb; }
|
||||
|
|
35
src/scene/animation/eventlistener.h
Normal file
35
src/scene/animation/eventlistener.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 The reone project contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace scene {
|
||||
|
||||
class IAnimationEventListener {
|
||||
public:
|
||||
virtual ~IAnimationEventListener() = default;
|
||||
|
||||
virtual void onEventSignalled(const std::string &name) = 0;
|
||||
};
|
||||
|
||||
} // namespace scene
|
||||
|
||||
} // namespace reone
|
|
@ -47,8 +47,15 @@ const float kMinDirectionalLightRadius = 1000.0f;
|
|||
|
||||
static bool g_debugAABB = false;
|
||||
|
||||
ModelSceneNode::ModelSceneNode(ModelUsage usage, const shared_ptr<Model> &model, SceneGraph *sceneGraph, set<string> ignoreNodes) :
|
||||
ModelSceneNode::ModelSceneNode(
|
||||
ModelUsage usage,
|
||||
const shared_ptr<Model> &model,
|
||||
SceneGraph *sceneGraph,
|
||||
set<string> ignoreNodes,
|
||||
IAnimationEventListener *animEventListener
|
||||
) :
|
||||
SceneNode(SceneNodeType::Model, sceneGraph),
|
||||
_animEventListener(animEventListener),
|
||||
_usage(usage),
|
||||
_model(model),
|
||||
_animator(this, ignoreNodes) {
|
||||
|
@ -370,6 +377,8 @@ void ModelSceneNode::signalEvent(const string &name) {
|
|||
for (auto &emitter : _emitters) {
|
||||
emitter->detonate();
|
||||
}
|
||||
} else if (_animEventListener) {
|
||||
_animEventListener->onEventSignalled(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "../../render/shaders.h"
|
||||
#include "../../render/walkmesh/walkmesh.h"
|
||||
|
||||
#include "../animation/eventlistener.h"
|
||||
#include "../animation/scenenodeanimator.h"
|
||||
#include "../types.h"
|
||||
|
||||
|
@ -43,7 +44,8 @@ public:
|
|||
ModelUsage usage,
|
||||
const std::shared_ptr<render::Model> &model,
|
||||
SceneGraph *sceneGraph,
|
||||
std::set<std::string> ignoreNodes = std::set<std::string>());
|
||||
std::set<std::string> ignoreNodes = std::set<std::string>(),
|
||||
IAnimationEventListener *animEventListener = nullptr);
|
||||
|
||||
void update(float dt) override;
|
||||
void draw() override;
|
||||
|
@ -97,6 +99,8 @@ public:
|
|||
// END Dynamic lighting
|
||||
|
||||
private:
|
||||
IAnimationEventListener *_animEventListener;
|
||||
|
||||
ModelUsage _usage;
|
||||
std::shared_ptr<render::Model> _model;
|
||||
std::shared_ptr<render::Walkmesh> _walkmesh;
|
||||
|
|
Loading…
Reference in a new issue