/*!
	\param flip
	\param disable_hsr
	\param update_time
	\param render_mirrored
*/
void Storm3D_Scene::RenderSceneWithParams(bool flip,bool disable_hsr, bool update_time, bool render_mirrored, IStorm3D_Texture *target)
{
	storm3d_dip_calls = 0;

	// Calculate time difference
	static DWORD last_time=SDL_GetTicks();
	DWORD time_now=SDL_GetTicks();
	if (flip)
	{
		time_dif=time_now-last_time;
		// added use of timing factor...
		if (this->Storm3D2->timeFactor != 1.0f)
		{
			// FIXME: may have a small error on some values
			// should work just fine for factor values like 0.5 though.
			time_dif = (int)(float(time_dif) * this->Storm3D2->timeFactor);
			last_time+=(int)(float(time_dif) / this->Storm3D2->timeFactor);
		} 
		else 
		{
			last_time+=time_dif;
		}
	}
	else
	{
		time_dif=time_now-last_time;
		if (this->Storm3D2->timeFactor != 1.0f)
		{
			// FIXME: may have a small error on some values
			// should work just fine for factor values like 0.5 though.
			time_dif = (int)(float(time_dif) * this->Storm3D2->timeFactor);
		}

		if(!update_time)
			time_dif = 0;
	}

	// Add time
	float ftime_dif=((float)time_dif)/1000.0f;
	time+=ftime_dif;

	// If paused
	if(scene_paused == true)
	{
		ftime_dif = 0.f;
		time_dif = 0;
		time_now = last_time;
	}

	this->camera.SetTime(time_now);

	// Reset active material and mesh
	Storm3D2->active_material=(Storm3D_Material*)1;	// NULL is not right!
	Storm3D2->active_mesh=NULL;

	// Basic renderstates
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_DITHER);
	glDisable(GL_NORMALIZE);
	//enable camera-relative specular highlights?

	// Clear renderlists
	for (int i=0;i<renderlist_size;i++)
	{
		renderlist_obj[i]=NULL;
		renderlist_points[i]=-99999999.0f;
	}

	// Put each model in set into the list
	if(terrains.empty())
	for (set<IStorm3D_Model*>::iterator mit=models.begin();mit!=models.end();++mit)
	{
		// Typecast (to simplify code)
		Storm3D_Model *mod=(Storm3D_Model*)*mit;

		// psd: Is the whole model visible (no animation/object stuff)
		if(!mod->bones.empty())
		{
			Vector &model_position = mod->position;

			// Calculate range (outside check part1... fastest)
			float radius = mod->bounding_radius;
			float nr = camera.vis_range + radius;
			
			if (fabsf(camera.position.x-model_position.x)>nr) continue;
			if (fabsf(camera.position.y-model_position.y)>nr) continue;
			if (fabsf(camera.position.z-model_position.z)>nr) continue;

			// Test sphere visibility
			if (!camera.TestSphereVisibility(model_position, radius)) 
				continue;
		}

		float range = camera.GetPosition().GetRangeTo(mod->GetPosition());
		mod->lodLevel = int(range / 20.f);
		if(mod->lodLevel >= IStorm3D_Mesh::LOD_AMOUNT)
			mod->lodLevel = IStorm3D_Mesh::LOD_AMOUNT - 1;

		// Animate bone structure
		mod->AdvanceAnimation(time_dif);

		if (flip)
		{
			// Apply animation to helpers
			for(set<IStorm3D_Helper*>::iterator ih=mod->helpers.begin();ih!=mod->helpers.end();++ih)
			{
				// Typecast (to simplify code)
				IStorm3D_Helper *hlp=(IStorm3D_Helper*)*ih;
				switch(hlp->GetHelperType())
				{
					case IStorm3D_Helper::HTYPE_POINT:
						((Storm3D_Helper_Point*)hlp)->animation.Apply(this);
						break;

					case IStorm3D_Helper::HTYPE_VECTOR:
						((Storm3D_Helper_Vector*)hlp)->animation.Apply(this);
						break;

					case IStorm3D_Helper::HTYPE_BOX:
						((Storm3D_Helper_Box*)hlp)->animation.Apply(this);
						break;

					case IStorm3D_Helper::HTYPE_CAMERA:
						((Storm3D_Helper_Camera*)hlp)->animation.Apply(this);
						break;

					case IStorm3D_Helper::HTYPE_SPHERE:
						((Storm3D_Helper_Sphere*)hlp)->animation.Apply(this);
						break;
				}
			}
		}

		// Put each object in set into the list
		for(set<IStorm3D_Model_Object*>::iterator io=mod->objects.begin();io!=mod->objects.end();++io)
		{	
			// Typecast (to simplify code)
			Storm3D_Model_Object *obj=(Storm3D_Model_Object*)*io;

			// Skip if object does not have a mesh
			if (obj->mesh==NULL) continue;

			// If object has no_render skip it
			if (obj->no_render) continue;

			// Calculate object world position
			VC3 owp=obj->GetGlobalPosition();

			// Calculate range (outside check part1... fastest)
			float mrad=obj->mesh->GetRadius();
			float nr=camera.vis_range+mrad;
			if (fabsf(camera.position.x-owp.x)>nr) continue;
			if (fabsf(camera.position.y-owp.y)>nr) continue;
			if (fabsf(camera.position.z-owp.z)>nr) continue;

			// Test sphere visibility
			if (!camera.TestSphereVisibility(owp,mrad)) continue;

			// Calculate range to camera (LOD needs)
			// Check if it's outside camera's range
			float range=camera.position.GetRangeTo(owp);
			if ((range-mrad)>camera.vis_range) continue;

			// Calculate object points
			float points=range;

			// Add points if alphablending is used:
			// alpha-object's are always rendered last
			bool alpha_on=false;
			if (obj->mesh->GetMaterial())
			{
				if (obj->mesh->GetMaterial()->GetAlphaType()!=Storm3D_Material::ATYPE_NONE) alpha_on=true;
			}
			if (alpha_on)
			{
				// Draw alpha's always last
				points-=999999;
			}
			else
			{
				// Inverse points if opaque (drawn from front to back)
				// Speeds up raster performance
				points=-points;
			}

			// Calculate list position (optimize)
			int lp = 0;
			for (;renderlist_points[lp]>points;lp++);	// OK!
			
			// Move end of list 1 position backwards
			for (int i=renderlist_size-1;i>lp;i--)
			{
				renderlist_points[i]=renderlist_points[i-1];
				renderlist_obj[i]=renderlist_obj[i-1];
			}

			// Put object into the list
			renderlist_points[lp]=points;
			renderlist_obj[lp]=obj;

			// Test if there is enough room in list (v3)
			if (renderlist_obj[renderlist_size-1])
			{
				// Allocate double size (v3)
				int new_renderlist_size=renderlist_size*2;
				PStorm3D_Model_Object *new_renderlist_obj=new PStorm3D_Model_Object[new_renderlist_size];
				float *new_renderlist_points=new float[new_renderlist_size];
				
				// Clear new renderlists
				for (int i=0;i<new_renderlist_size;i++)
				{
					new_renderlist_obj[i]=NULL;
					new_renderlist_points[i]=-99999999.0f;
				}

				// Copy data
				memcpy(new_renderlist_obj,renderlist_obj,sizeof(PStorm3D_Model_Object)*renderlist_size);
				memcpy(new_renderlist_points,renderlist_points,sizeof(float)*renderlist_size);

				// Delete old data
				delete[] renderlist_obj;
				delete[] renderlist_points;

				// Set values
				renderlist_size=new_renderlist_size;
				renderlist_obj=new_renderlist_obj;
				renderlist_points=new_renderlist_points;
			}
		}
	}

	// Start REAL scene rendering
#ifdef NVPERFSDK
	if (flip) {
		int nCount = 0;
		NVPMBeginExperiment(&nCount);
		if (nCount > 0) {
			igiosWarning("begin experiment, %d cycles\n", nCount);
			for (int i = 0; i < nCount; i++ ) {
				NVPMBeginPass(i);
				renderRealScene(flip, render_mirrored);
				NVPMEndPass(i);
			}
			NVPMEndExperiment();

			UINT64 value = 0, cycles = 0;
			char *bname = new char[50];
			NVPMGetCounterValueByName("GPU Bottleneck", 0, &value, &cycles);
			NVPMGetGPUBottleneckName(value, bname);
			bottlenecks[value]++;
			igiosWarning("GPU Bottleneck value: %lu cycles: %lu\n", value, cycles);
			igiosWarning("Bottleneck : %s\n", bname);
			delete[] bname;
		} else {
			Storm3D_Texture *tgt = static_cast<Storm3D_Texture*>(target);
			renderRealScene(flip, render_mirrored, tgt);
		}
	} else {
		Storm3D_Texture *tgt = static_cast<Storm3D_Texture*>(target);
		renderRealScene(flip, render_mirrored, tgt);
	}
#else
	Storm3D_Texture *tgt = static_cast<Storm3D_Texture*>(target);
	renderRealScene(flip, render_mirrored, tgt);
#endif
}
// redraw the window
void display(void)
{   
    if(glutGetWindow() == mainwinid)
    {
#ifdef OSG_WITH_NVPERFSDK
        if(nvDataProvider->nCounters())
        {
            nvDataProvider->sample();

            OSG::Char8 str[40];
            
            for(int i = 0; nvStatElems[i] != NULL; ++i)
            {
                if(collector != NULL)
                {
                    sprintf(str, "%s: %f", nvStatElems[i]->getDescription().c_str(),
                            nvDataProvider->value(i)); 

                    OSG::StatStringElem *e = dynamic_cast<OSG::StatStringElem*>(
                        collector->getElem(*nvStatElems[i]));  

                    e->set(str);
                }
            }
        }
        
        if(runExperiment)
        {
            int nCount;
            
            const char *expCounters[] = { 
                "2D Bottleneck", "2D SOL", 
                "IDX Bottleneck", "IDX SOL", 
                "GEOM Bottleneck", "GEOM SOL", 
                "ZCULL Bottleneck", "ZCULL SOL", 
                "TEX Bottleneck", "TEX SOL", 
                "ROP Bottleneck", "ROP SOL", 
                "SHD Bottleneck", "SHD SOL", 
                "FB Bottleneck", "FB SOL", 
                "GPU Bottleneck", // Needs to be last 
                NULL };
            
            for(int i = 0; expCounters[i] != NULL; ++i)
            {
                NVPMAddCounterByName(const_cast<char *>(expCounters[i]));
            }
            
            NVPMBeginExperiment(&nCount);
            
            FLOG(("NVPerfKitSDK: Running %d passes\n", nCount));
            
            for(int i = 0; i < nCount; i++)
            {
                NVPMBeginPass(i);
                mgr->redraw();
                NVPMEndPass(i);
            }
            NVPMEndExperiment();
            
            UINT64 value, cycles;
            
            for(int i = 0; expCounters[i] != NULL; ++i)
            {
                NVPMGetCounterValueByName(const_cast<char *>(expCounters[i]), 0, &value, &cycles);
                FLOG(("%s: %lld value, %lld cycles (%.4f%%)\n", 
                        expCounters[i], value, cycles, value * 100. / cycles));
            }
            
            char buffer[1000] = "";
            
            NVPMGetGPUBottleneckName(value, buffer);

            FLOG(("GPU Bottleneck: '%s'\n", buffer));

            for(int i = 0; expCounters[i] != NULL; ++i)
            {
                //NVPMRemoveCounterByName(expCounters[i]);               
            }
            
            runExperiment = false;
        }
#endif
        mgr->redraw();
    }
    else if(glutGetWindow() == debugwinid)
    {
        // Use RenderAction to prevent new occlusion culling on debug output
        debugwin->render(debugact);
    }
}