File Physics2D.c

File List > engine > src > gameframework > layers > Game2D > Physics2D.c

Go to the documentation of this file

#include "Physics2D.h"
#include "box2d/box2d.h"
#include "ObjectPool.h"
#include "Game2DLayer.h"
#include "AssertLib.h"
#include "GameFramework.h"
#include "Entities.h"

struct Phys2dWorld
{
    /* data */
    b2WorldId id;
    float gravX;
    float gravY;
    float pxlPerMeter;
};

struct Body2D
{
    b2BodyType type;
    b2BodyId bodyID;

    enum PhysicsBodyType shapeType;
    union 
    {
        b2Polygon poly;
        b2Circle circle;
    }shape;

    b2ShapeDef shapedef;
    b2ShapeId shapeID;
};

static OBJECT_POOL(struct Phys2dWorld) gWorldDefPool = NULL;

static OBJECT_POOL(struct Body2D) g2DPhysBodyPool = NULL;



void Ph_DestroyPhysicsWorld(HPhysicsWorld world)
{
    b2DestroyWorld(gWorldDefPool[world].id);
    FreeObjectPoolIndex(gWorldDefPool, world);
}

void Ph_Init()
{
    gWorldDefPool = NEW_OBJECT_POOL(struct Phys2dWorld, 32);
    g2DPhysBodyPool = NEW_OBJECT_POOL(struct Body2D, 256);
}

HPhysicsWorld Ph_GetPhysicsWorld(float gravityX, float gravityY, float pixelsPerMeter)
{
    HPhysicsWorld index = -1;
    gWorldDefPool = GetObjectPoolIndex(gWorldDefPool, &index);
    b2WorldDef def = b2DefaultWorldDef();
    def.gravity.x = gravityX;
    def.gravity.y = gravityY;
    gWorldDefPool[index].gravX = gravityX;
    gWorldDefPool[index].gravY = gravityY;
    gWorldDefPool[index].pxlPerMeter = pixelsPerMeter;
    gWorldDefPool[index].id = b2CreateWorld(&def);
    return index;
}

void Ph_PhysicsWorldStep(HPhysicsWorld hWorld, float timestep, int substepCount)
{
    b2World_Step(gWorldDefPool[hWorld].id, timestep, substepCount);
    //b2Body_SetLinearVelocity
}


void Ph_PhysicsWorldDoCollisionEvents(struct GameFrameworkLayer* pLayer)
{
    struct GameLayer2DData* pLayerData = pLayer->userData;
    struct Entity2DCollection* pEntCollection = &pLayerData->entities;
    HPhysicsWorld hWorld = pLayerData->hPhysicsWorld;
    b2SensorEvents sensorEvents = b2World_GetSensorEvents(gWorldDefPool[hWorld].id);
    for (int i = 0; i < sensorEvents.beginCount; ++i)
    {
        b2SensorBeginTouchEvent* beginTouch = sensorEvents.beginEvents + i;
        HEntity2D hVisitor, hSensor;
        u16 visitorComponentIndex, sensorComponentIndex;
        u16 visitorComponentType, sensorComponentType;
        Ph_UnpackShapeUserData(b2Shape_GetUserData(beginTouch->visitorShapeId), &hVisitor, &visitorComponentIndex, &visitorComponentType);
        Ph_UnpackShapeUserData(b2Shape_GetUserData(beginTouch->sensorShapeId), &hSensor, &sensorComponentIndex, &sensorComponentType);
        struct Entity2D* pSensorEnt = Et2D_GetEntity(pEntCollection, hSensor);

        if(sensorComponentType == b2_dynamicBody)
        {
            if(pSensorEnt->components[sensorComponentIndex].data.dynamicCollider.onSensorOverlapBegin)
                pSensorEnt->components[sensorComponentIndex].data.dynamicCollider.onSensorOverlapBegin(pLayer, hVisitor, pSensorEnt->thisEntity);
        }
        else if(sensorComponentType == b2_staticBody)
        {
            if(pSensorEnt->components[sensorComponentIndex].data.staticCollider.onSensorOverlapBegin)
                pSensorEnt->components[sensorComponentIndex].data.staticCollider.onSensorOverlapBegin(pLayer, hVisitor, pSensorEnt->thisEntity);
        }
    }
    // COMMENTED OUT UNTIL ACTUALLY NEEDED: THIS BREAKS WHEN AN ENTITY IS DELETED IN THE BEGIN EVENT
    // for(int i=0; i < sensorEvents.endCount; i++)
    // {
    //     b2SensorEndTouchEvent* beginTouch = sensorEvents.endEvents + i;
    //     HEntity2D hVisitor, hSensor;
    //     u16 visitorComponentIndex, sensorComponentIndex;
    //     u16 visitorComponentType, sensorComponentType;
    //     Ph_UnpackShapeUserData(b2Shape_GetUserData(beginTouch->visitorShapeId), &hVisitor, &visitorComponentIndex, &visitorComponentType);
    //     Ph_UnpackShapeUserData(b2Shape_GetUserData(beginTouch->sensorShapeId), &hSensor, &sensorComponentIndex, &sensorComponentType);
    //     struct Entity2D* pSensorEnt = Et2D_GetEntity(pEntCollection, hSensor);

    //     if(sensorComponentType == b2_dynamicBody)
    //     {
    //         if(pSensorEnt->components[sensorComponentIndex].data.dynamicCollider.onSensorOverlapEnd)
    //             pSensorEnt->components[sensorComponentIndex].data.dynamicCollider.onSensorOverlapEnd(pLayer, hVisitor, pSensorEnt->thisEntity);
    //     }
    //     else if(sensorComponentType == b2_staticBody)
    //     {
    //         if(pSensorEnt->components[sensorComponentIndex].data.staticCollider.onSensorOverlapEnd)
    //             pSensorEnt->components[sensorComponentIndex].data.staticCollider.onSensorOverlapEnd(pLayer, hVisitor, pSensorEnt->thisEntity);
    //     }
    // }
}

float Ph_GetPixelsPerMeter(HPhysicsWorld world)
{
    return gWorldDefPool[world].pxlPerMeter;
}

void Ph_PixelCoords2PhysicsCoords(HPhysicsWorld world, vec2 inPixelCoords, vec2 outPhysicsCoords)
{
    struct Phys2dWorld* pPool = &gWorldDefPool[world];
    outPhysicsCoords[0] = inPixelCoords[0] / pPool->pxlPerMeter;
    outPhysicsCoords[1] = inPixelCoords[1] / pPool->pxlPerMeter;
}

void Ph_PhysicsCoords2PixelCoords(HPhysicsWorld world, vec2 inPhysicsCoords, vec2 outPixelCoords)
{
    struct Phys2dWorld* pPool = &gWorldDefPool[world];
    outPixelCoords[0] = inPhysicsCoords[0] * pPool->pxlPerMeter;
    outPixelCoords[1] = inPhysicsCoords[1] * pPool->pxlPerMeter;
}

u64 Ph_PackShapeUserData(HEntity2D hEnt, u16 componentIndex, u16 bodyType)
{
    u64 ud = 0;
    ud |= hEnt;
    ud |= componentIndex << 32;
    ud |= bodyType << 48;
    return ud;
}

void Ph_UnpackShapeUserData(void* pUserData, HEntity2D* pOutEnt, u16* pOutCompIndex, u16* pOutBodyType)
{
    u64 ud = (u64)pUserData;
    *pOutEnt = ud & 0xffffffff;
    *pOutCompIndex = (ud >> 32) & 0xffff;
    *pOutBodyType = (ud >> 48) & 0xffff;
}

static H2DBody GetBody(HPhysicsWorld world, struct PhysicsShape2D* pShape, struct Transform2D* pTransform, b2BodyType type, HEntity2D entity, bool bIsSensor, int entityComponentIndex, bool bEnableSensorEvents)
{
    H2DBody hStatic = -1;
    g2DPhysBodyPool = GetObjectPoolIndex(g2DPhysBodyPool, &hStatic);
    g2DPhysBodyPool[hStatic].type = type;


    switch (pShape->type)
    {
    case PBT_Rect:
        {
            vec2 pixelsRectCenter = {
                pTransform->position[0] + pShape->data.rect.w / 2,
                pTransform->position[1] + pShape->data.rect.h / 2,
            };
            vec2 pixelDims = {
                pShape->data.rect.w,
                pShape->data.rect.h
            };
            vec2 physicsDims;
            vec2 physicsPos;
            Ph_PixelCoords2PhysicsCoords(world, pixelsRectCenter, physicsPos);
            Ph_PixelCoords2PhysicsCoords(world, pixelDims, physicsDims);

            b2BodyDef bodyDef = b2DefaultBodyDef();
            bodyDef.type = type;
            bodyDef.position = (b2Vec2){physicsPos[0], physicsPos[1]};
            b2BodyId id = b2CreateBody(gWorldDefPool[world].id, &bodyDef);
            g2DPhysBodyPool[hStatic].bodyID = id;
            g2DPhysBodyPool[hStatic].shapedef = b2DefaultShapeDef();
            u64 ud = Ph_PackShapeUserData(entity, entityComponentIndex, (u16)type);
            g2DPhysBodyPool[hStatic].shapedef.userData = (void*)ud;
            if(bIsSensor)
            {
                g2DPhysBodyPool[hStatic].shapedef.isSensor = true;

            }
            if(bEnableSensorEvents)
            {
                g2DPhysBodyPool[hStatic].shapedef.enableSensorEvents = true;
            }

            g2DPhysBodyPool[hStatic].shape.poly = b2MakeBox(physicsDims[0] / 2.0f, physicsDims[1] / 2.0f);
            g2DPhysBodyPool[hStatic].shapeID = b2CreatePolygonShape(id, &g2DPhysBodyPool[hStatic].shapedef, &g2DPhysBodyPool[hStatic].shape.poly);
        }
        break;
    case PBT_Circle:
        {
            vec2 physicsPos;
            float physicsRadius = pShape->data.circle.radius / Ph_GetPixelsPerMeter(world);
            Ph_PixelCoords2PhysicsCoords(world, pShape->data.circle.center, physicsPos);

            b2BodyDef bodyDef = b2DefaultBodyDef();
            bodyDef.type = type;
            bodyDef.position = (b2Vec2){physicsPos[0], physicsPos[1]};
            b2BodyId id = b2CreateBody(gWorldDefPool[world].id, &bodyDef);

            g2DPhysBodyPool[hStatic].bodyID = id;
            g2DPhysBodyPool[hStatic].shapedef = b2DefaultShapeDef();
            /* WARNING: 64 BIT POINTER SIZE SPECIFIC CODE */

            u64 ud = Ph_PackShapeUserData(entity, entityComponentIndex, (u16)type);

            g2DPhysBodyPool[hStatic].shapedef.userData = (void*)ud;
            if(bIsSensor)
            {
                g2DPhysBodyPool[hStatic].shapedef.isSensor = true;
            }
            if(bEnableSensorEvents)
            {
                g2DPhysBodyPool[hStatic].shapedef.enableSensorEvents = true;
            }

            g2DPhysBodyPool[hStatic].shape.circle.center.x = 0.0f;//physicsPos[0];
            g2DPhysBodyPool[hStatic].shape.circle.center.y = 0.0f;//physicsPos[1];
            g2DPhysBodyPool[hStatic].shape.circle.radius = physicsRadius;

            g2DPhysBodyPool[hStatic].shapeID = b2CreateCircleShape(id, &g2DPhysBodyPool[hStatic].shapedef, &g2DPhysBodyPool[hStatic].shape.circle);
        }
        break;
    case PBT_Ellipse:
        EASSERT(false);
        break;
    case PBT_Poly:
        EASSERT(false);
        break;
    default:
        break;
    }
    return hStatic;
}

H2DBody Ph_GetStaticBody2D(HPhysicsWorld world, struct PhysicsShape2D* pShape, struct Transform2D* pTransform, HEntity2D entity, bool bIsSensor, int entityComponentIndex, bool bGenerateSensorEvents)
{
    return GetBody(world, pShape, pTransform, b2_staticBody, entity, bIsSensor, entityComponentIndex, bGenerateSensorEvents);
}

H2DBody Ph_GetDynamicBody(HPhysicsWorld world, struct PhysicsShape2D* pShape, struct KinematicBodyOptions* pOptions, struct Transform2D* pTransform, HEntity2D entity, bool bIsSensor,  int entityComponentIndex, bool bGenerateSensorEvents)
{
    return GetBody(world, pShape, pTransform, b2_dynamicBody, entity, bIsSensor, entityComponentIndex, bGenerateSensorEvents);
}

void Ph_SetDynamicBodyVelocity(H2DBody hBody, vec2 velocity)
{
    //EASSERT(g2DPhysBodyPool[hBody].type == b2_kinematicBody);
    b2Vec2 b2Vec = {.x = velocity[0], .y = velocity[1]};
    b2Body_SetLinearVelocity(g2DPhysBodyPool[hBody].bodyID, b2Vec);
}

void Ph_GetDynamicBodyVelocity(H2DBody hBody, vec2 outVelocity)
{
    EASSERT(g2DPhysBodyPool[hBody].type == b2_kinematicBody);
    b2Vec2 b2Vec = b2Body_GetLinearVelocity(g2DPhysBodyPool[hBody].bodyID);
    outVelocity[0] = b2Vec.x;
    outVelocity[1] = b2Vec.y;
}

void Ph_GetDynamicBodyPosition(H2DBody hBody, vec2 outPos)
{
    b2Vec2 b2Vec = b2Body_GetPosition(g2DPhysBodyPool[hBody].bodyID);
    outPos[0] = b2Vec.x;
    outPos[1] = b2Vec.y;
}

void Ph_SetDynamicBodyPosition(H2DBody hBody, vec2 inPos)
{
    b2Rot rot = b2Body_GetRotation(g2DPhysBodyPool[hBody].bodyID);
    b2Vec2 b2Vec2 = {
        .x = inPos[0],
        .y = inPos[1]
    };
    b2Body_SetTransform(g2DPhysBodyPool[hBody].bodyID, b2Vec2, rot);
}

void Ph_DestroyBody(H2DBody body)
{
    b2DestroyBody(g2DPhysBodyPool[body].bodyID);
    FreeObjectPoolIndex(g2DPhysBodyPool, body);
}