AnimationPath* ChessUtils::createChessPieceMoveAnimationPath(Vec3f initialPosition, Vec3f finalPosition, float rotationAngle, Vec3f rotationAxis, float pieceTravelSpeed, size_t numberSamplesInPath) {
	AnimationPath* animationPath = new AnimationPath();
	animationPath->setLoopMode(osg::AnimationPath::NO_LOOPING);

	Vec3 positionsOffset = finalPosition - initialPosition;
	float positionsDistance = positionsOffset.length();	
	float jumpHeight = positionsDistance / 2.0;
	float maxHeightPosition = initialPosition.z() + jumpHeight;

	float animationTimeSeconds = std::max(positionsDistance / pieceTravelSpeed, 2.0f);
	float numberSamplesInPathF = (float)numberSamplesInPath;
	float halfAnimationTime = animationTimeSeconds / 2.0;
	
	float deltaTime = animationTimeSeconds / numberSamplesInPathF;

	// cross product to obtain a perpendicular vector in relation to the animation direction
	//Vec3 rotationAxis = positionsOffset ^ osg::Z_AXIS;
	
	float fullRotation = osg::PI * 2.0;
	osgAnimation::Motion* rotationEaseMotion = new osgAnimation::InOutCubicMotion(0, animationTimeSeconds, rotationAngle);
	osgAnimation::Motion* xMovingEaseMotion = new osgAnimation::InOutCubicMotion(initialPosition.x(), animationTimeSeconds, positionsOffset.x());
	osgAnimation::Motion* yMovingEaseMotion = new osgAnimation::InOutCubicMotion(initialPosition.y(), animationTimeSeconds, positionsOffset.y());
	osgAnimation::Motion* zClimbingEaseMotion = new osgAnimation::InQuadMotion(initialPosition.z(), halfAnimationTime, jumpHeight);
	osgAnimation::Motion* zFallingEaseMotion = new osgAnimation::OutBounceMotion(maxHeightPosition, halfAnimationTime, -jumpHeight);

	size_t halfNumberSamplesInPath = numberSamplesInPath / 2;
	double currenTime = 0;
	for (size_t currentSamplePosition = 0; currentSamplePosition < numberSamplesInPath; ++currentSamplePosition) {
		float sampleXPosition = xMovingEaseMotion->getValue();
		xMovingEaseMotion->update(deltaTime);

		float sampleYPosition = yMovingEaseMotion->getValue();
		yMovingEaseMotion->update(deltaTime);

		float sampleZPosition;		
		if (currentSamplePosition < halfNumberSamplesInPath) {
			sampleZPosition = zClimbingEaseMotion->getValue();
			zClimbingEaseMotion->update(deltaTime);
		} else {
			sampleZPosition = zFallingEaseMotion->getValue();
			zFallingEaseMotion->update(deltaTime);
		}

		float sampleRotationAngle = rotationEaseMotion->getValue();
		rotationEaseMotion->update(deltaTime);		

		Vec3 position(sampleXPosition, sampleYPosition, sampleZPosition);
		Quat rotationQuad(sampleRotationAngle, rotationAxis);
		animationPath->insert(currenTime, AnimationPath::ControlPoint(position, rotationQuad));
		currenTime += deltaTime;
	}

	return animationPath;
}
AnimationPath* ChessUtils::createScaleAnimationPath(Vec3f position, osgAnimation::Motion* scaleEaseMotion, float animationTimeSeconds, size_t numberSamplesInPath) {
	AnimationPath* animationPath = new AnimationPath();
	animationPath->setLoopMode(osg::AnimationPath::NO_LOOPING);	
			
	float deltaTime = animationTimeSeconds / (float)numberSamplesInPath;	
	
	double currenTime = 0;
	while (currenTime <= animationTimeSeconds) {		
		float scaleFactor = scaleEaseMotion->getValue();
		scaleEaseMotion->update(deltaTime);
		
		Quat rotationQuad(0, osg::Z_AXIS);
		Vec3d scale(scaleFactor, scaleFactor, scaleFactor);
		animationPath->insert(currenTime, AnimationPath::ControlPoint(position, rotationQuad, scale));
		currenTime += deltaTime;
	}

	return animationPath;
}
/***************************************************************
* Function: ANIMCreateSingleIconToolkitSwitchAnimation()
***************************************************************/
void ANIMCreateSingleIconToolkitSwitchAnimation(int idx, ANIMIconToolkitSwitchEntry **iconToolkitEntry)
{
    (*iconToolkitEntry)->mSwitch = new Switch;
    PositionAttitudeTransform *iconToolkitPATransFwd = new PositionAttitudeTransform;
    PositionAttitudeTransform *iconToolkitPATransBwd = new PositionAttitudeTransform;

    /* 'mSwitch' has two decendents for foward and backward animations */
    (*iconToolkitEntry)->mSwitch->setAllChildrenOff();
    (*iconToolkitEntry)->mSwitch->addChild(iconToolkitPATransFwd);
    (*iconToolkitEntry)->mSwitch->addChild(iconToolkitPATransBwd);

    /* attach the same 'CAVEGroupIconToolkit' object to both 'PositionAttitudeTransform' */
    const CAVEGeodeIconToolkit::Type type = (CAVEGeodeIconToolkit::Type) idx;
    CAVEGroupIconToolkit *groupIconToolkit = new CAVEGroupIconToolkit(type);
    iconToolkitPATransFwd->addChild(groupIconToolkit);
    iconToolkitPATransBwd->addChild(groupIconToolkit);

    /* set up the forward / backward scale animation paths for toolkit icons */
    AnimationPath* animScaleFwd = new AnimationPath;
    AnimationPath* animScaleBwd = new AnimationPath;
    animScaleFwd->setLoopMode(AnimationPath::NO_LOOPING);
    animScaleBwd->setLoopMode(AnimationPath::NO_LOOPING);
   
    Vec3 scaleFwd, scaleBwd;
    float step = 1.f / ANIM_GEOMETRY_EDITOR_TOOLKIT_SHOWUP_SAMPS;
    for (int i = 0; i < ANIM_GEOMETRY_EDITOR_TOOLKIT_SHOWUP_SAMPS + 1; i++)
    {
        float val = i * step;
        scaleFwd = Vec3(val, val, val);
        scaleBwd = Vec3(1.f-val, 1.f-val, 1.f-val);
        animScaleFwd->insert(val, AnimationPath::ControlPoint(Vec3(0,0,0), Quat(), scaleFwd));
        animScaleBwd->insert(val, AnimationPath::ControlPoint(Vec3(0,0,0), Quat(), scaleBwd));
    }

    (*iconToolkitEntry)->mFwdAnimCallback = new AnimationPathCallback(animScaleFwd, 
					0.0, 1.f / ANIM_GEOMETRY_EDITOR_TOOLKIT_SHOWUP_TIME);
    (*iconToolkitEntry)->mBwdAnimCallback = new AnimationPathCallback(animScaleBwd, 
					0.0, 1.f / ANIM_GEOMETRY_EDITOR_TOOLKIT_SHOWUP_TIME);
    iconToolkitPATransFwd->setUpdateCallback((*iconToolkitEntry)->mFwdAnimCallback);
    iconToolkitPATransBwd->setUpdateCallback((*iconToolkitEntry)->mBwdAnimCallback);
}
/***************************************************************
* Function: ANIMLoadSketchBook()
***************************************************************/
void ANIMLoadSketchBook(PositionAttitudeTransform** xformScaleFwd, PositionAttitudeTransform** xformScaleBwd,
			int &numPages, ANIMPageEntry ***pageEntryArray)
{
    *xformScaleFwd = new PositionAttitudeTransform;
    *xformScaleBwd = new PositionAttitudeTransform;

    MatrixTransform *sketchbookTrans = new MatrixTransform;
    Matrixf transMat, scaleMat;
    transMat.makeTranslate(Vec3(0, 0, ANIM_VIRTUAL_SPHERE_RADIUS));
    scaleMat.makeScale(Vec3(ANIM_PARA_PAINT_FRAME_ZOOM_FACTOR,
			    ANIM_PARA_PAINT_FRAME_ZOOM_FACTOR,
			    ANIM_PARA_PAINT_FRAME_ZOOM_FACTOR));
    sketchbookTrans->setMatrix(transMat * scaleMat);

    (*xformScaleFwd)->addChild(sketchbookTrans);
    (*xformScaleBwd)->addChild(sketchbookTrans);

    // load sketch book node from VRML file, create page geodes 
    Node* sketchbookNode = osgDB::readNodeFile(ANIMDataDir() + "VRMLFiles/SketchBook.WRL");
    sketchbookTrans->addChild(sketchbookNode);


    // Load floorplan filenames from config file
    bool isFile = true;
    int j = 0, numTex;
    std::string file = "", dir, path;
    std::vector<std::string> filenames;
    
    dir = cvr::ConfigManager::getEntry("dir", "Plugin.CaveCADBeta.Floorplans", "/home/cehughes");
    dir = dir + "/";
    path = "Plugin.CaveCADBeta.Floorplans.0";
    file = cvr::ConfigManager::getEntry(path, "", &isFile);

    while (isFile)
    {
        filenames.push_back(dir + file);
        j++;
        char buf[50];
        sprintf(buf, "Plugin.CaveCADBeta.Floorplans.%d", j);
        std::string path = std::string(buf);
        file = cvr::ConfigManager::getEntry(path, "", &isFile);
    }
    
    numPages = j;

    //numPages = 3;
    
    // create tree structured page entry array 
    *pageEntryArray = new ANIMPageEntry*[numPages];

    for (int i = 0; i < numPages; i++)
    {
/*        char idxStr[16];
        if (i < 10) 
        {
            sprintf(idxStr, "0%d", i);
        }
        else if (i < 100) 
        {
            sprintf(idxStr, "%d", i);
        }*/
        string filename = filenames[i];//ANIMDataDir() + "Textures/Floorplans/Floorplan" + string(idxStr) + string(".JPG");

        (*pageEntryArray)[i] = new ANIMPageEntry;
        Switch *singlePageSwitch = new Switch;
        PositionAttitudeTransform *flipUpTrans = new PositionAttitudeTransform;
        PositionAttitudeTransform *flipDownTrans = new PositionAttitudeTransform;

        sketchbookTrans->addChild(singlePageSwitch);
        singlePageSwitch->addChild(flipUpTrans);
        singlePageSwitch->addChild(flipDownTrans);
        singlePageSwitch->setAllChildrenOff();

        // set up flip up / flip down animation paths for each page 
        Geode *flipUpGeode, *flipDownGeode;
        AnimationPathCallback *flipUpCallback, *flipDownCallback;
        ANIMCreateSinglePageGeodeAnimation(filename, &flipUpGeode, &flipDownGeode, &flipUpCallback, &flipDownCallback);

        flipUpTrans->addChild(flipUpGeode);
        flipUpTrans->setUpdateCallback(flipUpCallback);
        flipDownTrans->addChild(flipDownGeode);
        flipDownTrans->setUpdateCallback(flipDownCallback);

        // write into page entry array record 
        (*pageEntryArray)[i]->mSwitch = singlePageSwitch;
        (*pageEntryArray)[i]->mFlipUpAnim = flipUpCallback;
        (*pageEntryArray)[i]->mFlipDownAnim = flipDownCallback;
        (*pageEntryArray)[i]->mPageGeode = flipDownGeode;
        (*pageEntryArray)[i]->mTexFilename = filename;
    }

    (*pageEntryArray)[0]->mSwitch->setSingleChildOn(1);
    (*pageEntryArray)[1]->mSwitch->setSingleChildOn(0);

    // size of floorplan 
    



    // FIX THIS - put sizes in the config file or something
    float alt = -2.9f;
    (*pageEntryArray)[0]->mLength = 32;		(*pageEntryArray)[0]->mWidth = 16;	(*pageEntryArray)[0]->mAlti = alt;
    (*pageEntryArray)[1]->mLength = 128;	(*pageEntryArray)[1]->mWidth = 128;	(*pageEntryArray)[1]->mAlti = alt;
    (*pageEntryArray)[2]->mLength = 32;		(*pageEntryArray)[2]->mWidth = 16;	(*pageEntryArray)[2]->mAlti = alt;
    (*pageEntryArray)[3]->mLength = 32;		(*pageEntryArray)[3]->mWidth = 32;	(*pageEntryArray)[3]->mAlti = alt;




    // set up the forward / backward scale animation paths for sketch book root switch 
    AnimationPath* animationPathScaleFwd = new AnimationPath;
    AnimationPath* animationPathScaleBwd = new AnimationPath;
    animationPathScaleFwd->setLoopMode(AnimationPath::NO_LOOPING);
    animationPathScaleBwd->setLoopMode(AnimationPath::NO_LOOPING);
   
    Vec3 scaleFwd, scaleBwd;
    float step = 1.f / ANIM_VIRTUAL_SPHERE_NUM_SAMPS;
    for (int i = 0; i < ANIM_VIRTUAL_SPHERE_NUM_SAMPS + 1; i++)
    {
        float val = i * step;
        scaleFwd = Vec3(val, val, val);
        scaleBwd = Vec3(1.f-val, 1.f-val, 1.f-val);
        animationPathScaleFwd->insert(val, AnimationPath::ControlPoint(Vec3(),Quat(), scaleFwd));
        animationPathScaleBwd->insert(val, AnimationPath::ControlPoint(Vec3(),Quat(), scaleBwd));
    }

    AnimationPathCallback *animCallbackFwd = new AnimationPathCallback(animationPathScaleFwd, 
						0.0, 1.f / ANIM_VIRTUAL_SPHERE_LAPSE_TIME);
    AnimationPathCallback *animCallbackBwd = new AnimationPathCallback(animationPathScaleBwd, 
						0.0, 1.f / ANIM_VIRTUAL_SPHERE_LAPSE_TIME); 
    (*xformScaleFwd)->setUpdateCallback(animCallbackFwd);
    (*xformScaleBwd)->setUpdateCallback(animCallbackBwd);
}
/***************************************************************
* Function: ANIMCreateSinglePageGeodeAnimation()
***************************************************************/
void ANIMCreateSinglePageGeodeAnimation(const string& texfilename, Geode **flipUpGeode, Geode **flipDownGeode,
					AnimationPathCallback **flipUpCallback, AnimationPathCallback **flipDownCallback)
{
    /* coordinates of page object */
    Vec3 topleft = Vec3(-0.19, 0, 0);
    Vec3 bottomleft = Vec3(-0.19, 0, -0.28);
    Vec3 bottomright = Vec3( 0.19, 0, -0.28);
    Vec3 topright = Vec3( 0.19, 0, 0);
    Vec3 start = Vec3(0, -0.004, 0);
    Vec3 end = Vec3(0, 0.008, 0);
    float pageH = 0.28, pageW = 0.38;

    /* create page pain geometry */
    *flipUpGeode = new Geode;
    *flipDownGeode = new Geode;

    Geometry *pageGeometry = new Geometry();
    Vec3Array* vertices = new Vec3Array;
    Vec2Array* texcoords = new Vec2Array(4);
    Vec3Array* normals = new Vec3Array;

    vertices->push_back(topleft);	(*texcoords)[0].set(0, 1);
    vertices->push_back(bottomleft);	(*texcoords)[1].set(0, 0);
    vertices->push_back(bottomright);	(*texcoords)[2].set(1, 0);
    vertices->push_back(topright);	(*texcoords)[3].set(1, 1);
    
    for (int i = 0; i < 4; i++) 
    {
        normals->push_back(Vec3(0, -1, 0));
    }

    DrawElementsUInt* rectangle = new DrawElementsUInt(PrimitiveSet::POLYGON, 0);
    rectangle->push_back(0);	rectangle->push_back(1);
    rectangle->push_back(2);	rectangle->push_back(3);

    pageGeometry->addPrimitiveSet(rectangle);
    pageGeometry->setVertexArray(vertices);
    pageGeometry->setTexCoordArray(0, texcoords);
    pageGeometry->setNormalArray(normals);
    pageGeometry->setNormalBinding(Geometry::BIND_PER_VERTEX);

    (*flipUpGeode)->addDrawable(pageGeometry);
    (*flipDownGeode)->addDrawable(pageGeometry);

    /* apply image textures to page geodes */
    Image* imgFloorplan = osgDB::readImageFile(texfilename);
    int imgW = imgFloorplan->s();
    int imgH = imgFloorplan->t();
    Texture2D* texFloorplan = new Texture2D(imgFloorplan); 
    texFloorplan->setWrap(Texture::WRAP_S, Texture::CLAMP);
    texFloorplan->setWrap(Texture::WRAP_T, Texture::CLAMP);

    float imgRatio = (float) imgW / imgH;
    float pageRatio = pageW / pageH;
    if (imgRatio <= pageRatio)
    {
        (*texcoords)[0].set((1.0 - pageRatio / imgRatio) * 0.5, 1);
        (*texcoords)[1].set((1.0 - pageRatio / imgRatio) * 0.5, 0);
        (*texcoords)[2].set((1.0 + pageRatio / imgRatio) * 0.5, 0);
        (*texcoords)[3].set((1.0 + pageRatio / imgRatio) * 0.5, 1);
    }
    else
    {
        (*texcoords)[0].set(0, (1.0 + imgRatio / pageRatio) * 0.5);
        (*texcoords)[1].set(0, (1.0 - imgRatio / pageRatio) * 0.5);
        (*texcoords)[2].set(1, (1.0 - imgRatio / pageRatio) * 0.5);
        (*texcoords)[3].set(1, (1.0 + imgRatio / pageRatio) * 0.5);
    }

    Material *transmaterial = new Material;
    transmaterial->setDiffuse(Material::FRONT_AND_BACK, Vec4(1, 1, 1, 1));
    transmaterial->setAlpha(Material::FRONT_AND_BACK, 0.8f);

    Material *solidmaterial = new Material;
    solidmaterial->setDiffuse(Material::FRONT_AND_BACK, Vec4(1, 1, 1, 1));
    solidmaterial->setAlpha(Material::FRONT_AND_BACK, 1.0f);

    StateSet *flipUpStateSet = (*flipUpGeode)->getOrCreateStateSet();
    flipUpStateSet->setTextureAttributeAndModes(0, texFloorplan, StateAttribute::ON);
    flipUpStateSet->setMode(GL_BLEND, StateAttribute::OVERRIDE | StateAttribute::ON );
    flipUpStateSet->setRenderingHint(StateSet::TRANSPARENT_BIN);
    flipUpStateSet->setAttributeAndModes(transmaterial, StateAttribute::OVERRIDE | StateAttribute::ON);

    StateSet *flipDownStateSet = (*flipDownGeode)->getOrCreateStateSet();
    flipDownStateSet->setTextureAttributeAndModes(0, texFloorplan, StateAttribute::ON);
    flipDownStateSet->setMode(GL_BLEND, StateAttribute::OVERRIDE | StateAttribute::ON );
    flipDownStateSet->setRenderingHint(StateSet::TRANSPARENT_BIN);
    flipDownStateSet->setAttributeAndModes(solidmaterial, StateAttribute::OVERRIDE | StateAttribute::ON);

    /* create page flipping animation call backs */
    AnimationPath* animationPathFlipUp = new AnimationPath;
    AnimationPath* animationPathFlipDown = new AnimationPath;
    animationPathFlipUp->setLoopMode(AnimationPath::NO_LOOPING);
    animationPathFlipDown->setLoopMode(AnimationPath::NO_LOOPING);

    Vec3 flipUpOffset, flipDownOffset;
    Quat flipUpQuat, flipDownQuat;
    Vec3 offsetstep = (end - start) / ANIM_SKETCH_BOOK_PAGE_FLIP_SAMPS;
    float anglestep = M_PI * 2 / ANIM_SKETCH_BOOK_PAGE_FLIP_SAMPS;
    float timestep = 1.0f / ANIM_SKETCH_BOOK_PAGE_FLIP_SAMPS;
    for (int i = 0; i < ANIM_SKETCH_BOOK_PAGE_FLIP_SAMPS + 1; i++)
    {
        float val = i * timestep;
        flipUpOffset = start + offsetstep * i;
        flipDownOffset = end - offsetstep * i;
        flipUpQuat = Quat(i * anglestep, Vec3(-1, 0, 0));
        flipDownQuat = Quat(i * anglestep, Vec3(1, 0, 0));
        animationPathFlipUp->insert(val, AnimationPath::ControlPoint(flipUpOffset, flipUpQuat, Vec3(1, 1, 1)));
        animationPathFlipDown->insert(val, AnimationPath::ControlPoint(flipDownOffset, flipDownQuat, Vec3(1, 1, 1)));
    }

    *flipUpCallback = new AnimationPathCallback(animationPathFlipUp, 0.0, 1.0f / ANIM_SKETCH_BOOK_PAGE_FLIP_TIME);
    *flipDownCallback = new AnimationPathCallback(animationPathFlipDown, 0.0, 1.0f / ANIM_SKETCH_BOOK_PAGE_FLIP_TIME);
}
/***************************************************************
* Function: ANIMLoadSketchBook()
***************************************************************/
void ANIMLoadSketchBook(PositionAttitudeTransform** xformScaleFwd, PositionAttitudeTransform** xformScaleBwd,
			int &numPages, ANIMPageEntry ***pageEntryArray)
{
    *xformScaleFwd = new PositionAttitudeTransform;
    *xformScaleBwd = new PositionAttitudeTransform;

    MatrixTransform *sketchbookTrans = new MatrixTransform;
    Matrixf transMat, scaleMat;
    transMat.makeTranslate(Vec3(0, 0, ANIM_VIRTUAL_SPHERE_RADIUS));
    scaleMat.makeScale(Vec3(ANIM_PARA_PAINT_FRAME_ZOOM_FACTOR,
			    ANIM_PARA_PAINT_FRAME_ZOOM_FACTOR,
			    ANIM_PARA_PAINT_FRAME_ZOOM_FACTOR));
    sketchbookTrans->setMatrix(transMat * scaleMat);

    (*xformScaleFwd)->addChild(sketchbookTrans);
    (*xformScaleBwd)->addChild(sketchbookTrans);

    /* load sketch book node from VRML file, create page geodes */
    Node* sketchbookNode = osgDB::readNodeFile(ANIMDataDir() + "VRMLFiles/SketchBook.WRL");
    sketchbookTrans->addChild(sketchbookNode);

    /* create tree structured page entry array */
    numPages = 3;
    *pageEntryArray = new ANIMPageEntry*[numPages];
    for (int i = 0; i < numPages; i++)
    {
	char idxStr[16];
	if (i < 10) sprintf(idxStr, "0%d", i);
	else if (i < 100) sprintf(idxStr, "%d", i);
	string filename = ANIMDataDir() + "Textures/Floorplans/Floorplan" + string(idxStr) + string(".JPG");

	(*pageEntryArray)[i] = new ANIMPageEntry;
	Switch *singlePageSwitch = new Switch;
	PositionAttitudeTransform *flipUpTrans = new PositionAttitudeTransform;
	PositionAttitudeTransform *flipDownTrans = new PositionAttitudeTransform;

	sketchbookTrans->addChild(singlePageSwitch);
	singlePageSwitch->addChild(flipUpTrans);
	singlePageSwitch->addChild(flipDownTrans);
	singlePageSwitch->setAllChildrenOff();

	/* set up flip up / flip down animation paths for each page */
	Geode *flipUpGeode, *flipDownGeode;
	AnimationPathCallback *flipUpCallback, *flipDownCallback;
	ANIMCreateSinglePageGeodeAnimation(filename, &flipUpGeode, &flipDownGeode, &flipUpCallback, &flipDownCallback);

	flipUpTrans->addChild(flipUpGeode);
	flipUpTrans->setUpdateCallback(flipUpCallback);
	flipDownTrans->addChild(flipDownGeode);
	flipDownTrans->setUpdateCallback(flipDownCallback);

	/* write into page entry array record */
	(*pageEntryArray)[i]->mSwitch = singlePageSwitch;
	(*pageEntryArray)[i]->mFlipUpAnim = flipUpCallback;
	(*pageEntryArray)[i]->mFlipDownAnim = flipDownCallback;
	(*pageEntryArray)[i]->mPageGeode = flipDownGeode;
	(*pageEntryArray)[i]->mTexFilename = filename;
    }
    (*pageEntryArray)[0]->mSwitch->setSingleChildOn(1);
    (*pageEntryArray)[1]->mSwitch->setSingleChildOn(0);

    /* size of floorplan */
    (*pageEntryArray)[0]->mLength = 32;		(*pageEntryArray)[0]->mWidth = 16;	(*pageEntryArray)[0]->mAlti = -1.5f;
    (*pageEntryArray)[1]->mLength = 128;	(*pageEntryArray)[1]->mWidth = 128;	(*pageEntryArray)[1]->mAlti = -1.5f;
    (*pageEntryArray)[2]->mLength = 32;		(*pageEntryArray)[2]->mWidth = 16;	(*pageEntryArray)[2]->mAlti = -1.5f;

    /* set up the forward / backward scale animation paths for sketch book root switch */
    AnimationPath* animationPathScaleFwd = new AnimationPath;
    AnimationPath* animationPathScaleBwd = new AnimationPath;
    animationPathScaleFwd->setLoopMode(AnimationPath::NO_LOOPING);
    animationPathScaleBwd->setLoopMode(AnimationPath::NO_LOOPING);
   
    Vec3 scaleFwd, scaleBwd;
    float step = 1.f / ANIM_VIRTUAL_SPHERE_NUM_SAMPS;
    for (int i = 0; i < ANIM_VIRTUAL_SPHERE_NUM_SAMPS + 1; i++)
    {
	float val = i * step;
	scaleFwd = Vec3(val, val, val);
	scaleBwd = Vec3(1.f-val, 1.f-val, 1.f-val);
	animationPathScaleFwd->insert(val, AnimationPath::ControlPoint(Vec3(),Quat(), scaleFwd));
	animationPathScaleBwd->insert(val, AnimationPath::ControlPoint(Vec3(),Quat(), scaleBwd));
    }

    AnimationPathCallback *animCallbackFwd = new AnimationPathCallback(animationPathScaleFwd, 
						0.0, 1.f / ANIM_VIRTUAL_SPHERE_LAPSE_TIME);
    AnimationPathCallback *animCallbackBwd = new AnimationPathCallback(animationPathScaleBwd, 
						0.0, 1.f / ANIM_VIRTUAL_SPHERE_LAPSE_TIME); 
    (*xformScaleFwd)->setUpdateCallback(animCallbackFwd);
    (*xformScaleBwd)->setUpdateCallback(animCallbackBwd);
}
/***************************************************************
* Function: ANIMLoadGeometryCreator()
*
* xformScaleFwd: Root transform node for inflating geometries
* xformScaleBwd: Root transform node for shrinking geometries
* sphereExteriorSwitch: Switch control for single exterior sphere
*
***************************************************************/
void ANIMLoadGeometryCreator(PositionAttitudeTransform** xformScaleFwd, PositionAttitudeTransform** xformScaleBwd,
			     Switch **sphereExteriorSwitch, Geode **sphereExteriorGeode,
			     int &numTypes, ANIMShapeSwitchEntry ***shapeSwitchEntryArray)
{
    *xformScaleFwd = new PositionAttitudeTransform;
    *xformScaleBwd = new PositionAttitudeTransform;

    MatrixTransform *geomCreatorTrans = new MatrixTransform;

    MatrixTransform *sphereExteriorTrans = new MatrixTransform;
    *sphereExteriorSwitch = new Switch;
    Switch *createBoxSwitch = new Switch;
    Switch *createCylinderSwitch = new Switch;

    (*xformScaleFwd)->addChild(geomCreatorTrans);
    (*xformScaleBwd)->addChild(geomCreatorTrans);

    geomCreatorTrans->addChild(*sphereExteriorSwitch);
    geomCreatorTrans->addChild(createBoxSwitch);
    geomCreatorTrans->addChild(createCylinderSwitch);
    
    osg::Vec3 pos(-1, 0, 0);

    // create drawables, geodes and attach them to animation switches
    *sphereExteriorGeode = new Geode();
    Sphere *sphere = new Sphere(osg::Vec3(), ANIM_VIRTUAL_SPHERE_RADIUS);
    ShapeDrawable *sphereDrawable = new ShapeDrawable(sphere);
    (*sphereExteriorGeode)->addDrawable(sphereDrawable);


    Box *box = new Box(osg::Vec3(0.1, 0, 0), ANIM_VIRTUAL_SPHERE_RADIUS / 1.9);
    (*sphereExteriorGeode)->addDrawable(new ShapeDrawable(box));

    float r = ANIM_VIRTUAL_SPHERE_RADIUS / 3.0;
    Cylinder *cylinder = new Cylinder(osg::Vec3(-0.05, 0, -0.05), r, r * 2);
    (*sphereExteriorGeode)->addDrawable(new ShapeDrawable(cylinder));
    
    Cone *cone = new osg::Cone(osg::Vec3(0, -0.1, 0.05), r, r * 2);
    (*sphereExteriorGeode)->addDrawable(new ShapeDrawable(cone));


    Material *transmaterial = new Material;
    transmaterial->setDiffuse(Material::FRONT_AND_BACK, Vec4(1, 1, 1, 1));
    transmaterial->setAlpha(Material::FRONT_AND_BACK, 0.6f);

    Image* envMap = osgDB::readImageFile(ANIMDataDir() + "Textures/ShapeContainer.JPG");
    Texture2D* envTex = new Texture2D(envMap);    
    
    StateSet *sphereStateSet = (sphereDrawable)->getOrCreateStateSet();
    sphereStateSet->setMode(GL_BLEND, StateAttribute::OVERRIDE | StateAttribute::ON );
    sphereStateSet->setRenderingHint(StateSet::TRANSPARENT_BIN);
    sphereStateSet->setAttributeAndModes(transmaterial, StateAttribute::OVERRIDE | StateAttribute::ON);
    sphereStateSet->setTextureAttributeAndModes(0, envTex, StateAttribute::ON);
    sphereStateSet->setMode(GL_CULL_FACE, StateAttribute::ON);

    sphereExteriorTrans->addChild(*sphereExteriorGeode);
    (*sphereExteriorSwitch)->addChild(sphereExteriorTrans);
    (*sphereExteriorSwitch)->setAllChildrenOn();

    // write into shape switch entry array record
    numTypes = 2;
    *shapeSwitchEntryArray = new ANIMShapeSwitchEntry*[numTypes];
    (*shapeSwitchEntryArray)[0] = new ANIMShapeSwitchEntry;
    (*shapeSwitchEntryArray)[1] = new ANIMShapeSwitchEntry;
    (*shapeSwitchEntryArray)[0]->mSwitch = createBoxSwitch;
    (*shapeSwitchEntryArray)[1]->mSwitch = createCylinderSwitch;

    ANIMCreateSingleShapeSwitchAnimation(&((*shapeSwitchEntryArray)[0]), CAVEGeodeShape::BOX);
    ANIMCreateSingleShapeSwitchAnimation(&((*shapeSwitchEntryArray)[1]), CAVEGeodeShape::CYLINDER);

    /* set up the forward / backward scale animation paths for geometry creator */
    AnimationPath* animationPathScaleFwd = new AnimationPath;
    AnimationPath* animationPathScaleBwd = new AnimationPath;
    animationPathScaleFwd->setLoopMode(AnimationPath::NO_LOOPING);
    animationPathScaleBwd->setLoopMode(AnimationPath::NO_LOOPING);
   
    Vec3 scaleFwd, scaleBwd;
    float step = 1.f / ANIM_VIRTUAL_SPHERE_NUM_SAMPS;
    for (int i = 0; i < ANIM_VIRTUAL_SPHERE_NUM_SAMPS + 1; i++)
    {
        float val = i * step;
        scaleFwd = Vec3(val, val, val);
        scaleBwd = Vec3(1.f-val, 1.f-val, 1.f-val);
        animationPathScaleFwd->insert(val, AnimationPath::ControlPoint(pos, Quat(), scaleFwd));
        animationPathScaleBwd->insert(val, AnimationPath::ControlPoint(pos, Quat(), scaleBwd));
    }

    AnimationPathCallback *animCallbackFwd = new AnimationPathCallback(animationPathScaleFwd, 
						0.0, 1.f / ANIM_VIRTUAL_SPHERE_LAPSE_TIME);
    AnimationPathCallback *animCallbackBwd = new AnimationPathCallback(animationPathScaleBwd, 
						0.0, 1.f / ANIM_VIRTUAL_SPHERE_LAPSE_TIME); 
    (*xformScaleFwd)->setUpdateCallback(animCallbackFwd);
    (*xformScaleBwd)->setUpdateCallback(animCallbackBwd);
}
/***************************************************************
* Function: ANIMCreateSingleShapeSwitchAnimation()
***************************************************************/
void ANIMCreateSingleShapeSwitchAnimation(ANIMShapeSwitchEntry **shapeEntry, const CAVEGeodeShape::Type &typ)
{
    PositionAttitudeTransform *flipUpFwdTrans = new PositionAttitudeTransform;
    PositionAttitudeTransform *flipDownFwdTrans = new PositionAttitudeTransform;
    PositionAttitudeTransform *flipUpBwdTrans = new PositionAttitudeTransform;
    PositionAttitudeTransform *flipDownBwdTrans = new PositionAttitudeTransform;

    (*shapeEntry)->mSwitch->addChild(flipUpFwdTrans);		// child #0
    (*shapeEntry)->mSwitch->addChild(flipDownFwdTrans);		// child #1
    (*shapeEntry)->mSwitch->addChild(flipUpBwdTrans);		// child #2
    (*shapeEntry)->mSwitch->addChild(flipDownBwdTrans);		// child #3
    (*shapeEntry)->mSwitch->setAllChildrenOff();

    osg::Vec3 pos(0, 0, 0);

    /* create shape geode based on 'ANIMShapeSwitchEntry::Type' */
    Geode *shapeGeode = new Geode;
    if (typ == CAVEGeodeShape::BOX)
    {
        Box *box = new Box(osg::Vec3(), ANIM_VIRTUAL_SPHERE_RADIUS / 0.9);
        shapeGeode->addDrawable(new ShapeDrawable(box));
    }
    else if (typ == CAVEGeodeShape::CYLINDER)
    {
        float r = ANIM_VIRTUAL_SPHERE_RADIUS / 1.5;
        Cylinder *cylinder = new Cylinder(osg::Vec3(), r, r * 2);
        shapeGeode->addDrawable(new ShapeDrawable(cylinder));
    }
    flipUpFwdTrans->addChild(shapeGeode);
    flipDownFwdTrans->addChild(shapeGeode);
    flipUpBwdTrans->addChild(shapeGeode);
    flipDownBwdTrans->addChild(shapeGeode);

    /* set up flip up / flip down animation paths for shape switch */
    AnimationPath* animationFlipUpFwd = new AnimationPath;
    AnimationPath* animationFlipDownFwd = new AnimationPath;
    AnimationPath* animationFlipUpBwd = new AnimationPath;
    AnimationPath* animationFlipDownBwd = new AnimationPath;
    animationFlipUpFwd->setLoopMode(AnimationPath::NO_LOOPING);
    animationFlipDownFwd->setLoopMode(AnimationPath::NO_LOOPING);
    animationFlipUpBwd->setLoopMode(AnimationPath::NO_LOOPING);
    animationFlipDownBwd->setLoopMode(AnimationPath::NO_LOOPING);
   
    Vec3 scaleUpVect, scaleDownVect;
    Quat flipUpFwdQuat, flipDownFwdQuat, flipUpBwdQuat, flipDownBwdQuat;
    float timestep = ANIM_GEOMETRY_CREATOR_SHAPE_FLIP_TIME / ANIM_GEOMETRY_CREATOR_SHAPE_FLIP_SAMPS;
    float scalestep = 1.f / ANIM_GEOMETRY_CREATOR_SHAPE_FLIP_SAMPS;
    float anglestep = M_PI * 0.5 / ANIM_GEOMETRY_CREATOR_SHAPE_FLIP_SAMPS;

    if (typ == CAVEGeodeShape::BOX)
        pos[2] -= 0.5;
    else if (typ == CAVEGeodeShape::CYLINDER)
        pos[2] -= 1.0;

    osg::Vec3 diff, startPos(0,0,0), fwd, bwd;

    for (int i = 0; i < ANIM_GEOMETRY_CREATOR_SHAPE_FLIP_SAMPS + 1; i++)
    {
        float t = i * timestep;
        float val = i * scalestep;
        scaleUpVect = Vec3(val, val, val);
        scaleDownVect = Vec3(1.f-val, 1.f-val, 1.f-val);

        flipUpFwdQuat = Quat(i * anglestep - M_PI / 2, Vec3(1, 0, 0));
        flipDownFwdQuat = Quat(i * anglestep, Vec3(1, 0, 0));
        flipUpBwdQuat = Quat(i * anglestep - M_PI / 2, Vec3(-1, 0, 0));
        flipDownBwdQuat = Quat(i * anglestep, Vec3(-1, 0, 0));

        diff = startPos - pos;
        
        for (int j = 0; j < 3; ++j)
            diff[j] *= val; 
        fwd = startPos - diff;
        bwd = pos + diff;

        animationFlipUpFwd->insert(t, AnimationPath::ControlPoint(fwd, flipUpFwdQuat, scaleUpVect));
        animationFlipDownFwd->insert(t, AnimationPath::ControlPoint(fwd, flipDownFwdQuat, scaleUpVect));
        animationFlipUpBwd->insert(t, AnimationPath::ControlPoint(bwd, flipUpBwdQuat, scaleDownVect));
        animationFlipDownBwd->insert(t, AnimationPath::ControlPoint(bwd, flipDownBwdQuat, scaleDownVect));
    }

    AnimationPathCallback *animCallbackFlipUpFwd = new AnimationPathCallback(animationFlipUpFwd, 
						0.0, 1.f / ANIM_GEOMETRY_CREATOR_SHAPE_FLIP_TIME);
    AnimationPathCallback *animCallbackFlipDownFwd = new AnimationPathCallback(animationFlipDownFwd, 
						0.0, 1.f / ANIM_GEOMETRY_CREATOR_SHAPE_FLIP_TIME);
    AnimationPathCallback *animCallbackFlipUpBwd = new AnimationPathCallback(animationFlipUpBwd, 
						0.0, 1.f / ANIM_GEOMETRY_CREATOR_SHAPE_FLIP_TIME);
    AnimationPathCallback *animCallbackFlipDownBwd = new AnimationPathCallback(animationFlipDownBwd, 
						0.0, 1.f / ANIM_GEOMETRY_CREATOR_SHAPE_FLIP_TIME);
    flipUpFwdTrans->setUpdateCallback(animCallbackFlipUpFwd);
    flipDownFwdTrans->setUpdateCallback(animCallbackFlipDownFwd);
    flipUpBwdTrans->setUpdateCallback(animCallbackFlipUpBwd);
    flipDownBwdTrans->setUpdateCallback(animCallbackFlipDownBwd);

    /* write into shape switch entry array record*/
    (*shapeEntry)->mFlipUpFwdAnim = animCallbackFlipUpFwd;
    (*shapeEntry)->mFlipDownFwdAnim = animCallbackFlipDownFwd;
    (*shapeEntry)->mFlipUpBwdAnim = animCallbackFlipUpBwd;
    (*shapeEntry)->mFlipDownBwdAnim = animCallbackFlipDownBwd;
}
/***************************************************************
* Function: ANIMCreateVirtualSphere()
*
***************************************************************/
void ANIMCreateVirtualSphere(osg::PositionAttitudeTransform** xformScaleFwd, 
			     osg::PositionAttitudeTransform** xformScaleBwd)
{
    // create sphere geometry
    *xformScaleFwd = new PositionAttitudeTransform;
    *xformScaleBwd = new PositionAttitudeTransform;
    Geode* sphereGeode = new Geode();
    Sphere* virtualSphere = new Sphere();
    Drawable* sphereDrawable = new ShapeDrawable(virtualSphere);

    virtualSphere->setRadius(ANIM_VIRTUAL_SPHERE_RADIUS);
    sphereGeode->addDrawable(sphereDrawable);
    (*xformScaleFwd)->addChild(sphereGeode);
    (*xformScaleBwd)->addChild(sphereGeode);

    osg::StateSet* stateset;   

    // highlights
/*    Sphere* highlightSphere = new Sphere();
    ShapeDrawable* highlightDrawable = new ShapeDrawable(highlightSphere);
    Geode* highlightGeode = new Geode();
    highlightSphere->setRadius(ANIM_VIRTUAL_SPHERE_RADIUS * 1.3);
    highlightDrawable->setColor(osg::Vec4(0,0,1,0.3));
    highlightGeode->addDrawable(highlightDrawable);
    (*xformScaleFwd)->addChild(highlightGeode);
    (*xformScaleBwd)->addChild(highlightGeode);

    stateset = highlightDrawable->getOrCreateStateSet();
    stateset->setMode(GL_BLEND, StateAttribute::ON);
    stateset->setMode(GL_CULL_FACE, StateAttribute::ON);
    stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
    stateset->setRenderingHint(StateSet::TRANSPARENT_BIN);
*/

    // set up the forward / backward scale animation path
    AnimationPath* animationPathScaleFwd = new AnimationPath;
    AnimationPath* animationPathScaleBwd = new AnimationPath;
    animationPathScaleFwd->setLoopMode(AnimationPath::NO_LOOPING);
    animationPathScaleBwd->setLoopMode(AnimationPath::NO_LOOPING);

    osg::Vec3 pos(-1.5, 0, 0);

    Vec3 scaleFwd, scaleBwd;
    float step = 1.f / ANIM_VIRTUAL_SPHERE_NUM_SAMPS;
    for (int i = 0; i < ANIM_VIRTUAL_SPHERE_NUM_SAMPS + 1; i++)
    {
        float val = i * step;
        scaleFwd = Vec3(val, val, val);
        scaleBwd = Vec3(1-val, 1-val, 1-val);
        animationPathScaleFwd->insert(val, AnimationPath::ControlPoint(pos, Quat(), scaleFwd));
        animationPathScaleBwd->insert(val, AnimationPath::ControlPoint(pos, Quat(), scaleBwd));
    }

    AnimationPathCallback *animCallbackFwd = new AnimationPathCallback(animationPathScaleFwd,
						0.0, 1.f / ANIM_VIRTUAL_SPHERE_LAPSE_TIME);
    AnimationPathCallback *animCallbackBwd = new AnimationPathCallback(animationPathScaleBwd,
						0.0, 1.f / ANIM_VIRTUAL_SPHERE_LAPSE_TIME);
    (*xformScaleFwd)->setUpdateCallback(animCallbackFwd);
    (*xformScaleBwd)->setUpdateCallback(animCallbackBwd);

    /* apply shaders to geode stateset */
    stateset = new StateSet();
    stateset->setMode(GL_BLEND, StateAttribute::OVERRIDE | StateAttribute::ON );
    stateset->setRenderingHint(StateSet::TRANSPARENT_BIN);
    //sphereGeode->setStateSet(stateset);
    sphereDrawable->setStateSet(stateset);

    Program* shaderProg = new Program;
    stateset->setAttribute(shaderProg);
    shaderProg->addShader(Shader::readShaderFile(Shader::VERTEX, ANIMDataDir() + "Shaders/VirtualSphere.vert"));
    shaderProg->addShader(Shader::readShaderFile(Shader::FRAGMENT, ANIMDataDir() + "Shaders/VirtualSphere.frag"));

    Image* envMap = osgDB::readImageFile(ANIMDataDir() + "Textures/EnvMap.JPG");
    Texture2D* envTex = new Texture2D(envMap);
    stateset->setTextureAttributeAndModes(0, envTex, StateAttribute::ON);

    Uniform* envMapSampler = new Uniform("EnvMap", 0);
    stateset->addUniform(envMapSampler);

    Uniform* baseColorUniform = new Uniform("BaseColor", Vec3(0.2, 1.0, 0.2));
    stateset->addUniform(baseColorUniform);

    Uniform* lightPosUniform = new Uniform("LightPos", Vec4(1.0, 0.0, 0.2, 0.0));
    stateset->addUniform(lightPosUniform);

}