const vec3 WORLD_UP = {0.0f, 0.0f, 1.0f}; typedef struct camera { vec3 position; vec3 front; vec3 right; float yaw; float pitch; mat4 view_mat; } camera_t; void camera_update_view(camera_t* camera) { // Create orthonormal basis for camera orientation vec3 target = vec3_add(camera->position, camera->front); vec3 front = vec3_normalize(vec3_sub(target, camera->position)); vec3 side = vec3_normalize(vec3_cross(front, WORLD_UP)); vec3 up = vec3_cross(side, front); // Create an inverse rotation matrix // Orthogonal inverse is a simple transpose where the columns become the rows // +X: Right // +Y: Forward // +Z: Up mat4 view_mat = { side.x, side.y, side.z, -vec3_dot(camera->position, side), up.x, up.y, up.z, -vec3_dot(camera->position, up), front.x, front.y, front.z, -vec3_dot(camera->position, front), 0.0f, 0.0f, 0.0f, 1.0f }; camera->view_mat = view_mat; } camera_t camera_create(vec3 position) { camera_t result = { .position = position, .front = {0.0f, 1.0f, 0.0f}, .right = {1.0f, 0.0f, 0.0f}, .yaw = 90.0f}; camera_update_view(&result); return result; } void camera_move(camera_t* camera, vec3 velocity) { // Preserve Z float position_z = camera->position.z; // Forward/Backward camera->position = vec3_add(camera->position, vec3_mult(camera->front, velocity.y)); // Left/Right camera->right = vec3_cross(camera->front, WORLD_UP); camera->position = vec3_add(camera->position, vec3_mult(vec3_normalize(camera->right), velocity.x)); // Restore Z camera->position.z = position_z; } void camera_rotate(camera_t* camera, float offset_x, float offset_y) { camera->yaw += offset_x; camera->pitch += offset_y; // Limit the camera's up/down motion if (camera->pitch > 60.0f) { camera->pitch = 60.0f; } else if (camera->pitch < -60.0f) { camera->pitch = -60.0f; } // Ensure camera yaw circles around 360 degrees rather than increases indefinitely if (camera->yaw > 360.0f) { camera->yaw -= 360.0f; } else if (camera->yaw < -360.0f) { camera->yaw += 360.0f; } float yaw_rad = DEG_TO_RAD(camera->yaw); float pitch_rad = DEG_TO_RAD(camera->pitch); camera->front.x = cosf(yaw_rad) * cosf(pitch_rad); camera->front.y = sinf(yaw_rad) * cosf(pitch_rad); camera->front.z = sinf(pitch_rad); camera->front = vec3_normalize(camera->front); }