suicmez/libsuicmez/libsuicmez.c

617 lines
16 KiB
C
Raw Normal View History

2025-12-17 13:16:53 +05:30
#include "raylib.h"
#include "libsuicmez.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// Simple garbage collection allocator
// This is a basic implementation that tracks allocations
// For production, consider using a more robust GC library
#define MAX_TRACKED_ALLOCATIONS 10000
typedef struct {
void* ptr;
size_t size;
int in_use;
} AllocationInfo;
static AllocationInfo tracked_allocations[MAX_TRACKED_ALLOCATIONS];
static int allocation_count = 0;
static const char* last_error = NULL;
// Simple linear search to find allocation
static int find_allocation(void* ptr) {
for (int i = 0; i < allocation_count; i++) {
if (tracked_allocations[i].ptr == ptr && tracked_allocations[i].in_use) {
return i;
}
}
return -1;
}
// GC-aware allocator
void* gc_suic_alloc(size_t size) {
if (size == 0) {
return NULL;
}
void* ptr = malloc(size);
if (!ptr) {
last_error = "Memory allocation failed";
return NULL;
}
// Track this allocation
if (allocation_count < MAX_TRACKED_ALLOCATIONS) {
tracked_allocations[allocation_count].ptr = ptr;
tracked_allocations[allocation_count].size = size;
tracked_allocations[allocation_count].in_use = 1;
allocation_count++;
} else {
last_error = "Too many tracked allocations";
free(ptr);
return NULL;
}
return ptr;
}
// Free memory (called by gc_suic_free)
void suic_gc_free(void* ptr) {
if (!ptr) return;
int idx = find_allocation(ptr);
if (idx >= 0) {
tracked_allocations[idx].in_use = 0;
free(ptr);
}
}
// Error handling
const char *suic_last_error(void) {
return last_error ? last_error : "No error";
}
void suic_clear_error(void) {
last_error = NULL;
}
// Logging
static suic_log_level current_log_level = SUIC_LOG_ALL;
void suic_set_log_level(suic_log_level level) {
current_log_level = level;
}
// Window management
bool suic_init_window(int width, int height, const char *title) {
InitWindow(width, height, title);
return !WindowShouldClose();
}
void suic_close_window(void) {
CloseWindow();
}
bool suic_window_should_close(void) {
return WindowShouldClose();
}
void suic_set_target_fps(int fps) {
SetTargetFPS(fps);
}
// Drawing
void suic_begin_drawing(void) {
BeginDrawing();
}
void suic_end_drawing(void) {
EndDrawing();
}
void suic_clear_background(unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
ClearBackground((Color){r, g, b, a});
}
// 3D mode
Camera3D suic_to_rl_camera3d(suic_camera3d cam) {
return (Camera3D){
.position = suic_to_rl_vec3(cam.position),
.target = suic_to_rl_vec3(cam.target),
.up = suic_to_rl_vec3(cam.up),
.fovy = cam.fovy,
.projection = cam.projection
};
}
void suic_begin_mode3d(float pos_x, float pos_y, float pos_z, float target_x, float target_y, float target_z, float up_x, float up_y, float up_z, float fovy, int projection) {
Camera3D camera = {
.position = (Vector3){pos_x, pos_y, pos_z},
.target = (Vector3){target_x, target_y, target_z},
.up = (Vector3){up_x, up_y, up_z},
.fovy = fovy,
.projection = projection
};
BeginMode3D(camera);
}
void suic_end_mode3d(void) {
EndMode3D();
}
// 3D Drawing
void suic_draw_cube(float x, float y, float z, float width, float height, float length, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
DrawCube((Vector3){x, y, z}, width, height, length, (Color){r, g, b, a});
}
void suic_draw_cube_wires(float x, float y, float z, float width, float height, float length, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
DrawCubeWires((Vector3){x, y, z}, width, height, length, (Color){r, g, b, a});
}
// Input
bool suic_is_key_down(int key) {
return IsKeyDown(key);
}
bool suic_is_mouse_button_down(int b) {
return IsMouseButtonDown(b);
}
suic_vec2 suic_get_mouse_position(void) {
Vector2 pos = GetMousePosition();
return (suic_vec2){pos.x, pos.y};
}
// Image and Texture
suic_image_handle suic_image_load(const char *path) {
Image img = LoadImage(path);
return (suic_image_handle){img, (img.data != NULL) ? true : false};
}
void suic_image_free(suic_image_handle *img) {
if (img && img->valid) {
UnloadImage(img->value);
img->valid = false;
}
}
suic_texture_handle suic_texture_load(const char *path) {
Texture2D tex = LoadTexture(path);
return (suic_texture_handle){tex, (tex.id != 0) ? true : false};
}
suic_texture_handle suic_texture_from_image(suic_image_handle img) {
if (!img.valid) {
return (suic_texture_handle){0, false};
}
Texture2D tex = LoadTextureFromImage(img.value);
return (suic_texture_handle){tex, (tex.id != 0) ? true : false};
}
void suic_texture_free(suic_texture_handle *tex) {
if (tex && tex->valid) {
UnloadTexture(tex->value);
tex->valid = false;
}
}
void suic_draw_texture(suic_texture_handle tex, int x, int y, unsigned char r,
unsigned char g, unsigned char b, unsigned char a) {
if (tex.valid) {
DrawTexture(tex.value, x, y, (Color){r, g, b, a});
}
}
// Audio
bool suic_audio_init(void) {
InitAudioDevice();
return IsAudioDeviceReady();
}
void suic_audio_close(void) {
CloseAudioDevice();
}
suic_sound_handle suic_sound_load(const char *path) {
Sound snd = LoadSound(path);
return (suic_sound_handle){snd, (snd.frameCount > 0) ? true : false};
}
void suic_sound_play(suic_sound_handle snd) {
if (snd.valid) {
PlaySound(snd.value);
}
}
void suic_sound_set_volume(suic_sound_handle snd, float volume) {
if (snd.valid) {
SetSoundVolume(snd.value, volume);
}
}
void suic_sound_free(suic_sound_handle *snd) {
if (snd && snd->valid) {
UnloadSound(snd->value);
snd->valid = false;
}
}
// Raycasting
suic_ray suic_get_mouse_ray(suic_vec2 mouse, suic_camera3d cam) {
Ray r = GetMouseRay(
(Vector2){mouse.x, mouse.y},
suic_to_rl_camera3d(cam)
);
return (suic_ray){
.origin = (suic_vec3){r.position.x, r.position.y, r.position.z},
.direction = (suic_vec3){r.direction.x, r.direction.y, r.direction.z}
};
}
suic_rayhit suic_raycast_aabb(suic_ray ray, suic_aabb box) {
Ray r = (Ray){
.position = (Vector3){ray.origin.x, ray.origin.y, ray.origin.z},
.direction = (Vector3){ray.direction.x, ray.direction.y, ray.direction.z}
};
BoundingBox bbox = (BoundingBox){
.min = (Vector3){box.min.x, box.min.y, box.min.z},
.max = (Vector3){box.max.x, box.max.y, box.max.z}
};
RayCollision collision = GetRayCollisionBox(r, bbox);
return (suic_rayhit){
.hit = collision.hit,
.distance = collision.distance,
.point = (suic_vec3){collision.point.x, collision.point.y, collision.point.z},
.normal = (suic_vec3){collision.normal.x, collision.normal.y, collision.normal.z}
};
}
// ODE Physics Engine Implementation
static int ode_initialized = 0;
void suic_ode_init(void) {
if (!ode_initialized) {
dInitODE();
ode_initialized = 1;
}
}
void suic_ode_close(void) {
if (ode_initialized) {
dCloseODE();
ode_initialized = 0;
}
}
suic_ode_world_id suic_ode_world_create(void) {
return dWorldCreate();
}
void suic_ode_world_destroy(suic_ode_world_id world) {
if (world) {
dWorldDestroy(world);
}
}
void suic_ode_world_set_gravity(suic_ode_world_id world, dReal x, dReal y, dReal z) {
if (world) {
dWorldSetGravity(world, x, y, z);
}
}
void suic_ode_world_step(suic_ode_world_id world, dReal stepsize) {
if (world) {
dWorldStep(world, stepsize);
}
}
// Body management
suic_ode_body_id suic_ode_body_create(suic_ode_world_id world) {
return dBodyCreate(world);
}
void suic_ode_body_destroy(suic_ode_body_id body) {
if (body) {
dBodyDestroy(body);
}
}
void suic_ode_body_set_position(suic_ode_body_id body, dReal x, dReal y, dReal z) {
if (body) {
dBodySetPosition(body, x, y, z);
}
}
void suic_ode_body_set_linear_vel(suic_ode_body_id body, dReal x, dReal y, dReal z) {
if (body) {
dBodySetLinearVel(body, x, y, z);
}
}
void suic_ode_body_get_position(suic_ode_body_id body, float *x, float *y, float *z) {
if (body) {
const dReal *pos = dBodyGetPosition(body);
*x = (float)pos[0];
*y = (float)pos[1];
*z = (float)pos[2];
}
}
// Mass and geometry
void suic_ode_body_set_box_mass(suic_ode_body_id body, dReal density, dReal lx, dReal ly, dReal lz) {
if (body) {
dMass mass;
dMassSetBox(&mass, density, lx, ly, lz);
dBodySetMass(body, &mass);
}
}
suic_ode_geom_id suic_ode_create_box_geom(suic_ode_space_id space, dReal lx, dReal ly, dReal lz) {
return dCreateBox(space, lx, ly, lz);
}
suic_ode_geom_id suic_ode_create_plane_geom(suic_ode_space_id space, dReal a, dReal b, dReal c, dReal d) {
return dCreatePlane(space, a, b, c, d);
}
void suic_ode_geom_set_body(suic_ode_geom_id geom, suic_ode_body_id body) {
if (geom) {
dGeomSetBody(geom, body);
}
}
void suic_ode_geom_destroy(suic_ode_geom_id geom) {
if (geom) {
dGeomDestroy(geom);
}
}
// Collision space
suic_ode_space_id suic_ode_simple_space_create(suic_ode_space_id parent) {
return dSimpleSpaceCreate(parent);
}
void suic_ode_space_destroy(suic_ode_space_id space) {
if (space) {
dSpaceDestroy(space);
}
}
// ============================================================================
// COLLISION DETECTION AND CONTACT JOINTS
// ============================================================================
// Global contact configuration
static dReal contact_max_force = 10000.0;
static dReal contact_erp = 0.8; // Error reduction parameter
static dReal contact_cfm = 0.00001; // Constraint force mixing
// Store world and contactgroup for collision callback
typedef struct {
dWorldID world;
dJointGroupID contactgroup;
} CollisionContext;
// Collision callback for detecting contacts between geometries
static void near_callback(void *data, dGeomID o1, dGeomID o2) {
CollisionContext *ctx = (CollisionContext*)data;
dBodyID b1 = dGeomGetBody(o1);
dBodyID b2 = dGeomGetBody(o2);
// Don't process if both bodies are static or same body
if ((b1 && b2 && b1 == b2) || (!b1 && !b2)) {
return;
}
const int N = 4; // Max contacts per collision
dContact contact[N];
// Check for collisions
int n = dCollide(o1, o2, N, &contact[0].geom, sizeof(dContact));
if (n > 0) {
for (int i = 0; i < n; i++) {
// Set contact properties
contact[i].surface.mu = 0.5; // Friction coefficient
contact[i].surface.mu2 = 0.5;
contact[i].surface.bounce = 0.1; // Bounciness
contact[i].surface.bounce_vel = 0.1;
contact[i].surface.mode = dContactBounce | dContactSoftERP | dContactSoftCFM;
contact[i].surface.soft_erp = contact_erp;
contact[i].surface.soft_cfm = contact_cfm;
// Create contact joint
dJointID c = dJointCreateContact(ctx->world, ctx->contactgroup, &contact[i]);
dJointAttach(c, b1, b2);
}
}
}
void suic_ode_space_collide(suic_ode_world_id world, suic_ode_space_id space, suic_ode_joint_group_id contactgroup) {
if (space && contactgroup && world) {
CollisionContext ctx = {.world = world, .contactgroup = contactgroup};
dSpaceCollide(space, (void*)&ctx, &near_callback);
}
}
suic_ode_joint_group_id suic_ode_joint_group_create(int max_size) {
return dJointGroupCreate(max_size);
}
void suic_ode_joint_group_destroy(suic_ode_joint_group_id group) {
if (group) {
dJointGroupDestroy(group);
}
}
void suic_ode_joint_group_empty(suic_ode_joint_group_id group) {
if (group) {
dJointGroupEmpty(group);
}
}
void suic_ode_set_contact_max_force(dReal force) {
contact_max_force = force;
}
void suic_ode_set_contact_erp(dReal erp) {
contact_erp = erp;
}
void suic_ode_set_contact_cfm(dReal cfm) {
contact_cfm = cfm;
}
// ============================================================================
// ADDITIONAL BODY FUNCTIONS
// ============================================================================
void suic_ode_body_get_linear_vel(suic_ode_body_id body, float *x, float *y, float *z) {
if (body) {
const dReal *vel = dBodyGetLinearVel(body);
*x = (float)vel[0];
*y = (float)vel[1];
*z = (float)vel[2];
}
}
void suic_ode_body_get_rotation(suic_ode_body_id body, float *q_w, float *q_x, float *q_y, float *q_z) {
if (body) {
const dReal *q = dBodyGetQuaternion(body);
*q_w = (float)q[0];
*q_x = (float)q[1];
*q_y = (float)q[2];
*q_z = (float)q[3];
}
}
void suic_ode_body_set_rotation(suic_ode_body_id body, dReal q_w, dReal q_x, dReal q_y, dReal q_z) {
if (body) {
dQuaternion q;
q[0] = q_w;
q[1] = q_x;
q[2] = q_y;
q[3] = q_z;
dBodySetQuaternion(body, q);
}
}
// ============================================================================
// GAME OBJECT SYSTEM - Unified Physics & Rendering
// ============================================================================
suic_game_object* suic_game_object_create_box(
suic_ode_world_id world,
suic_ode_space_id space,
float x, float y, float z,
float width, float height, float length,
float density,
unsigned char r, unsigned char g, unsigned char b, unsigned char a
) {
suic_game_object* obj = (suic_game_object*)gc_suic_alloc(sizeof(suic_game_object));
if (!obj) return NULL;
// Create physics body
obj->body = dBodyCreate(world);
if (!obj->body) {
suic_gc_free(obj);
return NULL;
}
// Set position
dBodySetPosition(obj->body, x, y, z);
// Create and attach geometry
obj->geom = dCreateBox(space, width, height, length);
if (!obj->geom) {
dBodyDestroy(obj->body);
suic_gc_free(obj);
return NULL;
}
dGeomSetBody(obj->geom, obj->body);
// Set mass
dMass mass;
dMassSetBox(&mass, density, width, height, length);
dBodySetMass(obj->body, &mass);
// Store rendering data
obj->width = width;
obj->height = height;
obj->length = length;
obj->r = r;
obj->g = g;
obj->b = b;
obj->a = a;
obj->shape_type = 0; // 0 = box
return obj;
}
void suic_game_object_destroy(suic_game_object* obj) {
if (obj) {
if (obj->geom) {
dGeomDestroy(obj->geom);
}
if (obj->body) {
dBodyDestroy(obj->body);
}
suic_gc_free(obj);
}
}
void suic_game_object_set_position(suic_game_object* obj, float x, float y, float z) {
if (obj && obj->body) {
dBodySetPosition(obj->body, x, y, z);
}
}
void suic_game_object_get_position(suic_game_object* obj, float *x, float *y, float *z) {
if (obj && obj->body) {
const dReal *pos = dBodyGetPosition(obj->body);
*x = (float)pos[0];
*y = (float)pos[1];
*z = (float)pos[2];
}
}
void suic_game_object_set_velocity(suic_game_object* obj, float x, float y, float z) {
if (obj && obj->body) {
dBodySetLinearVel(obj->body, x, y, z);
}
}
void suic_game_object_get_velocity(suic_game_object* obj, float *x, float *y, float *z) {
if (obj && obj->body) {
const dReal *vel = dBodyGetLinearVel(obj->body);
*x = (float)vel[0];
*y = (float)vel[1];
*z = (float)vel[2];
}
}
void suic_game_object_draw(suic_game_object* obj) {
if (obj && obj->body) {
float x, y, z;
suic_game_object_get_position(obj, &x, &y, &z);
if (obj->shape_type == 0) { // Box
DrawCube(
(Vector3){x, y, z},
obj->width, obj->height, obj->length,
(Color){obj->r, obj->g, obj->b, obj->a}
);
// Draw wireframe for visual clarity
DrawCubeWires(
(Vector3){x, y, z},
obj->width, obj->height, obj->length,
(Color){0, 0, 0, 200}
);
}
}
}