void LLLMImpl::moveFlycam()
{
	if(!mOverrideCamera)
	{
		return;
	}

	// simple feathering of the positional data
	mFlycamFeatheredDelta+=(mFlycamDelta-mFlycamFeatheredDelta)/10.0f;

	// simple feathering of the rotational data
	mFlycamFeatheredRot+=(mFlycamRot-mFlycamFeatheredRot)/10.0f;

	LLQuaternion final_flycam_rot=mayaQ(
		mFlycamFeatheredRot.mV[VX],
		mFlycamFeatheredRot.mV[VY],
		mFlycamFeatheredRot.mV[VZ],
		LLQuaternion::XYZ
	);

	final_flycam_rot=final_flycam_rot*mayaQ(
		0.0f,
		0.0f,
		mFlycamFeatheredRot.mV[VZ],
		LLQuaternion::XYZ
	);

	final_flycam_rot=final_flycam_rot*mFlycamInitialRot;

	mFlycamPos+=mFlycamFeatheredDelta*final_flycam_rot;

	LLViewerCamera::instance().setView(LLViewerCamera::instance().getView());
	LLViewerCamera::instance().setOrigin(mFlycamPos);

	LLMatrix3 mat(final_flycam_rot);
	LLViewerCamera::instance().mXAxis=LLVector3(mat.mMatrix[0]);
	LLViewerCamera::instance().mYAxis=LLVector3(mat.mMatrix[1]);
	LLViewerCamera::instance().mZAxis=LLVector3(mat.mMatrix[2]);
}
示例#2
0
	void llquat_test_object_t::test<17>()
	{
		//test case for LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order)
		F32 x = 2.0f;
		F32 y = 1.0f;
		F32 z = 3.0f;
		
		LLQuaternion result = mayaQ(x, y, z, LLQuaternion::XYZ);
		ensure(
			"1. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for XYZ",
			is_approx_equal_fraction(0.0172174f, result.mQ[0], 16) &&
			is_approx_equal_fraction(0.009179f, result.mQ[1], 16) &&
			is_approx_equal_fraction(0.026020f, result.mQ[2], 16) &&
			is_approx_equal_fraction(0.999471f, result.mQ[3], 16));

		LLQuaternion result1 = mayaQ(x, y, z, LLQuaternion::YZX);
		ensure(
			"2. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for XYZ",
			is_approx_equal_fraction(0.017217f, result1.mQ[0], 16) &&
			is_approx_equal_fraction(0.008265f, result1.mQ[1], 16) &&
			is_approx_equal_fraction(0.026324f, result1.mQ[2], 16) &&
			is_approx_equal_fraction(0.999471f, result1.mQ[3], 16));
		
		LLQuaternion result2 = mayaQ(x, y, z, LLQuaternion::ZXY);
		ensure(
			"3. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for ZXY", 
			is_approx_equal_fraction(0.017674f, result2.mQ[0], 16) &&
			is_approx_equal_fraction(0.008265f, result2.mQ[1], 16) &&
			is_approx_equal_fraction(0.026020f, result2.mQ[2], 16) &&
			is_approx_equal_fraction(0.999471f, result2.mQ[3], 16));
											
		LLQuaternion result3 = mayaQ(x, y, z, LLQuaternion::XZY);
		ensure(
			"4. TLLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for XZY", 
			is_approx_equal_fraction(0.017674f, result3.mQ[0], 16) &&
			is_approx_equal_fraction(0.009179f, result3.mQ[1], 16) &&
			is_approx_equal_fraction(0.026020f, result3.mQ[2], 16) &&
			is_approx_equal_fraction(0.999463f, result3.mQ[3], 16));
											
		LLQuaternion result4 = mayaQ(x, y, z, LLQuaternion::YXZ);
		ensure(
			"5. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for YXZ", 
			is_approx_equal_fraction(0.017217f, result4.mQ[0], 16) &&
			is_approx_equal_fraction(0.009179f, result4.mQ[1], 16) &&
			is_approx_equal_fraction(0.026324f, result4.mQ[2], 16) &&
			is_approx_equal_fraction(0.999463f, result4.mQ[3], 16));
											
		LLQuaternion result5 = mayaQ(x, y, z, LLQuaternion::ZYX);
		ensure(
			"6. LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order) failed for ZYX", 
			is_approx_equal_fraction(0.017674f, result5.mQ[0], 16) &&
			is_approx_equal_fraction(0.008265f, result5.mQ[1], 16) &&
			is_approx_equal_fraction(0.026324f, result5.mQ[2], 16) &&
			is_approx_equal_fraction(0.999463f, result5.mQ[3], 16));
	}
示例#3
0
//--------------------------------------------------------------------
// LLPolyMeshSharedData::loadMesh()
//--------------------------------------------------------------------
BOOL LLPolyMeshSharedData::loadMesh( const std::string& fileName )
{
        //-------------------------------------------------------------------------
        // Open the file
        //-------------------------------------------------------------------------
        if(fileName.empty())
        {
                LL_ERRS() << "Filename is Empty!" << LL_ENDL;
                return FALSE;
        }
        LLFILE* fp = LLFile::fopen(fileName, "rb");                     /*Flawfinder: ignore*/
        if (!fp)
        {
                LL_ERRS() << "can't open: " << fileName << LL_ENDL;
                return FALSE;
        }

        //-------------------------------------------------------------------------
        // Read a chunk
        //-------------------------------------------------------------------------
        char header[128];               /*Flawfinder: ignore*/
        if (fread(header, sizeof(char), 128, fp) != 128)
        {
                LL_WARNS() << "Short read" << LL_ENDL;
        }

        //-------------------------------------------------------------------------
        // Check for proper binary header
        //-------------------------------------------------------------------------
        BOOL status = FALSE;
        if ( strncmp(header, HEADER_BINARY, strlen(HEADER_BINARY)) == 0 )       /*Flawfinder: ignore*/
        {
                LL_DEBUGS() << "Loading " << fileName << LL_ENDL;

                //----------------------------------------------------------------
                // File Header (seek past it)
                //----------------------------------------------------------------
                fseek(fp, 24, SEEK_SET);

                //----------------------------------------------------------------
                // HasWeights
                //----------------------------------------------------------------
                U8 hasWeights;
                size_t numRead = fread(&hasWeights, sizeof(U8), 1, fp);
                if (numRead != 1)
                {
                        LL_ERRS() << "can't read HasWeights flag from " << fileName << LL_ENDL;
                        return FALSE;
                }
                if (!isLOD())
                {
                        mHasWeights = (hasWeights==0) ? FALSE : TRUE;
                }

                //----------------------------------------------------------------
                // HasDetailTexCoords
                //----------------------------------------------------------------
                U8 hasDetailTexCoords;
                numRead = fread(&hasDetailTexCoords, sizeof(U8), 1, fp);
                if (numRead != 1)
                {
                        LL_ERRS() << "can't read HasDetailTexCoords flag from " << fileName << LL_ENDL;
                        return FALSE;
                }

                //----------------------------------------------------------------
                // Position
                //----------------------------------------------------------------
                LLVector3 position;
                numRead = fread(position.mV, sizeof(float), 3, fp);
                llendianswizzle(position.mV, sizeof(float), 3);
                if (numRead != 3)
                {
                        LL_ERRS() << "can't read Position from " << fileName << LL_ENDL;
                        return FALSE;
                }
                setPosition( position );

                //----------------------------------------------------------------
                // Rotation
                //----------------------------------------------------------------
                LLVector3 rotationAngles;
                numRead = fread(rotationAngles.mV, sizeof(float), 3, fp);
                llendianswizzle(rotationAngles.mV, sizeof(float), 3);
                if (numRead != 3)
                {
                        LL_ERRS() << "can't read RotationAngles from " << fileName << LL_ENDL;
                        return FALSE;
                }

                U8 rotationOrder;
                numRead = fread(&rotationOrder, sizeof(U8), 1, fp);

                if (numRead != 1)
                {
                        LL_ERRS() << "can't read RotationOrder from " << fileName << LL_ENDL;
                        return FALSE;
                }

                rotationOrder = 0;

                setRotation( mayaQ(     rotationAngles.mV[0],
                                        rotationAngles.mV[1],
                                        rotationAngles.mV[2],
                                        (LLQuaternion::Order)rotationOrder ) );

                //----------------------------------------------------------------
                // Scale
                //----------------------------------------------------------------
                LLVector3 scale;
                numRead = fread(scale.mV, sizeof(float), 3, fp);
                llendianswizzle(scale.mV, sizeof(float), 3);
                if (numRead != 3)
                {
                        LL_ERRS() << "can't read Scale from " << fileName << LL_ENDL;
                        return FALSE;
                }
                setScale( scale );

                //-------------------------------------------------------------------------
                // Release any existing mesh geometry
                //-------------------------------------------------------------------------
                freeMeshData();

                U16 numVertices = 0;

                //----------------------------------------------------------------
                // NumVertices
                //----------------------------------------------------------------
                if (!isLOD())
                {
                        numRead = fread(&numVertices, sizeof(U16), 1, fp);
                        llendianswizzle(&numVertices, sizeof(U16), 1);
                        if (numRead != 1)
                        {
                                LL_ERRS() << "can't read NumVertices from " << fileName << LL_ENDL;
                                return FALSE;
                        }

                        allocateVertexData( numVertices );      

						for (U16 i = 0; i < numVertices; ++i)
						{
							//----------------------------------------------------------------
							// Coords
							//----------------------------------------------------------------
							numRead = fread(&mBaseCoords[i], sizeof(float), 3, fp);
							llendianswizzle(&mBaseCoords[i], sizeof(float), 3);
							if (numRead != 3)
							{
									LL_ERRS() << "can't read Coordinates from " << fileName << LL_ENDL;
									return FALSE;
							}
						}

						for (U16 i = 0; i < numVertices; ++i)
						{
							//----------------------------------------------------------------
							// Normals
							//----------------------------------------------------------------
							numRead = fread(&mBaseNormals[i], sizeof(float), 3, fp);
							llendianswizzle(&mBaseNormals[i], sizeof(float), 3);
							if (numRead != 3)
							{
									LL_ERRS() << " can't read Normals from " << fileName << LL_ENDL;
									return FALSE;
							}
						}

						for (U16 i = 0; i < numVertices; ++i)
						{
							//----------------------------------------------------------------
							// Binormals
							//----------------------------------------------------------------
							numRead = fread(&mBaseBinormals[i], sizeof(float), 3, fp);
							llendianswizzle(&mBaseBinormals[i], sizeof(float), 3);
							if (numRead != 3)
							{
									LL_ERRS() << " can't read Binormals from " << fileName << LL_ENDL;
									return FALSE;
							}
						}

                        //----------------------------------------------------------------
                        // TexCoords
                        //----------------------------------------------------------------
                        numRead = fread(mTexCoords, 2*sizeof(float), numVertices, fp);
                        llendianswizzle(mTexCoords, sizeof(float), 2*numVertices);
                        if (numRead != numVertices)
                        {
                                LL_ERRS() << "can't read TexCoords from " << fileName << LL_ENDL;
                                return FALSE;
                        }

                        //----------------------------------------------------------------
                        // DetailTexCoords
                        //----------------------------------------------------------------
                        if (mHasDetailTexCoords)
                        {
                                numRead = fread(mDetailTexCoords, 2*sizeof(float), numVertices, fp);
                                llendianswizzle(mDetailTexCoords, sizeof(float), 2*numVertices);
                                if (numRead != numVertices)
                                {
                                        LL_ERRS() << "can't read DetailTexCoords from " << fileName << LL_ENDL;
                                        return FALSE;
                                }
                        }

                        //----------------------------------------------------------------
                        // Weights
                        //----------------------------------------------------------------
                        if (mHasWeights)
                        {
                                numRead = fread(mWeights, sizeof(float), numVertices, fp);
                                llendianswizzle(mWeights, sizeof(float), numVertices);
                                if (numRead != numVertices)
                                {
                                        LL_ERRS() << "can't read Weights from " << fileName << LL_ENDL;
                                        return FALSE;
                                }
                        }
                }

                //----------------------------------------------------------------
                // NumFaces
                //----------------------------------------------------------------
                U16 numFaces;
                numRead = fread(&numFaces, sizeof(U16), 1, fp);
                llendianswizzle(&numFaces, sizeof(U16), 1);
                if (numRead != 1)
                {
                        LL_ERRS() << "can't read NumFaces from " << fileName << LL_ENDL;
                        return FALSE;
                }
                allocateFaceData( numFaces );


                //----------------------------------------------------------------
                // Faces
                //----------------------------------------------------------------
                U32 i;
                U32 numTris = 0;
                for (i = 0; i < numFaces; i++)
                {
                        S16 face[3];
                        numRead = fread(face, sizeof(U16), 3, fp);
                        llendianswizzle(face, sizeof(U16), 3);
                        if (numRead != 3)
                        {
                                LL_ERRS() << "can't read Face[" << i << "] from " << fileName << LL_ENDL;
                                return FALSE;
                        }
                        if (mReferenceData)
                        {
                                llassert(face[0] < mReferenceData->mNumVertices);
                                llassert(face[1] < mReferenceData->mNumVertices);
                                llassert(face[2] < mReferenceData->mNumVertices);
                        }
                        
                        if (isLOD())
                        {
                                // store largest index in case of LODs
                                for (S32 j = 0; j < 3; j++)
                                {
                                        if (face[j] > mNumVertices - 1)
                                        {
                                                mNumVertices = face[j] + 1;
                                        }
                                }
                        }
                        mFaces[i][0] = face[0];
                        mFaces[i][1] = face[1];
                        mFaces[i][2] = face[2];

//                      S32 j;
//                      for(j = 0; j < 3; j++)
//                      {
//                              std::vector<S32> *face_list = mVertFaceMap.getIfThere(face[j]);
//                              if (!face_list)
//                              {
//                                      face_list = new std::vector<S32>;
//                                      mVertFaceMap.addData(face[j], face_list);
//                              }
//                              face_list->put(i);
//                      }

                        numTris++;
                }

                LL_DEBUGS() << "verts: " << numVertices 
                         << ", faces: "   << numFaces
                         << ", tris: "    << numTris
                         << LL_ENDL;

                //----------------------------------------------------------------
                // NumSkinJoints
                //----------------------------------------------------------------
                if (!isLOD())
                {
                        U16 numSkinJoints = 0;
                        if ( mHasWeights )
                        {
                                numRead = fread(&numSkinJoints, sizeof(U16), 1, fp);
                                llendianswizzle(&numSkinJoints, sizeof(U16), 1);
                                if (numRead != 1)
                                {
                                        LL_ERRS() << "can't read NumSkinJoints from " << fileName << LL_ENDL;
                                        return FALSE;
                                }
                                allocateJointNames( numSkinJoints );
                        }

                        //----------------------------------------------------------------
                        // SkinJoints
                        //----------------------------------------------------------------
                        for (i=0; i < numSkinJoints; i++)
                        {
                                char jointName[64+1];
                                numRead = fread(jointName, sizeof(jointName)-1, 1, fp);
                                jointName[sizeof(jointName)-1] = '\0'; // ensure nul-termination
                                if (numRead != 1)
                                {
                                        LL_ERRS() << "can't read Skin[" << i << "].Name from " << fileName << LL_ENDL;
                                        return FALSE;
                                }

                                std::string *jn = &mJointNames[i];
                                *jn = jointName;
                        }

                        //-------------------------------------------------------------------------
                        // look for morph section
                        //-------------------------------------------------------------------------
                        char morphName[64+1];
                        morphName[sizeof(morphName)-1] = '\0'; // ensure nul-termination
                        while(fread(&morphName, sizeof(char), 64, fp) == 64)
                        {
                                if (!strcmp(morphName, "End Morphs"))
                                {
                                        // we reached the end of the morphs
                                        break;
                                }
                                LLPolyMorphData* morph_data = new LLPolyMorphData(std::string(morphName));

                                BOOL result = morph_data->loadBinary(fp, this);

                                if (!result)
                                {
                                        delete morph_data;
                                        continue;
                                }

                                mMorphData.insert(morph_data);

                                if (!strcmp(morphName, "Breast_Female_Cleavage"))
                                {
                                        mMorphData.insert(clone_morph_param_cleavage(morph_data,
                                                                                     .75f,
                                                                                     "Breast_Physics_LeftRight_Driven"));
                                }

                                if (!strcmp(morphName, "Breast_Female_Cleavage"))
                                {
                                        mMorphData.insert(clone_morph_param_duplicate(morph_data,
										      "Breast_Physics_InOut_Driven"));
                                }
                                if (!strcmp(morphName, "Breast_Gravity"))
                                {
                                        mMorphData.insert(clone_morph_param_duplicate(morph_data,
										      "Breast_Physics_UpDown_Driven"));
                                }

                                if (!strcmp(morphName, "Big_Belly_Torso"))
                                {
                                        mMorphData.insert(clone_morph_param_direction(morph_data,
										      LLVector3(0,0,0.05f),
										      "Belly_Physics_Torso_UpDown_Driven"));
                                }

                                if (!strcmp(morphName, "Big_Belly_Legs"))
                                {
                                        mMorphData.insert(clone_morph_param_direction(morph_data,
										      LLVector3(0,0,0.05f),
										      "Belly_Physics_Legs_UpDown_Driven"));
                                }

                                if (!strcmp(morphName, "skirt_belly"))
                                {
                                        mMorphData.insert(clone_morph_param_direction(morph_data,
										      LLVector3(0,0,0.05f),
										      "Belly_Physics_Skirt_UpDown_Driven"));
                                }

                                if (!strcmp(morphName, "Small_Butt"))
                                {
                                        mMorphData.insert(clone_morph_param_direction(morph_data,
										      LLVector3(0,0,0.05f),
										      "Butt_Physics_UpDown_Driven"));
                                }
                                if (!strcmp(morphName, "Small_Butt"))
                                {
                                        mMorphData.insert(clone_morph_param_direction(morph_data,
										      LLVector3(0,0.03f,0),
										      "Butt_Physics_LeftRight_Driven"));
                                }
                        }

                        S32 numRemaps;
                        if (fread(&numRemaps, sizeof(S32), 1, fp) == 1)
                        {
                                llendianswizzle(&numRemaps, sizeof(S32), 1);
                                for (S32 i = 0; i < numRemaps; i++)
                                {
                                        S32 remapSrc;
                                        S32 remapDst;
                                        if (fread(&remapSrc, sizeof(S32), 1, fp) != 1)
                                        {
                                                LL_ERRS() << "can't read source vertex in vertex remap data" << LL_ENDL;
                                                break;
                                        }
                                        if (fread(&remapDst, sizeof(S32), 1, fp) != 1)
                                        {
                                                LL_ERRS() << "can't read destination vertex in vertex remap data" << LL_ENDL;
                                                break;
                                        }
                                        llendianswizzle(&remapSrc, sizeof(S32), 1);
                                        llendianswizzle(&remapDst, sizeof(S32), 1);

                                        mSharedVerts[remapSrc] = remapDst;
                                }
                        }
                }

                status = TRUE;
        }
        else
        {
                LL_ERRS() << "invalid mesh file header: " << fileName << LL_ENDL;
                status = FALSE;
        }

        if (0 == mNumJointNames)
        {
                allocateJointNames(1);
        }

        fclose( fp );

        return status;
}
// writes contents to datapacker
BOOL LLBVHLoader::serialize(LLDataPacker& dp)
{
	JointVector::iterator ji;
	KeyVector::iterator ki;
	F32 time;

	// count number of non-ignored joints
	S32 numJoints = 0;
	for (ji=mJoints.begin(); ji!=mJoints.end(); ++ji)
	{
		Joint *joint = *ji;
		if ( ! joint->mIgnore )
			numJoints++;
	}

	// print header
	dp.packU16(KEYFRAME_MOTION_VERSION, "version");
	dp.packU16(KEYFRAME_MOTION_SUBVERSION, "sub_version");
	dp.packS32(mPriority, "base_priority");
	dp.packF32(mDuration, "duration");
	dp.packString(mEmoteName, "emote_name");
	dp.packF32(mLoopInPoint, "loop_in_point");
	dp.packF32(mLoopOutPoint, "loop_out_point");
	dp.packS32(mLoop, "loop");
	dp.packF32(mEaseIn, "ease_in_duration");
	dp.packF32(mEaseOut, "ease_out_duration");
	dp.packU32(mHand, "hand_pose");
	dp.packU32(numJoints, "num_joints");

	for (	ji = mJoints.begin();
			ji != mJoints.end();
			++ji )
	{
		Joint *joint = *ji;
		// if ignored, skip it
		if ( joint->mIgnore )
			continue;

		LLQuaternion first_frame_rot;
		LLQuaternion fixup_rot;

		dp.packString(joint->mOutName, "joint_name");
		dp.packS32(joint->mPriority, "joint_priority");

		// compute coordinate frame rotation
		LLQuaternion frameRot( joint->mFrameMatrix );
		LLQuaternion frameRotInv = ~frameRot;

		LLQuaternion offsetRot( joint->mOffsetMatrix );

		// find mergechild and mergeparent joints, if specified
		LLQuaternion mergeParentRot;
		LLQuaternion mergeChildRot;
		Joint *mergeParent = NULL;
		Joint *mergeChild = NULL;

		JointVector::iterator mji;
		for (mji=mJoints.begin(); mji!=mJoints.end(); ++mji)
		{
			Joint *mjoint = *mji;
			if ( !joint->mMergeParentName.empty() && (mjoint->mName == joint->mMergeParentName) )
			{
				mergeParent = *mji;
			}
			if ( !joint->mMergeChildName.empty() && (mjoint->mName == joint->mMergeChildName) )
			{
				mergeChild = *mji;
			}
		}

		dp.packS32(joint->mNumRotKeys, "num_rot_keys");

		LLQuaternion::Order order = bvhStringToOrder( joint->mOrder );
		S32 outcount = 0;
		S32 frame = 1;
		for (	ki = joint->mKeys.begin();
				ki != joint->mKeys.end();
				++ki )
		{
			if ((frame == 1) && joint->mRelativeRotationKey)
			{
				first_frame_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
				
				fixup_rot.shortestArc(LLVector3::z_axis * first_frame_rot * frameRot, LLVector3::z_axis);
			}

			if (ki->mIgnoreRot)
			{
				frame++;
				continue;
			}

			time = (F32)frame * mFrameTime;

			if (mergeParent)
			{
				mergeParentRot = mayaQ(	mergeParent->mKeys[frame-1].mRot[0], 
										mergeParent->mKeys[frame-1].mRot[1],
										mergeParent->mKeys[frame-1].mRot[2],
										bvhStringToOrder(mergeParent->mOrder) );
				LLQuaternion parentFrameRot( mergeParent->mFrameMatrix );
				LLQuaternion parentOffsetRot( mergeParent->mOffsetMatrix );
				mergeParentRot = ~parentFrameRot * mergeParentRot * parentFrameRot * parentOffsetRot;
			}
			else
			{
				mergeParentRot.loadIdentity();
			}

			if (mergeChild)
			{
				mergeChildRot = mayaQ(	mergeChild->mKeys[frame-1].mRot[0], 
										mergeChild->mKeys[frame-1].mRot[1],
										mergeChild->mKeys[frame-1].mRot[2],
										bvhStringToOrder(mergeChild->mOrder) );
				LLQuaternion childFrameRot( mergeChild->mFrameMatrix );
				LLQuaternion childOffsetRot( mergeChild->mOffsetMatrix );
				mergeChildRot = ~childFrameRot * mergeChildRot * childFrameRot * childOffsetRot;
				
			}
			else
			{
				mergeChildRot.loadIdentity();
			}

			LLQuaternion inRot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);

			LLQuaternion outRot =  frameRotInv* mergeChildRot * inRot * mergeParentRot * ~first_frame_rot * frameRot * offsetRot;

			U16 time_short = F32_to_U16(time, 0.f, mDuration);
			dp.packU16(time_short, "time");
			U16 x, y, z;
			LLVector3 rot_vec = outRot.packToVector3();
			rot_vec.quantize16(-1.f, 1.f, -1.f, 1.f);
			x = F32_to_U16(rot_vec.mV[VX], -1.f, 1.f);
			y = F32_to_U16(rot_vec.mV[VY], -1.f, 1.f);
			z = F32_to_U16(rot_vec.mV[VZ], -1.f, 1.f);
			dp.packU16(x, "rot_angle_x");
			dp.packU16(y, "rot_angle_y");
			dp.packU16(z, "rot_angle_z");
			outcount++;
			frame++;
		}
		
		// output position keys (only for 1st joint)
		if ( ji == mJoints.begin() && !joint->mIgnorePositions )
		{
			dp.packS32(joint->mNumPosKeys, "num_pos_keys");

			LLVector3 relPos = joint->mRelativePosition;
			LLVector3 relKey;

			frame = 1;
			for (	ki = joint->mKeys.begin();
					ki != joint->mKeys.end();
					++ki )
			{
				if ((frame == 1) && joint->mRelativePositionKey)
				{
					relKey.setVec(ki->mPos);
				}

				if (ki->mIgnorePos)
				{
					frame++;
					continue;
				}

				time = (F32)frame * mFrameTime;

				LLVector3 inPos = (LLVector3(ki->mPos) - relKey) * ~first_frame_rot;// * fixup_rot;
				LLVector3 outPos = inPos * frameRot * offsetRot;

				outPos *= INCHES_TO_METERS;

				outPos -= relPos;
				outPos.clamp(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);

				U16 time_short = F32_to_U16(time, 0.f, mDuration);
				dp.packU16(time_short, "time");

				U16 x, y, z;
				outPos.quantize16(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
				x = F32_to_U16(outPos.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
				y = F32_to_U16(outPos.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
				z = F32_to_U16(outPos.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
				dp.packU16(x, "pos_x");
				dp.packU16(y, "pos_y");
				dp.packU16(z, "pos_z");

				frame++;
			}
		}
		else
		{
			dp.packS32(0, "num_pos_keys");
		}
	}

	S32 num_constraints = (S32)mConstraints.size();
	dp.packS32(num_constraints, "num_constraints");

	for (ConstraintVector::iterator constraint_it = mConstraints.begin();
		constraint_it != mConstraints.end();
		constraint_it++)
		{
			U8 byte = constraint_it->mChainLength;
			dp.packU8(byte, "chain_length");
			
			byte = constraint_it->mConstraintType;
			dp.packU8(byte, "constraint_type");
			dp.packBinaryDataFixed((U8*)constraint_it->mSourceJointName, 16, "source_volume");
			dp.packVector3(constraint_it->mSourceOffset, "source_offset");
			dp.packBinaryDataFixed((U8*)constraint_it->mTargetJointName, 16, "target_volume");
			dp.packVector3(constraint_it->mTargetOffset, "target_offset");
			dp.packVector3(constraint_it->mTargetDir, "target_dir");
			dp.packF32(constraint_it->mEaseInStart,	"ease_in_start");
			dp.packF32(constraint_it->mEaseInStop,	"ease_in_stop");
			dp.packF32(constraint_it->mEaseOutStart,	"ease_out_start");
			dp.packF32(constraint_it->mEaseOutStop,	"ease_out_stop");
		}

	return TRUE;
}
//-----------------------------------------------------------------------------
// LLBVHLoader::optimize()
//-----------------------------------------------------------------------------
void LLBVHLoader::optimize()
{
	//RN: assumes motion blend, which is the default now
	if (!mLoop && mEaseIn + mEaseOut > mDuration && mDuration != 0.f)
	{
		F32 factor = mDuration / (mEaseIn + mEaseOut);
		mEaseIn *= factor;
		mEaseOut *= factor;
	}

	JointVector::iterator ji;
	for (ji = mJoints.begin(); ji != mJoints.end(); ++ji)
	{
		Joint *joint = *ji;
		BOOL pos_changed = FALSE;
		BOOL rot_changed = FALSE;

		if ( ! joint->mIgnore )
		{
			joint->mNumPosKeys = 0;
			joint->mNumRotKeys = 0;
			LLQuaternion::Order order = bvhStringToOrder( joint->mOrder );

			KeyVector::iterator first_key = joint->mKeys.begin();

			// no keys?
			if (first_key == joint->mKeys.end())
			{
				joint->mIgnore = TRUE;
				continue;
			}

			LLVector3 first_frame_pos(first_key->mPos);
			LLQuaternion first_frame_rot = mayaQ( first_key->mRot[0], first_key->mRot[1], first_key->mRot[2], order);
	
			// skip first key
			KeyVector::iterator ki = joint->mKeys.begin();
			if (joint->mKeys.size() == 1)
			{
				// *FIX: use single frame to move pelvis
				// if only one keyframe force output for this joint
				rot_changed = TRUE;
			}
			else
			{
				// if more than one keyframe, use first frame as reference and skip to second
				first_key->mIgnorePos = TRUE;
				first_key->mIgnoreRot = TRUE;
				++ki;
			}

			KeyVector::iterator ki_prev = ki;
			KeyVector::iterator ki_last_good_pos = ki;
			KeyVector::iterator ki_last_good_rot = ki;
			S32 numPosFramesConsidered = 2;
			S32 numRotFramesConsidered = 2;

			F32 rot_threshold = ROTATION_KEYFRAME_THRESHOLD / llmax((F32)joint->mChildTreeMaxDepth * 0.33f, 1.f);

			double diff_max = 0;
			KeyVector::iterator ki_max = ki;
			for (; ki != joint->mKeys.end(); ++ki)
			{
				if (ki_prev == ki_last_good_pos)
				{
					joint->mNumPosKeys++;
					if (dist_vec_squared(LLVector3(ki_prev->mPos), first_frame_pos) > POSITION_MOTION_THRESHOLD_SQUARED)
					{
						pos_changed = TRUE;
					}
				}
				else
				{
					//check position for noticeable effect
					LLVector3 test_pos(ki_prev->mPos);
					LLVector3 last_good_pos(ki_last_good_pos->mPos);
					LLVector3 current_pos(ki->mPos);
					LLVector3 interp_pos = lerp(current_pos, last_good_pos, 1.f / (F32)numPosFramesConsidered);

					if (dist_vec_squared(current_pos, first_frame_pos) > POSITION_MOTION_THRESHOLD_SQUARED)
					{
						pos_changed = TRUE;
					}

					if (dist_vec_squared(interp_pos, test_pos) < POSITION_KEYFRAME_THRESHOLD_SQUARED)
					{
						ki_prev->mIgnorePos = TRUE;
						numPosFramesConsidered++;
					}
					else
					{
						numPosFramesConsidered = 2;
						ki_last_good_pos = ki_prev;
						joint->mNumPosKeys++;
					}
				}

				if (ki_prev == ki_last_good_rot)
				{
					joint->mNumRotKeys++;
					LLQuaternion test_rot = mayaQ( ki_prev->mRot[0], ki_prev->mRot[1], ki_prev->mRot[2], order);
					F32 x_delta = dist_vec(LLVector3::x_axis * first_frame_rot, LLVector3::x_axis * test_rot);
					F32 y_delta = dist_vec(LLVector3::y_axis * first_frame_rot, LLVector3::y_axis * test_rot);
					F32 rot_test = x_delta + y_delta;

					if (rot_test > ROTATION_MOTION_THRESHOLD)
					{
						rot_changed = TRUE;
					}
				}
				else
				{
					//check rotation for noticeable effect
					LLQuaternion test_rot = mayaQ( ki_prev->mRot[0], ki_prev->mRot[1], ki_prev->mRot[2], order);
					LLQuaternion last_good_rot = mayaQ( ki_last_good_rot->mRot[0], ki_last_good_rot->mRot[1], ki_last_good_rot->mRot[2], order);
					LLQuaternion current_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
					LLQuaternion interp_rot = lerp(1.f / (F32)numRotFramesConsidered, current_rot, last_good_rot);

					F32 x_delta;
					F32 y_delta;
					F32 rot_test;
					
					// Test if the rotation has changed significantly since the very first frame.  If false
					// for all frames, then we'll just throw out this joint's rotation entirely.
					x_delta = dist_vec(LLVector3::x_axis * first_frame_rot, LLVector3::x_axis * test_rot);
					y_delta = dist_vec(LLVector3::y_axis * first_frame_rot, LLVector3::y_axis * test_rot);
					rot_test = x_delta + y_delta;
					if (rot_test > ROTATION_MOTION_THRESHOLD)
					{
						rot_changed = TRUE;
					}
					x_delta = dist_vec(LLVector3::x_axis * interp_rot, LLVector3::x_axis * test_rot);
					y_delta = dist_vec(LLVector3::y_axis * interp_rot, LLVector3::y_axis * test_rot);
					rot_test = x_delta + y_delta;

					// Draw a line between the last good keyframe and current.  Test the distance between the last frame (current-1, i.e. ki_prev)
					// and the line.  If it's greater than some threshold, then it represents a significant frame and we want to include it.
					if (rot_test >= rot_threshold ||
						(ki+1 == joint->mKeys.end() && numRotFramesConsidered > 2))
					{
						// Add the current test keyframe (which is technically the previous key, i.e. ki_prev).
						numRotFramesConsidered = 2;
						ki_last_good_rot = ki_prev;
						joint->mNumRotKeys++;

						// Add another keyframe between the last good keyframe and current, at whatever point was the most "significant" (i.e.
						// had the largest deviation from the earlier tests).  Note that a more robust approach would be test all intermediate
						// keyframes against the line between the last good keyframe and current, but we're settling for this other method
						// because it's significantly faster.
						if (diff_max > 0)
						{
							if (ki_max->mIgnoreRot == TRUE)
							{
								ki_max->mIgnoreRot = FALSE;
								joint->mNumRotKeys++;
							}
							diff_max = 0;
						}
					}
					else
					{
						// This keyframe isn't significant enough, throw it away.
						ki_prev->mIgnoreRot = TRUE;
						numRotFramesConsidered++;
						// Store away the keyframe that has the largest deviation from the interpolated line, for insertion later.
						if (rot_test > diff_max)
						{
							diff_max = rot_test;
							ki_max = ki;
						}
					}
				}

				ki_prev = ki;
			}
		}	

		// don't output joints with no motion
		if (!(pos_changed || rot_changed))
		{
			//LL_INFOS() << "Ignoring joint " << joint->mName << LL_ENDL;
			joint->mIgnore = TRUE;
		}
	}
}