Initial commit
This commit is contained in:
380
engines/grim/emi/animationemi.cpp
Normal file
380
engines/grim/emi/animationemi.cpp
Normal file
@@ -0,0 +1,380 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "math/vector3d.h"
|
||||
#include "math/quat.h"
|
||||
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/emi/animationemi.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
AnimationEmi::AnimationEmi(const Common::String &filename, Common::SeekableReadStream *data) :
|
||||
_name(""), _duration(0.0f), _numBones(0), _bones(nullptr) {
|
||||
_fname = filename;
|
||||
loadAnimation(data);
|
||||
}
|
||||
|
||||
// Use modelemi's solution for the LA-strings.
|
||||
void AnimationEmi::loadAnimation(Common::SeekableReadStream *data) {
|
||||
int len = data->readUint32LE();
|
||||
char *inString = new char[len];
|
||||
data->read(inString, len);
|
||||
_name = inString;
|
||||
delete[] inString;
|
||||
|
||||
_duration = 1000 * data->readFloatLE();
|
||||
_numBones = data->readUint32LE();
|
||||
|
||||
_bones = new Bone[_numBones];
|
||||
for (int i = 0; i < _numBones; i++) {
|
||||
_bones[i].loadBinary(data);
|
||||
}
|
||||
}
|
||||
|
||||
AnimationEmi::~AnimationEmi() {
|
||||
g_resourceloader->uncacheAnimationEmi(this);
|
||||
delete[] _bones;
|
||||
}
|
||||
|
||||
void Bone::loadBinary(Common::SeekableReadStream *data) {
|
||||
uint32 len = data->readUint32LE();
|
||||
char *inString = new char[len];
|
||||
data->read(inString, len);
|
||||
_boneName = inString;
|
||||
delete[] inString;
|
||||
_operation = data->readUint32LE();
|
||||
_priority = data->readUint32LE();
|
||||
_c = data->readUint32LE();
|
||||
_count = data->readUint32LE();
|
||||
|
||||
if (_operation == 3) { // Translation
|
||||
_translations = new AnimTranslation[_count];
|
||||
for (int j = 0; j < _count; j++) {
|
||||
_translations[j]._vec.readFromStream(data);
|
||||
_translations[j]._time = 1000 * data->readFloatLE();
|
||||
}
|
||||
} else if (_operation == 4) { // Rotation
|
||||
_rotations = new AnimRotation[_count];
|
||||
for (int j = 0; j < _count; j++) {
|
||||
_rotations[j]._quat.readFromStream(data);
|
||||
_rotations[j]._time = 1000 * data->readFloatLE();
|
||||
}
|
||||
} else {
|
||||
error("Unknown animation-operation %d", _operation);
|
||||
}
|
||||
}
|
||||
|
||||
Bone::~Bone() {
|
||||
if (_operation == 3) {
|
||||
delete[] _translations;
|
||||
} else if (_operation == 4) {
|
||||
delete[] _rotations;
|
||||
}
|
||||
}
|
||||
|
||||
AnimationStateEmi::AnimationStateEmi(const Common::String &anim) :
|
||||
_skel(nullptr), _looping(false), _active(false), _paused(false),
|
||||
_fadeMode(Animation::None), _fade(1.0f), _fadeLength(0), _time(-1), _startFade(1.0f),
|
||||
_boneJoints(nullptr) {
|
||||
_anim = g_resourceloader->getAnimationEmi(anim);
|
||||
if (_anim)
|
||||
_boneJoints = new int[_anim->_numBones];
|
||||
}
|
||||
|
||||
AnimationStateEmi::~AnimationStateEmi() {
|
||||
deactivate();
|
||||
delete[] _boneJoints;
|
||||
}
|
||||
|
||||
void AnimationStateEmi::activate() {
|
||||
if (!_active) {
|
||||
_active = true;
|
||||
if (_skel)
|
||||
_skel->addAnimation(this);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationStateEmi::deactivate() {
|
||||
if (_active) {
|
||||
_active = false;
|
||||
if (_skel)
|
||||
_skel->removeAnimation(this);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationStateEmi::update(uint time) {
|
||||
if (!_active)
|
||||
return;
|
||||
|
||||
if (!_anim) {
|
||||
deactivate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_paused) {
|
||||
int durationMs = (int)_anim->_duration;
|
||||
if (_time >= durationMs) {
|
||||
if (_looping) {
|
||||
_time = _time % durationMs;
|
||||
} else {
|
||||
if (_fadeMode != Animation::FadeOut)
|
||||
deactivate();
|
||||
}
|
||||
}
|
||||
if (_time < 0) {
|
||||
_time = 0;
|
||||
} else {
|
||||
_time += time;
|
||||
}
|
||||
}
|
||||
|
||||
if (_fadeMode != Animation::None) {
|
||||
if (_fadeMode == Animation::FadeIn) {
|
||||
_fade += (float)time * (1.0f - _startFade) / _fadeLength;
|
||||
if (_fade >= 1.f) {
|
||||
_fade = 1.f;
|
||||
_fadeMode = Animation::None;
|
||||
}
|
||||
} else {
|
||||
_fade -= (float)time * _startFade / _fadeLength;
|
||||
if (_fade <= 0.f) {
|
||||
_fade = 0.f;
|
||||
// Don't reset the _fadeMode here. This way if fadeOut() was called
|
||||
// on a looping chore its keyframe animations will remain faded out
|
||||
// when it calls play() again.
|
||||
deactivate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationStateEmi::computeWeights() {
|
||||
if (_fade <= 0.0f)
|
||||
return;
|
||||
|
||||
for (int bone = 0; bone < _anim->_numBones; ++bone) {
|
||||
Bone &curBone = _anim->_bones[bone];
|
||||
int jointIndex = _boneJoints[bone];
|
||||
if (jointIndex == -1)
|
||||
continue;
|
||||
|
||||
AnimationLayer *layer = _skel->getLayer(curBone._priority);
|
||||
JointAnimation &jointAnim = layer->_jointAnims[jointIndex];
|
||||
|
||||
if (curBone._rotations) {
|
||||
jointAnim._rotWeight += _fade;
|
||||
}
|
||||
if (curBone._translations) {
|
||||
jointAnim._transWeight += _fade;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationStateEmi::animate() {
|
||||
if (_fade <= 0.0f)
|
||||
return;
|
||||
|
||||
if (_time < 0)
|
||||
return;
|
||||
|
||||
for (int bone = 0; bone < _anim->_numBones; ++bone) {
|
||||
Bone &curBone = _anim->_bones[bone];
|
||||
int jointIndex = _boneJoints[bone];
|
||||
if (jointIndex == -1)
|
||||
continue;
|
||||
|
||||
Joint *target = &_skel->_joints[jointIndex];
|
||||
AnimationLayer *layer = _skel->getLayer(curBone._priority);
|
||||
JointAnimation &jointAnim = layer->_jointAnims[jointIndex];
|
||||
|
||||
if (curBone._rotations) {
|
||||
int keyfIdx = -1;
|
||||
Math::Quaternion quat;
|
||||
|
||||
// Normalize the weight so that the sum of applied weights will equal 1.
|
||||
float normalizedRotWeight = _fade;
|
||||
if (jointAnim._rotWeight > 1.0f) {
|
||||
// Note: Division by unnormalized sum of weights.
|
||||
normalizedRotWeight = _fade / jointAnim._rotWeight;
|
||||
}
|
||||
|
||||
for (int curKeyFrame = 0; curKeyFrame < curBone._count; curKeyFrame++) {
|
||||
if (curBone._rotations[curKeyFrame]._time >= _time) {
|
||||
keyfIdx = curKeyFrame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyfIdx == 0) {
|
||||
quat = curBone._rotations[0]._quat;
|
||||
}
|
||||
else if (keyfIdx != -1) {
|
||||
float timeDelta = curBone._rotations[keyfIdx]._time - curBone._rotations[keyfIdx - 1]._time;
|
||||
float interpVal = (_time - curBone._rotations[keyfIdx - 1]._time) / timeDelta;
|
||||
|
||||
quat = curBone._rotations[keyfIdx - 1]._quat.slerpQuat(curBone._rotations[keyfIdx]._quat, interpVal);
|
||||
}
|
||||
else {
|
||||
quat = curBone._rotations[curBone._count - 1]._quat;
|
||||
}
|
||||
|
||||
Math::Quaternion &quatFinal = jointAnim._quat;
|
||||
quat = target->_quat.inverse() * quat;
|
||||
quat = quatFinal * quat;
|
||||
quatFinal = quatFinal.slerpQuat(quat, normalizedRotWeight);
|
||||
}
|
||||
|
||||
if (curBone._translations) {
|
||||
int keyfIdx = -1;
|
||||
Math::Vector3d vec;
|
||||
|
||||
// Normalize the weight so that the sum of applied weights will equal 1.
|
||||
float normalizedTransWeight = _fade;
|
||||
if (jointAnim._transWeight > 1.0f) {
|
||||
// Note: Division by unnormalized sum of weights.
|
||||
normalizedTransWeight = _fade / jointAnim._transWeight;
|
||||
}
|
||||
|
||||
for (int curKeyFrame = 0; curKeyFrame < curBone._count; curKeyFrame++) {
|
||||
if (curBone._translations[curKeyFrame]._time >= _time) {
|
||||
keyfIdx = curKeyFrame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyfIdx == 0) {
|
||||
vec = curBone._translations[0]._vec;
|
||||
}
|
||||
else if (keyfIdx != -1) {
|
||||
float timeDelta = curBone._translations[keyfIdx]._time - curBone._translations[keyfIdx - 1]._time;
|
||||
float interpVal = (_time - curBone._translations[keyfIdx - 1]._time) / timeDelta;
|
||||
|
||||
vec = curBone._translations[keyfIdx - 1]._vec +
|
||||
(curBone._translations[keyfIdx]._vec - curBone._translations[keyfIdx - 1]._vec) * interpVal;
|
||||
}
|
||||
else {
|
||||
vec = curBone._translations[curBone._count - 1]._vec;
|
||||
}
|
||||
|
||||
Math::Vector3d &posFinal = jointAnim._pos;
|
||||
vec = vec - target->_relMatrix.getPosition();
|
||||
posFinal = posFinal + vec * normalizedTransWeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationStateEmi::play() {
|
||||
if (!_active) {
|
||||
_time = -1;
|
||||
if (_fadeMode == Animation::FadeOut)
|
||||
_fadeMode = Animation::None;
|
||||
if (_fadeMode == Animation::FadeIn || _fade > 0.f)
|
||||
activate();
|
||||
}
|
||||
_paused = false;
|
||||
}
|
||||
|
||||
void AnimationStateEmi::stop() {
|
||||
_fadeMode = Animation::None;
|
||||
_time = -1;
|
||||
deactivate();
|
||||
}
|
||||
|
||||
void AnimationStateEmi::setPaused(bool paused) {
|
||||
_paused = paused;
|
||||
}
|
||||
|
||||
void AnimationStateEmi::setLooping(bool loop) {
|
||||
_looping = loop;
|
||||
}
|
||||
|
||||
void AnimationStateEmi::setSkeleton(Skeleton *skel) {
|
||||
if (skel != _skel) {
|
||||
if (_skel)
|
||||
_skel->removeAnimation(this);
|
||||
_skel = skel;
|
||||
if (_active)
|
||||
skel->addAnimation(this);
|
||||
|
||||
if (_anim) {
|
||||
for (int i = 0; i < _anim->_numBones; ++i) {
|
||||
_boneJoints[i] = skel->findJointIndex(_anim->_bones[i]._boneName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationStateEmi::fade(Animation::FadeMode mode, int fadeLength) {
|
||||
if (mode == Animation::None) {
|
||||
_fade = 1.f;
|
||||
} else if (_fadeMode != Animation::FadeOut && mode == Animation::FadeIn) {
|
||||
_fade = 0.f;
|
||||
}
|
||||
_startFade = _fade;
|
||||
_fadeMode = mode;
|
||||
_fadeLength = fadeLength;
|
||||
}
|
||||
|
||||
void AnimationStateEmi::advance(uint msecs) {
|
||||
if (_time >= 0) {
|
||||
_time += msecs;
|
||||
} else {
|
||||
_time = msecs;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationStateEmi::saveState(SaveGame *state) {
|
||||
state->writeBool(_looping);
|
||||
state->writeBool(_active);
|
||||
state->writeBool(_paused);
|
||||
state->writeLESint32(_time);
|
||||
state->writeFloat(_fade);
|
||||
state->writeFloat(_startFade);
|
||||
state->writeLESint32((int)_fadeMode);
|
||||
state->writeLESint32(_fadeLength);
|
||||
}
|
||||
|
||||
void AnimationStateEmi::restoreState(SaveGame *state) {
|
||||
if (state->saveMinorVersion() >= 10) {
|
||||
_looping = state->readBool();
|
||||
bool active = state->readBool();
|
||||
_paused = state->readBool();
|
||||
if (state->saveMinorVersion() < 22) {
|
||||
_time = (uint)state->readFloat();
|
||||
} else {
|
||||
_time = state->readLESint32();
|
||||
}
|
||||
_fade = state->readFloat();
|
||||
_startFade = state->readFloat();
|
||||
_fadeMode = (Animation::FadeMode)state->readLESint32();
|
||||
_fadeLength = state->readLESint32();
|
||||
|
||||
if (active)
|
||||
activate();
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
113
engines/grim/emi/animationemi.h
Normal file
113
engines/grim/emi/animationemi.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_ANIMATIONEMI_H
|
||||
#define GRIM_ANIMATIONEMI_H
|
||||
|
||||
#include "common/str.h"
|
||||
|
||||
#include "math/mathfwd.h"
|
||||
#include "math/quat.h"
|
||||
|
||||
#include "engines/grim/animation.h"
|
||||
#include "engines/grim/object.h"
|
||||
#include "engines/grim/emi/skeleton.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
struct AnimRotation {
|
||||
Math::Quaternion _quat;
|
||||
float _time;
|
||||
};
|
||||
|
||||
struct AnimTranslation {
|
||||
Math::Vector3d _vec;
|
||||
float _time;
|
||||
};
|
||||
|
||||
struct Bone {
|
||||
Common::String _boneName;
|
||||
int _operation;
|
||||
int _priority;
|
||||
int _c;
|
||||
int _count;
|
||||
AnimRotation *_rotations;
|
||||
AnimTranslation *_translations;
|
||||
Joint *_target;
|
||||
Bone() : _rotations(NULL), _translations(NULL), _boneName(""), _operation(0), _target(NULL) {}
|
||||
~Bone();
|
||||
void loadBinary(Common::SeekableReadStream *data);
|
||||
};
|
||||
|
||||
class AnimationEmi : public Object {
|
||||
void loadAnimation(Common::SeekableReadStream *data);
|
||||
public:
|
||||
Common::String _name;
|
||||
Common::String _fname;
|
||||
float _duration;
|
||||
int _numBones;
|
||||
Bone *_bones;
|
||||
AnimationEmi(const Common::String &filename, Common::SeekableReadStream *data);
|
||||
~AnimationEmi();
|
||||
|
||||
const Common::String &getFilename() const { return _fname; }
|
||||
};
|
||||
|
||||
class AnimationStateEmi {
|
||||
public:
|
||||
AnimationStateEmi(const Common::String &anim);
|
||||
~AnimationStateEmi();
|
||||
|
||||
void update(uint time);
|
||||
void computeWeights();
|
||||
void animate();
|
||||
void play();
|
||||
void stop();
|
||||
void setPaused(bool paused);
|
||||
void setLooping(bool loop);
|
||||
void setSkeleton(Skeleton *skel);
|
||||
void fade(Animation::FadeMode mode, int fadeLength);
|
||||
void advance(uint msecs);
|
||||
void saveState(SaveGame *state);
|
||||
void restoreState(SaveGame *state);
|
||||
|
||||
private:
|
||||
void activate();
|
||||
void deactivate();
|
||||
|
||||
friend class Skeleton;
|
||||
|
||||
Skeleton *_skel;
|
||||
ObjectPtr<AnimationEmi> _anim;
|
||||
bool _looping;
|
||||
bool _active;
|
||||
bool _paused;
|
||||
int _time;
|
||||
float _fade;
|
||||
float _startFade;
|
||||
Animation::FadeMode _fadeMode;
|
||||
int _fadeLength;
|
||||
int *_boneJoints;
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
138
engines/grim/emi/costume/emianim_component.cpp
Normal file
138
engines/grim/emi/costume/emianim_component.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/emi/costume/emianim_component.h"
|
||||
#include "engines/grim/emi/costume/emiskel_component.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/costume.h"
|
||||
#include "engines/grim/emi/costumeemi.h"
|
||||
#include "engines/grim/emi/modelemi.h"
|
||||
#include "engines/grim/emi/skeleton.h"
|
||||
#include "engines/grim/emi/animationemi.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
EMIAnimComponent::EMIAnimComponent(Component *p, int parentID, const char *filename, Component *prevComponent, tag32 t) :
|
||||
Component(p, parentID, filename, t), _animState(nullptr) {
|
||||
}
|
||||
|
||||
EMIAnimComponent::~EMIAnimComponent() {
|
||||
delete _animState;
|
||||
}
|
||||
|
||||
void EMIAnimComponent::init() {
|
||||
_visible = true;
|
||||
_animState = new AnimationStateEmi(_name);
|
||||
}
|
||||
|
||||
int EMIAnimComponent::update(uint time) {
|
||||
EMISkelComponent *skel = ((EMICostume *)_cost)->_emiSkel;
|
||||
if (skel) {
|
||||
_animState->setSkeleton(skel->_obj);
|
||||
_animState->update(time);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EMIAnimComponent::setKey(int f) {
|
||||
switch (f) {
|
||||
case 0: // Stop
|
||||
_animState->stop();
|
||||
break;
|
||||
case 1: // Play
|
||||
_animState->play();
|
||||
break;
|
||||
case 2: // Pause
|
||||
_animState->setPaused(true);
|
||||
break;
|
||||
case 3: // Loop
|
||||
_animState->setLooping(true);
|
||||
_animState->play();
|
||||
break;
|
||||
case 4: // No loop
|
||||
_animState->setLooping(false);
|
||||
break;
|
||||
case 5: // Fade in 1
|
||||
_animState->fade(Animation::FadeIn, 1000);
|
||||
break;
|
||||
case 6: // Fade in 3/4
|
||||
_animState->fade(Animation::FadeIn, 750);
|
||||
break;
|
||||
case 7: // Fade in 1/2
|
||||
_animState->fade(Animation::FadeIn, 500);
|
||||
break;
|
||||
case 8: // Fade in 1/4
|
||||
_animState->fade(Animation::FadeIn, 250);
|
||||
break;
|
||||
case 9: // Fade in 1/8
|
||||
_animState->fade(Animation::FadeIn, 125);
|
||||
break;
|
||||
case 10: // Fade out 1
|
||||
_animState->fade(Animation::FadeOut, 1000);
|
||||
break;
|
||||
case 11: // Fade out 3/4
|
||||
_animState->fade(Animation::FadeOut, 750);
|
||||
break;
|
||||
case 12: // Fade out 1/2
|
||||
_animState->fade(Animation::FadeOut, 500);
|
||||
break;
|
||||
case 13: // Fade out 1/4
|
||||
_animState->fade(Animation::FadeOut, 250);
|
||||
break;
|
||||
case 14: // Fade out 1/8
|
||||
_animState->fade(Animation::FadeOut, 125);
|
||||
break;
|
||||
default:
|
||||
Debug::warning(Debug::Costumes, "Unknown key %d for component %s", f, _name.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EMIAnimComponent::reset() {
|
||||
_visible = true;
|
||||
_animState->stop();
|
||||
}
|
||||
|
||||
void EMIAnimComponent::fade(Animation::FadeMode mode, int fadeLength) {
|
||||
_animState->fade(mode, fadeLength);
|
||||
}
|
||||
|
||||
void EMIAnimComponent::advance(uint msecs) {
|
||||
_animState->advance(msecs);
|
||||
}
|
||||
|
||||
void EMIAnimComponent::setPaused(bool paused) {
|
||||
_animState->setPaused(paused);
|
||||
}
|
||||
|
||||
void EMIAnimComponent::draw() {
|
||||
}
|
||||
|
||||
void EMIAnimComponent::saveState(SaveGame *state) {
|
||||
_animState->saveState(state);
|
||||
}
|
||||
|
||||
void EMIAnimComponent::restoreState(SaveGame *state) {
|
||||
_animState->restoreState(state);
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
54
engines/grim/emi/costume/emianim_component.h
Normal file
54
engines/grim/emi/costume/emianim_component.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_EMI_ANIM_COMPONENT_H
|
||||
#define GRIM_EMI_ANIM_COMPONENT_H
|
||||
|
||||
#include "engines/grim/costume/component.h"
|
||||
|
||||
// This is mostly stubbed for testing the animation loading at the moment.
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class AnimationStateEmi;
|
||||
|
||||
class EMIAnimComponent : public Component {
|
||||
public:
|
||||
EMIAnimComponent(Component *parent, int parentID, const char *filename, Component *prevComponent, tag32 tag);
|
||||
~EMIAnimComponent();
|
||||
void init() override;
|
||||
void setKey(int) override;
|
||||
int update(uint time) override;
|
||||
void reset() override ;
|
||||
void fade(Animation::FadeMode mode, int fadeLength) override ;
|
||||
void advance(uint msecs) override ;
|
||||
void setPaused(bool paused) override;
|
||||
void draw() override;
|
||||
void saveState(SaveGame *state) override;
|
||||
void restoreState(SaveGame *state) override;
|
||||
|
||||
private:
|
||||
AnimationStateEmi *_animState;
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
140
engines/grim/emi/costume/emichore.cpp
Normal file
140
engines/grim/emi/costume/emichore.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/grim/emi/costume/emichore.h"
|
||||
#include "engines/grim/emi/modelemi.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
EMIChore::EMIChore(char name[32], int id, Costume *owner, int length, int numTracks) :
|
||||
Chore(name, id, owner, length, numTracks), _mesh(nullptr), _skeleton(nullptr),
|
||||
_fadeMode(Animation::None), _fade(1.f), _fadeLength(0), _startFade(1.0f) {
|
||||
}
|
||||
|
||||
void EMIChore::addComponent(Component *component) {
|
||||
if (component->isComponentType('m', 'e', 's', 'h')) {
|
||||
_mesh = static_cast<EMIMeshComponent *>(component);
|
||||
} else if (component->isComponentType('s', 'k', 'e', 'l')) {
|
||||
_skeleton = static_cast<EMISkelComponent *>(component);
|
||||
}
|
||||
if (_mesh && _mesh->_obj && _skeleton) {
|
||||
_mesh->_obj->setSkeleton(_skeleton->_obj);
|
||||
}
|
||||
}
|
||||
|
||||
void EMIChore::update(uint time) {
|
||||
if (!_playing || _paused)
|
||||
return;
|
||||
|
||||
if (_fadeMode != Animation::None) {
|
||||
if (_fadeMode == Animation::FadeIn) {
|
||||
_fade += (float)time * (1.0f - _startFade) / _fadeLength;
|
||||
if (_fade >= 1.f) {
|
||||
_fade = 1.f;
|
||||
_fadeMode = Animation::None;
|
||||
}
|
||||
} else {
|
||||
_fade -= (float)time * _startFade / _fadeLength;
|
||||
if (_fade <= 0.f) {
|
||||
_fade = 0.f;
|
||||
stop(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int newTime;
|
||||
if (_currTime < 0)
|
||||
newTime = 0; // For first time through
|
||||
else
|
||||
newTime = _currTime + time;
|
||||
|
||||
setKeys(_currTime, newTime);
|
||||
|
||||
if (_length >= 0 && newTime > _length) {
|
||||
if (!_looping && _fadeMode != Animation::FadeOut) {
|
||||
stop(0);
|
||||
}
|
||||
else {
|
||||
do {
|
||||
newTime -= _length;
|
||||
setKeys(-1, newTime);
|
||||
} while (newTime > _length);
|
||||
}
|
||||
}
|
||||
_currTime = newTime;
|
||||
}
|
||||
|
||||
void EMIChore::stop(uint msecs) {
|
||||
if (msecs > 0) {
|
||||
fade(Animation::FadeOut, msecs);
|
||||
} else {
|
||||
_playing = false;
|
||||
_hasPlayed = false;
|
||||
|
||||
for (int i = 0; i < _numTracks; i++) {
|
||||
Component *comp = getComponentForTrack(i);
|
||||
if (comp)
|
||||
comp->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EMIChore::fade(Animation::FadeMode mode, uint msecs) {
|
||||
if (mode == Animation::None) {
|
||||
_fade = 1.0f;
|
||||
}
|
||||
_startFade = _fade;
|
||||
_fadeMode = mode;
|
||||
_fadeLength = msecs;
|
||||
|
||||
for (int i = 0; i < _numTracks; i++) {
|
||||
Component *comp = getComponentForTrack(i);
|
||||
if (comp) {
|
||||
comp->fade(mode, msecs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EMIChore::saveState(SaveGame *state) const {
|
||||
Chore::saveState(state);
|
||||
|
||||
state->writeLESint32((int)_fadeMode);
|
||||
state->writeFloat(_fade);
|
||||
state->writeFloat(_startFade);
|
||||
state->writeLESint32(_fadeLength);
|
||||
}
|
||||
|
||||
void EMIChore::restoreState(SaveGame *state) {
|
||||
Chore::restoreState(state);
|
||||
|
||||
if (state->saveMinorVersion() >= 10) {
|
||||
_fadeMode = (Animation::FadeMode)state->readLESint32();
|
||||
_fade = state->readFloat();
|
||||
_startFade = state->readFloat();
|
||||
_fadeLength = state->readLESint32();
|
||||
} else {
|
||||
if (_length == -1 && _playing)
|
||||
_currTime = -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
60
engines/grim/emi/costume/emichore.h
Normal file
60
engines/grim/emi/costume/emichore.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_EMICHORE_H
|
||||
#define GRIM_EMICHORE_H
|
||||
|
||||
#include "engines/grim/costume/chore.h"
|
||||
#include "engines/grim/pool.h"
|
||||
#include "engines/grim/emi/costume/emimesh_component.h"
|
||||
#include "engines/grim/emi/costume/emiskel_component.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class EMIChore : public PoolObject<EMIChore>, public Chore {
|
||||
public:
|
||||
EMIChore(char name[32], int id, Costume *owner, int length, int numTracks);
|
||||
static int32 getStaticTag() { return MKTAG('C', 'H', 'O', 'R'); }
|
||||
|
||||
void update(uint msecs) override;
|
||||
void stop(uint msecs) override;
|
||||
void addComponent(Component *component);
|
||||
bool isWearChore() { return _mesh && _skeleton; }
|
||||
void saveState(SaveGame *state) const override;
|
||||
void restoreState(SaveGame *state) override;
|
||||
EMIMeshComponent *getMesh() { return _mesh; }
|
||||
EMISkelComponent *getSkeleton() { return _skeleton; }
|
||||
|
||||
private:
|
||||
void fade(Animation::FadeMode mode, uint msecs) override;
|
||||
|
||||
Animation::FadeMode _fadeMode;
|
||||
float _fade;
|
||||
float _startFade;
|
||||
int _fadeLength;
|
||||
|
||||
EMIMeshComponent *_mesh;
|
||||
EMISkelComponent *_skeleton;
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
168
engines/grim/emi/costume/emihead.cpp
Normal file
168
engines/grim/emi/costume/emihead.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/grim/grim.h"
|
||||
#include "engines/grim/emi/costume/emihead.h"
|
||||
#include "engines/grim/emi/costumeemi.h"
|
||||
#include "engines/grim/emi/skeleton.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
EMIHead::EMIHead(EMICostume *costume) : _yawRange(80.0f), _minPitch(-30.0f), _maxPitch(30.0f) {
|
||||
_cost = costume;
|
||||
}
|
||||
|
||||
void EMIHead::setJoint(const char *joint, const Math::Vector3d &offset) {
|
||||
_jointName = joint;
|
||||
_offset = offset;
|
||||
}
|
||||
|
||||
void EMIHead::setLimits(float yawRange, float maxPitch, float minPitch) {
|
||||
_yawRange = yawRange;
|
||||
_maxPitch = maxPitch;
|
||||
_minPitch = minPitch;
|
||||
}
|
||||
|
||||
void EMIHead::lookAt(bool entering, const Math::Vector3d &point, float rate, const Math::Matrix4 &matrix) {
|
||||
if (!_cost->_emiSkel || !_cost->_emiSkel->_obj)
|
||||
return;
|
||||
|
||||
if (_jointName.empty())
|
||||
return;
|
||||
|
||||
Joint *joint = _cost->_emiSkel->_obj->getJointNamed(_jointName);
|
||||
if (!joint)
|
||||
return;
|
||||
|
||||
Math::Quaternion lookAtQuat; // Note: Identity if not looking at anything.
|
||||
|
||||
if (entering) {
|
||||
Math::Matrix4 jointToWorld = _cost->getOwner()->getFinalMatrix() * joint->_finalMatrix;
|
||||
Math::Vector3d jointWorldPos = jointToWorld.getPosition();
|
||||
Math::Matrix4 worldToJoint = jointToWorld;
|
||||
worldToJoint.invertAffineOrthonormal();
|
||||
|
||||
Math::Vector3d targetDir = (point + _offset) - jointWorldPos;
|
||||
targetDir.normalize();
|
||||
|
||||
const Math::Vector3d worldUp(0, 1, 0);
|
||||
Math::Vector3d frontDir = Math::Vector3d(worldToJoint(0, 1), worldToJoint(1, 1), worldToJoint(2, 1)); // Look straight ahead. (+Y)
|
||||
Math::Vector3d modelFront(0, 0, 1);
|
||||
Math::Vector3d modelUp(0, 1, 0);
|
||||
|
||||
modelFront = modelFront * joint->_absMatrix.getRotation();
|
||||
modelUp = modelUp * joint->_absMatrix.getRotation();
|
||||
|
||||
// Generate a world-space look at matrix.
|
||||
Math::Matrix4 lookAtTM;
|
||||
lookAtTM.setToIdentity();
|
||||
|
||||
if (Math::Vector3d::dotProduct(targetDir, worldUp) >= 0.98f) // Avoid singularity if trying to look straight up.
|
||||
lookAtTM.buildFromTargetDir(modelFront, targetDir, modelUp, -frontDir); // Instead of orienting head towards scene up, orient head towards character "back",
|
||||
else if (Math::Vector3d::dotProduct(targetDir, worldUp) <= -0.98f) // Avoid singularity if trying to look straight down.
|
||||
lookAtTM.buildFromTargetDir(modelFront, targetDir, modelUp, frontDir); // Instead of orienting head towards scene down, orient head towards character "front",
|
||||
else
|
||||
lookAtTM.buildFromTargetDir(modelFront, targetDir, modelUp, worldUp);
|
||||
|
||||
// Convert from world-space to joint-space.
|
||||
lookAtTM = worldToJoint * lookAtTM;
|
||||
|
||||
// Apply angle limits.
|
||||
Math::Angle p, y, r;
|
||||
lookAtTM.getEuler(&y, &p, &r, Math::EO_ZXY);
|
||||
|
||||
y.clampDegrees(_yawRange);
|
||||
p.clampDegrees(_minPitch, _maxPitch);
|
||||
r.clampDegrees(30.0f);
|
||||
|
||||
lookAtTM.buildFromEuler(y, p, r, Math::EO_ZXY);
|
||||
|
||||
lookAtQuat.fromMatrix(lookAtTM.getRotation());
|
||||
}
|
||||
|
||||
if (_headRot != lookAtQuat) {
|
||||
Math::Quaternion diff = _headRot.inverse() * lookAtQuat;
|
||||
diff.normalize();
|
||||
float angle = 2 * acos(MIN(MAX(diff.w(), -1.0f), 1.0f));
|
||||
if (diff.w() < 0.0f) {
|
||||
angle = 2 * (float)M_PI - angle;
|
||||
}
|
||||
|
||||
float turnAmount = g_grim->getPerSecond(rate * ((float)M_PI / 180.0f));
|
||||
if (turnAmount < angle)
|
||||
_headRot = _headRot.slerpQuat(lookAtQuat, turnAmount / angle);
|
||||
else
|
||||
_headRot = lookAtQuat;
|
||||
}
|
||||
|
||||
if (_headRot != Math::Quaternion()) { // If not identity..
|
||||
joint->_animMatrix = joint->_animMatrix * _headRot.toMatrix();
|
||||
joint->_animQuat = joint->_animQuat * _headRot;
|
||||
_cost->_emiSkel->_obj->commitAnim();
|
||||
}
|
||||
}
|
||||
|
||||
void EMIHead::saveState(SaveGame *state) const {
|
||||
state->writeString(_jointName);
|
||||
state->writeVector3d(_offset);
|
||||
state->writeFloat(_headRot.x());
|
||||
state->writeFloat(_headRot.y());
|
||||
state->writeFloat(_headRot.z());
|
||||
state->writeFloat(_headRot.w());
|
||||
state->writeFloat(_yawRange);
|
||||
state->writeFloat(_minPitch);
|
||||
state->writeFloat(_maxPitch);
|
||||
}
|
||||
|
||||
void EMIHead::restoreState(SaveGame *state) {
|
||||
if (state->saveMinorVersion() >= 15) {
|
||||
_jointName = state->readString();
|
||||
_offset = state->readVector3d();
|
||||
_headRot.x() = state->readFloat();
|
||||
_headRot.y() = state->readFloat();
|
||||
_headRot.z() = state->readFloat();
|
||||
_headRot.w() = state->readFloat();
|
||||
if (state->saveMinorVersion() >= 16) {
|
||||
_yawRange = state->readFloat();
|
||||
_minPitch = state->readFloat();
|
||||
_maxPitch = state->readFloat();
|
||||
}
|
||||
} else {
|
||||
state->readLESint32();
|
||||
state->readLESint32();
|
||||
state->readLESint32();
|
||||
state->readFloat();
|
||||
state->readFloat();
|
||||
state->readFloat();
|
||||
if (state->saveMinorVersion() < 2) {
|
||||
state->readFloat();
|
||||
state->readFloat();
|
||||
} else {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
state->readFloat();
|
||||
state->readFloat();
|
||||
state->readFloat();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
57
engines/grim/emi/costume/emihead.h
Normal file
57
engines/grim/emi/costume/emihead.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_EMIHEAD_H
|
||||
#define GRIM_EMIHEAD_H
|
||||
|
||||
#include "math/vector3d.h"
|
||||
#include "math/quat.h"
|
||||
|
||||
#include "engines/grim/costume/head.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class EMICostume;
|
||||
|
||||
class EMIHead : public BaseHead {
|
||||
public:
|
||||
EMIHead(EMICostume *costume);
|
||||
|
||||
void setJoint(const char *joint, const Math::Vector3d &offset);
|
||||
void setLimits(float yawRange, float maxPitch, float minPitch);
|
||||
void lookAt(bool entering, const Math::Vector3d &point, float rate, const Math::Matrix4 &matrix) override;
|
||||
void loadJoints(ModelNode *nodes) override {}
|
||||
void saveState(SaveGame *state) const override;
|
||||
void restoreState(SaveGame *state) override;
|
||||
|
||||
private:
|
||||
EMICostume *_cost;
|
||||
Common::String _jointName;
|
||||
Math::Vector3d _offset;
|
||||
Math::Quaternion _headRot;
|
||||
float _yawRange;
|
||||
float _maxPitch;
|
||||
float _minPitch;
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
53
engines/grim/emi/costume/emiluacode_component.cpp
Normal file
53
engines/grim/emi/costume/emiluacode_component.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/emi/costume/emiluacode_component.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
EMILuaCodeComponent::EMILuaCodeComponent(Component *p, int parentID, const char *name, Component *prevComponent, tag32 t) : Component(p, parentID, name, t) {
|
||||
}
|
||||
|
||||
EMILuaCodeComponent::~EMILuaCodeComponent() {
|
||||
}
|
||||
|
||||
void EMILuaCodeComponent::init() {
|
||||
}
|
||||
|
||||
int EMILuaCodeComponent::update(uint time) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EMILuaCodeComponent::setKey(int val) {
|
||||
Debug::debug(Debug::Lua, "LuaC component: executing code [%s]", _name.c_str());
|
||||
lua_dostring(_name.c_str());
|
||||
}
|
||||
|
||||
void EMILuaCodeComponent::reset() {
|
||||
}
|
||||
|
||||
void EMILuaCodeComponent::draw() {
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
44
engines/grim/emi/costume/emiluacode_component.h
Normal file
44
engines/grim/emi/costume/emiluacode_component.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_EMI_LUACODE_COMPONENT_H
|
||||
#define GRIM_EMI_LUACODE_COMPONENT_H
|
||||
|
||||
#include "engines/grim/costume/component.h"
|
||||
#include "engines/grim/lua/lua.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class EMILuaCodeComponent : public Component {
|
||||
public:
|
||||
EMILuaCodeComponent(Component *parent, int parentID, const char *name, Component *prevComponent, tag32 tag);
|
||||
~EMILuaCodeComponent();
|
||||
void init() override;
|
||||
int update(uint time) override;
|
||||
void reset() override;
|
||||
void draw() override;
|
||||
void setKey(int val) override;
|
||||
private:
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
55
engines/grim/emi/costume/emiluavar_component.cpp
Normal file
55
engines/grim/emi/costume/emiluavar_component.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/emi/costume/emiluavar_component.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
EMILuaVarComponent::EMILuaVarComponent(Component *p, int parentID, const char *name, Component *prevComponent, tag32 t) : Component(p, parentID, name, t) {
|
||||
}
|
||||
|
||||
EMILuaVarComponent::~EMILuaVarComponent() {
|
||||
}
|
||||
|
||||
void EMILuaVarComponent::init() {
|
||||
}
|
||||
|
||||
int EMILuaVarComponent::update(uint time) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EMILuaVarComponent::setKey(int val) {
|
||||
Debug::debug(Debug::Lua, "LuaV component: setting %s to %d", _name.c_str(), val);
|
||||
lua_pushnumber(val);
|
||||
lua_setglobal(_name.c_str());
|
||||
}
|
||||
|
||||
void EMILuaVarComponent::reset() {
|
||||
}
|
||||
|
||||
void EMILuaVarComponent::draw() {
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
44
engines/grim/emi/costume/emiluavar_component.h
Normal file
44
engines/grim/emi/costume/emiluavar_component.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_EMI_LUAVAR_COMPONENT_H
|
||||
#define GRIM_EMI_LUAVAR_COMPONENT_H
|
||||
|
||||
#include "engines/grim/lua/lua.h"
|
||||
#include "engines/grim/costume/component.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class EMILuaVarComponent : public Component {
|
||||
public:
|
||||
EMILuaVarComponent(Component *parent, int parentID, const char *name, Component *prevComponent, tag32 tag);
|
||||
~EMILuaVarComponent();
|
||||
void init() override;
|
||||
int update(uint time) override;
|
||||
void reset() override;
|
||||
void draw() override;
|
||||
void setKey(int val) override;
|
||||
private:
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
99
engines/grim/emi/costume/emimesh_component.cpp
Normal file
99
engines/grim/emi/costume/emimesh_component.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/grim/emi/costume/emimesh_component.h"
|
||||
#include "engines/grim/emi/modelemi.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/costume.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
EMIMeshComponent::EMIMeshComponent(Component *p, int parentID, const char *filename, Component *prevComponent, tag32 t, EMICostume *costume) :
|
||||
Component(p, parentID, filename, t), _costume(costume), _obj(nullptr), _parentModel(nullptr), _hasComplained(false) {
|
||||
_hierShared = false;
|
||||
}
|
||||
|
||||
EMIMeshComponent::~EMIMeshComponent() {
|
||||
if (_hierShared) {
|
||||
_obj = nullptr; // Keep ~ModelComp from deleting it
|
||||
//_animation = NULL;
|
||||
} else {
|
||||
delete _obj;
|
||||
}
|
||||
|
||||
for (EMIMeshComponent *child : _children) {
|
||||
child->_obj = nullptr;
|
||||
//child->_hier = NULL;
|
||||
child->_parentModel = nullptr;
|
||||
}
|
||||
|
||||
if (_parentModel) {
|
||||
_parentModel->_children.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
void EMIMeshComponent::init() {
|
||||
_visible = true;
|
||||
_obj = g_resourceloader->loadModelEMI(_name, _costume);
|
||||
}
|
||||
|
||||
int EMIMeshComponent::update(uint time) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EMIMeshComponent::reset() {
|
||||
_visible = true;
|
||||
}
|
||||
|
||||
void EMIMeshComponent::draw() {
|
||||
// If the object was drawn by being a component
|
||||
// of it's parent then don't draw it
|
||||
if (_parent && _parent->isVisible())
|
||||
return;
|
||||
if (!_obj) {
|
||||
if (!_hasComplained) {
|
||||
warning("Tried to draw component we have no file for %s", _name.c_str());
|
||||
_hasComplained = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Need to translate object to be in accordance
|
||||
// with the setup of the parent
|
||||
//translateObject(false);
|
||||
|
||||
_obj->draw();
|
||||
|
||||
// Need to un-translate when done
|
||||
//translateObject(true);
|
||||
}
|
||||
|
||||
void EMIMeshComponent::getBoundingBox(int *x1, int *y1, int *x2, int *y2) const {
|
||||
// If the object was drawn by being a component
|
||||
// of it's parent then don't draw it
|
||||
|
||||
if (_parent && _parent->isVisible())
|
||||
return;
|
||||
|
||||
if (_obj)
|
||||
_obj->getBoundingBox(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
53
engines/grim/emi/costume/emimesh_component.h
Normal file
53
engines/grim/emi/costume/emimesh_component.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_EMI_MESH_COMPONENT_H
|
||||
#define GRIM_EMI_MESH_COMPONENT_H
|
||||
|
||||
#include "engines/grim/costume/component.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class EMICostume;
|
||||
class EMIModel;
|
||||
|
||||
class EMIMeshComponent : public Component {
|
||||
public:
|
||||
EMIMeshComponent(Component *parent, int parentID, const char *filename, Component *prevComponent, tag32 tag, EMICostume *costume);
|
||||
~EMIMeshComponent();
|
||||
void init() override;
|
||||
int update(uint time) override;
|
||||
void reset() override;
|
||||
void draw() override;
|
||||
void getBoundingBox(int *x1, int *y1, int *x2, int *y2) const;
|
||||
|
||||
public:
|
||||
EMICostume *_costume;
|
||||
bool _hierShared;
|
||||
Common::List<EMIMeshComponent*> _children;
|
||||
EMIMeshComponent *_parentModel;
|
||||
EMIModel *_obj;
|
||||
bool _hasComplained; // Temporary fix for warning-spam.
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
58
engines/grim/emi/costume/emiskel_component.cpp
Normal file
58
engines/grim/emi/costume/emiskel_component.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/grim/emi/costume/emiskel_component.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/emi/modelemi.h"
|
||||
#include "engines/grim/emi/skeleton.h"
|
||||
#include "engines/grim/costume.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
EMISkelComponent::EMISkelComponent(Component *p, int parentID, const char *filename, Component *prevComponent, tag32 t) : Component(p, parentID, filename, t), _obj(nullptr), _parentModel(nullptr), _hierShared(false) {
|
||||
}
|
||||
|
||||
EMISkelComponent::~EMISkelComponent() {
|
||||
delete _obj;
|
||||
}
|
||||
|
||||
void EMISkelComponent::init() {
|
||||
_visible = true;
|
||||
_obj = g_resourceloader->loadSkeleton(_name);
|
||||
}
|
||||
|
||||
void EMISkelComponent::animate() {
|
||||
if (_obj)
|
||||
_obj->animate();
|
||||
}
|
||||
|
||||
int EMISkelComponent::update(uint time) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EMISkelComponent::reset() {
|
||||
_visible = true;
|
||||
}
|
||||
|
||||
void EMISkelComponent::draw() {
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
51
engines/grim/emi/costume/emiskel_component.h
Normal file
51
engines/grim/emi/costume/emiskel_component.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_EMI_SKEL_COMPONENT_H
|
||||
#define GRIM_EMI_SKEL_COMPONENT_H
|
||||
|
||||
#include "engines/grim/costume/component.h"
|
||||
|
||||
// This is mostly stubbed for testing the skeletonloading at the moment.
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class Skeleton;
|
||||
|
||||
class EMISkelComponent : public Component {
|
||||
public:
|
||||
EMISkelComponent(Component *parent, int parentID, const char *filename, Component *prevComponent, tag32 tag);
|
||||
~EMISkelComponent();
|
||||
void init() override;
|
||||
void animate() override;
|
||||
int update(uint time) override;
|
||||
void reset() override;
|
||||
void draw() override;
|
||||
|
||||
public:
|
||||
bool _hierShared;
|
||||
Component *_parentModel;
|
||||
Skeleton *_obj;
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
55
engines/grim/emi/costume/emisprite_component.cpp
Normal file
55
engines/grim/emi/costume/emisprite_component.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/grim/emi/costume/emisprite_component.h"
|
||||
#include "engines/grim/emi/costumeemi.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/costume.h"
|
||||
#include "engines/grim/sprite.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
EMISpriteComponent::EMISpriteComponent(Component *p, int parentID, const char *filename, Component *prevComponent, tag32 t) : Component(p, parentID, filename, t), _sprite(nullptr) {
|
||||
}
|
||||
|
||||
EMISpriteComponent::~EMISpriteComponent() {
|
||||
delete _sprite;
|
||||
}
|
||||
|
||||
void EMISpriteComponent::init() {
|
||||
EMICostume *c = static_cast<EMICostume *>(_cost);
|
||||
_sprite = g_resourceloader->loadSprite(_name, c);
|
||||
}
|
||||
|
||||
int EMISpriteComponent::update(uint time) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EMISpriteComponent::reset() {
|
||||
}
|
||||
|
||||
void EMISpriteComponent::draw() {
|
||||
if (_sprite) {
|
||||
_sprite->draw();
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
46
engines/grim/emi/costume/emisprite_component.h
Normal file
46
engines/grim/emi/costume/emisprite_component.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_EMI_SPRITE_COMPONENT_H
|
||||
#define GRIM_EMI_SPRITE_COMPONENT_H
|
||||
|
||||
#include "engines/grim/costume/component.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class Sprite;
|
||||
|
||||
class EMISpriteComponent : public Component {
|
||||
public:
|
||||
EMISpriteComponent(Component *parent, int parentID, const char *filename, Component *prevComponent, tag32 tag);
|
||||
~EMISpriteComponent();
|
||||
void init() override;
|
||||
int update(uint time) override;
|
||||
void reset() override;
|
||||
void draw() override;
|
||||
|
||||
public:
|
||||
Sprite *_sprite;
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
59
engines/grim/emi/costume/emitexi_component.cpp
Normal file
59
engines/grim/emi/costume/emitexi_component.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/grim/costume.h"
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/material.h"
|
||||
#include "engines/grim/savegame.h"
|
||||
#include "engines/grim/emi/costumeemi.h"
|
||||
#include "engines/grim/emi/modelemi.h"
|
||||
#include "engines/grim/emi/costume/emimesh_component.h"
|
||||
#include "engines/grim/emi/costume/emitexi_component.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
EMITexiComponent::EMITexiComponent(Component *parent, int parentID, const char *filename, Component *prevComponent, tag32 tag) : Component(parent, parentID, filename, tag) {
|
||||
}
|
||||
|
||||
EMITexiComponent::~EMITexiComponent() {
|
||||
}
|
||||
|
||||
void EMITexiComponent::init() {
|
||||
EMICostume *c = static_cast<EMICostume *>(_cost);
|
||||
_mat = c->findMaterial(_name);
|
||||
}
|
||||
|
||||
int EMITexiComponent::update(uint time) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EMITexiComponent::setKey(int k) {
|
||||
if (_mat && _mat->getNumTextures() > k)
|
||||
_mat->setActiveTexture(k);
|
||||
}
|
||||
|
||||
void EMITexiComponent::reset() {
|
||||
}
|
||||
|
||||
void EMITexiComponent::draw() {
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
49
engines/grim/emi/costume/emitexi_component.h
Normal file
49
engines/grim/emi/costume/emitexi_component.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_EMI_TEXI_COMPONENT_H
|
||||
#define GRIM_EMI_TEXI_COMPONENT_H
|
||||
|
||||
#include "engines/grim/costume/component.h"
|
||||
#include "engines/grim/material.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/model.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class EMITexiComponent : public Component {
|
||||
public:
|
||||
EMITexiComponent(Component *parent, int parentID, const char *filename, Component *prevComponent, tag32 tag);
|
||||
~EMITexiComponent();
|
||||
|
||||
void init() override;
|
||||
int update(uint time) override;
|
||||
void reset() override;
|
||||
void draw() override;
|
||||
void setKey(int k) override;
|
||||
|
||||
private:
|
||||
Material *_mat;
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
328
engines/grim/emi/costumeemi.cpp
Normal file
328
engines/grim/emi/costumeemi.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/endian.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/costume.h"
|
||||
#include "engines/grim/grim.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/actor.h"
|
||||
#include "engines/grim/emi/costumeemi.h"
|
||||
#include "engines/grim/emi/modelemi.h"
|
||||
#include "engines/grim/emi/skeleton.h"
|
||||
#include "engines/grim/emi/costume/emihead.h"
|
||||
#include "engines/grim/emi/costume/emianim_component.h"
|
||||
#include "engines/grim/emi/costume/emiluavar_component.h"
|
||||
#include "engines/grim/emi/costume/emiluacode_component.h"
|
||||
#include "engines/grim/emi/costume/emimesh_component.h"
|
||||
#include "engines/grim/emi/costume/emiskel_component.h"
|
||||
#include "engines/grim/emi/costume/emisprite_component.h"
|
||||
#include "engines/grim/emi/costume/emitexi_component.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
EMICostume::EMICostume(const Common::String &fname, Actor *owner, Costume *prevCost) :
|
||||
Costume(fname, owner, prevCost), _wearChore(nullptr), _emiSkel(nullptr) {
|
||||
}
|
||||
|
||||
void EMICostume::load(Common::SeekableReadStream *data) {
|
||||
Common::Array<Component *> components;
|
||||
|
||||
_numChores = data->readUint32LE();
|
||||
_chores = new Chore *[_numChores];
|
||||
for (int i = 0; i < _numChores; i++) {
|
||||
uint32 nameLength;
|
||||
Component *prevComponent = nullptr;
|
||||
nameLength = data->readUint32LE();
|
||||
assert(nameLength < 32);
|
||||
|
||||
char name[32];
|
||||
data->read(name, nameLength);
|
||||
float length = data->readFloatLE();
|
||||
int numTracks = data->readUint32LE();
|
||||
|
||||
if (length == 1000)
|
||||
length = -1.0f;
|
||||
else
|
||||
length *= 1000;
|
||||
|
||||
EMIChore *chore = new EMIChore(name, i, this, (int)length, numTracks);
|
||||
_chores[i] = chore;
|
||||
|
||||
for (int k = 0; k < numTracks; k++) {
|
||||
int componentNameLength = data->readUint32LE();
|
||||
|
||||
char *componentName = new char[componentNameLength];
|
||||
data->read(componentName, componentNameLength);
|
||||
|
||||
data->readUint32LE();
|
||||
int parentID = data->readUint32LE();
|
||||
if (parentID == -1 && _prevCostume) {
|
||||
// However, only the first item can actually share the
|
||||
// node hierarchy with the previous costume, so flag
|
||||
// that component so it knows what to do
|
||||
if (i == 0)
|
||||
parentID = -2;
|
||||
prevComponent = _prevCostume->getComponent(0);
|
||||
// Make sure that the component is valid
|
||||
if (!prevComponent->isComponentType('M', 'M', 'D', 'L'))
|
||||
prevComponent = nullptr;
|
||||
}
|
||||
// Actually load the appropriate component
|
||||
Component *component = loadEMIComponent(parentID < 0 ? nullptr : components[parentID], parentID, componentName, prevComponent);
|
||||
if (component) {
|
||||
component->setCostume(this);
|
||||
component->init();
|
||||
chore->addComponent(component);
|
||||
}
|
||||
|
||||
components.push_back(component);
|
||||
|
||||
ChoreTrack &track = chore->_tracks[k];
|
||||
track.numKeys = data->readUint32LE();
|
||||
track.keys = new TrackKey[track.numKeys];
|
||||
track.component = component;
|
||||
track.compID = -1; // -1 means "look at .component"
|
||||
|
||||
for (int j = 0; j < track.numKeys; j++) {
|
||||
float time, value;
|
||||
time = data->readFloatLE();
|
||||
value = data->readFloatLE();
|
||||
track.keys[j].time = (int)(time * 1000);
|
||||
length = MAX(length, time * 1000);
|
||||
track.keys[j].value = (int)value;
|
||||
}
|
||||
delete[] componentName;
|
||||
}
|
||||
}
|
||||
|
||||
_numComponents = components.size();
|
||||
_components = new Component *[_numComponents];
|
||||
for (int i = 0; i < _numComponents; ++i) {
|
||||
_components[i] = components[i];
|
||||
}
|
||||
|
||||
_head = new EMIHead(this);
|
||||
}
|
||||
|
||||
void EMICostume::playChore(int num, uint msecs) {
|
||||
// FIXME: Original EMI can play multiple instances of a chore at the same time.
|
||||
EMIChore *chore = static_cast<EMIChore *>(_chores[num]);
|
||||
if (chore->isWearChore()) {
|
||||
setWearChore(chore);
|
||||
}
|
||||
Costume::playChore(num, msecs);
|
||||
}
|
||||
|
||||
void EMICostume::playChoreLooping(int num, uint msecs) {
|
||||
// FIXME: Original EMI can play multiple instances of a chore at the same time.
|
||||
EMIChore *chore = static_cast<EMIChore *>(_chores[num]);
|
||||
if (chore->isWearChore()) {
|
||||
setWearChore(chore);
|
||||
}
|
||||
Costume::playChoreLooping(num, msecs);
|
||||
}
|
||||
|
||||
Component *EMICostume::loadEMIComponent(Component *parent, int parentID, const char *name, Component *prevComponent) {
|
||||
assert(name[0] == '!');
|
||||
++name;
|
||||
|
||||
char type[5];
|
||||
tag32 tag = 0;
|
||||
memcpy(&tag, name, 4);
|
||||
memcpy(&type, name, 4);
|
||||
type[4] = 0;
|
||||
tag = FROM_BE_32(tag);
|
||||
|
||||
name += 4;
|
||||
|
||||
if (tag == MKTAG('m', 'e', 's', 'h')) {
|
||||
return new EMIMeshComponent(parent, parentID, name, prevComponent, tag, this);
|
||||
} else if (tag == MKTAG('s', 'k', 'e', 'l')) {
|
||||
return new EMISkelComponent(parent, parentID, name, prevComponent, tag);
|
||||
} else if (tag == MKTAG('t', 'e', 'x', 'i')) {
|
||||
return new EMITexiComponent(parent, parentID, name, prevComponent, tag);
|
||||
} else if (tag == MKTAG('a', 'n', 'i', 'm')) {
|
||||
return new EMIAnimComponent(parent, parentID, name, prevComponent, tag);
|
||||
} else if (tag == MKTAG('l', 'u', 'a', 'c')) {
|
||||
return new EMILuaCodeComponent(parent, parentID, name, prevComponent, tag);
|
||||
} else if (tag == MKTAG('l', 'u', 'a', 'v')) {
|
||||
return new EMILuaVarComponent(parent, parentID, name, prevComponent, tag);
|
||||
} else if (tag == MKTAG('s', 'p', 'r', 't')) {
|
||||
return new EMISpriteComponent(parent, parentID, name, prevComponent, tag);
|
||||
} else if (tag == MKTAG('s', 'h', 'a', 'd')) {
|
||||
Debug::warning(Debug::Costumes, "Actor::loadComponentEMI Implement SHAD-handling: %s" , name);
|
||||
} else if (tag == MKTAG('a', 'w', 'g', 't')) {
|
||||
Debug::warning(Debug::Costumes, "Actor::loadComponentEMI Implement AWGT-handling: %s" , name);
|
||||
} else if (tag == MKTAG('s', 'n', 'd', '2')) {
|
||||
// ignore, this is a leftover from an earlier engine.
|
||||
} else {
|
||||
error("Actor::loadComponentEMI missing tag: %s for %s", name, type);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EMICostume::draw() {
|
||||
bool drewMesh = false;
|
||||
for (Common::List<Chore*>::iterator it = _playingChores.begin(); it != _playingChores.end(); ++it) {
|
||||
Chore *c = (*it);
|
||||
if (!c->_playing)
|
||||
continue;
|
||||
for (int i = 0; i < c->_numTracks; ++i) {
|
||||
if (c->_tracks[i].component) {
|
||||
c->_tracks[i].component->draw();
|
||||
if (c->_tracks[i].component->isComponentType('m', 'e', 's', 'h'))
|
||||
drewMesh = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_wearChore && !drewMesh) {
|
||||
_wearChore->getMesh()->draw();
|
||||
}
|
||||
}
|
||||
|
||||
int EMICostume::update(uint time) {
|
||||
for (Common::List<Chore*>::iterator i = _playingChores.begin(); i != _playingChores.end(); ++i) {
|
||||
Chore *c = *i;
|
||||
c->update(time);
|
||||
|
||||
for (int t = 0; t < c->_numTracks; ++t) {
|
||||
if (c->_tracks[t].component) {
|
||||
c->_tracks[t].component->update(time);
|
||||
}
|
||||
}
|
||||
|
||||
if (!c->isPlaying()) {
|
||||
i = _playingChores.erase(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EMICostume::saveState(SaveGame *state) const {
|
||||
Costume::saveState(state);
|
||||
|
||||
for (int i = 0; i < _numChores; ++i) {
|
||||
EMIChore *chore = (EMIChore *)_chores[i];
|
||||
state->writeLESint32(chore->getId());
|
||||
}
|
||||
|
||||
state->writeLESint32(_wearChore ? _wearChore->getChoreId() : -1);
|
||||
}
|
||||
|
||||
bool EMICostume::restoreState(SaveGame *state) {
|
||||
bool ret = Costume::restoreState(state);
|
||||
if (ret) {
|
||||
if (state->saveMinorVersion() >= 11) {
|
||||
EMIChore::Pool &pool = EMIChore::getPool();
|
||||
for (int i = 0; i < _numChores; ++i) {
|
||||
EMIChore *chore = (EMIChore *)_chores[i];
|
||||
int id = state->readLESint32();
|
||||
pool.removeObject(chore->getId());
|
||||
EMIChore* oldChore = pool.getObject(id);
|
||||
if (oldChore) {
|
||||
pool.removeObject(id);
|
||||
oldChore->setId(chore->getId());
|
||||
pool.addObject(oldChore);
|
||||
}
|
||||
chore->setId(id);
|
||||
pool.addObject(chore);
|
||||
}
|
||||
}
|
||||
|
||||
if (state->saveMinorVersion() < 13) {
|
||||
// Used to be active texture IDs for materials. Materials are now
|
||||
// managed by the owner Actor of this Costume.
|
||||
for (uint i = 0; i < _materials.size(); ++i) {
|
||||
state->readLESint32();
|
||||
}
|
||||
}
|
||||
|
||||
int id = state->readLESint32();
|
||||
if (id >= 0) {
|
||||
EMIChore *chore = static_cast<EMIChore *>(_chores[id]);
|
||||
setWearChore(chore);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Material *EMICostume::findMaterial(const Common::String &name) {
|
||||
return _owner->findMaterial(name);
|
||||
}
|
||||
|
||||
Material *EMICostume::loadMaterial(const Common::String &name, bool clamp) {
|
||||
MaterialPtr mat = _owner->loadMaterial(name, clamp);
|
||||
if (mat) {
|
||||
// Save a reference to the material, so it will not be freed during the
|
||||
// lifetime of this costume.
|
||||
if (Common::find(_materials.begin(), _materials.end(), mat) == _materials.end())
|
||||
_materials.push_back(mat);
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
void EMICostume::setWearChore(EMIChore *chore) {
|
||||
if (chore != _wearChore) {
|
||||
_wearChore = chore;
|
||||
|
||||
if (_emiSkel) {
|
||||
_emiSkel->reset();
|
||||
}
|
||||
_emiSkel = chore->getSkeleton();
|
||||
}
|
||||
}
|
||||
|
||||
void EMICostume::setHead(const char *joint, const Math::Vector3d &offset) {
|
||||
static_cast<EMIHead *>(_head)->setJoint(joint, offset);
|
||||
}
|
||||
|
||||
void EMICostume::setHeadLimits(float yawRange, float maxPitch, float minPitch) {
|
||||
static_cast<EMIHead *>(_head)->setLimits(yawRange, maxPitch, minPitch);
|
||||
}
|
||||
|
||||
EMIModel *EMICostume::getEMIModel() const {
|
||||
if (!_wearChore)
|
||||
return nullptr;
|
||||
return _wearChore->getMesh()->_obj;
|
||||
}
|
||||
|
||||
EMIModel *EMICostume::getEMIModel(int num) const {
|
||||
if (num >= _numChores) {
|
||||
return nullptr;
|
||||
}
|
||||
EMIChore *chore = static_cast<EMIChore *>(_chores[num]);
|
||||
if (chore == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
EMIMeshComponent *mesh = chore->getMesh();
|
||||
if (mesh == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return mesh->_obj;
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
77
engines/grim/emi/costumeemi.h
Normal file
77
engines/grim/emi/costumeemi.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_COSTUMEEMI_H
|
||||
#define GRIM_COSTUMEEMI_H
|
||||
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "engines/grim/object.h"
|
||||
#include "engines/grim/costume.h"
|
||||
#include "engines/grim/emi/costume/emichore.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
typedef uint32 tag32;
|
||||
|
||||
class EMISkelComponent;
|
||||
class EMIMeshComponent;
|
||||
class Material;
|
||||
class EMIModel;
|
||||
|
||||
class EMICostume : public Costume {
|
||||
public:
|
||||
EMICostume(const Common::String &filename, Actor *owner, Costume *prevCost);
|
||||
|
||||
void load(Common::SeekableReadStream *data) override;
|
||||
|
||||
void draw() override;
|
||||
int update(uint time) override;
|
||||
|
||||
void playChore(int num, uint msecs = 0) override;
|
||||
void playChoreLooping(int num, uint msecs = 0) override;
|
||||
|
||||
void saveState(SaveGame *state) const override;
|
||||
bool restoreState(SaveGame *state) override;
|
||||
|
||||
Material *loadMaterial(const Common::String &name, bool clamp);
|
||||
Material *findMaterial(const Common::String &name);
|
||||
|
||||
void setHead(const char *joint, const Math::Vector3d &offset);
|
||||
void setHeadLimits(float yawRange, float maxPitch, float minPitch);
|
||||
|
||||
EMIModel *getEMIModel() const;
|
||||
EMIModel *getEMIModel(int num) const;
|
||||
public:
|
||||
EMIChore *_wearChore;
|
||||
EMISkelComponent *_emiSkel;
|
||||
private:
|
||||
Common::List<ObjectPtr<Material> > _materials;
|
||||
static bool compareChores(const Chore *c1, const Chore *c2);
|
||||
Component *loadEMIComponent(Component *parent, int parentID, const char *name, Component *prevComponent);
|
||||
void setWearChore(EMIChore *chore);
|
||||
|
||||
friend class Chore;
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
334
engines/grim/emi/emi.cpp
Normal file
334
engines/grim/emi/emi.cpp
Normal file
@@ -0,0 +1,334 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/grim/emi/emi.h"
|
||||
#include "engines/grim/emi/emi_registry.h"
|
||||
#include "engines/grim/emi/lua_v2.h"
|
||||
#include "engines/grim/primitives.h"
|
||||
#include "engines/grim/set.h"
|
||||
#include "engines/grim/gfx_base.h"
|
||||
#include "engines/grim/actor.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
EMIEngine *g_emi = nullptr;
|
||||
|
||||
EMIEngine::EMIEngine(OSystem *syst, uint32 gameFlags, GrimGameType gameType, Common::Platform platform, Common::Language language) :
|
||||
GrimEngine(syst, gameFlags, gameType, platform, language), _sortOrderInvalidated(false), _textObjectsSortOrderInvalidated(true) {
|
||||
g_emi = this;
|
||||
g_emiregistry = new EmiRegistry();
|
||||
}
|
||||
|
||||
EMIEngine::~EMIEngine() {
|
||||
g_emi = nullptr;
|
||||
delete g_emiregistry;
|
||||
g_emiregistry = nullptr;
|
||||
}
|
||||
|
||||
LuaBase *EMIEngine::createLua() {
|
||||
return new Lua_V2();
|
||||
}
|
||||
|
||||
const char *EMIEngine::getUpdateFilename() {
|
||||
if (getGamePlatform() == Common::kPlatformWindows && !(getGameFlags() & ADGF_DEMO)) {
|
||||
switch (getGameLanguage()) {
|
||||
case Common::FR_FRA:
|
||||
return "MonkeyUpdate_FRA.exe";
|
||||
break;
|
||||
case Common::DE_DEU:
|
||||
return "MonkeyUpdate_DEU.exe";
|
||||
break;
|
||||
case Common::IT_ITA:
|
||||
return "MonkeyUpdate_ITA.exe";
|
||||
break;
|
||||
case Common::PT_BRA:
|
||||
return "MonkeyUpdate_BRZ.exe";
|
||||
break;
|
||||
case Common::ES_ESP:
|
||||
return "MonkeyUpdate_ESP.exe";
|
||||
break;
|
||||
case Common::EN_ANY:
|
||||
case Common::EN_GRB:
|
||||
case Common::EN_USA:
|
||||
default:
|
||||
return "MonkeyUpdate.exe";
|
||||
break;
|
||||
}
|
||||
} else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EMIEngine::pushText() {
|
||||
for (TextObject *t : TextObject::getPool()) {
|
||||
t->incStackLevel();
|
||||
}
|
||||
invalidateTextObjectsSortOrder();
|
||||
}
|
||||
|
||||
void EMIEngine::popText() {
|
||||
Common::List<TextObject *> toDelete;
|
||||
|
||||
for (TextObject *t : TextObject::getPool()) {
|
||||
if (t->getStackLevel() == 0) {
|
||||
warning("Text stack top not empty; deleting object");
|
||||
toDelete.push_back(t);
|
||||
} else {
|
||||
t->decStackLevel();
|
||||
}
|
||||
}
|
||||
|
||||
while (!toDelete.empty()) {
|
||||
TextObject *t = toDelete.front();
|
||||
toDelete.pop_front();
|
||||
delete t;
|
||||
}
|
||||
|
||||
invalidateTextObjectsSortOrder();
|
||||
}
|
||||
|
||||
void EMIEngine::purgeText() {
|
||||
Common::List<TextObject *> toDelete;
|
||||
|
||||
for (TextObject *t : TextObject::getPool()) {
|
||||
if (t->getStackLevel() == 0) {
|
||||
toDelete.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
while (!toDelete.empty()) {
|
||||
TextObject *t = toDelete.front();
|
||||
toDelete.pop_front();
|
||||
delete t;
|
||||
}
|
||||
|
||||
invalidateTextObjectsSortOrder();
|
||||
}
|
||||
|
||||
void EMIEngine::drawNormalMode() {
|
||||
_currSet->setupCamera();
|
||||
|
||||
g_driver->set3DMode();
|
||||
|
||||
if (_setupChanged) {
|
||||
cameraPostChangeHandle(_currSet->getSetup());
|
||||
_setupChanged = false;
|
||||
}
|
||||
|
||||
// Draw actors
|
||||
buildActiveActorsList();
|
||||
sortActiveActorsList();
|
||||
sortLayers();
|
||||
|
||||
Bitmap *background = _currSet->getCurrSetup()->_bkgndBm;
|
||||
background->_data->load();
|
||||
uint32 numLayers = background->_data->_numLayers;
|
||||
|
||||
Common::List<Layer *>::const_iterator nextLayer = _layers.begin();
|
||||
Common::List<Actor *>::const_iterator nextActor = _activeActors.begin();
|
||||
int32 currentLayer = numLayers - 1;
|
||||
|
||||
int aso = (nextActor != _activeActors.end()) ? (*nextActor)->getEffectiveSortOrder() : -1;
|
||||
int lso = (nextLayer != _layers.end()) ? (*nextLayer)->getSortOrder() : -1;
|
||||
int bgso = currentLayer * 10;
|
||||
|
||||
// interleave actors, background layers and additional stand-alone layers based
|
||||
// on their sortorder
|
||||
//
|
||||
// priority for same sort order:
|
||||
// background layers (highest priority)
|
||||
// stand-alone layers
|
||||
// actors
|
||||
while (1) {
|
||||
if (aso >= 0 && aso > bgso && aso > lso) {
|
||||
if ((*nextActor)->isVisible() && ! (*nextActor)->isInOverworld())
|
||||
(*nextActor)->draw();
|
||||
nextActor++;
|
||||
aso = (nextActor != _activeActors.end()) ? (*nextActor)->getEffectiveSortOrder() : -1;
|
||||
continue;
|
||||
}
|
||||
if (bgso >= 0 && bgso >= lso && bgso >= aso) {
|
||||
background->drawLayer(currentLayer);
|
||||
currentLayer--;
|
||||
bgso = currentLayer * 10;
|
||||
continue;
|
||||
}
|
||||
if (lso >= 0 && lso > bgso && lso >= aso) {
|
||||
(*nextLayer)->draw();
|
||||
nextLayer++;
|
||||
lso = (nextLayer != _layers.end()) ? (*nextLayer)->getSortOrder() : -1;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear depth buffer before starting to draw the Overworld:
|
||||
* - all actors of the Overworld should cover any non-Overworld drawings
|
||||
* - Overworld actors need to use the depth Buffer so that e.g. the pause screen
|
||||
* is drawn above the inventory
|
||||
*/
|
||||
g_driver->clearDepthBuffer();
|
||||
|
||||
g_driver->drawDimPlane();
|
||||
|
||||
for (Actor *a : _activeActors) {
|
||||
if (a->isInOverworld())
|
||||
a->draw();
|
||||
}
|
||||
|
||||
// Draw Primitives
|
||||
for (PrimitiveObject *p : PrimitiveObject::getPool()) {
|
||||
p->draw();
|
||||
}
|
||||
|
||||
flagRefreshShadowMask(false);
|
||||
|
||||
}
|
||||
|
||||
void EMIEngine::storeSaveGameImage(SaveGame *state) {
|
||||
unsigned int width = 160, height = 120;
|
||||
Bitmap *screenshot = g_driver->getScreenshot(width, height, true);
|
||||
if (!screenshot) {
|
||||
warning("Unable to store screenshot.");
|
||||
return;
|
||||
}
|
||||
|
||||
// screenshots are not using the whole size of the texture
|
||||
// copy the actual screenshot to the correct position
|
||||
unsigned int texWidth = 256, texHeight = 128;
|
||||
unsigned int size = texWidth * texHeight;
|
||||
Graphics::Surface tmp = screenshot->getData(0);
|
||||
Graphics::Surface *buffer = tmp.scale(texWidth, texHeight, true);
|
||||
buffer->convertToInPlace(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
|
||||
|
||||
state->beginSection('SIMG');
|
||||
uint16 *data = (uint16 *)buffer->getPixels();
|
||||
for (unsigned int l = 0; l < size; l++) {
|
||||
state->writeLEUint16(data[l]);
|
||||
}
|
||||
state->endSection();
|
||||
delete screenshot;
|
||||
buffer->free();
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
void EMIEngine::temporaryStoreSaveGameImage() {
|
||||
// store current rendered screen in g_driver
|
||||
g_grim->updateDisplayScene();
|
||||
g_driver->storeDisplay();
|
||||
}
|
||||
|
||||
void EMIEngine::updateDrawMode() {
|
||||
// For EMI, draw mode is just like normal mode with frozen frame time.
|
||||
updateNormalMode();
|
||||
}
|
||||
|
||||
void EMIEngine::invalidateTextObjectsSortOrder() {
|
||||
_textObjectsSortOrderInvalidated = true;
|
||||
}
|
||||
|
||||
void EMIEngine::invalidateActiveActorsList() {
|
||||
GrimEngine::invalidateActiveActorsList();
|
||||
invalidateSortOrder();
|
||||
}
|
||||
|
||||
void EMIEngine::invalidateSortOrder() {
|
||||
_sortOrderInvalidated = true;
|
||||
}
|
||||
|
||||
bool EMIEngine::compareTextLayer(const TextObject *x, const TextObject *y) {
|
||||
int xl = x->getLayer();
|
||||
int yl = y->getLayer();
|
||||
|
||||
if (xl == yl) {
|
||||
return x->getId() < y->getId();
|
||||
} else {
|
||||
return xl < yl;
|
||||
}
|
||||
}
|
||||
|
||||
bool EMIEngine::compareLayer(const Layer *x, const Layer *y) {
|
||||
return x->getSortOrder() > y->getSortOrder();
|
||||
}
|
||||
|
||||
void EMIEngine::drawTextObjects() {
|
||||
sortTextObjects();
|
||||
for (TextObject *t : _textObjects) {
|
||||
t->draw();
|
||||
}
|
||||
}
|
||||
|
||||
void EMIEngine::sortTextObjects() {
|
||||
if (!_textObjectsSortOrderInvalidated)
|
||||
return;
|
||||
|
||||
_textObjectsSortOrderInvalidated = false;
|
||||
|
||||
_textObjects.clear();
|
||||
for (TextObject *t : TextObject::getPool()) {
|
||||
if (t->getStackLevel() == 0) {
|
||||
_textObjects.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
Common::sort(_textObjects.begin(), _textObjects.end(), compareTextLayer);
|
||||
}
|
||||
|
||||
void EMIEngine::sortLayers() {
|
||||
_layers.clear();
|
||||
for (Layer *l : Layer::getPool()) {
|
||||
_layers.push_back(l);
|
||||
}
|
||||
|
||||
Common::sort(_layers.begin(), _layers.end(), compareLayer);
|
||||
}
|
||||
|
||||
bool EMIEngine::compareActor(const Actor *x, const Actor *y) {
|
||||
if (x->getEffectiveSortOrder() == y->getEffectiveSortOrder()) {
|
||||
Set::Setup *setup = g_grim->getCurrSet()->getCurrSetup();
|
||||
Math::Matrix4 camRot = setup->_rot;
|
||||
|
||||
Math::Vector3d xp(x->getWorldPos() - setup->_pos);
|
||||
Math::Vector3d yp(y->getWorldPos() - setup->_pos);
|
||||
xp = xp * camRot.getRotation();
|
||||
yp = yp * camRot.getRotation();
|
||||
|
||||
if (fabs(xp.z() - yp.z()) < 0.001f) {
|
||||
return x->getId() < y->getId();
|
||||
} else {
|
||||
return xp.z() > yp.z();
|
||||
}
|
||||
}
|
||||
return x->getEffectiveSortOrder() > y->getEffectiveSortOrder();
|
||||
}
|
||||
|
||||
void EMIEngine::sortActiveActorsList() {
|
||||
if (!_sortOrderInvalidated) {
|
||||
return;
|
||||
}
|
||||
|
||||
_sortOrderInvalidated = false;
|
||||
|
||||
Common::sort(_activeActors.begin(), _activeActors.end(), compareActor);
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
73
engines/grim/emi/emi.h
Normal file
73
engines/grim/emi/emi.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef EMI_ENGINE_H
|
||||
#define EMI_ENGINE_H
|
||||
|
||||
#include "engines/grim/grim.h"
|
||||
#include "engines/grim/emi/layer.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class TextObject;
|
||||
class Actor;
|
||||
|
||||
class EMIEngine : public GrimEngine {
|
||||
public:
|
||||
EMIEngine(OSystem *syst, uint32 gameFlags, GrimGameType gameType, Common::Platform platform, Common::Language language);
|
||||
virtual ~EMIEngine();
|
||||
|
||||
const char *getUpdateFilename() override;
|
||||
|
||||
void pushText();
|
||||
void popText();
|
||||
void purgeText();
|
||||
|
||||
void invalidateActiveActorsList() override;
|
||||
void invalidateTextObjectsSortOrder() override;
|
||||
void invalidateSortOrder();
|
||||
void sortActiveActorsList();
|
||||
void temporaryStoreSaveGameImage();
|
||||
void storeSaveGameImage(SaveGame *state) override;
|
||||
|
||||
private:
|
||||
LuaBase *createLua() override;
|
||||
void drawNormalMode() override;
|
||||
void updateDrawMode() override;
|
||||
static bool compareTextLayer(const TextObject *x, const TextObject *y);
|
||||
void drawTextObjects() override;
|
||||
static bool compareActor(const Actor *x, const Actor *y);
|
||||
void sortTextObjects();
|
||||
static bool compareLayer(const Layer *x, const Layer *y);
|
||||
void sortLayers();
|
||||
|
||||
Common::List<TextObject *> _textObjects;
|
||||
Common::List<Layer *> _layers;
|
||||
|
||||
bool _textObjectsSortOrderInvalidated;
|
||||
bool _sortOrderInvalidated;
|
||||
};
|
||||
|
||||
extern EMIEngine *g_emi;
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
178
engines/grim/emi/emi_registry.cpp
Normal file
178
engines/grim/emi/emi_registry.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/grim/emi/emi_registry.h"
|
||||
#include "engines/grim/debug.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
EmiRegistry *g_emiregistry = nullptr;
|
||||
|
||||
const char *EmiRegistry::_translTable[][2] = {
|
||||
{"speech_mode", ""}, // Translated key not needed, see below
|
||||
{"vocvolume", "speech_volume"},
|
||||
{"sfxvolume", "sfx_volume"},
|
||||
{"musvolume", "music_volume"},
|
||||
{"textspeed", "talkspeed"},
|
||||
{"gamma", "gamma"},
|
||||
{"joystick_enabled", "joystick_enabled"},
|
||||
{"analog_mode", "analog_joystick_mode"},
|
||||
{"subtitles", "movie_subtitles"},
|
||||
{"camera_mode", "camera_relative_mode"},
|
||||
{"shadowfx", "shadow_effects"},
|
||||
{"vocfx", "audio_effects"},
|
||||
{"miscfx", "misc_video_effects"},
|
||||
{"moviequality", "movie_quality"},
|
||||
{"musicquality", "music_quality"},
|
||||
{nullptr,nullptr}
|
||||
};
|
||||
|
||||
const char *EmiRegistry::_boolValues[] = {
|
||||
"joystick_enabled",
|
||||
"analog_mode",
|
||||
"subtitles",
|
||||
"camera_mode",
|
||||
"vocfx",
|
||||
"moviequality",
|
||||
"musicquality",
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
||||
EmiRegistry::EmiRegistry() {
|
||||
int i = 0;
|
||||
while (_translTable[i][0] != nullptr) {
|
||||
_transMap[_translTable[i][0]] = _translTable[i][1];
|
||||
++i;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (_boolValues[i] != nullptr) {
|
||||
_boolSet[_boolValues[i]] = true;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
uint EmiRegistry::convertTalkSpeedToGUI(uint talkspeed) const {
|
||||
return CLIP<uint>(talkspeed * 255 / 10, 0, 255);
|
||||
}
|
||||
|
||||
uint EmiRegistry::convertTalkSpeedFromGUI(uint talkspeed) const {
|
||||
return CLIP<uint>(talkspeed * 10 / 255, 1, 10);
|
||||
}
|
||||
|
||||
const Common::String EmiRegistry::convertGammaToRegistry(float gamma) const {
|
||||
return Common::String().format("%.2f", gamma);
|
||||
}
|
||||
|
||||
float EmiRegistry::convertGammaFromRegistry(const Common::String &gamma) const {
|
||||
float gamma_f;
|
||||
sscanf(gamma.c_str(), "%f", &gamma_f);
|
||||
return CLIP<float>(gamma_f, 0.5, 1.5);
|
||||
}
|
||||
|
||||
uint EmiRegistry::convertVolumeToMixer(uint emiVolume) const {
|
||||
float vol = float(emiVolume - 25)/(100 - 25)*Audio::Mixer::kMaxMixerVolume;
|
||||
return CLIP<uint>(uint(vol), 0, Audio::Mixer::kMaxMixerVolume);
|
||||
}
|
||||
|
||||
uint EmiRegistry::convertVolumeFromMixer(uint volume) const {
|
||||
float vol = float(volume)*(100 - 25)/Audio::Mixer::kMaxMixerVolume + 25;
|
||||
return CLIP<uint>(uint(vol), 0, Audio::Mixer::kMaxMixerVolume);
|
||||
}
|
||||
|
||||
uint EmiRegistry::convertSpeechModeFromGUI(bool subtitles, bool speechMute) const {
|
||||
if (!subtitles && !speechMute) // Speech only
|
||||
return 2;
|
||||
else if (subtitles && !speechMute) // Speech and subtitles
|
||||
return 3;
|
||||
else if (subtitles && speechMute) // Subtitles only
|
||||
return 1;
|
||||
else
|
||||
warning("Wrong configuration: Both subtitles and speech are off. Assuming subtitles only");
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool EmiRegistry::Get(const Common::String &key, float &res) const {
|
||||
Debug::debug(Debug::Engine, "GetResidualVMPreference(%s)", key.c_str());
|
||||
|
||||
if (!_transMap.contains(key))
|
||||
return false;
|
||||
|
||||
res = 0.;
|
||||
|
||||
if (key == "speech_mode") {
|
||||
if (!(ConfMan.hasKey("subtitles") && ConfMan.hasKey("speech_mute")))
|
||||
return false;
|
||||
|
||||
res = convertSpeechModeFromGUI(ConfMan.getBool("subtitles"), ConfMan.getBool("speech_mute"));
|
||||
} else {
|
||||
if (!(ConfMan.hasKey(_transMap[key])))
|
||||
return false;
|
||||
|
||||
if (key == "vocvolume" || key == "sfxvolume" || key == "musvolume")
|
||||
res = convertVolumeFromMixer(ConfMan.getInt(_transMap[key]));
|
||||
else if (key == "textspeed")
|
||||
res = convertTalkSpeedFromGUI(ConfMan.getInt(_transMap[key]));
|
||||
else if (key == "gamma")
|
||||
res = convertGammaFromRegistry(ConfMan.get(_transMap[key]));
|
||||
else if (key == "shadowfx")
|
||||
res = ConfMan.getBool(_transMap[key]) + 1;
|
||||
else if (_boolSet.contains(key))
|
||||
res = ConfMan.getBool(_transMap[key]);
|
||||
else
|
||||
res = ConfMan.getInt(_transMap[key]);
|
||||
}
|
||||
|
||||
Debug::debug(Debug::Engine, "Pushing %f", res);
|
||||
return true;
|
||||
}
|
||||
|
||||
void EmiRegistry::Set(const Common::String &key, float &value) {
|
||||
Debug::debug(Debug::Engine, "SetResidualVMPreference(%s, %f)", key.c_str(), value);
|
||||
|
||||
if (!_transMap.contains(key))
|
||||
return;
|
||||
|
||||
uint value_i = uint(value);
|
||||
|
||||
if (key == "speech_mode") {
|
||||
ConfMan.setBool("subtitles", (value_i == 1 || value_i == 3));
|
||||
ConfMan.setBool("speech_mute", (value_i == 1));
|
||||
} else if (key == "vocvolume" || key == "sfxvolume" || key == "musvolume")
|
||||
ConfMan.setInt(_transMap[key], convertVolumeToMixer(value_i));
|
||||
else if (key == "textspeed")
|
||||
ConfMan.setInt(_transMap[key], convertTalkSpeedToGUI(value_i));
|
||||
else if (key == "gamma")
|
||||
ConfMan.set(_transMap[key], convertGammaToRegistry(value));
|
||||
else if (key == "shadowfx")
|
||||
ConfMan.setBool(_transMap[key], (value_i == 2));
|
||||
else if (_boolSet.contains(key))
|
||||
ConfMan.setBool(_transMap[key], (value_i == 1));
|
||||
else
|
||||
ConfMan.setInt(_transMap[key], value_i);
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
63
engines/grim/emi/emi_registry.h
Normal file
63
engines/grim/emi/emi_registry.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef EMI_REGISTRY_H
|
||||
#define EMI_REGISTRY_H
|
||||
|
||||
#include "common/str.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-str.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class EmiRegistry {
|
||||
public:
|
||||
EmiRegistry();
|
||||
~EmiRegistry() { }
|
||||
|
||||
bool Get(const Common::String &key, float &res) const;
|
||||
void Set(const Common::String &key, float &value);
|
||||
|
||||
private:
|
||||
uint convertVolumeToMixer(uint volume) const;
|
||||
uint convertVolumeFromMixer(uint volume) const;
|
||||
uint convertTalkSpeedToGUI(uint talkspeed) const;
|
||||
uint convertTalkSpeedFromGUI(uint talkspeed) const;
|
||||
bool convertSubtitlesToGUI(uint speechmode) const;
|
||||
bool convertSpeechMuteToGUI(uint speechmode) const;
|
||||
uint convertSpeechModeFromGUI(bool subtitles, bool speechMute) const;
|
||||
const Common::String convertGammaToRegistry(float gamma) const;
|
||||
float convertGammaFromRegistry(const Common::String &gamma) const;
|
||||
|
||||
typedef Common::HashMap<Common::String, bool, Common::IgnoreCase_Hash> StringSet;
|
||||
|
||||
Common::StringMap _transMap;
|
||||
StringSet _boolSet;
|
||||
|
||||
static const char *_boolValues[];
|
||||
static const char *_translTable[][2];
|
||||
};
|
||||
|
||||
extern EmiRegistry *g_emiregistry;
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
82
engines/grim/emi/layer.cpp
Normal file
82
engines/grim/emi/layer.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/str.h"
|
||||
|
||||
#include "engines/grim/emi/layer.h"
|
||||
#include "engines/grim/bitmap.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
Layer::Layer() : _filename(""), _sortOrder(0), _frame(0), _bitmap(nullptr) {
|
||||
}
|
||||
|
||||
Layer::Layer(const Common::String &filename, int sortOrder) : _filename(filename), _sortOrder(sortOrder), _frame(0) {
|
||||
_bitmap = Bitmap::create(filename);
|
||||
}
|
||||
|
||||
Layer::~Layer() {
|
||||
delete _bitmap;
|
||||
}
|
||||
|
||||
void Layer::draw() {
|
||||
_bitmap->drawLayer(_frame);
|
||||
}
|
||||
|
||||
void Layer::setFrame(int frame) {
|
||||
int numframes = _bitmap->getNumLayers();
|
||||
if (frame >= numframes || frame < 0) {
|
||||
warning("Layer::setFrame: invalid frame number: %d, numLayers: %d", frame, numframes);
|
||||
return;
|
||||
}
|
||||
_frame = frame;
|
||||
}
|
||||
|
||||
void Layer::setSortOrder(int order) {
|
||||
_sortOrder = order;
|
||||
}
|
||||
|
||||
void Layer::advanceFrame(int num) {
|
||||
_frame += num;
|
||||
int numframes = _bitmap->getNumLayers();
|
||||
_frame %= numframes;
|
||||
}
|
||||
|
||||
void Layer::saveState(SaveGame *state) {
|
||||
if (_bitmap) {
|
||||
state->writeBool(true);
|
||||
state->writeString(_bitmap->getFilename());
|
||||
} else {
|
||||
state->writeBool(false);
|
||||
}
|
||||
state->writeLESint32(_frame);
|
||||
state->writeLESint32(_sortOrder);
|
||||
}
|
||||
|
||||
void Layer::restoreState(SaveGame *state) {
|
||||
bool hasBitmap = state->readBool();
|
||||
if (hasBitmap)
|
||||
_bitmap = Bitmap::create(state->readString());
|
||||
_frame = state->readLESint32();
|
||||
_sortOrder = state->readLESint32();
|
||||
}
|
||||
|
||||
}
|
||||
59
engines/grim/emi/layer.h
Normal file
59
engines/grim/emi/layer.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_LAYER_H
|
||||
#define GRIM_LAYER_H
|
||||
|
||||
#include "common/endian.h"
|
||||
|
||||
#include "engines/grim/pool.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class Bitmap;
|
||||
|
||||
class Layer : public PoolObject<Layer> {
|
||||
public:
|
||||
Layer();
|
||||
Layer(const Common::String &filename, int sortOrder);
|
||||
~Layer();
|
||||
|
||||
static int32 getStaticTag() { return MKTAG('L','A','Y','R'); }
|
||||
|
||||
void draw();
|
||||
void setFrame(int frame);
|
||||
void setSortOrder(int order);
|
||||
void advanceFrame(int num);
|
||||
int getSortOrder() const { return _sortOrder; }
|
||||
|
||||
void saveState(SaveGame *state);
|
||||
void restoreState(SaveGame *state);
|
||||
|
||||
private:
|
||||
Common::String _filename;
|
||||
Bitmap *_bitmap;
|
||||
int _sortOrder;
|
||||
int _frame;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
914
engines/grim/emi/lua_v2.cpp
Normal file
914
engines/grim/emi/lua_v2.cpp
Normal file
@@ -0,0 +1,914 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/savefile.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "engines/grim/emi/lua_v2.h"
|
||||
#include "engines/grim/emi/emi_registry.h"
|
||||
#include "engines/grim/emi/sound/emisound.h"
|
||||
#include "engines/grim/lua/lauxlib.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/set.h"
|
||||
#include "engines/grim/grim.h"
|
||||
#include "engines/grim/gfx_base.h"
|
||||
#include "engines/grim/font.h"
|
||||
#include "engines/grim/emi/layer.h"
|
||||
#include "engines/grim/emi/emi.h"
|
||||
#include "engines/grim/movie/movie.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
void Lua_V2::UndimAll() {
|
||||
g_driver->setDimLevel(0);
|
||||
warning("Lua_V2::UndimAll: stub");
|
||||
}
|
||||
|
||||
void Lua_V2::UndimRegion() {
|
||||
lua_Object regionObj = lua_getparam(1);
|
||||
|
||||
if (lua_isnumber(regionObj)) {
|
||||
int region = (int)lua_getnumber(regionObj);
|
||||
// FIXME func(region);
|
||||
warning("Lua_V2::UndimRegion: region: %d", region);
|
||||
} else {
|
||||
lua_pushnil();
|
||||
// HACK: The demo uses this to undim the intro-screen.
|
||||
// thus UndimRegion(nil) might mean UndimScreen.
|
||||
g_driver->setDimLevel(0);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::DimScreen() {
|
||||
lua_Object dimObj = lua_getparam(1);
|
||||
float dim = 0.6999f;
|
||||
|
||||
if (lua_isnumber(dimObj))
|
||||
dim = lua_getnumber(dimObj);
|
||||
|
||||
g_driver->setDimLevel(dim);
|
||||
// FIXME func(dim);
|
||||
warning("Lua_V2::DimScreen: dim: %f", dim);
|
||||
}
|
||||
|
||||
void Lua_V2::MakeCurrentSetup() {
|
||||
lua_Object setupObj = lua_getparam(1);
|
||||
if (lua_isnumber(setupObj)) {
|
||||
int num = (int)lua_getnumber(setupObj);
|
||||
g_grim->makeCurrentSetup(num);
|
||||
} else if (lua_isstring(setupObj)) {
|
||||
const char *setupName = lua_getstring(setupObj);
|
||||
error("Lua_V2::MakeCurrentSetup: Not implemented case: setup: %s", setupName);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::LockBackground() {
|
||||
lua_Object filenameObj = lua_getparam(1);
|
||||
|
||||
if (!lua_isstring(filenameObj)) {
|
||||
lua_pushnil();
|
||||
return;
|
||||
}
|
||||
const char *filename = lua_getstring(filenameObj);
|
||||
warning("Lua_V2::LockBackground, filename: %s", filename);
|
||||
// FIXME: implement missing rest part of code
|
||||
}
|
||||
|
||||
void Lua_V2::UnLockBackground() {
|
||||
lua_Object filenameObj = lua_getparam(1);
|
||||
|
||||
if (!lua_isstring(filenameObj)) {
|
||||
lua_pushnil();
|
||||
return;
|
||||
}
|
||||
|
||||
const char *filename = lua_getstring(filenameObj);
|
||||
// FIXME: implement missin code
|
||||
warning("Lua_V2::UnLockBackground: stub, filename: %s", filename);
|
||||
}
|
||||
|
||||
void Lua_V2::MakeScreenTextures() {
|
||||
lua_Object indexObj = lua_getparam(1);
|
||||
|
||||
if (!lua_isnil(indexObj) && lua_isnumber(indexObj)) {
|
||||
//int index = (int)lua_getnumber(indexObj);
|
||||
// The index does not seem to matter
|
||||
|
||||
g_driver->makeScreenTextures();
|
||||
lua_pushnumber(1.0);
|
||||
} else {
|
||||
lua_pushnil();
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::ClearSpecialtyTexture() {
|
||||
// This seems to be used in the save/load menu
|
||||
// Not sure why the specialty textures need to be cleared.
|
||||
warning("Lua_V2::ClearSpecialtyTexture: stub");
|
||||
}
|
||||
|
||||
void Lua_V2::LoadBundle() {
|
||||
lua_Object paramObj = lua_getparam(1);
|
||||
if (lua_isstring(paramObj) || lua_isnil(paramObj)) {
|
||||
const char *name = lua_getstring(paramObj);
|
||||
// FIXME: implement missing function
|
||||
/* if (!func(name))
|
||||
lua_pushnil();
|
||||
else*/
|
||||
lua_pushnumber(1.0);
|
||||
warning("Lua_V2::LoadBundle: stub, name: %s", name);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::AreWeInternational() {
|
||||
if (g_grim->getGameLanguage() != Common::EN_ANY)
|
||||
lua_pushnumber(1.0);
|
||||
}
|
||||
|
||||
void Lua_V2::GetCPUSpeed() {
|
||||
lua_pushnumber(500); // anything above 333 make best configuration
|
||||
}
|
||||
|
||||
// This should be correct, judging by the Demo
|
||||
// the only real difference from L1 is the lack of looping
|
||||
void Lua_V2::StartMovie() {
|
||||
lua_Object name = lua_getparam(1);
|
||||
lua_Object subtitlesObj = lua_getparam(2);
|
||||
|
||||
if (!lua_isstring(name)) {
|
||||
lua_pushnil();
|
||||
return;
|
||||
}
|
||||
Lua_V1::CleanBuffer();
|
||||
|
||||
bool showSubtitles = false;
|
||||
if (lua_isnumber(subtitlesObj)) {
|
||||
if ((int)lua_getnumber(subtitlesObj)) {
|
||||
showSubtitles = true;
|
||||
}
|
||||
}
|
||||
if (g_grim->getGameFlags() & ADGF_DEMO) {
|
||||
showSubtitles = true;
|
||||
}
|
||||
|
||||
GrimEngine::EngineMode prevEngineMode = g_grim->getMode();
|
||||
g_grim->setMode(GrimEngine::SmushMode);
|
||||
g_grim->setMovieSubtitle(nullptr);
|
||||
bool result = g_movie->play(lua_getstring(name), false, 0, 0, true, showSubtitles);
|
||||
if (!result)
|
||||
g_grim->setMode(prevEngineMode);
|
||||
pushbool(result);
|
||||
|
||||
// The following line causes issues after 9547a9b61674546077301bf09f89a2d120046d8e
|
||||
//g_grim->setMode(GrimEngine::SmushMode);
|
||||
}
|
||||
|
||||
void Lua_V2::EscapeMovie() {
|
||||
g_movie->stop();
|
||||
}
|
||||
|
||||
void Lua_V2::IsMoviePlaying() {
|
||||
pushbool(g_movie->isPlaying());
|
||||
}
|
||||
|
||||
void Lua_V2::SetActiveCD() {
|
||||
lua_Object cdObj = lua_getparam(1);
|
||||
int cd = (int)lua_getnumber(cdObj);
|
||||
|
||||
if (cd == 1 || cd == 2) {
|
||||
warning("Lua_V2::GetActiveCD: set to CD: %d", cd);
|
||||
// FIXME
|
||||
lua_pushnumber(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::GetActiveCD() {
|
||||
// FIXME: return current CD number 1 or 2, original can also avoid push any numer
|
||||
warning("Lua_V2::GetActiveCD: return const CD 1");
|
||||
lua_pushnumber(1);
|
||||
}
|
||||
|
||||
void Lua_V2::PurgeText() {
|
||||
g_emi->purgeText();
|
||||
}
|
||||
|
||||
void Lua_V2::GetFontDimensions() {
|
||||
lua_Object fontObj = lua_getparam(1);
|
||||
if (!lua_isstring(fontObj))
|
||||
return;
|
||||
|
||||
const char *fontName = lua_getstring(fontObj);
|
||||
|
||||
Font *font = Font::getByFileName(fontName);
|
||||
if (!font) {
|
||||
font = g_resourceloader->loadFont(fontName);
|
||||
}
|
||||
if (font) {
|
||||
int32 h = font->getBaseOffsetY();
|
||||
int32 w = font->getFontWidth();
|
||||
lua_pushnumber(w);
|
||||
lua_pushnumber(h);
|
||||
} else {
|
||||
warning("Lua_V2::GetFontDimensions for font '%s': returns 0,0", fontName);
|
||||
lua_pushnumber(0.f);
|
||||
lua_pushnumber(0.f);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::GetTextCharPosition() {
|
||||
lua_Object textObj = lua_getparam(1);
|
||||
lua_Object posObj = lua_getparam(2);
|
||||
if (lua_isuserdata(textObj) && lua_tag(textObj) == MKTAG('T', 'E', 'X', 'T')) {
|
||||
TextObject *textObject = gettextobject(textObj);
|
||||
int pos = (int)lua_getnumber(posObj);
|
||||
float textPos = textObject->getTextCharPosition(pos);
|
||||
lua_pushnumber(textPos / 320.f);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::GetTextObjectDimensions() {
|
||||
lua_Object textObj = lua_getparam(1);
|
||||
|
||||
if (lua_isuserdata(textObj) && lua_tag(textObj) == MKTAG('T', 'E', 'X', 'T')) {
|
||||
TextObject *textObject = gettextobject(textObj);
|
||||
lua_pushnumber(textObject->getBitmapWidth() / 320.f);
|
||||
lua_pushnumber(textObject->getBitmapHeight() / 240.f);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::ToggleOverworld() {
|
||||
lua_Object boolObj = lua_getparam(1);
|
||||
bool backToNormal = (lua_isnil(boolObj) == 0);
|
||||
if (backToNormal) {
|
||||
GrimEngine::EngineMode previous = g_grim->getPreviousMode();
|
||||
g_grim->setPreviousMode(GrimEngine::OverworldMode);
|
||||
// HACK: ToggleOverworld is only called after we load a save game.
|
||||
// However, the engine saved PreviousMode as OverworldMode.
|
||||
// Reset it to normal here.
|
||||
if (previous == GrimEngine::OverworldMode)
|
||||
previous = GrimEngine::NormalMode;
|
||||
g_grim->setMode(previous);
|
||||
} else {
|
||||
GrimEngine::EngineMode previous = g_grim->getMode();
|
||||
g_grim->setPreviousMode(previous);
|
||||
g_grim->setMode(GrimEngine::OverworldMode);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::ClearOverworld() {
|
||||
warning("Lua_V2::ClearOverworld: implement opcode");
|
||||
}
|
||||
|
||||
void Lua_V2::ScreenshotForSavegame() {
|
||||
g_emi->temporaryStoreSaveGameImage();
|
||||
}
|
||||
|
||||
void Lua_V2::EngineDisplay() {
|
||||
// dummy
|
||||
}
|
||||
|
||||
void Lua_V2::SetAmbientLight() {
|
||||
// dummy
|
||||
}
|
||||
|
||||
void Lua_V2::Display() {
|
||||
// dummy
|
||||
}
|
||||
|
||||
void Lua_V2::GetCameraPitch() {
|
||||
Set *set = g_grim->getCurrSet();
|
||||
if (set == nullptr) {
|
||||
lua_pushnil();
|
||||
return;
|
||||
}
|
||||
|
||||
Set::Setup *setup = set->getCurrSetup();
|
||||
float pitch;
|
||||
if (g_grim->getGameType() == GType_MONKEY4) {
|
||||
setup->getRotation(nullptr, nullptr, &pitch);
|
||||
} else {
|
||||
setup->getRotation(nullptr, &pitch, nullptr);
|
||||
}
|
||||
lua_pushnumber(pitch);
|
||||
}
|
||||
|
||||
void Lua_V2::GetCameraYaw() {
|
||||
Set *set = g_grim->getCurrSet();
|
||||
if (set == nullptr) {
|
||||
lua_pushnil();
|
||||
return;
|
||||
}
|
||||
|
||||
Set::Setup *setup = set->getCurrSetup();
|
||||
float yaw;
|
||||
if (g_grim->getGameType() == GType_MONKEY4) {
|
||||
setup->getRotation(nullptr, &yaw, nullptr);
|
||||
} else {
|
||||
setup->getRotation(&yaw, nullptr, nullptr);
|
||||
}
|
||||
lua_pushnumber(yaw);
|
||||
}
|
||||
|
||||
void Lua_V2::GetCameraRoll() {
|
||||
Set *set = g_grim->getCurrSet();
|
||||
if (set == nullptr) {
|
||||
lua_pushnil();
|
||||
return;
|
||||
}
|
||||
|
||||
Set::Setup *setup = set->getCurrSetup();
|
||||
float roll;
|
||||
if (g_grim->getGameType() == GType_MONKEY4) {
|
||||
setup->getRotation(&roll, nullptr, nullptr);
|
||||
} else {
|
||||
setup->getRotation(nullptr, nullptr, &roll);
|
||||
}
|
||||
lua_pushnumber(roll);
|
||||
}
|
||||
|
||||
void Lua_V2::PitchCamera() {
|
||||
lua_Object pitchObj = lua_getparam(1);
|
||||
if (!lua_isnumber(pitchObj)) {
|
||||
error("Lua_V2::PitchCamera - Parameter is not a number!");
|
||||
return;
|
||||
}
|
||||
|
||||
Set *set = g_grim->getCurrSet();
|
||||
if (set == nullptr)
|
||||
return;
|
||||
|
||||
Set::Setup *setup = set->getCurrSetup();
|
||||
setup->setPitch(Math::Angle(lua_getnumber(pitchObj)));
|
||||
}
|
||||
|
||||
void Lua_V2::YawCamera() {
|
||||
lua_Object yawObj = lua_getparam(1);
|
||||
if (!lua_isnumber(yawObj)) {
|
||||
error("Lua_V2::YawCamera - Parameter is not a number!");
|
||||
return;
|
||||
}
|
||||
|
||||
Set *set = g_grim->getCurrSet();
|
||||
if (set == nullptr)
|
||||
return;
|
||||
|
||||
Set::Setup *setup = set->getCurrSetup();
|
||||
setup->setYaw(Math::Angle(lua_getnumber(yawObj)));
|
||||
}
|
||||
|
||||
void Lua_V2::RollCamera() {
|
||||
lua_Object rollObj = lua_getparam(1);
|
||||
if (!lua_isnumber(rollObj)) {
|
||||
error("Lua_V2::RollCamera - Parameter is not a number!");
|
||||
return;
|
||||
}
|
||||
|
||||
Set *set = g_grim->getCurrSet();
|
||||
if (set == nullptr)
|
||||
return;
|
||||
|
||||
Set::Setup *setup = set->getCurrSetup();
|
||||
setup->setRoll(Math::Angle(lua_getnumber(rollObj)));
|
||||
}
|
||||
|
||||
void Lua_V2::PushText() {
|
||||
g_emi->pushText();
|
||||
}
|
||||
|
||||
void Lua_V2::PopText() {
|
||||
g_emi->popText();
|
||||
}
|
||||
|
||||
void Lua_V2::GetSectorName() {
|
||||
lua_Object xObj = lua_getparam(1);
|
||||
lua_Object yObj = lua_getparam(2);
|
||||
lua_Object zObj = lua_getparam(3);
|
||||
|
||||
if (!lua_isnumber(xObj) || !lua_isnumber(yObj) || !lua_isnumber(zObj)) {
|
||||
lua_pushnil();
|
||||
return;
|
||||
}
|
||||
float x, y, z;
|
||||
x = lua_getnumber(xObj);
|
||||
y = lua_getnumber(yObj);
|
||||
z = lua_getnumber(zObj);
|
||||
|
||||
Math::Vector3d pos(x, y, z);
|
||||
Set *set = g_grim->getCurrSet();
|
||||
Sector *sector = set->findPointSector(pos, Sector::NoneType);
|
||||
if (sector) {
|
||||
lua_pushstring(sector->getName().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::GammaEnabled() {
|
||||
warning("Lua_V2::GammaEnabled: implement opcode, pushing nil");
|
||||
lua_pushnil();
|
||||
}
|
||||
|
||||
void Lua_V2::FileFindFirst() {
|
||||
lua_Object extObj = lua_getparam(1);
|
||||
if (!lua_isstring(extObj)) {
|
||||
lua_pushnil();
|
||||
return;
|
||||
}
|
||||
|
||||
FileFindDispose();
|
||||
|
||||
const char *extension = lua_getstring(extObj);
|
||||
if (0 == strncmp(extension, "Saves/", 6))
|
||||
extension += 6;
|
||||
|
||||
// _menus.lua: saveload_menu.get_file_list searches for *.gsv.
|
||||
// This avoids conflicts with grim saves.
|
||||
if (0 == strcmp(extension, "*.gsv"))
|
||||
extension = "efmi###.gsv";
|
||||
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
g_grim->_listFiles = saveFileMan->listSavefiles(extension);
|
||||
Common::sort(g_grim->_listFiles.begin(), g_grim->_listFiles.end());
|
||||
g_grim->_listFilesIter = g_grim->_listFiles.begin();
|
||||
|
||||
if (g_grim->_listFilesIter == g_grim->_listFiles.end())
|
||||
lua_pushnil();
|
||||
else
|
||||
FileFindNext();
|
||||
}
|
||||
|
||||
void Lua_V2::ThumbnailFromFile() {
|
||||
lua_Object texIdObj = lua_getparam(1);
|
||||
lua_Object filenameObj = lua_getparam(2);
|
||||
|
||||
if (!lua_isnumber(texIdObj) || !lua_isstring(filenameObj)) {
|
||||
warning("Lua_V2::ThumbnailFromFile: wrong parameters");
|
||||
return;
|
||||
}
|
||||
int index = (int)lua_getnumber(texIdObj);
|
||||
Common::String filename(lua_getstring(filenameObj));
|
||||
|
||||
if (g_grim->getGameType() == GType_MONKEY4 &&
|
||||
g_grim->getGamePlatform() == Common::kPlatformPS2) {
|
||||
filename += ".ps2";
|
||||
}
|
||||
|
||||
int width = 256, height = 128;
|
||||
|
||||
SaveGame *savedState = SaveGame::openForLoading(filename);
|
||||
if (!savedState || !savedState->isCompatible()) {
|
||||
delete savedState;
|
||||
warning("Lua_V2::ThumbnailFromFile: savegame %s not compatible", filename.c_str());
|
||||
lua_pushnil();
|
||||
return;
|
||||
}
|
||||
int dataSize = savedState->beginSection('SIMG');
|
||||
if (dataSize != width * height * 2) {
|
||||
warning("Lua_V2::ThumbnailFromFile: savegame uses unexpected thumbnail size, ignore it");
|
||||
lua_pushnil();
|
||||
delete savedState;
|
||||
return;
|
||||
}
|
||||
uint16 *data = new uint16[dataSize / 2];
|
||||
for (int l = 0; l < dataSize / 2; l++) {
|
||||
data[l] = savedState->readLEUint16();
|
||||
}
|
||||
Graphics::Surface buf;
|
||||
buf.init(width, height, width * 2, (void *)data, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
|
||||
Bitmap *screenshot = new Bitmap(buf, width, height, "screenshot");
|
||||
if (!screenshot) {
|
||||
lua_pushnil();
|
||||
warning("Lua_V2::ThumbnailFromFile: Could not restore screenshot from file %s", filename.c_str());
|
||||
delete screenshot;
|
||||
delete[] data;
|
||||
delete savedState;
|
||||
return;
|
||||
}
|
||||
|
||||
screenshot->_data->convertToColorFormat(Graphics::PixelFormat::createFormatRGBA32());
|
||||
g_driver->createSpecialtyTexture(index, (const uint8 *)screenshot->getData(0).getPixels(), width, height);
|
||||
delete screenshot;
|
||||
delete[] data;
|
||||
savedState->endSection();
|
||||
delete savedState;
|
||||
|
||||
pushbool(true);
|
||||
}
|
||||
|
||||
void Lua_V2::GetMemoryCardId() {
|
||||
// 0 - No mem card
|
||||
// 1 - Not formatted
|
||||
// 2 - Not enough space
|
||||
// 3 - Error occurred
|
||||
lua_pushnumber(4);
|
||||
}
|
||||
|
||||
void Lua_V2::SetReplayMode() {
|
||||
lua_Object intObj = lua_getparam(1);
|
||||
lua_Object strObj = lua_getparam(2);
|
||||
|
||||
if (!lua_isnumber(intObj) || (!lua_isnil(strObj) && !lua_isstring(strObj))) {
|
||||
warning("Lua_V2::SetReplayMode: wrong parameters");
|
||||
return;
|
||||
}
|
||||
|
||||
int num = lua_getnumber(intObj);
|
||||
if (lua_isstring(strObj)) {
|
||||
const char *str = lua_getstring(strObj);
|
||||
warning("SetReplayMode(%d, %s)", num, str);
|
||||
} else {
|
||||
warning("SetReplayMode(%d)", num);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::LocalizeString() {
|
||||
char msgId[50], buf[1000];
|
||||
lua_Object strObj = lua_getparam(1);
|
||||
msgId[0] = 0;
|
||||
|
||||
if (lua_isstring(strObj)) {
|
||||
const char *str = lua_getstring(strObj);
|
||||
Common::String msg = parseMsgText(str, msgId);
|
||||
Common::sprintf_s(buf, "/%s/%s", msgId, msg.c_str());
|
||||
|
||||
lua_pushstring(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::OverWorldToScreen() { // TODO
|
||||
lua_Object param1 = lua_getparam(1);
|
||||
lua_Object param2 = lua_getparam(2);
|
||||
lua_Object param3 = lua_getparam(3);
|
||||
|
||||
float x = 0, y = 0, z = 0;
|
||||
if (!lua_isnumber(param1) || !lua_isnumber(param2) || !lua_isnumber(param3)) {
|
||||
error("Param not a number for OverWorldToScreen");
|
||||
} else {
|
||||
x = lua_getnumber(param1);
|
||||
y = lua_getnumber(param2);
|
||||
z = lua_getnumber(param3);
|
||||
}
|
||||
|
||||
warning("Stub function: OverWorldToScreen(%f, %f, %f) - returning 0,0", x, y, z);
|
||||
|
||||
lua_pushnumber(0);
|
||||
lua_pushnumber(0);
|
||||
}
|
||||
|
||||
void Lua_V2::WorldToScreen() {
|
||||
lua_Object xObj = lua_getparam(1);
|
||||
lua_Object yObj = lua_getparam(2);
|
||||
lua_Object zObj = lua_getparam(3);
|
||||
if (!lua_isnumber(xObj) || !lua_isnumber(yObj) || !lua_isnumber(zObj)) {
|
||||
lua_pushnumber(0.0);
|
||||
lua_pushnumber(0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
float x = lua_getnumber(xObj);
|
||||
float y = lua_getnumber(yObj);
|
||||
float z = lua_getnumber(zObj);
|
||||
Math::Vector3d pos = Math::Vector3d(x, y, z);
|
||||
|
||||
const Set::Setup *setup = g_emi->getCurrSet()->getCurrSetup();
|
||||
Math::Matrix4 view = setup->_rot;
|
||||
view.transpose();
|
||||
|
||||
pos -= setup->_pos;
|
||||
pos = view.getRotation() * pos;
|
||||
pos.z() = -pos.z();
|
||||
|
||||
Math::Matrix4 proj = GfxBase::makeProjMatrix(setup->_fov, setup->_nclip, setup->_fclip);
|
||||
proj.transpose();
|
||||
Math::Vector4d screen = proj * Math::Vector4d(pos.x(), pos.y(), pos.z(), 1.0);
|
||||
screen /= screen.w();
|
||||
|
||||
lua_pushnumber(screen.x());
|
||||
lua_pushnumber(screen.y());
|
||||
}
|
||||
|
||||
void Lua_V2::NewLayer() {
|
||||
lua_Object param1 = lua_getparam(1);
|
||||
lua_Object param2 = lua_getparam(2);
|
||||
lua_Object param3 = lua_getparam(3);
|
||||
|
||||
const char *til = nullptr;
|
||||
int sortorder = 0; // zero = 0;
|
||||
if (lua_isstring(param1) && lua_isnumber(param2) && lua_isnumber(param3)) {
|
||||
til = lua_getstring(param1);
|
||||
sortorder = (int)lua_getnumber(param2);
|
||||
|
||||
// This one is always specified, but also always 0...
|
||||
//zero = (int)lua_getnumber(param3);
|
||||
|
||||
Layer *layer = new Layer(til, sortorder);
|
||||
|
||||
// Need to return something that can be looked up later
|
||||
lua_pushusertag(layer->getId(), MKTAG('L','A','Y','R'));
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::FreeLayer() {
|
||||
lua_Object param1 = lua_getparam(1);
|
||||
if (lua_isuserdata(param1) && lua_tag(param1) == MKTAG('L','A','Y','R')) {
|
||||
int layer = (int)lua_getuserdata(param1);
|
||||
Layer *l = Layer::getPool().getObject(layer);
|
||||
delete l;
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::AdvanceLayerFrame() {
|
||||
lua_Object param1 = lua_getparam(1);
|
||||
lua_Object param2 = lua_getparam(2);
|
||||
if (lua_isuserdata(param1) && lua_tag(param1) == MKTAG('L','A','Y','R') && lua_isnumber(param2)) {
|
||||
int layer = (int)lua_getuserdata(param1);
|
||||
int one = (int)lua_getnumber(param2);
|
||||
Layer *l = Layer::getPool().getObject(layer);
|
||||
if (!l) {
|
||||
warning("Lua_V2::AdvanceLayerFrame: no layer found");
|
||||
return;
|
||||
}
|
||||
l->advanceFrame(one);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::SetLayerFrame() {
|
||||
lua_Object param1 = lua_getparam(1);
|
||||
lua_Object param2 = lua_getparam(2);
|
||||
if (lua_isuserdata(param1) && lua_tag(param1) == MKTAG('L','A','Y','R') && lua_isnumber(param2)) {
|
||||
int layer = (int)lua_getuserdata(param1);
|
||||
int one = (int)lua_getnumber(param2);
|
||||
Layer *l = Layer::getPool().getObject(layer);
|
||||
l->setFrame(one);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::SetLayerSortOrder() {
|
||||
lua_Object param1 = lua_getparam(1);
|
||||
lua_Object param2 = lua_getparam(2);
|
||||
if (lua_isuserdata(param1) && lua_tag(param1) == MKTAG('L','A','Y','R') && lua_isnumber(param2)) {
|
||||
int layer = (int)lua_getuserdata(param1);
|
||||
int sortorder = (int)lua_getnumber(param2);
|
||||
Layer *l = Layer::getPool().getObject(layer);
|
||||
l->setSortOrder(sortorder);
|
||||
} else {
|
||||
warning("Lua_V2::SetLayerSortOrder: wrong parameters");
|
||||
}
|
||||
}
|
||||
|
||||
// Stub function for builtin functions not yet implemented
|
||||
/*static void stubWarning(const char *funcName) {
|
||||
warning("Stub function: %s", funcName);
|
||||
}*/
|
||||
|
||||
static void stubError(const char *funcName) {
|
||||
error("Stub function: %s", funcName);
|
||||
}
|
||||
|
||||
#define STUB_FUNC(name) void name() { stubWarning(#name); }
|
||||
#define STUB_FUNC2(name) void name() { stubError(#name); }
|
||||
|
||||
// Opcodes more or less differ to Grim Lua_V1::* LUA_OPCODEs
|
||||
// STUB_FUNC2(Lua_V2::SetActorWalkChore)
|
||||
// STUB_FUNC2(Lua_V2::SetActorTurnChores)
|
||||
// STUB_FUNC2(Lua_V2::SetActorRestChore)
|
||||
// STUB_FUNC2(Lua_V2::SetActorMumblechore)
|
||||
// STUB_FUNC2(Lua_V2::SetActorTalkChore)
|
||||
// STUB_FUNC2(Lua_V2::SetActorLookRate)
|
||||
// STUB_FUNC2(Lua_V2::GetActorLookRate)
|
||||
// STUB_FUNC2(Lua_V2::GetVisibleThings)
|
||||
// STUB_FUNC2(Lua_V2::GetActorRot)
|
||||
// STUB_FUNC2(Lua_V2::LockSet)
|
||||
// STUB_FUNC2(Lua_V2::UnLockSet)
|
||||
// STUB_FUNC2(Lua_V2::PlaySound)
|
||||
// STUB_FUNC2(Lua_V2::IsSoundPlaying)
|
||||
// STUB_FUNC2(Lua_V2::MakeSectorActive)
|
||||
// STUB_FUNC2(Lua_V2::TurnActorTo)
|
||||
// STUB_FUNC2(Lua_V2::GetAngleBetweenActors)
|
||||
// STUB_FUNC2(Lua_V2::ImStartSound)
|
||||
// STUB_FUNC2(Lua_V2::ImGetSfxVol)
|
||||
// STUB_FUNC2(Lua_V2::ImGetVoiceVol)
|
||||
// STUB_FUNC2(Lua_V2::ImGetMusicVol)
|
||||
// STUB_FUNC2(Lua_V2::ImSetSequence)
|
||||
// STUB_FUNC2(Lua_V2::ChangeTextObject)
|
||||
// STUB_FUNC2(Lua_V2::GetTextCharPosition)
|
||||
// STUB_FUNC2(Lua_V2::SetOffscreenTextPos)
|
||||
// STUB_FUNC2(Lua_V2::FadeInChore)
|
||||
// STUB_FUNC2(Lua_V2::FadeOutChore)
|
||||
// STUB_FUNC2(Lua_V2::SetLightPosition)
|
||||
// STUB_FUNC2(Lua_V2::GetAngleBetweenVectors)
|
||||
// STUB_FUNC2(Lua_V2::IsPointInSector)
|
||||
|
||||
// Stubbed functions with semi-known arguments:
|
||||
// TODO: Verify and implement these: (And add type-checking), also rename params
|
||||
void Lua_V2::NukeAllScriptLocks() {
|
||||
warning("Lua_V2::NukeAllScriptLocks() - TODO: Implement opcode");
|
||||
}
|
||||
|
||||
void Lua_V2::FRUTEY_Begin() {
|
||||
lua_Object param1 = lua_getparam(1);
|
||||
|
||||
if (!lua_isstring(param1))
|
||||
error("Lua_V2::FRUTEY_Begin - Unknown parameters");
|
||||
|
||||
const char *paramText = lua_getstring(param1);
|
||||
error("Lua_V2::FRUTEY_Begin(%s) - TODO: Implement opcode", paramText);
|
||||
}
|
||||
|
||||
void Lua_V2::FRUTEY_End() {
|
||||
error("Lua_V2::FRUTEY_End() - TODO: Implement opcode");
|
||||
}
|
||||
|
||||
void Lua_V2::RenderModeUser() {
|
||||
lua_Object param1 = lua_getparam(1);
|
||||
if (!lua_isnil(param1) && g_grim->getMode() != GrimEngine::DrawMode) {
|
||||
g_grim->setPreviousMode(g_grim->getMode());
|
||||
g_movie->pause(true);
|
||||
g_emiSound->pause(true);
|
||||
g_grim->setMode(GrimEngine::DrawMode);
|
||||
} else if (lua_isnil(param1) && g_grim->getMode() == GrimEngine::DrawMode) {
|
||||
g_movie->pause(false);
|
||||
g_emiSound->pause(false);
|
||||
g_grim->setMode(g_grim->getPreviousMode());
|
||||
}
|
||||
}
|
||||
|
||||
// Monkey specific LUA_OPCODEs only used for debug
|
||||
STUB_FUNC2(Lua_V2::ToggleDebugDraw)
|
||||
STUB_FUNC2(Lua_V2::ToggleDrawCameras)
|
||||
STUB_FUNC2(Lua_V2::ToggleDrawLights)
|
||||
STUB_FUNC2(Lua_V2::ToggleDrawSectors)
|
||||
STUB_FUNC2(Lua_V2::ToggleDrawBBoxes)
|
||||
STUB_FUNC2(Lua_V2::ToggleDrawFPS)
|
||||
STUB_FUNC2(Lua_V2::ToggleDrawPerformance)
|
||||
STUB_FUNC2(Lua_V2::ToggleDrawActorStats)
|
||||
STUB_FUNC2(Lua_V2::SectEditSelect)
|
||||
STUB_FUNC2(Lua_V2::SectEditPlace)
|
||||
STUB_FUNC2(Lua_V2::SectEditDelete)
|
||||
STUB_FUNC2(Lua_V2::SectEditInsert)
|
||||
STUB_FUNC2(Lua_V2::SectEditSortAdd)
|
||||
STUB_FUNC2(Lua_V2::SectEditForgetIt)
|
||||
|
||||
// ResidualVM-hacks:
|
||||
void Lua_V2::GetResidualVMPreference() {
|
||||
lua_Object keyObj = lua_getparam(1);
|
||||
|
||||
if (lua_isstring(keyObj)) {
|
||||
const Common::String key = lua_getstring(keyObj);
|
||||
|
||||
float value;
|
||||
if (g_emiregistry->Get(key, value))
|
||||
lua_pushnumber(value);
|
||||
else
|
||||
lua_pushnil();
|
||||
} else
|
||||
lua_pushnil();
|
||||
}
|
||||
|
||||
void Lua_V2::SetResidualVMPreference() {
|
||||
lua_Object keyObj = lua_getparam(1);
|
||||
lua_Object valueObj = lua_getparam(2);
|
||||
|
||||
if (lua_isstring(keyObj) && lua_isnumber(valueObj)) {
|
||||
const Common::String key = lua_getstring(keyObj);
|
||||
float value = lua_getnumber(valueObj);
|
||||
|
||||
g_emiregistry->Set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
struct luaL_reg monkeyMainOpcodes[] = {
|
||||
// Monkey specific LUA_OPCODEs:
|
||||
{ "ScreenshotForSavegame", LUA_OPCODE(Lua_V2, ScreenshotForSavegame) },
|
||||
{ "GetActorWorldPos", LUA_OPCODE(Lua_V2, GetActorWorldPos) },
|
||||
{ "SetActiveCD", LUA_OPCODE(Lua_V2, SetActiveCD) },
|
||||
{ "GetActiveCD", LUA_OPCODE(Lua_V2, GetActiveCD) },
|
||||
{ "AreWeInternational", LUA_OPCODE(Lua_V2, AreWeInternational) },
|
||||
{ "MakeScreenTextures", LUA_OPCODE(Lua_V2, MakeScreenTextures) },
|
||||
{ "ThumbnailFromFile", LUA_OPCODE(Lua_V2, ThumbnailFromFile) },
|
||||
{ "ClearSpecialtyTexture", LUA_OPCODE(Lua_V2, ClearSpecialtyTexture) },
|
||||
{ "UnloadActor", LUA_OPCODE(Lua_V2, UnloadActor) },
|
||||
{ "PutActorInOverworld", LUA_OPCODE(Lua_V2, PutActorInOverworld) },
|
||||
{ "RemoveActorFromOverworld", LUA_OPCODE(Lua_V2, RemoveActorFromOverworld) },
|
||||
{ "ClearOverworld", LUA_OPCODE(Lua_V2, ClearOverworld) },
|
||||
{ "ToggleOverworld", LUA_OPCODE(Lua_V2, ToggleOverworld) },
|
||||
{ "ActorStopMoving", LUA_OPCODE(Lua_V2, ActorStopMoving) },
|
||||
{ "SetActorFOV", LUA_OPCODE(Lua_V2, SetActorFOV) },
|
||||
{ "SetActorLighting", LUA_OPCODE(Lua_V2, SetActorLighting) },
|
||||
{ "SetActorHeadLimits", LUA_OPCODE(Lua_V2, SetActorHeadLimits) },
|
||||
{ "ActorActivateShadow", LUA_OPCODE(Lua_V2, ActorActivateShadow) },
|
||||
{ "EnableActorPuck", LUA_OPCODE(Lua_V2, EnableActorPuck) },
|
||||
{ "SetActorGlobalAlpha", LUA_OPCODE(Lua_V2, SetActorGlobalAlpha) },
|
||||
{ "SetActorLocalAlpha", LUA_OPCODE(Lua_V2, SetActorLocalAlpha) },
|
||||
{ "SetActorSortOrder", LUA_OPCODE(Lua_V2, SetActorSortOrder) },
|
||||
{ "GetActorSortOrder", LUA_OPCODE(Lua_V2, GetActorSortOrder) },
|
||||
{ "AttachActor", LUA_OPCODE(Lua_V2, AttachActor) },
|
||||
{ "DetachActor", LUA_OPCODE(Lua_V2, DetachActor) },
|
||||
{ "IsChoreValid", LUA_OPCODE(Lua_V2, IsChoreValid) },
|
||||
{ "IsChorePlaying", LUA_OPCODE(Lua_V2, IsChorePlaying) },
|
||||
{ "IsChoreLooping", LUA_OPCODE(Lua_V2, IsChoreLooping) },
|
||||
{ "SetChoreLooping", LUA_OPCODE(Lua_V2, SetChoreLooping) },
|
||||
{ "StopActorChores", LUA_OPCODE(Lua_V2, StopActorChores) },
|
||||
{ "PlayChore", LUA_OPCODE(Lua_V2, PlayChore) },
|
||||
{ "StopChore", LUA_OPCODE(Lua_V2, StopChore) },
|
||||
{ "PauseChore", LUA_OPCODE(Lua_V2, PauseChore) },
|
||||
{ "AdvanceChore", LUA_OPCODE(Lua_V2, AdvanceChore) },
|
||||
{ "CompleteChore", LUA_OPCODE(Lua_V2, CompleteChore) },
|
||||
{ "LockChore", LUA_OPCODE(Lua_V2, LockChore) },
|
||||
{ "UnlockChore", LUA_OPCODE(Lua_V2, UnlockChore) },
|
||||
{ "LockChoreSet", LUA_OPCODE(Lua_V2, LockChoreSet) },
|
||||
{ "UnlockChoreSet", LUA_OPCODE(Lua_V2, UnlockChoreSet) },
|
||||
{ "LockBackground", LUA_OPCODE(Lua_V2, LockBackground) },
|
||||
{ "UnLockBackground", LUA_OPCODE(Lua_V2, UnLockBackground) },
|
||||
{ "EscapeMovie", LUA_OPCODE(Lua_V2, EscapeMovie) },
|
||||
{ "StopAllSounds", LUA_OPCODE(Lua_V2, StopAllSounds) },
|
||||
{ "LoadSound", LUA_OPCODE(Lua_V2, LoadSound) },
|
||||
{ "FreeSound", LUA_OPCODE(Lua_V2, FreeSound) },
|
||||
{ "PlayLoadedSound", LUA_OPCODE(Lua_V2, PlayLoadedSound) },
|
||||
{ "StopSound", LUA_OPCODE(Lua_V2, StopSound) },
|
||||
{ "SetGroupVolume", LUA_OPCODE(Lua_V2, SetGroupVolume) },
|
||||
{ "GetSoundVolume", LUA_OPCODE(Lua_V2, GetSoundVolume) },
|
||||
{ "SetSoundVolume", LUA_OPCODE(Lua_V2, SetSoundVolume) },
|
||||
{ "EnableAudioGroup", LUA_OPCODE(Lua_V2, EnableAudioGroup) },
|
||||
{ "EnableVoiceFX", LUA_OPCODE(Lua_V2, EnableVoiceFX) },
|
||||
{ "PlaySoundFrom", LUA_OPCODE(Lua_V2, PlaySoundFrom) },
|
||||
{ "PlayLoadedSoundFrom", LUA_OPCODE(Lua_V2, PlayLoadedSoundFrom) },
|
||||
{ "SetReverb", LUA_OPCODE(Lua_V2, SetReverb) },
|
||||
{ "UpdateSoundPosition", LUA_OPCODE(Lua_V2, UpdateSoundPosition) },
|
||||
{ "ImSelectSet", LUA_OPCODE(Lua_V2, ImSelectSet) },
|
||||
{ "ImStateHasLooped", LUA_OPCODE(Lua_V2, ImStateHasLooped) },
|
||||
{ "ImStateHasEnded", LUA_OPCODE(Lua_V2, ImStateHasEnded) },
|
||||
{ "ImPushState", LUA_OPCODE(Lua_V2, ImPushState) },
|
||||
{ "ImPopState", LUA_OPCODE(Lua_V2, ImPopState) },
|
||||
{ "ImFlushStack", LUA_OPCODE(Lua_V2, ImFlushStack) },
|
||||
{ "ImGetMillisecondPosition", LUA_OPCODE(Lua_V2, ImGetMillisecondPosition) },
|
||||
{ "GetSectorName", LUA_OPCODE(Lua_V2, GetSectorName) },
|
||||
{ "GetCameraYaw", LUA_OPCODE(Lua_V2, GetCameraYaw) },
|
||||
{ "YawCamera", LUA_OPCODE(Lua_V2, YawCamera) },
|
||||
{ "GetCameraPitch", LUA_OPCODE(Lua_V2, GetCameraPitch) },
|
||||
{ "PitchCamera", LUA_OPCODE(Lua_V2, PitchCamera) },
|
||||
{ "RollCamera", LUA_OPCODE(Lua_V2, RollCamera) },
|
||||
{ "UndimAll", LUA_OPCODE(Lua_V2, UndimAll) },
|
||||
{ "UndimRegion", LUA_OPCODE(Lua_V2, UndimRegion) },
|
||||
{ "GetCPUSpeed", LUA_OPCODE(Lua_V2, GetCPUSpeed) },
|
||||
{ "NewLayer", LUA_OPCODE(Lua_V2, NewLayer) },
|
||||
{ "FreeLayer", LUA_OPCODE(Lua_V2, FreeLayer) },
|
||||
{ "SetLayerSortOrder", LUA_OPCODE(Lua_V2, SetLayerSortOrder) },
|
||||
{ "SetLayerFrame", LUA_OPCODE(Lua_V2, SetLayerFrame) },
|
||||
{ "AdvanceLayerFrame", LUA_OPCODE(Lua_V2, AdvanceLayerFrame) },
|
||||
{ "PushText", LUA_OPCODE(Lua_V2, PushText) },
|
||||
{ "PopText", LUA_OPCODE(Lua_V2, PopText) },
|
||||
{ "NukeAllScriptLocks", LUA_OPCODE(Lua_V2, NukeAllScriptLocks) },
|
||||
{ "ToggleDebugDraw", LUA_OPCODE(Lua_V2, ToggleDebugDraw) },
|
||||
{ "ToggleDrawCameras", LUA_OPCODE(Lua_V2, ToggleDrawCameras) },
|
||||
{ "ToggleDrawLights", LUA_OPCODE(Lua_V2, ToggleDrawLights) },
|
||||
{ "ToggleDrawSectors", LUA_OPCODE(Lua_V2, ToggleDrawSectors) },
|
||||
{ "ToggleDrawBBoxes", LUA_OPCODE(Lua_V2, ToggleDrawBBoxes) },
|
||||
{ "ToggleDrawFPS", LUA_OPCODE(Lua_V2, ToggleDrawFPS) },
|
||||
{ "ToggleDrawPerformance", LUA_OPCODE(Lua_V2, ToggleDrawPerformance) },
|
||||
{ "ToggleDrawActorStats", LUA_OPCODE(Lua_V2, ToggleDrawActorStats) },
|
||||
{ "SectEditSelect", LUA_OPCODE(Lua_V2, SectEditSelect) },
|
||||
{ "SectEditPlace", LUA_OPCODE(Lua_V2, SectEditPlace) },
|
||||
{ "SectEditDelete", LUA_OPCODE(Lua_V2, SectEditDelete) },
|
||||
{ "SectEditInsert", LUA_OPCODE(Lua_V2, SectEditInsert) },
|
||||
{ "SectEditSortAdd", LUA_OPCODE(Lua_V2, SectEditSortAdd) },
|
||||
{ "SectEditForgetIt", LUA_OPCODE(Lua_V2, SectEditForgetIt) },
|
||||
{ "GammaEnabled", LUA_OPCODE(Lua_V2, GammaEnabled) },
|
||||
{ "FRUTEY_Begin", LUA_OPCODE(Lua_V2, FRUTEY_Begin) },
|
||||
{ "FRUTEY_End", LUA_OPCODE(Lua_V2, FRUTEY_End) },
|
||||
{ "LocalizeString", LUA_OPCODE(Lua_V2, LocalizeString) },
|
||||
// PS2:
|
||||
{ "GetMemoryCardId", LUA_OPCODE(Lua_V2, GetMemoryCardId) },
|
||||
{ "OverWorldToScreen", LUA_OPCODE(Lua_V2, OverWorldToScreen) },
|
||||
{ "SetReplayMode", LUA_OPCODE(Lua_V2, SetReplayMode) },
|
||||
// ResidualVM-hacks:
|
||||
{ "GetResidualVMPreference", LUA_OPCODE(Lua_V2, GetResidualVMPreference) },
|
||||
{ "SetResidualVMPreference", LUA_OPCODE(Lua_V2, SetResidualVMPreference) }
|
||||
};
|
||||
|
||||
void Lua_V2::registerOpcodes() {
|
||||
Lua_V1::registerOpcodes();
|
||||
|
||||
// Register main opcodes functions
|
||||
luaL_openlib(monkeyMainOpcodes, ARRAYSIZE(monkeyMainOpcodes));
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
194
engines/grim/emi/lua_v2.h
Normal file
194
engines/grim/emi/lua_v2.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_LUA_V2
|
||||
#define GRIM_LUA_V2
|
||||
|
||||
#include "engines/grim/lua_v1.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class Lua_V2 : public Lua_V1 {
|
||||
public:
|
||||
typedef Lua_V2 LuaClass;
|
||||
void registerOpcodes() override;
|
||||
|
||||
protected:
|
||||
bool findCostume(lua_Object costumeObj, Actor *actor, Costume **costume) override;
|
||||
void setChoreAndCostume(lua_Object choreObj, lua_Object costumeObj, Actor *actor, Costume *&costume, int &chore);
|
||||
static uint convertEmiVolumeToMixer(uint emiVolume);
|
||||
static uint convertMixerVolumeToEmi(uint volume);
|
||||
static const uint MAX_EMI_VOLUME = 100;
|
||||
|
||||
DECLARE_LUA_OPCODE(UndimAll);
|
||||
DECLARE_LUA_OPCODE(SetActorLocalAlpha);
|
||||
DECLARE_LUA_OPCODE(UndimRegion);
|
||||
DECLARE_LUA_OPCODE(DimScreen) override;
|
||||
DECLARE_LUA_OPCODE(MakeCurrentSetup) override;
|
||||
DECLARE_LUA_OPCODE(SetActorGlobalAlpha);
|
||||
DECLARE_LUA_OPCODE(ImGetMillisecondPosition);
|
||||
DECLARE_LUA_OPCODE(RemoveActorFromOverworld);
|
||||
DECLARE_LUA_OPCODE(UnloadActor);
|
||||
DECLARE_LUA_OPCODE(SetActorWalkRate) override;
|
||||
DECLARE_LUA_OPCODE(GetActorWalkRate) override;
|
||||
DECLARE_LUA_OPCODE(SetActorTurnRate) override;
|
||||
DECLARE_LUA_OPCODE(SetReverb);
|
||||
DECLARE_LUA_OPCODE(LockBackground);
|
||||
DECLARE_LUA_OPCODE(UnLockBackground);
|
||||
DECLARE_LUA_OPCODE(LockChore);
|
||||
DECLARE_LUA_OPCODE(IsActorChoring) override;
|
||||
DECLARE_LUA_OPCODE(IsChoreValid);
|
||||
DECLARE_LUA_OPCODE(IsChorePlaying);
|
||||
DECLARE_LUA_OPCODE(StopChore);
|
||||
DECLARE_LUA_OPCODE(AdvanceChore);
|
||||
DECLARE_LUA_OPCODE(SetActorSortOrder);
|
||||
DECLARE_LUA_OPCODE(ActorActivateShadow);
|
||||
DECLARE_LUA_OPCODE(ActorStopMoving);
|
||||
DECLARE_LUA_OPCODE(ActorLookAt) override;
|
||||
DECLARE_LUA_OPCODE(PutActorInOverworld);
|
||||
DECLARE_LUA_OPCODE(GetActorWorldPos);
|
||||
DECLARE_LUA_OPCODE(MakeScreenTextures);
|
||||
DECLARE_LUA_OPCODE(PutActorInSet) override;
|
||||
DECLARE_LUA_OPCODE(LoadBundle) override;
|
||||
DECLARE_LUA_OPCODE(AreWeInternational);
|
||||
DECLARE_LUA_OPCODE(ImSetState) override;
|
||||
DECLARE_LUA_OPCODE(EnableVoiceFX);
|
||||
DECLARE_LUA_OPCODE(SetGroupVolume);
|
||||
DECLARE_LUA_OPCODE(EnableAudioGroup);
|
||||
DECLARE_LUA_OPCODE(ImSelectSet);
|
||||
DECLARE_LUA_OPCODE(GetActorChores) override;
|
||||
DECLARE_LUA_OPCODE(PlayActorChore) override;
|
||||
DECLARE_LUA_OPCODE(StopActorChores);
|
||||
DECLARE_LUA_OPCODE(SetActorLighting);
|
||||
DECLARE_LUA_OPCODE(SetActorCollisionMode) override;
|
||||
DECLARE_LUA_OPCODE(SetActorCollisionScale) override;
|
||||
DECLARE_LUA_OPCODE(GetActorPuckVector) override;
|
||||
DECLARE_LUA_OPCODE(SetActorHeadLimits);
|
||||
DECLARE_LUA_OPCODE(SetActorHead) override;
|
||||
DECLARE_LUA_OPCODE(SetActorFOV);
|
||||
DECLARE_LUA_OPCODE(AttachActor);
|
||||
DECLARE_LUA_OPCODE(DetachActor);
|
||||
DECLARE_LUA_OPCODE(GetCPUSpeed);
|
||||
DECLARE_LUA_OPCODE(StartMovie) override;
|
||||
DECLARE_LUA_OPCODE(IsMoviePlaying) override;
|
||||
DECLARE_LUA_OPCODE(SetActiveCD);
|
||||
DECLARE_LUA_OPCODE(GetActiveCD);
|
||||
DECLARE_LUA_OPCODE(PurgeText) override;
|
||||
DECLARE_LUA_OPCODE(ImFlushStack);
|
||||
DECLARE_LUA_OPCODE(LoadSound);
|
||||
DECLARE_LUA_OPCODE(ImSetMusicVol) override;
|
||||
DECLARE_LUA_OPCODE(ImSetSfxVol) override;
|
||||
DECLARE_LUA_OPCODE(ImSetVoiceVol) override;
|
||||
DECLARE_LUA_OPCODE(ImSetVoiceEffect) override;
|
||||
DECLARE_LUA_OPCODE(ToggleOverworld);
|
||||
DECLARE_LUA_OPCODE(ScreenshotForSavegame);
|
||||
DECLARE_LUA_OPCODE(EngineDisplay) override;
|
||||
DECLARE_LUA_OPCODE(SetAmbientLight) override;
|
||||
DECLARE_LUA_OPCODE(Display) override;
|
||||
DECLARE_LUA_OPCODE(ThumbnailFromFile);
|
||||
DECLARE_LUA_OPCODE(ClearSpecialtyTexture);
|
||||
DECLARE_LUA_OPCODE(ClearOverworld);
|
||||
DECLARE_LUA_OPCODE(EnableActorPuck);
|
||||
DECLARE_LUA_OPCODE(GetActorSortOrder);
|
||||
DECLARE_LUA_OPCODE(IsChoreLooping);
|
||||
DECLARE_LUA_OPCODE(SetChoreLooping);
|
||||
DECLARE_LUA_OPCODE(PlayChore);
|
||||
DECLARE_LUA_OPCODE(PauseChore);
|
||||
DECLARE_LUA_OPCODE(CompleteChore);
|
||||
DECLARE_LUA_OPCODE(UnlockChore);
|
||||
DECLARE_LUA_OPCODE(LockChoreSet);
|
||||
DECLARE_LUA_OPCODE(UnlockChoreSet);
|
||||
DECLARE_LUA_OPCODE(EscapeMovie);
|
||||
DECLARE_LUA_OPCODE(StopAllSounds);
|
||||
DECLARE_LUA_OPCODE(FreeSound);
|
||||
DECLARE_LUA_OPCODE(PlayLoadedSound);
|
||||
DECLARE_LUA_OPCODE(StopSound);
|
||||
DECLARE_LUA_OPCODE(PlaySound) override;
|
||||
DECLARE_LUA_OPCODE(IsSoundPlaying) override;
|
||||
DECLARE_LUA_OPCODE(GetSoundVolume);
|
||||
DECLARE_LUA_OPCODE(SetSoundVolume);
|
||||
DECLARE_LUA_OPCODE(PlaySoundFrom);
|
||||
DECLARE_LUA_OPCODE(PlayLoadedSoundFrom);
|
||||
DECLARE_LUA_OPCODE(UpdateSoundPosition);
|
||||
DECLARE_LUA_OPCODE(ImStateHasLooped);
|
||||
DECLARE_LUA_OPCODE(ImStateHasEnded);
|
||||
DECLARE_LUA_OPCODE(ImPushState);
|
||||
DECLARE_LUA_OPCODE(ImPopState);
|
||||
DECLARE_LUA_OPCODE(ImPause) override;
|
||||
DECLARE_LUA_OPCODE(ImResume) override;
|
||||
DECLARE_LUA_OPCODE(GetSectorName);
|
||||
DECLARE_LUA_OPCODE(GetCameraYaw);
|
||||
DECLARE_LUA_OPCODE(YawCamera);
|
||||
DECLARE_LUA_OPCODE(GetCameraPitch);
|
||||
DECLARE_LUA_OPCODE(GetCameraRoll) override;
|
||||
DECLARE_LUA_OPCODE(PitchCamera);
|
||||
DECLARE_LUA_OPCODE(RollCamera);
|
||||
DECLARE_LUA_OPCODE(NewLayer);
|
||||
DECLARE_LUA_OPCODE(FreeLayer);
|
||||
DECLARE_LUA_OPCODE(SetLayerSortOrder);
|
||||
DECLARE_LUA_OPCODE(SetLayerFrame);
|
||||
DECLARE_LUA_OPCODE(AdvanceLayerFrame);
|
||||
DECLARE_LUA_OPCODE(PushText);
|
||||
DECLARE_LUA_OPCODE(PopText);
|
||||
DECLARE_LUA_OPCODE(NukeAllScriptLocks);
|
||||
DECLARE_LUA_OPCODE(ToggleDebugDraw);
|
||||
DECLARE_LUA_OPCODE(ToggleDrawCameras);
|
||||
DECLARE_LUA_OPCODE(ToggleDrawLights);
|
||||
DECLARE_LUA_OPCODE(ToggleDrawSectors);
|
||||
DECLARE_LUA_OPCODE(ToggleDrawBBoxes);
|
||||
DECLARE_LUA_OPCODE(ToggleDrawFPS);
|
||||
DECLARE_LUA_OPCODE(ToggleDrawPerformance);
|
||||
DECLARE_LUA_OPCODE(ToggleDrawActorStats);
|
||||
DECLARE_LUA_OPCODE(SectEditSelect);
|
||||
DECLARE_LUA_OPCODE(SectEditPlace);
|
||||
DECLARE_LUA_OPCODE(SectEditDelete);
|
||||
DECLARE_LUA_OPCODE(SectEditInsert);
|
||||
DECLARE_LUA_OPCODE(SectEditSortAdd);
|
||||
DECLARE_LUA_OPCODE(SectEditForgetIt);
|
||||
DECLARE_LUA_OPCODE(FRUTEY_Begin);
|
||||
DECLARE_LUA_OPCODE(FRUTEY_End);
|
||||
DECLARE_LUA_OPCODE(GetFontDimensions) override;
|
||||
DECLARE_LUA_OPCODE(GetTextObjectDimensions) override;
|
||||
DECLARE_LUA_OPCODE(GetTextCharPosition) override;
|
||||
DECLARE_LUA_OPCODE(SetActorRestChore) override;
|
||||
DECLARE_LUA_OPCODE(SetActorWalkChore) override;
|
||||
DECLARE_LUA_OPCODE(SetActorTurnChores) override;
|
||||
DECLARE_LUA_OPCODE(SetActorTalkChore) override;
|
||||
DECLARE_LUA_OPCODE(SetActorMumblechore) override;
|
||||
DECLARE_LUA_OPCODE(GammaEnabled);
|
||||
DECLARE_LUA_OPCODE(FileFindFirst) override;
|
||||
DECLARE_LUA_OPCODE(WalkActorToAvoiding) override;
|
||||
DECLARE_LUA_OPCODE(WalkActorVector) override;
|
||||
DECLARE_LUA_OPCODE(LocalizeString) override;
|
||||
DECLARE_LUA_OPCODE(WorldToScreen) override;
|
||||
DECLARE_LUA_OPCODE(RenderModeUser) override;
|
||||
// PS2:
|
||||
DECLARE_LUA_OPCODE(GetMemoryCardId);
|
||||
DECLARE_LUA_OPCODE(OverWorldToScreen);
|
||||
DECLARE_LUA_OPCODE(SetReplayMode);
|
||||
// ResidualVM-hacks:
|
||||
DECLARE_LUA_OPCODE(GetResidualVMPreference);
|
||||
DECLARE_LUA_OPCODE(SetResidualVMPreference);
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
1098
engines/grim/emi/lua_v2_actor.cpp
Normal file
1098
engines/grim/emi/lua_v2_actor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
592
engines/grim/emi/lua_v2_sound.cpp
Normal file
592
engines/grim/emi/lua_v2_sound.cpp
Normal file
@@ -0,0 +1,592 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#include "engines/grim/set.h"
|
||||
#include "engines/grim/emi/sound/emisound.h"
|
||||
#include "engines/grim/emi/lua_v2.h"
|
||||
#include "engines/grim/emi/poolsound.h"
|
||||
#include "engines/grim/lua/lua.h"
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/sound.h"
|
||||
#include "engines/grim/grim.h"
|
||||
#include "engines/grim/resource.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
// Helper function, not called from LUA directly
|
||||
uint Lua_V2::convertEmiVolumeToMixer(uint emiVolume) {
|
||||
// EmiVolume range: 0 .. 100
|
||||
// Mixer range: 0 .. kMaxChannelVolume
|
||||
float vol = float(emiVolume) / MAX_EMI_VOLUME * Audio::Mixer::kMaxChannelVolume;
|
||||
return CLIP<uint>(uint(vol), 0, Audio::Mixer::kMaxChannelVolume);
|
||||
}
|
||||
|
||||
// Helper function, not called from LUA directly
|
||||
uint Lua_V2::convertMixerVolumeToEmi(uint volume) {
|
||||
float vol = float(volume) * MAX_EMI_VOLUME / Audio::Mixer::kMaxChannelVolume;
|
||||
return CLIP<uint>(uint(vol), 0, MAX_EMI_VOLUME);
|
||||
}
|
||||
|
||||
// Note: debug output for volume values uses the engine's mixer range
|
||||
|
||||
void Lua_V2::ImGetMillisecondPosition() {
|
||||
lua_Object soundObj = lua_getparam(1);
|
||||
|
||||
if (lua_isnumber(soundObj)) {
|
||||
int sound = (int)lua_getnumber(soundObj);
|
||||
// FIXME int ms = func(sound);
|
||||
// lua_pushnumber(ms);
|
||||
// push -1 for now
|
||||
// Currently a bit of guesswork, and probably wrong, as the stateId
|
||||
// is ignored by emisound (which only has one music-track now).
|
||||
uint32 msPos = g_emiSound->getMsPos(sound);
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::ImGetMillisecondPosition: sound: %d ms: %d", sound, msPos);
|
||||
lua_pushnumber(msPos);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::SetReverb() {
|
||||
lua_Object eaxObj = lua_getparam(1);
|
||||
lua_Object decayObj = lua_getparam(2);
|
||||
lua_Object mixObj = lua_getparam(3);
|
||||
lua_Object predelayObj = lua_getparam(4);
|
||||
lua_Object dampingObj = lua_getparam(5);
|
||||
|
||||
if (!lua_isnumber(eaxObj))
|
||||
return;
|
||||
|
||||
int eax = (int)lua_getnumber(eaxObj);
|
||||
int param = 0;
|
||||
float decay = -1;
|
||||
float mix = -1;
|
||||
float predelay = -1;
|
||||
float damping = -1;
|
||||
|
||||
if (eax == 60) {
|
||||
param = 26;
|
||||
} else if (eax == 70) {
|
||||
param = 27;
|
||||
} else if (eax >= 0 && eax <= 25) {
|
||||
param = eax;
|
||||
// there is some table, initialy is like eax
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lua_isnumber(decayObj))
|
||||
decay = lua_getnumber(decayObj);
|
||||
if (lua_isnumber(mixObj))
|
||||
mix = lua_getnumber(mixObj);
|
||||
if (lua_isnumber(predelayObj))
|
||||
predelay = lua_getnumber(predelayObj);
|
||||
if (lua_isnumber(dampingObj))
|
||||
damping = lua_getnumber(dampingObj);
|
||||
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::SetReverb, eax: %d, decay: %f, mix: %f, predelay: %f, damping: %f", param, decay, mix, predelay, damping);
|
||||
// FIXME: func(param, decay, mix, predelay, damping);
|
||||
}
|
||||
|
||||
void Lua_V2::ImSetState() {
|
||||
lua_Object stateObj = lua_getparam(1);
|
||||
if (!lua_isnumber(stateObj))
|
||||
return;
|
||||
|
||||
int state = (int)lua_getnumber(stateObj);
|
||||
g_imuseState = state;
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::ImSetState: stub, state: %d", state);
|
||||
}
|
||||
|
||||
void Lua_V2::ImStateHasEnded() {
|
||||
lua_Object stateObj = lua_getparam(1);
|
||||
if (!lua_isnumber(stateObj))
|
||||
return;
|
||||
|
||||
int state = (int)lua_getnumber(stateObj);
|
||||
|
||||
pushbool(g_emiSound->stateHasEnded(state));
|
||||
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::ImStateHasEnded: state %d.", state);
|
||||
}
|
||||
|
||||
// TODO: Implement this:
|
||||
void Lua_V2::ImStateHasLooped() {
|
||||
lua_Object stateObj = lua_getparam(1);
|
||||
if (!lua_isnumber(stateObj))
|
||||
return;
|
||||
|
||||
int state = (int)lua_getnumber(stateObj);
|
||||
|
||||
pushbool(g_emiSound->stateHasLooped(state));
|
||||
}
|
||||
|
||||
void Lua_V2::EnableVoiceFX() {
|
||||
lua_Object stateObj = lua_getparam(1);
|
||||
|
||||
bool state = false;
|
||||
if (!lua_isnil(stateObj))
|
||||
state = true;
|
||||
|
||||
// FIXME: func(state);
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::EnableVoiceFX: implement opcode, state: %d", (int)state);
|
||||
}
|
||||
|
||||
void Lua_V2::SetGroupVolume() {
|
||||
lua_Object groupObj = lua_getparam(1);
|
||||
lua_Object volumeObj = lua_getparam(2);
|
||||
|
||||
if (!lua_isnumber(groupObj))
|
||||
return;
|
||||
int group = (int)lua_getnumber(groupObj);
|
||||
|
||||
int volume = Audio::Mixer::kMaxChannelVolume;
|
||||
if (lua_isnumber(volumeObj))
|
||||
volume = convertEmiVolumeToMixer((int)lua_getnumber(volumeObj));
|
||||
|
||||
switch (group) {
|
||||
case 1: // SFX
|
||||
g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volume);
|
||||
g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, volume);
|
||||
break;
|
||||
case 2: // Voice
|
||||
g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume);
|
||||
break;
|
||||
case 3: // Music
|
||||
g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume);
|
||||
break;
|
||||
default:
|
||||
error("Lua_V2::SetGroupVolume - unknown group %d", group);
|
||||
}
|
||||
// FIXME: func(group, volume);
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::SetGroupVolume: group: %d, volume %d", group, volume);
|
||||
}
|
||||
|
||||
void Lua_V2::EnableAudioGroup() {
|
||||
lua_Object groupObj = lua_getparam(1);
|
||||
lua_Object stateObj = lua_getparam(2);
|
||||
|
||||
if (!lua_isnumber(groupObj))
|
||||
return;
|
||||
int group = (int)lua_getnumber(groupObj);
|
||||
|
||||
bool state = false;
|
||||
if (!lua_isnil(stateObj))
|
||||
state = true;
|
||||
|
||||
// FIXME: func(group, state);
|
||||
switch (group) {
|
||||
case 1: // SFX
|
||||
g_system->getMixer()->muteSoundType(Audio::Mixer::kSFXSoundType, !state);
|
||||
g_system->getMixer()->muteSoundType(Audio::Mixer::kPlainSoundType, !state);
|
||||
break;
|
||||
case 2: // Voice
|
||||
g_system->getMixer()->muteSoundType(Audio::Mixer::kSpeechSoundType, !state);
|
||||
break;
|
||||
case 3: // Music
|
||||
g_system->getMixer()->muteSoundType(Audio::Mixer::kMusicSoundType, !state);
|
||||
break;
|
||||
default:
|
||||
error("Lua_V2::EnableAudioGroup - unknown group %d", group);
|
||||
}
|
||||
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::EnableAudioGroup: group: %d, state %d", group, (int)state);
|
||||
}
|
||||
|
||||
void Lua_V2::ImSelectSet() {
|
||||
lua_Object qualityObj = lua_getparam(1);
|
||||
|
||||
if (lua_isnumber(qualityObj)) {
|
||||
int quality = (int)lua_getnumber(qualityObj);
|
||||
// FIXME: func(quality);
|
||||
g_emiSound->selectMusicSet(quality);
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::ImSelectSet: quality mode: %d", quality);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::ImFlushStack() {
|
||||
// FIXME
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::ImFlushStack: currently guesswork");
|
||||
g_emiSound->flushStack();
|
||||
}
|
||||
|
||||
static Common::String addSoundSuffix(const char *fname) {
|
||||
Common::String filename = fname;
|
||||
if (!(g_grim->getGameFlags() & ADGF_DEMO)) {
|
||||
if (g_grim->getGamePlatform() == Common::kPlatformPS2) {
|
||||
filename += ".scx";
|
||||
} else {
|
||||
if (!filename.hasSuffix(".aif") && !filename.hasSuffix(".AIF")) {
|
||||
filename += ".aif";
|
||||
}
|
||||
}
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
void Lua_V2::LoadSound() {
|
||||
lua_Object strObj = lua_getparam(1);
|
||||
|
||||
if (!lua_isstring(strObj))
|
||||
return;
|
||||
|
||||
const char *str = lua_getstring(strObj);
|
||||
|
||||
Common::String filename = addSoundSuffix(str);
|
||||
|
||||
PoolSound *sound = new PoolSound(filename);
|
||||
lua_pushusertag(sound->getId(), MKTAG('A', 'I', 'F', 'F'));
|
||||
}
|
||||
|
||||
void Lua_V2::FreeSound() {
|
||||
lua_Object idObj = lua_getparam(1);
|
||||
if (!lua_isuserdata(idObj) || lua_tag(idObj) != MKTAG('A', 'I', 'F', 'F'))
|
||||
return;
|
||||
PoolSound *sound = PoolSound::getPool().getObject(lua_getuserdata(idObj));
|
||||
delete sound;
|
||||
}
|
||||
|
||||
void Lua_V2::PlayLoadedSound() {
|
||||
lua_Object idObj = lua_getparam(1);
|
||||
lua_Object loopingObj = lua_getparam(2);
|
||||
lua_Object volumeObj = lua_getparam(3);
|
||||
// FIXME: unknown parameter
|
||||
/*lua_Object bool2Obj =*/ lua_getparam(4);
|
||||
|
||||
if (!lua_isuserdata(idObj) || lua_tag(idObj) != MKTAG('A', 'I', 'F', 'F')) {
|
||||
// can't use error since it actually may happen:
|
||||
// when entering the bait shop after the termites were already put on Mandrill's cane,
|
||||
// the LUA code will not load the termite sound files but the script which starts
|
||||
// the sounds is running anyway
|
||||
warning("Lua_V2::PlayLoadedSound - ERROR: Unknown parameters");
|
||||
return;
|
||||
}
|
||||
|
||||
bool looping = !lua_isnil(loopingObj);
|
||||
|
||||
PoolSound *sound = PoolSound::getPool().getObject(lua_getuserdata(idObj));
|
||||
if (!sound) {
|
||||
warning("Lua_V2::PlayLoadedSound: can't find requested sound object");
|
||||
return;
|
||||
}
|
||||
|
||||
int volume = MAX_EMI_VOLUME;
|
||||
if (!lua_isnumber(volumeObj)) {
|
||||
// In the demo when the dart hits the balloon in the scumm bar, nil is passed
|
||||
// to the volume parameter.
|
||||
warning("Lua_V2::PlayLoadedSound - Unexpected parameter found, using default volume");
|
||||
} else {
|
||||
volume = (int)lua_getnumber(volumeObj);
|
||||
}
|
||||
sound->setVolume(convertEmiVolumeToMixer(volume));
|
||||
sound->play(looping);
|
||||
}
|
||||
|
||||
void Lua_V2::PlayLoadedSoundFrom() {
|
||||
lua_Object idObj = lua_getparam(1);
|
||||
lua_Object xObj = lua_getparam(2);
|
||||
lua_Object yObj = lua_getparam(3);
|
||||
lua_Object zObj = lua_getparam(4);
|
||||
lua_Object volumeOrLoopingObj = lua_getparam(5);
|
||||
lua_Object volumeObj = lua_getparam(6);
|
||||
|
||||
if (!lua_isuserdata(idObj) || lua_tag(idObj) != MKTAG('A', 'I', 'F', 'F')) {
|
||||
warning("Lua_V2::PlayLoadedSoundFrom - ERROR: Unknown parameters");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!lua_isnumber(xObj) || !lua_isnumber(yObj) || !lua_isnumber(zObj) ||
|
||||
!lua_isnumber(volumeObj)) {
|
||||
error("Lua_V2::PlayLoadedSoundFrom - ERROR: Unknown parameters");
|
||||
return;
|
||||
}
|
||||
|
||||
float x = lua_getnumber(xObj);
|
||||
float y = lua_getnumber(yObj);
|
||||
float z = lua_getnumber(zObj);
|
||||
|
||||
int volume = MAX_EMI_VOLUME;
|
||||
bool looping = false;
|
||||
|
||||
if (lua_isnumber(volumeOrLoopingObj)) {
|
||||
volume = (int)lua_getnumber(volumeOrLoopingObj);
|
||||
// special handling if 5th parameter is a boolean
|
||||
if (volume <= 1) {
|
||||
looping = volume;
|
||||
volume = (int)lua_getnumber(volumeObj);
|
||||
}
|
||||
} else {
|
||||
volume = (int)lua_getnumber(volumeObj);
|
||||
looping = !lua_isnil(volumeOrLoopingObj);
|
||||
}
|
||||
|
||||
PoolSound *sound = PoolSound::getPool().getObject(lua_getuserdata(idObj));
|
||||
if (!sound) {
|
||||
warning("Lua_V2::PlayLoadedSoundFrom: can't find requested sound object");
|
||||
return;
|
||||
}
|
||||
Math::Vector3d pos(x, y, z);
|
||||
sound->setVolume(convertEmiVolumeToMixer(volume));
|
||||
sound->playFrom(pos, looping);
|
||||
}
|
||||
|
||||
void Lua_V2::StopSound() {
|
||||
lua_Object idObj = lua_getparam(1);
|
||||
|
||||
if (!lua_isuserdata(idObj) || lua_tag(idObj) != MKTAG('A', 'I', 'F', 'F')) {
|
||||
warning("Lua_V2::StopSound - ERROR: Unknown parameters");
|
||||
return;
|
||||
}
|
||||
|
||||
PoolSound *sound = PoolSound::getPool().getObject(lua_getuserdata(idObj));
|
||||
if (!sound) {
|
||||
warning("Lua_V2::StopSound: can't find requested sound object");
|
||||
return;
|
||||
}
|
||||
sound->stop();
|
||||
}
|
||||
|
||||
void Lua_V2::IsSoundPlaying() {
|
||||
lua_Object idObj = lua_getparam(1);
|
||||
|
||||
if (!lua_isuserdata(idObj) || lua_tag(idObj) != MKTAG('A', 'I', 'F', 'F')) {
|
||||
// can't use error since it actually may happen during normal operation
|
||||
warning("Lua_V2::IsSoundPlaying - ERROR: Unknown parameters");
|
||||
pushbool(false);
|
||||
return;
|
||||
}
|
||||
|
||||
PoolSound *sound = PoolSound::getPool().getObject(lua_getuserdata(idObj));
|
||||
if (sound) {
|
||||
if (sound->isPlaying()) {
|
||||
pushbool(true);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
warning("Lua_V2::IsSoundPlaying: no sound track associated");
|
||||
}
|
||||
pushbool(false);
|
||||
}
|
||||
|
||||
void Lua_V2::PlaySound() {
|
||||
lua_Object strObj = lua_getparam(1);
|
||||
lua_Object volumeObj = lua_getparam(2);
|
||||
|
||||
if (!lua_isstring(strObj)) {
|
||||
error("Lua_V2::PlaySound - ERROR: Unknown parameters");
|
||||
return;
|
||||
}
|
||||
const char *str = lua_getstring(strObj);
|
||||
|
||||
int volume = MAX_EMI_VOLUME;
|
||||
if (!lua_isnumber(volumeObj)) {
|
||||
warning("Lua_V2::PlaySound - Unexpected parameter(s) found, using default volume for %s", str);
|
||||
} else {
|
||||
volume = (int)lua_getnumber(volumeObj);
|
||||
}
|
||||
|
||||
Common::String filename = addSoundSuffix(str);
|
||||
|
||||
if (!g_emiSound->startSfx(filename, convertEmiVolumeToMixer(volume))) {
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::PlaySound: Could not open sound '%s'", filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::PlaySoundFrom() {
|
||||
lua_Object strObj = lua_getparam(1);
|
||||
lua_Object xObj = lua_getparam(2);
|
||||
lua_Object yObj = lua_getparam(3);
|
||||
lua_Object zObj = lua_getparam(4);
|
||||
// FIXME: unknown parameter
|
||||
lua_Object volumeOrUnknownObj = lua_getparam(5);
|
||||
lua_Object volumeObj = lua_getparam(6);
|
||||
|
||||
int volume = MAX_EMI_VOLUME;
|
||||
|
||||
if (!lua_isstring(strObj)) {
|
||||
error("Lua_V2::PlaySoundFrom - ERROR: Unknown parameters");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!lua_isnumber(xObj) || !lua_isnumber(yObj) || !lua_isnumber(zObj)) {
|
||||
error("Lua_V2::PlayLoadedSoundFrom - ERROR: Unknown parameters");
|
||||
return;
|
||||
}
|
||||
float x = lua_getnumber(xObj);
|
||||
float y = lua_getnumber(yObj);
|
||||
float z = lua_getnumber(zObj);
|
||||
|
||||
// arg5 is optional and if present, it is FALSE
|
||||
if (lua_isnumber(volumeObj)) {
|
||||
volume = (int)lua_getnumber(volumeObj);
|
||||
} else if (lua_isnumber(volumeOrUnknownObj)) {
|
||||
volume = (int)lua_getnumber(volumeOrUnknownObj);
|
||||
} else {
|
||||
error("Lua_V2::PlaySoundFrom - ERROR: Unknown parameters");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *str = lua_getstring(strObj);
|
||||
Common::String filename = addSoundSuffix(str);
|
||||
|
||||
Math::Vector3d pos(x, y, z);
|
||||
|
||||
if (!g_emiSound->startSfxFrom(filename.c_str(), pos, convertEmiVolumeToMixer(volume))) {
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::PlaySoundFrom: Could not open sound '%s'", filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::GetSoundVolume() {
|
||||
lua_Object idObj = lua_getparam(1);
|
||||
if (!lua_isuserdata(idObj) || lua_tag(idObj) != MKTAG('A', 'I', 'F', 'F')) {
|
||||
error("Lua_V2::GetSoundVolume: Unknown Parameters");
|
||||
return;
|
||||
}
|
||||
PoolSound *sound = PoolSound::getPool().getObject(lua_getuserdata(idObj));
|
||||
if (sound) {
|
||||
lua_pushnumber(convertMixerVolumeToEmi(sound->getVolume()));
|
||||
} else {
|
||||
warning("Lua_V2::GetSoundVolume: can't find sound track");
|
||||
lua_pushnumber(convertMixerVolumeToEmi(Audio::Mixer::kMaxChannelVolume));
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::SetSoundVolume() {
|
||||
lua_Object idObj = lua_getparam(1);
|
||||
lua_Object volumeObj = lua_getparam(2);
|
||||
if (!lua_isuserdata(idObj) || lua_tag(idObj) != MKTAG('A', 'I', 'F', 'F')) {
|
||||
error("Lua_V2::SetSoundVolume: no valid sound id");
|
||||
return;
|
||||
}
|
||||
if (!lua_isnumber(volumeObj)) {
|
||||
error("Lua_V2::SetSoundVolume - ERROR: Unknown parameters");
|
||||
return;
|
||||
}
|
||||
|
||||
int volume = (int)lua_getnumber(volumeObj);
|
||||
PoolSound *sound = PoolSound::getPool().getObject(lua_getuserdata(idObj));
|
||||
|
||||
if (sound) {
|
||||
sound->setVolume(convertEmiVolumeToMixer(volume));
|
||||
} else {
|
||||
warning("Lua_V2:SetSoundVolume: can't find sound track");
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_V2::UpdateSoundPosition() {
|
||||
lua_Object idObj = lua_getparam(1);
|
||||
lua_Object param2 = lua_getparam(2);
|
||||
lua_Object param3 = lua_getparam(3);
|
||||
lua_Object param4 = lua_getparam(4);
|
||||
|
||||
if (!lua_isuserdata(idObj) || lua_tag(idObj) != MKTAG('A', 'I', 'F', 'F'))
|
||||
return;
|
||||
|
||||
if (!lua_isnumber(param2) || !lua_isnumber(param3) || !lua_isnumber(param4))
|
||||
return;
|
||||
|
||||
float x = lua_getnumber(param2);
|
||||
float y = lua_getnumber(param3);
|
||||
float z = lua_getnumber(param4);
|
||||
PoolSound *sound = PoolSound::getPool().getObject(lua_getuserdata(idObj));
|
||||
if (!sound)
|
||||
return;
|
||||
Math::Vector3d pos(x, y, z);
|
||||
sound->setPosition(pos);
|
||||
}
|
||||
|
||||
void Lua_V2::ImSetMusicVol() {
|
||||
// This only seems to be used in the demo.
|
||||
lua_Object volumeObj = lua_getparam(1);
|
||||
|
||||
if (!lua_isnumber(volumeObj))
|
||||
return;
|
||||
int volume = (int)lua_getnumber(volumeObj);
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::ImSetMusicVol: implement opcode, wants volume %d", convertEmiVolumeToMixer(volume));
|
||||
}
|
||||
|
||||
void Lua_V2::ImSetSfxVol() {
|
||||
// This only seems to be used in the demo.
|
||||
lua_Object volumeObj = lua_getparam(1);
|
||||
|
||||
if (!lua_isnumber(volumeObj))
|
||||
return;
|
||||
int volume = (int)lua_getnumber(volumeObj);
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::ImSetSfxVol: implement opcode, wants volume %d", convertEmiVolumeToMixer(volume));
|
||||
}
|
||||
|
||||
void Lua_V2::ImSetVoiceVol() {
|
||||
// This only seems to be used in the demo.
|
||||
lua_Object volumeObj = lua_getparam(1);
|
||||
|
||||
if (!lua_isnumber(volumeObj))
|
||||
return;
|
||||
int volume = (int)lua_getnumber(volumeObj);
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::ImSetVoiceVol: implement opcode, wants volume %d", convertEmiVolumeToMixer(volume));
|
||||
}
|
||||
|
||||
void Lua_V2::ImSetVoiceEffect() {
|
||||
// This only seems to be used in the demo.
|
||||
lua_Object strObj = lua_getparam(1);
|
||||
|
||||
if (!lua_isstring(strObj))
|
||||
return;
|
||||
|
||||
const char *str = lua_getstring(strObj);
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::ImSetVoiceEffect: implement opcode, wants effect %s", str);
|
||||
}
|
||||
|
||||
void Lua_V2::StopAllSounds() {
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::StopAllSounds: implement opcode");
|
||||
}
|
||||
|
||||
void Lua_V2::ImPushState() {
|
||||
lua_Object stateObj = lua_getparam(1);
|
||||
//lua_Object unknownBoolObj = lua_getparam(2);
|
||||
|
||||
g_emiSound->pushStateToStack();
|
||||
|
||||
if (lua_isnumber(stateObj)) {
|
||||
int state = (int)lua_getnumber(stateObj);
|
||||
g_imuseState = state;
|
||||
}
|
||||
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::ImPushState: currently guesswork");
|
||||
}
|
||||
void Lua_V2::ImPopState() {
|
||||
g_emiSound->popStateFromStack();
|
||||
Debug::debug(Debug::Sound | Debug::Scripts, "Lua_V2::ImPopState: currently guesswork");
|
||||
}
|
||||
|
||||
// Used in the demo only.
|
||||
void Lua_V2::ImPause() {
|
||||
g_emiSound->pause(true);
|
||||
}
|
||||
|
||||
// Used in the demo only.
|
||||
void Lua_V2::ImResume() {
|
||||
g_emiSound->pause(false);
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
484
engines/grim/emi/modelemi.cpp
Normal file
484
engines/grim/emi/modelemi.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/endian.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/grim.h"
|
||||
#include "engines/grim/material.h"
|
||||
#include "engines/grim/gfx_base.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/set.h"
|
||||
#include "engines/grim/emi/costumeemi.h"
|
||||
#include "engines/grim/emi/modelemi.h"
|
||||
#include "engines/grim/emi/animationemi.h"
|
||||
#include "engines/grim/emi/skeleton.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
struct Vector3int {
|
||||
uint16 _x;
|
||||
uint16 _y;
|
||||
uint16 _z;
|
||||
void setVal(uint16 x, uint16 y, uint16 z) {
|
||||
_x = x; _y = y; _z = z;
|
||||
}
|
||||
};
|
||||
|
||||
struct BoneInfo {
|
||||
int _incFac;
|
||||
int _joint;
|
||||
float _weight;
|
||||
};
|
||||
|
||||
Common::String readLAString(Common::ReadStream *ms) {
|
||||
int strLength = ms->readUint32LE();
|
||||
char *readString = new char[strLength];
|
||||
ms->read(readString, strLength);
|
||||
|
||||
Common::String retVal(readString);
|
||||
delete[] readString;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void EMIMeshFace::loadFace(Common::SeekableReadStream *data) {
|
||||
_flags = data->readUint32LE();
|
||||
_hasTexture = data->readUint32LE();
|
||||
|
||||
if (_hasTexture)
|
||||
_texID = data->readUint32LE();
|
||||
_faceLength = data->readUint32LE();
|
||||
_faceLength = _faceLength / 3;
|
||||
int x = 0, y = 0, z = 0;
|
||||
_indexes = new Vector3int[_faceLength];
|
||||
int j = 0;
|
||||
for (uint32 i = 0; i < _faceLength; i ++) {
|
||||
if (g_grim->getGamePlatform() == Common::kPlatformPS2) {
|
||||
x = data->readUint32LE();
|
||||
y = data->readUint32LE();
|
||||
z = data->readUint32LE();
|
||||
} else {
|
||||
x = data->readUint16LE();
|
||||
y = data->readUint16LE();
|
||||
z = data->readUint16LE();
|
||||
}
|
||||
_indexes[j++].setVal(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
EMIMeshFace::~EMIMeshFace() {
|
||||
delete[] _indexes;
|
||||
}
|
||||
|
||||
void EMIModel::setTex(uint32 index) {
|
||||
if (index < _numTextures && _mats[index]) {
|
||||
_mats[index]->select();
|
||||
g_driver->setBlendMode(_texFlags[index] & BlendAdditive);
|
||||
}
|
||||
}
|
||||
|
||||
void EMIModel::loadMesh(Common::SeekableReadStream *data) {
|
||||
//int strLength = 0; // Useful for PS2-strings
|
||||
|
||||
Common::String nameString = readLAString(data);
|
||||
|
||||
for (uint l = 0; l < nameString.size(); ++l) {
|
||||
if (nameString[l] == '\\') {
|
||||
nameString.setChar('/', l);
|
||||
}
|
||||
}
|
||||
_meshName = nameString;
|
||||
_radius = data->readFloatLE();
|
||||
_center->readFromStream(data);
|
||||
|
||||
_boxData->readFromStream(data);
|
||||
_boxData2->readFromStream(data);
|
||||
|
||||
_numTexSets = data->readUint32LE();
|
||||
_setType = data->readUint32LE();
|
||||
_numTextures = data->readUint32LE();
|
||||
|
||||
_texNames = new Common::String[_numTextures];
|
||||
_texFlags = new uint32[_numTextures];
|
||||
|
||||
for (uint32 i = 0; i < _numTextures; i++) {
|
||||
_texNames[i] = readLAString(data);
|
||||
_texFlags[i] = data->readUint32LE();
|
||||
if (_texFlags[i] & ~(BlendAdditive)) {
|
||||
Debug::debug(Debug::Models, "Model %s has unknown flags (%d) for texture %s", nameString.c_str(), _texFlags[i], _texNames[i].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
prepareTextures();
|
||||
|
||||
int type = data->readUint32LE();
|
||||
// Check that it is one of the known types
|
||||
// 3 is no texture vertecies
|
||||
// 18 is no normals
|
||||
// 19 is regular
|
||||
assert(type == 19 || type == 18 || type == 3);
|
||||
|
||||
_numVertices = data->readUint32LE();
|
||||
|
||||
_lighting = new Math::Vector3d[_numVertices];
|
||||
for (int i = 0; i < _numVertices; i++) {
|
||||
_lighting[i].set(1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
// Vertices
|
||||
_vertices = new Math::Vector3d[_numVertices];
|
||||
_drawVertices = new Math::Vector3d[_numVertices];
|
||||
for (int i = 0; i < _numVertices; i++) {
|
||||
_vertices[i].readFromStream(data);
|
||||
_drawVertices[i] = _vertices[i];
|
||||
}
|
||||
_normals = new Math::Vector3d[_numVertices];
|
||||
_drawNormals = new Math::Vector3d[_numVertices];
|
||||
if (type != 18) {
|
||||
for (int i = 0; i < _numVertices; i++) {
|
||||
_normals[i].readFromStream(data);
|
||||
_drawNormals[i] = _normals[i];
|
||||
}
|
||||
}
|
||||
_colorMap = new EMIColormap[_numVertices];
|
||||
for (int i = 0; i < _numVertices; ++i) {
|
||||
_colorMap[i].r = data->readByte();
|
||||
_colorMap[i].g = data->readByte();
|
||||
_colorMap[i].b = data->readByte();
|
||||
_colorMap[i].a = data->readByte();
|
||||
}
|
||||
if (type != 3) {
|
||||
_texVerts = new Math::Vector2d[_numVertices];
|
||||
for (int i = 0; i < _numVertices; i++) {
|
||||
_texVerts[i].readFromStream(data);
|
||||
}
|
||||
}
|
||||
// Faces
|
||||
|
||||
_numFaces = data->readUint32LE();
|
||||
if (data->eos()) {
|
||||
_numFaces = 0;
|
||||
_faces = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
_faces = new EMIMeshFace[_numFaces];
|
||||
|
||||
for (uint32 j = 0; j < _numFaces; j++) {
|
||||
_faces[j].setParent(this);
|
||||
_faces[j].loadFace(data);
|
||||
}
|
||||
|
||||
int hasBones = data->readUint32LE();
|
||||
|
||||
if (hasBones == 1) {
|
||||
_numBones = data->readUint32LE();
|
||||
_boneNames = new Common::String[_numBones];
|
||||
for (int i = 0; i < _numBones; i++) {
|
||||
_boneNames[i] = readLAString(data);
|
||||
}
|
||||
|
||||
_numBoneInfos = data->readUint32LE();
|
||||
_boneInfos = new BoneInfo[_numBoneInfos];
|
||||
|
||||
for (int i = 0; i < _numBoneInfos; i++) {
|
||||
_boneInfos[i]._incFac = data->readUint32LE();
|
||||
_boneInfos[i]._joint = data->readUint32LE();
|
||||
_boneInfos[i]._weight = data->readFloatLE();
|
||||
}
|
||||
} else {
|
||||
_numBones = 0;
|
||||
_numBoneInfos = 0;
|
||||
}
|
||||
prepareForRender();
|
||||
}
|
||||
|
||||
void EMIModel::setSkeleton(Skeleton *skel) {
|
||||
if (_skeleton == skel) {
|
||||
return;
|
||||
}
|
||||
_skeleton = skel;
|
||||
if (!skel || !_numBoneInfos) {
|
||||
return;
|
||||
}
|
||||
delete[] _vertexBoneInfo; _vertexBoneInfo = nullptr;
|
||||
_vertexBoneInfo = new int[_numBoneInfos];
|
||||
for (int i = 0; i < _numBoneInfos; i++) {
|
||||
_vertexBoneInfo[i] = _skeleton->findJointIndex(_boneNames[_boneInfos[i]._joint]);
|
||||
}
|
||||
}
|
||||
|
||||
void EMIModel::prepareForRender() {
|
||||
if (!_skeleton || !_vertexBoneInfo)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < _numVertices; i++) {
|
||||
_drawVertices[i].set(0.0f, 0.0f, 0.0f);
|
||||
_drawNormals[i].set(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
int boneVert = -1;
|
||||
for (int i = 0; i < _numBoneInfos; i++) {
|
||||
if (_boneInfos[i]._incFac == 1) {
|
||||
boneVert++;
|
||||
}
|
||||
|
||||
int jointIndex = _vertexBoneInfo[i];
|
||||
const Math::Matrix4 &jointMatrix = _skeleton->_joints[jointIndex]._finalMatrix;
|
||||
const Math::Matrix4 &bindPose = _skeleton->_joints[jointIndex]._absMatrix;
|
||||
|
||||
Math::Vector3d vert = _vertices[boneVert];
|
||||
vert -= bindPose.getPosition();
|
||||
vert = vert * bindPose.getRotation();
|
||||
jointMatrix.transform(&vert, true);
|
||||
_drawVertices[boneVert] += vert * _boneInfos[i]._weight;
|
||||
|
||||
Math::Vector3d normal = _normals[boneVert];
|
||||
normal = normal * bindPose.getRotation();
|
||||
jointMatrix.transform(&normal, false);
|
||||
_drawNormals[boneVert] += normal * _boneInfos[i]._weight;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _numVertices; i++) {
|
||||
_drawNormals[i].normalize();
|
||||
}
|
||||
|
||||
g_driver->updateEMIModel(this);
|
||||
}
|
||||
|
||||
void EMIModel::prepareTextures() {
|
||||
_mats = new Material*[_numTextures];
|
||||
for (uint32 i = 0; i < _numTextures; i++) {
|
||||
_mats[i] = _costume->loadMaterial(_texNames[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
void EMIModel::draw() {
|
||||
prepareForRender();
|
||||
|
||||
Actor *actor = _costume->getOwner();
|
||||
Math::Matrix4 modelToWorld = actor->getFinalMatrix();
|
||||
|
||||
if (!actor->isInOverworld()) {
|
||||
Math::AABB bounds = calculateWorldBounds(modelToWorld);
|
||||
if (bounds.isValid() && !g_grim->getCurrSet()->getFrustum().isInside(bounds))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_driver->supportsShaders()) {
|
||||
// If shaders are not available, we calculate lighting in software.
|
||||
Actor::LightMode lightMode = actor->getLightMode();
|
||||
if (lightMode != Actor::LightNone) {
|
||||
if (lightMode != Actor::LightStatic)
|
||||
_lightingDirty = true;
|
||||
|
||||
if (_lightingDirty) {
|
||||
updateLighting(modelToWorld);
|
||||
_lightingDirty = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (actor->getLightMode() == Actor::LightNone) {
|
||||
g_driver->disableLights();
|
||||
}
|
||||
}
|
||||
// We will need to add a call to the skeleton, to get the modified vertices, but for now,
|
||||
// I'll be happy with just static drawing
|
||||
for (uint32 i = 0; i < _numFaces; i++) {
|
||||
setTex(_faces[i]._texID);
|
||||
g_driver->drawEMIModelFace(this, &_faces[i]);
|
||||
}
|
||||
|
||||
if (g_driver->supportsShaders() && actor->getLightMode() == Actor::LightNone) {
|
||||
g_driver->enableLights();
|
||||
}
|
||||
}
|
||||
|
||||
void EMIModel::updateLighting(const Math::Matrix4 &modelToWorld) {
|
||||
// Current lighting implementation mimics the NormDyn mode of the original game, even if
|
||||
// FastDyn is requested. We assume that FastDyn mode was used only for the purpose of
|
||||
// performance optimization, but NormDyn mode is visually superior in all cases.
|
||||
|
||||
Common::Array<Grim::Light *> activeLights;
|
||||
bool hasAmbient = false;
|
||||
|
||||
Actor *actor = _costume->getOwner();
|
||||
|
||||
for (Light *l : g_grim->getCurrSet()->getLights(actor->isInOverworld())) {
|
||||
if (l->_enabled) {
|
||||
activeLights.push_back(l);
|
||||
if (l->_type == Light::Ambient)
|
||||
hasAmbient = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _numVertices; i++) {
|
||||
Math::Vector3d &result = _lighting[i];
|
||||
result.set(0.0f, 0.0f, 0.0f);
|
||||
|
||||
Math::Vector3d normal = _drawNormals[i];
|
||||
Math::Vector3d vertex = _drawVertices[i];
|
||||
modelToWorld.transform(&vertex, true);
|
||||
modelToWorld.transform(&normal, false);
|
||||
|
||||
for (uint j = 0; j < activeLights.size(); ++j) {
|
||||
Light *l = activeLights[j];
|
||||
float shade = l->_intensity;
|
||||
|
||||
if (l->_type != Light::Ambient) {
|
||||
// Direction of incident light
|
||||
Math::Vector3d dir = l->_dir;
|
||||
|
||||
if (l->_type != Light::Direct) {
|
||||
dir = l->_pos - vertex;
|
||||
float distSq = dir.getSquareMagnitude();
|
||||
if (distSq > l->_falloffFar * l->_falloffFar)
|
||||
continue;
|
||||
|
||||
dir.normalize();
|
||||
|
||||
if (distSq > l->_falloffNear * l->_falloffNear) {
|
||||
float dist = sqrt(distSq);
|
||||
float attn = 1.0f - (dist - l->_falloffNear) / (l->_falloffFar - l->_falloffNear);
|
||||
shade *= attn;
|
||||
}
|
||||
}
|
||||
|
||||
if (l->_type == Light::Spot) {
|
||||
float cosAngle = l->_dir.dotProduct(dir);
|
||||
if (cosAngle < 0.0f)
|
||||
continue;
|
||||
|
||||
float angle = acos(MIN(cosAngle, 1.0f));
|
||||
if (angle > l->_penumbraangle)
|
||||
continue;
|
||||
|
||||
if (angle > l->_umbraangle)
|
||||
shade *= 1.0f - (angle - l->_umbraangle) / (l->_penumbraangle - l->_umbraangle);
|
||||
}
|
||||
|
||||
float dot = MAX(0.0f, normal.dotProduct(dir));
|
||||
shade *= dot;
|
||||
}
|
||||
|
||||
Math::Vector3d color;
|
||||
color.x() = l->_color.getRed() / 255.0f;
|
||||
color.y() = l->_color.getGreen() / 255.0f;
|
||||
color.z() = l->_color.getBlue() / 255.0f;
|
||||
|
||||
result += color * shade;
|
||||
}
|
||||
|
||||
if (!hasAmbient) {
|
||||
// If the set does not specify an ambient light, a default ambient light is used
|
||||
// instead. The effect of this is visible for example in the set gmi.
|
||||
result += Math::Vector3d(0.5f, 0.5f, 0.5f);
|
||||
}
|
||||
|
||||
float max = MAX(MAX(result.x(), result.y()), result.z());
|
||||
if (max > 1.0f) {
|
||||
result.x() = result.x() / max;
|
||||
result.y() = result.y() / max;
|
||||
result.z() = result.z() / max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EMIModel::getBoundingBox(int *x1, int *y1, int *x2, int *y2) const {
|
||||
int winX1, winY1, winX2, winY2;
|
||||
g_driver->getScreenBoundingBox(this, &winX1, &winY1, &winX2, &winY2);
|
||||
if (winX1 != -1 && winY1 != -1 && winX2 != -1 && winY2 != -1) {
|
||||
*x1 = MIN(*x1, winX1);
|
||||
*y1 = MIN(*y1, winY1);
|
||||
*x2 = MAX(*x2, winX2);
|
||||
*y2 = MAX(*y2, winY2);
|
||||
}
|
||||
}
|
||||
|
||||
Math::AABB EMIModel::calculateWorldBounds(const Math::Matrix4 &matrix) const {
|
||||
Math::AABB bounds;
|
||||
for (int i = 0; i < _numVertices; i++) {
|
||||
bounds.expand(_drawVertices[i]);
|
||||
}
|
||||
bounds.transform(matrix);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
EMIModel::EMIModel(const Common::String &filename, Common::SeekableReadStream *data, EMICostume *costume) :
|
||||
_fname(filename), _costume(costume) {
|
||||
_meshAlphaMode = Actor::AlphaOff;
|
||||
_meshAlpha = 1.0;
|
||||
_numVertices = 0;
|
||||
_vertices = nullptr;
|
||||
_drawVertices = nullptr;
|
||||
_normals = nullptr;
|
||||
_drawNormals = nullptr;
|
||||
_colorMap = nullptr;
|
||||
_texVerts = nullptr;
|
||||
_numFaces = 0;
|
||||
_faces = nullptr;
|
||||
_numTextures = 0;
|
||||
_texNames = nullptr;
|
||||
_mats = nullptr;
|
||||
_numBones = 0;
|
||||
_boneInfos = nullptr;
|
||||
_numBoneInfos = 0;
|
||||
_vertexBoneInfo = nullptr;
|
||||
_skeleton = nullptr;
|
||||
_radius = 0;
|
||||
_center = new Math::Vector3d();
|
||||
_boxData = new Math::Vector3d();
|
||||
_boxData2 = new Math::Vector3d();
|
||||
_numTexSets = 0;
|
||||
_setType = 0;
|
||||
_boneNames = nullptr;
|
||||
_lighting = nullptr;
|
||||
_lightingDirty = true;
|
||||
_texFlags = nullptr;
|
||||
|
||||
loadMesh(data);
|
||||
g_driver->createEMIModel(this);
|
||||
}
|
||||
|
||||
EMIModel::~EMIModel() {
|
||||
g_driver->destroyEMIModel(this);
|
||||
|
||||
delete[] _vertices;
|
||||
delete[] _drawVertices;
|
||||
delete[] _normals;
|
||||
delete[] _drawNormals;
|
||||
delete[] _colorMap;
|
||||
delete[] _texVerts;
|
||||
delete[] _faces;
|
||||
delete[] _texNames;
|
||||
delete[] _mats;
|
||||
delete[] _boneInfos;
|
||||
delete[] _vertexBoneInfo;
|
||||
delete[] _boneNames;
|
||||
delete[] _lighting;
|
||||
delete[] _texFlags;
|
||||
delete _center;
|
||||
delete _boxData;
|
||||
delete _boxData2;
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
148
engines/grim/emi/modelemi.h
Normal file
148
engines/grim/emi/modelemi.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_MODELEMI_H
|
||||
#define GRIM_MODELEMI_H
|
||||
|
||||
#include "engines/grim/object.h"
|
||||
#include "engines/grim/actor.h"
|
||||
|
||||
#include "math/matrix4.h"
|
||||
#include "math/vector2d.h"
|
||||
#include "math/vector3d.h"
|
||||
#include "math/vector4d.h"
|
||||
#include "math/aabb.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class Material;
|
||||
|
||||
struct EMIColormap {
|
||||
unsigned char r, g, b, a;
|
||||
};
|
||||
|
||||
// Todo: port this to math::vector
|
||||
struct Vector3int;
|
||||
|
||||
class EMICostume;
|
||||
class EMIModel;
|
||||
struct BoneInfo;
|
||||
struct Bone;
|
||||
class Skeleton;
|
||||
|
||||
class EMIMeshFace {
|
||||
public:
|
||||
Vector3int *_indexes;
|
||||
uint32 _indicesEBO;
|
||||
uint32 _faceLength;
|
||||
uint32 _numFaces;
|
||||
uint32 _hasTexture;
|
||||
uint32 _texID;
|
||||
uint32 _flags;
|
||||
EMIModel *_parent;
|
||||
|
||||
enum MeshFaceFlags {
|
||||
kNoLighting = 0x20, // guessed, but distinctive for screen actors
|
||||
kAlphaBlend = 0x10000,
|
||||
kUnknownBlend = 0x40000 // used only in intro screen actors
|
||||
};
|
||||
|
||||
EMIMeshFace() : _faceLength(0), _numFaces(0), _hasTexture(0), _texID(0), _flags(0), _indexes(NULL), _parent(NULL), _indicesEBO(0) { }
|
||||
~EMIMeshFace();
|
||||
void loadFace(Common::SeekableReadStream *data);
|
||||
void setParent(EMIModel *m) { _parent = m; }
|
||||
void render();
|
||||
};
|
||||
|
||||
/* TODO: Remember to credit JohnDoe for his EMIMeshViewer, as most of the Skeletal
|
||||
* math, and understandings comes from his Delphi-code.
|
||||
*/
|
||||
class EMIModel : public Object {
|
||||
public:
|
||||
enum TextureFlags {
|
||||
BlendAdditive = 0x400
|
||||
// There are more flags, but their purpose is currently unknown.
|
||||
};
|
||||
|
||||
Common::String _meshName;
|
||||
Actor::AlphaMode _meshAlphaMode;
|
||||
float _meshAlpha;
|
||||
int _numVertices;
|
||||
Math::Vector3d *_vertices;
|
||||
Math::Vector3d *_drawVertices;
|
||||
Math::Vector3d *_normals;
|
||||
Math::Vector3d *_drawNormals;
|
||||
Math::Vector3d *_lighting;
|
||||
EMIColormap *_colorMap;
|
||||
Math::Vector2d *_texVerts;
|
||||
|
||||
uint32 _numFaces;
|
||||
EMIMeshFace *_faces;
|
||||
uint32 _numTextures;
|
||||
Common::String *_texNames;
|
||||
uint32 *_texFlags;
|
||||
Material **_mats;
|
||||
|
||||
Skeleton *_skeleton;
|
||||
|
||||
int _numBones;
|
||||
|
||||
// Bone-stuff:
|
||||
int _numBoneInfos;
|
||||
BoneInfo *_boneInfos;
|
||||
Common::String *_boneNames;
|
||||
int *_vertexBoneInfo;
|
||||
|
||||
// Stuff we dont know how to use:
|
||||
float _radius;
|
||||
Math::Vector3d *_center;
|
||||
Math::Vector3d *_boxData;
|
||||
Math::Vector3d *_boxData2;
|
||||
int _numTexSets;
|
||||
int _setType;
|
||||
|
||||
Common::String _fname;
|
||||
EMICostume *_costume;
|
||||
|
||||
void *_userData;
|
||||
bool _lightingDirty;
|
||||
|
||||
public:
|
||||
EMIModel(const Common::String &filename, Common::SeekableReadStream *data, EMICostume *costume);
|
||||
~EMIModel();
|
||||
void setTex(uint32 index);
|
||||
void setSkeleton(Skeleton *skel);
|
||||
void loadMesh(Common::SeekableReadStream *data);
|
||||
void prepareForRender();
|
||||
void prepareTextures();
|
||||
void draw();
|
||||
void updateLighting(const Math::Matrix4 &modelToWorld);
|
||||
void getBoundingBox(int *x1, int *y1, int *x2, int *y2) const;
|
||||
Math::AABB calculateWorldBounds(const Math::Matrix4 &matrix) const;
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
118
engines/grim/emi/poolsound.cpp
Normal file
118
engines/grim/emi/poolsound.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/str.h"
|
||||
|
||||
#include "engines/grim/emi/sound/emisound.h"
|
||||
#include "engines/grim/emi/poolsound.h"
|
||||
#include "engines/grim/resource.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
PoolSound::PoolSound() : _filename(""), _loaded(false), _soundId(0) {
|
||||
}
|
||||
|
||||
PoolSound::PoolSound(const Common::String &filename) : _filename(""), _loaded(false), _soundId(0) {
|
||||
openFile(filename);
|
||||
}
|
||||
|
||||
// Called when the engine restarts or Lua code calls FreeSound
|
||||
PoolSound::~PoolSound() {
|
||||
if (_loaded) {
|
||||
g_emiSound->freeLoadedSound(_soundId);
|
||||
}
|
||||
}
|
||||
|
||||
void PoolSound::setVolume(int volume) {
|
||||
if (_loaded) {
|
||||
g_emiSound->setLoadedSoundVolume(_soundId, volume);
|
||||
}
|
||||
}
|
||||
|
||||
void PoolSound::setBalance(int balance) {
|
||||
if (_loaded) {
|
||||
g_emiSound->setLoadedSoundPan(_soundId, balance);
|
||||
}
|
||||
}
|
||||
|
||||
void PoolSound::setPosition(Math::Vector3d &pos) {
|
||||
if (_loaded) {
|
||||
g_emiSound->setLoadedSoundPosition(_soundId, pos);
|
||||
}
|
||||
}
|
||||
|
||||
void PoolSound::play(bool looping) {
|
||||
if (_loaded) {
|
||||
g_emiSound->playLoadedSound(_soundId, looping);
|
||||
}
|
||||
}
|
||||
|
||||
void PoolSound::playFrom(const Math::Vector3d &pos, bool looping) {
|
||||
if (_loaded) {
|
||||
g_emiSound->playLoadedSoundFrom(_soundId, pos, looping);
|
||||
}
|
||||
}
|
||||
|
||||
void PoolSound::stop() {
|
||||
if (_loaded) {
|
||||
g_emiSound->stopLoadedSound(_soundId);
|
||||
}
|
||||
}
|
||||
|
||||
int PoolSound::getVolume() {
|
||||
if (_loaded) {
|
||||
return g_emiSound->getLoadedSoundVolume(_soundId);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool PoolSound::isPlaying() {
|
||||
if (_loaded) {
|
||||
return g_emiSound->getLoadedSoundStatus(_soundId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PoolSound::openFile(const Common::String &filename) {
|
||||
_filename = filename;
|
||||
_loaded = g_emiSound->loadSfx(filename.c_str(), _soundId);
|
||||
if (!_loaded) {
|
||||
warning("Could not open PoolSound file %s", filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void PoolSound::saveState(SaveGame *state) {
|
||||
state->writeBool(_loaded);
|
||||
state->writeLESint32(_soundId);
|
||||
}
|
||||
|
||||
void PoolSound::restoreState(SaveGame *state) {
|
||||
if (state->saveMinorVersion() >= 21) {
|
||||
_loaded = state->readBool();
|
||||
_soundId = state->readLESint32();
|
||||
} else {
|
||||
bool hasStream = state->readBool();
|
||||
if (hasStream)
|
||||
openFile(state->readString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
59
engines/grim/emi/poolsound.h
Normal file
59
engines/grim/emi/poolsound.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_POOLSOUND_H
|
||||
#define GRIM_POOLSOUND_H
|
||||
|
||||
#include "common/endian.h"
|
||||
|
||||
#include "engines/grim/pool.h"
|
||||
|
||||
#include "math/vector3d.h"
|
||||
|
||||
namespace Grim {
|
||||
class PoolSound : public PoolObject<PoolSound> {
|
||||
public:
|
||||
PoolSound();
|
||||
PoolSound(const Common::String &filename);
|
||||
~PoolSound();
|
||||
|
||||
void openFile(const Common::String &filename);
|
||||
void play(bool looping);
|
||||
void playFrom(const Math::Vector3d &pos, bool looping);
|
||||
void setVolume(int volume);
|
||||
void setBalance(int balance);
|
||||
void setPosition(Math::Vector3d &pos);
|
||||
void stop();
|
||||
int getVolume();
|
||||
bool isPlaying();
|
||||
void saveState(SaveGame *state);
|
||||
void restoreState(SaveGame *state);
|
||||
|
||||
static int32 getStaticTag() { return MKTAG('A','I','F','F'); }
|
||||
|
||||
private:
|
||||
Common::String _filename;
|
||||
int _soundId;
|
||||
bool _loaded;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
228
engines/grim/emi/skeleton.cpp
Normal file
228
engines/grim/emi/skeleton.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "math/vector3d.h"
|
||||
#include "math/vector4d.h"
|
||||
#include "math/quat.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/emi/animationemi.h"
|
||||
#include "engines/grim/emi/skeleton.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
#define ROTATE_OP 4
|
||||
#define TRANSLATE_OP 3
|
||||
|
||||
Skeleton::Skeleton(const Common::String &filename, Common::SeekableReadStream *data) :
|
||||
_numJoints(0), _joints(nullptr), _animLayers(nullptr) {
|
||||
loadSkeleton(data);
|
||||
}
|
||||
|
||||
Skeleton::~Skeleton() {
|
||||
for (int i = 0; i < MAX_ANIMATION_LAYERS; ++i) {
|
||||
delete[] _animLayers[i]._jointAnims;
|
||||
}
|
||||
delete[] _animLayers;
|
||||
delete[] _joints;
|
||||
}
|
||||
|
||||
void Skeleton::loadSkeleton(Common::SeekableReadStream *data) {
|
||||
_numJoints = data->readUint32LE();
|
||||
_joints = new Joint[_numJoints];
|
||||
|
||||
char inString[32];
|
||||
|
||||
for (int i = 0; i < _numJoints; i++) {
|
||||
data->read(inString, 32);
|
||||
_joints[i]._name = inString;
|
||||
data->read(inString, 32);
|
||||
_joints[i]._parent = inString;
|
||||
|
||||
_joints[i]._trans.readFromStream(data);
|
||||
_joints[i]._quat.readFromStream(data);
|
||||
|
||||
_joints[i]._parentIndex = findJointIndex(_joints[i]._parent);
|
||||
|
||||
_jointsMap[_joints[i]._name] = i;
|
||||
}
|
||||
initBones();
|
||||
resetAnim();
|
||||
}
|
||||
|
||||
void Skeleton::initBone(int index) {
|
||||
// The matrix should have identity at this point.
|
||||
_joints[index]._quat.toMatrix(_joints[index]._relMatrix);
|
||||
// Might need to be translate instead.
|
||||
_joints[index]._relMatrix.setPosition(_joints[index]._trans);
|
||||
if (_joints[index]._parentIndex == -1) {
|
||||
_joints[index]._absMatrix = _joints[index]._relMatrix;
|
||||
} else {
|
||||
_joints[index]._absMatrix = _joints[_joints[index]._parentIndex]._absMatrix;
|
||||
|
||||
// Might be the other way around.
|
||||
_joints[index]._absMatrix = _joints[index]._absMatrix * _joints[index]._relMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
void Skeleton::initBones() {
|
||||
for (int i = 0; i < _numJoints; i++) {
|
||||
initBone(i);
|
||||
}
|
||||
|
||||
_animLayers = new AnimationLayer[MAX_ANIMATION_LAYERS];
|
||||
for (int i = 0; i < MAX_ANIMATION_LAYERS; ++i) {
|
||||
_animLayers[i]._jointAnims = new JointAnimation[_numJoints];
|
||||
}
|
||||
}
|
||||
|
||||
void Skeleton::resetAnim() {
|
||||
for (int i = 0; i < MAX_ANIMATION_LAYERS; ++i) {
|
||||
AnimationLayer &layer = _animLayers[i];
|
||||
for (int j = 0; j < _numJoints; ++j) {
|
||||
JointAnimation &jointAnim = layer._jointAnims[j];
|
||||
jointAnim._pos.set(0.f, 0.f, 0.f);
|
||||
jointAnim._quat.set(0.f, 0.f, 0.f, 1.f);
|
||||
jointAnim._transWeight = 0.0f;
|
||||
jointAnim._rotWeight = 0.0f;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < _numJoints; ++i) {
|
||||
_joints[i]._animMatrix = _joints[i]._relMatrix;
|
||||
_joints[i]._animQuat = _joints[i]._quat;
|
||||
}
|
||||
}
|
||||
|
||||
void Skeleton::animate() {
|
||||
resetAnim();
|
||||
|
||||
// This first pass over the animations calculates bone-specific sums of blend weights for all
|
||||
// animation layers. The sums must be pre-computed in order to be able to normalize the blend
|
||||
// weights properly in the next step.
|
||||
for (Common::List<AnimationStateEmi*>::iterator j = _activeAnims.begin(); j != _activeAnims.end(); ++j) {
|
||||
(*j)->computeWeights();
|
||||
}
|
||||
|
||||
// Now make a second pass over the animations to actually accumulate animation to layers.
|
||||
for (Common::List<AnimationStateEmi*>::iterator j = _activeAnims.begin(); j != _activeAnims.end(); ++j) {
|
||||
(*j)->animate();
|
||||
}
|
||||
|
||||
// Blend the layers together in priority order to produce the final result. Highest priority
|
||||
// layer will get as much weight as it wants, while the next highest priority will get the
|
||||
// amount that remains and so on.
|
||||
for (int i = 0; i < _numJoints; ++i) {
|
||||
float remainingTransWeight = 1.0f;
|
||||
float remainingRotWeight = 1.0f;
|
||||
|
||||
for (int j = MAX_ANIMATION_LAYERS - 1; j >= 0; --j) {
|
||||
AnimationLayer &layer = _animLayers[j];
|
||||
JointAnimation &jointAnim = layer._jointAnims[i];
|
||||
|
||||
if (remainingRotWeight > 0.0f && jointAnim._rotWeight != 0.0f) {
|
||||
Math::Vector3d pos = _joints[i]._animMatrix.getPosition();
|
||||
_joints[i]._animQuat = _joints[i]._animQuat.slerpQuat(_joints[i]._animQuat * jointAnim._quat, remainingRotWeight);
|
||||
_joints[i]._animQuat.toMatrix(_joints[i]._animMatrix);
|
||||
_joints[i]._animMatrix.setPosition(pos);
|
||||
|
||||
remainingRotWeight *= 1.0f - jointAnim._rotWeight;
|
||||
}
|
||||
|
||||
if (remainingTransWeight > 0.0f && jointAnim._transWeight != 0.0f) {
|
||||
Math::Vector3d pos = _joints[i]._animMatrix.getPosition();
|
||||
Math::Vector3d delta = jointAnim._pos;
|
||||
_joints[i]._animMatrix.setPosition(pos + delta * remainingTransWeight);
|
||||
|
||||
remainingTransWeight *= 1.0f - jointAnim._transWeight;
|
||||
}
|
||||
|
||||
if (remainingRotWeight <= 0.0f && remainingTransWeight <= 0.0f)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
commitAnim();
|
||||
}
|
||||
|
||||
void Skeleton::addAnimation(AnimationStateEmi *anim) {
|
||||
_activeAnims.push_back(anim);
|
||||
}
|
||||
void Skeleton::removeAnimation(AnimationStateEmi *anim) {
|
||||
_activeAnims.remove(anim);
|
||||
}
|
||||
|
||||
void Skeleton::commitAnim() {
|
||||
for (int m = 0; m < _numJoints; ++m) {
|
||||
const Joint *parent = getParentJoint(&_joints[m]);
|
||||
if (parent) {
|
||||
_joints[m]._finalMatrix = parent->_finalMatrix * _joints[m]._animMatrix;
|
||||
_joints[m]._finalQuat = parent->_finalQuat * _joints[m]._animQuat;
|
||||
} else {
|
||||
_joints[m]._finalMatrix = _joints[m]._animMatrix;
|
||||
_joints[m]._finalQuat = _joints[m]._animQuat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Skeleton::findJointIndex(const Common::String &name) const {
|
||||
JointMap::const_iterator it = _jointsMap.find(name);
|
||||
if (it != _jointsMap.end())
|
||||
return it->_value;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Skeleton::hasJoint(const Common::String &name) const {
|
||||
return name.empty() || findJointIndex(name) >= 0;
|
||||
}
|
||||
|
||||
Joint *Skeleton::getJointNamed(const Common::String &name) const {
|
||||
int idx = findJointIndex(name);
|
||||
if (name.empty()) {
|
||||
return & _joints[0];
|
||||
} else if (idx == -1) {
|
||||
warning("Skeleton has no joint named '%s'!", name.c_str());
|
||||
return nullptr;
|
||||
} else {
|
||||
return & _joints[idx];
|
||||
}
|
||||
}
|
||||
|
||||
Joint *Skeleton::getParentJoint(const Joint *j) const {
|
||||
assert(j);
|
||||
if (j->_parentIndex == -1)
|
||||
return nullptr;
|
||||
return &_joints[j->_parentIndex];
|
||||
}
|
||||
|
||||
int Skeleton::getJointIndex(const Joint *j) const {
|
||||
int idx = j - _joints;
|
||||
assert(idx >= 0 && idx < _numJoints);
|
||||
return idx;
|
||||
}
|
||||
|
||||
AnimationLayer* Skeleton::getLayer(int priority) const {
|
||||
assert(priority >= 0 && priority < MAX_ANIMATION_LAYERS);
|
||||
return &_animLayers[priority];
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
103
engines/grim/emi/skeleton.h
Normal file
103
engines/grim/emi/skeleton.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_SKELETON_H
|
||||
#define GRIM_SKELETON_H
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-str.h"
|
||||
|
||||
#include "math/mathfwd.h"
|
||||
#include "math/quat.h"
|
||||
|
||||
#include "engines/grim/object.h"
|
||||
#include "engines/grim/actor.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class AnimationStateEmi;
|
||||
class AnimationEmi;
|
||||
|
||||
struct Joint {
|
||||
Common::String _name;
|
||||
Common::String _parent;
|
||||
Math::Vector3d _trans;
|
||||
Math::Quaternion _quat;
|
||||
int _parentIndex;
|
||||
Math::Matrix4 _absMatrix;
|
||||
Math::Matrix4 _relMatrix;
|
||||
Math::Matrix4 _animMatrix;
|
||||
Math::Quaternion _animQuat;
|
||||
Math::Matrix4 _finalMatrix;
|
||||
Math::Quaternion _finalQuat;
|
||||
};
|
||||
|
||||
struct JointAnimation {
|
||||
Math::Vector3d _pos;
|
||||
Math::Quaternion _quat;
|
||||
float _transWeight;
|
||||
float _rotWeight;
|
||||
};
|
||||
|
||||
struct AnimationLayer {
|
||||
JointAnimation* _jointAnims;
|
||||
};
|
||||
|
||||
class Skeleton : public Object {
|
||||
|
||||
void loadSkeleton(Common::SeekableReadStream *data);
|
||||
void initBone(int index);
|
||||
void initBones();
|
||||
void resetAnim();
|
||||
public:
|
||||
// Note: EMI uses priority 5 at most.
|
||||
static const int MAX_ANIMATION_LAYERS = 8;
|
||||
|
||||
int _numJoints;
|
||||
Joint *_joints;
|
||||
|
||||
typedef Common::HashMap<Common::String, int, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> JointMap;
|
||||
JointMap _jointsMap;
|
||||
|
||||
Skeleton(const Common::String &filename, Common::SeekableReadStream *data);
|
||||
~Skeleton();
|
||||
void animate();
|
||||
void commitAnim();
|
||||
void addAnimation(AnimationStateEmi *anim);
|
||||
void removeAnimation(AnimationStateEmi *anim);
|
||||
int findJointIndex(const Common::String &name) const;
|
||||
bool hasJoint(const Common::String &name) const;
|
||||
Joint *getJointNamed(const Common::String &name) const;
|
||||
Joint *getParentJoint(const Joint *j) const;
|
||||
int getJointIndex(const Joint *j) const;
|
||||
AnimationLayer* getLayer(int priority) const;
|
||||
private:
|
||||
AnimationLayer *_animLayers;
|
||||
Common::List<AnimationStateEmi*> _activeAnims;
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
||||
101
engines/grim/emi/sound/aifftrack.cpp
Normal file
101
engines/grim/emi/sound/aifftrack.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/mutex.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/aiff.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/emi/sound/aifftrack.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
AIFFTrack::AIFFTrack(Audio::Mixer::SoundType soundType) {
|
||||
_soundType = soundType;
|
||||
_looping = false;
|
||||
// A preloaded AIFF track may be played multiple times, so we don't
|
||||
// want to dispose after playing. The destructor of SoundTrack will
|
||||
// take care of disposing the stream instead.
|
||||
_disposeAfterPlaying = DisposeAfterUse::NO;
|
||||
}
|
||||
|
||||
AIFFTrack::~AIFFTrack() {
|
||||
stop();
|
||||
if (_handle) {
|
||||
g_system->getMixer()->stopHandle(*_handle);
|
||||
delete _handle;
|
||||
}
|
||||
}
|
||||
|
||||
bool AIFFTrack::openSound(const Common::String &filename, const Common::String &soundName, const Audio::Timestamp *start) {
|
||||
Common::SeekableReadStream *file = g_resourceloader->openNewStreamFile(filename, true);
|
||||
if (!file) {
|
||||
Debug::debug(Debug::Sound, "Stream for %s not open", soundName.c_str());
|
||||
return false;
|
||||
}
|
||||
_soundName = soundName;
|
||||
Audio::RewindableAudioStream *aiffStream = Audio::makeAIFFStream(file, DisposeAfterUse::YES);
|
||||
Audio::SeekableAudioStream *seekStream = dynamic_cast<Audio::SeekableAudioStream *>(aiffStream);
|
||||
_stream = aiffStream;
|
||||
if (start)
|
||||
seekStream->seek(*start);
|
||||
if (!_stream)
|
||||
return false;
|
||||
_handle = new Audio::SoundHandle();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AIFFTrack::setLooping(bool looping) {
|
||||
if (_looping == looping)
|
||||
return;
|
||||
_looping = looping;
|
||||
if (looping && _stream) {
|
||||
_stream = Audio::makeLoopingAudioStream(dynamic_cast<Audio::SeekableAudioStream *>(_stream), 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool AIFFTrack::play() {
|
||||
if (_stream) {
|
||||
Audio::RewindableAudioStream *stream = dynamic_cast<Audio::RewindableAudioStream *>(_stream);
|
||||
if (!_looping) {
|
||||
stream->rewind();
|
||||
}
|
||||
return SoundTrack::play();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AIFFTrack::isPlaying() {
|
||||
if (!_handle)
|
||||
return false;
|
||||
|
||||
return g_system->getMixer()->isSoundHandleActive(*_handle);
|
||||
}
|
||||
|
||||
Audio::Timestamp AIFFTrack::getPos() {
|
||||
// FIXME: Return actual stream position.
|
||||
return g_system->getMixer()->getSoundElapsedTime(*_handle);
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
53
engines/grim/emi/sound/aifftrack.h
Normal file
53
engines/grim/emi/sound/aifftrack.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_AIFFTRACK_H
|
||||
#define GRIM_AIFFTRACK_H
|
||||
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "engines/grim/emi/sound/track.h"
|
||||
|
||||
namespace Audio {
|
||||
class AudioStream;
|
||||
class SoundHandle;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class AIFFTrack : public SoundTrack {
|
||||
public:
|
||||
AIFFTrack(Audio::Mixer::SoundType soundType);
|
||||
~AIFFTrack();
|
||||
bool openSound(const Common::String &filename, const Common::String &soundName, const Audio::Timestamp *start = nullptr) override;
|
||||
bool isPlaying() override;
|
||||
bool isStreamOpen() { return _stream != NULL; }
|
||||
void setLooping(bool looping) override;
|
||||
bool isLooping() const override { return _looping; }
|
||||
bool play() override;
|
||||
Audio::Timestamp getPos() override;
|
||||
private:
|
||||
bool _looping;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
207
engines/grim/emi/sound/codecs/scx.cpp
Normal file
207
engines/grim/emi/sound/codecs/scx.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/xa.h"
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "engines/grim/emi/sound/codecs/scx.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
SCXStream::SCXStream(Common::SeekableReadStream *stream, const Audio::Timestamp *start, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
static const uint32 stereoChannelNames[SCX_MAX_CHANNELS] = { MKTAG('L', 'E', 'F', 'T'), MKTAG('R', 'G', 'H', 'T') };
|
||||
|
||||
stream->readUint32BE(); // 'SCRX'
|
||||
stream->readUint32LE();
|
||||
|
||||
_blockSize = stream->readUint16LE();
|
||||
/* totalBlockSize = */ stream->readUint16LE();
|
||||
|
||||
if (_blockSize & 0xf)
|
||||
error("Bad SCX block size %04x", _blockSize);
|
||||
|
||||
// Base our channel count based off the block size
|
||||
_channels = (_blockSize == 0) ? 1 : 2;
|
||||
|
||||
stream->skip(12);
|
||||
|
||||
uint32 channelSize[SCX_MAX_CHANNELS];
|
||||
for (int i = 0; i < _channels; i++) {
|
||||
uint32 tag = stream->readUint32BE();
|
||||
|
||||
if (isStereo()) {
|
||||
if (tag != stereoChannelNames[i])
|
||||
error("Bad stereo channel tag found '%s'", tag2str(tag));
|
||||
} else if (tag != MKTAG('M', 'O', 'N', 'O'))
|
||||
error("Bad mono channel tag found '%s'", tag2str(tag));
|
||||
|
||||
channelSize[i] = stream->readUint32LE();
|
||||
}
|
||||
|
||||
stream->seek(0x80);
|
||||
|
||||
uint32 leftRate = 0, rightRate = 0;
|
||||
for (int i = 0; i < _channels; i++) {
|
||||
if (stream->readUint32BE() != MKTAG('V', 'A', 'G', 'p'))
|
||||
error("Bad VAG header");
|
||||
|
||||
/* uint32 version = */ stream->readUint32BE();
|
||||
stream->readUint32BE();
|
||||
stream->readUint32BE();
|
||||
|
||||
if (i == 0)
|
||||
leftRate = stream->readUint32BE();
|
||||
else
|
||||
rightRate = stream->readUint32BE();
|
||||
|
||||
stream->skip(12); // skip useless info
|
||||
stream->skip(16); // skip name
|
||||
stream->skip(16); // skip zeroes
|
||||
}
|
||||
|
||||
if (isStereo() && leftRate != rightRate)
|
||||
error("Mismatching SCX rates");
|
||||
|
||||
_rate = leftRate;
|
||||
|
||||
if (isStereo()) {
|
||||
// TODO: Make XAStream allow for appending data (similar to how ScummVM
|
||||
// handles AAC/QDM2. For now, we de-interleave the XA ADPCM data and then
|
||||
// re-interleave in readBuffer().
|
||||
// Of course, in doing something that does better streaming, it would
|
||||
// screw up the XA loop points. So, I'm not really sure what is best atm.
|
||||
byte *leftOut = (byte*)malloc(channelSize[0]);
|
||||
byte *rightOut = (byte*)malloc(channelSize[1]);
|
||||
Common::MemoryWriteStream *leftStream = new Common::MemoryWriteStream(leftOut, channelSize[0]);
|
||||
Common::MemoryWriteStream *rightStream = new Common::MemoryWriteStream(rightOut, channelSize[1]);
|
||||
byte *buf = new byte[_blockSize];
|
||||
|
||||
while (stream->pos() < stream->size()) {
|
||||
stream->read(buf, _blockSize);
|
||||
leftStream->write(buf, _blockSize);
|
||||
stream->read(buf, _blockSize);
|
||||
rightStream->write(buf, _blockSize);
|
||||
}
|
||||
|
||||
_fileStreams[0] = new Common::MemoryReadStream(leftOut, channelSize[0], DisposeAfterUse::YES);
|
||||
_fileStreams[1] = new Common::MemoryReadStream(rightOut, channelSize[1], DisposeAfterUse::YES);
|
||||
|
||||
_xaStreams[0] = Audio::makeXAStream(_fileStreams[0], _rate);
|
||||
_xaStreams[1] = Audio::makeXAStream(_fileStreams[1], _rate);
|
||||
|
||||
delete[] buf;
|
||||
delete leftStream;
|
||||
delete rightStream;
|
||||
} else {
|
||||
_fileStreams[0] = stream->readStream(channelSize[0]);
|
||||
_fileStreams[1] = nullptr;
|
||||
_xaStreams[0] = Audio::makeXAStream(_fileStreams[0], _rate);
|
||||
_xaStreams[1] = nullptr;
|
||||
}
|
||||
|
||||
if (start) {
|
||||
// Read data from the sound stream until we hit the desired start position.
|
||||
// We do this instead of seeking so the loop point gets set up properly.
|
||||
int samples = (int)((int64)start->msecs() * _rate / 1000);
|
||||
int16 temp[1024];
|
||||
while (samples > 0) {
|
||||
samples -= _xaStreams[0]->readBuffer(temp, samples < 1024 ? samples : 1024);
|
||||
if (_xaStreams[1]) {
|
||||
_xaStreams[1]->readBuffer(temp, samples < 1024 ? samples : 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete stream;
|
||||
}
|
||||
|
||||
SCXStream::~SCXStream() {
|
||||
for (int i = 0; i < SCX_MAX_CHANNELS; i++)
|
||||
delete _xaStreams[i];
|
||||
}
|
||||
|
||||
int SCXStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
if (isStereo()) {
|
||||
// Needs to be divisible by the channel count
|
||||
assert((numSamples % 2) == 0);
|
||||
|
||||
// TODO: As per above, this probably should do more actual streaming
|
||||
|
||||
// Decode enough data from each channel
|
||||
int samplesPerChannel = numSamples / 2;
|
||||
int16 *leftSamples = new int16[samplesPerChannel];
|
||||
int16 *rightSamples = new int16[samplesPerChannel];
|
||||
|
||||
int samplesDecodedLeft = _xaStreams[0]->readBuffer(leftSamples, samplesPerChannel);
|
||||
int samplesDecodedRight = _xaStreams[1]->readBuffer(rightSamples, samplesPerChannel);
|
||||
assert(samplesDecodedLeft == samplesDecodedRight);
|
||||
(void)samplesDecodedRight;
|
||||
|
||||
// Now re-interleave the data
|
||||
int samplesDecoded = 0;
|
||||
int16 *leftSrc = leftSamples, *rightSrc = rightSamples;
|
||||
for (; samplesDecoded < samplesDecodedLeft * 2; samplesDecoded += 2) {
|
||||
*buffer++ = *leftSrc++;
|
||||
*buffer++ = *rightSrc++;
|
||||
}
|
||||
|
||||
delete[] leftSamples;
|
||||
delete[] rightSamples;
|
||||
return samplesDecoded;
|
||||
}
|
||||
|
||||
// Just read from the stream directly for mono
|
||||
return _xaStreams[0]->readBuffer(buffer, numSamples);
|
||||
}
|
||||
|
||||
bool SCXStream::rewind() {
|
||||
if (!_xaStreams[0]->rewind())
|
||||
return false;
|
||||
|
||||
return !isStereo() || _xaStreams[1]->rewind();
|
||||
}
|
||||
|
||||
Audio::Timestamp SCXStream::getPos() const {
|
||||
int32 pos = _fileStreams[0]->pos();
|
||||
|
||||
// Each XA ADPCM block of 16 bytes decompresses to 28 samples.
|
||||
int32 samples = pos * 28 / 16;
|
||||
uint32 msecs = (uint32)((int64)samples * 1000 / _rate);
|
||||
|
||||
return Audio::Timestamp(msecs);
|
||||
}
|
||||
|
||||
SCXStream *makeSCXStream(Common::SeekableReadStream *stream, const Audio::Timestamp *start, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
if (stream->readUint32BE() != MKTAG('S', 'C', 'R', 'X')) {
|
||||
delete stream;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
stream->seek(0);
|
||||
return new SCXStream(stream, start, disposeAfterUse);
|
||||
}
|
||||
|
||||
} // End of namespace Grim
|
||||
71
engines/grim/emi/sound/codecs/scx.h
Normal file
71
engines/grim/emi/sound/codecs/scx.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_SCX_H
|
||||
#define GRIM_SCX_H
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
// I've only ever seen up to two
|
||||
#define SCX_MAX_CHANNELS 2
|
||||
|
||||
class SCXStream : public Audio::RewindableAudioStream {
|
||||
public:
|
||||
SCXStream(Common::SeekableReadStream *stream, const Audio::Timestamp *start, DisposeAfterUse::Flag disposeAfterUse);
|
||||
~SCXStream();
|
||||
|
||||
bool isStereo() const override { return _channels == 2; }
|
||||
bool endOfData() const override { return _xaStreams[0]->endOfData(); }
|
||||
int getRate() const override { return _rate; }
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
|
||||
bool rewind() override;
|
||||
Audio::Timestamp getPos() const;
|
||||
|
||||
private:
|
||||
int _channels;
|
||||
int _rate;
|
||||
uint16 _blockSize;
|
||||
|
||||
Common::SeekableReadStream *_fileStreams[SCX_MAX_CHANNELS];
|
||||
Audio::RewindableAudioStream *_xaStreams[SCX_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes an input stream containing SCX sound data and creates
|
||||
* a RewindableAudioStream from that.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the SCX data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @return a new RewindableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
SCXStream *makeSCXStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
const Audio::Timestamp *start,
|
||||
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
|
||||
} // End of namespace Grim
|
||||
|
||||
#endif
|
||||
1094
engines/grim/emi/sound/emisound.cpp
Normal file
1094
engines/grim/emi/sound/emisound.cpp
Normal file
File diff suppressed because it is too large
Load Diff
146
engines/grim/emi/sound/emisound.h
Normal file
146
engines/grim/emi/sound/emisound.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_MSS_H
|
||||
#define GRIM_MSS_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "common/str.h"
|
||||
#include "common/stack.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/hashmap.h"
|
||||
|
||||
#include "math/vector3d.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class SoundTrack;
|
||||
class SaveGame;
|
||||
|
||||
struct MusicEntry {
|
||||
int _x;
|
||||
int _y;
|
||||
int _sync;
|
||||
int _trim;
|
||||
int _id;
|
||||
Common::String _type;
|
||||
Common::String _name;
|
||||
Common::String _filename;
|
||||
};
|
||||
|
||||
// Currently this class only implements the exact functions called in iMuse
|
||||
// from Actor, to allow for splitting that into EMI-sound and iMuse without
|
||||
// changing iMuse.
|
||||
class EMISound {
|
||||
public:
|
||||
EMISound(int fps);
|
||||
~EMISound();
|
||||
bool startVoice(const Common::String &soundName, int volume = static_cast<int>(Audio::Mixer::kMaxChannelVolume), int pan = 64);
|
||||
bool startSfx(const Common::String &soundName, int volume = static_cast<int>(Audio::Mixer::kMaxChannelVolume), int pan = 64);
|
||||
bool startSfxFrom(const Common::String &soundName, const Math::Vector3d &pos, int volume = static_cast<int>(Audio::Mixer::kMaxChannelVolume));
|
||||
bool getSoundStatus(const Common::String &soundName);
|
||||
void stopSound(const Common::String &soundName);
|
||||
int32 getPosIn16msTicks(const Common::String &soundName);
|
||||
|
||||
void setVolume(const Common::String &soundName, int volume);
|
||||
void setPan(const Common::String &soundName, int pan); /* pan: 0 .. 127 */
|
||||
|
||||
bool loadSfx(const Common::String &soundName, int &id);
|
||||
void playLoadedSound(int id, bool looping);
|
||||
void playLoadedSoundFrom(int id, const Math::Vector3d &pos, bool looping);
|
||||
void setLoadedSoundLooping(int id, bool looping);
|
||||
void stopLoadedSound(int id);
|
||||
void freeLoadedSound(int id);
|
||||
void setLoadedSoundVolume(int id, int volume);
|
||||
void setLoadedSoundPan(int id, int pan);
|
||||
void setLoadedSoundPosition(int id, const Math::Vector3d &pos);
|
||||
bool getLoadedSoundStatus(int id);
|
||||
int getLoadedSoundVolume(int id);
|
||||
|
||||
void setMusicState(int stateId);
|
||||
void selectMusicSet(int setId);
|
||||
|
||||
bool stateHasLooped(int stateId);
|
||||
bool stateHasEnded(int stateId);
|
||||
|
||||
void restoreState(SaveGame *savedState);
|
||||
void saveState(SaveGame *savedState);
|
||||
|
||||
void pushStateToStack();
|
||||
void popStateFromStack();
|
||||
void flushStack();
|
||||
void pause(bool paused);
|
||||
void flushTracks();
|
||||
|
||||
uint32 getMsPos(int stateId);
|
||||
|
||||
void updateSoundPositions();
|
||||
|
||||
private:
|
||||
struct StackEntry {
|
||||
int _state;
|
||||
SoundTrack *_track;
|
||||
};
|
||||
|
||||
typedef Common::List<SoundTrack *> TrackList;
|
||||
TrackList _playingTracks;
|
||||
SoundTrack *_musicTrack;
|
||||
MusicEntry *_musicTable;
|
||||
Common::String _musicPrefix;
|
||||
Common::Stack<StackEntry> _stateStack;
|
||||
// A mutex to avoid concurrent modification of the sound channels by the engine thread
|
||||
// and the timer callback, which may run in a different thread.
|
||||
Common::Mutex _mutex;
|
||||
|
||||
typedef Common::HashMap<int, SoundTrack *> TrackMap;
|
||||
TrackMap _preloadedTrackMap;
|
||||
|
||||
int _curMusicState;
|
||||
int _numMusicStates;
|
||||
int _callbackFps;
|
||||
int _curTrackId;
|
||||
|
||||
static void timerHandler(void *refConf);
|
||||
void removeItem(SoundTrack *item);
|
||||
TrackList::iterator getPlayingTrackByName(const Common::String &name);
|
||||
void freeChannel(int32 channel);
|
||||
void initMusicTable();
|
||||
|
||||
void callback();
|
||||
void updateTrack(SoundTrack *track);
|
||||
void freePlayingSounds();
|
||||
void freeLoadedSounds();
|
||||
SoundTrack *initTrack(const Common::String &soundName, Audio::Mixer::SoundType soundType, const Audio::Timestamp *start = nullptr) const;
|
||||
SoundTrack *restartTrack(SoundTrack *track);
|
||||
bool startSound(const Common::String &soundName, Audio::Mixer::SoundType soundType, int volume, int pan);
|
||||
bool startSoundFrom(const Common::String &soundName, Audio::Mixer::SoundType soundType, const Math::Vector3d &pos, int volume);
|
||||
void saveTrack(SoundTrack *track, SaveGame *savedState);
|
||||
SoundTrack *restoreTrack(SaveGame *savedState);
|
||||
MusicEntry *initMusicTableDemo(const Common::String &filename);
|
||||
void initMusicTableRetail(MusicEntry *table, const Common::String &filename);
|
||||
};
|
||||
|
||||
extern EMISound *g_emiSound;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
223
engines/grim/emi/sound/mp3track.cpp
Normal file
223
engines/grim/emi/sound/mp3track.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/mutex.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/mp3.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/textsplit.h"
|
||||
#include "engines/grim/emi/sound/mp3track.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
/**
|
||||
* This is a an extension of Audio::SubLooppingAudioStream that adds a start
|
||||
* time parameter as well as a getter for the stream position.
|
||||
*/
|
||||
class EMISubLoopingAudioStream : public Audio::AudioStream {
|
||||
public:
|
||||
EMISubLoopingAudioStream(Audio::SeekableAudioStream *stream, uint loops,
|
||||
const Audio::Timestamp start,
|
||||
const Audio::Timestamp loopStart,
|
||||
const Audio::Timestamp loopEnd,
|
||||
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES)
|
||||
: _parent(stream, disposeAfterUse),
|
||||
_pos(convertTimeToStreamPos(start, getRate(), isStereo())),
|
||||
_loopStart(convertTimeToStreamPos(loopStart, getRate(), isStereo())),
|
||||
_loopEnd(convertTimeToStreamPos(loopEnd, getRate(), isStereo())),
|
||||
_done(false), _hasLooped(false) {
|
||||
assert(loopStart < loopEnd);
|
||||
|
||||
if (!_parent->seek(_pos))
|
||||
_done = true;
|
||||
}
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override {
|
||||
if (_done)
|
||||
return 0;
|
||||
|
||||
int framesLeft = MIN(_loopEnd.frameDiff(_pos), numSamples);
|
||||
int framesRead = _parent->readBuffer(buffer, framesLeft);
|
||||
_pos = _pos.addFrames(framesRead);
|
||||
|
||||
if (framesRead < framesLeft && _parent->endOfData()) {
|
||||
// TODO: Proper error indication.
|
||||
_done = true;
|
||||
return framesRead;
|
||||
}
|
||||
else if (_pos == _loopEnd) {
|
||||
if (!_parent->seek(_loopStart)) {
|
||||
// TODO: Proper error indication.
|
||||
_done = true;
|
||||
return framesRead;
|
||||
}
|
||||
|
||||
_pos = _loopStart;
|
||||
framesLeft = numSamples - framesLeft;
|
||||
_hasLooped = true;
|
||||
return framesRead + readBuffer(buffer + framesRead, framesLeft);
|
||||
}
|
||||
else {
|
||||
return framesRead;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasLooped() const { return _hasLooped; }
|
||||
bool endOfData() const override { return _done; }
|
||||
|
||||
bool isStereo() const override { return _parent->isStereo(); }
|
||||
int getRate() const override { return _parent->getRate(); }
|
||||
Audio::Timestamp getPos() const { return _pos; }
|
||||
|
||||
private:
|
||||
Common::DisposablePtr<Audio::SeekableAudioStream> _parent;
|
||||
|
||||
Audio::Timestamp _pos;
|
||||
Audio::Timestamp _loopStart, _loopEnd;
|
||||
|
||||
bool _done;
|
||||
bool _hasLooped;
|
||||
};
|
||||
|
||||
void MP3Track::parseRIFFHeader(Common::SeekableReadStream *data) {
|
||||
uint32 tag = data->readUint32BE();
|
||||
if (tag == MKTAG('R','I','F','F')) {
|
||||
_endFlag = false;
|
||||
data->seek(18, SEEK_CUR);
|
||||
_channels = data->readByte();
|
||||
data->readByte();
|
||||
_freq = data->readUint32LE();
|
||||
data->seek(6, SEEK_CUR);
|
||||
_bits = data->readByte();
|
||||
data->seek(5, SEEK_CUR);
|
||||
_regionLength = data->readUint32LE();
|
||||
_headerSize = 44;
|
||||
} else {
|
||||
error("Unknown file header");
|
||||
}
|
||||
}
|
||||
|
||||
MP3Track::JMMCuePoints MP3Track::parseJMMFile(const Common::String &filename) {
|
||||
JMMCuePoints cuePoints;
|
||||
Common::SeekableReadStream *stream = g_resourceloader->openNewStreamFile(filename);
|
||||
if (stream) {
|
||||
TextSplitter ts(filename, stream);
|
||||
float startMs = 0.0f;
|
||||
float loopStartMs = 0.0f, loopEndMs = 0.0f;
|
||||
|
||||
ts.scanString(".start %f", 1, &startMs);
|
||||
if (ts.checkString(".jump"))
|
||||
ts.scanString(".jump %f %f", 2, &loopEndMs, &loopStartMs);
|
||||
|
||||
// Use microsecond precision for the timestamps.
|
||||
cuePoints._start = Audio::Timestamp(startMs / 1000, (int)(startMs * 1000) % 1000000, 1000000);
|
||||
cuePoints._loopStart = Audio::Timestamp(loopStartMs / 1000, (int)(loopStartMs * 1000) % 1000000, 1000000);
|
||||
cuePoints._loopEnd = Audio::Timestamp(loopEndMs / 1000, (int)(loopEndMs * 1000) % 1000000, 1000000);
|
||||
}
|
||||
delete stream;
|
||||
return cuePoints;
|
||||
}
|
||||
|
||||
MP3Track::MP3Track(Audio::Mixer::SoundType soundType) {
|
||||
_soundType = soundType;
|
||||
_headerSize = 0;
|
||||
_regionLength = 0;
|
||||
_freq = 0;
|
||||
_bits = 0,
|
||||
_channels = 0;
|
||||
_endFlag = false;
|
||||
_looping = false;
|
||||
}
|
||||
|
||||
MP3Track::~MP3Track() {
|
||||
stop();
|
||||
if (_handle) {
|
||||
g_system->getMixer()->stopHandle(*_handle);
|
||||
delete _handle;
|
||||
}
|
||||
}
|
||||
|
||||
bool MP3Track::openSound(const Common::String &filename, const Common::String &soundName, const Audio::Timestamp *start) {
|
||||
Common::SeekableReadStream *file = g_resourceloader->openNewStreamFile(filename);
|
||||
if (!file) {
|
||||
Debug::debug(Debug::Sound, "Stream for %s not open", soundName.c_str());
|
||||
return false;
|
||||
}
|
||||
_soundName = soundName;
|
||||
#ifndef USE_MAD
|
||||
warning("Cannot open %s, MP3 support not enabled", soundName.c_str());
|
||||
return true;
|
||||
#else
|
||||
parseRIFFHeader(file);
|
||||
|
||||
MP3Track::JMMCuePoints cuePoints;
|
||||
if (soundName.size() > 4) {
|
||||
cuePoints = parseJMMFile(Common::String(filename.c_str(), filename.size() - 4) + ".jmm");
|
||||
}
|
||||
|
||||
if (start)
|
||||
cuePoints._start = *start;
|
||||
|
||||
Audio::SeekableAudioStream *mp3Stream = Audio::makeMP3Stream(file, DisposeAfterUse::YES);
|
||||
|
||||
if (cuePoints._loopEnd <= cuePoints._loopStart) {
|
||||
_stream = mp3Stream;
|
||||
mp3Stream->seek(cuePoints._start);
|
||||
_looping = false;
|
||||
} else {
|
||||
_stream = new EMISubLoopingAudioStream(mp3Stream, 0, cuePoints._start, cuePoints._loopStart, cuePoints._loopEnd);
|
||||
_looping = true;
|
||||
}
|
||||
_handle = new Audio::SoundHandle();
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MP3Track::hasLooped() {
|
||||
if (!_stream || !_looping)
|
||||
return false;
|
||||
EMISubLoopingAudioStream *las = static_cast<EMISubLoopingAudioStream*>(_stream);
|
||||
return las->hasLooped();
|
||||
}
|
||||
|
||||
bool MP3Track::isPlaying() {
|
||||
if (!_handle)
|
||||
return false;
|
||||
|
||||
return g_system->getMixer()->isSoundHandleActive(*_handle);
|
||||
}
|
||||
|
||||
Audio::Timestamp MP3Track::getPos() {
|
||||
if (!_stream)
|
||||
return Audio::Timestamp(0);
|
||||
if (_looping) {
|
||||
EMISubLoopingAudioStream *slas = static_cast<EMISubLoopingAudioStream*>(_stream);
|
||||
return slas->getPos();
|
||||
} else {
|
||||
return g_system->getMixer()->getSoundElapsedTime(*_handle);
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
64
engines/grim/emi/sound/mp3track.h
Normal file
64
engines/grim/emi/sound/mp3track.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_MP3TRACK_H
|
||||
#define GRIM_MP3TRACK_H
|
||||
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "audio/timestamp.h"
|
||||
|
||||
#include "engines/grim/emi/sound/track.h"
|
||||
|
||||
namespace Audio {
|
||||
class AudioStream;
|
||||
class SoundHandle;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class MP3Track : public SoundTrack {
|
||||
struct JMMCuePoints {
|
||||
Audio::Timestamp _start;
|
||||
Audio::Timestamp _loopStart;
|
||||
Audio::Timestamp _loopEnd;
|
||||
};
|
||||
uint32 _headerSize;
|
||||
uint32 _regionLength;
|
||||
uint32 _freq;
|
||||
char _bits;
|
||||
char _channels;
|
||||
bool _endFlag;
|
||||
bool _looping;
|
||||
void parseRIFFHeader(Common::SeekableReadStream *data);
|
||||
JMMCuePoints parseJMMFile(const Common::String &filename);
|
||||
public:
|
||||
MP3Track(Audio::Mixer::SoundType soundType);
|
||||
~MP3Track();
|
||||
bool openSound(const Common::String &filename, const Common::String &soundName, const Audio::Timestamp *start = nullptr) override;
|
||||
bool hasLooped() override;
|
||||
bool isPlaying() override;
|
||||
Audio::Timestamp getPos() override;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
95
engines/grim/emi/sound/scxtrack.cpp
Normal file
95
engines/grim/emi/sound/scxtrack.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/mutex.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/emi/sound/codecs/scx.h"
|
||||
#include "engines/grim/emi/sound/scxtrack.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
SCXTrack::SCXTrack(Audio::Mixer::SoundType soundType) {
|
||||
_disposeAfterPlaying = DisposeAfterUse::NO;
|
||||
_soundType = soundType;
|
||||
_looping = false;
|
||||
}
|
||||
|
||||
SCXTrack::~SCXTrack() {
|
||||
stop();
|
||||
if (_handle) {
|
||||
g_system->getMixer()->stopHandle(*_handle);
|
||||
delete _handle;
|
||||
}
|
||||
}
|
||||
|
||||
bool SCXTrack::openSound(const Common::String &filename, const Common::String &soundName, const Audio::Timestamp *start) {
|
||||
Common::SeekableReadStream *file = g_resourceloader->openNewStreamFile(filename);
|
||||
if (!file) {
|
||||
Debug::debug(Debug::Sound, "Stream for %s not open", soundName.c_str());
|
||||
return false;
|
||||
}
|
||||
_soundName = soundName;
|
||||
Audio::RewindableAudioStream *scxStream = makeSCXStream(file, start, DisposeAfterUse::YES);
|
||||
_stream = scxStream;
|
||||
_handle = new Audio::SoundHandle();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SCXTrack::isPlaying() {
|
||||
if (!_handle)
|
||||
return false;
|
||||
|
||||
return g_system->getMixer()->isSoundHandleActive(*_handle);
|
||||
}
|
||||
|
||||
Audio::Timestamp SCXTrack::getPos() {
|
||||
if (!_stream || _looping)
|
||||
return Audio::Timestamp(0);
|
||||
return dynamic_cast<SCXStream*>(_stream)->getPos();
|
||||
}
|
||||
|
||||
bool SCXTrack::play() {
|
||||
if (_stream) {
|
||||
Audio::RewindableAudioStream *stream = dynamic_cast<Audio::RewindableAudioStream *>(_stream);
|
||||
if (!_looping) {
|
||||
stream->rewind();
|
||||
}
|
||||
return SoundTrack::play();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SCXTrack::setLooping(bool looping) {
|
||||
if (_looping == looping)
|
||||
return;
|
||||
_looping = looping;
|
||||
if (looping && _stream) {
|
||||
_stream = Audio::makeLoopingAudioStream(dynamic_cast<Audio::RewindableAudioStream *>(_stream), 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
52
engines/grim/emi/sound/scxtrack.h
Normal file
52
engines/grim/emi/sound/scxtrack.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_SCXTRACK_H
|
||||
#define GRIM_SCXTRACK_H
|
||||
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "engines/grim/emi/sound/track.h"
|
||||
|
||||
namespace Audio {
|
||||
class AudioStream;
|
||||
class SoundHandle;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class SCXTrack : public SoundTrack {
|
||||
public:
|
||||
SCXTrack(Audio::Mixer::SoundType soundType);
|
||||
~SCXTrack();
|
||||
bool openSound(const Common::String &filename, const Common::String &soundName, const Audio::Timestamp *start = nullptr) override;
|
||||
bool isPlaying() override;
|
||||
Audio::Timestamp getPos() override;
|
||||
bool play() override;
|
||||
void setLooping(bool looping) override;
|
||||
|
||||
private:
|
||||
bool _looping;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
154
engines/grim/emi/sound/track.cpp
Normal file
154
engines/grim/emi/sound/track.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/mutex.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#include "engines/grim/savegame.h"
|
||||
#include "engines/grim/emi/sound/track.h"
|
||||
#include "engines/grim/grim.h"
|
||||
#include "engines/grim/set.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
SoundTrack::SoundTrack() {
|
||||
_stream = nullptr;
|
||||
_handle = nullptr;
|
||||
_paused = false;
|
||||
_positioned = false;
|
||||
_balance = 0;
|
||||
_volume = Audio::Mixer::kMaxChannelVolume;
|
||||
_disposeAfterPlaying = DisposeAfterUse::YES;
|
||||
_sync = 0;
|
||||
_fadeMode = FadeNone;
|
||||
_fade = 1.0f;
|
||||
_attenuation = 1.0f;
|
||||
|
||||
// Initialize to a plain sound for now
|
||||
_soundType = Audio::Mixer::kPlainSoundType;
|
||||
}
|
||||
|
||||
SoundTrack::~SoundTrack() {
|
||||
if (_stream && (_disposeAfterPlaying == DisposeAfterUse::NO || !_handle))
|
||||
delete _stream;
|
||||
}
|
||||
|
||||
Common::String SoundTrack::getSoundName() {
|
||||
return _soundName;
|
||||
}
|
||||
|
||||
void SoundTrack::setSoundName(const Common::String &name) {
|
||||
_soundName = name;
|
||||
}
|
||||
|
||||
void SoundTrack::setVolume(int volume) {
|
||||
_volume = MIN(volume, static_cast<int>(Audio::Mixer::kMaxChannelVolume));
|
||||
if (_handle) {
|
||||
g_system->getMixer()->setChannelVolume(*_handle, (byte)getEffectiveVolume());
|
||||
}
|
||||
}
|
||||
|
||||
void SoundTrack::setPosition(bool positioned, const Math::Vector3d &pos) {
|
||||
_positioned = positioned;
|
||||
_pos = pos;
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
void SoundTrack::updatePosition() {
|
||||
if (!_positioned)
|
||||
return;
|
||||
|
||||
Set *set = g_grim->getCurrSet();
|
||||
Set::Setup *setup = set->getCurrSetup();
|
||||
Math::Vector3d cameraPos = setup->_pos;
|
||||
Math::Vector3d vector = _pos - cameraPos;
|
||||
float distance = vector.getMagnitude();
|
||||
if (_volume == 0) {
|
||||
_attenuation = 0.0f;
|
||||
} else {
|
||||
_attenuation = MAX(0.0f, 1.0f - distance / (_volume * 100.0f / Audio::Mixer::kMaxChannelVolume));
|
||||
}
|
||||
|
||||
Math::Matrix4 worldRot = setup->_rot;
|
||||
Math::Vector3d relPos = (_pos - setup->_pos);
|
||||
Math::Vector3d p(relPos);
|
||||
p = p * worldRot.getRotation();
|
||||
float angle = atan2(p.x(), p.z());
|
||||
float pan = sin(angle);
|
||||
_balance = (int)(pan * 127.0f);
|
||||
|
||||
if (_handle) {
|
||||
g_system->getMixer()->setChannelBalance(*_handle, _balance);
|
||||
g_system->getMixer()->setChannelVolume(*_handle, (byte)getEffectiveVolume());
|
||||
}
|
||||
}
|
||||
|
||||
void SoundTrack::setBalance(int balance) {
|
||||
if (_positioned)
|
||||
return;
|
||||
_balance = balance;
|
||||
if (_handle) {
|
||||
g_system->getMixer()->setChannelBalance(*_handle, _balance);
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundTrack::play() {
|
||||
if (_stream) {
|
||||
if (isPlaying()) {
|
||||
warning("sound: %s already playing, don't start again!", _soundName.c_str());
|
||||
return true;
|
||||
}
|
||||
// If _disposeAfterPlaying is NO, the destructor will take care of the stream.
|
||||
g_system->getMixer()->playStream(_soundType, _handle, _stream, -1, (byte)getEffectiveVolume(), _balance, _disposeAfterPlaying);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoundTrack::pause() {
|
||||
_paused = !_paused;
|
||||
if (_stream) {
|
||||
g_system->getMixer()->pauseHandle(*_handle, _paused);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundTrack::stop() {
|
||||
if (_handle)
|
||||
g_system->getMixer()->stopHandle(*_handle);
|
||||
}
|
||||
|
||||
void SoundTrack::setFade(float fade) {
|
||||
_fade = fade;
|
||||
if (_handle) {
|
||||
g_system->getMixer()->setChannelVolume(*_handle, (byte)getEffectiveVolume());
|
||||
}
|
||||
}
|
||||
|
||||
int SoundTrack::getEffectiveVolume() {
|
||||
return _volume * _attenuation * _fade;
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
106
engines/grim/emi/sound/track.h
Normal file
106
engines/grim/emi/sound/track.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_SOUNDTRACK_H
|
||||
#define GRIM_SOUNDTRACK_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/timestamp.h"
|
||||
|
||||
#include "math/vector3d.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
class AudioStream;
|
||||
class SoundHandle;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class SaveGame;
|
||||
|
||||
/**
|
||||
* @class Super-class for the different codecs used in EMI
|
||||
*/
|
||||
class SoundTrack {
|
||||
public:
|
||||
enum FadeMode {
|
||||
FadeNone,
|
||||
FadeIn,
|
||||
FadeOut
|
||||
};
|
||||
protected:
|
||||
Common::String _soundName;
|
||||
Audio::AudioStream *_stream;
|
||||
Audio::SoundHandle *_handle;
|
||||
Audio::Mixer::SoundType _soundType;
|
||||
DisposeAfterUse::Flag _disposeAfterPlaying;
|
||||
bool _paused;
|
||||
bool _positioned;
|
||||
Math::Vector3d _pos;
|
||||
FadeMode _fadeMode;
|
||||
float _fade;
|
||||
float _attenuation;
|
||||
int _balance;
|
||||
int _volume;
|
||||
int _sync;
|
||||
public:
|
||||
SoundTrack();
|
||||
virtual ~SoundTrack();
|
||||
virtual bool openSound(const Common::String &filename, const Common::String &voiceName, const Audio::Timestamp *start = nullptr) = 0;
|
||||
virtual bool isPlaying() = 0;
|
||||
virtual bool play();
|
||||
virtual void pause();
|
||||
virtual void stop();
|
||||
|
||||
void fadeIn() { _fadeMode = FadeIn; }
|
||||
void fadeOut() { _fadeMode = FadeOut; }
|
||||
void setFadeMode(FadeMode fadeMode) { _fadeMode = fadeMode; }
|
||||
void setFade(float fade);
|
||||
float getFade() const { return _fade; }
|
||||
FadeMode getFadeMode() const { return _fadeMode; }
|
||||
void setBalance(int balance);
|
||||
void setVolume(int volume);
|
||||
void setPosition(bool positioned, const Math::Vector3d &pos = Math::Vector3d());
|
||||
void updatePosition();
|
||||
void setSync(int sync) { _sync = sync; }
|
||||
int getEffectiveVolume();
|
||||
int getVolume() const { return _volume; }
|
||||
int getBalance() const { return _balance; }
|
||||
int getSync() const { return _sync; }
|
||||
virtual Audio::Timestamp getPos() = 0;
|
||||
Common::String getSoundName();
|
||||
void setSoundName(const Common::String &name);
|
||||
virtual bool hasLooped() { return false; }
|
||||
virtual void setLooping(bool looping) { }
|
||||
virtual bool isLooping() const { return false; }
|
||||
bool isPaused() const { return _paused; }
|
||||
bool isPositioned() const { return _positioned; }
|
||||
Math::Vector3d getWorldPos() const { return _pos; }
|
||||
Audio::Mixer::SoundType getSoundType() const { return _soundType; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
260
engines/grim/emi/sound/vimatrack.cpp
Normal file
260
engines/grim/emi/sound/vimatrack.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/resource.h"
|
||||
#include "engines/grim/imuse/imuse_mcmp_mgr.h"
|
||||
#include "engines/grim/emi/sound/vimatrack.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
struct Region {
|
||||
int32 offset; // offset of region
|
||||
int32 length; // length of region
|
||||
};
|
||||
|
||||
struct SoundDesc {
|
||||
uint16 freq; // frequency
|
||||
byte channels; // stereo or mono
|
||||
byte bits; // 8, 12, 16
|
||||
int numRegions; // number of Regions
|
||||
Region *region;
|
||||
bool endFlag;
|
||||
bool inUse;
|
||||
char name[32];
|
||||
McmpMgr *mcmpMgr;
|
||||
int type;
|
||||
int volGroupId;
|
||||
bool mcmpData;
|
||||
uint32 headerSize;
|
||||
Common::SeekableReadStream *inStream;
|
||||
};
|
||||
|
||||
bool VimaTrack::isPlaying() {
|
||||
// FIXME: Actually clean up the data better
|
||||
// (we don't currently handle the case where it isn't asked for through isPlaying, or deleted explicitly).
|
||||
if (!_handle)
|
||||
return false;
|
||||
|
||||
if (g_system->getMixer()->isSoundHandleActive(*_handle)) {
|
||||
if (_stream->endOfData()) {
|
||||
g_system->getMixer()->stopHandle(*_handle);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VimaTrack::openSound(const Common::String &filename, const Common::String &voiceName, const Audio::Timestamp *start) {
|
||||
Common::SeekableReadStream *file = g_resourceloader->openNewStreamFile(filename);
|
||||
if (!file) {
|
||||
Debug::debug(Debug::Sound, "Stream for %s not open", voiceName.c_str());
|
||||
return false;
|
||||
}
|
||||
_soundName = voiceName;
|
||||
_mcmp = new McmpMgr();
|
||||
_desc = new SoundDesc();
|
||||
_desc->inStream = file;
|
||||
_desc->mcmpData = true;
|
||||
_desc->mcmpMgr = _mcmp;
|
||||
int headerSize = 0;
|
||||
|
||||
if (_mcmp->openSound(voiceName.c_str(), file, headerSize)) {
|
||||
parseSoundHeader(_desc, headerSize);
|
||||
|
||||
_stream = Audio::makeQueuingAudioStream(_desc->freq, (false));
|
||||
|
||||
playTrack(start);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void VimaTrack::parseSoundHeader(SoundDesc *sound, int &headerSize) {
|
||||
Common::SeekableReadStream *data = sound->inStream;
|
||||
|
||||
uint32 tag = data->readUint32BE();
|
||||
if (tag == MKTAG('R','I','F','F')) {
|
||||
sound->endFlag = false;
|
||||
sound->region = new Region[1];
|
||||
sound->numRegions = 1;
|
||||
sound->region[0].offset = 0;
|
||||
data->seek(18, SEEK_CUR);
|
||||
sound->channels = data->readByte();
|
||||
data->readByte();
|
||||
sound->freq = data->readUint32LE();
|
||||
data->seek(6, SEEK_CUR);
|
||||
sound->bits = data->readByte();
|
||||
data->seek(5, SEEK_CUR);
|
||||
sound->region[0].length = data->readUint32LE();
|
||||
headerSize = 44;
|
||||
} else {
|
||||
assert(tag != MKTAG('i','M','U','S'));
|
||||
error("VimaTrack::parseSoundHeader() Unknown sound format");
|
||||
}
|
||||
}
|
||||
|
||||
int32 VimaTrack::getDataFromRegion(SoundDesc *sound, int region, byte **buf, int32 offset, int32 size, int32 *flags) {
|
||||
//assert(checkForProperHandle(sound));
|
||||
assert(buf && offset >= 0 && size >= 0);
|
||||
assert(region >= 0 && region < sound->numRegions);
|
||||
|
||||
int32 region_offset = sound->region[region].offset;
|
||||
int32 region_length = sound->region[region].length;
|
||||
|
||||
if (offset + size > region_length) {
|
||||
size = region_length - offset;
|
||||
sound->endFlag = true;
|
||||
} else {
|
||||
sound->endFlag = false;
|
||||
}
|
||||
|
||||
if (sound->mcmpData) {
|
||||
size = sound->mcmpMgr->decompressSample(region_offset + offset, size, buf);
|
||||
*flags |= Audio::FLAG_LITTLE_ENDIAN;
|
||||
} else {
|
||||
*buf = new byte[size];
|
||||
sound->inStream->seek(region_offset + offset + sound->headerSize, SEEK_SET);
|
||||
sound->inStream->read(*buf, size);
|
||||
*flags &= ~Audio::FLAG_LITTLE_ENDIAN;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
void VimaTrack::playTrack(const Audio::Timestamp *start) {
|
||||
//Common::StackLock lock(_mutex);
|
||||
if (!_stream) {
|
||||
error("Stream not loaded");
|
||||
}
|
||||
byte *data = nullptr;
|
||||
int32 result = 0;
|
||||
|
||||
int32 curRegion = -1;
|
||||
int32 regionOffset = 0;
|
||||
int32 mixerFlags = Audio::FLAG_16BITS;
|
||||
|
||||
curRegion++;
|
||||
|
||||
int channels = _desc->channels;
|
||||
|
||||
//int32 mixer_size = track->feedSize / _callbackFps;
|
||||
int32 mixer_size = _desc->freq * channels * 2;
|
||||
|
||||
if (start) {
|
||||
regionOffset = (start->msecs() * mixer_size) / 1000;
|
||||
regionOffset = (regionOffset / 2) * 2; // Ensure that the offset is divisible by 2.
|
||||
while (regionOffset > _desc->region[curRegion].length) {
|
||||
regionOffset -= _desc->region[curRegion].length;
|
||||
++curRegion;
|
||||
}
|
||||
|
||||
if (curRegion > _desc->numRegions - 1)
|
||||
return;
|
||||
}
|
||||
|
||||
if (_stream->endOfData()) { // FIXME: Currently we just allocate a bunch here, try to find the correct size instead.
|
||||
mixer_size *= 8;
|
||||
}
|
||||
|
||||
if (channels == 1)
|
||||
mixer_size &= ~1;
|
||||
if (channels == 2)
|
||||
mixer_size &= ~3;
|
||||
|
||||
if (mixer_size == 0)
|
||||
return;
|
||||
|
||||
do {
|
||||
result = getDataFromRegion(_desc, curRegion, &data, regionOffset, mixer_size, &mixerFlags);
|
||||
if (channels == 1) {
|
||||
result &= ~1;
|
||||
}
|
||||
if (channels == 2) {
|
||||
result &= ~3;
|
||||
}
|
||||
|
||||
if (result > mixer_size)
|
||||
result = mixer_size;
|
||||
|
||||
if (g_system->getMixer()->isReady()) {
|
||||
((Audio::QueuingAudioStream *)_stream)->queueBuffer(data, result, DisposeAfterUse::YES, mixerFlags);
|
||||
regionOffset += result;
|
||||
} else
|
||||
delete[] data;
|
||||
|
||||
if (curRegion >= 0 && curRegion < _desc->numRegions - 1) {
|
||||
curRegion++;
|
||||
regionOffset = 0;
|
||||
|
||||
if (!_stream) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mixer_size -= result;
|
||||
assert(mixer_size >= 0);
|
||||
} while (mixer_size && !_desc->endFlag);
|
||||
if (g_system->getMixer()->isReady()) {
|
||||
//g_system->getMixer()->setChannelVolume(track->handle, track->getVol());
|
||||
//g_system->getMixer()->setChannelBalance(track->handle, track->getPan());
|
||||
}
|
||||
}
|
||||
|
||||
Audio::Timestamp VimaTrack::getPos() {
|
||||
// FIXME: Return actual stream position.
|
||||
return g_system->getMixer()->getSoundElapsedTime(*_handle);
|
||||
}
|
||||
|
||||
VimaTrack::VimaTrack() {
|
||||
_soundType = Audio::Mixer::kSpeechSoundType;
|
||||
_handle = new Audio::SoundHandle();
|
||||
_file = nullptr;
|
||||
_mcmp = nullptr;
|
||||
_desc = nullptr;
|
||||
}
|
||||
|
||||
VimaTrack::~VimaTrack() {
|
||||
stop();
|
||||
|
||||
delete _mcmp;
|
||||
|
||||
if (_desc) {
|
||||
delete[] _desc->region;
|
||||
delete _desc->inStream;
|
||||
}
|
||||
|
||||
if (_handle) {
|
||||
g_system->getMixer()->stopHandle(*_handle);
|
||||
delete _handle;
|
||||
}
|
||||
delete _desc;
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
||||
57
engines/grim/emi/sound/vimatrack.h
Normal file
57
engines/grim/emi/sound/vimatrack.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRIM_VIMATRACK_H
|
||||
#define GRIM_VIMATRACK_H
|
||||
|
||||
#include "common/str.h"
|
||||
|
||||
#include "engines/grim/emi/sound/track.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
struct SoundDesc;
|
||||
class McmpMgr;
|
||||
|
||||
/**
|
||||
* @class Vima-implementation for the EMI-sound system
|
||||
* Vima is used for voices in the PC version of EMI, a
|
||||
* similar implementation for SCX will be required for PS2-support.
|
||||
*/
|
||||
class VimaTrack : public SoundTrack {
|
||||
Common::SeekableReadStream *_file;
|
||||
void parseSoundHeader(SoundDesc *sound, int &headerSize);
|
||||
int32 getDataFromRegion(SoundDesc *sound, int region, byte **buf, int32 offset, int32 size, int32 *flags);
|
||||
public:
|
||||
VimaTrack();
|
||||
virtual ~VimaTrack();
|
||||
|
||||
bool isPlaying() override;
|
||||
bool openSound(const Common::String &filename, const Common::String &soundName, const Audio::Timestamp *start = nullptr) override;
|
||||
void playTrack(const Audio::Timestamp *start);
|
||||
Audio::Timestamp getPos() override;
|
||||
SoundDesc *_desc;
|
||||
McmpMgr *_mcmp;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user