diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 909f55ff..aabcadfe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -172,10 +172,10 @@ add_library(REGothEngine STATIC scripting/daedalus/DaedalusVMForGameWorld.hpp scripting/daedalus/REGothDaedalusVM.cpp scripting/daedalus/REGothDaedalusVM.hpp - world/internals/ConstructFromZEN.cpp - world/internals/ConstructFromZEN.hpp - world/internals/ImportSingleVob.cpp - world/internals/ImportSingleVob.hpp + world/internals/ZenImporter.cpp + world/internals/ZenImporter.hpp + world/internals/ZenCache.cpp + world/internals/ZenCache.hpp ) target_link_libraries(REGothEngine PUBLIC bsf BsZenLib) diff --git a/src/components/GameWorld.cpp b/src/components/GameWorld.cpp index 00c1908d..0ad08e6a 100644 --- a/src/components/GameWorld.cpp +++ b/src/components/GameWorld.cpp @@ -21,12 +21,9 @@ #include #include #include -#include namespace REGoth { - const char* const WORLD_STARTPOINT = "STARTPOINT"; - GameWorld::GameWorld(const bs::HSceneObject& parent, const bs::String& zenFile) : bs::Component(parent) , mZenFile(zenFile) @@ -67,7 +64,7 @@ namespace REGoth if (!mZenFile.empty()) { // Import the ZEN and add all scene objects as children to this SO. - bs::HSceneObject so = Internals::constructFromZEN(thisWorld, mZenFile); + bs::HSceneObject so = mZenImporter.constructFromZEN(thisWorld, mZenFile); if (!so) { diff --git a/src/components/GameWorld.hpp b/src/components/GameWorld.hpp index 6d76870d..a56daf63 100644 --- a/src/components/GameWorld.hpp +++ b/src/components/GameWorld.hpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -28,8 +29,6 @@ namespace REGoth class Waypoint; using HWaypoint = bs::GameObjectHandle; - extern const char* const WORLD_STARTPOINT; - namespace Scripting { class ScriptVMForGameWorld; @@ -140,6 +139,11 @@ namespace REGoth return mWaynet; } + bs::HSceneObject getWorldStartpoint() + { + return mZenImporter.getStartpoint(); + } + /** * @return Handle to the GameClock of the world. */ @@ -376,6 +380,11 @@ namespace REGoth void findAllItems(); void findAllFocusables(); + /** + * ZEN-File Importer. + */ + ZenImporter mZenImporter; + /** * ZEN-File this world was created from, e.g. `NEWWORLD.ZEN`. */ diff --git a/src/core/Gothic1Game.cpp b/src/core/Gothic1Game.cpp index 2ab6abcd..628b3bbf 100644 --- a/src/core/Gothic1Game.cpp +++ b/src/core/Gothic1Game.cpp @@ -31,7 +31,7 @@ void Gothic1Game::setupScene() { world = GameWorld::importZEN(WORLD); - HCharacter hero = world->insertCharacter("PC_HERO", WORLD_STARTPOINT); + HCharacter hero = world->insertCharacter("PC_HERO", world->getWorldStartpoint()->getName()); hero->useAsHero(); hero->SO()->addComponent(world); diff --git a/src/core/Gothic2Game.cpp b/src/core/Gothic2Game.cpp index a436cff7..fc068b84 100644 --- a/src/core/Gothic2Game.cpp +++ b/src/core/Gothic2Game.cpp @@ -30,7 +30,7 @@ void Gothic2Game::setupScene() { world = GameWorld::importZEN(WORLD); - HCharacter hero = world->insertCharacter("PC_HERO", WORLD_STARTPOINT); + HCharacter hero = world->insertCharacter("PC_HERO", world->getWorldStartpoint()->getName()); hero->useAsHero(); hero->SO()->addComponent(world); diff --git a/src/main_WorldMeshViewer.cpp b/src/main_WorldMeshViewer.cpp index 84e2add1..18670834 100644 --- a/src/main_WorldMeshViewer.cpp +++ b/src/main_WorldMeshViewer.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include class REGothWorldMeshViewer : public REGoth::EmptyGame { @@ -21,11 +21,12 @@ class REGothWorldMeshViewer : public REGoth::EmptyGame void setupScene() override { - REGoth::Internals::loadWorldMeshFromZEN("ADDONWORLD.ZEN"); + mZenImporter.loadWorldMeshFromZEN("ADDONWORLD.ZEN"); } protected: bs::HFPSCamera mFPSCamera; + REGoth::ZenImporter mZenImporter; }; int main(int argc, char** argv) diff --git a/src/main_WorldViewer.cpp b/src/main_WorldViewer.cpp index 8d3e1716..02641297 100644 --- a/src/main_WorldViewer.cpp +++ b/src/main_WorldViewer.cpp @@ -100,7 +100,7 @@ class REGothWorldViewer : public REGoth::Engine { world = GameWorld::importZEN(config()->world); - HCharacter hero = world->insertCharacter("PC_HERO", WORLD_STARTPOINT); + HCharacter hero = world->insertCharacter("PC_HERO", world->getWorldStartpoint()->getName()); hero->useAsHero(); hero->SO()->addComponent(world); diff --git a/src/world/internals/ConstructFromZEN.cpp b/src/world/internals/ConstructFromZEN.cpp deleted file mode 100644 index 84a253a9..00000000 --- a/src/world/internals/ConstructFromZEN.cpp +++ /dev/null @@ -1,231 +0,0 @@ -#include "ConstructFromZEN.hpp" -#include "ImportSingleVob.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace REGoth -{ - struct OriginalZen - { - bs::String fileName; - ZenLoad::oCWorldData vobTree; - ZenLoad::PackedMesh worldMesh; - }; - - static bool importZEN(const bs::String& zenFile, OriginalZen& result); - static bs::HSceneObject importWorldMesh(const OriginalZen& zen); - static void importVobs(bs::HSceneObject sceneRoot, HGameWorld gameWorld, const OriginalZen& zen); - static void importWaynet(bs::HSceneObject sceneRoot, const OriginalZen& zen); - static void walkVobTree(bs::HSceneObject bsfParent, HGameWorld gameWorld, - const ZenLoad::zCVobData& zenParent); - - bs::HSceneObject Internals::constructFromZEN(HGameWorld gameWorld, const bs::String& zenFile) - { - OriginalZen zen; - - bool hasLoadedZEN = importZEN(zenFile, zen); - - if (!hasLoadedZEN) - { - REGOTH_LOG(Warning, Uncategorized, "[ConstructFromZEN] Failed to read zen-file: {0}", zenFile); - return {}; - } - - bs::HSceneObject worldMesh = importWorldMesh(zen); - worldMesh->setParent(gameWorld->SO()); - - importVobs(gameWorld->SO(), gameWorld, zen); - importWaynet(gameWorld->SO(), zen); - - return worldMesh; - } - - bs::HSceneObject Internals::loadWorldMeshFromZEN(const bs::String& zenFile) - { - OriginalZen zen; - - bool hasLoadedZEN = importZEN(zenFile, zen); - - if (!hasLoadedZEN) - { - REGOTH_LOG(Warning, Uncategorized, "[ConstructFromZEN] Failed to read zen-file: {0}", zenFile); - return {}; - } - - return importWorldMesh(zen); - } - - static void importVobs(bs::HSceneObject sceneRoot, HGameWorld gameWorld, const OriginalZen& zen) - { - for (const ZenLoad::zCVobData& root : zen.vobTree.rootVobs) - { - walkVobTree(sceneRoot, gameWorld, root); - } - } - - static void walkVobTree(bs::HSceneObject bsfParent, HGameWorld gameWorld, - const ZenLoad::zCVobData& zenParent) - { - for (const auto& v : zenParent.childVobs) - { - bs::HSceneObject so = Internals::importSingleVob(v, bsfParent, gameWorld); - - if (so) - { - walkVobTree(so, gameWorld, v); - } - } - } - - /** - * Import a zenfile and load it into datastructures to work with - */ - static bool importZEN(const bs::String& zenFile, OriginalZen& result) - { - ZenLoad::ZenParser zenParser(zenFile.c_str(), gVirtualFileSystem().getFileIndex()); - - if (zenParser.getFileSize() == 0) return false; - - zenParser.readHeader(); - - result.fileName = zenFile; - - zenParser.readWorld(result.vobTree); - - // FIXME: Don't pack the mesh if it was already cached, packing takes a long time... - zenParser.getWorldMesh()->packMesh(result.worldMesh, 0.01f); - - return true; - } - - /** - * Create a bs:f scene object holding the world mesh. - */ - static bs::HSceneObject importWorldMesh(const OriginalZen& zen) - { - bs::String meshFileName = zen.fileName + ".worldmesh"; - - BsZenLib::Res::HMeshWithMaterials mesh; - if (BsZenLib::HasCachedStaticMesh(meshFileName)) - { - mesh = BsZenLib::LoadCachedStaticMesh(meshFileName); - - // This shouldn't be needed, but sometimes the worldmesh in mesh->getMesh() seems to get lost? - if (!mesh.isLoaded()) - { - REGOTH_LOG(Warning, Uncategorized, - "Failed to load cached world mesh of zen {0} - recaching it!", zen.fileName); - mesh = BsZenLib::ImportAndCacheStaticMesh(meshFileName, zen.worldMesh, - gVirtualFileSystem().getFileIndex()); - } - } - else - { - mesh = BsZenLib::ImportAndCacheStaticMesh(meshFileName, zen.worldMesh, - gVirtualFileSystem().getFileIndex()); - } - - bs::HMesh actualMesh = mesh->getMesh(); - - if (!mesh.isLoaded() || !actualMesh.isLoaded()) - { - REGOTH_THROW(InvalidStateException, "Failed to load world mesh for zen " + zen.fileName); - } - - bs::HSceneObject meshSO = bs::SceneObject::create(meshFileName); - bs::HRenderable renderable = meshSO->addComponent(); - renderable->setMesh(mesh->getMesh()); - renderable->setMaterials(mesh->getMaterials()); - - if (!actualMesh->getCachedData()) - { - REGOTH_LOG(Error, Uncategorized, - "Cannot extract world mesh for physics, no mesh data available!"); - } - else - { - bs::HPhysicsMesh physicsMesh = - bs::PhysicsMesh::create(mesh->getMesh()->getCachedData(), bs::PhysicsMeshType::Triangle); - - bs::Path physicsMeshPath = BsZenLib::GothicPathToCachedStaticMesh(meshFileName + ".physics"); - - BsZenLib::AddToResourceManifest(physicsMesh, physicsMeshPath); - bs::gResources().save(physicsMesh, physicsMeshPath, true); - - bs::HMeshCollider collider = meshSO->addComponent(); - collider->setMesh(physicsMesh); - } - - return meshSO; - } - - static void importWaynet(bs::HSceneObject sceneRoot, const OriginalZen& zen) - { - const ZenLoad::zCWayNetData& zenWaynet = zen.vobTree.waynet; - - bs::HSceneObject waynetSO = bs::SceneObject::create("Waynet"); - waynetSO->setParent(sceneRoot); - - HWaynet waynet = waynetSO->addComponent(); - - bs::Vector waypoints; - - for (const ZenLoad::zCWaypointData& zenWP : zenWaynet.waypoints) - { - bs::String wpName = zenWP.wpName.c_str(); - bs::HSceneObject wpSO = bs::SceneObject::create(wpName); - wpSO->setParent(waynetSO); - - bs::Vector3 positionCM = bs::Vector3(zenWP.position.x, zenWP.position.y, zenWP.position.z); - - wpSO->setPosition(positionCM * 0.01f); - wpSO->setForward(bs::Vector3(zenWP.direction.x, zenWP.direction.y, zenWP.direction.z)); - - HWaypoint wp = wpSO->addComponent(); - - waynet->addWaypoint(wp); - waypoints.push_back(wp); - } - - for (const auto& edge : zenWaynet.edges) - { - if (edge.first > waypoints.size() || edge.second > waypoints.size()) - { - REGOTH_THROW(InvalidParametersException, "Waynet Edge Indices out of range!"); - } - - waypoints[edge.first]->addPathTo(waypoints[edge.second]); - waypoints[edge.second]->addPathTo(waypoints[edge.first]); - } - - // FIXME: Initializes internal data structures for findComponents() to work. Should be removed - // once this is fixed upstream. - bs::gSceneManager().setComponentState(bs::ComponentState::Paused); - bs::gSceneManager().setComponentState(bs::ComponentState::Running); - - // Find Freepoints and save them into the waynet - auto freepoints = bs::gSceneManager().findComponents(false); - - for (const auto& fp : freepoints) - { - waynet->addFreepoint(fp); - } - } - -} // namespace REGoth diff --git a/src/world/internals/ConstructFromZEN.hpp b/src/world/internals/ConstructFromZEN.hpp deleted file mode 100644 index 89a7c178..00000000 --- a/src/world/internals/ConstructFromZEN.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/** \file - */ - -#pragma once - -#include - -namespace REGoth -{ - class GameWorld; - using HGameWorld = bs::GameObjectHandle; - - namespace Internals - { - /** - * This function will load the given zenFile from the virtual file system - * and fully convert it into a bs::f scene. - * - * @param gameWorld World to create the objects in. - * @param zenFile Uppercase ZEN-file name, e.g. "OLDWORLD.ZEN". - * - * @return Root of the created scene. - */ - bs::HSceneObject constructFromZEN(HGameWorld gameWorld, const bs::String& zenFile); - - /** - * Will load the given ZEN, but only add its world mesh to the scene. - * - * @param zenFile Uppercase ZEN-File name, e.g. "OLDWORLD.ZEN". - * - * @return Root of the created scene. - */ - bs::HSceneObject loadWorldMeshFromZEN(const bs::String& zenFile); - } // namespace Internals -} // namespace REGoth diff --git a/src/world/internals/ImportSingleVob.cpp b/src/world/internals/ImportSingleVob.cpp deleted file mode 100644 index f270f507..00000000 --- a/src/world/internals/ImportSingleVob.cpp +++ /dev/null @@ -1,359 +0,0 @@ -#include "ImportSingleVob.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace -{ - static bs::Matrix4 convertMatrix(const ZMath::Matrix& m) - { - bs::Matrix4 bs = {m.mv[0], m.mv[1], m.mv[2], m.mv[3], m.mv[4], m.mv[5], m.mv[6], m.mv[7], - m.mv[8], m.mv[9], m.mv[10], m.mv[11], m.mv[12], m.mv[13], m.mv[14], m.mv[15]}; - - return bs.transpose(); - } -} // namespace - -namespace REGoth -{ - static bs::HSceneObject import_zCVob(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, - HGameWorld gameWorld); - static bs::HSceneObject import_zCVobLight(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld); - static bs::HSceneObject import_zCVobStartpoint(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld); - static bs::HSceneObject import_zCVobSpot(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, - HGameWorld gameWorld); - static bs::HSceneObject import_oCItem(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, - HGameWorld gameWorld); - static bs::HSceneObject import_zCVobSound(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld); - static bs::HSceneObject import_zCVobAnimate(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld); - static bs::HSceneObject import_oCMobInter(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld); - static bs::HSceneObject import_oCMobContainer(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld); - static bs::HSceneObject import_oCMobBed(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, - HGameWorld gameWorld); - static bs::HSceneObject import_oCMobDoor(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, - HGameWorld gameWorld); - static void addVisualTo(bs::HSceneObject sceneObject, const bs::String& visualName); - static void addCollisionTo(bs::HSceneObject sceneObject); - static bs::Transform transformFromVob(const ZenLoad::zCVobData& vob); - - bs::HSceneObject Internals::importSingleVob(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld) - { - if (vob.objectClass == "zCVob") - { - return import_zCVob(vob, bsfParent, gameWorld); - } - else if (vob.objectClass == "zCVobLight:zCVob") - { - return import_zCVobLight(vob, bsfParent, gameWorld); - } - else if (vob.objectClass == "zCVobStartpoint:zCVob") - { - return import_zCVobStartpoint(vob, bsfParent, gameWorld); - } - else if (vob.objectClass == "zCVobSpot:zCVob") - { - return import_zCVobSpot(vob, bsfParent, gameWorld); - } - else if (vob.objectClass == "zCVobSound:zCVob") - { - return import_zCVobSound(vob, bsfParent, gameWorld); - } - else if (vob.objectClass == "oCItem:zCVob") - { - return import_oCItem(vob, bsfParent, gameWorld); - } - else if (vob.objectClass == "zCVobAnimate:zCVob") - { - return import_zCVobAnimate(vob, bsfParent, gameWorld); - } - // else if (vob.objectClass == "oCMobInter:oCMOB:zCVob") - // { - // return import_oCMobInter(vob, bsfParent, gameWorld); - // } - // else if (vob.objectClass == "oCMobContainer:oCMobInter:oCMOB:zCVob") - // { - // return import_oCMobContainer(vob, bsfParent, gameWorld); - // } - // else if (vob.objectClass == "oCMobBed:oCMobInter:oCMOB:zCVob") - // { - // return import_oCMobBed(vob, bsfParent, gameWorld); - // } - // else if (vob.objectClass == "oCMobDoor:oCMobInter:oCMOB:zCVob") - // { - // return import_oCMobDoor(vob, bsfParent, gameWorld); - // } - else - { - REGOTH_LOG(Warning, Uncategorized, "[ImportSingleVob] Unsupported vob class: {0}", - bs::String(vob.objectClass.c_str())); - - return {}; - } - } - - static bs::Transform transformFromVob(const ZenLoad::zCVobData& vob) - { - bs::Transform result = bs::Transform::IDENTITY; - - bs::Matrix4 worldMatrix = convertMatrix(vob.worldMatrix); - bs::Quaternion rotation; - rotation.fromRotationMatrix(worldMatrix.get3x3()); - - bs::Vector3 positionCM = bs::Vector3(vob.position.x, vob.position.y, vob.position.z); - - float centimetersToMeters = 0.01f; - - result.setPosition(positionCM * centimetersToMeters); - result.setRotation(rotation); - - return result; - } - - /** - * The zCVob is the most basic object class we will encounter. It is - * the base class of most other objects, so it makes sense to handle - * position, rotation and the visual here as these are used by most vobs. - * - * @note Not all object types will call `import_zCVob`! - */ - static bs::HSceneObject import_zCVob(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, - HGameWorld gameWorld) - { - bs::HSceneObject so = bs::SceneObject::create(vob.vobName.c_str()); - - so->setParent(gameWorld->SO()); - - bs::Transform transform = transformFromVob(vob); - - so->setPosition(transform.pos()); - so->setRotation(transform.rot()); - - if (!vob.visual.empty()) - { - addVisualTo(so, vob.visual.c_str()); - } - - // cdDyn seems to be the general "this is supposed to collide with stuff"-flag. - if (vob.cdDyn) - { - addCollisionTo(so); - } - - return so; - } - - /** - * Lights can be pointlights or spotlights, although spotlights are not - * used within the original game as it seems. - */ - static bs::HSceneObject import_zCVobLight(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld) - { - bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); - - // FIXME: Put lights back in - return so; -#if 0 - bs::HLight light = so->addComponent(); - - auto lightColor = bs::Color::fromRGBA(vob.zCVobLight.color); - - light->setType(bs::LightType::Radial); - light->setUseAutoAttenuation(false); - light->setAttenuationRadius(vob.zCVobLight.range); - light->setColor(lightColor); - - return so; -#endif - } - - /** - * The startpoint of the player in the current world. There should be only one. - */ - static bs::HSceneObject import_zCVobStartpoint(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld) - { - bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); - - // Startpoint is found by name of the scene object - REGOTH_LOG(Info, Uncategorized, "[ImportSingleVob] Found startpoint: {0}", so->getName()); - - // A startpoint has an arbitrary names, which makes it hard to find it at a later point. - // Thus, rename it to a known constant. As there should always only be one startpoint in - // a world, this should cause no conflicts. - so->setName(WORLD_STARTPOINT); - - return so; - } - - /** - * Spots like free-points. - */ - static bs::HSceneObject import_zCVobSpot(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, - HGameWorld gameWorld) - { - bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); - - so->addComponent(); - - return so; - } - - static bs::HSceneObject import_oCItem(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, - HGameWorld gameWorld) - { - if (vob.oCItem.instanceName.empty()) - { - REGOTH_LOG(Warning, Uncategorized, "[ImportSingleVob] Item with empty script instance: {0}", - bs::String(vob.vobName.c_str())); - return {}; - } - - bs::Transform transform = transformFromVob(vob); - - // Items are interactable, they need to be registered within the GameWorld - HItem item = gameWorld->insertItem(vob.oCItem.instanceName.c_str(), transform); - - item->SO()->setParent(bsfParent); - - return item->SO(); - } - - static bs::HSceneObject import_zCVobSound(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld) - { - bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); - - // TODO: Implement - - return so; - } - - static bs::HSceneObject import_zCVobAnimate(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld) - { - bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); - - // TODO: Implement - - return so; - } - - static bs::HSceneObject import_oCMobInter(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld) - { - bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); - - // TODO: Implement - - return so; - } - - static bs::HSceneObject import_oCMobContainer(const ZenLoad::zCVobData& vob, - bs::HSceneObject bsfParent, HGameWorld gameWorld) - { - bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); - - // TODO: Implement - - return so; - } - - static bs::HSceneObject import_oCMobBed(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, - HGameWorld gameWorld) - { - bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); - - // TODO: Implement - - return so; - } - - static bs::HSceneObject import_oCMobDoor(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, - HGameWorld gameWorld) - { - bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); - - // TODO: Implement - - return so; - } - - /** - * Adds the given visual to the scene object. If that's not possible - * nothing will be added. - */ - static void addVisualTo(bs::HSceneObject sceneObject, const bs::String& visualName) - { - bool hasAdded = Visual::addToSceneObject(sceneObject, visualName); - - if (!hasAdded) - { - REGOTH_LOG(Warning, Uncategorized, "[ImportSingleVob] Unsupported visual: {0}", visualName); - } - } - - /** - * Adds a triangle physics mesh to the given scene object. Only works if the - * scene object has a renderable with a mesh set. The mesh must also have - * CPU-caching enabled, so we get access to the mesh data. - */ - static void addCollisionTo(bs::HSceneObject sceneObject) - { - bs::HRenderable renderable = sceneObject->getComponent(); - - if (!renderable) return; - - auto mesh = renderable->getMesh(); - - if (!mesh) return; - - auto meshData = mesh->getCachedData(); - - if (!meshData) return; - - bs::Path physicsMeshPath = BsZenLib::GothicPathToCachedStaticMesh(mesh->getName() + ".physics"); - - bs::HPhysicsMesh physicsMesh; - if (bs::FileSystem::exists(physicsMeshPath)) - { - physicsMesh = bs::gResources().load(physicsMeshPath); - } - else - { - REGOTH_LOG(Info, Uncategorized, "[ImportSingleVob] Caching physics mesh for {0}", - mesh->getName()); - - physicsMesh = bs::PhysicsMesh::create(meshData, bs::PhysicsMeshType::Triangle); - - BsZenLib::AddToResourceManifest(physicsMesh, physicsMeshPath); - bs::gResources().save(physicsMesh, physicsMeshPath, true); - } - - if (!physicsMesh) return; - - bs::HMeshCollider collider = sceneObject->addComponent(); - collider->setMesh(physicsMesh); - } - -} // namespace REGoth diff --git a/src/world/internals/ImportSingleVob.hpp b/src/world/internals/ImportSingleVob.hpp deleted file mode 100644 index 2ebf045b..00000000 --- a/src/world/internals/ImportSingleVob.hpp +++ /dev/null @@ -1,32 +0,0 @@ -/** \file - */ - -#pragma once - -#include - -namespace ZenLoad -{ - struct zCVobData; -} - -namespace REGoth -{ - class GameWorld; - using HGameWorld = bs::GameObjectHandle; - - namespace Internals - { - /** - * Imports a single vob and creates a bs:f object as similar as possible. - * - * @param vob Information from importing the zen-file. - * @param bsfParent Parent game object. - * @param gameWorld World to create the object in. - * - * @return Scene object modeled after the vob - */ - bs::HSceneObject importSingleVob(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, - HGameWorld gameWorld); - } // namespace Worlds -} // namespace REGoth diff --git a/src/world/internals/LoadCachedZEN.cpp b/src/world/internals/ZenCache.cpp similarity index 69% rename from src/world/internals/LoadCachedZEN.cpp rename to src/world/internals/ZenCache.cpp index 89e45eb9..9b30bc14 100644 --- a/src/world/internals/LoadCachedZEN.cpp +++ b/src/world/internals/ZenCache.cpp @@ -1,4 +1,4 @@ -#include "LoadCachedZEN.hpp" +#include "ZenCache.hpp" #include #include #include @@ -7,9 +7,7 @@ namespace REGoth { - namespace World - { - bs::HSceneObject loadCachedZEN(const bs::String& zenFile) + bs::HSceneObject ZenCache::loadCachedZEN(const bs::String& zenFile) { if (!hasCachedZEN(zenFile)) { @@ -23,23 +21,23 @@ namespace REGoth return prefab->instantiate(); } - void saveCacheForZEN(bs::HSceneObject root, const bs::String& zenFile) + void ZenCache::saveCacheForZEN(bs::HSceneObject root, const bs::String& zenFile) { bs::HPrefab cached = bs::Prefab::create(root); enum { - Overwrite = true, - KeepExisting = false, + overwrite = true, + keepExisting = false, }; bs::Path path = BsZenLib::GothicPathToCachedWorld(zenFile); - bs::gResources().save(cached, path, Overwrite); + bs::gResources().save(cached, path, overwrite); } - bool hasCachedZEN(const bs::String& zenFile) + bool ZenCache::hasCachedZEN(const bs::String& zenFile) { return bs::FileSystem::exists(BsZenLib::GothicPathToCachedWorld(zenFile)); } - } // namespace World + } // namespace REGoth diff --git a/src/world/internals/LoadCachedZEN.hpp b/src/world/internals/ZenCache.hpp similarity index 94% rename from src/world/internals/LoadCachedZEN.hpp rename to src/world/internals/ZenCache.hpp index 053b2a17..1dafe2b2 100644 --- a/src/world/internals/LoadCachedZEN.hpp +++ b/src/world/internals/ZenCache.hpp @@ -3,8 +3,13 @@ namespace REGoth { - namespace World + + class ZenCache { + public: + ZenCache() = default; + ~ZenCache() = default; + /** * Loads the cached ZEN with the given name. * @@ -46,5 +51,6 @@ namespace REGoth * @See loadCachedZEN() to load it. */ bool hasCachedZEN(const bs::String& zenFile); - } + }; + } // namespace REGoth diff --git a/src/world/internals/ZenImporter.cpp b/src/world/internals/ZenImporter.cpp new file mode 100644 index 00000000..fb79e984 --- /dev/null +++ b/src/world/internals/ZenImporter.cpp @@ -0,0 +1,546 @@ +#include "ZenImporter.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace REGoth +{ + ZenImporter::ZenImporter() : mVobImporter(new VobImporter) + {} + + ZenImporter::~ZenImporter(){ + delete mVobImporter; + } + + bs::HSceneObject ZenImporter::constructFromZEN(HGameWorld gameWorld, const bs::String& zenFile) + { + OriginalZen zen; + + bool hasLoadedZEN = importZEN(zenFile, zen); + + if (!hasLoadedZEN) + { + REGOTH_LOG(Warning, Uncategorized, "[ConstructFromZEN] Failed to read zen-file: {0}", zenFile); + return {}; + } + + bs::HSceneObject worldMesh = importWorldMesh(zen); + worldMesh->setParent(gameWorld->SO()); + + importVobs(gameWorld->SO(), gameWorld, zen); + importWaynet(gameWorld->SO(), zen); + + return worldMesh; + } + + bs::HSceneObject ZenImporter::loadWorldMeshFromZEN(const bs::String& zenFile) + { + OriginalZen zen; + + bool hasLoadedZEN = importZEN(zenFile, zen); + + if (!hasLoadedZEN) + { + REGOTH_LOG(Warning, Uncategorized, "[ConstructFromZEN] Failed to read zen-file: {0}", zenFile); + return {}; + } + + return importWorldMesh(zen); + } + + void ZenImporter::importVobs(bs::HSceneObject sceneRoot, HGameWorld gameWorld, const OriginalZen& zen) + { + for (const ZenLoad::zCVobData& root : zen.vobTree.rootVobs) + { + walkVobTree(sceneRoot, gameWorld, root); + } + } + + void ZenImporter::walkVobTree(bs::HSceneObject bsfParent, HGameWorld gameWorld, + const ZenLoad::zCVobData& zenParent) + { + for (const auto& v : zenParent.childVobs) + { + bs::HSceneObject so = mVobImporter->importSingleVob(*this, v, bsfParent, gameWorld); + + if (so) + { + walkVobTree(so, gameWorld, v); + } + } + } + + /** + * Import a zenfile and load it into datastructures to work with + */ + bool ZenImporter::importZEN(const bs::String& zenFile, OriginalZen& result) + { + ZenLoad::ZenParser zenParser(zenFile.c_str(), gVirtualFileSystem().getFileIndex()); + + if (zenParser.getFileSize() == 0) return false; + + zenParser.readHeader(); + + result.fileName = zenFile; + + zenParser.readWorld(result.vobTree); + + // FIXME: Don't pack the mesh if it was already cached, packing takes a long time... + zenParser.getWorldMesh()->packMesh(result.worldMesh, 0.01f); + + return true; + } + + /** + * Create a bs:f scene object holding the world mesh. + */ + bs::HSceneObject ZenImporter::importWorldMesh(const OriginalZen& zen) + { + bs::String meshFileName = zen.fileName + ".worldmesh"; + + BsZenLib::Res::HMeshWithMaterials mesh; + if (BsZenLib::HasCachedStaticMesh(meshFileName)) + { + mesh = BsZenLib::LoadCachedStaticMesh(meshFileName); + + // This shouldn't be needed, but sometimes the worldmesh in mesh->getMesh() seems to get lost? + if (!mesh.isLoaded()) + { + REGOTH_LOG(Warning, Uncategorized, + "Failed to load cached world mesh of zen {0} - recaching it!", zen.fileName); + mesh = BsZenLib::ImportAndCacheStaticMesh(meshFileName, zen.worldMesh, + gVirtualFileSystem().getFileIndex()); + } + } + else + { + mesh = BsZenLib::ImportAndCacheStaticMesh(meshFileName, zen.worldMesh, + gVirtualFileSystem().getFileIndex()); + } + + bs::HMesh actualMesh = mesh->getMesh(); + + if (!mesh.isLoaded() || !actualMesh.isLoaded()) + { + REGOTH_THROW(InvalidStateException, "Failed to load world mesh for zen " + zen.fileName); + } + + bs::HSceneObject meshSO = bs::SceneObject::create(meshFileName); + bs::HRenderable renderable = meshSO->addComponent(); + renderable->setMesh(mesh->getMesh()); + renderable->setMaterials(mesh->getMaterials()); + + if (!actualMesh->getCachedData()) + { + REGOTH_LOG(Error, Uncategorized, + "Cannot extract world mesh for physics, no mesh data available!"); + } + else + { + bs::HPhysicsMesh physicsMesh = + bs::PhysicsMesh::create(mesh->getMesh()->getCachedData(), bs::PhysicsMeshType::Triangle); + + bs::Path physicsMeshPath = BsZenLib::GothicPathToCachedStaticMesh(meshFileName + ".physics"); + + BsZenLib::AddToResourceManifest(physicsMesh, physicsMeshPath); + bs::gResources().save(physicsMesh, physicsMeshPath, true); + + bs::HMeshCollider collider = meshSO->addComponent(); + collider->setMesh(physicsMesh); + } + + return meshSO; + } + + void ZenImporter::importWaynet(bs::HSceneObject sceneRoot, const OriginalZen& zen) + { + const ZenLoad::zCWayNetData& zenWaynet = zen.vobTree.waynet; + + bs::HSceneObject waynetSO = bs::SceneObject::create("Waynet"); + waynetSO->setParent(sceneRoot); + + HWaynet waynet = waynetSO->addComponent(); + + bs::Vector waypoints; + + for (const ZenLoad::zCWaypointData& zenWP : zenWaynet.waypoints) + { + bs::String wpName = zenWP.wpName.c_str(); + bs::HSceneObject wpSO = bs::SceneObject::create(wpName); + wpSO->setParent(waynetSO); + + bs::Vector3 positionCM = bs::Vector3(zenWP.position.x, zenWP.position.y, zenWP.position.z); + + wpSO->setPosition(positionCM * 0.01f); + wpSO->setForward(bs::Vector3(zenWP.direction.x, zenWP.direction.y, zenWP.direction.z)); + + HWaypoint wp = wpSO->addComponent(); + + waynet->addWaypoint(wp); + waypoints.push_back(wp); + } + + for (const auto& edge : zenWaynet.edges) + { + if (edge.first > waypoints.size() || edge.second > waypoints.size()) + { + REGOTH_THROW(InvalidParametersException, "Waynet Edge Indices out of range!"); + } + + waypoints[edge.first]->addPathTo(waypoints[edge.second]); + waypoints[edge.second]->addPathTo(waypoints[edge.first]); + } + + // FIXME: Initializes internal data structures for findComponents() to work. Should be removed + // once this is fixed upstream. + bs::gSceneManager().setComponentState(bs::ComponentState::Paused); + bs::gSceneManager().setComponentState(bs::ComponentState::Running); + + // Find Freepoints and save them into the waynet + auto freepoints = bs::gSceneManager().findComponents(false); + + for (const auto& fp : freepoints) + { + waynet->addFreepoint(fp); + } + } + +} // namespace REGoth + +namespace REGoth +{ + + bs::HSceneObject ZenImporter::VobImporter::importSingleVob(ZenImporter& zenImporter, const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld) + { + if (vob.objectClass == "zCVob") + { + return import_zCVob(vob, bsfParent, gameWorld); + } + else if (vob.objectClass == "zCVobLight:zCVob") + { + return import_zCVobLight(vob, bsfParent, gameWorld); + } + else if (vob.objectClass == "zCVobStartpoint:zCVob") + { + return import_zCVobStartpoint(zenImporter, vob, bsfParent, gameWorld); + } + else if (vob.objectClass == "zCVobSpot:zCVob") + { + return import_zCVobSpot(vob, bsfParent, gameWorld); + } + else if (vob.objectClass == "zCVobSound:zCVob") + { + return import_zCVobSound(vob, bsfParent, gameWorld); + } + else if (vob.objectClass == "oCItem:zCVob") + { + return import_oCItem(vob, bsfParent, gameWorld); + } + else if (vob.objectClass == "zCVobAnimate:zCVob") + { + return import_zCVobAnimate(vob, bsfParent, gameWorld); + } + // else if (vob.objectClass == "oCMobInter:oCMOB:zCVob") + // { + // return import_oCMobInter(vob, bsfParent, gameWorld); + // } + // else if (vob.objectClass == "oCMobContainer:oCMobInter:oCMOB:zCVob") + // { + // return import_oCMobContainer(vob, bsfParent, gameWorld); + // } + // else if (vob.objectClass == "oCMobBed:oCMobInter:oCMOB:zCVob") + // { + // return import_oCMobBed(vob, bsfParent, gameWorld); + // } + // else if (vob.objectClass == "oCMobDoor:oCMobInter:oCMOB:zCVob") + // { + // return import_oCMobDoor(vob, bsfParent, gameWorld); + // } + else + { + REGOTH_LOG(Warning, Uncategorized, "[ImportSingleVob] Unsupported vob class: {0}", + bs::String(vob.objectClass.c_str())); + + return {}; + } + } + + bs::Transform ZenImporter::VobImporter::transformFromVob(const ZenLoad::zCVobData& vob) + { + bs::Transform result = bs::Transform::IDENTITY; + + bs::Matrix4 worldMatrix = convertMatrix(vob.worldMatrix); + bs::Quaternion rotation; + rotation.fromRotationMatrix(worldMatrix.get3x3()); + + bs::Vector3 positionCM = bs::Vector3(vob.position.x, vob.position.y, vob.position.z); + + float centimetersToMeters = 0.01f; + + result.setPosition(positionCM * centimetersToMeters); + result.setRotation(rotation); + + return result; + } + + /** + * The zCVob is the most basic object class we will encounter. It is + * the base class of most other objects, so it makes sense to handle + * position, rotation and the visual here as these are used by most vobs. + * + * @note Not all object types will call `import_zCVob`! + */ + bs::HSceneObject ZenImporter::VobImporter::import_zCVob(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, + HGameWorld gameWorld) + { + bs::HSceneObject so = bs::SceneObject::create(vob.vobName.c_str()); + + so->setParent(gameWorld->SO()); + + bs::Transform transform = transformFromVob(vob); + + so->setPosition(transform.pos()); + so->setRotation(transform.rot()); + + if (!vob.visual.empty()) + { + addVisualTo(so, vob.visual.c_str()); + } + + // cdDyn seems to be the general "this is supposed to collide with stuff"-flag. + if (vob.cdDyn) + { + addCollisionTo(so); + } + + return so; + } + + /** + * Lights can be pointlights or spotlights, although spotlights are not + * used within the original game as it seems. + */ + bs::HSceneObject ZenImporter::VobImporter::import_zCVobLight(const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld) + { + bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); + + // FIXME: Put lights back in + return so; +#if 0 + bs::HLight light = so->addComponent(); + + auto lightColor = bs::Color::fromRGBA(vob.zCVobLight.color); + + light->setType(bs::LightType::Radial); + light->setUseAutoAttenuation(false); + light->setAttenuationRadius(vob.zCVobLight.range); + light->setColor(lightColor); + + return so; +#endif + } + + /** + * The startpoint of the player in the current world. There should be only one. + */ + bs::HSceneObject ZenImporter::VobImporter::import_zCVobStartpoint(ZenImporter& zenImporter, const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld) + { + bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); + //FIXME: Rotate by 180Degrees?; Why do I spawn in the Oldmine looking the wrong way? + /* + bs::Quaternion rotate180Y = bs::Quaternion(bs::Radian(0), + bs::Radian(bs::Degree(180)), + bs::Radian(0)); + so->rotate(rotate180Y); +*/ + + // Startpoint is found by name of the scene object + REGOTH_LOG(Info, Uncategorized, "[ImportSingleVob] Found startpoint: {0}", so->getName()); + + zenImporter.mStartpoint = so; + + return so; + } + + /** + * Spots like free-points. + */ + bs::HSceneObject ZenImporter::VobImporter::import_zCVobSpot(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, + HGameWorld gameWorld) + { + bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); + + so->addComponent(); + + return so; + } + + bs::HSceneObject ZenImporter::VobImporter::import_oCItem(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, + HGameWorld gameWorld) + { + if (vob.oCItem.instanceName.empty()) + { + REGOTH_LOG(Warning, Uncategorized, "[ImportSingleVob] Item with empty script instance: {0}", + bs::String(vob.vobName.c_str())); + return {}; + } + + bs::Transform transform = transformFromVob(vob); + + // Items are interactable, they need to be registered within the GameWorld + HItem item = gameWorld->insertItem(vob.oCItem.instanceName.c_str(), transform); + + item->SO()->setParent(bsfParent); + + return item->SO(); + } + + bs::HSceneObject ZenImporter::VobImporter::import_zCVobSound(const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld) + { + bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); + + // TODO: Implement + + return so; + } + + bs::HSceneObject ZenImporter::VobImporter::import_zCVobAnimate(const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld) + { + bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); + + // TODO: Implement + + return so; + } + + bs::HSceneObject ZenImporter::VobImporter::import_oCMobInter(const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld) + { + bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); + + // TODO: Implement + + return so; + } + + bs::HSceneObject ZenImporter::VobImporter::import_oCMobContainer(const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld) + { + bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); + + // TODO: Implement + + return so; + } + + bs::HSceneObject ZenImporter::VobImporter::import_oCMobBed(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, + HGameWorld gameWorld) + { + bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); + + // TODO: Implement + + return so; + } + + bs::HSceneObject ZenImporter::VobImporter::import_oCMobDoor(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, + HGameWorld gameWorld) + { + bs::HSceneObject so = import_zCVob(vob, bsfParent, gameWorld); + + // TODO: Implement + + return so; + } + + /** + * Adds the given visual to the scene object. If that's not possible + * nothing will be added. + */ + void ZenImporter::VobImporter::addVisualTo(bs::HSceneObject sceneObject, const bs::String& visualName) + { + bool hasAdded = Visual::addToSceneObject(sceneObject, visualName); + + if (!hasAdded) + { + REGOTH_LOG(Warning, Uncategorized, "[ImportSingleVob] Unsupported visual: {0}", visualName); + } + } + + /** + * Adds a triangle physics mesh to the given scene object. Only works if the + * scene object has a renderable with a mesh set. The mesh must also have + * CPU-caching enabled, so we get access to the mesh data. + */ + void ZenImporter::VobImporter::addCollisionTo(bs::HSceneObject sceneObject) + { + bs::HRenderable renderable = sceneObject->getComponent(); + + if (!renderable) return; + + auto mesh = renderable->getMesh(); + + if (!mesh) return; + + auto meshData = mesh->getCachedData(); + + if (!meshData) return; + + bs::Path physicsMeshPath = BsZenLib::GothicPathToCachedStaticMesh(mesh->getName() + ".physics"); + + bs::HPhysicsMesh physicsMesh; + if (bs::FileSystem::exists(physicsMeshPath)) + { + physicsMesh = bs::gResources().load(physicsMeshPath); + } + else + { + REGOTH_LOG(Info, Uncategorized, "[ImportSingleVob] Caching physics mesh for {0}", + mesh->getName()); + + physicsMesh = bs::PhysicsMesh::create(meshData, bs::PhysicsMeshType::Triangle); + + BsZenLib::AddToResourceManifest(physicsMesh, physicsMeshPath); + bs::gResources().save(physicsMesh, physicsMeshPath, true); + } + + if (!physicsMesh) return; + + bs::HMeshCollider collider = sceneObject->addComponent(); + collider->setMesh(physicsMesh); + } + + bs::Matrix4 ZenImporter::VobImporter::convertMatrix(const ZMath::Matrix& m) + { + bs::Matrix4 bs = {m.mv[0], m.mv[1], m.mv[2], m.mv[3], m.mv[4], m.mv[5], m.mv[6], m.mv[7], + m.mv[8], m.mv[9], m.mv[10], m.mv[11], m.mv[12], m.mv[13], m.mv[14], m.mv[15]}; + + return bs.transpose(); + } + +} // namespace REGoth diff --git a/src/world/internals/ZenImporter.hpp b/src/world/internals/ZenImporter.hpp new file mode 100644 index 00000000..e01a2640 --- /dev/null +++ b/src/world/internals/ZenImporter.hpp @@ -0,0 +1,114 @@ +/** \file + */ + +#pragma once + +#include +#include +#include + +namespace REGoth +{ + class GameWorld; + using HGameWorld = bs::GameObjectHandle; + + class ZenImporter + { + public: + ZenImporter(); + ~ZenImporter(); + + /** + * This function will load the given zenFile from the virtual file system + * and fully convert it into a bs::f scene. + * + * @param gameWorld World to create the objects in. + * @param zenFile Uppercase ZEN-file name, e.g. "OLDWORLD.ZEN". + * + * @return Root of the created scene. + */ + bs::HSceneObject constructFromZEN(HGameWorld gameWorld, const bs::String& zenFile); + + /** + * Will load the given ZEN, but only add its world mesh to the scene. + * + * @param zenFile Uppercase ZEN-File name, e.g. "OLDWORLD.ZEN". + * + * @return Root of the created scene. + */ + bs::HSceneObject loadWorldMeshFromZEN(const bs::String& zenFile); + + bs::HSceneObject getStartpoint() + { + return mStartpoint; + } + + private: + bs::HSceneObject mStartpoint; + class VobImporter; + VobImporter* mVobImporter; + + struct OriginalZen + { + bs::String fileName; + ZenLoad::oCWorldData vobTree; + ZenLoad::PackedMesh worldMesh; + }; + + bool importZEN(const bs::String& zenFile, OriginalZen& result); + bs::HSceneObject importWorldMesh(const OriginalZen& zen); + void importVobs(bs::HSceneObject sceneRoot, HGameWorld gameWorld, const OriginalZen& zen); + void importWaynet(bs::HSceneObject sceneRoot, const OriginalZen& zen); + void walkVobTree(bs::HSceneObject bsfParent, HGameWorld gameWorld, + const ZenLoad::zCVobData& zenParent); + }; + + class ZenImporter::VobImporter + { + public: + VobImporter() = default; + ~VobImporter() = default; + + /** + * Imports a single vob and creates a bs:f object as similar as possible. + * + * @param vob Information from importing the zen-file. + * @param bsfParent Parent game object. + * @param gameWorld World to create the object in. + * + * @return Scene object modeled after the vob + */ + bs::HSceneObject importSingleVob(ZenImporter& zenImporter, const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, + HGameWorld gameWorld); + + private: + bs::Matrix4 convertMatrix(const ZMath::Matrix& m); + bs::HSceneObject import_zCVob(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, + HGameWorld gameWorld); + bs::HSceneObject import_zCVobLight(const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld); + bs::HSceneObject import_zCVobStartpoint(ZenImporter& zenImporter, const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld); + bs::HSceneObject import_zCVobSpot(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, + HGameWorld gameWorld); + bs::HSceneObject import_oCItem(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, + HGameWorld gameWorld); + bs::HSceneObject import_zCVobSound(const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld); + bs::HSceneObject import_zCVobAnimate(const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld); + bs::HSceneObject import_oCMobInter(const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld); + bs::HSceneObject import_oCMobContainer(const ZenLoad::zCVobData& vob, + bs::HSceneObject bsfParent, HGameWorld gameWorld); + bs::HSceneObject import_oCMobBed(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, + HGameWorld gameWorld); + bs::HSceneObject import_oCMobDoor(const ZenLoad::zCVobData& vob, bs::HSceneObject bsfParent, + HGameWorld gameWorld); + void addVisualTo(bs::HSceneObject sceneObject, const bs::String& visualName); + void addCollisionTo(bs::HSceneObject sceneObject); + bs::Transform transformFromVob(const ZenLoad::zCVobData& vob); + + }; + +} // namespace REGoth