Archived
1
Fork 0

Add support for multiple worlds in a single cinematic

This commit is contained in:
Joshua Goins 2018-12-18 06:24:28 -05:00
parent c7d87e9a9f
commit d91c7d7221
12 changed files with 425 additions and 267 deletions

View file

@ -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)

11
data/empty.world Normal file
View file

@ -0,0 +1,11 @@
{
"meshes": [
],
"lights": [
{
"position": "66,56,25",
"type": 1
}
]
}

View file

@ -70,7 +70,7 @@
]
},
{
"world": "test.world",
"world": "empty.world",
"start": 5,
"end": 10,
"meshes": [

20
include/animationsystem.h Normal file
View file

@ -0,0 +1,20 @@
#pragma once
#include <string>
#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;
};

22
include/assetmanager.h Normal file
View file

@ -0,0 +1,22 @@
#pragma once
#include <string>
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;

View file

@ -19,15 +19,24 @@ enum class AnimationProperty {
struct Animation {
Mesh* target = nullptr;
std::string targetName;
AnimationProperty property = AnimationProperty::Position;
std::vector<Keyframe> keyframes;
};
struct CinematicMesh {
std::string name, modelPath, materialPath;
};
struct Shot {
int start = 0, end = 0;
bool loaded = false;
std::string world;
std::vector<Mesh*> meshes;
std::vector<CinematicMesh> meshes;
std::vector<Mesh*> loadedMeshes;
std::vector<Animation*> animations;
};

View file

@ -2,6 +2,8 @@
#include <vector>
#include "camera.h"
class Mesh;
class Light;
@ -9,4 +11,6 @@ class World {
public:
std::vector<Mesh*> meshes;
std::vector<Light*> lights;
Camera camera;
};

23
include/worldmanager.h Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <map>
struct World;
class WorldManager {
public:
void loadWorld(const std::string& path);
void switchWorld(const std::string& path);
World* getCurrentWorld() {
return currentWorld;
}
private:
std::map<std::string, World*> loadedWorlds;
World* currentWorld = nullptr;
};
inline WorldManager worldManager;

190
src/animationsystem.cpp Normal file
View file

@ -0,0 +1,190 @@
#include "animationsystem.h"
#include <fstream>
#include <sstream>
#include <iostream>
#include <json.hpp>
#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());
}

61
src/assetmanager.cpp Normal file
View file

@ -0,0 +1,61 @@
#include "assetmanager.h"
#include <fstream>
#include <sstream>
#include <json.hpp>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#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<std::string>();
renderer->fillMaterialBuffers(material);
return material;
}

View file

@ -5,9 +5,6 @@
#include <fstream>
#include <sstream>
#include <json.hpp>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#ifdef DEBUG
#include <imgui.h>
@ -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<std::string>();
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<float>(mouseX), static_cast<float>(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);

54
src/worldmanager.cpp Normal file
View file

@ -0,0 +1,54 @@
#include "worldmanager.h"
#include <fstream>
#include <sstream>
#include <iostream>
#include <json.hpp>
#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];
}