markcwm / openb3d.mod

OpenB3D BlitzMax wrapper, see openb3d.docs for examples
18 stars 7 forks source link

Request / Help: Implementing a ManualCollision function #25

Open Kippykip opened 2 years ago

Kippykip commented 2 years ago

Hey, I'm trying to implement a new ManualCollisions function into collisions2.c & collisions2.h

Basically it would work like so: int ManualCollision(Entity& ent,Entity& ent2, int col_method, int col_response) So instead of bothering with collision types and whatnot, I can just check between them manually within blitzmax. Should return the number of hits while also doing the collision response.

The reasoning is because I've divided my map into chunks/blockmap, and I don't need objects testing collisions for far away objects or other entities in my project. Only every 500x500x500 metres. I've got the rest of the stuff handled but for it to work, I need a manual way of doing the collisions.

I imagine the game loop will look something like this:

While not AppTerminate()  
  ResetEntity(player)  
  if(ManualCollision(player, map, 2, 1) > 0)  
    print "hit!"  
  endif  
  RenderWorld()  
  Flip()  
Wend

However I'm struggling to even add the function, I think I've made something(?) inside collisions2.c and can compile the modified module fine. But I just can't seem to figure out how to call the .cpp function within BlitzMax itself. As soon as I attempt to add the function to functions.bmx I get:

Compile Error
Identifier 'ManualCollision' not found

C++ isn't really in my expertise, it took me a while to get the first function to compile because I was using ** instead of & hahaha. Any hints or clues?

markcwm commented 2 years ago

Hi KippyKip, rather than me trying to explain how to wrap a function, just send me the file or function and I'll wrap it for you. Is that OK?

Kippykip commented 2 years ago

Sure! This is the function I wanna add in collision2.c I have no idea if it's gonna work as it as I have no way of calling it

int ManualCollision(Entity& ent,Entity& ent2, int col_method, int col_response)
{
    DebugLog("Call worked");
    // if src entity is hidden or it's parent is hidden then do not check for collision
    if(ent.Hidden()==true) return;

    Vector c_vec_a(ent.EntityX(true),ent.EntityY(true),ent.EntityZ(true));
    Vector c_vec_b(ent.old_x,ent.old_y,ent.old_z);
    Vector c_vec_radius(ent.radius_x,ent.radius_y,ent.radius_x);

    CollisionInfo* c_col_info=C_CreateCollisionInfoObject(&c_vec_a,&c_vec_b,&c_vec_radius);

    Collision* c_coll=NULL;

    int response=0;

    // repeat until there's no collision between src and dest entities
    for(;;)
    {
        int hit=false;

        c_coll=C_CreateCollisionObject();
        Entity* ent2_hit=NULL;

        // if des entity is hidden or it's parent is hidden then do not check for collision
        // if src ent is same as des entity then do not check for collision
        if(ent2.Hidden()==false && &ent!=&ent2)
        {
            //if(QuickCheck(ent,ent2)==false) continue; // quick check to see if entities are colliding
            Matrix mat;

            if(ent2.dynamic!=true){
                mat.Overwrite(ent2.mat);
            }else{
                mat.Overwrite(ent2.old_mat);
            }

            Vector c_vec_i(mat.grid[0][0],mat.grid[0][1],-mat.grid[0][2]);
            Vector c_vec_j(mat.grid[1][0],mat.grid[1][1],-mat.grid[1][2]);
            Vector c_vec_k(-mat.grid[2][0],-mat.grid[2][1],mat.grid[2][2]);

            MMatrix c_mat(c_vec_i,c_vec_j,c_vec_k);
            Vector c_vec_v(mat.grid[3][0],mat.grid[3][1],-mat.grid[3][2]);

            Transform c_tform(c_mat,c_vec_v);

            // if pick mode is sphere or box then update collision info object to include entity radius/box info
            if(col_method!=COLLISION_METHOD_POLYGON){
                C_UpdateCollisionInfoObject(c_col_info,ent2.radius_x,ent2.box_x,ent2.box_y,ent2.box_z,ent2.box_x+ent2.box_w,ent2.box_y+ent2.box_h,ent2.box_z+ent2.box_d);
            }

            MeshCollider* tree=NULL;
            if(dynamic_cast<Mesh*>(&ent2)!=0){
                Mesh* m=dynamic_cast<Mesh*>(&ent2);
                m->TreeCheck(); // create collision tree for mesh if necessary
                tree=m->c_col_tree;
            }else if(dynamic_cast<Terrain*>(&ent2)!=0){
                Terrain* t=dynamic_cast<Terrain*>(&ent2);
                t->TreeCheck(c_col_info); // create collision tree for terrain if necessary
                tree=t->c_col_tree;
            }

            hit=C_CollisionDetect(c_col_info,c_coll,&c_tform,tree,col_method);

            if(hit){

                ent2_hit=&ent2;

            }

            response=col_response;

        } // end of dest ent loop

        if(ent2_hit){
                            DebugLog("StaticLoop");

            int x=C_CollisionResponse(c_col_info,c_coll,response); // fixes collision functions lag, topic=87446
            ent.no_collisions=ent.no_collisions+1;

            //int i=ent.no_collisions-1;
            CollisionImpact* eci=new CollisionImpact;
            //ent.collision.push_back(eci);
            ent.collision.insert(ent.collision.begin(),eci); // fixes some collisions breaking, inverts order

            eci->x=C_CollisionX();
            eci->y=C_CollisionY();
            eci->z=C_CollisionZ();
            eci->nx=C_CollisionNX();
            eci->ny=C_CollisionNY();
            eci->nz=C_CollisionNZ();
            eci->ent=ent2_hit;

            if(dynamic_cast<Mesh*>(ent2_hit)!=NULL){
                eci->surf=dynamic_cast<Mesh*>(ent2_hit)->GetSurface(C_CollisionSurface());
            }else{
                eci->surf=NULL;
            }

            eci->tri=C_CollisionTriangle();

            //if(C_CollisionResponse(c_col_info,c_coll,response)==false) break;
            if(x==false) break;

        }else{

            break;

        }

        C_DeleteCollisionObject(c_coll);

    } // end of infinite loop

    C_DeleteCollisionObject(c_coll);

    int hits=C_CollisionFinal(c_col_info);

    if(hits){

        ent.new_x=C_CollisionPosX();
        ent.new_y=C_CollisionPosY();
        ent.new_z=C_CollisionPosZ();

        // moved from PositionEntities
        ent.PositionEntity(ent.new_x,ent.new_y,ent.new_z,true);
    }
    C_DeleteCollisionInfoObject(c_col_info);
return hits;
}
markcwm commented 2 years ago

Hi KippyKip,

thanks for the function, I wrapped it tonight and have added it as a general function, it seems to work fine. Here is the commit: a824d2e0d8ad2bcb5c06aedfe399e892bd4ed6d1

I'm fairly sure you will still need UpdateWorld, so I added an internal flag in UpdateCollisions to disable automatic collisions when ManualCollision is called, so UpdateWorld can still be used for animations, particles, etc.

Kippykip commented 2 years ago

Wow that was quick, surprised it worked out too and got added to the main branch. I'm gonna give it a test later when I come back from work, I'm interested to see how it will deal with multiple objects like an NPC that walks onto a piece of map and also gets shot with a weapon.

If I find any problems I'll report them and attempt to fix them probably here.

Kippykip commented 2 years ago

Finally got a chance to try it out tonight, but unfortunately I'm getting EXCEPTION_ACCESS_VIOLATION as soon as I call ManualCollision(). Even if I stub out the function with return 0; I still get the error. Here's a quick throwaway example file

Import openb3d.b3dglgraphics
Graphics3D(640,480,32,60)
Local gllight:TLight = CreateLight()
Local cam:TCamera = CreateCamera()
Local box:TMesh = CreateCube()
MoveEntity(box, 0, -4, 20)
Local ball:TMesh = CreateSphere()
MoveEntity(ball, -10, -4, 20)
Local cone:TMesh = CreateCone()
MoveEntity(cone, 10, -4, 20)

EntityColor(box, 0, 255, 0)
EntityColor(ball, 255, 0, 0)
EntityColor(cone, 0, 255, 255)

Local type_ground=1
Local type_character=2
'Collisions(type_character, type_ground, 2, 1)
EntityType(box, type_ground)
EntityType(ball, type_ground)
EntityType(cone, type_ground)

Local plr:TMesh = CreateSphere()

EntityRadius(plr, 0.5)
EntityType(plr, type_character)
ScaleEntity(plr, 0.5, 0.5, 0.5)
MoveEntity(plr, 0, -4, 0.1)

While(Not AppTerminate())
    ResetEntity(plr)
    ManualCollision(plr, box, 1, 2)
    MoveEntity(plr, 0, 0, 0.1)
    UpdateWorld()
    RenderWorld()
    Flip
Wend
markcwm commented 2 years ago

Your example works but I have only been able to test in Ubuntu so it could be a Windows specific issue, my Windows install is broken since installing MSE and uninstalling a few things, now nothing will build and I tried disabling realtime protection and firewall but it doesn't seem to help.

I may have declared the collisions_manual variable wrong, but I can't test, so you could try commenting out all occurences of that.