/*! Push \a srcTrans into \a dstGeo
 */
void TransformPushGraphOp::pushToGeometry(
    const Transform *srcTrans, Geometry *dstGeo)
{
    typedef Vec4f  VecType;
    typedef Pnt3f  PntType;
    
    Matrix mat    = srcTrans->getMatrix();
    Matrix invMat = mat;

    invMat[3][0] = invMat[3][1] = invMat[3][2] = 0.f;

    invMat.invert   ();
    invMat.transpose();

    const Geometry::SFLengthsType     *sfLen  = dstGeo->getSFLengths     ();
          Geometry::MFPropertiesType  *mfProp = dstGeo->editMFProperties ();
          Geometry::MFPropIndicesType *mfInd  = dstGeo->editMFPropIndices();
    
    Geometry::MFPropertiesType::const_iterator  propIt  = mfProp->begin();
    Geometry::MFPropertiesType::const_iterator  propEnd = mfProp->end  ();
    
    Geometry::MFPropIndicesType::const_iterator indIt   = mfInd ->begin();
    Geometry::MFPropIndicesType::const_iterator indEnd  = mfInd ->end  ();
    
    UInt32 propUsed = 0;
    for(UInt32 i = 0; i < sfLen->getValue()->size(); ++i)
        propUsed += sfLen->getValue()->getValue<UInt32>(i);
    
    for(; propIt != propEnd && indIt != indEnd; ++propIt, ++indIt)
    {
              GeoVectorProperty   *prop = *propIt;
        const GeoIntegralProperty *ind  = *indIt;
        
        if(prop == NULL)
            continue;
        
        if(ind == NULL)
        {
            // unindexed
            if((prop->getUsage() & GeoProperty::UsageObjectSpace) != 0x0000)
            {
                // transform with mat
                for(UInt32 i = 0; i < propUsed; ++i)
                {
                    PntType pnt = prop->getValue<PntType>(i);
                    mat.multFull(pnt, pnt);
                    prop->setValue(pnt, i);
                }
            }
            else if((prop->getUsage() & GeoProperty::UsageTangentSpace) != 0x0000)
            {
                // transform with invMat
                for(UInt32 i = 0; i < propUsed; ++i)
                {
                    VecType vec = prop->getValue<VecType>(i);
                    invMat.mult(vec, vec);
                    prop->setValue(vec, i);
                }
            }
        }
        else
        {
            std::set<UInt32> transInd;
            
            if((prop->getUsage() & GeoProperty::UsageObjectSpace) != 0x0000)
            {
                // transform with mat
                for(UInt32 i = 0; i < propUsed; ++i)
                {
                    UInt32 j = ind->getValue(i);
                    
                    if(transInd.find(j) == transInd.end())
                    {
                        PntType pnt = prop->getValue<PntType>(j);
                        mat.multFull(pnt, pnt);
                        prop->setValue(pnt, j);
                        
                        transInd.insert(j);
                    }
                }
            }
            else if((prop->getUsage() & GeoProperty::UsageTangentSpace) != 0x0000)
            {
                // transform with invMat
                for(UInt32 i = 0; i < propUsed; ++i)
                {
                    UInt32 j = ind->getValue(i);
                    
                    if(transInd.find(j) == transInd.end())
                    {
                        VecType vec = prop->getValue<VecType>(j);
                        invMat.mult(vec, vec);
                        prop->setValue(vec, j);
                        
                        transInd.insert(j);
                    }
                }
            }
        }
    }
}
void CPUSkinningAlgorithm::transformGeometry(
    SkinnedGeometry           *skinGeo,
    Skeleton                  *skel,
    CPUSkinningDataAttachment *data)
{
    const SkinnedGeometry::MFPropIndicesType *mfOrigIdx   =
        skinGeo->getMFPropIndices();
    const SkinnedGeometry::MFPropertiesType  *mfOrigProps =
        skinGeo->getMFProperties();
    SkinnedGeometry::MFPropertiesType        *mfProps     =
        data->editMFProperties();

    const GeoVec4fProperty *jointIdxProp    =
        dynamic_cast<const GeoVec4fProperty *>(
            (*mfOrigProps)[skinGeo->getJointIndexProperty()]);
    const GeoVec4fProperty *jointWeightProp =
        dynamic_cast<const GeoVec4fProperty *>(
            (*mfOrigProps)[skinGeo->getJointWeightProperty()]);

    if(skinGeo->getIndex(skinGeo->getJointIndexProperty ()) !=
       skinGeo->getIndex(skinGeo->getJointWeightProperty())   )
    {
        SWARNING << "CPUSkinningAlgorithm::transformGeometry: "
                 << "Multi indexed geometry not supported!"
                 << std::endl;
        return;
    }

    mfProps->resize(mfOrigProps->size(), NULL);

    const MFMatrix *mfJointMat       = NULL;
    const MFMatrix *mfJointNormalMat = NULL;

    // if the geometry has a non-identity bind shape matrix
    // premultiply joint matrices with bind shape matrix and store in data
    // attachment
    if(skinGeo->getBindShapeMatrix().equals(Matrix::identity(), Eps) == false)
    {
        mfJointMat               = skel->getMFJointMatrices ();
        MFMatrix *mfJointBindMat = data->editMFJointMatrices();

        mfJointBindMat->resize(mfJointMat->size());

        for(UInt32 i = 0; i < mfJointMat->size(); ++i)
        {
            (*mfJointBindMat)[i] = (*mfJointMat)[i];
            (*mfJointBindMat)[i].mult(skinGeo->getBindShapeMatrix());
        }

        mfJointMat = data->getMFJointMatrices();

        if(skel->getCalcNormalMatrices() == true)
        {
            mfJointNormalMat               = skel->getMFJointNormalMatrices ();
            MFMatrix *mfJointNormalBindMat = data->editMFJointNormalMatrices();

            mfJointNormalBindMat->resize(mfJointNormalMat->size());

            for(UInt32 i = 0; i < mfJointNormalMat->size(); ++i)
            {
                (*mfJointNormalBindMat)[i] = (*mfJointNormalMat)[i];
                (*mfJointNormalBindMat)[i].mult(skinGeo->getBindShapeMatrix());
            }

            mfJointNormalMat = data->getMFJointNormalMatrices();
        }
        else
        {
            mfJointNormalMat = data->getMFJointMatrices();
        }
    }
    else
    {
        mfJointMat = skel->getMFJointMatrices();

        if(skel->getCalcNormalMatrices() == true)
        {
            mfJointNormalMat = skel->getMFJointNormalMatrices();
        }
        else
        {
            mfJointNormalMat = skel->getMFJointMatrices();
        }
    }

    // transform all properties

    for(UInt16 i = 0; i < mfProps->size(); ++i)
    {
        // do not transform the vertex joint indices/weights
        if(i == skinGeo->getJointIndexProperty () ||
           i == skinGeo->getJointWeightProperty()   )
        {
            continue;
        }

        GeoIntegralProperty *origPropIdx = (*mfOrigIdx  )[i];
        GeoVectorProperty   *origProp    = (*mfOrigProps)[i];
        GeoVectorProperty   *prop        = (*mfProps    )[i];

        if(origProp == NULL)
            continue;

        if(prop == NULL)
        {
            GeoVectorPropertyUnrecPtr newProp =
                dynamic_pointer_cast<GeoVectorProperty>(origProp->clone());

            mfProps->replace(i, newProp);
            prop = newProp;
        }
        else if(prop->size() != origProp->size())
        {
            prop->resize(origProp->size());
        }

        switch(origProp->getUsage() & GeoProperty::UsageSpaceMask)
        {
        case GeoProperty::UsageObjectSpace:
        {
             transformObjectSpaceProperty(
                 mfJointMat,
                 jointIdxProp, jointWeightProp,
                 origPropIdx, origProp, prop);
        }
        break;

        case GeoProperty::UsageTangentSpace:
        {
            transformTangentSpaceProperty(
                mfJointNormalMat,
                jointIdxProp, jointWeightProp,
                origPropIdx, origProp, prop);
        }
        break;

        default:
            // only need to transform object/tangent space properties
            break;
        }
    }
}