From d91c7d72215aa05ac52748647d19955e35dee9a0 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Tue, 18 Dec 2018 06:24:28 -0500 Subject: [PATCH] Add support for multiple worlds in a single cinematic --- CMakeLists.txt | 8 +- data/empty.world | 11 ++ data/test.cim | 2 +- include/animationsystem.h | 20 +++ include/assetmanager.h | 22 +++ include/cinematic.h | 11 +- include/world.h | 4 + include/worldmanager.h | 23 +++ src/animationsystem.cpp | 190 +++++++++++++++++++++++++ src/assetmanager.cpp | 61 ++++++++ src/main.cpp | 286 +++----------------------------------- src/worldmanager.cpp | 54 +++++++ 12 files changed, 425 insertions(+), 267 deletions(-) create mode 100644 data/empty.world create mode 100644 include/animationsystem.h create mode 100644 include/assetmanager.h create mode 100644 include/worldmanager.h create mode 100644 src/animationsystem.cpp create mode 100644 src/assetmanager.cpp create mode 100644 src/worldmanager.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a946266..fd55ad7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,10 @@ set(GRAPH_SRC src/skypass.cpp src/shadowpass.cpp src/config.cpp - src/stringutils.cpp) + src/stringutils.cpp + src/animationsystem.cpp + src/worldmanager.cpp + src/assetmanager.cpp) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(GRAPH_SRC @@ -109,4 +112,5 @@ add_data(Graph data/tile.jpg data/graphics_presets.cfg data/test.world - data/test.material) + data/test.material + data/empty.world) diff --git a/data/empty.world b/data/empty.world new file mode 100644 index 0000000..8a8f144 --- /dev/null +++ b/data/empty.world @@ -0,0 +1,11 @@ +{ + "meshes": [ + + ], + "lights": [ + { + "position": "66,56,25", + "type": 1 + } + ] +} diff --git a/data/test.cim b/data/test.cim index 8b625d4..bc845b2 100644 --- a/data/test.cim +++ b/data/test.cim @@ -70,7 +70,7 @@ ] }, { - "world": "test.world", + "world": "empty.world", "start": 5, "end": 10, "meshes": [ diff --git a/include/animationsystem.h b/include/animationsystem.h new file mode 100644 index 0000000..1017a0f --- /dev/null +++ b/include/animationsystem.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "cinematic.h" + +class AnimationSystem { +public: + Cinematic* loadCinematic(const std::string& path); + + void update(Cinematic* cinematic, float deltaTime); + +private: + void preloadShot(Shot* shot); + void loadShot(Shot* shot); + void unloadShot(Shot* shot); + + float animationTime = 0.0f; + int currentShot = 0; +}; diff --git a/include/assetmanager.h b/include/assetmanager.h new file mode 100644 index 0000000..fa57a20 --- /dev/null +++ b/include/assetmanager.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +struct Mesh; +struct Material; +class Renderer; + +class AssetManager { +public: + void setRenderer(Renderer* r) { + renderer = r; + } + + Mesh* loadMesh(const std::string& path); + Material* loadMaterial(const std::string& path); + +private: + Renderer* renderer = nullptr; +}; + +inline AssetManager assetManager; diff --git a/include/cinematic.h b/include/cinematic.h index f112884..b4f19f4 100644 --- a/include/cinematic.h +++ b/include/cinematic.h @@ -19,15 +19,24 @@ enum class AnimationProperty { struct Animation { Mesh* target = nullptr; + std::string targetName; AnimationProperty property = AnimationProperty::Position; std::vector keyframes; }; +struct CinematicMesh { + std::string name, modelPath, materialPath; +}; + struct Shot { int start = 0, end = 0; + bool loaded = false; + + std::string world; - std::vector meshes; + std::vector meshes; + std::vector loadedMeshes; std::vector animations; }; diff --git a/include/world.h b/include/world.h index 15af9ea..ccbe316 100644 --- a/include/world.h +++ b/include/world.h @@ -2,6 +2,8 @@ #include +#include "camera.h" + class Mesh; class Light; @@ -9,4 +11,6 @@ class World { public: std::vector meshes; std::vector lights; + + Camera camera; }; diff --git a/include/worldmanager.h b/include/worldmanager.h new file mode 100644 index 0000000..47c3112 --- /dev/null +++ b/include/worldmanager.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +struct World; + +class WorldManager { +public: + void loadWorld(const std::string& path); + + void switchWorld(const std::string& path); + + World* getCurrentWorld() { + return currentWorld; + } + +private: + std::map loadedWorlds; + World* currentWorld = nullptr; +}; + +inline WorldManager worldManager; diff --git a/src/animationsystem.cpp b/src/animationsystem.cpp new file mode 100644 index 0000000..b31ebba --- /dev/null +++ b/src/animationsystem.cpp @@ -0,0 +1,190 @@ +#include "animationsystem.h" + +#include +#include +#include +#include + +#include "mesh.h" +#include "stringutils.h" +#include "worldmanager.h" +#include "world.h" +#include "assetmanager.h" + +Cinematic* AnimationSystem::loadCinematic(const std::string& path) { + std::ifstream file("data/" + path); + if(!file) + return nullptr; + + nlohmann::json json; + file >> json; + + auto cinematic = new Cinematic(); + for(auto shotObject : json["shots"]) { + Shot* shot = new Shot(); + shot->start = shotObject["start"]; + shot->end = shotObject["end"]; + shot->world = shotObject["world"]; + + for(auto meshObject : shotObject["meshes"]) { + CinematicMesh mesh; + mesh.name = meshObject["name"]; + mesh.modelPath = meshObject["path"]; + mesh.materialPath = meshObject["material"]; + + shot->meshes.push_back(mesh); + } + + for(auto animationObject : shotObject["animations"]) { + auto animation = new Animation(); + animation->targetName = animationObject["target"]; + + const auto property = animationObject["property"]; + if(property == "position") + animation->property = AnimationProperty::Position; + else if(property == "target") + animation->property = AnimationProperty::Target; + else if(property == "fov") + animation->property = AnimationProperty::FoV; + + for(auto keyframeObject : animationObject["keyframes"]) { + Keyframe keyframe; + keyframe.time = keyframeObject["time"]; + + if(animation->property == AnimationProperty::Position || animation->property == AnimationProperty::Target) { + auto tokens = tokenize(keyframeObject["value"]); + + keyframe.valueVec3[0] = atof(tokens[0].c_str()); + keyframe.valueVec3[1] = atof(tokens[1].c_str()); + keyframe.valueVec3[2] = atof(tokens[2].c_str()); + + animation->keyframes.push_back(keyframe); + } else if(animation->property == AnimationProperty::FoV) { + keyframe.valueInt = keyframeObject["value"]; + + animation->keyframes.push_back(keyframe); + } + } + + shot->animations.push_back(animation); + } + + cinematic->shots.push_back(shot); + } + + preloadShot(cinematic->shots[0]); + loadShot(cinematic->shots[0]); + + return cinematic; +} + +void AnimationSystem::update(Cinematic* cinematic, float deltaTime) { + float endTime = 0.0f; + for(uint32_t i = 0; i < cinematic->shots.size(); i++) { + // load the shot before it happens + if((i + 1) <= cinematic->shots.size() && i == (currentShot + 1) && !cinematic->shots[currentShot + 1]->loaded) { + preloadShot(cinematic->shots[currentShot + 1]); + cinematic->shots[currentShot + 1]->loaded = true; + } + + if(i > currentShot && animationTime > cinematic->shots[i]->start) { + unloadShot(cinematic->shots[currentShot]); + currentShot = i; + loadShot(cinematic->shots[i]); + } + } + + for(auto animation : cinematic->shots[currentShot]->animations) { + unsigned int frameIndex = 0; + for(size_t i = 0; i < animation->keyframes.size(); i++) { + if(animationTime < animation->keyframes[i + 1].time) { + frameIndex = i; + break; + } + } + + const auto currentFrame = animation->keyframes[frameIndex]; + const auto nextFrame = animation->keyframes[(frameIndex + 1) % animation->keyframes.size()]; + + const float delta = (animationTime - currentFrame.time) / (nextFrame.time - currentFrame.time); + + switch(animation->property) { + case AnimationProperty::Position: + { + auto pos = currentFrame.valueVec3 + delta * (nextFrame.valueVec3 - currentFrame.valueVec3); + + if(animation->target == nullptr) + worldManager.getCurrentWorld()->camera.position = pos; + else { + animation->target->position = pos; + } + } + break; + case AnimationProperty::Target: + { + auto pos = currentFrame.valueVec3 + delta * (nextFrame.valueVec3 - currentFrame.valueVec3); + + worldManager.getCurrentWorld()->camera.target = pos; + } + break; + case AnimationProperty::FoV: + { + auto pos = currentFrame.valueInt + delta * (nextFrame.valueInt - currentFrame.valueInt); + worldManager.getCurrentWorld()->camera.fov = pos; + } + break; + } + } + + animationTime += deltaTime; +} + +void AnimationSystem::preloadShot(Shot* shot) { + std::cout << "preloading shot..." << std::endl; + + worldManager.loadWorld(shot->world); + + for(auto cimMesh : shot->meshes) { + Mesh* mesh = assetManager.loadMesh(cimMesh.modelPath); + mesh->name = cimMesh.name; + mesh->material = assetManager.loadMaterial(cimMesh.materialPath); + mesh->tag = "cinematic"; + + for(auto animation : shot->animations) { + if(animation->targetName == mesh->name) + animation->target = mesh; + } + + shot->loadedMeshes.push_back(mesh); + } +} + +void AnimationSystem::loadShot(Shot* shot) { + std::cout << "loading shot..." << std::endl; + + worldManager.switchWorld(shot->world); + + for(auto mesh : shot->loadedMeshes) + worldManager.getCurrentWorld()->meshes.push_back(mesh); +} + +void AnimationSystem::unloadShot(Shot* shot) { + std::cout << "unloading shot..." << std::endl; + + World* world = worldManager.getCurrentWorld(); + + for(auto mesh : world->meshes) { + if(mesh->tag == "cinematic") { + //renderer->destroyMaterialBuffers(mesh->material); + delete mesh->material; + + //renderer->destroyMeshBuffers(mesh); + delete mesh; + } + } + + world->meshes.erase(std::remove_if(world->meshes.begin(), + world->meshes.end(), + [](Mesh* m){return m->tag == "cinematic";}), + world->meshes.end()); +} diff --git a/src/assetmanager.cpp b/src/assetmanager.cpp new file mode 100644 index 0000000..293ecb7 --- /dev/null +++ b/src/assetmanager.cpp @@ -0,0 +1,61 @@ +#include "assetmanager.h" + +#include +#include +#include +#include +#include +#include + +#include "mesh.h" +#include "material.h" +#include "renderer.h" + +Mesh* AssetManager::loadMesh(const std::string& path) { + std::string fixedPath = "data/" + path; + + Assimp::Importer importer; + const aiScene* scene = importer.ReadFile(fixedPath.c_str(), aiProcess_Triangulate); + + Mesh* mesh = new Mesh(); + + unsigned int indexOffset = 0; + for(unsigned mi = 0; mi < scene->mNumMeshes; mi++) { + aiMesh* m = scene->mMeshes[mi]; + + for(unsigned int i = 0; i < m->mNumVertices; i++) { + Vertex vertex; + vertex.position = glm::vec3(m->mVertices[i].x, m->mVertices[i].y, m->mVertices[i].z); + vertex.normal = glm::vec3(m->mNormals[i].x, m->mNormals[i].y, m->mNormals[i].z); + vertex.uv = glm::vec2(m->mTextureCoords[0][i].x, m->mTextureCoords[0][i].y); + + mesh->vertices.push_back(vertex); + } + + for(unsigned int i = 0; i < m->mNumFaces; i++) { + aiFace face = m->mFaces[i]; + for(unsigned int j = 0; j < face.mNumIndices; j++) + mesh->indices.push_back(indexOffset + face.mIndices[j]); + } + + indexOffset += m->mNumVertices; + } + + renderer->fillMeshBuffers(mesh); + + return mesh; +} + +Material* AssetManager::loadMaterial(const std::string& path) { + std::ifstream file("data/" + path); + + nlohmann::json json; + file >> json; + + Material* material = new Material(); + material->albedoTexturePath = "data/" + json["albedoTexture"].get(); + + renderer->fillMaterialBuffers(material); + + return material; +} diff --git a/src/main.cpp b/src/main.cpp index 315463f..88bf250 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,9 +5,6 @@ #include #include #include -#include -#include -#include #ifdef DEBUG #include @@ -19,10 +16,12 @@ #include "mesh.h" #include "light.h" #include "camera.h" -#include "cinematic.h" +#include "animationsystem.h" #include "material.h" #include "config.h" #include "stringutils.h" +#include "worldmanager.h" +#include "assetmanager.h" SDL_Window* window = nullptr; Renderer* renderer = nullptr; @@ -49,8 +48,6 @@ int windowFullscreen = 0; std::string currentGraphicsPreset = "Medium"; GraphicsConfig graphicsConfig; -World* world = nullptr; - int toInt(const std::string &str) { std::stringstream ss(str); @@ -105,161 +102,6 @@ void writeConfig() { config.save("user.cfg"); } -Mesh* loadMesh(const std::string& path) { - std::string fixedPath = "data/" + path; - - Assimp::Importer importer; - const aiScene* scene = importer.ReadFile(fixedPath.c_str(), aiProcess_Triangulate); - - Mesh* mesh = new Mesh(); - - unsigned int indexOffset = 0; - for(unsigned mi = 0; mi < scene->mNumMeshes; mi++) { - aiMesh* m = scene->mMeshes[mi]; - - for(unsigned int i = 0; i < m->mNumVertices; i++) { - Vertex vertex; - vertex.position = glm::vec3(m->mVertices[i].x, m->mVertices[i].y, m->mVertices[i].z); - vertex.normal = glm::vec3(m->mNormals[i].x, m->mNormals[i].y, m->mNormals[i].z); - vertex.uv = glm::vec2(m->mTextureCoords[0][i].x, m->mTextureCoords[0][i].y); - - mesh->vertices.push_back(vertex); - } - - for(unsigned int i = 0; i < m->mNumFaces; i++) { - aiFace face = m->mFaces[i]; - for(unsigned int j = 0; j < face.mNumIndices; j++) - mesh->indices.push_back(indexOffset + face.mIndices[j]); - } - - indexOffset += m->mNumVertices; - } - - renderer->fillMeshBuffers(mesh); - - return mesh; -} - -Material* loadMaterial(const std::string& path) { - std::ifstream file("data/" + path); - - nlohmann::json json; - file >> json; - - Material* material = new Material(); - material->albedoTexturePath = "data/" + json["albedoTexture"].get(); - - renderer->fillMaterialBuffers(material); - - return material; -} - -World* loadWorld(const std::string& path) { - std::ifstream file("data/" + path); - - nlohmann::json json; - file >> json; - - World* world = new World(); - - for(auto meshObject : json["meshes"]) { - Mesh* mesh = loadMesh(meshObject["path"]); - mesh->material = loadMaterial(meshObject["material"]); - - auto tokens = tokenize(meshObject["position"]); - - mesh->position[0] = atof(tokens[0].c_str()); - mesh->position[1] = atof(tokens[1].c_str()); - mesh->position[2] = atof(tokens[2].c_str()); - - world->meshes.push_back(mesh); - } - - for(auto lightObject : json["lights"]) { - Light* light = new Light(); - - auto tokens = tokenize(lightObject["position"]); - - light->position[0] = atof(tokens[0].c_str()); - light->position[1] = atof(tokens[1].c_str()); - light->position[2] = atof(tokens[2].c_str()); - - world->lights.push_back(light); - } - - return world; -} - -Cinematic* loadCinematic(const std::string& path) { - std::ifstream file("data/" + path); - if(!file) - return nullptr; - - nlohmann::json json; - file >> json; - - auto cinematic = new Cinematic(); - for(auto shotObject : json["shots"]) { - Shot* shot = new Shot(); - shot->start = shotObject["start"]; - shot->end = shotObject["end"]; - - // TODO: concurrent worlds not implemented, we just load the first one - if(world == nullptr) - world = loadWorld(shotObject["world"]); - - for(auto meshObject : shotObject["meshes"]) { - Mesh* mesh = loadMesh(meshObject["path"]); - mesh->name = meshObject["name"]; - mesh->material = loadMaterial(meshObject["material"]); - mesh->tag = "cinematic"; - - shot->meshes.push_back(mesh); - } - - for(auto animationObject : shotObject["animations"]) { - auto animation = new Animation(); - for(auto mesh : shot->meshes) { - if(mesh->name == animationObject["target"]) - animation->target = mesh; - } - - const auto property = animationObject["property"]; - if(property == "position") - animation->property = AnimationProperty::Position; - else if(property == "target") - animation->property = AnimationProperty::Target; - else if(property == "fov") - animation->property = AnimationProperty::FoV; - - for(auto keyframeObject : animationObject["keyframes"]) { - Keyframe keyframe; - keyframe.time = keyframeObject["time"]; - - if(animation->property == AnimationProperty::Position || animation->property == AnimationProperty::Target) { - auto tokens = tokenize(keyframeObject["value"]); - - keyframe.valueVec3[0] = atof(tokens[0].c_str()); - keyframe.valueVec3[1] = atof(tokens[1].c_str()); - keyframe.valueVec3[2] = atof(tokens[2].c_str()); - - animation->keyframes.push_back(keyframe); - } else if(animation->property == AnimationProperty::FoV) { - keyframe.valueInt = keyframeObject["value"]; - - animation->keyframes.push_back(keyframe); - } - } - - shot->animations.push_back(animation); - } - - cinematic->shots.push_back(shot); - } - - return cinematic; -} - Cinematic* cinematic = nullptr; int main(int argc, char* argv[]) { @@ -300,18 +142,19 @@ int main(int argc, char* argv[]) { SDL_Vulkan_CreateSurface(window, renderer->getInstance(), &surface); RenderTarget* target = renderer->createSurfaceRenderTarget(surface); + + assetManager.setRenderer(renderer); + + AnimationSystem* animationSystem = new AnimationSystem(); if(cinematicMode) - cinematic = loadCinematic(argv[2]); + cinematic = animationSystem->loadCinematic(argv[2]); else { - world = loadWorld("test.world"); + worldManager.loadWorld("test.world"); + worldManager.switchWorld("test.world"); } - Camera camera; - camera.position = {5.0, 5.0, 5.0}; - - float currentTime = 0.0f, lastTime = 0.0f, animationTime = 0.0f; - Shot* currentShot = nullptr; + float currentTime = 0.0f, lastTime = 0.0f; bool running = true; while(running) { @@ -347,93 +190,13 @@ int main(int argc, char* argv[]) { } } - if(cinematicMode) { - float endTime = 0.0f; - for(auto shot : cinematic->shots) { - if(shot->end > endTime) - endTime = shot->end; - - if(animationTime >= shot->start && animationTime < shot->end && currentShot != shot) { - if(currentShot != nullptr) { - for(auto mesh : world->meshes) { - if(mesh->tag == "cinematic") { - renderer->destroyMaterialBuffers(mesh->material); - delete mesh->material; - - renderer->destroyMeshBuffers(mesh); - delete mesh; - } - } - - world->meshes.erase(std::remove_if(world->meshes.begin(), - world->meshes.end(), - [](Mesh* m){return m->tag == "cinematic";}), - world->meshes.end()); - } - - for(auto mesh : shot->meshes) - world->meshes.push_back(mesh); - - currentShot = shot; - } - } - - // we have reached the end of the cinematic - if(animationTime >= endTime) { - currentShot = nullptr; - - running = false; - } - - if(currentShot != nullptr) { - for(auto animation : currentShot->animations) { - unsigned int frameIndex = 0; - for(size_t i = 0; i < animation->keyframes.size(); i++) { - if(animationTime < animation->keyframes[i + 1].time) { - frameIndex = i; - break; - } - } - - const auto currentFrame = animation->keyframes[frameIndex]; - const auto nextFrame = animation->keyframes[(frameIndex + 1) % animation->keyframes.size()]; - - const float delta = (animationTime - currentFrame.time) / (nextFrame.time - currentFrame.time); - - switch(animation->property) { - case AnimationProperty::Position: - { - auto pos = currentFrame.valueVec3 + delta * (nextFrame.valueVec3 - currentFrame.valueVec3); - - if(animation->target == nullptr) - camera.position = pos; - else { - animation->target->position = pos; - } - } - break; - case AnimationProperty::Target: - { - auto pos = currentFrame.valueVec3 + delta * (nextFrame.valueVec3 - currentFrame.valueVec3); - - camera.target = pos; - } - break; - case AnimationProperty::FoV: - { - auto pos = currentFrame.valueInt + delta * (nextFrame.valueInt - currentFrame.valueInt); - camera.fov = pos; - } - break; - } - } - } - } - currentTime = SDL_GetTicks() / 1000.0f; const float deltaTime = currentTime - lastTime; lastTime = currentTime; + if(cinematicMode) + animationSystem->update(cinematic, deltaTime); + #ifdef DEBUG ImGui::NewFrame(); @@ -448,21 +211,18 @@ int main(int argc, char* argv[]) { if(SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS) io.MousePos = ImVec2(static_cast(mouseX), static_cast(mouseY)); - ImGui::DragFloat3("Light Position", &world->lights[0]->position[0]); - ImGui::DragFloat3("Camera Position", &camera.position[0], 0.1f); - ImGui::DragFloat3("Target", &camera.target[0], 0.1f); - ImGui::DragFloat("Aperture", &camera.aperture, 0.01f, 0.0f, 1.0f); - ImGui::DragFloat("Focus Distance", &camera.focusDistance); - ImGui::Text("dpack[2] = %f", (100 - camera.focusDistance) / 100.0f); + ImGui::DragFloat3("Light Position", &worldManager.getCurrentWorld()->lights[0]->position[0]); + ImGui::DragFloat3("Camera Position", &worldManager.getCurrentWorld()->camera.position[0], 0.1f); + ImGui::DragFloat3("Target", &worldManager.getCurrentWorld()->camera.target[0], 0.1f); + ImGui::DragFloat("Aperture", &worldManager.getCurrentWorld()->camera.aperture, 0.01f, 0.0f, 1.0f); + ImGui::DragFloat("Focus Distance", &worldManager.getCurrentWorld()->camera.focusDistance); + ImGui::Text("dpack[2] = %f", (100 - worldManager.getCurrentWorld()->camera.focusDistance) / 100.0f); ImGui::Render(); #endif - renderer->render(*world, camera, target); + renderer->render(*worldManager.getCurrentWorld(), worldManager.getCurrentWorld()->camera, target); - if(cinematicMode) - animationTime += deltaTime; - if(record) { static int frameNum = 0; std::string screenshotName = "frame" + std::to_string(frameNum) + ".ppm"; @@ -472,7 +232,7 @@ int main(int argc, char* argv[]) { } } - for(auto mesh : world->meshes) { + /*for(auto mesh : world->meshes) { renderer->destroyMaterialBuffers(mesh->material); delete mesh->material; @@ -484,7 +244,7 @@ int main(int argc, char* argv[]) { delete light; } - delete world; + delete world;*/ renderer->destroyRenderTarget(target); diff --git a/src/worldmanager.cpp b/src/worldmanager.cpp new file mode 100644 index 0000000..dddba09 --- /dev/null +++ b/src/worldmanager.cpp @@ -0,0 +1,54 @@ +#include "worldmanager.h" + +#include +#include +#include +#include + +#include "stringutils.h" +#include "world.h" +#include "assetmanager.h" +#include "mesh.h" +#include "light.h" + +void WorldManager::loadWorld(const std::string& path) { + std::ifstream file("data/" + path); + if(!file) + std::cout << "failed to load " << path << std::endl; + + nlohmann::json json; + file >> json; + + World* world = new World(); + + for(auto meshObject : json["meshes"]) { + Mesh* mesh = assetManager.loadMesh(meshObject["path"]); + mesh->material = assetManager.loadMaterial(meshObject["material"]); + + auto tokens = tokenize(meshObject["position"]); + + mesh->position[0] = atof(tokens[0].c_str()); + mesh->position[1] = atof(tokens[1].c_str()); + mesh->position[2] = atof(tokens[2].c_str()); + + world->meshes.push_back(mesh); + } + + for(auto lightObject : json["lights"]) { + Light* light = new Light(); + + auto tokens = tokenize(lightObject["position"]); + + light->position[0] = atof(tokens[0].c_str()); + light->position[1] = atof(tokens[1].c_str()); + light->position[2] = atof(tokens[2].c_str()); + + world->lights.push_back(light); + } + + loadedWorlds[path] = world; +} + +void WorldManager::switchWorld(const std::string& path) { + currentWorld = loadedWorlds[path]; +}