-
-
Notifications
You must be signed in to change notification settings - Fork 1
3D Physics Engine
The --gui 3D mode renders a real-time physically simulated lottery drum using OpenGL (fixed-function pipeline, no GLU or external math library).
When --gui 3D is passed, gui_opengl.c takes over and runs the full draw animation:
- Balls fall into a spinning drum.
- The drum rotates; balls collide with the shell and each other.
- One ball per pick is ejected from the drum and placed in the result row below.
- After all main numbers are drawn, the extra drum (if any) runs its own cycle.
Games with extra numbers (Eurojackpot, Lotto 6aus49) show two drums side by side:
| Drum | Position (world X) | Radius | Ball range |
|---|---|---|---|
| Main drum | −390 | 250 | Main number range |
| Extra drum | +360 | 150 | Extra number range |
The extra drum starts in a waiting state and only begins spinning after the main drum has finished all its picks.
Each drum is a self-contained DrumInstance:
typedef struct {
/* Balls */
DrumBall *balls;
int ball_count;
int ball_min; /* number label offset */
/* Geometry */
float drum_radius;
float world_x, world_y;
/* Animation state machine */
DrumPhase phase;
float sim_time;
float drum_rotation_x, drum_rotation_y, drum_rotation_z;
float stop_omega;
/* Draw cycle */
int picks_done, picks_total;
float phase_timer;
float spin_before_pick;
int current_pick_idx;
int waiting; /* extra drum waits for main to finish */
/* Results */
int draw_numbers[16];
int result_balls[16];
int result_ball_count;
/* Textures */
GLuint *number_textures;
int texture_count;
} DrumInstance;| Constant | Value | Purpose |
|---|---|---|
DRUM_RADIUS |
250.0 | Main drum shell radius |
BALL_RADIUS |
18.0 | Radius of each numbered ball |
BALL_ROLLING_FRICTION |
0.45 | Rolling resistance coefficient |
BALL_ANGULAR_DAMPING |
0.985 | Angular velocity decay per frame |
BALL_SHELL_FRICTION |
0.22 | Friction between ball and drum shell |
Each ball tracks both linear and angular state:
typedef struct {
float x, y, z; /* position */
float vx, vy, vz; /* linear velocity */
float rot_x, rot_y, rot_z; /* rotation angles (degrees) */
float omega_x, omega_y, omega_z; /* angular velocity (deg/frame) */
int number; /* displayed label */
int picked; /* 1 = ejected from drum */
} DrumBall;-
Gravity — apply
GRAVITYdownward acceleration to each ball. -
Shell collision — if
|pos| > drum_radius - BALL_RADIUS, reflect velocity off the shell normal and applyBALL_SHELL_FRICTIONtangential damping. Transfer spin via cross-product (tangent × normal). - Ball–ball collision — O(n²) pairwise check. On penetration, apply impulse along the contact normal. Compute tangential impulse and update angular velocities of both balls.
-
Rolling friction —
apply_rolling_friction()damps angular velocity byBALL_ANGULAR_DAMPINGper frame and converts spin to linear motion proportional toBALL_ROLLING_FRICTION.
The drum shell itself rotates around its local X and Z axes. Ball positions are updated in world space; the shell rotation modulates the effective gravity direction experienced by balls inside, creating the tumbling effect.
Each drum runs an independent state machine:
FALLING ──▶ ROTATING ──▶ STOPPING ──▶ PICK_PAUSE ──▶ (repeat for each pick)
└──▶ DRAW_COMPLETE
| Phase | Description |
|---|---|
FALLING |
Balls spawn above the drum and fall in under gravity |
ROTATING |
Drum spins at full speed; balls tumble for spin_before_pick seconds |
STOPPING |
Drum decelerates smoothly |
PICK_PAUSE |
One ball is ejected and placed in the result row; brief pause |
DRAW_COMPLETE |
All picks done; drum idles |
The extra drum's FALLING phase is blocked by waiting = 1 until the main drum reaches DRAW_COMPLETE.
Ball number labels are rendered as SDL2 TTF surfaces, uploaded to OpenGL textures at startup by init_ball_textures(). Each ball number maps directly to a GLuint texture ID.
For each drum:
- Draw the transparent drum shell (cylinder with alpha blending).
- For each non-picked ball: push matrix, translate to ball position, apply
glRotatefusingomega_x/y/zfor tumbling animation, bind number texture, draw sphere quad. - Draw the result row below the drum: picked balls rendered in a horizontal slot layout at
Y = -(drum_radius + BALL_RADIUS * 3.5).
Single fixed camera at Z = −808, looking at origin. Both drums fit in the viewport at the default window size (1280 × 720).
The main drum optionally uses GPU-based physics via an OpenGL compute shader, enabled by setting the LOTTO_GPU_COMPUTE environment variable. The extra drum always uses CPU physics (≤ 12 balls, negligible cost).
LOTTO_GPU_COMPUTE=1 ./build/open-lotto --game "Eurojackpot" --gui 3D