412 lines
13 KiB
C#
412 lines
13 KiB
C#
|
/* The MIT License
|
|||
|
*
|
|||
|
* Copyright (c) 2010 Intel Corporation.
|
|||
|
* All rights reserved.
|
|||
|
*
|
|||
|
* Based on the convexdecomposition library from
|
|||
|
* <http://codesuppository.googlecode.com> by John W. Ratcliff and Stan Melax.
|
|||
|
*
|
|||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|||
|
* of this software and associated documentation files (the "Software"), to deal
|
|||
|
* in the Software without restriction, including without limitation the rights
|
|||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
|
* copies of the Software, and to permit persons to whom the Software is
|
|||
|
* furnished to do so, subject to the following conditions:
|
|||
|
*
|
|||
|
* The above copyright notice and this permission notice shall be included in
|
|||
|
* all copies or substantial portions of the Software.
|
|||
|
*
|
|||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||
|
* THE SOFTWARE.
|
|||
|
*/
|
|||
|
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Diagnostics;
|
|||
|
|
|||
|
namespace OpenSim.Region.Physics.ConvexDecompositionDotNet
|
|||
|
{
|
|||
|
public class DecompDesc
|
|||
|
{
|
|||
|
public List<float3> mVertices;
|
|||
|
public List<int> mIndices;
|
|||
|
|
|||
|
// options
|
|||
|
public uint mDepth; // depth to split, a maximum of 10, generally not over 7.
|
|||
|
public float mCpercent; // the concavity threshold percentage. 0=20 is reasonable.
|
|||
|
public float mPpercent; // the percentage volume conservation threshold to collapse hulls. 0-30 is reasonable.
|
|||
|
|
|||
|
// hull output limits.
|
|||
|
public uint mMaxVertices; // maximum number of vertices in the output hull. Recommended 32 or less.
|
|||
|
public float mSkinWidth; // a skin width to apply to the output hulls.
|
|||
|
|
|||
|
public ConvexDecompositionCallback mCallback; // the interface to receive back the results.
|
|||
|
|
|||
|
public DecompDesc()
|
|||
|
{
|
|||
|
mDepth = 5;
|
|||
|
mCpercent = 5;
|
|||
|
mPpercent = 5;
|
|||
|
mMaxVertices = 32;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public class CHull
|
|||
|
{
|
|||
|
public float[] mMin = new float[3];
|
|||
|
public float[] mMax = new float[3];
|
|||
|
public float mVolume;
|
|||
|
public float mDiagonal;
|
|||
|
public ConvexResult mResult;
|
|||
|
|
|||
|
public CHull(ConvexResult result)
|
|||
|
{
|
|||
|
mResult = new ConvexResult(result);
|
|||
|
mVolume = Concavity.computeMeshVolume(result.HullVertices, result.HullIndices);
|
|||
|
|
|||
|
mDiagonal = getBoundingRegion(result.HullVertices, mMin, mMax);
|
|||
|
|
|||
|
float dx = mMax[0] - mMin[0];
|
|||
|
float dy = mMax[1] - mMin[1];
|
|||
|
float dz = mMax[2] - mMin[2];
|
|||
|
|
|||
|
dx *= 0.1f; // inflate 1/10th on each edge
|
|||
|
dy *= 0.1f; // inflate 1/10th on each edge
|
|||
|
dz *= 0.1f; // inflate 1/10th on each edge
|
|||
|
|
|||
|
mMin[0] -= dx;
|
|||
|
mMin[1] -= dy;
|
|||
|
mMin[2] -= dz;
|
|||
|
|
|||
|
mMax[0] += dx;
|
|||
|
mMax[1] += dy;
|
|||
|
mMax[2] += dz;
|
|||
|
}
|
|||
|
|
|||
|
public void Dispose()
|
|||
|
{
|
|||
|
mResult = null;
|
|||
|
}
|
|||
|
|
|||
|
public bool overlap(CHull h)
|
|||
|
{
|
|||
|
return overlapAABB(mMin, mMax, h.mMin, h.mMax);
|
|||
|
}
|
|||
|
|
|||
|
// returns the d1Giagonal distance
|
|||
|
private static float getBoundingRegion(List<float3> points, float[] bmin, float[] bmax)
|
|||
|
{
|
|||
|
float3 first = points[0];
|
|||
|
|
|||
|
bmin[0] = first.x;
|
|||
|
bmin[1] = first.y;
|
|||
|
bmin[2] = first.z;
|
|||
|
|
|||
|
bmax[0] = first.x;
|
|||
|
bmax[1] = first.y;
|
|||
|
bmax[2] = first.z;
|
|||
|
|
|||
|
for (int i = 1; i < points.Count; i++)
|
|||
|
{
|
|||
|
float3 p = points[i];
|
|||
|
|
|||
|
if (p[0] < bmin[0]) bmin[0] = p[0];
|
|||
|
if (p[1] < bmin[1]) bmin[1] = p[1];
|
|||
|
if (p[2] < bmin[2]) bmin[2] = p[2];
|
|||
|
|
|||
|
if (p[0] > bmax[0]) bmax[0] = p[0];
|
|||
|
if (p[1] > bmax[1]) bmax[1] = p[1];
|
|||
|
if (p[2] > bmax[2]) bmax[2] = p[2];
|
|||
|
}
|
|||
|
|
|||
|
float dx = bmax[0] - bmin[0];
|
|||
|
float dy = bmax[1] - bmin[1];
|
|||
|
float dz = bmax[2] - bmin[2];
|
|||
|
|
|||
|
return (float)Math.Sqrt(dx * dx + dy * dy + dz * dz);
|
|||
|
}
|
|||
|
|
|||
|
// return true if the two AABB's overlap.
|
|||
|
private static bool overlapAABB(float[] bmin1, float[] bmax1, float[] bmin2, float[] bmax2)
|
|||
|
{
|
|||
|
if (bmax2[0] < bmin1[0]) return false; // if the maximum is less than our minimum on any axis
|
|||
|
if (bmax2[1] < bmin1[1]) return false;
|
|||
|
if (bmax2[2] < bmin1[2]) return false;
|
|||
|
|
|||
|
if (bmin2[0] > bmax1[0]) return false; // if the minimum is greater than our maximum on any axis
|
|||
|
if (bmin2[1] > bmax1[1]) return false; // if the minimum is greater than our maximum on any axis
|
|||
|
if (bmin2[2] > bmax1[2]) return false; // if the minimum is greater than our maximum on any axis
|
|||
|
|
|||
|
return true; // the extents overlap
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public class ConvexBuilder
|
|||
|
{
|
|||
|
public List<CHull> mChulls = new List<CHull>();
|
|||
|
private ConvexDecompositionCallback mCallback;
|
|||
|
|
|||
|
private int MAXDEPTH = 8;
|
|||
|
private float CONCAVE_PERCENT = 1f;
|
|||
|
private float MERGE_PERCENT = 2f;
|
|||
|
|
|||
|
public ConvexBuilder(ConvexDecompositionCallback callback)
|
|||
|
{
|
|||
|
mCallback = callback;
|
|||
|
}
|
|||
|
|
|||
|
public void Dispose()
|
|||
|
{
|
|||
|
int i;
|
|||
|
for (i = 0; i < mChulls.Count; i++)
|
|||
|
{
|
|||
|
CHull cr = mChulls[i];
|
|||
|
cr.Dispose();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool isDuplicate(uint i1, uint i2, uint i3, uint ci1, uint ci2, uint ci3)
|
|||
|
{
|
|||
|
uint dcount = 0;
|
|||
|
|
|||
|
Debug.Assert(i1 != i2 && i1 != i3 && i2 != i3);
|
|||
|
Debug.Assert(ci1 != ci2 && ci1 != ci3 && ci2 != ci3);
|
|||
|
|
|||
|
if (i1 == ci1 || i1 == ci2 || i1 == ci3)
|
|||
|
dcount++;
|
|||
|
if (i2 == ci1 || i2 == ci2 || i2 == ci3)
|
|||
|
dcount++;
|
|||
|
if (i3 == ci1 || i3 == ci2 || i3 == ci3)
|
|||
|
dcount++;
|
|||
|
|
|||
|
return dcount == 3;
|
|||
|
}
|
|||
|
|
|||
|
public void getMesh(ConvexResult cr, VertexPool vc, List<int> indices)
|
|||
|
{
|
|||
|
List<int> src = cr.HullIndices;
|
|||
|
|
|||
|
for (int i = 0; i < src.Count / 3; i++)
|
|||
|
{
|
|||
|
int i1 = src[i * 3 + 0];
|
|||
|
int i2 = src[i * 3 + 1];
|
|||
|
int i3 = src[i * 3 + 2];
|
|||
|
|
|||
|
float3 p1 = cr.HullVertices[i1];
|
|||
|
float3 p2 = cr.HullVertices[i2];
|
|||
|
float3 p3 = cr.HullVertices[i3];
|
|||
|
|
|||
|
i1 = vc.getIndex(p1);
|
|||
|
i2 = vc.getIndex(p2);
|
|||
|
i3 = vc.getIndex(p3);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public CHull canMerge(CHull a, CHull b)
|
|||
|
{
|
|||
|
if (!a.overlap(b)) // if their AABB's (with a little slop) don't overlap, then return.
|
|||
|
return null;
|
|||
|
|
|||
|
CHull ret = null;
|
|||
|
|
|||
|
// ok..we are going to combine both meshes into a single mesh
|
|||
|
// and then we are going to compute the concavity...
|
|||
|
|
|||
|
VertexPool vc = new VertexPool();
|
|||
|
|
|||
|
List<int> indices = new List<int>();
|
|||
|
|
|||
|
getMesh(a.mResult, vc, indices);
|
|||
|
getMesh(b.mResult, vc, indices);
|
|||
|
|
|||
|
int vcount = vc.GetSize();
|
|||
|
List<float3> vertices = vc.GetVertices();
|
|||
|
int tcount = indices.Count / 3;
|
|||
|
|
|||
|
//don't do anything if hull is empty
|
|||
|
if (tcount == 0)
|
|||
|
{
|
|||
|
vc.Clear();
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
HullResult hresult = new HullResult();
|
|||
|
HullDesc desc = new HullDesc();
|
|||
|
|
|||
|
desc.SetHullFlag(HullFlag.QF_TRIANGLES);
|
|||
|
desc.Vertices = vertices;
|
|||
|
|
|||
|
HullError hret = HullUtils.CreateConvexHull(desc, ref hresult);
|
|||
|
|
|||
|
if (hret == HullError.QE_OK)
|
|||
|
{
|
|||
|
float combineVolume = Concavity.computeMeshVolume(hresult.OutputVertices, hresult.Indices);
|
|||
|
float sumVolume = a.mVolume + b.mVolume;
|
|||
|
|
|||
|
float percent = (sumVolume * 100) / combineVolume;
|
|||
|
if (percent >= (100.0f - MERGE_PERCENT))
|
|||
|
{
|
|||
|
ConvexResult cr = new ConvexResult(hresult.OutputVertices, hresult.Indices);
|
|||
|
ret = new CHull(cr);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
vc.Clear();
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
public bool combineHulls()
|
|||
|
{
|
|||
|
bool combine = false;
|
|||
|
|
|||
|
sortChulls(mChulls); // sort the convex hulls, largest volume to least...
|
|||
|
|
|||
|
List<CHull> output = new List<CHull>(); // the output hulls...
|
|||
|
|
|||
|
int i;
|
|||
|
for (i = 0; i < mChulls.Count && !combine; ++i)
|
|||
|
{
|
|||
|
CHull cr = mChulls[i];
|
|||
|
|
|||
|
int j;
|
|||
|
for (j = 0; j < mChulls.Count; j++)
|
|||
|
{
|
|||
|
CHull match = mChulls[j];
|
|||
|
|
|||
|
if (cr != match) // don't try to merge a hull with itself, that be stoopid
|
|||
|
{
|
|||
|
|
|||
|
CHull merge = canMerge(cr, match); // if we can merge these two....
|
|||
|
|
|||
|
if (merge != null)
|
|||
|
{
|
|||
|
output.Add(merge);
|
|||
|
|
|||
|
++i;
|
|||
|
while (i != mChulls.Count)
|
|||
|
{
|
|||
|
CHull cr2 = mChulls[i];
|
|||
|
if (cr2 != match)
|
|||
|
{
|
|||
|
output.Add(cr2);
|
|||
|
}
|
|||
|
i++;
|
|||
|
}
|
|||
|
|
|||
|
cr.Dispose();
|
|||
|
match.Dispose();
|
|||
|
combine = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (combine)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
output.Add(cr);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (combine)
|
|||
|
{
|
|||
|
mChulls.Clear();
|
|||
|
mChulls = output;
|
|||
|
output.Clear();
|
|||
|
}
|
|||
|
|
|||
|
return combine;
|
|||
|
}
|
|||
|
|
|||
|
public int process(DecompDesc desc)
|
|||
|
{
|
|||
|
int ret = 0;
|
|||
|
|
|||
|
MAXDEPTH = (int)desc.mDepth;
|
|||
|
CONCAVE_PERCENT = desc.mCpercent;
|
|||
|
MERGE_PERCENT = desc.mPpercent;
|
|||
|
|
|||
|
ConvexDecomposition.calcConvexDecomposition(desc.mVertices, desc.mIndices, ConvexDecompResult, 0f, 0, MAXDEPTH, CONCAVE_PERCENT, MERGE_PERCENT);
|
|||
|
|
|||
|
while (combineHulls()) // keep combinging hulls until I can't combine any more...
|
|||
|
;
|
|||
|
|
|||
|
int i;
|
|||
|
for (i = 0; i < mChulls.Count; i++)
|
|||
|
{
|
|||
|
CHull cr = mChulls[i];
|
|||
|
|
|||
|
// before we hand it back to the application, we need to regenerate the hull based on the
|
|||
|
// limits given by the user.
|
|||
|
|
|||
|
ConvexResult c = cr.mResult; // the high resolution hull...
|
|||
|
|
|||
|
HullResult result = new HullResult();
|
|||
|
HullDesc hdesc = new HullDesc();
|
|||
|
|
|||
|
hdesc.SetHullFlag(HullFlag.QF_TRIANGLES);
|
|||
|
|
|||
|
hdesc.Vertices = c.HullVertices;
|
|||
|
hdesc.MaxVertices = desc.mMaxVertices; // maximum number of vertices allowed in the output
|
|||
|
|
|||
|
if (desc.mSkinWidth != 0f)
|
|||
|
{
|
|||
|
hdesc.SkinWidth = desc.mSkinWidth;
|
|||
|
hdesc.SetHullFlag(HullFlag.QF_SKIN_WIDTH); // do skin width computation.
|
|||
|
}
|
|||
|
|
|||
|
HullError ret2 = HullUtils.CreateConvexHull(hdesc, ref result);
|
|||
|
|
|||
|
if (ret2 == HullError.QE_OK)
|
|||
|
{
|
|||
|
ConvexResult r = new ConvexResult(result.OutputVertices, result.Indices);
|
|||
|
|
|||
|
r.mHullVolume = Concavity.computeMeshVolume(result.OutputVertices, result.Indices); // the volume of the hull.
|
|||
|
|
|||
|
// compute the best fit OBB
|
|||
|
//computeBestFitOBB(result.mNumOutputVertices, result.mOutputVertices, sizeof(float) * 3, r.mOBBSides, r.mOBBTransform);
|
|||
|
|
|||
|
//r.mOBBVolume = r.mOBBSides[0] * r.mOBBSides[1] * r.mOBBSides[2]; // compute the OBB volume.
|
|||
|
|
|||
|
//fm_getTranslation(r.mOBBTransform, r.mOBBCenter); // get the translation component of the 4x4 matrix.
|
|||
|
|
|||
|
//fm_matrixToQuat(r.mOBBTransform, r.mOBBOrientation); // extract the orientation as a quaternion.
|
|||
|
|
|||
|
//r.mSphereRadius = computeBoundingSphere(result.mNumOutputVertices, result.mOutputVertices, r.mSphereCenter);
|
|||
|
//r.mSphereVolume = fm_sphereVolume(r.mSphereRadius);
|
|||
|
|
|||
|
mCallback(r);
|
|||
|
}
|
|||
|
|
|||
|
result = null;
|
|||
|
cr.Dispose();
|
|||
|
}
|
|||
|
|
|||
|
ret = mChulls.Count;
|
|||
|
|
|||
|
mChulls.Clear();
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
public void ConvexDecompResult(ConvexResult result)
|
|||
|
{
|
|||
|
CHull ch = new CHull(result);
|
|||
|
mChulls.Add(ch);
|
|||
|
}
|
|||
|
|
|||
|
public void sortChulls(List<CHull> hulls)
|
|||
|
{
|
|||
|
hulls.Sort(delegate(CHull a, CHull b) { return a.mVolume.CompareTo(b.mVolume); });
|
|||
|
}
|
|||
|
}
|
|||
|
}
|