From 09b6b7e47a5623a22654ab81e013edbbede29d45 Mon Sep 17 00:00:00 2001
From: jpan <jpan@253336fb-580f-4252-a368-f3cef5a2a82b>
Date: Wed, 20 Jun 2012 18:06:58 +0000
Subject: [PATCH] The shape collision basic code is finished. Support both
 collision and distance. can use gjk in libccd or use independent gjk. tested
 on simple examples. remove geometric_shapes_intersect.h/cpp, replace by
 gjk_libccd.h/cpp and move them to narrowphase folder

Next step is testing the external interface for shape/mesh



git-svn-id: https://kforge.ros.org/fcl/fcl_ros@103 253336fb-580f-4252-a368-f3cef5a2a82b
---
 trunk/fcl/CMakeLists.txt                      |   2 +-
 trunk/fcl/include/fcl/collision_object.h      |   2 +-
 .../geometric_shapes_intersect.cpp            |   7 +-
 .../geometric_shapes_intersect.h              |  71 +-
 trunk/fcl/include/fcl/deprecated/gjk.h        | 412 ++++++++
 trunk/fcl/include/fcl/geometric_shapes.h      |  11 +
 .../include/fcl/geometric_shapes_utility.h    |   4 +
 trunk/fcl/include/fcl/{ => narrowphase}/gjk.h | 101 +-
 .../fcl/include/fcl/narrowphase/gjk_libccd.h  | 170 ++++
 .../fcl/include/fcl/narrowphase/narrowphase.h | 300 ++++++
 trunk/fcl/include/fcl/traversal_node_shapes.h |   2 +-
 trunk/fcl/src/collision_func_matrix.cpp       |   2 +-
 trunk/fcl/src/geometric_shapes_utility.cpp    |  29 +
 trunk/fcl/src/gjk.cpp                         |  63 +-
 trunk/fcl/src/gjk_libccd.cpp                  | 959 ++++++++++++++++++
 15 files changed, 1990 insertions(+), 145 deletions(-)
 rename trunk/fcl/{src => include/fcl/deprecated}/geometric_shapes_intersect.cpp (99%)
 rename trunk/fcl/include/fcl/{ => deprecated}/geometric_shapes_intersect.h (76%)
 create mode 100644 trunk/fcl/include/fcl/deprecated/gjk.h
 rename trunk/fcl/include/fcl/{ => narrowphase}/gjk.h (71%)
 create mode 100644 trunk/fcl/include/fcl/narrowphase/gjk_libccd.h
 create mode 100644 trunk/fcl/include/fcl/narrowphase/narrowphase.h
 create mode 100644 trunk/fcl/src/gjk_libccd.cpp

diff --git a/trunk/fcl/CMakeLists.txt b/trunk/fcl/CMakeLists.txt
index 3cdce3a6..780aca93 100644
--- a/trunk/fcl/CMakeLists.txt
+++ b/trunk/fcl/CMakeLists.txt
@@ -37,7 +37,7 @@ link_directories(${CCD_LIBRARY_DIRS})
 
 add_definitions(-DUSE_SVMLIGHT=0)
 
-add_library(${PROJECT_NAME} SHARED src/BV/AABB.cpp src/BV/OBB.cpp src/BV/RSS.cpp src/BV/kIOS.cpp src/traversal_node_base.cpp src/traversal_node_bvhs.cpp src/intersect.cpp src/motion.cpp src/BV_fitter.cpp src/BV_splitter.cpp src/BVH_model.cpp src/BVH_utility.cpp src/transform.cpp src/simple_setup.cpp src/geometric_shapes.cpp src/geometric_shapes_utility.cpp src/geometric_shapes_intersect.cpp src/collision_node.cpp src/traversal_recurse.cpp src/broad_phase_collision.cpp src/collision.cpp src/collision_func_matrix.cpp src/interval_tree.cpp src/conservative_advancement.cpp src/matrix_3f.cpp src/interval.cpp src/interval_vector.cpp src/interval_matrix.cpp src/taylor_model.cpp src/taylor_vector.cpp src/taylor_matrix.cpp src/distance_func_matrix.cpp src/distance.cpp src/gjk.cpp)
+add_library(${PROJECT_NAME} SHARED src/BV/AABB.cpp src/BV/OBB.cpp src/BV/RSS.cpp src/BV/kIOS.cpp src/traversal_node_base.cpp src/traversal_node_bvhs.cpp src/intersect.cpp src/motion.cpp src/BV_fitter.cpp src/BV_splitter.cpp src/BVH_model.cpp src/BVH_utility.cpp src/transform.cpp src/simple_setup.cpp src/geometric_shapes.cpp src/geometric_shapes_utility.cpp src/gjk_libccd.cpp src/collision_node.cpp src/traversal_recurse.cpp src/broad_phase_collision.cpp src/collision.cpp src/collision_func_matrix.cpp src/interval_tree.cpp src/conservative_advancement.cpp src/matrix_3f.cpp src/interval.cpp src/interval_vector.cpp src/interval_matrix.cpp src/taylor_model.cpp src/taylor_vector.cpp src/taylor_matrix.cpp src/distance_func_matrix.cpp src/distance.cpp src/gjk.cpp)
 
 target_link_libraries(${PROJECT_NAME} ${FLANN_LIBRARIES} ${CCD_LIBRARIES})
 
diff --git a/trunk/fcl/include/fcl/collision_object.h b/trunk/fcl/include/fcl/collision_object.h
index 5de9b44c..9a274085 100644
--- a/trunk/fcl/include/fcl/collision_object.h
+++ b/trunk/fcl/include/fcl/collision_object.h
@@ -48,7 +48,7 @@ namespace fcl
 enum OBJECT_TYPE {OT_UNKNOWN, OT_BVH, OT_GEOM};
 
 enum NODE_TYPE {BV_UNKNOWN, BV_AABB, BV_OBB, BV_RSS, BV_kIOS, BV_OBBRSS, BV_KDOP16, BV_KDOP18, BV_KDOP24,
-                GEOM_BOX, GEOM_SPHERE, GEOM_CAPSULE, GEOM_CONE, GEOM_CYLINDER, GEOM_CONVEX, GEOM_PLANE};
+                GEOM_BOX, GEOM_SPHERE, GEOM_CAPSULE, GEOM_CONE, GEOM_CYLINDER, GEOM_CONVEX, GEOM_PLANE, GEOM_TRIANGLE};
 
 class CollisionGeometry
 {
diff --git a/trunk/fcl/src/geometric_shapes_intersect.cpp b/trunk/fcl/include/fcl/deprecated/geometric_shapes_intersect.cpp
similarity index 99%
rename from trunk/fcl/src/geometric_shapes_intersect.cpp
rename to trunk/fcl/include/fcl/deprecated/geometric_shapes_intersect.cpp
index ecf63767..46dfe608 100644
--- a/trunk/fcl/src/geometric_shapes_intersect.cpp
+++ b/trunk/fcl/include/fcl/deprecated/geometric_shapes_intersect.cpp
@@ -42,6 +42,9 @@
 namespace fcl
 {
 
+namespace details
+{
+
 struct ccd_obj_t
 {
   ccd_vec3_t pos;
@@ -951,4 +954,6 @@ void triDeleteGJKObject(void* o_)
   delete o;
 }
 
-}
+} // details
+
+} // fcl
diff --git a/trunk/fcl/include/fcl/geometric_shapes_intersect.h b/trunk/fcl/include/fcl/deprecated/geometric_shapes_intersect.h
similarity index 76%
rename from trunk/fcl/include/fcl/geometric_shapes_intersect.h
rename to trunk/fcl/include/fcl/deprecated/geometric_shapes_intersect.h
index 2ff0129c..a6ed5b38 100644
--- a/trunk/fcl/include/fcl/geometric_shapes_intersect.h
+++ b/trunk/fcl/include/fcl/deprecated/geometric_shapes_intersect.h
@@ -47,6 +47,9 @@
 namespace fcl
 {
 
+namespace details
+{
+
 /** \brief recall function used by GJK algorithm */
 typedef void (*GJKSupportFunction)(const void* obj, const ccd_vec3_t* dir_, ccd_vec3_t* v);
 typedef void (*GJKCenterFunction)(const void* obj, ccd_vec3_t* c);
@@ -154,6 +157,13 @@ bool GJKCollide(void* obj1, ccd_support_fn supp1, ccd_center_fn cen1,
                void* obj2, ccd_support_fn supp2, ccd_center_fn cen2,
                Vec3f* contact_points, BVH_REAL* penetration_depth, Vec3f* normal);
 
+bool GJKDistance(void* obj1, ccd_support_fn supp1,
+                 void* obj2, ccd_support_fn supp2,
+                 BVH_REAL* dist);
+
+
+} // details
+
 
 /** intersection checking between two shapes */
 template<typename S1, typename S2>
@@ -161,15 +171,15 @@ bool shapeIntersect(const S1& s1, const SimpleTransform& tf1,
                     const S2& s2, const SimpleTransform& tf2,
                     Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
 {
-  void* o1 = GJKInitializer<S1>::createGJKObject(s1, tf1);
-  void* o2 = GJKInitializer<S2>::createGJKObject(s2, tf2);
+  void* o1 = details::GJKInitializer<S1>::createGJKObject(s1, tf1);
+  void* o2 = details::GJKInitializer<S2>::createGJKObject(s2, tf2);
 
-  bool res =  GJKCollide(o1, GJKInitializer<S1>::getSupportFunction(), GJKInitializer<S1>::getCenterFunction(),
-                         o2, GJKInitializer<S2>::getSupportFunction(), GJKInitializer<S2>::getCenterFunction(),
-                         contact_points, penetration_depth, normal);
+  bool res =  details::GJKCollide(o1, details::GJKInitializer<S1>::getSupportFunction(), details::GJKInitializer<S1>::getCenterFunction(),
+                                  o2, details::GJKInitializer<S2>::getSupportFunction(), details::GJKInitializer<S2>::getCenterFunction(),
+                                  contact_points, penetration_depth, normal);
 
-  GJKInitializer<S1>::deleteGJKObject(o1);
-  GJKInitializer<S2>::deleteGJKObject(o2);
+  details::GJKInitializer<S1>::deleteGJKObject(o1);
+  details::GJKInitializer<S2>::deleteGJKObject(o2);
 
   return res;
 }
@@ -179,15 +189,15 @@ template<typename S>
 bool shapeTriangleIntersect(const S& s, const SimpleTransform& tf,
                             const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
 {
-  void* o1 = GJKInitializer<S>::createGJKObject(s, tf);
-  void* o2 = triCreateGJKObject(P1, P2, P3);
+  void* o1 = details::GJKInitializer<S>::createGJKObject(s, tf);
+  void* o2 = details::triCreateGJKObject(P1, P2, P3);
 
-  bool res = GJKCollide(o1, GJKInitializer<S>::getSupportFunction(), GJKInitializer<S>::getCenterFunction(),
-                        o2, triGetSupportFunction(), triGetCenterFunction(),
-                        contact_points, penetration_depth, normal);
+  bool res = details::GJKCollide(o1, details::GJKInitializer<S>::getSupportFunction(), details::GJKInitializer<S>::getCenterFunction(),
+                                 o2, details::triGetSupportFunction(), details::triGetCenterFunction(),
+                                 contact_points, penetration_depth, normal);
 
-  GJKInitializer<S>::deleteGJKObject(o1);
-  triDeleteGJKObject(o2);
+  details::GJKInitializer<S>::deleteGJKObject(o1);
+  details::triDeleteGJKObject(o2);
 
   return res;
 }
@@ -198,42 +208,37 @@ bool shapeTriangleIntersect(const S& s, const SimpleTransform& tf,
                             const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, const Matrix3f& R, const Vec3f& T,
                             Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
 {
-  void* o1 = GJKInitializer<S>::createGJKObject(s, tf);
-  void* o2 = triCreateGJKObject(P1, P2, P3, R, T);
+  void* o1 = details::GJKInitializer<S>::createGJKObject(s, tf);
+  void* o2 = details::triCreateGJKObject(P1, P2, P3, R, T);
 
-  bool res = GJKCollide(o1, GJKInitializer<S>::getSupportFunction(), GJKInitializer<S>::getCenterFunction(),
-                        o2, triGetSupportFunction(), triGetCenterFunction(),
-                        contact_points, penetration_depth, normal);
+  bool res = details::GJKCollide(o1, details::GJKInitializer<S>::getSupportFunction(), details::GJKInitializer<S>::getCenterFunction(),
+                                 o2, details::triGetSupportFunction(), details::triGetCenterFunction(),
+                                 contact_points, penetration_depth, normal);
 
-  GJKInitializer<S>::deleteGJKObject(o1);
-  triDeleteGJKObject(o2);
+  details::GJKInitializer<S>::deleteGJKObject(o1);
+  details::triDeleteGJKObject(o2);
 
   return res;
 }
 
 
-bool GJKDistance(void* obj1, ccd_support_fn supp1,
-                 void* obj2, ccd_support_fn supp2,
-                 BVH_REAL* dist);
-
-
 /** \brief distance computation between two shapes */
 template<typename S1, typename S2>
 bool shapeDistance(const S1& s1, const SimpleTransform& tf1,
                    const S2& s2, const SimpleTransform& tf2,
                    BVH_REAL* dist)
 {
-  void* o1 = GJKInitializer<S1>::createGJKObject(s1, tf1);
-  void* o2 = GJKInitializer<S2>::createGJKObject(s2, tf2);
+  void* o1 = details::GJKInitializer<S1>::createGJKObject(s1, tf1);
+  void* o2 = details::GJKInitializer<S2>::createGJKObject(s2, tf2);
 
-  bool res =  GJKDistance(o1, GJKInitializer<S1>::getSupportFunction(),
-                          o2, GJKInitializer<S2>::getSupportFunction(),
-                          dist);
+  bool res =  details::GJKDistance(o1, details::GJKInitializer<S1>::getSupportFunction(),
+                                   o2, details::GJKInitializer<S2>::getSupportFunction(),
+                                   dist);
 
   if(*dist > 0) *dist = std::sqrt(*dist);
 
-  GJKInitializer<S1>::deleteGJKObject(o1);
-  GJKInitializer<S2>::deleteGJKObject(o2);
+  details::GJKInitializer<S1>::deleteGJKObject(o1);
+  details::GJKInitializer<S2>::deleteGJKObject(o2);
 
   return res;
 }
diff --git a/trunk/fcl/include/fcl/deprecated/gjk.h b/trunk/fcl/include/fcl/deprecated/gjk.h
new file mode 100644
index 00000000..595f298d
--- /dev/null
+++ b/trunk/fcl/include/fcl/deprecated/gjk.h
@@ -0,0 +1,412 @@
+/*
+ * Software License Agreement (BSD License)
+ *
+ *  Copyright (c) 2011, Willow Garage, Inc.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of Willow Garage, Inc. nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** \author Jia Pan */
+
+#ifndef FCL_GJK_H
+#define FCL_GJK_H
+
+#include "fcl/geometric_shapes.h"
+#include "fcl/matrix_3f.h"
+#include "fcl/transform.h"
+
+namespace fcl
+{
+
+namespace details
+{
+
+Vec3f getSupport(const ShapeBase* shape, const Vec3f& dir); 
+
+struct MinkowskiDiff
+{
+  const ShapeBase* shapes[2];
+  Matrix3f toshape1;
+  SimpleTransform toshape0;
+
+  MinkowskiDiff() { }
+
+  inline Vec3f support0(const Vec3f& d) const
+  {
+    return getSupport(shapes[0], d);
+  }
+
+  inline Vec3f support1(const Vec3f& d) const
+  {
+    return toshape0.transform(getSupport(shapes[1], toshape1 * d));
+  }
+
+  inline Vec3f support(const Vec3f& d) const
+  {
+    return support0(d) - support1(-d);
+  }
+
+  inline Vec3f support(const Vec3f& d, size_t index) const
+  {
+    if(index)
+      return support1(d);
+    else
+      return support0(d);
+  }
+
+};
+
+
+BVH_REAL projectOrigin(const Vec3f& a, const Vec3f& b, BVH_REAL* w, size_t& m);
+
+BVH_REAL projectOrigin(const Vec3f& a, const Vec3f& b, const Vec3f& c, BVH_REAL* w, size_t& m);
+
+BVH_REAL projectOrigin(const Vec3f& a, const Vec3f& b, const Vec3f& c, const Vec3f& d, BVH_REAL* w, size_t& m);
+
+
+static const BVH_REAL GJK_EPS = 0.000001;
+static const size_t GJK_MAX_ITERATIONS = 128;
+
+struct GJK
+{
+  struct SimplexV
+  {
+    Vec3f d; // support direction
+    Vec3f w; // support vector
+  };
+
+  struct Simplex
+  {
+    SimplexV* c[4]; // simplex vertex
+    BVH_REAL p[4]; // weight
+    size_t rank; // size of simplex (number of vertices)
+  };
+
+  enum Status {Valid, Inside, Failed};
+
+  MinkowskiDiff shape;
+  Vec3f ray;
+  BVH_REAL distance;
+  Simplex simplices[2];
+
+
+  GJK() { initialize(); }
+  
+  void initialize();
+
+  Status evaluate(const MinkowskiDiff& shape_, const Vec3f& guess);
+
+  void getSupport(const Vec3f& d, SimplexV& sv) const;
+
+  void removeVertex(Simplex& simplex);
+
+  void appendVertex(Simplex& simplex, const Vec3f& v);
+
+  bool encloseOrigin();
+
+  inline Simplex* getSimplex() const
+  {
+    return simplex;
+  }
+  
+private:
+  SimplexV store_v[4];
+  SimplexV* free_v[4];
+  size_t nfree;
+  size_t current;
+  Simplex* simplex;
+  Status status;
+};
+
+
+static const size_t EPA_MAX_FACES = 128;
+static const size_t EPA_MAX_VERTICES = 64;
+static const BVH_REAL EPA_EPS = 0.000001;
+static const size_t EPA_MAX_ITERATIONS = 255;
+
+struct EPA
+{
+private:
+  typedef GJK::SimplexV SimplexV;
+  struct SimplexF
+  {
+    Vec3f n;
+    BVH_REAL d;
+    SimplexV* c[3]; // a face has three vertices
+    SimplexF* f[3]; // a face has three adjacent faces
+    SimplexF* l[2]; // the pre and post faces in the list
+    size_t e[3];
+    size_t pass;
+  };
+
+  struct SimplexList
+  {
+    SimplexF* root;
+    size_t count;
+    SimplexList() : root(NULL), count(0) {}
+    void append(SimplexF* face)
+    {
+      face->l[0] = NULL;
+      face->l[1] = root;
+      if(root) root->l[0] = face;
+      root = face;
+      ++count;
+    }
+
+    void remove(SimplexF* face)
+    {
+      if(face->l[1]) face->l[1]->l[0] = face->l[0];
+      if(face->l[0]) face->l[0]->l[1] = face->l[1];
+      if(face == root) root = face->l[1];
+      --count;
+    }
+  };
+
+  static inline void bind(SimplexF* fa, size_t ea, SimplexF* fb, size_t eb)
+  {
+    fa->e[ea] = eb; fa->f[ea] = fb;
+    fb->e[eb] = ea; fb->f[eb] = fa;
+  }
+
+  struct SimplexHorizon
+  {
+    SimplexF* cf; // current face in the horizon
+    SimplexF* ff; // first face in the horizon
+    size_t nf; // number of faces in the horizon
+    SimplexHorizon() : cf(NULL), ff(NULL), nf(0) {}
+  };
+
+public:
+
+  enum Status {Valid, Touching, Degenerated, NonConvex, InvalidHull, OutOfFaces, OutOfVertices, AccuracyReached, FallBack, Failed};
+  
+  Status status;
+  GJK::Simplex result;
+  Vec3f normal;
+  BVH_REAL depth;
+  SimplexV sv_store[EPA_MAX_VERTICES];
+  SimplexF fc_store[EPA_MAX_FACES];
+  size_t nextsv;
+  SimplexList hull, stock;
+
+  EPA()
+  {
+    initialize();
+  }
+
+  void initialize();
+
+  bool getEdgeDist(SimplexF* face, SimplexV* a, SimplexV* b, BVH_REAL& dist);
+
+  SimplexF* newFace(SimplexV* a, SimplexV* b, SimplexV* c, bool forced);
+
+  /** \brief Find the best polytope face to split */
+  SimplexF* findBest();
+
+  Status evaluate(GJK& gjk, const Vec3f& guess);
+
+  /** \brief the goal is to add a face connecting vertex w and face edge f[e] */
+  bool expand(size_t pass, SimplexV* w, SimplexF* f, size_t e, SimplexHorizon& horizon);  
+};
+
+
+} // details
+
+
+
+
+
+template<typename S1, typename S2>
+bool shapeDistance2(const S1& s1, const SimpleTransform& tf1,
+                    const S2& s2, const SimpleTransform& tf2,
+                    BVH_REAL* distance)
+{
+  Vec3f guess(1, 0, 0);
+  details::MinkowskiDiff shape;
+  shape.shapes[0] = &s1;
+  shape.shapes[1] = &s2;
+  shape.toshape1 = tf2.getRotation().transposeTimes(tf1.getRotation());
+  shape.toshape0 = tf1.inverseTimes(tf2);
+
+  details::GJK gjk;
+  details::GJK::Status gjk_status = gjk.evaluate(shape, -guess);
+  if(gjk_status == details::GJK::Valid)
+  {
+    Vec3f w0, w1;
+    for(size_t i = 0; i < gjk.getSimplex()->rank; ++i)
+    {
+      BVH_REAL p = gjk.getSimplex()->p[i];
+      w0 += shape.support(gjk.getSimplex()->c[i]->d, 0) * p;
+      w1 += shape.support(-gjk.getSimplex()->c[i]->d, 1) * p;
+    }
+
+    *distance = (w0 - w1).length();
+    return true;
+  }
+  else
+  {
+    *distance = -1;
+    return false;
+  }
+}
+
+template<typename S1, typename S2>
+bool shapeIntersect2(const S1& s1, const SimpleTransform& tf1,
+                     const S2& s2, const SimpleTransform& tf2,
+                     Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
+{
+  Vec3f guess(1, 0, 0);
+  details::MinkowskiDiff shape;
+  shape.shapes[0] = &s1;
+  shape.shapes[1] = &s2;
+  shape.toshape1 = tf2.getRotation().transposeTimes(tf1.getRotation());
+  shape.toshape0 = tf1.inverseTimes(tf2);
+  
+  details::GJK gjk;
+  details::GJK::Status gjk_status = gjk.evaluate(shape, -guess);
+  switch(gjk_status)
+  {
+  case details::GJK::Inside:
+    {
+      details::EPA epa;
+      details::EPA::Status epa_status = epa.evaluate(gjk, -guess);
+      if(epa_status != details::EPA::Failed)
+      {
+        Vec3f w0;
+        for(size_t i = 0; i < epa.result.rank; ++i)
+        {
+          w0 += shape.support(epa.result.c[i]->d, 0) * epa.result.p[i];
+        }
+        if(penetration_depth) *penetration_depth = -epa.depth;
+        if(normal) *normal = -epa.normal;
+        if(contact_points) *contact_points = tf1.transform(w0 - epa.normal*(epa.depth *0.5));
+        return true;
+      }
+      else return false;
+    }
+    break;
+  default:
+    ;
+  }
+
+  return false;
+}
+
+
+template<typename S>
+bool shapeTriangleIntersect2(const S& s, const SimpleTransform& tf,
+                             const Vec3f& P1, const Vec3f& P2, const Vec3f& P3,
+                             Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
+{
+  Triangle2 tri(P1, P2, P3);
+  Vec3f guess(1, 0, 0);
+  details::MinkowskiDiff shape;
+  shape.shapes[0] = &s;
+  shape.shapes[1] = &tri;
+  shape.toshape1 = tf.getRotation();
+  shape.toshape0 = tf.inverse();
+  
+  details::GJK gjk;
+  details::GJK::Status gjk_status = gjk.evaluate(shape, -guess);
+  switch(gjk_status)
+  {
+  case details::GJK::Inside:
+    {
+      details::EPA epa;
+      details::EPA::Status epa_status = epa.evaluate(gjk, -guess);
+      if(epa_status != details::EPA::Failed)
+      {
+        Vec3f w0;
+        for(size_t i = 0; i < epa.result.rank; ++i)
+        {
+          w0 += shape.support(epa.result.c[i]->d, 0) * epa.result.p[i];
+        }
+        if(penetration_depth) *penetration_depth = -epa.depth;
+        if(normal) *normal = -epa.normal;
+        if(contact_points) *contact_points = tf.transform(w0 - epa.normal*(epa.depth *0.5));
+        return true;
+      }
+      else return false;
+    }
+    break;
+  default:
+    ;
+  }
+
+  return false;
+}
+
+template<typename S>
+bool shapeTriangleIntersect2(const S& s, const SimpleTransform& tf,
+                             const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, const Matrix3f& R, const Vec3f& T,
+                             Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
+{
+  Triangle2 tri(P1, P2, P3);
+  SimpleTransform tf2(R, T);
+  Vec3f guess(1, 0, 0);
+  details::MinkowskiDiff shape;
+  shape.shapes[0] = &s;
+  shape.shapes[1] = &tri;
+  shape.toshape1 = tf2.getRotation().transposeTimes(tf.getRotation());
+  shape.toshape0 = tf.inverseTimes(tf2);
+  
+  details::GJK gjk;
+  details::GJK::Status gjk_status = gjk.evaluate(shape, -guess);
+  switch(gjk_status)
+  {
+  case details::GJK::Inside:
+    {
+      details::EPA epa;
+      details::EPA::Status epa_status = epa.evaluate(gjk, -guess);
+      if(epa_status != details::EPA::Failed)
+      {
+        Vec3f w0;
+        for(size_t i = 0; i < epa.result.rank; ++i)
+        {
+          w0 += shape.support(epa.result.c[i]->d, 0) * epa.result.p[i];
+        }
+        if(penetration_depth) *penetration_depth = -epa.depth;
+        if(normal) *normal = -epa.normal;
+        if(contact_points) *contact_points = tf.transform(w0 - epa.normal*(epa.depth *0.5));
+        return true;
+      }
+      else return false;
+    }
+    break;
+  default:
+    ;
+  }
+
+  return false;
+}
+
+}
+
+
+#endif
diff --git a/trunk/fcl/include/fcl/geometric_shapes.h b/trunk/fcl/include/fcl/geometric_shapes.h
index 608fe846..0a317ab7 100644
--- a/trunk/fcl/include/fcl/geometric_shapes.h
+++ b/trunk/fcl/include/fcl/geometric_shapes.h
@@ -57,6 +57,17 @@ public:
   OBJECT_TYPE getObjectType() const { return OT_GEOM; }
 };
 
+class Triangle2 : public ShapeBase
+{
+public:
+  Triangle2(const Vec3f& a_, const Vec3f& b_, const Vec3f& c_) : a(a_), b(b_), c(c_) {}
+
+  void computeLocalAABB();
+  
+  NODE_TYPE getNodeType() const { return GEOM_TRIANGLE; }
+
+  Vec3f a, b, c;
+};
 
 /** Center at zero point, axis aligned box */
 class Box : public ShapeBase
diff --git a/trunk/fcl/include/fcl/geometric_shapes_utility.h b/trunk/fcl/include/fcl/geometric_shapes_utility.h
index 208a3b6d..8e1f04aa 100644
--- a/trunk/fcl/include/fcl/geometric_shapes_utility.h
+++ b/trunk/fcl/include/fcl/geometric_shapes_utility.h
@@ -53,6 +53,7 @@ std::vector<Vec3f> getBoundVertices(const Capsule& capsule, const SimpleTransfor
 std::vector<Vec3f> getBoundVertices(const Cone& cone, const SimpleTransform& tf);
 std::vector<Vec3f> getBoundVertices(const Cylinder& cylinder, const SimpleTransform& tf);
 std::vector<Vec3f> getBoundVertices(const Convex& convex, const SimpleTransform& tf);
+std::vector<Vec3f> getBoundVertices(const Triangle2& triangle, const SimpleTransform& tf);
 } // end detail
 
 
@@ -81,6 +82,9 @@ void computeBV<AABB, Cylinder>(const Cylinder& s, const SimpleTransform& tf, AAB
 template<>
 void computeBV<AABB, Convex>(const Convex& s, const SimpleTransform& tf, AABB& bv);
 
+template<>
+void computeBV<AABB, Triangle2>(const Triangle2& s, const SimpleTransform& tf, AABB& bv);
+
 
 /** \brief the bounding volume for half space back of plane for OBB, it is the plane itself */
 template<>
diff --git a/trunk/fcl/include/fcl/gjk.h b/trunk/fcl/include/fcl/narrowphase/gjk.h
similarity index 71%
rename from trunk/fcl/include/fcl/gjk.h
rename to trunk/fcl/include/fcl/narrowphase/gjk.h
index ed59d44b..8e67c7b3 100644
--- a/trunk/fcl/include/fcl/gjk.h
+++ b/trunk/fcl/include/fcl/narrowphase/gjk.h
@@ -44,6 +44,8 @@
 namespace fcl
 {
 
+namespace details
+{
 
 Vec3f getSupport(const ShapeBase* shape, const Vec3f& dir); 
 
@@ -80,8 +82,6 @@ struct MinkowskiDiff
 
 };
 
-namespace details
-{
 
 BVH_REAL projectOrigin(const Vec3f& a, const Vec3f& b, BVH_REAL* w, size_t& m);
 
@@ -89,7 +89,6 @@ BVH_REAL projectOrigin(const Vec3f& a, const Vec3f& b, const Vec3f& c, BVH_REAL*
 
 BVH_REAL projectOrigin(const Vec3f& a, const Vec3f& b, const Vec3f& c, const Vec3f& d, BVH_REAL* w, size_t& m);
 
-}
 
 static const BVH_REAL GJK_EPS = 0.000001;
 static const size_t GJK_MAX_ITERATIONS = 128;
@@ -119,14 +118,7 @@ struct GJK
 
   GJK() { initialize(); }
   
-  void initialize()
-  {
-    ray = Vec3f();
-    nfree = 0;
-    status = Failed;
-    current = 0;
-    distance = 0.0;
-  }
+  void initialize();
 
   Status evaluate(const MinkowskiDiff& shape_, const Vec3f& guess);
 
@@ -160,6 +152,7 @@ static const size_t EPA_MAX_ITERATIONS = 255;
 
 struct EPA
 {
+private:
   typedef GJK::SimplexV SimplexV;
   struct SimplexF
   {
@@ -209,6 +202,8 @@ struct EPA
     SimplexHorizon() : cf(NULL), ff(NULL), nf(0) {}
   };
 
+public:
+
   enum Status {Valid, Touching, Degenerated, NonConvex, InvalidHull, OutOfFaces, OutOfVertices, AccuracyReached, FallBack, Failed};
   
   Status status;
@@ -225,15 +220,7 @@ struct EPA
     initialize();
   }
 
-  void initialize()
-  {
-    status = Failed;
-    normal = Vec3f(0, 0, 0);
-    depth = 0;
-    nextsv = 0;
-    for(size_t i = 0; i < EPA_MAX_FACES; ++i)
-      stock.append(&fc_store[EPA_MAX_FACES-i-1]);
-  }
+  void initialize();
 
   bool getEdgeDist(SimplexF* face, SimplexV* a, SimplexV* b, BVH_REAL& dist);
 
@@ -248,81 +235,11 @@ struct EPA
   bool expand(size_t pass, SimplexV* w, SimplexF* f, size_t e, SimplexHorizon& horizon);  
 };
 
-template<typename S1, typename S2>
-bool shapeDistance2(const S1& s1, const SimpleTransform& tf1,
-                    const S2& s2, const SimpleTransform& tf2,
-                    BVH_REAL* distance)
-{
-  Vec3f guess(1, 0, 0);
-  MinkowskiDiff shape;
-  shape.shapes[0] = &s1;
-  shape.shapes[1] = &s2;
-  shape.toshape1 = tf2.getRotation().transposeTimes(tf1.getRotation());
-  shape.toshape0 = tf1.inverseTimes(tf2);
-
-  GJK gjk;
-  GJK::Status gjk_status = gjk.evaluate(shape, -guess);
-  if(gjk_status == GJK::Valid)
-  {
-    Vec3f w0, w1;
-    for(size_t i = 0; i < gjk.getSimplex()->rank; ++i)
-    {
-      BVH_REAL p = gjk.getSimplex()->p[i];
-      w0 += shape.support(gjk.getSimplex()->c[i]->d, 0) * p;
-      w1 += shape.support(-gjk.getSimplex()->c[i]->d, 1) * p;
-    }
 
-    *distance = (w0 - w1).length();
-    return true;
-  }
-  else
-  {
-    *distance = -1;
-    return false;
-  }
-}
+} // details
+
 
-template<typename S1, typename S2>
-bool shapeIntersect2(const S1& s1, const SimpleTransform& tf1,
-                     const S2& s2, const SimpleTransform& tf2,
-                     Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
-{
-  Vec3f guess(1, 0, 0);
-  MinkowskiDiff shape;
-  shape.shapes[0] = &s1;
-  shape.shapes[1] = &s2;
-  shape.toshape1 = tf2.getRotation().transposeTimes(tf1.getRotation());
-  shape.toshape0 = tf1.inverseTimes(tf2);
-  
-  GJK gjk;
-  GJK::Status gjk_status = gjk.evaluate(shape, -guess);
-  switch(gjk_status)
-  {
-  case GJK::Inside:
-    {
-      EPA epa;
-      EPA::Status epa_status = epa.evaluate(gjk, -guess);
-      if(epa_status != EPA::Failed)
-      {
-        Vec3f w0;
-        for(size_t i = 0; i < epa.result.rank; ++i)
-        {
-          w0 += shape.support(epa.result.c[i]->d, 0) * epa.result.p[i];
-        }
-        if(penetration_depth) *penetration_depth = -epa.depth;
-        if(normal) *normal = -epa.normal;
-        if(contact_points) *contact_points = tf1.transform(w0 - epa.normal*(epa.depth *0.5));
-        return true;
-      }
-      else return false;
-    }
-    break;
-  default:
-    ;
-  }
 
-  return false;
-}
 
 }
 
diff --git a/trunk/fcl/include/fcl/narrowphase/gjk_libccd.h b/trunk/fcl/include/fcl/narrowphase/gjk_libccd.h
new file mode 100644
index 00000000..b14faa8e
--- /dev/null
+++ b/trunk/fcl/include/fcl/narrowphase/gjk_libccd.h
@@ -0,0 +1,170 @@
+/*
+ * Software License Agreement (BSD License)
+ *
+ *  Copyright (c) 2011, Willow Garage, Inc.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of Willow Garage, Inc. nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** \author Jia Pan */
+
+
+#ifndef FCL_GJK_LIBCCD_H
+#define FCL_GJK_LIBCCD_H
+
+#include "fcl/geometric_shapes.h"
+#include "fcl/transform.h"
+
+#include <ccd/ccd.h>
+#include <ccd/quat.h>
+
+namespace fcl
+{
+
+namespace details
+{
+
+/** \brief recall function used by GJK algorithm */
+typedef void (*GJKSupportFunction)(const void* obj, const ccd_vec3_t* dir_, ccd_vec3_t* v);
+typedef void (*GJKCenterFunction)(const void* obj, ccd_vec3_t* c);
+
+/** \brief initialize GJK stuffs */
+template<typename T>
+class GJKInitializer
+{
+public:
+  /** \brief Get GJK support function */
+  static GJKSupportFunction getSupportFunction() { return NULL; }
+
+  /** \brief Get GJK center function */
+  static GJKCenterFunction getCenterFunction() { return NULL; }
+
+  /** \brief Get GJK object from a shape
+   * Notice that only local transformation is applied.
+   * Gloal transformation are considered later
+   */
+  static void* createGJKObject(const T& s, const SimpleTransform& tf) { return NULL; }
+
+  /** \brief Delete GJK object */
+  static void deleteGJKObject(void* o) {}
+};
+
+/** \brief initialize GJK Cylinder */
+template<>
+class GJKInitializer<Cylinder>
+{
+public:
+  static GJKSupportFunction getSupportFunction();
+  static GJKCenterFunction getCenterFunction();
+  static void* createGJKObject(const Cylinder& s, const SimpleTransform& tf);
+  static void deleteGJKObject(void* o);
+};
+
+/** \brief initialize GJK Sphere */
+template<>
+class GJKInitializer<Sphere>
+{
+public:
+  static GJKSupportFunction getSupportFunction();
+  static GJKCenterFunction getCenterFunction();
+  static void* createGJKObject(const Sphere& s, const SimpleTransform& tf);
+  static void deleteGJKObject(void* o);
+};
+
+/** \brief initialize GJK Box */
+template<>
+class GJKInitializer<Box>
+{
+public:
+  static GJKSupportFunction getSupportFunction();
+  static GJKCenterFunction getCenterFunction();
+  static void* createGJKObject(const Box& s, const SimpleTransform& tf);
+  static void deleteGJKObject(void* o);
+};
+
+/** \brief initialize GJK Capsule */
+template<>
+class GJKInitializer<Capsule>
+{
+public:
+  static GJKSupportFunction getSupportFunction();
+  static GJKCenterFunction getCenterFunction();
+  static void* createGJKObject(const Capsule& s, const SimpleTransform& tf);
+  static void deleteGJKObject(void* o);
+};
+
+/** \brief initialize GJK Cone */
+template<>
+class GJKInitializer<Cone>
+{
+public:
+  static GJKSupportFunction getSupportFunction();
+  static GJKCenterFunction getCenterFunction();
+  static void* createGJKObject(const Cone& s, const SimpleTransform& tf);
+  static void deleteGJKObject(void* o);
+};
+
+/** \brief initialize GJK Convex */
+template<>
+class GJKInitializer<Convex>
+{
+public:
+  static GJKSupportFunction getSupportFunction();
+  static GJKCenterFunction getCenterFunction();
+  static void* createGJKObject(const Convex& s, const SimpleTransform& tf);
+  static void deleteGJKObject(void* o);
+};
+
+/** \brief initialize GJK Triangle */
+GJKSupportFunction triGetSupportFunction();
+
+GJKCenterFunction triGetCenterFunction();
+
+void* triCreateGJKObject(const Vec3f& P1, const Vec3f& P2, const Vec3f& P3);
+
+void* triCreateGJKObject(const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, const Matrix3f& R, const Vec3f& T);
+
+void triDeleteGJKObject(void* o);
+
+/** \brief GJK collision algorithm */
+bool GJKCollide(void* obj1, ccd_support_fn supp1, ccd_center_fn cen1,
+                void* obj2, ccd_support_fn supp2, ccd_center_fn cen2,
+                Vec3f* contact_points, BVH_REAL* penetration_depth, Vec3f* normal);
+
+bool GJKDistance(void* obj1, ccd_support_fn supp1,
+                 void* obj2, ccd_support_fn supp2,
+                 BVH_REAL* dist);
+
+
+} // details
+
+
+}
+
+#endif
diff --git a/trunk/fcl/include/fcl/narrowphase/narrowphase.h b/trunk/fcl/include/fcl/narrowphase/narrowphase.h
new file mode 100644
index 00000000..736ef0c7
--- /dev/null
+++ b/trunk/fcl/include/fcl/narrowphase/narrowphase.h
@@ -0,0 +1,300 @@
+/*
+ * Software License Agreement (BSD License)
+ *
+ *  Copyright (c) 2011, Willow Garage, Inc.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of Willow Garage, Inc. nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** \author Jia Pan */
+
+#ifndef FCL_NARROWPHASE_H
+#define FCL_NARROWPHASE_H
+
+#include "fcl/narrowphase/gjk.h"
+#include "fcl/narrowphase/gjk_libccd.h"
+
+namespace fcl
+{
+
+/** intersection checking between two shapes */
+template<typename S1, typename S2>
+bool shapeIntersect(const S1& s1, const SimpleTransform& tf1,
+                    const S2& s2, const SimpleTransform& tf2,
+                    Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
+{
+  void* o1 = details::GJKInitializer<S1>::createGJKObject(s1, tf1);
+  void* o2 = details::GJKInitializer<S2>::createGJKObject(s2, tf2);
+
+  bool res = details::GJKCollide(o1, details::GJKInitializer<S1>::getSupportFunction(), details::GJKInitializer<S1>::getCenterFunction(),
+                                 o2, details::GJKInitializer<S2>::getSupportFunction(), details::GJKInitializer<S2>::getCenterFunction(),
+                                 contact_points, penetration_depth, normal);
+
+  details::GJKInitializer<S1>::deleteGJKObject(o1);
+  details::GJKInitializer<S2>::deleteGJKObject(o2);
+
+  return res;
+}
+
+/** \brief intersection checking between one shape and a triangle */
+template<typename S>
+bool shapeTriangleIntersect(const S& s, const SimpleTransform& tf,
+                            const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
+{
+  void* o1 = details::GJKInitializer<S>::createGJKObject(s, tf);
+  void* o2 = details::triCreateGJKObject(P1, P2, P3);
+
+  bool res = details::GJKCollide(o1, details::GJKInitializer<S>::getSupportFunction(), details::GJKInitializer<S>::getCenterFunction(),
+                                 o2, details::triGetSupportFunction(), details::triGetCenterFunction(),
+                                 contact_points, penetration_depth, normal);
+
+  details::GJKInitializer<S>::deleteGJKObject(o1);
+  details::triDeleteGJKObject(o2);
+
+  return res;
+}
+
+/** \brief intersection checking between one shape and a triangle with transformation */
+template<typename S>
+bool shapeTriangleIntersect(const S& s, const SimpleTransform& tf,
+                            const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, const Matrix3f& R, const Vec3f& T,
+                            Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
+{
+  void* o1 = details::GJKInitializer<S>::createGJKObject(s, tf);
+  void* o2 = details::triCreateGJKObject(P1, P2, P3, R, T);
+
+  bool res = details::GJKCollide(o1, details::GJKInitializer<S>::getSupportFunction(), details::GJKInitializer<S>::getCenterFunction(),
+                                 o2, details::triGetSupportFunction(), details::triGetCenterFunction(),
+                                 contact_points, penetration_depth, normal);
+
+  details::GJKInitializer<S>::deleteGJKObject(o1);
+  details::triDeleteGJKObject(o2);
+
+  return res;
+}
+
+
+/** \brief distance computation between two shapes */
+template<typename S1, typename S2>
+bool shapeDistance(const S1& s1, const SimpleTransform& tf1,
+                   const S2& s2, const SimpleTransform& tf2,
+                   BVH_REAL* dist)
+{
+  void* o1 = details::GJKInitializer<S1>::createGJKObject(s1, tf1);
+  void* o2 = details::GJKInitializer<S2>::createGJKObject(s2, tf2);
+
+  bool res =  details::GJKDistance(o1, details::GJKInitializer<S1>::getSupportFunction(),
+                                   o2, details::GJKInitializer<S2>::getSupportFunction(),
+                                   dist);
+
+  if(*dist > 0) *dist = std::sqrt(*dist);
+
+  details::GJKInitializer<S1>::deleteGJKObject(o1);
+  details::GJKInitializer<S2>::deleteGJKObject(o2);
+
+  return res;
+}
+
+}
+
+
+
+namespace fcl
+{
+
+
+
+template<typename S1, typename S2>
+bool shapeDistance2(const S1& s1, const SimpleTransform& tf1,
+                    const S2& s2, const SimpleTransform& tf2,
+                    BVH_REAL* distance)
+{
+  Vec3f guess(1, 0, 0);
+  details::MinkowskiDiff shape;
+  shape.shapes[0] = &s1;
+  shape.shapes[1] = &s2;
+  shape.toshape1 = tf2.getRotation().transposeTimes(tf1.getRotation());
+  shape.toshape0 = tf1.inverseTimes(tf2);
+
+  details::GJK gjk;
+  details::GJK::Status gjk_status = gjk.evaluate(shape, -guess);
+  if(gjk_status == details::GJK::Valid)
+  {
+    Vec3f w0, w1;
+    for(size_t i = 0; i < gjk.getSimplex()->rank; ++i)
+    {
+      BVH_REAL p = gjk.getSimplex()->p[i];
+      w0 += shape.support(gjk.getSimplex()->c[i]->d, 0) * p;
+      w1 += shape.support(-gjk.getSimplex()->c[i]->d, 1) * p;
+    }
+
+    *distance = (w0 - w1).length();
+    return true;
+  }
+  else
+  {
+    *distance = -1;
+    return false;
+  }
+}
+
+template<typename S1, typename S2>
+bool shapeIntersect2(const S1& s1, const SimpleTransform& tf1,
+                     const S2& s2, const SimpleTransform& tf2,
+                     Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
+{
+  Vec3f guess(1, 0, 0);
+  details::MinkowskiDiff shape;
+  shape.shapes[0] = &s1;
+  shape.shapes[1] = &s2;
+  shape.toshape1 = tf2.getRotation().transposeTimes(tf1.getRotation());
+  shape.toshape0 = tf1.inverseTimes(tf2);
+  
+  details::GJK gjk;
+  details::GJK::Status gjk_status = gjk.evaluate(shape, -guess);
+  switch(gjk_status)
+  {
+  case details::GJK::Inside:
+    {
+      details::EPA epa;
+      details::EPA::Status epa_status = epa.evaluate(gjk, -guess);
+      if(epa_status != details::EPA::Failed)
+      {
+        Vec3f w0;
+        for(size_t i = 0; i < epa.result.rank; ++i)
+        {
+          w0 += shape.support(epa.result.c[i]->d, 0) * epa.result.p[i];
+        }
+        if(penetration_depth) *penetration_depth = -epa.depth;
+        if(normal) *normal = -epa.normal;
+        if(contact_points) *contact_points = tf1.transform(w0 - epa.normal*(epa.depth *0.5));
+        return true;
+      }
+      else return false;
+    }
+    break;
+  default:
+    ;
+  }
+
+  return false;
+}
+
+
+template<typename S>
+bool shapeTriangleIntersect2(const S& s, const SimpleTransform& tf,
+                             const Vec3f& P1, const Vec3f& P2, const Vec3f& P3,
+                             Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
+{
+  Triangle2 tri(P1, P2, P3);
+  Vec3f guess(1, 0, 0);
+  details::MinkowskiDiff shape;
+  shape.shapes[0] = &s;
+  shape.shapes[1] = &tri;
+  shape.toshape1 = tf.getRotation();
+  shape.toshape0 = tf.inverse();
+  
+  details::GJK gjk;
+  details::GJK::Status gjk_status = gjk.evaluate(shape, -guess);
+  switch(gjk_status)
+  {
+  case details::GJK::Inside:
+    {
+      details::EPA epa;
+      details::EPA::Status epa_status = epa.evaluate(gjk, -guess);
+      if(epa_status != details::EPA::Failed)
+      {
+        Vec3f w0;
+        for(size_t i = 0; i < epa.result.rank; ++i)
+        {
+          w0 += shape.support(epa.result.c[i]->d, 0) * epa.result.p[i];
+        }
+        if(penetration_depth) *penetration_depth = -epa.depth;
+        if(normal) *normal = -epa.normal;
+        if(contact_points) *contact_points = tf.transform(w0 - epa.normal*(epa.depth *0.5));
+        return true;
+      }
+      else return false;
+    }
+    break;
+  default:
+    ;
+  }
+
+  return false;
+}
+
+template<typename S>
+bool shapeTriangleIntersect2(const S& s, const SimpleTransform& tf,
+                             const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, const Matrix3f& R, const Vec3f& T,
+                             Vec3f* contact_points = NULL, BVH_REAL* penetration_depth = NULL, Vec3f* normal = NULL)
+{
+  Triangle2 tri(P1, P2, P3);
+  SimpleTransform tf2(R, T);
+  Vec3f guess(1, 0, 0);
+  details::MinkowskiDiff shape;
+  shape.shapes[0] = &s;
+  shape.shapes[1] = &tri;
+  shape.toshape1 = tf2.getRotation().transposeTimes(tf.getRotation());
+  shape.toshape0 = tf.inverseTimes(tf2);
+  
+  details::GJK gjk;
+  details::GJK::Status gjk_status = gjk.evaluate(shape, -guess);
+  switch(gjk_status)
+  {
+  case details::GJK::Inside:
+    {
+      details::EPA epa;
+      details::EPA::Status epa_status = epa.evaluate(gjk, -guess);
+      if(epa_status != details::EPA::Failed)
+      {
+        Vec3f w0;
+        for(size_t i = 0; i < epa.result.rank; ++i)
+        {
+          w0 += shape.support(epa.result.c[i]->d, 0) * epa.result.p[i];
+        }
+        if(penetration_depth) *penetration_depth = -epa.depth;
+        if(normal) *normal = -epa.normal;
+        if(contact_points) *contact_points = tf.transform(w0 - epa.normal*(epa.depth *0.5));
+        return true;
+      }
+      else return false;
+    }
+    break;
+  default:
+    ;
+  }
+
+  return false;
+}
+
+
+}
+
+#endif
diff --git a/trunk/fcl/include/fcl/traversal_node_shapes.h b/trunk/fcl/include/fcl/traversal_node_shapes.h
index 214a25cb..0fde38b9 100644
--- a/trunk/fcl/include/fcl/traversal_node_shapes.h
+++ b/trunk/fcl/include/fcl/traversal_node_shapes.h
@@ -39,7 +39,7 @@
 #define FCL_TRAVERSAL_NODE_SHAPES_H
 
 #include "fcl/traversal_node_base.h"
-#include "fcl/geometric_shapes_intersect.h"
+#include "fcl/narrowphase/narrowphase.h"
 
 namespace fcl
 {
diff --git a/trunk/fcl/src/collision_func_matrix.cpp b/trunk/fcl/src/collision_func_matrix.cpp
index c06c4f32..001102ea 100644
--- a/trunk/fcl/src/collision_func_matrix.cpp
+++ b/trunk/fcl/src/collision_func_matrix.cpp
@@ -42,7 +42,7 @@
 #include "fcl/geometric_shapes.h"
 #include "fcl/BVH_model.h"
 #include "fcl/collision_node.h"
-#include "fcl/geometric_shapes_intersect.h"
+#include "fcl/narrowphase/narrowphase.h"
 
 
 namespace fcl
diff --git a/trunk/fcl/src/geometric_shapes_utility.cpp b/trunk/fcl/src/geometric_shapes_utility.cpp
index 89d98a4f..045641a0 100644
--- a/trunk/fcl/src/geometric_shapes_utility.cpp
+++ b/trunk/fcl/src/geometric_shapes_utility.cpp
@@ -203,6 +203,16 @@ std::vector<Vec3f> getBoundVertices(const Convex& convex, const SimpleTransform&
   return result;
 }
 
+std::vector<Vec3f> getBoundVertices(const Triangle2& triangle, const SimpleTransform& tf)
+{
+  std::vector<Vec3f> result(3);
+  result[0] = tf.transform(triangle.a);
+  result[1] = tf.transform(triangle.b);
+  result[2] = tf.transform(triangle.c);
+
+  return result;
+}
+
 } // end detail
 
 
@@ -287,6 +297,17 @@ void computeBV<AABB, Convex>(const Convex& s, const SimpleTransform& tf, AABB& b
   bv = bv_;
 }
 
+template<>
+void computeBV<AABB, Triangle2>(const Triangle2& s, const SimpleTransform& tf, AABB& bv)
+{
+  AABB bv_;
+  bv_ += tf.transform(s.a);
+  bv_ += tf.transform(s.b);
+  bv_ += tf.transform(s.c);
+
+  bv = bv_;
+}
+
 template<>
 void computeBV<AABB, Plane>(const Plane& s, const SimpleTransform& tf, AABB& bv)
 {
@@ -509,6 +530,14 @@ void Plane::computeLocalAABB()
   aabb_radius = (aabb.min_ - aabb_center).length();
 }
 
+void Triangle2::computeLocalAABB()
+{
+  AABB aabb;
+  computeBV<AABB>(*this, SimpleTransform(), aabb);
+  aabb_center = aabb.center();
+  aabb_radius = (aabb.min_ - aabb_center).length();
+}
+
 
 
 
diff --git a/trunk/fcl/src/gjk.cpp b/trunk/fcl/src/gjk.cpp
index 76b3f206..d19f2f49 100644
--- a/trunk/fcl/src/gjk.cpp
+++ b/trunk/fcl/src/gjk.cpp
@@ -34,17 +34,40 @@
 
 /** \author Jia Pan */
 
-#include "fcl/gjk.h"
+#include "fcl/narrowphase/gjk.h"
 
 namespace fcl
 {
 
-
+namespace details
+{
 
 Vec3f getSupport(const ShapeBase* shape, const Vec3f& dir)
 {
   switch(shape->getNodeType())
   {
+  case GEOM_TRIANGLE:
+    {
+      const Triangle2* triangle = static_cast<const Triangle2*>(shape);
+      BVH_REAL dota = dir.dot(triangle->a);
+      BVH_REAL dotb = dir.dot(triangle->b);
+      BVH_REAL dotc = dir.dot(triangle->c);
+      if(dota > dotb)
+      {
+        if(dotc > dota)
+          return triangle->c;
+        else
+          return triangle->a;
+      }
+      else
+      {
+        if(dotc > dotb)
+          return triangle->c;
+        else
+          return triangle->b;
+      }
+    }
+    break;
   case GEOM_BOX:
     {
       const Box* box = static_cast<const Box*>(shape);
@@ -115,14 +138,12 @@ Vec3f getSupport(const ShapeBase* shape, const Vec3f& dir)
   case GEOM_CONVEX:
     {
       const Convex* convex = static_cast<const Convex*>(shape);
-      const Vec3f& center = convex->center;
       BVH_REAL maxdot = - std::numeric_limits<BVH_REAL>::max();
       Vec3f* curp = convex->points;
       Vec3f bestv;
       for(size_t i = 0; i < convex->num_points; ++i, curp+=1)
       {
-        Vec3f p = (*curp) - center;
-        BVH_REAL dot = dir.dot(p);
+        BVH_REAL dot = dir.dot(*curp);
         if(dot > maxdot)
         {
           bestv = *curp;
@@ -143,9 +164,6 @@ Vec3f getSupport(const ShapeBase* shape, const Vec3f& dir)
 }
 
 
-namespace details
-{
-
 BVH_REAL projectOrigin(const Vec3f& a, const Vec3f& b, BVH_REAL* w, size_t& m)
 {
   const Vec3f d = b - a;
@@ -255,9 +273,14 @@ BVH_REAL projectOrigin(const Vec3f& a, const Vec3f& b, const Vec3f& c, const Vec
 }
 
 
-} // detail
-
-
+void GJK::initialize()
+{
+  ray = Vec3f();
+  nfree = 0;
+  status = Failed;
+  current = 0;
+  distance = 0.0;
+}
 
 GJK::Status GJK::evaluate(const MinkowskiDiff& shape_, const Vec3f& guess)
 {
@@ -337,11 +360,11 @@ GJK::Status GJK::evaluate(const MinkowskiDiff& shape_, const Vec3f& guess)
     switch(curr_simplex.rank)
     {
     case 2:
-      sqdist = details::projectOrigin(curr_simplex.c[0]->w, curr_simplex.c[1]->w, weights, mask); break;
+      sqdist = projectOrigin(curr_simplex.c[0]->w, curr_simplex.c[1]->w, weights, mask); break;
     case 3:
-      sqdist = details::projectOrigin(curr_simplex.c[0]->w, curr_simplex.c[1]->w, curr_simplex.c[2]->w, weights, mask); break;
+      sqdist = projectOrigin(curr_simplex.c[0]->w, curr_simplex.c[1]->w, curr_simplex.c[2]->w, weights, mask); break;
     case 4:
-      sqdist = details::projectOrigin(curr_simplex.c[0]->w, curr_simplex.c[1]->w, curr_simplex.c[2]->w, curr_simplex.c[3]->w, weights, mask); break;
+      sqdist = projectOrigin(curr_simplex.c[0]->w, curr_simplex.c[1]->w, curr_simplex.c[2]->w, curr_simplex.c[3]->w, weights, mask); break;
     }
       
     if(sqdist >= 0)
@@ -464,6 +487,16 @@ bool GJK::encloseOrigin()
 }
 
 
+void EPA::initialize()
+{
+  status = Failed;
+  normal = Vec3f(0, 0, 0);
+  depth = 0;
+  nextsv = 0;
+  for(size_t i = 0; i < EPA_MAX_FACES; ++i)
+    stock.append(&fc_store[EPA_MAX_FACES-i-1]);
+}
+
 bool EPA::getEdgeDist(SimplexF* face, SimplexV* a, SimplexV* b, BVH_REAL& dist)
 {
   Vec3f ba = b->w - a->w;
@@ -723,6 +756,6 @@ bool EPA::expand(size_t pass, SimplexV* w, SimplexF* f, size_t e, SimplexHorizon
   return false;
 }
 
-
+} // details
 
 } // fcl
diff --git a/trunk/fcl/src/gjk_libccd.cpp b/trunk/fcl/src/gjk_libccd.cpp
new file mode 100644
index 00000000..fc8e1f24
--- /dev/null
+++ b/trunk/fcl/src/gjk_libccd.cpp
@@ -0,0 +1,959 @@
+/*
+ * Software License Agreement (BSD License)
+ *
+ *  Copyright (c) 2011, Willow Garage, Inc.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of Willow Garage, Inc. nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** \author Jia Pan */
+
+
+#include "fcl/narrowphase/gjk_libccd.h"
+#include <ccd/simplex.h>
+#include <ccd/vec3.h>
+
+namespace fcl
+{
+
+namespace details
+{
+
+struct ccd_obj_t
+{
+  ccd_vec3_t pos;
+  ccd_quat_t rot, rot_inv;
+};
+
+struct ccd_box_t : public ccd_obj_t
+{
+  ccd_real_t dim[3];
+};
+
+struct ccd_cap_t : public ccd_obj_t
+{
+  ccd_real_t radius, height;
+};
+
+struct ccd_cyl_t : public ccd_obj_t
+{
+  ccd_real_t radius, height;
+};
+
+struct ccd_cone_t : public ccd_obj_t
+{
+  ccd_real_t radius, height;
+};
+
+struct ccd_sphere_t : public ccd_obj_t
+{
+  ccd_real_t radius;
+};
+
+struct ccd_convex_t : public ccd_obj_t
+{
+  const Convex* convex;
+};
+
+struct ccd_triangle_t : public ccd_obj_t
+{
+  ccd_vec3_t p[3];
+  ccd_vec3_t c;
+};
+
+static void tripleCross(const ccd_vec3_t* a, const ccd_vec3_t* b, 
+                        const ccd_vec3_t* c, ccd_vec3_t* d)
+{
+  ccd_vec3_t e;
+  ccdVec3Cross(&e, a, b);
+  ccdVec3Cross(d, &e, c);
+}
+
+static int doSimplex2Dist(ccd_simplex_t *simplex, ccd_vec3_t *dir, ccd_real_t* dist)
+{
+  const ccd_support_t *A, *B;
+  ccd_vec3_t AB, AO, tmp;
+  ccd_real_t dot;
+
+  // get last added as A
+  A = ccdSimplexLast(simplex);
+  // get the other point
+  B = ccdSimplexPoint(simplex, 0);
+  // compute AB oriented segment
+  ccdVec3Sub2(&AB, &B->v, &A->v);
+  // compute AO vector
+  ccdVec3Copy(&AO, &A->v);
+  ccdVec3Scale(&AO, -CCD_ONE);
+
+  // dot product AB . -AO
+  dot = ccdVec3Dot(&AB, &AO);
+
+  // check if origin doesn't lie on AB segment
+  ccdVec3Cross(&tmp, &AB, &AO);
+  if(ccdIsZero(ccdVec3Len2(&tmp)) && dot > CCD_ZERO)
+  {
+    return 1;
+  }
+
+  *dist = ccdVec3PointSegmentDist2(ccd_vec3_origin, &A->v, &B->v, NULL);
+
+  // check if origin is in area where AB segment is
+  if(ccdIsZero(dot) || dot < CCD_ZERO)
+  {
+    // origin is in outside are of A
+    ccdSimplexSet(simplex, 0, A);
+    ccdSimplexSetSize(simplex, 1);
+    ccdVec3Copy(dir, &AO);
+  }
+  else
+  {
+    // origin is in area where AB segment is
+
+    // keep simplex untouched and set direction to
+    // AB x AO x AB
+    tripleCross(&AB, &AO, &AB, dir);
+  }
+
+  return 0;
+}
+
+static int doSimplex3Dist(ccd_simplex_t *simplex, ccd_vec3_t *dir, ccd_real_t* dist)
+{
+  const ccd_support_t *A, *B, *C;
+  ccd_vec3_t AO, AB, AC, ABC, tmp;
+  ccd_real_t dot;
+
+  // get last added as A
+  A = ccdSimplexLast(simplex);
+  // get the other points
+  B = ccdSimplexPoint(simplex, 1);
+  C = ccdSimplexPoint(simplex, 0);
+
+  // check touching contact
+  *dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &C->v, NULL);
+  if(ccdIsZero(*dist))
+  {
+    return 1;
+  }
+
+  // check if triangle is really triangle (has area > 0)
+  // if not simplex can't be expanded and thus no itersection is found
+  if(ccdVec3Eq(&A->v, &B->v) || ccdVec3Eq(&A->v, &C->v))
+  {
+    return -1;
+  }
+
+  // compute AO vector
+  ccdVec3Copy(&AO, &A->v);
+  ccdVec3Scale(&AO, -CCD_ONE);
+
+  // compute AB and AC segments and ABC vector (perpendircular to triangle)
+  ccdVec3Sub2(&AB, &B->v, &A->v);
+  ccdVec3Sub2(&AC, &C->v, &A->v);
+  ccdVec3Cross(&ABC, &AB, &AC);
+
+  ccdVec3Cross(&tmp, &ABC, &AC);
+  dot = ccdVec3Dot(&tmp, &AO);
+  if(ccdIsZero(dot) || dot > CCD_ZERO)
+  {
+    dot = ccdVec3Dot(&AC, &AO);
+    if(ccdIsZero(dot) || dot > CCD_ZERO)
+    {
+      // C is already in place
+      ccdSimplexSet(simplex, 1, A);
+      ccdSimplexSetSize(simplex, 2);
+      tripleCross(&AC, &AO, &AC, dir);
+    }
+    else
+    {
+    ccd_do_simplex3_45:
+      dot = ccdVec3Dot(&AB, &AO);
+      if(ccdIsZero(dot) || dot > CCD_ZERO)
+      {
+        ccdSimplexSet(simplex, 0, B);
+        ccdSimplexSet(simplex, 1, A);
+        ccdSimplexSetSize(simplex, 2);
+        tripleCross(&AB, &AO, &AB, dir);
+      }
+      else
+      {
+        ccdSimplexSet(simplex, 0, A);
+        ccdSimplexSetSize(simplex, 1);
+        ccdVec3Copy(dir, &AO);
+      }
+    }
+  }
+  else
+  {
+    ccdVec3Cross(&tmp, &AB, &ABC);
+    dot = ccdVec3Dot(&tmp, &AO);
+    if(ccdIsZero(dot) || dot > CCD_ZERO)
+    {
+      goto ccd_do_simplex3_45;
+    }
+    else
+    {
+      dot = ccdVec3Dot(&ABC, &AO);
+      if (ccdIsZero(dot) || dot > CCD_ZERO)
+      {
+        ccdVec3Copy(dir, &ABC);
+      }
+      else
+      {
+        ccd_support_t Ctmp;
+        ccdSupportCopy(&Ctmp, C);
+        ccdSimplexSet(simplex, 0, B);
+        ccdSimplexSet(simplex, 1, &Ctmp);
+
+        ccdVec3Copy(dir, &ABC);
+        ccdVec3Scale(dir, -CCD_ONE);
+      }
+    }
+  }
+
+
+  return 0;
+}
+
+static int doSimplex4Dist(ccd_simplex_t *simplex, ccd_vec3_t *dir, ccd_real_t* dist)
+{
+  const ccd_support_t *A, *B, *C, *D;
+  ccd_vec3_t AO, AB, AC, AD, ABC, ACD, ADB;
+  int B_on_ACD, C_on_ADB, D_on_ABC;
+  int AB_O, AC_O, AD_O;
+
+  // get last added as A
+  A = ccdSimplexLast(simplex);
+  // get the other points
+  B = ccdSimplexPoint(simplex, 2);
+  C = ccdSimplexPoint(simplex, 1);
+  D = ccdSimplexPoint(simplex, 0);
+
+  // check if tetrahedron is really tetrahedron (has volume > 0)
+  // if it is not simplex can't be expanded and thus no intersection is
+  // found
+  ccd_real_t volume = ccdVec3PointTriDist2(&A->v, &B->v, &C->v, &D->v, NULL);
+  if(ccdIsZero(volume))
+  {
+    return -1;
+  }
+
+  // check if origin lies on some of tetrahedron's face - if so objects
+  // intersect
+  volume = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &C->v, NULL);
+  if(ccdIsZero(volume))
+    return 1;
+  volume = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &C->v, &D->v, NULL);
+  if(ccdIsZero(volume))
+    return 1;
+  volume = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &D->v, NULL);
+  if(ccdIsZero(volume))
+    return 1;
+  volume = ccdVec3PointTriDist2(ccd_vec3_origin, &B->v, &C->v, &D->v, NULL);
+  if(ccdIsZero(volume))
+    return 1;
+
+  // compute AO, AB, AC, AD segments and ABC, ACD, ADB normal vectors
+  ccdVec3Copy(&AO, &A->v);
+  ccdVec3Scale(&AO, -CCD_ONE);
+  ccdVec3Sub2(&AB, &B->v, &A->v);
+  ccdVec3Sub2(&AC, &C->v, &A->v);
+  ccdVec3Sub2(&AD, &D->v, &A->v);
+  ccdVec3Cross(&ABC, &AB, &AC);
+  ccdVec3Cross(&ACD, &AC, &AD);
+  ccdVec3Cross(&ADB, &AD, &AB);
+
+  // side (positive or negative) of B, C, D relative to planes ACD, ADB
+  // and ABC respectively
+  B_on_ACD = ccdSign(ccdVec3Dot(&ACD, &AB));
+  C_on_ADB = ccdSign(ccdVec3Dot(&ADB, &AC));
+  D_on_ABC = ccdSign(ccdVec3Dot(&ABC, &AD));
+
+  // whether origin is on same side of ACD, ADB, ABC as B, C, D
+  // respectively
+  AB_O = ccdSign(ccdVec3Dot(&ACD, &AO)) == B_on_ACD;
+  AC_O = ccdSign(ccdVec3Dot(&ADB, &AO)) == C_on_ADB;
+  AD_O = ccdSign(ccdVec3Dot(&ABC, &AO)) == D_on_ABC;
+
+  if(AB_O && AC_O && AD_O)
+  {
+    // origin is in tetrahedron
+    return 1;
+
+    // rearrange simplex to triangle and call doSimplex3Dist()
+  }
+  else if(!AB_O)
+  {
+    // B is farthest from the origin among all of the tetrahedron's
+    // points, so remove it from the list and go on with the triangle
+    // case
+
+    // D and C are in place
+    ccdSimplexSet(simplex, 2, A);
+    ccdSimplexSetSize(simplex, 3);
+  }
+  else if(!AC_O)
+  {
+    // C is farthest
+    ccdSimplexSet(simplex, 1, D);
+    ccdSimplexSet(simplex, 0, B);
+    ccdSimplexSet(simplex, 2, A);
+    ccdSimplexSetSize(simplex, 3);
+  }
+  else
+  { 
+    // (!AD_O)
+    ccdSimplexSet(simplex, 0, C);
+    ccdSimplexSet(simplex, 1, B);
+    ccdSimplexSet(simplex, 2, A);
+    ccdSimplexSetSize(simplex, 3);
+  }
+
+  return doSimplex3Dist(simplex, dir, dist);
+}
+
+static int doSimplexDist(ccd_simplex_t *simplex, ccd_vec3_t *dir, ccd_real_t* dist)
+{
+  if(ccdSimplexSize(simplex) == 2)
+  {
+    // simplex contains segment only one segment
+    return doSimplex2Dist(simplex, dir, dist);
+  }
+  else if(ccdSimplexSize(simplex) == 3)
+  {
+    // simplex contains triangle
+    return doSimplex3Dist(simplex, dir, dist);
+  }
+  else
+  { 
+    // ccdSimplexSize(simplex) == 4
+    // tetrahedron - this is the only shape which can encapsule origin
+    // so doSimplex4() also contains test on it
+    return doSimplex4Dist(simplex, dir, dist);
+  }
+}
+
+
+static ccd_real_t __ccdGJKDist(const void *obj1, const void *obj2,
+                               const ccd_t *ccd, ccd_simplex_t *simplex)
+{
+  unsigned long iterations;
+  ccd_vec3_t dir; // direction vector
+  ccd_support_t last; // last support point
+  int do_simplex_res;
+  ccd_real_t min_dist = -1;
+
+  // initialize simplex struct
+  ccdSimplexInit(simplex);
+
+  // get first direction
+  ccd->first_dir(obj1, obj2, &dir);
+  // get first support point
+  __ccdSupport(obj1, obj2, &dir, ccd, &last);
+  // and add this point to simplex as last one
+  ccdSimplexAdd(simplex, &last);
+
+  // set up direction vector to as (O - last) which is exactly -last
+  ccdVec3Copy(&dir, &last.v);
+  ccdVec3Scale(&dir, -CCD_ONE);
+
+  // start iterations
+  for (iterations = 0UL; iterations < ccd->max_iterations; ++iterations)
+  {
+    // obtain support point
+    __ccdSupport(obj1, obj2, &dir, ccd, &last);
+
+    // check if farthest point in Minkowski difference in direction dir
+    // isn't somewhere before origin (the test on negative dot product)
+    // - because if it is, objects are not intersecting at all.
+    if(ccdVec3Dot(&last.v, &dir) < CCD_ZERO)
+    {
+      ccd_real_t dist = ccdVec3Len2(&last.v);
+      if(min_dist < 0) min_dist = dist;
+      else 
+        min_dist = std::min(min_dist, dist);
+    }
+
+    // add last support vector to simplex
+    ccdSimplexAdd(simplex, &last);
+
+    // if doSimplex returns 1 if objects intersect, -1 if objects don't
+    // intersect and 0 if algorithm should continue
+    ccd_real_t dist;
+    do_simplex_res = doSimplexDist(simplex, &dir, &dist);
+
+    if(do_simplex_res == 1)
+    {
+      return -1; // intersection found
+    }
+    else if(do_simplex_res == -1)
+    {
+      return min_dist;
+    }
+
+    if(ccdIsZero(ccdVec3Len2(&dir)))
+    {
+      return min_dist; // intersection not found
+    }
+
+    if(min_dist > 0)
+    {
+      if(fabs(min_dist - dist) < 0.000001 && iterations > 0)
+        break;
+      else
+        min_dist = std::min(min_dist, dist);
+    }
+    else min_dist = dist;
+  }
+
+  // intersection wasn't found
+  return min_dist;
+}
+
+
+/** Basic shape to ccd shape */
+static void shapeToGJK(const ShapeBase& s, const SimpleTransform& tf, ccd_obj_t* o)
+{
+  const SimpleQuaternion& q = tf.getQuatRotation();
+  const Vec3f& T = tf.getTranslation();
+  ccdVec3Set(&o->pos, T[0], T[1], T[2]);
+  ccdQuatSet(&o->rot, q.getX(), q.getY(), q.getZ(), q.getW());
+  ccdQuatInvert2(&o->rot_inv, &o->rot);
+}
+
+static void boxToGJK(const Box& s, const SimpleTransform& tf, ccd_box_t* box)
+{
+  shapeToGJK(s, tf, box);
+  box->dim[0] = s.side[0] / 2.0;
+  box->dim[1] = s.side[1] / 2.0;
+  box->dim[2] = s.side[2] / 2.0;
+}
+
+static void capToGJK(const Capsule& s, const SimpleTransform& tf, ccd_cap_t* cap)
+{
+  shapeToGJK(s, tf, cap);
+  cap->radius = s.radius;
+  cap->height = s.lz / 2;
+}
+
+static void cylToGJK(const Cylinder& s, const SimpleTransform& tf, ccd_cyl_t* cyl)
+{
+  shapeToGJK(s, tf, cyl);
+  cyl->radius = s.radius;
+  cyl->height = s.lz / 2;
+}
+
+static void coneToGJK(const Cone& s, const SimpleTransform& tf, ccd_cone_t* cone)
+{
+  shapeToGJK(s, tf, cone);
+  cone->radius = s.radius;
+  cone->height = s.lz / 2;
+}
+
+static void sphereToGJK(const Sphere& s, const SimpleTransform& tf, ccd_sphere_t* sph)
+{
+  shapeToGJK(s, tf, sph);
+  sph->radius = s.radius;
+}
+
+static void convexToGJK(const Convex& s, const SimpleTransform& tf, ccd_convex_t* conv)
+{
+  shapeToGJK(s, tf, conv);
+  conv->convex = &s;
+}
+
+/** Support functions */
+static void supportBox(const void* obj, const ccd_vec3_t* dir_, ccd_vec3_t* v)
+{
+  const ccd_box_t* o = (const ccd_box_t*)obj;
+  ccd_vec3_t dir;
+  ccdVec3Copy(&dir, dir_);
+  ccdQuatRotVec(&dir, &o->rot_inv);
+  ccdVec3Set(v, ccdSign(ccdVec3X(&dir)) * o->dim[0],
+                ccdSign(ccdVec3Y(&dir)) * o->dim[1],
+                ccdSign(ccdVec3Z(&dir)) * o->dim[2]);
+  ccdQuatRotVec(v, &o->rot);
+  ccdVec3Add(v, &o->pos);
+}
+
+static void supportCap(const void* obj, const ccd_vec3_t* dir_, ccd_vec3_t* v)
+{
+  const ccd_cap_t* o = (const ccd_cap_t*)obj;
+  ccd_vec3_t dir, pos1, pos2;
+
+  ccdVec3Copy(&dir, dir_);
+  ccdQuatRotVec(&dir, &o->rot_inv);
+
+  ccdVec3Set(&pos1, CCD_ZERO, CCD_ZERO, o->height);
+  ccdVec3Set(&pos2, CCD_ZERO, CCD_ZERO, -o->height);
+
+  ccdVec3Copy(v, &dir);
+  ccdVec3Scale(v, o->radius);
+  ccdVec3Add(&pos1, v);
+  ccdVec3Add(&pos2, v);
+
+  if(ccdVec3Dot(&dir, &pos1) > ccdVec3Dot(&dir, &pos2))
+    ccdVec3Copy(v, &pos1);
+  else
+    ccdVec3Copy(v, &pos2);
+
+  // transform support vertex
+  ccdQuatRotVec(v, &o->rot);
+  ccdVec3Add(v, &o->pos);
+}
+
+static void supportCyl(const void* obj, const ccd_vec3_t* dir_, ccd_vec3_t* v)
+{
+  const ccd_cyl_t* cyl = (const ccd_cyl_t*)obj;
+  ccd_vec3_t dir;
+  double zdist, rad;
+
+  ccdVec3Copy(&dir, dir_);
+  ccdQuatRotVec(&dir, &cyl->rot_inv);
+
+  zdist = dir.v[0] * dir.v[0] + dir.v[1] * dir.v[1];
+  zdist = sqrt(zdist);
+  if(ccdIsZero(zdist))
+    ccdVec3Set(v, 0., 0., ccdSign(ccdVec3Z(&dir)) * cyl->height);
+  else
+  {
+    rad = cyl->radius / zdist;
+
+    ccdVec3Set(v, rad * ccdVec3X(&dir),
+                  rad * ccdVec3Y(&dir),
+                  ccdSign(ccdVec3Z(&dir)) * cyl->height);
+  }
+
+  // transform support vertex
+  ccdQuatRotVec(v, &cyl->rot);
+  ccdVec3Add(v, &cyl->pos);
+}
+
+static void supportCone(const void* obj, const ccd_vec3_t* dir_, ccd_vec3_t* v)
+{
+  const ccd_cone_t* cone = (const ccd_cone_t*)obj;
+  ccd_vec3_t dir;
+
+  ccdVec3Copy(&dir, dir_);
+  ccdQuatRotVec(&dir, &cone->rot_inv);
+
+  double zdist, len, rad;
+  zdist = dir.v[0] * dir.v[0] + dir.v[1] * dir.v[1];
+  len = zdist + dir.v[2] * dir.v[2];
+  zdist = sqrt(zdist);
+  len = sqrt(len);
+
+  double sin_a = cone->radius / sqrt(cone->radius * cone->radius + 4 * cone->height * cone->height);
+
+  if(dir.v[2] > len * sin_a)
+    ccdVec3Set(v, 0., 0., cone->height);
+  else if(zdist > 0)
+  {
+    rad = cone->radius / zdist;
+    ccdVec3Set(v, rad * ccdVec3X(&dir), rad * ccdVec3Y(&dir), -cone->height);
+  }
+  else
+    ccdVec3Set(v, 0, 0, -cone->height);
+
+  // transform support vertex
+  ccdQuatRotVec(v, &cone->rot);
+  ccdVec3Add(v, &cone->pos);
+}
+
+static void supportSphere(const void* obj, const ccd_vec3_t* dir_, ccd_vec3_t* v)
+{
+  const ccd_sphere_t* s = (const ccd_sphere_t*)obj;
+  ccd_vec3_t dir;
+
+  ccdVec3Copy(&dir, dir_);
+  ccdQuatRotVec(&dir, &s->rot_inv);
+
+  ccdVec3Copy(v, &dir);
+  ccdVec3Scale(v, s->radius);
+  ccdVec3Scale(v, CCD_ONE / CCD_SQRT(ccdVec3Len2(&dir)));
+
+  // transform support vertex
+  ccdQuatRotVec(v, &s->rot);
+  ccdVec3Add(v, &s->pos);
+}
+
+static void supportConvex(const void* obj, const ccd_vec3_t* dir_, ccd_vec3_t* v)
+{
+  const ccd_convex_t* c = (const ccd_convex_t*)obj;
+  ccd_vec3_t dir, p;
+  ccd_real_t maxdot, dot;
+  int i;
+  Vec3f* curp;
+  const Vec3f& center = c->convex->center;
+
+  ccdVec3Copy(&dir, dir_);
+  ccdQuatRotVec(&dir, &c->rot_inv);
+
+  maxdot = -CCD_REAL_MAX;
+  curp = c->convex->points;
+
+  for(i = 0; i < c->convex->num_points; ++i, curp += 1)
+  {
+    ccdVec3Set(&p, (*curp)[0] - center[0], (*curp)[1] - center[1], (*curp)[2] - center[2]);
+    dot = ccdVec3Dot(&dir, &p);
+    if(dot > maxdot)
+    {
+      ccdVec3Set(v, (*curp)[0], (*curp)[1], (*curp)[2]);
+      maxdot = dot;
+    }
+  }
+
+  // transform support vertex
+  ccdQuatRotVec(v, &c->rot);
+  ccdVec3Add(v, &c->pos);
+}
+
+static void supportTriangle(const void* obj, const ccd_vec3_t* dir_, ccd_vec3_t* v)
+{
+  const ccd_triangle_t* tri = (const ccd_triangle_t*)obj;
+  ccd_vec3_t dir, p;
+  ccd_real_t maxdot, dot;
+  int i;
+
+  ccdVec3Copy(&dir, dir_);
+  ccdQuatRotVec(&dir, &tri->rot_inv);
+
+  maxdot = -CCD_REAL_MAX;
+
+  for(i = 0; i < 3; ++i)
+  {
+    ccdVec3Set(&p, tri->p[i].v[0] - tri->c.v[0], tri->p[i].v[1] - tri->c.v[1], tri->p[i].v[2] - tri->c.v[2]);
+    dot = ccdVec3Dot(&dir, &p);
+    if(dot > maxdot)
+    {
+      ccdVec3Copy(v, &tri->p[i]);
+      maxdot = dot;
+    }
+  }
+
+  // transform support vertex
+  ccdQuatRotVec(v, &tri->rot);
+  ccdVec3Add(v, &tri->pos);
+}
+
+static void centerShape(const void* obj, ccd_vec3_t* c)
+{
+    const ccd_obj_t *o = (const ccd_obj_t*)obj;
+    ccdVec3Copy(c, &o->pos);
+}
+
+static void centerConvex(const void* obj, ccd_vec3_t* c)
+{
+  const ccd_convex_t *o = (const ccd_convex_t*)obj;
+  ccdVec3Set(c, o->convex->center[0], o->convex->center[1], o->convex->center[2]);
+  ccdQuatRotVec(c, &o->rot);
+  ccdVec3Add(c, &o->pos);
+}
+
+static void centerTriangle(const void* obj, ccd_vec3_t* c)
+{
+    const ccd_triangle_t *o = (const ccd_triangle_t*)obj;
+    ccdVec3Copy(c, &o->c);
+    ccdQuatRotVec(c, &o->rot);
+    ccdVec3Add(c, &o->pos);
+}
+
+bool GJKCollide(void* obj1, ccd_support_fn supp1, ccd_center_fn cen1,
+                void* obj2, ccd_support_fn supp2, ccd_center_fn cen2,
+                Vec3f* contact_points, BVH_REAL* penetration_depth, Vec3f* normal)
+{
+  ccd_t ccd;
+  int res;
+  ccd_real_t depth;
+  ccd_vec3_t dir, pos;
+
+
+  CCD_INIT(&ccd);
+  ccd.support1 = supp1;
+  ccd.support2 = supp2;
+  ccd.center1 = cen1;
+  ccd.center2 = cen2;
+  ccd.max_iterations = 500;
+  ccd.mpr_tolerance = 1e-6;
+
+  if(!contact_points)
+  {
+    return ccdMPRIntersect(obj1, obj2, &ccd);
+  }
+
+
+  res = ccdMPRPenetration(obj1, obj2, &ccd, &depth, &dir, &pos);
+  if(res == 0)
+  {
+    contact_points->setValue(ccdVec3X(&pos), ccdVec3Y(&pos), ccdVec3Z(&pos));
+    *penetration_depth = depth;
+    normal->setValue(-ccdVec3X(&dir), -ccdVec3Y(&dir), -ccdVec3Z(&dir));
+
+    return true;
+  }
+
+  return false;
+}
+
+bool GJKDistance(void* obj1, ccd_support_fn supp1,
+                 void* obj2, ccd_support_fn supp2,
+                 BVH_REAL* res)
+{
+  ccd_t ccd;
+  ccd_real_t dist;
+  ccd_vec3_t dir, pos;
+  CCD_INIT(&ccd);
+  ccd.support1 = supp1;
+  ccd.support2 = supp2;
+  
+  ccd.max_iterations = 500;
+  
+  ccd_simplex_t simplex;
+  dist = __ccdGJKDist(obj1, obj2, &ccd, &simplex);
+  *res = dist;
+  if(dist < 0) return false;
+  else return true;
+}
+
+
+GJKSupportFunction GJKInitializer<Cylinder>::getSupportFunction()
+{
+  return &supportCyl;
+}
+
+
+GJKCenterFunction GJKInitializer<Cylinder>::getCenterFunction()
+{
+  return &centerShape;
+}
+
+
+void* GJKInitializer<Cylinder>::createGJKObject(const Cylinder& s, const SimpleTransform& tf)
+{
+  ccd_cyl_t* o = new ccd_cyl_t;
+  cylToGJK(s, tf, o);
+  return o;
+}
+
+
+void GJKInitializer<Cylinder>::deleteGJKObject(void* o_)
+{
+  ccd_cyl_t* o = (ccd_cyl_t*)o_;
+  delete o;
+}
+
+
+GJKSupportFunction GJKInitializer<Sphere>::getSupportFunction()
+{
+  return &supportSphere;
+}
+
+
+GJKCenterFunction GJKInitializer<Sphere>::getCenterFunction()
+{
+  return &centerShape;
+}
+
+
+void* GJKInitializer<Sphere>::createGJKObject(const Sphere& s, const SimpleTransform& tf)
+{
+  ccd_sphere_t* o = new ccd_sphere_t;
+  sphereToGJK(s, tf, o);
+  return o;
+}
+
+void GJKInitializer<Sphere>::deleteGJKObject(void* o_)
+{
+  ccd_sphere_t* o = (ccd_sphere_t*)o_;
+  delete o;
+}
+
+GJKSupportFunction GJKInitializer<Box>::getSupportFunction()
+{
+  return &supportBox;
+}
+
+
+GJKCenterFunction GJKInitializer<Box>::getCenterFunction()
+{
+  return &centerShape;
+}
+
+
+void* GJKInitializer<Box>::createGJKObject(const Box& s, const SimpleTransform& tf)
+{
+  ccd_box_t* o = new ccd_box_t;
+  boxToGJK(s, tf, o);
+  return o;
+}
+
+
+void GJKInitializer<Box>::deleteGJKObject(void* o_)
+{
+  ccd_box_t* o = (ccd_box_t*)o_;
+  delete o;
+}
+
+
+GJKSupportFunction GJKInitializer<Capsule>::getSupportFunction()
+{
+  return &supportCap;
+}
+
+
+GJKCenterFunction GJKInitializer<Capsule>::getCenterFunction()
+{
+  return &centerShape;
+}
+
+
+void* GJKInitializer<Capsule>::createGJKObject(const Capsule& s, const SimpleTransform& tf)
+{
+  ccd_cap_t* o = new ccd_cap_t;
+  capToGJK(s, tf, o);
+  return o;
+}
+
+
+void GJKInitializer<Capsule>::deleteGJKObject(void* o_)
+{
+  ccd_cap_t* o = (ccd_cap_t*)o_;
+  delete o;
+}
+
+
+GJKSupportFunction GJKInitializer<Cone>::getSupportFunction()
+{
+  return &supportCone;
+}
+
+
+GJKCenterFunction GJKInitializer<Cone>::getCenterFunction()
+{
+  return &centerShape;
+}
+
+
+void* GJKInitializer<Cone>::createGJKObject(const Cone& s, const SimpleTransform& tf)
+{
+  ccd_cone_t* o = new ccd_cone_t;
+  coneToGJK(s, tf, o);
+  return o;
+}
+
+
+void GJKInitializer<Cone>::deleteGJKObject(void* o_)
+{
+  ccd_cone_t* o = (ccd_cone_t*)o_;
+  delete o;
+}
+
+
+GJKSupportFunction GJKInitializer<Convex>::getSupportFunction()
+{
+  return &supportConvex;
+}
+
+
+GJKCenterFunction GJKInitializer<Convex>::getCenterFunction()
+{
+  return &centerConvex;
+}
+
+
+void* GJKInitializer<Convex>::createGJKObject(const Convex& s, const SimpleTransform& tf)
+{
+  ccd_convex_t* o = new ccd_convex_t;
+  convexToGJK(s, tf, o);
+  return o;
+}
+
+
+void GJKInitializer<Convex>::deleteGJKObject(void* o_)
+{
+  ccd_convex_t* o = (ccd_convex_t*)o_;
+  delete o;
+}
+
+
+GJKSupportFunction triGetSupportFunction()
+{
+  return &supportTriangle;
+}
+
+
+GJKCenterFunction triGetCenterFunction()
+{
+  return &centerTriangle;
+}
+
+
+void* triCreateGJKObject(const Vec3f& P1, const Vec3f& P2, const Vec3f& P3)
+{
+  ccd_triangle_t* o = new ccd_triangle_t;
+  Vec3f center((P1[0] + P2[0] + P3[0]) / 3, (P1[1] + P2[1] + P3[1]) / 3, (P1[2] + P2[2] + P3[2]) / 3);
+
+  ccdVec3Set(&o->p[0], P1[0], P1[1], P1[2]);
+  ccdVec3Set(&o->p[1], P2[0], P2[1], P2[2]);
+  ccdVec3Set(&o->p[2], P3[0], P3[1], P3[2]);
+  ccdVec3Set(&o->c, center[0], center[1], center[2]);
+  ccdVec3Set(&o->pos, 0., 0., 0.);
+  ccdQuatSet(&o->rot, 0., 0., 0., 1.);
+  ccdQuatInvert2(&o->rot_inv, &o->rot);
+
+  return o;
+}
+
+void* triCreateGJKObject(const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, const Matrix3f& R, Vec3f const& T)
+{
+  ccd_triangle_t* o = new ccd_triangle_t;
+  Vec3f center((P1[0] + P2[0] + P3[0]) / 3, (P1[1] + P2[1] + P3[1]) / 3, (P1[2] + P2[2] + P3[2]) / 3);
+
+  ccdVec3Set(&o->p[0], P1[0], P1[1], P1[2]);
+  ccdVec3Set(&o->p[1], P2[0], P2[1], P2[2]);
+  ccdVec3Set(&o->p[2], P3[0], P3[1], P3[2]);
+  ccdVec3Set(&o->c, center[0], center[1], center[2]);
+  SimpleQuaternion q;
+  q.fromRotation(R);
+  ccdVec3Set(&o->pos, T[0], T[1], T[2]);
+  ccdQuatSet(&o->rot, q.getX(), q.getY(), q.getZ(), q.getW());
+  ccdQuatInvert2(&o->rot_inv, &o->rot);
+
+  return o;
+}
+
+void triDeleteGJKObject(void* o_)
+{
+  ccd_triangle_t* o = (ccd_triangle_t*)o_;
+  delete o;
+}
+
+} // details
+
+} // fcl
-- 
GitLab