Initial commit
This commit is contained in:
3
engines/stark/POTFILES
Normal file
3
engines/stark/POTFILES
Normal file
@@ -0,0 +1,3 @@
|
||||
engines/stark/detection.cpp
|
||||
engines/stark/metaengine.cpp
|
||||
engines/stark/stark.cpp
|
||||
3
engines/stark/configure.engine
Normal file
3
engines/stark/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine stark "The Longest Journey" yes "" "" "16bit 3d highres freetype2 vorbis bink" "tinygl"
|
||||
774
engines/stark/console.cpp
Normal file
774
engines/stark/console.cpp
Normal file
@@ -0,0 +1,774 @@
|
||||
/* 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/stark/console.h"
|
||||
|
||||
#include "engines/stark/formats/xarc.h"
|
||||
#include "engines/stark/resources/object.h"
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/level.h"
|
||||
#include "engines/stark/resources/location.h"
|
||||
#include "engines/stark/resources/knowledge.h"
|
||||
#include "engines/stark/resources/root.h"
|
||||
#include "engines/stark/resources/script.h"
|
||||
#include "engines/stark/resources/knowledgeset.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/textureset.h"
|
||||
#include "engines/stark/services/archiveloader.h"
|
||||
#include "engines/stark/services/dialogplayer.h"
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/resourceprovider.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
#include "engines/stark/tools/decompiler.h"
|
||||
|
||||
#include "common/file.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
Console::Console() :
|
||||
GUI::Debugger() {
|
||||
registerCmd("dumpArchive", WRAP_METHOD(Console, Cmd_DumpArchive));
|
||||
registerCmd("dumpRoot", WRAP_METHOD(Console, Cmd_DumpRoot));
|
||||
registerCmd("dumpStatic", WRAP_METHOD(Console, Cmd_DumpStatic));
|
||||
registerCmd("dumpGlobal", WRAP_METHOD(Console, Cmd_DumpGlobal));
|
||||
registerCmd("dumpLevel", WRAP_METHOD(Console, Cmd_DumpLevel));
|
||||
registerCmd("dumpKnowledge", WRAP_METHOD(Console, Cmd_DumpKnowledge));
|
||||
registerCmd("dumpLocation", WRAP_METHOD(Console, Cmd_DumpLocation));
|
||||
registerCmd("listScripts", WRAP_METHOD(Console, Cmd_ListScripts));
|
||||
registerCmd("enableScript", WRAP_METHOD(Console, Cmd_EnableScript));
|
||||
registerCmd("forceScript", WRAP_METHOD(Console, Cmd_ForceScript));
|
||||
registerCmd("decompileScript", WRAP_METHOD(Console, Cmd_DecompileScript));
|
||||
registerCmd("testDecompiler", WRAP_METHOD(Console, Cmd_TestDecompiler));
|
||||
registerCmd("listAnimations", WRAP_METHOD(Console, Cmd_ListAnimations));
|
||||
registerCmd("forceAnimation", WRAP_METHOD(Console, Cmd_ForceAnimation));
|
||||
registerCmd("listInventoryItems", WRAP_METHOD(Console, Cmd_ListInventoryItems));
|
||||
registerCmd("listLocations", WRAP_METHOD(Console, Cmd_ListLocations));
|
||||
registerCmd("location", WRAP_METHOD(Console, Cmd_Location));
|
||||
registerCmd("chapter", WRAP_METHOD(Console, Cmd_Chapter));
|
||||
registerCmd("changeLocation", WRAP_METHOD(Console, Cmd_ChangeLocation));
|
||||
registerCmd("changeChapter", WRAP_METHOD(Console, Cmd_ChangeChapter));
|
||||
registerCmd("changeKnowledge", WRAP_METHOD(Console, Cmd_ChangeKnowledge));
|
||||
registerCmd("enableInventoryItem", WRAP_METHOD(Console, Cmd_EnableInventoryItem));
|
||||
registerCmd("extractAllTextures", WRAP_METHOD(Console, Cmd_ExtractAllTextures));
|
||||
}
|
||||
|
||||
Console::~Console() {
|
||||
}
|
||||
|
||||
bool Console::Cmd_DumpArchive(int argc, const char **argv) {
|
||||
if (argc != 2) {
|
||||
debugPrintf("Extract all the files from a game archive\n");
|
||||
debugPrintf("The destination folder, named 'dump', is in the location ScummVM was launched from\n");
|
||||
debugPrintf("Usage :\n");
|
||||
debugPrintf("dumpArchive [path to archive]\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
Formats::XARCArchive xarc;
|
||||
if (!xarc.open(argv[1])) {
|
||||
debugPrintf("Can't open archive with name '%s'\n", argv[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::ArchiveMemberList members;
|
||||
xarc.listMembers(members);
|
||||
|
||||
for (Common::ArchiveMemberList::const_iterator it = members.begin(); it != members.end(); it++) {
|
||||
Common::Path fileName(Common::String::format("dump/%s", it->get()->getName().c_str()));
|
||||
|
||||
// Open the output file
|
||||
Common::DumpFile outFile;
|
||||
if (!outFile.open(fileName, true)) {
|
||||
debugPrintf("Unable to open file '%s' for writing\n", fileName.toString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copy the archive content to the output file using a temporary buffer
|
||||
Common::SeekableReadStream *inStream = it->get()->createReadStream();
|
||||
uint8 *buf = new uint8[inStream->size()];
|
||||
|
||||
inStream->read(buf, inStream->size());
|
||||
outFile.write(buf, inStream->size());
|
||||
|
||||
delete[] buf;
|
||||
delete inStream;
|
||||
outFile.close();
|
||||
|
||||
debugPrintf("Extracted '%s'\n", it->get()->getName().c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_DumpRoot(int argc, const char **argv) {
|
||||
Resources::Root *root = StarkGlobal->getRoot();
|
||||
if (root) {
|
||||
root->print();
|
||||
} else {
|
||||
debugPrintf("The global root has not been loaded\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_DumpGlobal(int argc, const char **argv) {
|
||||
Resources::Level *level = StarkGlobal->getLevel();
|
||||
if (level) {
|
||||
level->print();
|
||||
} else {
|
||||
debugPrintf("The global level has not been loaded\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_DumpStatic(int argc, const char **argv) {
|
||||
// Static resources are initialized in the beginning of the running
|
||||
StarkStaticProvider->getLevel()->print();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_DumpLevel(int argc, const char **argv) {
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
if (current) {
|
||||
current->getLevel()->print();
|
||||
} else {
|
||||
debugPrintf("Game levels have not been loaded\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_DumpKnowledge(int argc, const char **argv) {
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
|
||||
if (!current) {
|
||||
debugPrintf("Game levels have not been loaded\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
Resources::Level *level = current->getLevel();
|
||||
Resources::Location *location = current->getLocation();
|
||||
Common::Array<Resources::Knowledge *> knowledge = level->listChildrenRecursive<Resources::Knowledge>();
|
||||
knowledge.insert_at(knowledge.size(), location->listChildrenRecursive<Resources::Knowledge>());
|
||||
Common::Array<Resources::Knowledge *>::iterator it;
|
||||
for (it = knowledge.begin(); it != knowledge.end(); ++it) {
|
||||
(*it)->print();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_ChangeKnowledge(int argc, const char **argv) {
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
|
||||
if (!current) {
|
||||
debugPrintf("Game levels have not been loaded\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
uint index = 0;
|
||||
char type = 0;
|
||||
|
||||
if (argc >= 4) {
|
||||
index = atoi(argv[1]);
|
||||
type = argv[2][0];
|
||||
if (type == 'b' || type == 'i') {
|
||||
Resources::Level *level = current->getLevel();
|
||||
Resources::Location *location = current->getLocation();
|
||||
Common::Array<Resources::Knowledge *> knowledgeArr = level->listChildrenRecursive<Resources::Knowledge>();
|
||||
knowledgeArr.insert_at(knowledgeArr.size(), location->listChildrenRecursive<Resources::Knowledge>());
|
||||
if (index < knowledgeArr.size() ) {
|
||||
Resources::Knowledge *knowledge = knowledgeArr[index];
|
||||
if (type == 'b') {
|
||||
knowledge->setBooleanValue(atoi(argv[3]));
|
||||
} else if (type == 'i') {
|
||||
knowledge->setIntegerValue(atoi(argv[3]));
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
debugPrintf("Invalid index %d, only %d indices available\n", index, knowledgeArr.size());
|
||||
}
|
||||
} else {
|
||||
debugPrintf("Invalid type: %c, only b and i are available\n", type);
|
||||
}
|
||||
} else if (argc > 1 ) {
|
||||
debugPrintf("Too few args\n");
|
||||
}
|
||||
|
||||
debugPrintf("Change the value of some knowledge. Use dumpKnowledge to get an id\n");
|
||||
debugPrintf("Usage :\n");
|
||||
debugPrintf("changeKnowledge [id] [type] [value]\n");
|
||||
debugPrintf("available types: b(inary), i(nteger)\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Array<Resources::Script *> Console::listAllLocationScripts() const {
|
||||
Common::Array<Resources::Script *> scripts;
|
||||
|
||||
Resources::Level *level = StarkGlobal->getCurrent()->getLevel();
|
||||
Resources::Location *location = StarkGlobal->getCurrent()->getLocation();
|
||||
scripts.push_back(level->listChildrenRecursive<Resources::Script>());
|
||||
scripts.push_back(location->listChildrenRecursive<Resources::Script>());
|
||||
|
||||
return scripts;
|
||||
}
|
||||
|
||||
bool Console::Cmd_ListScripts(int argc, const char **argv) {
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
if (!current) {
|
||||
debugPrintf("Game levels have not been loaded\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Array<Resources::Script *> scripts = listAllLocationScripts();
|
||||
|
||||
for (uint i = 0; i < scripts.size(); i++) {
|
||||
Resources::Script *script = scripts[i];
|
||||
|
||||
debugPrintf("%d: %s - enabled: %d", i, script->getName().c_str(), script->isEnabled());
|
||||
|
||||
// Print which resource is causing the script to wait
|
||||
if (script->isSuspended()) {
|
||||
Resources::Object *suspending = script->getSuspendingResource();
|
||||
if (suspending) {
|
||||
debugPrintf(", waiting for: %s (%s)", suspending->getName().c_str(), suspending->getType().getName());
|
||||
} else {
|
||||
debugPrintf(", paused");
|
||||
}
|
||||
}
|
||||
|
||||
debugPrintf("\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_EnableScript(int argc, const char **argv) {
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
if (!current) {
|
||||
debugPrintf("Game levels have not been loaded\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
uint index = 0;
|
||||
|
||||
if (argc >= 2) {
|
||||
index = atoi(argv[1]);
|
||||
|
||||
bool value = true;
|
||||
if (argc >= 3) {
|
||||
value = atoi(argv[2]);
|
||||
}
|
||||
|
||||
Common::Array<Resources::Script *> scripts = listAllLocationScripts();
|
||||
if (index < scripts.size() ) {
|
||||
Resources::Script *script = scripts[index];
|
||||
script->enable(value);
|
||||
return true;
|
||||
} else {
|
||||
debugPrintf("Invalid index %d, only %d indices available\n", index, scripts.size());
|
||||
}
|
||||
}
|
||||
|
||||
debugPrintf("Enable or disable a script. Use listScripts to get an id\n");
|
||||
debugPrintf("Usage :\n");
|
||||
debugPrintf("enableScript [id] (value)\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_ForceScript(int argc, const char **argv) {
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
if (!current) {
|
||||
debugPrintf("Game levels have not been loaded\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
uint index = 0;
|
||||
|
||||
if (argc >= 2) {
|
||||
index = atoi(argv[1]);
|
||||
|
||||
Common::Array<Resources::Script *> scripts = listAllLocationScripts();
|
||||
if (index < scripts.size() ) {
|
||||
Resources::Script *script = scripts[index];
|
||||
script->enable(true);
|
||||
script->goToNextCommand(); // Skip the begin command to avoid checks
|
||||
script->execute(Resources::Script::kCallModePlayerAction);
|
||||
return true;
|
||||
} else {
|
||||
debugPrintf("Invalid index %d, only %d indices available\n", index, scripts.size());
|
||||
}
|
||||
}
|
||||
|
||||
debugPrintf("Force the execution of a script. Use listScripts to get an id\n");
|
||||
debugPrintf("Usage :\n");
|
||||
debugPrintf("forceScript [id]\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_DecompileScript(int argc, const char **argv) {
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
if (!current) {
|
||||
debugPrintf("Game levels have not been loaded\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc >= 2) {
|
||||
uint index = atoi(argv[1]);
|
||||
|
||||
Common::Array<Resources::Script *> scripts = listAllLocationScripts();
|
||||
if (index < scripts.size()) {
|
||||
Resources::Script *script = scripts[index];
|
||||
|
||||
Tools::Decompiler *decompiler = new Tools::Decompiler(script);
|
||||
if (decompiler->getError() != "") {
|
||||
debugPrintf("Decompilation failure: %s\n", decompiler->getError().c_str());
|
||||
}
|
||||
|
||||
debug("Script %d - %s:", index, script->getName().c_str());
|
||||
decompiler->printDecompiled();
|
||||
|
||||
delete decompiler;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
debugPrintf("Invalid index %d, only %d indices available\n", index, scripts.size());
|
||||
}
|
||||
}
|
||||
|
||||
debugPrintf("Decompile a script. Use listScripts to get an id\n");
|
||||
debugPrintf("Usage :\n");
|
||||
debugPrintf("decompileScript [id]\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
class ArchiveVisitor {
|
||||
public:
|
||||
virtual ~ArchiveVisitor() {}
|
||||
virtual void acceptLevelArchive(Resources::Level *level) = 0;
|
||||
virtual void acceptLocationArchive(Resources::Location *location) = 0;
|
||||
};
|
||||
|
||||
void Console::walkAllArchives(ArchiveVisitor *visitor) {
|
||||
ArchiveLoader *archiveLoader = new ArchiveLoader();
|
||||
|
||||
// Temporarily replace the global archive loader with our instance
|
||||
ArchiveLoader *gameArchiveLoader = StarkArchiveLoader;
|
||||
StarkArchiveLoader = archiveLoader;
|
||||
|
||||
archiveLoader->load("x.xarc");
|
||||
Resources::Root *root = archiveLoader->useRoot<Resources::Root>("x.xarc");
|
||||
|
||||
// Find all the levels
|
||||
Common::Array<Resources::Level *> levels = root->listChildren<Resources::Level>();
|
||||
|
||||
// Loop over the levels
|
||||
for (uint i = 0; i < levels.size(); i++) {
|
||||
Resources::Level *level = levels[i];
|
||||
|
||||
Common::Path levelArchive = archiveLoader->buildArchiveName(level);
|
||||
debug("%s - %s", levelArchive.toString(Common::Path::kNativeSeparator).c_str(), level->getName().c_str());
|
||||
|
||||
// Load the detailed level archive
|
||||
archiveLoader->load(levelArchive);
|
||||
level = archiveLoader->useRoot<Resources::Level>(levelArchive);
|
||||
|
||||
// Visit the level archive
|
||||
visitor->acceptLevelArchive(level);
|
||||
|
||||
Common::Array<Resources::Location *> locations = level->listChildren<Resources::Location>();
|
||||
|
||||
// Loop over the locations
|
||||
for (uint j = 0; j < locations.size(); j++) {
|
||||
Resources::Location *location = locations[j];
|
||||
|
||||
Common::Path locationArchive = archiveLoader->buildArchiveName(level, location);
|
||||
debug("%s - %s", locationArchive.toString(Common::Path::kNativeSeparator).c_str(), location->getName().c_str());
|
||||
|
||||
// Load the detailed location archive
|
||||
archiveLoader->load(locationArchive);
|
||||
location = archiveLoader->useRoot<Resources::Location>(locationArchive);
|
||||
|
||||
// Visit the location archive
|
||||
visitor->acceptLocationArchive(location);
|
||||
|
||||
archiveLoader->returnRoot(locationArchive);
|
||||
archiveLoader->unloadUnused();
|
||||
}
|
||||
|
||||
archiveLoader->returnRoot(levelArchive);
|
||||
archiveLoader->unloadUnused();
|
||||
}
|
||||
|
||||
// Restore the global archive loader
|
||||
StarkArchiveLoader = gameArchiveLoader;
|
||||
|
||||
delete archiveLoader;
|
||||
}
|
||||
|
||||
class DecompilingArchiveVisitor : public ArchiveVisitor {
|
||||
public:
|
||||
DecompilingArchiveVisitor() :
|
||||
_totalScripts(0),
|
||||
_okScripts(0) {}
|
||||
|
||||
void acceptLevelArchive(Resources::Level *level) override {
|
||||
decompileScriptChildren(level);
|
||||
}
|
||||
|
||||
void acceptLocationArchive(Resources::Location *location) override {
|
||||
decompileScriptChildren(location);
|
||||
}
|
||||
|
||||
int getTotalScripts() const { return _totalScripts; }
|
||||
int getOKScripts() const { return _okScripts; }
|
||||
|
||||
private:
|
||||
int _totalScripts;
|
||||
int _okScripts;
|
||||
|
||||
void decompileScriptChildren(Resources::Object *resource) {
|
||||
Common::Array<Resources::Script *> scripts = resource->listChildrenRecursive<Resources::Script>();
|
||||
|
||||
for (uint i = 0; i < scripts.size(); i++) {
|
||||
Resources::Script *script = scripts[i];
|
||||
|
||||
Tools::Decompiler decompiler(script);
|
||||
_totalScripts++;
|
||||
|
||||
Common::String result;
|
||||
if (decompiler.getError() == "") {
|
||||
result = "OK";
|
||||
_okScripts++;
|
||||
} else {
|
||||
result = decompiler.getError();
|
||||
}
|
||||
|
||||
debug("%d - %s: %s", script->getIndex(), script->getName().c_str(), result.c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool Console::Cmd_TestDecompiler(int argc, const char **argv) {
|
||||
DecompilingArchiveVisitor visitor;
|
||||
walkAllArchives(&visitor);
|
||||
|
||||
debugPrintf("Successfully decompiled %d scripts out of %d\n", visitor.getOKScripts(), visitor.getTotalScripts());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class TextureExtractingArchiveVisitor : public ArchiveVisitor {
|
||||
public:
|
||||
void acceptLevelArchive(Resources::Level *level) override {
|
||||
decompileScriptChildren(level);
|
||||
}
|
||||
|
||||
void acceptLocationArchive(Resources::Location *location) override {
|
||||
decompileScriptChildren(location);
|
||||
}
|
||||
|
||||
private:
|
||||
void decompileScriptChildren(Resources::Object *resource) {
|
||||
Common::Array<Resources::TextureSet *> textureSets = resource->listChildrenRecursive<Resources::TextureSet>();
|
||||
|
||||
for (uint i = 0; i < textureSets.size(); i++) {
|
||||
Resources::TextureSet *textureSet = textureSets[i];
|
||||
textureSet->extractArchive();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool Console::Cmd_ExtractAllTextures(int argc, const char **argv) {
|
||||
TextureExtractingArchiveVisitor visitor;
|
||||
walkAllArchives(&visitor);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Array<Resources::Anim *> Console::listAllLocationAnimations() const {
|
||||
Common::Array<Resources::Anim *> animations;
|
||||
|
||||
Resources::Level *level = StarkGlobal->getCurrent()->getLevel();
|
||||
Resources::Location *location = StarkGlobal->getCurrent()->getLocation();
|
||||
animations.push_back(level->listChildrenRecursive<Resources::Anim>());
|
||||
animations.push_back(location->listChildrenRecursive<Resources::Anim>());
|
||||
|
||||
return animations;
|
||||
}
|
||||
|
||||
bool Console::Cmd_ListAnimations(int argc, const char **argv) {
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
if (!current) {
|
||||
debugPrintf("This command is only available in game.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Array<Resources::Anim *> animations = listAllLocationAnimations();
|
||||
|
||||
for (uint i = 0; i < animations.size(); i++) {
|
||||
Resources::Anim *anim = animations[i];
|
||||
Resources::Item *item = anim->findParent<Resources::Item>();
|
||||
|
||||
debugPrintf("%d: %s - %s - in use: %d\n", i, item->getName().c_str(), anim->getName().c_str(), anim->isInUse());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_ForceAnimation(int argc, const char **argv) {
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
if (!current) {
|
||||
debugPrintf("This command is only available in game.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
debugPrintf("Force the execution of an animation. Use listAnimations to get an id\n");
|
||||
debugPrintf("Usage :\n");
|
||||
debugPrintf("forceAnimation [id]\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
uint index = atoi(argv[1]);
|
||||
|
||||
Common::Array<Resources::Anim *> animations = listAllLocationAnimations();
|
||||
if (index >= animations.size() ) {
|
||||
debugPrintf("Invalid animation %d\n", index);
|
||||
return true;
|
||||
}
|
||||
|
||||
Resources::Anim *anim = animations[index];
|
||||
Resources::Item *item = anim->findParent<Resources::Item>();
|
||||
Resources::ItemVisual *sceneItem = item->getSceneInstance();
|
||||
|
||||
if (!sceneItem->isEnabled()) {
|
||||
sceneItem->setEnabled(true);
|
||||
}
|
||||
|
||||
sceneItem->playActionAnim(anim);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Console::Cmd_DumpLocation(int argc, const char **argv) {
|
||||
if (StarkStaticProvider->isStaticLocation()) {
|
||||
StarkStaticProvider->getLocation()->print();
|
||||
return true;
|
||||
}
|
||||
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
if (current) {
|
||||
current->getLocation()->print();
|
||||
} else {
|
||||
debugPrintf("Locations have not been loaded\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_ListInventoryItems(int argc, const char **argv) {
|
||||
Resources::KnowledgeSet *inventory = StarkGlobal->getInventory();
|
||||
|
||||
if (!inventory) {
|
||||
debugPrintf("The inventory has not been loaded\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Array<Resources::Item*> inventoryItems = inventory->listChildren<Resources::Item>(Resources::Item::kItemInventory);
|
||||
Common::Array<Resources::Item*>::iterator it = inventoryItems.begin();
|
||||
for (int i = 0; it != inventoryItems.end(); ++it, i++) {
|
||||
debugPrintf("Item %d: %s%s\n", i, (*it)->getName().c_str(), (*it)->isEnabled() ? " (enabled)" : "");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_EnableInventoryItem(int argc, const char **argv) {
|
||||
Resources::KnowledgeSet *inventory = StarkGlobal->getInventory();
|
||||
|
||||
if (!inventory) {
|
||||
debugPrintf("The inventory has not been loaded\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc != 2) {
|
||||
debugPrintf("Enable a specific inventory item. Use listInventoryItems to get an id\n");
|
||||
debugPrintf("Usage :\n");
|
||||
debugPrintf("enableInventoryItem [id]\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
uint num = atoi(argv[1]);
|
||||
Common::Array<Resources::Item*> inventoryItems = inventory->listChildren<Resources::Item>(Resources::Item::kItemInventory);
|
||||
if (num < inventoryItems.size()) {
|
||||
inventoryItems[num]->setEnabled(true);
|
||||
} else {
|
||||
debugPrintf("Invalid index %d, only %d indices available\n", num, inventoryItems.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_ListLocations(int argc, const char **argv) {
|
||||
ArchiveLoader *archiveLoader = new ArchiveLoader();
|
||||
|
||||
// Temporarily replace the global archive loader with our instance
|
||||
ArchiveLoader *gameArchiveLoader = StarkArchiveLoader;
|
||||
StarkArchiveLoader = archiveLoader;
|
||||
|
||||
archiveLoader->load("x.xarc");
|
||||
Resources::Root *root = archiveLoader->useRoot<Resources::Root>("x.xarc");
|
||||
|
||||
// Find all the levels
|
||||
Common::Array<Resources::Level *> levels = root->listChildren<Resources::Level>();
|
||||
|
||||
// Loop over the levels
|
||||
for (uint i = 0; i < levels.size(); i++) {
|
||||
Resources::Level *level = levels[i];
|
||||
|
||||
Common::Path levelArchive = archiveLoader->buildArchiveName(level);
|
||||
debugPrintf("%s - %s\n", levelArchive.toString(Common::Path::kNativeSeparator).c_str(), level->getName().c_str());
|
||||
|
||||
// Load the detailed level archive
|
||||
archiveLoader->load(levelArchive);
|
||||
level = archiveLoader->useRoot<Resources::Level>(levelArchive);
|
||||
|
||||
Common::Array<Resources::Location *> locations = level->listChildren<Resources::Location>();
|
||||
|
||||
// Loop over the locations
|
||||
for (uint j = 0; j < locations.size(); j++) {
|
||||
Resources::Location *location = locations[j];
|
||||
|
||||
Common::Path roomArchive = archiveLoader->buildArchiveName(level, location);
|
||||
debugPrintf("%s - %s\n", roomArchive.toString(Common::Path::kNativeSeparator).c_str(), location->getName().c_str());
|
||||
}
|
||||
|
||||
archiveLoader->returnRoot(levelArchive);
|
||||
archiveLoader->unloadUnused();
|
||||
}
|
||||
|
||||
// Restore the global archive loader
|
||||
StarkArchiveLoader = gameArchiveLoader;
|
||||
|
||||
delete archiveLoader;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_ChangeLocation(int argc, const char **argv) {
|
||||
if (argc >= 3) {
|
||||
// Assert indices
|
||||
Common::Path xarcFileName(Common::String::format("%s/%s/%s.xarc", argv[1], argv[2], argv[2]));
|
||||
if (!Common::File::exists(xarcFileName)) {
|
||||
debugPrintf("Invalid location %s %s. Use listLocations to get correct indices\n", argv[1], argv[2]);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint levelIndex = strtol(argv[1] , nullptr, 16);
|
||||
uint locationIndex = strtol(argv[2] , nullptr, 16);
|
||||
|
||||
StarkUserInterface->changeScreen(Screen::kScreenGame);
|
||||
|
||||
if (!StarkGlobal->getRoot()) {
|
||||
StarkResourceProvider->initGlobal();
|
||||
}
|
||||
|
||||
StarkResourceProvider->requestLocationChange(levelIndex, locationIndex);
|
||||
|
||||
return false;
|
||||
} else if (argc > 1) {
|
||||
debugPrintf("Too few args\n");
|
||||
}
|
||||
|
||||
debugPrintf("Change the current location. Use listLocations to get indices\n");
|
||||
debugPrintf("Usage :\n");
|
||||
debugPrintf("changeLocation [level] [location]\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_ChangeChapter(int argc, const char **argv) {
|
||||
if (!StarkGlobal->getLevel()) {
|
||||
debugPrintf("The global level has not been loaded\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc != 2) {
|
||||
debugPrintf("Change the current chapter\n");
|
||||
debugPrintf("Usage :\n");
|
||||
debugPrintf("changeChapter [value]\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
char *endPtr = nullptr;
|
||||
long value = strtol(argv[1], &endPtr, 10);
|
||||
if (*endPtr == '\0' && value >= 0 && value <= INT_MAX)
|
||||
StarkGlobal->setCurrentChapter((int32) value);
|
||||
else
|
||||
debugPrintf("Invalid chapter\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_Location(int argc, const char **argv) {
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
|
||||
if (!current) {
|
||||
debugPrintf("Game levels have not been loaded\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc != 1) {
|
||||
debugPrintf("Display the current location\n");
|
||||
debugPrintf("Usage :\n");
|
||||
debugPrintf("location\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
debugPrintf("location: %02x %02x\n", current->getLevel()->getIndex(), current->getLocation()->getIndex());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::Cmd_Chapter(int argc, const char **argv) {
|
||||
if (!StarkGlobal->getLevel()) {
|
||||
debugPrintf("The global level has not been loaded\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc != 1) {
|
||||
debugPrintf("Display the current chapter\n");
|
||||
debugPrintf("Usage :\n");
|
||||
debugPrintf("chapter\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 value = StarkGlobal->getCurrentChapter();
|
||||
|
||||
debugPrintf("chapter: %d\n", value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
75
engines/stark/console.h
Normal file
75
engines/stark/console.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/* 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 CONSOLE_H_
|
||||
#define CONSOLE_H_
|
||||
|
||||
#include "gui/debugger.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Resources {
|
||||
class Anim;
|
||||
class Object;
|
||||
class Script;
|
||||
}
|
||||
|
||||
class ArchiveVisitor;
|
||||
|
||||
class Console : public GUI::Debugger {
|
||||
public:
|
||||
Console();
|
||||
virtual ~Console();
|
||||
|
||||
private:
|
||||
bool Cmd_DumpArchive(int argc, const char **argv);
|
||||
bool Cmd_DumpRoot(int argc, const char **argv);
|
||||
bool Cmd_DumpStatic(int argc, const char **argv);
|
||||
bool Cmd_DumpGlobal(int argc, const char **argv);
|
||||
bool Cmd_DumpKnowledge(int argc, const char **argv);
|
||||
bool Cmd_DumpLevel(int argc, const char **argv);
|
||||
bool Cmd_DumpLocation(int argc, const char **argv);
|
||||
bool Cmd_ForceScript(int argc, const char **argv);
|
||||
bool Cmd_DecompileScript(int argc, const char **argv);
|
||||
bool Cmd_TestDecompiler(int argc, const char** argv);
|
||||
bool Cmd_ListInventoryItems(int argc, const char **argv);
|
||||
bool Cmd_EnableInventoryItem(int argc, const char **argv);
|
||||
bool Cmd_ListLocations(int argc, const char** argv);
|
||||
bool Cmd_ListScripts(int argc, const char** argv);
|
||||
bool Cmd_EnableScript(int argc, const char** argv);
|
||||
bool Cmd_ListAnimations(int argc, const char **argv);
|
||||
bool Cmd_ForceAnimation(int argc, const char **argv);
|
||||
bool Cmd_Location(int argc, const char **argv);
|
||||
bool Cmd_Chapter(int argc, const char **argv);
|
||||
bool Cmd_ChangeLocation(int argc, const char **argv);
|
||||
bool Cmd_ChangeChapter(int argc, const char **argv);
|
||||
bool Cmd_ChangeKnowledge(int argc, const char **argv);
|
||||
bool Cmd_ExtractAllTextures(int argc, const char **argv);
|
||||
|
||||
Common::Array<Resources::Anim *> listAllLocationAnimations() const;
|
||||
Common::Array<Resources::Script *> listAllLocationScripts() const;
|
||||
|
||||
void walkAllArchives(ArchiveVisitor *visitor);
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // CONSOLE_H_
|
||||
5
engines/stark/credits.pl
Normal file
5
engines/stark/credits.pl
Normal file
@@ -0,0 +1,5 @@
|
||||
begin_section("Stark");
|
||||
add_person("Bastien Bouclet", "bgK", "");
|
||||
add_person("Einar Johan T. Sømåen", "somaen", "");
|
||||
add_person("Liu Zhaosong", "Douglas", "");
|
||||
end_section();
|
||||
34
engines/stark/debug.h
Normal file
34
engines/stark/debug.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* 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 STARK_DEBUG_H
|
||||
#define STARK_DEBUG_H
|
||||
|
||||
enum {
|
||||
kDebugArchive = 1,
|
||||
kDebugXMG,
|
||||
kDebugXRC,
|
||||
kDebugModding,
|
||||
kDebugAnimation,
|
||||
kDebugUnknown,
|
||||
};
|
||||
|
||||
#endif // STARK_DEBUG_H
|
||||
441
engines/stark/detection.cpp
Normal file
441
engines/stark/detection.cpp
Normal file
@@ -0,0 +1,441 @@
|
||||
/* 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/advancedDetector.h"
|
||||
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "stark/detection.h"
|
||||
#include "stark/debug.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
static const PlainGameDescriptor starkGames[] = {
|
||||
{ "tlj", "The Longest Journey" },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
static const DebugChannelDef debugFlagList[] = {
|
||||
{kDebugArchive, "Archive", "Debug the archive loading"},
|
||||
{kDebugXMG, "XMG", "Debug the loading of XMG images"},
|
||||
{kDebugXRC, "XRC", "Debug the loading of XRC resource trees"},
|
||||
{kDebugModding, "Modding", "Debug the loading of modded assets"},
|
||||
{kDebugAnimation, "Animation", "Debug the animation changes"},
|
||||
{kDebugUnknown, "Unknown", "Debug unknown values on the data"},
|
||||
DEBUG_CHANNEL_END
|
||||
};
|
||||
|
||||
static const ADGameDescription gameDescriptions[] = {
|
||||
|
||||
// The Longest Journey
|
||||
// English Steam (game.exe missing valid 147.bmp resource for dialog boxes background)
|
||||
{
|
||||
"tlj", "Steam",
|
||||
AD_ENTRY3s("x.xarc", "de8327850d7bba90b690b141eaa23f61", 3032,
|
||||
"chapters.ini", "5b5a1f1dd2297d9ce0d3d12216d5d2c5", 485,
|
||||
"game.exe", "2a68bd64e71635c74a5c6bb172ec1cb1", 95744),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
GF_MISSING_EXE_RESOURCES|ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// English DVD
|
||||
{
|
||||
"tlj", "DVD",
|
||||
AD_ENTRY2s("x.xarc", "de8327850d7bba90b690b141eaa23f61", 3032,
|
||||
"chapters.ini", "5b5a1f1dd2297d9ce0d3d12216d5d2c5", 485),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// GOG edition
|
||||
{
|
||||
"tlj", "GOG.com",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "5b5a1f1dd2297d9ce0d3d12216d5d2c5", 485),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// English Old Demo
|
||||
{
|
||||
"tlj", "Old Demo",
|
||||
AD_ENTRY2s("x.xarc", "97abc1bb9239dee4c208e533f3c97e1c", 98,
|
||||
"chapters.ini", "5b5a1f1dd2297d9ce0d3d12216d5d2c5", 485),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DEMO,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// English v1.61 Demo
|
||||
{
|
||||
"tlj", "v1.61 Demo",
|
||||
AD_ENTRY2s("x.xarc", "61093bcd499b386ed5c0345c52f48909", 98,
|
||||
"chapters.ini", "5b5a1f1dd2297d9ce0d3d12216d5d2c5", 485),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DEMO,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Czech 4CD
|
||||
// Bugreport #11914
|
||||
{
|
||||
"tlj", "4 CD build 142",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "547f0b9c04c00d330b60eed6e8d24732", 484),
|
||||
Common::CS_CZE,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// French Demo
|
||||
{
|
||||
"tlj", "Demo",
|
||||
AD_ENTRY2s("x.xarc", "97abc1bb9239dee4c208e533f3c97e1c", 98,
|
||||
"chapters.ini", "e54f6370dca06496069790840409cf95", 506),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DEMO,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Norwegian Demo
|
||||
{
|
||||
"tlj", "Demo",
|
||||
AD_ENTRY2s("x.xarc", "97abc1bb9239dee4c208e533f3c97e1c", 98,
|
||||
"chapters.ini", "f358f604abd1aa1476ed05d6d271ac70", 473),
|
||||
Common::NB_NOR,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DEMO,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Norwegian 4 CD version - supplied by L0ngcat
|
||||
{
|
||||
"tlj", "4 CD",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "f358f604abd1aa1476ed05d6d271ac70", 473),
|
||||
Common::NB_NOR,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Norwegian DLC-edition (DVD?)
|
||||
{
|
||||
"tlj", "DVD",
|
||||
AD_ENTRY2s("x.xarc", "de8327850d7bba90b690b141eaa23f61", 3032,
|
||||
"chapters.ini", "f358f604abd1aa1476ed05d6d271ac70", 473),
|
||||
Common::NB_NOR,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// German DVD version supplied by Vorph on the forums
|
||||
{
|
||||
"tlj", "DVD",
|
||||
AD_ENTRY2s("x.xarc", "de8327850d7bba90b690b141eaa23f61", 3032,
|
||||
"chapters.ini", "e4611d143a87b263d8d7a54edc7e7cd7", 515),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// German 4CD
|
||||
{
|
||||
"tlj", "4 CD",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "e4611d143a87b263d8d7a54edc7e7cd7", 515),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Italian DVD version
|
||||
{
|
||||
"tlj", "DVD",
|
||||
AD_ENTRY2s("x.xarc", "de8327850d7bba90b690b141eaa23f61", 3032,
|
||||
"chapters.ini", "9a81ea4e6f5b84511dd4e56d04a64e2e", 498),
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Italian 4CD
|
||||
{
|
||||
"tlj", "4 CD",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "9a81ea4e6f5b84511dd4e56d04a64e2e", 498),
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Dutch 4CD
|
||||
{
|
||||
"tlj", "4 CD",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "c8dadd9a3b41640734d6213e89cd5635", 508),
|
||||
Common::NL_NLD,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Spanish 4CD
|
||||
{
|
||||
"tlj", "4 CD",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "3640df6d536b186bff228337284d9631", 525),
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// French 2CD
|
||||
{
|
||||
"tlj", "2 CD",
|
||||
AD_ENTRY2s("x.xarc", "de8327850d7bba90b690b141eaa23f61", 3032,
|
||||
"chapters.ini", "e54f6370dca06496069790840409cf95", 506),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// French 4CD
|
||||
{
|
||||
"tlj", "4 CD",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "e54f6370dca06496069790840409cf95", 506),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Swedish Demo
|
||||
{
|
||||
"tlj", "Demo",
|
||||
AD_ENTRY2s("x.xarc", "97abc1bb9239dee4c208e533f3c97e1c", 98,
|
||||
"chapters.ini", "f6a2007300209492b7b90b4c0467832d", 462),
|
||||
Common::SV_SWE,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DEMO,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Swedish 4CD
|
||||
{
|
||||
"tlj", "4 CD",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "f6a2007300209492b7b90b4c0467832d", 462),
|
||||
Common::SV_SWE,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Swedish DVD Nordic Special Edition - supplied by L0ngcat
|
||||
{
|
||||
"tlj", "DVD",
|
||||
AD_ENTRY2s("x.xarc", "de8327850d7bba90b690b141eaa23f61", 3032,
|
||||
"chapters.ini", "f6a2007300209492b7b90b4c0467832d", 462),
|
||||
Common::SV_SWE,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Polish 4CD
|
||||
{
|
||||
"tlj", "4 CD",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "6abc5c38e6e31face4b675355b117620", 499),
|
||||
Common::PL_POL,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Polish Demo.
|
||||
// Provided by Faalargon, Bugreport #11883 (#1440 in Residualvm)
|
||||
// Folder structure is completely different. Unsupported for now
|
||||
{
|
||||
"tlj", MetaEngineDetection::GAME_NOT_IMPLEMENTED, // Reason for being unsupported
|
||||
AD_ENTRY2s("x.xarc", "6c6c388f757adcc49e7f33b0b2cccf96", 2904,
|
||||
"chapters.ini", "6ee43a176a5eb94153c2d813261c3226", 252),
|
||||
Common::PL_POL,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DEMO | ADGF_UNSUPPORTED,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Russian 2CD by 1C
|
||||
{
|
||||
"tlj", "2 CD/Fargus",
|
||||
AD_ENTRY2s("x.xarc", "de8327850d7bba90b690b141eaa23f61", 3032,
|
||||
"chapters.ini", "740b97b94e97ed11f064f5fa125ebee1", 486),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Russian 2CD by 7Wolf
|
||||
{
|
||||
"tlj", "2 CD/7Wolf",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "8e08025c89575d2573c2edf0daa1cb34", 406),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_UNSTABLE,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Russian by Triada
|
||||
{
|
||||
"tlj", "Triada",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "17510546145f6f574702094dca436a8d", 409),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_UNSTABLE,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Hungarian fan-made
|
||||
{
|
||||
"tlj", "Fanmade",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "790b51a88b5493bff5168a77738e0e84", 451),
|
||||
Common::HU_HUN,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Hungarian fan-made applied to German DVD version
|
||||
// Trac report #14384
|
||||
{
|
||||
"tlj", "Fanmade",
|
||||
AD_ENTRY2s("x.xarc", "de8327850d7bba90b690b141eaa23f61", 3032,
|
||||
"chapters.ini", "790b51a88b5493bff5168a77738e0e84", 451),
|
||||
Common::HU_HUN,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// iOS - v1.0.7 - provided by Aeryl on Discord
|
||||
{
|
||||
"tlj", "Remastered",
|
||||
AD_ENTRY2s("x.xarc", "de8327850d7bba90b690b141eaa23f61", 3032,
|
||||
"chapters.ini", "da19240d49f714a27da2054caadc0057", 500),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformIOS,
|
||||
ADGF_UNSTABLE,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
// The Longest Journey
|
||||
// Hebrew fan-made
|
||||
{
|
||||
"tlj", "Fanmade",
|
||||
AD_ENTRY2s("x.xarc", "a0559457126caadab0cadac02d35f26f", 3032,
|
||||
"chapters.ini", "53c9b6087044be0ba05f8d7b39c8ae48", 367),
|
||||
Common::HE_ISR,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
||||
AD_TABLE_END_MARKER
|
||||
};
|
||||
|
||||
class StarkMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
|
||||
public:
|
||||
StarkMetaEngineDetection() : AdvancedMetaEngineDetection(gameDescriptions, starkGames) {
|
||||
_guiOptions = GUIO4(GUIO_NOMIDI, GAMEOPTION_ASSETS_MOD, GAMEOPTION_LINEAR_FILTERING, GAMEOPTION_FONT_ANTIALIASING);
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "Stark";
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "stark";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "(C) Funcom";
|
||||
}
|
||||
|
||||
const DebugChannelDef *getDebugChannels() const override {
|
||||
return debugFlagList;
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
REGISTER_PLUGIN_STATIC(STARK_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, Stark::StarkMetaEngineDetection);
|
||||
37
engines/stark/detection.h
Normal file
37
engines/stark/detection.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/* 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 STARK_DETECTION_H
|
||||
#define STARK_DETECTION_H
|
||||
|
||||
namespace Stark {
|
||||
|
||||
enum GameFlags {
|
||||
GF_MISSING_EXE_RESOURCES = 1 << 0
|
||||
};
|
||||
|
||||
#define GAMEOPTION_ASSETS_MOD GUIO_GAMEOPTIONS1
|
||||
#define GAMEOPTION_LINEAR_FILTERING GUIO_GAMEOPTIONS2
|
||||
#define GAMEOPTION_FONT_ANTIALIASING GUIO_GAMEOPTIONS3
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_DETECTION_H
|
||||
124
engines/stark/formats/biff.cpp
Normal file
124
engines/stark/formats/biff.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
/* 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/stark/formats/biff.h"
|
||||
|
||||
#include "engines/stark/services/archiveloader.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
BiffArchive::BiffArchive(ArchiveReadStream *stream, ObjectBuilder objectBuilder) :
|
||||
_objectBuilder(objectBuilder) {
|
||||
read(stream);
|
||||
}
|
||||
|
||||
BiffArchive::~BiffArchive() {
|
||||
for (uint i = 0; i < _rootObjects.size(); i++) {
|
||||
delete _rootObjects[i];
|
||||
}
|
||||
}
|
||||
|
||||
void BiffArchive::read(ArchiveReadStream *stream) {
|
||||
uint32 id = stream->readUint32BE();
|
||||
if (id != MKTAG('B', 'I', 'F', 'F')) {
|
||||
error("Wrong magic while reading biff archive");
|
||||
}
|
||||
|
||||
_version = stream->readUint32LE();
|
||||
/*uint32 u1 = */stream->readUint32LE();
|
||||
/*uint32 u2 = */stream->readUint32LE();
|
||||
|
||||
uint32 len = stream->readUint32LE();
|
||||
for (uint32 i = 0; i < len; i++) {
|
||||
BiffObject *object = readObject(stream, nullptr);
|
||||
|
||||
_rootObjects.push_back(object);
|
||||
}
|
||||
}
|
||||
|
||||
BiffObject *BiffArchive::readObject(ArchiveReadStream *stream, BiffObject *parent) {
|
||||
uint32 marker = stream->readUint32LE();
|
||||
if (marker != 0xf0f0f0f0) {
|
||||
error("Wrong magic while reading biff archive");
|
||||
}
|
||||
|
||||
uint32 type = stream->readUint32LE();
|
||||
BiffObject *object = _objectBuilder(type);
|
||||
if (!object) {
|
||||
error("Unimplemented BIFF object type %x", type);
|
||||
}
|
||||
|
||||
object->_parent = parent;
|
||||
object->_u3 = stream->readUint32LE();
|
||||
uint32 size = stream->readUint32LE();
|
||||
|
||||
if (_version >= 2) {
|
||||
object->_version = stream->readUint32LE();
|
||||
}
|
||||
|
||||
object->readData(stream, size);
|
||||
|
||||
marker = stream->readUint32LE();
|
||||
if (marker != 0x0f0f0f0f) {
|
||||
error("Wrong magic while reading biff archive");
|
||||
}
|
||||
|
||||
uint32 len = stream->readUint32LE();
|
||||
for (uint32 i = 0; i < len; ++i) {
|
||||
BiffObject *child = readObject(stream, object);
|
||||
object->addChild(child);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
Common::Array<BiffObject *> BiffArchive::listObjects() {
|
||||
return _rootObjects;
|
||||
}
|
||||
|
||||
BiffObject::BiffObject() :
|
||||
_u3(0),
|
||||
_version(0),
|
||||
_parent(nullptr),
|
||||
_type(0) {
|
||||
|
||||
}
|
||||
|
||||
uint32 BiffObject::getType() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
void BiffObject::addChild(BiffObject *child) {
|
||||
_children.push_back(child);
|
||||
}
|
||||
|
||||
BiffObject::~BiffObject() {
|
||||
// Delete the children objects
|
||||
Common::Array<BiffObject *>::iterator i = _children.begin();
|
||||
while (i != _children.end()) {
|
||||
delete *i;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
135
engines/stark/formats/biff.h
Normal file
135
engines/stark/formats/biff.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/* 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 STARK_FORMATS_BIFF_H
|
||||
#define STARK_FORMATS_BIFF_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class ArchiveReadStream;
|
||||
|
||||
namespace Formats {
|
||||
|
||||
class BiffObject;
|
||||
|
||||
/**
|
||||
* A tree-style container for BiffObjects
|
||||
*
|
||||
* Users of this class must provide a factory method for the BiffObject subclasses
|
||||
* contained in the archive. This class can only read the archive's structure
|
||||
* and not specific object types.
|
||||
*/
|
||||
class BiffArchive {
|
||||
public:
|
||||
typedef BiffObject *(*ObjectBuilder)(uint32 type);
|
||||
|
||||
BiffArchive(ArchiveReadStream *stream, ObjectBuilder objectBuilder);
|
||||
~BiffArchive();
|
||||
|
||||
/** List the objects at the root level of the archive */
|
||||
Common::Array<BiffObject *> listObjects();
|
||||
|
||||
/** List objects recursively matching the template parameter type */
|
||||
template<class T>
|
||||
Common::Array<T *> listObjectsRecursive();
|
||||
|
||||
private:
|
||||
void read(ArchiveReadStream *stream);
|
||||
BiffObject *readObject(ArchiveReadStream *stream, BiffObject *parent);
|
||||
|
||||
ObjectBuilder _objectBuilder;
|
||||
uint32 _version;
|
||||
|
||||
Common::Array<BiffObject *> _rootObjects;
|
||||
};
|
||||
|
||||
/**
|
||||
* An object which can be read from a BiffArchive
|
||||
*
|
||||
* Each object has a list of children objects, resulting in a tree structure
|
||||
*/
|
||||
class BiffObject {
|
||||
public:
|
||||
BiffObject();
|
||||
virtual ~BiffObject();
|
||||
|
||||
/**
|
||||
* Used to read the object data from the stream
|
||||
*/
|
||||
virtual void readData(ArchiveReadStream *stream, uint32 dataLength) = 0;
|
||||
|
||||
/** Get the object type */
|
||||
uint32 getType() const;
|
||||
|
||||
/** List children recursively matching the template parameter type */
|
||||
template<class T>
|
||||
Common::Array<T *> listChildrenRecursive();
|
||||
|
||||
/** Add an object to the children list */
|
||||
void addChild(BiffObject *child);
|
||||
|
||||
protected:
|
||||
uint32 _type;
|
||||
uint32 _u3;
|
||||
uint32 _version;
|
||||
|
||||
BiffObject *_parent;
|
||||
Common::Array<BiffObject *> _children;
|
||||
|
||||
friend class BiffArchive;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
Common::Array<T *> BiffArchive::listObjectsRecursive() {
|
||||
Common::Array<BiffObject *> objects = listObjects();
|
||||
|
||||
Common::Array<T *> array;
|
||||
for (uint i = 0; i < objects.size(); i++) {
|
||||
array.push_back(objects[i]->listChildrenRecursive<T>());
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
Common::Array<T *> BiffObject::listChildrenRecursive() {
|
||||
Common::Array<T *> list;
|
||||
|
||||
for (uint i = 0; i < _children.size(); i++) {
|
||||
if (_children[i]->getType() == T::TYPE) {
|
||||
// Found a matching child
|
||||
list.push_back(static_cast<T *>(_children[i]));
|
||||
}
|
||||
|
||||
// Look for matching resources in the child's children
|
||||
list.push_back(_children[i]->listChildrenRecursive<T>());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_FORMATS_BIFF_H
|
||||
443
engines/stark/formats/biffmesh.cpp
Normal file
443
engines/stark/formats/biffmesh.cpp
Normal file
@@ -0,0 +1,443 @@
|
||||
/* 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/stark/formats/biffmesh.h"
|
||||
|
||||
#include "engines/stark/formats/biff.h"
|
||||
#include "engines/stark/services/archiveloader.h"
|
||||
|
||||
#include "common/hashmap.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
enum BiffMeshObjectType {
|
||||
kMeshObjectSceneData = 0x5a4aa94,
|
||||
kMeshObjectBase = 0x5a4aa89,
|
||||
kMeshObjectTri = 0x5a4aa8d,
|
||||
kMeshObjectMaterial = 0x5a4aa8e
|
||||
};
|
||||
|
||||
class MeshObjectSceneData : public BiffObject {
|
||||
public:
|
||||
static const uint32 TYPE = kMeshObjectSceneData;
|
||||
|
||||
MeshObjectSceneData() :
|
||||
BiffObject(),
|
||||
_animStart(0),
|
||||
_animEnd(0) {
|
||||
_type = TYPE;
|
||||
}
|
||||
|
||||
// BiffObject API
|
||||
void readData(ArchiveReadStream *stream, uint32 dataLength) override {
|
||||
_animStart = stream->readUint32LE();
|
||||
_animEnd = stream->readUint32LE();
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 _animStart;
|
||||
uint32 _animEnd;
|
||||
};
|
||||
|
||||
class MeshObjectBase : public BiffObject {
|
||||
public:
|
||||
static const uint32 TYPE = kMeshObjectBase;
|
||||
|
||||
MeshObjectBase() :
|
||||
BiffObject() {
|
||||
_type = TYPE;
|
||||
}
|
||||
|
||||
// BiffObject API
|
||||
void readData(ArchiveReadStream *stream, uint32 dataLength) override {
|
||||
_name = stream->readString16();
|
||||
}
|
||||
|
||||
private:
|
||||
Common::String _name;
|
||||
};
|
||||
|
||||
class MeshObjectTri : public BiffObject {
|
||||
public:
|
||||
static const uint32 TYPE = kMeshObjectTri;
|
||||
|
||||
MeshObjectTri() :
|
||||
BiffObject(),
|
||||
_hasPhysics(false) {
|
||||
_type = TYPE;
|
||||
}
|
||||
|
||||
struct KeyFrame {
|
||||
uint32 time;
|
||||
Math::Quaternion essentialRotation;
|
||||
float determinant;
|
||||
Math::Quaternion stretchRotation;
|
||||
Math::Vector3d scale;
|
||||
Math::Vector3d translation;
|
||||
};
|
||||
|
||||
struct Vertex {
|
||||
Common::String animName1;
|
||||
Common::String animName2;
|
||||
float animInfluence1;
|
||||
float animInfluence2;
|
||||
Math::Vector3d position;
|
||||
};
|
||||
|
||||
struct RawFace {
|
||||
uint32 vertexIndex[3];
|
||||
uint32 normalIndex[3];
|
||||
uint32 textureVertexIndex[3];
|
||||
uint32 materialId;
|
||||
uint32 smoothingGroup;
|
||||
};
|
||||
|
||||
// BiffObject API
|
||||
void readData(ArchiveReadStream *stream, uint32 dataLength) override {
|
||||
_name = stream->readString16();
|
||||
|
||||
uint32 keyFrameCount = stream->readUint32LE();
|
||||
for (uint i = 0; i < keyFrameCount; i++) {
|
||||
KeyFrame keyFrame;
|
||||
keyFrame.time = stream->readUint32LE();
|
||||
keyFrame.essentialRotation = stream->readQuaternion();
|
||||
keyFrame.determinant = stream->readFloatLE();
|
||||
keyFrame.stretchRotation = stream->readQuaternion();
|
||||
keyFrame.scale = stream->readVector3();
|
||||
keyFrame.translation = stream->readVector3();
|
||||
|
||||
_keyFrames.push_back(keyFrame);
|
||||
}
|
||||
|
||||
if (_version >= 2) {
|
||||
uint32 uvKeyFrameCount = stream->readUint32LE();
|
||||
assert(uvKeyFrameCount == 0); // Reading the uv keyframes is not implemented
|
||||
uint32 attributeCount = stream->readUint32LE();
|
||||
assert(attributeCount == 0); // Reading the attributes is not implemented
|
||||
}
|
||||
|
||||
uint32 vertexCount = stream->readUint32LE();
|
||||
for (uint i = 0; i < vertexCount; i++) {
|
||||
Vertex vertex;
|
||||
vertex.animName1 = stream->readString16();
|
||||
vertex.animName2 = stream->readString16();
|
||||
vertex.animInfluence1 = stream->readFloatLE();
|
||||
vertex.animInfluence2 = stream->readFloatLE();
|
||||
vertex.position = stream->readVector3();
|
||||
|
||||
_rawVertices.push_back(vertex);
|
||||
}
|
||||
|
||||
uint32 normalCount = stream->readUint32LE();
|
||||
for (uint i = 0; i < normalCount; i++) {
|
||||
_rawNormals.push_back(stream->readVector3());
|
||||
}
|
||||
|
||||
uint32 textureVertexCount = stream->readUint32LE();
|
||||
for (uint i = 0; i < textureVertexCount; i++) {
|
||||
_rawTexturePositions.push_back(stream->readVector3());
|
||||
}
|
||||
|
||||
uint32 faceCount = stream->readUint32LE();
|
||||
for (uint i = 0; i < faceCount; i++) {
|
||||
RawFace face;
|
||||
face.vertexIndex[0] = stream->readUint32LE();
|
||||
face.vertexIndex[1] = stream->readUint32LE();
|
||||
face.vertexIndex[2] = stream->readUint32LE();
|
||||
face.normalIndex[0] = stream->readUint32LE();
|
||||
face.normalIndex[1] = stream->readUint32LE();
|
||||
face.normalIndex[2] = stream->readUint32LE();
|
||||
face.textureVertexIndex[0] = stream->readUint32LE();
|
||||
face.textureVertexIndex[1] = stream->readUint32LE();
|
||||
face.textureVertexIndex[2] = stream->readUint32LE();
|
||||
face.materialId = stream->readUint32LE();
|
||||
face.smoothingGroup = stream->readUint32LE();
|
||||
|
||||
_rawFaces.push_back(face);
|
||||
}
|
||||
|
||||
_hasPhysics = stream->readByte();
|
||||
}
|
||||
|
||||
void reindex() {
|
||||
// The raw data loaded from the BIFF archive needs to be split into faces of identical material.
|
||||
// Reserve enough faces in our faces array
|
||||
for (uint i = 0; i < _rawFaces.size(); i++) {
|
||||
if (_rawFaces[i].materialId >= _faces.size()) {
|
||||
_faces.resize(_rawFaces[i].materialId + 1);
|
||||
}
|
||||
_faces[_rawFaces[i].materialId].materialId = _rawFaces[i].materialId;
|
||||
}
|
||||
|
||||
// The raw data loaded from the BIFF archive is multi-indexed, which is not simple to use to draw.
|
||||
// Here, we reindex the data so that each vertex owns all of its related attributes, hence requiring
|
||||
// a single index list.
|
||||
Common::HashMap<VertexKey, uint32, VertexKey::Hash, VertexKey::EqualTo> vertexIndexMap;
|
||||
for (uint i = 0; i < _rawFaces.size(); i++) {
|
||||
for (uint j = 0; j < 3; j++) {
|
||||
VertexKey vertexKey(_rawFaces[i].vertexIndex[j], _rawFaces[i].normalIndex[j], _rawFaces[i].textureVertexIndex[j]);
|
||||
if (!vertexIndexMap.contains(vertexKey)) {
|
||||
BiffMesh::Vertex vertex;
|
||||
vertex.position = _rawVertices[_rawFaces[i].vertexIndex[j]].position;
|
||||
vertex.normal = _rawNormals[_rawFaces[i].normalIndex[j]];
|
||||
vertex.texturePosition = _rawTexturePositions[_rawFaces[i].textureVertexIndex[j]];
|
||||
|
||||
_vertices.push_back(vertex);
|
||||
|
||||
vertexIndexMap.setVal(vertexKey, _vertices.size() - 1);
|
||||
}
|
||||
|
||||
uint32 vertexIndex = vertexIndexMap.getVal(vertexKey);
|
||||
|
||||
// Add the index to a face according to its material
|
||||
_faces[_rawFaces[i].materialId].vertexIndices.push_back(vertexIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the raw data
|
||||
_rawVertices.clear();
|
||||
_rawNormals.clear();
|
||||
_rawTexturePositions.clear();
|
||||
_rawFaces.clear();
|
||||
}
|
||||
|
||||
Math::Matrix4 getTransform(uint keyframeIndex) const {
|
||||
const KeyFrame &keyframe = _keyFrames[keyframeIndex];
|
||||
|
||||
Math::Matrix4 translation;
|
||||
translation.setPosition(keyframe.translation);
|
||||
|
||||
Math::Matrix4 essentialRotation = keyframe.essentialRotation.toMatrix();
|
||||
|
||||
Math::Matrix4 determinant;
|
||||
determinant.setValue(0, 0, keyframe.determinant);
|
||||
determinant.setValue(1, 1, keyframe.determinant);
|
||||
determinant.setValue(2, 2, keyframe.determinant);
|
||||
|
||||
Math::Matrix4 stretchRotation = keyframe.stretchRotation.toMatrix();
|
||||
|
||||
Math::Matrix4 stretchRotationTransposed = stretchRotation;
|
||||
stretchRotationTransposed.transpose();
|
||||
|
||||
Math::Matrix4 scale;
|
||||
scale.setValue(0, 0, keyframe.scale.x());
|
||||
scale.setValue(1, 1, keyframe.scale.y());
|
||||
scale.setValue(2, 2, keyframe.scale.z());
|
||||
|
||||
return translation * essentialRotation * determinant * stretchRotationTransposed * scale * stretchRotation;
|
||||
}
|
||||
|
||||
const Common::Array<BiffMesh::Vertex> &getVertices() const {
|
||||
return _vertices;
|
||||
}
|
||||
|
||||
const Common::Array<Face> &getFaces() const {
|
||||
return _faces;
|
||||
}
|
||||
|
||||
private:
|
||||
struct VertexKey {
|
||||
uint32 _vertexIndex;
|
||||
uint32 _normalIndex;
|
||||
uint32 _textureVertexIndex;
|
||||
|
||||
VertexKey(uint32 vertexIndex, uint32 normalIndex, uint32 textureVertexIndex) {
|
||||
_vertexIndex = vertexIndex;
|
||||
_normalIndex = normalIndex;
|
||||
_textureVertexIndex = textureVertexIndex;
|
||||
}
|
||||
|
||||
struct Hash {
|
||||
uint operator() (const VertexKey &x) const {
|
||||
return x._vertexIndex + x._normalIndex + x._textureVertexIndex;
|
||||
}
|
||||
};
|
||||
|
||||
struct EqualTo {
|
||||
bool operator() (const VertexKey &x, const VertexKey &y) const {
|
||||
return x._vertexIndex == y._vertexIndex &&
|
||||
x._normalIndex == y._normalIndex &&
|
||||
x._textureVertexIndex == y._textureVertexIndex;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Common::String _name;
|
||||
|
||||
Common::Array<KeyFrame> _keyFrames;
|
||||
|
||||
Common::Array<Vertex> _rawVertices;
|
||||
Common::Array<RawFace> _rawFaces;
|
||||
Common::Array<Math::Vector3d> _rawNormals;
|
||||
Common::Array<Math::Vector3d> _rawTexturePositions;
|
||||
|
||||
Common::Array<BiffMesh::Vertex> _vertices;
|
||||
Common::Array<Face> _faces;
|
||||
|
||||
bool _hasPhysics;
|
||||
};
|
||||
|
||||
class MeshObjectMaterial : public BiffObject {
|
||||
public:
|
||||
static const uint32 TYPE = kMeshObjectMaterial;
|
||||
|
||||
MeshObjectMaterial() :
|
||||
BiffObject(),
|
||||
_shading(0),
|
||||
_shininess(0),
|
||||
_opacity(1),
|
||||
_doubleSided(false),
|
||||
_textureTiling(0),
|
||||
_alphaTiling(0),
|
||||
_environementTiling(0),
|
||||
_isColorKey(false),
|
||||
_colorKey(0) {
|
||||
_type = TYPE;
|
||||
}
|
||||
|
||||
// BiffObject API
|
||||
void readData(ArchiveReadStream *stream, uint32 dataLength) override {
|
||||
_name = stream->readString16();
|
||||
_texture = stream->readString16();
|
||||
_alpha = stream->readString16();
|
||||
_environment = stream->readString16();
|
||||
|
||||
_shading = stream->readUint32LE();
|
||||
_ambiant = stream->readVector3();
|
||||
_diffuse = stream->readVector3();
|
||||
_specular = stream->readVector3();
|
||||
|
||||
_shininess = stream->readFloatLE();
|
||||
_opacity = stream->readFloatLE();
|
||||
|
||||
_doubleSided = stream->readByte();
|
||||
_textureTiling = stream->readUint32LE();
|
||||
_alphaTiling = stream->readUint32LE();
|
||||
_environementTiling = stream->readUint32LE();
|
||||
|
||||
_isColorKey = stream->readByte();
|
||||
_colorKey = stream->readUint32LE();
|
||||
|
||||
uint32 attributeCount = stream->readUint32LE();
|
||||
assert(attributeCount == 0); // Reading the attributes is not implemented
|
||||
}
|
||||
|
||||
Material toMaterial() const {
|
||||
Material material;
|
||||
material.name = _name;
|
||||
material.texture = _texture;
|
||||
material.r = _diffuse.x();
|
||||
material.g = _diffuse.y();
|
||||
material.b = _diffuse.z();
|
||||
material.doubleSided = _doubleSided;
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
private:
|
||||
Common::String _name;
|
||||
Common::String _texture;
|
||||
Common::String _alpha;
|
||||
Common::String _environment;
|
||||
|
||||
uint32 _shading;
|
||||
Math::Vector3d _ambiant;
|
||||
Math::Vector3d _diffuse;
|
||||
Math::Vector3d _specular;
|
||||
|
||||
float _shininess;
|
||||
float _opacity;
|
||||
|
||||
bool _doubleSided;
|
||||
uint32 _textureTiling;
|
||||
uint32 _alphaTiling;
|
||||
uint32 _environementTiling;
|
||||
|
||||
bool _isColorKey;
|
||||
uint32 _colorKey;
|
||||
};
|
||||
|
||||
BiffMesh *BiffMeshReader::read(ArchiveReadStream *stream) {
|
||||
BiffArchive archive = BiffArchive(stream, &biffObjectBuilder);
|
||||
Common::Array<MeshObjectTri *> tris = archive.listObjectsRecursive<MeshObjectTri>();
|
||||
Common::Array<MeshObjectMaterial *> materialObjects = archive.listObjectsRecursive<MeshObjectMaterial>();
|
||||
|
||||
if (tris.size() != 1) {
|
||||
error("Unexpected tri count in BIFF archive: '%d'", tris.size());
|
||||
}
|
||||
|
||||
tris[0]->reindex();
|
||||
|
||||
Common::Array<Material> materials;
|
||||
for (uint i = 0; i < materialObjects.size(); i++) {
|
||||
materials.push_back(materialObjects[i]->toMaterial());
|
||||
}
|
||||
|
||||
BiffMesh *mesh = new BiffMesh(tris[0]->getVertices(), tris[0]->getFaces(), materials);
|
||||
mesh->setTransform(tris[0]->getTransform(0));
|
||||
return mesh;
|
||||
}
|
||||
|
||||
BiffObject *BiffMeshReader::biffObjectBuilder(uint32 type) {
|
||||
switch (type) {
|
||||
case kMeshObjectSceneData:
|
||||
return new MeshObjectSceneData();
|
||||
case kMeshObjectBase:
|
||||
return new MeshObjectBase();
|
||||
case kMeshObjectTri:
|
||||
return new MeshObjectTri();
|
||||
case kMeshObjectMaterial:
|
||||
return new MeshObjectMaterial();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
BiffMesh::BiffMesh(const Common::Array<Vertex> &vertices, const Common::Array<Face> &faces,
|
||||
const Common::Array<Material> &materials) :
|
||||
_vertices(vertices),
|
||||
_faces(faces),
|
||||
_materials(materials) {
|
||||
}
|
||||
|
||||
const Common::Array<BiffMesh::Vertex> &BiffMesh::getVertices() const {
|
||||
return _vertices;
|
||||
}
|
||||
|
||||
const Common::Array<Face> &BiffMesh::getFaces() const {
|
||||
return _faces;
|
||||
}
|
||||
|
||||
const Common::Array<Material> &BiffMesh::getMaterials() const {
|
||||
return _materials;
|
||||
}
|
||||
|
||||
void BiffMesh::setTransform(const Math::Matrix4 &transform) {
|
||||
_transform = transform;
|
||||
}
|
||||
|
||||
Math::Matrix4 BiffMesh::getTransform() const {
|
||||
return _transform;
|
||||
}
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
87
engines/stark/formats/biffmesh.h
Normal file
87
engines/stark/formats/biffmesh.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/* 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 STARK_FORMATS_BIFF_MESH_H
|
||||
#define STARK_FORMATS_BIFF_MESH_H
|
||||
|
||||
#include "engines/stark/model/model.h"
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
#include "math/matrix4.h"
|
||||
#include "math/vector3d.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class ArchiveReadStream;
|
||||
|
||||
namespace Formats {
|
||||
|
||||
class BiffObject;
|
||||
class BiffMesh;
|
||||
|
||||
/**
|
||||
* A mesh reader
|
||||
*
|
||||
* Reads a mesh out of a BIFF archive
|
||||
*/
|
||||
class BiffMeshReader {
|
||||
public:
|
||||
/** Read a mesh from a BIFF archive stream */
|
||||
static BiffMesh *read(ArchiveReadStream *stream);
|
||||
|
||||
private:
|
||||
static BiffObject *biffObjectBuilder(uint32 type);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* A mesh read out of a BIFF archive
|
||||
*/
|
||||
class BiffMesh {
|
||||
public:
|
||||
struct Vertex {
|
||||
Math::Vector3d position;
|
||||
Math::Vector3d normal;
|
||||
Math::Vector3d texturePosition;
|
||||
};
|
||||
|
||||
BiffMesh(const Common::Array<Vertex> &vertices, const Common::Array<Face> &faces, const Common::Array<Material> &materials);
|
||||
|
||||
const Common::Array<Vertex> &getVertices() const;
|
||||
const Common::Array<Face> &getFaces() const;
|
||||
const Common::Array<Material> &getMaterials() const;
|
||||
Math::Matrix4 getTransform() const;
|
||||
|
||||
void setTransform(const Math::Matrix4 &transform);
|
||||
|
||||
private:
|
||||
Common::Array<Vertex> _vertices;
|
||||
Common::Array<Face> _faces;
|
||||
Common::Array<Material> _materials;
|
||||
|
||||
Math::Matrix4 _transform;
|
||||
};
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_FORMATS_BIFF_MESH_H
|
||||
190
engines/stark/formats/dds.cpp
Normal file
190
engines/stark/formats/dds.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/* 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/stark/formats/dds.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
// Based on xoreos' DDS code
|
||||
|
||||
static const uint32 kDDSID = MKTAG('D', 'D', 'S', ' ');
|
||||
|
||||
static const uint32 kHeaderFlagsHasMipMaps = 0x00020000;
|
||||
|
||||
static const uint32 kPixelFlagsHasAlpha = 0x00000001;
|
||||
static const uint32 kPixelFlagsHasFourCC = 0x00000004;
|
||||
static const uint32 kPixelFlagsIsIndexed = 0x00000020;
|
||||
static const uint32 kPixelFlagsIsRGB = 0x00000040;
|
||||
|
||||
DDS::~DDS() {
|
||||
for (uint i = 0; i < _mipmaps.size(); i++) {
|
||||
_mipmaps[i].free();
|
||||
}
|
||||
}
|
||||
|
||||
bool DDS::load(Common::SeekableReadStream &dds, const Common::String &name) {
|
||||
assert(_mipmaps.empty());
|
||||
|
||||
_name = name;
|
||||
|
||||
if (!readHeader(dds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return readData(dds);
|
||||
}
|
||||
|
||||
const DDS::MipMaps &DDS::getMipMaps() const {
|
||||
return _mipmaps;
|
||||
}
|
||||
|
||||
bool DDS::readHeader(Common::SeekableReadStream &dds) {
|
||||
// We found the FourCC of a standard DDS
|
||||
uint32 magic = dds.readUint32BE();
|
||||
if (magic != kDDSID) {
|
||||
warning("Invalid DDS magic number: %d for %s", magic, _name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// All DDS header should be 124 bytes (+ 4 for the FourCC)
|
||||
uint32 headerSize = dds.readUint32LE();
|
||||
if (headerSize != 124) {
|
||||
warning("Invalid DDS header size: %d for %s", headerSize, _name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// DDS features
|
||||
uint32 flags = dds.readUint32LE();
|
||||
|
||||
// Image dimensions
|
||||
uint32 height = dds.readUint32LE();
|
||||
uint32 width = dds.readUint32LE();
|
||||
|
||||
if ((width >= 0x8000) || (height >= 0x8000)) {
|
||||
warning("Unsupported DDS image dimensions (%ux%u) for %s", width, height, _name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
dds.skip(4 + 4); // Pitch + Depth
|
||||
//uint32 pitchOrLineSize = dds.readUint32LE();
|
||||
//uint32 depth = dds.readUint32LE();
|
||||
uint32 mipMapCount = dds.readUint32LE();
|
||||
|
||||
// DDS doesn't provide any mip maps, only one full-size image
|
||||
if ((flags & kHeaderFlagsHasMipMaps) == 0) {
|
||||
mipMapCount = 1;
|
||||
}
|
||||
|
||||
dds.skip(44); // Reserved
|
||||
|
||||
// Read the pixel data format
|
||||
DDSPixelFormat format;
|
||||
format.size = dds.readUint32LE();
|
||||
format.flags = dds.readUint32LE();
|
||||
format.fourCC = dds.readUint32BE();
|
||||
format.bitCount = dds.readUint32LE();
|
||||
format.rBitMask = dds.readUint32LE();
|
||||
format.gBitMask = dds.readUint32LE();
|
||||
format.bBitMask = dds.readUint32LE();
|
||||
format.aBitMask = dds.readUint32LE();
|
||||
|
||||
// Detect which specific format it describes
|
||||
if (!detectFormat(format)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dds.skip(16 + 4); // DDCAPS2 + Reserved
|
||||
|
||||
_mipmaps.resize(mipMapCount);
|
||||
for (uint32 i = 0; i < mipMapCount; i++) {
|
||||
_mipmaps[i].create(width, height, _format);
|
||||
|
||||
width >>= 1;
|
||||
height >>= 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DDS::readData(Common::SeekableReadStream &dds) {
|
||||
for (uint i = 0; i < _mipmaps.size(); i++) {
|
||||
Graphics::Surface &mipmap = _mipmaps[i];
|
||||
|
||||
uint32 size = mipmap.pitch * mipmap.h;
|
||||
uint32 readSize = dds.read(mipmap.getPixels(), size);
|
||||
|
||||
if (readSize != size) {
|
||||
warning("Inconsistent read size in DDS file: %d, expected %d for %s level %d",
|
||||
readSize, size, _name.c_str(), i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DDS::detectFormat(const DDSPixelFormat &format) {
|
||||
if (format.flags & kPixelFlagsHasFourCC) {
|
||||
warning("Unsupported DDS feature: FourCC pixel format %d for %s", format.fourCC, _name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (format.flags & kPixelFlagsIsIndexed) {
|
||||
warning("Unsupported DDS feature: Indexed %d-bits pixel format for %s", format.bitCount, _name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(format.flags & kPixelFlagsIsRGB)) {
|
||||
warning("Only RGB DDS files are supported for %s", _name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (format.bitCount != 24 && format.bitCount != 32) {
|
||||
warning("Only 24-bits and 32-bits DDS files are supported for %s", _name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((format.flags & kPixelFlagsHasAlpha) &&
|
||||
(format.bitCount == 32) &&
|
||||
(format.rBitMask == 0x00FF0000) && (format.gBitMask == 0x0000FF00) &&
|
||||
(format.bBitMask == 0x000000FF) && (format.aBitMask == 0xFF000000)) {
|
||||
_format = Graphics::PixelFormat::createFormatBGRA32();
|
||||
return true;
|
||||
} else if (!(format.flags & kPixelFlagsHasAlpha) &&
|
||||
(format.bitCount == 24) &&
|
||||
(format.rBitMask == 0x00FF0000) && (format.gBitMask == 0x0000FF00) &&
|
||||
(format.bBitMask == 0x000000FF)) {
|
||||
_format = Graphics::PixelFormat::createFormatBGR24();
|
||||
return true;
|
||||
} else {
|
||||
warning("Unsupported pixel format (%X, %X, %d, %X, %X, %X, %X) for %s",
|
||||
format.flags, format.fourCC, format.bitCount,
|
||||
format.rBitMask, format.gBitMask, format.bBitMask, format.aBitMask,
|
||||
_name.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
100
engines/stark/formats/dds.h
Normal file
100
engines/stark/formats/dds.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* 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 STARK_FORMATS_DDS_H
|
||||
#define STARK_FORMATS_DDS_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/stream.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
// Based on xoreos' DDS code
|
||||
|
||||
/**
|
||||
* DDS texture
|
||||
*
|
||||
* Only a very small subset of DDS features are supported. Especially,
|
||||
* compressed formats are not supported. This class is meant to
|
||||
* load a single DDS file per instance.
|
||||
*/
|
||||
class DDS {
|
||||
public:
|
||||
~DDS();
|
||||
|
||||
typedef Common::Array<Graphics::Surface> MipMaps;
|
||||
|
||||
/** Load a DDS texture from a stream */
|
||||
bool load(Common::SeekableReadStream &dds, const Common::String &name);
|
||||
|
||||
/**
|
||||
* Retrieve the mip map levels for a loaded texture
|
||||
*
|
||||
* The first mipmap is the full size image. Each further
|
||||
* mipmap divides by two the with and the height of the
|
||||
* previous one.
|
||||
*/
|
||||
const MipMaps &getMipMaps() const;
|
||||
|
||||
private:
|
||||
/** The specific pixel format of the included image data. */
|
||||
struct DDSPixelFormat {
|
||||
/** The size of the image data in bytes */
|
||||
uint32 size;
|
||||
|
||||
/** Features of the image data */
|
||||
uint32 flags;
|
||||
|
||||
/** The FourCC to detect the format by */
|
||||
uint32 fourCC;
|
||||
|
||||
/** Number of bits per pixel */
|
||||
uint32 bitCount;
|
||||
|
||||
/** Bit mask for the red color component */
|
||||
uint32 rBitMask;
|
||||
|
||||
/** Bit mask for the green color component */
|
||||
uint32 gBitMask;
|
||||
|
||||
/** Bit mask for the blue color component */
|
||||
uint32 bBitMask;
|
||||
|
||||
/** Bit mask for the alpha component */
|
||||
uint32 aBitMask;
|
||||
};
|
||||
|
||||
bool readHeader(Common::SeekableReadStream &dds);
|
||||
bool readData(Common::SeekableReadStream &dds);
|
||||
|
||||
bool detectFormat(const DDSPixelFormat &format);
|
||||
|
||||
MipMaps _mipmaps;
|
||||
Graphics::PixelFormat _format;
|
||||
Common::String _name;
|
||||
};
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_FORMATS_DDS_H
|
||||
154
engines/stark/formats/iss.cpp
Normal file
154
engines/stark/formats/iss.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 "engines/stark/formats/iss.h"
|
||||
|
||||
#include "audio/decoders/adpcm_intern.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "common/substream.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
/**
|
||||
* ADPCM decoder for the .iss files
|
||||
*/
|
||||
class ISSADPCMStream : public Audio::Ima_ADPCMStream {
|
||||
public:
|
||||
ISSADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {}
|
||||
|
||||
protected:
|
||||
int readBuffer(int16 *buffer, const int numSamples) override {
|
||||
// Similar to MS IMA, but without the four-bytes-per-channel requirement
|
||||
int samples;
|
||||
|
||||
assert(numSamples % 2 == 0);
|
||||
|
||||
for (samples = 0; samples < numSamples && !endOfData(); samples += 2) {
|
||||
if (_blockPos[0] == _blockAlign) {
|
||||
// read block header
|
||||
for (byte i = 0; i < _channels; i++) {
|
||||
_status.ima_ch[i].last = _stream->readSint16LE();
|
||||
_status.ima_ch[i].stepIndex = _stream->readSint16LE();
|
||||
}
|
||||
_blockPos[0] = 4 * _channels;
|
||||
}
|
||||
|
||||
byte data = _stream->readByte();
|
||||
buffer[samples + (isStereo() ? 1 : 0)] = decodeIMA(data & 0x0f, isStereo() ? 1 : 0);
|
||||
buffer[samples + (isStereo() ? 0 : 1)] = decodeIMA((data >> 4) & 0x0f);
|
||||
_blockPos[0]++;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
};
|
||||
|
||||
static void skipString(Common::SeekableReadStream *stream) {
|
||||
// Skip until the next space. Note that this will read past \0
|
||||
// characters as well. That's not a bug.
|
||||
byte ch;
|
||||
while ((ch = stream->readByte()) != 0x20)
|
||||
;
|
||||
}
|
||||
|
||||
static Common::String readString(Common::SeekableReadStream *stream) {
|
||||
Common::String ret = "";
|
||||
byte ch;
|
||||
while ((ch = stream->readByte()) != 0x20)
|
||||
ret += ch;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Audio::RewindableAudioStream *makeISSStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
Common::String codec;
|
||||
uint16 blockSize, channels, freq = 44100;
|
||||
uint32 size;
|
||||
byte flags;
|
||||
|
||||
codec = readString(stream);
|
||||
|
||||
if (codec.equals("IMA_ADPCM_Sound")) {
|
||||
|
||||
codec = readString(stream);
|
||||
blockSize = (uint16)strtol(codec.c_str(), 0, 10);
|
||||
|
||||
skipString(stream);
|
||||
// name ?
|
||||
|
||||
skipString(stream);
|
||||
// ?
|
||||
|
||||
codec = readString(stream);
|
||||
channels = (uint16)strtol(codec.c_str(), 0, 10) + 1;
|
||||
|
||||
skipString(stream);
|
||||
// ?
|
||||
|
||||
codec = readString(stream);
|
||||
int val = strtol(codec.c_str(), 0, 10);
|
||||
if (val)
|
||||
freq /= val;
|
||||
|
||||
skipString(stream);
|
||||
|
||||
skipString(stream);
|
||||
|
||||
codec = readString(stream);
|
||||
size = (uint32)strtol(codec.c_str(), 0, 10);
|
||||
|
||||
return new ISSADPCMStream(stream, DisposeAfterUse::YES, size, freq, channels, blockSize);
|
||||
} else if (codec.equals("Sound")) {
|
||||
|
||||
skipString(stream);
|
||||
// name ?
|
||||
|
||||
codec = readString(stream);
|
||||
// sample count ?
|
||||
|
||||
codec = readString(stream);
|
||||
channels = (uint16)strtol(codec.c_str(), 0, 10) + 1;
|
||||
|
||||
skipString(stream);
|
||||
// ?
|
||||
|
||||
codec = readString(stream);
|
||||
int val = strtol(codec.c_str(), 0, 10);
|
||||
if (val)
|
||||
freq /= val;
|
||||
|
||||
skipString(stream);
|
||||
|
||||
skipString(stream);
|
||||
|
||||
flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
|
||||
if (channels == 2)
|
||||
flags |= Audio::FLAG_STEREO;
|
||||
return Audio::makeRawStream(new Common::SeekableSubReadStream(stream, stream->pos(), stream->size(), DisposeAfterUse::YES), freq, flags, DisposeAfterUse::YES);
|
||||
} else {
|
||||
error("Unknown ISS codec '%s'", codec.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
45
engines/stark/formats/iss.h
Normal file
45
engines/stark/formats/iss.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* 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 STARK_ISS_H
|
||||
#define STARK_ISS_H
|
||||
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
/**
|
||||
* Create a new RewindableAudioStream from the ISS data in the given stream.
|
||||
* ISS is the file format used by the 4 CD version of the game.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the ISS data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @return a new RewindableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
Audio::RewindableAudioStream *makeISSStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_ISS_H
|
||||
155
engines/stark/formats/tm.cpp
Normal file
155
engines/stark/formats/tm.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
/* 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/stark/formats/tm.h"
|
||||
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/gfx/texture.h"
|
||||
|
||||
#include "engines/stark/formats/biff.h"
|
||||
|
||||
#include "engines/stark/services/archiveloader.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
class TextureGroup : public BiffObject {
|
||||
public:
|
||||
static const uint32 TYPE = kTextureSetGroup;
|
||||
|
||||
TextureGroup() :
|
||||
BiffObject(),
|
||||
_palette(nullptr) {
|
||||
_type = TYPE;
|
||||
}
|
||||
|
||||
~TextureGroup() override {
|
||||
delete[] _palette;
|
||||
}
|
||||
|
||||
const byte *getPalette() {
|
||||
return _palette;
|
||||
}
|
||||
|
||||
// BiffObject API
|
||||
void readData(ArchiveReadStream *stream, uint32 dataLength) override {
|
||||
int entries = stream->readUint32LE();
|
||||
_palette = new byte[entries * 3];
|
||||
|
||||
byte *ptr = _palette;
|
||||
for (int i = 0; i < entries; ++i) {
|
||||
*ptr++ = (byte) stream->readUint16LE();
|
||||
*ptr++ = (byte) stream->readUint16LE();
|
||||
*ptr++ = (byte) stream->readUint16LE();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
byte *_palette;
|
||||
};
|
||||
|
||||
Texture::Texture() :
|
||||
BiffObject(),
|
||||
_texture(nullptr),
|
||||
_u(0) {
|
||||
_type = TYPE;
|
||||
}
|
||||
|
||||
Texture::~Texture() {
|
||||
_surface.free();
|
||||
delete _texture;
|
||||
}
|
||||
|
||||
void Texture::readData(ArchiveReadStream *stream, uint32 dataLength) {
|
||||
TextureGroup *textureGroup = static_cast<TextureGroup *>(_parent);
|
||||
|
||||
_name = stream->readString16();
|
||||
_u = stream->readByte();
|
||||
|
||||
uint32 w = stream->readUint32LE();
|
||||
uint32 h = stream->readUint32LE();
|
||||
uint32 levels = stream->readUint32LE();
|
||||
|
||||
_texture = StarkGfx->createTexture();
|
||||
_texture->setLevelCount(levels);
|
||||
|
||||
for (uint32 i = 0; i < levels; ++i) {
|
||||
// Read the pixel data to a surface
|
||||
Graphics::Surface level;
|
||||
Graphics::Surface *surface = i == 0 ? &_surface : &level;
|
||||
|
||||
surface->create(w, h, Graphics::PixelFormat::createFormatCLUT8());
|
||||
stream->read(surface->getPixels(), surface->w * surface->h);
|
||||
|
||||
// Add the mipmap level to the texture
|
||||
_texture->addLevel(i, surface, textureGroup->getPalette());
|
||||
|
||||
level.free();
|
||||
|
||||
w /= 2;
|
||||
h /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
Gfx::Texture *Texture::acquireTexturePointer() {
|
||||
Gfx::Texture *texture = _texture;
|
||||
_texture = nullptr;
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
Graphics::Surface *Texture::getSurface() const {
|
||||
TextureGroup *textureGroup = static_cast<TextureGroup *>(_parent);
|
||||
|
||||
return _surface.convertTo(Gfx::Driver::getRGBAPixelFormat(), textureGroup->getPalette());
|
||||
}
|
||||
|
||||
Gfx::TextureSet *TextureSetReader::read(ArchiveReadStream *stream) {
|
||||
BiffArchive archive = BiffArchive(stream, &biffObjectBuilder);
|
||||
|
||||
Common::Array<Texture *> textures = archive.listObjectsRecursive<Texture>();
|
||||
|
||||
Gfx::TextureSet *textureSet = new Gfx::TextureSet();
|
||||
for (uint i = 0; i < textures.size(); i++) {
|
||||
textureSet->addTexture(textures[i]->getName(), textures[i]->acquireTexturePointer());
|
||||
}
|
||||
|
||||
return textureSet;
|
||||
}
|
||||
|
||||
BiffArchive *TextureSetReader::readArchive(ArchiveReadStream *stream) {
|
||||
return new BiffArchive(stream, &biffObjectBuilder);
|
||||
}
|
||||
|
||||
BiffObject *TextureSetReader::biffObjectBuilder(uint32 type) {
|
||||
switch (type) {
|
||||
case kTextureSetGroup:
|
||||
return new TextureGroup();
|
||||
case kTextureSetTexture:
|
||||
return new Texture();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
107
engines/stark/formats/tm.h
Normal file
107
engines/stark/formats/tm.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/* 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 STARK_FORMATS_TM_H
|
||||
#define STARK_FORMATS_TM_H
|
||||
|
||||
#include "engines/stark/formats/biff.h"
|
||||
|
||||
#include "common/str.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class ArchiveReadStream;
|
||||
|
||||
namespace Gfx {
|
||||
class TextureSet;
|
||||
class Texture;
|
||||
}
|
||||
|
||||
namespace Formats {
|
||||
|
||||
/**
|
||||
* A texture set loader able to read '.tm' files
|
||||
*/
|
||||
class TextureSetReader {
|
||||
public:
|
||||
/**
|
||||
* Load a texture set from the provided stream.
|
||||
*
|
||||
* The caller is responsible for freeing the texture set.
|
||||
*/
|
||||
static Gfx::TextureSet *read(ArchiveReadStream *stream);
|
||||
|
||||
/** Read the texture set archive from the provided stream */
|
||||
static BiffArchive *readArchive(ArchiveReadStream *stream);
|
||||
|
||||
private:
|
||||
static BiffObject *biffObjectBuilder(uint32 type);
|
||||
|
||||
};
|
||||
|
||||
enum TextureSetType {
|
||||
kTextureSetGroup = 0x02faf082,
|
||||
kTextureSetTexture = 0x02faf080
|
||||
};
|
||||
|
||||
/**
|
||||
* A texture contained in a '.tm' texture set archive
|
||||
*
|
||||
* Textures have mipmaps.
|
||||
*/
|
||||
class Texture : public BiffObject {
|
||||
public:
|
||||
static const uint32 TYPE = kTextureSetTexture;
|
||||
|
||||
Texture();
|
||||
~Texture() override;
|
||||
|
||||
Common::String getName() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pointer to a texture ready for rendering
|
||||
*
|
||||
* The caller takes ownership of the texture.
|
||||
* This method can only be called successfully once
|
||||
* per texture. Subsequent calls return a null pointer.
|
||||
*/
|
||||
Gfx::Texture *acquireTexturePointer();
|
||||
|
||||
/** Return a RGBA copy of the pixel data */
|
||||
Graphics::Surface *getSurface() const;
|
||||
|
||||
// BiffObject API
|
||||
void readData(ArchiveReadStream *stream, uint32 dataLength) override;
|
||||
|
||||
private:
|
||||
Common::String _name;
|
||||
Gfx::Texture *_texture;
|
||||
Graphics::Surface _surface;
|
||||
byte _u;
|
||||
};
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_FORMATS_TM_H
|
||||
229
engines/stark/formats/xarc.cpp
Normal file
229
engines/stark/formats/xarc.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on the Xentax Wiki documentation:
|
||||
// https://web.archive.org/web/20090621212912/http://wiki.xentax.com/index.php?title=The_Longest_Journey_XARC
|
||||
|
||||
#include "engines/stark/formats/xarc.h"
|
||||
#include "engines/stark/debug.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/substream.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
// ARCHIVE MEMBER
|
||||
|
||||
class XARCMember : public Common::ArchiveMember {
|
||||
public:
|
||||
XARCMember(const XARCArchive *xarc, Common::ReadStream &stream, uint32 offset);
|
||||
|
||||
Common::SeekableReadStream *createReadStream() const override;
|
||||
Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override;
|
||||
Common::String getName() const override { return _name.baseName(); }
|
||||
Common::Path getPathInArchive() const override { return _name; }
|
||||
Common::String getFileName() const override { return _name.baseName(); }
|
||||
uint32 getLength() const { return _length; }
|
||||
uint32 getOffset() const { return _offset; }
|
||||
|
||||
private:
|
||||
const XARCArchive *_xarc;
|
||||
Common::Path _name;
|
||||
uint32 _offset;
|
||||
uint32 _length;
|
||||
|
||||
// Helper function
|
||||
Common::String readString(Common::ReadStream &stream);
|
||||
};
|
||||
|
||||
XARCMember::XARCMember(const XARCArchive *xarc, Common::ReadStream &stream, uint32 offset) {
|
||||
_xarc = xarc;
|
||||
|
||||
// Read the information about this archive member
|
||||
_name = Common::Path(readString(stream));
|
||||
_offset = offset;
|
||||
_length = stream.readUint32LE();
|
||||
debugC(20, kDebugArchive, "Stark::XARC Member: \"%s\" starts at offset=%d and has length=%d", _name.toString().c_str(), _offset, _length);
|
||||
|
||||
// Unknown value. English: 0, others: 1
|
||||
uint32 unknown = stream.readUint32LE();
|
||||
debugC(kDebugUnknown, "Stark::XARC Member: \"%s\" has unknown=%d", _name.toString().c_str(), unknown);
|
||||
if (unknown != 0 && unknown != 1) {
|
||||
warning("Stark::XARC Member: \"%s\" has unknown=%d with unknown meaning", _name.toString().c_str(), unknown);
|
||||
}
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *XARCMember::createReadStream() const {
|
||||
return _xarc->createReadStreamForMember(this);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *XARCMember::createReadStreamForAltStream(Common::AltStreamType altStreamType) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::String XARCMember::readString(Common::ReadStream &stream) {
|
||||
Common::String str;
|
||||
|
||||
// Read until we find a 0
|
||||
char c = 1;
|
||||
while (c != 0) {
|
||||
c = stream.readByte();
|
||||
if (stream.eos()) {
|
||||
c = 0;
|
||||
}
|
||||
if (c != 0) {
|
||||
str += c;
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
// ARCHIVE
|
||||
|
||||
bool XARCArchive::open(const Common::Path &filename) {
|
||||
Common::File stream;
|
||||
if (!stream.open(filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_filename = filename;
|
||||
|
||||
// Unknown: always 1? version?
|
||||
uint32 unknown = stream.readUint32LE();
|
||||
debugC(kDebugUnknown, "Stark::XARC: \"%s\" has unknown=%d", _filename.toString(Common::Path::kNativeSeparator).c_str(), unknown);
|
||||
if (unknown != 1) {
|
||||
warning("Stark::XARC: \"%s\" has unknown=%d with unknown meaning", _filename.toString(Common::Path::kNativeSeparator).c_str(), unknown);
|
||||
}
|
||||
|
||||
// Read the number of contained files
|
||||
uint32 numFiles = stream.readUint32LE();
|
||||
debugC(20, kDebugArchive, "Stark::XARC: \"%s\" contains %d files", _filename.toString(Common::Path::kNativeSeparator).c_str(), numFiles);
|
||||
|
||||
// Read the offset to the contents of the first file
|
||||
uint32 offset = stream.readUint32LE();
|
||||
debugC(20, kDebugArchive, "Stark::XARC: \"%s\"'s first file has offset=%d", _filename.toString(Common::Path::kNativeSeparator).c_str(), offset);
|
||||
|
||||
for (uint32 i = 0; i < numFiles; i++) {
|
||||
XARCMember *member = new XARCMember(this, stream, offset);
|
||||
_members.push_back(Common::ArchiveMemberPtr(member));
|
||||
|
||||
// Set the offset to the next member
|
||||
offset += member->getLength();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Path XARCArchive::getFilename() const {
|
||||
return _filename;
|
||||
}
|
||||
|
||||
bool XARCArchive::hasFile(const Common::Path &path) const {
|
||||
Common::String name = path.toString();
|
||||
for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) {
|
||||
if ((*it)->getName() == name) {
|
||||
// Found it
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found
|
||||
return false;
|
||||
}
|
||||
|
||||
int XARCArchive::listMatchingMembers(Common::ArchiveMemberList &list, const Common::Path &pattern, bool matchPathComponents) const {
|
||||
Common::String patternString = pattern.toString();
|
||||
int matches = 0;
|
||||
for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) {
|
||||
if ((*it)->getName().matchString(patternString)) {
|
||||
// This file matches, add it
|
||||
list.push_back(*it);
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
int XARCArchive::listMembers(Common::ArchiveMemberList &list) const {
|
||||
int files = 0;
|
||||
for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) {
|
||||
// Add all the members to the list
|
||||
list.push_back(*it);
|
||||
files++;
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
const Common::ArchiveMemberPtr XARCArchive::getMember(const Common::Path &path) const {
|
||||
Common::String name = path.toString();
|
||||
for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) {
|
||||
if ((*it)->getName() == name) {
|
||||
// Found it
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found, return an empty ptr
|
||||
return Common::ArchiveMemberPtr();
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *XARCArchive::createReadStreamForMember(const Common::Path &path) const {
|
||||
Common::String name = path.toString();
|
||||
for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) {
|
||||
if ((*it)->getName() == name) {
|
||||
// Found it
|
||||
return createReadStreamForMember((const XARCMember *)it->get());
|
||||
}
|
||||
}
|
||||
|
||||
// Not found
|
||||
return 0;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *XARCArchive::createReadStreamForMember(const XARCMember *member) const {
|
||||
// Open the xarc file
|
||||
Common::File *f = new Common::File;
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
if (!f->open(_filename)) {
|
||||
delete f;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return the substream that contains the archive member
|
||||
uint32 offset = member->getOffset();
|
||||
uint32 length = member->getLength();
|
||||
return new Common::SeekableSubReadStream(f, offset, offset + length, DisposeAfterUse::YES);
|
||||
|
||||
// Different approach: keep the archive open and read full resources to memory
|
||||
//f.seek(member->getOffset());
|
||||
//return f.readStream(member->getLength());
|
||||
}
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
55
engines/stark/formats/xarc.h
Normal file
55
engines/stark/formats/xarc.h
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_ARCHIVE_H
|
||||
#define STARK_ARCHIVE_H
|
||||
|
||||
#include "common/archive.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
class XARCMember;
|
||||
|
||||
class XARCArchive : public Common::Archive {
|
||||
public:
|
||||
bool open(const Common::Path &filename);
|
||||
Common::Path getFilename() const;
|
||||
|
||||
// Archive API
|
||||
bool hasFile(const Common::Path &path) const;
|
||||
int listMatchingMembers(Common::ArchiveMemberList &list, const Common::Path &pattern, bool matchPathComponents = false) const;
|
||||
int listMembers(Common::ArchiveMemberList &list) const;
|
||||
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const;
|
||||
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const;
|
||||
|
||||
Common::SeekableReadStream *createReadStreamForMember(const XARCMember *member) const;
|
||||
|
||||
private:
|
||||
Common::Path _filename;
|
||||
Common::ArchiveMemberList _members;
|
||||
};
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_ARCHIVE_H
|
||||
248
engines/stark/formats/xmg.cpp
Normal file
248
engines/stark/formats/xmg.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/* 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/stark/formats/xmg.h"
|
||||
#include "engines/stark/debug.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
|
||||
#include "graphics/conversion.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
XMGDecoder::XMGDecoder(Common::ReadStream *stream) :
|
||||
_width(0),
|
||||
_height(0),
|
||||
_currX(0),
|
||||
_currY(0),
|
||||
_stream(stream),
|
||||
_transColor(0) {
|
||||
}
|
||||
|
||||
Graphics::Surface *XMGDecoder::decode(Common::ReadStream *stream) {
|
||||
XMGDecoder dec(stream);
|
||||
dec.readHeader();
|
||||
return dec.decodeImage();
|
||||
}
|
||||
|
||||
void XMGDecoder::readSize(Common::ReadStream *stream, uint &width, uint &height) {
|
||||
XMGDecoder dec(stream);
|
||||
dec.readHeader();
|
||||
|
||||
width = dec._width;
|
||||
height = dec._height;
|
||||
}
|
||||
|
||||
void XMGDecoder::readHeader() {
|
||||
// Read the file version
|
||||
uint32 version = _stream->readUint32LE();
|
||||
if (version != 3) {
|
||||
error("Stark::XMG: File version unknown: %d", version);
|
||||
}
|
||||
|
||||
// Read the transparency color (RGBA)
|
||||
_transColor = _stream->readUint32LE();
|
||||
|
||||
// Read the image size
|
||||
_width = _stream->readUint32LE();
|
||||
_height = _stream->readUint32LE();
|
||||
debugC(10, kDebugXMG, "Stark::XMG: Version=%d, TransparencyColor=0x%08x, size=%dx%d", version, _transColor, _width, _height);
|
||||
|
||||
// Read the scan length
|
||||
uint32 scanLen = _stream->readUint32LE();
|
||||
if (scanLen != 3 * _width) {
|
||||
error("Stark::XMG: The scan length (%d) doesn't match the width bytes (%d)", scanLen, 3 * _width);
|
||||
}
|
||||
|
||||
// Unknown
|
||||
uint32 unknown2 = _stream->readUint32LE();
|
||||
debugC(kDebugUnknown, "Stark::XMG: unknown2 = %08x = %d", unknown2, unknown2);
|
||||
uint32 unknown3 = _stream->readUint32LE();
|
||||
debugC(kDebugUnknown, "Stark::XMG: unknown3 = %08x = %d", unknown3, unknown3);
|
||||
}
|
||||
|
||||
Graphics::Surface *XMGDecoder::decodeImage() {
|
||||
// Create the destination surface
|
||||
Graphics::Surface *surface = new Graphics::Surface();
|
||||
surface->create(_width, _height, Gfx::Driver::getRGBAPixelFormat());
|
||||
|
||||
_currX = 0, _currY = 0;
|
||||
while (!_stream->eos()) {
|
||||
if (_currX >= _width) {
|
||||
assert(_currX == _width);
|
||||
_currX = 0;
|
||||
_currY += 2;
|
||||
if (_currY >= _height)
|
||||
break;
|
||||
}
|
||||
|
||||
// Read the number and mode of the tiles
|
||||
byte op = _stream->readByte();
|
||||
uint16 count;
|
||||
if ((op & 0xC0) != 0xC0) {
|
||||
count = op & 0x3F;
|
||||
} else {
|
||||
count = ((op & 0xF) << 8) + _stream->readByte();
|
||||
op <<= 2;
|
||||
}
|
||||
op &= 0xC0;
|
||||
|
||||
// Process the current serie
|
||||
for (int i = 0; i < count; i++) {
|
||||
Block block = decodeBlock(op);
|
||||
drawBlock(block, surface);
|
||||
}
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
XMGDecoder::Block XMGDecoder::decodeBlock(byte op) {
|
||||
Block block;
|
||||
|
||||
switch (op) {
|
||||
case 0x00:
|
||||
// YCrCb
|
||||
block = processYCrCb();
|
||||
break;
|
||||
case 0x40:
|
||||
// Trans
|
||||
block = processTrans();
|
||||
break;
|
||||
case 0x80:
|
||||
// RGB
|
||||
block = processRGB();
|
||||
break;
|
||||
default:
|
||||
error("Unsupported color mode '%d'", op);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
void XMGDecoder::drawBlock(const Block &block, Graphics::Surface *surface) {
|
||||
uint32 *pixels = (uint32 *)surface->getBasePtr(_currX, _currY);
|
||||
|
||||
bool drawTwoColumns = _currX + 1 < _width;
|
||||
bool drawTwoLines = _currY + 1 < _height;
|
||||
|
||||
pixels[0] = TO_LE_32(block.a1);
|
||||
|
||||
if (drawTwoColumns) {
|
||||
pixels[1] = TO_LE_32(block.a2);
|
||||
}
|
||||
|
||||
if (drawTwoLines) {
|
||||
pixels[_width + 0] = TO_LE_32(block.b1);
|
||||
}
|
||||
|
||||
if (drawTwoColumns && drawTwoLines) {
|
||||
pixels[_width + 1] = TO_LE_32(block.b2);
|
||||
}
|
||||
|
||||
_currX += drawTwoColumns ? 2 : 1;
|
||||
}
|
||||
|
||||
XMGDecoder::Block XMGDecoder::processYCrCb() {
|
||||
byte y0, y1, y2, y3;
|
||||
byte cr, cb;
|
||||
|
||||
y0 = _stream->readByte();
|
||||
y1 = _stream->readByte();
|
||||
y2 = _stream->readByte();
|
||||
y3 = _stream->readByte();
|
||||
cr = _stream->readByte();
|
||||
cb = _stream->readByte();
|
||||
|
||||
byte r, g, b;
|
||||
Block block;
|
||||
|
||||
Graphics::YUV2RGB(y0, cb, cr, r, g, b);
|
||||
block.a1 = (255u << 24) + (b << 16) + (g << 8) + r;
|
||||
|
||||
Graphics::YUV2RGB(y1, cb, cr, r, g, b);
|
||||
block.a2 = (255u << 24) + (b << 16) + (g << 8) + r;
|
||||
|
||||
Graphics::YUV2RGB(y2, cb, cr, r, g, b);
|
||||
block.b1 = (255u << 24) + (b << 16) + (g << 8) + r;
|
||||
|
||||
Graphics::YUV2RGB(y3, cb, cr, r, g, b);
|
||||
block.b2 = (255u << 24) + (b << 16) + (g << 8) + r;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
XMGDecoder::Block XMGDecoder::processTrans() {
|
||||
Block block;
|
||||
|
||||
block.a1 = 0;
|
||||
block.a2 = 0;
|
||||
block.b1 = 0;
|
||||
block.b2 = 0;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
XMGDecoder::Block XMGDecoder::processRGB() {
|
||||
Block block;
|
||||
uint32 color;
|
||||
|
||||
color = _stream->readUint16LE();
|
||||
color += _stream->readByte() << 16;
|
||||
if (color != _transColor)
|
||||
color += 255u << 24;
|
||||
else
|
||||
color = 0;
|
||||
block.a1 = color;
|
||||
|
||||
color = _stream->readUint16LE();
|
||||
color += _stream->readByte() << 16;
|
||||
if (color != _transColor)
|
||||
color += 255u << 24;
|
||||
else
|
||||
color = 0;
|
||||
block.a2 = color;
|
||||
|
||||
color = _stream->readUint16LE();
|
||||
color += _stream->readByte() << 16;
|
||||
if (color != _transColor)
|
||||
color += 255u << 24;
|
||||
else
|
||||
color = 0;
|
||||
block.b1 = color;
|
||||
|
||||
color = _stream->readUint16LE();
|
||||
color += _stream->readByte() << 16;
|
||||
if (color != _transColor)
|
||||
color += 255u << 24;
|
||||
else
|
||||
color = 0;
|
||||
block.b2 = color;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
78
engines/stark/formats/xmg.h
Normal file
78
engines/stark/formats/xmg.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/* 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 STARK_XMG_H
|
||||
#define STARK_XMG_H
|
||||
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
/**
|
||||
* XMG (still image) decoder
|
||||
*/
|
||||
class XMGDecoder {
|
||||
public:
|
||||
static Graphics::Surface *decode(Common::ReadStream *stream);
|
||||
static void readSize(Common::ReadStream *stream, uint &width, uint &height);
|
||||
|
||||
private:
|
||||
explicit XMGDecoder(Common::ReadStream *stream);
|
||||
|
||||
struct Block {
|
||||
uint32 a1, a2;
|
||||
uint32 b1, b2;
|
||||
};
|
||||
|
||||
void readHeader();
|
||||
Graphics::Surface *decodeImage();
|
||||
Block decodeBlock(byte op);
|
||||
void drawBlock(const Block &block, Graphics::Surface *surface);
|
||||
|
||||
Block processYCrCb();
|
||||
Block processTrans();
|
||||
Block processRGB();
|
||||
|
||||
uint32 _width;
|
||||
uint32 _height;
|
||||
|
||||
uint32 _currX;
|
||||
uint32 _currY;
|
||||
|
||||
Common::ReadStream *_stream;
|
||||
|
||||
/**
|
||||
* The transparency color in the RGB and transparency blocks.
|
||||
* In the output surface, the transparent color is black with zero
|
||||
* alpha. So the images are effectively pre-multiplied alpha.
|
||||
*/
|
||||
uint32 _transColor;
|
||||
};
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_XMG_H
|
||||
333
engines/stark/formats/xrc.cpp
Normal file
333
engines/stark/formats/xrc.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
/* 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/stark/formats/xrc.h"
|
||||
|
||||
#include "engines/stark/formats/xarc.h"
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/animhierarchy.h"
|
||||
#include "engines/stark/resources/animscript.h"
|
||||
#include "engines/stark/resources/animsoundtrigger.h"
|
||||
#include "engines/stark/resources/bonesmesh.h"
|
||||
#include "engines/stark/resources/bookmark.h"
|
||||
#include "engines/stark/resources/camera.h"
|
||||
#include "engines/stark/resources/container.h"
|
||||
#include "engines/stark/resources/command.h"
|
||||
#include "engines/stark/resources/dialog.h"
|
||||
#include "engines/stark/resources/direction.h"
|
||||
#include "engines/stark/resources/fmv.h"
|
||||
#include "engines/stark/resources/image.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/floor.h"
|
||||
#include "engines/stark/resources/floorface.h"
|
||||
#include "engines/stark/resources/floorfield.h"
|
||||
#include "engines/stark/resources/knowledge.h"
|
||||
#include "engines/stark/resources/knowledgeset.h"
|
||||
#include "engines/stark/resources/layer.h"
|
||||
#include "engines/stark/resources/level.h"
|
||||
#include "engines/stark/resources/light.h"
|
||||
#include "engines/stark/resources/lipsync.h"
|
||||
#include "engines/stark/resources/location.h"
|
||||
#include "engines/stark/resources/path.h"
|
||||
#include "engines/stark/resources/pattable.h"
|
||||
#include "engines/stark/resources/root.h"
|
||||
#include "engines/stark/resources/script.h"
|
||||
#include "engines/stark/resources/scroll.h"
|
||||
#include "engines/stark/resources/speech.h"
|
||||
#include "engines/stark/resources/sound.h"
|
||||
#include "engines/stark/resources/string.h"
|
||||
#include "engines/stark/resources/textureset.h"
|
||||
#include "engines/stark/resourcereference.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
XRCReadStream::XRCReadStream(const Common::Path &archiveName,
|
||||
Common::SeekableReadStream *parentStream, DisposeAfterUse::Flag disposeParentStream) :
|
||||
SeekableSubReadStream(parentStream, 0, parentStream->size(), disposeParentStream),
|
||||
_archiveName(archiveName) {
|
||||
}
|
||||
|
||||
XRCReadStream::~XRCReadStream() {
|
||||
}
|
||||
|
||||
Common::String XRCReadStream::readString() {
|
||||
// Read the string length
|
||||
uint16 length = readUint16LE();
|
||||
|
||||
// Read the string
|
||||
char *data = new char[length];
|
||||
read(data, length);
|
||||
Common::String string(data, length);
|
||||
delete[] data;
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
Resources::Type XRCReadStream::readResourceType() {
|
||||
byte rawType;
|
||||
rawType = readByte();
|
||||
return Resources::Type((Resources::Type::ResourceType) (rawType));
|
||||
}
|
||||
|
||||
ResourceReference XRCReadStream::readResourceReference() {
|
||||
ResourceReference reference;
|
||||
reference.loadFromStream(this);
|
||||
|
||||
return reference;
|
||||
}
|
||||
|
||||
Math::Vector3d XRCReadStream::readVector3() {
|
||||
Math::Vector3d v;
|
||||
v.readFromStream(this);
|
||||
return v;
|
||||
}
|
||||
|
||||
Common::Rect XRCReadStream::readRect() {
|
||||
Common::Rect r;
|
||||
r.left = readSint32LE();
|
||||
r.top = readSint32LE();
|
||||
r.right = readSint32LE();
|
||||
r.bottom = readSint32LE();
|
||||
return r;
|
||||
}
|
||||
|
||||
Common::Point XRCReadStream::readPoint() {
|
||||
uint32 x = readUint32LE();
|
||||
uint32 y = readUint32LE();
|
||||
|
||||
return Common::Point(x, y);
|
||||
}
|
||||
|
||||
bool XRCReadStream::readBool() {
|
||||
uint32 b = readUint32LE();
|
||||
return b != 0;
|
||||
}
|
||||
|
||||
bool XRCReadStream::isDataLeft() {
|
||||
return pos() < size();
|
||||
}
|
||||
|
||||
Common::Path XRCReadStream::getArchiveName() const {
|
||||
return _archiveName;
|
||||
}
|
||||
|
||||
Resources::Object *XRCReader::importTree(XARCArchive *archive) {
|
||||
// Find the XRC file
|
||||
Common::ArchiveMemberList members;
|
||||
archive->listMatchingMembers(members, "*.xrc");
|
||||
if (members.size() == 0) {
|
||||
error("No resource tree in archive '%s'", archive->getFilename().toString(Common::Path::kNativeSeparator).c_str());
|
||||
}
|
||||
if (members.size() > 1) {
|
||||
error("Too many resource scripts in archive '%s'", archive->getFilename().toString(Common::Path::kNativeSeparator).c_str());
|
||||
}
|
||||
|
||||
// Open the XRC file
|
||||
Common::SeekableReadStream *stream = archive->createReadStreamForMember(members.front()->getPathInArchive());
|
||||
XRCReadStream *xrcStream = new XRCReadStream(archive->getFilename(), stream);
|
||||
|
||||
// Import the resource tree
|
||||
Resources::Object *root = importResource(xrcStream, nullptr);
|
||||
|
||||
delete xrcStream;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
Resources::Object *XRCReader::importResource(XRCReadStream *stream, Resources::Object *parent) {
|
||||
Resources::Object *resource = createResource(stream, parent);
|
||||
importResourceData(stream, resource);
|
||||
importResourceChildren(stream, resource);
|
||||
|
||||
// Resource lifecycle update
|
||||
resource->onPostRead();
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
Resources::Object *XRCReader::createResource(XRCReadStream *stream, Resources::Object *parent) {
|
||||
// Read the resource type and subtype
|
||||
Resources::Type type = stream->readResourceType();
|
||||
byte subType = stream->readByte();
|
||||
|
||||
// Read the resource properties
|
||||
uint16 index = stream->readUint16LE();
|
||||
Common::String name = stream->readString();
|
||||
|
||||
// Create a new resource
|
||||
Resources::Object *resource;
|
||||
switch (type.get()) {
|
||||
case Resources::Type::kRoot:
|
||||
resource = new Resources::Root(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kLevel:
|
||||
resource = new Resources::Level(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kLocation:
|
||||
resource = new Resources::Location(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kLayer:
|
||||
resource = Resources::Layer::construct(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kCamera:
|
||||
resource = new Resources::Camera(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kFloor:
|
||||
resource = new Resources::Floor(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kFloorFace:
|
||||
resource = new Resources::FloorFace(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kItem:
|
||||
resource = Resources::Item::construct(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kScript:
|
||||
resource = new Resources::Script(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kAnimHierarchy:
|
||||
resource = new Resources::AnimHierarchy(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kAnim:
|
||||
resource = Resources::Anim::construct(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kDirection:
|
||||
resource = new Resources::Direction(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kImage:
|
||||
resource = Resources::Image::construct(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kAnimScript:
|
||||
resource = new Resources::AnimScript(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kAnimScriptItem:
|
||||
resource = new Resources::AnimScriptItem(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kSoundItem:
|
||||
resource = new Resources::Sound(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kPath:
|
||||
resource = Resources::Path::construct(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kFloorField:
|
||||
resource = new Resources::FloorField(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kBookmark:
|
||||
resource = new Resources::Bookmark(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kKnowledgeSet:
|
||||
resource = new Resources::KnowledgeSet(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kKnowledge:
|
||||
resource = new Resources::Knowledge(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kCommand:
|
||||
resource = new Resources::Command(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kPATTable:
|
||||
resource = new Resources::PATTable(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kContainer:
|
||||
resource = new Resources::Container(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kDialog:
|
||||
resource = new Resources::Dialog(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kSpeech:
|
||||
resource = new Resources::Speech(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kLight:
|
||||
resource = new Resources::Light(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kBonesMesh:
|
||||
resource = new Resources::BonesMesh(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kScroll:
|
||||
resource = new Resources::Scroll(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kFMV:
|
||||
resource = new Resources::FMV(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kLipSync:
|
||||
resource = new Resources::LipSync(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kAnimSoundTrigger:
|
||||
resource = new Resources::AnimSoundTrigger(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kString:
|
||||
resource = new Resources::String(parent, subType, index, name);
|
||||
break;
|
||||
case Resources::Type::kTextureSet:
|
||||
resource = new Resources::TextureSet(parent, subType, index, name);
|
||||
break;
|
||||
default:
|
||||
resource = new Resources::UnimplementedResource(parent, type, subType, index, name);
|
||||
break;
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
void XRCReader::importResourceData(XRCReadStream *stream, Resources::Object *resource) {
|
||||
// Read the data length
|
||||
uint32 dataLength = stream->readUint32LE();
|
||||
|
||||
// Read the resource type specific data using a memory stream
|
||||
if (dataLength > 0) {
|
||||
XRCReadStream *xrcDataStream = new XRCReadStream(stream->getArchiveName(), stream->readStream(dataLength));
|
||||
|
||||
resource->readData(xrcDataStream);
|
||||
|
||||
if (xrcDataStream->isDataLeft()) {
|
||||
warning("Not all XRC data was read. Type %s, subtype %d, name %s",
|
||||
resource->getType().getName(), resource->getSubType(), resource->getName().c_str());
|
||||
}
|
||||
|
||||
if (xrcDataStream->eos()) {
|
||||
warning("Too much XRC data was read. Type %s, subtype %d, name %s",
|
||||
resource->getType().getName(), resource->getSubType(), resource->getName().c_str());
|
||||
}
|
||||
|
||||
delete xrcDataStream;
|
||||
}
|
||||
}
|
||||
|
||||
void XRCReader::importResourceChildren(XRCReadStream *stream, Resources::Object *resource) {
|
||||
// Get the number of children
|
||||
uint16 numChildren = stream->readUint16LE();
|
||||
|
||||
// Read more unknown data
|
||||
uint16 unknown3 = stream->readUint16LE();
|
||||
if (unknown3 != 0) {
|
||||
warning("Stark::XRCReader: \"%s\" has unknown3=0x%04X with unknown meaning", resource->getName().c_str(), unknown3);
|
||||
}
|
||||
|
||||
// Read the children resources
|
||||
for (int i = 0; i < numChildren; i++) {
|
||||
Resources::Object *child = importResource(stream, resource);
|
||||
|
||||
// Add child to parent
|
||||
resource->addChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
86
engines/stark/formats/xrc.h
Normal file
86
engines/stark/formats/xrc.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/* 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 STARK_XRC_READER_H
|
||||
#define STARK_XRC_READER_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str.h"
|
||||
#include "common/substream.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#include "math/vector3d.h"
|
||||
#include "math/vector4d.h"
|
||||
|
||||
#include "engines/stark/resources/object.h"
|
||||
#include "engines/stark/resourcereference.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
class XARCArchive;
|
||||
|
||||
/**
|
||||
* A read stream with helper functions to read usual XRC data types
|
||||
*/
|
||||
class XRCReadStream : public Common::SeekableSubReadStream {
|
||||
public:
|
||||
XRCReadStream(const Common::Path &archiveName, Common::SeekableReadStream *parentStream, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::YES);
|
||||
virtual ~XRCReadStream();
|
||||
|
||||
/** Obtain the file name of the archive containing the XRC tree */
|
||||
Common::Path getArchiveName() const;
|
||||
|
||||
Common::String readString();
|
||||
Resources::Type readResourceType();
|
||||
ResourceReference readResourceReference();
|
||||
Math::Vector3d readVector3();
|
||||
Common::Rect readRect();
|
||||
Common::Point readPoint();
|
||||
bool readBool();
|
||||
bool isDataLeft();
|
||||
|
||||
private:
|
||||
Common::Path _archiveName;
|
||||
};
|
||||
|
||||
/**
|
||||
* An XRC stream parser, used to build resource trees.
|
||||
*/
|
||||
class XRCReader {
|
||||
public:
|
||||
/**
|
||||
* Build a resource tree from a stream
|
||||
*/
|
||||
static Resources::Object *importTree(XARCArchive *archive);
|
||||
|
||||
protected:
|
||||
static Resources::Object *importResource(XRCReadStream *stream, Resources::Object *parent);
|
||||
static Resources::Object *createResource(XRCReadStream *stream, Resources::Object *parent);
|
||||
static void importResourceChildren(XRCReadStream *stream, Resources::Object *resource);
|
||||
static void importResourceData(XRCReadStream* stream, Resources::Object* resource);
|
||||
};
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_XRC_READER_H
|
||||
74
engines/stark/gfx/bitmap.h
Normal file
74
engines/stark/gfx/bitmap.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* 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 STARK_GFX_BITMAP_H
|
||||
#define STARK_GFX_BITMAP_H
|
||||
|
||||
#include "common/hash-str.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct PixelFormat;
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
/**
|
||||
* An abstract bitmap
|
||||
*/
|
||||
class Bitmap {
|
||||
public:
|
||||
Bitmap() : _width(0), _height(0) {}
|
||||
virtual ~Bitmap() {}
|
||||
|
||||
enum SamplingFilter {
|
||||
kNearest,
|
||||
kLinear
|
||||
};
|
||||
|
||||
/** Make the texture active */
|
||||
virtual void bind() const = 0;
|
||||
|
||||
/** Define or update the texture pixel data */
|
||||
virtual void update(const Graphics::Surface *surface, const byte *palette = nullptr) = 0;
|
||||
|
||||
/** Set the filter used when sampling the texture */
|
||||
virtual void setSamplingFilter(SamplingFilter filter) = 0;
|
||||
|
||||
/** Get the most ideal pixel format for uploading to a texture */
|
||||
virtual Graphics::PixelFormat getBestPixelFormat() const = 0;
|
||||
|
||||
/** Get the texture width */
|
||||
uint32 width() const { return _width; }
|
||||
|
||||
/** Get the texture height */
|
||||
uint32 height() const { return _height; }
|
||||
|
||||
protected:
|
||||
uint32 _width;
|
||||
uint32 _height;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_BITMAP_H
|
||||
50
engines/stark/gfx/color.h
Normal file
50
engines/stark/gfx/color.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* 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 STARK_GFX_COLOR_H
|
||||
#define STARK_GFX_COLOR_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
struct Color {
|
||||
uint8 r;
|
||||
uint8 g;
|
||||
uint8 b;
|
||||
uint8 a;
|
||||
|
||||
Color(uint8 red, uint8 green, uint8 blue, uint8 alpha = 0xFF) :
|
||||
r(red), g(green), b(blue), a(alpha) {}
|
||||
|
||||
bool operator==(const Color &color) const {
|
||||
return r == color.r &&
|
||||
g == color.g &&
|
||||
b == color.b &&
|
||||
a == color.a;
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_COLOR_H
|
||||
168
engines/stark/gfx/driver.cpp
Normal file
168
engines/stark/gfx/driver.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/stark/gfx/driver.h"
|
||||
#include "engines/stark/gfx/opengl.h"
|
||||
#include "engines/stark/gfx/opengls.h"
|
||||
#include "engines/stark/gfx/tinygl.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/settings.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include "graphics/renderer.h"
|
||||
#include "graphics/surface.h"
|
||||
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
#include "graphics/opengl/context.h"
|
||||
#endif
|
||||
|
||||
#include "gui/error.h"
|
||||
|
||||
#include "engines/util.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
Driver *Driver::create() {
|
||||
Common::String rendererConfig = ConfMan.get("renderer");
|
||||
Graphics::RendererType desiredRendererType = Graphics::Renderer::parseTypeCode(rendererConfig);
|
||||
Graphics::RendererType matchingRendererType = Graphics::Renderer::getBestMatchingAvailableType(desiredRendererType,
|
||||
#if defined(USE_OPENGL_GAME)
|
||||
Graphics::kRendererTypeOpenGL |
|
||||
#endif
|
||||
#if defined(USE_OPENGL_SHADERS)
|
||||
Graphics::kRendererTypeOpenGLShaders |
|
||||
#endif
|
||||
#if defined(USE_TINYGL)
|
||||
Graphics::kRendererTypeTinyGL |
|
||||
#endif
|
||||
0);
|
||||
|
||||
bool softRenderer = matchingRendererType == Graphics::kRendererTypeTinyGL;
|
||||
if (!softRenderer) {
|
||||
initGraphics3d(kOriginalWidth, kOriginalHeight);
|
||||
} else {
|
||||
initGraphics(kOriginalWidth, kOriginalHeight, nullptr);
|
||||
}
|
||||
|
||||
#if defined(USE_OPENGL_SHADERS)
|
||||
if (matchingRendererType == Graphics::kRendererTypeOpenGLShaders) {
|
||||
return new OpenGLSDriver();
|
||||
}
|
||||
#endif
|
||||
#if defined(USE_OPENGL_GAME)
|
||||
if (matchingRendererType == Graphics::kRendererTypeOpenGL) {
|
||||
return new OpenGLDriver();
|
||||
}
|
||||
#endif
|
||||
#if defined(USE_TINYGL)
|
||||
if (matchingRendererType == Graphics::kRendererTypeTinyGL) {
|
||||
return new TinyGLDriver();
|
||||
}
|
||||
#endif
|
||||
/* We should never end up here, getBestMatchingRendererType would have failed before */
|
||||
error("Unable to create a renderer");
|
||||
}
|
||||
|
||||
const Graphics::PixelFormat Driver::getRGBAPixelFormat() {
|
||||
return Graphics::PixelFormat::createFormatRGBA32();
|
||||
}
|
||||
|
||||
bool Driver::computeScreenViewport() {
|
||||
int32 screenWidth = g_system->getWidth();
|
||||
int32 screenHeight = g_system->getHeight();
|
||||
|
||||
Common::Rect viewport;
|
||||
if (g_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection)) {
|
||||
// Aspect ratio correction
|
||||
int32 viewportWidth = MIN<int32>(screenWidth, screenHeight * kOriginalWidth / kOriginalHeight);
|
||||
int32 viewportHeight = MIN<int32>(screenHeight, screenWidth * kOriginalHeight / kOriginalWidth);
|
||||
viewport = Common::Rect(viewportWidth, viewportHeight);
|
||||
|
||||
// Pillarboxing
|
||||
viewport.translate((screenWidth - viewportWidth) / 2,
|
||||
(screenHeight - viewportHeight) / 2);
|
||||
} else {
|
||||
// Aspect ratio correction disabled, just stretch
|
||||
viewport = Common::Rect(screenWidth, screenHeight);
|
||||
}
|
||||
|
||||
if (viewport == _screenViewport) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_screenViewport = viewport;
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Rect Driver::gameViewport() const {
|
||||
Common::Rect game = Common::Rect(_screenViewport.width(), _screenViewport.height() * kGameViewportHeight / kOriginalHeight);
|
||||
game.translate(_screenViewport.left, _screenViewport.top + _screenViewport.height() * kTopBorderHeight / kOriginalHeight);
|
||||
|
||||
return game;
|
||||
}
|
||||
|
||||
Common::Point Driver::convertCoordinateCurrentToOriginal(const Common::Point &point) const {
|
||||
// Most of the engine expects 640x480 coordinates
|
||||
Common::Point scaledPosition = point;
|
||||
scaledPosition.x -= _screenViewport.left;
|
||||
scaledPosition.y -= _screenViewport.top;
|
||||
scaledPosition.x = CLIP<int16>(scaledPosition.x, 0, _screenViewport.width());
|
||||
scaledPosition.y = CLIP<int16>(scaledPosition.y, 0, _screenViewport.height());
|
||||
scaledPosition.x *= kOriginalWidth / (float)_screenViewport.width();
|
||||
scaledPosition.y *= kOriginalHeight / (float)_screenViewport.height();
|
||||
|
||||
return scaledPosition;
|
||||
}
|
||||
|
||||
uint Driver::scaleWidthOriginalToCurrent(uint width) const {
|
||||
return _screenViewport.width() * width / kOriginalWidth;
|
||||
}
|
||||
|
||||
uint Driver::scaleHeightOriginalToCurrent(uint height) const {
|
||||
return _screenViewport.height() * height / kOriginalHeight;
|
||||
}
|
||||
|
||||
uint Driver::scaleWidthCurrentToOriginal(uint width) const {
|
||||
return kOriginalWidth * width / _screenViewport.width();
|
||||
}
|
||||
|
||||
uint Driver::scaleHeightCurrentToOriginal(uint height) const {
|
||||
return kOriginalHeight * height / _screenViewport.height();
|
||||
}
|
||||
|
||||
void Driver::flipVertical(Graphics::Surface *s) {
|
||||
for (int y = 0; y < s->h / 2; ++y) {
|
||||
// Flip the lines
|
||||
byte *line1P = (byte *)s->getBasePtr(0, y);
|
||||
byte *line2P = (byte *)s->getBasePtr(0, s->h - y - 1);
|
||||
|
||||
for (int x = 0; x < s->pitch; ++x)
|
||||
SWAP(line1P[x], line2P[x]);
|
||||
}
|
||||
}
|
||||
|
||||
bool Driver::isPosInScreenBounds(const Common::Point &point) const {
|
||||
return _screenViewport.contains(point);
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
165
engines/stark/gfx/driver.h
Normal file
165
engines/stark/gfx/driver.h
Normal file
@@ -0,0 +1,165 @@
|
||||
/* 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 STARK_GFX_DRIVER_H
|
||||
#define STARK_GFX_DRIVER_H
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class VisualActor;
|
||||
class VisualProp;
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
class SurfaceRenderer;
|
||||
class FadeRenderer;
|
||||
class Bitmap;
|
||||
class Texture;
|
||||
|
||||
class Driver {
|
||||
public:
|
||||
static Driver *create();
|
||||
|
||||
virtual ~Driver() {}
|
||||
|
||||
virtual void init() = 0;
|
||||
|
||||
bool computeScreenViewport();
|
||||
virtual void setScreenViewport(bool noScaling) = 0; // deprecated
|
||||
|
||||
virtual void setViewport(const Common::Rect &rect) = 0;
|
||||
|
||||
/** Get the screen viewport in actual resolution */
|
||||
Common::Rect getScreenViewport() { return _screenViewport; }
|
||||
|
||||
Common::Rect gameViewport() const;
|
||||
|
||||
virtual void clearScreen() = 0;
|
||||
virtual void flipBuffer() = 0;
|
||||
|
||||
/**
|
||||
* Create a new texture for 3D
|
||||
*
|
||||
* The caller is responsible for freeing it.
|
||||
*
|
||||
*/
|
||||
virtual Texture *createTexture() = 0;
|
||||
|
||||
/**
|
||||
* Create a new bitmap for 2D
|
||||
*
|
||||
* The caller is responsible for freeing it.
|
||||
*
|
||||
*/
|
||||
virtual Bitmap *createBitmap(const Graphics::Surface *surface = nullptr, const byte *palette = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* Create a new actor renderer
|
||||
*
|
||||
* The caller is responsible for freeing it.
|
||||
*/
|
||||
virtual VisualActor *createActorRenderer() = 0;
|
||||
|
||||
/**
|
||||
* Create a new prop renderer
|
||||
*
|
||||
* The caller is responsible for freeing it.
|
||||
*/
|
||||
virtual VisualProp *createPropRenderer() = 0;
|
||||
|
||||
/**
|
||||
* Create a new surface renderer
|
||||
*
|
||||
* The caller is responsible for freeing it.
|
||||
*/
|
||||
virtual SurfaceRenderer *createSurfaceRenderer() = 0;
|
||||
|
||||
/**
|
||||
* Create a new fade renderer
|
||||
*
|
||||
* The caller is responsible for freeing it.
|
||||
*/
|
||||
virtual FadeRenderer *createFadeRenderer() = 0;
|
||||
|
||||
/** Checks if a screenpoint coord is within window bounds */
|
||||
bool isPosInScreenBounds(const Common::Point &point) const;
|
||||
|
||||
/** Convert a coordinate from current to original resolution */
|
||||
Common::Point convertCoordinateCurrentToOriginal(const Common::Point &point) const;
|
||||
|
||||
/** Scale a width value from original resolution to current resolution */
|
||||
uint scaleWidthOriginalToCurrent(uint width) const;
|
||||
|
||||
/** Scale a height value from original resolution to current resolution */
|
||||
uint scaleHeightOriginalToCurrent(uint height) const;
|
||||
|
||||
/** Scale a width value from current resolution to original resolution */
|
||||
uint scaleWidthCurrentToOriginal(uint width) const;
|
||||
|
||||
/** Scale a height value from current resolution to original resolution */
|
||||
uint scaleHeightCurrentToOriginal(uint width) const;
|
||||
|
||||
/**
|
||||
* Textures are expected to be in the RGBA byte order
|
||||
*
|
||||
* That is to say bitmaps sent to OpenGL need to have the following layout:
|
||||
* R G B A R G B A, ...
|
||||
*
|
||||
* This method can be used to retrieve what that means in terms
|
||||
* of pixel format according to the current platform's endianness.
|
||||
*/
|
||||
static const Graphics::PixelFormat getRGBAPixelFormat();
|
||||
|
||||
/** Grab a screenshot of the currently active viewport as defined by setViewport */
|
||||
virtual Graphics::Surface *getViewportScreenshot() const = 0;
|
||||
|
||||
virtual void set3DMode() = 0;
|
||||
virtual bool computeLightsEnabled() = 0;
|
||||
|
||||
virtual bool supportsModdedAssets() const { return true; }
|
||||
|
||||
static const int32 kOriginalWidth = 640;
|
||||
static const int32 kOriginalHeight = 480;
|
||||
|
||||
static const int32 kTopBorderHeight = 36;
|
||||
static const int32 kGameViewportHeight = 365;
|
||||
static const int32 kBottomBorderHeight = 79;
|
||||
|
||||
static const int32 kGameViewportWidth = 640;
|
||||
|
||||
protected:
|
||||
static void flipVertical(Graphics::Surface *s);
|
||||
|
||||
Common::Rect _screenViewport;
|
||||
bool _computeLights;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_DRIVER_H
|
||||
45
engines/stark/gfx/faderenderer.h
Normal file
45
engines/stark/gfx/faderenderer.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* 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 STARK_GFX_FADE_RENDERER_H
|
||||
#define STARK_GFX_FADE_RENDERER_H
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
/**
|
||||
* A renderer to draw fade screen to the current viewport
|
||||
*/
|
||||
class FadeRenderer {
|
||||
public:
|
||||
virtual ~FadeRenderer() { }
|
||||
|
||||
/**
|
||||
* Draw the fade screen at the provided fade level
|
||||
*/
|
||||
virtual void render(float fadeLevel) = 0;
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_FADE_RENDERER_H
|
||||
287
engines/stark/gfx/opengl.cpp
Normal file
287
engines/stark/gfx/opengl.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
/* 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/stark/gfx/opengl.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#include "math/matrix4.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME)
|
||||
|
||||
#include "engines/stark/gfx/openglactor.h"
|
||||
#include "engines/stark/gfx/openglbitmap.h"
|
||||
#include "engines/stark/gfx/openglprop.h"
|
||||
#include "engines/stark/gfx/openglsurface.h"
|
||||
#include "engines/stark/gfx/openglfade.h"
|
||||
#include "engines/stark/gfx/opengltexture.h"
|
||||
#include "engines/stark/scene.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
OpenGLDriver::OpenGLDriver() {
|
||||
_computeLights = true;
|
||||
}
|
||||
|
||||
OpenGLDriver::~OpenGLDriver() {
|
||||
}
|
||||
|
||||
void OpenGLDriver::init() {
|
||||
computeScreenViewport();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glDisable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
void OpenGLDriver::setScreenViewport(bool noScaling) {
|
||||
if (noScaling) {
|
||||
_viewport = Common::Rect(g_system->getWidth(), g_system->getHeight());
|
||||
_unscaledViewport = _viewport;
|
||||
} else {
|
||||
_viewport = _screenViewport;
|
||||
_unscaledViewport = Common::Rect(kOriginalWidth, kOriginalHeight);
|
||||
}
|
||||
|
||||
glViewport(_viewport.left, _viewport.top, _viewport.width(), _viewport.height());
|
||||
}
|
||||
|
||||
void OpenGLDriver::setViewport(const Common::Rect &rect) {
|
||||
_viewport = Common::Rect(
|
||||
_screenViewport.width() * rect.width() / kOriginalWidth,
|
||||
_screenViewport.height() * rect.height() / kOriginalHeight
|
||||
);
|
||||
|
||||
_viewport.translate(
|
||||
_screenViewport.left + _screenViewport.width() * rect.left / kOriginalWidth,
|
||||
_screenViewport.top + _screenViewport.height() * rect.top / kOriginalHeight
|
||||
);
|
||||
|
||||
_unscaledViewport = rect;
|
||||
|
||||
glViewport(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height());
|
||||
}
|
||||
|
||||
void OpenGLDriver::clearScreen() {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void OpenGLDriver::flipBuffer() {
|
||||
g_system->updateScreen();
|
||||
}
|
||||
|
||||
void OpenGLDriver::setupLights(const LightEntryArray &lights) {
|
||||
static const uint maxLights = 10;
|
||||
|
||||
assert(lights.size() >= 1);
|
||||
assert(lights.size() <= maxLights);
|
||||
|
||||
const LightEntry *ambient = lights[0];
|
||||
assert(ambient->type == LightEntry::kAmbient); // The first light must be the ambient light
|
||||
|
||||
Math::Matrix4 viewMatrix = StarkScene->getViewMatrix();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
for (uint i = 0; i < lights.size(); i++) {
|
||||
const LightEntry *l = lights[i];
|
||||
GLfloat ambientColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
GLfloat lightColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
GLfloat lightPos[] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
GLfloat lightDir[] = { 0.0f, 0.0f, -1.0f };
|
||||
GLfloat cutoff = 180.0f;
|
||||
GLfloat spotExp = 0.0f;
|
||||
GLfloat c_attenuation = 1.0f;
|
||||
GLfloat l_attenuation = 0.0f;
|
||||
GLfloat q_attenuation = 0.0f;
|
||||
|
||||
Math::Vector4d worldPosition;
|
||||
worldPosition.x() = l->position.x();
|
||||
worldPosition.y() = l->position.y();
|
||||
worldPosition.z() = l->position.z();
|
||||
worldPosition.w() = 1.0;
|
||||
|
||||
Math::Vector4d eyePosition = viewMatrix * worldPosition;
|
||||
|
||||
Math::Vector3d worldDirection = l->direction;
|
||||
Math::Vector3d eyeDirection = viewMatrix.getRotation() * worldDirection;
|
||||
eyeDirection.normalize();
|
||||
|
||||
switch (l->type) {
|
||||
case LightEntry::kPoint:
|
||||
lightColor[0] = (GLfloat)l->color.x();
|
||||
lightColor[1] = (GLfloat)l->color.y();
|
||||
lightColor[2] = (GLfloat)l->color.z();
|
||||
lightPos[0] = (GLfloat)eyePosition.x();
|
||||
lightPos[1] = (GLfloat)eyePosition.y();
|
||||
lightPos[2] = (GLfloat)eyePosition.z();
|
||||
break;
|
||||
case LightEntry::kDirectional:
|
||||
lightColor[0] = (GLfloat)l->color.x();
|
||||
lightColor[1] = (GLfloat)l->color.y();
|
||||
lightColor[2] = (GLfloat)l->color.z();
|
||||
lightPos[0] = (GLfloat)-eyeDirection.x();
|
||||
lightPos[1] = (GLfloat)-eyeDirection.y();
|
||||
lightPos[2] = (GLfloat)-eyeDirection.z();
|
||||
lightPos[3] = 0;
|
||||
break;
|
||||
case LightEntry::kSpot:
|
||||
lightColor[0] = (GLfloat)l->color.x();
|
||||
lightColor[1] = (GLfloat)l->color.y();
|
||||
lightColor[2] = (GLfloat)l->color.z();
|
||||
lightPos[0] = (GLfloat)eyePosition.x();
|
||||
lightPos[1] = (GLfloat)eyePosition.y();
|
||||
lightPos[2] = (GLfloat)eyePosition.z();
|
||||
lightDir[0] = (GLfloat)eyeDirection.x();
|
||||
lightDir[1] = (GLfloat)eyeDirection.y();
|
||||
lightDir[2] = (GLfloat)eyeDirection.z();
|
||||
cutoff = (l->outerConeAngle.getDegrees() + l->innerConeAngle.getDegrees()) / 2.26f;
|
||||
break;
|
||||
case LightEntry::kAmbient:
|
||||
lightColor[0] = (GLfloat)l->color.x();
|
||||
lightColor[1] = (GLfloat)l->color.y();
|
||||
lightColor[2] = (GLfloat)l->color.z();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
glLightfv(GL_LIGHT0 + i, GL_AMBIENT, ambientColor);
|
||||
glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, lightColor);
|
||||
glLightfv(GL_LIGHT0 + i, GL_POSITION, lightPos);
|
||||
glLightfv(GL_LIGHT0 + i, GL_SPOT_DIRECTION, lightDir);
|
||||
glLightf(GL_LIGHT0 + i, GL_SPOT_EXPONENT, spotExp);
|
||||
glLightf(GL_LIGHT0 + i, GL_SPOT_CUTOFF, cutoff);
|
||||
glLightf(GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION, c_attenuation);
|
||||
glLightf(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION, l_attenuation);
|
||||
glLightf(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION, q_attenuation);
|
||||
glEnable(GL_LIGHT0 + i);
|
||||
}
|
||||
|
||||
for (uint i = lights.size() - 1; i < maxLights; i++) {
|
||||
// Make sure unused lights are disabled
|
||||
glDisable(GL_LIGHT0 + i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Texture *OpenGLDriver::createTexture() {
|
||||
return new OpenGlTexture();
|
||||
}
|
||||
|
||||
Bitmap *OpenGLDriver::createBitmap(const Graphics::Surface *surface, const byte *palette) {
|
||||
OpenGlBitmap *bitmap = new OpenGlBitmap();
|
||||
|
||||
if (surface) {
|
||||
bitmap->update(surface, palette);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
VisualActor *OpenGLDriver::createActorRenderer() {
|
||||
return new OpenGLActorRenderer(this);
|
||||
}
|
||||
|
||||
VisualProp *OpenGLDriver::createPropRenderer() {
|
||||
return new OpenGLPropRenderer(this);
|
||||
}
|
||||
|
||||
SurfaceRenderer *OpenGLDriver::createSurfaceRenderer() {
|
||||
return new OpenGLSurfaceRenderer(this);
|
||||
}
|
||||
|
||||
FadeRenderer *OpenGLDriver::createFadeRenderer() {
|
||||
return new OpenGLFadeRenderer(this);
|
||||
}
|
||||
|
||||
void OpenGLDriver::start2DMode() {
|
||||
// Enable alpha blending
|
||||
glEnable(GL_BLEND);
|
||||
//glBlendEquation(GL_FUNC_ADD); // It's the default
|
||||
|
||||
// This blend mode prevents color fringes due to filtering.
|
||||
// It requires the textures to have their color values pre-multiplied
|
||||
// with their alpha value. This is the "Premultiplied Alpha" technique.
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
if (!_computeLights)
|
||||
glDisable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
void OpenGLDriver::end2DMode() {
|
||||
// Disable alpha blending
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
void OpenGLDriver::set3DMode() {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
// Blending and stencil test are only used in rendering shadows
|
||||
// They are manually enabled and disabled there
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glStencilFunc(GL_EQUAL, 0, 0xFF);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
|
||||
|
||||
if (!_computeLights)
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
bool OpenGLDriver::computeLightsEnabled() {
|
||||
return _computeLights;
|
||||
}
|
||||
|
||||
Common::Rect OpenGLDriver::getViewport() const {
|
||||
return _viewport;
|
||||
}
|
||||
|
||||
Common::Rect OpenGLDriver::getUnscaledViewport() const {
|
||||
return _unscaledViewport;
|
||||
}
|
||||
|
||||
Graphics::Surface *OpenGLDriver::getViewportScreenshot() const {
|
||||
Graphics::Surface *s = new Graphics::Surface();
|
||||
s->create(_viewport.width(), _viewport.height(), getRGBAPixelFormat());
|
||||
|
||||
glReadPixels(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height(),
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, s->getPixels());
|
||||
|
||||
flipVertical(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME)
|
||||
79
engines/stark/gfx/opengl.h
Normal file
79
engines/stark/gfx/opengl.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* 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 STARK_GFX_OPENGL_H
|
||||
#define STARK_GFX_OPENGL_H
|
||||
|
||||
#include "common/system.h"
|
||||
#include "math/vector3d.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME)
|
||||
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/gfx/renderentry.h"
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class OpenGLDriver : public Driver {
|
||||
public:
|
||||
OpenGLDriver();
|
||||
~OpenGLDriver();
|
||||
|
||||
void init() override;
|
||||
|
||||
void setScreenViewport(bool noScaling) override;
|
||||
void setViewport(const Common::Rect &rect) override;
|
||||
|
||||
void clearScreen() override;
|
||||
void flipBuffer() override;
|
||||
|
||||
Texture *createTexture() override;
|
||||
Bitmap *createBitmap(const Graphics::Surface *surface = nullptr, const byte *palette = nullptr) override;
|
||||
VisualActor *createActorRenderer() override;
|
||||
VisualProp *createPropRenderer() override;
|
||||
SurfaceRenderer *createSurfaceRenderer() override;
|
||||
FadeRenderer *createFadeRenderer() override;
|
||||
|
||||
void start2DMode();
|
||||
void end2DMode();
|
||||
void set3DMode() override;
|
||||
bool computeLightsEnabled() override;
|
||||
|
||||
Common::Rect getViewport() const;
|
||||
Common::Rect getUnscaledViewport() const;
|
||||
void setupLights(const LightEntryArray &lights);
|
||||
|
||||
Graphics::Surface *getViewportScreenshot() const override;
|
||||
|
||||
private:
|
||||
Common::Rect _viewport;
|
||||
Common::Rect _unscaledViewport;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME)
|
||||
|
||||
#endif // STARK_GFX_OPENGL_H
|
||||
489
engines/stark/gfx/openglactor.cpp
Normal file
489
engines/stark/gfx/openglactor.cpp
Normal file
@@ -0,0 +1,489 @@
|
||||
/* 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/stark/gfx/openglactor.h"
|
||||
|
||||
#include "engines/stark/model/model.h"
|
||||
#include "engines/stark/model/animhandler.h"
|
||||
#include "engines/stark/scene.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/settings.h"
|
||||
#include "engines/stark/gfx/texture.h"
|
||||
|
||||
#include "math/vector2d.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME)
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
OpenGLActorRenderer::OpenGLActorRenderer(OpenGLDriver *gfx) :
|
||||
VisualActor(),
|
||||
_gfx(gfx),
|
||||
_faceVBO(nullptr) {
|
||||
}
|
||||
|
||||
OpenGLActorRenderer::~OpenGLActorRenderer() {
|
||||
clearVertices();
|
||||
}
|
||||
|
||||
void OpenGLActorRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
|
||||
if (_modelIsDirty) {
|
||||
clearVertices();
|
||||
uploadVertices();
|
||||
_modelIsDirty = false;
|
||||
}
|
||||
|
||||
// TODO: Move updates outside of the rendering code
|
||||
_animHandler->animate(_time);
|
||||
_model->updateBoundingBox();
|
||||
|
||||
bool drawShadow = false;
|
||||
if (_castsShadow &&
|
||||
StarkScene->shouldRenderShadows() &&
|
||||
StarkSettings->getBoolSetting(Settings::kShadow)) {
|
||||
drawShadow = true;
|
||||
}
|
||||
Math::Vector3d lightDirection;
|
||||
|
||||
_gfx->set3DMode();
|
||||
if (!_gfx->computeLightsEnabled())
|
||||
_gfx->setupLights(lights);
|
||||
|
||||
Math::Matrix4 model = getModelMatrix(position, direction);
|
||||
Math::Matrix4 view = StarkScene->getViewMatrix();
|
||||
Math::Matrix4 projection = StarkScene->getProjectionMatrix();
|
||||
|
||||
Math::Matrix4 modelViewMatrix = view * model;
|
||||
modelViewMatrix.transpose(); // OpenGL expects matrices transposed
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadMatrixf(modelViewMatrix.getData());
|
||||
|
||||
Math::Matrix4 projectionMatrix = projection;
|
||||
projectionMatrix.transpose(); // OpenGL expects matrices transposed
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadMatrixf(projectionMatrix.getData());
|
||||
|
||||
Math::Matrix4 normalMatrix;
|
||||
if (_gfx->computeLightsEnabled()) {
|
||||
projectionMatrix.transpose();
|
||||
modelViewMatrix.transpose();
|
||||
|
||||
normalMatrix = modelViewMatrix;
|
||||
normalMatrix.invertAffineOrthonormal();
|
||||
}
|
||||
|
||||
Math::Matrix4 mvp;
|
||||
if (drawShadow) {
|
||||
mvp = view * model;
|
||||
mvp.transpose();
|
||||
Math::Matrix4 modelInverse = model;
|
||||
modelInverse.inverse();
|
||||
lightDirection = getShadowLightDirection(lights, position, modelInverse.getRotation());
|
||||
}
|
||||
|
||||
Common::Array<Face *> faces = _model->getFaces();
|
||||
Common::Array<Material *> mats = _model->getMaterials();
|
||||
const Common::Array<BoneNode *> &bones = _model->getBones();
|
||||
|
||||
if (!_gfx->computeLightsEnabled()) {
|
||||
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
|
||||
glEnable(GL_COLOR_MATERIAL);
|
||||
}
|
||||
|
||||
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
const Material *material = mats[(*face)->materialId];
|
||||
Math::Vector3d color;
|
||||
const Gfx::Texture *tex = resolveTexture(material);
|
||||
if (tex) {
|
||||
tex->bind();
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
auto vertexIndices = _faceEBO[*face];
|
||||
auto numVertexIndices = (*face)->vertexIndices.size();
|
||||
for (uint32 i = 0; i < numVertexIndices; i++) {
|
||||
if (tex) {
|
||||
if (_gfx->computeLightsEnabled())
|
||||
color = Math::Vector3d(1.0f, 1.0f, 1.0f);
|
||||
else
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
} else {
|
||||
if (_gfx->computeLightsEnabled())
|
||||
color = Math::Vector3d(material->r, material->g, material->b);
|
||||
else
|
||||
glColor4f(material->r, material->g, material->b, 1.0f);
|
||||
}
|
||||
uint32 index = vertexIndices[i];
|
||||
auto vertex = _faceVBO[index];
|
||||
uint32 bone1 = vertex.bone1;
|
||||
uint32 bone2 = vertex.bone2;
|
||||
Math::Vector3d position1 = Math::Vector3d(vertex.pos1x, vertex.pos1y, vertex.pos1z);
|
||||
Math::Vector3d position2 = Math::Vector3d(vertex.pos2x, vertex.pos2y, vertex.pos2z);
|
||||
Math::Vector3d bone1Position = Math::Vector3d(bones[bone1]->_animPos.x(),
|
||||
bones[bone1]->_animPos.y(),
|
||||
bones[bone1]->_animPos.z());
|
||||
Math::Vector3d bone2Position = Math::Vector3d(bones[bone2]->_animPos.x(),
|
||||
bones[bone2]->_animPos.y(),
|
||||
bones[bone2]->_animPos.z());
|
||||
Math::Quaternion bone1Rotation = Math::Quaternion(bones[bone1]->_animRot.x(),
|
||||
bones[bone1]->_animRot.y(),
|
||||
bones[bone1]->_animRot.z(),
|
||||
bones[bone1]->_animRot.w());
|
||||
Math::Quaternion bone2Rotation = Math::Quaternion(bones[bone2]->_animRot.x(),
|
||||
bones[bone2]->_animRot.y(),
|
||||
bones[bone2]->_animRot.z(),
|
||||
bones[bone2]->_animRot.w());
|
||||
float boneWeight = vertex.boneWeight;
|
||||
Math::Vector3d normal = Math::Vector3d(vertex.normalx, vertex.normaly, vertex.normalz);
|
||||
|
||||
// Compute the vertex position in eye-space
|
||||
bone1Rotation.transform(position1);
|
||||
position1 += bone1Position;
|
||||
bone2Rotation.transform(position2);
|
||||
position2 += bone2Position;
|
||||
Math::Vector3d modelPosition = Math::Vector3d::interpolate(position2, position1, boneWeight);
|
||||
vertex.x = modelPosition.x();
|
||||
vertex.y = modelPosition.y();
|
||||
vertex.z = modelPosition.z();
|
||||
Math::Vector4d modelEyePosition;
|
||||
if (_gfx->computeLightsEnabled()) {
|
||||
modelEyePosition = modelViewMatrix * Math::Vector4d(modelPosition.x(),
|
||||
modelPosition.y(),
|
||||
modelPosition.z(),
|
||||
1.0);
|
||||
}
|
||||
// Compute the vertex normal in eye-space
|
||||
Math::Vector3d n1 = normal;
|
||||
bone1Rotation.transform(n1);
|
||||
Math::Vector3d n2 = normal;
|
||||
bone2Rotation.transform(n2);
|
||||
Math::Vector3d modelNormal = Math::Vector3d(Math::Vector3d::interpolate(n2, n1, boneWeight)).getNormalized();
|
||||
vertex.nx = modelNormal.x();
|
||||
vertex.ny = modelNormal.y();
|
||||
vertex.nz = modelNormal.z();
|
||||
Math::Vector3d modelEyeNormal;
|
||||
if (_gfx->computeLightsEnabled()) {
|
||||
modelEyeNormal = normalMatrix.getRotation() * modelNormal;
|
||||
modelEyeNormal.normalize();
|
||||
}
|
||||
|
||||
if (drawShadow) {
|
||||
Math::Vector3d shadowPosition = modelPosition + lightDirection * (-modelPosition.y() / lightDirection.y());
|
||||
vertex.sx = shadowPosition.x();
|
||||
vertex.sy = 0.0f;
|
||||
vertex.sz = shadowPosition.z();
|
||||
}
|
||||
|
||||
if (_gfx->computeLightsEnabled()) {
|
||||
static const uint maxLights = 10;
|
||||
|
||||
assert(lights.size() >= 1);
|
||||
assert(lights.size() <= maxLights);
|
||||
|
||||
const LightEntry *ambient = lights[0];
|
||||
assert(ambient->type == LightEntry::kAmbient); // The first light must be the ambient light
|
||||
|
||||
Math::Vector3d lightColor = ambient->color;
|
||||
|
||||
for (uint li = 0; li < lights.size() - 1; li++) {
|
||||
const LightEntry *l = lights[li + 1];
|
||||
|
||||
switch (l->type) {
|
||||
case LightEntry::kPoint: {
|
||||
Math::Vector3d vertexToLight = l->eyePosition.getXYZ() - modelEyePosition.getXYZ();
|
||||
|
||||
float dist = vertexToLight.length();
|
||||
vertexToLight.normalize();
|
||||
float attn = CLIP((l->falloffFar - dist) / MAX(0.001f, l->falloffFar - l->falloffNear), 0.0f, 1.0f);
|
||||
float incidence = MAX(0.0f, Math::Vector3d::dotProduct(modelEyeNormal, vertexToLight));
|
||||
lightColor += l->color * attn * incidence;
|
||||
break;
|
||||
}
|
||||
case LightEntry::kDirectional: {
|
||||
float incidence = MAX(0.0f, Math::Vector3d::dotProduct(modelEyeNormal, -l->eyeDirection));
|
||||
lightColor += (l->color * incidence);
|
||||
break;
|
||||
}
|
||||
case LightEntry::kSpot: {
|
||||
Math::Vector3d vertexToLight = l->eyePosition.getXYZ() - modelEyePosition.getXYZ();
|
||||
|
||||
float dist = vertexToLight.length();
|
||||
float attn = CLIP((l->falloffFar - dist) / MAX(0.001f, l->falloffFar - l->falloffNear), 0.0f, 1.0f);
|
||||
|
||||
vertexToLight.normalize();
|
||||
float incidence = MAX(0.0f, modelEyeNormal.dotProduct(vertexToLight));
|
||||
|
||||
float cosAngle = MAX(0.0f, vertexToLight.dotProduct(-l->eyeDirection));
|
||||
float cone = CLIP((cosAngle - l->innerConeAngle.getCosine()) / MAX(0.001f, l->outerConeAngle.getCosine() - l->innerConeAngle.getCosine()), 0.0f, 1.0f);
|
||||
|
||||
lightColor += l->color * attn * incidence * cone;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lightColor.x() = CLIP(lightColor.x(), 0.0f, 1.0f);
|
||||
lightColor.y() = CLIP(lightColor.y(), 0.0f, 1.0f);
|
||||
lightColor.z() = CLIP(lightColor.z(), 0.0f, 1.0f);
|
||||
color = color * lightColor;
|
||||
vertex.r = color.x();
|
||||
vertex.g = color.y();
|
||||
vertex.b = color.z();
|
||||
vertex.a = 1.0f; /* needed for compatibility with OpenGL ES 1.x */
|
||||
}
|
||||
|
||||
_faceVBO[index] = vertex;
|
||||
}
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
if (_gfx->computeLightsEnabled())
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
if (tex)
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].x);
|
||||
if (tex)
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].texS);
|
||||
glNormalPointer(GL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].nx);
|
||||
if (_gfx->computeLightsEnabled())
|
||||
glColorPointer(4, GL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].r);
|
||||
|
||||
glDrawElements(GL_TRIANGLES, numVertexIndices, GL_UNSIGNED_INT, vertexIndices);
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
if (_gfx->computeLightsEnabled())
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
}
|
||||
if (!_gfx->computeLightsEnabled())
|
||||
glDisable(GL_COLOR_MATERIAL);
|
||||
|
||||
|
||||
if (drawShadow) {
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
if (!_gfx->computeLightsEnabled())
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
|
||||
|
||||
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].sx);
|
||||
|
||||
glDrawElements(GL_TRIANGLES, (*face)->vertexIndices.size(), GL_UNSIGNED_INT, _faceEBO[*face]);
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
if (!_gfx->computeLightsEnabled())
|
||||
glEnable(GL_LIGHTING);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLActorRenderer::clearVertices() {
|
||||
delete[] _faceVBO;
|
||||
_faceVBO = nullptr;
|
||||
|
||||
for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
|
||||
delete[] it->_value;
|
||||
}
|
||||
|
||||
_faceEBO.clear();
|
||||
}
|
||||
|
||||
void OpenGLActorRenderer::uploadVertices() {
|
||||
_faceVBO = createModelVBO(_model);
|
||||
|
||||
Common::Array<Face *> faces = _model->getFaces();
|
||||
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
_faceEBO[*face] = createFaceEBO(*face);
|
||||
}
|
||||
}
|
||||
|
||||
ActorVertex *OpenGLActorRenderer::createModelVBO(const Model *model) {
|
||||
const Common::Array<VertNode *> &modelVertices = model->getVertices();
|
||||
|
||||
auto vertices = new ActorVertex[modelVertices.size()];
|
||||
// Build a vertex array
|
||||
int i = 0;
|
||||
for (Common::Array<VertNode *>::const_iterator tri = modelVertices.begin(); tri != modelVertices.end(); ++tri, i++) {
|
||||
vertices[i].pos1x = (*tri)->_pos1.x();
|
||||
vertices[i].pos1y = (*tri)->_pos1.y();
|
||||
vertices[i].pos1z = (*tri)->_pos1.z();
|
||||
vertices[i].pos2x = (*tri)->_pos2.x();
|
||||
vertices[i].pos2y = (*tri)->_pos2.y();
|
||||
vertices[i].pos2z = (*tri)->_pos2.z();
|
||||
vertices[i].bone1 = (*tri)->_bone1;
|
||||
vertices[i].bone2 = (*tri)->_bone2;
|
||||
vertices[i].boneWeight = (*tri)->_boneWeight;
|
||||
vertices[i].normalx = (*tri)->_normal.x();
|
||||
vertices[i].normaly = (*tri)->_normal.y();
|
||||
vertices[i].normalz = (*tri)->_normal.z();
|
||||
vertices[i].texS = -(*tri)->_texS;
|
||||
vertices[i].texT = (*tri)->_texT;
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
uint32 *OpenGLActorRenderer::createFaceEBO(const Face *face) {
|
||||
auto indices = new uint32[face->vertexIndices.size()];
|
||||
for (uint32 index = 0; index < face->vertexIndices.size(); index++) {
|
||||
indices[index] = face->vertexIndices[index];
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
Math::Vector3d OpenGLActorRenderer::getShadowLightDirection(const LightEntryArray &lights, const Math::Vector3d &actorPosition,
|
||||
Math::Matrix3 worldToModelRot) {
|
||||
Math::Vector3d sumDirection;
|
||||
bool hasLight = false;
|
||||
|
||||
// Compute the contribution from each lights
|
||||
// The ambient light is skipped intentionally
|
||||
for (uint i = 1; i < lights.size(); ++i) {
|
||||
LightEntry *light = lights[i];
|
||||
bool contributes = false;
|
||||
|
||||
Math::Vector3d lightDirection;
|
||||
switch (light->type) {
|
||||
case LightEntry::kPoint:
|
||||
contributes = getPointLightContribution(light, actorPosition, lightDirection);
|
||||
break;
|
||||
case LightEntry::kDirectional:
|
||||
contributes = getDirectionalLightContribution(light, lightDirection);
|
||||
break;
|
||||
case LightEntry::kSpot:
|
||||
contributes = getSpotLightContribution(light, actorPosition, lightDirection);
|
||||
break;
|
||||
case LightEntry::kAmbient:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (contributes) {
|
||||
sumDirection += lightDirection;
|
||||
hasLight = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasLight) {
|
||||
// Clip the horizontal length
|
||||
Math::Vector2d horizontalProjection(sumDirection.x(), sumDirection.y());
|
||||
float shadowLength = MIN(horizontalProjection.getMagnitude(), StarkScene->getMaxShadowLength());
|
||||
|
||||
horizontalProjection.normalize();
|
||||
horizontalProjection *= shadowLength;
|
||||
|
||||
sumDirection.x() = horizontalProjection.getX();
|
||||
sumDirection.y() = horizontalProjection.getY();
|
||||
sumDirection.z() = -1;
|
||||
} else {
|
||||
// Cast from above by default
|
||||
sumDirection.x() = 0;
|
||||
sumDirection.y() = 0;
|
||||
sumDirection.z() = -1;
|
||||
}
|
||||
|
||||
//Transform the direction to the model space and pass to the shader
|
||||
return worldToModelRot * sumDirection;
|
||||
}
|
||||
|
||||
bool OpenGLActorRenderer::getPointLightContribution(LightEntry *light, const Math::Vector3d &actorPosition,
|
||||
Math::Vector3d &direction, float weight) {
|
||||
float distance = light->position.getDistanceTo(actorPosition);
|
||||
|
||||
if (distance > light->falloffFar) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float factor;
|
||||
if (distance > light->falloffNear) {
|
||||
if (light->falloffFar - light->falloffNear > 1) {
|
||||
factor = 1 - (distance - light->falloffNear) / (light->falloffFar - light->falloffNear);
|
||||
} else {
|
||||
factor = 0;
|
||||
}
|
||||
} else {
|
||||
factor = 1;
|
||||
}
|
||||
|
||||
float brightness = (light->color.x() + light->color.y() + light->color.z()) / 3.0f;
|
||||
|
||||
if (factor <= 0 || brightness <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
direction = actorPosition - light->position;
|
||||
direction.normalize();
|
||||
direction *= factor * brightness * weight;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLActorRenderer::getDirectionalLightContribution(LightEntry *light, Math::Vector3d &direction) {
|
||||
float brightness = (light->color.x() + light->color.y() + light->color.z()) / 3.0f;
|
||||
|
||||
if (brightness <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
direction = light->direction;
|
||||
direction.normalize();
|
||||
direction *= brightness;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLActorRenderer::getSpotLightContribution(LightEntry *light, const Math::Vector3d &actorPosition,
|
||||
Math::Vector3d &direction) {
|
||||
Math::Vector3d lightToActor = actorPosition - light->position;
|
||||
lightToActor.normalize();
|
||||
|
||||
float cosAngle = MAX(0.0f, lightToActor.dotProduct(light->direction));
|
||||
float cone = (cosAngle - light->innerConeAngle.getCosine()) /
|
||||
MAX(0.001f, light->outerConeAngle.getCosine() - light->innerConeAngle.getCosine());
|
||||
cone = CLIP(cone, 0.0f, 1.0f);
|
||||
|
||||
if (cone <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getPointLightContribution(light, actorPosition, direction, cone);
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME)
|
||||
106
engines/stark/gfx/openglactor.h
Normal file
106
engines/stark/gfx/openglactor.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 STARK_GFX_OPENGL_ACTOR_H
|
||||
#define STARK_GFX_OPENGL_ACTOR_H
|
||||
|
||||
#include "engines/stark/gfx/renderentry.h"
|
||||
#include "engines/stark/visual/actor.h"
|
||||
#include "engines/stark/gfx/opengl.h"
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-ptr.h"
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME)
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class OpenGLDriver;
|
||||
|
||||
struct _ActorVertex {
|
||||
float pos1x;
|
||||
float pos1y;
|
||||
float pos1z;
|
||||
float pos2x;
|
||||
float pos2y;
|
||||
float pos2z;
|
||||
uint32 bone1;
|
||||
uint32 bone2;
|
||||
float boneWeight;
|
||||
float normalx;
|
||||
float normaly;
|
||||
float normalz;
|
||||
float texS;
|
||||
float texT;
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float nx;
|
||||
float ny;
|
||||
float nz;
|
||||
float sx;
|
||||
float sy;
|
||||
float sz;
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
float a;
|
||||
};
|
||||
typedef _ActorVertex ActorVertex;
|
||||
|
||||
class OpenGLActorRenderer : public VisualActor {
|
||||
public:
|
||||
OpenGLActorRenderer(OpenGLDriver *gfx);
|
||||
virtual ~OpenGLActorRenderer();
|
||||
|
||||
void render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) override;
|
||||
|
||||
protected:
|
||||
typedef Common::HashMap<Face *, uint32 *> FaceBufferMap;
|
||||
|
||||
OpenGLDriver *_gfx;
|
||||
|
||||
ActorVertex *_faceVBO;
|
||||
FaceBufferMap _faceEBO;
|
||||
|
||||
void clearVertices();
|
||||
void uploadVertices();
|
||||
ActorVertex *createModelVBO(const Model *model);
|
||||
uint32 *createFaceEBO(const Face *face);
|
||||
void setLightArrayUniform(const LightEntryArray &lights);
|
||||
|
||||
Math::Vector3d getShadowLightDirection(const LightEntryArray &lights, const Math::Vector3d &actorPosition, Math::Matrix3 worldToModelRot);
|
||||
|
||||
bool getPointLightContribution(LightEntry *light, const Math::Vector3d &actorPosition,
|
||||
Math::Vector3d &direction, float weight = 1.0f);
|
||||
bool getDirectionalLightContribution(LightEntry *light, Math::Vector3d &direction);
|
||||
bool getSpotLightContribution(LightEntry *light, const Math::Vector3d &actorPosition, Math::Vector3d &direction);
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME)
|
||||
|
||||
#endif // STARK_GFX_OPENGL_ACTOR_H
|
||||
122
engines/stark/gfx/openglbitmap.cpp
Normal file
122
engines/stark/gfx/openglbitmap.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/* 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/stark/gfx/openglbitmap.h"
|
||||
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#include "graphics/opengl/context.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
OpenGlBitmap::OpenGlBitmap() :
|
||||
Bitmap(),
|
||||
_id(0) {
|
||||
glGenTextures(1, &_id);
|
||||
|
||||
bind();
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
OpenGlBitmap::~OpenGlBitmap() {
|
||||
glDeleteTextures(1, &_id);
|
||||
}
|
||||
|
||||
void OpenGlBitmap::bind() const {
|
||||
glBindTexture(GL_TEXTURE_2D, _id);
|
||||
}
|
||||
|
||||
void OpenGlBitmap::update(const Graphics::Surface *surface, const byte *palette) {
|
||||
bind();
|
||||
|
||||
const Graphics::Surface *rgbaSurface = surface;
|
||||
if (surface->format != Driver::getRGBAPixelFormat()) {
|
||||
// Convert the surface to texture format
|
||||
rgbaSurface = surface->convertTo(Driver::getRGBAPixelFormat(), palette);
|
||||
}
|
||||
|
||||
_width = rgbaSurface->w;
|
||||
_height = rgbaSurface->h;
|
||||
|
||||
GLfloat s, t;
|
||||
|
||||
if (!OpenGLContext.NPOTSupported) {
|
||||
uint32 texWidth = Common::nextHigher2(rgbaSurface->w);
|
||||
uint32 texHeight = Common::nextHigher2(rgbaSurface->h);
|
||||
s = (GLfloat)_width / texWidth;
|
||||
t = (GLfloat)_height / texHeight;
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rgbaSurface->w, rgbaSurface->h,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, rgbaSurface->getPixels());
|
||||
} else {
|
||||
s = t = 1.f;
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rgbaSurface->w, rgbaSurface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbaSurface->getPixels());
|
||||
}
|
||||
|
||||
_texCoords[0*2+0] = 0.0f;
|
||||
_texCoords[0*2+1] = 0.0f;
|
||||
_texCoords[1*2+0] = s;
|
||||
_texCoords[1*2+1] = 0.0f;
|
||||
_texCoords[2*2+0] = 0.0f;
|
||||
_texCoords[2*2+1] = t;
|
||||
_texCoords[3*2+0] = s;
|
||||
_texCoords[3*2+1] = t;
|
||||
|
||||
if (rgbaSurface != surface) {
|
||||
const_cast<Graphics::Surface *>(rgbaSurface)->free();
|
||||
delete rgbaSurface;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGlBitmap::setSamplingFilter(Bitmap::SamplingFilter filter) {
|
||||
switch (filter) {
|
||||
case kNearest:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
break;
|
||||
case kLinear:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
break;
|
||||
default:
|
||||
warning("Unhandled sampling filter %d", filter);
|
||||
}
|
||||
}
|
||||
|
||||
Graphics::PixelFormat OpenGlBitmap::getBestPixelFormat() const {
|
||||
return Driver::getRGBAPixelFormat();
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
60
engines/stark/gfx/openglbitmap.h
Normal file
60
engines/stark/gfx/openglbitmap.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 STARK_GFX_OPENGL_BITMAP_H
|
||||
#define STARK_GFX_OPENGL_BITMAP_H
|
||||
|
||||
#include "engines/stark/gfx/bitmap.h"
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
/**
|
||||
* An OpenGL texture wrapper for 2D elements
|
||||
*/
|
||||
class OpenGlBitmap : public Bitmap {
|
||||
public:
|
||||
OpenGlBitmap();
|
||||
virtual ~OpenGlBitmap();
|
||||
|
||||
// Bitmap API
|
||||
void bind() const override;
|
||||
void update(const Graphics::Surface *surface, const byte *palette = nullptr) override;
|
||||
void setSamplingFilter(SamplingFilter filter) override;
|
||||
Graphics::PixelFormat getBestPixelFormat() const override;
|
||||
|
||||
const GLfloat *getTexCoords() const { return _texCoords; }
|
||||
|
||||
protected:
|
||||
GLuint _id;
|
||||
GLfloat _texCoords[2*4];
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#endif // STARK_GFX_OPENGL_BITMAP_H
|
||||
81
engines/stark/gfx/openglfade.cpp
Normal file
81
engines/stark/gfx/openglfade.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/* 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/stark/gfx/openglfade.h"
|
||||
|
||||
#include "engines/stark/gfx/opengl.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME)
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
static const GLfloat fadeVertices[] = {
|
||||
// X Y
|
||||
-1.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
-1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
};
|
||||
|
||||
OpenGLFadeRenderer::OpenGLFadeRenderer(OpenGLDriver *gfx) :
|
||||
FadeRenderer(),
|
||||
_gfx(gfx) {
|
||||
}
|
||||
|
||||
OpenGLFadeRenderer::~OpenGLFadeRenderer() {
|
||||
}
|
||||
|
||||
void OpenGLFadeRenderer::render(float fadeLevel) {
|
||||
_gfx->start2DMode();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glColor4f(0.0f, 0.0f, 0.0f, 1.0f - fadeLevel);
|
||||
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), &fadeVertices[0]);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
|
||||
_gfx->end2DMode();
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME)
|
||||
56
engines/stark/gfx/openglfade.h
Normal file
56
engines/stark/gfx/openglfade.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* 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 STARK_GFX_OPENGL_FADE_H
|
||||
#define STARK_GFX_OPENGL_FADE_H
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME)
|
||||
|
||||
#include "engines/stark/gfx/faderenderer.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class OpenGLDriver;
|
||||
|
||||
/**
|
||||
* A programmable pipeline OpenGL fade screen renderer
|
||||
*/
|
||||
class OpenGLFadeRenderer : public FadeRenderer {
|
||||
public:
|
||||
OpenGLFadeRenderer(OpenGLDriver *gfx);
|
||||
~OpenGLFadeRenderer();
|
||||
|
||||
// FadeRenderer API
|
||||
void render(float fadeLevel);
|
||||
|
||||
private:
|
||||
OpenGLDriver *_gfx;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME)
|
||||
|
||||
#endif // STARK_GFX_OPENGL_FADE_H
|
||||
267
engines/stark/gfx/openglprop.cpp
Normal file
267
engines/stark/gfx/openglprop.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
/* 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/stark/gfx/openglprop.h"
|
||||
|
||||
#include "engines/stark/gfx/texture.h"
|
||||
#include "engines/stark/formats/biffmesh.h"
|
||||
#include "engines/stark/scene.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME)
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
OpenGLPropRenderer::OpenGLPropRenderer(OpenGLDriver *gfx) :
|
||||
VisualProp(),
|
||||
_gfx(gfx),
|
||||
_faceVBO(nullptr),
|
||||
_modelIsDirty(true) {
|
||||
}
|
||||
|
||||
OpenGLPropRenderer::~OpenGLPropRenderer() {
|
||||
clearVertices();
|
||||
}
|
||||
|
||||
void OpenGLPropRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
|
||||
if (_modelIsDirty) {
|
||||
clearVertices();
|
||||
uploadVertices();
|
||||
_modelIsDirty = false;
|
||||
}
|
||||
|
||||
_gfx->set3DMode();
|
||||
if (!_gfx->computeLightsEnabled())
|
||||
_gfx->setupLights(lights);
|
||||
|
||||
Math::Matrix4 model = getModelMatrix(position, direction);
|
||||
Math::Matrix4 view = StarkScene->getViewMatrix();
|
||||
Math::Matrix4 projection = StarkScene->getProjectionMatrix();
|
||||
|
||||
Math::Matrix4 modelViewMatrix = view * model;
|
||||
modelViewMatrix.transpose(); // OpenGL expects matrices transposed
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadMatrixf(modelViewMatrix.getData());
|
||||
|
||||
Math::Matrix4 projectionMatrix = projection;
|
||||
projectionMatrix.transpose(); // OpenGL expects matrices transposed
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadMatrixf(projectionMatrix.getData());
|
||||
|
||||
Math::Matrix4 normalMatrix;
|
||||
if (_gfx->computeLightsEnabled()) {
|
||||
projectionMatrix.transpose();
|
||||
modelViewMatrix.transpose();
|
||||
|
||||
normalMatrix = modelViewMatrix;
|
||||
normalMatrix.invertAffineOrthonormal();
|
||||
}
|
||||
|
||||
const Common::Array<Face> &faces = _model->getFaces();
|
||||
const Common::Array<Material> &materials = _model->getMaterials();
|
||||
|
||||
if (!_gfx->computeLightsEnabled())
|
||||
glEnable(GL_COLOR_MATERIAL);
|
||||
for (Common::Array<Face>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
const Material &material = materials[face->materialId];
|
||||
Math::Vector3d color;
|
||||
const Gfx::Texture *tex = _texture->getTexture(material.texture);
|
||||
if (tex) {
|
||||
tex->bind();
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
auto vertexIndices = _faceEBO[face];
|
||||
auto numVertexIndices = (face)->vertexIndices.size();
|
||||
if (!_gfx->computeLightsEnabled()) {
|
||||
if (material.doubleSided)
|
||||
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
|
||||
else
|
||||
glColorMaterial(GL_FRONT, GL_DIFFUSE);
|
||||
}
|
||||
for (uint32 i = 0; i < numVertexIndices; i++) {
|
||||
uint32 index = vertexIndices[i];
|
||||
auto vertex = _faceVBO[index];
|
||||
if (tex) {
|
||||
if (_gfx->computeLightsEnabled())
|
||||
color = Math::Vector3d(1.0f, 1.0f, 1.0f);
|
||||
else
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
if (material.doubleSided) {
|
||||
vertex.texS = vertex.stexS;
|
||||
vertex.texT = 1.0f - vertex.stexT;
|
||||
} else {
|
||||
vertex.texS = 1.0f - vertex.stexS;
|
||||
vertex.texT = 1.0f - vertex.stexT;
|
||||
}
|
||||
} else {
|
||||
if (_gfx->computeLightsEnabled())
|
||||
color = Math::Vector3d(material.r, material.g, material.b);
|
||||
else
|
||||
glColor4f(material.r, material.g, material.b, 1.0f);
|
||||
}
|
||||
|
||||
if (_gfx->computeLightsEnabled()) {
|
||||
Math::Vector4d modelEyePosition = modelViewMatrix * Math::Vector4d(vertex.x, vertex.y, vertex.z, 1.0);
|
||||
Math::Vector3d modelEyeNormal = normalMatrix.getRotation() * Math::Vector3d(vertex.nx, vertex.ny, vertex.nz);
|
||||
modelEyeNormal.normalize();
|
||||
|
||||
static const uint maxLights = 10;
|
||||
|
||||
assert(lights.size() >= 1);
|
||||
assert(lights.size() <= maxLights);
|
||||
|
||||
const LightEntry *ambient = lights[0];
|
||||
assert(ambient->type == LightEntry::kAmbient); // The first light must be the ambient light
|
||||
|
||||
Math::Vector3d lightColor = ambient->color;
|
||||
|
||||
for (uint li = 0; li < lights.size() - 1; li++) {
|
||||
const LightEntry *l = lights[li + 1];
|
||||
|
||||
switch (l->type) {
|
||||
case LightEntry::kPoint: {
|
||||
Math::Vector3d vertexToLight = l->eyePosition.getXYZ() - modelEyePosition.getXYZ();
|
||||
|
||||
float dist = vertexToLight.length();
|
||||
vertexToLight.normalize();
|
||||
float attn = CLIP((l->falloffFar - dist) / MAX(0.001f, l->falloffFar - l->falloffNear), 0.0f, 1.0f);
|
||||
float incidence = MAX(0.0f, Math::Vector3d::dotProduct(modelEyeNormal, vertexToLight));
|
||||
lightColor += l->color * attn * incidence;
|
||||
break;
|
||||
}
|
||||
case LightEntry::kDirectional: {
|
||||
float incidence = MAX(0.0f, Math::Vector3d::dotProduct(modelEyeNormal, -l->eyeDirection));
|
||||
lightColor += (l->color * incidence);
|
||||
break;
|
||||
}
|
||||
case LightEntry::kSpot: {
|
||||
Math::Vector3d vertexToLight = l->eyePosition.getXYZ() - modelEyePosition.getXYZ();
|
||||
|
||||
float dist = vertexToLight.length();
|
||||
float attn = CLIP((l->falloffFar - dist) / MAX(0.001f, l->falloffFar - l->falloffNear), 0.0f, 1.0f);
|
||||
|
||||
vertexToLight.normalize();
|
||||
float incidence = MAX(0.0f, modelEyeNormal.dotProduct(vertexToLight));
|
||||
|
||||
float cosAngle = MAX(0.0f, vertexToLight.dotProduct(-l->eyeDirection));
|
||||
float cone = CLIP((cosAngle - l->innerConeAngle.getCosine()) / MAX(0.001f, l->outerConeAngle.getCosine() - l->innerConeAngle.getCosine()), 0.0f, 1.0f);
|
||||
|
||||
lightColor += l->color * attn * incidence * cone;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lightColor.x() = CLIP(lightColor.x(), 0.0f, 1.0f);
|
||||
lightColor.y() = CLIP(lightColor.y(), 0.0f, 1.0f);
|
||||
lightColor.z() = CLIP(lightColor.z(), 0.0f, 1.0f);
|
||||
color = color * lightColor;
|
||||
vertex.r = color.x();
|
||||
vertex.g = color.y();
|
||||
vertex.b = color.z();
|
||||
vertex.a = 1.0f; /* needed for compatibility with OpenGL ES 1.x */
|
||||
}
|
||||
_faceVBO[index] = vertex;
|
||||
}
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
if (_gfx->computeLightsEnabled())
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
if (tex)
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(PropVertex), &_faceVBO[0].x);
|
||||
if (tex)
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(PropVertex), &_faceVBO[0].texS);
|
||||
glNormalPointer(GL_FLOAT, sizeof(PropVertex), &_faceVBO[0].nx);
|
||||
if (_gfx->computeLightsEnabled())
|
||||
glColorPointer(4, GL_FLOAT, sizeof(PropVertex), &_faceVBO[0].r);
|
||||
|
||||
glDrawElements(GL_TRIANGLES, face->vertexIndices.size(), GL_UNSIGNED_INT, vertexIndices);
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
if (_gfx->computeLightsEnabled())
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
}
|
||||
if (!_gfx->computeLightsEnabled())
|
||||
glDisable(GL_COLOR_MATERIAL);
|
||||
}
|
||||
|
||||
void OpenGLPropRenderer::clearVertices() {
|
||||
delete[] _faceVBO;
|
||||
_faceVBO = nullptr;
|
||||
|
||||
for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
|
||||
delete[] it->_value;
|
||||
}
|
||||
|
||||
_faceEBO.clear();
|
||||
}
|
||||
|
||||
void OpenGLPropRenderer::uploadVertices() {
|
||||
_faceVBO = createFaceVBO();
|
||||
|
||||
const Common::Array<Face> &faces = _model->getFaces();
|
||||
for (Common::Array<Face>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
_faceEBO[face] = createFaceEBO(face);
|
||||
}
|
||||
}
|
||||
|
||||
PropVertex *OpenGLPropRenderer::createFaceVBO() {
|
||||
const Common::Array<Formats::BiffMesh::Vertex> &modelVertices = _model->getVertices();
|
||||
auto vertices = new PropVertex[modelVertices.size()];
|
||||
// Build a vertex array
|
||||
for (uint32 i = 0; i < modelVertices.size(); i++) {
|
||||
vertices[i].x = modelVertices[i].position.x();
|
||||
vertices[i].y = modelVertices[i].position.y();
|
||||
vertices[i].z = modelVertices[i].position.z();
|
||||
vertices[i].nx = modelVertices[i].normal.x();
|
||||
vertices[i].ny = modelVertices[i].normal.y();
|
||||
vertices[i].nz = modelVertices[i].normal.z();
|
||||
vertices[i].stexS = modelVertices[i].texturePosition.x();
|
||||
vertices[i].stexT = modelVertices[i].texturePosition.y();
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
uint32 *OpenGLPropRenderer::createFaceEBO(const Face *face) {
|
||||
auto indices = new uint32[face->vertexIndices.size()];
|
||||
for (uint32 index = 0; index < face->vertexIndices.size(); index++) {
|
||||
indices[index] = face->vertexIndices[index];
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME)
|
||||
87
engines/stark/gfx/openglprop.h
Normal file
87
engines/stark/gfx/openglprop.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/* 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 STARK_GFX_OPENGL_RENDERED_H
|
||||
#define STARK_GFX_OPENGL_RENDERED_H
|
||||
|
||||
#include "engines/stark/model/model.h"
|
||||
#include "engines/stark/visual/prop.h"
|
||||
#include "engines/stark/gfx/opengl.h"
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-ptr.h"
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME)
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
class Driver;
|
||||
|
||||
struct _PropVertex {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float nx;
|
||||
float ny;
|
||||
float nz;
|
||||
float stexS;
|
||||
float stexT;
|
||||
float texS;
|
||||
float texT;
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
float a;
|
||||
};
|
||||
typedef _PropVertex PropVertex;
|
||||
|
||||
class OpenGLPropRenderer : public VisualProp {
|
||||
public:
|
||||
explicit OpenGLPropRenderer(OpenGLDriver *gfx);
|
||||
~OpenGLPropRenderer() override;
|
||||
|
||||
void render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) override;
|
||||
|
||||
protected:
|
||||
typedef Common::HashMap<const Face *, uint32 *> FaceBufferMap;
|
||||
|
||||
OpenGLDriver *_gfx;
|
||||
|
||||
bool _modelIsDirty;
|
||||
PropVertex *_faceVBO;
|
||||
FaceBufferMap _faceEBO;
|
||||
|
||||
void clearVertices();
|
||||
void uploadVertices();
|
||||
PropVertex *createFaceVBO();
|
||||
uint32 *createFaceEBO(const Face *face);
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_OPENGL_S_RENDERED_H
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME)
|
||||
250
engines/stark/gfx/opengls.cpp
Normal file
250
engines/stark/gfx/opengls.cpp
Normal file
@@ -0,0 +1,250 @@
|
||||
/* 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/stark/gfx/opengls.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#include "math/matrix4.h"
|
||||
|
||||
#if defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#include "engines/stark/gfx/openglsactor.h"
|
||||
#include "engines/stark/gfx/openglbitmap.h"
|
||||
#include "engines/stark/gfx/openglsprop.h"
|
||||
#include "engines/stark/gfx/openglssurface.h"
|
||||
#include "engines/stark/gfx/openglsfade.h"
|
||||
#include "engines/stark/gfx/opengltexture.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/opengl/shader.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
static const GLfloat surfaceVertices[] = {
|
||||
// XS YT
|
||||
0.0f, 0.0f,
|
||||
1.0f, 0.0f,
|
||||
0.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
};
|
||||
|
||||
static const GLfloat fadeVertices[] = {
|
||||
// XS YT
|
||||
-1.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
-1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
};
|
||||
|
||||
OpenGLSDriver::OpenGLSDriver() :
|
||||
_surfaceShader(nullptr),
|
||||
_surfaceFillShader(nullptr),
|
||||
_actorShader(nullptr),
|
||||
_fadeShader(nullptr),
|
||||
_shadowShader(nullptr),
|
||||
_surfaceVBO(0),
|
||||
_fadeVBO(0) {
|
||||
}
|
||||
|
||||
OpenGLSDriver::~OpenGLSDriver() {
|
||||
OpenGL::Shader::freeBuffer(_surfaceVBO);
|
||||
OpenGL::Shader::freeBuffer(_fadeVBO);
|
||||
delete _surfaceFillShader;
|
||||
delete _surfaceShader;
|
||||
delete _actorShader;
|
||||
delete _fadeShader;
|
||||
delete _shadowShader;
|
||||
}
|
||||
|
||||
void OpenGLSDriver::init() {
|
||||
computeScreenViewport();
|
||||
|
||||
static const char* attributes[] = { "position", "texcoord", nullptr };
|
||||
_surfaceShader = OpenGL::Shader::fromFiles("stark_surface", attributes);
|
||||
_surfaceVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(surfaceVertices), surfaceVertices);
|
||||
_surfaceShader->enableVertexAttribute("position", _surfaceVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0);
|
||||
_surfaceShader->enableVertexAttribute("texcoord", _surfaceVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0);
|
||||
|
||||
static const char* fillAttributes[] = { "position", nullptr };
|
||||
_surfaceFillShader = OpenGL::Shader::fromFiles("stark_surface_fill", fillAttributes);
|
||||
_surfaceFillShader->enableVertexAttribute("position", _surfaceVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0);
|
||||
|
||||
static const char* actorAttributes[] = { "position1", "position2", "bone1", "bone2", "boneWeight", "normal", "texcoord", nullptr };
|
||||
_actorShader = OpenGL::Shader::fromFiles("stark_actor", actorAttributes);
|
||||
|
||||
static const char* shadowAttributes[] = { "position1", "position2", "bone1", "bone2", "boneWeight", nullptr };
|
||||
_shadowShader = OpenGL::Shader::fromFiles("stark_shadow", shadowAttributes);
|
||||
|
||||
static const char* fadeAttributes[] = { "position", nullptr };
|
||||
_fadeShader = OpenGL::Shader::fromFiles("stark_fade", fadeAttributes);
|
||||
_fadeVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(fadeVertices), fadeVertices);
|
||||
_fadeShader->enableVertexAttribute("position", _fadeVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0);
|
||||
}
|
||||
|
||||
void OpenGLSDriver::setScreenViewport(bool noScaling) {
|
||||
if (noScaling) {
|
||||
_viewport = Common::Rect(g_system->getWidth(), g_system->getHeight());
|
||||
_unscaledViewport = _viewport;
|
||||
} else {
|
||||
_viewport = _screenViewport;
|
||||
_unscaledViewport = Common::Rect(kOriginalWidth, kOriginalHeight);
|
||||
}
|
||||
|
||||
glViewport(_viewport.left, _viewport.top, _viewport.width(), _viewport.height());
|
||||
}
|
||||
|
||||
void OpenGLSDriver::setViewport(const Common::Rect &rect) {
|
||||
_viewport = Common::Rect(
|
||||
_screenViewport.width() * rect.width() / kOriginalWidth,
|
||||
_screenViewport.height() * rect.height() / kOriginalHeight
|
||||
);
|
||||
|
||||
_viewport.translate(
|
||||
_screenViewport.left + _screenViewport.width() * rect.left / kOriginalWidth,
|
||||
_screenViewport.top + _screenViewport.height() * rect.top / kOriginalHeight
|
||||
);
|
||||
|
||||
_unscaledViewport = rect;
|
||||
|
||||
glViewport(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height());
|
||||
}
|
||||
|
||||
void OpenGLSDriver::clearScreen() {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void OpenGLSDriver::flipBuffer() {
|
||||
g_system->updateScreen();
|
||||
}
|
||||
|
||||
Texture *OpenGLSDriver::createTexture() {
|
||||
return new OpenGlTexture();
|
||||
}
|
||||
|
||||
Bitmap *OpenGLSDriver::createBitmap(const Graphics::Surface *surface, const byte *palette) {
|
||||
OpenGlBitmap *bitmap = new OpenGlBitmap();
|
||||
|
||||
if (surface) {
|
||||
bitmap->update(surface, palette);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
VisualActor *OpenGLSDriver::createActorRenderer() {
|
||||
return new OpenGLSActorRenderer(this);
|
||||
}
|
||||
|
||||
VisualProp *OpenGLSDriver::createPropRenderer() {
|
||||
return new OpenGLSPropRenderer(this);
|
||||
}
|
||||
|
||||
SurfaceRenderer *OpenGLSDriver::createSurfaceRenderer() {
|
||||
return new OpenGLSSurfaceRenderer(this);
|
||||
}
|
||||
|
||||
FadeRenderer *OpenGLSDriver::createFadeRenderer() {
|
||||
return new OpenGLSFadeRenderer(this);
|
||||
}
|
||||
|
||||
void OpenGLSDriver::start2DMode() {
|
||||
// Enable alpha blending
|
||||
glEnable(GL_BLEND);
|
||||
//glBlendEquation(GL_FUNC_ADD); // It's the default
|
||||
|
||||
// This blend mode prevents color fringes due to filtering.
|
||||
// It requires the textures to have their color values pre-multiplied
|
||||
// with their alpha value. This is the "Premultiplied Alpha" technique.
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
}
|
||||
|
||||
void OpenGLSDriver::end2DMode() {
|
||||
// Disable alpha blending
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
void OpenGLSDriver::set3DMode() {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
// Blending and stencil test are only used in rendering shadows
|
||||
// They are manually enabled and disabled there
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glStencilFunc(GL_EQUAL, 0, 0xFF);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
|
||||
}
|
||||
|
||||
bool OpenGLSDriver::computeLightsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::Rect OpenGLSDriver::getViewport() const {
|
||||
return _viewport;
|
||||
}
|
||||
|
||||
Common::Rect OpenGLSDriver::getUnscaledViewport() const {
|
||||
return _unscaledViewport;
|
||||
}
|
||||
|
||||
OpenGL::Shader *OpenGLSDriver::createActorShaderInstance() {
|
||||
return _actorShader->clone();
|
||||
}
|
||||
|
||||
OpenGL::Shader *OpenGLSDriver::createSurfaceShaderInstance() {
|
||||
return _surfaceShader->clone();
|
||||
}
|
||||
|
||||
OpenGL::Shader *OpenGLSDriver::createSurfaceFillShaderInstance() {
|
||||
return _surfaceFillShader->clone();
|
||||
}
|
||||
|
||||
OpenGL::Shader *OpenGLSDriver::createFadeShaderInstance() {
|
||||
return _fadeShader->clone();
|
||||
}
|
||||
|
||||
OpenGL::Shader *OpenGLSDriver::createShadowShaderInstance() {
|
||||
return _shadowShader->clone();
|
||||
}
|
||||
|
||||
Graphics::Surface *OpenGLSDriver::getViewportScreenshot() const {
|
||||
Graphics::Surface *s = new Graphics::Surface();
|
||||
s->create(_viewport.width(), _viewport.height(), getRGBAPixelFormat());
|
||||
|
||||
glReadPixels(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height(),
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, s->getPixels());
|
||||
|
||||
flipVertical(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_SHADERS)
|
||||
94
engines/stark/gfx/opengls.h
Normal file
94
engines/stark/gfx/opengls.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/* 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 STARK_GFX_OPENGLS_H
|
||||
#define STARK_GFX_OPENGLS_H
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#if defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
namespace OpenGL {
|
||||
class Shader;
|
||||
}
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class OpenGLSDriver : public Driver {
|
||||
public:
|
||||
OpenGLSDriver();
|
||||
~OpenGLSDriver();
|
||||
|
||||
void init() override;
|
||||
|
||||
void setScreenViewport(bool noScaling) override;
|
||||
void setViewport(const Common::Rect &rect) override;
|
||||
|
||||
void clearScreen() override;
|
||||
void flipBuffer() override;
|
||||
|
||||
Texture *createTexture() override;
|
||||
Bitmap *createBitmap(const Graphics::Surface *surface = nullptr, const byte *palette = nullptr) override;
|
||||
VisualActor *createActorRenderer() override;
|
||||
VisualProp *createPropRenderer() override;
|
||||
SurfaceRenderer *createSurfaceRenderer() override;
|
||||
FadeRenderer *createFadeRenderer() override;
|
||||
|
||||
OpenGL::Shader *createActorShaderInstance();
|
||||
OpenGL::Shader *createSurfaceShaderInstance();
|
||||
OpenGL::Shader *createSurfaceFillShaderInstance();
|
||||
OpenGL::Shader *createFadeShaderInstance();
|
||||
OpenGL::Shader *createShadowShaderInstance();
|
||||
|
||||
void start2DMode();
|
||||
void end2DMode();
|
||||
void set3DMode() override;
|
||||
bool computeLightsEnabled() override;
|
||||
|
||||
Common::Rect getViewport() const;
|
||||
Common::Rect getUnscaledViewport() const;
|
||||
|
||||
Graphics::Surface *getViewportScreenshot() const override;
|
||||
|
||||
private:
|
||||
Common::Rect _viewport;
|
||||
Common::Rect _unscaledViewport;
|
||||
|
||||
OpenGL::Shader *_surfaceShader;
|
||||
OpenGL::Shader *_surfaceFillShader;
|
||||
OpenGL::Shader *_actorShader;
|
||||
OpenGL::Shader *_fadeShader;
|
||||
OpenGL::Shader *_shadowShader;
|
||||
GLuint _surfaceVBO;
|
||||
GLuint _fadeVBO;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#endif // STARK_GFX_OPENGLS_H
|
||||
431
engines/stark/gfx/openglsactor.cpp
Normal file
431
engines/stark/gfx/openglsactor.cpp
Normal file
@@ -0,0 +1,431 @@
|
||||
/* 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/stark/gfx/openglsactor.h"
|
||||
|
||||
#include "engines/stark/model/model.h"
|
||||
#include "engines/stark/model/animhandler.h"
|
||||
#include "engines/stark/scene.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/settings.h"
|
||||
#include "engines/stark/gfx/opengls.h"
|
||||
#include "engines/stark/gfx/texture.h"
|
||||
|
||||
#if defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#include "graphics/opengl/shader.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
OpenGLSActorRenderer::OpenGLSActorRenderer(OpenGLSDriver *gfx) :
|
||||
VisualActor(),
|
||||
_gfx(gfx),
|
||||
_faceVBO(0) {
|
||||
_shader = _gfx->createActorShaderInstance();
|
||||
_shadowShader = _gfx->createShadowShaderInstance();
|
||||
}
|
||||
|
||||
OpenGLSActorRenderer::~OpenGLSActorRenderer() {
|
||||
clearVertices();
|
||||
|
||||
delete _shader;
|
||||
delete _shadowShader;
|
||||
}
|
||||
|
||||
void OpenGLSActorRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
|
||||
if (_modelIsDirty) {
|
||||
// Update the OpenGL Buffer Objects if required
|
||||
clearVertices();
|
||||
uploadVertices();
|
||||
_modelIsDirty = false;
|
||||
}
|
||||
|
||||
// TODO: Move updates outside of the rendering code
|
||||
_animHandler->animate(_time);
|
||||
_model->updateBoundingBox();
|
||||
|
||||
_gfx->set3DMode();
|
||||
|
||||
Math::Matrix4 model = getModelMatrix(position, direction);
|
||||
Math::Matrix4 view = StarkScene->getViewMatrix();
|
||||
Math::Matrix4 projection = StarkScene->getProjectionMatrix();
|
||||
|
||||
Math::Matrix4 modelViewMatrix = view * model;
|
||||
modelViewMatrix.transpose(); // OpenGL expects matrices transposed
|
||||
|
||||
Math::Matrix4 projectionMatrix = projection;
|
||||
projectionMatrix.transpose(); // OpenGL expects matrices transposed
|
||||
|
||||
Math::Matrix4 normalMatrix = modelViewMatrix;
|
||||
normalMatrix.invertAffineOrthonormal();
|
||||
|
||||
_shader->enableVertexAttribute("position1", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 0);
|
||||
_shader->enableVertexAttribute("position2", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 12);
|
||||
_shader->enableVertexAttribute("bone1", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 24);
|
||||
_shader->enableVertexAttribute("bone2", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 28);
|
||||
_shader->enableVertexAttribute("boneWeight", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 32);
|
||||
_shader->enableVertexAttribute("normal", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 36);
|
||||
_shader->enableVertexAttribute("texcoord", _faceVBO, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 48);
|
||||
_shader->use(true);
|
||||
|
||||
_shader->setUniform("modelViewMatrix", modelViewMatrix);
|
||||
_shader->setUniform("projectionMatrix", projectionMatrix);
|
||||
_shader->setUniform("normalMatrix", normalMatrix.getRotation());
|
||||
setBoneRotationArrayUniform(_shader, "boneRotation");
|
||||
setBonePositionArrayUniform(_shader, "bonePosition");
|
||||
setLightArrayUniform(lights);
|
||||
|
||||
Common::Array<Face *> faces = _model->getFaces();
|
||||
Common::Array<Material *> mats = _model->getMaterials();
|
||||
|
||||
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
// For each face draw its vertices from the VBO, indexed by the EBO
|
||||
const Material *material = mats[(*face)->materialId];
|
||||
const Gfx::Texture *tex = resolveTexture(material);
|
||||
if (tex) {
|
||||
tex->bind();
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
_shader->setUniform("textured", tex != nullptr);
|
||||
_shader->setUniform("color", Math::Vector3d(material->r, material->g, material->b));
|
||||
|
||||
GLuint ebo = _faceEBO[*face];
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
glDrawElements(GL_TRIANGLES, (*face)->vertexIndices.size(), GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
|
||||
_shader->unbind();
|
||||
|
||||
if (_castsShadow &&
|
||||
StarkScene->shouldRenderShadows() &&
|
||||
StarkSettings->getBoolSetting(Settings::kShadow)) {
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
|
||||
_shadowShader->enableVertexAttribute("position1", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 0);
|
||||
_shadowShader->enableVertexAttribute("position2", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 12);
|
||||
_shadowShader->enableVertexAttribute("bone1", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 24);
|
||||
_shadowShader->enableVertexAttribute("bone2", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 28);
|
||||
_shadowShader->enableVertexAttribute("boneWeight", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 32);
|
||||
_shadowShader->use(true);
|
||||
|
||||
Math::Matrix4 mvp = projection * view * model;
|
||||
mvp.transpose();
|
||||
_shadowShader->setUniform("mvp", mvp);
|
||||
|
||||
setBoneRotationArrayUniform(_shadowShader, "boneRotation");
|
||||
setBonePositionArrayUniform(_shadowShader, "bonePosition");
|
||||
|
||||
Math::Matrix4 modelInverse = model;
|
||||
modelInverse.inverse();
|
||||
setShadowUniform(lights, position, modelInverse.getRotation());
|
||||
|
||||
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
GLuint ebo = _faceEBO[*face];
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
glDrawElements(GL_TRIANGLES, (*face)->vertexIndices.size(), GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
_shadowShader->unbind();
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLSActorRenderer::clearVertices() {
|
||||
OpenGL::Shader::freeBuffer(_faceVBO); // Zero names are silently ignored
|
||||
_faceVBO = 0;
|
||||
|
||||
for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
|
||||
OpenGL::Shader::freeBuffer(it->_value);
|
||||
}
|
||||
|
||||
_faceEBO.clear();
|
||||
}
|
||||
|
||||
void OpenGLSActorRenderer::uploadVertices() {
|
||||
_faceVBO = createModelVBO(_model);
|
||||
|
||||
Common::Array<Face *> faces = _model->getFaces();
|
||||
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
_faceEBO[*face] = createFaceEBO(*face);
|
||||
}
|
||||
}
|
||||
|
||||
GLuint OpenGLSActorRenderer::createModelVBO(const Model *model) {
|
||||
const Common::Array<VertNode *> &modelVertices = model->getVertices();
|
||||
|
||||
float *vertices = new float[14 * modelVertices.size()];
|
||||
float *vertPtr = vertices;
|
||||
|
||||
// Build a vertex array
|
||||
for (Common::Array<VertNode *>::const_iterator tri = modelVertices.begin(); tri != modelVertices.end(); ++tri) {
|
||||
*vertPtr++ = (*tri)->_pos1.x();
|
||||
*vertPtr++ = (*tri)->_pos1.y();
|
||||
*vertPtr++ = (*tri)->_pos1.z();
|
||||
|
||||
*vertPtr++ = (*tri)->_pos2.x();
|
||||
*vertPtr++ = (*tri)->_pos2.y();
|
||||
*vertPtr++ = (*tri)->_pos2.z();
|
||||
|
||||
*vertPtr++ = (*tri)->_bone1;
|
||||
*vertPtr++ = (*tri)->_bone2;
|
||||
|
||||
*vertPtr++ = (*tri)->_boneWeight;
|
||||
|
||||
*vertPtr++ = (*tri)->_normal.x();
|
||||
*vertPtr++ = (*tri)->_normal.y();
|
||||
*vertPtr++ = (*tri)->_normal.z();
|
||||
|
||||
*vertPtr++ = -(*tri)->_texS;
|
||||
*vertPtr++ = (*tri)->_texT;
|
||||
}
|
||||
|
||||
GLuint vbo = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(float) * 14 * modelVertices.size(), vertices);
|
||||
delete[] vertices;
|
||||
|
||||
return vbo;
|
||||
}
|
||||
|
||||
GLuint OpenGLSActorRenderer::createFaceEBO(const Face *face) {
|
||||
return OpenGL::Shader::createBuffer(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32) * face->vertexIndices.size(), &face->vertexIndices[0]);
|
||||
}
|
||||
|
||||
void OpenGLSActorRenderer::setBonePositionArrayUniform(OpenGL::Shader *shader, const char *uniform) {
|
||||
const Common::Array<BoneNode *> &bones = _model->getBones();
|
||||
|
||||
GLint pos = shader->getUniformLocation(uniform);
|
||||
if (pos == -1) {
|
||||
error("No uniform named '%s'", uniform);
|
||||
}
|
||||
|
||||
float *positions = new float[3 * bones.size()];
|
||||
float *positionsPtr = positions;
|
||||
|
||||
for (uint i = 0; i < bones.size(); i++) {
|
||||
*positionsPtr++ = bones[i]->_animPos.x();
|
||||
*positionsPtr++ = bones[i]->_animPos.y();
|
||||
*positionsPtr++ = bones[i]->_animPos.z();
|
||||
}
|
||||
|
||||
glUniform3fv(pos, bones.size(), positions);
|
||||
delete[] positions;
|
||||
}
|
||||
|
||||
void OpenGLSActorRenderer::setBoneRotationArrayUniform(OpenGL::Shader *shader, const char *uniform) {
|
||||
const Common::Array<BoneNode *> &bones = _model->getBones();
|
||||
|
||||
GLint rot = shader->getUniformLocation(uniform);
|
||||
if (rot == -1) {
|
||||
error("No uniform named '%s'", uniform);
|
||||
}
|
||||
|
||||
float *rotations = new float[4 * bones.size()];
|
||||
float *rotationsPtr = rotations;
|
||||
|
||||
for (uint i = 0; i < bones.size(); i++) {
|
||||
*rotationsPtr++ = bones[i]->_animRot.x();
|
||||
*rotationsPtr++ = bones[i]->_animRot.y();
|
||||
*rotationsPtr++ = bones[i]->_animRot.z();
|
||||
*rotationsPtr++ = bones[i]->_animRot.w();
|
||||
}
|
||||
|
||||
glUniform4fv(rot, bones.size(), rotations);
|
||||
delete[] rotations;
|
||||
}
|
||||
|
||||
void OpenGLSActorRenderer::setLightArrayUniform(const LightEntryArray &lights) {
|
||||
static const uint maxLights = 10;
|
||||
|
||||
assert(lights.size() >= 1);
|
||||
assert(lights.size() <= maxLights);
|
||||
|
||||
const LightEntry *ambient = lights[0];
|
||||
assert(ambient->type == LightEntry::kAmbient); // The first light must be the ambient light
|
||||
_shader->setUniform("ambientColor", ambient->color);
|
||||
|
||||
Math::Matrix4 viewMatrix = StarkScene->getViewMatrix();
|
||||
Math::Matrix3 viewMatrixRot = viewMatrix.getRotation();
|
||||
|
||||
for (uint i = 0; i < lights.size() - 1; i++) {
|
||||
const LightEntry *l = lights[i + 1];
|
||||
|
||||
Math::Vector4d worldPosition;
|
||||
worldPosition.x() = l->position.x();
|
||||
worldPosition.y() = l->position.y();
|
||||
worldPosition.z() = l->position.z();
|
||||
worldPosition.w() = 1.0;
|
||||
|
||||
Math::Vector4d eyePosition = viewMatrix * worldPosition;
|
||||
|
||||
// The light type is stored in the w coordinate of the position to save an uniform slot
|
||||
eyePosition.w() = l->type;
|
||||
|
||||
Math::Vector3d worldDirection = l->direction;
|
||||
Math::Vector3d eyeDirection = viewMatrixRot * worldDirection;
|
||||
eyeDirection.normalize();
|
||||
|
||||
_shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), eyePosition);
|
||||
_shader->setUniform(Common::String::format("lights[%d].direction", i).c_str(), eyeDirection);
|
||||
_shader->setUniform(Common::String::format("lights[%d].color", i).c_str(), l->color);
|
||||
|
||||
Math::Vector4d params;
|
||||
params.x() = l->falloffNear;
|
||||
params.y() = l->falloffFar;
|
||||
params.z() = l->innerConeAngle.getCosine();
|
||||
params.w() = l->outerConeAngle.getCosine();
|
||||
|
||||
_shader->setUniform(Common::String::format("lights[%d].params", i).c_str(), params);
|
||||
}
|
||||
|
||||
for (uint i = lights.size() - 1; i < maxLights; i++) {
|
||||
// Make sure unused lights are disabled
|
||||
_shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), Math::Vector4d());
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLSActorRenderer::setShadowUniform(const LightEntryArray &lights, const Math::Vector3d &actorPosition,
|
||||
Math::Matrix3 worldToModelRot) {
|
||||
Math::Vector3d sumDirection;
|
||||
bool hasLight = false;
|
||||
|
||||
// Compute the contribution from each lights
|
||||
// The ambient light is skipped intentionally
|
||||
for (uint i = 1; i < lights.size(); ++i) {
|
||||
LightEntry *light = lights[i];
|
||||
bool contributes = false;
|
||||
|
||||
Math::Vector3d lightDirection;
|
||||
switch (light->type) {
|
||||
case LightEntry::kPoint:
|
||||
contributes = getPointLightContribution(light, actorPosition, lightDirection);
|
||||
break;
|
||||
case LightEntry::kDirectional:
|
||||
contributes = getDirectionalLightContribution(light, lightDirection);
|
||||
break;
|
||||
case LightEntry::kSpot:
|
||||
contributes = getSpotLightContribution(light, actorPosition, lightDirection);
|
||||
break;
|
||||
case LightEntry::kAmbient:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (contributes) {
|
||||
sumDirection += lightDirection;
|
||||
hasLight = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasLight) {
|
||||
// Clip the horizontal length
|
||||
Math::Vector2d horizontalProjection(sumDirection.x(), sumDirection.y());
|
||||
float shadowLength = MIN(horizontalProjection.getMagnitude(), StarkScene->getMaxShadowLength());
|
||||
|
||||
horizontalProjection.normalize();
|
||||
horizontalProjection *= shadowLength;
|
||||
|
||||
sumDirection.x() = horizontalProjection.getX();
|
||||
sumDirection.y() = horizontalProjection.getY();
|
||||
sumDirection.z() = -1;
|
||||
} else {
|
||||
// Cast from above by default
|
||||
sumDirection.x() = 0;
|
||||
sumDirection.y() = 0;
|
||||
sumDirection.z() = -1;
|
||||
}
|
||||
|
||||
//Transform the direction to the model space and pass to the shader
|
||||
sumDirection = worldToModelRot * sumDirection;
|
||||
_shadowShader->setUniform("lightDirection", sumDirection);
|
||||
}
|
||||
|
||||
bool OpenGLSActorRenderer::getPointLightContribution(LightEntry *light, const Math::Vector3d &actorPosition,
|
||||
Math::Vector3d &direction, float weight) {
|
||||
float distance = light->position.getDistanceTo(actorPosition);
|
||||
|
||||
if (distance > light->falloffFar) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float factor;
|
||||
if (distance > light->falloffNear) {
|
||||
if (light->falloffFar - light->falloffNear > 1) {
|
||||
factor = 1 - (distance - light->falloffNear) / (light->falloffFar - light->falloffNear);
|
||||
} else {
|
||||
factor = 0;
|
||||
}
|
||||
} else {
|
||||
factor = 1;
|
||||
}
|
||||
|
||||
float brightness = (light->color.x() + light->color.y() + light->color.z()) / 3.0f;
|
||||
|
||||
if (factor <= 0 || brightness <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
direction = actorPosition - light->position;
|
||||
direction.normalize();
|
||||
direction *= factor * brightness * weight;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLSActorRenderer::getDirectionalLightContribution(LightEntry *light, Math::Vector3d &direction) {
|
||||
float brightness = (light->color.x() + light->color.y() + light->color.z()) / 3.0f;
|
||||
|
||||
if (brightness <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
direction = light->direction;
|
||||
direction.normalize();
|
||||
direction *= brightness;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLSActorRenderer::getSpotLightContribution(LightEntry *light, const Math::Vector3d &actorPosition,
|
||||
Math::Vector3d &direction) {
|
||||
Math::Vector3d lightToActor = actorPosition - light->position;
|
||||
lightToActor.normalize();
|
||||
|
||||
float cosAngle = MAX(0.0f, lightToActor.dotProduct(light->direction));
|
||||
float cone = (cosAngle - light->innerConeAngle.getCosine()) /
|
||||
MAX(0.001f, light->outerConeAngle.getCosine() - light->innerConeAngle.getCosine());
|
||||
cone = CLIP(cone, 0.0f, 1.0f);
|
||||
|
||||
if (cone <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getPointLightContribution(light, actorPosition, direction, cone);
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_SHADERS)
|
||||
81
engines/stark/gfx/openglsactor.h
Normal file
81
engines/stark/gfx/openglsactor.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/* 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 STARK_GFX_OPENGL_S_ACTOR_H
|
||||
#define STARK_GFX_OPENGL_S_ACTOR_H
|
||||
|
||||
#include "engines/stark/gfx/renderentry.h"
|
||||
#include "engines/stark/visual/actor.h"
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-ptr.h"
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if defined(USE_OPENGL_SHADERS)
|
||||
|
||||
namespace OpenGL {
|
||||
class Shader;
|
||||
}
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class OpenGLSDriver;
|
||||
|
||||
class OpenGLSActorRenderer : public VisualActor {
|
||||
public:
|
||||
OpenGLSActorRenderer(OpenGLSDriver *gfx);
|
||||
virtual ~OpenGLSActorRenderer();
|
||||
|
||||
void render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) override;
|
||||
|
||||
protected:
|
||||
typedef Common::HashMap<Face *, GLuint> FaceBufferMap;
|
||||
|
||||
OpenGLSDriver *_gfx;
|
||||
OpenGL::Shader *_shader, *_shadowShader;
|
||||
|
||||
GLuint _faceVBO;
|
||||
FaceBufferMap _faceEBO;
|
||||
|
||||
void clearVertices();
|
||||
void uploadVertices();
|
||||
GLuint createModelVBO(const Model *model);
|
||||
GLuint createFaceEBO(const Face *face);
|
||||
void setBonePositionArrayUniform(OpenGL::Shader *shader, const char *uniform);
|
||||
void setBoneRotationArrayUniform(OpenGL::Shader *shader, const char *uniform);
|
||||
void setLightArrayUniform(const LightEntryArray &lights);
|
||||
|
||||
void setShadowUniform(const LightEntryArray &lights, const Math::Vector3d &actorPosition, Math::Matrix3 worldToModelRot);
|
||||
|
||||
bool getPointLightContribution(LightEntry *light, const Math::Vector3d &actorPosition,
|
||||
Math::Vector3d &direction, float weight = 1.0f);
|
||||
bool getDirectionalLightContribution(LightEntry *light, Math::Vector3d &direction);
|
||||
bool getSpotLightContribution(LightEntry *light, const Math::Vector3d &actorPosition, Math::Vector3d &direction);
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#endif // STARK_GFX_OPENGL_S_ACTOR_H
|
||||
57
engines/stark/gfx/openglsfade.cpp
Normal file
57
engines/stark/gfx/openglsfade.cpp
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/stark/gfx/openglsfade.h"
|
||||
|
||||
#include "engines/stark/gfx/opengls.h"
|
||||
|
||||
#if defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#include "graphics/opengl/shader.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
OpenGLSFadeRenderer::OpenGLSFadeRenderer(OpenGLSDriver *gfx) :
|
||||
FadeRenderer(),
|
||||
_gfx(gfx) {
|
||||
_shader = _gfx->createFadeShaderInstance();
|
||||
}
|
||||
|
||||
OpenGLSFadeRenderer::~OpenGLSFadeRenderer() {
|
||||
delete _shader;
|
||||
}
|
||||
|
||||
void OpenGLSFadeRenderer::render(float fadeLevel) {
|
||||
_gfx->start2DMode();
|
||||
|
||||
_shader->use();
|
||||
_shader->setUniform1f("alphaLevel", 1.0 - fadeLevel);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
_shader->unbind();
|
||||
|
||||
_gfx->end2DMode();
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_SHADERS)
|
||||
61
engines/stark/gfx/openglsfade.h
Normal file
61
engines/stark/gfx/openglsfade.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* 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 STARK_GFX_OPENGL_S_FADE_H
|
||||
#define STARK_GFX_OPENGL_S_FADE_H
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#include "engines/stark/gfx/faderenderer.h"
|
||||
|
||||
namespace OpenGL {
|
||||
class Shader;
|
||||
}
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class OpenGLSDriver;
|
||||
|
||||
/**
|
||||
* A programmable pipeline OpenGL fade screen renderer
|
||||
*/
|
||||
class OpenGLSFadeRenderer : public FadeRenderer {
|
||||
public:
|
||||
OpenGLSFadeRenderer(OpenGLSDriver *gfx);
|
||||
~OpenGLSFadeRenderer();
|
||||
|
||||
// FadeRenderer API
|
||||
void render(float fadeLevel);
|
||||
|
||||
private:
|
||||
OpenGLSDriver *_gfx;
|
||||
OpenGL::Shader *_shader;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#endif // STARK_GFX_OPENGL_S_FADE_H
|
||||
193
engines/stark/gfx/openglsprop.cpp
Normal file
193
engines/stark/gfx/openglsprop.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/* 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/stark/gfx/openglsprop.h"
|
||||
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/gfx/texture.h"
|
||||
#include "engines/stark/formats/biffmesh.h"
|
||||
#include "engines/stark/scene.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
|
||||
#if defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#include "graphics/opengl/shader.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
OpenGLSPropRenderer::OpenGLSPropRenderer(Driver *gfx) :
|
||||
VisualProp(),
|
||||
_gfx(gfx),
|
||||
_faceVBO(0),
|
||||
_modelIsDirty(true) {
|
||||
static const char* attributes[] = { "position", "normal", "texcoord", nullptr };
|
||||
_shader = OpenGL::Shader::fromFiles("stark_prop", attributes);
|
||||
}
|
||||
|
||||
OpenGLSPropRenderer::~OpenGLSPropRenderer() {
|
||||
clearVertices();
|
||||
|
||||
delete _shader;
|
||||
}
|
||||
|
||||
void OpenGLSPropRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
|
||||
if (_modelIsDirty) {
|
||||
// Update the OpenGL Buffer Objects if required
|
||||
clearVertices();
|
||||
uploadVertices();
|
||||
_modelIsDirty = false;
|
||||
}
|
||||
|
||||
_gfx->set3DMode();
|
||||
|
||||
Math::Matrix4 model = getModelMatrix(position, direction);
|
||||
Math::Matrix4 view = StarkScene->getViewMatrix();
|
||||
Math::Matrix4 projection = StarkScene->getProjectionMatrix();
|
||||
|
||||
Math::Matrix4 modelViewMatrix = view * model;
|
||||
modelViewMatrix.transpose(); // OpenGL expects matrices transposed
|
||||
|
||||
Math::Matrix4 projectionMatrix = projection;
|
||||
projectionMatrix.transpose(); // OpenGL expects matrices transposed
|
||||
|
||||
Math::Matrix4 normalMatrix = modelViewMatrix;
|
||||
normalMatrix.invertAffineOrthonormal();
|
||||
|
||||
_shader->enableVertexAttribute("position", _faceVBO, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), 0);
|
||||
_shader->enableVertexAttribute("normal", _faceVBO, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), 12);
|
||||
_shader->enableVertexAttribute("texcoord", _faceVBO, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), 24);
|
||||
_shader->use(true);
|
||||
|
||||
_shader->setUniform("modelViewMatrix", modelViewMatrix);
|
||||
_shader->setUniform("projectionMatrix", projectionMatrix);
|
||||
_shader->setUniform("normalMatrix", normalMatrix.getRotation());
|
||||
setLightArrayUniform(lights);
|
||||
|
||||
const Common::Array<Face> &faces = _model->getFaces();
|
||||
const Common::Array<Material> &materials = _model->getMaterials();
|
||||
|
||||
for (Common::Array<Face>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
const Material &material = materials[face->materialId];
|
||||
|
||||
// For each face draw its vertices from the VBO, indexed by the EBO
|
||||
const Gfx::Texture *tex = _texture->getTexture(material.texture);
|
||||
if (tex) {
|
||||
tex->bind();
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
_shader->setUniform("textured", tex != nullptr);
|
||||
_shader->setUniform("color", Math::Vector3d(material.r, material.g, material.b));
|
||||
_shader->setUniform("doubleSided", material.doubleSided ? 1 : 0);
|
||||
|
||||
GLuint ebo = _faceEBO[face];
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
glDrawElements(GL_TRIANGLES, face->vertexIndices.size(), GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
|
||||
_shader->unbind();
|
||||
}
|
||||
|
||||
void OpenGLSPropRenderer::clearVertices() {
|
||||
OpenGL::Shader::freeBuffer(_faceVBO);
|
||||
|
||||
for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
|
||||
OpenGL::Shader::freeBuffer(it->_value);
|
||||
}
|
||||
|
||||
_faceEBO.clear();
|
||||
}
|
||||
|
||||
void OpenGLSPropRenderer::uploadVertices() {
|
||||
_faceVBO = createFaceVBO();
|
||||
|
||||
const Common::Array<Face> &faces = _model->getFaces();
|
||||
for (Common::Array<Face>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
_faceEBO[face] = createFaceEBO(face);
|
||||
}
|
||||
}
|
||||
|
||||
GLuint OpenGLSPropRenderer::createFaceVBO() {
|
||||
const Common::Array<Formats::BiffMesh::Vertex> &vertices = _model->getVertices();
|
||||
|
||||
return OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(float) * 9 * vertices.size(), &vertices.front());
|
||||
}
|
||||
|
||||
GLuint OpenGLSPropRenderer::createFaceEBO(const Face *face) {
|
||||
return OpenGL::Shader::createBuffer(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32) * face->vertexIndices.size(), &face->vertexIndices.front());
|
||||
}
|
||||
|
||||
void OpenGLSPropRenderer::setLightArrayUniform(const LightEntryArray &lights) {
|
||||
static const uint maxLights = 10;
|
||||
|
||||
assert(lights.size() >= 1);
|
||||
assert(lights.size() <= maxLights);
|
||||
|
||||
const LightEntry *ambient = lights[0];
|
||||
assert(ambient->type == LightEntry::kAmbient); // The first light must be the ambient light
|
||||
_shader->setUniform("ambientColor", ambient->color);
|
||||
|
||||
Math::Matrix4 viewMatrix = StarkScene->getViewMatrix();
|
||||
Math::Matrix3 viewMatrixRot = viewMatrix.getRotation();
|
||||
|
||||
for (uint i = 0; i < lights.size() - 1; i++) {
|
||||
const LightEntry *l = lights[i + 1];
|
||||
|
||||
Math::Vector4d worldPosition;
|
||||
worldPosition.x() = l->position.x();
|
||||
worldPosition.y() = l->position.y();
|
||||
worldPosition.z() = l->position.z();
|
||||
worldPosition.w() = 1.0;
|
||||
|
||||
Math::Vector4d eyePosition = viewMatrix * worldPosition;
|
||||
|
||||
// The light type is stored in the w coordinate of the position to save an uniform slot
|
||||
eyePosition.w() = l->type;
|
||||
|
||||
Math::Vector3d worldDirection = l->direction;
|
||||
Math::Vector3d eyeDirection = viewMatrixRot * worldDirection;
|
||||
eyeDirection.normalize();
|
||||
|
||||
_shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), eyePosition);
|
||||
_shader->setUniform(Common::String::format("lights[%d].direction", i).c_str(), eyeDirection);
|
||||
_shader->setUniform(Common::String::format("lights[%d].color", i).c_str(), l->color);
|
||||
|
||||
Math::Vector4d params;
|
||||
params.x() = l->falloffNear;
|
||||
params.y() = l->falloffFar;
|
||||
params.z() = l->innerConeAngle.getCosine();
|
||||
params.w() = l->outerConeAngle.getCosine();
|
||||
|
||||
_shader->setUniform(Common::String::format("lights[%d].params", i).c_str(), params);
|
||||
}
|
||||
|
||||
for (uint i = lights.size() - 1; i < maxLights; i++) {
|
||||
// Make sure unused lights are disabled
|
||||
_shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), Math::Vector4d());
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_SHADERS)
|
||||
76
engines/stark/gfx/openglsprop.h
Normal file
76
engines/stark/gfx/openglsprop.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* 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 STARK_GFX_OPENGL_S_RENDERED_H
|
||||
#define STARK_GFX_OPENGL_S_RENDERED_H
|
||||
|
||||
#include "engines/stark/model/model.h"
|
||||
#include "engines/stark/visual/prop.h"
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-ptr.h"
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if defined(USE_OPENGL_SHADERS)
|
||||
|
||||
namespace OpenGL {
|
||||
class Shader;
|
||||
}
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
class Driver;
|
||||
|
||||
class OpenGLSPropRenderer : public VisualProp {
|
||||
public:
|
||||
explicit OpenGLSPropRenderer(Driver *gfx);
|
||||
~OpenGLSPropRenderer() override;
|
||||
|
||||
void render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) override;
|
||||
|
||||
protected:
|
||||
typedef Common::HashMap<const Face *, GLuint> FaceBufferMap;
|
||||
|
||||
Driver *_gfx;
|
||||
OpenGL::Shader *_shader;
|
||||
|
||||
bool _modelIsDirty;
|
||||
GLuint _faceVBO;
|
||||
FaceBufferMap _faceEBO;
|
||||
|
||||
void clearVertices();
|
||||
void uploadVertices();
|
||||
GLuint createFaceVBO();
|
||||
GLuint createFaceEBO(const Face *face);
|
||||
|
||||
void setLightArrayUniform(const LightEntryArray &lights);
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_OPENGL_S_RENDERED_H
|
||||
|
||||
#endif // defined(USE_OPENGL_SHADERS)
|
||||
113
engines/stark/gfx/openglssurface.cpp
Normal file
113
engines/stark/gfx/openglssurface.cpp
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/stark/gfx/openglssurface.h"
|
||||
|
||||
#include "engines/stark/gfx/opengls.h"
|
||||
#include "engines/stark/gfx/bitmap.h"
|
||||
#include "engines/stark/gfx/color.h"
|
||||
|
||||
#if defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#include "graphics/opengl/shader.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
OpenGLSSurfaceRenderer::OpenGLSSurfaceRenderer(OpenGLSDriver *gfx) :
|
||||
SurfaceRenderer(),
|
||||
_gfx(gfx) {
|
||||
_shader = _gfx->createSurfaceShaderInstance();
|
||||
_shaderFill = _gfx->createSurfaceFillShaderInstance();
|
||||
}
|
||||
|
||||
OpenGLSSurfaceRenderer::~OpenGLSSurfaceRenderer() {
|
||||
delete _shaderFill;
|
||||
delete _shader;
|
||||
}
|
||||
|
||||
void OpenGLSSurfaceRenderer::render(const Bitmap *bitmap, const Common::Point &dest) {
|
||||
render(bitmap, dest, bitmap->width(), bitmap->height());
|
||||
}
|
||||
|
||||
void OpenGLSSurfaceRenderer::render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) {
|
||||
// Destination rectangle with given width and height
|
||||
_gfx->start2DMode();
|
||||
|
||||
_shader->use();
|
||||
_shader->setUniform1f("fadeLevel", _fadeLevel);
|
||||
_shader->setUniform("snapToGrid", _snapToGrid ? 1 : 0);
|
||||
_shader->setUniform("verOffsetXY", normalizeOriginalCoordinates(dest.x, dest.y));
|
||||
if (_noScalingOverride) {
|
||||
_shader->setUniform("verSizeWH", normalizeCurrentCoordinates(width, height));
|
||||
} else {
|
||||
_shader->setUniform("verSizeWH", normalizeOriginalCoordinates(width, height));
|
||||
}
|
||||
|
||||
Common::Rect nativeViewport = _gfx->getViewport();
|
||||
_shader->setUniform("viewport", Math::Vector2d(nativeViewport.width(), nativeViewport.height()));
|
||||
|
||||
bitmap->bind();
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
_shader->unbind();
|
||||
_gfx->end2DMode();
|
||||
}
|
||||
|
||||
void OpenGLSSurfaceRenderer::fill(const Color &color, const Common::Point &dest, uint width, uint height) {
|
||||
// Destination rectangle with given width and height
|
||||
_gfx->start2DMode();
|
||||
|
||||
_shaderFill->use();
|
||||
_shaderFill->setUniform1f("fadeLevel", _fadeLevel);
|
||||
_shaderFill->setUniform("snapToGrid", _snapToGrid ? 1 : 0);
|
||||
_shaderFill->setUniform("verOffsetXY", normalizeOriginalCoordinates(dest.x, dest.y));
|
||||
if (_noScalingOverride) {
|
||||
_shaderFill->setUniform("verSizeWH", normalizeCurrentCoordinates(width, height));
|
||||
} else {
|
||||
_shaderFill->setUniform("verSizeWH", normalizeOriginalCoordinates(width, height));
|
||||
}
|
||||
|
||||
Common::Rect nativeViewport = _gfx->getViewport();
|
||||
_shaderFill->setUniform("viewport", Math::Vector2d(nativeViewport.width(), nativeViewport.height()));
|
||||
|
||||
_shaderFill->setUniform("color", Math::Vector4d(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f));
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
_shaderFill->unbind();
|
||||
_gfx->end2DMode();
|
||||
}
|
||||
|
||||
Math::Vector2d OpenGLSSurfaceRenderer::normalizeOriginalCoordinates(int x, int y) const {
|
||||
Common::Rect viewport = _gfx->getUnscaledViewport();
|
||||
return Math::Vector2d(x / (float)viewport.width(), y / (float)viewport.height());
|
||||
}
|
||||
|
||||
Math::Vector2d OpenGLSSurfaceRenderer::normalizeCurrentCoordinates(int x, int y) const {
|
||||
Common::Rect viewport = _gfx->getViewport();
|
||||
return Math::Vector2d(x / (float)viewport.width(), y / (float)viewport.height());
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // if defined(USE_OPENGL_SHADERS)
|
||||
68
engines/stark/gfx/openglssurface.h
Normal file
68
engines/stark/gfx/openglssurface.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* 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 STARK_GFX_OPENGL_S_SURFACE_H
|
||||
#define STARK_GFX_OPENGL_S_SURFACE_H
|
||||
|
||||
#include "engines/stark/gfx/surfacerenderer.h"
|
||||
|
||||
#include "math/vector2d.h"
|
||||
|
||||
#if defined(USE_OPENGL_SHADERS)
|
||||
|
||||
namespace OpenGL {
|
||||
class Shader;
|
||||
}
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class OpenGLSDriver;
|
||||
class Bitmap;
|
||||
|
||||
/**
|
||||
* A programmable pipeline OpenGL surface renderer
|
||||
*/
|
||||
class OpenGLSSurfaceRenderer : public SurfaceRenderer {
|
||||
public:
|
||||
OpenGLSSurfaceRenderer(OpenGLSDriver *gfx);
|
||||
virtual ~OpenGLSSurfaceRenderer();
|
||||
|
||||
// SurfaceRenderer API
|
||||
void render(const Bitmap *bitmap, const Common::Point &dest) override;
|
||||
void render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) override;
|
||||
void fill(const Color &color, const Common::Point &dest, uint width, uint height) override;
|
||||
|
||||
private:
|
||||
Math::Vector2d normalizeOriginalCoordinates(int x, int y) const;
|
||||
Math::Vector2d normalizeCurrentCoordinates(int x, int y) const;
|
||||
|
||||
OpenGLSDriver *_gfx;
|
||||
OpenGL::Shader *_shader;
|
||||
OpenGL::Shader *_shaderFill;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#endif // STARK_GFX_OPENGL_S_SURFACE_H
|
||||
166
engines/stark/gfx/openglsurface.cpp
Normal file
166
engines/stark/gfx/openglsurface.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/* 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/stark/gfx/openglsurface.h"
|
||||
#include "engines/stark/gfx/openglbitmap.h"
|
||||
#include "engines/stark/gfx/color.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME)
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
OpenGLSurfaceRenderer::OpenGLSurfaceRenderer(OpenGLDriver *gfx) :
|
||||
SurfaceRenderer(),
|
||||
_gfx(gfx) {
|
||||
}
|
||||
|
||||
OpenGLSurfaceRenderer::~OpenGLSurfaceRenderer() {
|
||||
}
|
||||
|
||||
void OpenGLSurfaceRenderer::render(const Bitmap *bitmap, const Common::Point &dest) {
|
||||
render(bitmap, dest, bitmap->width(), bitmap->height());
|
||||
}
|
||||
|
||||
void OpenGLSurfaceRenderer::render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) {
|
||||
// Destination rectangle with given width and height
|
||||
_gfx->start2DMode();
|
||||
|
||||
SurfaceVertex vertices[4] = {};
|
||||
convertToVertices(vertices, dest, width, height);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(SurfaceVertex), &vertices[0].x);
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(float), ((const OpenGlBitmap *)bitmap)->getTexCoords());
|
||||
glColor4f(1.0f - _fadeLevel, 1.0f - _fadeLevel, 1.0f - _fadeLevel, 1.0f);
|
||||
|
||||
bitmap->bind();
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
|
||||
_gfx->end2DMode();
|
||||
}
|
||||
|
||||
void OpenGLSurfaceRenderer::fill(const Color &color, const Common::Point &dest, uint width, uint height) {
|
||||
_gfx->start2DMode();
|
||||
|
||||
SurfaceVertex vertices[4] = {};
|
||||
convertToVertices(vertices, dest, width, height);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(SurfaceVertex), &vertices[0].x);
|
||||
glColor4f((color.r / 255.0f) - _fadeLevel, (color.g / 255.0f) - _fadeLevel, (color.b / 255.0f) - _fadeLevel, color.a / 255.0f);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
|
||||
_gfx->end2DMode();
|
||||
}
|
||||
|
||||
void OpenGLSurfaceRenderer::convertToVertices(SurfaceVertex *vertices, const Common::Point &dest, uint width, uint height) const {
|
||||
const Math::Vector2d surfaceVertices[] = {
|
||||
// X Y
|
||||
{ 0.0f, 0.0f },
|
||||
{ 1.0f, 0.0f },
|
||||
{ 0.0f, 1.0f },
|
||||
{ 1.0f, 1.0f },
|
||||
};
|
||||
|
||||
Math::Vector2d verSizeWH;
|
||||
if (_noScalingOverride) {
|
||||
verSizeWH = normalizeCurrentCoordinates(width, height);
|
||||
} else {
|
||||
verSizeWH = normalizeOriginalCoordinates(width, height);
|
||||
}
|
||||
auto verOffsetXY = normalizeOriginalCoordinates(dest.x, dest.y);
|
||||
auto nativeViewport = _gfx->getViewport();
|
||||
auto viewport = Math::Vector2d(nativeViewport.width(), nativeViewport.height());
|
||||
|
||||
for (int32 v = 0; v < 4; v++) {
|
||||
Math::Vector2d pos = verOffsetXY + (surfaceVertices[v] * verSizeWH);
|
||||
|
||||
if (_snapToGrid) {
|
||||
// Align vertex coordinates to the native pixel grid
|
||||
// This ensures text does not get garbled by nearest neighbors scaling
|
||||
pos.setX(floor(pos.getX() * viewport.getX() + 0.5) / viewport.getX());
|
||||
pos.setY(floor(pos.getY() * viewport.getY() + 0.5) / viewport.getY());
|
||||
}
|
||||
|
||||
// position coords
|
||||
vertices[v].x = pos.getX() * 2.0 - 1.0;
|
||||
vertices[v].y = -1.0 * (pos.getY() * 2.0 - 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
Math::Vector2d OpenGLSurfaceRenderer::normalizeOriginalCoordinates(int x, int y) const {
|
||||
Common::Rect viewport = _gfx->getUnscaledViewport();
|
||||
return Math::Vector2d(x / (float)viewport.width(), y / (float)viewport.height());
|
||||
}
|
||||
|
||||
Math::Vector2d OpenGLSurfaceRenderer::normalizeCurrentCoordinates(int x, int y) const {
|
||||
Common::Rect viewport = _gfx->getViewport();
|
||||
return Math::Vector2d(x / (float)viewport.width(), y / (float)viewport.height());
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // if defined(USE_OPENGL_GAME)
|
||||
69
engines/stark/gfx/openglsurface.h
Normal file
69
engines/stark/gfx/openglsurface.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* 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 STARK_GFX_OPENGL_SURFACE_H
|
||||
#define STARK_GFX_OPENGL_SURFACE_H
|
||||
|
||||
#include "engines/stark/gfx/surfacerenderer.h"
|
||||
#include "engines/stark/gfx/opengl.h"
|
||||
|
||||
#include "math/vector2d.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME)
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class OpenGLDriver;
|
||||
class Bitmap;
|
||||
|
||||
/**
|
||||
* A programmable pipeline OpenGL surface renderer
|
||||
*/
|
||||
class OpenGLSurfaceRenderer : public SurfaceRenderer {
|
||||
public:
|
||||
OpenGLSurfaceRenderer(OpenGLDriver *gfx);
|
||||
virtual ~OpenGLSurfaceRenderer();
|
||||
|
||||
// SurfaceRenderer API
|
||||
void render(const Bitmap *bitmap, const Common::Point &dest) override;
|
||||
void render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) override;
|
||||
void fill(const Color &color, const Common::Point &dest, uint width, uint height) override;
|
||||
|
||||
private:
|
||||
struct SurfaceVertex {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
Math::Vector2d normalizeOriginalCoordinates(int x, int y) const;
|
||||
Math::Vector2d normalizeCurrentCoordinates(int x, int y) const;
|
||||
void convertToVertices(SurfaceVertex *vertices, const Common::Point &dest, uint width, uint height) const;
|
||||
|
||||
OpenGLDriver *_gfx;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME)
|
||||
|
||||
#endif // STARK_GFX_OPENGL_SURFACE_H
|
||||
107
engines/stark/gfx/opengltexture.cpp
Normal file
107
engines/stark/gfx/opengltexture.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/* 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/stark/gfx/opengltexture.h"
|
||||
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#include "graphics/opengl/context.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
OpenGlTexture::OpenGlTexture() :
|
||||
Texture(),
|
||||
_id(0),
|
||||
_levelCount(0) {
|
||||
glGenTextures(1, &_id);
|
||||
|
||||
bind();
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
OpenGlTexture::~OpenGlTexture() {
|
||||
glDeleteTextures(1, &_id);
|
||||
}
|
||||
|
||||
void OpenGlTexture::bind() const {
|
||||
glBindTexture(GL_TEXTURE_2D, _id);
|
||||
}
|
||||
|
||||
void OpenGlTexture::updateLevel(uint32 level, const Graphics::Surface *surface, const byte *palette) {
|
||||
const Graphics::Surface *rgbaSurface = surface;
|
||||
if (surface->format != Driver::getRGBAPixelFormat()) {
|
||||
// Convert the surface to texture format
|
||||
rgbaSurface = surface->convertTo(Driver::getRGBAPixelFormat(), palette);
|
||||
}
|
||||
|
||||
// Stark textures (not bitmaps!) are always POT
|
||||
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, rgbaSurface->w, rgbaSurface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbaSurface->getPixels());
|
||||
|
||||
if (rgbaSurface != surface) {
|
||||
const_cast<Graphics::Surface *>(rgbaSurface)->free();
|
||||
delete rgbaSurface;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGlTexture::setLevelCount(uint32 count) {
|
||||
_levelCount = count;
|
||||
|
||||
if (count >= 1) {
|
||||
// GLES1 and GLES2 do not allow setting the max provided mipmap level.
|
||||
// It expects all the levels to be provided, which is not the case in TLJ.
|
||||
// FIXME: Enable mipmapping on GLES without this extension
|
||||
if (OpenGLContext.textureMaxLevelSupported) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
}
|
||||
|
||||
// TODO: Provide a fallback if this isn't available.
|
||||
if (OpenGLContext.textureMirrorRepeatSupported) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGlTexture::addLevel(uint32 level, const Graphics::Surface *surface, const byte *palette) {
|
||||
assert(level < _levelCount);
|
||||
|
||||
if (level == 0 || OpenGLContext.textureMaxLevelSupported) {
|
||||
updateLevel(level, surface, palette);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
59
engines/stark/gfx/opengltexture.h
Normal file
59
engines/stark/gfx/opengltexture.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 STARK_GFX_OPENGL_TEXTURE_H
|
||||
#define STARK_GFX_OPENGL_TEXTURE_H
|
||||
|
||||
#include "engines/stark/gfx/texture.h"
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
/**
|
||||
* An OpenGL texture wrapper
|
||||
*/
|
||||
class OpenGlTexture : public Texture {
|
||||
public:
|
||||
OpenGlTexture();
|
||||
virtual ~OpenGlTexture();
|
||||
|
||||
// Texture API
|
||||
void bind() const override;
|
||||
void setLevelCount(uint32 count) override;
|
||||
void addLevel(uint32 level, const Graphics::Surface *surface, const byte *palette = nullptr) override;
|
||||
|
||||
protected:
|
||||
void updateLevel(uint32 level, const Graphics::Surface *surface, const byte *palette = nullptr);
|
||||
|
||||
GLuint _id;
|
||||
uint32 _levelCount;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#endif // STARK_GFX_OPENGL_TEXTURE_H
|
||||
234
engines/stark/gfx/renderentry.cpp
Normal file
234
engines/stark/gfx/renderentry.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
/* 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/stark/gfx/renderentry.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
|
||||
#include "engines/stark/resources/item.h"
|
||||
|
||||
#include "engines/stark/visual/actor.h"
|
||||
#include "engines/stark/visual/effects/bubbles.h"
|
||||
#include "engines/stark/visual/effects/fireflies.h"
|
||||
#include "engines/stark/visual/effects/fish.h"
|
||||
#include "engines/stark/visual/image.h"
|
||||
#include "engines/stark/visual/prop.h"
|
||||
#include "engines/stark/visual/smacker.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
#include "engines/stark/visual/visual.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
RenderEntry::RenderEntry(Resources::ItemVisual *owner, const Common::String &name) :
|
||||
_visual(nullptr),
|
||||
_name(name),
|
||||
_owner(owner),
|
||||
_direction3D(0.0),
|
||||
_sortKey(0.0),
|
||||
_clickable(true) {
|
||||
}
|
||||
|
||||
void RenderEntry::render(const LightEntryArray &lights) {
|
||||
if (!_visual) {
|
||||
// warning("No visual for render entry '%s'", _name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
VisualImageXMG *imageXMG = _visual->get<VisualImageXMG>();
|
||||
if (imageXMG) {
|
||||
imageXMG->render(_position, true);
|
||||
}
|
||||
|
||||
VisualActor *actor = _visual->get<VisualActor>();
|
||||
if (actor) {
|
||||
actor->render(_position3D, _direction3D, lights);
|
||||
}
|
||||
|
||||
VisualProp *prop = _visual->get<VisualProp>();
|
||||
if (prop) {
|
||||
prop->render(_position3D, _direction3D, lights);
|
||||
}
|
||||
|
||||
VisualSmacker *smacker = _visual->get<VisualSmacker>();
|
||||
if (smacker) {
|
||||
smacker->render(_position);
|
||||
}
|
||||
|
||||
VisualText *text = _visual->get<VisualText>();
|
||||
if (text) {
|
||||
text->render(_position);
|
||||
}
|
||||
|
||||
VisualEffectBubbles *bubbles = _visual->get<VisualEffectBubbles>();
|
||||
if (bubbles) {
|
||||
bubbles->render(_position);
|
||||
}
|
||||
|
||||
VisualEffectFireFlies *fireflies = _visual->get<VisualEffectFireFlies>();
|
||||
if (fireflies) {
|
||||
fireflies->render(_position);
|
||||
}
|
||||
|
||||
VisualEffectFish *fish = _visual->get<VisualEffectFish>();
|
||||
if (fish) {
|
||||
fish->render(_position);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderEntry::setVisual(Visual *visual) {
|
||||
_visual = visual;
|
||||
}
|
||||
|
||||
void RenderEntry::setPosition(const Common::Point &position) {
|
||||
_position = position;
|
||||
}
|
||||
|
||||
void RenderEntry::setPosition3D(const Math::Vector3d &position, float direction) {
|
||||
_position3D = position;
|
||||
_direction3D = direction;
|
||||
}
|
||||
|
||||
void RenderEntry::setSortKey(float sortKey) {
|
||||
_sortKey = sortKey;
|
||||
}
|
||||
|
||||
void RenderEntry::setClickable(bool clickable) {
|
||||
_clickable = clickable;
|
||||
}
|
||||
|
||||
bool RenderEntry::compare(const RenderEntry *x, const RenderEntry *y) {
|
||||
if (x->_sortKey != y->_sortKey) {
|
||||
return x->_sortKey < y->_sortKey;
|
||||
} else if (x->_owner && y->_owner) {
|
||||
// The original used a stable sort. Common::sort is not.
|
||||
// This should ensure the items remain in the same order if they have the same sort key
|
||||
return x->_owner->getIndex() < y->_owner->getIndex();
|
||||
} else {
|
||||
return (x->_owner < y->_owner);
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderEntry::containsPoint(const Common::Point &position, Common::Point &relativePosition, const Common::Rect &cursorRect) const {
|
||||
if (!_visual || !_clickable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VisualImageXMG *image = _visual->get<VisualImageXMG>();
|
||||
if (image) {
|
||||
Common::Rect imageRect = Common::Rect(image->getWidth(), image->getHeight());
|
||||
imageRect.translate(_position.x, _position.y);
|
||||
imageRect.translate(-image->getHotspot().x, -image->getHotspot().y);
|
||||
|
||||
relativePosition.x = position.x - imageRect.left;
|
||||
relativePosition.y = position.y - imageRect.top;
|
||||
if (imageRect.contains(position) && image->isPointSolid(relativePosition)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (imageRect.width() < 32 && imageRect.height() < 32
|
||||
&& !cursorRect.isEmpty() && cursorRect.intersects(imageRect)) {
|
||||
// If the item in the scene is way smaller than the cursor,
|
||||
// use the whole cursor as a hit rectangle.
|
||||
relativePosition.x = 1 - image->getHotspot().x;
|
||||
relativePosition.y = 1 - image->getHotspot().y;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
VisualSmacker *smacker = _visual->get<VisualSmacker>();
|
||||
if (smacker) {
|
||||
Common::Point smackerPosition = smacker->getPosition();
|
||||
smackerPosition -= _position;
|
||||
|
||||
Common::Rect smackerRect = Common::Rect(smacker->getWidth(), smacker->getHeight());
|
||||
smackerRect.translate(smackerPosition.x, smackerPosition.y);
|
||||
|
||||
relativePosition.x = position.x - smackerRect.left;
|
||||
relativePosition.y = position.y - smackerRect.top;
|
||||
if (smackerRect.contains(position) && smacker->isPointSolid(relativePosition)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
VisualText *text = _visual->get<VisualText>();
|
||||
if (text) {
|
||||
Common::Rect textRect = text->getRect();
|
||||
textRect.translate(_position.x, _position.y);
|
||||
|
||||
relativePosition.x = position.x - textRect.left;
|
||||
relativePosition.y = position.y - textRect.top;
|
||||
if (textRect.contains(position)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RenderEntry::intersectRay(const Math::Ray &ray) const {
|
||||
if (!_visual || !_clickable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VisualActor *actor = _visual->get<VisualActor>();
|
||||
if (actor) {
|
||||
return actor->intersectRay(ray, _position3D, _direction3D);
|
||||
}
|
||||
|
||||
VisualProp *prop = _visual->get<VisualProp>();
|
||||
if (prop) {
|
||||
return prop->intersectRay(ray, _position3D, _direction3D);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
VisualImageXMG *RenderEntry::getImage() const {
|
||||
if (!_visual) {
|
||||
return nullptr;
|
||||
}
|
||||
return _visual->get<VisualImageXMG>();
|
||||
}
|
||||
|
||||
VisualText *RenderEntry::getText() const {
|
||||
if (!_visual) {
|
||||
return nullptr;
|
||||
}
|
||||
return _visual->get<VisualText>();
|
||||
}
|
||||
|
||||
Common::Rect RenderEntry::getBoundingRect() const {
|
||||
if (!_visual) {
|
||||
return Common::Rect();
|
||||
}
|
||||
|
||||
VisualActor *actor = _visual->get<VisualActor>();
|
||||
if (actor) {
|
||||
return actor->getBoundingRect(_position3D, _direction3D);
|
||||
}
|
||||
|
||||
warning("RenderEntry::getBoundingRect is not implemented for '%s'", _name.c_str());
|
||||
return Common::Rect();
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
134
engines/stark/gfx/renderentry.h
Normal file
134
engines/stark/gfx/renderentry.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/* 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 STARK_GFX_RENDER_ENTRY_H
|
||||
#define STARK_GFX_RENDER_ENTRY_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str.h"
|
||||
|
||||
#include "math/ray.h"
|
||||
#include "math/vector3d.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class Visual;
|
||||
class VisualImageXMG;
|
||||
class VisualText;
|
||||
|
||||
namespace Resources {
|
||||
class ItemVisual;
|
||||
}
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
struct LightEntry {
|
||||
enum Type {
|
||||
kAmbient = 0,
|
||||
kPoint = 1,
|
||||
kDirectional = 2,
|
||||
kSpot = 4
|
||||
};
|
||||
|
||||
Type type;
|
||||
Math::Vector3d color;
|
||||
Math::Vector3d position;
|
||||
Math::Vector3d direction;
|
||||
Math::Angle innerConeAngle;
|
||||
Math::Angle outerConeAngle;
|
||||
float falloffNear;
|
||||
float falloffFar;
|
||||
Math::Vector4d worldPosition;
|
||||
Math::Vector4d eyePosition;
|
||||
Math::Vector3d eyeDirection;
|
||||
};
|
||||
|
||||
typedef Common::Array<LightEntry *> LightEntryArray;
|
||||
|
||||
class RenderEntry {
|
||||
public:
|
||||
RenderEntry(Resources::ItemVisual *owner, const Common::String &name);
|
||||
virtual ~RenderEntry() {}
|
||||
|
||||
void render(const LightEntryArray &lights = LightEntryArray());
|
||||
|
||||
void setVisual(Visual *visual);
|
||||
void setPosition(const Common::Point &position);
|
||||
void setPosition3D(const Math::Vector3d &position, float direction);
|
||||
void setSortKey(float sortKey);
|
||||
void setClickable(bool clickable);
|
||||
|
||||
/** Gets the position */
|
||||
Common::Point getPosition() const { return _position; }
|
||||
|
||||
/** Gets the owner-object */
|
||||
Resources::ItemVisual *getOwner() const { return _owner; }
|
||||
|
||||
/** Gets the entry's name */
|
||||
const Common::String &getName() const { return _name; }
|
||||
|
||||
/** Obtain the underlying image visual, if any */
|
||||
VisualImageXMG *getImage() const;
|
||||
|
||||
/** Obtain the underlying text visual, if any */
|
||||
VisualText *getText() const;
|
||||
|
||||
/**
|
||||
* Mouse picking test for 2D items
|
||||
*
|
||||
* @param position game window coordinates to test
|
||||
* @param relativePosition successful hit item relative coordinates
|
||||
* @param cursorRect cursor rectangle to be used to test small world items
|
||||
* @return successful hit
|
||||
*/
|
||||
bool containsPoint(const Common::Point &position, Common::Point &relativePosition, const Common::Rect &cursorRect) const;
|
||||
|
||||
/** Mouse picking test for 3D items */
|
||||
bool intersectRay(const Math::Ray &ray) const;
|
||||
|
||||
/** Compare two render entries by their sort keys */
|
||||
static bool compare(const RenderEntry *x, const RenderEntry *y);
|
||||
|
||||
/**
|
||||
* Compute the 2D screen space bounding rect for the item,
|
||||
* in original game view coordinates.
|
||||
*/
|
||||
Common::Rect getBoundingRect() const;
|
||||
|
||||
protected:
|
||||
Common::String _name;
|
||||
Resources::ItemVisual *_owner;
|
||||
|
||||
Visual *_visual;
|
||||
Common::Point _position;
|
||||
Math::Vector3d _position3D;
|
||||
float _direction3D;
|
||||
float _sortKey;
|
||||
bool _clickable;
|
||||
};
|
||||
|
||||
typedef Common::Array<RenderEntry *> RenderEntryArray;
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_RENDER_ENTRY_H
|
||||
49
engines/stark/gfx/surfacerenderer.cpp
Normal file
49
engines/stark/gfx/surfacerenderer.cpp
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/stark/gfx/surfacerenderer.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
SurfaceRenderer::SurfaceRenderer() :
|
||||
_noScalingOverride(false),
|
||||
_fadeLevel(0),
|
||||
_snapToGrid(false) {
|
||||
}
|
||||
|
||||
SurfaceRenderer::~SurfaceRenderer() {
|
||||
}
|
||||
|
||||
void SurfaceRenderer::setNoScalingOverride(bool noScalingOverride) {
|
||||
_noScalingOverride = noScalingOverride;
|
||||
}
|
||||
|
||||
void SurfaceRenderer::setFadeLevel(float fadeLevel) {
|
||||
_fadeLevel = fadeLevel;
|
||||
}
|
||||
|
||||
void SurfaceRenderer::setSnapToGrid(bool snapToGrid) {
|
||||
_snapToGrid = snapToGrid;
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
85
engines/stark/gfx/surfacerenderer.h
Normal file
85
engines/stark/gfx/surfacerenderer.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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 STARK_GFX_SURFACE_RENDERER_H
|
||||
#define STARK_GFX_SURFACE_RENDERER_H
|
||||
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class Bitmap;
|
||||
struct Color;
|
||||
|
||||
/**
|
||||
* A renderer to draw textures as two dimensional surfaces to the current viewport
|
||||
*/
|
||||
class SurfaceRenderer {
|
||||
public:
|
||||
SurfaceRenderer();
|
||||
virtual ~SurfaceRenderer();
|
||||
|
||||
/**
|
||||
* Draw a 2D surface from the specified bitmap
|
||||
*/
|
||||
virtual void render(const Bitmap *bitmap, const Common::Point &dest) = 0;
|
||||
|
||||
/**
|
||||
* Draw a 2D surface from the specified bitmap with given width and height
|
||||
*/
|
||||
virtual void render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) = 0;
|
||||
|
||||
/**
|
||||
* Draw a filled 2D rectangle using the specified color
|
||||
*/
|
||||
virtual void fill(const Color &color, const Common::Point &dest, uint width, uint height) = 0;
|
||||
|
||||
/**
|
||||
* When this is set to true, the texture size is expected to be in current
|
||||
* coordinates, and is to be drawn without scaling.
|
||||
*
|
||||
* This setting does not affect the destination point coordinates
|
||||
*/
|
||||
void setNoScalingOverride(bool noScalingOverride);
|
||||
|
||||
/**
|
||||
* The fade level is added to the color value of each pixel
|
||||
*
|
||||
* It is a value between -1 and 1
|
||||
*/
|
||||
void setFadeLevel(float fadeLevel);
|
||||
|
||||
/**
|
||||
* Align vertex coordinates to the native pixel grid
|
||||
*/
|
||||
void setSnapToGrid(bool snapToGrid);
|
||||
|
||||
protected:
|
||||
bool _noScalingOverride;
|
||||
float _fadeLevel;
|
||||
bool _snapToGrid;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_SURFACE_RENDERER_H
|
||||
55
engines/stark/gfx/texture.cpp
Normal file
55
engines/stark/gfx/texture.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/stark/gfx/texture.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
TextureSet::TextureSet() {
|
||||
}
|
||||
|
||||
TextureSet::~TextureSet() {
|
||||
for (TextureMap::iterator it = _texMap.begin(); it != _texMap.end(); ++it) {
|
||||
delete it->_value;
|
||||
}
|
||||
}
|
||||
|
||||
void TextureSet::addTexture(const Common::String &name, Texture *texture) {
|
||||
if (_texMap.contains(name)) {
|
||||
error("A texture with the name '%s' already exists in the set.", name.c_str());
|
||||
}
|
||||
|
||||
_texMap.setVal(name, texture);
|
||||
}
|
||||
|
||||
const Texture *TextureSet::getTexture(const Common::String &name) const {
|
||||
TextureMap::const_iterator it = _texMap.find(name);
|
||||
if (it != _texMap.end())
|
||||
return it->_value;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
85
engines/stark/gfx/texture.h
Normal file
85
engines/stark/gfx/texture.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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 STARK_GFX_TEXTURE_H
|
||||
#define STARK_GFX_TEXTURE_H
|
||||
|
||||
#include "common/hash-str.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
/**
|
||||
* An abstract texture
|
||||
*/
|
||||
class Texture {
|
||||
public:
|
||||
Texture() {}
|
||||
virtual ~Texture() {}
|
||||
|
||||
/** Make the texture active */
|
||||
virtual void bind() const = 0;
|
||||
|
||||
/**
|
||||
* Define the total number of levels of details
|
||||
*
|
||||
* Must be called before adding levels
|
||||
*/
|
||||
virtual void setLevelCount(uint32 count) = 0;
|
||||
|
||||
/**
|
||||
* Add a detail level to the texture
|
||||
*/
|
||||
virtual void addLevel(uint32 level, const Graphics::Surface *surface, const byte *palette = nullptr) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A collection of textures referenced by their names
|
||||
*/
|
||||
class TextureSet {
|
||||
public:
|
||||
TextureSet();
|
||||
~TextureSet();
|
||||
|
||||
/**
|
||||
* Add a texture to the set
|
||||
*/
|
||||
void addTexture(const Common::String &name, Texture *texture);
|
||||
|
||||
/**
|
||||
* Retrieve a texture from the set
|
||||
*/
|
||||
const Texture *getTexture(const Common::String &name) const;
|
||||
|
||||
private:
|
||||
typedef Common::HashMap<Common::String, Texture *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> TextureMap;
|
||||
|
||||
TextureMap _texMap;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_TEXTURE_H
|
||||
188
engines/stark/gfx/tinygl.cpp
Normal file
188
engines/stark/gfx/tinygl.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/* 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/system.h"
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include "math/matrix4.h"
|
||||
|
||||
#include "engines/stark/gfx/tinygl.h"
|
||||
#include "engines/stark/gfx/tinyglactor.h"
|
||||
#include "engines/stark/gfx/tinyglbitmap.h"
|
||||
#include "engines/stark/gfx/tinyglprop.h"
|
||||
#include "engines/stark/gfx/tinyglsurface.h"
|
||||
#include "engines/stark/gfx/tinyglfade.h"
|
||||
#include "engines/stark/gfx/tinygltexture.h"
|
||||
#include "engines/stark/scene.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
TinyGLDriver::TinyGLDriver() {
|
||||
}
|
||||
|
||||
TinyGLDriver::~TinyGLDriver() {
|
||||
TinyGL::destroyContext();
|
||||
}
|
||||
|
||||
void TinyGLDriver::init() {
|
||||
computeScreenViewport();
|
||||
|
||||
TinyGL::createContext(kOriginalWidth, kOriginalHeight, g_system->getScreenFormat(), 512, true, ConfMan.getBool("dirtyrects"));
|
||||
|
||||
tglMatrixMode(TGL_PROJECTION);
|
||||
tglLoadIdentity();
|
||||
tglMatrixMode(TGL_MODELVIEW);
|
||||
tglLoadIdentity();
|
||||
tglDisable(TGL_LIGHTING);
|
||||
}
|
||||
|
||||
void TinyGLDriver::setScreenViewport(bool noScaling) {
|
||||
if (noScaling) {
|
||||
_viewport = Common::Rect(g_system->getWidth(), g_system->getHeight());
|
||||
_unscaledViewport = _viewport;
|
||||
} else {
|
||||
_viewport = _screenViewport;
|
||||
_unscaledViewport = Common::Rect(kOriginalWidth, kOriginalHeight);
|
||||
}
|
||||
|
||||
tglViewport(_viewport.left, _viewport.top, _viewport.width(), _viewport.height());
|
||||
}
|
||||
|
||||
void TinyGLDriver::setViewport(const Common::Rect &rect) {
|
||||
_viewport = Common::Rect(_screenViewport.width() * rect.width() / kOriginalWidth,
|
||||
_screenViewport.height() * rect.height() / kOriginalHeight);
|
||||
|
||||
_viewport.translate(_screenViewport.left + _screenViewport.width() * rect.left / kOriginalWidth,
|
||||
_screenViewport.top + _screenViewport.height() * rect.top / kOriginalHeight);
|
||||
|
||||
_unscaledViewport = rect;
|
||||
|
||||
tglViewport(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height());
|
||||
}
|
||||
|
||||
void TinyGLDriver::clearScreen() {
|
||||
tglClear(TGL_COLOR_BUFFER_BIT | TGL_DEPTH_BUFFER_BIT | TGL_STENCIL_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void TinyGLDriver::flipBuffer() {
|
||||
Common::List<Common::Rect> dirtyAreas;
|
||||
TinyGL::presentBuffer(dirtyAreas);
|
||||
|
||||
Graphics::Surface glBuffer;
|
||||
TinyGL::getSurfaceRef(glBuffer);
|
||||
|
||||
if (!dirtyAreas.empty()) {
|
||||
for (Common::List<Common::Rect>::iterator itRect = dirtyAreas.begin(); itRect != dirtyAreas.end(); ++itRect) {
|
||||
g_system->copyRectToScreen(glBuffer.getBasePtr((*itRect).left, (*itRect).top), glBuffer.pitch,
|
||||
(*itRect).left, (*itRect).top, (*itRect).width(), (*itRect).height());
|
||||
}
|
||||
}
|
||||
|
||||
g_system->updateScreen();
|
||||
}
|
||||
|
||||
Texture *TinyGLDriver::createTexture() {
|
||||
return new TinyGlTexture();
|
||||
}
|
||||
|
||||
Bitmap *TinyGLDriver::createBitmap(const Graphics::Surface *surface, const byte *palette) {
|
||||
TinyGlBitmap *texture = new TinyGlBitmap();
|
||||
|
||||
if (surface) {
|
||||
texture->update(surface, palette);
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
VisualActor *TinyGLDriver::createActorRenderer() {
|
||||
return new TinyGLActorRenderer(this);
|
||||
}
|
||||
|
||||
VisualProp *TinyGLDriver::createPropRenderer() {
|
||||
return new TinyGLPropRenderer(this);
|
||||
}
|
||||
|
||||
SurfaceRenderer *TinyGLDriver::createSurfaceRenderer() {
|
||||
return new TinyGLSurfaceRenderer(this);
|
||||
}
|
||||
|
||||
FadeRenderer *TinyGLDriver::createFadeRenderer() {
|
||||
return new TinyGLFadeRenderer(this);
|
||||
}
|
||||
|
||||
void TinyGLDriver::start2DMode() {
|
||||
// This blend mode prevents color fringes due to filtering.
|
||||
// It requires the textures to have their color values pre-multiplied
|
||||
// with their alpha value. This is the "Premultiplied Alpha" technique.
|
||||
tglBlendFunc(TGL_ONE, TGL_ONE_MINUS_SRC_ALPHA);
|
||||
tglEnable(TGL_BLEND);
|
||||
|
||||
tglDisable(TGL_DEPTH_TEST);
|
||||
tglDepthMask(TGL_FALSE);
|
||||
}
|
||||
|
||||
void TinyGLDriver::end2DMode() {
|
||||
tglDisable(TGL_BLEND);
|
||||
tglEnable(TGL_DEPTH_TEST);
|
||||
tglDepthMask(TGL_TRUE);
|
||||
}
|
||||
|
||||
void TinyGLDriver::set3DMode() {
|
||||
tglEnable(TGL_DEPTH_TEST);
|
||||
tglDepthFunc(TGL_LESS);
|
||||
|
||||
// Stencil test are only used in rendering shadows
|
||||
// They are manually enabled and disabled there
|
||||
tglStencilFunc(TGL_EQUAL, 0, 0xFF);
|
||||
tglStencilOp(TGL_KEEP, TGL_KEEP, TGL_INCR);
|
||||
}
|
||||
|
||||
bool TinyGLDriver::computeLightsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::Rect TinyGLDriver::getViewport() const {
|
||||
return _viewport;
|
||||
}
|
||||
|
||||
Common::Rect TinyGLDriver::getUnscaledViewport() const {
|
||||
return _unscaledViewport;
|
||||
}
|
||||
|
||||
Graphics::Surface *TinyGLDriver::getViewportScreenshot() const {
|
||||
Graphics::Surface *tmp = TinyGL::copyFromFrameBuffer(getRGBAPixelFormat());
|
||||
Graphics::Surface *s = new Graphics::Surface();
|
||||
s->create(_viewport.width(), _viewport.height(), getRGBAPixelFormat());
|
||||
byte *src = (byte *)tmp->getPixels();
|
||||
s->copyRectToSurface(src + tmp->pitch * _viewport.top + _viewport.left * tmp->format.bytesPerPixel,
|
||||
tmp->pitch, 0, 0, _viewport.width(), _viewport.height());
|
||||
tmp->free();
|
||||
delete tmp;
|
||||
return s;
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
78
engines/stark/gfx/tinygl.h
Normal file
78
engines/stark/gfx/tinygl.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/* 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 STARK_GFX_TINYGL_H
|
||||
#define STARK_GFX_TINYGL_H
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#include "math/vector3d.h"
|
||||
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/gfx/renderentry.h"
|
||||
|
||||
#include "graphics/tinygl/tinygl.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class TinyGLDriver : public Driver {
|
||||
public:
|
||||
TinyGLDriver();
|
||||
~TinyGLDriver();
|
||||
|
||||
void init() override;
|
||||
|
||||
void setScreenViewport(bool noScaling) override;
|
||||
void setViewport(const Common::Rect &rect) override;
|
||||
|
||||
void clearScreen() override;
|
||||
void flipBuffer() override;
|
||||
|
||||
Texture *createTexture() override;
|
||||
Bitmap *createBitmap(const Graphics::Surface *surface = nullptr, const byte *palette = nullptr) override;
|
||||
VisualActor *createActorRenderer() override;
|
||||
VisualProp *createPropRenderer() override;
|
||||
SurfaceRenderer *createSurfaceRenderer() override;
|
||||
FadeRenderer *createFadeRenderer() override;
|
||||
|
||||
void start2DMode();
|
||||
void end2DMode();
|
||||
void set3DMode() override;
|
||||
bool computeLightsEnabled() override;
|
||||
|
||||
Common::Rect getViewport() const;
|
||||
Common::Rect getUnscaledViewport() const;
|
||||
void setupLights(const LightEntryArray &lights);
|
||||
|
||||
Graphics::Surface *getViewportScreenshot() const override;
|
||||
|
||||
bool supportsModdedAssets() const override { return false; }
|
||||
|
||||
private:
|
||||
Common::Rect _viewport;
|
||||
Common::Rect _unscaledViewport;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_TINYGL_H
|
||||
453
engines/stark/gfx/tinyglactor.cpp
Normal file
453
engines/stark/gfx/tinyglactor.cpp
Normal file
@@ -0,0 +1,453 @@
|
||||
/* 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/stark/gfx/tinyglactor.h"
|
||||
#include "engines/stark/model/model.h"
|
||||
#include "engines/stark/model/animhandler.h"
|
||||
#include "engines/stark/scene.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/settings.h"
|
||||
#include "engines/stark/gfx/texture.h"
|
||||
|
||||
#include "math/vector2d.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
TinyGLActorRenderer::TinyGLActorRenderer(TinyGLDriver *gfx) :
|
||||
VisualActor(),
|
||||
_gfx(gfx),
|
||||
_faceVBO(nullptr) {
|
||||
}
|
||||
|
||||
TinyGLActorRenderer::~TinyGLActorRenderer() {
|
||||
clearVertices();
|
||||
}
|
||||
|
||||
void TinyGLActorRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
|
||||
if (_modelIsDirty) {
|
||||
clearVertices();
|
||||
uploadVertices();
|
||||
_modelIsDirty = false;
|
||||
}
|
||||
|
||||
// TODO: Move updates outside of the rendering code
|
||||
_animHandler->animate(_time);
|
||||
_model->updateBoundingBox();
|
||||
|
||||
bool drawShadow = false;
|
||||
if (_castsShadow &&
|
||||
StarkScene->shouldRenderShadows() &&
|
||||
StarkSettings->getBoolSetting(Settings::kShadow)) {
|
||||
drawShadow = true;
|
||||
}
|
||||
|
||||
Math::Vector3d lightDirection;
|
||||
|
||||
_gfx->set3DMode();
|
||||
|
||||
Math::Matrix4 model = getModelMatrix(position, direction);
|
||||
Math::Matrix4 view = StarkScene->getViewMatrix();
|
||||
Math::Matrix4 projection = StarkScene->getProjectionMatrix();
|
||||
|
||||
Math::Matrix4 modelViewMatrix = view * model;
|
||||
modelViewMatrix.transpose(); // TinyGL expects matrices transposed
|
||||
tglMatrixMode(TGL_MODELVIEW);
|
||||
tglLoadMatrixf(modelViewMatrix.getData());
|
||||
|
||||
Math::Matrix4 projectionMatrix = projection;
|
||||
projectionMatrix.transpose(); // TinyGL expects matrices transposed
|
||||
tglMatrixMode(TGL_PROJECTION);
|
||||
tglLoadMatrixf(projectionMatrix.getData());
|
||||
|
||||
Math::Matrix4 normalMatrix;
|
||||
projectionMatrix.transpose();
|
||||
modelViewMatrix.transpose();
|
||||
|
||||
normalMatrix = modelViewMatrix;
|
||||
normalMatrix.invertAffineOrthonormal();
|
||||
|
||||
Math::Matrix4 mvp;
|
||||
if (drawShadow) {
|
||||
mvp = view * model;
|
||||
mvp.transpose();
|
||||
Math::Matrix4 modelInverse = model;
|
||||
modelInverse.inverse();
|
||||
lightDirection = getShadowLightDirection(lights, position, modelInverse.getRotation());
|
||||
}
|
||||
|
||||
Common::Array<Face *> faces = _model->getFaces();
|
||||
Common::Array<Material *> mats = _model->getMaterials();
|
||||
const Common::Array<BoneNode *> &bones = _model->getBones();
|
||||
|
||||
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
const Material *material = mats[(*face)->materialId];
|
||||
Math::Vector3d color;
|
||||
const Gfx::Texture *tex = resolveTexture(material);
|
||||
if (tex) {
|
||||
tex->bind();
|
||||
tglEnable(TGL_TEXTURE_2D);
|
||||
} else {
|
||||
tglBindTexture(TGL_TEXTURE_2D, 0);
|
||||
tglDisable(TGL_TEXTURE_2D);
|
||||
}
|
||||
auto vertexIndices = _faceEBO[*face];
|
||||
auto numVertexIndices = (*face)->vertexIndices.size();
|
||||
for (uint32 i = 0; i < numVertexIndices; i++) {
|
||||
if (tex) {
|
||||
color = Math::Vector3d(1.0f, 1.0f, 1.0f);
|
||||
} else {
|
||||
color = Math::Vector3d(material->r, material->g, material->b);
|
||||
}
|
||||
uint32 index = vertexIndices[i];
|
||||
auto vertex = _faceVBO[index];
|
||||
uint32 bone1 = vertex.bone1;
|
||||
uint32 bone2 = vertex.bone2;
|
||||
Math::Vector3d position1 = Math::Vector3d(vertex.pos1x, vertex.pos1y, vertex.pos1z);
|
||||
Math::Vector3d position2 = Math::Vector3d(vertex.pos2x, vertex.pos2y, vertex.pos2z);
|
||||
Math::Vector3d bone1Position = Math::Vector3d(bones[bone1]->_animPos.x(),
|
||||
bones[bone1]->_animPos.y(),
|
||||
bones[bone1]->_animPos.z());
|
||||
Math::Vector3d bone2Position = Math::Vector3d(bones[bone2]->_animPos.x(),
|
||||
bones[bone2]->_animPos.y(),
|
||||
bones[bone2]->_animPos.z());
|
||||
Math::Quaternion bone1Rotation = Math::Quaternion(bones[bone1]->_animRot.x(),
|
||||
bones[bone1]->_animRot.y(),
|
||||
bones[bone1]->_animRot.z(),
|
||||
bones[bone1]->_animRot.w());
|
||||
Math::Quaternion bone2Rotation = Math::Quaternion(bones[bone2]->_animRot.x(),
|
||||
bones[bone2]->_animRot.y(),
|
||||
bones[bone2]->_animRot.z(),
|
||||
bones[bone2]->_animRot.w());
|
||||
float boneWeight = vertex.boneWeight;
|
||||
Math::Vector3d normal = Math::Vector3d(vertex.normalx, vertex.normaly, vertex.normalz);
|
||||
|
||||
// Compute the vertex position in eye-space
|
||||
bone1Rotation.transform(position1);
|
||||
position1 += bone1Position;
|
||||
bone2Rotation.transform(position2);
|
||||
position2 += bone2Position;
|
||||
Math::Vector3d modelPosition = Math::Vector3d::interpolate(position2, position1, boneWeight);
|
||||
vertex.x = modelPosition.x();
|
||||
vertex.y = modelPosition.y();
|
||||
vertex.z = modelPosition.z();
|
||||
Math::Vector4d modelEyePosition;
|
||||
modelEyePosition = modelViewMatrix * Math::Vector4d(modelPosition.x(),
|
||||
modelPosition.y(),
|
||||
modelPosition.z(),
|
||||
1.0);
|
||||
// Compute the vertex normal in eye-space
|
||||
Math::Vector3d n1 = normal;
|
||||
bone1Rotation.transform(n1);
|
||||
Math::Vector3d n2 = normal;
|
||||
bone2Rotation.transform(n2);
|
||||
Math::Vector3d modelNormal = Math::Vector3d(Math::Vector3d::interpolate(n2, n1, boneWeight)).getNormalized();
|
||||
vertex.nx = modelNormal.x();
|
||||
vertex.ny = modelNormal.y();
|
||||
vertex.nz = modelNormal.z();
|
||||
Math::Vector3d modelEyeNormal;
|
||||
modelEyeNormal = normalMatrix.getRotation() * modelNormal;
|
||||
modelEyeNormal.normalize();
|
||||
|
||||
if (drawShadow) {
|
||||
Math::Vector3d shadowPosition = modelPosition + lightDirection * (-modelPosition.y() / lightDirection.y());
|
||||
vertex.sx = shadowPosition.x();
|
||||
vertex.sy = 0.0f;
|
||||
vertex.sz = shadowPosition.z();
|
||||
}
|
||||
|
||||
static const uint maxLights = 10;
|
||||
|
||||
assert(lights.size() >= 1);
|
||||
assert(lights.size() <= maxLights);
|
||||
|
||||
const LightEntry *ambient = lights[0];
|
||||
assert(ambient->type == LightEntry::kAmbient); // The first light must be the ambient light
|
||||
|
||||
Math::Vector3d lightColor = ambient->color;
|
||||
|
||||
for (uint li = 0; li < lights.size() - 1; li++) {
|
||||
const LightEntry *l = lights[li + 1];
|
||||
|
||||
switch (l->type) {
|
||||
case LightEntry::kPoint: {
|
||||
Math::Vector3d vertexToLight = l->eyePosition.getXYZ() - modelEyePosition.getXYZ();
|
||||
|
||||
float dist = vertexToLight.length();
|
||||
vertexToLight.normalize();
|
||||
float attn = CLIP((l->falloffFar - dist) / MAX(0.001f, l->falloffFar - l->falloffNear), 0.0f, 1.0f);
|
||||
float incidence = MAX(0.0f, Math::Vector3d::dotProduct(modelEyeNormal, vertexToLight));
|
||||
lightColor += l->color * attn * incidence;
|
||||
break;
|
||||
}
|
||||
case LightEntry::kDirectional: {
|
||||
float incidence = MAX(0.0f, Math::Vector3d::dotProduct(modelEyeNormal, -l->eyeDirection));
|
||||
lightColor += (l->color * incidence);
|
||||
break;
|
||||
}
|
||||
case LightEntry::kSpot: {
|
||||
Math::Vector3d vertexToLight = l->eyePosition.getXYZ() - modelEyePosition.getXYZ();
|
||||
|
||||
float dist = vertexToLight.length();
|
||||
float attn = CLIP((l->falloffFar - dist) / MAX(0.001f, l->falloffFar - l->falloffNear), 0.0f, 1.0f);
|
||||
|
||||
vertexToLight.normalize();
|
||||
float incidence = MAX(0.0f, modelEyeNormal.dotProduct(vertexToLight));
|
||||
|
||||
float cosAngle = MAX(0.0f, vertexToLight.dotProduct(-l->eyeDirection));
|
||||
float cone = CLIP((cosAngle - l->innerConeAngle.getCosine()) / MAX(0.001f, l->outerConeAngle.getCosine() - l->innerConeAngle.getCosine()), 0.0f, 1.0f);
|
||||
|
||||
lightColor += l->color * attn * incidence * cone;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lightColor.x() = CLIP(lightColor.x(), 0.0f, 1.0f);
|
||||
lightColor.y() = CLIP(lightColor.y(), 0.0f, 1.0f);
|
||||
lightColor.z() = CLIP(lightColor.z(), 0.0f, 1.0f);
|
||||
color = color * lightColor;
|
||||
vertex.r = color.x();
|
||||
vertex.g = color.y();
|
||||
vertex.b = color.z();
|
||||
_faceVBO[index] = vertex;
|
||||
}
|
||||
|
||||
tglEnableClientState(TGL_VERTEX_ARRAY);
|
||||
tglEnableClientState(TGL_COLOR_ARRAY);
|
||||
if (tex)
|
||||
tglEnableClientState(TGL_TEXTURE_COORD_ARRAY);
|
||||
tglEnableClientState(TGL_NORMAL_ARRAY);
|
||||
|
||||
tglVertexPointer(3, TGL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].x);
|
||||
if (tex)
|
||||
tglTexCoordPointer(2, TGL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].texS);
|
||||
tglNormalPointer(TGL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].nx);
|
||||
tglColorPointer(3, TGL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].r);
|
||||
|
||||
tglDrawElements(TGL_TRIANGLES, numVertexIndices, TGL_UNSIGNED_INT, vertexIndices);
|
||||
|
||||
tglDisableClientState(TGL_VERTEX_ARRAY);
|
||||
tglDisableClientState(TGL_COLOR_ARRAY);
|
||||
tglDisableClientState(TGL_TEXTURE_COORD_ARRAY);
|
||||
tglDisableClientState(TGL_NORMAL_ARRAY);
|
||||
}
|
||||
|
||||
if (drawShadow) {
|
||||
tglEnable(TGL_BLEND);
|
||||
tglEnable(TGL_STENCIL_TEST);
|
||||
tglDisable(TGL_TEXTURE_2D);
|
||||
|
||||
tglColor4f(0.0f, 0.0f, 0.0f, 0.5f);
|
||||
|
||||
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
tglEnableClientState(TGL_VERTEX_ARRAY);
|
||||
|
||||
tglVertexPointer(3, TGL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].sx);
|
||||
|
||||
tglDrawElements(TGL_TRIANGLES, (*face)->vertexIndices.size(), TGL_UNSIGNED_INT, _faceEBO[*face]);
|
||||
|
||||
tglDisableClientState(TGL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
tglEnable(TGL_TEXTURE_2D);
|
||||
tglDisable(TGL_BLEND);
|
||||
tglDisable(TGL_STENCIL_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
void TinyGLActorRenderer::clearVertices() {
|
||||
delete[] _faceVBO;
|
||||
_faceVBO = nullptr;
|
||||
|
||||
for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
|
||||
delete[] it->_value;
|
||||
}
|
||||
|
||||
_faceEBO.clear();
|
||||
}
|
||||
|
||||
void TinyGLActorRenderer::uploadVertices() {
|
||||
_faceVBO = createModelVBO(_model);
|
||||
|
||||
Common::Array<Face *> faces = _model->getFaces();
|
||||
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
_faceEBO[*face] = createFaceEBO(*face);
|
||||
}
|
||||
}
|
||||
|
||||
ActorVertex *TinyGLActorRenderer::createModelVBO(const Model *model) {
|
||||
const Common::Array<VertNode *> &modelVertices = model->getVertices();
|
||||
|
||||
auto vertices = new ActorVertex[modelVertices.size()];
|
||||
// Build a vertex array
|
||||
int i = 0;
|
||||
for (Common::Array<VertNode *>::const_iterator tri = modelVertices.begin(); tri != modelVertices.end(); ++tri, i++) {
|
||||
vertices[i].pos1x = (*tri)->_pos1.x();
|
||||
vertices[i].pos1y = (*tri)->_pos1.y();
|
||||
vertices[i].pos1z = (*tri)->_pos1.z();
|
||||
vertices[i].pos2x = (*tri)->_pos2.x();
|
||||
vertices[i].pos2y = (*tri)->_pos2.y();
|
||||
vertices[i].pos2z = (*tri)->_pos2.z();
|
||||
vertices[i].bone1 = (*tri)->_bone1;
|
||||
vertices[i].bone2 = (*tri)->_bone2;
|
||||
vertices[i].boneWeight = (*tri)->_boneWeight;
|
||||
vertices[i].normalx = (*tri)->_normal.x();
|
||||
vertices[i].normaly = (*tri)->_normal.y();
|
||||
vertices[i].normalz = (*tri)->_normal.z();
|
||||
vertices[i].texS = -(*tri)->_texS;
|
||||
vertices[i].texT = (*tri)->_texT;
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
uint32 *TinyGLActorRenderer::createFaceEBO(const Face *face) {
|
||||
auto indices = new uint32[face->vertexIndices.size()];
|
||||
for (uint32 index = 0; index < face->vertexIndices.size(); index++) {
|
||||
indices[index] = face->vertexIndices[index];
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
Math::Vector3d TinyGLActorRenderer::getShadowLightDirection(const LightEntryArray &lights,
|
||||
const Math::Vector3d &actorPosition, Math::Matrix3 worldToModelRot) {
|
||||
Math::Vector3d sumDirection;
|
||||
bool hasLight = false;
|
||||
|
||||
// Compute the contribution from each lights
|
||||
// The ambient light is skipped intentionally
|
||||
for (uint i = 1; i < lights.size(); ++i) {
|
||||
LightEntry *light = lights[i];
|
||||
bool contributes = false;
|
||||
|
||||
Math::Vector3d lightDirection;
|
||||
switch (light->type) {
|
||||
case LightEntry::kPoint:
|
||||
contributes = getPointLightContribution(light, actorPosition, lightDirection);
|
||||
break;
|
||||
case LightEntry::kDirectional:
|
||||
contributes = getDirectionalLightContribution(light, lightDirection);
|
||||
break;
|
||||
case LightEntry::kSpot:
|
||||
contributes = getSpotLightContribution(light, actorPosition, lightDirection);
|
||||
break;
|
||||
case LightEntry::kAmbient:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (contributes) {
|
||||
sumDirection += lightDirection;
|
||||
hasLight = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasLight) {
|
||||
// Clip the horizontal length
|
||||
Math::Vector2d horizontalProjection(sumDirection.x(), sumDirection.y());
|
||||
float shadowLength = MIN(horizontalProjection.getMagnitude(), StarkScene->getMaxShadowLength());
|
||||
|
||||
horizontalProjection.normalize();
|
||||
horizontalProjection *= shadowLength;
|
||||
|
||||
sumDirection.x() = horizontalProjection.getX();
|
||||
sumDirection.y() = horizontalProjection.getY();
|
||||
sumDirection.z() = -1;
|
||||
} else {
|
||||
// Cast from above by default
|
||||
sumDirection.x() = 0;
|
||||
sumDirection.y() = 0;
|
||||
sumDirection.z() = -1;
|
||||
}
|
||||
|
||||
//Transform the direction to the model space and pass to the shader
|
||||
return worldToModelRot * sumDirection;
|
||||
}
|
||||
|
||||
bool TinyGLActorRenderer::getPointLightContribution(LightEntry *light,
|
||||
const Math::Vector3d &actorPosition, Math::Vector3d &direction, float weight) {
|
||||
float distance = light->position.getDistanceTo(actorPosition);
|
||||
|
||||
if (distance > light->falloffFar) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float factor;
|
||||
if (distance > light->falloffNear) {
|
||||
if (light->falloffFar - light->falloffNear > 1) {
|
||||
factor = 1 - (distance - light->falloffNear) / (light->falloffFar - light->falloffNear);
|
||||
} else {
|
||||
factor = 0;
|
||||
}
|
||||
} else {
|
||||
factor = 1;
|
||||
}
|
||||
|
||||
float brightness = (light->color.x() + light->color.y() + light->color.z()) / 3.0f;
|
||||
|
||||
if (factor <= 0 || brightness <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
direction = actorPosition - light->position;
|
||||
direction.normalize();
|
||||
direction *= factor * brightness * weight;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TinyGLActorRenderer::getDirectionalLightContribution(LightEntry *light, Math::Vector3d &direction) {
|
||||
float brightness = (light->color.x() + light->color.y() + light->color.z()) / 3.0f;
|
||||
|
||||
if (brightness <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
direction = light->direction;
|
||||
direction.normalize();
|
||||
direction *= brightness;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TinyGLActorRenderer::getSpotLightContribution(LightEntry *light, const Math::Vector3d &actorPosition,
|
||||
Math::Vector3d &direction) {
|
||||
Math::Vector3d lightToActor = actorPosition - light->position;
|
||||
lightToActor.normalize();
|
||||
|
||||
float cosAngle = MAX(0.0f, lightToActor.dotProduct(light->direction));
|
||||
float cone = (cosAngle - light->innerConeAngle.getCosine()) /
|
||||
MAX(0.001f, light->outerConeAngle.getCosine() - light->innerConeAngle.getCosine());
|
||||
cone = CLIP(cone, 0.0f, 1.0f);
|
||||
|
||||
if (cone <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getPointLightContribution(light, actorPosition, direction, cone);
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
101
engines/stark/gfx/tinyglactor.h
Normal file
101
engines/stark/gfx/tinyglactor.h
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_GFX_TINYGL_ACTOR_H
|
||||
#define STARK_GFX_TINYGL_ACTOR_H
|
||||
|
||||
#include "engines/stark/gfx/renderentry.h"
|
||||
#include "engines/stark/visual/actor.h"
|
||||
#include "engines/stark/gfx/tinygl.h"
|
||||
|
||||
#include "graphics/tinygl/tinygl.h"
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-ptr.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class TinyGLDriver;
|
||||
|
||||
struct _ActorVertex {
|
||||
float pos1x;
|
||||
float pos1y;
|
||||
float pos1z;
|
||||
float pos2x;
|
||||
float pos2y;
|
||||
float pos2z;
|
||||
uint32 bone1;
|
||||
uint32 bone2;
|
||||
float boneWeight;
|
||||
float normalx;
|
||||
float normaly;
|
||||
float normalz;
|
||||
float texS;
|
||||
float texT;
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float nx;
|
||||
float ny;
|
||||
float nz;
|
||||
float sx;
|
||||
float sy;
|
||||
float sz;
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
};
|
||||
typedef _ActorVertex ActorVertex;
|
||||
|
||||
class TinyGLActorRenderer : public VisualActor {
|
||||
public:
|
||||
TinyGLActorRenderer(TinyGLDriver *gfx);
|
||||
virtual ~TinyGLActorRenderer();
|
||||
|
||||
void render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) override;
|
||||
|
||||
protected:
|
||||
typedef Common::HashMap<Face *, uint32 *> FaceBufferMap;
|
||||
|
||||
TinyGLDriver *_gfx;
|
||||
|
||||
ActorVertex *_faceVBO;
|
||||
FaceBufferMap _faceEBO;
|
||||
|
||||
void clearVertices();
|
||||
void uploadVertices();
|
||||
ActorVertex *createModelVBO(const Model *model);
|
||||
uint32 *createFaceEBO(const Face *face);
|
||||
void setLightArrayUniform(const LightEntryArray &lights);
|
||||
|
||||
Math::Vector3d getShadowLightDirection(const LightEntryArray &lights, const Math::Vector3d &actorPosition, Math::Matrix3 worldToModelRot);
|
||||
|
||||
bool getPointLightContribution(LightEntry *light, const Math::Vector3d &actorPosition,
|
||||
Math::Vector3d &direction, float weight = 1.0f);
|
||||
bool getDirectionalLightContribution(LightEntry *light, Math::Vector3d &direction);
|
||||
bool getSpotLightContribution(LightEntry *light, const Math::Vector3d &actorPosition, Math::Vector3d &direction);
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_TINYGL_ACTOR_H
|
||||
70
engines/stark/gfx/tinyglbitmap.cpp
Normal file
70
engines/stark/gfx/tinyglbitmap.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/* 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/stark/gfx/tinyglbitmap.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
|
||||
#include "common/system.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
TinyGlBitmap::TinyGlBitmap() :
|
||||
Bitmap() {
|
||||
_blitImage = tglGenBlitImage();
|
||||
}
|
||||
|
||||
TinyGlBitmap::~TinyGlBitmap() {
|
||||
tglDeleteBlitImage(_blitImage);
|
||||
}
|
||||
|
||||
void TinyGlBitmap::bind() const {
|
||||
}
|
||||
|
||||
void TinyGlBitmap::update(const Graphics::Surface *surface, const byte *palette) {
|
||||
_width = surface->w;
|
||||
_height = surface->h;
|
||||
|
||||
if (palette) {
|
||||
// TinyGL doesn't currently support images with palettes, so we handle conversion here.
|
||||
Graphics::Surface *convertedSurface = surface->convertTo(getBestPixelFormat(), palette);
|
||||
tglUploadBlitImage(_blitImage, *convertedSurface, 0, false);
|
||||
convertedSurface->free();
|
||||
delete convertedSurface;
|
||||
} else {
|
||||
tglUploadBlitImage(_blitImage, *surface, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
void TinyGlBitmap::setSamplingFilter(Bitmap::SamplingFilter filter) {
|
||||
}
|
||||
|
||||
Graphics::PixelFormat TinyGlBitmap::getBestPixelFormat() const {
|
||||
return g_system->getScreenFormat();
|
||||
}
|
||||
|
||||
TinyGL::BlitImage *TinyGlBitmap::getBlitImage() const {
|
||||
return _blitImage;
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
54
engines/stark/gfx/tinyglbitmap.h
Normal file
54
engines/stark/gfx/tinyglbitmap.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 STARK_GFX_TINYGL_BITMAP_H
|
||||
#define STARK_GFX_TINYGL_BITMAP_H
|
||||
|
||||
#include "engines/stark/gfx/bitmap.h"
|
||||
|
||||
#include "graphics/tinygl/tinygl.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
/**
|
||||
* A TinyGL bitmap wrapper
|
||||
*/
|
||||
class TinyGlBitmap : public Bitmap {
|
||||
public:
|
||||
TinyGlBitmap();
|
||||
virtual ~TinyGlBitmap();
|
||||
|
||||
// Bitmap API
|
||||
void bind() const override;
|
||||
TinyGL::BlitImage *getBlitImage() const;
|
||||
void update(const Graphics::Surface *surface, const byte *palette = nullptr) override;
|
||||
void setSamplingFilter(SamplingFilter filter) override;
|
||||
Graphics::PixelFormat getBestPixelFormat() const override;
|
||||
|
||||
protected:
|
||||
TinyGL::BlitImage *_blitImage;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_TINYGL_TEXTURE_H
|
||||
77
engines/stark/gfx/tinyglfade.cpp
Normal file
77
engines/stark/gfx/tinyglfade.cpp
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/stark/gfx/tinyglfade.h"
|
||||
#include "engines/stark/gfx/tinygl.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
static const TGLfloat fadeVertices[] = {
|
||||
// X Y
|
||||
-1.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
-1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
};
|
||||
|
||||
TinyGLFadeRenderer::TinyGLFadeRenderer(TinyGLDriver *gfx) :
|
||||
FadeRenderer(),
|
||||
_gfx(gfx) {
|
||||
}
|
||||
|
||||
TinyGLFadeRenderer::~TinyGLFadeRenderer() {
|
||||
}
|
||||
|
||||
void TinyGLFadeRenderer::render(float fadeLevel) {
|
||||
_gfx->start2DMode();
|
||||
|
||||
tglMatrixMode(TGL_PROJECTION);
|
||||
tglPushMatrix();
|
||||
tglLoadIdentity();
|
||||
|
||||
tglMatrixMode(TGL_MODELVIEW);
|
||||
tglPushMatrix();
|
||||
tglLoadIdentity();
|
||||
|
||||
tglDisable(TGL_TEXTURE_2D);
|
||||
|
||||
tglColor4f(0.0f, 0.0f, 0.0f, 1.0f - fadeLevel);
|
||||
|
||||
tglEnableClientState(TGL_VERTEX_ARRAY);
|
||||
|
||||
tglVertexPointer(2, TGL_FLOAT, 2 * sizeof(TGLfloat), &fadeVertices[0]);
|
||||
|
||||
tglDrawArrays(TGL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
tglDisableClientState(TGL_VERTEX_ARRAY);
|
||||
|
||||
tglMatrixMode(TGL_MODELVIEW);
|
||||
tglPopMatrix();
|
||||
|
||||
tglMatrixMode(TGL_PROJECTION);
|
||||
tglPopMatrix();
|
||||
|
||||
_gfx->end2DMode();
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
52
engines/stark/gfx/tinyglfade.h
Normal file
52
engines/stark/gfx/tinyglfade.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 STARK_GFX_TINYGL_FADE_H
|
||||
#define STARK_GFX_TINYGL_FADE_H
|
||||
|
||||
#include "engines/stark/gfx/faderenderer.h"
|
||||
|
||||
#include "graphics/tinygl/tinygl.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class TinyGLDriver;
|
||||
|
||||
/**
|
||||
* A programmable pipeline TinyGL fade screen renderer
|
||||
*/
|
||||
class TinyGLFadeRenderer : public FadeRenderer {
|
||||
public:
|
||||
TinyGLFadeRenderer(TinyGLDriver *gfx);
|
||||
~TinyGLFadeRenderer();
|
||||
|
||||
// FadeRenderer API
|
||||
void render(float fadeLevel);
|
||||
|
||||
private:
|
||||
TinyGLDriver *_gfx;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_TINYGL_FADE_H
|
||||
236
engines/stark/gfx/tinyglprop.cpp
Normal file
236
engines/stark/gfx/tinyglprop.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
/* 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/stark/gfx/tinyglprop.h"
|
||||
#include "engines/stark/gfx/texture.h"
|
||||
#include "engines/stark/formats/biffmesh.h"
|
||||
#include "engines/stark/scene.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
TinyGLPropRenderer::TinyGLPropRenderer(TinyGLDriver *gfx) :
|
||||
VisualProp(),
|
||||
_gfx(gfx),
|
||||
_faceVBO(nullptr),
|
||||
_modelIsDirty(true) {
|
||||
}
|
||||
|
||||
TinyGLPropRenderer::~TinyGLPropRenderer() {
|
||||
clearVertices();
|
||||
}
|
||||
|
||||
void TinyGLPropRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
|
||||
if (_modelIsDirty) {
|
||||
clearVertices();
|
||||
uploadVertices();
|
||||
_modelIsDirty = false;
|
||||
}
|
||||
|
||||
_gfx->set3DMode();
|
||||
|
||||
Math::Matrix4 model = getModelMatrix(position, direction);
|
||||
Math::Matrix4 view = StarkScene->getViewMatrix();
|
||||
Math::Matrix4 projection = StarkScene->getProjectionMatrix();
|
||||
|
||||
Math::Matrix4 modelViewMatrix = view * model;
|
||||
modelViewMatrix.transpose(); // TinyGL expects matrices transposed
|
||||
tglMatrixMode(TGL_MODELVIEW);
|
||||
tglLoadMatrixf(modelViewMatrix.getData());
|
||||
|
||||
Math::Matrix4 projectionMatrix = projection;
|
||||
projectionMatrix.transpose(); // TinyGL expects matrices transposed
|
||||
tglMatrixMode(TGL_PROJECTION);
|
||||
tglLoadMatrixf(projectionMatrix.getData());
|
||||
|
||||
Math::Matrix4 normalMatrix;
|
||||
projectionMatrix.transpose();
|
||||
modelViewMatrix.transpose();
|
||||
|
||||
normalMatrix = modelViewMatrix;
|
||||
normalMatrix.invertAffineOrthonormal();
|
||||
|
||||
const Common::Array<Face> &faces = _model->getFaces();
|
||||
const Common::Array<Material> &materials = _model->getMaterials();
|
||||
|
||||
for (Common::Array<Face>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
const Material &material = materials[face->materialId];
|
||||
Math::Vector3d color;
|
||||
const Gfx::Texture *tex = _texture->getTexture(material.texture);
|
||||
if (tex) {
|
||||
tex->bind();
|
||||
tglEnable(TGL_TEXTURE_2D);
|
||||
} else {
|
||||
tglBindTexture(TGL_TEXTURE_2D, 0);
|
||||
tglDisable(TGL_TEXTURE_2D);
|
||||
}
|
||||
auto vertexIndices = _faceEBO[face];
|
||||
auto numVertexIndices = (face)->vertexIndices.size();
|
||||
for (uint32 i = 0; i < numVertexIndices; i++) {
|
||||
uint32 index = vertexIndices[i];
|
||||
auto vertex = _faceVBO[index];
|
||||
if (tex) {
|
||||
color = Math::Vector3d(1.0f, 1.0f, 1.0f);
|
||||
if (material.doubleSided) {
|
||||
vertex.texS = vertex.stexS;
|
||||
vertex.texT = 1.0f - vertex.stexT;
|
||||
} else {
|
||||
vertex.texS = 1.0f - vertex.stexS;
|
||||
vertex.texT = 1.0f - vertex.stexT;
|
||||
}
|
||||
} else {
|
||||
color = Math::Vector3d(material.r, material.g, material.b);
|
||||
}
|
||||
|
||||
Math::Vector4d modelEyePosition = modelViewMatrix * Math::Vector4d(vertex.x, vertex.y, vertex.z, 1.0);
|
||||
Math::Vector3d modelEyeNormal = normalMatrix.getRotation() * Math::Vector3d(vertex.nx, vertex.ny, vertex.nz);
|
||||
modelEyeNormal.normalize();
|
||||
|
||||
static const uint maxLights = 10;
|
||||
|
||||
assert(lights.size() >= 1);
|
||||
assert(lights.size() <= maxLights);
|
||||
|
||||
const LightEntry *ambient = lights[0];
|
||||
assert(ambient->type == LightEntry::kAmbient); // The first light must be the ambient light
|
||||
|
||||
Math::Vector3d lightColor = ambient->color;
|
||||
|
||||
for (uint li = 0; li < lights.size() - 1; li++) {
|
||||
const LightEntry *l = lights[li + 1];
|
||||
|
||||
switch (l->type) {
|
||||
case LightEntry::kPoint: {
|
||||
Math::Vector3d vertexToLight = l->eyePosition.getXYZ() - modelEyePosition.getXYZ();
|
||||
|
||||
float dist = vertexToLight.length();
|
||||
vertexToLight.normalize();
|
||||
float attn = CLIP((l->falloffFar - dist) / MAX(0.001f, l->falloffFar - l->falloffNear), 0.0f, 1.0f);
|
||||
float incidence = MAX(0.0f, Math::Vector3d::dotProduct(modelEyeNormal, vertexToLight));
|
||||
lightColor += l->color * attn * incidence;
|
||||
break;
|
||||
}
|
||||
case LightEntry::kDirectional: {
|
||||
float incidence = MAX(0.0f, Math::Vector3d::dotProduct(modelEyeNormal, -l->eyeDirection));
|
||||
lightColor += (l->color * incidence);
|
||||
break;
|
||||
}
|
||||
case LightEntry::kSpot: {
|
||||
Math::Vector3d vertexToLight = l->eyePosition.getXYZ() - modelEyePosition.getXYZ();
|
||||
|
||||
float dist = vertexToLight.length();
|
||||
float attn = CLIP((l->falloffFar - dist) / MAX(0.001f, l->falloffFar - l->falloffNear), 0.0f, 1.0f);
|
||||
|
||||
vertexToLight.normalize();
|
||||
float incidence = MAX(0.0f, modelEyeNormal.dotProduct(vertexToLight));
|
||||
|
||||
float cosAngle = MAX(0.0f, vertexToLight.dotProduct(-l->eyeDirection));
|
||||
float cone = CLIP((cosAngle - l->innerConeAngle.getCosine()) / MAX(0.001f, l->outerConeAngle.getCosine() - l->innerConeAngle.getCosine()), 0.0f, 1.0f);
|
||||
|
||||
lightColor += l->color * attn * incidence * cone;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lightColor.x() = CLIP(lightColor.x(), 0.0f, 1.0f);
|
||||
lightColor.y() = CLIP(lightColor.y(), 0.0f, 1.0f);
|
||||
lightColor.z() = CLIP(lightColor.z(), 0.0f, 1.0f);
|
||||
color = color * lightColor;
|
||||
vertex.r = color.x();
|
||||
vertex.g = color.y();
|
||||
vertex.b = color.z();
|
||||
_faceVBO[index] = vertex;
|
||||
}
|
||||
|
||||
tglEnableClientState(TGL_VERTEX_ARRAY);
|
||||
tglEnableClientState(TGL_COLOR_ARRAY);
|
||||
if (tex)
|
||||
tglEnableClientState(TGL_TEXTURE_COORD_ARRAY);
|
||||
tglEnableClientState(TGL_NORMAL_ARRAY);
|
||||
|
||||
tglVertexPointer(3, TGL_FLOAT, sizeof(PropVertex), &_faceVBO[0].x);
|
||||
if (tex)
|
||||
tglTexCoordPointer(2, TGL_FLOAT, sizeof(PropVertex), &_faceVBO[0].texS);
|
||||
tglNormalPointer(TGL_FLOAT, sizeof(PropVertex), &_faceVBO[0].nx);
|
||||
tglColorPointer(3, TGL_FLOAT, sizeof(PropVertex), &_faceVBO[0].r);
|
||||
|
||||
tglDrawElements(TGL_TRIANGLES, face->vertexIndices.size(), TGL_UNSIGNED_INT, vertexIndices);
|
||||
|
||||
tglDisableClientState(TGL_VERTEX_ARRAY);
|
||||
tglDisableClientState(TGL_COLOR_ARRAY);
|
||||
tglDisableClientState(TGL_TEXTURE_COORD_ARRAY);
|
||||
tglDisableClientState(TGL_NORMAL_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
void TinyGLPropRenderer::clearVertices() {
|
||||
delete[] _faceVBO;
|
||||
_faceVBO = nullptr;
|
||||
|
||||
for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
|
||||
delete[] it->_value;
|
||||
}
|
||||
|
||||
_faceEBO.clear();
|
||||
}
|
||||
|
||||
void TinyGLPropRenderer::uploadVertices() {
|
||||
_faceVBO = createFaceVBO();
|
||||
|
||||
const Common::Array<Face> &faces = _model->getFaces();
|
||||
for (Common::Array<Face>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
|
||||
_faceEBO[face] = createFaceEBO(face);
|
||||
}
|
||||
}
|
||||
|
||||
PropVertex *TinyGLPropRenderer::createFaceVBO() {
|
||||
const Common::Array<Formats::BiffMesh::Vertex> &modelVertices = _model->getVertices();
|
||||
auto vertices = new PropVertex[modelVertices.size()];
|
||||
// Build a vertex array
|
||||
for (uint32 i = 0; i < modelVertices.size(); i++) {
|
||||
vertices[i].x = modelVertices[i].position.x();
|
||||
vertices[i].y = modelVertices[i].position.y();
|
||||
vertices[i].z = modelVertices[i].position.z();
|
||||
vertices[i].nx = modelVertices[i].normal.x();
|
||||
vertices[i].ny = modelVertices[i].normal.y();
|
||||
vertices[i].nz = modelVertices[i].normal.z();
|
||||
vertices[i].stexS = modelVertices[i].texturePosition.x();
|
||||
vertices[i].stexT = modelVertices[i].texturePosition.y();
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
uint32 *TinyGLPropRenderer::createFaceEBO(const Face *face) {
|
||||
auto indices = new uint32[face->vertexIndices.size()];
|
||||
for (uint32 index = 0; index < face->vertexIndices.size(); index++) {
|
||||
indices[index] = face->vertexIndices[index];
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
82
engines/stark/gfx/tinyglprop.h
Normal file
82
engines/stark/gfx/tinyglprop.h
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_GFX_TINYGL_RENDERED_H
|
||||
#define STARK_GFX_TINYGL_RENDERED_H
|
||||
|
||||
#include "engines/stark/model/model.h"
|
||||
#include "engines/stark/visual/prop.h"
|
||||
#include "engines/stark/gfx/tinygl.h"
|
||||
|
||||
#include "graphics/tinygl/tinygl.h"
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-ptr.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
class Driver;
|
||||
|
||||
struct _PropVertex {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float nx;
|
||||
float ny;
|
||||
float nz;
|
||||
float stexS;
|
||||
float stexT;
|
||||
float texS;
|
||||
float texT;
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
};
|
||||
typedef _PropVertex PropVertex;
|
||||
|
||||
class TinyGLPropRenderer : public VisualProp {
|
||||
public:
|
||||
explicit TinyGLPropRenderer(TinyGLDriver *gfx);
|
||||
~TinyGLPropRenderer() override;
|
||||
|
||||
void render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) override;
|
||||
|
||||
protected:
|
||||
typedef Common::HashMap<const Face *, uint32 *> FaceBufferMap;
|
||||
|
||||
TinyGLDriver *_gfx;
|
||||
|
||||
bool _modelIsDirty;
|
||||
PropVertex *_faceVBO;
|
||||
FaceBufferMap _faceEBO;
|
||||
|
||||
void clearVertices();
|
||||
void uploadVertices();
|
||||
PropVertex *createFaceVBO();
|
||||
uint32 *createFaceEBO(const Face *face);
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_TINYGL_S_RENDERED_H
|
||||
157
engines/stark/gfx/tinyglsurface.cpp
Normal file
157
engines/stark/gfx/tinyglsurface.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
/* 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/stark/gfx/tinyglsurface.h"
|
||||
#include "engines/stark/gfx/tinyglbitmap.h"
|
||||
#include "engines/stark/gfx/color.h"
|
||||
|
||||
#include "graphics/tinygl/tinygl.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
TinyGLSurfaceRenderer::TinyGLSurfaceRenderer(TinyGLDriver *gfx) :
|
||||
SurfaceRenderer(),
|
||||
_gfx(gfx) {
|
||||
}
|
||||
|
||||
TinyGLSurfaceRenderer::~TinyGLSurfaceRenderer() {
|
||||
}
|
||||
|
||||
void TinyGLSurfaceRenderer::render(const Bitmap *bitmap, const Common::Point &dest) {
|
||||
render(bitmap, dest, bitmap->width(), bitmap->height());
|
||||
}
|
||||
|
||||
void TinyGLSurfaceRenderer::render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) {
|
||||
if (width == 0 || height == 0)
|
||||
return;
|
||||
_gfx->start2DMode();
|
||||
|
||||
Math::Vector2d sizeWH;
|
||||
if (_noScalingOverride) {
|
||||
sizeWH = normalizeCurrentCoordinates(width, height);
|
||||
} else {
|
||||
sizeWH = normalizeOriginalCoordinates(width, height);
|
||||
}
|
||||
auto verOffsetXY = normalizeOriginalCoordinates(dest.x, dest.y);
|
||||
auto nativeViewport = _gfx->getViewport();
|
||||
auto viewport = Math::Vector2d(nativeViewport.width(), nativeViewport.height());
|
||||
auto blitImage = ((TinyGlBitmap *)const_cast<Bitmap *>(bitmap))->getBlitImage();
|
||||
int blitImageWidth, blitImageHeight;
|
||||
tglGetBlitImageSize(blitImage, blitImageWidth, blitImageHeight);
|
||||
int posX = viewport.getX() * verOffsetXY.getX() + nativeViewport.left;
|
||||
int posY = viewport.getY() * verOffsetXY.getY() + nativeViewport.top;
|
||||
TinyGL::BlitTransform transform(posX, posY);
|
||||
|
||||
// W/A for not clipped bitmaps in prompt dialog
|
||||
if (width == 256 && height == 256) {
|
||||
blitImageHeight = viewport.getY() - dest.y;
|
||||
blitImageWidth = viewport.getX() - dest.x;
|
||||
}
|
||||
|
||||
transform.sourceRectangle(0, 0, blitImageWidth, blitImageHeight);
|
||||
transform.tint(1.0, 1.0 - _fadeLevel, 1.0 - _fadeLevel, 1.0 - _fadeLevel);
|
||||
tglBlit(blitImage, transform);
|
||||
|
||||
_gfx->end2DMode();
|
||||
}
|
||||
|
||||
void TinyGLSurfaceRenderer::fill(const Color &color, const Common::Point &dest, uint width, uint height) {
|
||||
_gfx->start2DMode();
|
||||
|
||||
SurfaceVertex vertices[4] = {};
|
||||
convertToVertices(vertices, dest, width, height);
|
||||
|
||||
tglMatrixMode(TGL_PROJECTION);
|
||||
tglPushMatrix();
|
||||
tglLoadIdentity();
|
||||
|
||||
tglMatrixMode(TGL_MODELVIEW);
|
||||
tglPushMatrix();
|
||||
tglLoadIdentity();
|
||||
|
||||
tglDisable(TGL_TEXTURE_2D);
|
||||
|
||||
tglEnableClientState(TGL_VERTEX_ARRAY);
|
||||
|
||||
tglVertexPointer(2, TGL_FLOAT, sizeof(SurfaceVertex), &vertices[0].x);
|
||||
tglColor4f((color.r / 255.0f) - _fadeLevel, (color.g / 255.0f) - _fadeLevel, (color.b / 255.0f) - _fadeLevel, color.a / 255.0f);
|
||||
|
||||
tglDrawArrays(TGL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
tglDisableClientState(TGL_VERTEX_ARRAY);
|
||||
|
||||
tglMatrixMode(TGL_MODELVIEW);
|
||||
tglPopMatrix();
|
||||
|
||||
tglMatrixMode(TGL_PROJECTION);
|
||||
tglPopMatrix();
|
||||
|
||||
_gfx->end2DMode();
|
||||
}
|
||||
|
||||
void TinyGLSurfaceRenderer::convertToVertices(SurfaceVertex *vertices, const Common::Point &dest, uint width, uint height) const {
|
||||
const Math::Vector2d surfaceVertices[] = {
|
||||
// X Y
|
||||
{ 0.0f, 0.0f },
|
||||
{ 1.0f, 0.0f },
|
||||
{ 0.0f, 1.0f },
|
||||
{ 1.0f, 1.0f },
|
||||
};
|
||||
|
||||
Math::Vector2d verSizeWH;
|
||||
if (_noScalingOverride) {
|
||||
verSizeWH = normalizeCurrentCoordinates(width, height);
|
||||
} else {
|
||||
verSizeWH = normalizeOriginalCoordinates(width, height);
|
||||
}
|
||||
auto verOffsetXY = normalizeOriginalCoordinates(dest.x, dest.y);
|
||||
auto nativeViewport = _gfx->getViewport();
|
||||
auto viewport = Math::Vector2d(nativeViewport.width(), nativeViewport.height());
|
||||
|
||||
for (int32 v = 0; v < 4; v++) {
|
||||
Math::Vector2d pos = verOffsetXY + (surfaceVertices[v] * verSizeWH);
|
||||
|
||||
if (_snapToGrid) {
|
||||
// Align vertex coordinates to the native pixel grid
|
||||
// This ensures text does not get garbled by nearest neighbors scaling
|
||||
pos.setX(floor(pos.getX() * viewport.getX() + 0.5) / viewport.getX());
|
||||
pos.setY(floor(pos.getY() * viewport.getY() + 0.5) / viewport.getY());
|
||||
}
|
||||
|
||||
// position coords
|
||||
vertices[v].x = pos.getX() * 2.0 - 1.0;
|
||||
vertices[v].y = -1.0 * (pos.getY() * 2.0 - 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
Math::Vector2d TinyGLSurfaceRenderer::normalizeOriginalCoordinates(int x, int y) const {
|
||||
Common::Rect viewport = _gfx->getUnscaledViewport();
|
||||
return Math::Vector2d(x / (float)viewport.width(), y / (float)viewport.height());
|
||||
}
|
||||
|
||||
Math::Vector2d TinyGLSurfaceRenderer::normalizeCurrentCoordinates(int x, int y) const {
|
||||
Common::Rect viewport = _gfx->getViewport();
|
||||
return Math::Vector2d(x / (float)viewport.width(), y / (float)viewport.height());
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
67
engines/stark/gfx/tinyglsurface.h
Normal file
67
engines/stark/gfx/tinyglsurface.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* 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 STARK_GFX_TINYGL_SURFACE_H
|
||||
#define STARK_GFX_TINYGL_SURFACE_H
|
||||
|
||||
#include "engines/stark/gfx/surfacerenderer.h"
|
||||
#include "engines/stark/gfx/tinygl.h"
|
||||
|
||||
#include "graphics/tinygl/tinygl.h"
|
||||
|
||||
#include "math/vector2d.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
class TinyGLDriver;
|
||||
class Bitmap;
|
||||
|
||||
/**
|
||||
* A programmable pipeline TinyGL surface renderer
|
||||
*/
|
||||
class TinyGLSurfaceRenderer : public SurfaceRenderer {
|
||||
public:
|
||||
TinyGLSurfaceRenderer(TinyGLDriver *gfx);
|
||||
virtual ~TinyGLSurfaceRenderer();
|
||||
|
||||
// SurfaceRenderer API
|
||||
void render(const Bitmap *bitmap, const Common::Point &dest) override;
|
||||
void render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) override;
|
||||
void fill(const Color &color, const Common::Point &dest, uint width, uint height) override;
|
||||
|
||||
private:
|
||||
struct SurfaceVertex {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
Math::Vector2d normalizeOriginalCoordinates(int x, int y) const;
|
||||
Math::Vector2d normalizeCurrentCoordinates(int x, int y) const;
|
||||
void convertToVertices(SurfaceVertex *vertices, const Common::Point &dest, uint width, uint height) const;
|
||||
|
||||
TinyGLDriver *_gfx;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_TINYGL_SURFACE_H
|
||||
86
engines/stark/gfx/tinygltexture.cpp
Normal file
86
engines/stark/gfx/tinygltexture.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/* 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/stark/gfx/tinygltexture.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
TinyGlTexture::TinyGlTexture() :
|
||||
Texture(),
|
||||
_id(0),
|
||||
_levelCount(0) {
|
||||
tglGenTextures(1, &_id);
|
||||
|
||||
bind();
|
||||
|
||||
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MIN_FILTER, TGL_NEAREST);
|
||||
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MAG_FILTER, TGL_NEAREST);
|
||||
|
||||
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_S, TGL_CLAMP_TO_EDGE);
|
||||
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_T, TGL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
TinyGlTexture::~TinyGlTexture() {
|
||||
tglDeleteTextures(1, &_id);
|
||||
}
|
||||
|
||||
void TinyGlTexture::bind() const {
|
||||
tglBindTexture(TGL_TEXTURE_2D, _id);
|
||||
}
|
||||
|
||||
void TinyGlTexture::updateLevel(uint32 level, const Graphics::Surface *surface, const byte *palette) {
|
||||
if (surface->format != Driver::getRGBAPixelFormat()) {
|
||||
// Convert the surface to texture format
|
||||
Graphics::Surface *convertedSurface = surface->convertTo(Driver::getRGBAPixelFormat(), palette);
|
||||
|
||||
tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, convertedSurface->w, convertedSurface->h, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, (char *)(convertedSurface->getPixels()));
|
||||
|
||||
convertedSurface->free();
|
||||
delete convertedSurface;
|
||||
} else {
|
||||
// Convert the surface to texture format
|
||||
tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, surface->w, surface->h, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, const_cast<void *>(surface->getPixels()));
|
||||
}
|
||||
}
|
||||
|
||||
void TinyGlTexture::setLevelCount(uint32 count) {
|
||||
_levelCount = count;
|
||||
|
||||
if (count >= 1) {
|
||||
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_S, TGL_MIRRORED_REPEAT);
|
||||
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_T, TGL_MIRRORED_REPEAT);
|
||||
}
|
||||
}
|
||||
|
||||
void TinyGlTexture::addLevel(uint32 level, const Graphics::Surface *surface, const byte *palette) {
|
||||
assert(level < _levelCount);
|
||||
|
||||
if (level == 0) {
|
||||
updateLevel(level, surface, palette);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
55
engines/stark/gfx/tinygltexture.h
Normal file
55
engines/stark/gfx/tinygltexture.h
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_GFX_TINYGL_TEXTURE_H
|
||||
#define STARK_GFX_TINYGL_TEXTURE_H
|
||||
|
||||
#include "engines/stark/gfx/texture.h"
|
||||
|
||||
#include "graphics/tinygl/tinygl.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Gfx {
|
||||
|
||||
/**
|
||||
* A TinyGL texture wrapper
|
||||
*/
|
||||
class TinyGlTexture : public Texture {
|
||||
public:
|
||||
TinyGlTexture();
|
||||
virtual ~TinyGlTexture();
|
||||
|
||||
// Texture API
|
||||
void bind() const override;
|
||||
void setLevelCount(uint32 count) override;
|
||||
void addLevel(uint32 level, const Graphics::Surface *surface, const byte *palette = nullptr) override;
|
||||
|
||||
protected:
|
||||
void updateLevel(uint32 level, const Graphics::Surface *surface, const byte *palette = nullptr);
|
||||
|
||||
TGLuint _id;
|
||||
uint32 _levelCount;
|
||||
};
|
||||
|
||||
} // End of namespace Gfx
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_GFX_TINYGL_TEXTURE_H
|
||||
355
engines/stark/metaengine.cpp
Normal file
355
engines/stark/metaengine.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
/* 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/advancedDetector.h"
|
||||
#include "engines/stark/savemetadata.h"
|
||||
#include "engines/stark/stark.h"
|
||||
#include "engines/stark/services/stateprovider.h"
|
||||
|
||||
#include "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
|
||||
#include "common/savefile.h"
|
||||
#include "common/system.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
static const ADExtraGuiOptionsMap optionsList[] = {
|
||||
{
|
||||
GAMEOPTION_ASSETS_MOD,
|
||||
{
|
||||
_s("Load modded assets"),
|
||||
_s("Enable loading of external replacement assets."),
|
||||
"enable_assets_mod",
|
||||
true,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
{
|
||||
GAMEOPTION_LINEAR_FILTERING,
|
||||
{
|
||||
_s("Enable linear filtering of the backgrounds images"),
|
||||
_s("When linear filtering is enabled the background graphics are smoother in full screen mode, at the cost of some details."),
|
||||
"use_linear_filtering",
|
||||
true,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
{
|
||||
GAMEOPTION_FONT_ANTIALIASING,
|
||||
{
|
||||
_s("Enable font anti-aliasing"),
|
||||
_s("When font anti-aliasing is enabled, the text is smoother."),
|
||||
"enable_font_antialiasing",
|
||||
true,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
AD_EXTRA_GUI_OPTIONS_TERMINATOR
|
||||
};
|
||||
|
||||
class StarkMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
|
||||
public:
|
||||
const char *getName() const override {
|
||||
return "stark";
|
||||
}
|
||||
|
||||
Common::KeymapArray initKeymaps(const char *target) const override;
|
||||
|
||||
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
|
||||
return optionsList;
|
||||
}
|
||||
|
||||
bool hasFeature(MetaEngineFeature f) const override {
|
||||
return
|
||||
(f == kSupportsListSaves) ||
|
||||
(f == kSupportsLoadingDuringStartup) ||
|
||||
(f == kSupportsDeleteSave) ||
|
||||
(f == kSavesSupportThumbnail) ||
|
||||
(f == kSavesSupportMetaInfo) ||
|
||||
(f == kSavesSupportPlayTime) ||
|
||||
(f == kSavesSupportCreationDate);
|
||||
}
|
||||
|
||||
int getMaximumSaveSlot() const override {
|
||||
return 999;
|
||||
}
|
||||
|
||||
SaveStateList listSaves(const char *target) const override {
|
||||
Common::StringArray filenames = StarkEngine::listSaveNames(target);
|
||||
|
||||
SaveStateList saveList;
|
||||
for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
|
||||
int slot = StarkEngine::getSaveNameSlot(target, *filename);
|
||||
|
||||
// Read the description from the save
|
||||
Common::String description;
|
||||
Common::InSaveFile *save = g_system->getSavefileManager()->openForLoading(*filename);
|
||||
if (save) {
|
||||
StateReadStream stream(save);
|
||||
description = stream.readString();
|
||||
}
|
||||
|
||||
saveList.push_back(SaveStateDescriptor(this, slot, description));
|
||||
}
|
||||
|
||||
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
||||
return saveList;
|
||||
}
|
||||
|
||||
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override {
|
||||
Common::String filename = StarkEngine::formatSaveName(target, slot);
|
||||
Common::InSaveFile *save = g_system->getSavefileManager()->openForLoading(filename);
|
||||
if (!save) {
|
||||
return SaveStateDescriptor();
|
||||
}
|
||||
|
||||
SaveStateDescriptor descriptor;
|
||||
descriptor.setSaveSlot(slot);
|
||||
|
||||
SaveMetadata metadata;
|
||||
Common::ErrorCode readError = metadata.read(save, filename);
|
||||
if (readError != Common::kNoError) {
|
||||
delete save;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
descriptor.setDescription(metadata.description);
|
||||
|
||||
if (metadata.version >= 9) {
|
||||
Graphics::Surface *thumb = metadata.readGameScreenThumbnail(save);
|
||||
descriptor.setThumbnail(thumb);
|
||||
descriptor.setPlayTime(metadata.totalPlayTime);
|
||||
descriptor.setSaveDate(metadata.saveYear, metadata.saveMonth, metadata.saveDay);
|
||||
descriptor.setSaveTime(metadata.saveHour, metadata.saveMinute);
|
||||
}
|
||||
|
||||
if (metadata.version >= 13) {
|
||||
descriptor.setAutosave(metadata.isAutoSave);
|
||||
}
|
||||
|
||||
delete save;
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
bool removeSaveState(const char *target, int slot) const override {
|
||||
Common::String filename = StarkEngine::formatSaveName(target, slot);
|
||||
return g_system->getSavefileManager()->removeSavefile(filename);
|
||||
}
|
||||
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override {
|
||||
*engine = new StarkEngine(syst, desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Common::String getSavegameFile(int saveGameIdx, const char *target) const override {
|
||||
if (!target)
|
||||
target = getName();
|
||||
if (saveGameIdx == kSavegameFilePattern)
|
||||
return Common::String::format("%s-###.tlj", target);
|
||||
else
|
||||
return StarkEngine::formatSaveName(target, saveGameIdx);
|
||||
}
|
||||
};
|
||||
|
||||
Common::KeymapArray StarkMetaEngine::initKeymaps(const char *target) const {
|
||||
using namespace Common;
|
||||
using namespace Stark;
|
||||
|
||||
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "stark-default", _("Default keymappings"));
|
||||
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
|
||||
|
||||
Action *act;
|
||||
|
||||
act = new Action(kStandardActionLeftClick, _("Left click"));
|
||||
act->setLeftClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_LEFT");
|
||||
act->addDefaultInputMapping("JOY_A");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionRightClick, _("Right click"));
|
||||
act->setRightClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_RIGHT");
|
||||
act->addDefaultInputMapping("JOY_B");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
// I18N: Opens in-game Diary
|
||||
act = new Action("DIARYMENU", _("Diary menu"));
|
||||
act->setCustomEngineActionEvent(kActionDiaryMenu);
|
||||
act->addDefaultInputMapping("F1");
|
||||
act->addDefaultInputMapping("JOY_X");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SAVEGAME", _("Save game"));
|
||||
act->setCustomEngineActionEvent(kActionSaveGame);
|
||||
act->addDefaultInputMapping("F2");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("LOADGAME", _("Load game"));
|
||||
act->setCustomEngineActionEvent(kActionLoadGame);
|
||||
act->addDefaultInputMapping("F3");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
// I18N: Opens in-game conversation log
|
||||
act = new Action("CONVOLOG", _("Conversation log"));
|
||||
act->setCustomEngineActionEvent(kActionConversationLog);
|
||||
act->addDefaultInputMapping("F4");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
// I18N: Opens in-game Diary. April is the female protagonist name
|
||||
act = new Action("APRILSDIARY", _("April's diary (initially disabled)"));
|
||||
act->setCustomEngineActionEvent(kActionAprilsDiary);
|
||||
act->addDefaultInputMapping("F5");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("VIDREPLAY", _("Video replay"));
|
||||
act->setCustomEngineActionEvent(kActionVideoReplay);
|
||||
act->addDefaultInputMapping("F6");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("GAMESETTINGS", _("Game settings"));
|
||||
act->setCustomEngineActionEvent(kActionGameSettings);
|
||||
act->addDefaultInputMapping("F7");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SAVESCRNSHOT", _("Save screenshot"));
|
||||
act->setCustomEngineActionEvent(kActionSaveScreenshot);
|
||||
act->addDefaultInputMapping("F8");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("TOGGLESUBS", _("Toggle subtitles"));
|
||||
act->setCustomEngineActionEvent(kActionToggleSubtitles);
|
||||
act->addDefaultInputMapping("F9");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("QUITTOMENU", _("Quit to menu"));
|
||||
act->setCustomEngineActionEvent(kActionQuitToMenu);
|
||||
act->addDefaultInputMapping("F10");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("CYCLEBACK", _("Cycle back through inventory cursor items"));
|
||||
act->setCustomEngineActionEvent(kActionCycleForwardInventory);
|
||||
act->addDefaultInputMapping("a");
|
||||
act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("CYCLEFORWARD", _("Cycle forward through inventory cursor items"));
|
||||
act->setCustomEngineActionEvent(kActionCycleBackInventory);
|
||||
act->addDefaultInputMapping("s");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("INVENTORY", _("Inventory"));
|
||||
act->setCustomEngineActionEvent(kActionInventory);
|
||||
act->addDefaultInputMapping("i");
|
||||
act->addDefaultInputMapping("JOY_Y");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
// I18N: A popup on screen shows shows the exits
|
||||
act = new Action("DISPLAYEXITS", _("Display all exits on current location"));
|
||||
act->setCustomEngineActionEvent(kActionDisplayExits);
|
||||
act->addDefaultInputMapping("x");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_STICK");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("EXITGAME", _("Quit game"));
|
||||
act->setCustomEngineActionEvent(kActionExitGame);
|
||||
act->addDefaultInputMapping("A+x");
|
||||
act->addDefaultInputMapping("A+q");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("PAUSE", _("Pause game"));
|
||||
act->setCustomEngineActionEvent(kActionPause);
|
||||
act->addDefaultInputMapping("p");
|
||||
act->addDefaultInputMapping("JOY_LEFT_STICK");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SCROLLUPINV", _("Scroll up in inventory"));
|
||||
act->setCustomEngineActionEvent(kActionInventoryScrollUp);
|
||||
act->addDefaultInputMapping("PAGEUP");
|
||||
act->addDefaultInputMapping("UP");
|
||||
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SCROLLDOWNINV", _("Scroll down in inventory"));
|
||||
act->setCustomEngineActionEvent(kActionInventoryScrollDown);
|
||||
act->addDefaultInputMapping("PAGEDOWN");
|
||||
act->addDefaultInputMapping("DOWN");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SCROLLUPDILOG", _("Scroll up in your dialogs"));
|
||||
act->setCustomEngineActionEvent(kActionDialogueScrollUp);
|
||||
act->addDefaultInputMapping("PAGEUP");
|
||||
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SCROLLDOWNDILOG", _("Scroll down in your dialogs"));
|
||||
act->setCustomEngineActionEvent(kActionDialogueScrollDown);
|
||||
act->addDefaultInputMapping("PAGEDOWN");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SCROLLUPINVPREVDILOG", _("Go to next dialog"));
|
||||
act->setCustomEngineActionEvent(kActionNextDialogue);
|
||||
act->addDefaultInputMapping("DOWN");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SCROLLDOWNINVNEXTDILOG", _("Go to previous dialogs"));
|
||||
act->setCustomEngineActionEvent(kActionPrevDialogue);
|
||||
act->addDefaultInputMapping("UP");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SELECTDILOG", _("Select dialog"));
|
||||
act->setCustomEngineActionEvent(kActionSelectDialogue);
|
||||
act->addDefaultInputMapping("RETURN");
|
||||
act->addDefaultInputMapping("KP_ENTER");
|
||||
act->addDefaultInputMapping("JOY_RIGHT");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SKIP", _("Skip video sequence or dialog"));
|
||||
act->setCustomEngineActionEvent(kActionSkip);
|
||||
act->addDefaultInputMapping("ESCAPE");
|
||||
act->addDefaultInputMapping("JOY_BACK");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
|
||||
KeymapArray keymaps(2);
|
||||
keymaps[0] = engineKeyMap;
|
||||
keymaps[1] = gameKeyMap;
|
||||
|
||||
return keymaps;
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(STARK)
|
||||
REGISTER_PLUGIN_DYNAMIC(STARK, PLUGIN_TYPE_ENGINE, Stark::StarkMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(STARK, PLUGIN_TYPE_ENGINE, Stark::StarkMetaEngine);
|
||||
#endif
|
||||
198
engines/stark/model/animhandler.cpp
Normal file
198
engines/stark/model/animhandler.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/* 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/stark/model/animhandler.h"
|
||||
|
||||
#include "engines/stark/model/model.h"
|
||||
#include "engines/stark/model/skeleton_anim.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
AnimHandler::AnimHandler() :
|
||||
_model(nullptr),
|
||||
_anim(nullptr),
|
||||
_animTime(-1),
|
||||
_framesBeforeCandidateReady(0),
|
||||
_candidateAnim(nullptr),
|
||||
_candidateAnimTime(-1),
|
||||
_blendAnim(nullptr),
|
||||
_blendAnimTime(-1),
|
||||
_blendTimeRemaining(0) {
|
||||
|
||||
}
|
||||
|
||||
AnimHandler::~AnimHandler() {
|
||||
}
|
||||
|
||||
void AnimHandler::setAnim(SkeletonAnim *anim) {
|
||||
if (_candidateAnim == anim) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_anim == anim) {
|
||||
// If we already have the correct anim, clean any candidates
|
||||
// that may have been set but not yet enacted as the active anim.
|
||||
_candidateAnim = nullptr;
|
||||
_candidateAnimTime = -1;
|
||||
_framesBeforeCandidateReady = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't use new animations the first frame they are set.
|
||||
// Scripts may change animation the very next frame,
|
||||
// causing animations to blend with animations that
|
||||
// were only visible for one frame, leading to animation
|
||||
// jumps. Instead store them as candidates.
|
||||
_framesBeforeCandidateReady = 2; // 2 because we are at the end of the frame
|
||||
_candidateAnim = anim;
|
||||
_candidateAnimTime = 0;
|
||||
}
|
||||
|
||||
void AnimHandler::setModel(Model *model) {
|
||||
_model = model;
|
||||
}
|
||||
|
||||
void AnimHandler::setNode(uint32 time, BoneNode *bone, const BoneNode *parent) {
|
||||
const Common::Array<BoneNode *> &bones = _model->getBones();
|
||||
|
||||
if (_blendTimeRemaining <= 0) {
|
||||
_anim->getCoordForBone(time, bone->_idx, bone->_animPos, bone->_animRot);
|
||||
} else {
|
||||
// Blend the coordinates of the previous and the current animation
|
||||
Math::Vector3d previousAnimPos, animPos;
|
||||
Math::Quaternion previousAnimRot, animRot;
|
||||
_blendAnim->getCoordForBone(_blendAnimTime, bone->_idx, previousAnimPos, previousAnimRot);
|
||||
_anim->getCoordForBone(time, bone->_idx, animPos, animRot);
|
||||
|
||||
float blendingRatio = 1.0 - _blendTimeRemaining / (float)_blendDuration;
|
||||
|
||||
bone->_animPos = previousAnimPos + (animPos - previousAnimPos) * blendingRatio;
|
||||
bone->_animRot = previousAnimRot.slerpQuat(animRot, blendingRatio);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
parent->_animRot.transform(bone->_animPos);
|
||||
|
||||
bone->_animPos = parent->_animPos + bone->_animPos;
|
||||
bone->_animRot = parent->_animRot * bone->_animRot;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < bone->_children.size(); ++i) {
|
||||
setNode(time, bones[bone->_children[i]], bone);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimHandler::animate(uint32 time) {
|
||||
if (!_anim && _candidateAnim) {
|
||||
// This is the first time we animate this item.
|
||||
enactCandidate();
|
||||
}
|
||||
|
||||
if (_candidateAnim && _anim && _anim->getBoneCount() != _model->getBones().size()) {
|
||||
// We changed to an incompatible model
|
||||
enactCandidate();
|
||||
|
||||
// And the anim we were previously blending with is incompatible as well
|
||||
if (_blendAnim && _blendAnim->getBoneCount() != _model->getBones().size()) {
|
||||
stopBlending();
|
||||
}
|
||||
}
|
||||
|
||||
if (_candidateAnim && _framesBeforeCandidateReady > 0) {
|
||||
|
||||
_candidateAnimTime = time;
|
||||
_framesBeforeCandidateReady--;
|
||||
|
||||
// We need to animate here, because the model may have
|
||||
// changed from under us.
|
||||
const Common::Array<BoneNode *> &bones = _model->getBones();
|
||||
setNode(_animTime, bones[0], nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_candidateAnim && _framesBeforeCandidateReady <= 0) {
|
||||
if (_anim) {
|
||||
startBlending();
|
||||
}
|
||||
enactCandidate();
|
||||
}
|
||||
|
||||
int32 deltaTime = time - _animTime;
|
||||
if (deltaTime < 0 || time > _blendDuration / 2) {
|
||||
deltaTime = 33;
|
||||
}
|
||||
|
||||
updateBlending(deltaTime);
|
||||
|
||||
// Start at root bone
|
||||
// For each child
|
||||
// - Set childs animation coordinate
|
||||
// - Process that childs children
|
||||
|
||||
const Common::Array<BoneNode *> &bones = _model->getBones();
|
||||
if (deltaTime >= 0) {
|
||||
setNode(time, bones[0], nullptr);
|
||||
_animTime = time;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimHandler::enactCandidate() {
|
||||
_anim = _candidateAnim;
|
||||
_animTime = _candidateAnimTime;
|
||||
_candidateAnim = nullptr;
|
||||
_candidateAnimTime = -1;
|
||||
_framesBeforeCandidateReady = 0;
|
||||
}
|
||||
|
||||
void AnimHandler::startBlending() {
|
||||
_blendTimeRemaining = _blendDuration;
|
||||
_blendAnim = _anim;
|
||||
_blendAnimTime = _animTime;
|
||||
}
|
||||
|
||||
void AnimHandler::updateBlending(int32 deltaTime) {
|
||||
_blendTimeRemaining -= deltaTime;
|
||||
if (_blendTimeRemaining > 0) {
|
||||
// If we are blending, also update the previous animation's time
|
||||
_blendAnimTime += deltaTime;
|
||||
if (_blendAnimTime >= (int32) _blendAnim->getLength()) {
|
||||
_blendAnimTime = _blendAnim->getLength() - 1;
|
||||
}
|
||||
} else {
|
||||
// Otherwise make sure blending is not enabled
|
||||
stopBlending();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimHandler::stopBlending() {
|
||||
_blendAnim = nullptr;
|
||||
_blendAnimTime = -1;
|
||||
_blendTimeRemaining = 0;
|
||||
}
|
||||
|
||||
void AnimHandler::resetBlending() {
|
||||
stopBlending();
|
||||
if (_candidateAnim) {
|
||||
enactCandidate();
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
81
engines/stark/model/animhandler.h
Normal file
81
engines/stark/model/animhandler.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/* 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 STARK_MODEL_ANIM_HANDLER_H
|
||||
#define STARK_MODEL_ANIM_HANDLER_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class Model;
|
||||
class BoneNode;
|
||||
class SkeletonAnim;
|
||||
|
||||
/**
|
||||
* Animate a skeletal model's bones according to an animation
|
||||
*/
|
||||
class AnimHandler {
|
||||
public:
|
||||
AnimHandler();
|
||||
~AnimHandler();
|
||||
|
||||
/**
|
||||
* Increment the animation timestamp, and apply bone animations if required
|
||||
*/
|
||||
void animate(uint32 time);
|
||||
|
||||
/** Set the skeletal model to animate */
|
||||
void setModel(Model *model);
|
||||
|
||||
/** Set the skeletal animation to use */
|
||||
void setAnim(SkeletonAnim *anim);
|
||||
|
||||
/** Stop blending and forget about the previous animation */
|
||||
void resetBlending();
|
||||
|
||||
private:
|
||||
void enactCandidate();
|
||||
void startBlending();
|
||||
void updateBlending(int32 deltaTime);
|
||||
void stopBlending();
|
||||
|
||||
void setNode(uint32 time, BoneNode *bone, const BoneNode *parent);
|
||||
|
||||
static const uint32 _blendDuration = 300; // ms
|
||||
|
||||
SkeletonAnim *_anim;
|
||||
int32 _animTime;
|
||||
|
||||
int32 _framesBeforeCandidateReady;
|
||||
SkeletonAnim *_candidateAnim;
|
||||
int32 _candidateAnimTime;
|
||||
|
||||
SkeletonAnim *_blendAnim;
|
||||
int32 _blendAnimTime;
|
||||
int32 _blendTimeRemaining;
|
||||
|
||||
Model *_model;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MODEL_ANIM_HANDLER_H
|
||||
233
engines/stark/model/model.cpp
Normal file
233
engines/stark/model/model.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
/* 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/stark/model/model.h"
|
||||
|
||||
#include "engines/stark/services/archiveloader.h"
|
||||
#include "engines/stark/model/animhandler.h"
|
||||
#include "engines/stark/gfx/texture.h"
|
||||
|
||||
#include "math/aabb.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
Model::Model() :
|
||||
_u1(0),
|
||||
_u2(0.0) {
|
||||
|
||||
}
|
||||
|
||||
Model::~Model() {
|
||||
for (Common::Array<VertNode *>::iterator it = _vertices.begin(); it != _vertices.end(); ++it)
|
||||
delete *it;
|
||||
|
||||
for (Common::Array<Material *>::iterator it = _materials.begin(); it != _materials.end(); ++it)
|
||||
delete *it;
|
||||
|
||||
for (Common::Array<Face *>::iterator it = _faces.begin(); it != _faces.end(); ++it)
|
||||
delete *it;
|
||||
|
||||
for (Common::Array<BoneNode *>::iterator it = _bones.begin(); it != _bones.end(); ++it)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
void Model::readFromStream(ArchiveReadStream *stream) {
|
||||
uint32 id = stream->readUint32LE();
|
||||
if (id != 4) {
|
||||
error("Wrong magic 1 while reading actor '%d'", id);
|
||||
}
|
||||
|
||||
uint32 format = stream->readUint32LE();
|
||||
if (format == 256) {
|
||||
_u1 = stream->readUint32LE();
|
||||
} else if (format == 16) {
|
||||
_u1 = 0;
|
||||
} else {
|
||||
error("Wrong format while reading actor '%d'", format);
|
||||
}
|
||||
|
||||
uint32 id2 = stream->readUint32LE();
|
||||
if (id2 != 0xDEADBABE) {
|
||||
error("Wrong magic 2 while reading actor '%d'", id2);
|
||||
}
|
||||
|
||||
_u2 = stream->readFloatLE();
|
||||
|
||||
uint32 numMaterials = stream->readUint32LE();
|
||||
|
||||
for (uint i = 0; i < numMaterials; ++i) {
|
||||
Material *node = new Material();
|
||||
node->name = stream->readString();
|
||||
stream->readUint32LE(); // CHECKME: Unknown data
|
||||
node->texture = stream->readString();
|
||||
node->r = stream->readFloatLE();
|
||||
node->g = stream->readFloatLE();
|
||||
node->b = stream->readFloatLE();
|
||||
_materials.push_back(node);
|
||||
}
|
||||
|
||||
uint32 numUnknowns = stream->readUint32LE();
|
||||
if (numUnknowns != 0) {
|
||||
error("Found a mesh with numUnknowns != 0");
|
||||
}
|
||||
|
||||
readBones(stream);
|
||||
|
||||
uint32 numMeshes = stream->readUint32LE();
|
||||
if (numMeshes != 1) {
|
||||
error("Found a mesh with numMeshes != 1 (%d)", numMeshes);
|
||||
}
|
||||
|
||||
_name = stream->readString();
|
||||
|
||||
uint32 numFaces = stream->readUint32LE();
|
||||
for (uint32 j = 0; j < numFaces; ++j) {
|
||||
uint faceVertexIndexOffset = _vertices.size();
|
||||
|
||||
Face *face = new Face();
|
||||
face->materialId = stream->readUint32LE();
|
||||
|
||||
uint32 numVertices = stream->readUint32LE();
|
||||
for (uint32 k = 0; k < numVertices; ++k) {
|
||||
VertNode *vert = new VertNode();
|
||||
vert->_pos1 = stream->readVector3();
|
||||
vert->_pos2 = stream->readVector3();
|
||||
vert->_normal = stream->readVector3();
|
||||
vert->_texS = stream->readFloatLE();
|
||||
vert->_texT = stream->readFloatLE();
|
||||
vert->_bone1 = stream->readUint32LE();
|
||||
vert->_bone2 = stream->readUint32LE();
|
||||
vert->_boneWeight = stream->readFloatLE();
|
||||
_vertices.push_back(vert);
|
||||
}
|
||||
|
||||
uint32 numTriangles = stream->readUint32LE();
|
||||
face->vertexIndices.resize(numTriangles * 3); // 3 vertex indices per triangle
|
||||
for (uint32 k = 0; k < numTriangles; ++k) {
|
||||
face->vertexIndices[k * 3 + 0] = stream->readUint32LE() + faceVertexIndexOffset;
|
||||
face->vertexIndices[k * 3 + 1] = stream->readUint32LE() + faceVertexIndexOffset;
|
||||
face->vertexIndices[k * 3 + 2] = stream->readUint32LE() + faceVertexIndexOffset;
|
||||
}
|
||||
|
||||
_faces.push_back(face);
|
||||
}
|
||||
|
||||
buildBonesBoundingBoxes();
|
||||
}
|
||||
|
||||
void Model::readBones(ArchiveReadStream *stream) {
|
||||
uint32 numBones = stream->readUint32LE();
|
||||
for (uint32 i = 0; i < numBones; ++i) {
|
||||
BoneNode *node = new BoneNode();
|
||||
node->_name = stream->readString();
|
||||
node->_u1 = stream->readFloatLE();
|
||||
|
||||
uint32 len = stream->readUint32LE();
|
||||
for (uint32 j = 0; j < len; ++j)
|
||||
node->_children.push_back(stream->readUint32LE());
|
||||
|
||||
node->_idx = _bones.size();
|
||||
_bones.push_back(node);
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < numBones; ++i) {
|
||||
BoneNode *node = _bones[i];
|
||||
for (uint j = 0; j < node->_children.size(); ++j) {
|
||||
_bones[node->_children[j]]->_parent = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Model::buildBonesBoundingBoxes() {
|
||||
for (uint i = 0; i < _bones.size(); i++) {
|
||||
buildBoneBoundingBox(_bones[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::buildBoneBoundingBox(BoneNode *bone) const {
|
||||
bone->_boundingBox.reset();
|
||||
|
||||
// Add all the vertices with a non zero weight for the bone to the bone's bounding box
|
||||
for (uint k = 0; k < _vertices.size(); k++) {
|
||||
VertNode *vert = _vertices[k];
|
||||
|
||||
if (vert->_bone1 == bone->_idx) {
|
||||
bone->_boundingBox.expand(vert->_pos1);
|
||||
}
|
||||
|
||||
if (vert->_bone2 == bone->_idx) {
|
||||
bone->_boundingBox.expand(vert->_pos2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Model::intersectRay(const Math::Ray &ray) const {
|
||||
for (uint i = 0; i < _bones.size(); i++) {
|
||||
if (_bones[i]->intersectRay(ray)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Model::updateBoundingBox() {
|
||||
_boundingBox.reset();
|
||||
for (uint i = 0; i < _bones.size(); i++) {
|
||||
_bones[i]->expandModelSpaceBB(_boundingBox);
|
||||
}
|
||||
}
|
||||
|
||||
Math::AABB Model::getBoundingBox() const {
|
||||
return _boundingBox;
|
||||
}
|
||||
|
||||
bool BoneNode::intersectRay(const Math::Ray &ray) const {
|
||||
Math::Ray localRay = ray;
|
||||
localRay.translate(-_animPos);
|
||||
localRay.rotate(_animRot.inverse());
|
||||
|
||||
return localRay.intersectAABB(_boundingBox);
|
||||
}
|
||||
|
||||
void BoneNode::expandModelSpaceBB(Math::AABB &aabb) const {
|
||||
// Transform the bounding box
|
||||
Math::Vector3d min = _boundingBox.getMin();
|
||||
Math::Vector3d max = _boundingBox.getMax();
|
||||
|
||||
Math::Vector3d verts[8];
|
||||
verts[0].set(min.x(), min.y(), min.z());
|
||||
verts[1].set(max.x(), min.y(), min.z());
|
||||
verts[2].set(min.x(), max.y(), min.z());
|
||||
verts[3].set(min.x(), min.y(), max.z());
|
||||
verts[4].set(max.x(), max.y(), min.z());
|
||||
verts[5].set(max.x(), min.y(), max.z());
|
||||
verts[6].set(min.x(), max.y(), max.z());
|
||||
verts[7].set(max.x(), max.y(), max.z());
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
_animRot.transform(verts[i]);
|
||||
verts[i] += _animPos;
|
||||
aabb.expand(verts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
133
engines/stark/model/model.h
Normal file
133
engines/stark/model/model.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/* 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 STARK_MODEL_MODEL_H
|
||||
#define STARK_MODEL_MODEL_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/str.h"
|
||||
|
||||
#include "math/ray.h"
|
||||
#include "math/vector3d.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Gfx {
|
||||
class TextureSet;
|
||||
}
|
||||
|
||||
class ArchiveReadStream;
|
||||
|
||||
class VertNode {
|
||||
public:
|
||||
Math::Vector3d _pos1, _pos2;
|
||||
Math::Vector3d _normal;
|
||||
float _texS, _texT;
|
||||
uint32 _bone1, _bone2;
|
||||
float _boneWeight;
|
||||
};
|
||||
|
||||
struct Face {
|
||||
uint32 materialId;
|
||||
Common::Array<uint32> vertexIndices;
|
||||
|
||||
Face() : materialId(0) {}
|
||||
};
|
||||
|
||||
struct Material {
|
||||
Common::String name;
|
||||
Common::String texture;
|
||||
float r, g, b;
|
||||
bool doubleSided;
|
||||
|
||||
Material() : r(0), g(0), b(0), doubleSided(false) {};
|
||||
};
|
||||
|
||||
class BoneNode {
|
||||
public:
|
||||
BoneNode() : _parent(-1), _idx(0), _u1(0) {}
|
||||
~BoneNode() { }
|
||||
|
||||
/** Perform a collision test with the ray */
|
||||
bool intersectRay(const Math::Ray &ray) const;
|
||||
|
||||
/** Expand a bounding box with the model space BB of this bone */
|
||||
void expandModelSpaceBB(Math::AABB &aabb) const;
|
||||
|
||||
Common::String _name;
|
||||
float _u1;
|
||||
Common::Array<uint32> _children;
|
||||
int _parent;
|
||||
uint32 _idx;
|
||||
|
||||
Math::Vector3d _animPos;
|
||||
Math::Quaternion _animRot;
|
||||
|
||||
/** Bone space bounding box */
|
||||
Math::AABB _boundingBox;
|
||||
};
|
||||
|
||||
/**
|
||||
* A 3D Model
|
||||
*/
|
||||
class Model {
|
||||
public:
|
||||
Model();
|
||||
~Model();
|
||||
|
||||
/**
|
||||
* Try and initialise object from the specified stream
|
||||
*/
|
||||
void readFromStream(ArchiveReadStream *stream);
|
||||
|
||||
const Common::Array<VertNode *> &getVertices() const { return _vertices; }
|
||||
const Common::Array<Face *> &getFaces() const { return _faces; }
|
||||
const Common::Array<Material *> &getMaterials() const { return _materials; }
|
||||
const Common::Array<BoneNode *> &getBones() const { return _bones; };
|
||||
|
||||
/** Perform a collision test with a ray */
|
||||
bool intersectRay(const Math::Ray &ray) const;
|
||||
|
||||
/** Update the model bounding box with the current animation state */
|
||||
void updateBoundingBox();
|
||||
|
||||
/** Retrieve the model space bounding box for the current animation state */
|
||||
Math::AABB getBoundingBox() const;
|
||||
|
||||
private:
|
||||
void buildBonesBoundingBoxes();
|
||||
void buildBoneBoundingBox(BoneNode *bone) const;
|
||||
void readBones(ArchiveReadStream *stream);
|
||||
|
||||
Common::String _name;
|
||||
uint32 _u1;
|
||||
float _u2;
|
||||
|
||||
Common::Array<VertNode *> _vertices;
|
||||
Common::Array<Material *> _materials;
|
||||
Common::Array<Face *> _faces;
|
||||
Common::Array<BoneNode *> _bones;
|
||||
Math::AABB _boundingBox;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MODEL_MODEL_H
|
||||
107
engines/stark/model/skeleton_anim.cpp
Normal file
107
engines/stark/model/skeleton_anim.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/* 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/stark/model/skeleton_anim.h"
|
||||
|
||||
#include "engines/stark/services/archiveloader.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
SkeletonAnim::SkeletonAnim() :
|
||||
_id(0),
|
||||
_ver(0),
|
||||
_u1(0),
|
||||
_u2(0),
|
||||
_time(0) {
|
||||
}
|
||||
|
||||
void SkeletonAnim::createFromStream(ArchiveReadStream *stream) {
|
||||
_id = stream->readUint32LE();
|
||||
_ver = stream->readUint32LE();
|
||||
if (_ver == 3) {
|
||||
_u1 = 0;
|
||||
_time = stream->readUint32LE();
|
||||
_u2 = stream->readUint32LE();
|
||||
} else {
|
||||
_u1 = stream->readUint32LE();
|
||||
_u2 = stream->readUint32LE();
|
||||
_time = stream->readUint32LE();
|
||||
}
|
||||
if (_u2 != 0xdeadbabe) {
|
||||
error("Wrong magic while reading animation");
|
||||
}
|
||||
|
||||
uint32 num = stream->readUint32LE();
|
||||
_boneAnims.resize(num);
|
||||
for (uint32 i = 0; i < num; ++i) {
|
||||
uint32 bone = stream->readUint32LE();
|
||||
uint32 numKeys = stream->readUint32LE();
|
||||
|
||||
BoneAnim &boneAnim = _boneAnims[bone];
|
||||
boneAnim._keys.resize(numKeys);
|
||||
for (uint32 j = 0; j < numKeys; ++j) {
|
||||
AnimKey &key = boneAnim._keys[j];
|
||||
key._time = stream->readUint32LE();
|
||||
key._rot = stream->readQuaternion();
|
||||
key._pos = stream->readVector3();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonAnim::getCoordForBone(uint32 time, int boneIdx, Math::Vector3d &pos, Math::Quaternion &rot) const {
|
||||
const Common::Array<AnimKey> &keys = _boneAnims[boneIdx]._keys;
|
||||
|
||||
if (keys.size() == 1) {
|
||||
// There is only one key for this bone, don't bother searching which one to use
|
||||
pos = keys[0]._pos;
|
||||
rot = keys[0]._rot;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (Common::Array<AnimKey>::const_iterator it = keys.begin(); it < keys.end(); ++it) {
|
||||
if (it->_time > time) {
|
||||
// Between two key frames, interpolate
|
||||
const AnimKey *a = it;
|
||||
--it;
|
||||
const AnimKey *b = it;
|
||||
|
||||
float t = (float)(time - b->_time) / (float)(a->_time - b->_time);
|
||||
|
||||
pos = b->_pos + (a->_pos - b->_pos) * t;
|
||||
rot = b->_rot.slerpQuat(a->_rot, t);
|
||||
|
||||
return;
|
||||
} else if (it->_time == time || it == keys.end() - 1){
|
||||
// At a key frame
|
||||
// If not right one but didn't find any, then use last one as default
|
||||
const AnimKey *key = it;
|
||||
pos = key->_pos;
|
||||
rot = key->_rot;
|
||||
if (it == keys.end() - 1) {
|
||||
warning("Unable to find keyframe for bone '%d' at %d ms, using default", boneIdx, time);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
73
engines/stark/model/skeleton_anim.h
Normal file
73
engines/stark/model/skeleton_anim.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 STARK_MODEL_SKELETON_ANIM_H
|
||||
#define STARK_MODEL_SKELETON_ANIM_H
|
||||
|
||||
#include "math/quat.h"
|
||||
#include "math/vector3d.h"
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class ArchiveReadStream;
|
||||
|
||||
/**
|
||||
* Data structure responsible for skeletal animation of an actor object.
|
||||
*/
|
||||
class SkeletonAnim {
|
||||
public:
|
||||
SkeletonAnim();
|
||||
|
||||
void createFromStream(ArchiveReadStream *stream);
|
||||
|
||||
/**
|
||||
* Get the interpolated bone coordinate for a given bone at a given animation timestamp
|
||||
*/
|
||||
void getCoordForBone(uint32 time, int boneIdx, Math::Vector3d &pos, Math::Quaternion &rot) const;
|
||||
|
||||
/**
|
||||
* Get total animation length (in ms)
|
||||
*/
|
||||
uint32 getLength() const { return _time; }
|
||||
|
||||
/** The number of bones a skeleton must have to play this animation */
|
||||
uint32 getBoneCount() const { return _boneAnims.size(); }
|
||||
|
||||
private:
|
||||
struct AnimKey {
|
||||
uint32 _time;
|
||||
Math::Quaternion _rot;
|
||||
Math::Vector3d _pos;
|
||||
};
|
||||
|
||||
struct BoneAnim {
|
||||
Common::Array<AnimKey> _keys;
|
||||
};
|
||||
|
||||
uint32 _id, _ver, _u1, _u2, _time;
|
||||
|
||||
Common::Array<BoneAnim> _boneAnims;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MODEL_SKELETON_ANIM_H
|
||||
148
engines/stark/module.mk
Normal file
148
engines/stark/module.mk
Normal file
@@ -0,0 +1,148 @@
|
||||
MODULE := engines/stark
|
||||
|
||||
MODULE_OBJS := \
|
||||
console.o \
|
||||
gfx/driver.o \
|
||||
gfx/opengls.o \
|
||||
gfx/openglsactor.o \
|
||||
gfx/openglsfade.o \
|
||||
gfx/openglsprop.o \
|
||||
gfx/openglssurface.o \
|
||||
gfx/opengl.o \
|
||||
gfx/openglactor.o \
|
||||
gfx/openglbitmap.o \
|
||||
gfx/openglfade.o \
|
||||
gfx/openglprop.o \
|
||||
gfx/openglsurface.o \
|
||||
gfx/opengltexture.o \
|
||||
gfx/renderentry.o \
|
||||
gfx/surfacerenderer.o \
|
||||
gfx/texture.o \
|
||||
formats/biff.o \
|
||||
formats/biffmesh.o \
|
||||
formats/dds.o \
|
||||
formats/iss.o \
|
||||
formats/tm.o \
|
||||
formats/xarc.o \
|
||||
formats/xmg.o \
|
||||
formats/xrc.o \
|
||||
metaengine.o \
|
||||
model/animhandler.o \
|
||||
model/model.o \
|
||||
model/skeleton_anim.o \
|
||||
movement/followpath.o \
|
||||
movement/followpathlight.o \
|
||||
movement/movement.o \
|
||||
movement/shortestpath.o \
|
||||
movement/stringpullingpath.o \
|
||||
movement/turn.o \
|
||||
movement/walk.o \
|
||||
resources/anim.o \
|
||||
resources/animhierarchy.o \
|
||||
resources/animscript.o \
|
||||
resources/animsoundtrigger.o \
|
||||
resources/bonesmesh.o \
|
||||
resources/bookmark.o \
|
||||
resources/camera.o \
|
||||
resources/container.o \
|
||||
resources/command.o \
|
||||
resources/dialog.o \
|
||||
resources/direction.o \
|
||||
resources/floor.o \
|
||||
resources/floorface.o \
|
||||
resources/floorfield.o \
|
||||
resources/fmv.o \
|
||||
resources/image.o \
|
||||
resources/item.o \
|
||||
resources/knowledge.o \
|
||||
resources/knowledgeset.o \
|
||||
resources/layer.o \
|
||||
resources/level.o \
|
||||
resources/light.o \
|
||||
resources/lipsync.o \
|
||||
resources/location.o \
|
||||
resources/object.o \
|
||||
resources/path.o \
|
||||
resources/pattable.o \
|
||||
resources/root.o \
|
||||
resources/script.o \
|
||||
resources/scroll.o \
|
||||
resources/sound.o \
|
||||
resources/speech.o \
|
||||
resources/string.o \
|
||||
resources/textureset.o \
|
||||
resourcereference.o \
|
||||
savemetadata.o \
|
||||
scene.o \
|
||||
services/archiveloader.o \
|
||||
services/dialogplayer.o \
|
||||
services/diary.o \
|
||||
services/fontprovider.o \
|
||||
services/gameinterface.o \
|
||||
services/global.o \
|
||||
services/resourceprovider.o \
|
||||
services/services.o \
|
||||
services/stateprovider.o \
|
||||
services/staticprovider.o \
|
||||
services/userinterface.o \
|
||||
services/settings.o \
|
||||
services/gamechapter.o \
|
||||
services/gamemessage.o \
|
||||
stark.o \
|
||||
tools/abstractsyntaxtree.o \
|
||||
tools/block.o \
|
||||
tools/command.o \
|
||||
tools/decompiler.o \
|
||||
ui/cursor.o \
|
||||
ui/dialogbox.o \
|
||||
ui/menu/diaryindex.o \
|
||||
ui/menu/locationscreen.o \
|
||||
ui/menu/mainmenu.o \
|
||||
ui/menu/settingsmenu.o \
|
||||
ui/menu/saveloadmenu.o \
|
||||
ui/menu/fmvmenu.o \
|
||||
ui/menu/diarypages.o \
|
||||
ui/menu/dialogmenu.o \
|
||||
ui/window.o \
|
||||
ui/world/actionmenu.o \
|
||||
ui/world/button.o \
|
||||
ui/world/clicktext.o \
|
||||
ui/world/topmenu.o \
|
||||
ui/world/dialogpanel.o \
|
||||
ui/world/fmvscreen.o \
|
||||
ui/world/gamescreen.o \
|
||||
ui/world/gamewindow.o \
|
||||
ui/world/inventorywindow.o \
|
||||
visual/actor.o \
|
||||
visual/effects/bubbles.o \
|
||||
visual/effects/effect.o \
|
||||
visual/effects/fireflies.o \
|
||||
visual/effects/fish.o \
|
||||
visual/explodingimage.o \
|
||||
visual/flashingimage.o \
|
||||
visual/image.o \
|
||||
visual/prop.o \
|
||||
visual/smacker.o \
|
||||
visual/text.o
|
||||
|
||||
ifdef USE_TINYGL
|
||||
MODULE_OBJS += \
|
||||
gfx/tinygl.o \
|
||||
gfx/tinyglactor.o \
|
||||
gfx/tinyglbitmap.o \
|
||||
gfx/tinyglfade.o \
|
||||
gfx/tinyglprop.o \
|
||||
gfx/tinyglsurface.o \
|
||||
gfx/tinygltexture.o
|
||||
endif
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_STARK), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
159
engines/stark/movement/followpath.cpp
Normal file
159
engines/stark/movement/followpath.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
/* 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/stark/movement/followpath.h"
|
||||
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/stateprovider.h"
|
||||
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/floor.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/path.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
FollowPath::FollowPath(Resources::ItemVisual *item) :
|
||||
Movement(item),
|
||||
_path(nullptr),
|
||||
_speed(0.0),
|
||||
_position(0.0),
|
||||
_previouslyEnabled(true),
|
||||
_anim(nullptr) {
|
||||
}
|
||||
|
||||
FollowPath::~FollowPath() {
|
||||
}
|
||||
|
||||
void FollowPath::start() {
|
||||
Movement::start();
|
||||
|
||||
_previouslyEnabled = _item->isEnabled();
|
||||
_item->setEnabled(true);
|
||||
|
||||
updateItemPosition(0, 0);
|
||||
changeItemAnim();
|
||||
}
|
||||
|
||||
void FollowPath::stop(bool force) {
|
||||
Movement::stop(force);
|
||||
|
||||
changeItemAnim();
|
||||
_item->setEnabled(_previouslyEnabled);
|
||||
}
|
||||
|
||||
void FollowPath::onGameLoop() {
|
||||
// Compute the new position on the path
|
||||
_position += _speed * StarkGlobal->getMillisecondsPerGameloop();
|
||||
|
||||
// Find the current path edge, and position on the path edge
|
||||
uint currentEdge = 0;
|
||||
float positionInEdge = _position;
|
||||
for (uint i = 0; i < _path->getEdgeCount(); i++) {
|
||||
float edgeLength = _path->getWeightedEdgeLength(i);
|
||||
if (positionInEdge < edgeLength) {
|
||||
break; // Found the current path edge
|
||||
}
|
||||
|
||||
positionInEdge -= edgeLength;
|
||||
currentEdge++;
|
||||
}
|
||||
|
||||
// Check if we went beyond the path's end
|
||||
if (currentEdge >= _path->getEdgeCount()) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
updateItemPosition(currentEdge, positionInEdge);
|
||||
}
|
||||
|
||||
void FollowPath::updateItemPosition(uint currentEdge, float positionInEdge) const {// Get the new position for the item
|
||||
Math::Vector3d newPosition = _path->getWeightedPositionInEdge(currentEdge, positionInEdge);
|
||||
|
||||
// Update the item's properties in the scene
|
||||
if (is3D()) {
|
||||
Resources::FloorPositionedItem *item3D = Resources::Object::cast<Resources::FloorPositionedItem>(_item);
|
||||
Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor();
|
||||
|
||||
int32 floorFaceIndex = floor->findFaceContainingPoint(newPosition);
|
||||
if (floorFaceIndex >= 0) {
|
||||
item3D->setFloorFaceIndex(floorFaceIndex);
|
||||
} else {
|
||||
item3D->overrideSortKey(_path->getSortKey());
|
||||
}
|
||||
|
||||
item3D->setPosition3D(newPosition);
|
||||
|
||||
Math::Vector3d direction = _path->getEdgeDirection(currentEdge);
|
||||
item3D->setDirection(computeAngleBetweenVectorsXYPlane(direction, Math::Vector3d(1.0, 0.0, 0.0)));
|
||||
} else {
|
||||
Common::Point position2D = Common::Point(newPosition.x(), newPosition.y());
|
||||
_item->setPosition2D(position2D);
|
||||
}
|
||||
}
|
||||
|
||||
void FollowPath::changeItemAnim() {
|
||||
if (_ended) {
|
||||
if (_anim) {
|
||||
_item->resetActionAnim();
|
||||
} else {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityIdle);
|
||||
}
|
||||
} else {
|
||||
if (_anim) {
|
||||
_item->playActionAnim(_anim);
|
||||
} else {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityWalk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FollowPath::setPath(Resources::Path *path) {
|
||||
_path = path;
|
||||
}
|
||||
|
||||
void FollowPath::setSpeed(float speed) {
|
||||
_speed = speed;
|
||||
}
|
||||
|
||||
bool FollowPath::is3D() const {
|
||||
return _path->getSubType() == Resources::Path::kPath3D;
|
||||
}
|
||||
|
||||
void FollowPath::setAnim(Resources::Anim *anim) {
|
||||
_anim = anim;
|
||||
}
|
||||
|
||||
uint32 FollowPath::getType() const {
|
||||
return kTypeFollowPath;
|
||||
}
|
||||
|
||||
void FollowPath::saveLoad(ResourceSerializer *serializer) {
|
||||
serializer->syncAsResourceReference(&_path);
|
||||
serializer->syncAsResourceReference(&_anim);
|
||||
serializer->syncAsFloat(_position);
|
||||
serializer->syncAsFloat(_speed);
|
||||
serializer->syncAsUint32LE(_previouslyEnabled);
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
76
engines/stark/movement/followpath.h
Normal file
76
engines/stark/movement/followpath.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* 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 STARK_MOVEMENT_FOLLOW_PATH_H
|
||||
#define STARK_MOVEMENT_FOLLOW_PATH_H
|
||||
|
||||
#include "engines/stark/movement/movement.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Resources {
|
||||
class Anim;
|
||||
class Path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an item follow pre-computed path
|
||||
*
|
||||
* Works for 2D and 3D items, with respectively 2D and 3D paths
|
||||
*/
|
||||
class FollowPath : public Movement {
|
||||
public:
|
||||
FollowPath(Resources::ItemVisual *item);
|
||||
virtual ~FollowPath();
|
||||
|
||||
// Movement API
|
||||
void start() override;
|
||||
void onGameLoop() override;
|
||||
void stop(bool force = false) override;
|
||||
uint32 getType() const override;
|
||||
void saveLoad(ResourceSerializer *serializer) override;
|
||||
|
||||
/** Set the path to follow */
|
||||
void setPath(Resources::Path *path);
|
||||
|
||||
/** Set the movement speed on the path */
|
||||
void setSpeed(float speed);
|
||||
|
||||
/** Override the animation to play while the item follows the path */
|
||||
void setAnim(Resources::Anim *anim);
|
||||
|
||||
private:
|
||||
void changeItemAnim();
|
||||
void updateItemPosition(uint currentEdge, float positionInEdge) const;
|
||||
bool is3D() const;
|
||||
|
||||
Resources::Path *_path;
|
||||
float _speed;
|
||||
|
||||
float _position;
|
||||
bool _previouslyEnabled;
|
||||
|
||||
Resources::Anim *_anim;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_FOLLOW_PATH_H
|
||||
116
engines/stark/movement/followpathlight.cpp
Normal file
116
engines/stark/movement/followpathlight.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/* 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/stark/movement/followpathlight.h"
|
||||
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/stateprovider.h"
|
||||
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/floor.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/light.h"
|
||||
#include "engines/stark/resources/path.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
FollowPathLight::FollowPathLight(Resources::ItemVisual *item) :
|
||||
Movement(item),
|
||||
_light(nullptr),
|
||||
_path(nullptr),
|
||||
_speed(0.0),
|
||||
_position(0.0),
|
||||
_previouslyEnabled(true) {
|
||||
}
|
||||
|
||||
FollowPathLight::~FollowPathLight() {
|
||||
}
|
||||
|
||||
void FollowPathLight::start() {
|
||||
Movement::start();
|
||||
|
||||
_previouslyEnabled = _item->isEnabled();
|
||||
_item->setEnabled(true);
|
||||
|
||||
Math::Vector3d newPosition = _path->getWeightedPositionInEdge(0, 0);
|
||||
_light->setPosition(newPosition);
|
||||
}
|
||||
|
||||
void FollowPathLight::stop(bool force) {
|
||||
Movement::stop(force);
|
||||
|
||||
_item->setEnabled(_previouslyEnabled);
|
||||
}
|
||||
|
||||
void FollowPathLight::onGameLoop() {
|
||||
// Compute the new position on the path
|
||||
_position += _speed * StarkGlobal->getMillisecondsPerGameloop();
|
||||
|
||||
// Find the current path edge, and position on the path edge
|
||||
uint currentEdge = 0;
|
||||
float positionInEdge = _position;
|
||||
for (uint i = 0; i < _path->getEdgeCount(); i++) {
|
||||
float edgeLength = _path->getWeightedEdgeLength(i);
|
||||
if (positionInEdge < edgeLength) {
|
||||
break; // Found the current path edge
|
||||
}
|
||||
|
||||
positionInEdge -= edgeLength;
|
||||
currentEdge++;
|
||||
}
|
||||
|
||||
// Check if we went beyond the path's end
|
||||
if (currentEdge >= _path->getEdgeCount()) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the new position for the light
|
||||
Math::Vector3d newPosition = _path->getWeightedPositionInEdge(currentEdge, positionInEdge);
|
||||
_light->setPosition(newPosition);
|
||||
}
|
||||
|
||||
void FollowPathLight::setPath(Resources::Path *path) {
|
||||
_path = path;
|
||||
}
|
||||
|
||||
void FollowPathLight::setSpeed(float speed) {
|
||||
_speed = speed;
|
||||
}
|
||||
|
||||
void FollowPathLight::setLight(Resources::Light *light) {
|
||||
_light = light;
|
||||
}
|
||||
|
||||
uint32 FollowPathLight::getType() const {
|
||||
return kTypeFollowPathLight;
|
||||
}
|
||||
|
||||
void FollowPathLight::saveLoad(ResourceSerializer *serializer) {
|
||||
serializer->syncAsResourceReference(&_path);
|
||||
serializer->syncAsResourceReference(&_light);
|
||||
serializer->syncAsFloat(_position);
|
||||
serializer->syncAsFloat(_speed);
|
||||
serializer->syncAsUint32LE(_previouslyEnabled);
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
69
engines/stark/movement/followpathlight.h
Normal file
69
engines/stark/movement/followpathlight.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* 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 STARK_MOVEMENT_FOLLOW_PATH_LIGHT_H
|
||||
#define STARK_MOVEMENT_FOLLOW_PATH_LIGHT_H
|
||||
|
||||
#include "engines/stark/movement/movement.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Resources {
|
||||
class Light;
|
||||
class Path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a light follow pre-computed path
|
||||
*/
|
||||
class FollowPathLight : public Movement {
|
||||
public:
|
||||
FollowPathLight(Resources::ItemVisual *item);
|
||||
virtual ~FollowPathLight();
|
||||
|
||||
// Movement API
|
||||
void start() override;
|
||||
void onGameLoop() override;
|
||||
void stop(bool force = false) override;
|
||||
uint32 getType() const override;
|
||||
void saveLoad(ResourceSerializer *serializer) override;
|
||||
|
||||
/** Set the path to follow */
|
||||
void setPath(Resources::Path *path);
|
||||
|
||||
/** Set the light to move */
|
||||
void setLight(Resources::Light *light);
|
||||
|
||||
/** Set the movement speed on the path */
|
||||
void setSpeed(float speed);
|
||||
|
||||
private:
|
||||
Resources::Path *_path;
|
||||
Resources::Light *_light;
|
||||
float _speed;
|
||||
|
||||
float _position;
|
||||
bool _previouslyEnabled;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_FOLLOW_PATH_LIGHT_H
|
||||
90
engines/stark/movement/movement.cpp
Normal file
90
engines/stark/movement/movement.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/* 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/stark/movement/movement.h"
|
||||
|
||||
#include "engines/stark/movement/walk.h"
|
||||
#include "engines/stark/movement/followpath.h"
|
||||
#include "engines/stark/movement/followpathlight.h"
|
||||
#include "engines/stark/movement/turn.h"
|
||||
|
||||
#include "engines/stark/resources/item.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
Movement *Movement::construct(uint32 type, Resources::ItemVisual *item) {
|
||||
switch (type) {
|
||||
case kTypeWalk:
|
||||
return new Walk(Resources::Object::cast<Resources::FloorPositionedItem>(item));
|
||||
case kTypeFollowPath:
|
||||
return new FollowPath(item);
|
||||
case kTypeFollowPathLight:
|
||||
return new FollowPathLight(item);
|
||||
case kTypeTurn:
|
||||
return new Turn(Resources::Object::cast<Resources::FloorPositionedItem>(item));
|
||||
default:
|
||||
error("Unexpected movement type '%d'", type);
|
||||
}
|
||||
}
|
||||
|
||||
Movement::Movement(Resources::ItemVisual *item) :
|
||||
_ended(false),
|
||||
_item(item),
|
||||
_defaultTurnAngleSpeed(18.0f * 30.0f / 1000.0f) { // 18 degrees per gameloop at 30 fps
|
||||
}
|
||||
|
||||
Movement::~Movement() {
|
||||
}
|
||||
|
||||
void Movement::start() {
|
||||
_ended = false;
|
||||
}
|
||||
|
||||
void Movement::stop(bool force) {
|
||||
_ended = true;
|
||||
}
|
||||
|
||||
bool Movement::hasEnded() const {
|
||||
return _ended;
|
||||
}
|
||||
|
||||
float Movement::computeAngleBetweenVectorsXYPlane(const Math::Vector3d &v1, const Math::Vector3d &v2) const {
|
||||
Math::Vector3d v1XY = v1;
|
||||
v1XY.z() = 0.0;
|
||||
|
||||
Math::Vector3d v2XY = v2;
|
||||
v2XY.z() = 0.0;
|
||||
|
||||
Math::Angle angle = Math::Vector3d::angle(v1XY, v2XY);
|
||||
Math::Vector3d cross = Math::Vector3d::crossProduct(v1XY, v2XY);
|
||||
if (cross.z() < 0) {
|
||||
angle = -angle;
|
||||
}
|
||||
|
||||
return angle.getDegrees();
|
||||
}
|
||||
|
||||
bool Movement::hasReachedDestination() const {
|
||||
return true;
|
||||
}
|
||||
} // End of namespace Stark
|
||||
103
engines/stark/movement/movement.h
Normal file
103
engines/stark/movement/movement.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 STARK_MOVEMENT_MOVEMENT_H
|
||||
#define STARK_MOVEMENT_MOVEMENT_H
|
||||
|
||||
#include "math/vector3d.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Resources {
|
||||
class ItemVisual;
|
||||
}
|
||||
|
||||
class ResourceSerializer;
|
||||
|
||||
/**
|
||||
* Abstract movement of an item on the current location's floor
|
||||
*/
|
||||
class Movement {
|
||||
public:
|
||||
Movement(Resources::ItemVisual *item);
|
||||
virtual ~Movement();
|
||||
|
||||
enum MovementType {
|
||||
kTypeWalk = 1,
|
||||
kTypeFollowPath = 2,
|
||||
kTypeFollowPathLight = 3,
|
||||
kTypeTurn = 4
|
||||
};
|
||||
|
||||
/** Movement factory */
|
||||
static Movement *construct(uint32 type, Resources::ItemVisual *item);
|
||||
|
||||
/** Obtain the effective movement type */
|
||||
virtual uint32 getType() const = 0;
|
||||
|
||||
/**
|
||||
* Initiate the movement
|
||||
*/
|
||||
virtual void start();
|
||||
|
||||
/**
|
||||
* Stop / abort the movement
|
||||
*/
|
||||
virtual void stop(bool force = false);
|
||||
|
||||
/**
|
||||
* Called once per game loop
|
||||
*/
|
||||
virtual void onGameLoop() = 0;
|
||||
|
||||
/**
|
||||
* Has the movement stopped?
|
||||
*/
|
||||
bool hasEnded() const;
|
||||
|
||||
/**
|
||||
* Has the movement reached its destination successfully?
|
||||
*/
|
||||
virtual bool hasReachedDestination() const;
|
||||
|
||||
/**
|
||||
* Persist / restore the state of the movement so it can be resumed using 'start'
|
||||
*/
|
||||
virtual void saveLoad(ResourceSerializer *serializer) = 0;
|
||||
|
||||
protected:
|
||||
enum TurnDirection {
|
||||
kTurnNone,
|
||||
kTurnLeft,
|
||||
kTurnRight
|
||||
};
|
||||
|
||||
const float _defaultTurnAngleSpeed; // Degrees per ms
|
||||
|
||||
float computeAngleBetweenVectorsXYPlane(const Math::Vector3d &v1, const Math::Vector3d &v2) const;
|
||||
|
||||
bool _ended;
|
||||
Resources::ItemVisual *_item;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_MOVEMENT_H
|
||||
100
engines/stark/movement/shortestpath.cpp
Normal file
100
engines/stark/movement/shortestpath.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/* 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/stark/movement/shortestpath.h"
|
||||
|
||||
#include "common/hash-ptr.h"
|
||||
|
||||
#include "engines/stark/resources/floor.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
ShortestPath::NodeList ShortestPath::search(const Resources::FloorEdge *start, const Resources::FloorEdge *goal) {
|
||||
NodeList frontier;
|
||||
NodePrecedenceMap cameFrom;
|
||||
NodeCostMap costSoFar;
|
||||
|
||||
frontier.push_back(start);
|
||||
cameFrom[start] = nullptr;
|
||||
costSoFar[start] = 0;
|
||||
|
||||
while (!frontier.empty()) {
|
||||
const Resources::FloorEdge *current = popEdgeWithLowestCost(frontier, costSoFar);
|
||||
|
||||
if (current == goal)
|
||||
break;
|
||||
|
||||
Common::Array<Resources::FloorEdge *> neighbours = current->getNeighbours();
|
||||
for (uint i = 0; i < neighbours.size(); i++) {
|
||||
const Resources::FloorEdge *next = neighbours[i];
|
||||
if (!next->isEnabled())
|
||||
continue;
|
||||
|
||||
float newCost = costSoFar[current] + current->costTo(next);
|
||||
if (!costSoFar.contains(next) || newCost < costSoFar[next]) {
|
||||
frontier.push_back(next);
|
||||
cameFrom[next] = current;
|
||||
costSoFar[next] = newCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rebuildPath(start, goal, cameFrom);
|
||||
}
|
||||
|
||||
ShortestPath::NodeList ShortestPath::rebuildPath(const Resources::FloorEdge *start, const Resources::FloorEdge *goal,
|
||||
const NodePrecedenceMap &cameFrom) const {
|
||||
NodeList path;
|
||||
|
||||
const Resources::FloorEdge *current = goal;
|
||||
path.push_front(goal);
|
||||
|
||||
while (current && current != start) {
|
||||
current = cameFrom.getValOrDefault(current, nullptr);
|
||||
path.push_front(current);
|
||||
}
|
||||
|
||||
if (current != start) {
|
||||
// No path has been found from start to goal
|
||||
return NodeList();
|
||||
}
|
||||
|
||||
path.push_front(start);
|
||||
return path;
|
||||
}
|
||||
|
||||
const Resources::FloorEdge *ShortestPath::popEdgeWithLowestCost(NodeList &frontier, const NodeCostMap &costSoFar) const {
|
||||
// Poor man's priority queue using a list ...
|
||||
NodeList::iterator lowestCostItem = frontier.begin();
|
||||
for (NodeList::iterator it = frontier.begin(); it != frontier.end(); it++) {
|
||||
if (costSoFar[*it] < costSoFar[*lowestCostItem]) {
|
||||
lowestCostItem = it;
|
||||
}
|
||||
}
|
||||
|
||||
const Resources::FloorEdge *result = *lowestCostItem;
|
||||
|
||||
frontier.erase(lowestCostItem);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
58
engines/stark/movement/shortestpath.h
Normal file
58
engines/stark/movement/shortestpath.h
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_MOVEMENT_SHORTEST_PATH_H
|
||||
#define STARK_MOVEMENT_SHORTEST_PATH_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/hashmap.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Resources {
|
||||
class FloorEdge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the shortest path between two nodes in a graph
|
||||
*
|
||||
* This is an implementation of Dijsktra's search algorithm
|
||||
*/
|
||||
class ShortestPath {
|
||||
public:
|
||||
typedef Common::List<const Resources::FloorEdge *> NodeList;
|
||||
|
||||
/** Computes the shortest path between the start and the goal graph nodes */
|
||||
NodeList search(const Resources::FloorEdge *start, const Resources::FloorEdge *goal);
|
||||
|
||||
private:
|
||||
typedef Common::HashMap<const Resources::FloorEdge *, const Resources::FloorEdge *> NodePrecedenceMap;
|
||||
typedef Common::HashMap<const Resources::FloorEdge *, float> NodeCostMap;
|
||||
|
||||
const Resources::FloorEdge *popEdgeWithLowestCost(NodeList &frontier, const NodeCostMap &costSoFar) const;
|
||||
|
||||
NodeList rebuildPath(const Resources::FloorEdge *start, const Resources::FloorEdge *goal,
|
||||
const NodePrecedenceMap &cameFrom) const;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_SHORTEST_PATH_H
|
||||
73
engines/stark/movement/stringpullingpath.cpp
Normal file
73
engines/stark/movement/stringpullingpath.cpp
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/stark/movement/stringpullingpath.h"
|
||||
|
||||
#include "engines/stark/resources/floor.h"
|
||||
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
|
||||
#include "math/line3d.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
StringPullingPath::StringPullingPath() :
|
||||
_targetStep(1) {
|
||||
}
|
||||
|
||||
void StringPullingPath::addStep(const Math::Vector3d &position) {
|
||||
_steps.push_back(position);
|
||||
}
|
||||
|
||||
void StringPullingPath::reset() {
|
||||
_steps.clear();
|
||||
_targetStep = 1;
|
||||
}
|
||||
|
||||
Math::Vector3d StringPullingPath::computeWalkTarget(const Math::Vector3d &fromPosition) {
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
Resources::Floor *floor = current->getFloor();
|
||||
|
||||
// HACK: Sometimes the character gets stuck because of rounding errors
|
||||
// If we detect the character is stuck on a step, just make it go to the next one.
|
||||
// TODO: Improve the string pulling code so that the targets can also be points between two steps.
|
||||
if (fromPosition.getDistanceTo(_steps[_targetStep]) < 1.0 && _targetStep < _steps.size() - 1) {
|
||||
_targetStep++;
|
||||
}
|
||||
|
||||
for (uint i = _targetStep + 1; i < _steps.size(); i++) {
|
||||
Math::Line3d testSegment = Math::Line3d(fromPosition, _steps[i]);
|
||||
if (!floor->isSegmentInside(testSegment)) {
|
||||
break;
|
||||
}
|
||||
|
||||
_targetStep = i;
|
||||
}
|
||||
|
||||
return _steps[_targetStep];
|
||||
}
|
||||
|
||||
bool StringPullingPath::hasSteps() const {
|
||||
return _steps.size() > 1;
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
60
engines/stark/movement/stringpullingpath.h
Normal file
60
engines/stark/movement/stringpullingpath.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 STARK_MOVEMENT_STRING_PULLING_PATH_H
|
||||
#define STARK_MOVEMENT_STRING_PULLING_PATH_H
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
#include "math/vector3d.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
/**
|
||||
* Store a path and allow to walk along it smoothly
|
||||
*
|
||||
* The base principle of the string pulling algorithm is to skip steps
|
||||
* if it is possible to walk directly to a later step in straight line.
|
||||
*/
|
||||
class StringPullingPath {
|
||||
public:
|
||||
StringPullingPath();
|
||||
|
||||
/** Append a step to the path */
|
||||
void addStep(const Math::Vector3d &position);
|
||||
|
||||
/** Reset the steps, and the current target on the path */
|
||||
void reset();
|
||||
|
||||
/** Move the walk target forward according to the position */
|
||||
Math::Vector3d computeWalkTarget(const Math::Vector3d &fromPosition);
|
||||
|
||||
/** Returns true if this path is not degenerated (empty or single point) */
|
||||
bool hasSteps() const;
|
||||
|
||||
private:
|
||||
Common::Array<Math::Vector3d> _steps;
|
||||
uint32 _targetStep;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_STRING_PULLING_PATH_H
|
||||
104
engines/stark/movement/turn.cpp
Normal file
104
engines/stark/movement/turn.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/* 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/stark/movement/turn.h"
|
||||
|
||||
#include "math/matrix3.h"
|
||||
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/stateprovider.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
Turn::Turn(Resources::FloorPositionedItem *item) :
|
||||
Movement(item),
|
||||
_item3D(item),
|
||||
_turnSpeed(_defaultTurnAngleSpeed) {
|
||||
}
|
||||
|
||||
Turn::~Turn() {
|
||||
}
|
||||
|
||||
void Turn::onGameLoop() {
|
||||
// Compute the direction to turn towards
|
||||
Math::Vector3d direction = _targetDirection;
|
||||
direction.z() = 0;
|
||||
direction.normalize();
|
||||
|
||||
// Compute the angle with the current character direction
|
||||
Math::Vector3d currentDirection = _item3D->getDirectionVector();
|
||||
float directionDeltaAngle = computeAngleBetweenVectorsXYPlane(currentDirection, direction);
|
||||
|
||||
// If the angle between the current direction and the new one is too high,
|
||||
// make the character turn on itself until the angle is low enough
|
||||
TurnDirection turnDirection;
|
||||
if (ABS(directionDeltaAngle) > getAngularSpeed() + 0.1f) {
|
||||
turnDirection = directionDeltaAngle < 0 ? kTurnLeft : kTurnRight;
|
||||
} else {
|
||||
turnDirection = kTurnNone;
|
||||
}
|
||||
|
||||
if (turnDirection == kTurnNone) {
|
||||
direction = _targetDirection;
|
||||
} else {
|
||||
// Make the character turn towards the target direction
|
||||
direction = currentDirection;
|
||||
|
||||
Math::Matrix3 rot;
|
||||
rot.buildAroundZ(turnDirection == kTurnLeft ? -getAngularSpeed() : getAngularSpeed());
|
||||
rot.transformVector(&direction);
|
||||
}
|
||||
|
||||
// Update the item's direction
|
||||
_item3D->setDirection(computeAngleBetweenVectorsXYPlane(direction, Math::Vector3d(1.0, 0.0, 0.0)));
|
||||
|
||||
// Check if we are close enough to the destination to stop
|
||||
if (direction == _targetDirection) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
float Turn::getAngularSpeed() const {
|
||||
return _turnSpeed * StarkGlobal->getMillisecondsPerGameloop();
|
||||
}
|
||||
|
||||
void Turn::setTargetDirection(const Math::Vector3d &direction) {
|
||||
_targetDirection = direction;
|
||||
}
|
||||
|
||||
void Turn::setSpeed(float speed) {
|
||||
_turnSpeed = speed;
|
||||
}
|
||||
|
||||
uint32 Turn::getType() const {
|
||||
return kTypeTurn;
|
||||
}
|
||||
|
||||
void Turn::saveLoad(ResourceSerializer *serializer) {
|
||||
serializer->syncAsVector3d(_targetDirection);
|
||||
serializer->syncAsFloat(_turnSpeed);
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
62
engines/stark/movement/turn.h
Normal file
62
engines/stark/movement/turn.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* 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 STARK_MOVEMENT_TURN_H
|
||||
#define STARK_MOVEMENT_TURN_H
|
||||
|
||||
#include "engines/stark/movement/movement.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Resources {
|
||||
class FloorPositionedItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an item turn on itself towards a target direction
|
||||
*/
|
||||
class Turn : public Movement {
|
||||
public:
|
||||
Turn(Resources::FloorPositionedItem *item);
|
||||
virtual ~Turn();
|
||||
|
||||
// Movement API
|
||||
void onGameLoop() override;
|
||||
uint32 getType() const override;
|
||||
void saveLoad(ResourceSerializer *serializer) override;
|
||||
|
||||
/** Set the direction to turn towards */
|
||||
void setTargetDirection(const Math::Vector3d &direction);
|
||||
|
||||
/** Override the default rotation speed */
|
||||
void setSpeed(float speed);
|
||||
|
||||
private:
|
||||
float getAngularSpeed() const;
|
||||
|
||||
Resources::FloorPositionedItem *_item3D;
|
||||
Math::Vector3d _targetDirection;
|
||||
float _turnSpeed;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_TURN_H
|
||||
525
engines/stark/movement/walk.cpp
Normal file
525
engines/stark/movement/walk.cpp
Normal file
@@ -0,0 +1,525 @@
|
||||
/* 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/stark/movement/walk.h"
|
||||
|
||||
#include "engines/stark/movement/shortestpath.h"
|
||||
#include "engines/stark/movement/stringpullingpath.h"
|
||||
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/stateprovider.h"
|
||||
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/floor.h"
|
||||
#include "engines/stark/resources/floorface.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/location.h"
|
||||
|
||||
#include "math/vector2d.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
Walk::Walk(Resources::FloorPositionedItem *item) :
|
||||
Movement(item),
|
||||
_item3D(item),
|
||||
_running(false),
|
||||
_reachedDestination(false),
|
||||
_turnDirection(kTurnNone),
|
||||
_collisionWaitTimeout(-1),
|
||||
_collisionWaitCount(0) {
|
||||
_path = new StringPullingPath();
|
||||
}
|
||||
|
||||
Walk::~Walk() {
|
||||
delete _path;
|
||||
}
|
||||
|
||||
void Walk::start() {
|
||||
Movement::start();
|
||||
|
||||
updatePath();
|
||||
changeItemAnim();
|
||||
|
||||
Resources::Location *location = StarkGlobal->getCurrent()->getLocation();
|
||||
location->startFollowingCharacter();
|
||||
}
|
||||
|
||||
void Walk::stop(bool force) {
|
||||
if (force) {
|
||||
_destinations.clear();
|
||||
}
|
||||
|
||||
if (_destinations.empty()) {
|
||||
Movement::stop(force);
|
||||
changeItemAnim();
|
||||
_avoidedItems.clear();
|
||||
} else {
|
||||
Math::Vector3d destination = _destinations.front();
|
||||
_destinations.remove_at(0);
|
||||
setDestination(destination);
|
||||
updatePath();
|
||||
}
|
||||
}
|
||||
|
||||
void Walk::updatePath() const {
|
||||
_path->reset();
|
||||
|
||||
Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor();
|
||||
|
||||
Math::Vector3d startPosition = _item3D->getPosition3D();
|
||||
int32 startFloorFaceIndex = floor->findFaceContainingPoint(startPosition);
|
||||
if (startFloorFaceIndex == -1) {
|
||||
startFloorFaceIndex = 0;
|
||||
}
|
||||
|
||||
Resources::FloorFace *startFloorFace = floor->getFace(startFloorFaceIndex);
|
||||
Resources::FloorEdge *startFloorEdge = startFloorFace->findNearestEdge(startPosition);
|
||||
if (!startFloorEdge) {
|
||||
// Unable to find enabled start edge
|
||||
return;
|
||||
}
|
||||
|
||||
int32 destinationFloorFaceIndex = floor->findFaceContainingPoint(_destination);
|
||||
if (destinationFloorFaceIndex < 0) {
|
||||
// Unable to find the destination's face
|
||||
return;
|
||||
}
|
||||
|
||||
Resources::FloorFace *destinationFloorFace = floor->getFace(destinationFloorFaceIndex);
|
||||
Resources::FloorEdge *destinationFloorEdge = destinationFloorFace->findNearestEdge(_destination);
|
||||
if (!destinationFloorEdge) {
|
||||
// Unable to find enabled destination edge
|
||||
return;
|
||||
}
|
||||
|
||||
ShortestPath pathSearch;
|
||||
ShortestPath::NodeList edgePath = pathSearch.search(startFloorEdge, destinationFloorEdge);
|
||||
|
||||
for (ShortestPath::NodeList::const_iterator it = edgePath.begin(); it != edgePath.end(); it++) {
|
||||
_path->addStep((*it)->getPosition());
|
||||
}
|
||||
|
||||
_path->addStep(_destination);
|
||||
}
|
||||
|
||||
void Walk::queueDestinationToAvoidItem(Resources::FloorPositionedItem *item, const Math::Vector3d &destination) {
|
||||
_destinations.push_back(destination);
|
||||
_avoidedItems.push_back(item);
|
||||
}
|
||||
|
||||
bool Walk::isItemAlreadyAvoided(Resources::FloorPositionedItem *item) const {
|
||||
return Common::find(_avoidedItems.begin(), _avoidedItems.end(), item) != _avoidedItems.end();
|
||||
}
|
||||
|
||||
void Walk::onGameLoop() {
|
||||
Resources::ItemVisual *interactiveItem = StarkGlobal->getCurrent()->getInteractive();
|
||||
|
||||
if (_item != interactiveItem) {
|
||||
// NPCs have a simple collision handling strategy.
|
||||
// They stop when they collide with other items,
|
||||
// and wait for their path to be clear.
|
||||
doWalkCollisionSimple();
|
||||
} else {
|
||||
// April has a more advanced collision handling approach.
|
||||
// She goes either left or right of the items on her path.
|
||||
// When impossible to pick a direction, she walks until the
|
||||
// obstacle is reached.
|
||||
doWalkCollisionAvoid();
|
||||
}
|
||||
}
|
||||
|
||||
void Walk::doWalk() {
|
||||
if (!_path->hasSteps()) {
|
||||
// There is no path to the destination
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor();
|
||||
|
||||
// Get the target to walk to
|
||||
Math::Vector3d currentPosition = _item3D->getPosition3D();
|
||||
Math::Vector3d target = _path->computeWalkTarget(currentPosition);
|
||||
|
||||
// Compute the direction to walk into
|
||||
Math::Vector3d direction = target - currentPosition;
|
||||
direction.z() = 0;
|
||||
direction.normalize();
|
||||
|
||||
// Compute the angle with the current character direction
|
||||
Math::Vector3d currentDirection = _item3D->getDirectionVector();
|
||||
float directionDeltaAngle = computeAngleBetweenVectorsXYPlane(currentDirection, direction);
|
||||
|
||||
// If the angle between the current direction and the new one is too high,
|
||||
// make the character turn on itself until the angle is low enough
|
||||
if (ABS(directionDeltaAngle) > getAngularSpeed() + 0.1f) {
|
||||
_turnDirection = directionDeltaAngle < 0 ? kTurnLeft : kTurnRight;
|
||||
} else {
|
||||
_turnDirection = kTurnNone;
|
||||
}
|
||||
|
||||
float distancePerGameloop = computeDistancePerGameLoop();
|
||||
|
||||
Math::Vector3d newPosition;
|
||||
if (_turnDirection == kTurnNone) {
|
||||
// Compute the new position using the distance per gameloop
|
||||
if (currentPosition.getDistanceTo(target) > distancePerGameloop) {
|
||||
newPosition = currentPosition + direction * distancePerGameloop;
|
||||
} else {
|
||||
newPosition = target;
|
||||
}
|
||||
} else {
|
||||
// The character does not change position when it is turning
|
||||
newPosition = currentPosition;
|
||||
direction = currentDirection;
|
||||
|
||||
Math::Matrix3 rot;
|
||||
rot.buildAroundZ(_turnDirection == kTurnLeft ? -getAngularSpeed() : getAngularSpeed());
|
||||
rot.transformVector(&direction);
|
||||
}
|
||||
|
||||
_previousPosition = currentPosition;
|
||||
_currentTarget = target;
|
||||
|
||||
// Some scripts expect the character position to be the exact destination
|
||||
if (newPosition == _destination) {
|
||||
_reachedDestination = true;
|
||||
stop();
|
||||
}
|
||||
|
||||
// Update the new position's height according to the floor
|
||||
int32 newFloorFaceIndex = floor->findFaceContainingPoint(newPosition);
|
||||
if (newFloorFaceIndex >= 0) {
|
||||
floor->computePointHeightInFace(newPosition, newFloorFaceIndex);
|
||||
} else {
|
||||
warning("Item %s is walking off the floor", _item->getName().c_str());
|
||||
}
|
||||
|
||||
// Update the item's properties
|
||||
_item3D->setPosition3D(newPosition);
|
||||
if (direction.getMagnitude() != 0.f) {
|
||||
_item3D->setDirection(computeAngleBetweenVectorsXYPlane(direction, Math::Vector3d(1.0, 0.0, 0.0)));
|
||||
}
|
||||
if (newFloorFaceIndex >= 0) {
|
||||
// When unable to find the face containing the new position, keep the previous one
|
||||
// to prevent draw order glitches.
|
||||
_item3D->setFloorFaceIndex(newFloorFaceIndex);
|
||||
}
|
||||
|
||||
changeItemAnim();
|
||||
}
|
||||
|
||||
bool Walk::isPointNearPath(const Math::Vector3d &point3d, const Math::Vector3d &pathStart3d, const Math::Vector3d &pathEnd3d) {
|
||||
Math::Vector2d point = Math::Vector2d(point3d.x(), point3d.y());
|
||||
Math::Vector2d pathStart = Math::Vector2d(pathStart3d.x(), pathStart3d.y());
|
||||
Math::Vector2d pathEnd = Math::Vector2d(pathEnd3d.x(), pathEnd3d.y());
|
||||
|
||||
// Project the point onto the path
|
||||
Math::Vector2d pointToStart = point - pathStart;
|
||||
Math::Vector2d path = pathEnd - pathStart;
|
||||
float dot = pointToStart.dotProduct(path);
|
||||
float len = path.getSquareMagnitude();
|
||||
|
||||
float t = dot / len;
|
||||
|
||||
Math::Vector2d projection;
|
||||
if (0.f <= t && t < 1.f) {
|
||||
projection = path * t + pathStart;
|
||||
} else {
|
||||
projection = pathEnd;
|
||||
}
|
||||
|
||||
// Check if the projection is near the actual point
|
||||
return point.getDistanceTo(projection) <= (15.f + 15.f);
|
||||
}
|
||||
|
||||
void Walk::doWalkCollisionSimple() {
|
||||
if (_collisionWaitTimeout > 0) {
|
||||
_collisionWaitTimeout -= StarkGlobal->getMillisecondsPerGameloop();
|
||||
return;
|
||||
} else {
|
||||
_collisionWaitTimeout = -1;
|
||||
}
|
||||
|
||||
Resources::Location *location = StarkGlobal->getCurrent()->getLocation();
|
||||
Common::Array<Resources::ModelItem *> characters = location->listModelItems();
|
||||
|
||||
// Check if any of the other characters is in our way
|
||||
for (uint i = 0; i < characters.size(); i++) {
|
||||
Resources::ModelItem *otherItem = characters[i];
|
||||
if (!otherItem || !otherItem->isEnabled() || otherItem == _item) continue;
|
||||
|
||||
Math::Vector3d otherPosition = otherItem->getPosition3D();
|
||||
|
||||
if (isPointNearPath(otherPosition, _previousPosition, _currentTarget)) {
|
||||
if (_previousPosition.getDistanceTo(otherPosition) <= 15.f * 3.f) {
|
||||
if (_collisionWaitCount >= 10) {
|
||||
doWalk();
|
||||
return;
|
||||
}
|
||||
|
||||
// A collision is detected. Remove the walk animation, and wait a bit.
|
||||
if (_item->getAnimActivity() != Resources::Anim::kActorActivityIdle) {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityIdle);
|
||||
}
|
||||
|
||||
_collisionWaitCount++;
|
||||
_collisionWaitTimeout = 500; // ms
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The path is clear, walk normally
|
||||
_collisionWaitCount = 0;
|
||||
doWalk();
|
||||
}
|
||||
|
||||
void Walk::doWalkCollisionAvoid() {
|
||||
float collisionRadius = 15.f * 2.0999999f;
|
||||
|
||||
Math::Vector3d previousPosition = _item3D->getPosition3D();
|
||||
doWalk();
|
||||
Math::Vector3d newPosition = _item3D->getPosition3D();
|
||||
|
||||
Resources::Location *location = StarkGlobal->getCurrent()->getLocation();
|
||||
Common::Array<Resources::ModelItem *> characters = location->listModelItems();
|
||||
|
||||
// Check if we're colliding with another character, but going away from it.
|
||||
// In that case, the collision is being solved. There is nothing to do.
|
||||
for (uint i = 0; i < characters.size(); i++) {
|
||||
Resources::FloorPositionedItem *otherItem = characters[i];
|
||||
if (!otherItem || !otherItem->isEnabled() || otherItem == _item) continue;
|
||||
|
||||
Math::Vector3d otherPosition = otherItem->getPosition3D();
|
||||
|
||||
Math::Vector2d newPosition2d(newPosition.x(), newPosition.y());
|
||||
Math::Vector2d otherPosition2d(otherPosition.x(), otherPosition.y());
|
||||
|
||||
float newDistance = newPosition2d.getDistanceTo(otherPosition2d);
|
||||
if (newDistance < 15.f + 15.f) {
|
||||
Math::Vector2d previousPosition2d(previousPosition.x(), previousPosition.y());
|
||||
|
||||
float previousDistance = previousPosition2d.getDistanceTo(otherPosition2d);
|
||||
if (previousDistance < newDistance) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor();
|
||||
|
||||
for (uint i = 0; i < characters.size(); i++) {
|
||||
Resources::FloorPositionedItem *otherItem = dynamic_cast<Resources::FloorPositionedItem *>(characters[i]);
|
||||
if (!otherItem || !otherItem->isEnabled() || otherItem == _item || isItemAlreadyAvoided(otherItem)) continue;
|
||||
|
||||
Math::Vector3d otherPosition = otherItem->getPosition3D();
|
||||
if (!isPointNearPath(otherPosition, _previousPosition, _currentTarget)) continue;
|
||||
|
||||
Math::Vector3d newPosition2d(newPosition.x(), newPosition.y(), 0.f);
|
||||
Math::Vector3d otherPosition2d(otherPosition.x(), otherPosition.y(), 0.f);
|
||||
|
||||
Math::Vector3d directionToOther = otherPosition2d - newPosition2d;
|
||||
float distanceToOther = directionToOther.getMagnitude();
|
||||
directionToOther.normalize();
|
||||
|
||||
Math::Vector3d up(0.f, 0.f, 1.f);
|
||||
Math::Vector3d rightDirection = Math::Vector3d::crossProduct(directionToOther, up);
|
||||
rightDirection.normalize();
|
||||
|
||||
Math::Vector3d otherPositionNear = newPosition2d + directionToOther * (distanceToOther - collisionRadius);
|
||||
Math::Vector3d otherPostionFar = newPosition2d + directionToOther * (distanceToOther + collisionRadius);
|
||||
|
||||
Math::Vector3d rightOfOtherNear = otherPositionNear + rightDirection * collisionRadius;
|
||||
Math::Vector3d leftOfOtherNear = otherPositionNear - rightDirection * collisionRadius;
|
||||
Math::Vector3d rightOfOtherFar = otherPostionFar + rightDirection * collisionRadius;
|
||||
Math::Vector3d leftOfOtherFar = otherPostionFar - rightDirection * collisionRadius;
|
||||
|
||||
bool canGoRight = false;
|
||||
if (floor->isSegmentInside(Math::Line3d(otherPositionNear, rightOfOtherNear))) {
|
||||
if (floor->isSegmentInside(Math::Line3d(rightOfOtherNear, rightOfOtherFar))) {
|
||||
canGoRight = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool canGoLeft = false;
|
||||
if (floor->isSegmentInside(Math::Line3d(otherPositionNear, leftOfOtherNear))) {
|
||||
if (floor->isSegmentInside(Math::Line3d(leftOfOtherNear, leftOfOtherFar))) {
|
||||
canGoLeft = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint j = 0; j < characters.size(); j++) {
|
||||
if (j == i) continue;
|
||||
Resources::FloorPositionedItem *anotherItem = dynamic_cast<Resources::FloorPositionedItem *>(characters[j]);
|
||||
if (!anotherItem || !anotherItem->isEnabled() || anotherItem == _item) continue;
|
||||
|
||||
Math::Vector3d anotherPosition = anotherItem->getPosition3D();
|
||||
|
||||
if (isPointNearPath(anotherPosition, otherPositionNear, rightOfOtherNear)) {
|
||||
canGoRight = false;
|
||||
}
|
||||
|
||||
if (isPointNearPath(anotherPosition, rightOfOtherNear, rightOfOtherFar)) {
|
||||
canGoRight = false;
|
||||
}
|
||||
|
||||
if (isPointNearPath(anotherPosition, otherPositionNear, leftOfOtherNear)) {
|
||||
canGoLeft = false;
|
||||
}
|
||||
|
||||
if (isPointNearPath(anotherPosition, leftOfOtherNear, leftOfOtherFar)) {
|
||||
canGoLeft = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (distanceToOther < collisionRadius) {
|
||||
int32 floorFace = floor->findFaceContainingPoint(previousPosition);
|
||||
if (floorFace >= 0) {
|
||||
floor->computePointHeightInFace(previousPosition, floorFace);
|
||||
|
||||
_item3D->setFloorFaceIndex(floorFace);
|
||||
_item3D->setPosition3D(previousPosition);
|
||||
}
|
||||
_reachedDestination = false;
|
||||
// _skipped = false;
|
||||
stop();
|
||||
break;
|
||||
}
|
||||
|
||||
// If our target destination is in the collision radius of the tested item
|
||||
// Then adjust our destination to be just outside of the item's collision radius.
|
||||
float distanceToDestination = _destination.getDistanceTo(otherPosition);
|
||||
if (distanceToDestination < collisionRadius) {
|
||||
setDestinationWithoutHeight(otherPosition - directionToOther * collisionRadius);
|
||||
// _field_51 = true;
|
||||
updatePath();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (canGoLeft) {
|
||||
Math::Vector3d lookDirection = _item3D->getDirectionVector();
|
||||
Math::Vector3d previousToLeft = leftOfOtherNear - _previousPosition;
|
||||
|
||||
Math::Angle angle = Math::Vector3d::angle(lookDirection, previousToLeft);
|
||||
if (angle > 270) {
|
||||
canGoLeft = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (canGoRight) {
|
||||
Math::Vector3d lookDirection = _item3D->getDirectionVector();
|
||||
Math::Vector3d previousToRight = rightOfOtherNear - _previousPosition;
|
||||
|
||||
Math::Angle angle = Math::Vector3d::angle(lookDirection, previousToRight);
|
||||
if (angle > 270) {
|
||||
canGoRight = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (canGoRight && !canGoLeft) {
|
||||
queueDestinationToAvoidItem(otherItem, _destination);
|
||||
setDestinationWithoutHeight(rightOfOtherNear);
|
||||
updatePath();
|
||||
} else if (!canGoRight && canGoLeft) {
|
||||
queueDestinationToAvoidItem(otherItem, _destination);
|
||||
setDestinationWithoutHeight(leftOfOtherNear);
|
||||
updatePath();
|
||||
} else if (canGoRight && canGoLeft) {
|
||||
Math::Vector3d forwardDirection = _currentTarget - _previousPosition;
|
||||
Math::Vector3d cross = Math::Vector3d::crossProduct(forwardDirection, directionToOther);
|
||||
|
||||
if (cross.z() < 0.f) {
|
||||
queueDestinationToAvoidItem(otherItem, _destination);
|
||||
setDestinationWithoutHeight(leftOfOtherNear);
|
||||
updatePath();
|
||||
} else {
|
||||
queueDestinationToAvoidItem(otherItem, _destination);
|
||||
setDestinationWithoutHeight(rightOfOtherNear);
|
||||
updatePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float Walk::getAngularSpeed() const {
|
||||
return _defaultTurnAngleSpeed * StarkGlobal->getMillisecondsPerGameloop();
|
||||
}
|
||||
|
||||
float Walk::computeDistancePerGameLoop() const {
|
||||
Resources::Anim *anim = _item->getAnim();
|
||||
float distancePerGameloop = anim->getMovementSpeed() * StarkGlobal->getMillisecondsPerGameloop() / 1000.f;
|
||||
|
||||
return distancePerGameloop;
|
||||
}
|
||||
|
||||
void Walk::setDestination(const Math::Vector3d &destination) {
|
||||
_destination = destination;
|
||||
}
|
||||
|
||||
void Walk::setDestinationWithoutHeight(Math::Vector3d destination) {
|
||||
Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor();
|
||||
int32 faceIndex = floor->findFaceContainingPoint(destination);
|
||||
if (faceIndex >= 0) {
|
||||
floor->computePointHeightInFace(destination, faceIndex);
|
||||
}
|
||||
|
||||
setDestination(destination);
|
||||
}
|
||||
|
||||
void Walk::setRunning() {
|
||||
_running = true;
|
||||
changeItemAnim();
|
||||
}
|
||||
|
||||
void Walk::changeItemAnim() {
|
||||
if (_ended) {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityIdle);
|
||||
} else if (_turnDirection != kTurnNone) {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityIdle);
|
||||
} else if (_running) {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityRun);
|
||||
} else {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityWalk);
|
||||
}
|
||||
}
|
||||
|
||||
void Walk::changeDestination(const Math::Vector3d &destination) {
|
||||
_collisionWaitTimeout = -1;
|
||||
setDestination(destination);
|
||||
updatePath();
|
||||
}
|
||||
|
||||
bool Walk::hasReachedDestination() const {
|
||||
return _reachedDestination;
|
||||
}
|
||||
|
||||
uint32 Walk::getType() const {
|
||||
return kTypeWalk;
|
||||
}
|
||||
|
||||
void Walk::saveLoad(ResourceSerializer *serializer) {
|
||||
serializer->syncAsVector3d(_destination);
|
||||
serializer->syncAsUint32LE(_running);
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
100
engines/stark/movement/walk.h
Normal file
100
engines/stark/movement/walk.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* 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 STARK_MOVEMENT_WALK_H
|
||||
#define STARK_MOVEMENT_WALK_H
|
||||
|
||||
#include "engines/stark/movement/movement.h"
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class StringPullingPath;
|
||||
|
||||
namespace Resources {
|
||||
class FloorPositionedItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an item walk / run to its destination on the current
|
||||
* location's floor
|
||||
*/
|
||||
class Walk : public Movement {
|
||||
public:
|
||||
Walk(Resources::FloorPositionedItem *item);
|
||||
virtual ~Walk();
|
||||
|
||||
// Movement API
|
||||
void start() override;
|
||||
void stop(bool force = false) override;
|
||||
void onGameLoop() override;
|
||||
bool hasReachedDestination() const override;
|
||||
uint32 getType() const override;
|
||||
void saveLoad(ResourceSerializer *serializer) override;
|
||||
|
||||
/** Set the destination */
|
||||
void setDestination(const Math::Vector3d &destination);
|
||||
void setDestinationWithoutHeight(Math::Vector3d destination);
|
||||
|
||||
/** Change the destination and recompute the path */
|
||||
void changeDestination(const Math::Vector3d &destination);
|
||||
|
||||
/** Set the running flag */
|
||||
void setRunning();
|
||||
|
||||
private:
|
||||
void doWalk();
|
||||
void doWalkCollisionSimple();
|
||||
void doWalkCollisionAvoid();
|
||||
|
||||
float computeDistancePerGameLoop() const;
|
||||
float getAngularSpeed() const;
|
||||
|
||||
void changeItemAnim();
|
||||
void updatePath() const;
|
||||
|
||||
void queueDestinationToAvoidItem(Resources::FloorPositionedItem *item, const Math::Vector3d &destination);
|
||||
bool isItemAlreadyAvoided(Resources::FloorPositionedItem *item) const;
|
||||
|
||||
static bool isPointNearPath(const Math::Vector3d &point3d, const Math::Vector3d &pathStart3d, const Math::Vector3d &pathEnd3d);
|
||||
|
||||
Resources::FloorPositionedItem *_item3D;
|
||||
StringPullingPath *_path;
|
||||
|
||||
Math::Vector3d _destination;
|
||||
Common::Array<Math::Vector3d> _destinations;
|
||||
|
||||
Common::Array<Resources::ItemVisual *> _avoidedItems;
|
||||
|
||||
bool _running;
|
||||
bool _reachedDestination;
|
||||
TurnDirection _turnDirection;
|
||||
|
||||
int32 _collisionWaitTimeout;
|
||||
int32 _collisionWaitCount;
|
||||
Math::Vector3d _previousPosition;
|
||||
Math::Vector3d _currentTarget;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_WALK_H
|
||||
207
engines/stark/resourcereference.cpp
Normal file
207
engines/stark/resourcereference.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 "engines/stark/resourcereference.h"
|
||||
|
||||
#include "engines/stark/debug.h"
|
||||
#include "engines/stark/resources/level.h"
|
||||
#include "engines/stark/resources/location.h"
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/resourceprovider.h"
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
|
||||
ResourceReference::PathElement::PathElement(Resources::Type type, uint16 index) :
|
||||
_type(type), _index(index) {
|
||||
}
|
||||
|
||||
Common::String ResourceReference::PathElement::describe() const {
|
||||
return Common::String::format("(%s idx %d)", _type.getName(), _index);
|
||||
}
|
||||
|
||||
ResourceReference::ResourceReference() {
|
||||
}
|
||||
|
||||
void ResourceReference::addPathElement(Resources::Type type, uint16 index) {
|
||||
_path.push_back(PathElement(type, index));
|
||||
}
|
||||
|
||||
Resources::Object *ResourceReference::resolve() const {
|
||||
Resources::Object *level = nullptr;
|
||||
Resources::Object *resource = nullptr;
|
||||
for (uint i = 0; i < _path.size(); i++) {
|
||||
const PathElement &element = _path[i];
|
||||
|
||||
switch (element.getType().get()) {
|
||||
case Resources::Type::kLevel:
|
||||
if (StarkStaticProvider->isStaticLocation()) {
|
||||
resource = level = StarkStaticProvider->getLevel();
|
||||
assert(resource->getIndex() == element.getIndex());
|
||||
} else if (element.getIndex()) {
|
||||
resource = level = StarkResourceProvider->getLevel(element.getIndex());
|
||||
} else {
|
||||
resource = level = StarkGlobal->getLevel();
|
||||
}
|
||||
|
||||
if (!level) {
|
||||
error("Level '%d' not found", element.getIndex());
|
||||
}
|
||||
|
||||
break;
|
||||
case Resources::Type::kLocation:
|
||||
if (!level) {
|
||||
error("Cannot resolve location '%d' without resolving a level first", element.getIndex());
|
||||
}
|
||||
|
||||
if (StarkStaticProvider->isStaticLocation()) {
|
||||
resource = StarkStaticProvider->getLocation();
|
||||
assert(resource->getIndex() == element.getIndex());
|
||||
} else {
|
||||
resource = StarkResourceProvider->getLocation(level->getIndex(), element.getIndex());
|
||||
}
|
||||
|
||||
if (!resource) {
|
||||
error("Location '%d' not found in level '%d'", element.getIndex(), level->getIndex());
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
assert(resource);
|
||||
resource = resource->findChildWithIndex(element.getType(), element.getIndex());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
bool ResourceReference::canResolve() const {
|
||||
if (empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Resources::Object *level = nullptr;
|
||||
for (uint i = 0; i < _path.size(); i++) {
|
||||
const PathElement &element = _path[i];
|
||||
|
||||
switch (element.getType().get()) {
|
||||
case Resources::Type::kLevel:
|
||||
if (element.getIndex()) {
|
||||
level = StarkResourceProvider->getLevel(element.getIndex());
|
||||
} else {
|
||||
level = StarkGlobal->getLevel();
|
||||
}
|
||||
|
||||
if (!level) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
case Resources::Type::kLocation: {
|
||||
if (!level) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Resources::Object *location = StarkResourceProvider->getLocation(level->getIndex(), element.getIndex());
|
||||
|
||||
if (!location) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceReference::empty() const {
|
||||
return _path.empty();
|
||||
}
|
||||
|
||||
Common::String ResourceReference::describe() const {
|
||||
Common::String desc;
|
||||
|
||||
for (uint i = 0; i < _path.size(); i++) {
|
||||
desc += _path[i].describe();
|
||||
|
||||
if (i != _path.size() - 1) {
|
||||
desc += " ";
|
||||
}
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
void ResourceReference::buildFromResource(Resources::Object *resource) {
|
||||
Common::Array<PathElement> reversePath;
|
||||
while (resource && resource->getType() != Resources::Type::kRoot) {
|
||||
reversePath.push_back(PathElement(resource->getType(), resource->getIndex()));
|
||||
|
||||
switch (resource->getType().get()) {
|
||||
case Resources::Type::kLocation: {
|
||||
Resources::Location *location = Resources::Object::cast<Resources::Location>(resource);
|
||||
resource = StarkResourceProvider->getLevelFromLocation(location);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
resource = resource->findParent<Resources::Object>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_path.clear();
|
||||
for (int i = reversePath.size() - 1; i >= 0; i--) {
|
||||
_path.push_back(reversePath[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceReference::loadFromStream(Common::ReadStream *stream) {
|
||||
_path.clear();
|
||||
|
||||
uint32 pathSize = stream->readUint32LE();
|
||||
for (uint i = 0; i < pathSize; i++) {
|
||||
byte rawType = stream->readByte();
|
||||
Resources::Type type = Resources::Type((Resources::Type::ResourceType) (rawType));
|
||||
uint16 index = stream->readUint16LE();
|
||||
|
||||
addPathElement(type, index);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceReference::saveToStream(Common::WriteStream *stream) {
|
||||
stream->writeUint32LE(_path.size());
|
||||
for (uint i = 0; i < _path.size(); i++) {
|
||||
byte rawType = _path[i].getType().get();
|
||||
uint16 index = _path[i].getIndex();
|
||||
|
||||
stream->writeByte(rawType);
|
||||
stream->writeUint16LE(index);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
90
engines/stark/resourcereference.h
Normal file
90
engines/stark/resourcereference.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* 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 STARK_RESOURCES_RESOURCE_REFERENCE_H
|
||||
#define STARK_RESOURCES_RESOURCE_REFERENCE_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "engines/stark/resources/object.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
/**
|
||||
* A reference to a resource.
|
||||
*
|
||||
* Internally, the referenced resource is designed by its path
|
||||
* in the resource tree.
|
||||
*
|
||||
*/
|
||||
class ResourceReference {
|
||||
public:
|
||||
ResourceReference();
|
||||
|
||||
Common::String describe() const;
|
||||
|
||||
/** Read the reference from a stream */
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
|
||||
/** Write the reference to a stream */
|
||||
void saveToStream(Common::WriteStream *stream);
|
||||
|
||||
/** Make the reference point to the specified object */
|
||||
void buildFromResource(Resources::Object *resource);
|
||||
|
||||
/** Resolve the reference to the actual resource */
|
||||
template <class T>
|
||||
T* resolve() const;
|
||||
|
||||
/** Return true if this reference is a null pointer */
|
||||
bool empty() const;
|
||||
|
||||
/** Can this reference be resolved using currently loaded archives? */
|
||||
bool canResolve() const;
|
||||
private:
|
||||
void addPathElement(Resources::Type type, uint16 index);
|
||||
Resources::Object *resolve() const;
|
||||
|
||||
class PathElement {
|
||||
public:
|
||||
PathElement(Resources::Type type, uint16 index);
|
||||
Common::String describe() const;
|
||||
|
||||
Resources::Type getType() const { return _type; }
|
||||
uint16 getIndex() const { return _index; }
|
||||
|
||||
private:
|
||||
Resources::Type _type;
|
||||
uint16 _index;
|
||||
};
|
||||
|
||||
Common::Array<PathElement> _path;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
T* ResourceReference::resolve() const {
|
||||
return Resources::Object::cast<T>(resolve());
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_RESOURCES_RESOURCE_REFERENCE_H
|
||||
677
engines/stark/resources/anim.cpp
Normal file
677
engines/stark/resources/anim.cpp
Normal file
@@ -0,0 +1,677 @@
|
||||
/* 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/stark/debug.h"
|
||||
#include "engines/stark/formats/biffmesh.h"
|
||||
#include "engines/stark/formats/tm.h"
|
||||
#include "engines/stark/formats/xrc.h"
|
||||
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/animscript.h"
|
||||
#include "engines/stark/resources/bonesmesh.h"
|
||||
#include "engines/stark/resources/direction.h"
|
||||
#include "engines/stark/resources/image.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/location.h"
|
||||
#include "engines/stark/resources/textureset.h"
|
||||
|
||||
#include "engines/stark/services/archiveloader.h"
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/settings.h"
|
||||
#include "engines/stark/services/stateprovider.h"
|
||||
|
||||
#include "engines/stark/model/animhandler.h"
|
||||
#include "engines/stark/model/skeleton_anim.h"
|
||||
#include "engines/stark/visual/actor.h"
|
||||
#include "engines/stark/visual/prop.h"
|
||||
#include "engines/stark/visual/smacker.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Resources {
|
||||
|
||||
Object *Anim::construct(Object *parent, byte subType, uint16 index, const Common::String &name) {
|
||||
switch (subType) {
|
||||
case kAnimImages:
|
||||
return new AnimImages(parent, subType, index, name);
|
||||
case kAnimProp:
|
||||
return new AnimProp(parent, subType, index, name);
|
||||
case kAnimVideo:
|
||||
return new AnimVideo(parent, subType, index, name);
|
||||
case kAnimSkeleton:
|
||||
return new AnimSkeleton(parent, subType, index, name);
|
||||
default:
|
||||
error("Unknown anim subtype %d", subType);
|
||||
}
|
||||
}
|
||||
|
||||
Anim::~Anim() {
|
||||
}
|
||||
|
||||
Anim::Anim(Object *parent, byte subType, uint16 index, const Common::String &name) :
|
||||
Object(parent, subType, index, name),
|
||||
_activity(0),
|
||||
_currentFrame(0),
|
||||
_numFrames(0),
|
||||
_refCount(0) {
|
||||
_type = TYPE;
|
||||
}
|
||||
|
||||
void Anim::readData(Formats::XRCReadStream *stream) {
|
||||
_activity = stream->readUint32LE();
|
||||
_numFrames = stream->readUint32LE();
|
||||
}
|
||||
|
||||
void Anim::selectFrame(uint32 frameIndex) {
|
||||
}
|
||||
|
||||
uint32 Anim::getActivity() const {
|
||||
return _activity;
|
||||
}
|
||||
|
||||
void Anim::applyToItem(Item *item) {
|
||||
_refCount++;
|
||||
}
|
||||
void Anim::removeFromItem(Item *item) {
|
||||
_refCount--;
|
||||
}
|
||||
|
||||
bool Anim::isInUse() const {
|
||||
return _refCount > 0;
|
||||
}
|
||||
|
||||
int Anim::getPointHotspotIndex(const Common::Point &point) const {
|
||||
// Most anim types only have one hotspot
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Anim::playAsAction(ItemVisual *item) {
|
||||
AnimScript *animScript = findChild<AnimScript>();
|
||||
animScript->goToScriptItem(0);
|
||||
}
|
||||
|
||||
bool Anim::isAtTime(uint32 time) const {
|
||||
warning("Anim::isAtTime is not implemented");
|
||||
return true;
|
||||
}
|
||||
|
||||
void Anim::shouldResetItem(bool resetItem) {
|
||||
// Script animations don't keep track of the item
|
||||
}
|
||||
|
||||
void Anim::resetItem() {
|
||||
// Script animations don't keep track of the item
|
||||
}
|
||||
|
||||
bool Anim::isDone() const {
|
||||
AnimScript *animScript = findChild<AnimScript>();
|
||||
return animScript->isDone();
|
||||
}
|
||||
|
||||
uint32 Anim::getMovementSpeed() const {
|
||||
return 100;
|
||||
}
|
||||
|
||||
uint32 Anim::getIdleActionFrequency() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Anim::printData() {
|
||||
debug("activity: %d", _activity);
|
||||
debug("numFrames: %d", _numFrames);
|
||||
}
|
||||
|
||||
AnimImages::~AnimImages() {
|
||||
}
|
||||
|
||||
AnimImages::AnimImages(Object *parent, byte subType, uint16 index, const Common::String &name) :
|
||||
Anim(parent, subType, index, name),
|
||||
_field_3C(0),
|
||||
_currentDirection(0),
|
||||
_currentFrameImage(nullptr) {
|
||||
}
|
||||
|
||||
void AnimImages::readData(Formats::XRCReadStream *stream) {
|
||||
Anim::readData(stream);
|
||||
|
||||
_field_3C = stream->readFloatLE();
|
||||
}
|
||||
|
||||
void AnimImages::onAllLoaded() {
|
||||
Anim::onAllLoaded();
|
||||
|
||||
_directions = listChildren<Direction>();
|
||||
}
|
||||
|
||||
void AnimImages::selectFrame(uint32 frameIndex) {
|
||||
if (frameIndex > _numFrames) {
|
||||
// The original silently ignores this as well
|
||||
warning("Request for frame %d for anim '%s' has been ignored, it is above max frame %d", frameIndex, getName().c_str(), _numFrames);
|
||||
_currentFrame = 0;
|
||||
}
|
||||
|
||||
_currentFrame = frameIndex;
|
||||
}
|
||||
|
||||
Visual *AnimImages::getVisual() {
|
||||
Direction *direction = _directions[_currentDirection];
|
||||
_currentFrameImage = direction->findChildWithIndex<Image>(_currentFrame);
|
||||
return _currentFrameImage->getVisual();
|
||||
}
|
||||
|
||||
void AnimImages::printData() {
|
||||
Anim::printData();
|
||||
|
||||
debug("field_3C: %f", _field_3C);
|
||||
}
|
||||
|
||||
int AnimImages::getPointHotspotIndex(const Common::Point &point) const {
|
||||
if (_currentFrameImage) {
|
||||
return _currentFrameImage->indexForPoint(point);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Common::Point AnimImages::getHotspotPosition(uint index) const {
|
||||
if (_currentFrameImage) {
|
||||
return _currentFrameImage->getHotspotPosition(index);
|
||||
}
|
||||
return Common::Point(-1, -1);
|
||||
}
|
||||
|
||||
void AnimImages::saveLoad(ResourceSerializer *serializer) {
|
||||
Anim::saveLoad(serializer);
|
||||
|
||||
serializer->syncAsUint32LE(_currentFrame);
|
||||
|
||||
if (serializer->isLoading()) {
|
||||
selectFrame(_currentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
AnimProp::~AnimProp() {
|
||||
delete _visual;
|
||||
}
|
||||
|
||||
AnimProp::AnimProp(Object *parent, byte subType, uint16 index, const Common::String &name) :
|
||||
Anim(parent, subType, index, name),
|
||||
_movementSpeed(100) {
|
||||
_visual = StarkGfx->createPropRenderer();
|
||||
}
|
||||
|
||||
Visual *AnimProp::getVisual() {
|
||||
return _visual;
|
||||
}
|
||||
|
||||
uint32 AnimProp::getMovementSpeed() const {
|
||||
return _movementSpeed;
|
||||
}
|
||||
|
||||
void AnimProp::readData(Formats::XRCReadStream *stream) {
|
||||
Anim::readData(stream);
|
||||
|
||||
_field_3C = stream->readString();
|
||||
|
||||
uint32 meshCount = stream->readUint32LE();
|
||||
for (uint i = 0; i < meshCount; i++) {
|
||||
_meshFilenames.push_back(Common::Path(stream->readString()));
|
||||
}
|
||||
|
||||
_textureFilename = stream->readString();
|
||||
_movementSpeed = stream->readUint32LE();
|
||||
_archiveName = stream->getArchiveName();
|
||||
}
|
||||
|
||||
void AnimProp::onPostRead() {
|
||||
if (_meshFilenames.size() != 1) {
|
||||
error("Unexpected mesh count in prop anim: '%d'", _meshFilenames.size());
|
||||
}
|
||||
|
||||
ArchiveReadStream *stream = StarkArchiveLoader->getFile(_meshFilenames[0], _archiveName);
|
||||
_visual->setModel(Formats::BiffMeshReader::read(stream));
|
||||
delete stream;
|
||||
|
||||
stream = StarkArchiveLoader->getFile(_textureFilename, _archiveName);
|
||||
_visual->setTexture(Formats::TextureSetReader::read(stream));
|
||||
delete stream;
|
||||
}
|
||||
|
||||
void AnimProp::printData() {
|
||||
Anim::printData();
|
||||
|
||||
debug("field_3C: %s", _field_3C.c_str());
|
||||
|
||||
Common::String description;
|
||||
for (uint32 i = 0; i < _meshFilenames.size(); i++) {
|
||||
debug("meshFilename[%d]: %s", i, _meshFilenames[i].toString().c_str());
|
||||
}
|
||||
debug("textureFilename: %s", _textureFilename.toString().c_str());
|
||||
debug("movementSpeed: %d", _movementSpeed);
|
||||
}
|
||||
|
||||
AnimVideo::~AnimVideo() {
|
||||
delete _smacker;
|
||||
}
|
||||
|
||||
AnimVideo::AnimVideo(Object *parent, byte subType, uint16 index, const Common::String &name) :
|
||||
Anim(parent, subType, index, name),
|
||||
_width(0),
|
||||
_height(0),
|
||||
_smacker(nullptr),
|
||||
_frameRateOverride(-1),
|
||||
_preload(false),
|
||||
_loop(false),
|
||||
_actionItem(nullptr),
|
||||
_shouldResetItem(true),
|
||||
_done(false) {
|
||||
}
|
||||
|
||||
void AnimVideo::readData(Formats::XRCReadStream *stream) {
|
||||
Anim::readData(stream);
|
||||
_smackerFile = stream->readString();
|
||||
_width = stream->readUint32LE();
|
||||
_height = stream->readUint32LE();
|
||||
|
||||
_positions.clear();
|
||||
_sizes.clear();
|
||||
|
||||
uint32 size = stream->readUint32LE();
|
||||
for (uint i = 0; i < size; i++) {
|
||||
_positions.push_back(stream->readPoint());
|
||||
_sizes.push_back(stream->readRect());
|
||||
}
|
||||
|
||||
_loop = stream->readBool();
|
||||
_frameRateOverride = stream->readUint32LE();
|
||||
|
||||
if (stream->isDataLeft()) {
|
||||
_preload = stream->readBool();
|
||||
}
|
||||
|
||||
_archiveName = stream->getArchiveName();
|
||||
|
||||
// WORKAROUND: Fix the position of various items being incorrect in the game datafiles
|
||||
Location *location = findParent<Location>();
|
||||
if (_name == "Mountain comes down" && location && location->getName() == "Below Floating Mountain") {
|
||||
for (uint i = 0; i < _sizes.size(); i++) {
|
||||
_positions[i].x = 352;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimVideo::onAllLoaded() {
|
||||
if (!_smacker) {
|
||||
|
||||
_smacker = new VisualSmacker(StarkGfx);
|
||||
|
||||
Common::SeekableReadStream *overrideStreamBink = nullptr;
|
||||
Common::SeekableReadStream *overrideStreamSmacker = nullptr;
|
||||
if (StarkSettings->isAssetsModEnabled() && StarkGfx->supportsModdedAssets()) {
|
||||
overrideStreamBink = openOverrideFile(".bik");
|
||||
if (!overrideStreamBink) {
|
||||
overrideStreamSmacker = openOverrideFile(".smk");
|
||||
}
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *stream = StarkArchiveLoader->getExternalFile(_smackerFile, _archiveName);
|
||||
if (overrideStreamBink) {
|
||||
_smacker->loadBink(overrideStreamBink);
|
||||
_smacker->readOriginalSize(stream);
|
||||
} else if (overrideStreamSmacker) {
|
||||
_smacker->loadSmacker(overrideStreamSmacker);
|
||||
_smacker->readOriginalSize(stream);
|
||||
} else {
|
||||
_smacker->loadSmacker(stream);
|
||||
}
|
||||
|
||||
_smacker->overrideFrameRate(_frameRateOverride);
|
||||
|
||||
updateSmackerPosition();
|
||||
}
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *AnimVideo::openOverrideFile(const Common::String &extension) const {
|
||||
Common::String baseName(_smackerFile.baseName());
|
||||
if (!baseName.hasSuffixIgnoreCase(".sss")) {
|
||||
return nullptr;
|
||||
}
|
||||
baseName = Common::String(baseName.c_str(), baseName.size() - 4) + extension;
|
||||
|
||||
Common::Path filePath(_smackerFile.getParent().appendComponent(baseName));
|
||||
filePath = StarkArchiveLoader->getExternalFilePath(filePath, _archiveName);
|
||||
|
||||
debugC(kDebugModding, "Attempting to load %s", filePath.toString(Common::Path::kNativeSeparator).c_str());
|
||||
|
||||
Common::SeekableReadStream *smkStream = SearchMan.createReadStreamForMember(filePath);
|
||||
if (!smkStream) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
debugC(kDebugModding, "Loaded %s", filePath.toString(Common::Path::kNativeSeparator).c_str());
|
||||
|
||||
return smkStream;
|
||||
}
|
||||
|
||||
void AnimVideo::onGameLoop() {
|
||||
if (!_smacker || !isInUse()) {
|
||||
return; // Animation not in use, no need to update the movie
|
||||
}
|
||||
|
||||
if (_smacker->isDone()) {
|
||||
// The last frame has been reached
|
||||
_done = true;
|
||||
|
||||
if (_shouldResetItem) {
|
||||
resetItem();
|
||||
}
|
||||
|
||||
if (_loop) {
|
||||
_smacker->rewind();
|
||||
}
|
||||
}
|
||||
|
||||
if (!_smacker->isDone()) {
|
||||
_smacker->update();
|
||||
updateSmackerPosition();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimVideo::resetItem() {
|
||||
if (!_loop && _actionItem) {
|
||||
// Reset our item if needed
|
||||
if (_actionItem->getActionAnim() == this) {
|
||||
_actionItem->resetActionAnim();
|
||||
}
|
||||
_actionItem = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimVideo::onEnginePause(bool pause) {
|
||||
Object::onEnginePause(pause);
|
||||
|
||||
if (_smacker && isInUse()) {
|
||||
_smacker->pause(pause);
|
||||
}
|
||||
}
|
||||
|
||||
Visual *AnimVideo::getVisual() {
|
||||
return _smacker;
|
||||
}
|
||||
|
||||
void AnimVideo::updateSmackerPosition() {
|
||||
int frame = _smacker->getFrameNumber();
|
||||
if (frame == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame < (int) _positions.size()) {
|
||||
_smacker->setPosition(_positions[frame]);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimVideo::shouldResetItem(bool resetItem) {
|
||||
_shouldResetItem = resetItem;
|
||||
}
|
||||
|
||||
void AnimVideo::playAsAction(ItemVisual *item) {
|
||||
_actionItem = item;
|
||||
_shouldResetItem = true;
|
||||
_done = false;
|
||||
|
||||
if (!_loop) {
|
||||
_smacker->rewind();
|
||||
}
|
||||
|
||||
// Update here so we have something up to date to show when rendering this frame
|
||||
_smacker->update();
|
||||
}
|
||||
|
||||
bool AnimVideo::isAtTime(uint32 time) const {
|
||||
uint32 currentTime = _smacker->getCurrentTime();
|
||||
return currentTime >= time;
|
||||
}
|
||||
|
||||
void AnimVideo::saveLoadCurrent(ResourceSerializer *serializer) {
|
||||
Anim::saveLoadCurrent(serializer);
|
||||
|
||||
int32 frameNumber = _smacker->getFrameNumber();
|
||||
serializer->syncAsSint32LE(frameNumber);
|
||||
serializer->syncAsSint32LE(_refCount);
|
||||
|
||||
// TODO: Seek to the saved frame number when loading
|
||||
}
|
||||
|
||||
void AnimVideo::printData() {
|
||||
Anim::printData();
|
||||
|
||||
debug("smackerFile: %s", _smackerFile.toString().c_str());
|
||||
debug("size: x %d, y %d", _width, _height);
|
||||
|
||||
Common::String description;
|
||||
for (uint32 i = 0; i < _positions.size(); i++) {
|
||||
description += Common::String::format("(x %d, y %d) ", _positions[i].x, _positions[i].y);
|
||||
}
|
||||
debug("positions: %s", description.c_str());
|
||||
|
||||
description.clear();
|
||||
for (uint32 i = 0; i < _sizes.size(); i++) {
|
||||
description += Common::String::format("(l %d, t %d, r %d, b %d) ",
|
||||
_sizes[i].left, _sizes[i].top, _sizes[i].right, _sizes[i].bottom);
|
||||
}
|
||||
debug("sizes: %s", description.c_str());
|
||||
|
||||
debug("frameRateOverride: %d", _frameRateOverride);
|
||||
debug("preload: %d", _preload);
|
||||
debug("loop: %d", _loop);
|
||||
}
|
||||
|
||||
AnimSkeleton::~AnimSkeleton() {
|
||||
delete _visual;
|
||||
delete _skeletonAnim;
|
||||
}
|
||||
|
||||
AnimSkeleton::AnimSkeleton(Object *parent, byte subType, uint16 index, const Common::String &name) :
|
||||
Anim(parent, subType, index, name),
|
||||
_castsShadow(true),
|
||||
_loop(false),
|
||||
_movementSpeed(100),
|
||||
_idleActionFrequency(1),
|
||||
_skeletonAnim(nullptr),
|
||||
_currentTime(0),
|
||||
_totalTime(0),
|
||||
_done(false),
|
||||
_actionItem(nullptr),
|
||||
_shouldResetItem(true) {
|
||||
_visual = StarkGfx->createActorRenderer();
|
||||
}
|
||||
|
||||
void AnimSkeleton::applyToItem(Item *item) {
|
||||
Anim::applyToItem(item);
|
||||
|
||||
if (!_loop) {
|
||||
_currentTime = 0;
|
||||
}
|
||||
|
||||
if (_currentTime > _totalTime) {
|
||||
_currentTime = 0;
|
||||
}
|
||||
|
||||
debugC(kDebugAnimation, "%s: add %s", item->getName().c_str(), getName().c_str());
|
||||
|
||||
ModelItem *modelItem = Object::cast<ModelItem>(item);
|
||||
|
||||
BonesMesh *mesh = modelItem->findBonesMesh();
|
||||
TextureSet *texture = modelItem->findTextureSet(TextureSet::kTextureNormal);
|
||||
|
||||
AnimHandler *animHandler = modelItem->getAnimHandler();
|
||||
animHandler->setModel(mesh->getModel());
|
||||
animHandler->setAnim(_skeletonAnim);
|
||||
|
||||
_visual->setModel(mesh->getModel());
|
||||
_visual->setAnimHandler(animHandler);
|
||||
_visual->setTexture(texture->getTexture());
|
||||
_visual->setTextureFacial(nullptr);
|
||||
_visual->setTime(_currentTime);
|
||||
_visual->setCastShadow(_castsShadow);
|
||||
}
|
||||
|
||||
void AnimSkeleton::removeFromItem(Item *item) {
|
||||
Anim::removeFromItem(item);
|
||||
|
||||
debugC(kDebugAnimation, "%s: remove %s", item->getName().c_str(), getName().c_str());
|
||||
|
||||
_actionItem = nullptr;
|
||||
}
|
||||
|
||||
Visual *AnimSkeleton::getVisual() {
|
||||
return _visual;
|
||||
}
|
||||
|
||||
void AnimSkeleton::readData(Formats::XRCReadStream *stream) {
|
||||
Anim::readData(stream);
|
||||
|
||||
_animFilename = stream->readString();
|
||||
stream->readString(); // Skipped in the original
|
||||
stream->readString(); // Skipped in the original
|
||||
stream->readString(); // Skipped in the original
|
||||
|
||||
_loop = stream->readBool();
|
||||
_movementSpeed = stream->readUint32LE();
|
||||
|
||||
if (_movementSpeed < 1) {
|
||||
_movementSpeed = 100;
|
||||
}
|
||||
|
||||
if (stream->isDataLeft()) {
|
||||
_castsShadow = stream->readBool();
|
||||
} else {
|
||||
_castsShadow = true;
|
||||
}
|
||||
|
||||
if (stream->isDataLeft()) {
|
||||
_idleActionFrequency = stream->readUint32LE();
|
||||
} else {
|
||||
_idleActionFrequency = 1;
|
||||
}
|
||||
|
||||
_archiveName = stream->getArchiveName();
|
||||
}
|
||||
|
||||
void AnimSkeleton::onPostRead() {
|
||||
ArchiveReadStream *stream = StarkArchiveLoader->getFile(_animFilename, _archiveName);
|
||||
|
||||
_skeletonAnim = new SkeletonAnim();
|
||||
_skeletonAnim->createFromStream(stream);
|
||||
|
||||
delete stream;
|
||||
}
|
||||
|
||||
void AnimSkeleton::onAllLoaded() {
|
||||
Anim::onAllLoaded();
|
||||
|
||||
_totalTime = _skeletonAnim->getLength();
|
||||
_currentTime = 0;
|
||||
}
|
||||
|
||||
void AnimSkeleton::onGameLoop() {
|
||||
Anim::onGameLoop();
|
||||
|
||||
if (isInUse() && _totalTime) {
|
||||
uint32 newTime = _currentTime + StarkGlobal->getMillisecondsPerGameloop();
|
||||
|
||||
if (!_loop && newTime >= _totalTime) {
|
||||
_done = true;
|
||||
|
||||
if (_shouldResetItem) {
|
||||
resetItem();
|
||||
}
|
||||
} else {
|
||||
_currentTime = newTime % _totalTime;
|
||||
_visual->setTime(_currentTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimSkeleton::resetItem() {
|
||||
if (_actionItem) {
|
||||
if (_actionItem->getActionAnim() == this) {
|
||||
_actionItem->resetActionAnim();
|
||||
}
|
||||
_actionItem = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimSkeleton::onPreDestroy() {
|
||||
resetItem();
|
||||
|
||||
Anim::onPreDestroy();
|
||||
}
|
||||
|
||||
uint32 AnimSkeleton::getMovementSpeed() const {
|
||||
return _movementSpeed;
|
||||
}
|
||||
|
||||
uint32 AnimSkeleton::getCurrentTime() const {
|
||||
return _currentTime;
|
||||
}
|
||||
|
||||
uint32 AnimSkeleton::getRemainingTime() const {
|
||||
int32 remainingTime = _totalTime - _currentTime;
|
||||
return CLIP<int32>(remainingTime, 0, _totalTime);
|
||||
}
|
||||
|
||||
void AnimSkeleton::shouldResetItem(bool resetItem) {
|
||||
_shouldResetItem = resetItem;
|
||||
}
|
||||
|
||||
void AnimSkeleton::playAsAction(ItemVisual *item) {
|
||||
_actionItem = item;
|
||||
_done = false;
|
||||
_shouldResetItem = true;
|
||||
|
||||
if (!_loop) {
|
||||
_currentTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool AnimSkeleton::isAtTime(uint32 time) const {
|
||||
return _currentTime >= time;
|
||||
}
|
||||
|
||||
uint32 AnimSkeleton::getIdleActionFrequency() const {
|
||||
return _idleActionFrequency;
|
||||
}
|
||||
|
||||
void AnimSkeleton::printData() {
|
||||
Anim::printData();
|
||||
|
||||
debug("filename: %s", _animFilename.toString().c_str());
|
||||
debug("castsShadow: %d", _castsShadow);
|
||||
debug("loop: %d", _loop);
|
||||
debug("movementSpeed: %d", _movementSpeed);
|
||||
debug("idleActionFrequency: %d", _idleActionFrequency);
|
||||
}
|
||||
|
||||
} // End of namespace Resources
|
||||
} // End of namespace Stark
|
||||
337
engines/stark/resources/anim.h
Normal file
337
engines/stark/resources/anim.h
Normal file
@@ -0,0 +1,337 @@
|
||||
/* 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 STARK_RESOURCES_ANIM_H
|
||||
#define STARK_RESOURCES_ANIM_H
|
||||
|
||||
#include "common/path.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str.h"
|
||||
|
||||
#include "engines/stark/resources/object.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class SkeletonAnim;
|
||||
class VisualActor;
|
||||
class VisualProp;
|
||||
class VisualSmacker;
|
||||
class Visual;
|
||||
namespace Formats {
|
||||
class XRCReadStream;
|
||||
}
|
||||
|
||||
namespace Resources {
|
||||
|
||||
class Direction;
|
||||
class Image;
|
||||
class Item;
|
||||
class ItemVisual;
|
||||
|
||||
/**
|
||||
* Animation base class
|
||||
*
|
||||
* Animations provide a time dependent visual state to Items
|
||||
*/
|
||||
class Anim : public Object {
|
||||
public:
|
||||
static const Type::ResourceType TYPE = Type::kAnim;
|
||||
|
||||
enum SubType {
|
||||
kAnimImages = 1,
|
||||
kAnimProp = 2,
|
||||
kAnimVideo = 3,
|
||||
kAnimSkeleton = 4
|
||||
};
|
||||
|
||||
enum ActionUsage {
|
||||
kActionUsagePassive = 1,
|
||||
kActionUsageActive = 2
|
||||
};
|
||||
|
||||
enum UIUsage {
|
||||
kUIUsageInventory = 1,
|
||||
kUIUsageUseCursorPassive = 4,
|
||||
kUIUsageUseCursorActive = 5
|
||||
};
|
||||
|
||||
enum ActorActivity {
|
||||
kActorActivityIdle = 1,
|
||||
kActorActivityWalk = 2,
|
||||
kActorActivityTalk = 3,
|
||||
kActorActivityRun = 6,
|
||||
kActorActivityIdleAction = 10
|
||||
};
|
||||
|
||||
/** Anim factory */
|
||||
static Object *construct(Object *parent, byte subType, uint16 index, const Common::String &name);
|
||||
|
||||
Anim(Object *parent, byte subType, uint16 index, const Common::String &name);
|
||||
~Anim() override;
|
||||
|
||||
// Resource API
|
||||
void readData(Formats::XRCReadStream *stream) override;
|
||||
|
||||
/** Get current displayed frame */
|
||||
uint32 getCurrentFrame() { return _currentFrame; }
|
||||
|
||||
/** Sets the animation frame to be displayed */
|
||||
virtual void selectFrame(uint32 frameIndex);
|
||||
|
||||
/** Obtain the Visual to be used to render the animation */
|
||||
virtual Visual *getVisual() = 0;
|
||||
|
||||
/** Associate the animation to an Item */
|
||||
virtual void applyToItem(Item *item);
|
||||
|
||||
/** Dissociate the animation from an item */
|
||||
virtual void removeFromItem(Item *item);
|
||||
|
||||
/** Check is the animation is being used by an item */
|
||||
bool isInUse() const;
|
||||
|
||||
/** Obtain the purpose of this anim */
|
||||
uint32 getActivity() const;
|
||||
|
||||
/** Return the hotspot index for a point given in relative coordinates */
|
||||
virtual int getPointHotspotIndex(const Common::Point &point) const;
|
||||
|
||||
/** Get the hotspot position for a given index of a pat-table */
|
||||
virtual Common::Point getHotspotPosition(uint index) const { return Common::Point(-1, -1); }
|
||||
|
||||
/**
|
||||
* Play the animation as an action for an item.
|
||||
*
|
||||
* This sets up a callback to the item for when the animation completes.
|
||||
*/
|
||||
virtual void playAsAction(ItemVisual *item);
|
||||
|
||||
/** Checks if the elapsed time since the animation start is greater than a specified duration */
|
||||
virtual bool isAtTime(uint32 time) const;
|
||||
|
||||
/** Get the anim movement speed in units per seconds */
|
||||
virtual uint32 getMovementSpeed() const;
|
||||
|
||||
/** Get the chance the animation has to play among other idle actions from the same anim hierarchy */
|
||||
virtual uint32 getIdleActionFrequency() const;
|
||||
|
||||
/**
|
||||
* When this animation is playing as an action should a new animation
|
||||
* be chosen for the item as soon as this one completes based on
|
||||
* the item's activity?
|
||||
* This is true by default, but setting it to false allows scripts
|
||||
* to chose precisely the new animation to play, and to start it
|
||||
* in the same frame as this one is removed.
|
||||
*/
|
||||
virtual void shouldResetItem(bool resetItem);
|
||||
|
||||
/**
|
||||
* Remove this action animation for the item and select a new animation
|
||||
* based on the item's current activity.
|
||||
*/
|
||||
virtual void resetItem();
|
||||
|
||||
/**
|
||||
* Is this animation done playing.
|
||||
*
|
||||
* Only valid for animations started with playAsAction.
|
||||
*/
|
||||
virtual bool isDone() const;
|
||||
|
||||
protected:
|
||||
void printData() override;
|
||||
|
||||
uint32 _activity;
|
||||
uint32 _currentFrame;
|
||||
uint32 _numFrames;
|
||||
int32 _refCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays still images controlled by an AnimScript
|
||||
*/
|
||||
class AnimImages : public Anim {
|
||||
public:
|
||||
AnimImages(Object *parent, byte subType, uint16 index, const Common::String &name);
|
||||
~AnimImages() override;
|
||||
|
||||
// Resource API
|
||||
void readData(Formats::XRCReadStream *stream) override;
|
||||
void onAllLoaded() override;
|
||||
void saveLoad(ResourceSerializer *serializer) override;
|
||||
|
||||
// Anim API
|
||||
void selectFrame(uint32 frameIndex) override;
|
||||
Visual *getVisual() override;
|
||||
int getPointHotspotIndex(const Common::Point &point) const override;
|
||||
Common::Point getHotspotPosition(uint index) const override;
|
||||
|
||||
protected:
|
||||
void printData() override;
|
||||
|
||||
float _field_3C;
|
||||
|
||||
uint32 _currentDirection;
|
||||
Common::Array<Direction *> _directions;
|
||||
|
||||
Image *_currentFrameImage;
|
||||
};
|
||||
|
||||
class AnimProp : public Anim {
|
||||
public:
|
||||
AnimProp(Object *parent, byte subType, uint16 index, const Common::String &name);
|
||||
~AnimProp() override;
|
||||
|
||||
// Resource API
|
||||
void readData(Formats::XRCReadStream *stream) override;
|
||||
void onPostRead() override;
|
||||
|
||||
// Anim API
|
||||
Visual *getVisual() override;
|
||||
uint32 getMovementSpeed() const override;
|
||||
|
||||
protected:
|
||||
void printData() override;
|
||||
|
||||
Common::String _field_3C;
|
||||
Common::Array<Common::Path> _meshFilenames;
|
||||
Common::Path _textureFilename;
|
||||
uint32 _movementSpeed;
|
||||
Common::Path _archiveName;
|
||||
|
||||
VisualProp *_visual;
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays a Smacker video
|
||||
*/
|
||||
class AnimVideo : public Anim {
|
||||
public:
|
||||
AnimVideo(Object *parent, byte subType, uint16 index, const Common::String &name);
|
||||
~AnimVideo() override;
|
||||
|
||||
// Resource API
|
||||
void readData(Formats::XRCReadStream *stream) override;
|
||||
void onAllLoaded() override;
|
||||
void onGameLoop() override;
|
||||
void onEnginePause(bool pause) override;
|
||||
void saveLoadCurrent(ResourceSerializer *serializer) override;
|
||||
|
||||
// Anim API
|
||||
Visual *getVisual() override;
|
||||
void playAsAction(ItemVisual *item) override;
|
||||
void shouldResetItem(bool resetItem) override;
|
||||
void resetItem() override;
|
||||
bool isAtTime(uint32 time) const override;
|
||||
bool isDone() const override { return _done || !isInUse(); }
|
||||
|
||||
protected:
|
||||
typedef Common::Array<Common::Point> PointArray;
|
||||
typedef Common::Array<Common::Rect> RectArray;
|
||||
|
||||
void printData() override;
|
||||
Common::SeekableReadStream *openOverrideFile(const Common::String &extension) const;
|
||||
|
||||
/** Update the position of the video for the current frame */
|
||||
void updateSmackerPosition();
|
||||
|
||||
Common::Path _smackerFile;
|
||||
Common::Path _archiveName;
|
||||
|
||||
VisualSmacker *_smacker;
|
||||
|
||||
uint32 _width;
|
||||
uint32 _height;
|
||||
|
||||
PointArray _positions;
|
||||
RectArray _sizes;
|
||||
|
||||
int32 _frameRateOverride;
|
||||
bool _preload;
|
||||
bool _loop;
|
||||
bool _done;
|
||||
|
||||
ItemVisual *_actionItem;
|
||||
bool _shouldResetItem;
|
||||
};
|
||||
|
||||
/**
|
||||
* Animates a 3D mesh skeleton
|
||||
*/
|
||||
class AnimSkeleton : public Anim {
|
||||
public:
|
||||
AnimSkeleton(Object *parent, byte subType, uint16 index, const Common::String &name);
|
||||
~AnimSkeleton() override;
|
||||
|
||||
// Resource API
|
||||
void readData(Formats::XRCReadStream *stream) override;
|
||||
void onPostRead() override;
|
||||
void onAllLoaded() override;
|
||||
void onGameLoop() override;
|
||||
void onPreDestroy() override;
|
||||
|
||||
// Anim API
|
||||
void applyToItem(Item *item) override;
|
||||
void removeFromItem(Item *item) override;
|
||||
Visual *getVisual() override;
|
||||
void playAsAction(ItemVisual *item) override;
|
||||
bool isAtTime(uint32 time) const override;
|
||||
bool isDone() const override { return _done || !isInUse(); }
|
||||
uint32 getMovementSpeed() const override;
|
||||
uint32 getIdleActionFrequency() const override;
|
||||
void shouldResetItem(bool resetItem) override;
|
||||
void resetItem() override;
|
||||
|
||||
/** Get the duration in milliseconds before the animation loops ends */
|
||||
uint32 getRemainingTime() const;
|
||||
|
||||
/** Get the position in the animation loop in milliseconds */
|
||||
uint32 getCurrentTime() const;
|
||||
|
||||
protected:
|
||||
void printData() override;
|
||||
|
||||
bool _castsShadow;
|
||||
Common::Path _archiveName;
|
||||
Common::Path _animFilename;
|
||||
bool _loop;
|
||||
uint32 _movementSpeed;
|
||||
uint32 _idleActionFrequency;
|
||||
|
||||
uint32 _totalTime;
|
||||
uint32 _currentTime;
|
||||
bool _done;
|
||||
|
||||
SkeletonAnim *_skeletonAnim;
|
||||
VisualActor *_visual;
|
||||
|
||||
ItemVisual *_actionItem;
|
||||
bool _shouldResetItem;
|
||||
};
|
||||
|
||||
} // End of namespace Resources
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_RESOURCES_ANIM_H
|
||||
209
engines/stark/resources/animhierarchy.cpp
Normal file
209
engines/stark/resources/animhierarchy.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
/* 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/stark/resources/animhierarchy.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/random.h"
|
||||
|
||||
#include "engines/stark/formats/xrc.h"
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/bonesmesh.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/textureset.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Resources {
|
||||
|
||||
AnimHierarchy::~AnimHierarchy() {
|
||||
}
|
||||
|
||||
AnimHierarchy::AnimHierarchy(Object *parent, byte subType, uint16 index, const Common::String &name) :
|
||||
Object(parent, subType, index, name),
|
||||
_currentActivity(0),
|
||||
_currentAnim(nullptr),
|
||||
_field_5C(0),
|
||||
_idleActionsFrequencySum(0) {
|
||||
_type = TYPE;
|
||||
}
|
||||
|
||||
void AnimHierarchy::readData(Formats::XRCReadStream *stream) {
|
||||
_animationReferences.clear();
|
||||
|
||||
uint32 refCount = stream->readUint32LE();
|
||||
for (uint32 i = 0; i < refCount; i++) {
|
||||
_animationReferences.push_back(stream->readResourceReference());
|
||||
}
|
||||
|
||||
_parentAnimHierarchyReference = stream->readResourceReference();
|
||||
_field_5C = stream->readFloatLE();
|
||||
}
|
||||
|
||||
void AnimHierarchy::onAllLoaded() {
|
||||
Object::onAllLoaded();
|
||||
|
||||
loadActivityAnimations();
|
||||
loadIdleAnimations();
|
||||
}
|
||||
|
||||
void AnimHierarchy::loadActivityAnimations() {
|
||||
AnimHierarchy *parentHierarchy = _parentAnimHierarchyReference.resolve<AnimHierarchy>();
|
||||
|
||||
// Activity animations are inherited from the parent ...
|
||||
if (parentHierarchy) {
|
||||
_activityAnimations = parentHierarchy->_activityAnimations;
|
||||
}
|
||||
|
||||
// ... but can be overridden
|
||||
for (uint i = 0; i < _animationReferences.size(); i++) {
|
||||
Anim *anim = _animationReferences[i].resolve<Anim>();
|
||||
|
||||
bool inserted = false;
|
||||
for (uint j = 0; j < _activityAnimations.size(); j++) {
|
||||
if (_activityAnimations[j]->getActivity() == anim->getActivity()) {
|
||||
_activityAnimations[j] = anim;
|
||||
inserted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inserted) {
|
||||
_activityAnimations.push_back(anim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimHierarchy::loadIdleAnimations() {
|
||||
AnimHierarchy *parentHierarchy = _parentAnimHierarchyReference.resolve<AnimHierarchy>();
|
||||
if (parentHierarchy) {
|
||||
_idleAnimations = parentHierarchy->_idleAnimations;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < _animationReferences.size(); i++) {
|
||||
Anim *anim = _animationReferences[i].resolve<Anim>();
|
||||
if (anim->getActivity() == Anim::kActorActivityIdleAction) {
|
||||
_idleAnimations.push_back(anim);
|
||||
}
|
||||
}
|
||||
|
||||
_idleActionsFrequencySum = 0;
|
||||
for (uint i = 0; i < _idleAnimations.size(); i++) {
|
||||
_idleActionsFrequencySum += _idleAnimations[i]->getIdleActionFrequency();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimHierarchy::setItemAnim(ItemVisual *item, int32 activity) {
|
||||
unselectItemAnim(item);
|
||||
_currentActivity = activity;
|
||||
selectItemAnim(item);
|
||||
}
|
||||
|
||||
void AnimHierarchy::unselectItemAnim(ItemVisual *item) {
|
||||
if (_currentAnim && _currentAnim->isInUse()) {
|
||||
_currentAnim->removeFromItem(item);
|
||||
}
|
||||
|
||||
_currentAnim = nullptr;
|
||||
}
|
||||
|
||||
void AnimHierarchy::selectItemAnim(ItemVisual *item) {
|
||||
// Search for an animation with the appropriate index
|
||||
for (uint i = 0; i < _activityAnimations.size(); i++) {
|
||||
if (_activityAnimations[i]->getActivity() == _currentActivity) {
|
||||
_currentAnim = _activityAnimations[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Default to the first animation
|
||||
if (!_currentAnim && !_activityAnimations.empty()) {
|
||||
_currentAnim = _activityAnimations[0];
|
||||
}
|
||||
|
||||
if (!_currentAnim) {
|
||||
error("Failed to set an animation for item %s", item->getName().c_str());
|
||||
}
|
||||
|
||||
if (!_currentAnim->isInUse()) {
|
||||
_currentAnim->applyToItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
Anim *AnimHierarchy::getCurrentAnim() {
|
||||
return _currentAnim;
|
||||
}
|
||||
|
||||
BonesMesh *AnimHierarchy::findBonesMesh() {
|
||||
return findChild<BonesMesh>();
|
||||
}
|
||||
|
||||
TextureSet *AnimHierarchy::findTextureSet(uint32 textureType) {
|
||||
return findChildWithSubtype<TextureSet>(textureType);
|
||||
}
|
||||
|
||||
Anim *AnimHierarchy::getAnimForActivity(uint32 activity) {
|
||||
// Search for an animation with the appropriate use
|
||||
for (uint i = 0; i < _activityAnimations.size(); i++) {
|
||||
if (_activityAnimations[i]->getActivity() == activity) {
|
||||
return _activityAnimations[i];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Visual *AnimHierarchy::getVisualForUsage(uint32 usage) {
|
||||
Anim *anim = getAnimForActivity(usage);
|
||||
if (anim) {
|
||||
return anim->getVisual();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Anim *AnimHierarchy::getIdleActionAnim() const {
|
||||
if (_idleActionsFrequencySum == 0) {
|
||||
return nullptr; // There are no idle animations
|
||||
}
|
||||
|
||||
int pick = StarkRandomSource->getRandomNumber(_idleActionsFrequencySum - 1);
|
||||
for (uint i = 0; i < _idleAnimations.size(); i++) {
|
||||
pick -= _idleAnimations[i]->getIdleActionFrequency();
|
||||
|
||||
if (pick < 0) {
|
||||
return _idleAnimations[i];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AnimHierarchy::printData() {
|
||||
for (uint i = 0; i < _animationReferences.size(); i++) {
|
||||
debug("anim %d: %s", i, _animationReferences[i].describe().c_str());
|
||||
}
|
||||
|
||||
debug("animHierarchy: %s", _parentAnimHierarchyReference.describe().c_str());
|
||||
debug("field_5C: %f", _field_5C);
|
||||
}
|
||||
|
||||
} // End of namespace Resources
|
||||
} // End of namespace Stark
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user