#include "stdafx.h"
#include "trees.h"
#include "infoview.h"
#include "psys.h"
#include "thingview.h"
#include <direct.h>
#include "camedit.h"
#include "talkie.h"
#include "seeddlg.h"

extern int curframeidx;
//using namespace D3D;

int treeseed = 272;

surf *backbuf;
RenderThing *audioproc;
RenderThing *videoproc;
int realtime;
CThingDoc *thedoc;
SoundOut *soundout;
stereo AudioBlock[BLOCKSIZE];
stereo TempAudioBlock[BLOCKSIZE];
int finited;

MeObj flowercentre;
MeObj petal;
D3DTexture *leaftex[3];
Surface *finalout;
float Time2Row(float t)
{
	return t / FRAMESPERROW * 25.f;
}

float Row2Time(float t)
{
	return t * FRAMESPERROW / 25.f;
}
	
struct lily
{
	v3 pos;
	float lowleaf ;
	float leafup ;
	float leafadj[21*5];	
	float overallscale ;
	float centresize ;
	float t;
	float spin;
	float dspin;
	
	lily(v3 ps, float sz)
	{
		t=0;
		spin=0;
		pos=ps;
		lowleaf = GaussRandom(0,PI/5.f);
		leafup = GaussRandom(PI/5.f,PI/7.f) * 0.8f;		
		for (int c1=0;c1<21*5;c1++) leafadj[c1]=GaussRandom();
		overallscale = powf(1.5f,GaussRandom()) / 2.f * 0.1f; // sz;
		centresize = powf(1.5f,GaussRandom()) * 1.1f ;
		dspin=GaussRandom()+0.1f;
	}

	void Draw(float dt, bool flipy=false)
	{		
		dt*=0.3f;
		t+=dt;
		spin+=dspin*dt*0.3f;
		v3 xpos = pos + v3(20 * Perlin::Noise1o(t*3*0.0031f+54.352f), 0, 20 * Perlin::Noise1o(t*3*-0.00217f-14.352f));
		m44 m;
		m.Scale(overallscale*centresize,overallscale*centresize,overallscale*centresize);
		m.MultTranslate(xpos);
		if (flipy)
		{
			m.MultScale(1,-1,1);
		}
		flowercentre.Draw(m);

		int ii=0;
		for (int row=0;row<3;row++)
		{
			float up=lowleaf + leafup * row;
			for (int c1=0;c1<petal.prim.size();c1++)
			{
				petal.prim[c1]->tex1 = leaftex[row];
			}
			for (int f=0;f<7;f++)
			{
				ii++;
	#define SCALRANGE 1.2f
				m.Scale(overallscale*powf(SCALRANGE,leafadj[row*7+f+21*2]),overallscale*powf(SCALRANGE,leafadj[row*7+f+21*3]),overallscale*powf(SCALRANGE,leafadj[row*7+f+21*4]));
				m.MultRotateX(up+leafadj[row*7+f]*0.1f*(row*row+1)+0.1f*Perlin::Noise2o(ii,t));
				m.MultTranslate(0,0,-4*overallscale);
				m.MultRotateY(spin+(f+row/2.f+leafadj[row*7+f+21])*PI*2/7.f+0.02f*Perlin::Noise2o(ii,t*1.0512f+24.352f));			
				m.MultTranslate(xpos);
				if (flipy)
				{
					m.MultScale(1,-1,1);
				}
				petal.Draw(m);
			}
		}
	}

};

typedef std::vector<lily> lilylist;

lilylist lillies;

v3 DrawVine(SimpleBuffer &b, PointList &pl, float len, float baserad, DWORD vinecol, DWORD vinecol2, SimpleBuffer2D *toshadow)
{
	v3 retval=pl.empty() ? v3(0,0,0) : pl[0];	
	len=bound(0,len,1);
	if (len<=0) return retval;
	int nump=pl.size();
	baserad*=0.5f+sqrtf(len)/2;
	len*=nump-0.01f;
	if (len<1) return retval;
	v3 lastp=pl[0]+v3(0,-1,0);
	v3 xx(1,0,0);
	v3 zz(0,0,1);
	float vv=0;
	for (int i=0;i<len-1;i++)
	{
		
		float therad=baserad - (i/len)*0.333f;
		if (i>len-4)
		{
			float fac = (i-(len-4))/3;
			therad*= 1-fac*fac*fac;
		}
		// add ring of points
		v3 yy=(pl[i]-lastp);
		vv+=Length(yy)*0.1f;
		yy=Normalize(yy);
		xx=Normalize(CrossProduct(yy,zz));
		zz=Normalize(CrossProduct(xx,yy));
		lastp=pl[i];
		float theta=0;
		for (int th=0;th<17;th++,theta+=PI*2/16.f)
		{		
			float ct=cosf(theta);
			float st=sinf(theta);
			v3 n=xx*ct+zz*st;
			v3 p=lastp + n*therad;
			if (p.y<0) p.y=0;
			v3 ldir = Normalize(sunpos-p);
			float dot = DotProduct(ldir,n);		
			dot+=0.2f;
			dot=dot*dot;
			if (dot<0.5f) dot=0.5f;			
			DWORD col=dot <= 1 ? INTERP(0xff000000,vinecol,int(dot*255)) : INTERP(vinecol,vinecol2,int((dot-1)*255));
			if (toshadow)
			{
				ShadowProject(p,true);
				toshadow->AddVertex(p,0xff000000);
				// add ring of tris
				if (i)
				{
					int th2=(th+1);
					toshadow->AddQuad(th+i*17,th+i*17-17,th2+i*17-17,th2+i*17);
				}
			}
			else
			{				
				b.AddVertex(p,col,theta/(PI*2),vv);				
				// add ring of tris
				if (i)
				{
					int th2=(th+1);
					b.AddQuad(th+i*17,th+i*17-17,th2+i*17-17,th2+i*17);
				}
			}
			
		}	
	}
	float dot = 0.5f;
	DWORD col=INTERP(0xff000000,vinecol,int(dot*200));
	float frac = len-i;
	v3 p=lastp*(1-frac) + pl[i]*frac;

	int i2 = i+0-1;
	int i3 = i+1-1;
	if (i2>=pl.size()) i2=pl.size()-1;
	if (i3>=pl.size()) i3=pl.size()-1;
	retval=pl[i2]*(1-frac) + pl[i3]*frac;
	
	if (p.y<0) p.y=0;
	if (toshadow)
	{
		ShadowProject(p,true);
		int peak=toshadow->AddVertex(p,0xff000000);
		for (int th=0;th<17;th++)
		{
			int th2=(th+1);
			toshadow->AddTri(peak,th+i*17-17,th2+i*17-17);
		}
	}
	else
	{
		int peak=b.AddVertex(p,col,0.5f,vv);
		for (int th=0;th<17;th++)
		{
			int th2=(th+1);
			b.AddTri(peak,th+i*17-17,th2+i*17-17);
		}
	}
	if (toshadow) toshadow->Draw(); else b.Draw();
	

	return retval;


}

void DrawGroundPlane(SimpleBuffer &b)
{

	d3ddev->SetRenderState(D3DRS_COLORWRITEENABLE,0);			
	int a1 = b.AddVertex(v3(-1000,0,-1000),-1);
	int a2 = b.AddVertex(v3( 1000,0,-1000),-1);
	int a3 = b.AddVertex(v3( 1000,0, 1000),-1);
	int a4 = b.AddVertex(v3(-1000,0,-1000),-1);
	b.AddQuad(a1,a2,a3,a4);
	b.Draw();
	d3ddev->SetRenderState(D3DRS_COLORWRITEENABLE,D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED );			
}

struct thefloor
{
	D3DTexture *circle;
	SimpleBuffer blah;
	PointList verts;
	IntPairSet edges;
	tree mytree;
	F *myf;
	int numf;

	void AddEdge(int aa, int bb,int col)
	{
		Vector3 pa = verts[aa];
		Vector3 pb = verts[bb];
		
		float wid=(max(Length(pa),Length(pb)))/200.f+0.002f;
		Vector3 along=Normalize(pb-pa);
		Vector3 across(along.z,along.y,-along.x);
		along*=wid;
		across*=wid;
		int p0=blah.AddVertexFog(pa-along-across,col,0,0);
		int p1=blah.AddVertexFog(pa-along+across,col,1,0);
		int p2=blah.AddVertexFog(pa      -across,col,0,0.5f);
		int p3=blah.AddVertexFog(pa      +across,col,1,0.5f);
		int p4=blah.AddVertexFog(pb      -across,col,0,0.5f);
		int p5=blah.AddVertexFog(pb      +across,col,1,0.5f);
		int p6=blah.AddVertexFog(pb+along-across,col,0,1);
		int p7=blah.AddVertexFog(pb+along+across,col,1,1);
		
		blah.AddQuad(p0,p1,p3,p2);
		blah.AddQuad(p2,p3,p5,p4);
		blah.AddQuad(p4,p5,p7,p6);			
	}

	void Draw(int col = 0xff000000)
	{
		d3ddev->SetTexture(0,0);
		d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,false);
		int fcol=0xffffff;
		for (int c1=0;c1<numf;c1++)
		{
			int alpha = (255*myf[c1].alpha) ;
			alpha<<=24;
			blah.AddTri(
				blah.AddVertexFog(verts[myf[c1].a],fcol+alpha),
				blah.AddVertexFog(verts[myf[c1].b],fcol+alpha),
				blah.AddVertexFog(verts[myf[c1].c],fcol+alpha)
				);
		}
		blah.Draw(true,true);
		
		d3ddev->SetTexture(0,circle);
		
		
		for (IntPairSet::iterator tit=edges.begin();tit!=edges.end();tit++)
		{
			AddEdge(tit->first,tit->second,col);
			blah.Flush(true,true);
		}
		blah.Draw(true,true);
		
		d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,true);
		d3ddev->SetTexture(0,0);
	}


	thefloor()
	{
		D3DXCreateTextureFromFile(d3ddev,"circle.tga",&circle);

		//SetupPixelFog(0xffffff,D3DFOG_LINEAR,20.f,300.f);

		for (int c1=0;c1<3000;c1++)
		{
			Vector3 p;
			float r=Random(0.001f,1);
			r=powf(r,1.5f)*1500;
			float theta=Random(-PI,PI);
			p.x=r*sin(theta);
			p.y=0;
			p.z=r*cos(theta);
			verts.push_back(p);
		}

		myf=new F[verts.size()*3];
		numf = CalcDelaunay(&*verts.begin(),verts.size(),myf);
		for (int ff=0;ff<numf;)
		{
			v3 va = verts[myf[ff].a];
			v3 vb = verts[myf[ff].b];
			v3 vc = verts[myf[ff].c];
			v3 cen=(va+vb+vc)/3;
			float dist=Length(cen);
			if (dist>15.f && Random(0,1)<powf((dist-15)/1000.f,0.1f))
			{
				float divalpha=1;
				if (dist>20 && dist < 240 && lillies.size()<20 && Random(0,100)>dist && Random(0,1)<0.21f)
				{
					float u = GaussRandom(0.2f,0.8f);
					float v = GaussRandom(0.2f,0.8f) * (1-u);
					float w = 1-u-v;
					lillies.push_back(lily(va*u+vb*v+vc*w,(lillies.size()*0.025f + 2.f) / (dist*dist) * 1.31f * Length(CrossProduct(va-vb,vc-vb))));
					divalpha=2;
				}
				//myf[ff]=myf[numf-1];				
				//numf--;
				myf[ff].alpha=Random(0,0.7f)/divalpha;
				ff++;

			}
			else
			{
				int aa,bb;
				std::pair<IntPairSet::iterator,bool> ips;
				aa=myf[ff].a;bb=myf[ff].b;ips=edges.insert(std::pair<int,int>(min(aa,bb),max(aa,bb)));//if (ips.second==false) edges.erase(ips.first);
				aa=myf[ff].c;bb=myf[ff].b;ips=edges.insert(std::pair<int,int>(min(aa,bb),max(aa,bb)));//if (ips.second==false) edges.erase(ips.first);
				aa=myf[ff].a;bb=myf[ff].c;ips=edges.insert(std::pair<int,int>(min(aa,bb),max(aa,bb)));//if (ips.second==false) edges.erase(ips.first);
				myf[ff].alpha=Random(0.5f,0.9f);
				ff++;
			}
			
		}

	}

	~thefloor()
	{

	}
};

Surface backsurf[4];
int halfmode=HALFMODE;

D3DTexture *linetex=NULL;
SimpleBuffer2D buf2d;

void DrawHLines(float dt, float zamrad)
{
	static float t=0;
	t+=dt*0.01f;
	if (!linetex)
	{
		linetex=LoadTexture("minton.jpg");
	}

	float x[4],y[4];
	float x2[4],y2[4];
	for (int ff=0;ff<4;ff++)
	{
		x2[ff]=Perlin::Noise2(ff*11+3.7f+t*0.1f,t)+0.5f;
		y2[ff]=Perlin::Noise2(624-t*1.152,ff*12+3.7f+t*0.1f)+0.5f;
		
	}	
	for (ff=0;ff<4;ff++)
	{
		if (ff&1)
		{
			x[ff]=-x2[ff]*zamrad + x2[ff-1];
			y[ff]=-y2[ff]*zamrad + y2[ff-1];
		}
		else
		{
			x[ff]=-x2[ff]*zamrad + x2[ff];
			y[ff]=-y2[ff]*zamrad + y2[ff];
		}
	}
	d3ddev->SetTexture(0,linetex);
	for (int l=0;l<256;l++)
	{
		int q[4];
		float uu[2],vv[2];
		float fac=l/256.f;
		for (int ff=0;ff<2;ff++)
		{
			uu[ff]=(x[ff]*fac)+(x[ff+2]*(1-fac));
			vv[ff]=(y[ff]*fac)+(y[ff+2]*(1-fac));
		}
		for (ff=0;ff<4;ff++)
		{
			q[ff]=buf2d.AddVertex(v3((ff&1)?0:1,((ff&2)?0:(1.f/256.f))+l/256.f,0.5f),0xffffffff,uu[ff&1],vv[ff&1]);
		}
		buf2d.AddQuad(q[0],q[1],q[3],q[2]);
	}
	buf2d.Draw();
	d3ddev->SetTexture(0,NULL);
}

struct PIN
{
	float u,v;
	DWORD col;
};

struct pinmap
{
	PIN p[26][41];
};


void DrawPinMap(pinmap &map)
{
	buf2d.Reset();
	for (int y=0;y<=25;y++)
	{
		for (int x=0;x<=40;x++)
		{
			buf2d.AddVertex(v3(x/40.f,y/25.f,0.5f),map.p[y][x].col,map.p[y][x].u,map.p[y][x].v);
			
			if (x<40 && y<25) 
			{
				int i=x+y*41;
				buf2d.AddQuad(i,i+1,i+41+1,i+41);
			}
		}
	}
	buf2d.Draw();
}

void ResetPinmap(pinmap &map, DWORD col=0xffffffff, float u1=0, float v1=0, float u2=1,float v2=1)
{
	for (int y=0;y<=25;y++)
	{
		for (int x=0;x<=40;x++)
		{
			map.p[y][x].col=col;
			map.p[y][x].u=u1+(u2-u1)*(x/40.f);
			map.p[y][x].v=v1+(v2-v1)*(y/25.f);
		}
	}
}

void BlendPinmap(pinmap &map, pinmap &b, float fac)
{
	int ifac=bound(0,int(fac*255),255);
	for (int y=0;y<=25;y++)
	{
		for (int x=0;x<=40;x++)
		{
			map.p[y][x].col=INTERP(b.p[y][x].col,map.p[y][x].col,ifac);
			map.p[y][x].u+=(b.p[y][x].u-map.p[y][x].u)*fac;
			map.p[y][x].v+=(b.p[y][x].v-map.p[y][x].v)*fac;
		}
	}
}

void AddNoiseToPinmap(pinmap &map, float urad, float vrad, float xufreq, float yufreq, float xvfreq, float yvfreq, float t)
{
	for (int y=0;y<=25;y++)
	{
		for (int x=0;x<=40;x++)
		{
			map.p[y][x].u+=Perlin::Noise3(t,x*xufreq,y*yufreq)*urad;
			map.p[y][x].v+=Perlin::Noise3(x*xvfreq,y*yvfreq,t*1.10725f+100.5f)*vrad;
		}
	}
}

float sigmoid(float x)
{
	return 3*x*x-2*x*x*x;
}

void BlendPinmapTimeDistort(pinmap &map, pinmap &b, float t, float tfac, float cenx, float ceny)
{
	
	float xfreq=0.1f;
	float yfreq=0.1f;

	for (int y=0;y<=25;y++)
	{
		for (int x=0;x<=40;x++)
		{
			float xx=(x/40.f-cenx);
			float yy=(y/40.f-ceny);
			float d=sqrtf(xx*xx+yy*yy)+0.2f*Perlin::Noise3(t,x*xfreq,y*yfreq);
			d=bound(0,d,1);
			float fac=tfac*2-d;
			fac=bound(0,fac,1);
			fac=sigmoid(fac);
			int ifac=int(fac*255);
			map.p[y][x].col=INTERP(b.p[y][x].col,map.p[y][x].col,ifac);
			map.p[y][x].u+=(b.p[y][x].u-map.p[y][x].u)*fac;
			map.p[y][x].v+=(b.p[y][x].v-map.p[y][x].v)*fac;
		}
	}
}
struct interpstuff
{
	union
	{
		struct
		{
			m44 mat;
			v3 foc;
			float fov;	// 20
			float user[8]; // 28
		};
		float f[32];
	};
	
	interpstuff()
	{
		memset(this,0,sizeof(*this));
		mat=cammat;
		fov=camfov;
		foc=camfoc;
	}
	
};

typedef std::vector<interpstuff> interplist;

struct scene;

extern HWND thehwnd;

struct fpscam
{
	int lastt;

	m44 mt;
	m44 imt;
	v3 p;
	float fov;

	v3 getf()
	{
		return p+mt.GetZ();
	}

	fpscam()
	{
		mt.Identity();
		p=v3(20,5,0);
		mt.Identity();		
		lastt=0;
		fov=PI/2.f;
	}

	
	void Update()
	{
		int tt=GetTickCount();
		if (lastt==0) lastt=tt;
		int idt=tt-lastt;
		lastt=tt;
		float dt=idt/1500.f;
		float MOVESPEED = 9.f;

		if (GetFocus() == thehwnd)
		{
		
				
			if (GetAsyncKeyState('Z')<0)
			{
				p-=mt.GetZ() * dt * MOVESPEED;
			}
			if (GetAsyncKeyState('A')<0)
			{
				p+=mt.GetZ() * dt * MOVESPEED;
			}
			if (GetAsyncKeyState('S')<0)
			{
				fov += dt;
			}
			if (GetAsyncKeyState('D')<0)
			{
				fov -= dt;
			}
			if (GetAsyncKeyState('X')<0)
			{
				m44 m;
				m.RotateZ(dt);
				mt=m*mt;
			}
			if (GetAsyncKeyState('C')<0)
			{			
				m44 m;
				m.RotateZ(-dt);
				mt=m*mt;
			}
			bool ctrl = GetAsyncKeyState(VK_CONTROL)<0;
			if (GetAsyncKeyState(VK_LEFT)<0)
			{
				if (ctrl)
				{
					p-=mt.GetX() * dt * MOVESPEED;
				}
				else
				{
				
					m44 m;
					m.RotateY(-dt);
					mt=m*mt;
				}
			}
			if (GetAsyncKeyState(VK_RIGHT)<0)
			{
				if (ctrl)
				{
					p+=mt.GetX() * dt * MOVESPEED;
				}
				else
				{
					m44 m;
					m.RotateY(dt);
					mt=m*mt;
				}
			}
			if (GetAsyncKeyState(VK_UP)<0)
			{
				if (ctrl)
				{
					p+=mt.GetY() * dt * MOVESPEED;
				}
				else
				{
					m44 m;
					m.RotateX(dt);
					mt=m*mt;
				}
			}
			if (GetAsyncKeyState(VK_DOWN)<0)
			{
				if (ctrl)
				{
					p-=mt.GetY() * dt * 15;
				}
				else
				{
					m44 m;
					m.RotateX(-dt);
					mt=m*mt;
				}
			}		
			
		}
		mt.SetT(p);
		imt.Inverse(mt);
		fov=bound(PI/16.f,fov,PI-PI/16.f);
	}
};


v3 vinefoc(0,0,0);
v3 svinefoc(0,0,0);

struct superproc;
superproc *super;
struct scene
{
	thecam myfpscam;

	static scene *first;
	scene *next;

	static scene *findscene(int id)
	{
		for (scene *s=first;s;s=s->next)
		{
			if (s->camid==id) return s;
		}
		//if (id==0) return NULL;
		//return findscene(0);
		return NULL;
	}

	int camid;
	interplist list;
	float campos;

	interpstuff calcinterp(float tt)
	{

	}

	scene(int ci)
	{
		camid=ci;
		next=first;
		first=this;
		campos=0;
	}

	virtual ~scene()
	{
		if (first==this) first=next; else for (scene *s=first;s;s=s->next)
		{
			if (s->next==this) 
			{
				s->next=next;
				break;
			}
		}
	}

	virtual void run(superproc *main, float curtime, float dt, float endtime)
	{
		
	}
};


struct filmsettings
{
	int actress; // 0 or 1
	int profile;
	float x1,y1,x2,y2;
	float u1,v1,u2,v2;
	float scale;
	Surface surf;
	

	filmsettings()
	{
		actress=0;
		profile=0;
		x1=y1=0;
		x2=y2=1;
		surf.SetSize(256,256);

		Randomize(0.5f,0.5f);
	}

	
	~filmsettings()
	{

	}

	void UpdateTex(BSMFile *picdata[2], BSMFile *alphadata[2])
	{
	
		actress&=1;	
		int frame=thedoc->animframe[1-actress];
		picdata[actress]->ReadFrame(frame);
		alphadata[actress]->ReadFrame(frame);
		pixel *p = surf.pix;
		
#define CUT 40

		static int precalc=1;
		static u8 curves[256];
		if (precalc) for (int c1=0;c1<256;c1++) curves[c1]=bound(55,(c1-128)*2+60,200);
		precalc=0;

		for (int y=0;y<256;y++)
		{
			u8 *pici = picdata[actress]->framebuf + (y)*512*3;
			u8 *alphai = alphadata[actress]->framebuf + (y)*512;
			if (profile) 
			{
				pici+=256*3;
				alphai+=256*3;
			}
			for (int x=0;x<256;x++)
			{
				
				p->r = *pici++;
				p->g = *pici++;
				p->b = *pici++;
				p->a = *alphai++;				

				int grey=(p->r+p->g*2+p->b)/4;
				p->r+=(grey-p->r)*0.9f;
				p->g+=(grey-p->g)*0.85f;
				p->b+=(grey-p->b)*0.85f;

				p->r=curves[p->r];
				p->g=curves[p->g];
				p->b=curves[p->b];

				p++;
			}
			pici+=256*3;
			alphai+=256;									
		}

		surf.UpdateToTexture();		
		
	}

	void Randomize(float balance, float profbalance)
	{
		float hill;
		hill=bound(0,balance,1);
		hill = 4*(hill-hill*hill);
		if (Random(0,1)<hill) actress=rand()&1; else if (balance<0.5) actress=0; else actress=1;

		hill=bound(0,profbalance,1);
		hill = 4*(hill-hill*hill);
		if (Random(0,1)<hill) profile=rand()&1; else if (profbalance<0.5) profile=0; else profile=1;

		if (Random(0,1)<0.6f) profile=0;

		float xx,yy;
		do
		{
		
			scale = Random(1.5,8);
			float xscale=scale;
			if (xscale>4) xscale=4;
			// when scale is 1, the u can range from 0 to 1
			// when scale is 4, the u can range from -1 to 2		
			xx=(Random(-0.3f,0.3f) * (xscale)) + 0.5f;
			yy=(GaussRandom(-0.3f,0.3f) * (xscale-1)) + 0.5f;		
			x1=xx-scale*3/8;
			x2=xx+scale*3/8;
			y1=yy-scale/2;
			y2=yy+scale/2;
			if (profile && xx<0.5f)
			{
				float t=x1;x1=x2;x2=t;
			}
		} while (0); // profile && (xx>=0.4f && xx<=0.6f));
				
		// u = a x + b
		// 0 = a x1 + b
		// 1 = a x2 + b
		// 1 = a (x2-x1) -> a = 1/(x2-x1)
		// b = -a * x1

		float au=1/(x2-x1);
		float av=1/(y2-y1);
		float bu = -au * x1;
		float bv = -av * y1;

		u1 = bu;
		u2 = au + bu;
		v1 = bv;
		v2 = av + bv;
		
	}
};


struct superproc : public RenderThing
{	
	thefloor myfloor;
	
	Surface credits;
	MeObj sun;
	MeObj sky;
	MeObj sky2;
	MeObj body[2];
	MeObj hands[2];
	MeObj hair[2];
	MeObj head[2];
	D3DTexture *splodge,*vinetex,*circle,*ring;
	D3DTexture *colours[256];
	D3DTexture *title[4];
	int numcolours;
	Surface sunsurf;
	Surface shadsurf;
	SimpleBuffer2D buf2d;
	SimpleBuffer buf;
	SimpleBufferUV2 bufuv2;
	SimpleBuffer linebuf;
	float treeamount;
	float vineamount;
	int needshadow;
	float totallag;
	int fullmode;	
	m44 bodym[2];
	int rendermode;
	float sunstrength;

	Surface tempsurf;

	filmsettings film[2];
	BSMFile *picdata[2];
	BSMFile *alphadata[2];
	
	

	void SetVineAmount( float f)
	{
		//if (CUTOFF!=0 && thedoc->GetCurTime()>1) f=1;
		if (f!=vineamount) needshadow|=1;
		vineamount=bound(0,f,totallag+1);
	}

	void SetTreeAmount( float f)
	{
		//if (CUTOFF!=0 && thedoc->GetCurTime()>1) f=1;
		static int treegrow=1;
		if (f<0)
		{
			if (treegrow)
			{
				treegrow=0;
				thedoc->pulsetrigger |= 8; // trigger sample!
			}
			f=0;
		}
		else treegrow=1;
		if (f!=treeamount) needshadow|=1;
		treeamount=bound(0,f,1);

	}

	int drawhuman;
	float titlefade,titlepos,facelevel,facebal,facealpha,facebend,facepulse,titlewipe,titlenoise;

	void SetFlipMat(bool f)
	{
		m44 m;
		m.Identity();
		if (f) m.m44[1][1]=-1;
		SetWorldMat(m);
	}

	/////////////////////////////////////////////////////////////////////////////// INIT
	superproc()
	{
		super=this;
		oscenes[0]=oscenes[1]=NULL;
		scenes[0]=scenes[1]=NULL;
		sunstrength=1;
		totallag = 10.f;
		needshadow=2;
		fullmode=0;
		rendermode=0;

		curcol=numcolours=0;

		SetVineAmount(0);

		splodge=LoadTexture("splodge.png");
		vinetex=LoadTexture("vine.png");

		char odir[256];
		_getcwd(odir,255);
		_chdir("colours");
		WIN32_FIND_DATA fd;
		HANDLE dllfind = FindFirstFile("*.*",&fd);
		int c1=0;
		if (dllfind!=INVALID_HANDLE_VALUE)
		{
			do
			{
				char buf[512],*dot;
				strcpy(buf,fd.cFileName);
				if ((dot=strstr(buf,".")) && (fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)==0)  
				{
					if (stricmp(dot,".dds")==0 || stricmp(dot,".tga")==0 || stricmp(dot,".png")==0 || stricmp(dot,".bmp")==0 || stricmp(dot,".jpg")==0 || stricmp(dot,".jpeg")==0)
					{
						//tex.insert(texlist::value_type(buf,NULL));								
						Report3D("coltex  %d %s\n",numcolours, buf);
						if (strstr(buf,"Trans64"))
						{
							curcol=numcolours;
						}
						colours[numcolours]=LoadTexture(buf);
						numcolours++;
					}
				}
			} while (FindNextFile(dllfind,&fd));
			FindClose(dllfind);
		}
		chdir(odir);

		D3DXCreateTextureFromFile(d3ddev,"circle.png",&circle);
		D3DXCreateTextureFromFile(d3ddev,"ring.png",&ring);

		float size = 0.025f;
		bodym[0].Scale(size,size,size);
		bodym[0].MultRotateY(-0.9f-PI/80.f);
		bodym[0].MultTranslate(13,0,0);
		m44 roty;
		roty.RotateY(PI/80.f);
		bodym[0]=bodym[0]*roty;

		bodym[1].Scale(size,size,size);
		bodym[1].MultTranslate(v3(200,0,0));

		treeamount=0;
		for (int s=0;s<4;s++) 
			backsurf[s].SetSize(XRES,YRES);

		tempsurf.SetSize(XRES,YRES);

		title[0]=LoadTexture("title1.png");
		title[1]=LoadTexture("title2.png");
		title[2]=LoadTexture("title1b.png");
		title[3]=LoadTexture("title2b.png");

		SuperFile sf;
		sf.Open("flowercentre.me",false);
		flowercentre.Load(sf,0.1f,true);
		sf.Close();
		sf.Open("petal.me",false);
		petal.Load(sf,0.1f,true,0,128);
		sf.Close();
		sf.Open("sun.me",false);
		sun.Load(sf,1.f,true);
		sf.Close();
		sf.Open("sky.me",false);
		sky.Load(sf,1.f,true);
		sf.Close();
		sf.Open("sky2.me",false);
		sky2.Load(sf,1.f,true);
		sf.Close();
		
		sf.Open("head.me",false);
		head[0].Load(sf,1.f,true);
		sf.Close();
		sf.Open("body.me",false);
		body[0].Load(sf,1.f,true,1);
		sf.Close();
		sf.Open("hands.me",false);
		hands[0].Load(sf,1.f,true);
		sf.Close();
		sf.Open("hair.me",false);
		hair[0].Load(sf,1.f,true,1);
		sf.Close();
		
		sf.Open("sithead.me",false);
		head[1].Load(sf,1.f,true);
		sf.Close();
		sf.Open("sitbody.me",false);
		body[1].Load(sf,1.f,true,1);
		sf.Close();
		sf.Open("sithands.me",false);
		hands[1].Load(sf,1.f,true);
		sf.Close();
		sf.Open("sithair.me",false);
		hair[1].Load(sf,1.f,true,1);
		sf.Close();

		picdata[0]=NULL;
		picdata[1]=NULL;
#ifdef AUDIOVIDEO
		picdata[0]=new BSMFile(0);
		alphadata[0]=new BSMFile(1);
		picdata[0]->Open("bluescreen2.mad");
		alphadata[0]->Open("bluescreen2.mad.alpha");			

		picdata[1]=new BSMFile(0);
		alphadata[1]=new BSMFile(1);
		picdata[1]->Open("bluescreen.mad");
		alphadata[1]->Open("bluescreen.mad.alpha");			
#endif

		pulseflash=0;

		leaftex[0]=LoadTexture("petal3.png");
		leaftex[1]=LoadTexture("petal2.png");
		leaftex[2]=LoadTexture("petal1.png");

		//srand(272); // because.
		extern int treeseed;

		seeddlg d;
		d.m_seed = treeseed;
		d.DoModal();
		treeseed = d.m_seed;

		myfloor.mytree.gen(treeseed);

		sunsurf.SetSize(512/2,512/2);
		shadsurf.SetSize(512,512);

		SetTreeAmount(0);

		SetupScenes();

		initstarspart();

		credits.LoadFromDisk("credits.raw");
		
	}

	void SetupScenes();

	/////////////////////////////////////////////////////////////////////////////////////////////////////// VINE DRAWING
	void DrawVines(bool shadow, int  blackcol = 0xff400000, SimpleBuffer2D *toshadow=NULL)
	{

		

		d3ddev->SetTexture(0,!shadow ? vinetex : NULL);
		for (int vine=0;vine<myfloor.mytree.numvines-1;vine++)
		{
			float lag = sqrtf(sqrtf(vine/float(myfloor.mytree.numvines)))*totallag;
			if (vineamount>lag)			
			{
				//myfloor.mytree.vinecol[thevine]=0xff00ff00;
				
				v3 voo = DrawVine(buf,myfloor.
				mytree.vinepath[vine],vineamount-lag,
				
				myfloor.mytree.vinesize[vine],shadow  ? blackcol: myfloor.mytree.vinecol[vine],shadow ? 0xff000000 : 0xff808080, toshadow);
				
				if (vine==thevine) vinefoc=voo;
			}
				
			else break;
		}
		d3ddev->SetTexture(0,0);
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////// MAIN SCENE RENDER 
	void RenderMainScene(float time, float dt)
	{
		int hidx = (drawhuman-1) & 1;

		if (!realtime) fullmode=1;

	
		if ((needshadow && fullmode) || needshadow>1)
		{
			needshadow=0;
		
			// draw tree shadow
			shadmin=v3(1e20,1e20,1e20);
			shadmax=-shadmin;
			for (int c1=0;c1<myfloor.mytree.mainlist.size();c1++)
			{
				polygon &p = myfloor.mytree.mainlist[c1];
				for (int i=0;i<p.p.size()*treeamount;i++)
				{
					v3 pp = p.p[i];
					ShadowProject(pp,false);
				}
			}

			
			if (drawhuman)
			{
			
				body[hidx].DrawToShadow(bodym[hidx], buf2d, false);
				hands[hidx].DrawToShadow(bodym[hidx], buf2d, false);
				head[hidx].DrawToShadow(bodym[hidx], buf2d, false);
				hair[hidx].DrawToShadow(bodym[hidx], buf2d, false);
			}

			if (shadmax.x<shadmin.x)
			{
				shadmin=v3(0,0,0);
				shadmax=v3(1,0,1);
			}

			v3 midp=(shadmin+shadmax)/2;
			shadmin=midp + (shadmin-midp)*1.1f;
			shadmax=midp + (shadmax-midp)*1.1f;
			Report3D("shadow from %0.2f,%0.2f to %0.2f,%0.2f\n",shadmin.x,shadmin.z,shadmax.x,shadmax.z);

			{
				//Surface &shadsurf	 = *backbuf;
				shadsurf.BeginFrame(true,true,0xffffffff);
				myfloor.mytree.drawtree(treeamount,0,0xc0,true);

				buf2d.Draw();
				DrawVines(true, 0xff000000, &buf2d);
				buf2d.Reset();

				if (drawhuman)
				{
				
					body[hidx].DrawToShadow(bodym[hidx], buf2d, true);
					hands[hidx].DrawToShadow(bodym[hidx], buf2d, true);
					head[hidx].DrawToShadow(bodym[hidx], buf2d, true);
					hair[hidx].DrawToShadow(bodym[hidx], buf2d, true);
				}

				{ // fade out shadow
				
					d3ddev->SetTexture(0,0);
					int suncol=0x78ffffff;
					buf2d.AddQuad(
						buf2d.AddVertex(v3(0,0,0.5f),suncol,0,0),
						buf2d.AddVertex(v3(1,0,0.5f),suncol,1,0),
						buf2d.AddVertex(v3(1,1,0.5f),suncol,1,1),
						buf2d.AddVertex(v3(0,1,0.5f),suncol,0,1)
						);
					buf2d.Draw();
				}

				shadsurf.EndFrame(true);
				shadsurf.BoxBlur(shadsurf.pix,4,4);
				shadsurf.BoxBlur(shadsurf.pix,4,4);
				shadsurf.UpdateToTexture();
				d3ddev->SetTexture(2,shadsurf.tex);
				d3ddev->SetTextureStageState(2,D3DTSS_ADDRESSU,D3DTADDRESS_CLAMP);
				d3ddev->SetTextureStageState(2,D3DTSS_ADDRESSV,D3DTADDRESS_CLAMP);
			}
		}
		
		if (0)
		{
			
			// test 
			backbuf->BeginFrame(true,true,0xff00);
					
		//	myfloor.Draw(0xc0000000);
		//	myfloor.mytree.drawtree(1,0.2f,0xc0,0);
			

			
			d3ddev->SetTexture(0,shadsurf.tex);
			//d3ddev->SetTexture(0,leaftex[0]);
			int suncol=0xffffffff;
			buf2d.AddQuad(
				buf2d.AddVertex(v3(0,0,0.5f),suncol,0,0),
				buf2d.AddVertex(v3(1,0,0.5f),suncol,1,0),
				buf2d.AddVertex(v3(1,1,0.5f),suncol,1,1),
				buf2d.AddVertex(v3(0,1,0.5f),suncol,0,1)
				);
			buf2d.Draw();
			
			
			backbuf->EndFrame(false);
		}				
				
		//if (0)
		if (!fullmode)
		{
			// fast version
			backbuf->BeginFrame(true,true,0);
			

			m44 m;
			m.Identity();

			m.RotateX(PI);
			d3ddev->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_CLAMP);
			sky.Draw(m);
			d3ddev->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_WRAP);

			
			
			myfloor.Draw(0xc0000000);		

			//for (int c1=0;c1<lillies.size();c1++) lillies[c1].Draw(dt);

			myfloor.mytree.drawtree(treeamount,0.2f,0xc0,0);

			
			DrawGroundPlane(buf);
			
			
			
			DrawVines(false);

			if (rainfall>0)
			{
				drawstarspart(time,dt,false,true,rainfall);
			}

			if (drawhuman)
			{
				body[hidx].Draw(bodym[hidx]);
				hair[hidx].Draw(bodym[hidx]);
				hands[hidx].Draw(bodym[hidx]);
				head[hidx].Draw(bodym[hidx]);
			}			
/*
			
			d3ddev->SetTexture(0,splodge);
			d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,false);
			body.DrawBlobs(bodym,buf,1.0f, 0xff000000);
			hair.DrawBlobs(bodym,buf,0.4f, 0xff000000);
			d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,true);
			

			
			//d3ddev->SetRenderState(D3DRS_COLORWRITEENABLE,false);			
			d3ddev->SetTexture(0,NULL);			
			
			d3ddev->SetRenderState(D3DRS_COLORWRITEENABLE,D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED );			

			
			
*/			
			
			

			d3ddev->SetTexture(0,NULL);

			//body.Draw(bodym);
			//hair.Draw(bodym);
			//hands.Draw(bodym);
			//head.Draw(bodym);

			
			camedit->path().Draw(linebuf);			


			backbuf->EndFrame(true);
			
		}
		else
		{
			// full version
		
			if (curframeidx<CUTOFF)
			{
				if (rainfall>0)
				{
					drawstarspart(time,dt,false,true,rainfall);
				}
				
				if (rainfall>0)
				{
					drawstarspart(time,dt,false,true,rainfall);
				}
				
			}
			else
			{
			
				// draw floor
				backbuf->BeginFrame(true,true,0);

				m44 m;
				m.Identity();

				m.RotateX(PI);
				d3ddev->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_CLAMP);
				sky.Draw(m);
				d3ddev->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_WRAP);

				//for (int c1=0;c1<lillies.size();c1++) lillies[c1].Draw(dt,true);

				SetFlipMat(true);

				
				int red=148+30*sin(thedoc->GetCurTime()*0.7f);
				DrawVines(true,0xff000000+(red<<16));
				
				SetFlipMat(false);

				if (drawhuman)
				{
				
					m44 oldbm = bodym[hidx];
					bodym[hidx].MultScale(1,-1,1);
					
					body[hidx].Draw(bodym[hidx]);
					hair[hidx].Draw(bodym[hidx]);
					hands[hidx].Draw(bodym[hidx]);
					head[hidx].Draw(bodym[hidx]);
					bodym[hidx]=oldbm;
				}

				SetFlipMat(true);

				myfloor.mytree.drawtree(treeamount,0.2f,0xc0,0);

				SetFlipMat(true);
				
				if (rainfall>0)
				{
					drawstarspart(time,dt,false,true,rainfall);
				}

				SetFlipMat(false);
				
				myfloor.Draw(0xc0000000);		

				
				
				if (drawhuman)
				{

					body[hidx].Draw(bodym[hidx]);
					hair[hidx].Draw(bodym[hidx]);
					
				}
				



				backbuf->EndFrame(true);
				
				//backbuf->Blt(true,OPER_SUB,Random(200,255),&title,RES(200),RES(100));

				// blur floor twice
				backbuf->BoxBlur(backsurf[0].pix,RES(25),RES(25));
				backbuf->BoxBlur(backsurf[1].pix,RES(8),RES(9));		
				backbuf->BoxHBlur(backsurf[0].pix,backsurf[2].pix,RES(100+Random(-25,25)));
				backbuf->Blt(false,OPER_MIN,170,&backsurf[0]);
				backbuf->Blt(false,OPER_OVERLAY,200,&backsurf[1]);
				backbuf->Blt(false,OPER_OVERLAY,200,&backsurf[2]);
				backbuf->BrightnessContrast(-20.9f,1.2f);

				//memcpy(backbuf->pix,backsurf[2].pix,backbuf->w*backbuf->h*4);
				
				

				// draw sun stuff
				sunsurf.BeginFrame(true,true,0);

				//m44 m;
				m.Identity();
				m.RotateX(PI);
				d3ddev->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_CLAMP);
				sun.Draw(m);
				d3ddev->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_WRAP);
				
				
				if (drawhuman)
				{
				
				
					body[hidx].Draw(bodym[hidx]);
					hair[hidx].Draw(bodym[hidx]);
					hands[hidx].Draw(bodym[hidx]);
					head[hidx].Draw(bodym[hidx]);
				}

				DrawGroundPlane(buf);
				
				DrawVines(true,0xff000000+(red<<16));

				myfloor.mytree.drawtree(treeamount,0,0x7f,0);

				sunsurf.EndFrame(true);
				sunsurf.BoxBlur(sunsurf.pix,10,10);
				sunsurf.BoxBlur(sunsurf.pix,10,10);
				sunsurf.BrightnessContrast(64,1.5f);
				sunsurf.UpdateToTexture();

				// draw tree and sun glow
				backbuf->UpdateToRendertarget();

				
				POINT pp;
				GetCursorPos(&pp);		
				backbuf->BeginFrame(true,false,0);
				
				float zam=pp.x/1600.f;
				//DrawHLines(dt*(zam*10+1),zam);


				
				

				DrawGroundPlane(buf);
				DrawVines(false);

				

				//for (c1=0;c1<lillies.size();c1++) lillies[c1].Draw(dt,false);

				if (drawhuman)
				{
				
					d3ddev->SetTexture(0,splodge);
					d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,false);
					float BLOBSIZE = 0.5f;
					if (hidx) BLOBSIZE *= 0.7f;
					body[hidx].DrawBlobs(bodym[hidx],bufuv2,BLOBSIZE, 0xff000000);
					hair[hidx].DrawBlobs(bodym[hidx],bufuv2,BLOBSIZE, 0xff000000);
					hands[hidx].DrawBlobs(bodym[hidx],bufuv2,BLOBSIZE, 0xff000000);
					head[hidx].DrawBlobs(bodym[hidx],bufuv2,BLOBSIZE, 0xff000000);
					d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,true);

					body[hidx].Draw(bodym[hidx]);
					if (hidx) hair[hidx].Draw(bodym[hidx]);


					hands[hidx].Draw(bodym[hidx]);
					head[hidx].Draw(bodym[hidx]);

					/*
					d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,false);
					d3ddev->SetTextureStageState(1,D3DTSS_COLORARG1,D3DTA_CURRENT);
					d3ddev->SetTextureStageState(1,D3DTSS_COLORARG2,D3DTA_TEXTURE);
					d3ddev->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_SELECTARG2);
					d3ddev->SetTextureStageState(1,D3DTSS_ALPHAARG1,D3DTA_CURRENT);		
					d3ddev->SetTextureStageState(1,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);
					d3ddev->SetTexture(0,splodge);
					head.DrawBlobs(bodym,bufuv2,0.8f, 0xffffffff);
					hands.DrawBlobs(bodym,bufuv2,0.5f, 0xffffffff);
					d3ddev->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_DISABLE);
					d3ddev->SetTextureStageState(1,D3DTSS_ALPHAOP,D3DTOP_DISABLE);
					
					d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,true);
							*/
				}		

				d3ddev->SetTexture(0,0);
				myfloor.mytree.drawtree(treeamount,0.2f,0xc0,0);

				if (rainfall>0)
				{
					drawstarspart(time,dt,false,true,rainfall);
				}
				

				d3ddev->SetTexture(0,sunsurf.tex);
				//d3ddev->SetTexture(0,leaftex[0]);
				d3ddev->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ONE);
				d3ddev->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ONE);
				d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
				

				int suncol=int(bound(0,sunstrength,1)*255);
				suncol=0xff000000|(suncol<<16)|(suncol<<8)|suncol;
				float l=1.f / 256.f;
				float h=1.f - l;
				buf2d.AddQuad(
					buf2d.AddVertex(v3(0,0,0.5f),suncol,l,l),
					buf2d.AddVertex(v3(1,0,0.5f),suncol,h,l),
					buf2d.AddVertex(v3(1,1,0.5f),suncol,h,h),
					buf2d.AddVertex(v3(0,1,0.5f),suncol,l,h)
					);
				buf2d.Draw();
				d3ddev->SetTexture(0,0);
				d3ddev->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
				d3ddev->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
				d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,true);


				backbuf->EndFrame(true);
				
			}

			
			svinefoc += (vinefoc-svinefoc) * (1-powf(0.04f,dt));
			if (time<78.4f) svinefoc=vinefoc;

			if (scenes[0] && oscenes[0] && scenes[0]->camid==14 && oscenes[0]->camid!=14)
			{
				svinefoc=vinefoc;
			}
			if (scenes[1] && oscenes[1] && scenes[1]->camid==14 && oscenes[1]->camid!=14)
			{
				svinefoc=vinefoc;
			}
		}
		
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////// star thingamy


#define NUMSTARS 32
	blobstar stars[NUMSTARS];
	int newcols;
	int curcol;
	psysBox psys;
	float dtleft;
	Surface minibuf;

	

	struct wiper
	{
		float wipepos;
		float chances;

		wiper()
		{
			wipepos=0;
			chances=0;
		}

		u8 buf[1024];
		u8 buf2[1024];

		struct stripe
		{
			float x;
			float wid;
			float speed;
			u8 col;
			u8 alpha;
		};

		typedef std::vector<stripe> stripelist;

		stripelist stripes;

		bool allblack()
		{
			return stripes.empty() && wipepos==0;
		}

		bool allwhite()
		{
			return stripes.empty() && wipepos==1;
		}

		void HBlur(int k)
		{
			int w=1024;
			if (k<=1) return;
			int rk = 65536/k;
			u8 *src = buf;
			u8 *dst = buf2;
			int k2=(k+1)/2;
			if (k2>w/2) k2=w/2;
			int k3=k-k2;	
			if (k3>w/2) k3=w/2;
			int tot=0;
			// for the first k2 pixels, we just accumulate on the right
			for (int x=-k3;x<0;x++) tot+=(src[k3+x]);
			for (;x<k2;x++)
			{					
				(*dst) = (tot*(65536/(x+k3))>>16);
				tot+=(src[k3]);					
				src++;dst++;
			}
			for (;x<w-k3;x++)
			{										
				(*dst) = (tot*rk>>16);
				tot+=(src[k3])-(src[-k2]);
				src++;dst++;
			}
			// for the final k3 pixels, we just decrement
			int k4=k+1;
			for (;x<w;x++)
			{										
				(*dst) = (tot*(65536/(--k4))>>16);
				tot-=(src[-k2]);
				src++;dst++;
			}			
		}


		void Update(float dt, float level, u8 *output, int xres)
		{
			if (level<0)
			{
				if (wipepos>0)
				{
					wipepos-=dt;
					if (wipepos<0) wipepos=0;
				}				
			}
			else
			{
				if (wipepos<1)
				{
					wipepos+=dt;
					if (wipepos>1) wipepos=1;
				}
			}
			// clear buf
			int iw = 1024-1024*wipepos;
			for (int c1=0;c1<iw;c1++)
			{
				buf[c1]=0;
			}
			for (;c1<1024;c1++)
			{
				buf[c1]=255;
			}

			// write and move stripes
			for (c1=0;c1<stripes.size();)
			{
				int x1=int(1024*stripes[c1].x);
				int x2=int(1024*(stripes[c1].x+stripes[c1].wid));
				x1=bound(0,x1,1024);
				x2=bound(0,x2,1024);
				int cc=stripes[c1].col;
				int aa=stripes[c1].alpha;
				for (int x=x1;x<x2;x++)
				{
					buf[x]=(buf[x]*256+(cc-buf[x])*aa)>>8;
				}
				stripes[c1].x-=stripes[c1].speed * dt / 2;
				if (stripes[c1].x<=-stripes[c1].wid)
				{
					stripes.erase(stripes.begin()+c1);
				}
				else c1++;
			}
			// create new stripes
			float hill = fabsf(level);
			hill = 4*(hill-hill*hill);

			chances-=dt;
			while (chances<0)
			{
				if (Random(0,1)<hill && hill!=0)
				{
					// ok lets do it!
					stripe p;
					p.alpha=Random(128,255);
					float greyprob = 1-level*level;
					if (Random(0,1)<greyprob) p.col=GaussRandom(0,255); else if (level<0) p.col=255; else p.col=0;
					p.x=1;
					p.wid=pow(3,Random(-1,1)) * 0.15f;
					if (Random(0,1)<0.25f) p.wid/=3;
					p.speed = 1/sqrtf(p.wid) * pow(2,Random(-1,1));;
					p.speed=sqrtf(p.speed);
					//if (p.speed>3) p.speed=3;

					stripes.push_back(p);
				}
				chances+=Random(0,0.5f);
			}


			// blur buf
			HBlur(25);
			

			for (c1=0;c1<xres;c1++)
			{
				*output++ = buf2[c1*1024/xres];
			}
		}

		static __inline u8 sat(int a)
		{
			if (a&0xffffff00)
			{
				return ~(a>>31);
			}
			else return a;
		}

		static void blendify(u8 *mix, pixel *dst, pixel*black, pixel*grey, pixel*white, bool subtractive)
		{
			// grey = 4*(x-x*x)
			static precalc=1;
			static u8 btab[256];
			static u8 gtab[256];
			static u8 wtab[256];
			if (precalc)
			{
				precalc=0;
				for (int c1=0;c1<256;c1++)
				{
					float x=(c1/256.f);
					u8 g = 4*255*(x-x*x);
					gtab[c1]=g;
					if (c1<128)
					{
						wtab[c1]=0;
						btab[c1]=255-g;
					}
					else
					{
						btab[c1]=0;
						wtab[c1]=255-g;
					}						
				}
			}
			for (int y=0;y<YRES;y++)
			{
				if (subtractive)
				{
					
					for (int x=0;x<XRES;x++)
					{
						int m=mix[x];
						int g=gtab[m];
						int w=wtab[m];							
						dst->r = sat(black->r - ((g*grey->r + w*white->r) >> 8));
						dst->g = sat(black->g - ((g*grey->g + w*white->g) >> 8));
						dst->b = sat(black->b - ((g*grey->b + w*white->b) >> 8));

						dst++;
						black++;
						white++;
						grey++;
					}					
				}
				else
				{
				
					for (int x=0;x<XRES;x++)
					{
						int m=mix[x];
						int g=gtab[m];
						int w=wtab[m];
						int b=btab[m];
						dst->r = (g*grey->r + w*white->r + b*black->r) >> 8;
						dst->g = (g*grey->g + w*white->g + b*black->g) >> 8;
						dst->b = (g*grey->b + w*white->b + b*black->b) >> 8;

						dst++;
						black++;
						white++;
						grey++;
					}
				}
			}

			
		}

	};


	wiper starwiper;

	void initstarspart()
	{
		dtleft=0.01;
		newcols=0;
		
	}

	void drawstarspart(float time, float dt, bool clear, bool juststars, float rainfall)
	{
		if (newcols || (GetAsyncKeyState(VK_F6)&1))
		{
			newcols=0;
			curcol=rand()%numcolours;
			Report3D("cur col %d\n",curcol);
		}

		//if (GetAsyncKeyState(VK_SHIFT)<0) clear=true;

		if (curframeidx<CUTOFF)
		{
			int mega=0;
			for (int c1=0;c1<NUMSTARS;c1++)
			{
				//stars[c1].Draw(buf,centroid,-20*2,60*2,255*super->starfade);
			
				if (mega==0) stars[c1].Update(dt*0.021f);
			}
			dtleft+=dt*0.15*0.90f;
			if (dtleft>1) dtleft=1;
			if (dtleft<0) dtleft=0;
			if (realtime) if (dtleft>0.05) dtleft=0.05f;
			
			if (!(juststars && super->scenes[0] && super->scenes[0]->camid>=20))
			{
			
				while (dtleft>=0.05f)
				{
					psys.Update(0.05f,stars,NUMSTARS,rainfall);
					dtleft-=0.05f;
				}		
				if (dtleft>0) psys.Update(dtleft,stars,NUMSTARS,rainfall);
				dtleft=0;
			}

			// gray!!!

			if (!juststars)
			{
			
				static u8 grey[XRES];			
				starwiper.Update(dt,super->starwipe,grey,XRES);

				//starwiper.blendify(grey,backbuf[0].pix,backsurf[0].pix,backsurf[1].pix,backbuf[0].pix,false);
			}

			return ;
		}

		for (int mega=0;mega<2;mega++)
		{
			if (juststars==false)
			{
			
				if (mega==0)
				{
					// draw blurred version
					backsurf[0].BeginFrame(clear,clear,0);
			
				}
				else
				{
					backbuf->BeginFrame(clear,clear,0);
				}
			}		
		

			m44 m;
			m.Identity();

			m.RotateX(PI);
			d3ddev->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_CLAMP);
			if (!juststars) sky2.Draw(m);
			d3ddev->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_WRAP);
			
			float centroid = super->centroid;//0.95f;

			//p.x=1600;
			for (int c1=0;c1<NUMSTARS;c1++)
			{
				stars[c1].Draw(buf,centroid,-20*2,60*2,255*super->starfade);
			
				if (mega==0) stars[c1].Update(dt*0.021f);
			}
			

			

			if (mega==1 || juststars)
			{
			
			

				
				d3ddev->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
				d3ddev->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ONE);
				d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
				d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,false);

				d3ddev->SetTexture(0,ring);
				d3ddev->SetTexture(1,colours[curcol]);

				d3ddev->SetTextureStageState(1,D3DTSS_COLORARG1,D3DTA_CURRENT);
				d3ddev->SetTextureStageState(1,D3DTSS_COLORARG2,D3DTA_TEXTURE);
				d3ddev->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_SELECTARG2);
				d3ddev->SetTextureStageState(1,D3DTSS_ALPHAARG1,D3DTA_CURRENT);		
				d3ddev->SetTextureStageState(1,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);
				
				dtleft+=dt*0.15*0.90f;
				if (dtleft>1) dtleft=1;
				if (dtleft<0) dtleft=0;
				if (realtime) if (dtleft>0.05) dtleft=0.05f;

				if (juststars) d3ddev->SetRenderState(D3DRS_BLENDOP,D3DBLENDOP_REVSUBTRACT);

				if (!(juststars && super->scenes[0] && super->scenes[0]->camid>=20))
				{
				
					while (dtleft>=0.05f)
					{
						psys.Update(0.05f,stars,NUMSTARS,rainfall);
						dtleft-=0.05f;
					}		
					if (dtleft>0) psys.Update(dtleft,stars,NUMSTARS,rainfall);
					dtleft=0;
				}
				
				psys.Run(bufuv2, juststars ? 0x85ffffff : 0x80ffffff, juststars ? 0.016f * SCALEFAC : 0.02f * SCALEFAC,rainfall);

				d3ddev->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_DISABLE);
				d3ddev->SetTextureStageState(1,D3DTSS_ALPHAOP,D3DTOP_DISABLE);
				
				if (juststars) d3ddev->SetRenderState(D3DRS_BLENDOP,D3DBLENDOP_ADD);

				d3ddev->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
				d3ddev->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
				d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
				d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,true);
				d3ddev->SetTexture(0,NULL);
				d3ddev->SetTexture(1,NULL);
			}				

			d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,false);
			if (!juststars) buf.Draw();
			buf.Reset();
			d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,true);


			if (clear)
			{
			
				if (mega==0&& juststars==false)
				{
				
					backsurf[0].EndFrame(true);
					backsurf[0].BoxBlur(backsurf[1].pix,RES(20),RES(20));
				}
				else
				{
					backbuf->EndFrame(true);
				}
			}

			if (juststars) break;
		} // mega loop			

		// gray!!!

		if (!juststars)
		{
		
			static u8 grey[XRES];			
			starwiper.Update(dt,super->starwipe,grey,XRES);

			starwiper.blendify(grey,backbuf[0].pix,backsurf[0].pix,backsurf[1].pix,backbuf[0].pix,false);
		}


	}

	void drawpostprocess(Surface *source, float dt)
	{
		static u8 grey[XRES];			
		mywiper.Update(dt,titlewipe,grey,XRES);


		if ((thedoc->pulsetrigger & 2) || (GetAsyncKeyState(VK_F7)&1))
		{
			thedoc->pulsetrigger&=~2;
			pulseflash=1;
			film[0].Randomize(facebal,0.5f);
			film[1].Randomize(facebal,0.5f);
		}
		else
		{
			pulseflash-=dt*2;
			if (pulseflash<0) pulseflash=0;
		}
		

		float overallalpha = facealpha + pulseflash * 1.2f;
		if (overallalpha<=0 && titlefade <=0) return;

		Surface *backbuf = &backsurf[3];
		
		static pinmap pin[2];
		if (overallalpha>1) overallalpha=1; 

		if (curframeidx>=CUTOFF)
		{
		
			backbuf->BeginFrame(true,true,0xff808080);
			//backbuf->BeginFrame(true,true,0xffffffff);
			if (overallalpha>0)
			{
			
				
				float visbalance=facebend;
				for (int c1=0;c1<2;c1++)
				{
					visbalance=1-visbalance;
					int alpha=255*visbalance*overallalpha;
					if (alpha>255) alpha=255;
					ResetPinmap(pin[c1],((alpha/4)<<24)|(0xffffff),
						film[c1].u1,film[c1].v1,film[c1].u2,film[c1].v2);						
					
				}
				float t=thedoc->GetCurTime();
				BlendPinmapTimeDistort(pin[0],pin[1],t,visbalance,0.5f,0.5f);
				BlendPinmapTimeDistort(pin[1],pin[0],t,1-visbalance,0.5f,0.5f);					

				d3ddev->SetTextureStageState(0,D3DTSS_ADDRESSU,D3DTADDRESS_CLAMP);
				d3ddev->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_CLAMP);

				for (c1=0;c1<2;c1++)
				{
					visbalance=1-visbalance;
					int alpha=255*(visbalance);
					if (alpha>255) alpha=255;
					if (alpha<0) alpha=0;
					if (alpha>0)
					{
						if (picdata[0])
						{
							film[c1].UpdateTex(picdata,alphadata);
							d3ddev->SetTexture(0,film[c1].surf.tex);
							pinmap temp=pin[c1];
							for (int layer=7;layer>=0;layer--)
							{			
								pin[c1]=temp;
								AddNoiseToPinmap(pin[c1],0.021f*layer,0.021f*layer,0.051f,0.072f,0.032f,0.061f,thedoc->GetCurTime()*0.1f+layer*11.53);
								DrawPinMap(pin[c1]);
							}
							
							
							
							d3ddev->SetTexture(0,0);
						}
					}
					
				}

				d3ddev->SetTextureStageState(0,D3DTSS_ADDRESSU,D3DTADDRESS_WRAP);
				d3ddev->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_WRAP);
				
				
			}
			backbuf->EndFrame(true);
			
		}
		
		if (titlefade>0 && curframeidx>=CUTOFF)
		{					
			backsurf[0].BeginFrame(true,true,0);
			for (int fra=0;fra<2;fra++)
			{
				float xbase=0.7f - titlepos * (fra ? 1.4f : 0.9f);
				d3ddev->SetTexture(0,title[fra]);
				float amount = titlenoise + pulseflash * 0.8f;/// noise
				int col=0xffffff|(int(titlefade*255)<<24);
				for (int y=0;y<256;y++)
				{
					float x=GaussRandom()*1.2f*amount;
					if (x>0) x*=x; else x*=-x;
					if (Random(0,1.2f)>amount) x*=0.1f;
					x+=xbase;

					int v0=buf2d.AddVertex(v3(x,y/512.f+0.25f,0.5f),col,0,y/256.f);
					int v1=buf2d.AddVertex(v3(x+0.5f*3/4,y/512.f+0.25f,0.5f),col,1,y/256.f);
					y++;
					buf2d.AddVertex(v3(x,y/512.f+0.25f,0.5f),col,0,y/256.f);
					buf2d.AddVertex(v3(x+0.5f*3/4,y/512.f+0.25f,0.5f),col,1,y/256.f);
					y--;
					if (y<255)
					{
						buf2d.AddQuad(v0,v1,v1+2,v0+2);
					}
				}					
				buf2d.Draw();
				d3ddev->SetTexture(0,0);					
			}
		
			backsurf[0].EndFrame(true);
			backsurf[0].BoxVBlur(backsurf[0].pix,backsurf[0].pix2,RES(40));

			
			

			mywiper.blendify(grey,backbuf[0].pix,backbuf[0].pix,backsurf[0].pix2,backsurf[0].pix,true);

		}
		
		// modulate2x source and backbuf
		if (curframeidx>=CUTOFF)
		{
			source->Blt(false,OPER_MUL2X,255,backbuf);
		}
		
		
	}

	scene *scenes[2];
	float scenefade;

	float centroid,rainfall,starwipe,starfade;
	
	wiper mywiper;
	
	
	float pulseflash;
	scene *oscenes[2];

	///////////////////////////////////////////////////////////////////////////////////////////////////// MAIN FRAME LOOP
	int Do(float time, float dt, stereo *audio, CThingDoc *doc)
	{
		
		
		d3ddev->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
		d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);

		float rowtime = Time2Row(time);

		Data d;
		float scpre[2],scpost[2];		
		float pre,post;
		
		
		oscenes[0]=scenes[0];
		oscenes[1]=scenes[1];
		d = doc->channels[XFADE		].GetVal(rowtime, &pre		, &post );	scenefade=d.f;
		d = doc->channels[SCENEID0	].GetVal(rowtime, &scpre[0], &scpost[0]);	scenes[0] = scenefade>=1 ? NULL : scene::findscene(int(d.f));if (scenes[0]) scenes[0]->campos = d.f-floor(d.f);
		d = doc->channels[SCENEID1	].GetVal(rowtime, &scpre[1], &scpost[1]);	scenes[1] = scenefade<=0 ? NULL : scene::findscene(int(d.f));if (scenes[1]) scenes[1]->campos = d.f-floor(d.f);
		d = doc->channels[TREEAMOUNT].GetVal(rowtime, &pre		, &post );	SetTreeAmount(d.f);
		d = doc->channels[VINEAMOUNT].GetVal(rowtime, &pre		, &post );	SetVineAmount(d.f*(1+totallag));
		
		d = doc->channels[HUMAN		].GetVal(rowtime, &pre		, &post );	if (d.i!=drawhuman) needshadow|=1;drawhuman=d.i;
		d = doc->channels[TITLEFADE	].GetVal(rowtime, &pre		, &post );	titlefade=d.f;
		d = doc->channels[TITLEPOS	].GetVal(rowtime, &pre		, &post );	titlepos=d.f;
		d = doc->channels[TITLENOISE].GetVal(rowtime, &pre		, &post );	titlenoise=d.f;
		d = doc->channels[TITLEWIPE	].GetVal(rowtime, &pre		, &post );	titlewipe=d.f*2-1;
		d = doc->channels[FACELEVEL ].GetVal(rowtime, &pre		, &post );	facelevel=d.f;
		d = doc->channels[FACEBAL	].GetVal(rowtime, &pre		, &post );	facebal=d.f;
		d = doc->channels[FACEALPHA	].GetVal(rowtime, &pre		, &post );	facealpha=d.f * (bound(0,sin(time*0.2f),1)*0.5f+0.5f);
		d = doc->channels[FACEBEND	].GetVal(rowtime, &pre		, &post );	facebend=d.f;
		d = doc->channels[FACEPULSEPROB].GetVal(rowtime, &pre		, &post );	facepulse=d.f;
		d = doc->channels[SUNLEVEL  ].GetVal(rowtime, &pre		, &post );	sunstrength=d.f;

		d = doc->channels[CENTRO	].GetVal(rowtime, &pre		, &post );	centroid=d.f;
		d = doc->channels[RAINFALL	].GetVal(rowtime, &pre		, &post );	rainfall=d.f;

		d = doc->channels[STARFADE].GetVal(rowtime, &pre		, &post );	starfade=d.f + 0.05f * sin(time*0.5f);
		d = doc->channels[STARWIPE].GetVal(rowtime, &pre		, &post );	starwipe=d.f;

		float credalpha,newtrig;
		static float trigger=0;
		d = doc->channels[CREDALPHA].GetVal(rowtime, &pre		, &post );	credalpha=d.f;
		d = doc->channels[TRIGGER].GetVal(rowtime, &pre		, &post );	newtrig=d.f;

		d = doc->channels[OVERALLVOL].GetVal(rowtime, &pre		, &post );	thedoc->mvol=d.f;

		if (newtrig!=trigger)
		{
			if (newtrig==1) thedoc->pulsetrigger|=16;
			if (newtrig==2) thedoc->pulsetrigger|=32;
			if (newtrig==5) thedoc->nonotes=1;
			if (newtrig==6) thedoc->nonotes=0;
		}

		trigger=newtrig;

		static scene *errsc=NULL;
		if (scenes[0] && scenes[1]==scenes[0] && scenefade!=0 && scenefade!=1)
		{
			if  (scenes[0]!=errsc && realtime) MessageBox(NULL,"scenes are same!","arg",MB_OK);
			errsc=scenes[0];
		}
		else errsc=NULL;
		

		doc->talkielevel = facelevel * 1.3f;
		doc->pulseprob = facepulse;

		


		
		/*
		POINT p;
		GetCursorPos(&p);
		float theta = p.x*PI*2/1600.f;
		//SetCam(v3(20*sin(theta),(p.y-600)/5,20*cos(theta)),v3(0,5,0));		
		*/


		int drawn=0;
		Surface *output[2]={NULL,NULL};
		finalout=NULL;
		for (int sc=0;sc<2;sc++)
		{
			scenefade=1-scenefade;
			rendermode=-1;
			if (scenefade>0 && scenes[sc])
			{
				scenes[sc]->run(this,time-Row2Time(scpre[sc]),dt,Row2Time(scpost[sc])-Row2Time(scpre[sc]));
					
				
				if (rendermode==0)
				{
					RenderMainScene(time,dt);
					
					output[sc]=backbuf;
				}
				else if (rendermode==1)
				{				
					drawstarspart(time,dt,true,false,rainfall);
					output[sc]=backbuf;

					//backbuf->BeginFrame(true,true,0);
					//backbuf->EndFrame(false);
					//backbuf->ClearPix(0);
				}
				else if (rendermode==4)
				{
					
					
					

				}
				else if (rendermode==3)
				{
					
					backbuf->BeginFrame(true,true,0);
					int col=0xffffffff;
					static float time = 0;
					time+=0.01f * dt;
					
					
					
					output[sc]=backsurf;
				}
				else if (rendermode==2)
				{

					u8 grey[XRES];
					POINT p;
					GetCursorPos(&p);
		
					mywiper.Update(dt,p.x/800.f-1,grey,XRES);

					pixel *d=backbuf->pix;
					for (int y=0;y<YRES;y++)
					{
						for (int x=0;x<XRES;x++)
						{
							d->r=d->g=d->b=grey[x];
							d++;
						}
					}
					//backbuf->UpdateToRendertarget();
					output[sc]=backbuf;
				}

				if (curframeidx>=CUTOFF)
				{
				
					if (output[sc])
					{
						if (scenefade>=1) // no fading required
						{
							finalout=output[sc];
							break;
						}
						else
						{
							static u8 multab[256];
							for (int c1=0;c1<256;c1++) multab[c1]=bound(0,scenefade*c1,255);
							if (drawn)
							{
								// mix with other guy
								pixel *d=output[sc]->pix;
								pixel *s=output[1-sc]->pix;
								for (int c1=0;c1<XRES*YRES;c1++) 
								{
									d->r = multab[d->r] + s->r;
									d->g = multab[d->g] + s->g;
									d->b = multab[d->b] + s->b;
									d++;
									s++;
								}
								finalout=output[sc];
								break;
							}
							else
							{
								// just darken me down
								pixel *s=output[sc]->pix;
								pixel *d=tempsurf.pix;
								for (int c1=0;c1<XRES*YRES;c1++) 
								{
									d->r = multab[s->r];
									d->g = multab[s->g];
									d->b = multab[s->b];
									d++;
									s++;
								}
								finalout=&tempsurf;
								output[sc]=finalout;
							}
						}
					}
					if (output[sc]) drawn++;
				}
			}
		}
		if (!finalout) finalout=output[0];
		if (!finalout) finalout=output[1];
		if (finalout==NULL)
		{
			tempsurf.ClearPix(0);
			finalout=&tempsurf;
			
		}
		drawpostprocess(finalout,dt);

		
		if (curframeidx>=CUTOFF)
		{
		
			if (finalout && credalpha>0) 
				finalout->Blt(false,OPER_ADD,bound(0,credalpha*255,255),&credits);
		}

		
	
		return 0;
	}
};


scene *scene::first=NULL;



v3 GetVineFoc()
{
	return svinefoc;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// SCENES
struct testscene : public scene
{
	
	

	virtual void run(superproc *main, float curtime, float dt, float endtime)
	{
		myfpscam.Update();
		
		

		main->rendermode=0;

		if (GetAsyncKeyState(VK_F1)&1)
		{
			main->fullmode=!main->fullmode;
		}
	}

	testscene() : scene(0)
	{

	}
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// SCENES
struct starscene : public scene
{
	
	

	virtual void run(superproc *main, float curtime, float dt, float endtime)
	{
		myfpscam.Update();
		
		
		if (camid!=33)
		{
			int camid=this->camid;
			if (camid==26) camid=25;
			camedit->SetPath(camid);
			camedit->path().previewmode=true;
			camedit->path().previewpos=(camedit->path().frames.size()-1) * (campos);
			myfpscam.Update();
		}

		main->rendermode=1;

		if (GetAsyncKeyState(VK_F1)&1)
		{
			main->fullmode=!main->fullmode;
		}
	}

	starscene(int xid) : scene(xid)
	{

	}
};



struct mainscene : public scene
{
	thecam myfpscam;
	int actualcamid;

	virtual void run(superproc *main, float curtime, float dt, float endtime)
	{
		

		{
			camedit->SetPath(actualcamid);
			camedit->path().previewmode=true;
			camedit->path().previewpos=(camedit->path().frames.size()-1) * (campos);
			myfpscam.Update();
		}
			
		main->rendermode=0;

		if (GetAsyncKeyState(VK_F1)&1)
		{
			main->fullmode=!main->fullmode;
		}
	}

	mainscene(int i) : scene(i)
	{
		actualcamid=camid;
	}

};

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// SETUP SCENES
void superproc::SetupScenes()
{
	new testscene();
	new starscene(33);
	new mainscene(25);
	for (int c1=20;c1<32;c1++) if (c1!=25) new starscene(c1);
	for (c1=1;c1<20;c1++) new mainscene(c1);
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// UTILS


void CALLBACK FilmFSCB(void *user, stereo *out, int len)
{
	if (thedoc)
	{
		memset(out,0,len*4);
		if (thedoc->playing)
		{
			if (audioproc) 	
			{
				audioproc->Do(thedoc->GetCurTime(), 1.f/25.f, &AudioBlock[0], thedoc);
				/*for (int c1=0;c1<len;c1++)
				{
					out[c1].l = bound(-32767,int(AudioBlock[c1].l*32767.f),32767);
					out[c1].r = bound(-32767,int(AudioBlock[c1].r*32767.f),32767);
				}*/
				memcpy(out,AudioBlock,4*len);
			}
			thedoc->IncTime(MBLURFAC);			
		}
		else
		{
			
		}
	}
}

extern  RenderThing *GetAudioProc();

void InitFilm(CThingDoc *doc, int realt)
{
	finited=1;
	realtime=realt;
	thedoc=doc;
	backbuf = Surface::backbufsurf;
	audioproc=NULL;
	videoproc=NULL;	
	if (realtime)
	{
		
		soundout=new SoundOut;
		soundout->fscb = FilmFSCB;
		soundout->Init();
		

	}

	videoproc = new superproc;

	

	audioproc = GetAudioProc();

}


void CloseFilm()
{
	finited=0;
	if (soundout) soundout->fscb=NULL;
	delete soundout;
	soundout=NULL;
}

extern int filmoutstarted;
extern int curframeidx;
int curframeidx = 0;
#define DFAC 1
int bigbuf[YRES/DFAC][XRES/DFAC][3];
unsigned char Data2[YRES/DFAC][XRES/DFAC][3];

void RenderFrame()
{
	if (finited)
	{
		if (realtime)
		{
			if (videoproc)
			{

				/*
				POINT p;
				GetCursorPos(&p);
				jitterx = p.x * XRES / 1280.f;
				jittery = p.y * YRES / 1024.f;
				SetFOV();
				*/

				float t=thedoc->GetCurTime();				
				memcpy(TempAudioBlock,AudioBlock,sizeof(AudioBlock));
				float dt=t-thedoc->lasttime;
				dt=bound(0,dt,1);
				if (!thedoc->playing) dt=0;
				videoproc->Do(t,dt,&TempAudioBlock[0], thedoc);
				thedoc->lasttime=t;

				backbuf->UpdateToRendertarget(finalout);
				
			}
		}
		else
		{
			if (videoproc)
			{
				
				thedoc->playing=true;
#define SPEEDUP 1
				float dt = 1.f/25.f/MBLURFAC*SPEEDUP;
				float t=thedoc->GetCurTime();
				if (audioproc) audioproc->Do(t, 1.f/25.f, &AudioBlock[0], thedoc);

				FILE *soundoutfile=fopen("output/sound.raw","ab");
				
				if (soundoutfile)
				{
#define BLOCKSIZE32 ((BLOCKSIZE*32000)/44100)
					stereo resamp[BLOCKSIZE32];
					int ps=0;
					for (int c1=0;c1<BLOCKSIZE32;c1++)
					{
						float frac = (ps&16383)/16384.f;
						resamp[c1].l=AudioBlock[(ps>>14)].l*(1-frac)+AudioBlock[(ps>>14)+1].l * frac;
						resamp[c1].r=AudioBlock[(ps>>14)].r*(1-frac)+AudioBlock[(ps>>14)+1].r * frac;
						ps+=(44100*16384)/32000;
					}
					fwrite(resamp,4,BLOCKSIZE32,soundoutfile);
					flushall();

					fclose(soundoutfile);
				}
				for (int c1=0;c1<MBLURFAC;c1++)
				{
					if (c1==0)
					{
						memset(bigbuf,0,sizeof(bigbuf));
					}
					UpdateJitter(c1&3);
					videoproc->Do(t,dt,&AudioBlock[0 /*BLOCKSIZE*c1/MBLURFAC*/], thedoc);
					// todo downsample and combine mblur frames...
					
					if (finalout && curframeidx>=CUTOFF)
					{ 
						//memset(finalout->pix,(c1&1)?255:0,2*XRES*YRES);

						pixel *src=finalout->pix;
						for (int yy=0;yy<YRES;yy++)
						{
							int yy2=yy/DFAC;
							for (int xx=0;xx<XRES;xx++)
							{
								int xx2=xx/DFAC;
								bigbuf[yy2][xx2][0]+=src->r*4;
								bigbuf[yy2][xx2][1]+=src->g*4;
								bigbuf[yy2][xx2][2]+=src->b*4;
								if (xx<XRES-1)
								{
									bigbuf[yy2][xx2][0]+=(src+1)->r*2;
									bigbuf[yy2][xx2][1]+=(src+1)->g*2;
									bigbuf[yy2][xx2][2]+=(src+1)->b*2;
								}
								if (yy<YRES-1)
								{
									bigbuf[yy2][xx2][0]+=(src+XRES)->r*2;
									bigbuf[yy2][xx2][1]+=(src+XRES)->g*2;
									bigbuf[yy2][xx2][2]+=(src+XRES)->b*2;
									if (xx<XRES-1)
									{
										bigbuf[yy2][xx2][0]+=(src+XRES+1)->r;
										bigbuf[yy2][xx2][1]+=(src+XRES+1)->g;
										bigbuf[yy2][xx2][2]+=(src+XRES+1)->b;
									}

								}
								src++;
							}
						}
						//if (c1==0)
						{
							backbuf->UpdateToRendertarget(finalout);
							Flip();
						}
					}
					t+=dt;
					thedoc->IncTime(1*SPEEDUP);
				}
				if (curframeidx >= CUTOFF+1)
				{
				
					// todo write final frame and audioblock
					char foutname[246];
					sprintf(foutname,"output/frame%04d.tga",curframeidx+1);
					FILE *fo = fopen(foutname,"wb");
					if (fo)
					{
						int new_w=XRES/DFAC;
						int new_h=YRES/DFAC+TOPNTAIL*2;
						unsigned char tgahead[18] = {0,0,2,0,0,0,0,0,0,0,0,0,new_w,(new_w>>8),new_h,(new_h>>8),24,0};		
						fwrite(tgahead,1,18,fo);
						static unsigned char black[TOPNTAIL*XRES*3];
						memset(black,0,sizeof(black));
						fwrite(black,1,TOPNTAIL*XRES*3,fo);
						for (int yy=0;yy<YRES/DFAC;yy++)
						{
							for (int xx=0;xx<XRES/DFAC;xx++)
							{
								Data2[YRES/DFAC-1-yy][xx][2]=bigbuf[yy][xx][0]/(MBLURFAC*9*(DFAC*DFAC));
								Data2[YRES/DFAC-1-yy][xx][1]=bigbuf[yy][xx][1]/(MBLURFAC*9*(DFAC*DFAC));
								Data2[YRES/DFAC-1-yy][xx][0]=bigbuf[yy][xx][2]/(MBLURFAC*9*(DFAC*DFAC));
							}
						}					
						fwrite(Data2,XRES*YRES*3,1,fo);
						fwrite(black,1,TOPNTAIL*XRES*3,fo);
						fclose(fo);
					}
					else
					{
						MessageBox(NULL,"error writing frame","oh no",MB_OK);
					}
					_sleep(50);
				}
				curframeidx++;
				if (curframeidx==MAXFRAME)
				//if (curframeidx==40)
				{
					realtime=1; // And.... relax
				}

				
				
			}
		}
		Flip();
		_sleep(10);
	}
	if (CInfoView::infoview) CInfoView::infoview->InvalidateRect(NULL);
}



int filmoutstarted;

void InitFilmOut()
{
	filmoutstarted=GetTickCount();	
	//_mkdir("c:/art");
	_mkdir("output");
	_unlink("output/sound.raw");

}