Issue with Sphere Rotation on Sega Saturn Using Jo Engine

I'm trying to build a simple low-poly sphere that is "playable" using a Sega Saturn gamepad (similar to the game Marble Madness) with Jo Engine
The code I'm using is as follows:

#include <jo/jo.h>
#include "sphere.h"

jo_camera cam;
jo_img_8bits floor_image;
jo_palette image_pal;
jo_pos3Df sphere_position = {0, 50, 0}; // Inicializando a posição da esfera
jo_pos3Df pos;
jo_rot3Df rot;
jo_rot3Df sphere_rotation = {0.0f, 0.0f, 0.0f}; // Inicializando a rotação da esfera

void my_draw(void) {
jo_printf(12, 1, "*Simple 3D demo*");
jo_3d_camera_look_at(&cam);

jo_3d_push_matrix();
{
jo_3d_translate_matrixf(sphere_position.x, sphere_position.y, sphere_position.z);
jo_3d_rotate_matrix_rad(sphere_rotation.rx, sphere_rotation.ry, sphere_rotation.rz);

display_sphere_mesh();
jo_3d_set_mesh_texture(&Meshsphere, 0);
}
jo_3d_pop_matrix();
}

void my_gamepad(void) {
const float movement_speed = 1.0f;
const float rotation_speed = JO_DEG_TO_RAD(5.0f);
const float DIAGONAL_FACTOR = 0.707f; //(1/sqrt(2))

float delta_x = 0.0f;
float delta_z = 0.0f;
float delta_rx = 0.0f;
float delta_ry = 0.0f;

switch (jo_get_input_direction_pressed(0))
{
case LEFT:
delta_x = -movement_speed;
delta_ry = -rotation_speed;
break;
case RIGHT:
delta_x = movement_speed;
delta_ry = rotation_speed;
break;
case UP:
delta_z = movement_speed;
delta_rx = -rotation_speed;
break;
case DOWN:
delta_z = -movement_speed;
delta_rx = rotation_speed;
break;
case UP_LEFT:
delta_x = -movement_speed * DIAGONAL_FACTOR;
delta_z = movement_speed * DIAGONAL_FACTOR;
delta_ry = -rotation_speed * DIAGONAL_FACTOR;
delta_rx = -rotation_speed * DIAGONAL_FACTOR;
break;
case UP_RIGHT:
delta_x = movement_speed * DIAGONAL_FACTOR;
delta_z = movement_speed * DIAGONAL_FACTOR;
delta_ry = rotation_speed * DIAGONAL_FACTOR;
delta_rx = -rotation_speed * DIAGONAL_FACTOR;
break;
case DOWN_LEFT:
delta_x = -movement_speed * DIAGONAL_FACTOR;
delta_z = -movement_speed * DIAGONAL_FACTOR;
delta_ry = -rotation_speed * DIAGONAL_FACTOR;
delta_rx = rotation_speed * DIAGONAL_FACTOR;
break;
case DOWN_RIGHT:
delta_x = movement_speed * DIAGONAL_FACTOR;
delta_z = -movement_speed * DIAGONAL_FACTOR;
delta_ry = rotation_speed * DIAGONAL_FACTOR;
delta_rx = rotation_speed * DIAGONAL_FACTOR;
break;
case NONE:
return;
}

sphere_position.x += delta_x;
sphere_position.z += delta_z;
sphere_rotation.rx += delta_rx;
sphere_rotation.ry += delta_ry;
}

void load_floor_texture(void) {
pos.x = 800.0;
pos.y = 800.0;
pos.z = -35.0;

rot.rx = JO_DEG_TO_RAD(90.0);
rot.ry = JO_DEG_TO_RAD(0.0);
rot.rz = JO_DEG_TO_RAD(0.0);

jo_core_tv_off();
jo_enable_background_3d_plane(JO_COLOR_Black);
jo_tga_8bits_loader(&floor_image, JO_ROOT_DIR, "FLOOR.TGA", 0);
jo_background_3d_plane_a_img(&floor_image, image_pal.id, true, true);
jo_core_tv_on();
}

jo_palette *my_tga_palette_handling(void) {
jo_create_palette(&image_pal);
return (&image_pal);
}

void draw_3d_planes(void) {
jo_3d_push_matrix();
{
jo_3d_rotate_matrix_rad(rot.rx, rot.ry, rot.rz);
jo_3d_translate_matrixf(pos.x, pos.y, pos.z);
jo_background_3d_plane_a_draw(true);
}
jo_3d_pop_matrix();
}

void jo_main(void) {
jo_core_init(JO_COLOR_Black);
jo_set_tga_palette_handling(my_tga_palette_handling);
jo_sprite_add_tga(JO_ROOT_DIR, "ROCK.TGA", JO_COLOR_Transparent);
load_floor_texture();
jo_3d_camera_init(&cam);
jo_core_add_callback(my_draw);
jo_core_add_callback(my_gamepad);
jo_core_add_callback(draw_3d_planes);
jo_core_run();
}

the result is:

https://imgur.com/rytTvAq

As you can see, the low-poly sphere is not rotating like a ball when I move the gamepad to the left or right. Instead, it seems to pivot awkwardly or rotate around the wrong axis.

How can I properly implement rotation for a sphere using Jo Engine so that it rotates smoothly like a rolling ball in the correct direction based on the gamepad input?
 
when you push left/right you're rotating the Y axis, which is actually "up"? think of the X/Y axis as the TV screen, and the Z axis as going into or out of the screen. the X axis follows the width of your screen, and the Y axis follows the height.
 
Back
Top