Skip to content

Commit eb6e22c

Browse files
committed
Camera quality of life changes
1 parent 3820625 commit eb6e22c

File tree

7 files changed

+199
-41
lines changed

7 files changed

+199
-41
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ add_custom_command(
4646
)
4747

4848

49-
add_executable(complex-plotter WIN32 ${SOURCE_FILES} ${HEADER_SRC} "main.cpp" ${TRANSPILED_MAPPER_CPP})
49+
add_executable(complex-plotter ${SOURCE_FILES} ${HEADER_SRC} "main.cpp" ${TRANSPILED_MAPPER_CPP})
5050

5151
if(NOT EMSCRIPTEN)
5252
find_package(OpenGL REQUIRED)

src/graphics/3d/camera_state.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
#include <graphics/3d/camera_state.h>
22
CameraState camera_state;
33

4+
void update_camera_orbit(CameraState& state) {
5+
state.position.x = state.orbit_target.x + state.orbit_radius * cos(glm::radians(state.yaw)) * cos(glm::radians(state.pitch));
6+
state.position.y = state.orbit_target.y + state.orbit_radius * sin(glm::radians(state.pitch));
7+
state.position.z = state.orbit_target.z + state.orbit_radius * sin(glm::radians(state.yaw)) * cos(glm::radians(state.pitch));
8+
state.front = glm::normalize(state.orbit_target - state.position);
9+
state.right = glm::normalize(glm::cross(state.front, state.world_up));
10+
state.up = glm::normalize(glm::cross(state.right, state.front));
11+
}
12+
413
void update_camera_vectors(CameraState& state) {
14+
if (state.is_orbit) {
15+
update_camera_orbit(state);
16+
return;
17+
}
518
glm::vec3 new_front;
619
new_front.x = cos(glm::radians(state.yaw)) * cos(glm::radians(state.pitch));
720
new_front.y = sin(glm::radians(state.pitch));
@@ -17,6 +30,20 @@ glm::mat4 get_view_matrix(const CameraState& state) {
1730

1831
void process_keyboard(CameraState& state, int direction, float delta_time) {
1932
float velocity = state.movement_speed * delta_time;
33+
if (state.is_orbit) {
34+
glm::vec3 flat_front = glm::normalize(glm::vec3(state.front.x, 0.0f, state.front.z));
35+
glm::vec3 flat_right = glm::normalize(glm::vec3(state.right.x, 0.0f, state.right.z));
36+
37+
if (direction == 0) state.orbit_target += flat_front * velocity;
38+
if (direction == 1) state.orbit_target -= flat_front * velocity;
39+
if (direction == 2) state.orbit_target -= flat_right * velocity;
40+
if (direction == 3) state.orbit_target += flat_right * velocity;
41+
if (direction == 4) state.orbit_target += state.world_up * velocity;
42+
if (direction == 5) state.orbit_target -= state.world_up * velocity;
43+
44+
update_camera_vectors(state);
45+
return;
46+
}
2047
if (direction == 0) state.position += state.front * velocity;
2148
if (direction == 1) state.position -= state.front * velocity;
2249
if (direction == 2) state.position -= state.right * velocity;
@@ -39,6 +66,11 @@ void process_mouse_movement(CameraState& state, float x_offset, float y_offset,
3966
}
4067

4168
void process_mouse_scroll(CameraState& state, float y_offset) {
69+
if (state.is_orbit) {
70+
state.orbit_radius -= (float)y_offset;
71+
if (state.orbit_radius < 0.1f) state.orbit_radius = 0.1f;
72+
update_camera_vectors(state);
73+
}
4274
state.zoom -= (float)y_offset;
4375
if (state.zoom < 1.0f) state.zoom = 1.0f;
4476
if (state.zoom > 45.0f) state.zoom = 45.0f;

src/graphics/3d/camera_state.h

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,49 @@ struct CameraState {
1414
float yaw;
1515
float pitch;
1616
float movement_speed = 2.5f;
17-
float mouse_sensitivity = 0.05f;
17+
float mouse_sensitivity = 0.15f;
1818
float zoom = 45.0f;
1919

20+
bool is_orbit = true;
21+
glm::vec3 orbit_target = glm::vec3(0.0, 0.0, 0.0);
22+
float orbit_radius = 5.0;
23+
2024
CameraState() {
21-
glm::vec3 direction = glm::normalize(-position);
22-
front = direction;
23-
pitch = glm::degrees(asin(direction.y));
24-
yaw = glm::degrees(atan2(direction.z, direction.x));
25+
orbit_radius = glm::distance(position, orbit_target);
26+
27+
if (is_orbit) {
28+
glm::vec3 dir = glm::normalize(position - orbit_target);
29+
pitch = glm::degrees(asin(dir.y));
30+
yaw = glm::degrees(atan2(dir.z, dir.x));
31+
front = glm::normalize(orbit_target - position);
32+
mouse_sensitivity = 0.15f;
33+
}
34+
else {
35+
glm::vec3 direction = glm::normalize(orbit_target - position);
36+
front = direction;
37+
pitch = glm::degrees(asin(direction.y));
38+
yaw = glm::degrees(atan2(direction.z, direction.x));
39+
mouse_sensitivity = 0.05f;
40+
}
41+
2542
right = glm::normalize(glm::cross(front, world_up));
2643
up = glm::normalize(glm::cross(right, front));
2744
}
45+
46+
void switch_to_orbit() {
47+
is_orbit = true;
48+
orbit_target = position + front * orbit_radius;
49+
50+
glm::vec3 dir = glm::normalize(position - orbit_target);
51+
pitch = glm::degrees(asin(dir.y));
52+
yaw = glm::degrees(atan2(dir.z, dir.x));
53+
}
54+
55+
void switch_to_free() {
56+
is_orbit = false;
57+
pitch = glm::degrees(asin(front.y));
58+
yaw = glm::degrees(atan2(front.z, front.x));
59+
}
2860
};
2961

3062
extern CameraState camera_state;

src/graphics/ui.cpp

Lines changed: 80 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <transformer/transformer.h>
44
#include <graphics/graphics.h>
55
#include <interactions/interactions.h>
6+
#include <graphics/3d/camera_state.h>
67
#include <string>
78
#include <iostream>
89

@@ -304,12 +305,6 @@ void render_and_update(FunctionState& state, ViewState& view_state, unsigned int
304305
ImGui::Unindent();
305306
}
306307
}
307-
if (UI::CollapsingHeader("3D Keybinds")) {
308-
ImGui::BulletText("WASD: Move");
309-
ImGui::BulletText("Right click + Drag: Move camera");
310-
ImGui::BulletText("Shift/Spacebar: Go up");
311-
ImGui::BulletText("Ctrl: Go down");
312-
}
313308
if (UI::CollapsingHeader("Help & Keybinds")) {
314309
ImGui::BulletText("Left Click + Drag: Pan Camera");
315310
ImGui::BulletText("Scroll Wheel: Zoom");
@@ -446,34 +441,86 @@ void render_and_update(FunctionState& state, ViewState& view_state, unsigned int
446441
}
447442
}
448443
if (view_state.is_high_precision) {
449-
ImGui::Begin("High Precision Render", &view_state.is_high_precision, ImGuiWindowFlags_AlwaysAutoResize);
450-
if (view_state.hp_texture != 0) {
451-
ImGui::Text("Resolution: %dx%d", view_state.hp_width, view_state.hp_height);
452-
if (view_state.is_rendering_hp) {
453-
glBindTexture(GL_TEXTURE_2D, view_state.hp_texture);
454-
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, view_state.hp_width, view_state.hp_height, GL_RGBA, GL_UNSIGNED_BYTE, view_state.hp_cpu_buffer.data());
455-
456-
float progress = (float)view_state.hp_rows_completed / (float)view_state.hp_height;
457-
ImGui::ProgressBar(progress, ImVec2(-1.0f, 0.0f), "Loading...");
458-
}
459-
else {
460-
if (ImGui::Button("Save to PNG")) {
461-
std::string filename = "high_precision_" + std::to_string(view_state.hp_width) + "x" + std::to_string(view_state.hp_height) + ".png";
462-
export_plot_to_png(view_state.hp_cpu_buffer.data(),view_state.hp_width,view_state.hp_height,"complex-plot-high-precision.png");
463-
view_state.show_export_success = true;
464-
}
465-
}
466-
ImGui::Image(
467-
(void*)(intptr_t)view_state.hp_texture,
468-
ImVec2((float)view_state.hp_width, (float)view_state.hp_height),
469-
ImVec2(0, 0),
470-
ImVec2(1, 1)
471-
);
472-
}
473-
else {
474-
ImGui::Text("Initializing...");
444+
ImGui::Begin("High Precision Render", &view_state.is_high_precision, ImGuiWindowFlags_AlwaysAutoResize);
445+
if (view_state.hp_texture != 0) {
446+
ImGui::Text("Resolution: %dx%d", view_state.hp_width, view_state.hp_height);
447+
if (view_state.is_rendering_hp) {
448+
glBindTexture(GL_TEXTURE_2D, view_state.hp_texture);
449+
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, view_state.hp_width, view_state.hp_height, GL_RGBA, GL_UNSIGNED_BYTE, view_state.hp_cpu_buffer.data());
450+
451+
float progress = (float)view_state.hp_rows_completed / (float)view_state.hp_height;
452+
ImGui::ProgressBar(progress, ImVec2(-1.0f, 0.0f), "Loading...");
453+
}
454+
else {
455+
if (ImGui::Button("Save to PNG")) {
456+
std::string filename = "high_precision_" + std::to_string(view_state.hp_width) + "x" + std::to_string(view_state.hp_height) + ".png";
457+
export_plot_to_png(view_state.hp_cpu_buffer.data(),view_state.hp_width,view_state.hp_height,"complex-plot-high-precision.png");
458+
view_state.show_export_success = true;
475459
}
476-
ImGui::End();
477460
}
461+
ImGui::Image(
462+
(void*)(intptr_t)view_state.hp_texture,
463+
ImVec2((float)view_state.hp_width, (float)view_state.hp_height),
464+
ImVec2(0, 0),
465+
ImVec2(1, 1)
466+
);
467+
}
468+
else {
469+
ImGui::Text("Initializing...");
470+
}
471+
ImGui::End();
472+
}
473+
if (view_state.is_3d) {
474+
ImGui::Begin("3D Settings");
475+
ImGui::Text("Height = ");
476+
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Height function. 'z' evaluates on the output of f(z). Defaults to mag(z).");
477+
ImGui::SameLine();
478+
bool height_pressed_enter = ImGui::InputText("##height_source",
479+
&state.height_expression,
480+
ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackAlways,
481+
FunctionInputCallback,
482+
(void*)&state);
483+
ImGui::SameLine();
484+
if (UI::Button("Compile##Height")) {
485+
height_pressed_enter = true;
486+
}
487+
if (ImGui::IsItemHovered()) {
488+
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
489+
}
490+
if (height_pressed_enter) {
491+
state.needs_height_reparse = true;
492+
}
493+
ImGui::Spacing();
494+
if (UI::CollapsingHeader("Camera Settings"), nullptr, ImGuiWindowFlags_AlwaysAutoResize) {
495+
ImGui::Text("Camera Mode:");
496+
ImGui::SameLine();
497+
498+
if (UI::RadioButton("Free", !camera_state.is_orbit)) {
499+
camera_state.is_orbit = false;
500+
camera_state.switch_to_free();
501+
}
502+
ImGui::SameLine();
503+
if (UI::RadioButton("Orbit", camera_state.is_orbit)) {
504+
camera_state.is_orbit = true;
505+
camera_state.switch_to_orbit();
506+
update_camera_vectors(camera_state);
507+
}
508+
if (UI::Button("Snap camera to origin")) {
509+
CameraState new_camerastate;
510+
new_camerastate.is_orbit = camera_state.is_orbit;
511+
camera_state = new_camerastate;
512+
513+
}
514+
}
515+
if (UI::CollapsingHeader("3D Keybinds")) {
516+
ImGui::BulletText("WASD: Move");
517+
ImGui::BulletText("Right click + Drag: Move camera");
518+
ImGui::BulletText("Shift/Spacebar: Go up");
519+
ImGui::BulletText("Ctrl: Go down");
520+
}
521+
522+
523+
ImGui::End();
524+
}
478525
ImGui::End();
479526
}

src/graphics/ui.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ typedef struct FunctionState {
1919
bool is_interpreted = true;
2020
bool is_3d = false;
2121
bool show_grid = true;
22+
std::string height_expression = "mag(z)";
23+
bool needs_height_reparse = true;
2224
} FunctionState;
2325

2426
void render(FunctionState& state, unsigned int& op_tex, unsigned int& const_tex, Shader& interpreter_shader);

src/interactions/interactions.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ void scroll_callback(GLFWwindow* window, const double xoffset, const double yoff
1111
ViewState* state = get_state(window);
1212
if (ImGui::GetIO().WantCaptureMouse || !state) return;
1313

14+
if (state->is_3d && camera_state.is_orbit) {
15+
process_mouse_scroll(camera_state, yoffset);
16+
return;
17+
}
18+
1419
double x, y;
1520
glfwGetCursorPos(window, &x, &y);
1621

@@ -30,7 +35,7 @@ void scroll_callback(GLFWwindow* window, const double xoffset, const double yoff
3035
state->hp_range = state->hp_range / hp_zoom_factor;
3136
}
3237

33-
if (state->is_3d) return;
38+
//if (state->is_3d) return;
3439

3540
big_float hp_range_diff = prev_hp_range - state->hp_range;
3641
state->hp_shift.x = state->hp_shift.x + hp_range_diff * hp_u;
@@ -45,6 +50,7 @@ void mouse_button_callback(GLFWwindow* window, const int button, int action, int
4550
ViewState* state = get_state(window);
4651
if (!state || ImGui::GetIO().WantCaptureMouse) return;
4752

53+
4854
if (button != GLFW_MOUSE_BUTTON_LEFT && button != GLFW_MOUSE_BUTTON_RIGHT) return;
4955

5056
if (action == GLFW_RELEASE) {
@@ -71,6 +77,11 @@ void cursor_position_callback(GLFWwindow* window, const double xpos, const doubl
7177
return;
7278
}
7379

80+
if (state->is_3d) {
81+
process_mouse_movement(camera_state,delta.x, -delta.y);
82+
return;
83+
}
84+
7485
float inv_height = 1.0f / state->height;
7586
big_float hp_inv_height = big_float(inv_height);
7687
big_float hp_scale = state->hp_range * hp_inv_height;

src/main.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,43 @@ struct AppContext {
7272
bool pressing_t;
7373
};
7474

75+
void update_height_function(AppContext* ctx) {
76+
try {
77+
std::vector<TokenOperator> height_stack = parser::parse(ctx->function_state->height_expression);
78+
string height_glsl = stack_to_glsl_string(height_stack);
7579

80+
string injection_code = "vec2 _temp_z = z; z = func_value; vec2 _h_val = " + height_glsl + "; float height = _h_val.x; z = _temp_z;";
7681

82+
string vert_source_3d = get_source("shaders/plotter3d.vert");
83+
vert_source_3d = build_shader_string(vert_source_3d, ctx->shader_program->fragment_source);
7784

85+
string target = "float height = length(func_value);";
86+
size_t pos = vert_source_3d.find(target);
87+
if (pos != string::npos) {
88+
vert_source_3d.replace(pos, target.length(), injection_code);
89+
}
90+
else {
91+
throw std::runtime_error("Could not find height target in vertex shader.");
92+
}
93+
const string frag_source_3d = get_source("shaders/plotter3d.frag");
7894

95+
ctx->shader_3d->compile(vert_source_3d, frag_source_3d);
96+
97+
ctx->compiled_shader_3d->prepare_source(vert_source_3d, frag_source_3d, true);
98+
99+
if (!ctx->function_state->is_interpreted) {
100+
string main_expr_glsl = stack_to_glsl_string(parser::parse(ctx->function_state->expression));
101+
ctx->compiled_shader_3d->compile(main_expr_glsl, true);
102+
}
79103

104+
ctx->function_state->error_message = "";
105+
}
106+
catch (const std::exception& e) {
107+
ctx->function_state->error_message = string("Height Function Error: ") + e.what();
108+
}
80109

110+
ctx->function_state->needs_height_reparse = false;
111+
}
81112

82113
void draw_scene(AppContext* ctx, float render_width, float render_height) {
83114
Shader* current_shader = ctx->function_state->current_shader;
@@ -247,7 +278,10 @@ void main_loop_step(AppContext* ctx) {
247278
render_and_update(*(ctx->function_state), *(ctx->view_state), stack_tbo_texture, constants_tbo_texture, *(ctx->shader_program), *(ctx->compiled_shader));
248279
}
249280

250-
281+
if (ctx->function_state->needs_height_reparse) {
282+
update_height_function(ctx);
283+
}
284+
251285
draw_scene(ctx, ctx->view_state->width, ctx->view_state->height);
252286

253287
if (is_3d != ctx->view_state->is_3d) {

0 commit comments

Comments
 (0)