2024-10-01 10:37:37 +02:00
|
|
|
#include <epoxy/gl.h>
|
|
|
|
#include <cglm/cglm.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <gdk/gdk.h> // General GDK events and types
|
|
|
|
#include <gtk/gtk.h> // GTK types
|
|
|
|
|
|
|
|
|
2024-10-05 18:32:30 +02:00
|
|
|
// General declaration zone ---------------------------------------------------
|
|
|
|
|
2024-10-01 10:37:37 +02:00
|
|
|
#define N_INSTANCE 5
|
|
|
|
#define N_SPACE_SIZE 20
|
|
|
|
|
|
|
|
GtkWidget *window;
|
|
|
|
|
|
|
|
// OpenGL objects
|
2024-10-07 09:05:54 +02:00
|
|
|
GLuint vao, arrow_vao;
|
|
|
|
GLuint vbo, arrow_vbo, cubes_instance_vbo, arrow_instance_vbo;
|
|
|
|
GLuint ebo, arrow_ebo, edge_ebo;
|
2024-10-01 10:37:37 +02:00
|
|
|
GLuint shader_program;
|
|
|
|
unsigned int num_instances = N_INSTANCE;
|
|
|
|
mat4 instance_matrices[N_INSTANCE];
|
|
|
|
|
|
|
|
static float pitch = 0.0f, yaw = -90.0f; // Initial angles for camera orientation
|
|
|
|
static const float sensitivity = 4.0f; // Adjust this for smoother mouse control
|
|
|
|
static float lastX = 400, lastY = 300; // Initial cursor position (center of the screen)
|
|
|
|
static float fov = 45.0f; // Field of View for zoom control
|
|
|
|
static gboolean firstMouse = TRUE; // To handle first mouse movement
|
|
|
|
static float rotation_angle_x = 0.0f; // Rotation angle around the X-axis
|
|
|
|
static float rotation_angle_y = 0.0f; // Rotation angle around the Y-axis
|
|
|
|
static float rotation_angle_z = 0.0f; // Rotation angle around the Z-axis
|
|
|
|
|
|
|
|
static mat4 rotation_matrix;
|
|
|
|
static bool rotate;
|
|
|
|
|
|
|
|
// Projection and view matrices
|
|
|
|
mat4 projection, view;
|
|
|
|
|
2024-10-05 18:32:30 +02:00
|
|
|
// Function Prototypes zone ---------------------------------------------------
|
2024-10-01 10:37:37 +02:00
|
|
|
|
|
|
|
GLuint create_shader_program ();
|
2024-10-07 09:05:54 +02:00
|
|
|
void setup_cubes_buffers ();
|
2024-10-01 10:37:37 +02:00
|
|
|
void setup_arrow_buffers ();
|
2024-10-07 09:05:54 +02:00
|
|
|
void setup_cube_instance_data ();
|
2024-10-01 10:37:37 +02:00
|
|
|
void setup_arrow_instance_data();
|
|
|
|
static void on_realize (GtkGLArea *area);
|
|
|
|
static gboolean on_render (GtkGLArea *area, GdkGLContext *context);
|
|
|
|
static void on_activate (GtkApplication *app, gpointer user_data);
|
|
|
|
static gboolean on_mouse_scroll(GtkEventControllerScroll *controller, gdouble dx, gdouble dy, gpointer user_data);
|
|
|
|
static gboolean on_mouse_move(GtkEventControllerMotion *controller, gdouble x, gdouble y, gpointer user_data);
|
|
|
|
|
2024-10-05 18:32:30 +02:00
|
|
|
// ----------------------------------------------------------------------------
|
2024-10-01 10:37:37 +02:00
|
|
|
// Vertex and fragment shader source code
|
2024-10-05 18:32:30 +02:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2024-10-01 10:37:37 +02:00
|
|
|
const char *vertex_shader_src = "#version 460 core\n"
|
|
|
|
"layout (location = 0) in vec3 aPos;\n"
|
|
|
|
"layout (location = 2) in mat4 instanceMatrix;\n"
|
|
|
|
"uniform mat4 projection;\n"
|
|
|
|
"uniform mat4 view;\n"
|
|
|
|
"void main() {\n"
|
|
|
|
" gl_Position = projection * view * instanceMatrix * vec4(aPos, 1.0);\n"
|
|
|
|
"}\n";
|
|
|
|
|
|
|
|
const char *fragment_shader_src = "#version 460 core\n"
|
|
|
|
"uniform vec4 FragColor;\n"
|
|
|
|
"out vec4 finalColor;\n"
|
|
|
|
"void main() {\n"
|
|
|
|
" finalColor = vec4(FragColor.rgb, 0.3); // Set alpha to 30%\n"
|
|
|
|
"}\n";
|
|
|
|
|
2024-10-05 18:32:30 +02:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Vertices and indices
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2024-10-01 10:37:37 +02:00
|
|
|
GLfloat vertices[] = {
|
|
|
|
-0.5f, -0.5f, -0.5f, // 0
|
|
|
|
0.5f, -0.5f, -0.5f, // 1
|
|
|
|
0.5f, 0.5f, -0.5f, // 2
|
|
|
|
-0.5f, 0.5f, -0.5f, // 3
|
|
|
|
-0.5f, -0.5f, 0.5f, // 4
|
|
|
|
0.5f, -0.5f, 0.5f, // 5
|
|
|
|
0.5f, 0.5f, 0.5f, // 6
|
|
|
|
-0.5f, 0.5f, 0.5f // 7
|
|
|
|
};
|
|
|
|
|
|
|
|
GLuint indices[] = {
|
|
|
|
4, 5, 6, 6, 7, 4, // Front face
|
|
|
|
0, 3, 2, 2, 1, 0, // Back face
|
|
|
|
0, 4, 7, 7, 3, 0, // Left face
|
|
|
|
1, 2, 6, 6, 5, 1, // Right face
|
|
|
|
3, 7, 6, 6, 2, 3, // Top face
|
|
|
|
0, 1, 5, 5, 4, 0 // Bottom face
|
|
|
|
};
|
|
|
|
|
|
|
|
GLuint edge_indices[] = {
|
|
|
|
// Bottom edges
|
|
|
|
0, 1,
|
|
|
|
1, 5,
|
|
|
|
5, 4,
|
|
|
|
4, 0,
|
|
|
|
|
|
|
|
// Top edges
|
|
|
|
3, 2,
|
|
|
|
2, 6,
|
|
|
|
6, 7,
|
|
|
|
7, 3,
|
|
|
|
|
|
|
|
// Vertical edges
|
|
|
|
0, 3,
|
|
|
|
1, 2,
|
|
|
|
5, 6,
|
|
|
|
4, 7
|
|
|
|
};
|
|
|
|
|
|
|
|
GLfloat arrow_vertices[] = {
|
|
|
|
0.0f, 0.0f, 0.0f, // Center of cube
|
|
|
|
0.5f, 0.0f, 0.0f, // Right face
|
|
|
|
-0.5f, 0.0f, 0.0f, // Left face
|
|
|
|
0.0f, 0.5f, 0.0f, // Top face
|
|
|
|
0.0f, -0.5f, 0.0f, // Bottom face
|
|
|
|
0.0f, 0.0f, 0.5f, // Front face
|
|
|
|
0.0f, 0.0f, -0.5f // Back face
|
|
|
|
};
|
|
|
|
|
|
|
|
GLuint arrow_indices[] = {
|
|
|
|
0, 1, // Center to Right face
|
|
|
|
0, 2, // Center to Left face
|
|
|
|
0, 3, // Center to Top face
|
|
|
|
0, 4, // Center to Bottom face
|
|
|
|
0, 5, // Center to Front face
|
|
|
|
0, 6 // Center to Back face
|
|
|
|
};
|
|
|
|
|
2024-10-05 18:32:30 +02:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// MOUSE MANAGEMENT ZONE
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2024-10-01 10:37:37 +02:00
|
|
|
// Maps a 2D point on the screen to a 3D point on a virtual trackball (sphere)
|
|
|
|
void trackball_map(float x, float y, int width, int height, vec3 out)
|
|
|
|
{
|
|
|
|
// Normalize x and y to the range [-1, 1]
|
|
|
|
float normalized_x = (2.0f * x - width) / width;
|
|
|
|
float normalized_y = (height - 2.0f * y) / height; // Inverted y axis
|
|
|
|
|
|
|
|
// Compute the distance from the origin
|
|
|
|
float d = sqrtf(normalized_x * normalized_x + normalized_y * normalized_y);
|
|
|
|
|
|
|
|
// Clamp the distance to 1 (the edge of the virtual trackball)
|
|
|
|
if (d > 1.0f) d = 1.0f;
|
|
|
|
|
|
|
|
// Compute the z coordinate based on the spherical projection
|
|
|
|
float z = sqrtf(1.0f - d * d);
|
|
|
|
|
|
|
|
// The output 3D point
|
|
|
|
out[0] = normalized_x;
|
|
|
|
out[1] = normalized_y;
|
|
|
|
out[2] = z;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean on_mouse_move(GtkEventControllerMotion *controller, gdouble x, gdouble y, gpointer user_data)
|
|
|
|
{
|
|
|
|
GdkModifierType state = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(controller));
|
|
|
|
|
|
|
|
if (state & GDK_BUTTON1_MASK) { // If the left mouse button is held
|
|
|
|
|
|
|
|
// Define screen width and height (adjust accordingly)
|
|
|
|
int width = gtk_widget_get_width(window);
|
|
|
|
int height = gtk_widget_get_height(window);
|
|
|
|
|
|
|
|
|
|
|
|
// Variables to store trackball positions
|
|
|
|
vec3 last_pos, cur_pos;
|
|
|
|
|
|
|
|
// Map the last and current mouse positions to the virtual trackball
|
|
|
|
trackball_map(lastX, lastY, width, height, last_pos);
|
|
|
|
trackball_map(x, y, width, height, cur_pos);
|
|
|
|
|
|
|
|
lastX = x;
|
|
|
|
lastY = y;
|
|
|
|
|
|
|
|
// Calculate the rotation axis as the cross product between the last and current positions
|
|
|
|
vec3 axis;
|
|
|
|
glm_vec3_cross(last_pos, cur_pos, axis);
|
|
|
|
|
|
|
|
// Calculate the angle of rotation based on the distance moved by the mouse
|
|
|
|
float angle = acosf(fmin(1.0f, glm_vec3_dot(last_pos, cur_pos)));
|
|
|
|
|
|
|
|
// Apply the rotation using the axis-angle method
|
|
|
|
if (angle > 0.001f) { // Avoid very small rotations
|
|
|
|
mat4 temp_rotation;
|
|
|
|
glm_mat4_identity(temp_rotation); // Initialize a temporary rotation matrix
|
|
|
|
glm_rotate(temp_rotation, angle * sensitivity, axis); // Rotate around the computed axis
|
|
|
|
glm_mul(temp_rotation, rotation_matrix, rotation_matrix); // Accumulate the rotation
|
|
|
|
}
|
|
|
|
|
|
|
|
// Request to redraw the scene with the updated rotation
|
|
|
|
gtk_widget_queue_draw(GTK_WIDGET(user_data));
|
|
|
|
}
|
|
|
|
|
|
|
|
rotate = TRUE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean on_mouse_scroll(GtkEventControllerScroll *controller, gdouble dx, gdouble dy, gpointer user_data)
|
|
|
|
{
|
|
|
|
if (dy > 0)
|
|
|
|
fov -= 2.0f;
|
|
|
|
else if (dy < 0)
|
|
|
|
fov += 2.0f;
|
|
|
|
|
|
|
|
// Clamp the field of view to avoid extreme zooms
|
|
|
|
if (fov < 10.0f) fov = 10.0f;
|
|
|
|
if (fov > 90.0f) fov = 90.0f;
|
|
|
|
|
|
|
|
// Update the projection matrix with the new FOV
|
|
|
|
glm_perspective(glm_rad(fov), 800.0f / 600.0f, 0.1f, 100.0f, projection);
|
|
|
|
|
|
|
|
// Recalculate the view matrix for zoom (moving the camera in/out)
|
|
|
|
glm_lookat((vec3){0.0f, 0.0f, 20.0f}, (vec3){0.0f, 0.0f, 0.0f}, (vec3){0.0f, 1.0f, 0.0f}, view);
|
|
|
|
|
|
|
|
gtk_widget_queue_draw(GTK_WIDGET(user_data)); // Request to redraw the scene
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2024-10-05 18:32:30 +02:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// OpenGL zone
|
|
|
|
// ----------------------------------------------------------------------------
|
2024-10-01 10:37:37 +02:00
|
|
|
|
|
|
|
// Create the shader program
|
|
|
|
GLuint create_shader_program ()
|
|
|
|
{
|
|
|
|
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
|
|
|
glShaderSource(vertex_shader, 1, &vertex_shader_src, NULL);
|
|
|
|
glCompileShader(vertex_shader);
|
|
|
|
|
|
|
|
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
|
|
glShaderSource(fragment_shader, 1, &fragment_shader_src, NULL);
|
|
|
|
glCompileShader(fragment_shader);
|
|
|
|
|
|
|
|
GLuint program = glCreateProgram();
|
|
|
|
glAttachShader(program, vertex_shader);
|
|
|
|
glAttachShader(program, fragment_shader);
|
|
|
|
glLinkProgram(program);
|
|
|
|
|
|
|
|
glDeleteShader(vertex_shader);
|
|
|
|
glDeleteShader(fragment_shader);
|
|
|
|
|
|
|
|
return program;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup OpenGL buffers (VBO, VAO, EBO for cubes and edges)
|
2024-10-07 09:05:54 +02:00
|
|
|
void setup_cubes_buffers ()
|
2024-10-01 10:37:37 +02:00
|
|
|
{
|
|
|
|
glGenVertexArrays(1, &vao);
|
|
|
|
glBindVertexArray(vao);
|
|
|
|
|
|
|
|
// Vertex buffer
|
|
|
|
glGenBuffers(1, &vbo);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
// Face Index buffer (EBO for cube faces)
|
|
|
|
glGenBuffers(1, &ebo);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
// Vertex attributes
|
|
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
|
|
|
glEnableVertexAttribArray(0);
|
|
|
|
|
|
|
|
// Edge Index buffer (EBO for cube edges)
|
|
|
|
glGenBuffers(1, &edge_ebo);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, edge_ebo);
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(edge_indices), edge_indices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
glBindVertexArray(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup instance data for cubes (random positions)
|
2024-10-07 09:05:54 +02:00
|
|
|
void setup_cube_instance_data ()
|
2024-10-01 10:37:37 +02:00
|
|
|
{
|
|
|
|
g_message("[%s] setting up cube instance data...",
|
|
|
|
__func__);
|
|
|
|
|
|
|
|
srand(time(NULL));
|
|
|
|
|
|
|
|
g_message("[%s] random seed initialized",
|
|
|
|
__func__);
|
|
|
|
|
2024-10-07 09:05:54 +02:00
|
|
|
glGenBuffers(1, &cubes_instance_vbo);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, cubes_instance_vbo);
|
2024-10-01 10:37:37 +02:00
|
|
|
|
2024-10-07 09:05:54 +02:00
|
|
|
g_message("[%s] cubes_instance_vbo buffer bound",
|
2024-10-01 10:37:37 +02:00
|
|
|
__func__);
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < num_instances; i++) {
|
|
|
|
glm_mat4_identity(instance_matrices[i]);
|
|
|
|
|
|
|
|
// Randomize positions
|
|
|
|
vec3 position = {
|
|
|
|
(float)(rand() % N_SPACE_SIZE) - 10.0f, // Random x from -10 to 10
|
|
|
|
(float)(rand() % N_SPACE_SIZE) - 10.0f, // Random y from -10 to 10
|
|
|
|
(float)(rand() % N_SPACE_SIZE) - 10.0f // Random z from -10 to 10
|
|
|
|
};
|
|
|
|
|
|
|
|
glm_translate(instance_matrices[i], position);
|
|
|
|
}
|
2024-10-07 09:05:54 +02:00
|
|
|
|
2024-10-01 10:37:37 +02:00
|
|
|
g_message("[%s] generated %d cube instances",
|
|
|
|
__func__,
|
|
|
|
num_instances);
|
|
|
|
|
|
|
|
// Pass the instance matrices to the instance VBO
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, num_instances * sizeof(mat4), instance_matrices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
g_message("[%s] instance matrices passed to VBO",
|
|
|
|
__func__);
|
|
|
|
|
|
|
|
// Setup vertex attribute for the model matrix (location = 2)
|
|
|
|
// Setup vertex attribute for the model matrix (location = 2)
|
|
|
|
glBindVertexArray(vao);
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
glVertexAttribPointer(2 + i, 4, GL_FLOAT, GL_FALSE, sizeof(mat4), (void*)(sizeof(vec4) * i));
|
|
|
|
glEnableVertexAttribArray(2 + i);
|
|
|
|
glVertexAttribDivisor(2 + i, 1); // Tell OpenGL this is per-instance data
|
|
|
|
}
|
|
|
|
glBindVertexArray(0);
|
|
|
|
|
|
|
|
|
|
|
|
g_message("[%s] finalized model matrix vertex attribute ",
|
|
|
|
__func__);
|
|
|
|
}
|
|
|
|
|
2024-10-07 09:05:54 +02:00
|
|
|
void setup_arrow_buffers()
|
|
|
|
{
|
|
|
|
glGenVertexArrays(1, &arrow_vao);
|
|
|
|
glBindVertexArray(arrow_vao);
|
|
|
|
|
|
|
|
// Vertex buffer for arrows
|
|
|
|
glGenBuffers(1, &arrow_vbo);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, arrow_vbo);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(arrow_vertices), arrow_vertices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
// Index buffer for arrows
|
|
|
|
glGenBuffers(1, &arrow_ebo);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, arrow_ebo);
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(arrow_indices), arrow_indices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
// Vertex attributes
|
|
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
|
|
|
glEnableVertexAttribArray(0);
|
|
|
|
|
|
|
|
glBindVertexArray(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void setup_arrow_instance_data()
|
|
|
|
{
|
2024-10-01 10:37:37 +02:00
|
|
|
// Define indices for the arrows (lines from center to cube faces)
|
|
|
|
GLuint arrow_indices[] = {
|
|
|
|
0, 1, // Arrow pointing to right face
|
|
|
|
0, 2, // Arrow pointing upwards
|
|
|
|
0, 3, // Arrow pointing to front face
|
|
|
|
0, 4, // Arrow pointing to left face
|
|
|
|
0, 5, // Arrow pointing downwards
|
|
|
|
0, 6 // Arrow pointing to back face
|
|
|
|
};
|
|
|
|
|
|
|
|
// Generate Vertex Buffer Object for arrows
|
|
|
|
glGenBuffers(1, &arrow_vbo);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, arrow_vbo);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(arrow_vertices), arrow_vertices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
// Generate Element Buffer Object for arrows
|
|
|
|
glGenBuffers(1, &arrow_ebo);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, arrow_ebo);
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(arrow_indices), arrow_indices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
// Define arrow vertex attributes
|
|
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
|
|
|
glEnableVertexAttribArray(0);
|
|
|
|
|
|
|
|
// Arrow instance matrices (positions) -- same as cubes
|
|
|
|
glGenBuffers(1, &arrow_instance_vbo);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, arrow_instance_vbo);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, num_instances * sizeof(mat4), instance_matrices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
// Define vertex attribute for arrow instance matrix
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
glVertexAttribPointer(2 + i, 4, GL_FLOAT, GL_FALSE, sizeof(mat4), (void*)(sizeof(vec4) * i));
|
|
|
|
glEnableVertexAttribArray(2 + i);
|
|
|
|
glVertexAttribDivisor(2 + i, 1); // Make this per-instance data
|
|
|
|
}
|
|
|
|
|
|
|
|
glBindVertexArray(0);
|
|
|
|
}
|
|
|
|
|
2024-10-05 18:32:30 +02:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// GTK / OpenGL zone
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Called when GtkGLArea is realized (OpenGL context is created)
|
|
|
|
static void on_realize(GtkGLArea *area)
|
|
|
|
{
|
|
|
|
g_message("[%s] GLArea realization in progress...", __func__);
|
|
|
|
|
|
|
|
gtk_gl_area_make_current(GTK_GL_AREA(area));
|
|
|
|
|
|
|
|
g_message("[%s] GLArea made current", __func__);
|
|
|
|
|
|
|
|
if (gtk_gl_area_get_error(GTK_GL_AREA(area)) != NULL) return;
|
|
|
|
|
|
|
|
g_message("[%s] OpenGL context successfully created", __func__);
|
|
|
|
|
|
|
|
// Enable depth testing
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
glDepthFunc(GL_LESS);
|
|
|
|
|
|
|
|
// Initialize cglm projection and view matrices
|
|
|
|
glm_mat4_identity(projection);
|
|
|
|
glm_perspective(glm_rad(60.0f), 800.0f / 600.0f, 0.1f, 100.0f, projection);
|
|
|
|
|
|
|
|
glm_mat4_identity(view);
|
|
|
|
glm_lookat((vec3){0.0f, 0.0f, 20.0f}, (vec3){0.0f, 0.0f, 0.0f}, (vec3){0.0f, 1.0f, 0.0f}, view);
|
|
|
|
|
|
|
|
// Initialize rotation matrix
|
|
|
|
glm_mat4_identity(rotation_matrix); // Initialize to identity
|
|
|
|
|
|
|
|
// Create shader program and setup buffers
|
|
|
|
shader_program = create_shader_program();
|
2024-10-07 09:05:54 +02:00
|
|
|
setup_cubes_buffers();
|
2024-10-05 18:32:30 +02:00
|
|
|
setup_arrow_buffers();
|
2024-10-07 09:05:54 +02:00
|
|
|
setup_cube_instance_data();
|
2024-10-05 18:32:30 +02:00
|
|
|
setup_arrow_instance_data(); // Setup arrow data
|
|
|
|
|
|
|
|
// Set the background color
|
|
|
|
glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // Dark gray background
|
|
|
|
glLineWidth(2.0f); // Increase the line width for cube edges
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Called on each frame to render the scene
|
|
|
|
static gboolean on_render(GtkGLArea *area, GdkGLContext *context)
|
|
|
|
{
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glDepthFunc(GL_LESS);
|
|
|
|
|
|
|
|
glUseProgram(shader_program);
|
|
|
|
|
|
|
|
// Pass projection and view matrices to the shader
|
|
|
|
GLint projectionLoc = glGetUniformLocation(shader_program, "projection");
|
|
|
|
GLint viewLoc = glGetUniformLocation(shader_program, "view");
|
|
|
|
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, (const GLfloat *)projection);
|
|
|
|
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, (const GLfloat *)view);
|
|
|
|
|
|
|
|
// Update instance matrices with the accumulated rotation matrix
|
|
|
|
if (rotate) {
|
|
|
|
for (unsigned int i = 0; i < num_instances; i++) {
|
|
|
|
mat4 model;
|
|
|
|
glm_mat4_copy(rotation_matrix, model); // Copy the accumulated rotation matrix
|
|
|
|
|
|
|
|
// Combine the rotation with the instance's individual matrix
|
|
|
|
glm_mul(model, instance_matrices[i], instance_matrices[i]);
|
|
|
|
}
|
|
|
|
rotate = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the instance matrix buffer with the modified instance matrices
|
2024-10-07 09:05:54 +02:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, cubes_instance_vbo);
|
2024-10-05 18:32:30 +02:00
|
|
|
glBufferSubData(GL_ARRAY_BUFFER, 0, num_instances * sizeof(mat4), instance_matrices);
|
|
|
|
|
|
|
|
// Draw the cubes with the updated instance matrices
|
|
|
|
glUniform4f(glGetUniformLocation(shader_program, "FragColor"), 0.4f, 0.6f, 0.9f, 1.0f);
|
|
|
|
glBindVertexArray(vao);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
|
|
glDrawElementsInstanced(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0, num_instances);
|
|
|
|
|
|
|
|
// Draw the cube edges
|
|
|
|
glUniform4f(glGetUniformLocation(shader_program, "FragColor"), 1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, edge_ebo);
|
|
|
|
glDrawElementsInstanced(GL_LINES, 24, GL_UNSIGNED_INT, 0, num_instances);
|
|
|
|
|
|
|
|
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
|
|
|
|
// Set arrow color (e.g., red) and draw arrows
|
|
|
|
glUniform4f(glGetUniformLocation(shader_program, "FragColor"), 1.0f, 0.0f, 0.0f, 1.0f); // Red, opaque
|
|
|
|
|
|
|
|
// Bind the arrow VAO and use the same transformation matrices
|
|
|
|
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, (const GLfloat *)projection);
|
|
|
|
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, (const GLfloat *)view);
|
|
|
|
|
|
|
|
// Draw the arrows
|
2024-10-07 09:05:54 +02:00
|
|
|
glBindVertexArray(arrow_vao);
|
2024-10-05 18:32:30 +02:00
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, arrow_ebo);
|
|
|
|
glDrawElementsInstanced(GL_LINES, 12, GL_UNSIGNED_INT, 0, num_instances);
|
|
|
|
|
|
|
|
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glBindVertexArray(0);
|
|
|
|
|
|
|
|
// Reset the rotation matrix to prevent continuous accumulation
|
|
|
|
glm_mat4_identity(rotation_matrix);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// GTK ZONE
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// Called when GtkApplication is activated
|
|
|
|
static void on_activate (GtkApplication *app, gpointer user_data)
|
|
|
|
{
|
|
|
|
g_message("[%s] activation in progress...",
|
|
|
|
__func__);
|
|
|
|
window = gtk_application_window_new(app);
|
|
|
|
gtk_window_set_title(GTK_WINDOW(window), "OpenGL 4.6 experiment");
|
|
|
|
gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
|
|
|
|
|
|
|
|
g_message("[%s] window created",
|
|
|
|
__func__);
|
|
|
|
|
|
|
|
GtkWidget *gl_area = gtk_gl_area_new();
|
|
|
|
gtk_gl_area_set_required_version(GTK_GL_AREA(gl_area), 4, 6);
|
|
|
|
gtk_gl_area_set_has_depth_buffer(GTK_GL_AREA(gl_area), TRUE);
|
|
|
|
gtk_gl_area_set_auto_render(GTK_GL_AREA(gl_area), TRUE);
|
|
|
|
|
|
|
|
g_message("[%s] GLArea created",
|
|
|
|
__func__);
|
|
|
|
|
|
|
|
g_signal_connect(gl_area, "realize", G_CALLBACK(on_realize), NULL);
|
|
|
|
g_signal_connect(gl_area, "render", G_CALLBACK(on_render), NULL);
|
|
|
|
|
|
|
|
g_message("[%s] GLArea signals linked",
|
|
|
|
__func__);
|
|
|
|
|
|
|
|
// Create and add motion event controller
|
|
|
|
GtkEventController *motion_controller = gtk_event_controller_motion_new();
|
|
|
|
g_signal_connect(motion_controller, "motion", G_CALLBACK(on_mouse_move), gl_area); // Pass gl_area as user_data
|
|
|
|
gtk_widget_add_controller(gl_area, GTK_EVENT_CONTROLLER(motion_controller));
|
|
|
|
|
|
|
|
// Create and add scroll event controller
|
|
|
|
GtkEventController *scroll_controller = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES);
|
|
|
|
g_signal_connect(scroll_controller, "scroll", G_CALLBACK(on_mouse_scroll), gl_area); // Pass gl_area as user_data
|
|
|
|
gtk_widget_add_controller(gl_area, GTK_EVENT_CONTROLLER(scroll_controller));
|
|
|
|
|
|
|
|
g_message("[%s] Mouse events now linked to signals",
|
|
|
|
__func__);
|
|
|
|
|
|
|
|
gtk_window_set_child(GTK_WINDOW(window), gl_area);
|
|
|
|
gtk_widget_set_visible(window, TRUE);
|
|
|
|
|
|
|
|
g_message("[%s] GLArea set visible",
|
|
|
|
__func__);
|
|
|
|
}
|
|
|
|
|
2024-10-01 10:37:37 +02:00
|
|
|
// Main Function
|
|
|
|
int main (int argc, char *argv[])
|
|
|
|
{
|
|
|
|
GtkApplication *app;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
g_message("[%s] welcome in this experiment with OpenGL 4.6 in GTK4",
|
|
|
|
__func__);
|
|
|
|
|
|
|
|
// Create a new GtkApplication
|
|
|
|
app = gtk_application_new("org.example.instanced_cubes", G_APPLICATION_DEFAULT_FLAGS);
|
|
|
|
|
|
|
|
g_message("[%s] application created",
|
|
|
|
__func__);
|
|
|
|
// Connect the "activate" signal to the activate callback
|
|
|
|
g_signal_connect(app, "activate", G_CALLBACK(on_activate), NULL);
|
|
|
|
|
|
|
|
g_message("[%s] activate callback linked",
|
|
|
|
__func__);
|
|
|
|
|
|
|
|
// Run the application
|
|
|
|
status = g_application_run(G_APPLICATION(app), argc, argv);
|
|
|
|
|
|
|
|
g_message("[%s] application terminated",
|
|
|
|
__func__);
|
|
|
|
|
|
|
|
// Free the application object
|
|
|
|
g_object_unref(app);
|
|
|
|
|
|
|
|
g_message("[%s] bye!",
|
|
|
|
__func__);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
2024-10-07 09:05:54 +02:00
|
|
|
|