/*****************************************************************************
 * $Id: eng_dyna_part.cc,v 1.3 2006/10/20 03:51:47 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Class for a part of a mesh that can transformed (rotated/translated).
 *//*
 * LEGAL:	COPYRIGHT (C) 2006 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#define ENG_DYNA_PART_CC 1
#include "eng.hh"
#include "eng_dyna.hh"
#include "eng_dyna_part.hh"

////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////  MeshPart  ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * Ctor.
 * @pre Dyna-derived class has overriden Dyna::GetSavedLocalVertexs()
 * to return a copy of original unmodified local vertexs.
 *****************************************************************************/
MeshPart::MeshPart( DynaModel& dyna, const vector<Vix>& vixs, const Vix pivotVix )
:   mDyna(dyna),
    mVixs(vixs),
    mPivot(dyna.GetLocalVertexs()[pivotVix])
{
CHECK_TYPESIG( &dyna,TYPESIG_DYNA );
ASSERT( mVixs.size() > 0 );
ASSERT( pivotVix > 0 && pivotVix < mDyna.GetVertexCnt() );

    IdentityMatrix( m );

    // mNixs:
	// Build a container of indexs of normals.
    // There isn't a 1:1 mapping between grouped vertexs/normals because
    // Blender may produce multiple normals for one vertex.
	mNixs.reserve( vixs.size() );  // generous estimate
    const Normal* localNormals = mDyna.GetLocalNormals(); CHECK_TYPESIG( localNormals, TYPESIG_LIB_NORMAL );
    const uint    normalCnt    = mDyna.GetNormalCnt();    ASSERT( normalCnt > 0 );
    vector<Vix>::const_iterator itrVix;
    for ( itrVix = vixs.begin(); itrVix != vixs.end(); ++itrVix )
    {

		// For each normal(s) associated with this vertex of this VertexGroup.
        const Vix vix = *itrVix;
ASSERT( vix < mDyna.GetVertexCnt() );
CHECK_TYPESIG( &mDyna.GetLocalVertexs()[vix], TYPESIG_LIB_VERTEX_LOCAL );
		for ( Nix nix = 0; nix < normalCnt; ++nix )
		{
			// Does this normal lie on this vertex (which belongs to the VertexGroup)?
			if ( localNormals[nix].mVixNormOrig == vix )
			{
CHECK_TYPESIG( &localNormals[nix], TYPESIG_LIB_NORMAL );

		        mNixs.push_back( nix );

				// Don't break.  Blender can associate multiple normals with one vertex.
			}
		}
    }
	ASSERT( mNixs.size() > 0 );
}

/*****************************************************************************
 * Rotate a part of a mesh.
 * @param   axis
 * @param   deg
 * @param   resetMatrix
 *          If true, matrix is reset to identity-mapped state.
 *          Else, rotation is applied to matrix's current state.
 *****************************************************************************/
void
MeshPart::Rotate( const uint axis, const Degree deg, const bool resetMatrix )
{
    // There are two spaces: local space and a pivot space.
    // The vertexs will be translated from local to pivot space,
    // rotated by this matrix, then translated back to local space.
	if ( resetMatrix ) IdentityMatrix( m );
	RotateMatrixLocalDegree( axis, deg, m );

    // Rotate local vertexs of this Vertex Group around pivot vertex.
    LocalVertex* localVertexs = mDyna.GetLocalVertexs(); CHECK_TYPESIG( localVertexs, TYPESIG_LIB_VERTEX_LOCAL );
    const LocalVertex* savedLocalVertexs = mDyna.GetSavedLocalVertexs(); CHECK_TYPESIG( savedLocalVertexs, TYPESIG_LIB_VERTEX_LOCAL );
    vector<Vix>::const_iterator itrVix;
    for ( itrVix = mVixs.begin(); itrVix != mVixs.end(); ++itrVix )
    {
		// Rotate local vertex.
        const Vix vix = *itrVix; ASSERT( vix < mDyna.GetVertexCnt() );
        LocalVertex v;
        v.x = savedLocalVertexs[vix].x - mPivot.x;                      // translate from local to pivot space
        v.y = savedLocalVertexs[vix].y - mPivot.y;
        v.z = savedLocalVertexs[vix].z - mPivot.z;
    	localVertexs[vix].x = v.x * m[Xx] + v.y * m[Xy] + v.z * m[Xz];  // rotate around pivot
    	localVertexs[vix].y = v.x * m[Yx] + v.y * m[Yy] + v.z * m[Yz];
    	localVertexs[vix].z = v.x * m[Zx] + v.y * m[Zy] + v.z * m[Zz];
        localVertexs[vix] += mPivot;                                    // translate back to local space
CHECK_TYPESIG( &localVertexs[vix], TYPESIG_LIB_VERTEX_LOCAL );
	}

	// Rotate local normals.
	// Order-dependent: must be after rotation of local vertexs.
    Normal* localNormals = mDyna.GetLocalNormals(); CHECK_TYPESIG( localNormals, TYPESIG_LIB_NORMAL );
    const Normal* savedLocalNormals = mDyna.GetSavedLocalNormals(); CHECK_TYPESIG( savedLocalNormals, TYPESIG_LIB_NORMAL );
	vector<Nix>::const_iterator itrNix;
    for ( itrNix = mNixs.begin(); itrNix != mNixs.end(); ++itrNix )
    {
		// Rotate normal vector.
		const Nix nix     = *itrNix;  ASSERT( nix < mDyna.GetNormalCnt() );
		const Normal norm = savedLocalNormals[nix];  // saved normal vector
        LocalVertex v;
		// Pre-rotation : subtract unrotated local vertex from unrotated normal (mSavedLocalVertexs[]).
		// Rotation     : rotate normal
		// Post-rotation: subtract rotated local vertex from rotated normal (localVertexs[]).
		v.x = norm.mNorm.x + savedLocalVertexs[norm.mVixNormOrig].x - mPivot.x;
		v.y = norm.mNorm.y + savedLocalVertexs[norm.mVixNormOrig].y - mPivot.y;
		v.z = norm.mNorm.z + savedLocalVertexs[norm.mVixNormOrig].z - mPivot.z;
    	localNormals[nix].mNorm.x = v.x * m[Xx] + v.y * m[Xy] + v.z * m[Xz];
    	localNormals[nix].mNorm.y = v.x * m[Yx] + v.y * m[Yy] + v.z * m[Yz];
    	localNormals[nix].mNorm.z = v.x * m[Zx] + v.y * m[Zy] + v.z * m[Zz];
        localNormals[nix].mNorm.x = localNormals[nix].mNorm.x + mPivot.x - localVertexs[norm.mVixNormOrig].x;
        localNormals[nix].mNorm.y = localNormals[nix].mNorm.y + mPivot.y - localVertexs[norm.mVixNormOrig].y;
        localNormals[nix].mNorm.z = localNormals[nix].mNorm.z + mPivot.z - localVertexs[norm.mVixNormOrig].z;
CHECK_TYPESIG( &localNormals[nix], TYPESIG_LIB_NORMAL );
CHECK_TYPESIG( &localVertexs[norm.mVixNormOrig], TYPESIG_LIB_VERTEX_LOCAL );
CHECK_TYPESIG( &savedLocalVertexs[norm.mVixNormOrig], TYPESIG_LIB_VERTEX_LOCAL );
    }

	// Force recalculating world normals as local normals were modified.
	mDyna.SetNeedRecalc();
}

/*****************************************************************************
 * Translate a part of a mesh.
 * Translation is relative: incremental movement from its current position.
 * @param   inc
 *****************************************************************************/
void
MeshPart::Translate( const LocalVertex& inc )
{
    LocalVertex* localVertexs = mDyna.GetLocalVertexs(); CHECK_TYPESIG( localVertexs, TYPESIG_LIB_VERTEX_LOCAL );
    vector<Vix>::const_iterator itrVix;
    for ( itrVix = mVixs.begin(); itrVix != mVixs.end(); ++itrVix )
    {
        const Vix vix = *itrVix; ASSERT( vix < mDyna.GetVertexCnt() );
    	localVertexs[vix] += inc;
CHECK_TYPESIG( &localVertexs[vix], TYPESIG_LIB_VERTEX_LOCAL );
	}

	// Force recalculating world normals as local normals were modified.
	mDyna.SetNeedRecalc();
}

/*****************************************************************************
 * Implode a submesh into a 3D point.
 * @param   point
 *****************************************************************************/
void
MeshPart::Implode( const LocalVertex& point )
{
    LocalVertex* localVertexs = mDyna.GetLocalVertexs(); CHECK_TYPESIG( localVertexs, TYPESIG_LIB_VERTEX_LOCAL );
    vector<Vix>::const_iterator itrVix;
    for ( itrVix = mVixs.begin(); itrVix != mVixs.end(); ++itrVix )
    {
        const Vix vix = *itrVix; ASSERT( vix < mDyna.GetVertexCnt() );
    	localVertexs[vix] = point;
CHECK_TYPESIG( &localVertexs[vix], TYPESIG_LIB_VERTEX_LOCAL );
	}

	// Force recalculating world normals as local normals were modified.
	mDyna.SetNeedRecalc();
}
