#ifndef DEF_CSG
 #define DEF_CSG
//------------------------------------------//
//	TKA4 CEngine and basis Graph classes	//
//				09. 11. 2007				//
//------------------------------------------//


//--- Includes
#include <windows.h>
#include <stdio.h>
#include <math.h>

#include "TX_Object.h"
//=== Includes 


//--- Pragma 
#pragma warning(disable:4305) 
//=== Pragma 


//---------------------------------------------------------------------


//---  CSG Base  ---//
class txObj_CSG_Base : public txObject {
protected :
	inline virtual void a(void) {}; // A figure
	inline virtual void b(void) {}; // B figure 

protected :

	//--- CSG :: OR operation
	inline void or () {
		glPushAttrib(GL_ALL_ATTRIB_BITS);
		glEnable(GL_DEPTH_TEST);
		a();
		b();
		glPopAttrib();
	}


	//--- CSG :: INSIDE operation
	inline void inside (GLenum face, GLenum test) {
		// paint A in depth buf
		glEnable(GL_DEPTH_TEST);
		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
		glCullFace(face);
		a();
	
		// use the stencil buf for finding A in B
		glDepthMask(GL_FALSE);
		glEnable(GL_STENCIL_TEST);
		glStencilFunc(GL_ALWAYS, 0, 0);
		glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
		glCullFace(GL_BACK);
		b();

		// less the stencil buf for the back plane
		glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
		glCullFace(GL_FRONT);
		b();

		// paint A in B
		glDepthMask(GL_TRUE);
		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
		glStencilFunc(test, 0, 1);
		glDisable(GL_DEPTH_TEST);
		glCullFace(face);
		a();

		glDisable(GL_STENCIL_TEST);
	}


	//--- CSG :: INSIDE INVERSE (a=b, b=a)operation
	inline void inside_i (GLenum face, GLenum test) {
		// paint A in depth buf
		glEnable(GL_DEPTH_TEST);
		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
		glCullFace(face);
		b();
	
		// use the stencil buf for finding A in B
		glDepthMask(GL_FALSE);
		glEnable(GL_STENCIL_TEST);
		glStencilFunc(GL_ALWAYS, 0, 0);
		glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
		glCullFace(GL_BACK);
		a();

		// less the stencil buf for the back plane
		glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
		glCullFace(GL_FRONT);
		a();

		// paint A in B
		glDepthMask(GL_TRUE);
		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
		glStencilFunc(test, 0, 1);
		glDisable(GL_DEPTH_TEST);
		glCullFace(face);
		b();

		glDisable(GL_STENCIL_TEST);
	}

	//--- CSG :: FIX Z-buf
	inline void fix () {
		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
		glEnable(GL_DEPTH_TEST);
		glDisable(GL_STENCIL_TEST);
		glDepthFunc(GL_ALWAYS);
		b();
		glDepthFunc(GL_LESS);
	}


	//--- CSG :: AND operation
	inline void and () {
		inside(GL_BACK, GL_NOTEQUAL);
		fix(); // paint B in Z-buf
		inside_i(GL_BACK, GL_NOTEQUAL);
	}


	//--- CSG :: SUB operation
	inline void sub () {
		inside(GL_FRONT, GL_NOTEQUAL);
		fix(); // paint B in Z-buf
		inside_i(GL_BACK, GL_EQUAL);
	}

};


//---  CSG Cubic-Sphere  ---//
class txObj_CSG_Cube : public txObj_CSG_Base {

protected :
	//--- Sphere
	inline virtual void a(void) { 
		glColor4f(0.6,0.3,0.2,0.8);
		glutSolidSphere(sphere_size , 50, 50);
	};

	//--- Cube
	inline virtual void b(void) {
		glColor4f(0.6,0.7,0.2,0.8);
		glutSolidCube(box_size);
	};

public :
	GLfloat  sphere_size;
	GLfloat  sphere_size0;
	GLfloat  box_size;
	
	txcSphere  sphere;
	GLfloat    col_sphere;
	
	GLuint  tex_number;


	//--- txObj_ :: Init
	virtual void Execute_Init() {
		matrix.t[1] = 10.0;

		sphere_size0 = 1.9;
		box_size = 5.0;
		col_sphere = (TwoSqrt/2) * (box_size/2);

		tex_number = createTexture2D(true, "../Resources/Images/CSG.bmp");

		sphere = 
			txcSphere(matrix.t[0],matrix.t[1],matrix.t[2],col_sphere*2,txc_outside); 
		World->Collision.AddTXC(&sphere);
	}


	//--- txObj_CSG :: Forward (Scene Drawing)
	virtual void Execute_Forward()
	{
		glPushMatrix();
		glDisable(GL_DEPTH_TEST);
		
		matrix.r[1] = (World->World_Time/2)%360;
		matrix.Apply();
		sphere_size = sphere_size0 + 2*sin( (matrix.r[1]*PI)/360.0 );

		glEnable(GL_TEXTURE_2D);
		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
		glEnable(GL_COLOR_MATERIAL);

		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
		glBindTexture(GL_TEXTURE_2D, tex_number);

		glClear(GL_STENCIL_BUFFER_BIT);
		glEnable(GL_BLEND);
		glEnable(GL_CULL_FACE);
		sub(); 
		glDisable(GL_CULL_FACE);

		glDisable(GL_TEXTURE_GEN_S);
		glDisable(GL_TEXTURE_GEN_T);
		glDisable(GL_TEXTURE_2D);
		
		glPopMatrix();
	}

};

#endif



