//! updates the absolute position based on the relative and the parents position
void CAnimatedMeshSceneNode::updateAbsolutePosition()
{
    if ( 0 == Mesh || Mesh->getMeshType() != EAMT_MD3 )
    {
        IAnimatedMeshSceneNode::updateAbsolutePosition();
        return;
    }

    SMD3QuaterionTag parent;
    if ( Parent && Parent->getType () == ESNT_ANIMATED_MESH)
    {
        parent = ((IAnimatedMeshSceneNode*) Parent)->getAbsoluteTransformation ( MD3Special.Tagname );
    }

    SMD3QuaterionTag relative( RelativeTranslation, RelativeRotation );

    SMD3QuaterionTagList *taglist;
    taglist = ( (IAnimatedMeshMD3*) Mesh )->getTagList ( getFrameNr(),255,getStartFrame (),getEndFrame () );
    if ( taglist )
    {
        MD3Special.AbsoluteTagList.Container.set_used ( taglist->size () );
        for ( u32 i = 0; i!= taglist->size (); ++i )
        {
            MD3Special.AbsoluteTagList[i].position = parent.position + (*taglist)[i].position + relative.position;
            MD3Special.AbsoluteTagList[i].rotation = parent.rotation * (*taglist)[i].rotation * relative.rotation;
        }

    }

}
void CGUISpriteBank::draw2DSprite(u32 index, const core::rect<s32>& destRect,
		const core::rect<s32>* clip, const video::SColor * const colors,
		u32 timeTicks, bool loop)
{
	if (index >= Sprites.size() || Sprites[index].Frames.empty() )
		return;

	u32 frame = getFrameNr(index, timeTicks, loop);
	const video::ITexture* tex = getTexture(Sprites[index].Frames[frame].textureNumber);
	if (!tex)
		return;

	const u32 rn = Sprites[index].Frames[frame].rectNumber;
	if (rn >= Rectangles.size())
		return;

	Driver->draw2DImage(tex, destRect, Rectangles[rn], clip, colors, true);
}
//! draws a sprite in 2d with scale and color
void CGUISpriteBank::draw2DSprite(u32 index, const core::position2di& pos,
		const core::rect<s32>* clip, const video::SColor& color,
		u32 starttime, u32 currenttime, bool loop, bool center)
{
	if (index >= Sprites.size() || Sprites[index].Frames.empty() )
		return;

	u32 frame = getFrameNr(index, currenttime - starttime, loop);
	const video::ITexture* tex = getTexture(Sprites[index].Frames[frame].textureNumber);
	if (!tex)
		return;

	const u32 rn = Sprites[index].Frames[frame].rectNumber;
	if (rn >= Rectangles.size())
		return;

	const core::rect<s32>& r = Rectangles[rn];
	core::position2di p(pos);
	if (center)
	{
		p -= r.getSize() / 2;
	}
	Driver->draw2DImage(tex, p, r, clip, color, true);
}
//! updates the joint positions of this mesh
void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions)
{
#ifndef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
	return;
#else
	if (Mesh && Mesh->getMeshType() == EAMT_SKINNED )
	{
		checkJoints();
		const f32 frame = getFrameNr(); //old?

		CSkinnedMesh* skinnedMesh=reinterpret_cast<CSkinnedMesh*>(Mesh);

		skinnedMesh->transferOnlyJointsHintsToMesh( JointChildSceneNodes );
		skinnedMesh->animateMesh(frame, 1.0f);
		skinnedMesh->recoverJointsFromMesh( JointChildSceneNodes);

		//-----------------------------------------
		//		Transition
		//-----------------------------------------

		if (Transiting != 0.f)
		{
			// Init additional matrices
			if (PretransitingSave.size()<JointChildSceneNodes.size())
			{
				for(u32 n=PretransitingSave.size(); n<JointChildSceneNodes.size(); ++n)
					PretransitingSave.push_back(core::matrix4());
			}

			for (u32 n=0; n<JointChildSceneNodes.size(); ++n)
			{
				//------Position------

				JointChildSceneNodes[n]->setPosition(
						core::lerp(
							PretransitingSave[n].getTranslation(),
							JointChildSceneNodes[n]->getPosition(),
							TransitingBlend));

				//------Rotation------

				//Code is slow, needs to be fixed up

				const core::quaternion RotationStart(PretransitingSave[n].getRotationDegrees()*core::DEGTORAD);
				const core::quaternion RotationEnd(JointChildSceneNodes[n]->getRotation()*core::DEGTORAD);

				core::quaternion QRotation;
				QRotation.slerp(RotationStart, RotationEnd, TransitingBlend);

				core::vector3df tmpVector;
				QRotation.toEuler(tmpVector);
				tmpVector*=core::RADTODEG; //convert from radians back to degrees
				JointChildSceneNodes[n]->setRotation( tmpVector );

				//------Scale------

				//JointChildSceneNodes[n]->setScale(
				//		core::lerp(
				//			PretransitingSave[n].getScale(),
				//			JointChildSceneNodes[n]->getScale(),
				//			TransitingBlend));
			}
		}

		if (CalculateAbsolutePositions)
		{
			//---slow---
			for (u32 n=0;n<JointChildSceneNodes.size();++n)
			{
				if (JointChildSceneNodes[n]->getParent()==this)
				{
					JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); //temp, should be an option
				}
			}
		}
	}
#endif
}
//! renders the node.
void CAnimatedMeshSceneNode::render()
{
    video::IVideoDriver* driver = SceneManager->getVideoDriver();

    if (!Mesh || !driver)
        return;

    bool isTransparentPass =
        SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;

    ++PassCount;

    s32 frame = getFrameNr();
    scene::IMesh* m = Mesh->getMesh(frame, 255, StartFrame, EndFrame);

    if ( 0 == m )
    {
#ifdef _DEBUG
        os::Printer::log("Animated Mesh returned no mesh to render.", Mesh->getDebugName(), ELL_WARNING);
#endif
    }

    driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);


    u32 i,g;

    // update all dummy transformation nodes
    if (!JointChildSceneNodes.empty() && Mesh &&
            (Mesh->getMeshType() == EAMT_MS3D || Mesh->getMeshType() == EAMT_X  || Mesh->getMeshType() == EAMT_B3D ))
    {
        IAnimatedMeshMS3D* amm = (IAnimatedMeshMS3D*)Mesh;
        core::matrix4* m;

        for ( i=0; i< JointChildSceneNodes.size(); ++i)
            if (JointChildSceneNodes[i])
            {
                m = amm->getMatrixOfJoint(i, frame);
                if (m)
                    JointChildSceneNodes[i]->getRelativeTransformationMatrix() = *m;
            }
    }

    if (Shadow && PassCount==1)
        Shadow->setMeshToRenderFrom(m);

    // for debug purposes only:

    u32 renderMeshes = 1;
    video::SMaterial mat;
    if (DebugDataVisible && PassCount==1)
    {
        // overwrite half transparency
        if ( DebugDataVisible & scene::EDS_HALF_TRANSPARENCY )
        {
            for ( g=0; g<m->getMeshBufferCount(); ++g)
            {
                mat = Materials[g];
                mat.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
                driver->setMaterial(mat);
                driver->drawMeshBuffer ( m->getMeshBuffer ( g ) );
            }
            renderMeshes = 0;
        }
    }

    // render original meshes
    if ( renderMeshes )
    {
        for ( i=0; i<m->getMeshBufferCount(); ++i)
        {
            video::IMaterialRenderer* rnd = driver->getMaterialRenderer(Materials[i].MaterialType);
            bool transparent = (rnd && rnd->isTransparent());

            // only render transparent buffer if this is the transparent render pass
            // and solid only in solid pass
            if (transparent == isTransparentPass)
            {
                scene::IMeshBuffer* mb = m->getMeshBuffer(i);
                driver->setMaterial(Materials[i]);
                driver->drawMeshBuffer(mb);
            }
        }
    }

    // for debug purposes only:
    if (DebugDataVisible && PassCount==1)
    {
        mat.Lighting = false;
        driver->setMaterial(mat);

        // show bounding box
        if ( DebugDataVisible & scene::EDS_BBOX_BUFFERS )
        {
            for ( g=0; g< m->getMeshBufferCount(); ++g)
            {
                driver->draw3DBox( m->getMeshBuffer(g)->getBoundingBox(),
                                   video::SColor(0,190,128,128)
                                 );
            }
        }

        if ( DebugDataVisible & scene::EDS_BBOX )
            driver->draw3DBox(Box, video::SColor(0,255,255,255));

        // show skeleton
        if ( DebugDataVisible & scene::EDS_SKELETON )
        {
            if (Mesh->getMeshType() == EAMT_X)
            {
                // draw skeleton
                const core::array<core::vector3df>* ds =
                    ((IAnimatedMeshX*)Mesh)->getDrawableSkeleton(frame);

                for ( g=0; g < ds->size(); g +=2 )
                    driver->draw3DLine((*ds)[g], (*ds)[g+1],  video::SColor(0,51,66,255));
            }

            // show tag for quake3 models
            if (Mesh->getMeshType() == EAMT_MD3 )
            {
                IAnimatedMesh * arrow = SceneManager->addArrowMesh ( "__tag_show",
                                        4, 8, 5.f, 4.f, 0.5f, 1.f, 0xFF0000FF, 0xFF000088
                                                                   );
                if ( 0 == arrow )
                {
                    arrow = SceneManager->getMesh ( "__tag_show" );
                }
                IMesh *arrowMesh = arrow->getMesh ( 0 );

                video::SMaterial material;
                material.Lighting = false;
                driver->setMaterial(material);

                core::matrix4 m;

                SMD3QuaterionTagList *taglist = ((IAnimatedMeshMD3*)Mesh)->getTagList (	getFrameNr(),
                                                255,
                                                getStartFrame (),
                                                getEndFrame ()
                                                                                      );
                if ( taglist )
                {
                    for ( u32 g = 0; g != taglist->size(); ++g )
                    {
                        (*taglist)[g].setto ( m );

                        driver->setTransform(video::ETS_WORLD, m );

                        for ( u32 a = 0; a != arrowMesh->getMeshBufferCount(); ++a )
                            driver->drawMeshBuffer ( arrowMesh->getMeshBuffer ( a ) );
                    }
                }
            }
        }

        // show normals
        if ( DebugDataVisible & scene::EDS_NORMALS )
        {
            IAnimatedMesh * arrow = SceneManager->addArrowMesh ( "__debugnormal",
                                    4, 8, 1.f, 0.6f, 0.05f, 0.3f, 0xFFECEC00, 0xFF999900
                                                               );
            if ( 0 == arrow )
            {
                arrow = SceneManager->getMesh ( "__debugnormal" );
            }
            IMesh *mesh = arrow->getMesh ( 0 );

            // find a good scaling factor

            core::matrix4 m2;

            // draw normals
            for ( g=0; g<m->getMeshBufferCount(); ++g)
            {
                scene::IMeshBuffer* mb = m->getMeshBuffer(g);
                const u32 vSize = mb->getVertexPitch();
                const video::S3DVertex* v = ( const video::S3DVertex*)mb->getVertices();
                for ( i = 0; i != mb->getVertexCount(); ++i )
                {
                    AlignToUpVector ( m2, v->Normal );
                    AbsoluteTransformation.transformVect ( m2.pointer(), v->Pos );

                    driver->setTransform(video::ETS_WORLD, m2 );
                    for ( u32 a = 0; a != mesh->getMeshBufferCount(); ++a )
                        driver->drawMeshBuffer ( mesh->getMeshBuffer ( a ) );

                    v = (const video::S3DVertex*) ( (u8*) v + vSize );
                }
            }
            driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
        }

        // show mesh
        if ( DebugDataVisible & scene::EDS_MESH_WIRE_OVERLAY )
        {
            mat.Lighting = false;
            mat.Wireframe = true;
            driver->setMaterial(mat);

            for ( g=0; g<m->getMeshBufferCount(); ++g)
            {
                driver->drawMeshBuffer ( m->getMeshBuffer ( g ) );
            }
        }
    }
}