// ********************************************************************************
// Example for lighting artifact
// Launch the app and zoom out: the cubes appear to flicker.
// Actually the light seem to be rotated as the last deleted model was (or something similar).
// Read the notes in the code about lines that affect the artifact.
// ********************************************************************************


#include <string>
#include <osgViewer/Viewer>
#include <osg/MatrixTransform>
#include <osg/ShapeDrawable>
#include <osgGA/TrackBallManipulator>

// Ugly globals
osg::ref_ptr<osg::Group> pRootNode;
osg::Group * pGameRoot;


/// Initialises the graph with some basics (light, cull face).
void loadGameInit() {
	// Base
	osg::Group * pInnerRoot = NULL;
	pGameRoot  = NULL;
	osg::LightSource * pLight0 = NULL;
	{
		pInnerRoot = new osg::Group;
		pLight0 = new osg::LightSource;

		pGameRoot = pLight0;
		pInnerRoot->addChild(pLight0);
		osg::Light * pLightParams0 = new osg::Light();
		pLightParams0->setAmbient(osg::Vec4f(.1f,.1f,.1f,1));
		pLightParams0->setDiffuse(osg::Vec4f(1,1,1,1));
		pLightParams0->setSpecular(osg::Vec4());

		osg::Vec3f direction(1,-1,-1);
		direction.normalize();
		pLightParams0->setPosition(osg::Vec4f(-direction, 0));
		//pLightParams0->setDirection(direction);
		pLightParams0->setLightNum(0);
		pLight0->setLight(pLightParams0);

		//pLight0->setLocalStateSetModes(osg::StateAttribute::ON);
		pLight0->getOrCreateStateSet()->setAttribute(pLightParams0, osg::StateAttribute::ON);
	}
	pRootNode->addChild(pInnerRoot);
}


// For testing purpose
osg::MatrixTransform * createModel(const osg::Matrix & rotation, const osg::Matrix & translation) {
	// ********************************************************************************
	// *** ABOUT THE ARTIFACT : if you remove the rotation transform, it will be okay (see note in the main loop). ***
	// ********************************************************************************

	osg::MatrixTransform * pModel = new osg::MatrixTransform(rotation * translation);
	//pModel->addChild(osgDB::readNodeFile("Something.osg"));		// Also woks with models
	osg::Geode * pGeode = new osg::Geode();
	pGeode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3f(0,0,0), 3)));
	pModel->addChild(pGeode);
	return pModel;
}


void load() {
	//pRootNode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON);
	pGameRoot->addChild(createModel(osg::Matrix::rotate(osg::PI, osg::Z_AXIS), osg::Matrix::translate(osg::Vec3(0,0,0))));
}


/// Init the viewer with no light and a TrackballManipulator.
void viewerInit(osgViewer::Viewer & viewer) {
	// Init the window
	viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded);
	viewer.setSceneData(pRootNode.get());
	viewer.realize();

	// Disable light attached to the viewer
	viewer.setLightingMode(osg::View::NO_LIGHT);

	viewer.setCameraManipulator( new osgGA::TrackballManipulator );

	// Set a view so that appearing models will be (at least partially) in the frustum (not needed for the artifact to appear)
	osg::Camera * pCamera = viewer.getCamera();
	pCamera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
	pCamera->setProjectionMatrixAsPerspective(70., 1., .05, 10000.);
}


int main(int argc, char **argv) {
	osgViewer::Viewer viewer;
	pRootNode = new osg::Group(); 

	loadGameInit();
	load();

	// ********************************************************************************
	// *** COMMENT THE FOLLOWING LINE TO REMOVE THE ARTIFACT ***
	// ********************************************************************************
	pRootNode->addChild(new osg::LightSource);
	//pGameRoot->addChild(new osg::LightSource);		// Also creates an artifact, but that appears only once

	viewerInit(viewer);

	double lastAdd = viewer.getFrameStamp()->getReferenceTime();
	int pos = 0;
	while( !viewer.done() ) {
		viewer.frame();

		// Add model each .5 sec
		if (viewer.getFrameStamp()->getReferenceTime() - lastAdd > .5f) {
			// ********************************************************************************
			// *** ABOUT THE ARTIFACT: It seems that the light direction is affected by the rotation.
			// *** Try changing "if (true)" to "if (false)" to change the way the artifact appears.
			// ********************************************************************************
			float rotation;
			if (true) {
				// Rotate a cube out of two: The light appear to flicker
				rotation = pos%2 ? osg::PI : 0;		// If you set a lower angle (say PI*.05f) then it will less flicker.
			} else {
				// Rotate each cube a little bit more; shows the lighting rotating around the models.
				rotation = osg::PI*2*pos/20.f;
			}
			pGameRoot->addChild(createModel(osg::Matrix::rotate(rotation, osg::Z_AXIS), osg::Matrix::translate(osg::Vec3(cos(osg::PI*2*pos/6.f)*7, 0, sin(osg::PI*2*pos/6.f)*7))));
			lastAdd = viewer.getFrameStamp()->getReferenceTime();
			++pos;

			// ********************************************************************************
			// *** COMMENT THE FOLLOWING LINE TO REMOVE THE ARTIFACT ***
			// *** This seems to indicate that it happens on deletion only.
			// ********************************************************************************
			if (pos>3) pGameRoot->removeChild(0, 1);
		}
	}

	pRootNode = NULL;

	return 0;
}
