diff --git a/gen/obj_defines.h b/gen/obj_defines.h
index 3cd44b8dacefb9da0272f88d1e9d68d990c92e06..da28ac781076fb3b673aeff79bc924e10106e1da 100644
--- a/gen/obj_defines.h
+++ b/gen/obj_defines.h
@@ -2196,7 +2196,12 @@ return InternalGetRefs(); \
 Vector3 translation; \
 Quaternion rotation; \
 float scale; \
-Vector3 unkVector1; \
+ushort translateOffset; \
+ushort unkInt1; \
+ushort rotateOffset; \
+ushort unkInt2; \
+ushort scaleOffset; \
+ushort unkInt3; \
 float translateBias; \
 float translateMultiplier; \
 float rotationBias; \
@@ -2209,7 +2214,7 @@ float scaleMultiplier; \
 #define NI_B_SPLINE_COMP_TRANSFORM_INTERPOLATOR_PARENT NiBSplineInterpolator \
 
 #define NI_B_SPLINE_COMP_TRANSFORM_INTERPOLATOR_CONSTRUCT \
- : scale(0.0f), translateBias(0.0f), translateMultiplier(0.0f), rotationBias(0.0f), rotationMultiplier(0.0f), scaleBias(0.0f), scaleMultiplier(0.0f) \
+ : scale(0.0f), translateOffset((ushort)0), unkInt1((ushort)0), rotateOffset((ushort)0), unkInt2((ushort)0), scaleOffset((ushort)0), unkInt3((ushort)0), translateBias(0.0f), translateMultiplier(0.0f), rotationBias(0.0f), rotationMultiplier(0.0f), scaleBias(0.0f), scaleMultiplier(0.0f) \
 
 #define NI_B_SPLINE_COMP_TRANSFORM_INTERPOLATOR_READ \
 InternalRead( in, link_stack, version, user_version ); \
diff --git a/gen/obj_impl.cpp b/gen/obj_impl.cpp
index df56df67b588405314250c4a86ce9523f61fc19f..8466cbd9edc5f326fa21ddb71b81fb02dc64cd6c 100644
--- a/gen/obj_impl.cpp
+++ b/gen/obj_impl.cpp
@@ -4550,7 +4550,12 @@ void NiBSplineCompTransformInterpolator::InternalRead( istream& in, list<uint> &
 	NifStream( translation, in, version );
 	NifStream( rotation, in, version );
 	NifStream( scale, in, version );
-	NifStream( unkVector1, in, version );
+	NifStream( translateOffset, in, version );
+	NifStream( unkInt1, in, version );
+	NifStream( rotateOffset, in, version );
+	NifStream( unkInt2, in, version );
+	NifStream( scaleOffset, in, version );
+	NifStream( unkInt3, in, version );
 	NifStream( translateBias, in, version );
 	NifStream( translateMultiplier, in, version );
 	NifStream( rotationBias, in, version );
@@ -4564,7 +4569,12 @@ void NiBSplineCompTransformInterpolator::InternalWrite( ostream& out, map<NiObje
 	NifStream( translation, out, version );
 	NifStream( rotation, out, version );
 	NifStream( scale, out, version );
-	NifStream( unkVector1, out, version );
+	NifStream( translateOffset, out, version );
+	NifStream( unkInt1, out, version );
+	NifStream( rotateOffset, out, version );
+	NifStream( unkInt2, out, version );
+	NifStream( scaleOffset, out, version );
+	NifStream( unkInt3, out, version );
 	NifStream( translateBias, out, version );
 	NifStream( translateMultiplier, out, version );
 	NifStream( rotationBias, out, version );
@@ -4579,7 +4589,12 @@ std::string NiBSplineCompTransformInterpolator::InternalAsString( bool verbose )
 	out << "  Translation:  " << translation << endl;
 	out << "  Rotation:  " << rotation << endl;
 	out << "  Scale:  " << scale << endl;
-	out << "  Unk Vector 1:  " << unkVector1 << endl;
+	out << "  Translate Offset:  " << translateOffset << endl;
+	out << "  Unk Int 1:  " << unkInt1 << endl;
+	out << "  Rotate Offset:  " << rotateOffset << endl;
+	out << "  Unk Int 2:  " << unkInt2 << endl;
+	out << "  Scale Offset:  " << scaleOffset << endl;
+	out << "  Unk Int 3:  " << unkInt3 << endl;
 	out << "  Translate Bias:  " << translateBias << endl;
 	out << "  Translate Multiplier:  " << translateMultiplier << endl;
 	out << "  Rotation Bias:  " << rotationBias << endl;
diff --git a/niflib.cpp b/niflib.cpp
index 9e95365811e3ba942fdee4cb5d130cd5dbcd2408..622a49f7bdf0ee67faf3b47990cb839c0d63897f 100644
--- a/niflib.cpp
+++ b/niflib.cpp
@@ -839,7 +839,7 @@ void MergeNifTrees( const Ref<NiNode> & target, const Ref<NiControllerSequence>
 					ctlr->SetStartTime( right->GetStartTime() );
 					ctlr->SetStopTime( right->GetStopTime() );
 					ctlr->SetFrequency( right->GetFrequency() );
-					ctlr->SetPhase( 1.0f ); //TODO:  Is phase somewhere in NiControllerSequence?
+               ctlr->SetPhase( 0.0f ); //TODO:  Is phase somewhere in NiControllerSequence?
 				}
 			}
 		}
diff --git a/obj/NiBSplineBasisData.cpp b/obj/NiBSplineBasisData.cpp
index d3af49cbda07551dc6366ade2499d4169b2b1f73..18b50aac27ad67376c227f52a41503d078f24007 100644
--- a/obj/NiBSplineBasisData.cpp
+++ b/obj/NiBSplineBasisData.cpp
@@ -35,3 +35,11 @@ const Type & NiBSplineBasisData::GetType() const {
 	return TYPE;
 };
 
+uint NiBSplineBasisData::GetNumControlPt() const {
+	return numControlPt;
+}
+
+void NiBSplineBasisData::SetNumControlPt( uint value ) {
+	numControlPt = value;
+}
+
diff --git a/obj/NiBSplineBasisData.h b/obj/NiBSplineBasisData.h
index ee99bff4958f548ce95bea93646c38ea32888c64..345893aa50d4b39bb35699a61bbed7eccd04d6d1 100644
--- a/obj/NiBSplineBasisData.h
+++ b/obj/NiBSplineBasisData.h
@@ -32,6 +32,12 @@ public:
 	virtual list<NiObjectRef> GetRefs() const;
 	virtual const Type & GetType() const;
 
+	/*!
+	 * The number of control points (Usually number of frames for animation).
+	 */
+	uint GetNumControlPt() const;
+	void SetNumControlPt( uint value );
+
 protected:
 	NI_B_SPLINE_BASIS_DATA_MEMBERS
 	STANDARD_INTERNAL_METHODS
diff --git a/obj/NiBSplineCompTransformInterpolator.cpp b/obj/NiBSplineCompTransformInterpolator.cpp
index ebf906ecb3ffc3c5cb43568b9d0558fd73ce6d37..8885e6afea9a6b14f3d68f773163734377239a5c 100644
--- a/obj/NiBSplineCompTransformInterpolator.cpp
+++ b/obj/NiBSplineCompTransformInterpolator.cpp
@@ -2,8 +2,14 @@
 All rights reserved.  Please see niflib.h for licence. */
 
 #include "NiBSplineCompTransformInterpolator.h"
+#include "NiBSplineBasisData.h"
+#include "NiBSplineData.h"
 using namespace Niflib;
 
+static const int SizeofQuat = 4;
+static const int SizeofTrans = 3;
+static const int SizeofScale = 1;
+
 //Definition of TYPE constant
 const Type NiBSplineCompTransformInterpolator::TYPE("NiBSplineCompTransformInterpolator", &NI_B_SPLINE_COMP_TRANSFORM_INTERPOLATOR_PARENT::TypeConst() );
 
@@ -35,3 +41,233 @@ const Type & NiBSplineCompTransformInterpolator::GetType() const {
 	return TYPE;
 };
 
+Vector3 NiBSplineCompTransformInterpolator::GetTranslation() const {
+	return translation;
+}
+
+void NiBSplineCompTransformInterpolator::SetTranslation( Vector3 value ) {
+	translation = value;
+}
+
+Quaternion NiBSplineCompTransformInterpolator::GetRotation() const {
+	return rotation;
+}
+
+void NiBSplineCompTransformInterpolator::SetRotation( Quaternion value ) {
+	rotation = value;
+}
+
+float NiBSplineCompTransformInterpolator::GetScale() const {
+	return scale;
+}
+
+void NiBSplineCompTransformInterpolator::SetScale( float value ) {
+	scale = value;
+}
+
+float NiBSplineCompTransformInterpolator::GetTranslateBias() const {
+	return translateBias;
+}
+
+void NiBSplineCompTransformInterpolator::SetTranslateBias( float value ) {
+	translateBias = value;
+}
+
+float NiBSplineCompTransformInterpolator::GetTranslateMultiplier() const {
+	return translateMultiplier;
+}
+
+void NiBSplineCompTransformInterpolator::SetTranslateMultiplier( float value ) {
+	translateMultiplier = value;
+}
+
+float NiBSplineCompTransformInterpolator::GetRotationBias() const {
+	return rotationBias;
+}
+
+void NiBSplineCompTransformInterpolator::SetRotationBias( float value ) {
+	rotationBias = value;
+}
+
+float NiBSplineCompTransformInterpolator::GetRotationMultiplier() const {
+	return rotationMultiplier;
+}
+
+void NiBSplineCompTransformInterpolator::SetRotationMultiplier( float value ) {
+	rotationMultiplier = value;
+}
+
+float NiBSplineCompTransformInterpolator::GetScaleBias() const {
+	return scaleBias;
+}
+
+void NiBSplineCompTransformInterpolator::SetScaleBias( float value ) {
+	scaleBias = value;
+}
+
+float NiBSplineCompTransformInterpolator::GetScaleMultiplier() const {
+	return scaleMultiplier;
+}
+
+void NiBSplineCompTransformInterpolator::SetScaleMultiplier( float value ) {
+	scaleMultiplier = value;
+}
+
+vector< Quaternion > NiBSplineCompTransformInterpolator::GetQuatRotateControlData() const
+{
+   vector< Quaternion > value;
+   if ((rotateOffset != USHRT_MAX) && splineData && basisData) { // has rotation data
+      int nctrl = basisData->GetNumControlPt();
+      int npts = nctrl * SizeofQuat;
+      vector<short> points = splineData->GetControlPointRange(rotateOffset, npts);
+      value.reserve(nctrl);
+      for (int i=0; i<npts; ) {
+         Quaternion key;
+         key.w = float(points[i++]) / float (32767) * rotationMultiplier + rotationBias;
+         key.x = float(points[i++]) / float (32767) * rotationMultiplier + rotationBias;
+         key.y = float(points[i++]) / float (32767) * rotationMultiplier + rotationBias;
+         key.z = float(points[i++]) / float (32767) * rotationMultiplier + rotationBias;
+         value.push_back(key);
+      }
+   }
+   return value;
+}
+
+vector< Vector3 > NiBSplineCompTransformInterpolator::GetTranslateControlData() const
+{
+   vector< Vector3 > value;
+   if ((translateOffset != USHRT_MAX) && splineData && basisData) { // has translation data
+      int nctrl = basisData->GetNumControlPt();
+      int npts = nctrl * SizeofTrans;
+      vector<short> points = splineData->GetControlPointRange(translateOffset, npts);
+      value.reserve(nctrl);
+      for (int i=0; i<npts; ) {
+         Vector3 key;
+         key.x = float(points[i++]) / float (32767) * translateMultiplier + translateBias;
+         key.y = float(points[i++]) / float (32767) * translateMultiplier + translateBias;
+         key.z = float(points[i++]) / float (32767) * translateMultiplier + translateBias;
+         value.push_back(key);
+      }
+   }
+   return value;
+}
+
+vector< float > NiBSplineCompTransformInterpolator::GetScaleControlData() const
+{
+   vector< float > value;
+   if ((scaleOffset != USHRT_MAX) && splineData && basisData) { // has translation data
+      int nctrl = basisData->GetNumControlPt();
+      int npts = nctrl * SizeofScale;
+      vector<short> points = splineData->GetControlPointRange(scaleOffset, npts);
+      value.reserve(nctrl);
+      for (int i=0; i<npts; ) {
+         float data = float(points[i++]) / float (32767) * scaleMultiplier + scaleBias;
+         value.push_back(data);
+      }
+   }
+   return value;
+}
+
+vector< Key<Quaternion> > NiBSplineCompTransformInterpolator::SampleQuatRotateKeys(int npoints, int degree) const
+{
+   vector< Key<Quaternion> > value;
+   if ((rotateOffset != USHRT_MAX) && splineData && basisData) { // has rotation data
+      int nctrl = basisData->GetNumControlPt();
+      int npts = nctrl * SizeofQuat;
+      vector<short> points = splineData->GetControlPointRange(rotateOffset, npts);
+      vector<float> control(npts);
+      vector<float> output(npoints*SizeofQuat);
+      for (int i=0, j=0; i<nctrl; ++i) {
+         for (int k=0; k<SizeofQuat; ++k)
+            control[i*SizeofQuat + k] = float(points[j++]) / float (32767);
+      }
+      // fit data
+      bspline(nctrl-1, degree+1, SizeofQuat, &control[0], &output[0], npoints);
+
+      // copy to key
+      float time = GetStartTime();
+      float incr = (GetStopTime() - GetStartTime()) / float(npoints) ;
+      value.reserve(npoints);
+      for (int i=0, j=0; i<npoints; i++) {
+         Key<Quaternion> key;
+         key.time = time;
+         key.backward_tangent.Set(1.0f,0.0f,0.0f,0.0f);
+         key.forward_tangent.Set(1.0f,0.0f,0.0f,0.0f); 
+         key.data.w = output[j++] * rotationMultiplier + rotationBias;
+         key.data.x = output[j++] * rotationMultiplier + rotationBias;
+         key.data.y = output[j++] * rotationMultiplier + rotationBias;
+         key.data.z = output[j++] * rotationMultiplier + rotationBias;
+         value.push_back(key);
+         time += incr;
+      }
+   }
+   return value;
+}
+
+vector< Key<Vector3> > NiBSplineCompTransformInterpolator::SampleTranslateKeys(int npoints, int degree) const
+{
+   vector< Key<Vector3> > value;
+   if ((translateOffset != USHRT_MAX) && splineData && basisData) { // has rotation data
+      int nctrl = basisData->GetNumControlPt();
+      int npts = nctrl * SizeofTrans;
+      vector<short> points = splineData->GetControlPointRange(translateOffset, npts);
+      vector<float> control(npts);
+      vector<float> output(npoints*SizeofTrans);
+      for (int i=0, j=0; i<nctrl; ++i) {
+         for (int k=0; k<SizeofTrans; ++k)
+            control[i*SizeofTrans + k] = float(points[j++]) / float (32767);
+      }
+      // fit data
+      bspline(nctrl-1, degree+1, SizeofTrans, &control[0], &output[0], npoints);
+
+      // copy to key
+      float time = GetStartTime();
+      float incr = (GetStopTime() - GetStartTime()) / float(npoints) ;
+      value.reserve(npoints);
+      for (int i=0, j=0; i<npoints; i++) {
+         Key<Vector3> key;
+         key.time = time;
+         key.backward_tangent.Set(0.0f,0.0f,0.0f);
+         key.forward_tangent.Set(0.0f,0.0f,0.0f); 
+         key.data.x = output[j++] * translateMultiplier + translateBias;
+         key.data.y = output[j++] * translateMultiplier + translateBias;
+         key.data.z = output[j++] * translateMultiplier + translateBias;
+         value.push_back(key);
+         time += incr;
+      }
+   }
+   return value;
+}
+
+vector< Key<float> > NiBSplineCompTransformInterpolator::SampleScaleKeys(int npoints, int degree) const
+{
+   vector< Key<float> > value;
+   if ((scaleOffset != USHRT_MAX) && splineData && basisData) // has rotation data
+   {
+      int nctrl = basisData->GetNumControlPt();
+      int npts = nctrl * SizeofScale;
+      vector<short> points = splineData->GetControlPointRange(scaleOffset, npts);
+      vector<float> control(npts);
+      vector<float> output(npoints*SizeofScale);
+      for (int i=0, j=0; i<nctrl; ++i) {
+         control[i] = float(points[j++]) / float (32767);
+      }
+      // fit data
+      bspline(nctrl-1, degree+1, SizeofScale, &control[0], &output[0], npoints);
+
+      // copy to key
+      float time = GetStartTime();
+      float incr = (GetStopTime() - GetStartTime()) / float(npoints) ;
+      value.reserve(npoints);
+      for (int i=0, j=0; i<npoints; i++) {
+         Key<float> key;
+         key.time = time;
+         key.backward_tangent = 0.0f;
+         key.forward_tangent = 0.0f; 
+         key.data = output[j++] * scaleMultiplier + scaleBias;
+         value.push_back(key);
+         time += incr;
+      }
+   }
+   return value;
+}
\ No newline at end of file
diff --git a/obj/NiBSplineCompTransformInterpolator.h b/obj/NiBSplineCompTransformInterpolator.h
index 408d81fd02cfe5e1c6feb0236cab792722f620a6..e8297e00a933c6328246b1fa9a927514f34fc1ca 100644
--- a/obj/NiBSplineCompTransformInterpolator.h
+++ b/obj/NiBSplineCompTransformInterpolator.h
@@ -16,9 +16,8 @@ class NiBSplineCompTransformInterpolator;
 typedef Ref<NiBSplineCompTransformInterpolator> NiBSplineCompTransformInterpolatorRef;
 
 /*!
- * NiBSplineCompTransformInterpolator - Unknown.
+ * NiBSplineCompTransformInterpolator
  */
-
 class NIFLIB_API NiBSplineCompTransformInterpolator : public NI_B_SPLINE_COMP_TRANSFORM_INTERPOLATOR_PARENT {
 public:
 	NiBSplineCompTransformInterpolator();
@@ -35,6 +34,96 @@ public:
 	virtual list<NiObjectRef> GetRefs() const;
 	virtual const Type & GetType() const;
 
+	/*!
+	 * Base translation when translate curve not defined.
+	 */
+	Vector3 GetTranslation() const;
+	void SetTranslation( Vector3 value );
+
+	/*!
+	 * Base rotation when rotation curve not defined.
+	 */
+	Quaternion GetRotation() const;
+	void SetRotation( Quaternion value );
+
+	/*!
+	 * Base scale when scale curve not defined.
+	 */
+	float GetScale() const;
+	void SetScale( float value );
+
+	/*!
+	 * Translate Bias
+	 */
+	float GetTranslateBias() const;
+	void SetTranslateBias( float value );
+
+	/*!
+	 * Translate Multiplier
+	 */
+	float GetTranslateMultiplier() const;
+	void SetTranslateMultiplier( float value );
+
+	/*!
+	 * Rotation Bias
+	 */
+	float GetRotationBias() const;
+	void SetRotationBias( float value );
+
+	/*!
+	 * Rotation Multiplier
+	 */
+	float GetRotationMultiplier() const;
+	void SetRotationMultiplier( float value );
+
+	/*!
+	 * Scale Bias
+	 */
+	float GetScaleBias() const;
+	void SetScaleBias( float value );
+
+	/*!
+	 * Scale Multiplier
+	 */
+	float GetScaleMultiplier() const;
+	void SetScaleMultiplier( float value );
+
+   /*! Retrieves the control quaternion rotation data.
+   * \return A vector containing control Quaternion data which specify rotation over time.
+   */
+   vector< Quaternion > GetQuatRotateControlData() const;
+
+   /*! Retrieves the control translation data.
+   * \return A vector containing control Vector3 data which specify translation over time.
+   */
+   vector< Vector3 > GetTranslateControlData() const;
+
+   /*! Retrieves the scale key data.
+   * \return A vector containing control float data which specify scale over time.
+   */
+   vector< float > GetScaleControlData() const;
+
+   /*! Retrieves the sampled quaternion rotation key data between start and stop time.
+   * \param npoints The number of data points to sample between start and stop time.
+   * \param degree N-th order degree of polynomial used to fit the data.
+   * \return A vector containing Key<Quaternion> data which specify rotation over time.
+   */
+   vector< Key<Quaternion> > SampleQuatRotateKeys(int npoints, int degree) const;
+
+   /*! Retrieves the sampled scale key data between start and stop time.
+   * \param npoints The number of data points to sample between start and stop time.
+   * \param degree N-th order degree of polynomial used to fit the data.
+   * \return A vector containing Key<Vector3> data which specify translation over time.
+   */
+   vector< Key<Vector3> > SampleTranslateKeys(int npoints, int degree) const;
+
+   /*! Retrieves the sampled scale key data between start and stop time.
+   * \param npoints The number of data points to sample between start and stop time.
+   * \param degree N-th order degree of polynomial used to fit the data.
+   * \return A vector containing Key<float> data which specify scale over time.
+   */
+   vector< Key<float> > SampleScaleKeys(int npoints, int degree) const;
+
 protected:
 	NI_B_SPLINE_COMP_TRANSFORM_INTERPOLATOR_MEMBERS
 	STANDARD_INTERNAL_METHODS
diff --git a/obj/NiBSplineData.cpp b/obj/NiBSplineData.cpp
index 9f6decdea2a2b73d60d259025cc571d482a062a9..91cc61bbedaa575cd8a3e07e8a11dc24a01e4eea 100644
--- a/obj/NiBSplineData.cpp
+++ b/obj/NiBSplineData.cpp
@@ -35,3 +35,18 @@ const Type & NiBSplineData::GetType() const {
 	return TYPE;
 };
 
+vector<short > NiBSplineData::GetControlPoints() const 
+{
+	return controlPoints;
+}
+
+vector<short > NiBSplineData::GetControlPointRange(int offset, int count) const
+{
+   vector<short> value;
+   if (offset < 0 || count < 0 || ((offset + count) > int(controlPoints.size())))
+      throw runtime_error("Invalid offset or count.");
+   vector<short>::const_iterator srcbeg = controlPoints.begin(), srcend = controlPoints.begin(); 
+   std::advance(srcbeg, offset);
+   std::advance(srcend, offset + count);
+   return vector<short>(srcbeg, srcend);
+}
\ No newline at end of file
diff --git a/obj/NiBSplineData.h b/obj/NiBSplineData.h
index 1d8bfa4d908ec2bc30ff3cb06cbcf65d44d3f93e..261ea1b65db8002121d15d0f31a69d0f71557349 100644
--- a/obj/NiBSplineData.h
+++ b/obj/NiBSplineData.h
@@ -32,6 +32,16 @@ public:
 	virtual list<NiObjectRef> GetRefs() const;
 	virtual const Type & GetType() const;
 
+   /*!
+   * Get Signed shorts representing the data scaled by SHRT_MAX.
+   */
+   vector<short > GetControlPoints() const;
+
+   /*!
+   * Get Range of signed shorts representing the data scaled by SHRT_MAX.
+   */
+   vector<short > GetControlPointRange(int offset, int count) const;
+
 protected:
 	NI_B_SPLINE_DATA_MEMBERS
 	STANDARD_INTERNAL_METHODS
diff --git a/obj/NiBSplineInterpolator.cpp b/obj/NiBSplineInterpolator.cpp
index ea280a1b6aa58909ad104b39a87a612444f2f359..4a1d1dd29d66363d680976b3cde906f139e23f30 100644
--- a/obj/NiBSplineInterpolator.cpp
+++ b/obj/NiBSplineInterpolator.cpp
@@ -67,4 +67,104 @@ Ref<NiBSplineBasisData > NiBSplineInterpolator::GetBasisData() const {
 
 void NiBSplineInterpolator::SetBasisData( Ref<NiBSplineBasisData > value ) {
 	basisData = value;
-}
\ No newline at end of file
+}
+/*********************************************************************
+Simple b-spline curve algorithm
+
+Copyright 1994 by Keith Vertanen (vertankd@cda.mrs.umn.edu)
+
+Released to the public domain (your mileage may vary)
+
+Found at: Programmers Heaven (www.programmersheaven.com/zone3/cat415/6660.htm)
+Modified by: Theo 
+- reformat and convert doubles to floats
+- removed point structure in favor of arbitrary sized float array
+**********************************************************************/
+static void copy_floats(float* dest, float *src, int l)
+{
+   for (int i=0; i<l; ++i)
+      dest[i] = src[i];
+}
+
+// calculate the blending value
+static float blend(int k, int t, int *u, float v)  
+{
+   float value;
+   if (t==1) {			// base case for the recursion
+      value = ((u[k]<=v) && (v<u[k+1])) ? 1.0f : 0.0f;
+   } else {
+      if ((u[k+t-1]==u[k]) && (u[k+t]==u[k+1]))  // check for divide by zero
+         value = 0;
+      else if (u[k+t-1]==u[k]) // if a term's denominator is zero,use just the other
+         value = (u[k+t] - v) / (u[k+t] - u[k+1]) * blend(k+1, t-1, u, v);
+      else if (u[k+t]==u[k+1])
+         value = (v - u[k]) / (u[k+t-1] - u[k]) * blend(k, t-1, u, v);
+      else
+         value = (v - u[k]) / (u[k+t-1] - u[k]) * blend(k, t-1, u, v) +
+         (u[k+t] - v) / (u[k+t] - u[k+1]) * blend(k+1, t-1, u, v);
+   }
+   return value;
+}
+
+// figure out the knots
+static void compute_intervals(int *u, int n, int t)
+{
+   for (int j=0; j<=n+t; j++) {
+      if (j<t)
+         u[j]=0;
+      else if ((t<=j) && (j<=n))
+         u[j]=j-t+1;
+      else if (j>n)
+         u[j]=n-t+2;  // if n-t=-2 then we're screwed, everything goes to 0
+   }
+}
+
+static void compute_point(int *u, int n, int t, float v, int l, float *control, float *output)
+{
+   // initialize the variables that will hold our output
+   for (int j=0; j<l; j++)
+      output[j] = 0;
+   for (int k=0; k<=n; k++) {
+      float temp = blend(k,t,u,v);  // same blend is used for each dimension coordinate
+      for (int j=0; j<l; j++)
+         output[j] = output[j] + control[k*l + j] * temp;
+   }
+}
+
+/*********************************************************************
+bspline(int n, int t, int l, float *control, float *output, int num_output)
+
+Parameters:
+n          - the number of control points minus 1
+t          - the degree of the polynomial plus 1
+l          - size of control and output float vector block
+control    - control point array made up of float structure
+output     - array in which the calculate spline points are to be put
+num_output - how many points on the spline are to be calculated
+
+Pre-conditions:
+n+2>t  (no curve results if n+2<=t)
+control array contains the number of points specified by n
+output array is the proper size to hold num_output point structures
+
+control and output vectors must be contiguous float arrays
+
+**********************************************************************/
+void NiBSplineInterpolator::bspline(int n, int t, int l, float *control, float *output, int num_output)
+{
+   float *calc = new float[l];
+   int *u = new int[n+t+1];
+   compute_intervals(u, n, t);
+
+   float increment=(float)(n-t+2)/(num_output-1);  // how much parameter goes up each time
+   float interval=0;
+   for (int output_index=0; output_index<num_output-1; output_index++) {
+      compute_point(u, n, t, interval, l, control, calc);
+      copy_floats(&output[output_index*l], calc, l);
+      interval=interval+increment;  // increment our parameter
+   }
+   copy_floats(&output[(num_output-1)*l], &control[n*l], l); // put in the last points
+   delete [] u;
+   delete [] calc;
+}
+
diff --git a/obj/NiBSplineInterpolator.h b/obj/NiBSplineInterpolator.h
index c3feaabdfa895619f47cbb83a3ec6666ed63ac7f..4cef7d35aeecf28b2d45ac321c5aeea5b19de7e8 100644
--- a/obj/NiBSplineInterpolator.h
+++ b/obj/NiBSplineInterpolator.h
@@ -60,6 +60,11 @@ public:
 	Ref<NiBSplineBasisData > GetBasisData() const;
 	void SetBasisData( Ref<NiBSplineBasisData > value );
 
+protected:
+
+   // internal method for bspline calculation in child classes
+   static void bspline(int n, int t, int l, float *control, float *output, int num_output);
+
 protected:
 	NI_B_SPLINE_INTERPOLATOR_MEMBERS
 	STANDARD_INTERNAL_METHODS