OpenSimMirror/addon-modules/ConvexDecompositionDotNet/ConvexBuilder.cs

412 lines
13 KiB
C#
Raw Normal View History

2011-05-19 20:36:01 +00:00
/* 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); });
}
}
}