Spaces:
Build error
Build error
/* | |
* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
* SPDX-License-Identifier: Apache-2.0 | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
/** @file camera_path.h | |
* @author Thomas Müller & Alex Evans, NVIDIA | |
*/ | |
struct ImDrawList; | |
namespace ngp { | |
struct CameraKeyframe { | |
quat R; | |
vec3 T; | |
float fov; | |
float timestamp = 0; | |
mat4x3 m() const { | |
auto rot = to_mat3(normalize(R)); | |
return mat4x3(rot[0], rot[1], rot[2], T); | |
} | |
void from_m(const mat4x3& rv) { | |
T = rv[3]; | |
R = quat(mat3(rv)); | |
} | |
CameraKeyframe() = default; | |
CameraKeyframe(const quat& r, const vec3& t, float fv, float time) : R(r), T(t), fov(fv), timestamp{time} {} | |
CameraKeyframe(mat4x3 m, float fv, float time) : fov(fv), timestamp(time) { from_m(m); } | |
CameraKeyframe operator*(float f) const { return {R * f, T * f, fov * f, timestamp}; } | |
CameraKeyframe operator+(const CameraKeyframe& rhs) const { | |
quat Rr = rhs.R; | |
if (dot(Rr, R) < 0.0f) { | |
Rr = -Rr; | |
} | |
return {R + Rr, T + rhs.T, fov + rhs.fov, rhs.timestamp}; | |
} | |
bool same_pos_as(const CameraKeyframe& rhs) const { return distance(T, rhs.T) < 0.0001f && fabsf(dot(R, rhs.R)) >= 0.999f; } | |
}; | |
CameraKeyframe lerp(const CameraKeyframe& p0, const CameraKeyframe& p1, float t, float t0, float t1); | |
CameraKeyframe spline_cm(float t, const CameraKeyframe& p0, const CameraKeyframe& p1, const CameraKeyframe& p2, const CameraKeyframe& p3); | |
CameraKeyframe spline_cubic(float t, const CameraKeyframe& p0, const CameraKeyframe& p1, const CameraKeyframe& p2, const CameraKeyframe& p3); | |
CameraKeyframe spline_quadratic(float t, const CameraKeyframe& p0, const CameraKeyframe& p1, const CameraKeyframe& p2); | |
CameraKeyframe spline_linear(float t, const CameraKeyframe& p0, const CameraKeyframe& p1); | |
enum class EEditingKernel : int { | |
None, | |
Gaussian, | |
Quartic, | |
Hat, | |
Box, | |
}; | |
static constexpr const char* EditingKernelStr = "None\0Gaussian\0Quartic\0Hat\0Box\0\0"; | |
struct CameraPath { | |
std::vector<CameraKeyframe> keyframes; | |
bool update_cam_from_path = false; | |
float play_time = 0.f; | |
float auto_play_speed = 0.f; | |
float default_duration_seconds = 3.0f; | |
// If loop is set true, the last frame set will be more like "next to last," | |
// with animation then returning back to the first frame, making a continuous loop. | |
// Note that the user does not have to (and should not normally) duplicate the first frame to be the last frame. | |
bool loop = false; | |
int keyframe_subsampling = 1; | |
EEditingKernel editing_kernel_type = EEditingKernel::None; | |
float editing_kernel_radius = 0.1f; | |
// Cubic spline per default. Order 1 (p/w linear) is also supported. | |
int spline_order = 3; | |
struct RenderSettings { | |
ivec2 resolution = {1920, 1080}; | |
int spp = 8; | |
float fps = 60.0f; | |
float shutter_fraction = 0.5f; | |
int quality = 8; | |
uint32_t n_frames(const float duration) const { return (uint32_t)((double)duration * fps); } | |
float frame_seconds(const float duration) const { return 1.0f / (duration * fps); } | |
float frame_milliseconds(const float duration) const { return 1000.0f / (duration * fps); } | |
std::string filename = "video.mp4"; | |
}; | |
RenderSettings render_settings; | |
bool rendering = false; | |
uint32_t render_frame_idx = 0; | |
std::chrono::time_point<std::chrono::steady_clock> render_start_time; | |
mat4x3 render_frame_end_camera; | |
struct Pos { | |
int kfidx; | |
float t; | |
}; | |
void clear() { | |
keyframes.clear(); | |
play_time = 0.0f; | |
} | |
bool empty() const { return keyframes.empty(); } | |
bool has_valid_timestamps() const; | |
void make_keyframe_timestamps_equidistant(const float duration_seconds); | |
float duration_seconds() const; | |
void set_duration_seconds(const float duration); | |
void sanitize_keyframes(); | |
Pos get_pos(float playtime); | |
float get_playtime(int i) { | |
if (i <= 0 || keyframes.size() < 2) { | |
return 0.0f; | |
} | |
const auto& kf = keyframes[clamp(i - 1, 0, (int)keyframes.size() - 1)]; | |
const float duration = loop ? keyframes.back().timestamp : keyframes[keyframes.size() - 2].timestamp; | |
return kf.timestamp / duration; | |
} | |
const CameraKeyframe& get_keyframe(int i) const { | |
if (loop) { | |
int size = (int)keyframes.size(); | |
// add size to ensure no negative value is generated by modulo | |
return keyframes[(i + size) % size]; | |
} else { | |
return keyframes[clamp(i, 0, (int)keyframes.size() - 1)]; | |
} | |
} | |
CameraKeyframe eval_camera_path(float t) { | |
if (keyframes.empty()) { | |
return {}; | |
} | |
auto p = get_pos(t); | |
switch (spline_order) { | |
case 0: return get_keyframe(p.kfidx + (int)round(p.t)); | |
case 1: return spline_linear(p.t, get_keyframe(p.kfidx), get_keyframe(p.kfidx + 1)); | |
case 2: return spline_quadratic(p.t, get_keyframe(p.kfidx - 1), get_keyframe(p.kfidx), get_keyframe(p.kfidx + 1)); | |
case 3: | |
return spline_cubic( | |
p.t, get_keyframe(p.kfidx - 1), get_keyframe(p.kfidx), get_keyframe(p.kfidx + 1), get_keyframe(p.kfidx + 2) | |
); | |
default: throw std::runtime_error{fmt::format("Spline of order {} is not supported.", spline_order)}; | |
} | |
} | |
void save(const fs::path& path); | |
void load(const fs::path& path, const mat4x3& first_xform); | |
void add_camera(const mat4x3& camera, float fov, float timestamp); | |
ImGuizmo::MODE m_gizmo_mode = ImGuizmo::LOCAL; | |
ImGuizmo::OPERATION m_gizmo_op = ImGuizmo::TRANSLATE; | |
int imgui(char path_filename_buf[1024], float frame_milliseconds, const mat4x3& camera, float fov, const mat4x3& first_xform); | |
bool imgui_viz( | |
ImDrawList* list, | |
mat4& view2proj, | |
mat4& world2proj, | |
mat4& world2view, | |
vec2 focal, | |
float aspect, | |
float znear, | |
float zfar | |
); | |
}; | |
void add_debug_line(ImDrawList* list, const mat4& proj, vec3 a, vec3 b, uint32_t col = 0xffffffff, float thickness = 1.0f); | |
void visualize_cube(ImDrawList* list, const mat4& world2proj, const vec3& a, const vec3& b, const mat3& render_aabb_to_local); | |
void visualize_camera( | |
ImDrawList* list, const mat4& world2proj, const mat4x3& xform, float aspect, uint32_t col = 0x80ffffff, float thickness = 1.0f | |
); | |
} // namespace ngp | |