text: /******************************************************************************
   * Creature Runtimes License
   *
   * Copyright (c) 2015, Kestrel Moon Studios
   * All rights reserved.
   *
   * Preamble: This Agreement governs the relationship between Licensee and Kestrel Moon Studios(Hereinafter: Licensor).
   * This Agreement sets the terms, rights, restrictions and obligations on using [Creature Runtimes] (hereinafter: The Software) created and owned by Licensor,
   * as detailed herein:
   * License Grant: Licensor hereby grants Licensee a Sublicensable, Non-assignable & non-transferable, Commercial, Royalty free,
   * Including the rights to create but not distribute derivative works, Non-exclusive license, all with accordance with the terms set forth and
   * other legal restrictions set forth in 3rd party software used while running Software.
   * Limited: Licensee may use Software for the purpose of:
   * Running Software on Licensee’s Website[s] and Server[s];
   * Allowing 3rd Parties to run Software on Licensee’s Website[s] and Server[s];
   * Publishing Software’s output to Licensee and 3rd Parties;
   * Distribute verbatim copies of Software’s output (including compiled binaries);
   * Modify Software to suit Licensee’s needs and specifications.
   * Binary Restricted: Licensee may sublicense Software as a part of a larger work containing more than Software,
   * distributed solely in Object or Binary form under a personal, non-sublicensable, limited license. Such redistribution shall be limited to unlimited codebases.
   * Non Assignable & Non-Transferable: Licensee may not assign or transfer his rights and duties under this license.
   * Commercial, Royalty Free: Licensee may use Software for any purpose, including paid-services, without any royalties
   * Including the Right to Create Derivative Works: Licensee may create derivative works based on Software,
   * including amending Software’s source code, modifying it, integrating it into a larger work or removing portions of Software,
   * as long as no distribution of the derivative works is made
   *
   * THE RUNTIMES IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE RUNTIMES OR THE USE OR OTHER DEALINGS IN THE
   * RUNTIMES.
   *****************************************************************************/
  
  #include "CreatureModule.h"
  #include <algorithm>
  
  template <typename T>
  static T clipNum(const T& n, const T& lower, const T& upper) {
      return std::max(lower, std::min(n, upper));
  }
  
  static JsonNode * GetJSONLevelNodeFromKey(JsonNode& json_obj,
                                            const std::string& key)
  {
      JsonNode * ret_node = NULL;
      if(std::string(json_obj.key) == key) {
          ret_node = &json_obj;
      }
      else {
          JsonNode * cur_node = &json_obj;
          while(true) {
              if(std::string(cur_node->key) == key) {
                  ret_node = cur_node;
                  break;
              }
              
              JsonNode * next_node = cur_node->next;
              if(next_node == NULL) {
                  break;
              }
              
              cur_node = next_node;
          }
      }
      
      return ret_node;
  }
  
  static JsonNode * GetJSONNodeFromKey(JsonNode& json_obj,
                                       const std::string& key)
  {
      JsonNode * ret_node = NULL;
      
      
      for(JsonIterator it = JsonBegin(json_obj.value);
          it != JsonEnd(json_obj.value); ++it)
      {
          if( std::string((*it)->key) == key) {
              ret_node = *it;
              break;
          }
      }
      
      return ret_node;
  }
  
  static std::vector<std::string> GetJSONKeysFromNode(JsonNode& json_obj)
  {
      std::vector<std::string> ret_keys;
      for(JsonIterator it = JsonBegin(json_obj.value);
          it != JsonEnd(json_obj.value); ++it)
      {
          ret_keys.push_back(std::string((*it)->key));
      }
      
      return ret_keys;
  }
  
  static std::vector<float> GetJSONNodeFloatArray(JsonNode& json_obj)
  {
      std::vector<float> ret_vals;
      for(JsonIterator it = JsonBegin(json_obj.value);
          it != JsonEnd(json_obj.value); ++it)
      {
          JsonNode * cur_node = *it;
          ret_vals.push_back((float)cur_node->value.toNumber());
      }
      
      return ret_vals;
  }
  
  static std::vector<int> GetJSONNodeIntArray(JsonNode& json_obj)
  {
      std::vector<int> ret_vals;
      for(JsonIterator it = JsonBegin(json_obj.value);
          it != JsonEnd(json_obj.value); ++it)
      {
          JsonNode * cur_node = *it;
          ret_vals.push_back((int)cur_node->value.toNumber());
      }
      
      return ret_vals;
  }
  
  static glm::float32 * ReadJSONPoints3D(JsonNode& json_obj,
                                         const std::string& key,
                                         int& num_pts)
  {
      std::vector<float> pts_array = GetJSONNodeFloatArray(*GetJSONNodeFromKey(json_obj, key));
      num_pts = (int)pts_array.size() / 2; // Since we are storing them as x,y pairs
      
      glm::float32 * ret_pts = new glm::float32[num_pts * 3];
      
      int cur_index = 0;
      for(size_t i = 0; i < pts_array.size(); i+=2)
      {
          ret_pts[cur_index] = pts_array[i];
          ret_pts[cur_index + 1] = pts_array[i + 1];
          ret_pts[cur_index + 2] = 0;
          
          cur_index += 3;
      }
      
      
      return ret_pts;
  }
  
  static glm::float32 * ReadJSONPoints2D(JsonNode& json_obj,
                                         const std::string& key,
                                         int& num_pts)
  {
      std::vector<float> pts_array = GetJSONNodeFloatArray(*GetJSONNodeFromKey(json_obj, key));
      glm::float32 * ret_pts = new glm::float32[pts_array.size()];
      for(size_t i = 0; i < pts_array.size(); i++)
      {
          ret_pts[i] = pts_array[i];
      }
      
      num_pts = (int)pts_array.size() / 2; // Since we are storing them as x,y pairs
      
      return ret_pts;
  }
  
  static std::vector<glm::vec2> ReadJSONPoints2DVector(JsonNode& json_obj,
                                                       const std::string& key)
  {
      std::vector<float> pts_array = GetJSONNodeFloatArray(*GetJSONNodeFromKey(json_obj, key));
      std::vector<glm::vec2> ret_pts(pts_array.size() / 2);
      
      int pt_index = 0;
      for(size_t i = 0; i < pts_array.size(); i+=2)
      {
          ret_pts[pt_index].x = pts_array[i];
          ret_pts[pt_index].y = pts_array[i + 1];
          
          pt_index++;
      }
      
      return ret_pts;
  }
  
  static glm::uint32 * ReadJSONUints(JsonNode& json_obj,
                                     const std::string& key,
                                     int& num_ints)
  {
      std::vector<int> ints_array = GetJSONNodeIntArray(*GetJSONNodeFromKey(json_obj, key));
      glm::uint32 * ret_pts = new glm::uint32[ints_array.size()];
      for(size_t i = 0; i < ints_array.size(); i++)
      {
          ret_pts[i] = (glm::uint32)ints_array[i];
      }
      
      num_ints = (int)ints_array.size();
      
      return ret_pts;
  }
  
  static glm::vec2 ReadJSONVec2(JsonNode& json_obj,
                                  const std::string& key)
  {
      std::vector<float> read_array = GetJSONNodeFloatArray(*GetJSONNodeFromKey(json_obj, key));
      return glm::vec2(read_array[0], read_array[1]);
  }
  
  static glm::vec4 ReadJSONVec4_2(JsonNode& json_obj,
                                  const std::string& key)
  {
      std::vector<float> read_array = GetJSONNodeFloatArray(*GetJSONNodeFromKey(json_obj, key));
      return glm::vec4(read_array[0], read_array[1], 0, 1.0f);
  }
  
  static glm::mat4 ReadJSONMat4(JsonNode& json_obj,
                                const std::string& key)
  {
      std::vector<float> read_array = GetJSONNodeFloatArray(*GetJSONNodeFromKey(json_obj, key));
      float mat_vals[16];
      for(int i = 0; i < 16; i++) {
          mat_vals[i] = read_array[i];
      }
      
      return glm::make_mat4(mat_vals);
  }
  
  static std::vector<int> ReadIntArray(JsonNode& json_obj,
                                       const std::string& key)
  {
      std::vector<int> read_array = GetJSONNodeIntArray(*GetJSONNodeFromKey(json_obj, key));;
      std::vector<int> ret_array(read_array.size());
      for(int i = 0; i < read_array.size(); i++)
      {
          ret_array[i] = read_array[i];
      }
      
      return ret_array;
  }
  
  static std::vector<float> ReadFloatArray(JsonNode& json_obj,
                                       const std::string& key)
  {
      std::vector<float> read_array = GetJSONNodeFloatArray(*GetJSONNodeFromKey(json_obj, key));
      std::vector<float> ret_array(read_array.size());
      for(int i = 0; i < read_array.size(); i++)
      {
          ret_array[i] = read_array[i];
      }
      
      return ret_array;
  }
  
  static meshBone * CreateBones(JsonNode& json_obj,
                                const std::string& key)
  {
      meshBone * root_bone = NULL;
      JsonNode * base_obj =  GetJSONLevelNodeFromKey(json_obj, key);
      std::map<int, std::pair<meshBone *, std::vector<int> > > bone_data;
      std::set<int> child_set;
  
      // layout bones
      for (JsonIterator it = JsonBegin(base_obj->value);
           it != JsonEnd(base_obj->value);
           ++it)
      {
          JsonNode * cur_node = *it;
          
          std::string cur_name(cur_node->key);
          int cur_id =  (int)GetJSONNodeFromKey(*cur_node, "id")->value.toNumber();
          glm::mat4 cur_parent_mat = ReadJSONMat4(*cur_node, "restParentMat");
          glm::vec4 cur_local_rest_start_pt = ReadJSONVec4_2(*cur_node, "localRestStartPt");
          glm::vec4 cur_local_rest_end_pt = ReadJSONVec4_2(*cur_node, "localRestEndPt");
          std::vector<int> cur_children_ids = ReadIntArray(*cur_node, "children");
          
          meshBone * new_bone = new meshBone(cur_name,
                                             glm::vec4(0),
                                             glm::vec4(0),
                                             cur_parent_mat);
          new_bone->getLocalRestStartPt() = cur_local_rest_start_pt;
          new_bone->getLocalRestEndPt() = cur_local_rest_end_pt;
          new_bone->calcRestData();
          new_bone->setTagId(cur_id);
          
          bone_data[cur_id] = std::make_pair(new_bone, cur_children_ids);
          
          for(auto& cur_child_id : cur_children_ids) {
              child_set.insert(cur_child_id);
          }
      }
      
      // Find root
      for(auto& cur_data : bone_data)
      {
          int cur_id = cur_data.first;
          if(child_set.count(cur_id) <= 0) {
              // not a child, so is root
              root_bone = cur_data.second.first;
              break;
          }
      }
      
      // construct hierarchy
      for(auto& cur_data : bone_data)
      {
          meshBone * cur_bone = cur_data.second.first;
          const std::vector<int>& children_ids = cur_data.second.second;
          for(auto& cur_child_id : children_ids)
          {
              meshBone * child_bone = bone_data[cur_child_id].first;
              cur_bone->addChild(child_bone);
          }
  
      }
      
      return root_bone;
  }
  
  static std::vector<meshRenderRegion *> CreateRegions(JsonNode& json_obj,
                                                       const std::string& key,
                                                       glm::uint32 * indices_in,
                                                       glm::float32 * rest_pts_in,
                                                       glm::float32 * uvs_in)
  {
      std::vector<meshRenderRegion *> ret_regions;
      JsonNode * base_obj =  GetJSONNodeFromKey(json_obj, key);
  
      for (JsonIterator it = JsonBegin(base_obj->value);
           it != JsonEnd(base_obj->value);
           ++it)
      {
          JsonNode * cur_node = *it;
  
          std::string cur_name(cur_node->key);
          
          int cur_id = (int)GetJSONNodeFromKey(*cur_node, "id")->value.toNumber();
          int cur_start_pt_index = (int)GetJSONNodeFromKey(*cur_node, "start_pt_index")->value.toNumber();
          int cur_end_pt_index = (int)GetJSONNodeFromKey(*cur_node, "end_pt_index")->value.toNumber();
          int cur_start_index = (int)GetJSONNodeFromKey(*cur_node, "start_index")->value.toNumber();
          int cur_end_index = (int)GetJSONNodeFromKey(*cur_node, "end_index")->value.toNumber();
       
          meshRenderRegion * new_region = new meshRenderRegion(indices_in,
                                                               rest_pts_in,
                                                               uvs_in,
                                                               cur_start_pt_index,
                                                               cur_end_pt_index,
                                                               cur_start_index,
                                                               cur_end_index);
          
          new_region->setName(cur_name);
          new_region->setTagId(cur_id);
          
          // Read in weights
          std::unordered_map<std::string, std::vector<float> >& weight_map =
              new_region->getWeights();
          JsonNode * weight_obj =  GetJSONNodeFromKey(*cur_node, "weights");
          
          for (JsonIterator w_it = JsonBegin(weight_obj->value);
               w_it != JsonEnd(weight_obj->value);
               ++w_it)
          {
              JsonNode * w_node = *w_it;
              
              const std::string& key(w_node->key);
              std::vector<float> values = ReadFloatArray(*weight_obj, key);
              weight_map[key] = values;
          }
          
          ret_regions.push_back(new_region);
      }
      
      return ret_regions;
  }
  
  static std::pair<int, int> GetStartEndTimes(JsonNode& json_obj,
                                              const std::string& key)
  {
      std::pair<int, int> ret_times(0,0);
      bool first = true;
      JsonNode * base_obj =  GetJSONNodeFromKey(json_obj, key);
      
      for (JsonIterator it = JsonBegin(base_obj->value);
           it != JsonEnd(base_obj->value);
           ++it)
      {
          JsonNode * cur_node = *it;
  
          int cur_val = atoi(cur_node->key);
          if(first) {
              ret_times.first = cur_val;
              ret_times.second = cur_val;
              first = false;
          }
          else {
              if(cur_val > ret_times.second) {
                  ret_times.second = cur_val;
              }
              
              if(cur_val < ret_times.first) {
                  ret_times.first = cur_val;
              }
          }
      }
      
      return ret_times;
  }
  
  static void FillBoneCache(JsonNode& json_obj,
                            const std::string& key,
                            int start_time,
                            int end_time,
                            meshBoneCacheManager& cache_manager)
  {
      JsonNode * base_obj =  GetJSONNodeFromKey(json_obj, key);
  
      cache_manager.init(start_time, end_time);
      
  	int prev_time = start_time;
      for (JsonIterator it = JsonBegin(base_obj->value);
           it != JsonEnd(base_obj->value);
           ++it)
      {
          JsonNode * cur_node = *it;
  
          int cur_time = atoi(cur_node->key);
          std::vector<meshBoneCache> cache_list;
          
          for (JsonIterator bone_it = JsonBegin(cur_node->value);
               bone_it != JsonEnd(cur_node->value);
               ++bone_it)
          {
              JsonNode * bone_node = *bone_it;
              
              std::string cur_name(bone_node->key);
              glm::vec4 cur_start_pt = ReadJSONVec4_2(*bone_node, "start_pt");
              glm::vec4 cur_end_pt = ReadJSONVec4_2(*bone_node, "end_pt");
              
              meshBoneCache cache_data(cur_name);
              cache_data.setWorldStartPt(cur_start_pt);
              cache_data.setWorldEndPt(cur_end_pt);
              
              cache_list.push_back(cache_data);
          }
          
          int set_index = cache_manager.getIndexByTime(cur_time);
          cache_manager.getCacheTable()[set_index] = cache_list;
  
  		auto gap_diff = cur_time - prev_time;
  		if (gap_diff > 1)
  		{
  			auto ptInterp = [&](const glm::vec4& src_pt, const glm::vec4& target_pt, float fraction)
  			{
  				return ((1.0f - fraction ) * src_pt) + (fraction * target_pt);
  			};
  
  			// Gap Step
  			auto prev_index = cache_manager.getIndexByTime(prev_time);
  			for (int j = 1; j < gap_diff; j++)
  			{
  				auto gap_fraction = (float)j / (float)gap_diff;
  				std::vector<meshBoneCache> gap_cache_list;
  				gap_cache_list.reserve(cache_list.size());
  				for (size_t k = 0; k < cache_list.size(); k++)
  				{
  					auto& cur_data = cache_manager.getCacheTable()[set_index][k];
  					auto& prev_data = cache_manager.getCacheTable()[prev_index][k];
  					meshBoneCache gap_cache_data(cur_data.getKey());
  					gap_cache_data.setWorldStartPt(
  						ptInterp(prev_data.getWorldStartPt(), cur_data.getWorldStartPt(), gap_fraction));
  					gap_cache_data.setWorldEndPt(
  						ptInterp(prev_data.getWorldEndPt(), cur_data.getWorldEndPt(), gap_fraction));
  
  					gap_cache_list.push_back(gap_cache_data);
  				}
  
  				cache_manager.getCacheTable()[prev_index + j] = gap_cache_list;
  			}
  		}
  
  		prev_time = cur_time;
      }
      
      cache_manager.makeAllReady();
  }
  
  static void FillDeformationCache(JsonNode& json_obj,
                            const std::string& key,
                            int start_time,
                            int end_time,
                            meshDisplacementCacheManager& cache_manager)
  {
      JsonNode * base_obj =  GetJSONNodeFromKey(json_obj, key);
  
      cache_manager.init(start_time, end_time);
      
  	int prev_time = start_time;
      for (JsonIterator it = JsonBegin(base_obj->value);
           it != JsonEnd(base_obj->value);
           ++it)
      {
          JsonNode * cur_node = *it;
  
          int cur_time = atoi(cur_node->key);
          std::vector<meshDisplacementCache> cache_list;
          
          for (JsonIterator mesh_it = JsonBegin(cur_node->value);
               mesh_it != JsonEnd(cur_node->value);
               ++mesh_it)
          {
              JsonNode * mesh_node = *mesh_it;
              
              std::string cur_name(mesh_node->key);
              
              meshDisplacementCache cache_data(cur_name);
              
              bool use_local_displacement = GetJSONNodeFromKey(*mesh_node, "use_local_displacements")->value.toBool();
              bool use_post_displacement = GetJSONNodeFromKey(*mesh_node, "use_post_displacements")->value.toBool();
              
              if(use_local_displacement) {
                  std::vector<glm::vec2> read_pts = ReadJSONPoints2DVector(*mesh_node, "local_displacements");
                  cache_data.setLocalDisplacements(read_pts);
              }
              
              if(use_post_displacement) {
                  std::vector<glm::vec2> read_pts = ReadJSONPoints2DVector(*mesh_node, "post_displacements");
                  cache_data.setPostDisplacements(read_pts);
              }
              
              cache_list.push_back(cache_data);
          }
          
          int set_index = cache_manager.getIndexByTime(cur_time);
          cache_manager.getCacheTable()[set_index] = cache_list;
  
  		auto gap_diff = cur_time - prev_time;
  		if (gap_diff > 1)
  		{
  			auto ptsInterp = [&](const std::vector<glm::vec2> src_pts, const std::vector<glm::vec2> target_pts, float fraction)
  			{
  				std::vector<glm::vec2> ret_pts(src_pts.size());
  				for (size_t i = 0; i < src_pts.size(); i++)
  				{
  					ret_pts[i] = ((1.0f - fraction) * src_pts[i]) + (fraction * target_pts[i]);
  				}
  
  				return ret_pts;
  			};
  
  			// Gap Step
  			auto prev_index = cache_manager.getIndexByTime(prev_time);
  			for (int j = 1; j < gap_diff; j++)
  			{
  				auto gap_fraction = (float)j / (float)gap_diff;
  				std::vector<meshDisplacementCache> gap_cache_list;
  				gap_cache_list.reserve(cache_list.size());
  				for (size_t k = 0; k < cache_list.size(); k++)
  				{
  					auto& cur_data = cache_manager.getCacheTable()[set_index][k];
  					auto& prev_data = cache_manager.getCacheTable()[prev_index][k];
  					meshDisplacementCache gap_cache_data(cur_data.getKey());
  					if (!cur_data.getLocalDisplacements().empty())
  					{
  						gap_cache_data.setLocalDisplacements(
  							ptsInterp(prev_data.getLocalDisplacements(), cur_data.getLocalDisplacements(), gap_fraction));
  					}
  					else {
  						gap_cache_data.setPostDisplacements(
  							ptsInterp(prev_data.getPostDisplacements(), cur_data.getPostDisplacements(), gap_fraction));
  					}
  
  					gap_cache_list.push_back(gap_cache_data);
  				}
  
  				cache_manager.getCacheTable()[prev_index + j] = gap_cache_list;
  			}
  		}
  
  		prev_time = cur_time;
      }
      
      cache_manager.makeAllReady();
  }
  
  
  static void FillUVSwapCache(JsonNode& json_obj,
                              const std::string& key,
                              int start_time,
                              int end_time,
                              meshUVWarpCacheManager& cache_manager)
  {
      JsonNode * base_obj =  GetJSONNodeFromKey(json_obj, key);
  
      cache_manager.init(start_time, end_time);
      
      for (JsonIterator it = JsonBegin(base_obj->value);
           it != JsonEnd(base_obj->value);
           ++it)
      {
          JsonNode * cur_node = *it;
  
          int cur_time = atoi(cur_node->key);
          std::vector<meshUVWarpCache> cache_list;
          
          for (JsonIterator uv_it = JsonBegin(cur_node->value);
               uv_it != JsonEnd(cur_node->value);
               ++uv_it)
          {
              JsonNode * uv_node = *uv_it;
              
              std::string cur_name(uv_node->key);
  
              meshUVWarpCache cache_data(cur_name);
              bool use_uv = GetJSONNodeFromKey(*uv_node, "enabled")->value.toBool();
              cache_data.setEnabled(use_uv);
              if(use_uv) {
                  glm::vec2 local_offset = ReadJSONVec2(*uv_node, "local_offset");
                  glm::vec2 global_offset = ReadJSONVec2(*uv_node, "global_offset");
                  glm::vec2 scale = ReadJSONVec2(*uv_node, "scale");
                  cache_data.setUvWarpLocalOffset(local_offset);
                  cache_data.setUvWarpGlobalOffset(global_offset);
                  cache_data.setUvWarpScale(scale);
              }
              
              cache_list.push_back(cache_data);
          }
          
          int set_index = cache_manager.getIndexByTime(cur_time);
          cache_manager.getCacheTable()[set_index] = cache_list;
      }
      
      cache_manager.makeAllReady();
  }
  
  static void FillOpacityCache(JsonNode& json_obj,
  	const std::string& key,
  	int start_time,
  	int end_time,
  	meshOpacityCacheManager& cache_manager)
  {
  	JsonNode * base_obj = GetJSONNodeFromKey(json_obj, key);
  
  	cache_manager.init(start_time, end_time);
  
  	if (base_obj == NULL)
  	{
  		return;
  	}
  
  	int prev_time = start_time;
  	for (JsonIterator it = JsonBegin(base_obj->value);
  		it != JsonEnd(base_obj->value);
  		++it)
  	{
  		JsonNode * cur_node = *it;
  
  		int cur_time = atoi(cur_node->key);
  		std::vector<meshOpacityCache> cache_list;
  
  		for (JsonIterator uv_it = JsonBegin(cur_node->value);
  			uv_it != JsonEnd(cur_node->value);
  			++uv_it)
  		{
  			JsonNode * opacity_node = *uv_it;
  
  			std::string cur_name(opacity_node->key);
  
  			meshOpacityCache cache_data(cur_name);
  			float cur_opacity = (float)GetJSONNodeFromKey(*opacity_node, "opacity")->value.toNumber();
  			cache_data.setOpacity(cur_opacity);
  
  			cache_list.push_back(cache_data);
  		}
  
  		int set_index = cache_manager.getIndexByTime(cur_time);
  		cache_manager.getCacheTable()[set_index] = cache_list;
  
  		auto gap_diff = cur_time - prev_time;
  		if (gap_diff > 1)
  		{
  			auto scalarInterp = [&](const float& src_val, const float& target_val, float fraction)
  			{
  				return ((1.0f - fraction) * src_val) + (fraction * target_val);
  			};
  
  			// Gap Step
  			auto prev_index = cache_manager.getIndexByTime(prev_time);
  			for (int j = 1; j < gap_diff; j++)
  			{
  				auto gap_fraction = (float)j / (float)gap_diff;
  				std::vector<meshOpacityCache> gap_cache_list;
  				gap_cache_list.reserve(cache_list.size());
  				for (size_t k = 0; k < cache_list.size(); k++)
  				{
  					auto& cur_data = cache_manager.getCacheTable()[set_index][k];
  					auto& prev_data = cache_manager.getCacheTable()[prev_index][k];
  					meshOpacityCache gap_cache_data(cur_data.getKey());
  					gap_cache_data.setOpacity(scalarInterp(prev_data.getOpacity(), cur_data.getOpacity(), gap_fraction));
  
  					gap_cache_list.push_back(gap_cache_data);
  				}
  
  				cache_manager.getCacheTable()[prev_index + j] = gap_cache_list;
  			}
  		}
  
  		prev_time = cur_time;
  	}
  
  	cache_manager.makeAllReady();
  }
  
  static std::map<std::string, std::vector<CreatureModule::CreatureUVSwapPacket> >
  FillSwapUVPacketMap(JsonNode& json_obj)
  {
  	std::map<std::string, std::vector<CreatureModule::CreatureUVSwapPacket> > ret_map;
  	for (JsonIterator it = JsonBegin(json_obj.value);
  		it != JsonEnd(json_obj.value);
  		++it)
  	{
  		JsonNode * cur_node = *it;
  		std::string cur_name(cur_node->key);
  		std::vector<CreatureModule::CreatureUVSwapPacket> cur_packets;
  
  		for (JsonIterator node_it = JsonBegin(cur_node->value); 
  			node_it != JsonEnd(cur_node->value);
  			++node_it)
  		{
  			JsonNode * packet_node = *node_it;
  			CreatureModule::CreatureUVSwapPacket new_packet(ReadJSONVec2(*packet_node, "local_offset"),
  				ReadJSONVec2(*packet_node, "global_offset"),
  				ReadJSONVec2(*packet_node, "scale"),
  				(int)GetJSONNodeFromKey(*packet_node, "tag")->value.toNumber());
  
  			cur_packets.push_back(new_packet);
  		}
  
  		
  		ret_map[cur_name] = cur_packets;
  	}
  
  	return ret_map;
  }
  
  static std::map<std::string, glm::vec2>
  FillAnchorPointMap(JsonNode& json_obj)
  {
  	auto anchor_data_node = GetJSONNodeFromKey(json_obj, "AnchorPoints");
  
  	std::map<std::string, glm::vec2> ret_map;
  	for (JsonIterator it = JsonBegin(anchor_data_node->value);
  	it != JsonEnd(anchor_data_node->value);
  		++it)
  	{
  		JsonNode * cur_node = *it;
  		glm::vec2 cur_pt = ReadJSONVec2(*cur_node, "point");
  		std::string cur_name(GetJSONNodeFromKey(*cur_node, "anim_clip_name")->value.toString());
  
  		ret_map[cur_name] = cur_pt;
  	}
  
  	return ret_map;
  }
  
  
  namespace CreatureModule {
      // Load the json structure
      void LoadCreatureJSONData(const std::string& filename_in,
                                CreatureLoadDataPacket& load_data)
      {
          std::ifstream read_file;
          read_file.open(filename_in.c_str());
          std::stringstream str_stream;
          str_stream << read_file.rdbuf();
          read_file.close();
          
          LoadCreatureJSONDataFromString(str_stream.str(), load_data);
      }
      
      void LoadCreatureJSONDataFromString(const std::string& string_in,
                                          CreatureLoadDataPacket& load_data)
      {
          char *endptr;
          size_t source_size = string_in.size();
          char *source_chars = new char[source_size+1];
          source_chars[source_size]=0;
          memcpy(source_chars,string_in.c_str(),source_size);
          
          JsonParseStatus status = jsonParse(source_chars, &endptr, &load_data.base_node, load_data.allocator);
          
          load_data.src_chars = source_chars;
          
          if(status != JSON_PARSE_OK) {
              std::cerr<<"LoadCreatureJSONData() - Error parsing JSON!"<<std::endl;
          }
      }
      
      // Creature class
      Creature::Creature(CreatureLoadDataPacket& load_data)
      {
  		anchor_points_active = false;
          LoadFromData(load_data);
      }
      
      Creature::~Creature()
      {
          delete global_pts;
          delete global_indices;
          delete global_uvs;
          delete render_colours;
          delete render_composition;
          delete render_pts;
      }
      
      glm::uint32 *
      Creature::GetGlobalIndices()
      {
          return global_indices;
      }
      
      glm::float32 *
      Creature::GetGlobalPts()
      {
          return global_pts;
      }
      
      glm::float32 *
      Creature::GetGlobalUvs()
      {
          return global_uvs;
      }
      
      glm::float32 *
      Creature::GetRenderPts()
      {
          return render_pts;
      }
      
      glm::uint8 *
      Creature::GetRenderColours()
      {
          return render_colours;
      }
      
      int
      Creature::GetTotalNumPoints() const
      {
          return total_num_pts;
      }
      
      int
      Creature::GetTotalNumIndices() const
      {
          return total_num_indices;
      }
      
      meshRenderBoneComposition *
      Creature::GetRenderComposition()
      {
          return render_composition;
      }
      
      void
      Creature::FillRenderColours(glm::uint8 r, glm::uint8 g, glm::uint8 b, glm::uint8 a)
      {
          for(int i = 0; i < total_num_pts; i++)
          {
              glm::uint8 * cur_colour = render_colours + (i * 4);
              cur_colour[0] = r;
              cur_colour[1] = g;
              cur_colour[2] = b;
              cur_colour[3] = a;
          }
      }
  
      const std::vector<std::string>& 
      Creature::GetAnimationNames() const
      {
          return animation_names;
      }
  
  	const std::map<std::string, std::vector<CreatureUVSwapPacket> >& 
  	Creature::GetUvSwapPackets() const
  	{
  		return uv_swap_packets;
  	}
  
  	void 
  	Creature::SetActiveItemSwap(const std::string& region_name, int swap_idx)
  	{
  		active_uv_swap_actions[region_name] = swap_idx;
  	}
  
  	void 
  	Creature::RemoveActiveItemSwap(const std::string& region_name)
  	{
  		active_uv_swap_actions.erase(region_name);
  	}
  
  	std::map<std::string, int>&
  	Creature::GetActiveItemSwaps()
  	{
  		return active_uv_swap_actions;
  	}
  
  	void Creature::SetAnchorPointsActive(bool flag_in)
  	{
  		anchor_points_active = flag_in;
  	}
  
  	bool Creature::GetAnchorPointsActive() const
  	{
  		return anchor_points_active;
  	}
  
  	glm::vec2 Creature::GetAnchorPoint(const std::string & anim_clip_name_in) const
  	{
  		if (anchor_point_map.count(anim_clip_name_in) > 0)
  		{
  			return anchor_point_map.at(anim_clip_name_in);
  		}
  
  		return glm::vec2(0, 0);
  	}
  
      void
      Creature::LoadFromData(CreatureLoadDataPacket& load_data)
      {
          JsonNode * json_root = load_data.base_node.toNode();
          
          // Load points and topology
          JsonNode * json_mesh = GetJSONLevelNodeFromKey(*json_root, "mesh");
          global_pts = ReadJSONPoints3D(*json_mesh, "points", total_num_pts);
          global_indices = ReadJSONUints(*json_mesh,"indices", total_num_indices);
          global_uvs = ReadJSONPoints2D(*json_mesh, "uvs", total_num_pts);
          
          render_colours = new glm::uint8[total_num_pts * 4];
          render_pts = new glm::float32[total_num_pts * 3];
          FillRenderColours(255, 255, 255, 255);
          
          // Load bones
          meshBone * root_bone = CreateBones(*json_root, "skeleton");
          
          // Load regions
          std::vector<meshRenderRegion *> regions = CreateRegions(*json_mesh,
                                                                  "regions",
                                                                  global_indices,
                                                                  global_pts,
                                                                  global_uvs);
          
          // Add into composition
          render_composition = new meshRenderBoneComposition();
          render_composition->setRootBone(root_bone);
          render_composition->getRootBone()->computeRestParentTransforms();
          
          for(auto& cur_region : regions) {
              cur_region->setMainBoneKey(root_bone->getKey());
              cur_region->determineMainBone(root_bone);
              render_composition->addRegion(cur_region);
          }
          
          render_composition->initBoneMap();
          render_composition->initRegionsMap();
          
          for(auto& cur_region : regions) {
              cur_region->initFastNormalWeightMap(render_composition->getBonesMap());
          }
          
          render_composition->resetToWorldRestPts();
  
          // Fill up available animation names
          JsonNode * json_anim_base = GetJSONLevelNodeFromKey(*json_root, "animation");
          animation_names = GetJSONKeysFromNode(*json_anim_base);
  
  		// Fill up uv swap packets
  		
  		JsonNode * json_uv_swap_base = GetJSONLevelNodeFromKey(*json_root, "uv_swap_items");
  		if (json_uv_swap_base)
  		{
  			uv_swap_packets = FillSwapUVPacketMap(*json_uv_swap_base);
  		}
  
  		// Load Anchor Points
  		JsonNode * anchor_point_base = GetJSONLevelNodeFromKey(*json_root, "anchor_points_items");
  		if (anchor_point_base)
  		{
  			anchor_point_map = FillAnchorPointMap(*anchor_point_base);
  		}
      }
  
      
      // CreatureAnimation class
      CreatureAnimation::CreatureAnimation(CreatureLoadDataPacket& load_data,
                                           const std::string& name_in)
      : name(name_in)
      {
              LoadFromData(name_in, load_data);
      }
      
      CreatureAnimation::~CreatureAnimation()
      {
          if(!cache_pts.empty())
          {
              for(auto pt_pool : cache_pts)
              {
                  delete [] pt_pool;
              }
          }
      }
      
      float CreatureAnimation::getStartTime() const
      {
          return start_time;
      }
      
      float CreatureAnimation::getEndTime() const
      {
          return end_time;
      }
  
  	void CreatureAnimation::setStartTime(int value_in)
  	{
  		start_time = (float)value_in;
  	}
  
  	void CreatureAnimation::setEndTime(int value_in)
  	{
  		end_time = (float)value_in;
  	}
      
      meshBoneCacheManager&
      CreatureAnimation::getBonesCache()
      {
          return bones_cache;
      }
      
      meshDisplacementCacheManager&
      CreatureAnimation::getDisplacementCache()
      {
          return displacement_cache;
      }
      
      meshUVWarpCacheManager&
      CreatureAnimation::getUVWarpCache()
      {
          return uv_warp_cache;
      }
  
  	meshOpacityCacheManager& 
  	CreatureAnimation::getOpacityCache()
  	{
  		return opacity_cache;
  	}
      
      const std::string&
      CreatureAnimation::getName() const
      {
          return name;
      }
  
      void
      CreatureAnimation::LoadFromData(const std::string& name_in,
                                      CreatureLoadDataPacket& load_data)
      {
          JsonNode * json_root = load_data.base_node.toNode();
          JsonNode * json_anim_base = GetJSONLevelNodeFromKey(*json_root, "animation");
          JsonNode * json_clip = GetJSONNodeFromKey(*json_anim_base, name_in);
          
          std::pair<int, int> start_end_times = GetStartEndTimes(*json_clip, "bones");
          start_time = (float)start_end_times.first;
          end_time = (float)start_end_times.second;
          
          // bone animation
          FillBoneCache(*json_clip,
                        "bones",
                        (int)start_time,
  					  (int)end_time,
                        bones_cache);
          
          // mesh deformation animation
          FillDeformationCache(*json_clip,
                               "meshes",
  							 (int)start_time,
  							 (int)end_time,
                               displacement_cache);
          
          // uv swapping animation
          FillUVSwapCache(*json_clip,
                          "uv_swaps",
  						(int)start_time,
  						(int)end_time,
                          uv_warp_cache);
  
  		// opacity animation
  		FillOpacityCache(*json_clip,
  			"mesh_opacities",
  			(int)start_time,
  			(int)end_time,
  			opacity_cache);
      }
      
      bool
      CreatureAnimation::hasCachePts() const
      {
          return !cache_pts.empty();
      }
      
      std::vector<glm::float32 *>&
      CreatureAnimation::getCachePts()
      {
          return cache_pts;
      }
  
  	void 
  	CreatureAnimation::clearCachePts()
  	{
  		for (size_t i = 0; i < cache_pts.size(); i++)
  		{
  			delete[] cache_pts[i];
  		}
  
  		cache_pts.clear();
  	}
      
      int
      CreatureAnimation::getIndexByTime(int time_in) const
      {
          int retval = time_in - (int)start_time;
          retval = clipNum(retval, 0, (int)cache_pts.size() - 1);
          
          return retval;
      }
      
      void
      CreatureAnimation::poseFromCachePts(float time_in, glm::float32 * target_pts, int num_pts)
      {
          int cur_floor_time = getIndexByTime((int)floorf(time_in));
          int cur_ceil_time = getIndexByTime((int)ceilf(time_in));
          float cur_ratio = (time_in - (float)floorf(time_in));
          
          glm::float32 * set_pt = target_pts;
          glm::float32 * floor_pts = cache_pts[cur_floor_time];
          glm::float32 * ceil_pts = cache_pts[cur_ceil_time];
          
          for(int i = 0; i < num_pts; i++)
          {
  
              
              set_pt[0] = ((1.0f - cur_ratio) * floor_pts[0]) + (cur_ratio * ceil_pts[0]);
              set_pt[1] = ((1.0f - cur_ratio) * floor_pts[1]) + (cur_ratio * ceil_pts[1]);
              set_pt[2] = ((1.0f - cur_ratio) * floor_pts[2]) + (cur_ratio * ceil_pts[2]);
  
              set_pt += 3;
              floor_pts += 3;
              ceil_pts += 3;
          }
          
      }
      
      // CreatureManager class
      CreatureManager::CreatureManager(std::shared_ptr<CreatureModule::Creature> target_creature_in)
      : target_creature(target_creature_in), is_playing(false), run_time(0), time_scale(30.0),
          do_blending(false),
          blending_factor(0), mirror_y(false), use_custom_time_range(false),
          custom_start_time(0), custom_end_time(0), should_loop(true),
          do_auto_blending(false), auto_blend_delta(0.1f), do_point_caching(false)
      {
          for(int i = 0; i < 2; i++) {
              blend_render_pts[i] = NULL;
          }
      }
      
      CreatureManager::~CreatureManager()
      {
          for(int i = 0; i < 2; i++) {
              if(blend_render_pts[i] != NULL) {
                  delete blend_render_pts[i];
              }
          }
      }
      
      void
      CreatureManager::AddAnimation(std::shared_ptr<CreatureModule::CreatureAnimation> animation_in)
      {
          animations[animation_in->getName()] = animation_in;
  		active_blend_run_times[animation_in->getName()] = animation_in->getStartTime();
      }
      
      void
      CreatureManager::CreateAnimation(CreatureLoadDataPacket& load_data,
                                       const std::string& name_in)
      {
          auto new_animation = std::shared_ptr<CreatureModule::CreatureAnimation>(new CreatureAnimation(load_data,
                                                                                                        name_in));
          AddAnimation(new_animation);
      }
  
      
      CreatureModule::CreatureAnimation *
      CreatureManager::GetAnimation(const std::string name_in)
      {
          if(animations.count(name_in) > 0) {
              return animations[name_in].get();
          }
          
          return NULL;
      }
  
      CreatureModule::Creature *
      CreatureManager::GetCreature()
      {
          return target_creature.get();
      }
  
      void
      CreatureManager::SetActiveAnimationName(const std::string& name_in, bool check_already_active)
      {
          if(check_already_active)
          {
              if(active_animation_name == name_in)
              {
                  // animation clip already set to this name, so just return
                  return;
              }
          }
          
          if(animations.count(name_in) > 0) {
              active_animation_name = name_in;
              auto& cur_animation = animations[active_animation_name];
              run_time = cur_animation->getStartTime();
              
  			UpdateRegionSwitches(name_in);
          }
      }
  
  	void 
  	CreatureManager::UpdateRegionSwitches(const std::string& animation_name_in)
  	{
  		if (animations.count(animation_name_in) > 0) {
  			auto& cur_animation = animations[animation_name_in];
  
  			auto& displacement_cache_manager = cur_animation->getDisplacementCache();
  			std::vector<meshDisplacementCache>& displacement_table =
  				displacement_cache_manager.getCacheTable()[0];
  
  			auto& uv_warp_cache_manager = cur_animation->getUVWarpCache();
  			std::vector<meshUVWarpCache>& uv_swap_table =
  				uv_warp_cache_manager.getCacheTable()[0];
  
  			meshRenderBoneComposition * render_composition =
  				target_creature->GetRenderComposition();
  			std::vector<meshRenderRegion *>& all_regions = render_composition->getRegions();
  
  			int index = 0;
  			for (auto& cur_region : all_regions) {
  				// Setup active or inactive displacements
  				bool use_local_displacements = !displacement_table[index].getLocalDisplacements().empty();
  				bool use_post_displacements = !displacement_table[index].getPostDisplacements().empty();
  				cur_region->setUseLocalDisplacements(use_local_displacements);
  				cur_region->setUsePostDisplacements(use_post_displacements);
  
  				// Setup active or inactive uv swaps
  				cur_region->setUseUvWarp(uv_swap_table[index].getEnabled());
  
  				index++;
  			}
  		}
  	}
  
      const std::string&
      CreatureManager::GetActiveAnimationName() const
      {
          return active_animation_name;
      }
  
      std::unordered_map<std::string, std::shared_ptr<CreatureModule::CreatureAnimation> >&
      CreatureManager::GetAllAnimations()
      {
          return animations;
      }
  
      bool
      CreatureManager::GetIsPlaying() const
      {
          return is_playing;
      }
      
      void
      CreatureManager::SetIsPlaying(bool flag_in)
      {
          is_playing = flag_in;
      }
      
      void
      CreatureManager::SetTimeScale(float scale_in)
      {
          time_scale = scale_in;
      }
      
      void
      CreatureManager::SetBlending(bool flag_in)
      {
          do_blending = flag_in;
          
          if(do_blending) {
              if(blend_render_pts[0] == NULL) {
                  blend_render_pts[0] = new glm::float32[target_creature->GetTotalNumPoints() * 3];
              }
              
              if(blend_render_pts[1] == NULL) {
                  blend_render_pts[1] = new glm::float32[target_creature->GetTotalNumPoints() * 3];
              }
          }
      }
      
      void
      CreatureManager::SetBlendingAnimations(const std::string& name_1, const std::string& name_2)
      {
          active_blend_animation_names[0] = name_1;
          active_blend_animation_names[1] = name_2;
      }
      
      void
      CreatureManager::SetAutoBlending(bool flag_in)
      {
          do_auto_blending = flag_in;
          SetBlending(flag_in);
          
          if(do_auto_blending)
          {
              AutoBlendTo(active_animation_name, 0.1f);
          }
      }
      
      void
      CreatureManager::AutoBlendTo(const std::string& animation_name_in, float blend_delta)
      {
          if(animation_name_in == auto_blend_names[1])
          {
              // already blending to that so just return
              return;
          }
  
  		ResetBlendTime(animation_name_in);
          
          auto_blend_delta = blend_delta;
          auto_blend_names[0] = active_animation_name;
          auto_blend_names[1] = animation_name_in;
          blending_factor = 0;
  
  		active_animation_name = animation_name_in;
          
          SetBlendingAnimations(auto_blend_names[0], auto_blend_names[1]);
      }
  
  	void CreatureManager::SetDoPointCache(bool flag_in)
  	{
  		do_point_caching = flag_in;
  	}
  
  	bool CreatureManager::GetDoPointCache() const
  	{
  		return do_point_caching;
  	}
  
  	void 
  	CreatureManager::ResetBlendTime(const std::string& name_in)
  	{
  		auto& cur_animation = animations[name_in];
  		active_blend_run_times[name_in] = cur_animation->getStartTime();
  	}
  
      void
      CreatureManager::ResetToStartTimes()
      {
          if(animations.count(active_animation_name) <= 0)
          {
              return;
          }
          
  		// reset non blend time
          auto& cur_animation = animations[active_animation_name];
          run_time = cur_animation->getStartTime();
  
  		// reset blend times too
  		for (auto& blend_time_data : active_blend_run_times)
  		{
  			ResetBlendTime(blend_time_data.first);
  		}
      }
  
      void
      CreatureManager::SetBlendingFactor(float value_in)
      {
          blending_factor = value_in;
      }
  
  	void 
  	CreatureManager::ClearPointCache(const std::string& animation_name_in)
  	{
  		if (animations.count(animation_name_in) == 0)
  		{
  			return;
  		}
  
  		auto cur_animation = animations[animation_name_in];
  		if (cur_animation->hasCachePts())
  		{
  			cur_animation->clearCachePts();
  		}
  	}
      
      void
  	CreatureManager::MakePointCache(const std::string& animation_name_in, int gap_step)
      {
  		if (animations.count(animation_name_in) == 0)
  		{
  			return;
  		}
  
  		if (gap_step < 1) {
  			gap_step = 1;
  		}
  
          float store_run_time = getRunTime();
          auto cur_animation = animations[animation_name_in];
          if(cur_animation->hasCachePts())
          {
              // cache already generated, just exit
              return;
          }
          
          std::vector<glm::float32 *>& cache_pts_list = cur_animation->getCachePts();
  		int array_size = target_creature->GetTotalNumPoints() * 3;
  
          UpdateRegionSwitches(animation_name_in);
  
          //for(int i = (int)cur_animation->getStartTime(); i <= (int)cur_animation->getEndTime(); i++)
  		int i = (int)cur_animation->getStartTime();
  		while (true)
          {
              run_time = (float)i;
  			auto new_pts = new glm::float32[array_size];
              PoseCreature(animation_name_in, new_pts, getRunTime());
              
  			int real_step = gap_step;
  			if (i + real_step > cur_animation->getEndTime())
  			{
  				real_step = (int)cur_animation->getEndTime() - i;
  			}
  
  			bool firstCase = real_step > 1;
  			bool secondCase = cache_pts_list.size() >= 1;
  			if (firstCase && secondCase)
  			{
  				// fill in the gaps
  				glm::float32 * prev_pts = cache_pts_list[cache_pts_list.size() - 1];
  				for (int j = 0; j < real_step; j++)
  				{
  					float factor = (float)j / (float)real_step;
  					glm::float32 * gap_pts = interpFloatArray(prev_pts, new_pts, factor, array_size);
  					cache_pts_list.push_back(gap_pts);
  				}
  			}
  
  			cache_pts_list.push_back(new_pts);
  			i += real_step;
  
  			if (i > cur_animation->getEndTime() || real_step == 0)
  			{
  				break;
  			}
          }
          
          setRunTime(store_run_time);
      }
      
  	glm::float32 * 
  	CreatureManager::interpFloatArray(glm::float32 * first_list, glm::float32 * second_list, float factor, int array_size)
  	{
  		glm::float32 * ret_array = new glm::float32[array_size];
  		for (int i = 0; i < array_size; i++)
  		{
  			float new_val = ((1.0f - factor) * first_list[i]) + (factor * second_list[i]);
  			ret_array[i] = new_val;
  		}
  
  		return ret_array;
  	}
  
      void
      CreatureManager::PoseCreature(const std::string& animation_name_in,
                                    glm::float32 * target_pts,
  								  float input_run_time)
      {
          if(animations.count(animation_name_in) <= 0)
          {
              std::cerr<<"CreatureManager::PoseCreature() - Animation not found: "<<animation_name_in<<std::endl;
              throw "CreatureManager::PoseCreature() - Invalid animation name!";
              return;
          }
          
          auto& cur_animation = animations[animation_name_in];
          
          auto& bone_cache_manager = cur_animation->getBonesCache();
          auto& displacement_cache_manager = cur_animation->getDisplacementCache();
          auto& uv_warp_cache_manager = cur_animation->getUVWarpCache();
  		auto& opacity_cache_manager = cur_animation->getOpacityCache();
          
          meshRenderBoneComposition * render_composition =
          target_creature->GetRenderComposition();
          
          // Extract values from caches
          std::unordered_map<std::string, meshBone *>& bones_map =
          render_composition->getBonesMap();
          std::unordered_map<std::string, meshRenderRegion *>& regions_map =
          render_composition->getRegionsMap();
          
  		bone_cache_manager.retrieveValuesAtTime(input_run_time,
                                                  bones_map);
  
  		AlterBonesByAnchor(bones_map, animation_name_in);
          
          if(bones_override_callback)
          {
              bones_override_callback(bones_map);
          }
          
  		displacement_cache_manager.retrieveValuesAtTime(input_run_time,
                                                          regions_map);
  		uv_warp_cache_manager.retrieveValuesAtTime(input_run_time,
                                                     regions_map);
  		opacity_cache_manager.retrieveValuesAtTime(input_run_time,
  													regions_map);
          
          
          // Do posing, decide if we are blending or not
          std::vector<meshRenderRegion *>& cur_regions =
          render_composition->getRegions();
          
          render_composition->updateAllTransforms(false);
          for(size_t j = 0; j < cur_regions.size(); j++) {
              meshRenderRegion * cur_region = cur_regions[j];
              
              int cur_pt_index = cur_region->getStartPtIndex();
              cur_region->poseFastFinalPts(target_pts + (cur_pt_index * 3));
          }
  
      }
      
      void
      CreatureManager::ProcessAutoBlending()
      {
          // process blending factor
          blending_factor += auto_blend_delta;
          if(blending_factor > 1)
          {
              blending_factor = 1;
          }
      }
  
  	void 
  		CreatureManager::PoseJustBones(const std::string& animation_name_in,
  											glm::float32 * target_pts,
  											float input_run_time)
  	{
  		if (animations.count(animation_name_in) <= 0)
  		{
  			std::cerr << "CreatureManager::PoseCreature() - Animation not found: " << animation_name_in << std::endl;
  			throw "CreatureManager::PoseCreature() - Invalid animation name!";
  			return;
  		}
  
  		auto& cur_animation = animations[animation_name_in];
  
  		auto& bone_cache_manager = cur_animation->getBonesCache();
  		auto& opacity_cache_manager = cur_animation->getOpacityCache();
  
  		meshRenderBoneComposition * render_composition =
  			target_creature->GetRenderComposition();
  
  		// Extract values from caches
  		std::unordered_map<std::string, meshBone *>& bones_map =
  			render_composition->getBonesMap();
  		std::unordered_map<std::string, meshRenderRegion *>& regions_map =
  			render_composition->getRegionsMap();
  
  		bone_cache_manager.retrieveValuesAtTime(input_run_time,
  			bones_map);
  
  		AlterBonesByAnchor(bones_map, animation_name_in);
  
  		if (bones_override_callback)
  		{
  			bones_override_callback(bones_map);
  		}
  
  		opacity_cache_manager.retrieveValuesAtTime(input_run_time,
  			regions_map);
  
  		JustRunUVWarps(animation_name_in, input_run_time);
  	}
  
  	void CreatureManager::JustRunUVWarps(const std::string& animation_name_in, float input_run_time)
  	{
  		auto& cur_animation = animations[animation_name_in];
  
  		meshRenderBoneComposition * render_composition =
  			target_creature->GetRenderComposition();
  		std::unordered_map<std::string, meshRenderRegion *>& regions_map =
  			render_composition->getRegionsMap();
  
  		auto& uv_warp_cache_manager = cur_animation->getUVWarpCache();
  		uv_warp_cache_manager.retrieveValuesAtTime(input_run_time,
  			regions_map);
  		std::vector<meshRenderRegion *>& all_regions = render_composition->getRegions();
  
  		int index = 0;
  		for (auto& cur_region : all_regions) {
  			if (cur_region->getUseUvWarp())
  			{
  				cur_region->runUvWarp();
  			}
  		}
  	}
  
  	void 
  	CreatureManager::RunUVItemSwap()
  	{
  		meshRenderBoneComposition * render_composition =
  			target_creature->GetRenderComposition();
  		std::unordered_map<std::string, meshRenderRegion *>& regions_map =
  			render_composition->getRegionsMap();
  
  		auto& swap_packets = target_creature->GetUvSwapPackets();
  		auto& active_swap_actions = target_creature->GetActiveItemSwaps();
  
  		if (swap_packets.empty() || active_swap_actions.empty())
  		{
  			return;
  		}
  
  		for(auto& cur_action : active_swap_actions)
  		{
  			if (regions_map.count(cur_action.first) > 0)
  			{
  				auto swap_tag = cur_action.second;
  				auto& swap_list = swap_packets.at(cur_action.first);
  				for (auto& cur_item : swap_list)
  				{
  					if (cur_item.tag == swap_tag)
  					{
  						// Perfrom UV Item Swap
  						auto cur_region = regions_map[cur_action.first];
  						cur_region->setUvWarpLocalOffset(cur_item.local_offset);
  						cur_region->setUvWarpGlobalOffset(cur_item.global_offset);
  						cur_region->setUvWarpScale(cur_item.scale);
  						cur_region->runUvWarp();
  
  						break;
  					}
  				}
  			}
  		}
  	}
  
  	void CreatureManager::AlterBonesByAnchor(std::unordered_map<std::string, meshBone*>& bones_map, const std::string & animation_name_in)
  	{
  		if (target_creature->GetAnchorPointsActive() == false)
  		{
  			return;
  		}
  
  		auto anchor_point = target_creature->GetAnchorPoint(animation_name_in);
  		for (auto& cur_data : bones_map)
  		{
  			auto cur_bone = cur_data.second;
  			auto start_pt = cur_bone->getWorldStartPt();
  			auto end_pt = cur_bone->getWorldEndPt();
  
  			start_pt -= glm::vec4(anchor_point.x, anchor_point.y, 0, 0);
  			end_pt -= glm::vec4(anchor_point.x, anchor_point.y, 0, 0);
  
  			cur_bone->setWorldStartPt(start_pt);
  			cur_bone->setWorldEndPt(end_pt);
  		}
  	}
  
      void
      CreatureManager::Update(float delta)
      {
          if(!is_playing)
          {
              return;
          }
          
          increRunTime(delta * time_scale);
          
          if(do_auto_blending)
          {
              ProcessAutoBlending();
  			// process run times for blends
  			increAutoBlendRuntimes(delta * time_scale);
          }
          
          if(do_blending)
          {
              for(int i = 0; i < 2; i++) {
  				auto& cur_animation_name = active_blend_animation_names[i];
  				auto& cur_animation = animations[cur_animation_name];
  				auto& cur_animation_run_time = active_blend_run_times[cur_animation_name];
  
                  if(cur_animation->hasCachePts() && do_point_caching)
                  {
  					UpdateRegionSwitches(cur_animation_name);
  					cur_animation->poseFromCachePts(cur_animation_run_time, blend_render_pts[i], target_creature->GetTotalNumPoints());
  					PoseJustBones(cur_animation_name, blend_render_pts[i], cur_animation_run_time);
                  }
                  else {
  					UpdateRegionSwitches(active_blend_animation_names[i]);
  					PoseCreature(active_blend_animation_names[i], blend_render_pts[i], cur_animation_run_time);
                  }
              }
              
              for(int j = 0; j < target_creature->GetTotalNumPoints() * 3; j++)
              {
                  glm::float32 * set_data = target_creature->GetRenderPts() + j;
                  glm::float32 * read_data_1 = blend_render_pts[0] + j;
                  glm::float32 * read_data_2 = blend_render_pts[1] + j;
                  
                  *set_data = ((1.0f - blending_factor) * (*read_data_1)) +
                  (blending_factor * (*read_data_2));
              }
          }
          else {
              auto& cur_animation = animations[active_animation_name];
              if(cur_animation->hasCachePts() && do_point_caching)
              {
  				cur_animation->poseFromCachePts(getRunTime(), target_creature->GetRenderPts(), target_creature->GetTotalNumPoints());
  				PoseJustBones(active_animation_name, target_creature->GetRenderPts(), getRunTime());
              }
              else {
  				PoseCreature(active_animation_name, target_creature->GetRenderPts(), getRunTime());
              }
          }
  
  		RunUVItemSwap();
          
          if(mirror_y)
          {
              glm::float32 * set_data = target_creature->GetRenderPts();
              for(int j = 0; j < target_creature->GetTotalNumPoints(); j++)
              {
                  set_data[0] = -set_data[0];
                  set_data += 3;
              }
          }
      }
      
      void
      CreatureManager::SetMirrorY(bool flag_in)
      {
          mirror_y = flag_in;
      }
      
      std::string
      CreatureManager::IsContactBone(const glm::vec2& pt_in,
                                     const glm::mat4& creature_xform,
                                     float radius) const
      {
          std::string ret_name;
          meshBone * cur_bone = target_creature->GetRenderComposition()->getRootBone();
          
          glm::vec4 local_pt = glm::inverse(creature_xform) * glm::vec4(pt_in, 0, 1);
          glm::vec2 real_pt(local_pt.x, local_pt.y);
          if(mirror_y)
          {
              real_pt.x = -real_pt.x;
          }
          
          return ProcessContactBone(real_pt, radius, cur_bone);
      }
      
      std::string
      CreatureManager::ProcessContactBone(const glm::vec2& pt_in,
                                          float radius,
                                          meshBone * bone_in) const
      {
          std::string ret_name;
          glm::vec2 cur_vec = glm::vec2(bone_in->getWorldEndPt() - bone_in->getWorldStartPt());
          float cur_length = glm::length(cur_vec);
          glm::vec2 unit_vec = glm::normalize(cur_vec);
          glm::vec2 norm_vec(unit_vec.y, unit_vec.x);
          
          glm::vec2 rel_vec = pt_in - glm::vec2(bone_in->getWorldStartPt());
          float proj = glm::dot(rel_vec, unit_vec);
          
          if( (proj >= 0) && (proj <= cur_length))
          {
              float norm_proj = glm::dot(rel_vec, norm_vec);
              if(norm_proj <= radius)
              {
                  return bone_in->getKey();
              }
          }
          
          auto& cur_children = bone_in->getChildren();
          for(auto& cur_child : cur_children)
          {
              ret_name = ProcessContactBone(pt_in, radius, cur_child);
              if(!ret_name.empty()) {
                  break;
              }
          }
          
          return ret_name;
      }
  
      float CreatureManager::getRunTime() const
      {
          return run_time;
      }
      
      void
      CreatureManager::setRunTime(float time_in)
      {
          run_time = time_in;
      }
  
  	float 
  	CreatureManager::getActualRunTime() const
  	{
  		if (do_auto_blending)
  		{
  			if (active_blend_run_times.count(active_animation_name)) {
  				return active_blend_run_times.at(active_animation_name);
  			}
  		}
  
  		return run_time;
  	}
  
   
  	float 
  	CreatureManager::correctRunTime(float time_in, const std::string& animation_name)
  	{
  		float ret_time = time_in;
  		auto& cur_animation = animations[animation_name];
  		float anim_start_time = cur_animation->getStartTime();
  		float anim_end_time = cur_animation->getEndTime();
  
  		if (use_custom_time_range)
  		{
  			anim_start_time = (float)custom_start_time;
  			anim_end_time = (float)custom_end_time;
  		}
  
  
  		if (ret_time > anim_end_time)
  		{
  			if (should_loop)
  			{
  				ret_time = anim_start_time;
  			}
  			else {
  				ret_time = anim_end_time;
  			}
  		}
  		else if (ret_time < anim_start_time)
  		{
  			if (should_loop)
  			{
  				ret_time = anim_end_time;
  			}
  			else {
  				ret_time = anim_start_time;
  			}
  		}
  
  		return ret_time;
  	}
  
      void
      CreatureManager::increRunTime(float delta_in)
      {
          if(animations.count(active_animation_name) <= 0)
          {
              return;
          }
          
          run_time += delta_in;
  		run_time = correctRunTime(run_time, active_animation_name);
      }
      
  	void 
  	CreatureManager::increAutoBlendRuntimes(float delta_in)
  	{
  		std::string set_animation_name;
  		for (auto& cur_animation_name : auto_blend_names)
  		{
  			if ((animations.count(cur_animation_name) > 0) 
  				&& (set_animation_name != cur_animation_name))
  			{
  				float& cur_run_time = active_blend_run_times[cur_animation_name];
  				cur_run_time += delta_in;
  				cur_run_time = correctRunTime(cur_run_time, cur_animation_name);
  
  				set_animation_name = cur_animation_name;
  			}
  		}
  	}
  
      void
      CreatureManager::SetShouldLoop(bool flag_in)
      {
          should_loop = flag_in;
      }
  
      void
      CreatureManager::SetUseCustomTimeRange(bool flag_in)
      {
          use_custom_time_range = flag_in;
      }
      
      void
      CreatureManager::SetCustomTimeRange(int start_time_in, int end_time_in)
      {
          custom_start_time = start_time_in;
          custom_end_time = end_time_in;
      }
      
      void
      CreatureManager::SetBonesOverrideCallback(std::function<void (std::unordered_map<std::string, meshBone *>&) >& callback_in)
      {
          bones_override_callback = callback_in;
      }
  
  }
  