java - 2 个 OBB 的联络点?

标签 java math geometry

我正在为类似 GTA2 的游戏研究物理原理,这样我就可以了解有关游戏物理原理的更多信息。

碰撞检测和解决效果非常好。

我现在只是不确定如何计算撞到墙上时的接触点。

这是我的 OBB 类(class):

public class OBB2D
{
   private Vector2D projVec = new Vector2D();
   private static Vector2D projAVec = new Vector2D();
   private static Vector2D projBVec = new Vector2D();
   private static Vector2D tempNormal = new Vector2D();
   private Vector2D deltaVec = new Vector2D();


// Corners of the box, where 0 is the lower left.
   private  Vector2D corner[] = new Vector2D[4];

   private Vector2D center = new Vector2D();
   private Vector2D extents = new Vector2D();

   private RectF boundingRect = new RectF();
   private float angle;

    //Two edges of the box extended away from corner[0]. 
   private  Vector2D axis[] = new Vector2D[2];

   private double origin[] = new double[2];

   public OBB2D(float centerx, float centery, float w, float h, float angle)
    {
       for(int i = 0; i < corner.length; ++i)
       {
           corner[i] = new Vector2D();
       }
       for(int i = 0; i < axis.length; ++i)
       {
           axis[i] = new Vector2D();
       }
       set(centerx,centery,w,h,angle);
    }

   public OBB2D(float left, float top, float width, float height)
  {
       for(int i = 0; i < corner.length; ++i)
       {
           corner[i] = new Vector2D();
       }
       for(int i = 0; i < axis.length; ++i)
       {
           axis[i] = new Vector2D();
       }
       set(left + (width / 2), top + (height / 2),width,height,0.0f);
   }

   public void set(float centerx,float centery,float w, float h,float angle)
   {
       float vxx = (float)Math.cos(angle);
       float vxy = (float)Math.sin(angle);
       float vyx = (float)-Math.sin(angle);
       float vyy = (float)Math.cos(angle);

           vxx *= w / 2;
           vxy *= (w / 2);
           vyx *= (h / 2);
           vyy *= (h / 2);

           corner[0].x = centerx - vxx - vyx;
           corner[0].y = centery - vxy - vyy;
           corner[1].x = centerx + vxx - vyx;
           corner[1].y = centery + vxy - vyy;
           corner[2].x = centerx + vxx + vyx;
           corner[2].y = centery + vxy + vyy;
           corner[3].x = centerx - vxx + vyx;
           corner[3].y = centery - vxy + vyy;

           this.center.x = centerx;
           this.center.y = centery;
           this.angle = angle;
           computeAxes();
           extents.x = w / 2;
           extents.y = h / 2;

           computeBoundingRect();
   }


   //Updates the axes after the corners move.  Assumes the
   //corners actually form a rectangle.
   private void computeAxes()
   {
       axis[0].x = corner[1].x - corner[0].x;
       axis[0].y = corner[1].y - corner[0].y;
       axis[1].x = corner[3].x - corner[0].x;
       axis[1].y = corner[3].y - corner[0].y;


       // Make the length of each axis 1/edge length so we know any
       // dot product must be less than 1 to fall within the edge.

       for (int a = 0; a < axis.length; ++a) 
       {
        float l = axis[a].length();
        float ll = l * l;
        axis[a].x = axis[a].x / ll;
        axis[a].y = axis[a].y / ll;
           origin[a] = corner[0].dot(axis[a]);
       }
   }



   public void computeBoundingRect()
   {
       boundingRect.left = JMath.min(JMath.min(corner[0].x, corner[3].x), JMath.min(corner[1].x, corner[2].x));
       boundingRect.top = JMath.min(JMath.min(corner[0].y, corner[1].y),JMath.min(corner[2].y, corner[3].y));
       boundingRect.right = JMath.max(JMath.max(corner[1].x, corner[2].x), JMath.max(corner[0].x, corner[3].x));
       boundingRect.bottom = JMath.max(JMath.max(corner[2].y, corner[3].y),JMath.max(corner[0].y, corner[1].y)); 
   }

   public void set(RectF rect)
   {
       set(rect.centerX(),rect.centerY(),rect.width(),rect.height(),0.0f);
   }

    // Returns true if other overlaps one dimension of this.
    private boolean overlaps1Way(OBB2D other)
    {
        for (int a = 0; a < axis.length; ++a) {

            double t = other.corner[0].dot(axis[a]);

            // Find the extent of box 2 on axis a
            double tMin = t;
            double tMax = t;

            for (int c = 1; c < corner.length; ++c) {
                t = other.corner[c].dot(axis[a]);

                if (t < tMin) {
                    tMin = t;
                } else if (t > tMax) {
                    tMax = t;
                }
            }

            // We have to subtract off the origin

            // See if [tMin, tMax] intersects [0, 1]
            if ((tMin > 1 + origin[a]) || (tMax < origin[a])) {
                // There was no intersection along this dimension;
                // the boxes cannot possibly overlap.
                return false;
            }
        }

        // There was no dimension along which there is no intersection.
        // Therefore the boxes overlap.
        return true;
    }



    public void moveTo(float centerx, float centery) 
    {
        float cx,cy;

        cx = center.x;
        cy = center.y;

        deltaVec.x = centerx - cx;
        deltaVec.y  = centery - cy;


        for (int c = 0; c < 4; ++c)
        {
            corner[c].x += deltaVec.x;
            corner[c].y += deltaVec.y;
        }

        boundingRect.left += deltaVec.x;
        boundingRect.top += deltaVec.y;
        boundingRect.right += deltaVec.x;
        boundingRect.bottom += deltaVec.y;


        this.center.x = centerx;
        this.center.y = centery;
        computeAxes();
    }

    // Returns true if the intersection of the boxes is non-empty.
    public boolean overlaps(OBB2D other)
    {
        if(right() < other.left())
        {
            return false;
        }

        if(bottom() < other.top())
        {
            return false;
        }

        if(left() > other.right())
        {
            return false;
        }

        if(top() > other.bottom())
        {
            return false;
        }


        if(other.getAngle() == 0.0f && getAngle() == 0.0f)
        {
            return true;
        }

        return overlaps1Way(other) && other.overlaps1Way(this);
    }

    public Vector2D getCenter()
    {
        return center;
    }

    public float getWidth()
    {
        return extents.x * 2;
    }

    public float getHeight() 
    {
        return extents.y * 2;
    }

    public void setAngle(float angle)
    {
        set(center.x,center.y,getWidth(),getHeight(),angle);
    }

    public float getAngle()
    {
        return angle;
    }

    public void setSize(float w,float h)
    {
        set(center.x,center.y,w,h,angle);
    }

    public float left()
    {
        return boundingRect.left;
    }

    public float right()
    {
        return boundingRect.right;
    }

    public float bottom()
    {
        return boundingRect.bottom;
    }

    public float top()
    {
        return boundingRect.top;
    }

    public RectF getBoundingRect()
    {
        return boundingRect;
    }

    public boolean overlaps(float left, float top, float right, float bottom)
    {
        if(right() < left)
        {
            return false;
        }

        if(bottom() < top)
        {
            return false;
        }

        if(left() > right)
        {
            return false;
        }

        if(top() > bottom)
        {
            return false;
        }

        return true;
    }

    public static float distance(float ax, float ay,float bx, float by)
    {
      if (ax < bx)
        return bx - ay;
      else
        return ax - by;
    }


    public Vector2D project(float ax, float ay)
    {
        projVec.x = Float.MAX_VALUE;
        projVec.y = Float.MIN_VALUE;

      for (int i = 0; i < corner.length; ++i)
      {
        float dot = Vector2D.dot(corner[i].x,corner[i].y,ax,ay);

        projVec.x = JMath.min(dot, projVec.x);
        projVec.y = JMath.max(dot, projVec.y);
      }

      return projVec;
    }

    public Vector2D getCorner(int c)
    {
        return corner[c];
    }

    public int getNumCorners()
    {
        return corner.length;
    }

    public static float collisionResponse(OBB2D a, OBB2D b,  Vector2D outNormal) 
    {

        float depth = Float.MAX_VALUE;


        for (int i = 0; i < a.getNumCorners() + b.getNumCorners(); ++i)
        {
            Vector2D edgeA;
            Vector2D edgeB;
            if(i >= a.getNumCorners())
            {
                edgeA = b.getCorner((i + b.getNumCorners() - 1) % b.getNumCorners());
                edgeB = b.getCorner(i % b.getNumCorners());
            }
            else
            {
                edgeA = a.getCorner((i + a.getNumCorners() - 1) % a.getNumCorners());
                edgeB = a.getCorner(i % a.getNumCorners());
            }

             tempNormal.x = edgeB.x -edgeA.x;
             tempNormal.y = edgeB.y - edgeA.y; 


          tempNormal.normalize();


          projAVec.equals(a.project(tempNormal.x,tempNormal.y));
          projBVec.equals(b.project(tempNormal.x,tempNormal.y));

          float distance = OBB2D.distance(projAVec.x, projAVec.y,projBVec.x,projBVec.y);

          if (distance > 0.0f)
          {
            return 0.0f;
          }
          else
          {
            float d = Math.abs(distance);

            if (d < depth)
            {
              depth = d;
              outNormal.equals(tempNormal);
            }
          }
        }

        float dx,dy;
        dx = b.getCenter().x - a.getCenter().x;
        dy = b.getCenter().y - a.getCenter().y;
        float dot = Vector2D.dot(dx,dy,outNormal.x,outNormal.y);
        if(dot > 0)
        {
            outNormal.x = -outNormal.x;
            outNormal.y = -outNormal.y;
        }

        return depth;
    }

    public Vector2D getMoveDeltaVec()
    {
    return deltaVec;
}
};

最佳答案

I'm now just unsure how to compute the point of contact when I hit a wall.

您可以用一个简单的平面来表示一面墙。

OBB 与平面相交测试是最简单的 separating axis test其中:

If two convex objects don't intersect, then there is a plane where the projection of these two objects will not intersect.

仅当平面法线形成分离轴时,长方体才与平面相交。计算盒子中心的投影和投影半径(4 个点积和一些加法),然后就可以开始了(您还可以免费获得穿透深度)。

情况如下:

|d| <= a1|n*A1| + a2|n*A2| + a3|n*A3|

这里:

d 从盒子中心到平面的距离。

a1...a3 框距中心的范围。

n 平面法线

A1...A3 框的 x,y,z 轴

一些伪代码:

//Test if OBB b intersects plane p
int TestOBBPlane(OBB b, Plane p)
{
   // Compute the projection interval radius of b onto L(t) = b.c + t * p.n
   float r = b.e[0]*Abs(Dot(p.n, b.u[0])) +
      b.e[1]*Abs(Dot(p.n, b.u[1])) +
      b.e[2]*Abs(Dot(p.n, b.u[2]));

   // Compute distance of box center from plane
   float s = Dot(p.n, b.c) – p.d;

   // Intersection occurs when distance s falls within [-r,+r] interval
   return Abs(s) <= r;
}

OBB-vs-OBB 交集测试更为复杂。

让我们引用this great tutorial :

In this case we no longer have corresponding separating lines that are perpendicular to the separating axes. Instead, we have separating planes that separate the bounding volumes (and they are perpendicular to their corresponding separating axes).

In 3D space, each OBB only has 3 unique planes extended by its faces, and the separating planes are parallel to these faces. We are interested in the separating planes parallel to the faces, but in 3D space, the faces are not the only concern. We are also interested in the edges. The separating planes of interest are parallel to the faces of the boxes, and the separating axes of interest are perpendicular to the separating planes. Hence the separating axes of interest are perpendicular to the 3 unique faces of each box. Notice these 6 separating axes of interest correspond to the 6 local (XYZ) axes of the two boxes.

So there are 9 separating axes to consider for edges collision in addition to the 6 separating axes we already have found for the faces collision. This makes the total number of possible separating axes to consider at 15.

以下是您需要测试的 15 个可能的分离轴 (L):

CASE 1:  L = Ax
CASE 2:  L = Ay
CASE 3:  L = Az
CASE 4:  L = Bx
CASE 5:  L = By
CASE 6:  L = Bz
CASE 7:  L = Ax x Bx
CASE 8:  L = Ax x By
CASE 9:  L = Ax x Bz
CASE 10: L = Ay x Bx
CASE 11: L = Ay x By
CASE 12: L = Ay x Bz
CASE 13: L = Az x Bx
CASE 14: L = Az x By
CASE 15: L = Az x Bz

这里:

Ax 表示 A 的 x 轴的单位 vector

Ay 表示 A 的 y 轴的单位 vector

Az 表示 A 的 z 轴的单位 vector

Bx 表示 B 的 x 轴的单位 vector

通过表示 B y 轴的单位 vector

Bz 表示 B 的 z 轴的单位 vector

现在您可以看到 OBB-OBB 相交测试背后的算法。

让我们跳转到源代码:

2D OBB-OBB:http://www.flipcode.com/archives/2D_OBB_Intersection.shtml

3D OBB-OBB:http://www.geometrictools.com/LibMathematics/Intersection/Intersection.html

附注:此链接 http://www.realtimerendering.com/intersections.html对于那些希望超越飞机和盒子的人来说将会很有用。

关于java - 2 个 OBB 的联络点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13338017/

相关文章:

java - 使用 JSoup for Java 时出现 HTML 格式问题

java - AWS Java Lambda 局部变量与对象变量

java - 私有(private) REST API 成熟度级别和额外的复杂性层

opencv - 降采样但不完全划分的影响

java - 从 Java 运行进程,使用 C++ 的输出

algorithm - 你如何找到一个数字属于弗洛伊德三角中的哪一行和哪一列

javascript - 每月总利息公式

Python - 具有一些允许的内部点的凸包

android gps定位精度圈

javascript - 如何选择从一个点给定半径内的所有元素?