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<MeshFilter>();
MeshRenderer meshRenderer = newObject.AddComponent<MeshRenderer>();
meshRenderer.materials = new Material[2]{
meshColliderA.transform.GetComponent<Renderer>().materials[0],
meshColliderB.transform.GetComponent<Renderer>().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<var.A.vertices.Count;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.A.triangles.Count;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<triangles.Length;i+=3){
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<triangulationA.vertices.Count;i++){
vertices[i] = triangulationA.vertices[i].localPos;
normals[i] = triangulationA.vertices[i].normal.normalized;
uv[i] = triangulationA.vertices[i].uv;
}
for(i=0;i<tA;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<tB;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<int> indexVertice;
public Polygon(int[] indexVertices) {
this.indexVertice = new List<int>();
this.indexVertice.AddRange(indexVertices);
}
public Polygon Clone(){
int[] index = new int[this.indexVertice.Count];
for(int i=0;i<this.indexVertice.Count;i++) index[i] = this.indexVertice[i];
Polygon polygon = new Polygon(index);
return polygon;
}
}
public List<Vertex> vertices;
public List<Polygon> triangles;
public float lowerAngle;
public Triangulation() {
this.vertices = new List<Vertex>();
this.triangles = new List<Polygon>();
this.lowerAngle = 1f;
}
public Triangulation(MeshCollider meshC){
int i;
this.lowerAngle = 1f;
this.vertices = new List<Vertex>();
for(i=0;i<meshC.sharedMesh.vertices.Length;i++) this.vertices.Add(new Vertex(meshC.sharedMesh.vertices[i],0,meshC.sharedMesh.normals[i],meshC.sharedMesh.uv[i]));
this.triangles = new List<Polygon>();
for(i=0;i<meshC.sharedMesh.triangles.Length;i+=3) this.triangles.Add(new Polygon(new int[3]{ meshC.sharedMesh.triangles[i] , meshC.sharedMesh.triangles[i+1] , meshC.sharedMesh.triangles[i+2]}));
}
public Triangulation Clone(){
Triangulation triangulation = new Triangulation();
int i;
for(i=0;i<this.vertices.Count;i++) triangulation.vertices.Add(this.vertices[i].Clone());
for(i=0;i<this.triangles.Count;i++) triangulation.triangles.Add(this.triangles[i].Clone());
return triangulation;
}
bool existOnTriangle (Vector3 worldPosition, int onTriangle){
for(int i=0;i<this.triangles[onTriangle].indexVertice.Count;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<this.vertices.Count;i++) this.vertices[i].localPos = matrix.worldToLocalMatrix.MultiplyPoint3x4(this.vertices[i].pos); }
public void updateWorldPosition(Transform matrix){ for(int i=0;i<this.vertices.Count;i++) this.vertices[i].pos = matrix.localToWorldMatrix.MultiplyPoint3x4(this.vertices[i].localPos); }
public void AddTriangles(Vertex[] vertices, Polygon[] polygons){
int head = this.vertices.Count;
int i,w;
this.vertices.AddRange(vertices);
for(i=0;i<polygons.Length;i++){
for(w=0;w<polygons[i].indexVertice.Count;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<this.vertices.Count;i++) this.vertices[i].normal *= -1f; }
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<int[]> combination = new List<int[]>();
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<i;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<this.triangles[q].indexVertice.Count-2;i++){ for(w=i+1;w<this.triangles[q].indexVertice.Count-1;w++){ for(x=w+1;x<this.triangles[q].indexVertice.Count;x++){
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<this.triangles[q].indexVertice.Count;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<combination.Count;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();
}
}
}
}
}
效果如图: