From 0b89c58604303d1567542130dcd2df13205d502d Mon Sep 17 00:00:00 2001
From: Tazpn <tazpn@users.sourceforge.net>
Date: Sat, 14 Jul 2007 22:23:00 +0000
Subject: [PATCH] Define NiBSplineTransformInterpolator and unknown for
 NiBSplineData.

---
 .../obj/NiBSplineCompTransformInterpolator.h  |  48 ----
 include/obj/NiBSplineData.h                   |  30 ++-
 include/obj/NiBSplineTransformInterpolator.h  |  98 ++++++++
 src/obj/NiBSplineCompFloatInterpolator.cpp    |   4 +-
 .../NiBSplineCompTransformInterpolator.cpp    |  56 +----
 src/obj/NiBSplineData.cpp                     |  79 +++++--
 src/obj/NiBSplineTransformInterpolator.cpp    | 221 +++++++++++++++++-
 7 files changed, 408 insertions(+), 128 deletions(-)

diff --git a/include/obj/NiBSplineCompTransformInterpolator.h b/include/obj/NiBSplineCompTransformInterpolator.h
index 750a91f5..39451391 100644
--- a/include/obj/NiBSplineCompTransformInterpolator.h
+++ b/include/obj/NiBSplineCompTransformInterpolator.h
@@ -54,42 +54,6 @@ public:
 
 	//--BEGIN MISC CUSTOM CODE--//
 
-	/*!
-	 * Gets the base translation when a translate curve is not defined.
-	 * \return The base translation.
-	 */
-	NIFLIB_API Vector3 GetTranslation() const;
-
-	/*!
-	 * Sets the base translation when a translate curve is not defined.
-	 * \param[in] value The new base translation.
-	 */
-	NIFLIB_API void SetTranslation( Vector3 value );
-
-	/*!
-	 * Gets the base rotation when a translate curve is not defined.
-	 * \return The base rotation.
-	 */
-	NIFLIB_API Quaternion GetRotation() const;
-
-	/*!
-	 * Sets the base rotation when a translate curve is not defined.
-	 * \param[in] value The new base rotation.
-	 */
-	NIFLIB_API void SetRotation( Quaternion value );
-
-	/*!
-	 * Gets the base scale when a translate curve is not defined.
-	 * \return The base scale.
-	 */
-	NIFLIB_API float GetScale() const;
-
-	/*!
-	 * Sets the base scale when a translate curve is not defined.
-	 * \param[in] value The new base scale.
-	 */
-	NIFLIB_API void SetScale( float value );
-
 	/*!
 	 * Gets translate bias.
 	 * \return The translate bias.
@@ -212,18 +176,6 @@ public:
 
 	//--END CUSTOM CODE--//
 protected:
-	/*! Base translation when translate curve not defined. */
-	Vector3 translation;
-	/*! Base rotation when rotation curve not defined. */
-	Quaternion rotation;
-	/*! Base scale when scale curve not defined. */
-	float scale;
-	/*! Starting offset for the translation data. (USHRT_MAX for no data.) */
-	unsigned int translateOffset;
-	/*! Starting offset for the rotation data. (USHRT_MAX for no data.) */
-	unsigned int rotateOffset;
-	/*! Starting offset for the scale data. (USHRT_MAX for no data.) */
-	unsigned int scaleOffset;
 	/*! Translate Bias */
 	float translateBias;
 	/*! Translate Multiplier */
diff --git a/include/obj/NiBSplineData.h b/include/obj/NiBSplineData.h
index 9aa75b04..efd2ec62 100644
--- a/include/obj/NiBSplineData.h
+++ b/include/obj/NiBSplineData.h
@@ -54,11 +54,25 @@ public:
 
 	//--BEGIN MISC CUSTOM CODE--//
 
+	/*!
+	 * Get floats representing the spline data.
+	 * \return The spline data.
+	 */
+	NIFLIB_API vector<float> GetFloatControlPoints() const;
+
+	/*!
+	 * Get Range of signed shorts representing the data scaled by SHRT_MAX.
+	 * \param[in] offset The start of the range.
+	 * \param[in] count The number of control points to get.
+	 * \return The control points that fall within the specified range.
+	 */
+	NIFLIB_API vector<float> GetFloatControlPointRange(int offset, int count) const;
+
 	/*!
 	 * Get Signed shorts representing the spline data scaled by SHRT_MAX.
 	 * \return The spline data.
 	 */
-	NIFLIB_API vector<short> GetControlPoints() const;
+	NIFLIB_API vector<short> GetShortControlPoints() const;
 
 	/*!
 	 * Get Range of signed shorts representing the data scaled by SHRT_MAX.
@@ -66,16 +80,18 @@ public:
 	 * \param[in] count The number of control points to get.
 	 * \return The control points that fall within the specified range.
 	 */
-	NIFLIB_API vector<short> GetControlPointRange(int offset, int count) const;
+	NIFLIB_API vector<short> GetShortControlPointRange(int offset, int count) const;
 
 	//--END CUSTOM CODE--//
 protected:
-	/*! Unknown. Zero? */
-	unsigned int unknownInt;
-	/*! Number of Data Points */
-	mutable unsigned int count;
+	/*! Number of Float Data Points */
+	mutable unsigned int floatCount;
+	/*! Float values representing the control data. */
+	vector<float > floatControlPoints;
+	/*! Number of Short Data Points */
+	mutable unsigned int shortCount;
 	/*! Signed shorts representing the data from 0 to 1 (scaled by SHRT_MAX). */
-	vector<short > controlPoints;
+	vector<short > shortControlPoints;
 public:
 	/*! NIFLIB_HIDDEN function.  For internal use only. */
 	NIFLIB_HIDDEN virtual void Read( istream& in, list<unsigned int> & link_stack, const NifInfo & info );
diff --git a/include/obj/NiBSplineTransformInterpolator.h b/include/obj/NiBSplineTransformInterpolator.h
index c667df60..39566334 100644
--- a/include/obj/NiBSplineTransformInterpolator.h
+++ b/include/obj/NiBSplineTransformInterpolator.h
@@ -53,7 +53,105 @@ public:
 	NIFLIB_API virtual const Type & GetType() const;
 
 	//--BEGIN MISC CUSTOM CODE--//
+
+	/*!
+	* Gets the base translation when a translate curve is not defined.
+	* \return The base translation.
+	*/
+	NIFLIB_API Vector3 GetTranslation() const;
+
+	/*!
+	* Sets the base translation when a translate curve is not defined.
+	* \param[in] value The new base translation.
+	*/
+	NIFLIB_API void SetTranslation( Vector3 value );
+
+	/*!
+	* Gets the base rotation when a translate curve is not defined.
+	* \return The base rotation.
+	*/
+	NIFLIB_API Quaternion GetRotation() const;
+
+	/*!
+	* Sets the base rotation when a translate curve is not defined.
+	* \param[in] value The new base rotation.
+	*/
+	NIFLIB_API void SetRotation( Quaternion value );
+
+	/*!
+	* Gets the base scale when a translate curve is not defined.
+	* \return The base scale.
+	*/
+	NIFLIB_API float GetScale() const;
+
+	/*!
+	* Sets the base scale when a translate curve is not defined.
+	* \param[in] value The new base scale.
+	*/
+	NIFLIB_API void SetScale( float value );
+
+
+	/*!
+	* Retrieves the control quaternion rotation data.
+	* \return A vector containing control Quaternion data which specify rotation over time.
+	*/
+	NIFLIB_API virtual vector< Quaternion > GetQuatRotateControlData() const;
+
+	/*!
+	* Retrieves the control translation data.
+	* \return A vector containing control Vector3 data which specify translation over time.
+	*/
+	NIFLIB_API virtual vector< Vector3 > GetTranslateControlData() const;
+
+	/*!
+	* Retrieves the scale key data.
+	* \return A vector containing control float data which specify scale over time.
+	*/
+	NIFLIB_API virtual 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.
+	*/
+	NIFLIB_API virtual 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.
+	*/
+	NIFLIB_API virtual 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.
+	*/
+	NIFLIB_API virtual vector< Key<float> > SampleScaleKeys(int npoints, int degree) const;
+
+	/*!
+	* Retrieves the number of control points used in the spline curve.
+	* \return The number of control points used in the spline curve.
+	*/
+	NIFLIB_API virtual int GetNumControlPt() const;
 	//--END CUSTOM CODE--//
+protected:
+	/*! Base translation when translate curve not defined. */
+	Vector3 translation;
+	/*! Base rotation when rotation curve not defined. */
+	Quaternion rotation;
+	/*! Base scale when scale curve not defined. */
+	float scale;
+	/*! Starting offset for the translation data. (USHRT_MAX for no data.) */
+	unsigned int translateOffset;
+	/*! Starting offset for the rotation data. (USHRT_MAX for no data.) */
+	unsigned int rotateOffset;
+	/*! Starting offset for the scale data. (USHRT_MAX for no data.) */
+	unsigned int scaleOffset;
 public:
 	/*! NIFLIB_HIDDEN function.  For internal use only. */
 	NIFLIB_HIDDEN virtual void Read( istream& in, list<unsigned int> & link_stack, const NifInfo & info );
diff --git a/src/obj/NiBSplineCompFloatInterpolator.cpp b/src/obj/NiBSplineCompFloatInterpolator.cpp
index ca630f2c..eb4ce701 100644
--- a/src/obj/NiBSplineCompFloatInterpolator.cpp
+++ b/src/obj/NiBSplineCompFloatInterpolator.cpp
@@ -134,7 +134,7 @@ vector< float > NiBSplineCompFloatInterpolator::GetControlData() const
 	if ((offset != USHRT_MAX) && splineData && basisData) { // has translation data
 		int nctrl = basisData->GetNumControlPt();
 		int npts = nctrl * SizeofValue;
-		vector<short> points = splineData->GetControlPointRange(offset, npts);
+		vector<short> points = splineData->GetShortControlPointRange(offset, npts);
 		value.reserve(nctrl);
 		for (int i=0; i<npts; ) {
 			float data = float(points[i++]) / float (32767) * multiplier + bias;
@@ -152,7 +152,7 @@ vector< Key<float> > NiBSplineCompFloatInterpolator::SampleKeys(int npoints, int
 	{
 		int nctrl = basisData->GetNumControlPt();
 		int npts = nctrl * SizeofValue;
-		vector<short> points = splineData->GetControlPointRange(offset, npts);
+		vector<short> points = splineData->GetShortControlPointRange(offset, npts);
 		vector<float> control(npts);
 		vector<float> output(npoints*SizeofValue);
 		for (int i=0, j=0; i<nctrl; ++i) {
diff --git a/src/obj/NiBSplineCompTransformInterpolator.cpp b/src/obj/NiBSplineCompTransformInterpolator.cpp
index 2846b128..15b68361 100644
--- a/src/obj/NiBSplineCompTransformInterpolator.cpp
+++ b/src/obj/NiBSplineCompTransformInterpolator.cpp
@@ -26,7 +26,7 @@ using namespace Niflib;
 //Definition of TYPE constant
 const Type NiBSplineCompTransformInterpolator::TYPE("NiBSplineCompTransformInterpolator", &NiBSplineTransformInterpolator::TYPE );
 
-NiBSplineCompTransformInterpolator::NiBSplineCompTransformInterpolator() : scale(0.0f), translateOffset((unsigned int)0), rotateOffset((unsigned int)0), scaleOffset((unsigned int)0), translateBias(0.0f), translateMultiplier(0.0f), rotationBias(0.0f), rotationMultiplier(0.0f), scaleBias(0.0f), scaleMultiplier(0.0f) {
+NiBSplineCompTransformInterpolator::NiBSplineCompTransformInterpolator() : translateBias(0.0f), translateMultiplier(0.0f), rotationBias(0.0f), rotationMultiplier(0.0f), scaleBias(0.0f), scaleMultiplier(0.0f) {
 	//--BEGIN CONSTRUCTOR CUSTOM CODE--//
 	//--END CUSTOM CODE--//
 }
@@ -49,12 +49,6 @@ void NiBSplineCompTransformInterpolator::Read( istream& in, list<unsigned int> &
 	//--END CUSTOM CODE--//
 
 	NiBSplineTransformInterpolator::Read( in, link_stack, info );
-	NifStream( translation, in, info );
-	NifStream( rotation, in, info );
-	NifStream( scale, in, info );
-	NifStream( translateOffset, in, info );
-	NifStream( rotateOffset, in, info );
-	NifStream( scaleOffset, in, info );
 	NifStream( translateBias, in, info );
 	NifStream( translateMultiplier, in, info );
 	NifStream( rotationBias, in, info );
@@ -71,12 +65,6 @@ void NiBSplineCompTransformInterpolator::Write( ostream& out, const map<NiObject
 	//--END CUSTOM CODE--//
 
 	NiBSplineTransformInterpolator::Write( out, link_map, info );
-	NifStream( translation, out, info );
-	NifStream( rotation, out, info );
-	NifStream( scale, out, info );
-	NifStream( translateOffset, out, info );
-	NifStream( rotateOffset, out, info );
-	NifStream( scaleOffset, out, info );
 	NifStream( translateBias, out, info );
 	NifStream( translateMultiplier, out, info );
 	NifStream( rotationBias, out, info );
@@ -95,12 +83,6 @@ std::string NiBSplineCompTransformInterpolator::asString( bool verbose ) const {
 	stringstream out;
 	unsigned int array_output_count = 0;
 	out << NiBSplineTransformInterpolator::asString();
-	out << "  Translation:  " << translation << endl;
-	out << "  Rotation:  " << rotation << endl;
-	out << "  Scale:  " << scale << endl;
-	out << "  Translate Offset:  " << translateOffset << endl;
-	out << "  Rotate Offset:  " << rotateOffset << endl;
-	out << "  Scale Offset:  " << scaleOffset << endl;
 	out << "  Translate Bias:  " << translateBias << endl;
 	out << "  Translate Multiplier:  " << translateMultiplier << endl;
 	out << "  Rotation Bias:  " << rotationBias << endl;
@@ -131,30 +113,6 @@ std::list<NiObjectRef> NiBSplineCompTransformInterpolator::GetRefs() const {
 
 //--BEGIN MISC CUSTOM CODE--//
 
-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;
 }
@@ -209,7 +167,7 @@ vector< Quaternion > NiBSplineCompTransformInterpolator::GetQuatRotateControlDat
    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<short> points = splineData->GetShortControlPointRange(rotateOffset, npts);
       value.reserve(nctrl);
       for (int i=0; i<npts; ) {
          Quaternion key;
@@ -229,7 +187,7 @@ vector< Vector3 > NiBSplineCompTransformInterpolator::GetTranslateControlData()
    if ((translateOffset != USHRT_MAX) && splineData && basisData) { // has translation data
       int nctrl = basisData->GetNumControlPt();
       int npts = nctrl * SizeofTrans;
-      vector<short> points = splineData->GetControlPointRange(translateOffset, npts);
+      vector<short> points = splineData->GetShortControlPointRange(translateOffset, npts);
       value.reserve(nctrl);
       for (int i=0; i<npts; ) {
          Vector3 key;
@@ -248,7 +206,7 @@ vector< float > NiBSplineCompTransformInterpolator::GetScaleControlData() const
    if ((scaleOffset != USHRT_MAX) && splineData && basisData) { // has translation data
       int nctrl = basisData->GetNumControlPt();
       int npts = nctrl * SizeofScale;
-      vector<short> points = splineData->GetControlPointRange(scaleOffset, npts);
+      vector<short> points = splineData->GetShortControlPointRange(scaleOffset, npts);
       value.reserve(nctrl);
       for (int i=0; i<npts; ) {
          float data = float(points[i++]) / float (32767) * scaleMultiplier + scaleBias;
@@ -264,7 +222,7 @@ vector< Key<Quaternion> > NiBSplineCompTransformInterpolator::SampleQuatRotateKe
    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<short> points = splineData->GetShortControlPointRange(rotateOffset, npts);
       vector<float> control(npts);
       vector<float> output(npoints*SizeofQuat);
       for (int i=0, j=0; i<nctrl; ++i) {
@@ -302,7 +260,7 @@ vector< Key<Vector3> > NiBSplineCompTransformInterpolator::SampleTranslateKeys(i
    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<short> points = splineData->GetShortControlPointRange(translateOffset, npts);
       vector<float> control(npts);
       vector<float> output(npoints*SizeofTrans);
       for (int i=0, j=0; i<nctrl; ++i) {
@@ -338,7 +296,7 @@ vector< Key<float> > NiBSplineCompTransformInterpolator::SampleScaleKeys(int npo
    {
       int nctrl = basisData->GetNumControlPt();
       int npts = nctrl * SizeofScale;
-      vector<short> points = splineData->GetControlPointRange(scaleOffset, npts);
+      vector<short> points = splineData->GetShortControlPointRange(scaleOffset, npts);
       vector<float> control(npts);
       vector<float> output(npoints*SizeofScale);
       for (int i=0, j=0; i<nctrl; ++i) {
diff --git a/src/obj/NiBSplineData.cpp b/src/obj/NiBSplineData.cpp
index c8ecb7ea..133d6669 100644
--- a/src/obj/NiBSplineData.cpp
+++ b/src/obj/NiBSplineData.cpp
@@ -19,7 +19,7 @@ using namespace Niflib;
 //Definition of TYPE constant
 const Type NiBSplineData::TYPE("NiBSplineData", &NiObject::TYPE );
 
-NiBSplineData::NiBSplineData() : unknownInt((unsigned int)0), count((unsigned int)0) {
+NiBSplineData::NiBSplineData() : floatCount((unsigned int)0), shortCount((unsigned int)0) {
 	//--BEGIN CONSTRUCTOR CUSTOM CODE--//
 	//--END CUSTOM CODE--//
 }
@@ -42,11 +42,15 @@ void NiBSplineData::Read( istream& in, list<unsigned int> & link_stack, const Ni
 	//--END CUSTOM CODE--//
 
 	NiObject::Read( in, link_stack, info );
-	NifStream( unknownInt, in, info );
-	NifStream( count, in, info );
-	controlPoints.resize(count);
-	for (unsigned int i1 = 0; i1 < controlPoints.size(); i1++) {
-		NifStream( controlPoints[i1], in, info );
+	NifStream( floatCount, in, info );
+	floatControlPoints.resize(floatCount);
+	for (unsigned int i1 = 0; i1 < floatControlPoints.size(); i1++) {
+		NifStream( floatControlPoints[i1], in, info );
+	};
+	NifStream( shortCount, in, info );
+	shortControlPoints.resize(shortCount);
+	for (unsigned int i1 = 0; i1 < shortControlPoints.size(); i1++) {
+		NifStream( shortControlPoints[i1], in, info );
 	};
 
 	//--BEGIN POST-READ CUSTOM CODE--//
@@ -58,11 +62,15 @@ void NiBSplineData::Write( ostream& out, const map<NiObjectRef,unsigned int> & l
 	//--END CUSTOM CODE--//
 
 	NiObject::Write( out, link_map, info );
-	count = (unsigned int)(controlPoints.size());
-	NifStream( unknownInt, out, info );
-	NifStream( count, out, info );
-	for (unsigned int i1 = 0; i1 < controlPoints.size(); i1++) {
-		NifStream( controlPoints[i1], out, info );
+	shortCount = (unsigned int)(shortControlPoints.size());
+	floatCount = (unsigned int)(floatControlPoints.size());
+	NifStream( floatCount, out, info );
+	for (unsigned int i1 = 0; i1 < floatControlPoints.size(); i1++) {
+		NifStream( floatControlPoints[i1], out, info );
+	};
+	NifStream( shortCount, out, info );
+	for (unsigned int i1 = 0; i1 < shortControlPoints.size(); i1++) {
+		NifStream( shortControlPoints[i1], out, info );
 	};
 
 	//--BEGIN POST-WRITE CUSTOM CODE--//
@@ -76,11 +84,24 @@ std::string NiBSplineData::asString( bool verbose ) const {
 	stringstream out;
 	unsigned int array_output_count = 0;
 	out << NiObject::asString();
-	count = (unsigned int)(controlPoints.size());
-	out << "  Unknown Int:  " << unknownInt << endl;
-	out << "  Count:  " << count << endl;
+	shortCount = (unsigned int)(shortControlPoints.size());
+	floatCount = (unsigned int)(floatControlPoints.size());
+	out << "  Float Count:  " << floatCount << endl;
+	array_output_count = 0;
+	for (unsigned int i1 = 0; i1 < floatControlPoints.size(); i1++) {
+		if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {
+			out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl;
+			break;
+		};
+		if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {
+			break;
+		};
+		out << "    Float Control Points[" << i1 << "]:  " << floatControlPoints[i1] << endl;
+		array_output_count++;
+	};
+	out << "  Short Count:  " << shortCount << endl;
 	array_output_count = 0;
-	for (unsigned int i1 = 0; i1 < controlPoints.size(); i1++) {
+	for (unsigned int i1 = 0; i1 < shortControlPoints.size(); i1++) {
 		if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {
 			out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl;
 			break;
@@ -88,7 +109,7 @@ std::string NiBSplineData::asString( bool verbose ) const {
 		if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {
 			break;
 		};
-		out << "    Control Points[" << i1 << "]:  " << controlPoints[i1] << endl;
+		out << "    Short Control Points[" << i1 << "]:  " << shortControlPoints[i1] << endl;
 		array_output_count++;
 	};
 	return out.str();
@@ -115,17 +136,33 @@ std::list<NiObjectRef> NiBSplineData::GetRefs() const {
 
 //--BEGIN MISC CUSTOM CODE--//
 
-vector<short > NiBSplineData::GetControlPoints() const 
+vector<float> NiBSplineData::GetFloatControlPoints() const 
+{
+	return floatControlPoints;
+}
+
+vector<float> NiBSplineData::GetFloatControlPointRange(int offset, int count) const
+{
+	vector<float> value;
+	if (offset < 0 || count < 0 || ((offset + count) > int(floatControlPoints.size())))
+		throw runtime_error("Invalid offset or count.");
+	vector<float>::const_iterator srcbeg = floatControlPoints.begin(), srcend = floatControlPoints.begin(); 
+	std::advance(srcbeg, offset);
+	std::advance(srcend, offset + count);
+	return vector<float>(srcbeg, srcend);
+}
+
+vector<short > NiBSplineData::GetShortControlPoints() const 
 {
-	return controlPoints;
+	return shortControlPoints;
 }
 
-vector<short > NiBSplineData::GetControlPointRange(int offset, int count) const
+vector<short > NiBSplineData::GetShortControlPointRange(int offset, int count) const
 {
    vector<short> value;
-   if (offset < 0 || count < 0 || ((offset + count) > int(controlPoints.size())))
+   if (offset < 0 || count < 0 || ((offset + count) > int(shortControlPoints.size())))
       throw runtime_error("Invalid offset or count.");
-   vector<short>::const_iterator srcbeg = controlPoints.begin(), srcend = controlPoints.begin(); 
+   vector<short>::const_iterator srcbeg = shortControlPoints.begin(), srcend = shortControlPoints.begin(); 
    std::advance(srcbeg, offset);
    std::advance(srcend, offset + count);
    return vector<short>(srcbeg, srcend);
diff --git a/src/obj/NiBSplineTransformInterpolator.cpp b/src/obj/NiBSplineTransformInterpolator.cpp
index 6eb200a1..4cd746d4 100644
--- a/src/obj/NiBSplineTransformInterpolator.cpp
+++ b/src/obj/NiBSplineTransformInterpolator.cpp
@@ -8,6 +8,12 @@ All rights reserved.  Please see niflib.h for license. */
 //-----------------------------------NOTICE----------------------------------//
 
 //--BEGIN FILE HEAD CUSTOM CODE--//
+#include "../../include/obj/NiBSplineBasisData.h"
+#include "../../include/obj/NiBSplineData.h"
+
+static const int SizeofQuat = 4;
+static const int SizeofTrans = 3;
+static const int SizeofScale = 1;
 //--END CUSTOM CODE--//
 
 #include "../../include/FixLink.h"
@@ -19,7 +25,7 @@ using namespace Niflib;
 //Definition of TYPE constant
 const Type NiBSplineTransformInterpolator::TYPE("NiBSplineTransformInterpolator", &NiBSplineInterpolator::TYPE );
 
-NiBSplineTransformInterpolator::NiBSplineTransformInterpolator() {
+NiBSplineTransformInterpolator::NiBSplineTransformInterpolator() : scale(0.0f), translateOffset((unsigned int)0), rotateOffset((unsigned int)0), scaleOffset((unsigned int)0) {
 	//--BEGIN CONSTRUCTOR CUSTOM CODE--//
 	//--END CUSTOM CODE--//
 }
@@ -42,6 +48,12 @@ void NiBSplineTransformInterpolator::Read( istream& in, list<unsigned int> & lin
 	//--END CUSTOM CODE--//
 
 	NiBSplineInterpolator::Read( in, link_stack, info );
+	NifStream( translation, in, info );
+	NifStream( rotation, in, info );
+	NifStream( scale, in, info );
+	NifStream( translateOffset, in, info );
+	NifStream( rotateOffset, in, info );
+	NifStream( scaleOffset, in, info );
 
 	//--BEGIN POST-READ CUSTOM CODE--//
 	//--END CUSTOM CODE--//
@@ -52,6 +64,12 @@ void NiBSplineTransformInterpolator::Write( ostream& out, const map<NiObjectRef,
 	//--END CUSTOM CODE--//
 
 	NiBSplineInterpolator::Write( out, link_map, info );
+	NifStream( translation, out, info );
+	NifStream( rotation, out, info );
+	NifStream( scale, out, info );
+	NifStream( translateOffset, out, info );
+	NifStream( rotateOffset, out, info );
+	NifStream( scaleOffset, out, info );
 
 	//--BEGIN POST-WRITE CUSTOM CODE--//
 	//--END CUSTOM CODE--//
@@ -64,6 +82,12 @@ std::string NiBSplineTransformInterpolator::asString( bool verbose ) const {
 	stringstream out;
 	unsigned int array_output_count = 0;
 	out << NiBSplineInterpolator::asString();
+	out << "  Translation:  " << translation << endl;
+	out << "  Rotation:  " << rotation << endl;
+	out << "  Scale:  " << scale << endl;
+	out << "  Translate Offset:  " << translateOffset << endl;
+	out << "  Rotate Offset:  " << rotateOffset << endl;
+	out << "  Scale Offset:  " << scaleOffset << endl;
 	return out.str();
 
 	//--BEGIN POST-STRING CUSTOM CODE--//
@@ -87,4 +111,199 @@ std::list<NiObjectRef> NiBSplineTransformInterpolator::GetRefs() const {
 }
 
 //--BEGIN MISC CUSTOM CODE--//
+Vector3 NiBSplineTransformInterpolator::GetTranslation() const {
+	return translation;
+}
+
+void NiBSplineTransformInterpolator::SetTranslation( Vector3 value ) {
+	translation = value;
+}
+
+Quaternion NiBSplineTransformInterpolator::GetRotation() const {
+	return rotation;
+}
+
+void NiBSplineTransformInterpolator::SetRotation( Quaternion value ) {
+	rotation = value;
+}
+
+float NiBSplineTransformInterpolator::GetScale() const {
+	return scale;
+}
+
+void NiBSplineTransformInterpolator::SetScale( float value ) {
+	scale = value;
+}
+
+
+vector< Quaternion > NiBSplineTransformInterpolator::GetQuatRotateControlData() const
+{
+	vector< Quaternion > value;
+	if ((rotateOffset != USHRT_MAX) && splineData && basisData) { // has rotation data
+		int nctrl = basisData->GetNumControlPt();
+		int npts = nctrl * SizeofQuat;
+		vector<float> points = splineData->GetFloatControlPointRange(rotateOffset, npts);
+		value.reserve(nctrl);
+		for (int i=0; i<npts; ) {
+			Quaternion key;
+			key.w = float(points[i++]);
+			key.x = float(points[i++]);
+			key.y = float(points[i++]);
+			key.z = float(points[i++]);
+			value.push_back(key);
+		}
+	}
+	return value;
+}
+
+vector< Vector3 > NiBSplineTransformInterpolator::GetTranslateControlData() const
+{
+	vector< Vector3 > value;
+	if ((translateOffset != USHRT_MAX) && splineData && basisData) { // has translation data
+		int nctrl = basisData->GetNumControlPt();
+		int npts = nctrl * SizeofTrans;
+		vector<float> points = splineData->GetFloatControlPointRange(translateOffset, npts);
+		value.reserve(nctrl);
+		for (int i=0; i<npts; ) {
+			Vector3 key;
+			key.x = float(points[i++]);
+			key.y = float(points[i++]);
+			key.z = float(points[i++]);
+			value.push_back(key);
+		}
+	}
+	return value;
+}
+
+vector< float > NiBSplineTransformInterpolator::GetScaleControlData() const
+{
+	vector< float > value;
+	if ((scaleOffset != USHRT_MAX) && splineData && basisData) { // has translation data
+		int nctrl = basisData->GetNumControlPt();
+		int npts = nctrl * SizeofScale;
+		vector<float> points = splineData->GetFloatControlPointRange(scaleOffset, npts);
+		value.reserve(nctrl);
+		for (int i=0; i<npts; ) {
+			float data = float(points[i++]);
+			value.push_back(data);
+		}
+	}
+	return value;
+}
+
+vector< Key<Quaternion> > NiBSplineTransformInterpolator::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<float> points = splineData->GetFloatControlPointRange(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++]);
+		}
+		if (degree>=nctrl)
+			degree = nctrl - 1;
+		// 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++];
+			key.data.x = output[j++];
+			key.data.y = output[j++];
+			key.data.z = output[j++];
+			value.push_back(key);
+			time += incr;
+		}
+	}
+	return value;
+}
+
+vector< Key<Vector3> > NiBSplineTransformInterpolator::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<float> points = splineData->GetFloatControlPointRange(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++]);
+		}
+		// 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++];
+			key.data.y = output[j++];
+			key.data.z = output[j++];
+			value.push_back(key);
+			time += incr;
+		}
+	}
+	return value;
+}
+
+vector< Key<float> > NiBSplineTransformInterpolator::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<float> points = splineData->GetFloatControlPointRange(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++];
+			value.push_back(key);
+			time += incr;
+		}
+	}
+	return value;
+}
+
+int NiBSplineTransformInterpolator::GetNumControlPt() const
+{
+	if (basisData)
+	{
+		return basisData->GetNumControlPt();
+	}
+	return 0;
+}
+
 //--END CUSTOM CODE--//
-- 
GitLab