File size: 6,772 Bytes
28451f7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*
 * 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
 */

#pragma once

#include <neural-graphics-primitives/common_host.h>

#include <tiny-cuda-nn/common.h>

#ifdef NGP_GUI
#	include <imgui/imgui.h>
#	include <imguizmo/ImGuizmo.h>
#endif

#include <chrono>
#include <vector>

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

#ifdef NGP_GUI
	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
	);
#endif
};

#ifdef NGP_GUI
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
);
#endif

} // namespace ngp