Menu

Collision (SweepAndSlide)

Help
Daniel
2019-05-04
2019-05-11
  • Daniel

    Daniel - 2019-05-04

    Hello everyone!

    I'm trying to use this procedure for collision handling of my fps-camera.
    It works nicely in general! But when I try to use it for a FPS-camera on a moving object (a ship), then it fails some times.
    The camera is a child of the ship model, so it follows the ships movement, so maybe the sweepAndSlide procedure uses absolute vectors which causes the camera to jump over obstacles when the ship is rolling/pitching?
    Any idea what to change to make it work with local vectors?
    I do not need the camera to move in the Y direction, only X and Z, so maybe it can be made 2 dimensional?

    Kind regards,
    Daniel

    procedure TForm1.SphereSweepAndSlide(freeform:TGLFreeform;SphereStart:TVector;var Velocity,newPosition:TVector;SphereRadius:Single);
    var
      oldPosition, ray:TVector;
      vel,slidedistance:Single;
      intPoint,intNormal:TVector;
      newDirection, newRay,collisionPosition, pointOnSphere,point2OnSphere:TVector;
      i:integer;
      CollisionState:TCollisionState;
      NegNormalizedVelocity:TVector;
    begin
      oldPosition:=SphereStart;
    
      //Direction sphere is moving in
      ray:=VectorSubtract(newPosition,oldPosition);
    //  ray:=Velocity;
    //  newPosition:=VectorAdd(newPosition,ray);
      //Speed of sphere
      vel:=VectorLength(ray);
    
      //if the Sphere is not moving, nothing is required
      // else do up to 7 loops
    
      if vel>0 then
      for i:=0 to 6 do
      begin
        //if an intersection occurs, will need to do further calculations
        if (freeform.OctreeSphereSweepIntersect(oldPosition,ray,vel,SphereRadius,@intPoint,@intNormal)) then
        begin
          if VectorDistance2(oldPosition,intPoint)<=sqr(SphereRadius) then
          begin
            //sphere is intersecting triangle
            intNormal:=VectorScale(VectorSubtract(oldPosition,intPoint),1.0001);
          end
          else
          begin
            //sphere is not intersecting triangle
            //intNormal:=VectorSubtract(oldPosition,intPoint);  //not correct but works okay at small time steps
            //intNormal:=VectorScale(VectorNormalize(intNormal),SphereRadius+0.0001);
            if RayCastSphereIntersect(intPoint,VectorNormalize(VectorNegate(ray)),oldPosition,SphereRadius,PointOnSphere,Point2OnSphere)>0 then
              intNormal:=VectorScale(VectorSubtract(oldPosition,PointOnSphere),1.0001)
              //intNormal:=VectorScale(VectorNormalize(VectorSubtract(oldPosition,PointOnSphere)),SphereRadius+0.001)//VectorDistance(oldPosition,PointOnSphere));
            else
            begin
    //          Assert(False);  //this shouldn't happen (this is here for debugging)
              intNormal:=VectorScale(VectorSubtract(oldPosition,intPoint),1.0001);
            end;
    
          end;
    
          //calculate position of centre of sphere when collision occurs
          collisionPosition:=VectorAdd(intPoint,intNormal);
          oldPosition:=collisionPosition;
    
          //calculate distance that wasn't travelled, due to obstacle
          newRay:=VectorSubtract(newPosition,collisionPosition);
    
          //calculate new direction when a wall is hit (could add bouncing to this)
          newDirection:=VectorCrossProduct(intNormal,VectorCrossProduct(newRay,intNormal));
          if VectorNorm(NewDirection)>0 then
            NormalizeVector(newDirection);
    
          //calculate distance that it should slide (depends on angle between plane & ray)
          SlideDistance:=vectorDotProduct(newRay,newDirection);
          //still need to implement friction properly
    //      if abs(SlideDistance)<10*deltaTime then SlideDistance:=0;
          ScaleVector(newDirection,SlideDistance);
    
          //calculate new position sphere is heading towards
          newPosition:=VectorAdd(collisionPosition,newDirection);
          ray:=newDirection;
          vel:=VectorLength(ray);
    
     {     //display arrows for collision normals & slide direction
          if i=0 then
          //  DrawArrows(intPoint,intNormal,Ray, GLArrowLine1, GLArrowLine4)
          else if i=1 then
          //  DrawArrows(intPoint,intNormal,Ray, GLArrowLine2, GLArrowLine5)
          else if i=2 then
           // DrawArrows(intPoint,intNormal,Ray, GLArrowLine3, GLArrowLine6)
          else if i=6 then
          begin
    //        caption:=FloatToStr(vectordistance(newPosition,oldPosition));
            newPosition:=oldPosition;
            break;
          end;           }
    
          //check if very small motion (e.g. when stuck in a corner)
          if vel<1E-10 then//deltaTime then
          begin
            newPosition:=oldPosition;
            break;
          end;
    
         {
          CollisionState:=TCollisionState.Create();
          CollisionState.Position:=oldPosition;
          CollisionState.Contact.intNormal:=intNormal;
          CollisionState.Contact.intPoint:=intPoint;
          CollisionState.Time:=GetTickCount64();
    
          CollisionStates.Add(CollisionState);    }
    
        end
        else //no collision occured, so quit loop
        begin
          Break;
        end;
      end; //end i loop
      Velocity:=Ray;
    end;
    
     
  • Daniel

    Daniel - 2019-05-11

    Hi gents!

    I found my problem as well as a simple solution for the local issue!
    First of all I used Scale instead of autoScaling for the model, this seems to have made "holes" in the freeForm.
    Secondly I let the collision model stay at postion 0,0,0 and use global position vectors, while my camera onboard the moving ship uses local position vectors. Then when I move onboard the ship, a sphere is moving inside the global collision model and my camera gets it's position from the sphere.
    So player local position := collision Spheres global position.

    Cheers!

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.