/////////////////////////////////////////////////////////////
// CINEMA SDK : SHADER																		 //
/////////////////////////////////////////////////////////////
// VERSION    : CINEMA 4D																	 //
/////////////////////////////////////////////////////////////
// (c) 1989-2002 MAXON Computer GmbH, all rights reserved	 //
/////////////////////////////////////////////////////////////

// example for an easy implementation of a volume (surface) shader
// the dialog uses C4Ds render engine to generate previews

#include "c4d.h"
#include "c4d_symbols.h"
#include "Msimplematerial.h"

// be sure to use a unique ID obtained from www.plugincafe.com
#define ID_SIMPLEMAT 1001164

class SimpleMaterial : public MaterialData
{
	INSTANCEOF(SimpleMaterial,MaterialData)

	private:
		Vector color;
	public:
		virtual Bool Init		(GeListNode *node);
		virtual	void CalcSurface			(PluginMaterial *mat, VolumeData *vd);
		virtual	LONG InitRender(PluginMaterial *mat, InitRenderStruct *irs);
		static NodeData *Alloc(void) { return gNew SimpleMaterial; }
};

Bool SimpleMaterial::Init(GeListNode *node)
{
	BaseContainer *data=((PluginMaterial*)node)->GetDataInstance();
	data->SetVector(SIMPLEMATERIAL_COLOR,1.0);
	return TRUE;
}

LONG SimpleMaterial::InitRender(PluginMaterial *mat, InitRenderStruct *irs)
{
	BaseContainer *data=mat->GetDataInstance();
	color = data->GetVector(SIMPLEMATERIAL_COLOR);
	return LOAD_OK;
}

static void SimpleIllumModel(VolumeData *sd, RayLightCache *rlc, void *dat)
{
	SimpleMaterial	*data=(SimpleMaterial*)dat;
	Bool						nodif,nospec;
	LONG						i;
	Real						cosa,cosb,exponent=5.0;
	Vector					v=SV(sd->ray->v);

	rlc->diffuse=rlc->specular=0.0;

	for (i=0; i<rlc->cnt; i++)
	{
		RayLightComponent *lc=rlc->comp[i];
		if (lc->lv==0.0) continue; // light invisible

		RayLight *ls=lc->light;

		nodif=nospec=FALSE;
		if (ls->lr.object) CalcRestrictionInc(&ls->lr,sd->op,nodif,nospec);

		lc->diffuse=lc->specular=0.0;
		if (ls->ambient) 
			lc->diffuse = 1.0; 
		else if (ls->type==RT_LT_TUBE || ls->type==RT_LT_AREA)
			sd->LightCalcSpecial(ls,exponent,nodif,nospec,v,sd->cosc,sd->p,sd->bumpn,lc->diffuse,lc->specular);
		else 
		{
			cosa=sd->bumpn*lc->lv;
			if (!(ls->nodiffuse || nodif) && sd->cosc*cosa>=0.0) 
			{
				Real trn=ls->trn;
				if (trn!=1.0)
					lc->diffuse = Pow(Abs(cosa),trn);
				else
					lc->diffuse = Abs(cosa);
			}

			if (!(ls->nospecular || nospec))
			{
				cosb=v * (lc->lv - sd->bumpn*(2.0 * cosa));

				if (cosb>0.0) 
					lc->specular = Pow(cosb,exponent);
			}
		}

		rlc->diffuse  += lc->diffuse*lc->col;
		rlc->specular += lc->specular*lc->col;
	}
}

void SimpleMaterial::CalcSurface(PluginMaterial *mat, VolumeData *vd)
{
	Vector diff,spec,att_spc,att_dif;
	LONG i;	

	//sd->Illuminance1(&diff,&spec,5.0);
	vd->Illuminance(&diff,&spec,SimpleIllumModel,this); // replace standard model by custom model

	att_spc = 0.5+0.5*Turbulence(vd->uvw*2.5,4.0,TRUE);
	att_dif = att_spc^color;

	vd->col = (att_dif^(diff+vd->ambient)) + (att_spc^spec);

	// process multipass data
	Multipass *buf = vd->multipass;
	if (!buf) return;

	*buf->vp_mat_color					= att_dif;
	*buf->vp_mat_specularcolor	= att_spc;
	*buf->vp_mat_specular				= 0.4; // 2.0/exponent (or similar value)
	
	// values have only to be filled if != 0.0
	// *buf->vp_mat_luminance			= 0.0;
	// *buf->vp_mat_environment		= 0.0;
	// *buf->vp_mat_transparency		= 0.0;
	// *buf->vp_mat_reflection			= 0.0;
	// *buf->vp_mat_diffusion			= 0.0;

	// calculate ambient component
	*buf->vp_ambient = att_dif^vd->ambient;

	// attenuate diffuse components
	for (i=0; i<buf->diffuse_cnt; i++)
		*buf->diffuse[i] = att_dif^(*buf->diffuse[i]);
	
	// attenuate specular components
	for (i=0; i<buf->specular_cnt; i++)
		*buf->specular[i] = att_spc^(*buf->specular[i]);
}


class ScenePreview;

class SceneThread : public Thread
{
	public:
		ScenePreview *prev;
		BaseDocument *doc;

		virtual void Main(void);
};

class ScenePreview : public GeUserArea
{
	friend class SceneThread;
	
	private:
		BaseBitmap			*col;
		SceneThread			st;
		SimpleMaterial	*current;
	public:
		ScenePreview(void);
		~ScenePreview(void);

		void InitSP(BaseDocument *doc, SimpleMaterial *t_curr);
		void CalcPreview(void);
		void StopPreview(void);

		virtual Bool GetMinSize(LONG &w,LONG &h);
		virtual void Sized(LONG w,LONG h);
		virtual void Draw(LONG x1,LONG y1,LONG x2,LONG y2);
};

void ScenePreview::InitSP(BaseDocument *doc, SimpleMaterial *t_curr)
{ 
	st.prev=this;
	st.doc=doc;
	current = t_curr;
}

void SceneThread::Main(void)
{
	if (!prev->col) return;
	LONG bw=prev->col->GetBw();
	LONG bh=prev->col->GetBh();
	if (!bh || !bw) return;

	if (doc)
	{
		BaseContainer rdata=doc->GetActiveRenderData()->GetData();
		rdata.SetLong(RDATA_XRES,bw);
		rdata.SetLong(RDATA_YRES,bh);

		BaseMaterial *mat = BaseMaterial::Alloc(ID_SIMPLEMAT);
		if (!mat) return;
		
		prev->current->CopyTo(mat->GetNodeData(),NULL,NULL,0,NULL); 

		doc->InsertMaterial(mat);
		BaseObject *op = doc->SearchObject("Stairs");
		TextureTag *tag= NULL;
		if (op) tag=(TextureTag*)op->GetTag(Ttexture);
		if (tag) tag->SetMaterial(mat);

		Bool finished = RenderDocument(doc,NULL,NULL,prev->col,rdata,TRUE,FALSE,this)==RAY_OK;
		
		if (finished)
			prev->Redraw(TRUE);

		blDelete(mat);
	}
}

ScenePreview::ScenePreview(void)
{
	col=BaseBitmap::Alloc();
}

ScenePreview::~ScenePreview(void)
{
	BaseBitmap::Free(col);
}

Bool ScenePreview::GetMinSize(LONG &w,LONG &h)
{
	w = 80;
	h = 60;
	return TRUE;
}

void ScenePreview::Sized(LONG w,LONG h)
{
	if (!col) return;
	StopPreview();
	col->Init(w,h);

	LONG r,g,b;
	GeGetGray(&r,&g,&b);
	col->Clear(r,g,b);

	CalcPreview();
}

void ScenePreview::StopPreview(void)
{
	st.End();
}

void ScenePreview::Draw(LONG x1,LONG y1,LONG x2,LONG y2)
{
	if (!col) return;
	LONG w = col->GetBw();
	LONG h = col->GetBh();
	DrawBitmap(col,0,0,w,h,0,0,w,h,0);
}

void ScenePreview::CalcPreview(void)
{
	st.End();
	st.Start(TRUE);
}

Bool RegisterSimpleMaterial(void)
{
	// decide by name if the plugin shall be registered - just for user convenience
	String name=GeLoadString(IDS_SIMPLEMATERIAL); if (!name.Content()) return TRUE;
	return RegisterMaterialPlugin(ID_SIMPLEMAT,name,0,SimpleMaterial::Alloc,"Msimplematerial",0);
}
