unity中两模型之间的OBB包围盒碰撞检测

unity中求两模型相交部分,利用布尔算法:

布尔求交

代码如下:

using UnityEngine;
using System.Collections;

public class csSceneBoolean : MonoBehaviour {

    public MeshCollider meshColliderA;
    public MeshCollider meshColliderB;

    // Use this for initialization
    void Start () {

        // Create new GameObject
        GameObject newObject = new GameObject();
        newObject.transform.localScale*=2f;
        MeshFilter meshFilter = newObject.AddComponent();
        MeshRenderer meshRenderer = newObject.AddComponent();
        meshRenderer.materials = new Material[2]{

            meshColliderA.transform.GetComponent().materials[0],
            meshColliderB.transform.GetComponent().materials[0],
            //meshColliderA.transform.renderer.materials[0],
            // meshColliderB.transform.renderer.materials[0]
        };
    
        // Assign booleanMesh
        BooleanMesh booleanMesh = new BooleanMesh(meshColliderA,meshColliderB);
        //meshFilter.mesh = booleanMesh.Difference();
        //meshFilter.mesh = booleanMesh.Union();
        meshFilter.mesh = booleanMesh.Intersection();
    
    }    

}
 

using UnityEngine;
using System.Collections.Generic;

public class BooleanMesh {

    MeshCollider ObjectA;
    MeshCollider ObjectB;

    Triangulation triangulationA;
    Triangulation triangulationB;

    float distance, customDistance;

    public BooleanMesh (MeshCollider A, MeshCollider B){
        
        this.ObjectA = A;
        this.ObjectB = B;
        this.triangulationA = new Triangulation(A);
        this.triangulationB = new Triangulation(B);
        this.distance = 100f;
        
    }

    class intersectionDATA {

        public Triangulation A,B;
        public MeshCollider meshColliderB;
        public int triangleA;
        public float customDistance;
        public Ray r1, r2;
        public RaycastHit hit;

        public intersectionDATA(Triangulation a, Triangulation b, MeshCollider m){

            this.A = a;
            this.B = b;
            this.meshColliderB = m;
            this.r1 = new Ray();
            this.r2 = new Ray();
            this.hit = new RaycastHit();

        }        
        
    }

    void intersectionPoint(intersectionDATA var){
        
        var.A.AddWorldPointOnTriangle(var.hit.point,var.triangleA);        
        var.B.AddWorldPointOnTriangle(var.hit);

    }

    void intersectionRay(int originVertice, int toVertice, intersectionDATA var){

        var.r1.origin = var.A.vertices[var.A.triangles[var.triangleA].indexVertice[originVertice]].pos;
        var.r2.origin = var.A.vertices[var.A.triangles[var.triangleA].indexVertice[toVertice]].pos;
        var.r1.direction = (var.r2.origin - var.r1.origin).normalized;
        var.r2.direction = (var.r1.origin - var.r2.origin).normalized;

        var.customDistance = Vector3.Distance(var.r1.origin,var.r2.origin);

        if(var.A.vertices[var.A.triangles[var.triangleA].indexVertice[originVertice]].type == 0) if(var.meshColliderB.Raycast(var.r1, out var.hit, var.customDistance)) intersectionPoint(var);
        if(var.A.vertices[var.A.triangles[var.triangleA].indexVertice[toVertice]].type == 0) if(var.meshColliderB.Raycast(var.r2, out var.hit, var.customDistance)) intersectionPoint(var);

    }

    void AInToB(intersectionDATA var){

        // Vertices A In MeshCollider B
        for(int i=0;i             
            if(In(var.meshColliderB,var.A.vertices[i].pos)) var.A.vertices[i].type = -1; //In
            else var.A.vertices[i].type = 0; //Out
            
        }

    }

    void intersectionsAtoB(intersectionDATA var){
    
        for(int i=0;i

            var.triangleA = i;
            intersectionRay(0,1,var);
            intersectionRay(0,2,var);
            intersectionRay(1,2,var);
            
        }

    }

    void clearVertices(Triangulation triangulation, int t){

        int i,w;

        for(i=triangulation.triangles.Count-1;i>-1;i--){ for(w=triangulation.triangles[i].indexVertice.Count-1;w>-1;w--){
        
                if(triangulation.vertices[triangulation.triangles[i].indexVertice[w]].type == t) triangulation.triangles[i].indexVertice.RemoveAt(w);

            }

            if(triangulation.triangles[i].indexVertice.Count<3) triangulation.triangles.RemoveAt(i);
        
        }

    }

    void recalculateTriangles(Vector3[] vertices,Vector3[] normals,int[] triangles){
        
        Vector3 a,b,c;
        int v1,v2,v3;
        
        for(int i=0;i             
            v1 = triangles[i];
            v2 = triangles[i+1];
            v3 = triangles[i+2];
            
            a = vertices[v1];
            b = vertices[v2];
            c = vertices[v3];
            
            if(Vector3.Dot(normals[v1]+normals[v2]+normals[v3],Vector3.Cross((b-a),(c-a)))<0f){
                
                triangles[i+2]=v1;
                triangles[i]=v3;
                
            }
            
        }
        
    }

    Mesh triangulationMesh(){

        this.triangulationA.Calculate();
        this.triangulationB.Calculate();

        int i;
        Mesh mesh = new Mesh();
        mesh.subMeshCount = 2;

        int tA = this.triangulationA.triangles.Count;
        int tB = this.triangulationB.triangles.Count;

        int[] trianglesA = new int[tA*3];
        int[] trianglesB = new int[tB*3];

        this.triangulationA.AddTriangles(triangulationB.vertices.ToArray(),triangulationB.triangles.ToArray());
        this.triangulationA.updateLocalPosition(ObjectA.transform);

        Vector3[] vertices = new Vector3[triangulationA.vertices.Count];
        Vector3[] normals = new Vector3[triangulationA.vertices.Count];
        Vector2[] uv = new Vector2[triangulationA.vertices.Count];

        for(i=0;i

            vertices[i] = triangulationA.vertices[i].localPos;
            normals[i] = triangulationA.vertices[i].normal.normalized;
            uv[i] = triangulationA.vertices[i].uv;

        }

        for(i=0;i

            trianglesA[i*3]   = triangulationA.triangles[i].indexVertice[0];
            trianglesA[i*3+1] = triangulationA.triangles[i].indexVertice[1];
            trianglesA[i*3+2] = triangulationA.triangles[i].indexVertice[2];

        }

        for(i=0;i             
            trianglesB[i*3]   = triangulationA.triangles[tA+i].indexVertice[0];
            trianglesB[i*3+1] = triangulationA.triangles[tA+i].indexVertice[1];
            trianglesB[i*3+2] = triangulationA.triangles[tA+i].indexVertice[2];
            
        }

        recalculateTriangles(vertices,normals,trianglesA);
        recalculateTriangles(vertices,normals,trianglesB);
        mesh.vertices = vertices;
        mesh.normals = normals;
        mesh.uv = uv;
        mesh.SetTriangles(trianglesA,0);
        mesh.SetTriangles(trianglesB,1);

        return mesh;

    }

    public Mesh Union() {

        intersections();
        clearVertices(this.triangulationA,-1);
        clearVertices(this.triangulationB,-1);
        return triangulationMesh();

    }

    public Mesh Intersection(){

        intersections();
        clearVertices(this.triangulationA,0);
        clearVertices(this.triangulationB,0);
        return triangulationMesh();

    }

    public Mesh Difference(){

        intersections();
        clearVertices(this.triangulationA,-1);
        clearVertices(this.triangulationB,0);
        this.triangulationB.invertNormals();
        return triangulationMesh();

    }
    
    void intersections(){

        //Update world position vertices
        this.triangulationA.updateWorldPosition(ObjectA.transform);
        this.triangulationB.updateWorldPosition(ObjectB.transform);

        //IntersectionDATA
        intersectionDATA varA = new intersectionDATA(this.triangulationA,this.triangulationB,this.ObjectB);
        intersectionDATA varB = new intersectionDATA(this.triangulationB,this.triangulationA,this.ObjectA);

        //In/Out Points
        AInToB(varA);
        AInToB(varB);

        //Intersections
        intersectionsAtoB(varA);
        intersectionsAtoB(varB);

    }


    ///////////////////////////////////////////////////////////////////////////////

    bool r,l,u,d,f,b;

    RaycastHit rightHit   = new RaycastHit();
    RaycastHit leftHit    = new RaycastHit();
    RaycastHit upHit      = new RaycastHit();
    RaycastHit downHit    = new RaycastHit();
    RaycastHit forwardHit = new RaycastHit();
    RaycastHit backHit    = new RaycastHit();
    RaycastHit tempHit    = new RaycastHit();
    
    Ray right   = new Ray(Vector3.zero , -Vector3.right);
    Ray left    = new Ray(Vector3.zero , -Vector3.left);
    Ray up      = new Ray(Vector3.zero , -Vector3.up);
    Ray down    = new Ray(Vector3.zero , -Vector3.down);
    Ray forward = new Ray(Vector3.zero , -Vector3.forward);
    Ray back    = new Ray(Vector3.zero , -Vector3.back);
    Ray tempRay = new Ray();

    bool ConcaveHull(MeshCollider meshCollider, Vector3 position, Ray ray, RaycastHit hit){
        
        
        tempRay.origin = position;
        tempRay.direction =- ray.direction;
        customDistance = distance - hit.distance;

        while(meshCollider.Raycast(tempRay, out tempHit, customDistance)){
            
            if(tempHit.triangleIndex == hit.triangleIndex) break;
            ray.origin = -ray.direction * customDistance + position;
            
            if(!meshCollider.Raycast(ray, out hit, customDistance)) return true;
                
            if(tempHit.triangleIndex == hit.triangleIndex) break;
            customDistance -= hit.distance;
            
        }
        
        return false;
        
    }

    bool In(MeshCollider meshCollider, Vector3 position) {
        
        right.origin   = -right.direction   * distance + position;
        left.origin    = -left.direction    * distance + position;
        up.origin      = -up.direction      * distance + position;
        down.origin    = -down.direction    * distance + position;
        forward.origin = -forward.direction * distance + position;
        back.origin    = -back.direction    * distance + position;
        
        r = meshCollider.Raycast(right   , out rightHit   , distance);
        l = meshCollider.Raycast(left    , out leftHit    , distance);
        u = meshCollider.Raycast(up      , out upHit      , distance);
        d = meshCollider.Raycast(down    , out downHit    , distance);
        f = meshCollider.Raycast(forward , out forwardHit , distance);
        b = meshCollider.Raycast(back    , out backHit    , distance);
        
        if(r&&l&&u&&d&&f&&b) {
            
            if(!ConcaveHull(meshCollider,position,right,rightHit))
                if(!ConcaveHull(meshCollider,position,left,leftHit))
                    if(!ConcaveHull(meshCollider,position,up,upHit))
                        if(!ConcaveHull(meshCollider,position,down,downHit))
                            if(!ConcaveHull(meshCollider,position,forward,forwardHit))
                                if(!ConcaveHull(meshCollider,position,back,backHit)) return true;
            
        }

        return false;
        
    }

    ///////////////////////////////////////////////////////////////////////////////


}
 

 

using UnityEngine;
using System;
using System.Collections.Generic;

public class Triangulation {

    public class Vertex{
        
        public Vector3 localPos; //Mesh-Local Position
        public Vector3 pos;      //World Position
        public Vector3 normal;
        public Vector2 uv;
        public int type;  // -1: In, 0: Out/Local, 1: Intersection
        
        public Vertex(Vector3 p, int t, Vector3 n, Vector2 u){
            
            this.type = t;
            if(t == 0) { this.localPos = new Vector3(p.x,p.y,p.z) ; this.pos = new Vector3(); }//Local Position
            else { this.pos = new Vector3(p.x,p.y,p.z);  this.localPos=new Vector3(); }//World Position
            this.normal = new Vector3(n.x,n.y,n.z);
            this.uv = new Vector2(u.x,u.y);
            
        }

        public Vertex(Vector3 lp, Vector3 p, int t, Vector3 n, Vector2 u){

            this.localPos = new Vector3(lp.x,lp.y,lp.z);
            this.pos = new Vector3(p.x,p.y,p.z);
            this.normal = new Vector3(n.x,n.y,n.z);
            this.uv = new Vector2(u.x,u.y);
            this.type = t;

        }

        public Vertex Clone(){

            return new Vertex(this.localPos,this.pos,this.type,this.normal,this.uv);

        }
        
    }
    
    public class Polygon{
        
        public List indexVertice;
        
        public Polygon(int[] indexVertices) {
            
            this.indexVertice = new List();
            this.indexVertice.AddRange(indexVertices);

        }

        public Polygon Clone(){

            int[] index = new int[this.indexVertice.Count];
            for(int i=0;i             Polygon polygon = new Polygon(index);
            return polygon;

        }


    }
    
    public List vertices;
    public List triangles;    
    public float lowerAngle;

    public Triangulation() {

        this.vertices = new List();
        this.triangles = new List();
        this.lowerAngle = 1f;
    
    }

    public Triangulation(MeshCollider meshC){
        
        int i;
        this.lowerAngle = 1f;
        this.vertices = new List();
        for(i=0;i         this.triangles = new List();
        for(i=0;i

    }

    public Triangulation Clone(){

        Triangulation triangulation = new Triangulation();
        int i;
        for(i=0;i         for(i=0;i

        return triangulation;

    }

    bool existOnTriangle (Vector3 worldPosition, int onTriangle){

        for(int i=0;i

            if(this.vertices[this.triangles[onTriangle].indexVertice[i]].pos == worldPosition) return true;

        }

        return false;

    }

    public void AddWorldPointOnTriangle(RaycastHit hit,int onTriangle){ AddWorldPointOnTriangle(hit.point, onTriangle); }
    public void AddWorldPointOnTriangle(Vector3 pos, int onTriangle){
        
        if(onTriangle<0 || onTriangle>= this.triangles.Count) return;

        if(!existOnTriangle(pos,onTriangle)){

            this.vertices.Add(new Vertex(pos,1,normalCoords(onTriangle),uvCoords(pos,onTriangle)));
            this.triangles[onTriangle].indexVertice.Add(this.vertices.Count-1);

        }
        
    }
    public void AddWorldPointOnTriangle(RaycastHit hit){

        if(!existOnTriangle(hit.point,hit.triangleIndex)){

            this.vertices.Add(new Vertex(hit.point,1,hit.normal,hit.textureCoord));
            this.triangles[hit.triangleIndex].indexVertice.Add(this.vertices.Count-1);

        }

    }

    public void updateLocalPosition(Transform matrix){ for(int i=0;i     public void updateWorldPosition(Transform matrix){ for(int i=0;i

    public void AddTriangles(Vertex[] vertices, Polygon[] polygons){

        int head = this.vertices.Count;
        int i,w;
        this.vertices.AddRange(vertices);
        for(i=0;i

            for(w=0;w

                polygons[i].indexVertice[w] += head;

            }

        }

        this.triangles.AddRange(polygons);
        
    }
    
    Vector3 normalCoords(int onTriangle){
        
        Vector3 a,b,c;

        a = this.vertices[this.triangles[onTriangle].indexVertice[0]].localPos;
        b = this.vertices[this.triangles[onTriangle].indexVertice[1]].localPos;
        c = this.vertices[this.triangles[onTriangle].indexVertice[2]].localPos;

        b = b - a;
        c = c - a;

        return Vector3.Cross(b,c).normalized;

    }
    
    public void invertNormals(){ for(int i=0;i     
    Vector2 uvCoords(Vector3 point, int onTriangle){
        
        // http://answers.unity3d.com/questions/383804/calculate-uv-coordinates-of-3d-point-on-plane-of-m.html
        // ... interpolate (extrapolate?) points outside the triangle, a more general approach must be used: the "sign" of each
        // area must be taken into account, which produces correct results for points inside or outside the triangle. In order 
        // to calculate the area "signs", we can use (guess what?) dot products - like this:
        
        
        // triangle points
        Vector3 p1 = this.vertices[this.triangles[onTriangle].indexVertice[0]].pos;
        Vector3 p2 = this.vertices[this.triangles[onTriangle].indexVertice[1]].pos;
        Vector3 p3 = this.vertices[this.triangles[onTriangle].indexVertice[2]].pos;
        // calculate vectors from point f to vertices p1, p2 and p3:
        Vector3 f1 = p1-point; //p1-f;
        Vector3 f2 = p2-point; //p2-f;
        Vector3 f3 = p3-point; //p3-f;
        // calculate the areas (parameters order is essential in this case):
        Vector3 va  = Vector3.Cross(p1-p2, p1-p3); // main triangle cross product
        Vector3 va1 = Vector3.Cross(f2, f3); // p1's triangle cross product
        Vector3 va2 = Vector3.Cross(f3, f1); // p2's triangle cross product
        Vector3 va3 = Vector3.Cross(f1, f2); // p3's triangle cross product
        float     a = va.magnitude; // main triangle area
        // calculate barycentric coordinates with sign:
        float a1 = va1.magnitude/a * Mathf.Sign(Vector3.Dot(va, va1));
        float a2 = va2.magnitude/a * Mathf.Sign(Vector3.Dot(va, va2));
        float a3 = va3.magnitude/a * Mathf.Sign(Vector3.Dot(va, va3));
        // find the uv corresponding to point f (uv1/uv2/uv3 are associated to p1/p2/p3):
        Vector2 uv1=this.vertices[this.triangles[onTriangle].indexVertice[0]].uv;
        Vector2 uv2=this.vertices[this.triangles[onTriangle].indexVertice[1]].uv;
        Vector2 uv3=this.vertices[this.triangles[onTriangle].indexVertice[2]].uv;
        
        return uv1 * a1 + uv2 * a2 + uv3 * a3;
        
    }
    
    
    public void Calculate(){
        
        if(this.vertices.Count == 0) return;
        
        int i,w,x,q;
        float circumsphereRadius;
        Vector3 a,b,c,ac,ab,abXac,toCircumsphereCenter,ccs;
        bool allIntersections;
        List combination = new List();

        for(q=this.triangles.Count-1;q>-1;q--){
        
            if(this.triangles[q].indexVertice.Count > 3){

                allIntersections = true;

                // Delete Duplicate
                for(i=this.triangles[q].indexVertice.Count-1;i>0;i--){
                    
                    for(w=0;w

                        if(this.vertices[this.triangles[q].indexVertice[i]].type < 1) allIntersections = false;
                        if(this.vertices[this.triangles[q].indexVertice[i]].pos == this.vertices[this.triangles[q].indexVertice[w]].pos) { this.triangles[q].indexVertice.RemoveAt(i); break; }
                                                
                    }                    
                    
                }

                if(this.triangles[q].indexVertice.Count > 3){

                    //All Combinations without repetition, some vertice of different type
                    for(i=0;i                         
                        if(!allIntersections) if(this.vertices[this.triangles[q].indexVertice[i]].type==this.vertices[this.triangles[q].indexVertice[w]].type && this.vertices[this.triangles[q].indexVertice[i]].type==this.vertices[this.triangles[q].indexVertice[x]].type) continue; // Same type
                        //if(Vector3.Angle(this.vertices[this.triangles[q].indexVertice[w]].pos-this.vertices[this.triangles[q].indexVertice[i]].pos,this.vertices[this.triangles[q].indexVertice[x]].pos-this.vertices[this.triangles[q].indexVertice[i]].pos) < this.lowerAngle) continue; // Remove triangles with angle near to 180º
                        combination.Add(new int[3]{this.triangles[q].indexVertice[i],this.triangles[q].indexVertice[w],this.triangles[q].indexVertice[x]});
                        
                    } } }

                    //Delaunay Condition
                    for(i=combination.Count-1;i>-1;i--){
                        
                        //Points
                        a = this.vertices[combination[i][0]].pos;
                        b = this.vertices[combination[i][1]].pos;
                        c = this.vertices[combination[i][2]].pos;
                        
                        //Circumcenter 3Dpoints
                        //http://gamedev.stackexchange.com/questions/60630/how-do-i-find-the-circumcenter-of-a-triangle-in-3d
                        ac = c - a ;
                        ab = b - a ;
                        abXac = Vector3.Cross(ab,ac);                
                        // this is the vector from a TO the circumsphere center
                        toCircumsphereCenter = (Vector3.Cross(abXac,ab)*ac.sqrMagnitude + Vector3.Cross(ac,abXac)*ab.sqrMagnitude) / (2f*abXac.sqrMagnitude);                
                        // The 3 space coords of the circumsphere center then:
                        ccs = a  +  toCircumsphereCenter ; // now this is the actual 3space location
                        // The three vertices A, B, C of the triangle ABC are the same distance from the circumcenter ccs.
                        circumsphereRadius = toCircumsphereCenter.magnitude;
                        // As defined by the Delaunay condition, circumcircle is empty if it contains no other vertices besides the three that define.
                        for(w=0;w

                            if(this.triangles[q].indexVertice[w]!=combination[i][0] && this.triangles[q].indexVertice[w]!=combination[i][1] && this.triangles[q].indexVertice[w]!=combination[i][2]){

                                // If it's not empty, remove.
                                if(Vector3.Distance(this.vertices[this.triangles[q].indexVertice[w]].pos,ccs)<=circumsphereRadius){

                                    combination.RemoveAt(i);
                                    break;

                                }

                            }

                        }

                    }

                    if(combination.Count>0){

                        this.triangles.RemoveAt(q);
                        for(i=0;i

                            /*
                            this.vertices.Add(this.vertices[combination[i][0]].Clone());
                            combination[i][0] = this.vertices.Count-1;
                            this.vertices.Add(this.vertices[combination[i][1]].Clone());
                            combination[i][1] = this.vertices.Count-1;
                            this.vertices.Add(this.vertices[combination[i][2]].Clone());
                            combination[i][2] = this.vertices.Count-1;
                            */

                            this.triangles.Add(new Polygon(combination[i]));

                        }

                    }

                    combination.Clear();

                }
        
            }

        }

    }
    
}
效果如图:

unity中两模型之间的OBB包围盒碰撞检测_第1张图片

你可能感兴趣的:(unity中两模型之间的OBB包围盒碰撞检测)