nitro-engine/source/NEPhysics.c
Antonio Niño Díaz 82171bbf69 chore: Simplify copyright years in notices
Instead of listing every individual year, keep only the first and last
years.
2024-03-09 01:42:29 +00:00

457 lines
13 KiB
C

// SPDX-License-Identifier: MIT
//
// Copyright (c) 2008-2022 Antonio Niño Díaz
//
// This file is part of Nitro Engine
#include "NEMain.h"
/// @file NEPhysics.c
static NE_Physics **NE_PhysicsPointers;
static bool ne_physics_system_inited = false;
static int NE_MAX_PHYSICS;
NE_Physics *NE_PhysicsCreate(NE_PhysicsTypes type)
{
if (!ne_physics_system_inited)
{
NE_DebugPrint("System not initialized");
return NULL;
}
// TODO
if (type == NE_BoundingSphere)
{
NE_DebugPrint("Bounding spheres not supported");
return NULL;
}
if (type == NE_Dot)
{
NE_DebugPrint("Dots not supported");
return NULL;
}
NE_Physics *temp = calloc(1, sizeof(NE_Physics));
if (temp == NULL)
{
NE_DebugPrint("Not enough memory");
return NULL;
}
int i = 0;
while (1)
{
if (i == NE_MAX_PHYSICS)
{
free(temp);
NE_DebugPrint("No free slots");
return NULL;
}
if (NE_PhysicsPointers[i] == NULL)
{
NE_PhysicsPointers[i] = temp;
break;
}
i++;
}
temp->type = type;
temp->keptpercent = 50;
temp->enabled = true;
temp->physicsgroup = 0;
temp->oncollision = NE_ColNothing;
return temp;
}
void NE_PhysicsDelete(NE_Physics *pointer)
{
if (!ne_physics_system_inited)
return;
NE_AssertPointer(pointer, "NULL pointer");
int i = 0;
while (1)
{
if (i == NE_MAX_PHYSICS)
{
NE_DebugPrint("Object not found");
return;
}
if (NE_PhysicsPointers[i] == pointer)
{
NE_PhysicsPointers[i] = NULL;
free(pointer);
return;
}
i++;
}
}
void NE_PhysicsDeleteAll(void)
{
if (!ne_physics_system_inited)
return;
for (int i = 0; i < NE_MAX_PHYSICS; i++)
NE_PhysicsDelete(NE_PhysicsPointers[i]);
}
int NE_PhysicsSystemReset(int max_objects)
{
if (ne_physics_system_inited)
NE_PhysicsSystemEnd();
if (max_objects < 1)
NE_MAX_PHYSICS = NE_DEFAULT_PHYSICS;
else
NE_MAX_PHYSICS = max_objects;
NE_PhysicsPointers = calloc(NE_MAX_PHYSICS, sizeof(NE_PhysicsPointers));
if (NE_PhysicsPointers == NULL)
{
NE_DebugPrint("Not enough memory");
return -1;
}
ne_physics_system_inited = true;
return 0;
}
void NE_PhysicsSystemEnd(void)
{
if (!ne_physics_system_inited)
return;
NE_PhysicsDeleteAll();
free(NE_PhysicsPointers);
ne_physics_system_inited = false;
}
void NE_PhysicsSetRadiusI(NE_Physics *pointer, int radius)
{
NE_AssertPointer(pointer, "NULL pointer");
NE_Assert(pointer->type == NE_BoundingSphere, "Not a bounding shpere");
NE_Assert(radius >= 0, "Radius must be positive");
pointer->radius = radius;
}
void NE_PhysicsSetSpeedI(NE_Physics *pointer, int x, int y, int z)
{
NE_AssertPointer(pointer, "NULL pointer");
pointer->xspeed = x;
pointer->yspeed = y;
pointer->zspeed = z;
}
void NE_PhysicsSetSizeI(NE_Physics *pointer, int x, int y, int z)
{
NE_AssertPointer(pointer, "NULL pointer");
NE_Assert(pointer->type == NE_BoundingBox, "Not a bounding box");
NE_Assert(x >= 0 && y >= 0 && z >= 0, "Size must be positive!!");
pointer->xsize = x;
pointer->ysize = y;
pointer->zsize = z;
}
void NE_PhysicsSetGravityI(NE_Physics *pointer, int gravity)
{
NE_AssertPointer(pointer, "NULL pointer");
pointer->gravity = gravity;
}
void NE_PhysicsSetFrictionI(NE_Physics *pointer, int friction)
{
NE_AssertPointer(pointer, "NULL pointer");
NE_Assert(friction >= 0, "Friction must be positive");
pointer->friction = friction;
}
void NE_PhysicsSetBounceEnergy(NE_Physics *pointer, int percent)
{
NE_AssertPointer(pointer, "NULL pointer");
NE_Assert(percent >= 0, "Percentage must be positive");
pointer->keptpercent = percent;
}
void NE_PhysicsEnable(NE_Physics *pointer, bool value)
{
NE_AssertPointer(pointer, "NULL pointer");
pointer->enabled = value;
}
void NE_PhysicsSetModel(NE_Physics *physics, NE_Model *modelpointer)
{
NE_AssertPointer(physics, "NULL physics pointer");
NE_AssertPointer(modelpointer, "NULL model pointer");
physics->model = modelpointer;
}
void NE_PhysicsSetGroup(NE_Physics *physics, int group)
{
NE_AssertPointer(physics, "NULL pointer");
physics->physicsgroup = group;
}
void NE_PhysicsOnCollision(NE_Physics *physics, NE_OnCollision action)
{
NE_AssertPointer(physics, "NULL pointer");
physics->oncollision = action;
}
bool NE_PhysicsIsColliding(const NE_Physics *pointer)
{
NE_AssertPointer(pointer, "NULL pointer");
return pointer->iscolliding;
}
void NE_PhysicsUpdateAll(void)
{
if (!ne_physics_system_inited)
return;
for (int i = 0; i < NE_MAX_PHYSICS; i++)
{
if (NE_PhysicsPointers[i] != NULL)
NE_PhysicsUpdate(NE_PhysicsPointers[i]);
}
}
void NE_PhysicsUpdate(NE_Physics *pointer)
{
if (!ne_physics_system_inited)
return;
NE_AssertPointer(pointer, "NULL pointer");
NE_AssertPointer(pointer->model, "NULL model pointer");
NE_Assert(pointer->type != 0, "Object has no type");
if (pointer->enabled == false)
return;
pointer->iscolliding = false;
// We change Y speed depending on gravity.
pointer->yspeed -= pointer->gravity;
// Now, let's move the object
// Used in collision checking to simplify the code
int posx = 0, posy = 0, posz = 0;
// Position before movement
int bposx = 0, bposy = 0, bposz = 0;
NE_Model *model = pointer->model;
bposx = model->x;
bposy = model->y;
bposz = model->z;
posx = model->x = model->x + pointer->xspeed;
posy = model->y = model->y + pointer->yspeed;
posz = model->z = model->z + pointer->zspeed;
// Gravity and movement have been applied, time to check collisions...
bool xenabled = true, yenabled = true, zenabled = true;
if (bposx == posx)
xenabled = false;
if (bposy == posy)
yenabled = false;
if (bposz == posz)
zenabled = false;
for (int i = 0; i < NE_MAX_PHYSICS; i++)
{
if (NE_PhysicsPointers[i] == NULL)
continue;
// Check that we aren't checking an object with itself
if (NE_PhysicsPointers[i] == pointer)
continue;
// Check that both objects are in the same group
if (NE_PhysicsPointers[i]->physicsgroup != pointer->physicsgroup)
continue;
NE_Physics *otherpointer = NE_PhysicsPointers[i];
//Get coordinates
int otherposx = 0, otherposy = 0, otherposz = 0;
model = otherpointer->model;
otherposx = model->x;
otherposy = model->y;
otherposz = model->z;
// Both are boxes
if (pointer->type == NE_BoundingBox && otherpointer->type == NE_BoundingBox)
{
bool collision =
((abs(posx - otherposx) < (pointer->xsize + otherpointer->xsize) >> 1) &&
(abs(posy - otherposy) < (pointer->ysize + otherpointer->ysize) >> 1) &&
(abs(posz - otherposz) < (pointer->zsize + otherpointer->zsize) >> 1));
if (!collision)
continue;
pointer->iscolliding = true;
if (pointer->oncollision == NE_ColBounce)
{
// Used to reduce speed:
int temp = divf32(inttof32(pointer->keptpercent), inttof32(100));
if ((yenabled) && ((abs(bposy - otherposy) >= (pointer->ysize + otherpointer->ysize) >> 1)))
{
yenabled = false;
pointer->yspeed += pointer->gravity;
if (posy > otherposy)
(pointer->model)->y = otherposy + ((pointer->ysize + otherpointer->ysize) >> 1);
if (posy < otherposy)
(pointer->model)->y = otherposy - ((pointer->ysize + otherpointer->ysize) >> 1);
if (pointer->gravity == 0)
{
pointer->yspeed =
-mulf32(temp, pointer->yspeed);
}
else
{
if (abs(pointer->yspeed) > NE_MIN_BOUNCE_SPEED)
{
pointer->yspeed = -mulf32(temp, pointer->yspeed - pointer->gravity);
}
else
{
pointer->yspeed = 0;
}
}
}
else if ((xenabled) && ((abs(bposx - otherposx) >= (pointer->xsize + otherpointer->xsize) >> 1)))
{
xenabled = false;
if (posx > otherposx)
(pointer->model)->x = otherposx + ((pointer->xsize + otherpointer->xsize) >> 1);
if (posx < otherposx)
(pointer->model)->x = otherposx - ((pointer->xsize + otherpointer->xsize) >> 1);
pointer->xspeed = -mulf32(temp, pointer->xspeed);
}
else if ((zenabled) && ((abs(bposz - otherposz) >= (pointer->zsize + otherpointer->zsize) >> 1)))
{
zenabled = false;
if (posz > otherposz)
(pointer->model)->z = otherposz + ((pointer->zsize + otherpointer->zsize) >> 1);
if (posz < otherposz)
(pointer->model)->z = otherposz - ((pointer->zsize + otherpointer->zsize) >> 1);
pointer->zspeed = -mulf32(temp, pointer->zspeed);
}
}
else if (pointer->oncollision == NE_ColStop)
{
if ((yenabled) && ((abs(bposy - otherposy) >= (pointer->ysize + otherpointer->ysize) >> 1)))
{
yenabled = false;
if (posy > otherposy)
(pointer->model)->y = otherposy + ((pointer->ysize + otherpointer->ysize) >> 1);
if (posy < otherposy)
(pointer->model)->y = otherposy - ((pointer->ysize + otherpointer->ysize) >> 1);
}
if ((xenabled) && ((abs(bposx - otherposx) >= (pointer->xsize + otherpointer->xsize) >> 1)))
{
xenabled = false;
if (posx > otherposx)
(pointer->model)->x = otherposx + ((pointer->xsize + otherpointer->xsize) >> 1);
if (posx < otherposx)
(pointer->model)->x = otherposx - ((pointer->xsize + otherpointer->xsize) >> 1);
}
if ((zenabled) && ((abs(bposz - otherposz) >= (pointer->zsize + otherpointer->zsize) >> 1)))
{
zenabled = false;
if (posz > otherposz)
(pointer->model)->z = otherposz + ((pointer->zsize + otherpointer->zsize) >> 1);
if (posz < otherposz)
(pointer->model)->z = otherposz - ((pointer->zsize + otherpointer->zsize) >> 1);
}
pointer->xspeed = pointer->yspeed = pointer->zspeed = 0;
}
}
}
// Now, we get the module of speed in order to apply friction.
if (pointer->friction != 0)
{
pointer->xspeed <<= 10;
pointer->yspeed <<= 10;
pointer->zspeed <<= 10;
int _mod_ = mulf32(pointer->xspeed, pointer->xspeed);
_mod_ += mulf32(pointer->yspeed, pointer->yspeed);
_mod_ += mulf32(pointer->zspeed, pointer->zspeed);
_mod_ = sqrtf32(_mod_);
// Check if module is very small -> speed = 0
if (_mod_ < pointer->friction)
{
pointer->xspeed = pointer->yspeed = pointer->zspeed = 0;
}
else
{
int newmod = _mod_ - pointer->friction;
// mod -- newmod -> newspeed = speed * newmod / mod
// speed -- newspeed
int number = divf32(newmod, _mod_);
pointer->xspeed = mulf32(pointer->xspeed, number);
pointer->yspeed = mulf32(pointer->yspeed, number);
pointer->zspeed = mulf32(pointer->zspeed, number);
pointer->xspeed >>= 10;
pointer->yspeed >>= 10;
pointer->zspeed >>= 10;
}
}
}
bool NE_PhysicsCheckCollision(const NE_Physics *pointer1,
const NE_Physics *pointer2)
{
NE_AssertPointer(pointer1, "NULL pointer 1");
NE_AssertPointer(pointer2, "NULL pointer 2");
NE_Assert(pointer1 != pointer2, "Both objects are the same one");
// Get coordinates
int posx = 0, posy = 0, posz = 0;
NE_Model *model = pointer1->model;
posx = model->x;
posy = model->y;
posz = model->z;
int otherposx = 0, otherposy = 0, otherposz = 0;
model = pointer2->model;
otherposx = model->x;
otherposy = model->y;
otherposz = model->z;
// Both are boxes
//if(pointer->type == NE_BoundingBox && otherpointer->type == NE_BoundingBox)
//{
if ((abs(posx - otherposx) < (pointer1->xsize + pointer2->xsize) >> 1) &&
(abs(posy - otherposy) < (pointer1->ysize + pointer2->ysize) >> 1) &&
(abs(posz - otherposz) < (pointer1->zsize + pointer2->zsize) >> 1))
{
return true;
}
// else if (...) TODO: Spheres and dots
return false;
}