From be8a3af04eabb64a5081f95929d86b16b714a821 Mon Sep 17 00:00:00 2001
From: Sebastian Pape <Pape@vr.rwth-aachen.de>
Date: Thu, 3 Jan 2019 11:21:08 +0100
Subject: [PATCH] Added video dolly feature

---
 demos/optical_bench/src/optical_bench.cpp     |   1 +
 demos/optical_bench/src/optix_engine.cpp      |  14 +-
 .../optical_bench/src/video_capture_pass.cpp  | 146 ++++++++++++++----
 .../optical_bench/src/video_capture_pass.hpp  |  19 ++-
 4 files changed, 143 insertions(+), 37 deletions(-)

diff --git a/demos/optical_bench/src/optical_bench.cpp b/demos/optical_bench/src/optical_bench.cpp
index 7c5af18b..60f6378d 100644
--- a/demos/optical_bench/src/optical_bench.cpp
+++ b/demos/optical_bench/src/optical_bench.cpp
@@ -152,6 +152,7 @@ int main(int argc, char** args) {
       videoPass[1]->Stop();
       video = false;
     }
+    if (key == 'u') videoPass[1]->ToggleDolly();
     if (key == '.') contextManager->EnableUsageCallback(usage);
     usage = !usage;
     if (key == 'l') object_manager->PrintLensInfos();
diff --git a/demos/optical_bench/src/optix_engine.cpp b/demos/optical_bench/src/optix_engine.cpp
index 24a7c914..b515ce09 100644
--- a/demos/optical_bench/src/optix_engine.cpp
+++ b/demos/optical_bench/src/optix_engine.cpp
@@ -163,10 +163,10 @@ void OpticalBenchSetup::SetupOptixFrameGraphWindow(
   frame_graph->AddRenderPass(
       std::make_unique<RayPass>(render_target, camera_target, engine, manager));
   frame_graph->AddRenderPass(std::make_unique<ScreenshotPass>(render_target));
-  frame_graph->AddRenderPass(
-      std::make_unique<phx::VideoCapturePass>(render_target, "normal_camera_"));
-  frame_graph->AddRenderPass(
-      std::make_unique<phx::VideoCapturePass>(camera_target, "static_camera_"));
+  frame_graph->AddRenderPass(std::make_unique<phx::VideoCapturePass>(
+      render_target, nullptr, "normal_camera_"));
+  frame_graph->AddRenderPass(std::make_unique<phx::VideoCapturePass>(
+      camera_target, render_target, "static_camera_"));
   frame_graph->AddRenderPass(std::make_unique<BlitPass>(render_target));
 
   frame_graph->Initialize();
@@ -214,9 +214,9 @@ void OpticalBenchSetup::SetupOptixFrameGraphOpenVR(
   frame_graph->AddRenderPass(
       std::make_unique<ScreenshotPass>(right_render_target));
   frame_graph->AddRenderPass(std::make_unique<phx::VideoCapturePass>(
-      right_render_target, "right_eye_"));
-  frame_graph->AddRenderPass(
-      std::make_unique<phx::VideoCapturePass>(camera_target, "static_camera_"));
+      right_render_target, nullptr, "right_eye_"));
+  frame_graph->AddRenderPass(std::make_unique<phx::VideoCapturePass>(
+      camera_target, right_render_target, "static_camera_"));
   frame_graph->AddRenderPass(std::make_unique<BlitPass>(camera_target));
 
   frame_graph->Initialize();
diff --git a/demos/optical_bench/src/video_capture_pass.cpp b/demos/optical_bench/src/video_capture_pass.cpp
index 08a84203..c2f3b503 100644
--- a/demos/optical_bench/src/video_capture_pass.cpp
+++ b/demos/optical_bench/src/video_capture_pass.cpp
@@ -21,10 +21,12 @@
 //------------------------------------------------------------------------------
 
 #include "video_capture_pass.hpp"
+#include "phx/core/entity.hpp"
 #include "phx/core/logger.hpp"
+#include "phx/rendering/components/transform.hpp"
 
-#include <fstream>
 #include <stdio.h>
+#include <fstream>
 
 namespace phx {
 
@@ -34,14 +36,17 @@ inline bool file_exists(const std::string& name) {
   return f.good();
 }
 
-VideoCapturePass::VideoCapturePass(RenderTarget* render_target, std::string file_name)
-    : render_target_(render_target) {
-	file_name_ = file_name;
+VideoCapturePass::VideoCapturePass(RenderTarget* render_target,
+                                   RenderTarget* dolly_target,
+                                   std::string file_name) {
+  render_target_ = render_target;
+  dolly_target_ = dolly_target;
+  file_name_ = file_name;
 }
 
 VideoCapturePass::~VideoCapturePass() {
-	Stop();
-	free(data_storage_);
+  Stop();
+  free(data_storage_);
 }
 
 void VideoCapturePass::Initialize() {
@@ -50,42 +55,125 @@ void VideoCapturePass::Initialize() {
 }
 
 void VideoCapturePass::Execute() {
+  if (video_dolly_running_) {
+    video_dolly_interpolation_ =
+        glm::clamp(video_dolly_interpolation_ + 0.01f, 0.0f, 1.0f);
+    if (video_dolly_target_is_head_) {
+      render_target_->GetEntity()
+          ->GetFirstComponent<phx::Transform>()
+          ->SetGlobalTranslation(
+              glm::mix(video_dolly_start_position,
+                       glm::vec3(dolly_target_->GetEntity()
+                                     ->GetFirstComponent<phx::Transform>()
+                                     ->GetGlobalTranslation()),
+                       video_dolly_interpolation_));
+      render_target_->GetEntity()
+          ->GetFirstComponent<phx::Transform>()
+          ->SetGlobalRotation(
+              glm::slerp(video_dolly_start_rotation,
+                         dolly_target_->GetEntity()
+                             ->GetFirstComponent<phx::Transform>()
+                             ->GetGlobalRotation(),
+                         video_dolly_interpolation_));
+      if (video_dolly_interpolation_ == 1.0f) {
+        render_target_->GetEntity()
+            ->GetFirstComponent<phx::Transform>()
+            ->SetParent(dolly_target_->GetEntity()
+                            ->GetFirstComponent<phx::Transform>());
+      }
+    } else {
+      render_target_->GetEntity()
+          ->GetFirstComponent<phx::Transform>()
+          ->SetGlobalTranslation(glm::mix(video_dolly_start_position,
+                                          video_dolly_camera_position,
+                                          video_dolly_interpolation_));
+      render_target_->GetEntity()
+          ->GetFirstComponent<phx::Transform>()
+          ->SetGlobalRotation(glm::slerp(video_dolly_start_rotation,
+                                         video_dolly_camera_rotation,
+                                         video_dolly_interpolation_));
+    }
+
+    if (video_dolly_interpolation_ == 1.0f) {
+      video_dolly_running_ = false;
+      phx::info("Panning finished.");
+    }
+  }
+
+  // Video capturing stuff
+
   if (!capturing) return;
 
   // Get Pixels into Memory
   render_target_->bind();
-  glReadPixels(0, 0, dims_.x, dims_.y, GL_RGBA, GL_UNSIGNED_BYTE, data_storage_);
+  glReadPixels(0, 0, dims_.x, dims_.y, GL_RGBA, GL_UNSIGNED_BYTE,
+               data_storage_);
   render_target_->unbind();
 
   // Stream to ffmpeg
-  fwrite(data_storage_, 4*dims_.x*dims_.y, 1, ffmpeg);
+  fwrite(data_storage_, 4 * dims_.x * dims_.y, 1, ffmpeg);
 }
 
-void VideoCapturePass::Start() { 
-	if (capturing) return;
-	capturing = true;
-	auto name = FindNextFileName(file_name_) + ".mp4";
-
-	// start ffmpeg telling it to expect raw rgba 720p-60hz frames
-	// -i - tells it to read frames from stdin
-	std::string cmd = "ffmpeg.exe -r 30 -f rawvideo -pix_fmt rgba -s " + std::to_string(dims_.x) + "x" + std::to_string(dims_.y) + " -i - "
-		"-threads 0 -preset fast -y -pix_fmt yuv420p -crf 21 -vf vflip -loglevel fatal " + name;
-
-	// open pipe to ffmpeg's stdin in binary write mode
-	ffmpeg = _popen(cmd.c_str(), "wb");
-
-	if (!ffmpeg){
-		phx::error("Can't open video stream");
-	}
+void VideoCapturePass::Start() {
+  if (capturing) return;
+  capturing = true;
+  auto name = FindNextFileName(file_name_) + ".mp4";
+
+  // start ffmpeg telling it to expect raw rgba 720p-60hz frames
+  // -i - tells it to read frames from stdin
+  std::string cmd = "ffmpeg.exe -r 30 -f rawvideo -pix_fmt rgba -s " +
+                    std::to_string(dims_.x) + "x" + std::to_string(dims_.y) +
+                    " -i - "
+                    "-threads 0 -preset fast -y -pix_fmt yuv420p -crf 21 -vf "
+                    "vflip -loglevel fatal " +
+                    name;
+
+  // open pipe to ffmpeg's stdin in binary write mode
+  ffmpeg = _popen(cmd.c_str(), "wb");
+
+  if (!ffmpeg) {
+    phx::error("Can't open video stream");
+  }
 
-	phx::info(("Started video recording on " + name + "...").c_str());
+  phx::info(("Started video recording on " + name + "...").c_str());
 }
 
 void VideoCapturePass::Stop() {
-	if (!capturing) return;
-	capturing = false;
-	_pclose(ffmpeg);
-	phx::info("Stopped video recording...");
+  if (!capturing) return;
+  capturing = false;
+  _pclose(ffmpeg);
+  phx::info("Stopped video recording...");
+}
+
+void VideoCapturePass::ToggleDolly() {
+  if (video_dolly_running_ || dolly_target_ == nullptr) return;
+  video_dolly_running_ = true;
+  video_dolly_target_is_head_ = !video_dolly_target_is_head_;
+  video_dolly_interpolation_ = 0.0f;
+
+  if (video_dolly_target_is_head_) {
+    video_dolly_camera_position = render_target_->GetEntity()
+                                      ->GetFirstComponent<phx::Transform>()
+                                      ->GetGlobalTranslation();
+
+    video_dolly_camera_rotation = render_target_->GetEntity()
+                                      ->GetFirstComponent<phx::Transform>()
+                                      ->GetGlobalRotation();
+    video_dolly_start_rotation = video_dolly_camera_rotation;
+    video_dolly_start_position = video_dolly_camera_position;
+    phx::info("Panning towards head started.");
+  } else {
+    video_dolly_start_position = dolly_target_->GetEntity()
+                                     ->GetFirstComponent<phx::Transform>()
+                                     ->GetGlobalTranslation();
+
+    video_dolly_start_rotation = dolly_target_->GetEntity()
+                                     ->GetFirstComponent<phx::Transform>()
+                                     ->GetGlobalRotation();
+    render_target_->GetEntity()->GetFirstComponent<phx::Transform>()->SetParent(
+        nullptr);
+    phx::info("Panning towards old camera position started.");
+  }
 }
 
 std::string VideoCapturePass::FindNextFileName(const std::string name) {
diff --git a/demos/optical_bench/src/video_capture_pass.hpp b/demos/optical_bench/src/video_capture_pass.hpp
index 802c326c..48fa7aa1 100644
--- a/demos/optical_bench/src/video_capture_pass.hpp
+++ b/demos/optical_bench/src/video_capture_pass.hpp
@@ -29,11 +29,17 @@
 #include "phx/rendering/backend/render_target.hpp"
 #include "phx/rendering/render_passes/render_pass.hpp"
 
+SUPPRESS_WARNINGS_BEGIN
+#include "glm/glm.hpp"
+#include "glm/gtc/quaternion.hpp"
+SUPPRESS_WARNINGS_END
+
 namespace phx {
 
 class VideoCapturePass : public RenderPass {
  public:
-  explicit VideoCapturePass(RenderTarget* render_target, std::string file_name);
+  explicit VideoCapturePass(RenderTarget* render_target,
+                            RenderTarget* dolly_target, std::string file_name);
   VideoCapturePass(const VideoCapturePass&) = default;
   VideoCapturePass(VideoCapturePass&&) = default;
   ~VideoCapturePass();
@@ -47,6 +53,8 @@ class VideoCapturePass : public RenderPass {
   void Start();
   void Stop();
 
+  void ToggleDolly();
+
  private:
   RenderTarget* render_target_;
   bool capturing = false;
@@ -56,6 +64,15 @@ class VideoCapturePass : public RenderPass {
   std::string FindNextFileName(const std::string name);
 
   FILE* ffmpeg;
+
+  RenderTarget* dolly_target_;
+  bool video_dolly_running_ = false;
+  float video_dolly_interpolation_ = 0.0f;
+  bool video_dolly_target_is_head_ = false;
+  glm::vec3 video_dolly_start_position = glm::vec3(0);
+  glm::quat video_dolly_start_rotation = glm::quat();
+  glm::vec3 video_dolly_camera_position = glm::vec3(0);
+  glm::quat video_dolly_camera_rotation = glm::quat();
 };
 
 }  // namespace phx
-- 
GitLab