public override void ConvexDecompResult(ConvexResult result)
        {
            TriangleMesh trimesh = new TriangleMesh();
            demo.trimeshes.Add(trimesh);

            Vector3 localScaling = new Vector3(6.0f, 6.0f, 6.0f);

            if (output == null)
                return;

            output.WriteLine("## Hull Piece {0} with {1} vertices and {2} triangles.", mHullCount, result.mHullVertices.Length, result.mHullIndices.Length / 3);

            output.WriteLine("usemtl Material{0}", mBaseCount);
            output.WriteLine("o Object{0}", mBaseCount);

            foreach (Vector3 p in result.mHullVertices)
            {
                output.WriteLine(string.Format(floatFormat, "v {0:F9} {1:F9} {2:F9}", p.X, p.Y, p.Z));
            }

            //calc centroid, to shift vertices around center of mass
            demo.centroid = Vector3.Zero;

            AlignedVector3Array vertices = new AlignedVector3Array();
            if (true)
            {
                foreach (Vector3 vertex in result.mHullVertices)
                {
                    demo.centroid += vertex * localScaling;
                }
            }

            demo.centroid /= (float)result.mHullVertices.Length;

            if (true)
            {
                foreach (Vector3 vertex in result.mHullVertices)
                {
                    vertices.Add(vertex * localScaling - demo.centroid);
                }
            }

            if (true)
            {
                int[] src = result.mHullIndices;
                for (int i = 0; i < src.Length; i += 3)
                {
                    int index0 = src[i];
                    int index1 = src[i + 1];
                    int index2 = src[i + 2];

                    Vector3 vertex0 = result.mHullVertices[index0] * localScaling - demo.centroid;
                    Vector3 vertex1 = result.mHullVertices[index1] * localScaling - demo.centroid;
                    Vector3 vertex2 = result.mHullVertices[index2] * localScaling - demo.centroid;

                    trimesh.AddTriangle(vertex0, vertex1, vertex2);

                    index0 += mBaseCount;
                    index1 += mBaseCount;
                    index2 += mBaseCount;

                    output.WriteLine("f {0} {1} {2}", index0 + 1, index1 + 1, index2 + 1);
                }
            }

            //this is a tools issue: due to collision margin, convex objects overlap, compensate for it here:
            //#define SHRINK_OBJECT_INWARDS 1
            #if SHRINK_OBJECT_INWARDS

            float collisionMargin = 0.01f;

            btAlignedObjectArray<btVector3> planeEquations;
            btGeometryUtil::getPlaneEquationsFromVertices(vertices,planeEquations);

            btAlignedObjectArray<btVector3> shiftedPlaneEquations;
            for (int p=0;p<planeEquations.size();p++)
            {
                btVector3 plane = planeEquations[p];
                plane[3] += collisionMargin;
                shiftedPlaneEquations.push_back(plane);
            }
            btAlignedObjectArray<btVector3> shiftedVertices;
            btGeometryUtil::getVerticesFromPlaneEquations(shiftedPlaneEquations,shiftedVertices);

            btConvexHullShape* convexShape = new btConvexHullShape(&(shiftedVertices[0].getX()),shiftedVertices.size());

            #else //SHRINK_OBJECT_INWARDS

            ConvexHullShape convexShape = new ConvexHullShape(vertices);
            #endif

            if (demo.sEnableSAT)
            {
                convexShape.InitializePolyhedralFeatures();
            }
            convexShape.Margin = 0.01f;
            convexShapes.Add(convexShape);
            convexCentroids.Add(demo.centroid);
            demo.CollisionShapes.Add(convexShape);
            mBaseCount += result.mHullVertices.Length; // advance the 'base index' counter.
        }
        protected override void OnInitializePhysics()
        {
            ManifoldPoint.ContactAdded += MyContactCallback;

            SetupEmptyDynamicsWorld();

            WavefrontObj wo = new WavefrontObj();
            int tcount = wo.LoadObj("data/file.obj");
            if (tcount > 0)
            {
                TriangleMesh trimesh = new TriangleMesh();
                trimeshes.Add(trimesh);

                Vector3 localScaling = new Vector3(6, 6, 6);
                List<int> indices = wo.Indices;
                List<Vector3> vertices = wo.Vertices;

                int i;
                for (i = 0; i < tcount; i++)
                {
                    int index0 = indices[i * 3];
                    int index1 = indices[i * 3 + 1];
                    int index2 = indices[i * 3 + 2];

                    Vector3 vertex0 = vertices[index0] * localScaling;
                    Vector3 vertex1 = vertices[index1] * localScaling;
                    Vector3 vertex2 = vertices[index2] * localScaling;

                    trimesh.AddTriangle(vertex0, vertex1, vertex2);
                }

                ConvexShape tmpConvexShape = new ConvexTriangleMeshShape(trimesh);

                //create a hull approximation
                ShapeHull hull = new ShapeHull(tmpConvexShape);
                float margin = tmpConvexShape.Margin;
                hull.BuildHull(margin);
                tmpConvexShape.UserObject = hull;

                ConvexHullShape convexShape = new ConvexHullShape();
                foreach (Vector3 v in hull.Vertices)
                {
                    convexShape.AddPoint(v);
                }

                if (sEnableSAT)
                {
                    convexShape.InitializePolyhedralFeatures();
                }
                tmpConvexShape.Dispose();
                //hull.Dispose();

                CollisionShapes.Add(convexShape);

                float mass = 1.0f;

                LocalCreateRigidBody(mass, Matrix.Translation(0, 2, 14), convexShape);

                const bool useQuantization = true;
                CollisionShape concaveShape = new BvhTriangleMeshShape(trimesh, useQuantization);
                LocalCreateRigidBody(0, Matrix.Translation(convexDecompositionObjectOffset), concaveShape);

                CollisionShapes.Add(concaveShape);

                // Bullet Convex Decomposition

                FileStream outputFile = new FileStream("file_convex.obj", FileMode.Create, FileAccess.Write);
                StreamWriter writer = new StreamWriter(outputFile);

                DecompDesc desc = new DecompDesc
                {
                    mVertices = wo.Vertices.ToArray(),
                    mTcount = tcount,
                    mIndices = wo.Indices.ToArray(),
                    mDepth = 5,
                    mCpercent = 5,
                    mPpercent = 15,
                    mMaxVertices = 16,
                    mSkinWidth = 0.0f
                };

                MyConvexDecomposition convexDecomposition = new MyConvexDecomposition(writer, this);
                desc.mCallback = convexDecomposition;

                // HACD

                Hacd myHACD = new Hacd();
                myHACD.SetPoints(wo.Vertices);
                myHACD.SetTriangles(wo.Indices);
                myHACD.CompacityWeight = 0.1;
                myHACD.VolumeWeight = 0.0;

                // HACD parameters
                // Recommended parameters: 2 100 0 0 0 0
                int nClusters = 2;
                const double concavity = 100;
                //bool invert = false;
                const bool addExtraDistPoints = false;
                const bool addNeighboursDistPoints = false;
                const bool addFacesPoints = false;

                myHACD.NClusters = nClusters;                     // minimum number of clusters
                myHACD.VerticesPerConvexHull = 100;               // max of 100 vertices per convex-hull
                myHACD.Concavity = concavity;                     // maximum concavity
                myHACD.AddExtraDistPoints = addExtraDistPoints;
                myHACD.AddNeighboursDistPoints = addNeighboursDistPoints;
                myHACD.AddFacesPoints = addFacesPoints;

                myHACD.Compute();
                nClusters = myHACD.NClusters;

                myHACD.Save("output.wrl", false);

                if (true)
                {
                    CompoundShape compound = new CompoundShape();
                    CollisionShapes.Add(compound);

                    Matrix trans = Matrix.Identity;

                    for (int c = 0; c < nClusters; c++)
                    {
                        //generate convex result
                        Vector3[] points;
                        int[] triangles;
                        myHACD.GetCH(c, out points, out triangles);

                        ConvexResult r = new ConvexResult(points, triangles);
                        convexDecomposition.ConvexDecompResult(r);
                    }

                    for (i = 0; i < convexDecomposition.convexShapes.Count; i++)
                    {
                        Vector3 centroid = convexDecomposition.convexCentroids[i];
                        trans = Matrix.Translation(centroid);
                        ConvexHullShape convexShape2 = convexDecomposition.convexShapes[i] as ConvexHullShape;
                        compound.AddChildShape(trans, convexShape2);

                        RigidBody body = LocalCreateRigidBody(1.0f, trans, convexShape2);
                    }

            #if true
                    mass = 10.0f;
                    trans = Matrix.Translation(-convexDecompositionObjectOffset);
                    RigidBody body2 = LocalCreateRigidBody(mass, trans, compound);
                    body2.CollisionFlags |= CollisionFlags.CustomMaterialCallback;

                    convexDecompositionObjectOffset.Z = 6;
                    trans = Matrix.Translation(-convexDecompositionObjectOffset);
                    body2 = LocalCreateRigidBody(mass, trans, compound);
                    body2.CollisionFlags |= CollisionFlags.CustomMaterialCallback;

                    convexDecompositionObjectOffset.Z = -6;
                    trans = Matrix.Translation(-convexDecompositionObjectOffset);
                    body2 = LocalCreateRigidBody(mass, trans, compound);
                    body2.CollisionFlags |= CollisionFlags.CustomMaterialCallback;
            #endif
                }

                writer.Dispose();
                outputFile.Dispose();
            }
        }
        protected override void OnInitializePhysics()
        {
            ManifoldPoint.ContactAdded += MyContactCallback;

            SetupEmptyDynamicsWorld();

            CompoundCollisionAlgorithm.CompoundChildShapePairCallback = MyCompoundChildShapeCallback;
            convexDecompositionObjectOffset = new Vector3(10, 0, 0);

            // Load wavefront file
            var wo = new WavefrontObj();
            int tcount = wo.LoadObj("data/file.obj");
            if (tcount == 0)
            {
                return;
            }

            // Convert file data to TriangleMesh
            var trimesh = new TriangleMesh();
            trimeshes.Add(trimesh);

            Vector3 localScaling = new Vector3(6, 6, 6);
            List<int> indices = wo.Indices;
            List<Vector3> vertices = wo.Vertices;

            int i;
            for (i = 0; i < tcount; i++)
            {
                int index0 = indices[i * 3];
                int index1 = indices[i * 3 + 1];
                int index2 = indices[i * 3 + 2];

                Vector3 vertex0 = vertices[index0] * localScaling;
                Vector3 vertex1 = vertices[index1] * localScaling;
                Vector3 vertex2 = vertices[index2] * localScaling;

                trimesh.AddTriangleRef(ref vertex0, ref vertex1, ref vertex2);
            }

            // Create a hull approximation
            ConvexHullShape convexShape;
            using (var tmpConvexShape = new ConvexTriangleMeshShape(trimesh))
            {
                using (var hull = new ShapeHull(tmpConvexShape))
                {
                    hull.BuildHull(tmpConvexShape.Margin);
                    convexShape = new ConvexHullShape(hull.Vertices);
                }
            }
            if (sEnableSAT)
            {
                convexShape.InitializePolyhedralFeatures();
            }
            CollisionShapes.Add(convexShape);

            // Add non-moving body to world
            float mass = 1.0f;
            LocalCreateRigidBody(mass, Matrix.Translation(0, 2, 14), convexShape);

            const bool useQuantization = true;
            var concaveShape = new BvhTriangleMeshShape(trimesh, useQuantization);
            LocalCreateRigidBody(0, Matrix.Translation(convexDecompositionObjectOffset), concaveShape);

            CollisionShapes.Add(concaveShape);

            // HACD
            var hacd = new Hacd();
            hacd.SetPoints(wo.Vertices);
            hacd.SetTriangles(wo.Indices);
            hacd.CompacityWeight = 0.1;
            hacd.VolumeWeight = 0.0;

            // Recommended HACD parameters: 2 100 false false false
            hacd.NClusters = 2;                      // minimum number of clusters
            hacd.Concavity = 100;                    // maximum concavity
            hacd.AddExtraDistPoints = false;
            hacd.AddNeighboursDistPoints = false;
            hacd.AddFacesPoints = false;
            hacd.NumVerticesPerConvexHull = 100;     // max of 100 vertices per convex-hull

            hacd.Compute();
            hacd.Save("output.wrl", false);

            // Generate convex result
            var outputFile = new FileStream("file_convex.obj", FileMode.Create, FileAccess.Write);
            var writer = new StreamWriter(outputFile);

            var convexDecomposition = new ConvexDecomposition(writer, this);
            convexDecomposition.LocalScaling = localScaling;

            for (int c = 0; c < hacd.NClusters; c++)
            {
                Vector3[] points;
                int[] triangles;
                hacd.GetCH(c, out points, out triangles);

                convexDecomposition.ConvexDecompResult(points, triangles);
            }

            // Combine convex shapes into a compound shape
            var compound = new CompoundShape();
            for (i = 0; i < convexDecomposition.convexShapes.Count; i++)
            {
                Vector3 centroid = convexDecomposition.convexCentroids[i];
                var convexShape2 = convexDecomposition.convexShapes[i];
                Matrix trans = Matrix.Translation(centroid);
                if (sEnableSAT)
                {
                    convexShape2.InitializePolyhedralFeatures();
                }
                CollisionShapes.Add(convexShape2);
                compound.AddChildShape(trans, convexShape2);

                LocalCreateRigidBody(1.0f, trans, convexShape2);
            }
            CollisionShapes.Add(compound);

            writer.Dispose();
            outputFile.Dispose();

            #if true
            mass = 10.0f;
            var body2 = LocalCreateRigidBody(mass, Matrix.Translation(-convexDecompositionObjectOffset), compound);
            body2.CollisionFlags |= CollisionFlags.CustomMaterialCallback;

            convexDecompositionObjectOffset.Z = 6;
            body2 = LocalCreateRigidBody(mass, Matrix.Translation(-convexDecompositionObjectOffset), compound);
            body2.CollisionFlags |= CollisionFlags.CustomMaterialCallback;

            convexDecompositionObjectOffset.Z = -6;
            body2 = LocalCreateRigidBody(mass, Matrix.Translation(-convexDecompositionObjectOffset), compound);
            body2.CollisionFlags |= CollisionFlags.CustomMaterialCallback;
            #endif
        }
        protected override void OnInitializePhysics()
        {
            // collision configuration contains default setup for memory, collision setup
            DefaultCollisionConstructionInfo cci = new DefaultCollisionConstructionInfo();
            cci.DefaultMaxPersistentManifoldPoolSize = 32768;
            CollisionConf = new DefaultCollisionConfiguration(cci);

            Dispatcher = new CollisionDispatcher(CollisionConf);
            Dispatcher.DispatcherFlags = DispatcherFlags.DisableContactPoolDynamicAllocation;

            // the maximum size of the collision world. Make sure objects stay within these boundaries
            // Don't make the world AABB size too large, it will harm simulation quality and performance
            Vector3 worldAabbMin = new Vector3(-1000, -1000, -1000);
            Vector3 worldAabbMax = new Vector3(1000, 1000, 1000);

            HashedOverlappingPairCache pairCache = new HashedOverlappingPairCache();
            Broadphase = new AxisSweep3(worldAabbMin, worldAabbMax, 3500, pairCache);
            //Broadphase = new DbvtBroadphase();

            Solver = new SequentialImpulseConstraintSolver();

            World = new DiscreteDynamicsWorld(Dispatcher, Broadphase, Solver, CollisionConf);
            World.Gravity = new Vector3(0, -10, 0);
            World.SolverInfo.SolverMode |= SolverModes.EnableFrictionDirectionCaching;
            World.SolverInfo.NumIterations = 5;

            if (benchmark < 5)
            {
                // create the ground
                CollisionShape groundShape = new BoxShape(250, 50, 250);
                CollisionShapes.Add(groundShape);
                CollisionObject ground = base.LocalCreateRigidBody(0, Matrix.Translation(0, -50, 0), groundShape);
                ground.UserObject = "Ground";
            }

            float cubeSize = 1.0f;
            float spacing = cubeSize;
            float mass = 1.0f;
            int size = 8;
            Vector3 pos = new Vector3(0.0f, cubeSize * 2, 0.0f);
            float offset = -size * (cubeSize * 2.0f + spacing) * 0.5f;

            switch (benchmark)
            {
                case 1:
                    // 3000

                    BoxShape blockShape = new BoxShape(cubeSize - collisionRadius);
                    mass = 2.0f;

                    for (int k = 0; k < 47; k++)
                    {
                        for (int j = 0; j < size; j++)
                        {
                            pos[2] = offset + (float)j * (cubeSize * 2.0f + spacing);
                            for (int i = 0; i < size; i++)
                            {
                                pos[0] = offset + (float)i * (cubeSize * 2.0f + spacing);
                                RigidBody cmbody = LocalCreateRigidBody(mass, Matrix.Translation(pos), blockShape);
                            }
                        }
                        offset -= 0.05f * spacing * (size - 1);
                        // spacing *= 1.01f;
                        pos[1] += (cubeSize * 2.0f + spacing);
                    }
                    break;

                case 2:
                    CreatePyramid(new Vector3(-20, 0, 0), 12, new Vector3(cubeSize));
                    CreateWall(new Vector3(-2.0f, 0.0f, 0.0f), 12, new Vector3(cubeSize));
                    CreateWall(new Vector3(4.0f, 0.0f, 0.0f), 12, new Vector3(cubeSize));
                    CreateWall(new Vector3(10.0f, 0.0f, 0.0f), 12, new Vector3(cubeSize));
                    CreateTowerCircle(new Vector3(25.0f, 0.0f, 0.0f), 8, 24, new Vector3(cubeSize));
                    break;

                case 3:
                    // TODO: Ragdolls
                    break;

                case 4:
                    cubeSize = 1.5f;

                    ConvexHullShape convexHullShape = new ConvexHullShape();

                    float scaling = 1;

                    convexHullShape.LocalScaling = new Vector3(scaling);

                    for (int i = 0; i < Taru.Vtx.Length / 3; i++)
                    {
                        Vector3 vtx = new Vector3(Taru.Vtx[i * 3], Taru.Vtx[i * 3 + 1], Taru.Vtx[i * 3 + 2]);
                        convexHullShape.AddPoint(vtx * (1.0f / scaling));
                    }

                    //this will enable polyhedral contact clipping, better quality, slightly slower
                    convexHullShape.InitializePolyhedralFeatures();

                    for (int k = 0; k < 15; k++)
                    {
                        for (int j = 0; j < size; j++)
                        {
                            pos[2] = offset + (float)j * (cubeSize * 2.0f + spacing);
                            for (int i = 0; i < size; i++)
                            {
                                pos[0] = offset + (float)i * (cubeSize * 2.0f + spacing);
                                LocalCreateRigidBody(mass, Matrix.Translation(pos), convexHullShape);
                            }
                        }
                        offset -= 0.05f * spacing * (size - 1);
                        spacing *= 1.01f;
                        pos[1] += (cubeSize * 2.0f + spacing);
                    }
                    break;

                case 5:
                    Vector3 boxSize = new Vector3(1.5f);
                    float boxMass = 1.0f;
                    float sphereRadius = 1.5f;
                    float sphereMass = 1.0f;
                    float capsuleHalf = 2.0f;
                    float capsuleRadius = 1.0f;
                    float capsuleMass = 1.0f;

                    size = 10;
                    int height = 10;

                    cubeSize = boxSize[0];
                    spacing = 2.0f;
                    pos = new Vector3(0.0f, 20.0f, 0.0f);
                    offset = -size * (cubeSize * 2.0f + spacing) * 0.5f;

                    int numBodies = 0;

                    Random random = new Random();

                    for (int k = 0; k < height; k++)
                    {
                        for (int j = 0; j < size; j++)
                        {
                            pos[2] = offset + (float)j * (cubeSize * 2.0f + spacing);
                            for (int i = 0; i < size; i++)
                            {
                                pos[0] = offset + (float)i * (cubeSize * 2.0f + spacing);
                                Vector3 bpos = new Vector3(0, 25, 0) + new Vector3(5.0f * pos.X, pos.Y, 5.0f * pos.Z);
                                int idx = random.Next(10);
                                Matrix trans = Matrix.Translation(bpos);

                                switch (idx)
                                {
                                    case 0:
                                    case 1:
                                    case 2:
                                        {
                                            float r = 0.5f * (idx + 1);
                                            BoxShape boxShape = new BoxShape(boxSize * r);
                                            LocalCreateRigidBody(boxMass * r, trans, boxShape);
                                        }
                                        break;

                                    case 3:
                                    case 4:
                                    case 5:
                                        {
                                            float r = 0.5f * (idx - 3 + 1);
                                            SphereShape sphereShape = new SphereShape(sphereRadius * r);
                                            LocalCreateRigidBody(sphereMass * r, trans, sphereShape);
                                        }
                                        break;

                                    case 6:
                                    case 7:
                                    case 8:
                                        {
                                            float r = 0.5f * (idx - 6 + 1);
                                            CapsuleShape capsuleShape = new CapsuleShape(capsuleRadius * r, capsuleHalf * r);
                                            LocalCreateRigidBody(capsuleMass * r, trans, capsuleShape);
                                        }
                                        break;
                                }

                                numBodies++;
                            }
                        }
                        offset -= 0.05f * spacing * (size - 1);
                        spacing *= 1.1f;
                        pos[1] += (cubeSize * 2.0f + spacing);
                    }

                    //CreateLargeMeshBody();

                    break;

                case 6:
                    boxSize = new Vector3(1.5f, 1.5f, 1.5f);

                    convexHullShape = new ConvexHullShape();

                    for (int i = 0; i < Taru.Vtx.Length / 3; i++)
                    {
                        Vector3 vtx = new Vector3(Taru.Vtx[i * 3], Taru.Vtx[i * 3 + 1], Taru.Vtx[i * 3 + 2]);
                        convexHullShape.AddPoint(vtx);
                    }

                    size = 10;
                    height = 10;

                    cubeSize = boxSize[0];
                    spacing = 2.0f;
                    pos = new Vector3(0.0f, 20.0f, 0.0f);
                    offset = -size * (cubeSize * 2.0f + spacing) * 0.5f;

                    for (int k = 0; k < height; k++)
                    {
                        for (int j = 0; j < size; j++)
                        {
                            pos[2] = offset + (float)j * (cubeSize * 2.0f + spacing);
                            for (int i = 0; i < size; i++)
                            {
                                pos[0] = offset + (float)i * (cubeSize * 2.0f + spacing);
                                Vector3 bpos = new Vector3(0, 25, 0) + new Vector3(5.0f * pos.X, pos.Y, 5.0f * pos.Z);

                                LocalCreateRigidBody(mass, Matrix.Translation(bpos), convexHullShape);
                            }
                        }
                        offset -= 0.05f * spacing * (size - 1);
                        spacing *= 1.1f;
                        pos[1] += (cubeSize * 2.0f + spacing);
                    }

                    //CreateLargeMeshBody();

                    break;

                case 7:
                    // TODO
                    //CreateTest6();
                    //InitRays();
                    break;
            }
        }
        protected override void OnInitializePhysics()
        {
            ManifoldPoint.ContactAdded += MyContactCallback;

            SetupEmptyDynamicsWorld();

            //CompoundCollisionAlgorithm.CompoundChildShapePairCallback = MyCompoundChildShapeCallback;
            convexDecompositionObjectOffset = new Vector3(10, 0, 0);

            // Load wavefront file
            var wo = new WavefrontObj();
            //string filename = UnityEngine.Application.dataPath + "/BulletUnity/Examples/Scripts/BulletSharpDemos/ConvexDecompositionDemo/data/file.obj";
            UnityEngine.TextAsset bytes = (UnityEngine.TextAsset)UnityEngine.Resources.Load("file.obj");
            System.IO.Stream byteStream = new System.IO.MemoryStream(bytes.bytes);

            int tcount = wo.LoadObj(byteStream);
            if (tcount == 0)
            {
                return;
            }

            // Convert file data to TriangleMesh
            var trimesh = new TriangleMesh();
            trimeshes.Add(trimesh);

            Vector3 localScaling = new Vector3(6, 6, 6);
            List<int> indices = wo.Indices;
            List<Vector3> vertices = wo.Vertices;

            int i;
            for (i = 0; i < tcount; i++)
            {
                int index0 = indices[i * 3];
                int index1 = indices[i * 3 + 1];
                int index2 = indices[i * 3 + 2];

                Vector3 vertex0 = vertices[index0] * localScaling;
                Vector3 vertex1 = vertices[index1] * localScaling;
                Vector3 vertex2 = vertices[index2] * localScaling;

                trimesh.AddTriangleRef(ref vertex0, ref vertex1, ref vertex2);
            }

            // Create a hull approximation
            ConvexHullShape convexShape;
            using (var tmpConvexShape = new ConvexTriangleMeshShape(trimesh))
            {
                using (var hull = new ShapeHull(tmpConvexShape))
                {
                    hull.BuildHull(tmpConvexShape.Margin);
                    convexShape = new ConvexHullShape(hull.Vertices);
                }
            }
            if (sEnableSAT)
            {
                convexShape.InitializePolyhedralFeatures();
            }
            CollisionShapes.Add(convexShape);

            // Add non-moving body to world
            float mass = 1.0f;
            LocalCreateRigidBody(mass, Matrix.Translation(0, 2, 14), convexShape);

            const bool useQuantization = true;
            var concaveShape = new BvhTriangleMeshShape(trimesh, useQuantization);
            LocalCreateRigidBody(0, Matrix.Translation(convexDecompositionObjectOffset), concaveShape);

            CollisionShapes.Add(concaveShape);

            // HACD
            var hacd = new Hacd();
            hacd.SetPoints(wo.Vertices);
            hacd.SetTriangles(wo.Indices);
            hacd.CompacityWeight = 0.1;
            hacd.VolumeWeight = 0.0;

            // Recommended HACD parameters: 2 100 false false false
            hacd.NClusters = 2;                      // minimum number of clusters
            hacd.Concavity = 100;                    // maximum concavity
            hacd.AddExtraDistPoints = false;
            hacd.AddNeighboursDistPoints = false;
            hacd.AddFacesPoints = false;
            hacd.NVerticesPerCH = 100;     // max of 100 vertices per convex-hull

            hacd.Compute();
            hacd.Save("output.wrl", false);

            // Generate convex result
            var outputFile = new FileStream("file_convex.obj", FileMode.Create, FileAccess.Write);
            var writer = new StreamWriter(outputFile);

            var convexDecomposition = new ConvexDecomposition(writer, this);
            convexDecomposition.LocalScaling = localScaling;

            for (int c = 0; c < hacd.NClusters; c++)
            {
                int nVertices = hacd.GetNPointsCH(c);
                int trianglesLen = hacd.GetNTrianglesCH(c) * 3;
                double[] points = new double[nVertices * 3];
                long[] triangles = new long[trianglesLen];
                hacd.GetCH(c, points, triangles);

                if (trianglesLen == 0)
                {
                    continue;
                }

                Vector3[] verticesArray = new Vector3[nVertices];
                int vi3 = 0;
                for (int vi = 0; vi < nVertices; vi++)
                {
                    verticesArray[vi] = new Vector3(
                        (float)points[vi3], (float)points[vi3 + 1], (float)points[vi3 + 2]);
                    vi3 += 3;
                }

                int[] trianglesInt = new int[trianglesLen];
                for (int ti = 0; ti < trianglesLen; ti++)
                {
                    trianglesInt[ti] = (int)triangles[ti];
                }

                convexDecomposition.ConvexDecompResult(verticesArray, trianglesInt);
            }

            // Combine convex shapes into a compound shape
            var compound = new CompoundShape();
            for (i = 0; i < convexDecomposition.convexShapes.Count; i++)
            {
                Vector3 centroid = convexDecomposition.convexCentroids[i];
                var convexShape2 = convexDecomposition.convexShapes[i];
                Matrix trans = Matrix.Translation(centroid);
                if (sEnableSAT)
                {
                    convexShape2.InitializePolyhedralFeatures();
                }
                CollisionShapes.Add(convexShape2);
                compound.AddChildShape(trans, convexShape2);

                LocalCreateRigidBody(1.0f, trans, convexShape2);
            }
            CollisionShapes.Add(compound);

            writer.Dispose();
            outputFile.Dispose();

            #if true
            mass = 10.0f;
            var body2 = LocalCreateRigidBody(mass, Matrix.Translation(-convexDecompositionObjectOffset), compound);
            body2.CollisionFlags |= CollisionFlags.CustomMaterialCallback;

            convexDecompositionObjectOffset.Z = 6;
            body2 = LocalCreateRigidBody(mass, Matrix.Translation(-convexDecompositionObjectOffset), compound);
            body2.CollisionFlags |= CollisionFlags.CustomMaterialCallback;

            convexDecompositionObjectOffset.Z = -6;
            body2 = LocalCreateRigidBody(mass, Matrix.Translation(-convexDecompositionObjectOffset), compound);
            body2.CollisionFlags |= CollisionFlags.CustomMaterialCallback;
            #endif
        }