diff --git a/collision_space_fcl_test/CMakeLists.txt b/collision_space_fcl_test/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a661bbcc1996c71f42871b665bbe3724f6207f51
--- /dev/null
+++ b/collision_space_fcl_test/CMakeLists.txt
@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 2.4.6)
+include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
+
+# Set the build type.  Options are:
+#  Coverage       : w/ debug symbols, w/o optimization, w/ code-coverage
+#  Debug          : w/ debug symbols, w/o optimization
+#  Release        : w/o debug symbols, w/ optimization
+#  RelWithDebInfo : w/ debug symbols, w/ optimization
+#  MinSizeRel     : w/o debug symbols, w/ optimization, stripped binaries
+#set(ROS_BUILD_TYPE RelWithDebInfo)
+
+rosbuild_init()
+
+#set the default path for built executables to the "bin" directory
+set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
+#set the default path for built libraries to the "lib" directory
+set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
+
+#uncomment if you have defined messages
+#rosbuild_genmsg()
+#uncomment if you have defined services
+#rosbuild_gensrv()
+
+#common commands for building c++ executables and libraries
+#rosbuild_add_library(${PROJECT_NAME} src/example.cpp)
+#target_link_libraries(${PROJECT_NAME} another_library)
+#rosbuild_add_boost_directories()
+#rosbuild_link_boost(${PROJECT_NAME} thread)
+#rosbuild_add_executable(example examples/example.cpp)
+#target_link_libraries(example ${PROJECT_NAME})
+
+rosbuild_add_gtest(test_collision_space_fcl test/test_collision_space_fcl.cpp)
+target_link_libraries(test_collision_space_fcl assimp)
diff --git a/collision_space_fcl_test/Makefile b/collision_space_fcl_test/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..b75b928f20ef9ea4f509a17db62e4e31b422c27f
--- /dev/null
+++ b/collision_space_fcl_test/Makefile
@@ -0,0 +1 @@
+include $(shell rospack find mk)/cmake.mk
\ No newline at end of file
diff --git a/collision_space_fcl_test/mainpage.dox b/collision_space_fcl_test/mainpage.dox
new file mode 100644
index 0000000000000000000000000000000000000000..b21790339305c7794f39d7cc90a5cb3964bbf2d6
--- /dev/null
+++ b/collision_space_fcl_test/mainpage.dox
@@ -0,0 +1,26 @@
+/**
+\mainpage
+\htmlinclude manifest.html
+
+\b collision_space_ccd_test is ... 
+
+<!-- 
+Provide an overview of your package.
+-->
+
+
+\section codeapi Code API
+
+<!--
+Provide links to specific auto-generated API documentation within your
+package that is of particular interest to a reader. Doxygen will
+document pretty much every part of your code, so do your best here to
+point the reader to the actual API.
+
+If your codebase is fairly large or has different sets of APIs, you
+should use the doxygen 'group' tag to keep these APIs together. For
+example, the roscpp documentation has 'libros' group.
+-->
+
+
+*/
diff --git a/collision_space_fcl_test/manifest.xml b/collision_space_fcl_test/manifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..46a558142eb5cf920a07abd318b877203818c0ec
--- /dev/null
+++ b/collision_space_fcl_test/manifest.xml
@@ -0,0 +1,24 @@
+<package>
+  <description brief="collision_space_ccd_test">
+
+     collision_space_ccd_test
+
+  </description>
+  <author>Sachin Chitta</author>
+  <license>BSD</license>
+  <review status="unreviewed" notes=""/>
+  <url>http://ros.org/wiki/collision_space_ccd_test</url>
+
+  <depend package="planning_models"/>
+  <depend package="collision_space_fcl"/>
+  <depend package="collision_space"/>
+  <depend package="tf"/>
+  <depend package="arm_navigation_msgs"/>
+
+  <platform os="ubuntu" version="9.04"/>
+  <platform os="ubuntu" version="9.10"/>
+  <platform os="ubuntu" version="10.04"/>
+
+</package>
+
+
diff --git a/collision_space_fcl_test/objects/meshes/9300.stl b/collision_space_fcl_test/objects/meshes/9300.stl
new file mode 100644
index 0000000000000000000000000000000000000000..7159c022ac7d8b620ffddf91aee16a8cc9f19b8e
Binary files /dev/null and b/collision_space_fcl_test/objects/meshes/9300.stl differ
diff --git a/collision_space_fcl_test/test/test_collision_space_fcl.cpp b/collision_space_fcl_test/test/test_collision_space_fcl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..95165cefccbfc96e16ff29a416dfc0ed8aa68b4a
--- /dev/null
+++ b/collision_space_fcl_test/test/test_collision_space_fcl.cpp
@@ -0,0 +1,758 @@
+/*********************************************************************
+* Software License Agreement (BSD License)
+* 
+*  Copyright (c) 2008, 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 the Willow Garage 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 E. Gil Jones */
+
+#include <planning_models/kinematic_model.h>
+#include <planning_models/kinematic_state.h>
+#include <gtest/gtest.h>
+#include <sstream>
+#include <ctype.h>
+#include <ros/package.h>
+
+#include <collision_space/environmentODE.h>
+#include <collision_space_fcl/environmentFCL.h>
+
+#include <tf/transform_datatypes.h>
+#include <arm_navigation_msgs/RobotState.h>
+
+#include <geometric_shapes/shape_operations.h>
+
+#define NUM_MESH_OBJECTS 10
+#define NUM_RANDOM_OBJECTS 100
+#define NUM_TEST_CONFIGURATIONS 1000
+
+//urdf location relative to the planning_models path
+static const std::string rel_path = "/test_urdf/robot.xml";
+
+inline double gen_rand(double min, double max)
+{
+  int rand_num = rand()%100+1;
+  double result = min + (double)((max-min)*rand_num)/101.0;
+  return result;
+}
+
+inline geometry_msgs::Quaternion generateRandomUnitQuaternion() {
+
+  geometry_msgs::Quaternion quat;
+  quat.x = gen_rand(-1.0, 1.0);
+  quat.w = gen_rand(-1.0, 1.0);
+  quat.z = gen_rand(-1.0, 1.0);
+  quat.w = gen_rand(-1.0, 1.0);
+
+  double mag = sqrt(pow(quat.x, 2.0)+pow(quat.y, 2.0)+pow(quat.z, 2.0)+pow(quat.w, 2.0));
+
+  quat.x /= mag;
+  quat.y /= mag;
+  quat.z /= mag;
+  quat.w /= mag; 
+
+  return quat;
+}
+
+inline std::string getNumberedString(const std::string &object, unsigned int num)
+{
+  std::stringstream my_string;
+  my_string <<  object << num;
+  return my_string.str();
+}
+
+class TestCollisionSpaceFCL : public testing::Test {
+public:
+
+  void spinThread() {
+    lock_.lock();
+    coll_space_->isCollision();
+    lock_.unlock();
+  }
+
+protected:
+  
+  virtual void SetUp() {
+
+    full_path_ = ros::package::getPath("planning_models")+rel_path;
+    
+    urdf_ok_ = urdf_model_.initFile(full_path_);
+
+    std::vector<planning_models::KinematicModel::MultiDofConfig> multi_dof_configs;
+    //now this should work with an identity transform
+    planning_models::KinematicModel::MultiDofConfig config("base_joint");
+    config.type = "Planar";
+    config.parent_frame_id = "base_footprint";
+    config.child_frame_id = "base_footprint";
+    multi_dof_configs.push_back(config);
+
+    std::vector<planning_models::KinematicModel::GroupConfig> gcs;
+    planning_models::KinematicModel::GroupConfig left_arm("left_arm",
+                                                          "torso_lift_link",
+                                                          "l_wrist_roll_link");
+
+    planning_models::KinematicModel::GroupConfig right_arm("right_arm",
+                                                           "torso_lift_link",
+                                                           "r_wrist_roll_link");
+    gcs.push_back(left_arm);
+    gcs.push_back(right_arm);
+    kinematic_model_ = new planning_models::KinematicModel(urdf_model_,
+                                                           gcs,
+                                                           multi_dof_configs);
+    coll_space_ode_ = new collision_space::EnvironmentModelODE();
+    coll_space_ = new collision_space_fcl::EnvironmentModelFCL();
+
+    workspace_x_extents_ = 1.0;
+    workspace_y_extents_ = 1.0;
+    workspace_z_extents_ = 1.0;
+    ros::Time::init();
+  };
+
+  virtual void TearDown() {
+    delete kinematic_model_;
+    //    delete coll_space_;
+    //    delete coll_space_ode_;
+  }
+
+  void getJointBoundsMap(const std::string &group, std::map<std::string, std::pair<double, double> > &joint_bounds_map)
+  {
+    const planning_models::KinematicModel::JointModelGroup* joint_model_group = kinematic_model_->getModelGroup(group);
+    if(joint_model_group == NULL) {
+      ROS_WARN_STREAM("No joint group " << group);
+      return;
+    }
+
+    for(unsigned int i = 0; i < joint_model_group->getJointModels().size(); i++) {
+      const planning_models::KinematicModel::JointModel* jm = joint_model_group->getJointModels()[i];
+      std::pair<double, double> bounds;
+      jm->getVariableBounds(jm->getName(), bounds);
+      joint_bounds_map[jm->getName()] = bounds;
+    }
+  }
+
+  void setupForRandomConfigurations()
+  {
+    srand(time(NULL));
+    std::vector<std::string> links;
+    kinematic_model_->getLinkModelNames(links);
+    std::map<std::string, double> link_padding_map;
+    
+    collision_space_fcl::EnvironmentModel::AllowedCollisionMatrix acm(links, false);
+    collision_space::EnvironmentModel::AllowedCollisionMatrix acm_ode(links, false);
+    
+    coll_space_->setRobotModel(kinematic_model_, acm, link_padding_map);  
+    coll_space_ode_->setRobotModel(kinematic_model_, acm_ode, link_padding_map);  
+    
+    {
+      //indented cause the state needs to cease to exist before we add the attached body
+      planning_models::KinematicState state(kinematic_model_);
+      state.setKinematicStateToDefault();
+      
+      coll_space_->updateRobotModel(&state);
+      coll_space_ode_->updateRobotModel(&state);
+    }
+    
+    ASSERT_FALSE(coll_space_->isEnvironmentCollision());
+    ASSERT_FALSE(coll_space_ode_->isEnvironmentCollision());    
+  }
+
+  void testForRandomConfigurations()
+  {
+    getJointBoundsMap("right_arm",joint_bounds_map_right_);
+    getJointBoundsMap("left_arm",joint_bounds_map_left_);
+
+    std::vector<arm_navigation_msgs::RobotState> robot_states_right;
+    std::vector<arm_navigation_msgs::RobotState> robot_states_left;
+    // Now generate random robot states and test them for collisions
+    for(unsigned int i=0; i < NUM_TEST_CONFIGURATIONS; i++)
+    {
+      arm_navigation_msgs::RobotState robot_state_right = generateRandomRobotStateWithinLimits(joint_bounds_map_right_);
+      arm_navigation_msgs::RobotState robot_state_left = generateRandomRobotStateWithinLimits(joint_bounds_map_left_);
+      robot_states_right.push_back(robot_state_right);
+      robot_states_left.push_back(robot_state_left);
+    }
+
+    std::vector<bool> is_collision, is_collision_ode;
+    is_collision.resize(robot_states_right.size());
+    is_collision_ode.resize(robot_states_right.size());
+
+    {
+      //indented cause the state needs to cease to exist before we add the attached body
+      planning_models::KinematicState state(kinematic_model_);
+      state.setKinematicStateToDefault();
+      planning_models::KinematicState::JointStateGroup *right_state = state.getJointStateGroup("right_arm");
+      planning_models::KinematicState::JointStateGroup *left_state = state.getJointStateGroup("left_arm");
+
+      ros::Time start_time = ros::Time::now();
+      for(unsigned int i=0; i < robot_states_right.size(); i++)
+      {
+        right_state->setKinematicState(robot_states_right[i].joint_state.position);  
+        left_state->setKinematicState(robot_states_left[i].joint_state.position);  
+        coll_space_->updateRobotModel(&state);
+        is_collision[i] = coll_space_->isEnvironmentCollision();
+      }
+      ros::Duration duration = ros::Time::now()-start_time;
+      ROS_INFO("FCL: Collision time: %f",duration.toSec());
+
+      start_time = ros::Time::now();
+      for(unsigned int i=0; i < robot_states_right.size(); i++)
+      {
+        right_state->setKinematicState(robot_states_right[i].joint_state.position);  
+        left_state->setKinematicState(robot_states_left[i].joint_state.position);  
+        coll_space_ode_->updateRobotModel(&state);
+        is_collision_ode[i] = coll_space_ode_->isEnvironmentCollision();
+      }
+      duration = ros::Time::now()-start_time;
+      ROS_INFO("ODE: Collision time: %f",duration.toSec());
+
+      unsigned int counter = 0;    
+      for(unsigned int i=0; i < robot_states_right.size(); i++)
+        if(is_collision_ode[i] != is_collision[i])
+          counter++;
+      ROS_DEBUG("%d mismatches between ODE and FCL result (out of %d)",(int) counter,(int) robot_states_right.size());
+
+      ASSERT_TRUE(counter == 0);
+    }
+  }
+
+  arm_navigation_msgs::RobotState generateRandomRobotStateWithinLimits(std::map<std::string, std::pair<double, double> > &joint_bounds_map) 
+  {    
+    arm_navigation_msgs::RobotState rs;
+    rs.joint_state.header.frame_id = "torso_lift_link";
+    rs.joint_state.header.stamp = ros::Time::now();
+
+    for(std::map<std::string,std::pair<double, double> >::iterator it = joint_bounds_map.begin();
+        it != joint_bounds_map.end();
+        it++) {
+      rs.joint_state.name.push_back(it->first);
+      rs.joint_state.position.push_back(gen_rand(it->second.first, it->second.second));
+    }
+    return rs;
+  }
+
+  geometry_msgs::Pose generateRandomPoseInWorkspace() {
+    
+    geometry_msgs::Pose rp;
+    rp.position.x = gen_rand(-workspace_x_extents_, workspace_x_extents_);
+    rp.position.y = gen_rand(-workspace_y_extents_, workspace_y_extents_);
+    rp.position.z = gen_rand(-workspace_z_extents_, workspace_z_extents_);
+
+    rp.orientation = generateRandomUnitQuaternion();
+
+    return rp;
+    
+  }
+
+
+protected:
+
+  boost::mutex lock_;
+  urdf::Model urdf_model_;
+  bool urdf_ok_;
+  std::string full_path_;
+  collision_space::EnvironmentModelODE* coll_space_ode_;
+  collision_space_fcl::EnvironmentModelFCL* coll_space_;
+  planning_models::KinematicModel* kinematic_model_;
+  std::map<std::string, std::pair<double, double> > joint_bounds_map_right_, joint_bounds_map_left_;
+  double workspace_x_extents_, workspace_y_extents_, workspace_z_extents_;
+};
+
+
+
+TEST_F(TestCollisionSpaceFCL, TestInit) {
+  std::vector<std::string> links;
+  kinematic_model_->getLinkModelNames(links);
+  std::map<std::string, double> link_padding_map;
+
+  {
+    collision_space_fcl::EnvironmentModel::AllowedCollisionMatrix acm(links,true);
+    coll_space_->setRobotModel(kinematic_model_, acm, link_padding_map);
+    //all AllowedCollisions set to true, so no collision
+    ASSERT_FALSE(coll_space_->isCollision());
+  }
+
+  {
+    collision_space_fcl::EnvironmentModel::AllowedCollisionMatrix acm(links,false);
+
+    coll_space_->setRobotModel(kinematic_model_, acm, link_padding_map);
+  
+    //now we are in collision with nothing disabled
+    ASSERT_TRUE(coll_space_->isCollision());
+  }
+
+  //one more time for good measure
+  {
+    collision_space_fcl::EnvironmentModel::AllowedCollisionMatrix acm(links,false);
+
+    coll_space_->setRobotModel(kinematic_model_, acm, link_padding_map);
+  
+    //now we are in collision with nothing disabled
+    ASSERT_TRUE(coll_space_->isCollision());
+  }
+}
+
+
+TEST_F(TestCollisionSpaceFCL, TestACM) {
+  std::vector<std::string> links;
+  kinematic_model_->getLinkModelNames(links);
+  std::map<std::string, double> link_padding_map;
+  
+  //first we get
+  {
+    collision_space_fcl::EnvironmentModel::AllowedCollisionMatrix acm(links, false);
+    coll_space_->setRobotModel(kinematic_model_, acm, link_padding_map);
+
+    planning_models::KinematicState state(kinematic_model_);
+    state.setKinematicStateToDefault();
+    coll_space_->updateRobotModel(&state);
+
+    //at default state in collision
+    ASSERT_TRUE(coll_space_->isCollision());
+
+    //now we get the full set of collisions in the default state
+    std::vector<collision_space_fcl::EnvironmentModel::AllowedContact> ac;
+    std::vector<collision_space_fcl::EnvironmentModel::Contact> contacts;
+      
+    coll_space_->getAllCollisionContacts(ac, contacts, 1);
+
+    ASSERT_TRUE(contacts.size() > 1);
+    //now we change all these pairwise to true
+    for(unsigned int i = 0; i < contacts.size(); i++) {
+      ASSERT_TRUE(contacts[i].body_type_1 == collision_space_fcl::EnvironmentModel::LINK);
+      ASSERT_TRUE(contacts[i].body_type_2 == collision_space_fcl::EnvironmentModel::LINK);
+      ASSERT_TRUE(acm.changeEntry(contacts[i].body_name_1,contacts[i].body_name_2, true));
+    }
+
+    coll_space_->setAlteredCollisionMatrix(acm);
+    
+    //with all of these disabled, no more collisions
+    ASSERT_FALSE(coll_space_->isCollision());
+
+    coll_space_->revertAlteredCollisionMatrix();
+    ASSERT_TRUE(coll_space_->isCollision());
+  }
+}
+
+TEST_F(TestCollisionSpaceFCL, TestAttachedObjects)
+{
+  std::vector<std::string> links;
+  kinematic_model_->getLinkModelNames(links);
+  std::map<std::string, double> link_padding_map;
+  
+  collision_space_fcl::EnvironmentModel::AllowedCollisionMatrix acm(links, false);
+  coll_space_->setRobotModel(kinematic_model_, acm, link_padding_map);  
+
+  {
+    //indented cause the state needs to cease to exist before we add the attached body
+    planning_models::KinematicState state(kinematic_model_);
+    state.setKinematicStateToDefault();
+    
+    coll_space_->updateRobotModel(&state);
+
+    //now we get the full set of collisions in the default state
+    std::vector<collision_space_fcl::EnvironmentModel::AllowedContact> ac;
+    std::vector<collision_space_fcl::EnvironmentModel::Contact> contacts;
+      
+    coll_space_->getAllCollisionContacts(ac, contacts, 1);
+
+    //now we change all these pairwise to true
+    for(unsigned int i = 0; i < contacts.size(); i++) {
+      ASSERT_TRUE(contacts[i].body_type_1 == collision_space_fcl::EnvironmentModel::LINK);
+      ASSERT_TRUE(contacts[i].body_type_2 == collision_space_fcl::EnvironmentModel::LINK);
+      ASSERT_TRUE(acm.changeEntry(contacts[i].body_name_1,contacts[i].body_name_2, true));
+    }
+
+    coll_space_->setRobotModel(kinematic_model_, acm, link_padding_map);
+    coll_space_->updateRobotModel(&state);
+  }
+  
+  //now we shouldn't be in collision
+  ASSERT_FALSE(coll_space_->isCollision());  
+
+  const planning_models::KinematicModel::LinkModel *link = kinematic_model_->getLinkModel("base_link");
+
+  //first a single box
+  shapes::Sphere* sphere1 = new shapes::Sphere();
+  sphere1->radius = .1;
+
+  shapes::Box* box2 = new shapes::Box();
+  box2->size[0] = .05;
+  box2->size[1] = .05;
+  box2->size[2] = .05;
+  
+  std::vector<shapes::Shape*> shape_vector;
+  shape_vector.push_back(sphere1);
+
+  btTransform pose;
+  pose.setIdentity();
+
+  std::vector<btTransform> poses;
+  poses.push_back(pose);
+  
+  std::vector<std::string> touch_links;
+
+  planning_models::KinematicModel::AttachedBodyModel* ab1 = 
+    new planning_models::KinematicModel::AttachedBodyModel(link, "box_1",
+                                                           poses,
+                                                           touch_links,
+                                                           shape_vector);
+
+  kinematic_model_->addAttachedBodyModel(link->getName(), ab1);
+  coll_space_->updateAttachedBodies();
+
+  const collision_space_fcl::EnvironmentModel::AllowedCollisionMatrix& aft_attached
+    = coll_space_->getDefaultAllowedCollisionMatrix();
+  
+  ASSERT_TRUE(aft_attached.hasEntry("box_1"));
+  bool allowed;
+  EXPECT_TRUE(aft_attached.getAllowedCollision("box_1", link->getName(), allowed));
+  EXPECT_FALSE(allowed);
+
+  {
+    //indented cause the state needs to cease to exist before we add the attached body
+    planning_models::KinematicState state(kinematic_model_);
+    state.setKinematicStateToDefault();
+    
+    coll_space_->updateRobotModel(&state);
+  }
+
+  //now it collides
+  ASSERT_TRUE(coll_space_->isCollision());  
+
+  kinematic_model_->clearLinkAttachedBodyModel(link->getName(), "box_1");
+  coll_space_->updateAttachedBodies();
+
+  ASSERT_FALSE(aft_attached.hasEntry("box_1"));
+
+  //now adding an attached object with two boxes, this time with two objects
+  shape_vector.clear();
+  shape_vector.push_back(box2);
+  pose.getOrigin().setX(.1);
+  poses.clear();
+  poses.push_back(pose);
+  touch_links.push_back("r_gripper_palm_link");
+  touch_links.push_back("r_gripper_r_finger_link");
+  touch_links.push_back("r_gripper_l_finger_link");
+  touch_links.push_back("r_gripper_r_finger_tip_link");
+  touch_links.push_back("r_gripper_l_finger_tip_link");
+  touch_links.push_back("base_link");
+  
+  planning_models::KinematicModel::AttachedBodyModel* ab2 = 
+    new planning_models::KinematicModel::AttachedBodyModel(link, "box_2",
+                                                           poses,
+                                                           touch_links,
+                                                           shape_vector);
+  kinematic_model_->addAttachedBodyModel(link->getName(), ab2);
+  coll_space_->updateAttachedBodies();
+
+  ASSERT_TRUE(aft_attached.hasEntry("box_2"));
+  EXPECT_TRUE(aft_attached.getAllowedCollision("box_2", link->getName(), allowed));
+  EXPECT_TRUE(allowed);
+
+  {
+    //indented cause the state needs to cease to exist before we add the attached body
+    planning_models::KinematicState state(kinematic_model_);
+    state.setKinematicStateToDefault();
+    
+    coll_space_->updateRobotModel(&state);
+  }
+
+  //now it doesn't collide
+  ASSERT_FALSE(coll_space_->isCollision());  
+}
+
+TEST_F(TestCollisionSpaceFCL, TestStaticObjects)
+{
+  std::vector<std::string> links;
+  kinematic_model_->getLinkModelNames(links);
+  std::map<std::string, double> link_padding_map;
+  
+  collision_space_fcl::EnvironmentModel::AllowedCollisionMatrix acm(links, false);
+  coll_space_->setRobotModel(kinematic_model_, acm, link_padding_map);  
+  
+  {
+    //indented cause the state needs to cease to exist before we add the attached body
+    planning_models::KinematicState state(kinematic_model_);
+    state.setKinematicStateToDefault();
+    
+    coll_space_->updateRobotModel(&state);
+  }
+
+  ASSERT_FALSE(coll_space_->isEnvironmentCollision());
+
+  shapes::Sphere* sphere1 = new shapes::Sphere();
+  sphere1->radius = .2;
+
+  btTransform pose;
+  pose.setIdentity();
+
+  std::vector<btTransform> poses;
+  poses.push_back(pose);
+
+  std::vector<shapes::Shape*> shape_vector;
+  shape_vector.push_back(sphere1);
+
+  coll_space_->addObjects("obj1", shape_vector, poses);
+
+
+  ASSERT_TRUE(coll_space_->isEnvironmentCollision());
+
+
+
+  //Now test interactions between static and attached objects
+
+  const planning_models::KinematicModel::LinkModel *link = kinematic_model_->getLinkModel("base_link");
+
+  shapes::Box* att_box = new shapes::Box();
+  att_box->size[0] = .05;
+  att_box->size[1] = .05;
+  att_box->size[2] = .05;
+  
+  std::vector<shapes::Shape*> att_shapes;
+  att_shapes.push_back(att_box);
+
+  btTransform att_pose;
+  att_pose.setIdentity();
+
+  std::vector<btTransform> att_poses;
+  att_poses.push_back(att_pose);
+  
+  std::vector<std::string> touch_links;
+  touch_links.push_back("base_link");
+  touch_links.push_back("base_footprint");
+
+  planning_models::KinematicModel::AttachedBodyModel* ab1 = 
+    new planning_models::KinematicModel::AttachedBodyModel(link, "att1",
+                                                           att_poses,
+                                                           touch_links,
+                                                           att_shapes);
+
+  kinematic_model_->addAttachedBodyModel(link->getName(), ab1);
+  coll_space_->updateAttachedBodies();
+
+  {
+    //indented cause the state needs to cease to exist before we add the attached body
+    planning_models::KinematicState state(kinematic_model_);
+    state.setKinematicStateToDefault();
+    
+    coll_space_->updateRobotModel(&state);
+  }
+
+  ASSERT_TRUE(coll_space_->isEnvironmentCollision());
+
+  //now we get the full set of collisions in the default state
+  std::vector<collision_space_fcl::EnvironmentModel::AllowedContact> ac;
+  std::vector<collision_space_fcl::EnvironmentModel::Contact> contacts;
+  
+  coll_space_->getAllCollisionContacts(ac, contacts, 1);
+
+  //now we change all these pairwise to true
+  for(unsigned int i = 0; i < contacts.size(); i++) {
+    if(contacts[i].body_type_1 == collision_space_fcl::EnvironmentModel::OBJECT) {
+      ASSERT_TRUE(contacts[i].body_name_1 == "obj1");
+    }
+    if(contacts[i].body_type_2 == collision_space_fcl::EnvironmentModel::OBJECT) {
+      ASSERT_TRUE(contacts[i].body_name_2 == "obj1");
+    }
+  }
+
+  acm = coll_space_->getDefaultAllowedCollisionMatrix();
+  bool allowed;
+  ASSERT_TRUE(acm.getAllowedCollision("obj1","att1",allowed));
+  EXPECT_FALSE(allowed);
+  
+  ASSERT_TRUE(acm.changeEntry(link->getName(), "obj1", true));
+  ASSERT_TRUE(acm.changeEntry("base_footprint", "obj1", true));
+  coll_space_->setAlteredCollisionMatrix(acm);
+
+  {
+    //indented cause the state needs to cease to exist before we add the attached body
+    planning_models::KinematicState state(kinematic_model_);
+    state.setKinematicStateToDefault();
+    
+    coll_space_->updateRobotModel(&state);
+  }
+
+  /* Remove because sphere-box collision does not work for mesh */
+  // EXPECT_TRUE(coll_space_->isEnvironmentCollision());
+  
+  ASSERT_TRUE(acm.changeEntry("att1", "obj1", true));
+  coll_space_->setAlteredCollisionMatrix(acm);
+  
+  allowed = false;
+  ASSERT_TRUE(coll_space_->getCurrentAllowedCollisionMatrix().getAllowedCollision("att1","obj1", allowed));
+  EXPECT_TRUE(allowed);
+
+  ASSERT_TRUE(coll_space_->getCurrentAllowedCollisionMatrix().getAllowedCollision("obj1","att1", allowed));
+  EXPECT_TRUE(allowed);
+
+  ASSERT_TRUE(coll_space_->getCurrentAllowedCollisionMatrix().getAllowedCollision("base_link","obj1", allowed));
+  EXPECT_TRUE(allowed);
+
+  ASSERT_TRUE(coll_space_->getCurrentAllowedCollisionMatrix().getAllowedCollision("obj1","base_link", allowed));
+  EXPECT_TRUE(allowed);
+
+  ASSERT_TRUE(coll_space_->getCurrentAllowedCollisionMatrix().getAllowedCollision("obj1","base_footprint", allowed));
+  EXPECT_TRUE(allowed);
+  
+  EXPECT_FALSE(coll_space_->isEnvironmentCollision());
+  contacts.clear();
+
+  coll_space_->getAllCollisionContacts(ac, contacts, 1);
+
+  //now we change all these pairwise to true
+  for(unsigned int i = 0; i < contacts.size(); i++) {
+    if(contacts[i].body_type_1 == collision_space_fcl::EnvironmentModel::OBJECT) {
+      ASSERT_TRUE(contacts[i].body_name_1 == "obj1");
+      ROS_INFO_STREAM(contacts[i].body_name_2);
+    }
+    if(contacts[i].body_type_2 == collision_space_fcl::EnvironmentModel::OBJECT) {
+      ASSERT_TRUE(contacts[i].body_name_2 == "obj1");
+      ROS_INFO_STREAM(contacts[i].body_name_1);
+    }
+  }
+}
+
+TEST_F(TestCollisionSpaceFCL, TestODEvsFCLSphere)
+{
+  setupForRandomConfigurations();
+  for(unsigned int i=0; i < NUM_RANDOM_OBJECTS; i++)
+  {
+    btTransform pose;
+    shapes::Sphere* sphere1 = new shapes::Sphere();
+    sphere1->radius = .1;    
+
+    geometry_msgs::Pose pose_msg = generateRandomPoseInWorkspace();
+    tf::poseMsgToTF(pose_msg,pose);
+
+    std::vector<btTransform> poses;
+    poses.push_back(pose);    
+
+    std::vector<shapes::Shape*> shape_vector;
+    shape_vector.push_back(sphere1);
+
+    std::string object_id = getNumberedString("obj",i);
+
+    coll_space_->addObjects(object_id, shape_vector, poses);
+    coll_space_ode_->addObjects(object_id, shape_vector, poses);
+  }
+  testForRandomConfigurations();
+}
+
+TEST_F(TestCollisionSpaceFCL, TestODEvsFCLCylinder)
+{
+  setupForRandomConfigurations();
+  for(unsigned int i=0; i < NUM_RANDOM_OBJECTS; i++)
+  {
+    btTransform pose;
+    shapes::Cylinder* c1 = new shapes::Cylinder();
+    c1->radius = .1;    
+    c1->length = 0.3;
+
+    geometry_msgs::Pose pose_msg = generateRandomPoseInWorkspace();
+    tf::poseMsgToTF(pose_msg,pose);
+
+    std::vector<btTransform> poses;
+    poses.push_back(pose);    
+
+    std::vector<shapes::Shape*> shape_vector;
+    shape_vector.push_back(c1);
+
+    std::string object_id = getNumberedString("obj",i);
+
+    coll_space_->addObjects(object_id, shape_vector, poses);
+    coll_space_ode_->addObjects(object_id, shape_vector, poses);
+  }
+  testForRandomConfigurations();
+}
+
+TEST_F(TestCollisionSpaceFCL, TestODEvsFCLBox)
+{
+  setupForRandomConfigurations();
+  for(unsigned int i=0; i < NUM_RANDOM_OBJECTS; i++)
+  {
+    btTransform pose;
+    shapes::Box* box = new shapes::Box();
+    box->size[0] = 0.1;    
+    box->size[1] = 0.2;
+    box->size[2] = 0.3;
+
+    geometry_msgs::Pose pose_msg = generateRandomPoseInWorkspace();
+    tf::poseMsgToTF(pose_msg,pose);
+
+    std::vector<btTransform> poses;
+    poses.push_back(pose);    
+
+    std::vector<shapes::Shape*> shape_vector;
+    shape_vector.push_back(box);
+
+    std::string object_id = getNumberedString("obj",i);
+
+    coll_space_->addObjects(object_id, shape_vector, poses);
+    coll_space_ode_->addObjects(object_id, shape_vector, poses);
+  }
+  testForRandomConfigurations();
+}
+
+TEST_F(TestCollisionSpaceFCL, TestODEvsFCLMesh)
+{
+  setupForRandomConfigurations();
+  std::string full_path = ros::package::getPath("collision_space_fcl_test")+"/objects/meshes/9300.stl";
+  for(unsigned int i=0; i < NUM_MESH_OBJECTS; i++)
+  {
+    shapes::Mesh* mesh = shapes::createMeshFromBinaryStl(full_path.c_str());
+    ASSERT_TRUE(mesh);
+    btTransform pose;
+    geometry_msgs::Pose pose_msg = generateRandomPoseInWorkspace();
+    tf::poseMsgToTF(pose_msg,pose);
+
+    std::vector<btTransform> poses;
+    poses.push_back(pose);    
+
+    std::vector<shapes::Shape*> shape_vector;
+    shape_vector.push_back(mesh);
+
+    std::string object_id = getNumberedString("obj",i);
+
+    coll_space_->addObjects(object_id, shape_vector, poses);
+    coll_space_ode_->addObjects(object_id, shape_vector, poses);
+    delete mesh;
+  }
+  testForRandomConfigurations();
+  //  coll_space_->clearObjects();
+  //  coll_space_ode_->clearObjects();
+}
+
+int main(int argc, char **argv)
+{
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
+
diff --git a/fcl/src/BVH_utility.cpp b/fcl/src/BVH_utility.cpp
index 7381001a214cb1ba527cba3360267bd03bdc92f3..39bf9c47d1525810a2625e3b752765272d46d409 100644
--- a/fcl/src/BVH_utility.cpp
+++ b/fcl/src/BVH_utility.cpp
@@ -36,7 +36,7 @@
 
 
 #include "fcl/BVH_utility.h"
-#include <ANN/ANN.h>
+#include <ann/ANN.h>
 
 namespace fcl
 {
diff --git a/fcl/test/test_core_broad_phase.cpp b/fcl/test/test_core_broad_phase.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..64b585aa38fbb67a48fc8a09a0682da30f7c1e4f
--- /dev/null
+++ b/fcl/test/test_core_broad_phase.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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/broad_phase_collision.h"
+#include "fcl/geometric_shape_to_BVH_model.h"
+#include "fcl/transform.h"
+#include "test_core_utility.h"
+#include <gtest/gtest.h>
+
+using namespace fcl;
+
+
+void generateEnvironments(std::vector<CollisionObject*>& env, int n);
+
+
+TEST(test_core, broad_phase_collision)
+{
+  bool exhaustive = false;
+
+  std::vector<CollisionObject*> env;
+  generateEnvironments(env, 100);
+
+  std::vector<CollisionObject*> query;
+  generateEnvironments(query, 10);
+
+  BroadPhaseCollisionManager* manager1 = new NaiveCollisionManager();
+  BroadPhaseCollisionManager* manager2 = new SSaPCollisionManager();
+  BroadPhaseCollisionManager* manager3 = new SaPCollisionManager();
+  BroadPhaseCollisionManager* manager4 = new IntervalTreeCollisionManager();
+
+
+  for(unsigned int i = 0; i < env.size(); ++i)
+  {
+    manager1->registerObject(env[i]);
+    manager2->registerObject(env[i]);
+    manager3->registerObject(env[i]);
+    manager4->registerObject(env[i]);
+  }
+
+  manager1->setup();
+  manager2->setup();
+  manager3->setup();
+  manager4->setup();
+
+  CollisionData self_data1;
+  self_data1.exhaustive = exhaustive;
+  CollisionData self_data2;
+  self_data2.exhaustive = exhaustive;
+  CollisionData self_data3;
+  self_data3.exhaustive = exhaustive;
+  CollisionData self_data4;
+  self_data4.exhaustive = exhaustive;
+
+  manager1->collide(&self_data1, defaultCollisionFunction);
+  manager2->collide(&self_data2, defaultCollisionFunction);
+  manager3->collide(&self_data3, defaultCollisionFunction);
+  manager3->collide(&self_data4, defaultCollisionFunction);
+
+  bool self_res1 = (self_data1.contacts.size() > 0);
+  bool self_res2 = (self_data2.contacts.size() > 0);
+  bool self_res3 = (self_data3.contacts.size() > 0);
+  bool self_res4 = (self_data4.contacts.size() > 0);
+
+  ASSERT_TRUE(self_res1 == self_res2);
+  ASSERT_TRUE(self_res1 == self_res3);
+  ASSERT_TRUE(self_res1 == self_res4);
+
+
+  for(unsigned int i = 0; i < query.size(); ++i)
+  {
+    CollisionData query_data1;
+    query_data1.exhaustive = exhaustive;
+    CollisionData query_data2;
+    query_data2.exhaustive = exhaustive;
+    CollisionData query_data3;
+    query_data3.exhaustive = exhaustive;
+    CollisionData query_data4;
+    query_data4.exhaustive = exhaustive;
+
+    manager1->collide(query[i], &query_data1, defaultCollisionFunction);
+    manager2->collide(query[i], &query_data2, defaultCollisionFunction);
+    manager3->collide(query[i], &query_data3, defaultCollisionFunction);
+    manager4->collide(query[i], &query_data4, defaultCollisionFunction);
+
+    bool query_res1 = (query_data1.contacts.size() > 0);
+    bool query_res2 = (query_data2.contacts.size() > 0);
+    bool query_res3 = (query_data3.contacts.size() > 0);
+    bool query_res4 = (query_data4.contacts.size() > 0);
+
+    ASSERT_TRUE(query_res1 == query_res2);
+    ASSERT_TRUE(query_res1 == query_res3);
+    ASSERT_TRUE(query_res1 == query_res4);
+  }
+
+  for(unsigned int i = 0; i < env.size(); ++i)
+    delete env[i];
+  for(unsigned int i = 0; i < query.size(); ++i)
+    delete query[i];
+
+  delete manager1;
+  delete manager2;
+  delete manager3;
+  delete manager4;
+}
+
+
+
+TEST(test_core, broad_phase_self_collision_exhaustive)
+{
+  bool exhaustive = true;
+
+  std::vector<CollisionObject*> env;
+  generateEnvironments(env, 100);
+
+  BroadPhaseCollisionManager* manager1 = new NaiveCollisionManager();
+  BroadPhaseCollisionManager* manager2 = new SSaPCollisionManager();
+  BroadPhaseCollisionManager* manager3 = new SaPCollisionManager();
+  BroadPhaseCollisionManager* manager4 = new IntervalTreeCollisionManager();
+
+
+  for(unsigned int i = 0; i < env.size(); ++i)
+  {
+    manager1->registerObject(env[i]);
+    manager2->registerObject(env[i]);
+    manager3->registerObject(env[i]);
+    manager4->registerObject(env[i]);
+  }
+
+  manager1->setup();
+  manager2->setup();
+  manager3->setup();
+  manager4->setup();
+
+  CollisionData self_data1;
+  self_data1.exhaustive = exhaustive;
+  CollisionData self_data2;
+  self_data2.exhaustive = exhaustive;
+  CollisionData self_data3;
+  self_data3.exhaustive = exhaustive;
+  CollisionData self_data4;
+  self_data4.exhaustive = exhaustive;
+
+  manager1->collide(&self_data1, defaultCollisionFunction);
+  manager2->collide(&self_data2, defaultCollisionFunction);
+  manager3->collide(&self_data3, defaultCollisionFunction);
+  manager3->collide(&self_data4, defaultCollisionFunction);
+
+  unsigned int self_res1 = self_data1.contacts.size();
+  unsigned int self_res2 = self_data2.contacts.size();
+  unsigned int self_res3 = self_data3.contacts.size();
+  unsigned int self_res4 = self_data4.contacts.size();
+
+  ASSERT_TRUE(self_res1 == self_res2);
+  ASSERT_TRUE(self_res1 == self_res3);
+  ASSERT_TRUE(self_res1 == self_res4);
+
+
+  for(unsigned int i = 0; i < env.size(); ++i)
+    delete env[i];
+
+  delete manager1;
+  delete manager2;
+  delete manager3;
+  delete manager4;
+}
+
+
+int main(int argc, char **argv)
+{
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
+
+
+void generateEnvironments(std::vector<CollisionObject*>& env, int n)
+{
+  BVH_REAL extents[] = {-200, 200, -200, 200, -200, 200};
+  BVH_REAL delta_trans[] = {1, 1, 1};
+  std::vector<Transform> transforms;
+  std::vector<Transform> transforms2;
+
+
+  generateRandomTransform(extents, transforms, transforms2, delta_trans, 0.005 * 2 * 3.1415, n);
+  Box box(5, 10, 20);
+  for(int i = 0; i < n; ++i)
+  {
+    BVHModel<OBB>* model = new BVHModel<OBB>();
+    box.setLocalTransform(transforms[i].R, transforms[i].T);
+    generateBVHModel(*model, box);
+    env.push_back(model);
+  }
+
+  generateRandomTransform(extents, transforms, transforms2, delta_trans, 0.005 * 2 * 3.1415, n);
+  Sphere sphere(30);
+  for(int i = 0; i < n; ++i)
+  {
+    BVHModel<OBB>* model = new BVHModel<OBB>();
+    sphere.setLocalTransform(transforms[i].R, transforms[i].T);
+    generateBVHModel(*model, sphere);
+    env.push_back(model);
+  }
+
+  generateRandomTransform(extents, transforms, transforms2, delta_trans, 0.005 * 2 * 3.1415, n);
+  Cylinder cylinder(10, 40);
+  for(int i = 0; i < n; ++i)
+  {
+    BVHModel<OBB>* model = new BVHModel<OBB>();
+    cylinder.setLocalTransform(transforms[i].R, transforms[i].T);
+    generateBVHModel(*model, cylinder);
+    env.push_back(model);
+  }
+}
diff --git a/fcl/test/test_core_collision_point.cpp b/fcl/test/test_core_collision_point.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fe0a988fe87d46164ea1fbed26265c6b01d2d9b1
--- /dev/null
+++ b/fcl/test/test_core_collision_point.cpp
@@ -0,0 +1,540 @@
+/*
+ * 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 */
+
+
+#if USE_SVMLIGHT
+
+#include "fcl/traversal_node_bvhs.h"
+#include "fcl/collision_node.h"
+#include "fcl/simple_setup.h"
+#include "test_core_utility.h"
+
+#if USE_PQP
+#include <PQP.h>
+#endif
+
+using namespace fcl;
+
+template<typename BV>
+bool collide_Test_PP(const Transform& tf,
+                     const std::vector<Vec3f>& vertices1, const std::vector<Vec3f>& vertices2, SplitMethodType split_method, bool verbose = true);
+
+template<typename BV>
+bool collide_Test_MP(const Transform& tf,
+                     const std::vector<Vec3f>& vertices1, const std::vector<Triangle>& triangles1,
+                     const std::vector<Vec3f>& vertices2, SplitMethodType split_method, bool verbose = true);
+template<typename BV>
+bool collide_Test_PM(const Transform& tf,
+                     const std::vector<Vec3f>& vertices1,
+                     const std::vector<Vec3f>& vertices2, const std::vector<Triangle>& triangles2, SplitMethodType split_method, bool verbose = true);
+
+bool collide_Test_PP_OBB(const Transform& tf,
+                         const std::vector<Vec3f>& vertices1, const std::vector<Vec3f>& vertices2, SplitMethodType split_method, bool verbose = true);
+
+bool collide_Test_MP_OBB(const Transform& tf,
+                         const std::vector<Vec3f>& vertices1, const std::vector<Triangle>& triangles1,
+                         const std::vector<Vec3f>& vertices2, SplitMethodType split_method, bool verbose = true);
+
+bool collide_Test_PM_OBB(const Transform& tf,
+                         const std::vector<Vec3f>& vertices1,
+                         const std::vector<Vec3f>& vertices2, const std::vector<Triangle>& triangles2, SplitMethodType split_method, bool verbose = true);
+
+int num_max_contacts = 1;
+
+int main()
+{
+  std::vector<Vec3f> p1, p2;
+  std::vector<Triangle> t1, t2;
+  loadOBJFile("env.obj", p1, t1);
+  loadOBJFile("rob.obj", p2, t2);
+
+  std::vector<Transform> transforms; // t0
+  std::vector<Transform> transforms2; // t1
+  std::vector<Transform> transforms_ccd; // t0
+  std::vector<Transform> transforms_ccd2; // t1
+  BVH_REAL extents[] = {-3000, -3000, 0, 3000, 3000, 3000};
+  BVH_REAL delta_trans[] = {1, 1, 1};
+  int n = 10;
+
+  generateRandomTransform(extents, transforms, transforms2, delta_trans, 0.005 * 2 * 3.1415, n);
+
+  for(unsigned int i = 0; i < transforms.size(); ++i)
+  {
+    std::cout << "test id " << i << std::endl;
+
+    std::vector<std::pair<int, int> > PQP_pairs;
+    DistanceRes PQP_dist;
+
+#if USE_PQP
+    collide_PQP(transforms[i], p1, t1, p2, t2, PQP_pairs);
+
+    distance_PQP(transforms[i], p1, t1, p2, t2, PQP_dist);
+#endif
+
+    collide_Test_PP<OBB>(transforms[i], p1, p2, SPLIT_METHOD_MEAN);
+
+    collide_Test_PP<OBB>(transforms[i], p1, p2, SPLIT_METHOD_BV_CENTER);
+
+    collide_Test_PP<OBB>(transforms[i], p1, p2, SPLIT_METHOD_MEDIAN);
+
+    collide_Test_PM<OBB>(transforms[i], p1, p2, t2, SPLIT_METHOD_MEAN);
+
+    collide_Test_PM<OBB>(transforms[i], p1, p2, t2, SPLIT_METHOD_BV_CENTER);
+
+    collide_Test_PM<OBB>(transforms[i], p1, p2, t2, SPLIT_METHOD_MEDIAN);
+
+    collide_Test_MP<OBB>(transforms[i], p1, t1, p2, SPLIT_METHOD_MEAN);
+
+    collide_Test_MP<OBB>(transforms[i], p1, t1, p2, SPLIT_METHOD_BV_CENTER);
+
+    collide_Test_MP<OBB>(transforms[i], p1, t1, p2, SPLIT_METHOD_MEDIAN);
+
+    collide_Test_PP_OBB(transforms[i], p1, p2, SPLIT_METHOD_MEAN);
+
+    collide_Test_PP_OBB(transforms[i], p1, p2, SPLIT_METHOD_BV_CENTER);
+
+    collide_Test_PP_OBB(transforms[i], p1, p2, SPLIT_METHOD_MEDIAN);
+
+    collide_Test_PM_OBB(transforms[i], p1, p2, t2, SPLIT_METHOD_MEAN);
+
+    collide_Test_PM_OBB(transforms[i], p1, p2, t2, SPLIT_METHOD_BV_CENTER);
+
+    collide_Test_PM_OBB(transforms[i], p1, p2, t2, SPLIT_METHOD_MEDIAN);
+
+    collide_Test_MP_OBB(transforms[i], p1, t1, p2, SPLIT_METHOD_MEAN);
+
+    collide_Test_MP_OBB(transforms[i], p1, t1, p2, SPLIT_METHOD_BV_CENTER);
+
+    collide_Test_MP_OBB(transforms[i], p1, t1, p2, SPLIT_METHOD_MEDIAN);
+
+    std::cout << std::endl;
+  }
+}
+
+template<typename BV>
+bool collide_Test_PP(const Transform& tf,
+                     const std::vector<Vec3f>& vertices1, const std::vector<Vec3f>& vertices2, SplitMethodType split_method, bool verbose)
+{
+  BVHModel<BV> m1;
+  BVHModel<BV> m2;
+  m1.bv_splitter.reset(new BVSplitter<BV>(split_method));
+  m2.bv_splitter.reset(new BVSplitter<BV>(split_method));
+
+  m1.beginModel();
+  m1.addSubModel(vertices1);
+  m1.endModel();
+
+  m2.beginModel();
+  m2.addSubModel(vertices2);
+  m2.endModel();
+
+  Vec3f R2[3];
+  R2[0] = Vec3f(1, 0, 0);
+  R2[1] = Vec3f(0, 1, 0);
+  R2[2] = Vec3f(0, 0, 1);
+  Vec3f T2;
+
+  PointCloudCollisionTraversalNode<BV> node;
+
+  if(!initialize<BV, false, false>(node, m1, m2, tf.R, tf.T, R2, T2, 0.6, 20))
+    std::cout << "initialize error" << std::endl;
+
+  node.enable_statistics = verbose;
+  node.num_max_contacts = num_max_contacts;
+
+  collide(&node);
+
+  if(node.pairs.size() > 0)
+  {
+    if(verbose)
+    {
+      std::cout << "in collision " << node.pairs.size() << ": " << std::endl;
+
+      std::vector<BVHPointCollisionPair> pairs(node.pairs.size());
+      for(unsigned i = 0; i < node.pairs.size(); ++i)
+        pairs[i] = node.pairs[i];
+
+      std::sort(pairs.begin(), pairs.end(), BVHPointCollisionPairComp());
+
+      for(unsigned i = 0; i < pairs.size(); ++i)
+      {
+        std::cout << "(" << pairs[i].id1_start << "(" << pairs[i].id1_num << ")" << " " << pairs[i].id2_start << "(" << pairs[i].id2_num << ")" << " " << pairs[i].collision_prob << ") ";
+      }
+      std::cout << std::endl;
+    }
+    if(verbose) std::cout << node.num_bv_tests << " " << node.num_leaf_tests << std::endl;
+    return true;
+  }
+  else
+  {
+    if(verbose) std::cout << "collision free " << std::endl;
+    if(verbose) std::cout << node.max_collision_prob << std::endl;
+    if(verbose) std::cout << node.num_bv_tests << " " << node.num_leaf_tests << std::endl;
+    return false;
+  }
+}
+
+bool collide_Test_PP_OBB(const Transform& tf,
+                         const std::vector<Vec3f>& vertices1, const std::vector<Vec3f>& vertices2, SplitMethodType split_method, bool verbose)
+{
+  BVHModel<OBB> m1;
+  BVHModel<OBB> m2;
+  m1.bv_splitter.reset(new BVSplitter<OBB>(split_method));
+  m2.bv_splitter.reset(new BVSplitter<OBB>(split_method));
+
+  m1.beginModel();
+  m1.addSubModel(vertices1);
+  m1.endModel();
+
+  m2.beginModel();
+  m2.addSubModel(vertices2);
+  m2.endModel();
+
+  Vec3f R2[3];
+  R2[0] = Vec3f(1, 0, 0);
+  R2[1] = Vec3f(0, 1, 0);
+  R2[2] = Vec3f(0, 0, 1);
+  Vec3f T2;
+
+  PointCloudCollisionTraversalNodeOBB node;
+
+  if(!initialize(node, m1, m2, tf.R, tf.T, R2, T2, 0.6, 20))
+    std::cout << "initialize error" << std::endl;
+
+  node.enable_statistics = verbose;
+  node.num_max_contacts = num_max_contacts;
+
+  collide(&node);
+
+  if(node.pairs.size() > 0)
+  {
+    if(verbose)
+    {
+      std::cout << "in collision " << node.pairs.size() << ": " << std::endl;
+
+      std::vector<BVHPointCollisionPair> pairs(node.pairs.size());
+      for(unsigned i = 0; i < node.pairs.size(); ++i)
+        pairs[i] = node.pairs[i];
+
+      std::sort(pairs.begin(), pairs.end(), BVHPointCollisionPairComp());
+
+      for(unsigned i = 0; i < pairs.size(); ++i)
+      {
+        std::cout << "(" << pairs[i].id1_start << "(" << pairs[i].id1_num << ")" << " " << pairs[i].id2_start << "(" << pairs[i].id2_num << ")" << " " << pairs[i].collision_prob << ") ";
+      }
+      std::cout << std::endl;
+    }
+    if(verbose) std::cout << node.num_bv_tests << " " << node.num_leaf_tests << std::endl;
+    return true;
+  }
+  else
+  {
+    if(verbose) std::cout << "collision free " << std::endl;
+    if(verbose) std::cout << node.max_collision_prob << std::endl;
+    if(verbose) std::cout << node.num_bv_tests << " " << node.num_leaf_tests << std::endl;
+    return false;
+  }
+}
+
+
+
+template<typename BV>
+bool collide_Test_MP(const Transform& tf,
+                     const std::vector<Vec3f>& vertices1, const std::vector<Triangle>& triangles1,
+                     const std::vector<Vec3f>& vertices2, SplitMethodType split_method, bool verbose)
+{
+  BVHModel<BV> m1;
+  BVHModel<BV> m2;
+  m1.bv_splitter.reset(new BVSplitter<BV>(split_method));
+  m2.bv_splitter.reset(new BVSplitter<BV>(split_method));
+
+  m1.beginModel();
+  m1.addSubModel(vertices1, triangles1);
+  m1.endModel();
+
+  m2.beginModel();
+  m2.addSubModel(vertices2);
+  m2.endModel();
+
+  Vec3f R2[3];
+  R2[0] = Vec3f(1, 0, 0);
+  R2[1] = Vec3f(0, 1, 0);
+  R2[2] = Vec3f(0, 0, 1);
+  Vec3f T2;
+
+  PointCloudMeshCollisionTraversalNode<BV> node;
+
+  if(!initialize<BV, false, false>(node, m2, m1, R2, T2, tf.R, tf.T, 0.6, 20))
+    std::cout << "initialize error" << std::endl;
+
+  node.enable_statistics = verbose;
+  node.num_max_contacts = num_max_contacts;
+
+  collide(&node);
+
+  if(node.pairs.size() > 0)
+  {
+    if(verbose)
+    {
+      std::cout << "in collision " << node.pairs.size() << ": " << std::endl;
+
+      std::vector<BVHPointCollisionPair> pairs(node.pairs.size());
+      for(unsigned i = 0; i < node.pairs.size(); ++i)
+        pairs[i] = node.pairs[i];
+
+      std::sort(pairs.begin(), pairs.end(), BVHPointCollisionPairComp());
+
+      for(unsigned i = 0; i < pairs.size(); ++i)
+      {
+        std::cout << "(" << pairs[i].id1_start << "(" << pairs[i].id1_num << ")" << " " << pairs[i].id2_start << "(" << pairs[i].id2_num << ")" << " " << pairs[i].collision_prob << ") ";
+      }
+      std::cout << std::endl;
+    }
+    if(verbose) std::cout << node.num_bv_tests << " " << node.num_leaf_tests << std::endl;
+    return true;
+  }
+  else
+  {
+    if(verbose) std::cout << "collision free " << std::endl;
+    if(verbose) std::cout << node.max_collision_prob << std::endl;
+    if(verbose) std::cout << node.num_bv_tests << " " << node.num_leaf_tests << std::endl;
+    return false;
+  }
+}
+
+
+
+bool collide_Test_MP_OBB(const Transform& tf,
+                         const std::vector<Vec3f>& vertices1, const std::vector<Triangle>& triangles1,
+                         const std::vector<Vec3f>& vertices2, SplitMethodType split_method, bool verbose)
+{
+  BVHModel<OBB> m1;
+  BVHModel<OBB> m2;
+  m1.bv_splitter.reset(new BVSplitter<OBB>(split_method));
+  m2.bv_splitter.reset(new BVSplitter<OBB>(split_method));
+
+  m1.beginModel();
+  m1.addSubModel(vertices1, triangles1);
+  m1.endModel();
+
+  m2.beginModel();
+  m2.addSubModel(vertices2);
+  m2.endModel();
+
+  Vec3f R2[3];
+  R2[0] = Vec3f(1, 0, 0);
+  R2[1] = Vec3f(0, 1, 0);
+  R2[2] = Vec3f(0, 0, 1);
+  Vec3f T2;
+
+  PointCloudMeshCollisionTraversalNodeOBB node;
+
+  if(!initialize(node, m2, m1, R2, T2, tf.R, tf.T, 0.6, 20))
+    std::cout << "initialize error" << std::endl;
+
+  node.enable_statistics = verbose;
+  node.num_max_contacts = num_max_contacts;
+
+  collide(&node);
+
+  if(node.pairs.size() > 0)
+  {
+    if(verbose)
+    {
+      std::cout << "in collision " << node.pairs.size() << ": " << std::endl;
+
+      std::vector<BVHPointCollisionPair> pairs(node.pairs.size());
+      for(unsigned i = 0; i < node.pairs.size(); ++i)
+        pairs[i] = node.pairs[i];
+
+      std::sort(pairs.begin(), pairs.end(), BVHPointCollisionPairComp());
+
+      for(unsigned i = 0; i < pairs.size(); ++i)
+      {
+        std::cout << "(" << pairs[i].id1_start << "(" << pairs[i].id1_num << ")" << " " << pairs[i].id2_start << "(" << pairs[i].id2_num << ")" << " " << pairs[i].collision_prob << ") ";
+      }
+      std::cout << std::endl;
+    }
+    if(verbose) std::cout << node.num_bv_tests << " " << node.num_leaf_tests << std::endl;
+    return true;
+  }
+  else
+  {
+    if(verbose) std::cout << "collision free " << std::endl;
+    if(verbose) std::cout << node.max_collision_prob << std::endl;
+    if(verbose) std::cout << node.num_bv_tests << " " << node.num_leaf_tests << std::endl;
+    return false;
+  }
+}
+
+
+
+template<typename BV>
+bool collide_Test_PM(const Transform& tf,
+                     const std::vector<Vec3f>& vertices1,
+                     const std::vector<Vec3f>& vertices2, const std::vector<Triangle>& triangles2, SplitMethodType split_method, bool verbose)
+{
+  BVHModel<BV> m1;
+  BVHModel<BV> m2;
+  m1.bv_splitter.reset(new BVSplitter<BV>(split_method));
+  m2.bv_splitter.reset(new BVSplitter<BV>(split_method));
+
+  m1.beginModel();
+  m1.addSubModel(vertices1);
+  m1.endModel();
+
+  m2.beginModel();
+  m2.addSubModel(vertices2, triangles2);
+  m2.endModel();
+
+  Vec3f R2[3];
+  R2[0] = Vec3f(1, 0, 0);
+  R2[1] = Vec3f(0, 1, 0);
+  R2[2] = Vec3f(0, 0, 1);
+  Vec3f T2;
+
+  PointCloudMeshCollisionTraversalNode<BV> node;
+
+  if(!initialize<BV, false, false>(node, m1, m2, tf.R, tf.T, R2, T2, 0.6, 20))
+    std::cout << "initialize error" << std::endl;
+
+  node.enable_statistics = verbose;
+  node.num_max_contacts = num_max_contacts;
+
+  collide(&node);
+
+  if(node.pairs.size() > 0)
+  {
+    if(verbose)
+    {
+      std::cout << "in collision " << node.pairs.size() << ": " << std::endl;
+
+      std::vector<BVHPointCollisionPair> pairs(node.pairs.size());
+      for(unsigned i = 0; i < node.pairs.size(); ++i)
+        pairs[i] = node.pairs[i];
+
+      std::sort(pairs.begin(), pairs.end(), BVHPointCollisionPairComp());
+
+      for(unsigned i = 0; i < pairs.size(); ++i)
+      {
+        std::cout << "(" << pairs[i].id1_start << "(" << pairs[i].id1_num << ")" << " " << pairs[i].id2_start << "(" << pairs[i].id2_num << ")" << " " << pairs[i].collision_prob << ") ";
+      }
+      std::cout << std::endl;
+    }
+    if(verbose) std::cout << node.num_bv_tests << " " << node.num_leaf_tests << std::endl;
+    return true;
+  }
+  else
+  {
+    if(verbose) std::cout << "collision free " << std::endl;
+    if(verbose) std::cout << node.max_collision_prob << std::endl;
+    if(verbose) std::cout << node.num_bv_tests << " " << node.num_leaf_tests << std::endl;
+    return false;
+  }
+}
+
+
+bool collide_Test_PM_OBB(const Transform& tf,
+                         const std::vector<Vec3f>& vertices1,
+                         const std::vector<Vec3f>& vertices2, const std::vector<Triangle>& triangles2, SplitMethodType split_method, bool verbose)
+{
+  BVHModel<OBB> m1;
+  BVHModel<OBB> m2;
+  m1.bv_splitter.reset(new BVSplitter<OBB>(split_method));
+  m2.bv_splitter.reset(new BVSplitter<OBB>(split_method));
+
+  m1.beginModel();
+  m1.addSubModel(vertices1);
+  m1.endModel();
+
+  m2.beginModel();
+  m2.addSubModel(vertices2, triangles2);
+  m2.endModel();
+
+  Vec3f R2[3];
+  R2[0] = Vec3f(1, 0, 0);
+  R2[1] = Vec3f(0, 1, 0);
+  R2[2] = Vec3f(0, 0, 1);
+  Vec3f T2;
+
+  PointCloudMeshCollisionTraversalNodeOBB node;
+
+  if(!initialize(node, m1, m2, tf.R, tf.T, R2, T2, 0.6, 20))
+    std::cout << "initialize error" << std::endl;
+
+  node.enable_statistics = verbose;
+  node.num_max_contacts = num_max_contacts;
+
+  collide(&node);
+
+  if(node.pairs.size() > 0)
+  {
+    if(verbose)
+    {
+      std::cout << "in collision " << node.pairs.size() << ": " << std::endl;
+
+      std::vector<BVHPointCollisionPair> pairs(node.pairs.size());
+      for(unsigned i = 0; i < node.pairs.size(); ++i)
+        pairs[i] = node.pairs[i];
+
+      std::sort(pairs.begin(), pairs.end(), BVHPointCollisionPairComp());
+
+      for(unsigned i = 0; i < pairs.size(); ++i)
+      {
+        std::cout << "(" << pairs[i].id1_start << "(" << pairs[i].id1_num << ")" << " " << pairs[i].id2_start << "(" << pairs[i].id2_num << ")" << " " << pairs[i].collision_prob << ") ";
+      }
+      std::cout << std::endl;
+    }
+    if(verbose) std::cout << node.num_bv_tests << " " << node.num_leaf_tests << std::endl;
+    return true;
+  }
+  else
+  {
+    if(verbose) std::cout << "collision free " << std::endl;
+    if(verbose) std::cout << node.max_collision_prob << std::endl;
+    if(verbose) std::cout << node.num_bv_tests << " " << node.num_leaf_tests << std::endl;
+    return false;
+  }
+}
+
+#else
+int main()
+{
+  return 1;
+}
+#endif
+
+