scene.cpp 22.11 KiB
#include "scene.hpp"
#include "tree.hpp"
#include "interpolation.hpp"
#include "ball.hpp"
#include "bird.hpp"
#include "bat.hpp"
#include "dragon.hpp"
#include "terrain.hpp"
#include "projectile.hpp"
#include "sapin.hpp"
#include "settings.hpp"
using namespace cgp;
std::vector<vec3> tree_position;
std::vector<vec3> grass_position;
void scene_structure::initialize()
{
//CAMERA
camera_control.initialize(inputs, window); // Give access to the inputs and window global state to the camera controler
//Orbit camera
//camera_control.set_rotation_axis_z();
//camera_control.look_at({ 20.0f,15.0f,15.0f }, {0,0,0});
//First person camera
camera_control.camera_model.position_camera = { 0,0,evaluate_terrain_height(0,0)+10};
//camera_control.camera_model.position_camera = { 0,-5,0};
camera_control.camera_model.set_rotation_axis({ 0,0,1 });
global_frame.initialize_data_on_gpu(mesh_primitive_frame());
//SKYBOX
//skybox_debug.png for debugging
image_structure image_skybox_template = image_load_file("assets/skybox2.jpg");
std::vector<image_structure> image_grid = image_split_grid(image_skybox_template, 4, 3);
image_structure image_skybox_template2 = image_load_file("assets/skyboxNight.jpeg");
std::vector<image_structure> image_grid2 = image_split_grid(image_skybox_template2, 4, 3);
skybox.initialize_data_on_gpu();
//x neg, x pos, y neg, y pos, z neg, z pos
skybox.texture.initialize_cubemap_on_gpu(image_grid[7].rotate_90_degrees_counterclockwise(), image_grid[1].rotate_90_degrees_clockwise(), image_grid[4].rotate_90_degrees_clockwise().rotate_90_degrees_clockwise(), image_grid[10], image_grid[3], image_grid[5].rotate_90_degrees_clockwise().rotate_90_degrees_clockwise());
skybox.texture2.initialize_cubemap_on_gpu(image_grid2[7].rotate_90_degrees_counterclockwise(), image_grid2[1].rotate_90_degrees_clockwise(), image_grid2[4].rotate_90_degrees_clockwise().rotate_90_degrees_clockwise(), image_grid2[10], image_grid2[3], image_grid2[5].rotate_90_degrees_clockwise().rotate_90_degrees_clockwise());
//TERRAIN
int N_terrain_samples = 500;
mesh const terrain_mesh = create_terrain_mesh(N_terrain_samples);
terrain.initialize_data_on_gpu(terrain_mesh);
// terrain.material.color = { 0.6f,0.85f,0.5f }; terrain de couleur de base verte
terrain.material.color = { 1,1,1 };
terrain.material.phong.specular = 0.0f; // non-specular terrain material
terrain.shader.load(project::path + "shaders/mesh_multi_texture/mesh_multi_texture.vert.glsl", project::path + "shaders/mesh_multi_texture/mesh_multi_texture.frag.glsl");
terrain.texture.load_and_initialize_texture_2d_on_gpu(project::path + "assets/grass.jpg",
GL_REPEAT,
GL_MIRRORED_REPEAT);
terrain.supplementary_texture["image_texture_rock"].load_and_initialize_texture_2d_on_gpu(project::path + "assets/rocks3.jpg",
GL_REPEAT,
GL_MIRRORED_REPEAT);
terrain.supplementary_texture["image_texture_snow"].load_and_initialize_texture_2d_on_gpu(project::path + "assets/snow.jpg",
GL_REPEAT,
GL_MIRRORED_REPEAT);
//OBJECTS
int r = 1;
int h = 5;
mesh cyl_mesh = create_cylinder_mesh(r, h);
cyl.initialize_data_on_gpu(cyl_mesh);
cyl.material.color = { 0.58f,0.294f,0.0f };
cyl.material.phong.specular = 0.0f;
r = 3;
h = 2;
int z = 3;
mesh cone_mesh = create_cone_mesh(r, h, z);
cone.initialize_data_on_gpu(cone_mesh);
cone.material.color = { 0.0f,0.8f,0.0f };
cone.material.phong.specular = 0.0f;
mesh tree_mesh = create_tree();
tree.initialize_data_on_gpu(tree_mesh);
float x = 1.0;
float y = 2.0;
tree.model.translation = { x, y, evaluate_terrain_height(x,y) };
tree_position = generate_positions_on_terrain(num_trees());
grass_position = generate_positions_on_terrain(num_grass());
mesh quadrangle_mesh;
quadrangle_mesh.position = { {0,0,0},{1, 0, 0}, {1,0,1}, {0, 0,1} };
quadrangle_mesh.uv = { {0,0},{1,0}, {1,1}, {0,1} };
quadrangle_mesh.connectivity = { {0,1,2}, {0,2,3} };
quadrangle_mesh.fill_empty_field(); // (fill with some default values the other buffers (colors, normals) that we didn't filled before)
// Convert the mesh structure into a mesh_drawable structure
grass.initialize_data_on_gpu(quadrangle_mesh);
grass.texture.load_and_initialize_texture_2d_on_gpu(project::path + "assets/grass.png",
GL_CLAMP_TO_BORDER,
GL_CLAMP_TO_BORDER);
chain1.initialize();
bouncing.initialize(20); //10 balls
bats.initialize_bats();
dragons.initialize_dragons();
vec3 bat_pos1 = { 0,0,evaluate_terrain_height(0,0) + 20 };
float bat_size1 = 0.2f;
numarray<vec3> bat_keypos1 = { {-1,1,0}, {0,1,0}, {1,1,0}, {1,2,0}, {2,2,0}, {2,2,1}, {2,0,1.5}, {1.5,-1,1}, {1.5,-1,0}, {1,-1,0}, {0,-0.5,0}, {-1,1,0} };
numarray<float> bat_keytimes1 = { 0.0f, 1.0f, 2.0f, 2.5f, 3.0f, 3.5f, 3.75f, 4.5f, 5.0f, 6.0f, 7.0f, 8.0f };
bats.add_bat(bat_pos1, bat_size1, bat_keypos1, bat_keytimes1);
vec3 dragon_pos = { 5,5,evaluate_terrain_height(0,0) + 10 };
float dragon_size = 0.5f;
numarray<vec3> dragon_keypos = { {-1,1,0}, {0,1,0}, {1,1,0}, {1,2,0}, {2,2,0}, {2,2,1}, {2,0,1.5}, {1.5,-1,1}, {1.5,-1,0}, {1,-1,0}, {0,-0.5,0}, {-1,1,0} };
numarray<float> dragon_keytimes = { 0.0f, 1.0f, 2.0f, 2.5f, 3.0f, 3.5f, 3.75f, 4.5f, 5.0f, 6.0f, 7.0f, 8.0f };
dragons.add_dragon(dragon_pos, dragon_size, dragon_keypos, dragon_keytimes);
projectiles.initialize();
sapin1.initialize_sapin();
sapin1.sapin["Trunk"].transform_local.rotation = rotation_transform::from_axis_angle({ 1,0,0 }, Pi / 2);
hitbox_sphere.initialize_data_on_gpu(mesh_primitive_sphere(1));
mesh background_mesh = mesh_primitive_quadrangle({ 0,0,0 }, { 1.1, 0, 0 }, { 1.1,0,0.4 }, { 0, 0,0.4 });
mesh death_mesh = mesh_primitive_quadrangle({ 0.05,-0.005,0.05 }, { 1.05, -0.005, 0.05 }, { 1.05,-0.005,0.35 }, { 0.05, -0.005,0.35 });
mesh health_mesh = mesh_primitive_quadrangle({ 0.05,-0.01,0.05 }, { 1.05, -0.01, 0.05 }, { 1.05,-0.01,0.35 }, { 0.05, -0.01,0.35 });
mesh_drawable background;
background.initialize_data_on_gpu(background_mesh);
mesh_drawable health;
health.initialize_data_on_gpu(health_mesh);
health.material.color = {0,1,0};
mesh_drawable death;
death.initialize_data_on_gpu(death_mesh);
death.material.color = { 1,0,0 };
healthbar.add(background, "Background");
healthbar.add(health, "Health", "Background");
healthbar.add(death, "Death", "Background");
}
void scene_structure::display_frame()
{
// Update the current time
timer.update();
// Must be called before drawing the other shapes and without writing in the Depth Buffer
glDepthMask(GL_FALSE); // disable depth-buffer writing
draw(skybox, timer.t, environment);
glDepthMask(GL_TRUE); // re-activate depth-buffer write
//Walking on ground
if (!gui.fly) {
vec3 eye_level = camera_control.camera_model.position_camera;
eye_level[2] = evaluate_terrain_height(eye_level[0], eye_level[1]) + 0.75;
camera_control.camera_model.position_camera = eye_level;
}
// Set the light to the current position of the camera
environment.light = camera_control.camera_model.position();
if (gui.display_frame)
draw(global_frame, environment);
draw(terrain, environment);
if (gui.display_wireframe)
draw_wireframe(terrain, environment);
//Draw trees if close enough
for (int i = 0; i < num_trees(); i++) {
if (norm(tree_position[i]-camera_control.camera_model.position_camera) < 75 ) {
sapin1.sapin["Trunk"].transform_local.translation = tree_position[i];
sapin1.sapin.update_local_to_global_coordinates();
draw(sapin1.sapin, environment);
}
}
display_ball();
display_bats();
display_dragons();
display_semiTransparent(); //Important to draw grass before projectiles (otherwise you see them through projectiles)
display_projectiles();
if (gui.display_wireframe)
draw_wireframe(tree, environment);
}
void scene_structure::display_bats()
{
bats.update_mvt();
for (int i = 0; i < bats.N; i++) {
if (!bats.bats_prop[i].isdead) {
//Wing animation
bats.bat_mesh["Bat wing left1"].transform_local.rotation = rotation_transform::from_axis_angle({ 0,1,0 }, 0.5 * cos(5 * bats.bats_prop[i].timer_mvt.t));
bats.bat_mesh["Bat wing right1"].transform_local.rotation = rotation_transform::from_axis_angle({ 0,1,0 }, -0.5 * cos(5 * bats.bats_prop[i].timer_mvt.t));
//Rescale
bats.bat_mesh["Bat base"].drawable.model.scaling = bats.bats_prop[i].size;
bats.bat_mesh["Bat wing left1"].drawable.model.scaling = bats.bats_prop[i].size;
bats.bat_mesh["Bat wing right1"].drawable.model.scaling = bats.bats_prop[i].size;
//Make sure wings are at the right distance
bats.bat_mesh["Bat wing left1"].transform_local.translation = { -0.25f * bats.bats_prop[i].size, 0, 0 };
bats.bat_mesh["Bat wing right1"].transform_local.translation = { 0.25f * bats.bats_prop[i].size, 0, 0 };
//Orientation and position of the bat along the path
bats.bats_prop[i].rot = rotation_transform::from_vector_transform({ 0,-1,0 }, normalize(bats.bats_prop[i].pos_futur - bats.bats_prop[i].pos));
bats.bat_mesh["Bat base"].transform_local.rotation = bats.bats_prop[i].rot;
bats.bat_mesh["Bat base"].transform_local.translation = bats.bats_prop[i].pos;
bats.bat_mesh.update_local_to_global_coordinates();
draw(bats.bat_mesh, environment);
display_healthbar(bats.bats_prop[i].hp, bats.bats_prop[i].max_hp, bats.bats_prop[i].pos);
if (gui.display_wireframe) {
draw_wireframe(bats.bat_mesh, environment);
}
if (gui.display_hitbox) {
display_hitbox(bats.bats_prop[i].bat_hitbox, bats.bats_prop[i].pos, bats.bats_prop[i].rot);
}
}
}
}
void scene_structure::display_dragons()
{
dragons.update_mvt();
for (int i = 0; i < bats.N; i++) {
if (!dragons.dragons_prop[i].isdead) {
//Wing animation
dragons.dragon_mesh["Dragon wing left"].transform_local.rotation = rotation_transform::from_axis_angle({ 1,0,0.5 }, 0.5 * cos(5 * dragons.dragons_prop[i].timer_mvt.t));
dragons.dragon_mesh["Dragon wing right"].transform_local.rotation = rotation_transform::from_axis_angle({ 1,0,0.5 }, -0.5 * cos(5 * dragons.dragons_prop[i].timer_mvt.t));
//Mouth animation
dragons.dragon_mesh["Dragon mouth"].transform_local.rotation = rotation_transform::from_axis_angle({ 0,1,0 }, 0.05 * cos(dragons.dragons_prop[i].timer_mvt.t));
//Tail animation
dragons.dragon_mesh["Dragon tailmiddle"].transform_local.rotation = rotation_transform::from_axis_angle({ 0,1,0.5 }, 0.05 * cos(2 * dragons.dragons_prop[i].timer_mvt.t));
dragons.dragon_mesh["Dragon tailend"].transform_local.rotation = rotation_transform::from_axis_angle({ 0.5,1,0.5 }, -0.01 * cos(2 * dragons.dragons_prop[i].timer_mvt.t));
//Rescale
dragons.dragon_mesh["Dragon base"].drawable.model.scaling = dragons.dragons_prop[i].size;
dragons.dragon_mesh["Dragon wing left"].drawable.model.scaling = dragons.dragons_prop[i].size;
dragons.dragon_mesh["Dragon wing right"].drawable.model.scaling = dragons.dragons_prop[i].size;
dragons.dragon_mesh["Dragon mouth"].drawable.model.scaling = dragons.dragons_prop[i].size;
dragons.dragon_mesh["Dragon tailmiddle"].drawable.model.scaling = dragons.dragons_prop[i].size;
dragons.dragon_mesh["Dragon tailend"].drawable.model.scaling = dragons.dragons_prop[i].size;
//Make sure wings are at the right distance
dragons.dragon_mesh["Dragon wing left"].transform_local.translation = { 0, -0.3f * dragons.dragons_prop[i].size, 0 };
dragons.dragon_mesh["Dragon wing right"].transform_local.translation = { 0, 0.3f * dragons.dragons_prop[i].size, 0 };
//Orientation and position of the bat along the path
dragons.dragons_prop[i].rot = rotation_transform::from_vector_transform({ 1,0,0 }, normalize(dragons.dragons_prop[i].pos_futur - dragons.dragons_prop[i].pos));
dragons.dragon_mesh["Dragon base"].transform_local.rotation = dragons.dragons_prop[i].rot;
dragons.dragon_mesh["Dragon base"].transform_local.translation = dragons.dragons_prop[i].pos;
dragons.dragon_mesh.update_local_to_global_coordinates();
draw(dragons.dragon_mesh, environment);
display_healthbar(dragons.dragons_prop[i].hp, dragons.dragons_prop[i].max_hp, dragons.dragons_prop[i].pos);
if (gui.display_wireframe) {
draw_wireframe(dragons.dragon_mesh, environment);
}
if (gui.display_hitbox) {
display_hitbox(dragons.dragons_prop[i].dragon_hitbox, dragons.dragons_prop[i].pos, dragons.dragons_prop[i].rot);
}
}
}
}
void scene_structure::display_hitbox(hitbox H, vec3 shift, rotation_transform rot) {
for (int i = 0; i < H.N; i++) {
hitbox_sphere.model.scaling = H.r[i];
hitbox_sphere.model.translation = rot * H.center[i] + shift;
draw_wireframe(hitbox_sphere, environment);
}
}
void scene_structure::display_healthbar(int hp, int max_hp, vec3 pos) {
// Correct placement and health levels
healthbar["Background"].transform_local.translation = pos + vec3(-1, 0, 1);
healthbar["Health"].drawable.model.scaling_xyz = { double(hp)/max_hp, 1, 1};
// Orient the healthbar so it faces the camera
auto const& camera = camera_control.camera_model;
vec3 const right = camera.right();
// Rotation such that the grass follows the right-vector of the camera, while pointing toward the z-direction
rotation_transform R = rotation_transform::from_frame_transform({ 1,0,0 }, { 0,0,1 }, right, { 0,0,1 });
healthbar["Background"].transform_local.rotation = R;
healthbar.update_local_to_global_coordinates();
draw(healthbar, environment);
}
void scene_structure::display_semiTransparent()
{
// Enable use of alpha component as color blending for transparent elements
// alpha = current_color.alpha
// new color = previous_color * alpha + current_color * (1-alpha)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Disable depth buffer writing
// - Transparent elements cannot use depth buffer
// - They are supposed to be display from furest to nearest elements
glDepthMask(false);
auto const& camera = camera_control.camera_model;
// Re-orient the grass shape to always face the camera direction
vec3 const right = camera.right();
// Rotation such that the grass follows the right-vector of the camera, while pointing toward the z-direction
for (int i = 0; i < num_grass(); i++) {
grass.model.translation = grass_position[i];
rotation_transform R = rotation_transform::from_frame_transform({ 1,0,0 }, { 0,0,1}, right, {0,0,1});
grass.model.rotation = R;
draw(grass, environment);
}
// Don't forget to re-activate the depth-buffer write
glDepthMask(true);
glDisable(GL_BLEND);
}
void scene_structure::draw_segment(vec3 const& a, vec3 const& b)
{
chain1.segment.vbo_position.update(numarray<vec3>{ a, b });
draw(chain1.segment, environment);
}
void scene_structure::display_chain(vec3 bird_pos)
{
// Update the current time
timer_chain.update();
chain1.simulation_step(timer_chain.scale * 0.01f, bird_pos);
for (int i = 0; i < chain1.n; i++) {
chain1.particle_sphere.model.translation = chain1.p[i];
chain1.particle_sphere.material.color = { (float)i / chain1.n,(float)i / chain1.n,0 };
draw(chain1.particle_sphere, environment);
}
for (int i = 0; i < chain1.n - 1; i++) {
draw_segment(chain1.p[i], chain1.p[i + 1]);
}
}
void scene_structure::display_gui()
{
ImGui::Checkbox("Frame", &gui.display_frame);
ImGui::Checkbox("Wireframe", &gui.display_wireframe);
ImGui::Checkbox("Hitbox", &gui.display_hitbox);
ImGui::Checkbox("Fly", &gui.fly);
//ImGui::SliderFloat("Time", &timer_mvt.t, timer_mvt.t_min, timer_mvt.t_max);
//ImGui::SliderFloat("Time scale", &timer_mvt.scale, 0.0f, 2.0f);
ImGui::SliderFloat("K", &gui.k, 0.0f, 10.0f);
ImGui::SliderFloat("Speed", &gui.speed, 0.0f, 10.0f);
// Display the GUI associated to the key position
//keyframe.display_gui();
}
void scene_structure::display_ball() {
bouncing.simulate(timer.scale * 0.01f);
for (int i = 0; i < bouncing.N; i++) {
bouncing.mesh.model.translation = bouncing.pos[i];
bouncing.mesh.material.color = bouncing.color[i];
draw(bouncing.mesh, environment);
}
}
void scene_structure::display_projectiles() {
vec3 const right = camera_control.camera_model.right();
rotation_transform R = rotation_transform::from_frame_transform({ cos(ball_turn_speed * timer.t + 90),sin(ball_turn_speed * timer.t + 90),0 }, { 0,0,1 }, right, { 0,0,1 });
rotation_transform R2 = rotation_transform::from_frame_transform({ cos(ball_turn_speed * timer.t -90), sin(ball_turn_speed * timer.t - 90),0 }, { 0,0,1 }, right, { 0,0,1 });
//Plus de prcision pour la collision
int iter_num = 40;
for(int i=0; i<iter_num; i++){
projectiles.simulate(timer.scale * 0.03f / iter_num);
for (int i = 0; i < projectiles.N; i++) {
if (projectiles.projectiles_prop[i].is_active) {
for (int j = 0; j < bats.N; j++) {
if (bats.bats_prop[j].bat_hitbox.is_in_hitbox(projectiles.projectiles_prop[i].pos, bats.bats_prop[j].pos, bats.bats_prop[j].rot)) {
projectiles.projectiles_prop[i].is_active = false;
bats.bats_prop[j].hp--;
if (bats.bats_prop[j].hp == 0) bats.bats_prop[j].isdead = true;
}
}
for (int j = 0; j < dragons.N; j++) {
if (dragons.dragons_prop[j].dragon_hitbox.is_in_hitbox(projectiles.projectiles_prop[i].pos, dragons.dragons_prop[j].pos, dragons.dragons_prop[j].rot)) {
projectiles.projectiles_prop[i].is_active = false;
dragons.dragons_prop[j].hp--;
if (dragons.dragons_prop[j].hp == 0) dragons.dragons_prop[j].isdead = true;
}
}
}
}
}
for (int i = 0; i < projectiles.N; i++) {
projectiles.mesh.model.translation = projectiles.projectiles_prop[i].pos;
projectiles.mesh.material.color = projectiles.projectiles_prop[i].color;
draw(projectiles.mesh, environment);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(false);
if (projectiles.projectiles_prop[i].elemental_type == projectile::projectile_type::fire) {
if (fmod(timer.t, ball_reset_speed) > 0.1) projectiles.projectiles_prop[i].change_animation = true;
if (fmod(timer.t, ball_reset_speed) < 0.1 && projectiles.projectiles_prop[i].change_animation) {
projectiles.projectiles_prop[i].change_animation = false;
projectiles.fireball.texture.update(projectiles.fire_grid[std::rand()%12]);
}
projectiles.fireball.model.translation = projectiles.projectiles_prop[i].pos;
draw(projectiles.fireball, environment);
projectiles.fireball.model.rotation = R;
draw(projectiles.fireball, environment);
projectiles.fireball.model.rotation = R2;
draw(projectiles.fireball, environment);
}
else if (projectiles.projectiles_prop[i].elemental_type == projectile::projectile_type::water) {
if (fmod(timer.t, ball_reset_speed) > 0.1) projectiles.projectiles_prop[i].change_animation = true;
if (fmod(timer.t, ball_reset_speed) < 0.1 && projectiles.projectiles_prop[i].change_animation) {
projectiles.projectiles_prop[i].change_animation = false;
projectiles.waterball.texture.update(projectiles.water_grid[std::rand() % 6]);
}
projectiles.waterball.model.translation = projectiles.projectiles_prop[i].pos;
draw(projectiles.waterball, environment);
projectiles.waterball.model.rotation = R;
draw(projectiles.waterball, environment);
projectiles.waterball.model.rotation = R2;
draw(projectiles.waterball, environment);
}
else if (projectiles.projectiles_prop[i].elemental_type == projectile::projectile_type::ice) {
if (fmod(timer.t, ball_reset_speed) > 0.1) projectiles.projectiles_prop[i].change_animation = true;
if (fmod(timer.t, ball_reset_speed) < 0.1 && projectiles.projectiles_prop[i].change_animation) {
projectiles.projectiles_prop[i].change_animation = false;
projectiles.iceball.texture.update(projectiles.ice_grid[std::rand() % 6]);
}
projectiles.iceball.model.translation = projectiles.projectiles_prop[i].pos;
draw(projectiles.iceball, environment);
projectiles.iceball.model.rotation = R;
draw(projectiles.iceball, environment);
projectiles.iceball.model.rotation = R2;
draw(projectiles.iceball, environment);
}
else if (projectiles.projectiles_prop[i].elemental_type == projectile::projectile_type::electric) {
projectiles.electroball.model.translation = projectiles.projectiles_prop[i].pos;
draw(projectiles.electroball, environment);
projectiles.electroball.model.rotation = R;
draw(projectiles.electroball, environment);
projectiles.electroball.model.rotation = R2;
draw(projectiles.electroball, environment);
}
else if (projectiles.projectiles_prop[i].elemental_type == projectile::projectile_type::rock) {
projectiles.rockball.model.translation = projectiles.projectiles_prop[i].pos;
draw(projectiles.rockball, environment);
projectiles.rockball.model.rotation = R;
draw(projectiles.rockball, environment);
projectiles.rockball.model.rotation = R2;
draw(projectiles.rockball, environment);
}
glDepthMask(true);
glDisable(GL_BLEND);
}
}
void scene_structure::mouse_move_event()
{
if (!inputs.keyboard.shift)
camera_control.action_mouse_move(environment.camera_view);
}
void scene_structure::mouse_click_event()
{
if (camera_control.inputs->mouse.click.left){
projectiles.add_ball(camera_control.camera_model.position_camera + 2*camera_control.camera_model.front(), 20*camera_control.camera_model.front());
}
camera_control.action_mouse_click(environment.camera_view);
}
void scene_structure::keyboard_event()
{
camera_control.action_keyboard(environment.camera_view);
if(camera_control.inputs->keyboard.shift) {
projectiles.reset();
}
}
void scene_structure::idle_frame()
{
vec4 dir_h = {
evaluate_terrain_height(camera_control.camera_model.position_camera.x - camera_control.camera_model.front().y * 0.1, camera_control.camera_model.position_camera.y + camera_control.camera_model.front().x * 0.1),
evaluate_terrain_height(camera_control.camera_model.position_camera.x + camera_control.camera_model.front().y * 0.1, camera_control.camera_model.position_camera.y - camera_control.camera_model.front().x * 0.1),
evaluate_terrain_height(camera_control.camera_model.position_camera.x - camera_control.camera_model.front().x * 0.1, camera_control.camera_model.position_camera.y - camera_control.camera_model.front().y * 0.1),
evaluate_terrain_height(camera_control.camera_model.position_camera.x + camera_control.camera_model.front().x * 0.1, camera_control.camera_model.position_camera.y + camera_control.camera_model.front().y * 0.1)
};
if (camera_control.inputs->keyboard.ctrl) {
camera_control.idle_frame(environment.camera_view, 10, gui.fly, dir_h);
}
else {
camera_control.idle_frame(environment.camera_view, gui.speed, gui.fly, dir_h);
}
}