Skip to content

Commit

Permalink
Merge pull request #90 from REGoth-project/feature/parallel-save
Browse files Browse the repository at this point in the history
Async "Save Game"
  • Loading branch information
ataulien authored Sep 20, 2019
2 parents a9e44ac + 74b53b0 commit d595803
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 39 deletions.
25 changes: 19 additions & 6 deletions src/components/GameWorld.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
#include "GameWorld.hpp"

#include <BsZenLib/ImportPath.hpp>
#include <RTTI/RTTI_GameWorld.hpp>
#include <daedalus/DATFile.h>

#include <Resources/BsResources.h>
#include <Scene/BsPrefab.h>
#include <Scene/BsSceneManager.h>
#include <Threading/BsTaskScheduler.h>

#include <components/Character.hpp>
#include <components/Focusable.hpp>
#include <components/GameClock.hpp>
#include <components/Item.hpp>
#include <components/VisualCharacter.hpp>
#include <components/Waynet.hpp>
#include <daedalus/DATFile.h>

#include <RTTI/RTTI_GameWorld.hpp>
#include <exception/Throw.hpp>
#include <log/logging.hpp>
#include <original-content/VirtualFileSystem.hpp>
Expand Down Expand Up @@ -376,7 +381,7 @@ namespace REGoth
return waynet()->findWay(waypointFrom, waypointTo);
}

void GameWorld::save(const bs::String& saveName)
bs::SPtr<bs::Task> GameWorld::save(const bs::String& saveName)
{
bs::HPrefab cached = bs::Prefab::create(SO());

Expand All @@ -386,9 +391,17 @@ namespace REGoth
KeepExisting = false,
};

// TODO: Should store at savegame location
bs::Path path = BsZenLib::GothicPathToCachedWorld(saveName);
bs::gResources().save(cached, path, Overwrite);
auto saveTask = bs::Task::create("Save:" + saveName, [cached, saveName]() {
// TODO: Should store at savegame location
bs::Path path = BsZenLib::GothicPathToCachedWorld(saveName);

// FIXME: This call can take quite long when running under a debugger
bs::gResources().save(cached, path, Overwrite);
});

bs::TaskScheduler::instance().addTask(saveTask);

return saveTask;
}

bs::HPrefab GameWorld::load(const bs::String& saveName)
Expand Down
10 changes: 7 additions & 3 deletions src/components/GameWorld.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,14 @@ namespace REGoth
static HGameWorld createEmpty();

/**
* Saves the current world and everything that goes with it to a
* savegame with the given name.
* Saves the current world and everything that goes with it to a savegame
* with the given name.
*
* Saving is done in parallel by first copying the game-state (on this thread)
* and then saving it in another thread. The task performing the actual save is
* returned so you can `wait` for it.
*/
void save(const bs::String& saveName);
bs::SPtr<bs::Task> save(const bs::String& saveName);

/**
* Loads the world with the given name previously saved via save().
Expand Down
68 changes: 64 additions & 4 deletions src/components/VisualCharacter.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
#include "VisualCharacter.hpp"
#include "original-content/VirtualFileSystem.hpp"
#include <Animation/BsAnimationClip.h>
#include <RTTI/RTTI_VisualCharacter.hpp>

#include <BsZenLib/ImportPath.hpp>
#include <BsZenLib/ImportSkeletalMesh.hpp>

#include <Components/BsCAnimation.h>
#include <Components/BsCRenderable.h>

#include <Animation/BsAnimationClip.h>
#include <Debug/BsDebug.h>
#include <Image/BsTexture.h>
#include <Material/BsMaterial.h>
#include <Mesh/BsMesh.h>
#include <RTTI/RTTI_VisualCharacter.hpp>
#include <Scene/BsSceneObject.h>

#include <animation/Animation.hpp>
#include <animation/StateNaming.hpp>
#include <components/NodeVisuals.hpp>
#include <exception/Throw.hpp>
#include <log/logging.hpp>
#include <original-content/OriginalGameResources.hpp>
#include <original-content/VirtualFileSystem.hpp>

const bs::String MODEL_NODE_NAME_R_HAND = "BIP01 R HAND";
const bs::String MODEL_NODE_NAME_L_HAND = "BIP01 L HAND";
Expand All @@ -30,7 +37,8 @@ namespace REGoth
setName("VisualCharacter");
}

void VisualCharacter::setBodyMesh(const bs::String& bodyMesh)
void VisualCharacter::setBodyMesh(const bs::String& bodyMesh, bs::UINT32 bodyTextureIdx,
bs::UINT32 bodySkinColorIdx)
{
if (!modelScript())
{
Expand Down Expand Up @@ -60,6 +68,11 @@ namespace REGoth

setMesh(modelScript()->getMeshes()[0]);
}

mBodyState.bodyTextureIdx = bodyTextureIdx;
mBodyState.bodySkinColorIdx = bodySkinColorIdx;

updateBodyMesh();
}

void VisualCharacter::setHeadMesh(const bs::String& headmesh, bs::UINT32 headTextureIdx,
Expand All @@ -81,6 +94,53 @@ namespace REGoth

void VisualCharacter::updateBodyMesh()
{
bs::String bodyTexName = getCurrentBodyTextureName();

if (bodyTexName.empty()) return;

bs::String newTextureName = bodyTexName;

bool isBodyTexture = bodyTexName.find("_BODY") != bs::String::npos;

// Only modify if that is the core body texture (not armor)
if (isBodyTexture)
{
if (mBodyState.bodyTextureIdx != 0)
newTextureName = bs::StringUtil::replaceAll(newTextureName, "_V0",
"_V" + bs::toString(mBodyState.bodyTextureIdx));

if (mBodyState.bodySkinColorIdx != 0)
newTextureName = bs::StringUtil::replaceAll(
newTextureName, "_C0", "_C" + bs::toString(mBodyState.bodySkinColorIdx));
}

setBodyTexture(newTextureName);
}

bs::String VisualCharacter::getCurrentBodyTextureName() const
{
if (mesh()->getMaterials().empty()) return "";

auto bodyMaterial = mesh()->getMaterials()[0];
auto albedo = bodyMaterial->getTexture("gAlbedoTex");

if (!albedo) return "";

return albedo->getName();
}

void VisualCharacter::setBodyTexture(const bs::String& originalFileName)
{
bs::HTexture texture = gOriginalGameResources().texture(originalFileName);

if (!texture)
{
REGOTH_THROW(InvalidParametersException, "Could not load body texture: " + originalFileName);
}

REGOTH_LOG(Info, Uncategorized, "Set body texture: {0}", originalFileName);

material(0)->setTexture("gAlbedoTex", texture);
}

void VisualCharacter::updateHeadMesh()
Expand Down
19 changes: 17 additions & 2 deletions src/components/VisualCharacter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ namespace REGoth
* @param bodyMesh Name of the body mesh to use, e.g. `HUM_BODY_NAKED0`. The
* file extension can be omitted.
*/
void setBodyMesh(const bs::String& bodyMesh);
void setBodyMesh(const bs::String& bodyMesh, bs::UINT32 bodyTextureIdx = 0,
bs::UINT32 bodySkinColorIdx = 0);

/**
* Sets the headmesh for this model.
Expand All @@ -56,6 +57,20 @@ namespace REGoth
*/
void updateBodyMesh();

/**
* @return The name of the current body texture. Empty string if not possible
* to extract.
*/
bs::String getCurrentBodyTextureName() const;

/**
* Sets the texture to be displayed on the body mesh.
*
* @param originalFileName Name of the texture file as in the original game files.
* For example, "STONE.TGA".
*/
void setBodyTexture(const bs::String& originalFileName);

/**
* Replaces the current headmesh of this model from the current body-state
*/
Expand All @@ -78,7 +93,7 @@ namespace REGoth
REGOTH_DECLARE_RTTI(VisualCharacter);

protected:
VisualCharacter() = default; // For RTTI
VisualCharacter() = default; // For RTTI
};

using HVisualCharacter = bs::GameObjectHandle<VisualCharacter>;
Expand Down
42 changes: 41 additions & 1 deletion src/components/VisualSkeletalAnimation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <Components/BsCAnimation.h>
#include <Components/BsCRenderable.h>
#include <Debug/BsDebug.h>
#include <Material/BsMaterial.h>
#include <Mesh/BsMesh.h>
#include <RTTI/RTTI_VisualSkeletalAnimation.hpp>
#include <Scene/BsSceneObject.h>
Expand All @@ -26,10 +27,13 @@ namespace REGoth
{
bs::Component::onInitialized();

// If this is called after deserialization, we need register the event-callback in here
if (mSubRenderable)
{
// If this is called after deserialization, we need register the event-callback in here
setupAnimationComponent();

// Also the materials need to be cloned again, as those resources were not saved.
cloneMaterials();
}
}

Expand All @@ -51,6 +55,41 @@ namespace REGoth
}
}

bs::HMaterial VisualSkeletalAnimation::material(bs::UINT32 index) const
{
auto& materials = mSubRenderable->getMaterials();

if (index >= materials.size()) return {};

return materials[index];
}

void VisualSkeletalAnimation::cloneMaterials()
{
// Access the original materials for cloning
const auto& materials = mMesh->getMaterials();

destroyClonedMaterials();
mClonedMaterials.resize(materials.size());

for (bs::UINT32 i = 0; i < (bs::UINT32)mClonedMaterials.size(); i++)
{
mClonedMaterials[i] = materials[i]->clone();
}

mSubRenderable->setMaterials(mClonedMaterials);
}

void VisualSkeletalAnimation::destroyClonedMaterials()
{
for (bs::HMaterial m : mClonedMaterials)
{
m->destroy();
}

mClonedMaterials.clear();
}

void VisualSkeletalAnimation::setModelScript(BsZenLib::Res::HModelScriptFile modelScript)
{
if (modelScript->getName().empty())
Expand Down Expand Up @@ -87,6 +126,7 @@ namespace REGoth

deleteObjectSubtree();
buildObjectSubtree();
cloneMaterials();

addDefaultAttachments();

Expand Down
24 changes: 23 additions & 1 deletion src/components/VisualSkeletalAnimation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,26 @@ namespace REGoth
return mMesh;
}

/**
* Access to the material at the given index. Returns an empty handle if
* the index is out of range.
*/
bs::HMaterial material(bs::UINT32 index) const;

/**
* Copies all materials currently used by the renderable. This makes it possible
* to have different materials on visuals using the same mesh. Note that the
* materials are never saved so that you need to re-build them after deserialization.
*/
void cloneMaterials();

/**
* After the materials have been cloned, they need to be destroyed when the mesh
* changes. Otherwise new materials would be cloned on every mesh change which
* would fill up memory.
*/
void destroyClonedMaterials();

/**
* Access to attaching something to specific nodes for sub-classes.
*/
Expand Down Expand Up @@ -302,10 +322,12 @@ namespace REGoth
bs::UINT32 getClipLayer(HZAnimationClip clip) const;

// Configuration ----------------------------------------------------------

BsZenLib::Res::HModelScriptFile mModelScript; /**< Model-script of the displayed model */
BsZenLib::Res::HMeshWithMaterials mMesh; /**< Currently displayed mesh, from the model script */

/** Filled incase cloneMaterials() has been called. Not serialized. */
bs::Vector<bs::HMaterial> mClonedMaterials;

// Object Sub Tree --------------------------------------------------------
bs::Vector<bs::HSceneObject> mSubObjects; /**< All created sub-objects by this component */

Expand Down
Loading

0 comments on commit d595803

Please sign in to comment.