Beispiel #1
0
        internal void CacheHulls(ulong meshHash, HacdConvexHull[] retHulls)
        {
            //write each state to disk
            SQLiteTransaction transaction = _connection.BeginTransaction();

            try
            {
                this.ClearExistingHulls(meshHash);

                foreach (HacdConvexHull hull in retHulls)
                {
                    this.WriteHullRow(meshHash, hull);
                    hull._rawVerts = null;
                }

                transaction.Commit();

                _memCache.Add(meshHash, HacdConvexHull.CloneHullArray(retHulls));
            }
            catch
            {
                transaction.Rollback();
                throw;
            }
        }
Beispiel #2
0
        private void WriteHullRow(ulong meshHash, HacdConvexHull hull)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                ProtoBuf.Serializer.Serialize(ms, hull);

                const string INSERT_CMD = "INSERT INTO Hulls(hash, hull_data) VALUES(@hhash, @hullData)";
                using (SQLiteCommand cmd = new SQLiteCommand(INSERT_CMD, _connection))
                {
                    SQLiteParameter hashParam = cmd.CreateParameter();
                    hashParam.ParameterName = "@hhash";
                    hashParam.DbType        = System.Data.DbType.Int64;
                    hashParam.Value         = (Int64)meshHash;

                    SQLiteParameter hullDataParam = cmd.CreateParameter();
                    hullDataParam.ParameterName = "@hullData";
                    hullDataParam.Value         = ms.ToArray();

                    cmd.Parameters.Add(hashParam);
                    cmd.Parameters.Add(hullDataParam);

                    cmd.ExecuteNonQuery();
                }
            }
        }
Beispiel #3
0
        public bool TryGetHulls(ulong meshHash, out HacdConvexHull[] cachedHulls)
        {
            HacdConvexHull[] memHulls;
            if (_memCache.TryGetValue(meshHash, out memHulls))
            {
                //we must clone here because the hulls will be scaled when they're used which would
                //change the values in this, inmemory, unscaled array
                cachedHulls = HacdConvexHull.CloneHullArray(memHulls);
                return(true);
            }

            const string FIND_QRY = "SELECT hull_data FROM Hulls WHERE hash = @hhash";

            using (SQLiteCommand cmd = new SQLiteCommand(FIND_QRY, _connection))
            {
                SQLiteParameter hashParam = cmd.CreateParameter();
                hashParam.ParameterName = "@hhash";
                hashParam.DbType        = System.Data.DbType.Int64;
                hashParam.Value         = (Int64)meshHash;

                cmd.Parameters.Add(hashParam);

                using (SQLiteDataReader reader = cmd.ExecuteReader())
                {
                    if (!reader.HasRows)
                    {
                        cachedHulls = null;
                        return(false);
                    }

                    List <HacdConvexHull> retHulls = new List <HacdConvexHull>();
                    while (reader.Read())
                    {
                        byte[] buffer = (byte[])reader[0];

                        using (MemoryStream hullStream = new MemoryStream(buffer))
                        {
                            HacdConvexHull hull = ProtoBuf.Serializer.Deserialize <HacdConvexHull>(hullStream);
                            if (hull == null)
                            {
                                throw new Exception("Protobuf deserialization of convex hull failed");
                            }

                            hull.FillVerticesFromRaw();
                            retHulls.Add(hull);
                        }
                    }

                    cachedHulls = retHulls.ToArray();
                    _memCache.Add(meshHash, HacdConvexHull.CloneHullArray(cachedHulls));
                    return(true);
                }
            }
        }
Beispiel #4
0
        public static void Scale(OpenMetaverse.Vector3 scale, HacdConvexHull[] hulls)
        {
            foreach (HacdConvexHull hull in hulls)
            {
                PhysX.Math.Vector3[] vertices = hull.Vertices;
                for (int i = 0; i < vertices.Length; ++i)
                {
                    PhysX.Math.Vector3 vert = vertices[i];
                    vert.X *= scale.X;
                    vert.Y *= scale.Y;
                    vert.Z *= scale.Z;

                    vertices[i] = vert;
                }
            }
        }
Beispiel #5
0
        public static HacdConvexHull[] CloneHullArray(HacdConvexHull[] hulls)
        {
            HacdConvexHull[] retHulls = new HacdConvexHull[hulls.Length];
            for (int i = 0; i < hulls.Length; ++i)
            {
                HacdConvexHull nextHull = hulls[i];

                HacdConvexHull newHull = new HacdConvexHull();
                newHull.Indicies = (int[])nextHull.Indicies.Clone();

                newHull.Vertices = new PhysX.Math.Vector3[nextHull.Vertices.Length];
                for (int j = 0; j < nextHull.Vertices.Length; j++)
                {
                    PhysX.Math.Vector3 vec = nextHull.Vertices[j];
                    newHull.Vertices[j] = new PhysX.Math.Vector3(vec.X, vec.Y, vec.Z);
                }

                retHulls[i] = newHull;
            }

            return(retHulls);
        }
Beispiel #6
0
        public static HacdConvexHull[] CloneHullArray(HacdConvexHull[] hulls)
        {
            HacdConvexHull[] retHulls = new HacdConvexHull[hulls.Length];
            for (int i = 0; i < hulls.Length; ++i)
            {
                HacdConvexHull nextHull = hulls[i];

                HacdConvexHull newHull = new HacdConvexHull();
                newHull.Indicies = (int[])nextHull.Indicies.Clone();

                newHull.Vertices = new PhysX.Math.Vector3[nextHull.Vertices.Length];
                for (int j = 0; j < nextHull.Vertices.Length; j++)
                {
                    PhysX.Math.Vector3 vec = nextHull.Vertices[j];
                    newHull.Vertices[j] = new PhysX.Math.Vector3(vec.X, vec.Y, vec.Z);
                }

                retHulls[i] = newHull;
            }

            return retHulls;
        }
Beispiel #7
0
        public static HacdConvexHull[] DecomposeToConvexHulls(ulong meshHash, bool useCache, HacdPreset preset, float[] verts, int[] indicies)
        {
            if (verts.Length % 3 != 0)
            {
                throw new InvalidOperationException("Number of verticies must be divisble by 3");
            }

            if (indicies.Length % 3 != 0)
            {
                throw new InvalidOperationException("Number of indicies must be divisble by 3");
            }


            if (IsCacheCandidate(useCache, verts.Length))
            {
                //try cache
                try
                {
                    HacdConvexHull[] cachedHulls;
                    if (MeshingStage.HullCache.TryGetHulls(meshHash, out cachedHulls))
                    {
                        return(cachedHulls);
                    }
                }
                catch (Exception e)
                {
                    m_log.ErrorFormat("[InWorldz.PhysX.HACD] Failure retrieving HACD hulls from cache: {0}: {1}", e, e.Message);
                }
            }

            IntPtr session = Decompose(verts, indicies, verts.Length, indicies.Length, preset.DEFAULT_CC_CONNECT_DIST,
                                       preset.MIN_HULL_COUNT, preset.CONCAVITY, preset.TARGET_TRIANGLES_IN_FULL_MESH, preset.MAX_VERTS_PER_HULL,
                                       true, true, preset.VOLUME_WEIGHT, preset.SMALL_CLUSTER_THRESHOLD);

            if (session == IntPtr.Zero)
            {
                return(null);
            }

            HacdConvexHull[] retHulls;

            try
            {
                int hullCount = GetConvexHullCount(session);

                retHulls = new HacdConvexHull[hullCount];

                for (int hullNum = 0; hullNum < hullCount; ++hullNum)
                {
                    int vertexCount = GetVertexCount(session, hullNum);
                    int indexCount  = GetIndexCount(session, hullNum);

                    float[] hullVerts   = new float[vertexCount];
                    int[]   hullIndexes = new int[indexCount];

                    if (!GetConvexVertsAndIndexes(session, hullNum, hullVerts, hullIndexes))
                    {
                        return(null);
                    }

                    HacdConvexHull hull = new HacdConvexHull
                    {
                        Vertices  = PhysUtil.FloatArrayToVectorArray(hullVerts),
                        _rawVerts = IsCacheCandidate(useCache, verts.Length) ? hullVerts : null,
                        Indicies  = hullIndexes
                    };

                    retHulls[hullNum] = hull;
                }

                //store in cache for later
                if (IsCacheCandidate(useCache, verts.Length))
                {
                    try
                    {
                        MeshingStage.HullCache.CacheHulls(meshHash, retHulls);
                    }
                    catch (Exception e)
                    {
                        m_log.ErrorFormat("[InWorldz.PhysX.HACD] Failure storing HACD results in cache: {0}: {1}", e, e.Message);
                    }
                }

                return(retHulls);
            }
            finally
            {
                FreeSession(session);
            }
        }
        public HacdConvexHull[] DecomposeToConvexHulls(ulong meshHash, bool useCache, List <float3> convVertices, List <int> convIndices)
        {
            if (convIndices.Count % 3 != 0)
            {
                throw new InvalidOperationException("Number of indicies must be divisble by 3");
            }

            if (IsCacheCandidate(useCache, convVertices.Count))
            {
                //try cache
                try
                {
                    HacdConvexHull[] cachedHulls;
                    if (MeshingStage.HullCache.TryGetHulls(meshHash, out cachedHulls))
                    {
                        return(cachedHulls);
                    }
                }
                catch (Exception e)
                {
                    m_log.ErrorFormat("[InWorldz.PhysX.RatcliffACD] Failure retrieving HACD hulls from cache: {0}: {1}", e, e.Message);
                }
            }

            ConvexBuilder builder = new ConvexBuilder(HullReturn);

            m_hulls = new List <ConvexResult>();

            DecompDesc dcomp = new DecompDesc();

            dcomp.mIndices  = convIndices;
            dcomp.mVertices = convVertices;

            builder.process(dcomp);

            var retHulls = new HacdConvexHull[m_hulls.Count];

            for (int i = 0; i < m_hulls.Count; i++)
            {
                ConvexResult hull = m_hulls[i];

                float[] rawVerts = null;
                if (IsCacheCandidate(useCache, convVertices.Count))
                {
                    rawVerts = new float[hull.HullVertices.Count * 3];
                }

                PhysX.Math.Vector3[] hullVerts = new PhysX.Math.Vector3[hull.HullVertices.Count];
                for (int j = 0; j < hull.HullVertices.Count; j++)
                {
                    hullVerts[j] = new PhysX.Math.Vector3(hull.HullVertices[j].x, hull.HullVertices[j].y, hull.HullVertices[j].z);

                    if (rawVerts != null)
                    {
                        rawVerts[j * 3 + 0] = hull.HullVertices[j].x;
                        rawVerts[j * 3 + 1] = hull.HullVertices[j].y;
                        rawVerts[j * 3 + 2] = hull.HullVertices[j].z;
                    }
                }

                retHulls[i] = new HacdConvexHull {
                    Indicies  = hull.HullIndices.ToArray(),
                    Vertices  = hullVerts,
                    _rawVerts = rawVerts,
                };
            }

            //store in cache for later
            if (IsCacheCandidate(useCache, convVertices.Count))
            {
                try
                {
                    MeshingStage.HullCache.CacheHulls(meshHash, retHulls);
                }
                catch (Exception e)
                {
                    m_log.ErrorFormat("[InWorldz.PhysX.RatcliffACD] Failure storing HACD results in cache: {0}: {1}", e, e.Message);
                }
            }

            return(retHulls);
        }
        private List <PhysX.ConvexMeshGeometry> GenerateComplexPhysXShape(ulong meshHash, string primName, PrimitiveBaseShape shape, OpenMetaverse.Vector3 size,
                                                                          float LOD, bool isDynamic)
        {
            //create the mesh and do not prescale it. the ACD algorithm can then cache the output hulls and
            //scale as appropriate
            MeshingResult result = _mesher.CreateMesh(primName, shape, size, LOD, ShapeType.DecomposedConvexHulls, false);

            if (result == null)
            {
                return(null);
            }

            HacdConvexHull[] hulls = null;

            if (result.ResultType == ShapeType.TriMesh)
            {
                IMesh mesh = result.TriMesh;
                if (mesh == null)
                {
                    return(null);
                }

                //Debugging.VrmlGenerator.SaveToVrmlFile("lastMesh.wrl", mesh.getVertexListAsArray(), mesh.getTriangleList());

                switch (ShapeDeterminer.FindBestAcdAlgorithm(shape))
                {
                case ShapeDeterminer.AcdAlgorithm.HACD:
                    hulls = DecomposeWithHACD(shape, LOD, mesh);
                    break;

                case ShapeDeterminer.AcdAlgorithm.RATCLIFF:
                    hulls = DecomposeWithRatcliff(shape, LOD, mesh);
                    break;

                default:
                    throw new PhysxSdkException("GenerateComplexPhysXShape(): Specified ACD algorithm does not exist");
                }

                if (hulls == null)
                {
                    return(null);
                }
            }
            else if (result.ResultType == ShapeType.DecomposedConvexHulls)
            {
                hulls = new HacdConvexHull[result.ConvexVerts.Count];

                for (int i = 0; i < result.ConvexVerts.Count; i++)
                {
                    hulls[i] = new HacdConvexHull {
                        Vertices = new PhysX.Math.Vector3[result.ConvexVerts[i].Count]
                    };
                    for (int j = 0; j < result.ConvexVerts[i].Count; j++)
                    {
                        var vert = result.ConvexVerts[i][j];
                        hulls[i].Vertices[j] = new PhysX.Math.Vector3(vert.X, vert.Y, vert.Z);
                    }
                }
            }
            else
            {
                return(null);
            }

            HacdConvexHull.Scale(size, hulls);

            List <PhysX.ConvexMeshGeometry> ret = new List <PhysX.ConvexMeshGeometry>();

            try
            {
                foreach (HacdConvexHull hull in hulls)
                {
                    PhysX.ConvexMeshDesc convexMeshDesc = new PhysX.ConvexMeshDesc()
                    {
                        Flags = PhysX.ConvexFlag.InflateConvex
                    };

                    if (hull.Indicies == null)
                    {
                        convexMeshDesc.Flags |= PhysX.ConvexFlag.ComputeConvex;
                    }

                    convexMeshDesc.SetPositions(hull.Vertices);
                    if (hull.Indicies != null)
                    {
                        convexMeshDesc.SetTriangles(hull.Indicies);
                    }

                    if (!convexMeshDesc.IsValid())
                    {
                        throw new PhysxSdkException("GenerateComplexPhysXShape: Convex mesh description is invalid");
                    }

                    using (MemoryStream ms = new MemoryStream())
                    {
                        if (!_cooking.CookConvexMesh(convexMeshDesc, ms))
                        {
                            throw new PhysxSdkException("GenerateComplexPhysXShape: CookConvexMesh() failed");
                        }

                        ms.Position = 0;

                        PhysX.ConvexMesh         convexMesh      = _scene.Physics.CreateConvexMesh(ms);
                        PhysX.ConvexMeshGeometry convexShapeGeom = new PhysX.ConvexMeshGeometry(convexMesh);
                        ret.Add(convexShapeGeom);
                    }
                }
            }
            catch (Exception e)
            {
                m_log.WarnFormat("[InWorldz.PhysxPhysics] Unable to create convex hullset for shape: {0}", e);

                result = _mesher.CreateMesh(primName, shape, size, LOD, ShapeType.SingleConvex, true);
                if (result == null)
                {
                    m_log.WarnFormat("[InWorldz.PhysxPhysics] Fallback hull generation failed, giving up", e);
                    return(null);
                }

                //direct convex available?
                if (result.ResultType == ShapeType.SingleConvex)
                {
                    if (!TryGenerateFallbackHullFromHullData(ret, e, result.SingleConvex))
                    {
                        return(null);
                    }
                }
                else if (result.ResultType == ShapeType.TriMesh)
                {
                    IMesh mesh = result.TriMesh;
                    if (mesh == null)
                    {
                        m_log.WarnFormat("[InWorldz.PhysxPhysics] Fallback hull generation failed, giving up", e);
                        return(null);
                    }

                    if (!TryGenerateFallbackHullFromTrimesh(ret, e, mesh))
                    {
                        return(null);
                    }
                }
            }

            return(ret);
        }
Beispiel #10
0
        private List<PhysX.ConvexMeshGeometry> GenerateComplexPhysXShape(ulong meshHash, string primName, PrimitiveBaseShape shape, OpenMetaverse.Vector3 size, 
            float LOD, bool isDynamic)
        {
            //create the mesh and do not prescale it. the ACD algorithm can then cache the output hulls and 
            //scale as appropriate
            MeshingResult result = _mesher.CreateMesh(primName, shape, size, LOD, ShapeType.DecomposedConvexHulls, false);
            if (result == null) return null;

            HacdConvexHull[] hulls = null;

            if (result.ResultType == ShapeType.TriMesh)
            {
                IMesh mesh = result.TriMesh;
                if (mesh == null) return null;

                //Debugging.VrmlGenerator.SaveToVrmlFile("lastMesh.wrl", mesh.getVertexListAsArray(), mesh.getTriangleList());

                switch (ShapeDeterminer.FindBestAcdAlgorithm(shape))
                {
                    case ShapeDeterminer.AcdAlgorithm.HACD:
                        hulls = DecomposeWithHACD(shape, LOD, mesh);
                        break;

                    case ShapeDeterminer.AcdAlgorithm.RATCLIFF:
                        hulls = DecomposeWithRatcliff(shape, LOD, mesh);
                        break;

                    default:
                        throw new PhysxSdkException("GenerateComplexPhysXShape(): Specified ACD algorithm does not exist");
                }

                if (hulls == null)
                {
                    return null;
                }
            }
            else if (result.ResultType == ShapeType.DecomposedConvexHulls)
            {
                hulls = new HacdConvexHull[result.ConvexVerts.Count];

                for (int i = 0; i < result.ConvexVerts.Count; i++)
                {
                    hulls[i] = new HacdConvexHull { Vertices = new PhysX.Math.Vector3[result.ConvexVerts[i].Count] };
                    for (int j = 0; j < result.ConvexVerts[i].Count; j++)
                    {
                        var vert = result.ConvexVerts[i][j];
                        hulls[i].Vertices[j] = new PhysX.Math.Vector3(vert.X, vert.Y, vert.Z);
                    }
                }
            }
            else
            {
                return null;
            }

            HacdConvexHull.Scale(size, hulls);

            List<PhysX.ConvexMeshGeometry> ret = new List<PhysX.ConvexMeshGeometry>();

            try
            {
                foreach (HacdConvexHull hull in hulls)
                {
                    PhysX.ConvexMeshDesc convexMeshDesc = new PhysX.ConvexMeshDesc()
                    {
                        Flags = PhysX.ConvexFlag.InflateConvex
                    };

                    if (hull.Indicies == null) convexMeshDesc.Flags |= PhysX.ConvexFlag.ComputeConvex;

                    convexMeshDesc.SetPositions(hull.Vertices);
                    if (hull.Indicies != null) convexMeshDesc.SetTriangles(hull.Indicies);

                    if (!convexMeshDesc.IsValid())
                    {
                        throw new PhysxSdkException("GenerateComplexPhysXShape: Convex mesh description is invalid");
                    }

                    using (MemoryStream ms = new MemoryStream())
                    {
                        if (!_cooking.CookConvexMesh(convexMeshDesc, ms))
                        {
                            throw new PhysxSdkException("GenerateComplexPhysXShape: CookConvexMesh() failed");
                        }

                        ms.Position = 0;

                        PhysX.ConvexMesh convexMesh = _scene.Physics.CreateConvexMesh(ms);
                        PhysX.ConvexMeshGeometry convexShapeGeom = new PhysX.ConvexMeshGeometry(convexMesh);
                        ret.Add(convexShapeGeom);
                    }
                }
            }
            catch (Exception e)
            {
                m_log.WarnFormat("[InWorldz.PhysxPhysics] Unable to create convex hullset for shape: {0}", e);

                result = _mesher.CreateMesh(primName, shape, size, LOD, ShapeType.SingleConvex, true);
                if (result == null)
                {
                    m_log.WarnFormat("[InWorldz.PhysxPhysics] Fallback hull generation failed, giving up", e);
                    return null;
                }

                //direct convex available?
                if (result.ResultType == ShapeType.SingleConvex)
                {
                    if (!TryGenerateFallbackHullFromHullData(ret, e, result.SingleConvex))
                    {
                        return null;
                    }
                }
                else if (result.ResultType == ShapeType.TriMesh)
                {
                    IMesh mesh = result.TriMesh;
                    if (mesh == null)
                    {
                        m_log.WarnFormat("[InWorldz.PhysxPhysics] Fallback hull generation failed, giving up", e);
                        return null;
                    }

                    if (!TryGenerateFallbackHullFromTrimesh(ret, e, mesh))
                    {
                        return null;
                    }
                }
            }

            return ret;
        }
Beispiel #11
0
        public HacdConvexHull[] DecomposeToConvexHulls(ulong meshHash, bool useCache, List<float3> convVertices, List<int> convIndices)
        {
            if (convIndices.Count % 3 != 0)
                throw new InvalidOperationException("Number of indicies must be divisble by 3");

            if (IsCacheCandidate(useCache, convVertices.Count))
            {
                //try cache
                try
                {
                    HacdConvexHull[] cachedHulls;
                    if (MeshingStage.HullCache.TryGetHulls(meshHash, out cachedHulls))
                    {
                        return cachedHulls;
                    }
                }
                catch (Exception e)
                {
                    m_log.ErrorFormat("[InWorldz.PhysX.RatcliffACD] Failure retrieving HACD hulls from cache: {0}: {1}", e, e.Message);
                }
            }

            ConvexBuilder builder = new ConvexBuilder(HullReturn);
            m_hulls = new List<ConvexResult>();

            DecompDesc dcomp = new DecompDesc();
            dcomp.mIndices = convIndices;
            dcomp.mVertices = convVertices;

            builder.process(dcomp);

            var retHulls = new HacdConvexHull[m_hulls.Count];

            for (int i = 0; i < m_hulls.Count; i++)
            {
                ConvexResult hull = m_hulls[i];

                float[] rawVerts = null;
                if (IsCacheCandidate(useCache, convVertices.Count))
                {
                    rawVerts = new float[hull.HullVertices.Count * 3];
                }

                PhysX.Math.Vector3[] hullVerts = new PhysX.Math.Vector3[hull.HullVertices.Count];
                for (int j = 0; j < hull.HullVertices.Count; j++)
                {
                    hullVerts[j] = new PhysX.Math.Vector3(hull.HullVertices[j].x, hull.HullVertices[j].y, hull.HullVertices[j].z);

                    if (rawVerts != null)
                    {
                        rawVerts[j * 3 + 0] = hull.HullVertices[j].x;
                        rawVerts[j * 3 + 1] = hull.HullVertices[j].y;
                        rawVerts[j * 3 + 2] = hull.HullVertices[j].z;
                    }
                }

                retHulls[i] = new HacdConvexHull { 
                    Indicies = hull.HullIndices.ToArray(), 
                    Vertices = hullVerts,
                    _rawVerts = rawVerts,
                };
            }

            //store in cache for later
            if (IsCacheCandidate(useCache, convVertices.Count))
            {
                try
                {
                    MeshingStage.HullCache.CacheHulls(meshHash, retHulls);
                }
                catch (Exception e)
                {
                    m_log.ErrorFormat("[InWorldz.PhysX.RatcliffACD] Failure storing HACD results in cache: {0}: {1}", e, e.Message);
                }
            }

            return retHulls;
        }
Beispiel #12
0
        public static HacdConvexHull[] DecomposeToConvexHulls(ulong meshHash, bool useCache, HacdPreset preset, float[] verts, int[] indicies)
        {
            if (verts.Length % 3 != 0)
                throw new InvalidOperationException("Number of verticies must be divisble by 3");

            if (indicies.Length % 3 != 0)
                throw new InvalidOperationException("Number of indicies must be divisble by 3");


            if (IsCacheCandidate(useCache, verts.Length))
            {
                //try cache
                try
                {
                    HacdConvexHull[] cachedHulls;
                    if (MeshingStage.HullCache.TryGetHulls(meshHash, out cachedHulls))
                    {
                        return cachedHulls;
                    }
                }
                catch (Exception e)
                {
                    m_log.ErrorFormat("[InWorldz.PhysX.HACD] Failure retrieving HACD hulls from cache: {0}: {1}", e, e.Message);
                }
            }

            IntPtr session = Decompose(verts, indicies, verts.Length, indicies.Length, preset.DEFAULT_CC_CONNECT_DIST,
                preset.MIN_HULL_COUNT, preset.CONCAVITY, preset.TARGET_TRIANGLES_IN_FULL_MESH, preset.MAX_VERTS_PER_HULL,
                true, true, preset.VOLUME_WEIGHT, preset.SMALL_CLUSTER_THRESHOLD);

            if (session == IntPtr.Zero)
            {
                return null;
            }

            HacdConvexHull[] retHulls;

            try
            {
                int hullCount = GetConvexHullCount(session);

                retHulls = new HacdConvexHull[hullCount];

                for (int hullNum = 0; hullNum < hullCount; ++hullNum)
                {
                    int vertexCount = GetVertexCount(session, hullNum);
                    int indexCount = GetIndexCount(session, hullNum);

                    float[] hullVerts = new float[vertexCount];
                    int[] hullIndexes = new int[indexCount];

                    if (!GetConvexVertsAndIndexes(session, hullNum, hullVerts, hullIndexes))
                    {
                        return null;
                    }

                    HacdConvexHull hull = new HacdConvexHull
                    {
                        Vertices = PhysUtil.FloatArrayToVectorArray(hullVerts),
                        _rawVerts = IsCacheCandidate(useCache, verts.Length) ? hullVerts : null,
                        Indicies = hullIndexes
                    };

                    retHulls[hullNum] = hull;
                }

                //store in cache for later
                if (IsCacheCandidate(useCache, verts.Length))
                {
                    try
                    {
                        MeshingStage.HullCache.CacheHulls(meshHash, retHulls);
                    }
                    catch (Exception e)
                    {
                        m_log.ErrorFormat("[InWorldz.PhysX.HACD] Failure storing HACD results in cache: {0}: {1}", e, e.Message);
                    }
                }

                return retHulls;
            }
            finally
            {
                FreeSession(session);
            }
        }