Archived
1
Fork 0

Add cinematic engine

This commit is contained in:
Joshua Goins 2018-10-30 21:13:36 -04:00
parent 918520cc9d
commit 491a9b67c7
9 changed files with 19248 additions and 36 deletions

View file

@ -31,4 +31,5 @@ add_shaders(Graph
shaders/post.frag)
add_data(Graph
data/suzanne.obj)
data/suzanne.obj
data/test.cim)

96
data/test.cim Normal file
View file

@ -0,0 +1,96 @@
{
"shots": [
{
"start": 0,
"end": 5,
"meshes": [
{
"name": "suzanne",
"path": "data/suzanne.obj"
},
{
"name": "suzanne2",
"path": "data/suzanne.obj"
}
],
"animations": [
{
"target": "suzanne",
"property": "position",
"keyframes": [
{
"time": 0,
"value": "0,0,0"
},
{
"time": 1,
"value": "0,2,-3.5"
},
{
"time": 3,
"value": "0,0.5,-4"
},
{
"time": 5,
"value": "0,0,-6"
}
]
},
{
"target": "suzanne2",
"property": "position",
"keyframes": [
{
"time": 0,
"value": "5,0,-2"
},
{
"time": 2,
"value": "-5,0,-2"
}
]
},
{
"target": "camera",
"property": "position",
"keyframes": [
{
"time": 0,
"value": "1,3,1"
},
{
"time": 3,
"value": "4,4,4"
}
]
}
]
},
{
"start": 5,
"end": 10,
"meshes": [
{
"name": "suzanne",
"path": "data/suzanne.obj"
}
],
"animations": [
{
"target": "camera",
"property": "position",
"keyframes": [
{
"time": 5,
"value": "-5,0,2"
},
{
"time": 10,
"value": "5,0,2"
}
]
}
]
}
]
}

34
include/cinematic.h Normal file
View file

@ -0,0 +1,34 @@
#pragma once
#include <vector>
#include <glm/glm.hpp>
class Mesh;
struct Keyframe {
int time = 0;
glm::vec3 value = glm::vec3(0);
};
enum class AnimationProperty {
Position
};
struct Animation {
Mesh* target = nullptr;
AnimationProperty property = AnimationProperty::Position;
std::vector<Keyframe> keyframes;
};
struct Shot {
int start = 0, end = 0;
std::vector<Mesh*> meshes;
std::vector<Animation*> animations;
};
class Cinematic {
public:
std::vector<Shot*> shots;
};

18928
include/json.hpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
#pragma once
#include <vector>
#include <string>
#include <cstdint>
#include <glm/glm.hpp>
#include <vulkan/vulkan.h>
@ -11,6 +12,9 @@ struct Vertex {
class Mesh {
public:
std::string name;
glm::vec3 position;
std::vector<Vertex> vertices;
std::vector<uint32_t> indices;

View file

@ -49,7 +49,7 @@ public:
RenderTarget* createSurfaceRenderTarget(VkSurfaceKHR surface, RenderTarget* oldTarget = nullptr);
void destroyRenderTarget(RenderTarget* target);
void takeScreenshot(RenderTarget* target);
void takeScreenshot(const char* path, RenderTarget* target);
VkShaderModule createShader(const char* path);

View file

@ -14,8 +14,11 @@
#include "mesh.h"
#include "light.h"
#include "camera.h"
#include "cinematic.h"
#include "json.hpp"
SDL_Window* window = nullptr;
Renderer* renderer = nullptr;
std::vector<const char*> platform::getRequiredExtensions() {
uint32_t count = 0;
@ -94,30 +97,9 @@ void writeConfig() {
file << "fullscreen=" << windowFullscreen << "\n";
}
int main(int argc, char* argv[]) {
readConfig();
window = SDL_CreateWindow("Graph",
windowX,
windowY,
windowWidth,
windowHeight,
SDL_WINDOW_VULKAN |
SDL_WINDOW_RESIZABLE);
if(window == nullptr)
return -1;
SDL_SetWindowFullscreen(window, windowFullscreen == 1 ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
auto renderer = new Renderer();
VkSurfaceKHR surface = nullptr;
SDL_Vulkan_CreateSurface(window, renderer->getInstance(), &surface);
RenderTarget* target = renderer->createSurfaceRenderTarget(surface);
Mesh* loadMesh(const char* path) {
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile("data/suzanne.obj", aiProcess_Triangulate);
const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate);
aiMesh* m = scene->mMeshes[0];
Mesh* mesh = new Mesh();
@ -138,8 +120,107 @@ int main(int argc, char* argv[]) {
renderer->fillMeshBuffers(mesh);
return mesh;
}
std::vector<std::string> tokenize(const std::string& str) {
size_t lastPos = str.find_first_not_of(',', 0);
size_t pos = str.find_first_of(',', lastPos);
std::vector<std::string> tokens;
while(pos != std::string::npos || lastPos != std::string::npos) {
tokens.push_back(str.substr(lastPos, pos - lastPos));
lastPos = str.find_first_not_of(',', pos);
pos = str.find_first_of(',', lastPos);
}
return tokens;
}
Cinematic* loadCinematic(const char* path) {
std::ifstream file(path);
if(!file)
return nullptr;
nlohmann::json json;
file >> json;
Cinematic* cinematic = new Cinematic();
for(auto shotObject : json["shots"]) {
Shot* shot = new Shot();
shot->start = shotObject["start"];
shot->end = shotObject["end"];
for(auto meshObject : shotObject["meshes"]) {
Mesh* mesh = loadMesh(meshObject["path"].get<std::string>().c_str());
mesh->name = meshObject["name"];
shot->meshes.push_back(mesh);
}
for(auto animationObject : shotObject["animations"]) {
Animation* 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;
for(auto keyframeObject : animationObject["keyframes"]) {
Keyframe keyframe;
keyframe.time = keyframeObject["time"];
auto tokens = tokenize(keyframeObject["value"]);
keyframe.value[0] = atof(tokens[0].c_str());
keyframe.value[1] = atof(tokens[1].c_str());
keyframe.value[2] = atof(tokens[2].c_str());
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[]) {
readConfig();
bool cinematicMode = false;
if(argc > 2 && strcmp(argv[1], "--cinematic") == 0)
cinematicMode = true;
window = SDL_CreateWindow("Graph",
windowX,
windowY,
windowWidth,
windowHeight,
SDL_WINDOW_VULKAN |
SDL_WINDOW_RESIZABLE);
if(window == nullptr)
return -1;
SDL_SetWindowFullscreen(window, windowFullscreen == 1 ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
renderer = new Renderer();
VkSurfaceKHR surface = nullptr;
SDL_Vulkan_CreateSurface(window, renderer->getInstance(), &surface);
RenderTarget* target = renderer->createSurfaceRenderTarget(surface);
World world;
world.meshes.push_back(mesh);
auto light = new Light();
light->position.y = 5;
@ -147,9 +228,16 @@ int main(int argc, char* argv[]) {
world.lights.push_back(light);
Camera camera;
camera.position.y = 1;
camera.position.z = 3;
if(cinematicMode)
cinematic = loadCinematic(argv[2]);
else
world.meshes.push_back(loadMesh("data/suzanne.obj"));
float currentTime = 0.0f, lastTime = 0.0f;
Shot* currentShot = nullptr;
bool running = true;
while(running) {
SDL_Event event = {};
@ -175,21 +263,81 @@ int main(int argc, char* argv[]) {
}
if(event.type == SDL_KEYDOWN && event.key.keysym.scancode == SDL_SCANCODE_F12) {
renderer->takeScreenshot(target);
renderer->takeScreenshot("screenshot.ppm", target);
}
}
camera.position.x = sin(platform::getTime() / 500.0f);
if(cinematicMode) {
float endTime = 0.0f;
for(auto shot : cinematic->shots) {
if(shot->end > endTime)
endTime = shot->end;
if(currentTime >= shot->start && currentTime < shot->end && currentShot != shot) {
if(currentShot != nullptr) {
world.meshes.clear();
}
for(auto mesh : shot->meshes)
world.meshes.push_back(mesh);
currentShot = shot;
}
}
// we have reached the end of the cinematic
if(currentTime >= endTime) {
currentShot = nullptr;
world.meshes.clear();
}
if(currentShot != nullptr) {
for(auto animation : currentShot->animations) {
unsigned int frameIndex = 0;
for(size_t i = 0; i < animation->keyframes.size(); i++) {
if(currentTime < 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 = (currentTime - currentFrame.time) / (nextFrame.time - currentFrame.time);
glm::vec3 pos = currentFrame.value + delta * (nextFrame.value - currentFrame.value);
if(animation->target != nullptr)
animation->target->position = pos;
else
camera.position = pos;
}
}
}
renderer->render(world, camera, target);
if(cinematicMode) {
currentTime += ((SDL_GetTicks() / 1000.0f) - lastTime);
lastTime = currentTime;
static int frameNum = 0;
std::string screenshotName = "frame" + std::to_string(frameNum) + ".ppm";
frameNum++;
renderer->takeScreenshot(screenshotName.c_str(), target);
}
}
if(cinematic == nullptr) {
renderer->destroyMeshBuffers(world.meshes[0]);
delete world.meshes[0];
}
delete light;
renderer->destroyMeshBuffers(mesh);
delete mesh;
renderer->destroyRenderTarget(target);
vkDestroySurfaceKHR(renderer->getInstance(), surface, nullptr);

View file

@ -404,7 +404,7 @@ void Renderer::destroyRenderTarget(RenderTarget* target) {
delete target;
}
void Renderer::takeScreenshot(RenderTarget* target) {
void Renderer::takeScreenshot(const char* path, RenderTarget* target) {
VkImageCreateInfo imageCreateInfo = {};
imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
@ -541,7 +541,7 @@ void Renderer::takeScreenshot(RenderTarget* target) {
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
imageMemoryBarrier.image = srcImage;
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageMemoryBarrier.subresourceRange.layerCount = 1;
@ -587,7 +587,7 @@ void Renderer::takeScreenshot(RenderTarget* target) {
vkMapMemory(device_, imageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&data);
data += subResourceLayout.offset;
std::ofstream file("screenshot.ppm", std::ios::out | std::ios::binary);
std::ofstream file(path, std::ios::out | std::ios::binary);
file << "P6\n" << target->extent.width << "\n" << target->extent.height << "\n" << 255 << "\n";
for(uint32_t y = 0; y < target->extent.height; y++) {

View file

@ -67,6 +67,7 @@ void WorldPass::render(VkCommandBuffer commandBuffer, World& world, Camera& came
glm::mat4 mvp;
mvp = glm::perspective(glm::radians(75.0f), (float)target->extent.width / target->extent.height, 0.1f, 100.0f);
mvp *= glm::lookAt(camera.position, camera.target, glm::vec3(0, -1, 0));
mvp = glm::translate(mvp, mesh->position);
vkCmdPushConstants(commandBuffer, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &mvp);