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
meshColliderB.transform.GetComponent
//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){ var.triangleA = i; } 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.triangles[i].indexVertice.Count<3) triangulation.triangles.RemoveAt(i); } void recalculateTriangles(Vector3[] vertices,Vector3[] normals,int[] triangles){ Mesh triangulationMesh(){ this.triangulationA.Calculate(); int i; int tA = this.triangulationA.triangles.Count; int[] trianglesA = new int[tA*3]; this.triangulationA.AddTriangles(triangulationB.vertices.ToArray(),triangulationB.triangles.ToArray()); Vector3[] vertices = new Vector3[triangulationA.vertices.Count]; for(i=0;i vertices[i] = triangulationA.vertices[i].localPos; } for(i=0;i trianglesA[i*3] = triangulationA.triangles[i].indexVertice[0]; } for(i=0;i recalculateTriangles(vertices,normals,trianglesA); return mesh; } public Mesh Union() { intersections(); } public Mesh Intersection(){ intersections(); } public Mesh Difference(){ intersections(); } //Update world position vertices //IntersectionDATA //In/Out Points //Intersections } bool r,l,u,d,f,b; RaycastHit rightHit = new RaycastHit(); bool ConcaveHull(MeshCollider meshCollider, Vector3 position, Ray ray, RaycastHit hit){ while(meshCollider.Raycast(tempRay, out tempHit, customDistance)){ bool In(MeshCollider meshCollider, Vector3 position) { return false; /////////////////////////////////////////////////////////////////////////////// using UnityEngine; public class Triangulation { public class Vertex{ public Vertex(Vector3 lp, Vector3 p, int t, Vector3 n, Vector2 u){ this.localPos = new Vector3(lp.x,lp.y,lp.z); } public Vertex Clone(){ return new Vertex(this.localPos,this.pos,this.type,this.normal,this.uv); } } public Polygon Clone(){ int[] index = new int[this.indexVertice.Count]; } public Triangulation() { this.vertices = new List public Triangulation(MeshCollider meshC){ } public Triangulation Clone(){ Triangulation triangulation = new Triangulation(); 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); } if(!existOnTriangle(pos,onTriangle)){ this.vertices.Add(new Vertex(pos,1,normalCoords(onTriangle),uvCoords(pos,onTriangle))); } if(!existOnTriangle(hit.point,hit.triangleIndex)){ this.vertices.Add(new Vertex(hit.point,1,hit.normal,hit.textureCoord)); } } public void updateLocalPosition(Transform matrix){ for(int i=0;i public void AddTriangles(Vertex[] vertices, Polygon[] polygons){ int head = this.vertices.Count; for(w=0;w polygons[i].indexVertice[w] += head; } } this.triangles.AddRange(polygons); a = this.vertices[this.triangles[onTriangle].indexVertice[0]].localPos; b = b - a; return Vector3.Cross(b,c).normalized; } for(q=this.triangles.Count-1;q>-1;q--){ allIntersections = true; // Delete Duplicate if(this.vertices[this.triangles[q].indexVertice[i]].type < 1) allIntersections = false; if(this.triangles[q].indexVertice.Count > 3){ //All Combinations without repetition, some vertice of different type //Delaunay Condition 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. combination.RemoveAt(i); } } } } if(combination.Count>0){ this.triangles.RemoveAt(q); /* this.triangles.Add(new Polygon(combination[i])); } } combination.Clear(); } } }
for(int i=0;i
intersectionRay(0,1,var);
intersectionRay(0,2,var);
intersectionRay(1,2,var);
}
if(triangulation.vertices[triangulation.triangles[i].indexVertice[w]].type == t) triangulation.triangles[i].indexVertice.RemoveAt(w);
}
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;
}
}
}
this.triangulationB.Calculate();
Mesh mesh = new Mesh();
mesh.subMeshCount = 2;
int tB = this.triangulationB.triangles.Count;
int[] trianglesB = new int[tB*3];
this.triangulationA.updateLocalPosition(ObjectA.transform);
Vector3[] normals = new Vector3[triangulationA.vertices.Count];
Vector2[] uv = new Vector2[triangulationA.vertices.Count];
normals[i] = triangulationA.vertices[i].normal.normalized;
uv[i] = triangulationA.vertices[i].uv;
trianglesA[i*3+1] = triangulationA.triangles[i].indexVertice[1];
trianglesA[i*3+2] = triangulationA.triangles[i].indexVertice[2];
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,trianglesB);
mesh.vertices = vertices;
mesh.normals = normals;
mesh.uv = uv;
mesh.SetTriangles(trianglesA,0);
mesh.SetTriangles(trianglesB,1);
clearVertices(this.triangulationA,-1);
clearVertices(this.triangulationB,-1);
return triangulationMesh();
clearVertices(this.triangulationA,0);
clearVertices(this.triangulationB,0);
return triangulationMesh();
clearVertices(this.triangulationA,-1);
clearVertices(this.triangulationB,0);
this.triangulationB.invertNormals();
return triangulationMesh();
void intersections(){
this.triangulationA.updateWorldPosition(ObjectA.transform);
this.triangulationB.updateWorldPosition(ObjectB.transform);
intersectionDATA varA = new intersectionDATA(this.triangulationA,this.triangulationB,this.ObjectB);
intersectionDATA varB = new intersectionDATA(this.triangulationB,this.triangulationA,this.ObjectA);
AInToB(varA);
AInToB(varB);
intersectionsAtoB(varA);
intersectionsAtoB(varB);
///////////////////////////////////////////////////////////////////////////////
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();
tempRay.origin = position;
tempRay.direction =- ray.direction;
customDistance = distance - hit.distance;
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;
}
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;
}
}
}
using System;
using System.Collections.Generic;
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);
}
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 class Polygon{
public List
public Polygon(int[] indexVertices) {
this.indexVertice = new List
this.indexVertice.AddRange(indexVertices);
for(int i=0;i
return polygon;
}
public List
public List
public float lowerAngle;
this.triangles = new List
this.lowerAngle = 1f;
}
int i;
this.lowerAngle = 1f;
this.vertices = new List
for(i=0;i
for(i=0;i
int i;
for(i=0;i
public void AddWorldPointOnTriangle(Vector3 pos, int onTriangle){
if(onTriangle<0 || onTriangle>= this.triangles.Count) return;
this.triangles[onTriangle].indexVertice.Add(this.vertices.Count-1);
}
public void AddWorldPointOnTriangle(RaycastHit hit){
this.triangles[hit.triangleIndex].indexVertice.Add(this.vertices.Count-1);
int i,w;
this.vertices.AddRange(vertices);
for(i=0;i
}
Vector3 normalCoords(int onTriangle){
Vector3 a,b,c;
b = this.vertices[this.triangles[onTriangle].indexVertice[1]].localPos;
c = this.vertices[this.triangles[onTriangle].indexVertice[2]].localPos;
c = c - a;
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
if(this.triangles[q].indexVertice.Count > 3){
for(i=this.triangles[q].indexVertice.Count-1;i>0;i--){
for(w=0;w
if(this.vertices[this.triangles[q].indexVertice[i]].pos == this.vertices[this.triangles[q].indexVertice[w]].pos) { this.triangles[q].indexVertice.RemoveAt(i); break; }
}
}
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]});
} } }
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(Vector3.Distance(this.vertices[this.triangles[q].indexVertice[w]].pos,ccs)<=circumsphereRadius){
break;
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;
*/
}
}
效果如图: