// 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; }