Pitchaya Kunarochrakse / Gameplay, Tools Developer 

LinkedIn E-mail

Individual Work

Advanced Rendering Techniques

     The program shows several advanced rendering techniques. They are HDR (High Dynamic Range), Reinhard Tonemapping, SSAO (Screen-Space Ambient Occlusion), Adaptive Luminance and Sobel edge detection.

     HDR is a technique storing a high range of lighting value, which is normally 0-1, in the scene data. The application relies heavily on HDR as a skeleton, and then expands to Reinhard Tonemapping. Then find the average light value of the scene from the camera’s perspective and uses that value to modify colors in the scene.

     Unlike humans that have the ability to adjust the light intensity in their eyes over time, computers possess the ability to change the light intensity instantly, which leads to an unnatural transition. For humans, if they look at dark areas then instantly look at bright areas, their irises will take time to adjust, resulting in the bright areas slowly getting darker. The name of this technique is Adaptive Luminance.

     SSAO is a technique that the developer storing depth values from the camera to the surface of an object then used those values to highlight the surface which is “in the front” when compared with its neighbors.

Genre : Rendering

Tech used : C++, DirectX, HLSL

Keyword : HDR, Reinhard, SSAO, Lighting


Code Sample

float pixelSizeX;
float pixelSizeY;
float calcLuminance;

// start VS
void MyVS(float4 pos:SATT_POSITION,
		  float2 tc:TEXCOORD0,
		  out float4 opos:SV_POSITION,
		  out float2 otc:TEXCOORD0
		  )
{
	opos = pos;
	otc = tc;
}

Texture2D tex;

SamplerState defss
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

float4 Color(float2 texcoord)
{
	return tex.Sample(defss,texcoord);
}

float4 average( float2 tc )
{
	float2 pixelSize;
	pixelSize.x = pixelSizeX;
	pixelSize.y = pixelSizeY;

	return (
		Color( tc+float2(-1,-1)*pixelSize ) +
		Color( tc+float2(1,-1)*pixelSize ) +
		Color( tc+float2(-1,1)*pixelSize ) +
		Color( tc+float2(1,1)*pixelSize )
		) * 0.25;
}

float luminance( float3 c )
{
	return dot( c, float3(0.2126,0.7132,0.0722) );
}

float4 averageLum( float2 tc )
{
	float2 pixelSize;
	pixelSize.x = pixelSizeX;
	pixelSize.y = pixelSizeY;

	float4 c = Color(tc);
	return luminance(c.xyz);
}

// start PS
float4 MyPS(float4 pos:SV_POSITION,
			float2 tc:TEXCOORD0
			):SV_Target
{
	if (calcLuminance == 0)
	{
		// use luminance function
		return averageLum(tc);
	}
	else
	{
		// use normal average function
		return average(tc);
	}
}

technique11 MyTechnique
{
	pass P0
	{
		SetVertexShader(CompileShader(vs_4_0,MyVS()));
		SetPixelShader(CompileShader(ps_4_0,MyPS()));
		SetGeometryShader(NULL);
	}
}
float g_timeStep; // set delta time here as seconds

// start VS
void MyVS(float4 pos:SATT_POSITION,
		  float2 tc:TEXCOORD0,
		  out float4 opos:SV_POSITION,
		  out float2 otc:TEXCOORD0
		  )
{
	opos = pos;
	otc = tc;
}

Texture2D oldAvg;
Texture2D newAvg;

SamplerState defss
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

// start PS
float4 MyPS(float4 pos:SV_POSITION,
			float2 tc:TEXCOORD0
			):SV_Target
{
	float tmp_timeStep = g_timeStep/1.0;

	float avg = newAvg.Sample(defss,tc).r;
	float adapted = oldAvg.Sample(defss,tc).r;
	float a = adapted + (avg-adapted)*( 1.0 - exp(-tmp_timeStep*2.0) );

	return float4(a,a,a,a);
}

technique11 MyTechnique
{
	pass P0
	{
		SetVertexShader(CompileShader(vs_4_0,MyVS()));
		SetPixelShader(CompileShader(ps_4_0,MyPS()));
		SetGeometryShader(NULL);
	}
}
float luminance( float3 c )
{
	return dot( c, float3(0.2126,0.7132,0.0722) );
}

// start VS
void MyVS(float4 pos:SATT_POSITION,
		  float2 tc:TEXCOORD0,
		  out float4 opos:SV_POSITION,
		  out float2 otc:TEXCOORD0
		  )
{
	opos = pos;
	otc = tc;
}

Texture2D tex;
Texture2D avgTone;

SamplerState defss
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

// start PS
float4 MyPS(float4 pos:SV_POSITION,
			float2 tc:TEXCOORD0
			):SV_Target
{
	// calc scale lum
	float pxLum = luminance(tex.Sample(defss,tc));
	float4 scLum = 0.18 * pxLum / avgTone.Sample(defss,tc);

	// calc Ld
	float4 tmpLx = (tex.Sample(defss,tc) * scLum);
	float4 Ld = tmpLx / (1+tmpLx);
	return Ld;
}

technique11 MyTechnique
{
	pass P0
	{
		SetVertexShader(CompileShader(vs_4_0,MyVS()));
		SetPixelShader(CompileShader(ps_4_0,MyPS()));
		SetGeometryShader(NULL);
	}
}
// start VS
void MyVS(float4 pos:SATT_POSITION,
		  float2 tc:TEXCOORD0,
		  out float4 opos:SV_POSITION,
		  out float2 otc:TEXCOORD0
		  )
{
	opos = pos;
	otc = tc;
}

Texture2D tex;
Texture2D depthTex;
float showTexture;
float pixelSizeX;
float pixelSizeY;
float threshold = 0.000001;

float sampleSize = 8;
float2 AO_SAMPLES[8] =
{
	float2(-0.71,0.71),float2(0,1),float2(0.71,0.71),
	float2(0,-1), float2(1,0),
	float2(0.71,-0.71),float2(-1,0),float2(-0.71,-0.71)
};

SamplerState defss
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

float4 Color(float x, float y)
{
	return tex.Sample(defss,float2(x,y));
}

float percentSSAO(float x, float y)
{
	float2 pixelSize;
	pixelSize.x = pixelSizeX;
	pixelSize.y = pixelSizeY;

	float4 oriPx = tex.Sample(defss,float2(x,y));
	float depthPx = depthTex.Sample(defss,float2(x,y)).x;

	float largerCount = 0;
	for (int j = 1; j 	{
		for (int i = 0; i < sampleSize; ++i) 		{ 			if (depthPx - threshold >= depthTex.Sample(defss, (float2(x,y) + AO_SAMPLES[i]*pixelSize*j) ).x)
			{
				largerCount += 1;
			}
		}
	}

	return largerCount / (sampleSize*sampleSize);
}

// start PS
float4 MyPS(float4 pos:SV_POSITION,
			float2 tc:TEXCOORD0
			):SV_Target
{
	float res = 1.0 * percentSSAO(tc.x, tc.y);
	return float4(res,res,res,1.0);
}

technique11 MyTechnique
{
	pass P0
	{
		SetVertexShader(CompileShader(vs_4_0,MyVS()));
		SetPixelShader(CompileShader(ps_4_0,MyPS()));
		SetGeometryShader(NULL);
	}
}

Real-time Bullet Editor

     The application can edit a bullet pattern in real-time. The editor can save and load bullet patterns in a specific file format. The renderer engine can also change the view from top-down to third person perspective.

     The bullet class has unique properties that it uses for simulation of the bullet trajectory. One of these properties is the bullet’s state. The bullet system can add more states and adding more states means the bullet has different behavior as time passes. The bullet will automatically destroy itself when at the end of its lifetime. The bullet system works like a state machine – it changes behavior based on its current state.

     The bullet system is also easy to integrate with any engine because it contains no platform-specific code.

Genre : Editor, Tools

Tech used : C++, DirectX

Keyword : Editor, Tools

Code Sample

#pragma once
#include "util.h"
#include "Camera.h"
#include "MaxImporter.h"

namespace banknamespace
{
	enum directionType
	{
		RELATE_TO_WORLD,
		RELATE_TO_BULLET,
		RELATE_TO_ENEMY,
		RELATE_TO_SELF,
		dLASTTYPE
	};

	struct bulletStateInit
	{
		int initName;
		vec3f initPos;
		directionType dirType;
		vec3f initDir;
		vec3f initSpeed;

		float initWait;
		int repeatRound;
		float repeatDelay;
		vec3f initDirChange;
		vec3f initSpeedChange;
		int childInitStateName;

		int movementStateName;
	};

	struct bulletStateMovement
	{
		int stateName;
		float stateTime;
		int nextStateName;

		directionType dirType;
		vec3f dirChange;
		vec3f speedChange;
		float drag;

		int changeOfStateGenerator;
	};

	struct bulletState
	{
		bool isDead;
		float lifeTime;
		bulletStateMovement* moveState;
		Camera bulletCam;
	};

	struct initPackage
	{
		std::string name;
		bulletState starterState;
		std::vector initList;
		std::vector movementList;
		float collisionRadius;
	};

	class BulletPatternGenerator
	{
	public:
		BulletPatternGenerator(void);
		~BulletPatternGenerator(void);

		void init(bulletStateInit* initstate, bulletStateInit* parentinitstate, int order, bulletState* parentstate, std::vector* ilist, std::vector* mlist, mat16f* scalMat, float collisionrad);

		void Tick(float deltaTime);
		bool chkCollision(const vec3f& center, float radius);
		void draw(MaxImporter &maxModel, Renderer* renderer, unsigned int nonAnimatedShader, unsigned int animatedShader, int mvpLocation, Camera& m_camera, mat16f& projectionMatrix); // draw all bulletList
		void drawCollision(mat16f& scal, MaxImporter &maxModel, Renderer* renderer, unsigned int nonAnimatedShader, unsigned int animatedShader, int mvpLocation, Camera& m_camera, mat16f& projectionMatrix); // draw all bulletList

		bool getIsDead();

		friend class EnemyGenerator;
	private:
		std::vector deadIdxMark;

		bulletStateMovement* findMovementStateName(int name);
		bulletStateInit* findInitStateName(int name);
		void resetStateVariable();

		bool allChildGenIsDead;
		bool isNotGenerator;
		bool isDead; // this generator is dead

		std::vector generatorList;
		std::vector* initList;
		std::vector* movementList;
		mat16f* scaleMat;
		float collisionRadius;
		mat16f collisionMat;

		bulletStateInit initState;
		bulletState thisBulletState;

		// init variables
		float initClock;
		bool isPassInitWait;
		int repeatedRoundCount;

	};

}

#include "StdAfx.h"
#include "BulletPatternGenerator.h"

namespace banknamespace
{

	BulletPatternGenerator::BulletPatternGenerator(void)
	{
	}

	BulletPatternGenerator::~BulletPatternGenerator(void)
	{
	}

	void BulletPatternGenerator::resetStateVariable()
	{
		initClock = 0.0f;
		isPassInitWait = false;
		repeatedRoundCount = 0;
	}

	bulletStateMovement* BulletPatternGenerator::findMovementStateName(int name)
	{
		for (unsigned int i = 0; i < movementList->size(); ++i)
		{
			if ((*movementList)[i].stateName == name)
			{
				return &(*movementList)[i];
			}
		}
		return 0;
	}
	bulletStateInit* BulletPatternGenerator::findInitStateName(int name)
	{
		for (unsigned int i = 0; i < initList->size(); ++i)
		{
			if ((*initList)[i].initName == name)
			{
				return &(*initList)[i];
			}
		}
		return 0;
	}

	void BulletPatternGenerator::init(bulletStateInit* initstate, bulletStateInit* parentinitstate, int order, bulletState* parentstate, std::vector* ilist, std::vector* mlist, mat16f* scalMat, float collisionrad)
	{
		// init variables
		this->isNotGenerator = false;
		this->isDead = false;
		this->allChildGenIsDead = false;

		initClock = 0.0f;
		isPassInitWait = false;
		repeatedRoundCount = 0;

		this->initList = ilist;
		this->movementList = mlist;
		this->scaleMat = scalMat;
		this->collisionRadius = collisionrad;

		setScaleMat(collisionMat, collisionRadius);

		// init this bullet state
		if (initstate == 0)
		{
			this->isDead = true;
			return;
		}

		initState = *initstate;
		initState.initDir = initState.initDir + parentinitstate->initDirChange*order + static_cast(parentstate->bulletCam.rotation());
		initState.initPos = initState.initPos + static_cast(parentstate->bulletCam.position());

		thisBulletState.isDead = false;
		thisBulletState.lifeTime = 0.0f;
		thisBulletState.moveState = findMovementStateName(initState.movementStateName);
		if (thisBulletState.moveState == 0)
		{
			thisBulletState.isDead = true;
			this->isDead = true;
			return;
		}

		thisBulletState.bulletCam.setPosition(initState.initPos);
		thisBulletState.bulletCam.setRotation(initState.initDir);
		thisBulletState.bulletCam.update();
		thisBulletState.bulletCam.setViewTransformation();
		if (initState.dirType == RELATE_TO_BULLET)
		{
			thisBulletState.bulletCam.setVelocity(initState.initSpeed);
		}
		else if (initState.dirType == RELATE_TO_WORLD)
		{
			thisBulletState.bulletCam.setVelocityNoEuler(initState.initSpeed);
		}
		thisBulletState.bulletCam.setDrag(thisBulletState.moveState->drag);
		// set impulse in Tick()
	}

	void BulletPatternGenerator::Tick(float deltaTime)
	{
		try
		{

		deadIdxMark.clear();

		for (unsigned int i = 0; i < generatorList.size(); ++i) 		{ 			if (generatorList[i].getIsDead() == true) 			{ 				// this generator is already dead, marking 				deadIdxMark.push_back(i); 			} 			else 			{ 				generatorList[i].Tick(deltaTime); 			} 		} 		// clear dead index 		if (generatorList.size() == 0) 		{ 			//this->allChildGenIsDead = true;
		}
		else
		{
			if (deadIdxMark.size() > 0)
			for (int i = deadIdxMark.size()-1; i >= 0; --i)
			{
				generatorList.erase(generatorList.begin()+deadIdxMark[i]);
			}
		}

		if (this->isDead) return;

		// change state gen
		if (thisBulletState.moveState == 0)
		{
			this->isDead = true;
			return;
		}
		if (thisBulletState.lifeTime > thisBulletState.moveState->stateTime + deltaTime)
		{
			// change state
			BulletPatternGenerator newGen;
			newGen.init( findInitStateName(thisBulletState.moveState->changeOfStateGenerator), &initState, 0, &thisBulletState, initList, movementList, this->scaleMat, this->collisionRadius );
			generatorList.push_back(newGen);

			this->isDead = true;

			return;
		}
		else
		{
			if (thisBulletState.moveState == 0)
			{
				this->isDead = true;
				return;
			}
			// move normally
			// apply force to camera here
			if (thisBulletState.moveState->dirType == RELATE_TO_WORLD)
			{
				//thisBulletState.bulletCam.applyImpulseNoEuler( initState.initSpeed );
				thisBulletState.bulletCam.applyImpulseNoEuler( thisBulletState.moveState->speedChange );
			}
			else if (thisBulletState.moveState->dirType == RELATE_TO_BULLET)
			{
				//thisBulletState.bulletCam.applyImpulse( initState.initSpeed );
				thisBulletState.bulletCam.applyImpulse( thisBulletState.moveState->speedChange );
			}

			thisBulletState.bulletCam.applyAngularImpulse( thisBulletState.moveState->dirChange );

			thisBulletState.bulletCam.update();
			thisBulletState.bulletCam.setViewTransformation();

		}

		// init gen
		if (!isPassInitWait && initClock >= initState.initWait)
		{
			initClock -= initState.initWait;
			isPassInitWait = true;
		}

		if (isPassInitWait)
		{
			// start init repeating pattern
			float tmpDelay = initState.repeatDelay * repeatedRoundCount;
			for (int i = repeatedRoundCount; i < initState.repeatRound && initClock > tmpDelay ; ++i)
			{
				BulletPatternGenerator newGen;
				newGen.init( findInitStateName(initState.childInitStateName), &initState, repeatedRoundCount, &thisBulletState, initList, movementList, this->scaleMat, this->collisionRadius );
				generatorList.push_back(newGen);

				tmpDelay += initState.repeatDelay;
				repeatedRoundCount++;
			}
		}

		initClock += deltaTime;
		thisBulletState.lifeTime += deltaTime;

		}
		catch (...)
		{
			this->isDead = true;
		}
	}

	bool BulletPatternGenerator::chkCollision(const vec3f& center, float radius)
	{
		// if collide, return true then mark the collision bullet as dead
		for (unsigned int i = 0; i < generatorList.size(); ++i)
		{
			//generatorList[i].draw(maxModel, renderer, nonAnimatedShader, animatedShader, mvpLocation, m_camera, projectionMatrix);
			if (generatorList[i].chkCollision(center, radius) )
			{
				return true;
			}
		}

		vec3f rad = thisBulletState.bulletCam.position() - center;
		float rad2 = rad.x*rad.x + rad.y*rad.y + rad.z*rad.z;
		if ( rad2 < (radius+collisionRadius)*(radius+collisionRadius) && !this->isDead )
		{
			// collide
			this->isDead = true;
			return true;
		}

		return false;
	}

	void BulletPatternGenerator::drawCollision(mat16f& scal, MaxImporter &maxModel, Renderer* renderer, unsigned int nonAnimatedShader, unsigned int animatedShader, int mvpLocation, Camera& m_camera, mat16f& projectionMatrix)
	{
		// draw child
		for (unsigned int i = 0; i < generatorList.size(); ++i) 		{ 			generatorList[i].drawCollision(scal, maxModel, renderer, nonAnimatedShader, animatedShader, mvpLocation, m_camera, projectionMatrix); 		} 		if (this->isDead) return;
		///// draw player + animation

		// set up animation frame data first
		for (int i = 0; i < maxModel.maxNodeArr.size(); ++i)
		{
			{
				unsigned int max = 0;
				max = std::max( maxModel.maxNodeArr[i].localTMAnim.pos.size(), max );
				max = std::max( maxModel.maxNodeArr[i].localTMAnim.rot.size(), max );
				max = std::max( maxModel.maxNodeArr[i].localTMAnim.scale.size(), max );
				//maxModel.maxNodeArr[i].setCurrentLocalTM( (frameCounter*2) % max );
				maxModel.maxNodeArr[i].setCurrentLocalTM( 0 );
			}
		}

		// draw animation
		for (int i = 0; i < maxModel.maxMeshArr.size(); ++i) 		{ 			{ 				mat16f modelMatrix = maxModel.getRecurseTM(maxModel.maxMeshArr[i].transformationId) * (scal) * (this->collisionMat) * thisBulletState.bulletCam.camMatrix;
				renderer->setVariable(mvpLocation, S_MAT4, (modelMatrix * m_camera.camMatrixInv * projectionMatrix).data);
				maxModel.draw( renderer, nonAnimatedShader, &(maxModel.maxMeshArr[i]) );
			}
		}

	}

	void BulletPatternGenerator::draw(MaxImporter &maxModel, Renderer* renderer, unsigned int nonAnimatedShader, unsigned int animatedShader, int mvpLocation, Camera& m_camera, mat16f& projectionMatrix)
	{
		// draw child
		for (unsigned int i = 0; i < generatorList.size(); ++i) 		{ 			generatorList[i].draw(maxModel, renderer, nonAnimatedShader, animatedShader, mvpLocation, m_camera, projectionMatrix); 		} 		if (this->isDead) return;
		///// draw player + animation

		// set up animation frame data first
		for (int i = 0; i < maxModel.maxNodeArr.size(); ++i)
		{
			/*if (i == 0)
			{
				maxModel[modelIdx].maxMeshArr[i].setCurrentLocalTM( 0 );
			}*/
			//else
			{
				unsigned int max = 0;
				max = std::max( maxModel.maxNodeArr[i].localTMAnim.pos.size(), max );
				max = std::max( maxModel.maxNodeArr[i].localTMAnim.rot.size(), max );
				max = std::max( maxModel.maxNodeArr[i].localTMAnim.scale.size(), max );
				//maxModel.maxNodeArr[i].setCurrentLocalTM( (frameCounter*2) % max );
				maxModel.maxNodeArr[i].setCurrentLocalTM( 0 );
			}
		}

		// draw animation
		for (int i = 0; i < maxModel.maxMeshArr.size(); ++i) 		{ 			if (maxModel.maxMeshArr[i].isSkin) 			{ 				// TODO animation supported here 			} 			else 			{ 				mat16f modelMatrix = maxModel.getRecurseTM(maxModel.maxMeshArr[i].transformationId) * (*this->scaleMat) * thisBulletState.bulletCam.camMatrix;
				renderer->setVariable(mvpLocation, S_MAT4, (modelMatrix * m_camera.camMatrixInv * projectionMatrix).data);
				maxModel.draw( renderer, nonAnimatedShader, &(maxModel.maxMeshArr[i]) );
			}
		}

	}

	bool BulletPatternGenerator::getIsDead()
	{
		bool childisnotdead = false;
		for (unsigned int i = 0; i < generatorList.size(); ++i) 		{ 			if (!generatorList[i].getIsDead()) 			{ 				childisnotdead = true; 				break; 			} 		} 		return this->isDead && !childisnotdead;
	}
}
#pragma once
#include "BulletPatternGenerator.h"

namespace banknamespace
{
	struct enemyStateInit
	{
		int initName;
		vec3f initPos;
		directionType dirType;
		vec3f initDir;
		vec3f initSpeed;
		int movementStateName;
	};

	struct enemyStateMovement
	{
		int stateName;
		float stateTime;
		int nextStateName;

		directionType dirType;
		vec3f dirChange;
		vec3f speedChange;
		float drag;
	};

	struct enemyState
	{
		float HP;
		initPackage* shootPattern;

		bool isDead;
		float lifeTime;
		enemyStateMovement* moveState;
		Camera bulletCam;
	};

	struct enemyInitPackage
	{
		std::string name;
		enemyState starterState;
		std::vector shootPatternList;
		enemyStateInit initParam;
		std::vector movementList;
		float collisionRadius;
	};

	class EnemyGenerator
	{
	public:
		EnemyGenerator(void);
		~EnemyGenerator(void);

		enemyStateMovement* findMovementStateName(int name);

		void setClip(float x, float y, float z);
		void initEnemy(float HP, std::vector* shootpattern, enemyStateInit* initstate, std::vector* mlist, std::vector* enemybulletgenerator, mat16f* scalMat, mat16f* bulletScalMat, float collisionrad, float shotinterval);

		void Tick(float deltaTime);
		bool chkCollision(const vec3f& center, float radius);
		void draw(MaxImporter &maxModel, Renderer* renderer, unsigned int nonAnimatedShader, unsigned int animatedShader, int mvpLocation, Camera& m_camera, mat16f& projectionMatrix);
		void drawCollision(mat16f& scal, MaxImporter &maxModel, Renderer* renderer, unsigned int nonAnimatedShader, unsigned int animatedShader, int mvpLocation, Camera& m_camera, mat16f& projectionMatrix);

		void Shoot(initPackage* shootpattern);
		void TakeDamage(float dmg);

		vec3f getPosition();
		bool getIsDead();

		friend class BulletPatternGenerator;

		// variable
		float collisionRadius;

	private:

		std::vector* movementList;
		mat16f* scaleMat;
		mat16f* bulletScaleMat;

		mat16f collisionMat;

		enemyStateInit initState;
		enemyState thisEnemyState;

		std::vector* enemyBulletGenerator;
		float shotCounter;
		float shootInterval;

		float initClock;

		// boundary
		float maxX;
		float maxY;
		float maxZ;
	};
}
#include "StdAfx.h"
#include "EnemyGenerator.h"

namespace banknamespace
{
	EnemyGenerator::EnemyGenerator(void)
	{

	}

	EnemyGenerator::~EnemyGenerator(void)
	{
	}

	enemyStateMovement* EnemyGenerator::findMovementStateName(int name)
	{
		for (unsigned int i = 0; i < movementList->size(); ++i)
		{
			if ((*movementList)[i].stateName == name)
			{
				return &(*movementList)[i];
			}
		}
		return 0;
	}

	void EnemyGenerator::setClip(float x, float y, float z)
	{
		this->maxX = x;
		this->maxY = y;
		this->maxZ = z;
	}

	void EnemyGenerator::initEnemy(float HP, std::vector* shootpattern, enemyStateInit* initstate, std::vector* mlist, std::vector* enemybulletgenerator, mat16f* scalMat, mat16f* bulletScalMat, float collisionrad, float shotinterval)
	{
		this->thisEnemyState.HP = HP;
		this->thisEnemyState.shootPattern = shootpattern->data();
		this->shootInterval = shotinterval;
		this->enemyBulletGenerator = enemybulletgenerator;

		initClock = 0.0f;
		this->movementList = mlist;
		this->scaleMat = scalMat;
		this->bulletScaleMat = bulletScalMat;

		this->collisionRadius = collisionrad;
		setScaleMat(collisionMat, collisionRadius);

		// init this bullet state
		if (initstate == 0)
		{
			thisEnemyState.isDead = true;
			return;
		}

		initState = *initstate;
		initState.initDir = initState.initDir;
		initState.initPos = initState.initPos;

		thisEnemyState.isDead = false;
		thisEnemyState.lifeTime = 0.0f;
		thisEnemyState.moveState = findMovementStateName(initState.movementStateName);
		thisEnemyState.bulletCam.setPosition(initState.initPos);
		thisEnemyState.bulletCam.setRotation(initState.initDir);
		thisEnemyState.bulletCam.update();
		thisEnemyState.bulletCam.setViewTransformation();
		if (initState.dirType == RELATE_TO_BULLET)
		{
			thisEnemyState.bulletCam.setVelocity(initState.initSpeed);
		}
		else if (initState.dirType == RELATE_TO_WORLD)
		{
			thisEnemyState.bulletCam.setVelocityNoEuler(initState.initSpeed);
		}
		thisEnemyState.bulletCam.setDrag(thisEnemyState.moveState->drag);
		// set impulse in Tick()

		this->shotCounter = 0.0f;
	}

	void EnemyGenerator::Tick(float deltaTime)
	{
		if (thisEnemyState.isDead) return;

		try
		{

		// change state gen
		if (thisEnemyState.lifeTime > thisEnemyState.moveState->stateTime + deltaTime)
		{
			// change state
			thisEnemyState.moveState = findMovementStateName( thisEnemyState.moveState->nextStateName );
			thisEnemyState.lifeTime = 0.0f;

			if (thisEnemyState.moveState == 0)
			{
				thisEnemyState.isDead = true;
			}

			return;
		}
		else
		{
			// move normally
			// apply force to camera here
			if (thisEnemyState.moveState->dirType == RELATE_TO_WORLD)
			{
				//thisBulletState.bulletCam.applyImpulseNoEuler( initState.initSpeed );
				thisEnemyState.bulletCam.applyImpulseNoEuler( thisEnemyState.moveState->speedChange );
			}
			else if (thisEnemyState.moveState->dirType == RELATE_TO_BULLET)
			{
				//thisBulletState.bulletCam.applyImpulse( initState.initSpeed );
				thisEnemyState.bulletCam.applyImpulse( thisEnemyState.moveState->speedChange );
			}

			thisEnemyState.bulletCam.applyAngularImpulse( thisEnemyState.moveState->dirChange );

			thisEnemyState.bulletCam.update();
			thisEnemyState.bulletCam.setViewTransformation();

			// shoot periodically
			if (shotCounter > shootInterval)
			{
				shotCounter -= shootInterval;
				Shoot(&this->thisEnemyState.shootPattern[0]);
			}

			// check boundary
			vec3f tmp = thisEnemyState.bulletCam.position();
			if (tmp.x > maxX)  thisEnemyState.isDead = true;
			if (tmp.x < -maxX) thisEnemyState.isDead = true; 			if (tmp.y > maxY)  thisEnemyState.isDead = true;
			if (tmp.y < -maxY) thisEnemyState.isDead = true; 			if (tmp.z > maxZ)  thisEnemyState.isDead = true;
			if (tmp.z < -maxZ) thisEnemyState.isDead = true;
		}

		shotCounter += deltaTime;
		initClock += deltaTime;
		thisEnemyState.lifeTime += deltaTime;

		}
		catch (...)
		{
			thisEnemyState.isDead = true;
		}

	}

	bool EnemyGenerator::chkCollision(const vec3f& center, float radius)
	{
		// if collide, return true then mark the collision bullet as dead

		vec3f rad = thisEnemyState.bulletCam.position() - center;
		float rad2 = rad.x*rad.x + rad.y*rad.y + rad.z*rad.z;
		if ( rad2 < (radius+collisionRadius)*(radius+collisionRadius) && !thisEnemyState.isDead  )
		{
			// collide
			thisEnemyState.isDead = true;
			return true;
		}

		return false;
	}

	void EnemyGenerator::draw(MaxImporter &maxModel, Renderer* renderer, unsigned int nonAnimatedShader, unsigned int animatedShader, int mvpLocation, Camera& m_camera, mat16f& projectionMatrix)
	{
		if (thisEnemyState.isDead) return;
		///// draw player + animation

		// set up animation frame data first
		for (int i = 0; i < maxModel.maxNodeArr.size(); ++i)
		{
			/*if (i == 0)
			{
				maxModel[modelIdx].maxMeshArr[i].setCurrentLocalTM( 0 );
			}*/
			//else
			{
				unsigned int max = 0;
				max = std::max( maxModel.maxNodeArr[i].localTMAnim.pos.size(), max );
				max = std::max( maxModel.maxNodeArr[i].localTMAnim.rot.size(), max );
				max = std::max( maxModel.maxNodeArr[i].localTMAnim.scale.size(), max );
				//maxModel.maxNodeArr[i].setCurrentLocalTM( (frameCounter*2) % max );
				maxModel.maxNodeArr[i].setCurrentLocalTM( 0 );
			}
		}

		// draw animation
		for (int i = 0; i < maxModel.maxMeshArr.size(); ++i) 		{ 			if (maxModel.maxMeshArr[i].isSkin) 			{ 				// TODO animation supported here 			} 			else 			{ 				mat16f modelMatrix = maxModel.getRecurseTM(maxModel.maxMeshArr[i].transformationId) * (*this->scaleMat) * thisEnemyState.bulletCam.camMatrix;
				renderer->setVariable(mvpLocation, S_MAT4, (modelMatrix * m_camera.camMatrixInv * projectionMatrix).data);
				maxModel.draw( renderer, nonAnimatedShader, &(maxModel.maxMeshArr[i]) );
			}
		}

	}

	void EnemyGenerator::drawCollision(mat16f& scal, MaxImporter &maxModel, Renderer* renderer, unsigned int nonAnimatedShader, unsigned int animatedShader, int mvpLocation, Camera& m_camera, mat16f& projectionMatrix)
	{
		if (thisEnemyState.isDead) return;
		///// draw player + animation

		// set up animation frame data first
		for (int i = 0; i < maxModel.maxNodeArr.size(); ++i)
		{
			{
				unsigned int max = 0;
				max = std::max( maxModel.maxNodeArr[i].localTMAnim.pos.size(), max );
				max = std::max( maxModel.maxNodeArr[i].localTMAnim.rot.size(), max );
				max = std::max( maxModel.maxNodeArr[i].localTMAnim.scale.size(), max );
				//maxModel.maxNodeArr[i].setCurrentLocalTM( (frameCounter*2) % max );
				maxModel.maxNodeArr[i].setCurrentLocalTM( 0 );
			}
		}

		// draw animation
		for (int i = 0; i < maxModel.maxMeshArr.size(); ++i) 		{ 			{ 				mat16f modelMatrix = maxModel.getRecurseTM(maxModel.maxMeshArr[i].transformationId) * (scal) * (this->collisionMat) * thisEnemyState.bulletCam.camMatrix;
				renderer->setVariable(mvpLocation, S_MAT4, (modelMatrix * m_camera.camMatrixInv * projectionMatrix).data);
				maxModel.draw( renderer, nonAnimatedShader, &(maxModel.maxMeshArr[i]) );
			}
		}

	}

	void EnemyGenerator::Shoot(initPackage* shootpattern)
	{
		BulletPatternGenerator tmpGen;
		initPackage& tmpPack = *shootpattern;
		tmpPack.starterState.bulletCam.setPosition(thisEnemyState.bulletCam.position());
		tmpPack.starterState.bulletCam.setRotation(thisEnemyState.bulletCam.rotation());
		tmpGen.init(&tmpPack.initList[0], &tmpPack.initList[0], 0, &tmpPack.starterState, &tmpPack.initList, &tmpPack.movementList, bulletScaleMat, tmpPack.collisionRadius);
		enemyBulletGenerator->push_back(tmpGen);
	}

	void EnemyGenerator::TakeDamage(float dmg)
	{
		thisEnemyState.HP -= dmg;
		if (thisEnemyState.HP 		{
			thisEnemyState.isDead = true;
		}
	}

	vec3f EnemyGenerator::getPosition()
	{
		return thisEnemyState.bulletCam.position();
	}

	bool EnemyGenerator::getIsDead()
	{
		return thisEnemyState.isDead;
	}
}
#include "StdAfx.h"
#include "TestBulletGeneratorPackage.h"

namespace banknamespace
{
	///// This is the class used for only testing the bullet system. The final product will configure these properties via UI in real-time.
	///// This just the example to show the unique properties for bullet system and how to initiate each bullet pattern.

	TestBulletGeneratorPackage::TestBulletGeneratorPackage(void)
	{
	}

	TestBulletGeneratorPackage::~TestBulletGeneratorPackage(void)
	{
	}

	// player package
	void TestBulletGeneratorPackage::getPlayerBulletPackage0(initPackage& container)
	{
		float bulletSpeed = 0.1f;
		// set init vector
		bulletStateInit tmpinit0;
		tmpinit0.initName = 110;
		tmpinit0.initPos = vec3f(0,0,0);
		tmpinit0.dirType = RELATE_TO_BULLET;
		tmpinit0.initDir = vec3f(0,0,0);
		tmpinit0.initSpeed = vec3f(0,0,1.0f);
		tmpinit0.initWait = 0.0f;
		tmpinit0.repeatRound = 0;
		tmpinit0.repeatDelay = 0.0f;
		tmpinit0.initDirChange = vec3f(0,0.0f,0);
		tmpinit0.initSpeedChange = vec3f(0,0,0);
		tmpinit0.childInitStateName = 0;
		tmpinit0.movementStateName = 50;
		container.initList.push_back(tmpinit0);

		// set movement vector
		bulletStateMovement tmpmove0;
		tmpmove0.stateName = 50;
		tmpmove0.stateTime = 5.0f;
		tmpmove0.nextStateName = 0;
		tmpmove0.dirType = RELATE_TO_BULLET;
		tmpmove0.dirChange = vec3f(0,0.0f,0);
		tmpmove0.speedChange = vec3f(0,0,0);
		tmpmove0.drag = 0.0f;
		tmpmove0.changeOfStateGenerator = 0;
		container.movementList.push_back(tmpmove0);

		container.collisionRadius = 2.0f;
	}

	// enemy package
	void TestBulletGeneratorPackage::getBulletPackage0(initPackage& container)
	{
		float bulletSpeed = 0.1f;
		// set init vector
		bulletStateInit tmpinit0;
		tmpinit0.initName = "init0";
		tmpinit0.initPos = vec3f(0,0,0);
		tmpinit0.dirType = RELATE_TO_BULLET;
		tmpinit0.initDir = vec3f(0,-40.0f,0);
		tmpinit0.initSpeed = vec3f(0,0,0);
		tmpinit0.initWait = 0.0f;
		tmpinit0.repeatRound = 5;
		tmpinit0.repeatDelay = 0.0f;
		tmpinit0.initDirChange = vec3f(0,20.0f,0);
		tmpinit0.initSpeedChange = vec3f(0,0,0);
		tmpinit0.childInitStateName = "init1";
		tmpinit0.movementStateName = "move0";
		container.initList.push_back(tmpinit0);

		bulletStateInit tmpinit01;
		tmpinit01.initName = "init01";
		tmpinit01.initPos = vec3f(0,0,0);
		tmpinit01.dirType = RELATE_TO_BULLET;
		tmpinit01.initDir = vec3f(0,-30.0f,0);
		tmpinit01.initSpeed = vec3f(0,0,0.0);
		tmpinit01.initWait = 0.0f;
		tmpinit01.repeatRound = 4;
		tmpinit01.repeatDelay = 0.0f;
		tmpinit01.initDirChange = vec3f(0,20.0f,0);
		tmpinit01.initSpeedChange = vec3f(0,0,0);
		tmpinit01.childInitStateName = "init1";
		tmpinit01.movementStateName = "move2";
		container.initList.push_back(tmpinit01);

		bulletStateInit tmpinit1;
		tmpinit1.initName = "init1";
		tmpinit1.initPos = vec3f(0,0,0);
		tmpinit1.dirType = RELATE_TO_BULLET;
		tmpinit1.initDir = vec3f(0,0,0);
		tmpinit1.initSpeed = vec3f(0,0,bulletSpeed);
		tmpinit1.initWait = 0.0f;
		tmpinit1.repeatRound = 0;
		tmpinit1.repeatDelay = 0.0f;
		tmpinit1.initDirChange = vec3f(0,0.0f,0);
		tmpinit1.initSpeedChange = vec3f(0,0,0);
		tmpinit1.childInitStateName = "";
		tmpinit1.movementStateName = "move1";
		container.initList.push_back(tmpinit1);

		bulletStateInit tmpinit2;
		tmpinit2.initName = "init2";
		tmpinit2.initPos = vec3f(0,0,0);
		tmpinit2.dirType = RELATE_TO_BULLET;
		tmpinit2.initDir = vec3f(0,0,0);
		tmpinit2.initSpeed = vec3f(0,0,bulletSpeed);
		tmpinit2.initWait = 0.0f;
		tmpinit2.repeatRound = 0;
		tmpinit2.repeatDelay = 0.0f;
		tmpinit2.initDirChange = vec3f(0,0,0);
		tmpinit2.initSpeedChange = vec3f(0,0,0);
		tmpinit2.childInitStateName = "";
		tmpinit2.movementStateName = "move1";
		container.initList.push_back(tmpinit2);

		// set movement vector
		bulletStateMovement tmpmove0;
		tmpmove0.stateName = "move0";
		tmpmove0.stateTime = 0.0f;
		tmpmove0.nextStateName = "";
		tmpmove0.dirType = RELATE_TO_BULLET;
		tmpmove0.dirChange = vec3f(0,1.0f,0);
		tmpmove0.speedChange = vec3f(0,0,0);
		tmpmove0.drag = 0.0f;
		tmpmove0.changeOfStateGenerator = "init2";
		container.movementList.push_back(tmpmove0);

		bulletStateMovement tmpmove1;
		tmpmove1.stateName = "move1";
		tmpmove1.stateTime = 3.0f;
		tmpmove1.nextStateName = "";
		tmpmove1.dirType = RELATE_TO_BULLET;
		tmpmove1.dirChange = vec3f(0,0.0,0);
		tmpmove1.speedChange = vec3f(0,0,0);
		tmpmove1.drag = 0.0f;
		tmpmove1.changeOfStateGenerator = "init01";
		container.movementList.push_back(tmpmove1);

		bulletStateMovement tmpmove2;
		tmpmove2.stateName = "move2";
		tmpmove2.stateTime = 0.00f;
		tmpmove2.nextStateName = "";
		tmpmove2.dirType = RELATE_TO_BULLET;
		tmpmove2.dirChange = vec3f(0,0.0,0);
		tmpmove2.speedChange = vec3f(0,0,0);
		tmpmove2.drag = 0.0f;
		tmpmove2.changeOfStateGenerator = "";
		container.movementList.push_back(tmpmove2);

		container.collisionRadius = 2.0f;
	}

	void TestBulletGeneratorPackage::getBulletPackage1(initPackage& container)
	{
		float bulletSpeed = 0.01f;
		// set init vector
		bulletStateInit tmpinit0;
		tmpinit0.initName = "init0";
		tmpinit0.initPos = vec3f(0,0,0);
		tmpinit0.dirType = RELATE_TO_BULLET;
		tmpinit0.initDir = vec3f(0,0,0);
		tmpinit0.initSpeed = vec3f(0,0,bulletSpeed*10);
		tmpinit0.initWait = 3.0f;
		tmpinit0.repeatRound = 10;
		tmpinit0.repeatDelay = 0.3f;
		tmpinit0.initDirChange = vec3f(0,20.0f,0);
		tmpinit0.initSpeedChange = vec3f(0,0,0);
		tmpinit0.childInitStateName = "init1";
		tmpinit0.movementStateName = "move0";
		container.initList.push_back(tmpinit0);

		// move0
		bulletStateMovement tmpmove0;
		tmpmove0.stateName = "move0";
		tmpmove0.stateTime = 7.0f;
		tmpmove0.nextStateName = "";
		tmpmove0.dirType = RELATE_TO_BULLET;
		tmpmove0.dirChange = vec3f(0,0.0f,0);
		tmpmove0.speedChange = vec3f(0,0,0);
		tmpmove0.drag = 0.0f;
		tmpmove0.changeOfStateGenerator = "";
		container.movementList.push_back(tmpmove0);

		bulletStateInit tmpinit1;
		tmpinit1.initName = "init1";
		tmpinit1.initPos = vec3f(0,0,0);
		tmpinit1.dirType = RELATE_TO_BULLET;
		tmpinit1.initDir = vec3f(0,0,0);
		tmpinit1.initSpeed = vec3f(0,0,0);
		tmpinit1.initWait = 0.0f;
		tmpinit1.repeatRound = 0;
		tmpinit1.repeatDelay = 0.0f;
		tmpinit1.initDirChange = vec3f(0,0.0f,0);
		tmpinit1.initSpeedChange = vec3f(0,0,0);
		tmpinit1.childInitStateName = "";
		tmpinit1.movementStateName = "move1";
		container.initList.push_back(tmpinit1);

		bulletStateInit tmpinit2;
		tmpinit2.initName = "init2";
		tmpinit2.initPos = vec3f(0,0,0);
		tmpinit2.dirType = RELATE_TO_BULLET;
		tmpinit2.initDir = vec3f(0,0,0);
		tmpinit2.initSpeed = vec3f(0,0,bulletSpeed);
		tmpinit2.initWait = 0.0f;
		tmpinit2.repeatRound = 0;
		tmpinit2.repeatDelay = 0.0f;
		tmpinit2.initDirChange = vec3f(0,0,0);
		tmpinit2.initSpeedChange = vec3f(0,0,0);
		tmpinit2.childInitStateName = "";
		tmpinit2.movementStateName = "move1";
		container.initList.push_back(tmpinit2);

		bulletStateInit tmpinit3;
		tmpinit3.initName = "init3";
		tmpinit3.initPos = vec3f(0,0,0);
		tmpinit3.dirType = RELATE_TO_BULLET;
		tmpinit3.initDir = vec3f(0,0,0);
		tmpinit3.initSpeed = vec3f(0,0,0);
		tmpinit3.initWait = 0.0f;
		tmpinit3.repeatRound = 0;
		tmpinit3.repeatDelay = 0.0f;
		tmpinit3.initDirChange = vec3f(0,0,0);
		tmpinit3.initSpeedChange = vec3f(0,0,0);
		tmpinit3.childInitStateName = "";
		tmpinit3.movementStateName = "move2";
		container.initList.push_back(tmpinit3);

		bulletStateMovement tmpmove1;
		tmpmove1.stateName = "move1";
		tmpmove1.stateTime = 2.0f;
		tmpmove1.nextStateName = "";
		tmpmove1.dirType = RELATE_TO_BULLET;
		tmpmove1.dirChange = vec3f(0,0.1,0);
		tmpmove1.speedChange = vec3f(0,0,bulletSpeed);
		tmpmove1.drag = 0.1f;
		tmpmove1.changeOfStateGenerator = "init3";
		container.movementList.push_back(tmpmove1);

		bulletStateMovement tmpmove2;
		tmpmove2.stateName = "move2";
		tmpmove2.stateTime = 2.0f;
		tmpmove2.nextStateName = "";
		tmpmove2.dirType = RELATE_TO_BULLET;
		tmpmove2.dirChange = vec3f(0,-0.1,0);
		tmpmove2.speedChange = vec3f(0,0,bulletSpeed);
		tmpmove2.drag = 0.1f;
		tmpmove2.changeOfStateGenerator = "init2";
		container.movementList.push_back(tmpmove2);

		container.collisionRadius = 2.0f;
	}

	void TestBulletGeneratorPackage::getBulletPackage2(initPackage& container)
	{
		float bulletSpeed = 0.1f;
		// set init vector
		bulletStateInit tmpinit0;
		tmpinit0.initName = "init0";
		tmpinit0.initPos = vec3f(0,0,0);
		tmpinit0.dirType = RELATE_TO_BULLET;
		tmpinit0.initDir = vec3f(0,0,0);
		tmpinit0.initSpeed = vec3f(0,0,0);
		tmpinit0.initWait = 0.0f;
		tmpinit0.repeatRound = 5;
		tmpinit0.repeatDelay = 0.0f;
		tmpinit0.initDirChange = vec3f(0,360.0f/5.0f,0);
		tmpinit0.initSpeedChange = vec3f(0,0,0);
		tmpinit0.childInitStateName = "init01";
		tmpinit0.movementStateName = "move0";
		container.initList.push_back(tmpinit0);

		bulletStateInit tmpinit01;
		tmpinit01.initName = "init01";
		tmpinit01.initPos = vec3f(0,0,0);
		tmpinit01.dirType = RELATE_TO_BULLET;
		tmpinit01.initDir = vec3f(0,0,0);
		tmpinit01.initSpeed = vec3f(0,0,0);
		tmpinit01.initWait = 0.0f;
		tmpinit01.repeatRound = 2;
		tmpinit01.repeatDelay = 0.0f;
		tmpinit01.initDirChange = vec3f(0,20.0f,0);
		tmpinit01.initSpeedChange = vec3f(0,0,0);
		tmpinit01.childInitStateName = "init1";
		tmpinit01.movementStateName = "move0";
		container.initList.push_back(tmpinit01);

		// move0
		bulletStateMovement tmpmove0;
		tmpmove0.stateName = "move0";
		tmpmove0.stateTime = 0.0f;
		tmpmove0.nextStateName = "";
		tmpmove0.dirType = RELATE_TO_BULLET;
		tmpmove0.dirChange = vec3f(0,0.0f,0);
		tmpmove0.speedChange = vec3f(0,0,0);
		tmpmove0.drag = 0.0f;
		tmpmove0.changeOfStateGenerator = "";
		container.movementList.push_back(tmpmove0);

		bulletStateInit tmpinit1;
		tmpinit1.initName = "init1";
		tmpinit1.initPos = vec3f(0,0,0);
		tmpinit1.dirType = RELATE_TO_WORLD;
		tmpinit1.initDir = vec3f(0,0,0);
		tmpinit1.initSpeed = vec3f(bulletSpeed,0,bulletSpeed);
		tmpinit1.initWait = 0.0f;
		tmpinit1.repeatRound = 100;
		tmpinit1.repeatDelay = 0.05f;
		//tmpinit1.repeatRound = 0;
		//tmpinit1.repeatDelay = 0.0f;
		tmpinit1.initDirChange = vec3f(0,0.0f,0);
		tmpinit1.initSpeedChange = vec3f(0,0,0);
		tmpinit1.childInitStateName = "init2";
		tmpinit1.movementStateName = "move1";
		container.initList.push_back(tmpinit1);

		bulletStateMovement tmpmove1;
		tmpmove1.stateName = "move1";
		tmpmove1.stateTime = 5.0f;
		tmpmove1.nextStateName = "";
		//tmpmove1.dirType = RELATE_TO_BULLET;
		tmpmove1.dirType = RELATE_TO_WORLD;
		tmpmove1.dirChange = vec3f(0,0.1,0);
		tmpmove1.speedChange = vec3f(-0.0006,0,-0.001);
		//tmpmove1.speedChange = vec3f(0,0,0);
		tmpmove1.drag = 0.0f;
		tmpmove1.changeOfStateGenerator = "";
		container.movementList.push_back(tmpmove1);

		bulletStateInit tmpinit2;
		tmpinit2.initName = "init2";
		tmpinit2.initPos = vec3f(0,0,0);
		tmpinit2.dirType = RELATE_TO_BULLET;
		tmpinit2.initDir = vec3f(0,0,0);
		tmpinit2.initSpeed = vec3f(0,0,bulletSpeed*5);
		tmpinit2.initWait = 0.0f;
		tmpinit2.repeatRound = 0;
		tmpinit2.repeatDelay = 0.0f;
		tmpinit2.initDirChange = vec3f(0,0.0f,0);
		tmpinit2.initSpeedChange = vec3f(0,0,0);
		tmpinit2.childInitStateName = "";
		tmpinit2.movementStateName = "move2";
		container.initList.push_back(tmpinit2);

		bulletStateMovement tmpmove2;
		tmpmove2.stateName = "move2";
		tmpmove2.stateTime = 5.0f;
		tmpmove2.nextStateName = "";
		tmpmove2.dirType = RELATE_TO_BULLET;
		tmpmove2.dirChange = vec3f(0,0,0);
		tmpmove2.speedChange = vec3f(0,0,0);
		tmpmove2.drag = 0.0f;
		tmpmove2.changeOfStateGenerator = "";
		container.movementList.push_back(tmpmove2);

		container.collisionRadius = 2.0f;
	}

	void TestBulletGeneratorPackage::getBulletPackage4(initPackage& container)
	{
		float bulletSpeed = 0.1f;
		// set init vector
		bulletStateInit tmpinit0;
		tmpinit0.initName = "init0";
		tmpinit0.initPos = vec3f(0,0,0);
		tmpinit0.dirType = RELATE_TO_BULLET;
		tmpinit0.initDir = vec3f(0,0,0);
		tmpinit0.initSpeed = vec3f(0,0,0.0f);
		tmpinit0.initWait = 0.0f;
		tmpinit0.repeatRound = 5;
		tmpinit0.repeatDelay = 0.0f;
		tmpinit0.initDirChange = vec3f(0,360.0f/5.0f,0);
		tmpinit0.initSpeedChange = vec3f(0,0,0);
		tmpinit0.childInitStateName = "init01";
		tmpinit0.movementStateName = "move0";
		container.initList.push_back(tmpinit0);

		bulletStateInit tmpinit01;
		tmpinit01.initName = "init01";
		tmpinit01.initPos = vec3f(0,0,0);
		tmpinit01.dirType = RELATE_TO_BULLET;
		tmpinit01.initDir = vec3f(0,0,0);
		tmpinit01.initSpeed = vec3f(0,0,0);
		tmpinit01.initWait = 0.0f;
		tmpinit01.repeatRound = 2;
		tmpinit01.repeatDelay = 0.0f;
		tmpinit01.initDirChange = vec3f(0,20.0f,0);
		tmpinit01.initSpeedChange = vec3f(0,0,0);
		tmpinit01.childInitStateName = "init1";
		tmpinit01.movementStateName = "move0";
		container.initList.push_back(tmpinit01);

		// move0
		bulletStateMovement tmpmove0;
		tmpmove0.stateName = "move0";
		tmpmove0.stateTime = 0.0f;
		tmpmove0.nextStateName = "";
		tmpmove0.dirType = RELATE_TO_WORLD;
		tmpmove0.dirChange = vec3f(0,0.0f,0);
		tmpmove0.speedChange = vec3f(0,0,0);
		tmpmove0.drag = 0.0f;
		tmpmove0.changeOfStateGenerator = "";
		container.movementList.push_back(tmpmove0);

		bulletStateInit tmpinit1;
		tmpinit1.initName = "init1";
		tmpinit1.initPos = vec3f(0,0,0);
		tmpinit1.dirType = RELATE_TO_WORLD;
		tmpinit1.initDir = vec3f(0,0,0);
		//tmpinit1.initSpeed = vec3f(bulletSpeed,0,bulletSpeed);
		tmpinit1.initSpeed = vec3f(0,0,0);
		tmpinit1.initWait = 0.0f;
		tmpinit1.repeatRound = 100;
		tmpinit1.repeatDelay = 0.2f;
		//tmpinit1.repeatRound = 0;
		//tmpinit1.repeatDelay = 0.0f;
		tmpinit1.initDirChange = vec3f(0,0.0f,0);
		tmpinit1.initSpeedChange = vec3f(0,0,0);
		tmpinit1.childInitStateName = "init2";
		tmpinit1.movementStateName = "move1";
		container.initList.push_back(tmpinit1);

		bulletStateMovement tmpmove1;
		tmpmove1.stateName = "move1";
		tmpmove1.stateTime = 2.0f;
		tmpmove1.nextStateName = "";
		//tmpmove1.dirType = RELATE_TO_BULLET;
		tmpmove1.dirType = RELATE_TO_WORLD;
		tmpmove1.dirChange = vec3f(0,0.1,0);
		tmpmove1.speedChange = vec3f(0,0,0);
		//tmpmove1.speedChange = vec3f(0,0,0);
		tmpmove1.drag = 0.0f;
		tmpmove1.changeOfStateGenerator = "";
		container.movementList.push_back(tmpmove1);

		bulletStateInit tmpinit2;
		tmpinit2.initName = "init2";
		tmpinit2.initPos = vec3f(0,0,0);
		tmpinit2.dirType = RELATE_TO_BULLET;
		tmpinit2.initDir = vec3f(0,0,0);
		tmpinit2.initSpeed = vec3f(0,0,bulletSpeed*5);
		tmpinit2.initWait = 0.0f;
		tmpinit2.repeatRound = 0;
		tmpinit2.repeatDelay = 0.0f;
		tmpinit2.initDirChange = vec3f(0,0.0f,0);
		tmpinit2.initSpeedChange = vec3f(0,0,0);
		tmpinit2.childInitStateName = "";
		tmpinit2.movementStateName = "move2";
		container.initList.push_back(tmpinit2);

		bulletStateMovement tmpmove2;
		tmpmove2.stateName = "move2";
		tmpmove2.stateTime = 3.0f;
		tmpmove2.nextStateName = "";
		tmpmove2.dirType = RELATE_TO_BULLET;
		tmpmove2.dirChange = vec3f(0,0,0);
		tmpmove2.speedChange = vec3f(0,0,0);
		tmpmove2.drag = 0.0f;
		tmpmove2.changeOfStateGenerator = "";
		container.movementList.push_back(tmpmove2);

		container.collisionRadius = 2.0f;
	}

	void TestBulletGeneratorPackage::getBulletPackage3(initPackage& container)
	{
		float bulletSpeed = 0.01f;
		// set init vector
		bulletStateInit tmpinit0;
		tmpinit0.initName = "init0";
		tmpinit0.initPos = vec3f(0,0,0);
		tmpinit0.dirType = RELATE_TO_BULLET;
		tmpinit0.initDir = vec3f(0,0,0);
		tmpinit0.initSpeed = vec3f(0,0,0);
		tmpinit0.initWait = 0.0f;
		tmpinit0.repeatRound = 5;
		tmpinit0.repeatDelay = 0.0f;
		tmpinit0.initDirChange = vec3f(0,360.0f/5.0f,0);
		tmpinit0.initSpeedChange = vec3f(0,0,0);
		tmpinit0.childInitStateName = "init01";
		tmpinit0.movementStateName = "move0";
		container.initList.push_back(tmpinit0);

		bulletStateInit tmpinit01;
		tmpinit01.initName = "init01";
		tmpinit01.initPos = vec3f(0,0,0);
		tmpinit01.dirType = RELATE_TO_BULLET;
		tmpinit01.initDir = vec3f(0,0,0);
		tmpinit01.initSpeed = vec3f(0,0,0);
		tmpinit01.initWait = 0.0f;
		tmpinit01.repeatRound = 2;
		tmpinit01.repeatDelay = 0.0f;
		tmpinit01.initDirChange = vec3f(0,20.0f,0);
		tmpinit01.initSpeedChange = vec3f(0,0,0);
		tmpinit01.childInitStateName = "init1";
		tmpinit01.movementStateName = "move0";
		container.initList.push_back(tmpinit01);

		// move0
		bulletStateMovement tmpmove0;
		tmpmove0.stateName = "move0";
		tmpmove0.stateTime = 0.0f;
		tmpmove0.nextStateName = "";
		tmpmove0.dirType = RELATE_TO_BULLET;
		tmpmove0.dirChange = vec3f(0,0.0f,0);
		tmpmove0.speedChange = vec3f(0,0,0);
		tmpmove0.drag = 0.0f;
		tmpmove0.changeOfStateGenerator = "";
		container.movementList.push_back(tmpmove0);

		bulletStateInit tmpinit1;
		tmpinit1.initName = "init1";
		tmpinit1.initPos = vec3f(0,0,0);
		tmpinit1.dirType = RELATE_TO_WORLD;
		tmpinit1.initDir = vec3f(0,0,0);
		tmpinit1.initSpeed = vec3f(0,0,0);
		tmpinit1.initWait = 0.0f;
		tmpinit1.repeatRound = 100;
		tmpinit1.repeatDelay = 0.1f;
		//tmpinit1.repeatRound = 0;
		//tmpinit1.repeatDelay = 0.0f;
		tmpinit1.initDirChange = vec3f(0,0.0f,0);
		tmpinit1.initSpeedChange = vec3f(0,0,0);
		tmpinit1.childInitStateName = "init2";
		tmpinit1.movementStateName = "move1";
		container.initList.push_back(tmpinit1);

		bulletStateMovement tmpmove1;
		tmpmove1.stateName = "move1";
		tmpmove1.stateTime = 5.0f;
		tmpmove1.nextStateName = "";
		tmpmove1.dirType = RELATE_TO_BULLET;
		//tmpmove1.dirType = RELATE_TO_WORLD;
		tmpmove1.dirChange = vec3f(0,0.1,0);
		tmpmove1.speedChange = vec3f(0,0,bulletSpeed*5);
		tmpmove1.drag = 0.1f;
		tmpmove1.changeOfStateGenerator = "";
		container.movementList.push_back(tmpmove1);

		bulletStateInit tmpinit2;
		tmpinit2.initName = "init2";
		tmpinit2.initPos = vec3f(0,0,0);
		tmpinit2.dirType = RELATE_TO_BULLET;
		tmpinit2.initDir = vec3f(0,0,0);
		tmpinit2.initSpeed = vec3f(0,0,bulletSpeed*10);
		tmpinit2.initWait = 0.0f;
		tmpinit2.repeatRound = 0;
		tmpinit2.repeatDelay = 0.0f;
		tmpinit2.initDirChange = vec3f(0,0.0f,0);
		tmpinit2.initSpeedChange = vec3f(0,0,0);
		tmpinit2.childInitStateName = "";
		tmpinit2.movementStateName = "move2";
		container.initList.push_back(tmpinit2);

		bulletStateMovement tmpmove2;
		tmpmove2.stateName = "move2";
		tmpmove2.stateTime = 2.0f;
		tmpmove2.nextStateName = "";
		tmpmove2.dirType = RELATE_TO_BULLET;
		tmpmove2.dirChange = vec3f(0,0,0);
		tmpmove2.speedChange = vec3f(0,0,0);
		tmpmove2.drag = 0.0f;
		tmpmove2.changeOfStateGenerator = "";
		container.movementList.push_back(tmpmove2);

		container.collisionRadius = 2.0f;
	}

	///// enemy package
	void TestBulletGeneratorPackage::getEnemyPackage0(enemyInitPackage& container)
	{
		float bulletSpeed = 0.01f;

		enemyStateInit tmpinit1;
		tmpinit1.initName = "init1";
		tmpinit1.initPos = vec3f(+20,20,10);
		tmpinit1.dirType = RELATE_TO_WORLD;
		tmpinit1.initDir = vec3f(0,180,0);
		tmpinit1.initSpeed = vec3f(0,0,0.0f);
		tmpinit1.movementStateName = "move1";
		container.initParam = tmpinit1;

		enemyStateMovement tmpmove1;
		tmpmove1.stateName = "move1";
		tmpmove1.stateTime = 4.0f;
		tmpmove1.nextStateName = "move2";
		tmpmove1.dirType = RELATE_TO_BULLET;
		tmpmove1.dirChange = vec3f(0,0.1,0);
		tmpmove1.speedChange = vec3f(0,0,0.03f);
		tmpmove1.drag = 0.1f;
		container.movementList.push_back(tmpmove1);

		enemyStateMovement tmpmove2;
		tmpmove2.stateName = "move2";
		tmpmove2.stateTime = 4.0f;
		tmpmove2.nextStateName = "move1";
		tmpmove2.dirType = RELATE_TO_BULLET;
		tmpmove2.dirChange = vec3f(0,0.1,0);
		tmpmove2.speedChange = vec3f(0,0,0.03f);
		tmpmove2.drag = 0.1f;
		container.movementList.push_back(tmpmove2);

		initPackage shoot0;
		//getPlayerBulletPackage0(shoot0);
		//getBulletPackage1(shoot0);

		getPlayerBulletPackage0(shoot0);
		//getBulletPackage0(shoot0);
		container.shootPatternList.push_back(shoot0);

		container.collisionRadius = 5.0f;
	}

	void TestBulletGeneratorPackage::getEnemyPackage1(enemyInitPackage& container)
	{
		float bulletSpeed = 0.01f;

		enemyStateInit tmpinit1;
		tmpinit1.initName = "init1";
		tmpinit1.initPos = vec3f(-20,20,30);
		tmpinit1.dirType = RELATE_TO_WORLD;
		tmpinit1.initDir = vec3f(0,180,0);
		tmpinit1.initSpeed = vec3f(0,0,0);
		tmpinit1.movementStateName = "move1";
		container.initParam = tmpinit1;

		enemyStateMovement tmpmove1;
		tmpmove1.stateName = "move1";
		tmpmove1.stateTime = 4.0f;
		tmpmove1.nextStateName = "move2";
		tmpmove1.dirType = RELATE_TO_WORLD;
		tmpmove1.dirChange = vec3f(0,0.0,0);
		tmpmove1.speedChange = vec3f(bulletSpeed,0,0);
		tmpmove1.drag = 0.1f;
		container.movementList.push_back(tmpmove1);

		enemyStateMovement tmpmove2;
		tmpmove2.stateName = "move2";
		tmpmove2.stateTime = 4.0f;
		tmpmove2.nextStateName = "move1";
		tmpmove2.dirType = RELATE_TO_WORLD;
		tmpmove2.dirChange = vec3f(0,0.0,0);
		tmpmove2.speedChange = vec3f(-bulletSpeed,0,0);
		tmpmove2.drag = 0.1f;
		container.movementList.push_back(tmpmove2);

		initPackage shoot0;
		getPlayerBulletPackage0(shoot0);
		//getBulletPackage1(shoot0);

		//getBulletPackage4(shoot0);
		container.shootPatternList.push_back(shoot0);

		container.collisionRadius = 5.0f;
	}
}


Lighting

     A cube is rendered with several lighting techniques combined. On top of that, it can use both OpenGL and DirectX to render the cube – specified by user.

     The cube starts with simple diffuse texture and applies several lighting techniques on top of each other. Specular lighting uses the light position and camera position to calculate the “bright” surface.

     Tangent space is very useful because it works with each surface of the cube with only one normal map of the surface. For example, the artist has to model a cube with six different normal maps on each surface; but with tangent space, the artist has to make only one when programmer will change the normal vector according to the orientation of the surface.

     Parallax shading is the illusion-like rendering when user sees the stone “come out” of the surface. This simple trick can be done by using a height map and applying some offset to a stone texture based on where the camera is.

     Shadow mapping is how to calculate and apply the shadow on a surface by taking the light source position and the first surface it hits into calculation. Such a calculation can be done by using the distance from light source to surface and storing it in the temporary buffer. Compare the values among each surface, which one should be “shadowed”.

Genre : Rendering

Tech used : C++, DirectX, HLSL, OpenGL, GLSL

Keyword : Diffuse map, Specular lighting, Normal mapping, Tangent space, Parallax shading, Shadow Mapping


Code Sample

uniform mat4 mvp;
uniform vec4 alightpos;
uniform vec4 alightpos2;
uniform vec4 aeyepos;
uniform float aradius;
uniform float aradius2;
attribute vec4 SATT_POSITION;
attribute vec2 SATT_TEXCOORD0;
attribute vec3 SATT_NORMAL;
attribute vec4 SATT_TANGENT4F;

varying vec2 texcoord;
varying vec3 normal, tangent, bitangent;
varying vec4 tangent4f;
varying vec4 lightDir, halfAngle, eyeDir;
varying vec4 lightDir2, halfAngle2;
varying float att, att2;

void calcAttenuation(in vec4 lightpos, in vec4 pos, in float radius, inout float att)
{
   float dist = length(lightpos  - pos);
   att = max( 0.0, (radius - dist)/radius );
}

void calcSpecularVariable(in mat4 transform, in vec4 pos, in vec4 lightpos, in vec4 eyeDir, inout vec4 alightDir, inout vec4 ahalfAngle)
{
   alightDir = normalize( lightpos  - pos );
   ahalfAngle = alightDir + eyeDir;

   ahalfAngle = ahalfAngle * transform;
   alightDir = alightDir * transform;
}

void main()
{
   gl_Position = mvp
                 * SATT_POSITION;

   texcoord = vec2(SATT_TEXCOORD0);

   normal = normalize(SATT_NORMAL);
   tangent4f = SATT_TANGENT4F;

   // calc bitangent
   bitangent = cross(tangent4f.xyz, normal) * tangent4f.w;
   bitangent = normalize( bitangent );

   calcAttenuation(alightpos, SATT_POSITION, aradius, att);
   calcAttenuation(alightpos2, SATT_POSITION, aradius2, att2);

   mat4 transform = mat4(vec4(tangent4f.xyz, 0.0), vec4(bitangent, 0.0), vec4(normal, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
   eyeDir = normalize( aeyepos  - SATT_POSITION );

   calcSpecularVariable(transform, SATT_POSITION, alightpos, eyeDir, lightDir, halfAngle);
   calcSpecularVariable(transform, SATT_POSITION, alightpos2, eyeDir, lightDir2, halfAngle2);

   eyeDir = eyeDir * transform;
}

uniform sampler2D tex, normalMap, heightMap;
varying vec2 texcoord;
varying vec4 eyeDir;
varying vec4 lightDir, halfAngle;
varying vec4 lightDir2, halfAngle2;
varying float att, att2;

void calcParallax(in vec4 eyeDir, inout vec2 texcoord)
{
   float height = texture2D(heightMap, texcoord).r * 0.04 - 0.02;
   texcoord += (eyeDir.xy * height);
}

float calcDiffuseLight(in vec4 normal, in vec4 alightDir)
{
   return max( 0.0, dot( normal, alightDir ) );
}

float calcSpecularLight(in vec4 normal, in vec4 ahalfAngle)
{
   return pow( clamp( dot( normal, ahalfAngle ), 0.0, 1.0 ), 32 );
}

float calcSumLight(in float diffuse, in float specular)
{
   return diffuse + specular;
}

void main()
{
   eyeDir = normalize( eyeDir );

   lightDir = normalize( lightDir );
   halfAngle = normalize( halfAngle );
   lightDir2 = normalize( lightDir2 );
   halfAngle2 = normalize( halfAngle2 );

   calcParallax( eyeDir, texcoord );

   vec3 normal2 = normalize(texture2D(normalMap, texcoord).rgb * 2.0 - 1.0);
   vec4 normal4f = vec4( normal2, 0 );

   float intensity = (( calcSumLight(calcDiffuseLight( normal4f, lightDir ), calcSpecularLight( normal4f, halfAngle )) ) * att)
			+ (( calcSumLight(calcDiffuseLight( normal4f, lightDir2 ), calcSpecularLight( normal4f, halfAngle2 )) ) * att2);

   gl_FragColor = texture2D(tex, texcoord) * intensity;
}

float4x4 mvp;
float4 alightpos;
float4 alightpos2;
float4 aeyepos;
float aradius;
float aradius2;

// function in VS
void calcAttenuation(float4 lightpos, float4 pos, float radius, inout float att)
{
   float dist = length(lightpos  - pos);
   att = max( 0.0, (radius - dist)/radius );
}

void calcSpecularVariable(float4x4 transform, float4 pos, float4 lightpos, float4 eyeDir, inout float4 alightDir, inout float4 ahalfAngle)
{
   alightDir = normalize( lightpos  - pos );
   ahalfAngle = alightDir + eyeDir;

   ahalfAngle = mul(ahalfAngle, transform);
   alightDir = mul(alightDir, transform);
}

// start VS
void MyVS(float4 pos:SATT_POSITION, float3 normal:SATT_NORMAL,
		  float2 tc:TEXCOORD0,float4 tangent4f:SATT_TANGENT4F,
		  out float4 opos:SV_POSITION,
		  out float2 otc:TEXCOORD0,
		  out float4 oeyedir:EYEDIR,
		  out float4 olightDir1:LIGHTDIR1,
		  out float4 ohalfAngle1:HALFANGLE1,
		  out float4 olightDir2:LIGHTDIR2,
		  out float4 ohalfAngle2:HALFANGLE2,
		  out float oatt1:ATT1,
		  out float oatt2:ATT2
		  )
{
	opos = mul(pos, mvp);
	otc = tc;
	normal = normalize(normal);

	// calc bitangent
	float3 bitangent = cross(tangent4f.xyz, normal) * tangent4f.w;
	bitangent = normalize( bitangent );

	float att, att2;
	calcAttenuation(alightpos, pos, aradius, oatt1);
	calcAttenuation(alightpos2, pos, aradius2, oatt2);

	float4x4 transform = float4x4(tangent4f[0],bitangent[0],normal[0],0.0,
								   tangent4f[1],bitangent[1],normal[1],0.0,
								   tangent4f[2],bitangent[2],normal[2],0.0,
								   0.0,0.0,0.0,1.0
								   );

	oeyedir = normalize( aeyepos - pos );

	calcSpecularVariable(transform, pos, alightpos, oeyedir, olightDir1, ohalfAngle1);
	calcSpecularVariable(transform, pos, alightpos2, oeyedir, olightDir2, ohalfAngle2);

	oeyedir = mul(oeyedir, transform);
}

Texture2D tex;
Texture2D normalMap;
Texture2D heightMap;

SamplerState defss
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

// function in PS
void calcParallax(float4 eyeDir, inout float2 texcoord)
{
   float height = heightMap.Sample(defss, texcoord).x * 0.04 - 0.02;
   texcoord += (eyeDir.xy * height);
}

float calcDiffuseLight(float4 normal, float4 alightDir)
{
   return max( 0.0, dot( normal, alightDir ) );
}

float calcSpecularLight(float4 normal, float4 ahalfAngle)
{
   return pow( clamp( dot( normal, ahalfAngle ), 0.0, 1.0 ), 32 );
}

float calcSumLight(float diffuse, float specular)
{
   return diffuse + specular;
}

// start PS
float4 MyPS(float4 pos:SV_POSITION,
			float2 tc:TEXCOORD0,
			float4 eyeDir:EYEDIR,
			float4 lightDir1:LIGHTDIR1,float4 halfAngle1:HALFANGLE1,
			float4 lightDir2:LIGHTDIR2,float4 halfAngle2:HALFANGLE2,
			float iatt1:ATT1, float iatt2:ATT2
			):SV_Target
{
	eyeDir = normalize( eyeDir );
	lightDir1 = normalize( lightDir1 );
	halfAngle1 = normalize( halfAngle1 );
	lightDir2 = normalize( lightDir2 );
	halfAngle2 = normalize( halfAngle2 );

	calcParallax( eyeDir, tc );

	float3 normal2 = normalize(normalMap.Sample(defss,tc).xyz * 2.0 - 1.0);
	float4 normal4f = float4( normal2, 0 );

	float intensity = (( calcSumLight(calcDiffuseLight( normal4f, lightDir1 ), calcSpecularLight( normal4f, halfAngle1 )) ) * iatt1)
			+ (( calcSumLight(calcDiffuseLight( normal4f, lightDir2 ), calcSpecularLight( normal4f, halfAngle2 )) ) * iatt2);

	return tex.Sample(defss, tc) * intensity;
}

technique11 MyTechnique
{
	pass P0
	{
		SetVertexShader(CompileShader(vs_5_0,MyVS()));
		SetPixelShader(CompileShader(ps_5_0,MyPS()));
		SetGeometryShader(NULL);
	}
}
float4x4 m; // model matrix - to change vertex pos into world space
float4x4 mvp;
float4 alightpos; // lightpos in world space

// start VS
void MyVS(float4 pos:SATT_POSITION,
		  float2 tc:TEXCOORD0,
		  out float4 opos:SV_POSITION,
		  out float4 oworldpos:WORLDPOS
		  )
{
	opos = mul(pos, mvp);
	oworldpos = mul(pos, m);
}

Texture2D tex;

SamplerState defss
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

// start PS
float4 MyPS(float4 pos:SV_POSITION,
			//float2 tc:TEXCOORD0,
			float4 worldpos:WORLDPOS
			):SV_Target
{
	float4 dist = alightpos - worldpos;
	float depth = length(dist)/1000.0;

	return float4(depth, 0.0, 0.0, 0.0);
}

technique11 MyTechnique
{
	pass P0
	{
		SetVertexShader(CompileShader(vs_5_0,MyVS()));
		SetPixelShader(CompileShader(ps_5_0,MyPS()));
		SetGeometryShader(NULL);
	}
}
float4x4 lvp; // lightclipspace - to change vertex pos into light clip space
float4x4 m; // model matrix - to change vertex pos into world space
float4x4 mvp;
float4 alightpos; // lightpos in world space

// start VS
void MyVS(float4 pos:SATT_POSITION,
		  float2 tc:TEXCOORD0,
		  out float4 opos:SV_POSITION,
		  out float2 otc:TEXCOORD0,
		  out float4 olightspacePos:LIGHTSPACEPOS,
		  out float4 oworldpos:WORLDPOS
		  )
{
	opos = mul(pos, mvp);
	otc = tc;
	olightspacePos = mul(pos, lvp);

	oworldpos = mul(pos, m);
}

Texture2D tex;
Texture2D shadowmap;

SamplerState defss
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Border;
	AddressV = Border;
	BorderColor = float4(0.0, 0.0, 0.0, 1.0);
};

SamplerComparisonState ss
{
	Filter = COMPARISON_MIN_MAG_MIP_LINEAR;
	AddressU = Border;
	AddressV = Border;
	BorderColor = float4(0.0, 0.0, 0.0, 1.0);
	ComparisonFunc = LESS;
};

// start PS
float4 MyPS(float4 pos:SV_POSITION,
			float2 tc:TEXCOORD0,
			float4 lightspacePos:LIGHTSPACEPOS,
			float4 worldpos:WORLDPOS
			):SV_Target
{
	float epsilon = 0.00005;

	float4 dist = alightpos - worldpos;
	float depth = (length(dist)/1000.0) + epsilon;

	lightspacePos = lightspacePos/lightspacePos.w;

	// change from range -1 to 1 -> 0 to 1
	float2 lightTc = (lightspacePos.xy / 2.0) + 0.5;
	lightTc.y = 1.0 - lightTc.y;

	float shadowdepth = shadowmap.Sample(defss, lightTc);

	if (depth > shadowdepth) return tex.Sample(defss, tc) * 0.5;
	else return tex.Sample(defss, tc);
}

technique11 MyTechnique
{
	pass P0
	{
		SetVertexShader(CompileShader(vs_5_0,MyVS()));
		SetPixelShader(CompileShader(ps_5_0,MyPS()));
		SetGeometryShader(NULL);
	}
}

Dual Renderer

     Dual rendering is the way to draw the things on the screen when those things do not have anything in common. In this case, I can use DirectX and OpenGL to draw a cube on the screen. The easiest way to achieve this is make a common interface and communicate via that interface. I have to change something in DirectX to make it behave like OpenGL and vice versa. The key of success is to coordinate those drawing engines to work with each other in peace – there is some performance and readability tradeoff for that, obviously.

Genre : Rendering

Tech used : C++, DirectX, HLSL, OpenGL, GLSL

Keyword : DirectX, OpenGL, Dual Render


Code Sample

#pragma once
#include
#include
#include "util.h"

namespace banknamespace
{
	class Renderer
	{
	public:
		Renderer();
		virtual ~Renderer(void);

		virtual void init( HWND& m_hWnd, HDC& m_hdcWindow ) = 0;
		virtual void renderTarget(unsigned int target, bool useShadowView) = 0;

		virtual void clear() = 0;
		virtual void swapBuffer() = 0;

		virtual void cullFace(cullType type, bool isCounterClockwise) = 0;
		virtual unsigned int loadTexture( const char* szFilePath ) = 0;
		virtual unsigned int loadMemoryTexture( unsigned char* pixels, int width, int height ) = 0;
		virtual void bindTexture( unsigned int textureState, unsigned int textureId ) = 0;
		virtual void bindRenderTargetTexture( unsigned int textureState, unsigned int target ) = 0;
		virtual void disableTexture( unsigned int textureState ) = 0;

		virtual unsigned int createRenderTargetTexture(int type, bool isShadowMap) = 0;

		virtual int createProgram(const char* programName, std::vector& attrString, std::vector& uniformString) = 0;
		virtual void useProgram(int progID) = 0;
		virtual int getLocation(const char* name) = 0;
		virtual void setVariable(int ulocation, shaderType type, void* value) = 0;

		virtual unsigned int createBuffer( void* start, int bufferSize, bufferType type ) = 0;
		virtual void drawIndex(unsigned int idVBO, unsigned int idIBO, int strideSize, unsigned int startVertex, unsigned int endVertex, int indexCount, int startIndex, int baseVertex) = 0;
	};
}

#pragma once
#include
#include "Renderer.h"
#include "ShaderManagerOpenGL.h"
#include

#include "Resource.h"

#include "IL/il.h"
#pragma comment (lib, "DevIL.lib")

#define MAX_LOADSTRING 100

namespace banknamespace
{

	class OpenGLrenderer : public Renderer
	{
	public:
		HDC* m_hdcWindow;
		HGLRC m_renderingContext;

		OpenGLrenderer();
		~OpenGLrenderer(void);

		void init( HWND& m_hWnd, HDC& m_hdcWindow );

		// GL method
		void clear();
		void swapBuffer();

		unsigned int loadTexture( const char* szFilePath );
		unsigned int loadMemoryTexture( unsigned char* pixels, int width, int height );
		void bindTexture( unsigned int textureState, unsigned int textureId );
		void disableTexture( unsigned int textureState );

		int createProgram(const char* programName, std::vector& attrString, std::vector& uniformString);
		void useProgram(int progID);
		int getLocation(const char* name);
		void setVariable(int ulocation, shaderType type, void* value);

		unsigned int createBuffer( void* start, int bufferSize, bufferType type );
		void drawIndex(unsigned int idVBO, unsigned int idIBO, int strideSize, unsigned int startVertex, unsigned int endVertex, int indexCount, int startIndex, int baseVertex);

		// test
		void draw();
	protected:
		ShaderManagerOpenGL shaderManager;

		void InitializeOpenGL();
		void ShutdownOpenGL();
	};

}

#include "StdAfx.h"
#include "OpenGLrenderer.h"

namespace banknamespace
{
	////////////////////////////////////////
	///// General function
	////////////////////////////////////////

	////////////////////////////////////////
	///// end of General function
	////////////////////////////////////////

	OpenGLrenderer::OpenGLrenderer()
	{

	}

	OpenGLrenderer::~OpenGLrenderer(void)
	{
		ShutdownOpenGL();
	}

	void OpenGLrenderer::init( HWND& m_hWnd, HDC& m_hdcWindow )
	{
		this->m_hdcWindow = &m_hdcWindow;

		InitializeOpenGL();
		glEnable( GL_DEPTH_TEST );

		glMatrixMode( GL_MODELVIEW );
	}

	///// GL method
	void OpenGLrenderer::clear()
	{
		glClearColor (0.25f, 0.25f, 0.25f, 1.0f);
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	}

	void OpenGLrenderer::swapBuffer()
	{
		SwapBuffers(*m_hdcWindow);
	}

	///// use Devil to load texture
	unsigned int OpenGLrenderer::loadTexture( const char* szFilePath )
	{
			ilInit();
			ilEnable( IL_ORIGIN_SET );
			ilOriginFunc( IL_ORIGIN_UPPER_LEFT );

			ILuint idImage = ilGenImage();
			ilBindImage( idImage );

			size_t requiredSize = 0;
			::mbstowcs_s(&requiredSize,NULL,0,szFilePath,0);
			wchar_t* w_szTexturePath = new wchar_t[requiredSize + 1];
			::mbstowcs_s(&requiredSize, w_szTexturePath,requiredSize + 1, szFilePath,requiredSize);

			ilLoadImage( w_szTexturePath );

			delete w_szTexturePath;

			if (IL_NO_ERROR != ilGetError())
			{
				return 0;
			}

			ILuint width = ilGetInteger( IL_IMAGE_WIDTH );
			ILuint height = ilGetInteger( IL_IMAGE_HEIGHT );
			ILuint format = ilGetInteger( IL_IMAGE_FORMAT );
			ILuint type = ilGetInteger( IL_IMAGE_TYPE );
			ILuint nChannels = ilGetInteger( IL_IMAGE_CHANNELS );

			GLuint idTexture;
			glGenTextures(1, &idTexture);
			glBindTexture( GL_TEXTURE_2D, idTexture );

			glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
			glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, type, ilGetData() );

			glGenerateMipmap( GL_TEXTURE_2D );

		    ilDeleteImage( idImage );

			return idTexture;
	}

	unsigned int OpenGLrenderer::loadMemoryTexture( unsigned char* pixels, int width, int height )
	{
		    //int width,height,nChannels;
		    //unsigned char* pixels = stbi_load(szFilePath, &width, &height, &nChannels, 4);
		    // ... process data if not NULL ...
		    // ... x = width, y = height, n = # 8-bit components per pixel ...
		    // ... replace '0' with '1'..'4' to force that many components per pixel
		    // ... but 'n' will always be the number that it would have been if you said 0
			//CONST wchar *lp
			if (!pixels)
			{
				//MessageBox(NULL, L"File load error", L"Error", 0);
				return 0;
			}

			GLuint idTexture;
			glGenTextures(1, &idTexture);
			glBindTexture( GL_TEXTURE_2D, idTexture );

			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
			glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels );

			glGenerateMipmap( GL_TEXTURE_2D );

		    //delete[] pixels;

			return idTexture;
	}

	void OpenGLrenderer::bindTexture( unsigned int textureState, unsigned int textureId )
	{
		unsigned int startByte = GL_TEXTURE0;
		startByte += textureState;

		glActiveTexture( startByte );
		glEnable( GL_TEXTURE_2D );
		glBindTexture( GL_TEXTURE_2D, textureId );
	}
	void OpenGLrenderer::disableTexture( unsigned int textureState )
	{
		unsigned int startByte = GL_TEXTURE0;
		startByte += textureState;

		glActiveTexture( startByte );
		glDisable( GL_TEXTURE_2D );
	}

	///// Shader
	int OpenGLrenderer::createProgram(const char* programName, std::vector& attrString, std::vector& uniformString)
	{
		return shaderManager.createProgram(programName, attrString, uniformString);
	}
	void OpenGLrenderer::useProgram(int progID)
	{
		shaderManager.useProgram(progID);
	}
	int OpenGLrenderer::getLocation(const char* name)
	{
		return shaderManager.getLocation(name);
	}
	void OpenGLrenderer::setVariable(int ulocation, shaderType type, void* value)
	{
		shaderManager.setVariable(ulocation, type, value);
	}

	/// Buffer
	unsigned int OpenGLrenderer::createBuffer( void* start, int bufferSize, bufferType type )
	{
		GLuint result;
		glGenBuffers(1, &result);
		unsigned int btype;
		if ( type == B_INDEXBUFFER )
		{
			btype = GL_ELEMENT_ARRAY_BUFFER;
		}
		else
		{
			btype = GL_ARRAY_BUFFER;
		}
		glBindBuffer( btype, result );	// TODO GL_ELEMENT_ARRAY_BUFFER
		glBufferData( btype, bufferSize, start, GL_STATIC_DRAW );

		return result;
	}

	void OpenGLrenderer::drawIndex(unsigned int idVBO, unsigned int idIBO, int strideSize, unsigned int startVertex, unsigned int endVertex, int indexCount, int startIndex, int baseVertex)
	{
		glBindBuffer( GL_ARRAY_BUFFER, idVBO );
		glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, idIBO );

		std::vector attributeArr = shaderManager.attributeProg[shaderManager.currentProgram];

		// set vertex pointer
		for (unsigned int i = 0; i < attributeArr.size(); ++i)
		{
			glEnableVertexAttribArray(attributeArr[i].id);

			switch (attributeArr[i].attrType)
			{
			case S_FLOAT4 :
				{
					glVertexAttribPointer( attributeArr[i].id, 4, GL_FLOAT, false, strideSize, reinterpret_cast< const GLvoid* >(attributeArr[i].offset) );
					break;
				}
			case S_FLOAT3 :
				{
					glVertexAttribPointer( attributeArr[i].id, 3, GL_FLOAT, false, strideSize, reinterpret_cast< const GLvoid* >(attributeArr[i].offset) );
					break;
				}
			case S_FLOAT2 :
				{
					glVertexAttribPointer( attributeArr[i].id, 2, GL_FLOAT, false, strideSize, reinterpret_cast< const GLvoid* >(attributeArr[i].offset) );
					break;
				}
			default: break;
			}
		}

		// draw
		unsigned int buf = startIndex * sizeof(unsigned int);
		glDrawRangeElementsBaseVertex(GL_TRIANGLES, startVertex, endVertex, indexCount, GL_UNSIGNED_INT, (void*)buf, baseVertex);
		//glDrawRangeElements( GL_TRIANGLES, 0, endVertex, indexCount, GL_UNSIGNED_INT, (void*)buf );
		//glDrawElements( GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0);

		// disable
		for (unsigned int i = 0; i < attributeArr.size(); ++i)
		{
			glDisableVertexAttribArray(attributeArr[i].id);
		}
	}

	///// test draw
	void OpenGLrenderer::draw()
	{
		glColor4f(1,1,1,1);
		glBegin( GL_TRIANGLES );
			glVertex3f(-1,0,0);
			glVertex3f(1,0,0);
			glVertex3f(0,1,0);
		glEnd();
	}

	///// protected
	void OpenGLrenderer::InitializeOpenGL()
	{
		assert( m_hdcWindow );

		// Set the window pixel format
		//
		PIXELFORMATDESCRIPTOR pixelFormatDescriptor = {0};

		pixelFormatDescriptor.nSize = sizeof( pixelFormatDescriptor );
		pixelFormatDescriptor.nVersion = 1;

		pixelFormatDescriptor.dwFlags =
			PFD_DRAW_TO_WINDOW |
			PFD_SUPPORT_OPENGL |
			PFD_DOUBLEBUFFER;
		pixelFormatDescriptor.dwLayerMask = PFD_MAIN_PLANE;
		pixelFormatDescriptor.iPixelType = PFD_TYPE_RGBA;
		pixelFormatDescriptor.cColorBits = 32;
		pixelFormatDescriptor.cDepthBits = 32;

		int pixelFormat = ChoosePixelFormat( *m_hdcWindow, &pixelFormatDescriptor );
		assert (pixelFormat != 0);
		SetPixelFormat( *m_hdcWindow, pixelFormat, &pixelFormatDescriptor );

		// Create the OpenGL render context
		//
		m_renderingContext = wglCreateContext( *m_hdcWindow );
		wglMakeCurrent ( *m_hdcWindow, m_renderingContext );

		glewInit();

		glEnable( GL_DEPTH_TEST );
	}

	void OpenGLrenderer::ShutdownOpenGL()
	{
		wglMakeCurrent( NULL, NULL );
		wglDeleteContext( m_renderingContext );
		m_renderingContext = 0;
	}
}

#pragma once
#include
#include "Renderer.h"
#include "ShaderManagerOpenGL.h"
#include

#include "D3D11.h"
#include "D3DX11.h"
#include "d3dx11dbg.h"
#include "d3dx11effect.h"
#include "d3dxGlobal.h"

#pragma comment (lib, "d3d11.lib")
#ifdef _DEBUG
	#pragma comment (lib, "d3dx11d.lib") // debug
#else
	#pragma comment (lib, "d3dx11.lib")
#endif

#pragma comment (lib, "d3dcompiler.lib")
#pragma comment (lib, "Effects11.lib")

#define MAX_LOADSTRING 100

namespace banknamespace
{
	template< typename T >
	void clearDX(std::vector& dxPointer)
	{
		for (unsigned int i = 0; i < dxPointer.size(); ++i) 		{ 			dxPointer[i]->Release();
			dxPointer[i] = 0;
		}
	}

	class DX11renderer : public Renderer
	{
	public:
		int scrWidth;
		int scrHeight;
		int shadowRes;

		ID3D11Device *device;
		ID3D11DeviceContext *context;
		IDXGISwapChain *swapchain;
		ID3D11RenderTargetView *rtview;
		ID3D11DepthStencilView *dsv;
		ID3D11DepthStencilView *dsv_shadow;
		ID3D11RasterizerState *rs;

		std::vector textureResourceArr;
		std::vector renderTargetViewArr;
		std::vector renderTargetResourceArr;
		std::vector vertexBufferArr;
		std::vector indexBufferArr;

		std::vector effectArr;
		std::vector inputLayoutArr;

		std::map> effectVariableMap;
		std::vector effectVariableArr;
		std::map> effectTextureLocation;

		DX11renderer(void);
		~DX11renderer(void);

		void init(  HWND& m_hWnd, HDC& m_hdcWindow );
		void renderTarget(unsigned int target, bool useShadowView);

		// DX method
		void clear();
		void swapBuffer();

		void cullFace(cullType type, bool isCounterClockwise);
		unsigned int loadTexture( const char* szFilePath );
		unsigned int loadMemoryTexture( unsigned char* pixels, int width, int height );
		void bindTexture( unsigned int textureState, unsigned int textureId );
		void bindRenderTargetTexture( unsigned int textureState, unsigned int target );
		void disableTexture( unsigned int textureState );

		unsigned int createRenderTargetTexture(int type, bool isShadowMap);

		int createProgram(const char* programName, std::vector& attrString, std::vector& uniformString);
		void useProgram(int progID);
		int getLocation(const char* name);
		void setVariable(int ulocation, shaderType type, void* value);

		unsigned int createBuffer( void* start, int bufferSize, bufferType type );
		void drawIndex(unsigned int idVBO, unsigned int idIBO, int strideSize, unsigned int startVertex, unsigned int endVertex, int indexCount, int startIndex, int baseVertex);

		int currentProgram;
	private:
		void HV(HRESULT res)
		{
			assert( res == S_OK );
		}
	};

}

#include "StdAfx.h"
#include "DX11renderer.h"

#define STBI_HEADER_FILE_ONLY
#include "stb_image.c"

namespace banknamespace
{

	DX11renderer::DX11renderer(void)
	{
		device = NULL;
		context = NULL;
		swapchain = NULL;
		rtview = NULL;
		dsv = NULL;
		dsv_shadow = NULL;
		rs = NULL;

		shadowRes = 2048;
	}

	DX11renderer::~DX11renderer(void)
	{
		// clean up
		device->Release();
		context->Release();
		swapchain->Release();
		rtview->Release();
		dsv->Release();
		dsv_shadow->Release();
		rs->Release();

		clearDX(textureResourceArr);
		clearDX(vertexBufferArr);
		clearDX(indexBufferArr);
		clearDX(effectArr);
		clearDX(inputLayoutArr);

		clearDX(renderTargetViewArr);
	}

	void DX11renderer::init( HWND& m_hWnd, HDC& m_hdcWindow )
	{
		RECT rc;
		GetClientRect( m_hWnd, &rc );
		scrWidth = rc.right;
		scrHeight = rc.bottom;

		DXGI_SWAP_CHAIN_DESC sd =
		{
			{ scrWidth, scrHeight, { 60, 1 }, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, DXGI_MODE_SCALING_UNSPECIFIED },
			{ 1, 0 },
			DXGI_USAGE_RENDER_TARGET_OUTPUT,
			2, //1,
			m_hWnd,
			true,//!fullscreen,
			DXGI_SWAP_EFFECT_DISCARD,
			0
		};

		D3D_FEATURE_LEVEL featurelevels[] =
		{
			D3D_FEATURE_LEVEL_11_0,
			D3D_FEATURE_LEVEL_10_1,
			D3D_FEATURE_LEVEL_10_0,
		};

		D3D_FEATURE_LEVEL featurelevelpicked;

		UINT createdeviceflags = 0;
		#ifdef _DEBUG
			createdeviceflags |= D3D11_CREATE_DEVICE_DEBUG;
		#endif

		HV(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createdeviceflags, featurelevels, sizeof(featurelevels)/sizeof(D3D_FEATURE_LEVEL), D3D11_SDK_VERSION, &sd, &swapchain, &device, &featurelevelpicked, &context));

		ID3D11Texture2D *backbuffer;
		HV(swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void **)&backbuffer));
		HV(device->CreateRenderTargetView(backbuffer, NULL, &rtview));
		HV(backbuffer->Release());

		cullFace(CULL_BACK, true);

		context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

		D3D11_TEXTURE2D_DESC desc =
		{
			scrWidth, scrHeight,
			1, 1,
			DXGI_FORMAT_D32_FLOAT,
			{ 1, 0 },
			D3D11_USAGE_DEFAULT,
			D3D11_BIND_DEPTH_STENCIL,
			0, 0
		};
		ID3D11Texture2D *tex;
		HV(device->CreateTexture2D(&desc, NULL, &tex));
		HV(device->CreateDepthStencilView(tex, NULL, &dsv));
		tex->Release();

		D3D11_TEXTURE2D_DESC desc_shadow =
		{
			shadowRes, shadowRes,
			1, 1,
			DXGI_FORMAT_D32_FLOAT,
			{ 1, 0 },
			D3D11_USAGE_DEFAULT,
			D3D11_BIND_DEPTH_STENCIL,
			0, 0
		};
		ID3D11Texture2D *tex_shadow;
		HV(device->CreateTexture2D(&desc_shadow, NULL, &tex_shadow));
		HV(device->CreateDepthStencilView(tex_shadow, NULL, &dsv_shadow));
		tex_shadow->Release();

		D3D11_VIEWPORT vp;
		vp.Width = (FLOAT)scrWidth;
		vp.Height = (FLOAT)scrHeight;
		vp.MinDepth = 0.0f;
		vp.MaxDepth = 1.0f;
		vp.TopLeftX = 0;
		vp.TopLeftY = 0;
		context->RSSetViewports( 1, &vp );
	}

	void DX11renderer::renderTarget(unsigned int target, bool useShadowView)
	{
		float ClearColor[4] = {0.2f, 0.2f, 0.24f, 1.0f};

		D3D11_VIEWPORT vp;
		vp.Width = (FLOAT)scrWidth;
		vp.Height = (FLOAT)scrHeight;
		vp.MinDepth = 0.0f;
		vp.MaxDepth = 1.0f;
		vp.TopLeftX = 0;
		vp.TopLeftY = 0;

		// -1 means real one
		if (target == -1)
		{
			vp.Width = (FLOAT)scrWidth;
			vp.Height = (FLOAT)scrHeight;
			context->RSSetViewports( 1, &vp );

			context->OMSetRenderTargets(1, &rtview, dsv);
			context->ClearRenderTargetView( rtview, ClearColor );
			context->ClearDepthStencilView( dsv, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0 );
		}
		else
		{
			if (useShadowView)
			{
				vp.Width = (FLOAT)shadowRes;
				vp.Height = (FLOAT)shadowRes;

				context->RSSetViewports( 1, &vp );

				context->OMSetRenderTargets(1, &(renderTargetViewArr[ renderTargetResourceArr[target].idRenderTarget ]), dsv_shadow);
				context->ClearRenderTargetView( (renderTargetViewArr[ renderTargetResourceArr[target].idRenderTarget ]), ClearColor );
				context->ClearDepthStencilView( dsv_shadow, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0 );
			}
			else
			{
				vp.Width = (FLOAT)scrWidth;
				vp.Height = (FLOAT)scrHeight;
				context->RSSetViewports( 1, &vp );

				context->OMSetRenderTargets(1, &(renderTargetViewArr[ renderTargetResourceArr[target].idRenderTarget ]), dsv);
				context->ClearRenderTargetView( (renderTargetViewArr[ renderTargetResourceArr[target].idRenderTarget ]), ClearColor );
				context->ClearDepthStencilView( dsv, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0 );
			}

		}
	}

	// DX method
	void DX11renderer::clear()
	{
		float ClearColor[4] = {0.2f, 0.2f, 0.24f, 1.0f};
		context->ClearRenderTargetView( rtview, ClearColor );

		for (unsigned int i = 0; i < renderTargetViewArr.size(); ++i) 		{ 			context->ClearRenderTargetView( renderTargetViewArr[i], ClearColor );
		}

		context->ClearDepthStencilView( dsv, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0 );
	}

	void DX11renderer::swapBuffer()
	{
		swapchain->Present( 0, 0 );
	}

	void DX11renderer::cullFace(cullType type, bool isCounterClockwise)
	{
		D3D11_CULL_MODE cullmode;
		switch (type)
		{
			case CULL_NONE : cullmode = D3D11_CULL_NONE; break;
			case CULL_BACK : cullmode = D3D11_CULL_BACK; break;
			case CULL_FRONT : cullmode = D3D11_CULL_FRONT; break;
			default : break;
		}
		D3D11_RASTERIZER_DESC rasterizerstate = { D3D11_FILL_SOLID, cullmode, isCounterClockwise, 0, 0, 0, true, false, false, false };
		HV(device->CreateRasterizerState(&rasterizerstate, &rs));
		context->RSSetState(rs);
	}

	unsigned int DX11renderer::loadTexture( const char* szFilePath )
	{
		    int width,height,nChannels;
		    unsigned char* pixels = stbi_load(szFilePath, &width, &height, &nChannels, 4);
		    // ... process data if not NULL ...
		    // ... x = width, y = height, n = # 8-bit components per pixel ...
		    // ... replace '0' with '1'..'4' to force that many components per pixel
		    // ... but 'n' will always be the number that it would have been if you said 0
			//CONST wchar *lp
			if (!pixels)
			{
				//MessageBox(NULL, L"File load error", L"Error", 0);
				return 0;
			}

			unsigned int idTexture = loadMemoryTexture(pixels, width, height);
		    stbi_image_free(pixels);

			return idTexture;
	}

	unsigned int DX11renderer::loadMemoryTexture( unsigned char* pixels, int width, int height )
	{
		ID3D11ShaderResourceView* tmp;

		D3D11_TEXTURE2D_DESC desc =
		{
			width, height,
			1,1,
			DXGI_FORMAT_R8G8B8A8_UNORM,
			{1,0},
			D3D11_USAGE_DYNAMIC,
			D3D11_BIND_SHADER_RESOURCE,
			D3D11_CPU_ACCESS_WRITE,
			0
		};

		D3D11_SUBRESOURCE_DATA initdata;
		initdata.pSysMem = pixels;
		initdata.SysMemPitch = width * sizeof(int);
		initdata.SysMemSlicePitch = 0;

		ID3D11Texture2D *tex = NULL;
		device->CreateTexture2D( &desc, &initdata, &tex );

		HRESULT res = device->CreateShaderResourceView( tex, NULL, &tmp );
		tex->Release();

		if (res != S_OK)
		{
			return 0;
		}
		else
		{
			textureResourceArr.push_back(tmp);
			return textureResourceArr.size() - 1;
		}
	}

	void DX11renderer::bindTexture( unsigned int textureState, unsigned int textureId )
	{
		if (currentProgram < 0) return; 		if (textureId == -1) 		{ 			effectVariableArr[effectTextureLocation[currentProgram][textureState]]->AsShaderResource()->SetResource( NULL );
		}
		else
		{
			effectVariableArr[effectTextureLocation[currentProgram][textureState]]->AsShaderResource()->SetResource( textureResourceArr[textureId] );
		}
	}
	void DX11renderer::bindRenderTargetTexture( unsigned int textureState, unsigned int target )
	{
		if (currentProgram < 0) return; 		if (target == -1)  		{ 			effectVariableArr[effectTextureLocation[currentProgram][textureState]]->AsShaderResource()->SetResource( NULL );
			return;
		}
		else
		{
			effectVariableArr[effectTextureLocation[currentProgram][textureState]]->AsShaderResource()->SetResource( textureResourceArr[ renderTargetResourceArr[target].idTexture ] );
			return;
		}
	}
	void DX11renderer::disableTexture( unsigned int textureState )
	{

	}

	unsigned int DX11renderer::createRenderTargetTexture(int type, bool isShadowMap)
	{
		enum DXGI_FORMAT format;
		switch (type)
		{
			case 0 : format = DXGI_FORMAT_R8G8B8A8_UNORM; break;
			case 1 : format = DXGI_FORMAT_R32_FLOAT; break;
			default : format = DXGI_FORMAT_R8G8B8A8_UNORM;
		}

		ID3D11ShaderResourceView* tmpResource;
		ID3D11RenderTargetView* tmpRenderTargetView;

		ID3D11Texture2D *tmpTexture;

		// create RenderTarget
		int width, height;

		if (isShadowMap)
		{
			width = shadowRes;
			height = shadowRes;
		}
		else
		{
			width = scrWidth;
			height = scrHeight;
		}

		D3D11_TEXTURE2D_DESC descRenderTarget =
		{
			width, height,
			1,1,
			format,
			{1,0},
			D3D11_USAGE_DEFAULT,
			D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET,
			0,
			0
		};

		HRESULT res = device->CreateTexture2D(&descRenderTarget, NULL, &tmpTexture);
		HV(device->CreateShaderResourceView(tmpTexture, NULL, &tmpResource));
		HV(device->CreateRenderTargetView(tmpTexture, NULL, &tmpRenderTargetView));
		tmpTexture->Release();

		if (res != S_OK)
		{
			return -2;
		}
		else
		{
			textureResourceArr.push_back(tmpResource);
			renderTargetViewArr.push_back(tmpRenderTargetView);

			renderTargetStruct tmpStruct = { renderTargetViewArr.size() - 1, textureResourceArr.size() - 1 };
			renderTargetResourceArr.push_back(tmpStruct);

			return renderTargetResourceArr.size() - 1;
		}
	}

	int DX11renderer::createProgram(const char* programName, std::vector& attrString, std::vector& uniformString)
	{
		///// compile .fx file
		ID3D10Blob *fxblob = NULL, *errblob = NULL;

		wchar_t* w_programName = CharToWChar(programName);
		std::wstring name = w_programName;
		delete w_programName;

		std::wstring filepath = L"Shader_HLSL/" + name + L".fx";
		HRESULT res = D3DX11CompileFromFile(filepath.c_str(), NULL, NULL, "", "fx_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, &fxblob, &errblob, NULL);
		if (res != S_OK)
		{
			if (errblob)
			{
				char* tmp = (char *)errblob->GetBufferPointer();

				size_t requiredSize = 0;
				::mbstowcs_s(&requiredSize,NULL,0,tmp,0);
				wchar_t* printText = new wchar_t[requiredSize + 1];
				::mbstowcs_s(&requiredSize, printText,requiredSize + 1, tmp,requiredSize);

				MessageBox(NULL, printText, L"Fail", 0);
				errblob->Release();
			}
			else
			{
				MessageBox(NULL, L"fx file not load", L"Fail", 0);
			}
		}

		ID3DX11Effect* tmpEffect;
		HV(D3DX11CreateEffectFromMemory(fxblob->GetBufferPointer(), fxblob->GetBufferSize(), 0, device, &tmpEffect));
		fxblob->Release();

		unsigned int len = attrString.size();
		D3D11_INPUT_ELEMENT_DESC *decl = new D3D11_INPUT_ELEMENT_DESC[len];
		memset(decl, 0, sizeof(D3D11_INPUT_ELEMENT_DESC)*len);

		int semidx = 0;
		for (unsigned int i = 0; i < len; ++i) 		{ 			D3D11_INPUT_ELEMENT_DESC &d = decl[i]; 			d.AlignedByteOffset = attrString[i].offset; 			d.SemanticName = attrString[i].name;  			switch (attrString[i].attrType) 			{ 			case S_FLOAT4 :  				{ 					d.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; 					break; 				} 			case S_FLOAT3 :  				{ 					d.Format = DXGI_FORMAT_R32G32B32_FLOAT; 					break; 				} 			case S_FLOAT2 :  				{ 					d.SemanticName = "TEXCOORD";  					d.Format = DXGI_FORMAT_R32G32_FLOAT; 					d.SemanticIndex = semidx++; 					break; 				} 			default: assert(0); 			} 		} 		 		D3DX11_PASS_DESC effectVsDesc; 		HV(tmpEffect->GetTechniqueByIndex(0)->GetPassByIndex(0)->GetDesc(&effectVsDesc));

		ID3D11InputLayout* tmpInputLayout;
		HV(device->CreateInputLayout(
		  decl, len, effectVsDesc.pIAInputSignature, effectVsDesc.IAInputSignatureSize, &tmpInputLayout));
		delete[] decl;

		effectArr.push_back( tmpEffect );
		inputLayoutArr.push_back( tmpInputLayout );

		currentProgram = effectArr.size() - 1;

		// set uniform location
		std::map tmp;
		for (unsigned int i = 0; i < uniformString.size(); ++i) 		{ 			effectVariableArr.push_back( effectArr[currentProgram]->GetVariableByName(uniformString[i]) );
			tmp[uniformString[i]] = effectVariableArr.size() - 1;
		}
		effectVariableMap[currentProgram] = tmp;
		return currentProgram;
	}
	void DX11renderer::useProgram(int progID)
	{
		effectArr[progID]->GetTechniqueByIndex(0)->GetPassByIndex(0)->Apply(0, context);
		//context->IASetInputLayout( inputLayoutArr[progID] );

		currentProgram = progID;
	}
	int DX11renderer::getLocation(const char* name)
	{
		return effectVariableMap[currentProgram][name];
	}
	void DX11renderer::setVariable(int ulocation, shaderType type, void* value)
	{
		switch (type)
		{
		case S_MAT4 :
			{
				effectVariableArr[ulocation]->AsMatrix()->SetMatrix( reinterpret_cast( value ) );

				/*ID3DX11EffectVariable* x = effectArr[currentProgram]->GetVariableByName("wvp");
				x->AsMatrix()->SetMatrix( reinterpret_cast( value ) );*/
				break;
			}
		case S_VEC4 :
			{
				//glUniform4fv( uniformLocation[currentProgram][name], 4, reinterpret_cast( value ) );
				effectVariableArr[ulocation]->AsVector()->SetFloatVector( reinterpret_cast( value ) );
				break;
			}
		case S_FLOAT :
			{
				//glUniform1f( uniformLocation[currentProgram][name], *(reinterpret_cast( value )) );
				effectVariableArr[ulocation]->AsScalar()->SetFloat( *(reinterpret_cast( value )) );
				break;
			}
		case S_TEXTURE :
			{
				//effectArr[currentProgram]->GetVariableByName("diftex")->AsShaderResource()->SetResource( textureResourceArr[2] );

				int tmp = *(reinterpret_cast( value ));
				//effectVariableArr[ulocation]->AsShaderResource()->SetResource( textureResourceArr[tmp] );

				effectTextureLocation[currentProgram][tmp] = ulocation;
				break;
			}
		default: break;
		}
	}

	unsigned int DX11renderer::createBuffer( void* start, int bufferSize, bufferType type )
	{
		ID3D11Buffer* tmpBuffer;
		D3D11_SUBRESOURCE_DATA srd = { start, 0, 0 };

		if (type == B_INDEXBUFFER)
		{
			HV(device->CreateBuffer(&CD3D11_BUFFER_DESC(bufferSize, (type == B_INDEXBUFFER) ? D3D11_BIND_INDEX_BUFFER : D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_IMMUTABLE), &srd, &tmpBuffer));
			indexBufferArr.push_back( tmpBuffer );
			return indexBufferArr.size() - 1;
		}
		else
		{
			HV(device->CreateBuffer(&CD3D11_BUFFER_DESC(bufferSize, (type == B_INDEXBUFFER) ? D3D11_BIND_INDEX_BUFFER : D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_IMMUTABLE), &srd, &tmpBuffer));
			vertexBufferArr.push_back( tmpBuffer );
			return vertexBufferArr.size() - 1;
		}
	}
	void DX11renderer::drawIndex(unsigned int idVBO, unsigned int idIBO, int strideSize, unsigned int startVertex, unsigned int endVertex, int indexCount, int startIndex, int baseVertex)
	{
		UINT stride = strideSize;
		UINT baseVert = baseVertex;
		UINT vertexOffset = baseVert * stride;

		context->IASetVertexBuffers( 0, 1, &vertexBufferArr[idVBO], &stride, &vertexOffset );
		context->IASetIndexBuffer( indexBufferArr[idIBO], DXGI_FORMAT_R32_UINT, startIndex * sizeof(unsigned int) );

		effectArr[currentProgram]->GetTechniqueByIndex(0)->GetPassByIndex(0)->Apply(0, context);
		context->IASetInputLayout( inputLayoutArr[currentProgram] );
		context->DrawIndexed( indexCount, 0, 0 );
	}
}


2D Editor

     An application that can help making 2D map. Users can save/load maps, add tile types, and divide tiles into categories. The saved data can be easily understand and modified.

     The application is built on top of QT, a cross-platform application and UI framework for developers using C++, by inheriting QT classes and adding more functions. Works with OpenGL only.

Genre : Level Editor

Tech used : C++, QT, OpenGL

Keyword : QT, Editor, Tool

Code Sample

#pragma once

#ifndef _TABCHILDWIDGET_H
#define _TABCHILDWIDGET_H

#include
#include <QtGui/QWidget>
#include <QtGui/QScrollArea>
#include "TabTileButton.h"
#include "TextureManager.h"

const int buttonSize = 50;

class TabChildWidget : public QScrollArea
{
	Q_OBJECT
public:
	QWidget* scrollAreaWidgetContents;
	unsigned int selectedButtonId;

	TabChildWidget(QWidget *parent = NULL);
	~TabChildWidget(void);

	void initResource(TextureManager* texMgr);

	void addButton(QString filePath);
	unsigned int deleteSelectedButton();
	void resizeEvent(QResizeEvent* event);
	void resetButtons();
	void repositionButtons();

	// variable
	std::vector buttons;
	TextureManager* texManager;

private:
};

#endif  /* _TABCHILDWIDGET_H */
#include "TabChildWidget.h"

TabChildWidget::TabChildWidget(QWidget *parent) : QScrollArea(parent)
{
	this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
	this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	//scrollArea->setObjectName(QString::fromUtf8("scrollArea"));
    this->setWidgetResizable(false);
    scrollAreaWidgetContents = new QWidget();
    scrollAreaWidgetContents->setObjectName(QString::fromUtf8("scrollAreaWidgetContents"));
    scrollAreaWidgetContents->setGeometry(QRect(0, 0, 665, 656));
    this->setWidget(scrollAreaWidgetContents);

    selectedButtonId = 0;
}

TabChildWidget::~TabChildWidget(void)
{

}

void TabChildWidget::initResource(TextureManager* texMgr)
{
	texManager = texMgr;
}

void TabChildWidget::addButton(QString filePath)
{
	QIcon icon;
    icon.addFile(filePath, QSize(), QIcon::Normal, QIcon::Off);

	TabTileButton* tmpTile = new TabTileButton(scrollAreaWidgetContents);
	tmpTile->setObjectName(filePath);
	tmpTile->initButtonList(&buttons, &selectedButtonId);
	tmpTile->setGeometry(0,0,buttonSize,buttonSize);
	tmpTile->setIcon(icon);
	tmpTile->setIconSize(QSize(buttonSize-10, buttonSize-10));
    tmpTile->setCheckable(true);
    tmpTile->setFlat(true);
	tmpTile->show();

	buttons.push_back(tmpTile);
	texManager->addTexture(tmpTile->buttonId, filePath);

	repositionButtons();

	tmpTile->handleClick();
}

unsigned int TabChildWidget::deleteSelectedButton()
{
	unsigned int res;
	res = selectedButtonId;

	for (unsigned int i = 0; i < buttons.size(); ++i) 	{ 		TabTileButton* tmpButton = buttons[i]; 		if (tmpButton->buttonId == selectedButtonId)
		{
			buttons.erase(buttons.begin() + i);
			tmpButton->close();
		}
	}
	repositionButtons();
	selectedButtonId = 0;

	return res;
}

void TabChildWidget::resizeEvent(QResizeEvent* event)
{
    this->QScrollArea::resizeEvent(event);
	repositionButtons();
}

void TabChildWidget::resetButtons()
{
	for (unsigned int i = 0; i < buttons.size(); ++i) 	{ 		buttons[i]->setChecked(false);
	}
	selectedButtonId = 0;
}

void TabChildWidget::repositionButtons()
{
	int row = 0;
	int col = 0;
	for (unsigned int i = 0; i < buttons.size(); ++i) 	{ 		if (row * buttonSize + buttonSize > this->width())
		{
			row = 0;
			col++;
		}

		TabTileButton* tmpButton = buttons[i];
		tmpButton->setGeometry(row * buttonSize, col * buttonSize, buttonSize, buttonSize);
		row++;
	}

	scrollAreaWidgetContents->resize(this->width(), (col+1) * buttonSize);
}
#pragma once
#include</pre>
<map>#include </map>
<map>#include <QtOpenGL/QGLWidget></map>

<map>class TextureManager</map>
<map>{</map>
<map>public:</map>
<map> TextureManager(void);</map>
<map> ~TextureManager(void);</map>

<map> QGLWidget* glWidget;</map>

<map> void initResource(QGLWidget* gl);</map>

<map> bool addTexture(unsigned int textureId, QString path);</map>
<map> bool removeTexture(unsigned int textureId);</map>
<map> unsigned int getGlTextureId(unsigned int textureId);</map>

<map> std::map textureIndexMap;</map>
<map> std::map glTextureIdMap;</map>
<map>};</map>

<map>
#include "TextureManager.h"

TextureManager::TextureManager(void)
{
}

TextureManager::~TextureManager(void)
{
}

void TextureManager::initResource(QGLWidget* gl)
{
	glWidget = gl;
}

bool TextureManager::addTexture(unsigned int textureId, QString path)
{
	textureIndexMap[textureId] = path;

	QPixmap pixmap = QPixmap(path);
	unsigned int id = glWidget->bindTexture(pixmap);
	glTextureIdMap[path] = id;

	return true;
}
bool TextureManager::removeTexture(unsigned int textureId)
{
	return true;
}
unsigned int TextureManager::getGlTextureId(unsigned int textureId)
{
	return glTextureIdMap[textureIndexMap[textureId]];
}


3dsMax Exporter, Importer

     A plugin for 3dsMax, which is used for extracting the models’ data into my own format. The extracted data can be easily used to draw models in my own engine. The extraction’s logic is based on 3dsMax 2010 data structure, animation included.

     In short, I did it by searching through the 3DSMax nodes, and collecting all materials on each face of the model. Then I extracted the positions of each face and mapped the materials on top of the face. The animation is a bit tricky when you have to keep track of the skeleton for each frame. The main problem about animation is how to record the data to the extracted file because there is a lot of things that don’t move and we don’t want to store redundant data, which can easily make a 5MB file uselessly turn into 300MB.

Genre : Add-on, Tools

Tech used : C++, DirectX, 3dsMax

Keyword : 3dsMax, Tools, Addon, Plugin, Exporter, Animation

Code Sample

#pragma once
#include "utils.h"
#include
#include
#include "maxMesh.h"
#include "maxNode.h"

namespace banknamespace
{
	/**
	 * 3dsmax exporter plugin.
	 */
	class My3dsmaxExporter : public SceneExport
	{
	public:
		My3dsmaxExporter();
		~My3dsmaxExporter();

		TimeValue sceneStart;
		TimeValue sceneEnd;

		fileDesc desc;

		std::vector matArr;

		std::vector maxNodeArr;
		std::vector maxMeshArr;
		std::vector nodeDescArr;
		std::vector meshDescArr;

		void addMaterial(matStruct mat);
		int getTransformId(void* pointer);
		unsigned int addBoneList(weightCompare vert, maxMesh* maxmesh);
		unsigned int addModelVertex(ModelVertex vert, maxMesh* maxmesh);
		unsigned int addSkinVertex(SkinVertex vert, maxMesh* maxmesh);

		void extractMaterial(IGameNode* node);
		void extractFace(IGameNode* node, int materialId, int parentId, GMatrix worldtolocal, maxMesh* maxmesh, std::vector &str);
		void extractSkin(IGameNode* node);
		void recurseNode(IGameNode* node, int level, int parentId, int materialId, std::vector &print);

		/** Exports the scene. */
		int 			DoExport( const TCHAR* name, ExpInterface *ei, Interface* i,
							BOOL suppressprompts=FALSE, DWORD options=0 );

		/** Show DLL's "About..." box */
		void			ShowAbout( HWND hwnd );

		/** Number of extensions supported */
		int				ExtCount();

		/** Extension #n (i.e. "3DS") */
		const TCHAR*	Ext( int n );

		/** Long ASCII description (i.e. "Autodesk 3D Studio File") */
		const TCHAR*	LongDesc();

		/** Short ASCII description (i.e. "3D Studio") */
		const TCHAR*	ShortDesc();

		/** ASCII Author name */
		const TCHAR*	AuthorName();

		/** ASCII Copyright message */
		const TCHAR*	CopyrightMessage();

		/** Other message #1 */
		const TCHAR*	OtherMessage1();

		/** Other message #2 */
		const TCHAR*	OtherMessage2();

		/** Version number * 100 (i.e. v3.01 = 301) */
		unsigned int	Version();

		/** Returns TRUE if option supported. */
		BOOL			SupportsOptions( int ext, DWORD options );

	private:

		IGameScene*		m_igame;
		FILE*			m_fh;

		void			deinit();

		My3dsmaxExporter( const My3dsmaxExporter& );
		My3dsmaxExporter& operator=( const My3dsmaxExporter& );
	};

}

#include "stdafx.h"
#include "My3dsmaxExporter.h"

namespace banknamespace
{
	void Tokenize(const std::string& str,
						std::vector& tokens,
						const std::string& delimiters)
	{
		// Skip delimiters at beginning.
		std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
		// Find first "non-delimiter".
		std::string::size_type pos     = str.find_first_of(delimiters, lastPos);

		while (std::string::npos != pos || std::string::npos != lastPos)
		{
			// Found a token, add it to the vector.
			tokens.push_back(str.substr(lastPos, pos - lastPos));
			// Skip delimiters.  Note the "not_of"
			lastPos = str.find_first_not_of(delimiters, pos);
			// Find next "non-delimiter"
			pos = str.find_first_of(delimiters, lastPos);
		}
	}

	My3dsmaxExporter::My3dsmaxExporter() :
		m_igame( 0 ),
		m_fh( 0 )
	{
	}

	My3dsmaxExporter::~My3dsmaxExporter()
	{
		deinit();
	}

	void My3dsmaxExporter::deinit()
	{
		if ( m_fh != 0 )
		{
			fclose( m_fh );
			m_fh = 0;
		}

		if ( m_igame != 0 )
		{
			m_igame->ReleaseIGame();
			m_igame = 0;
		}
	}

	void My3dsmaxExporter::addMaterial(matStruct mat)
	{
		for each (matStruct tmp in matArr)
		{
			if (tmp.pointer == mat.pointer)
			{
				// duplicate material, not added
				return;
			}
		}
		matArr.push_back(mat);
	}

	int My3dsmaxExporter::getTransformId(void* pointer)
	{
		for (int i = 0; i < maxNodeArr.size(); ++i)
		{
			if (maxNodeArr[i].pointer == pointer)
			{
				return i;
			}
		}
	}

	unsigned int My3dsmaxExporter::addBoneList(weightCompare bone, maxMesh* maxmesh)
	{
		for (unsigned int i = 0; i < maxmesh->boneList.size(); ++i)
		{
			if (maxmesh->boneList[i].boneId == bone.boneId)
			{
				// duplicate material, not added
				return i;
			}
		}
		maxmesh->boneList.push_back(bone);
		return maxmesh->boneList.size()-1;
	}

	unsigned int My3dsmaxExporter::addModelVertex(ModelVertex vert, maxMesh* maxmesh)
	{
		for (unsigned int i = 0; i < maxmesh->vertices.size(); ++i)
		{
			if (maxmesh->vertices[i].position == vert.position && maxmesh->vertices[i].texCoord == vert.texCoord && maxmesh->vertices[i].normal == vert.normal)
			{
				// duplicate material, not added
				return i;
			}
		}
		maxmesh->vertices.push_back(vert);
		return maxmesh->vertices.size()-1;
	}

	unsigned int My3dsmaxExporter::addSkinVertex(SkinVertex vert, maxMesh* maxmesh)
	{
		for (unsigned int i = 0; i < maxmesh->skinvertices.size(); ++i)
		{
			if (maxmesh->skinvertices[i].position == vert.position && maxmesh->skinvertices[i].texCoord == vert.texCoord && maxmesh->skinvertices[i].normal == vert.normal)
			{
				// duplicate material, not added
				return i;
			}
		}
		maxmesh->skinvertices.push_back(vert);
		return maxmesh->skinvertices.size()-1;
	}

	void My3dsmaxExporter::extractMaterial(IGameNode* node)
	{
		if (node->GetIGameObject()->GetIGameType() != IGameObject::IGAME_MESH) return;
		IGameMesh* mesh = (IGameMesh*)(node->GetIGameObject());
		mesh->InitializeData();

		matStruct mat0;
		mat0.pointer = 0;
		mat0.name = "no material";
		addMaterial(mat0);

		int numface = mesh->GetNumberOfFaces();

		for (int i = 0; i < numface; ++i) 		{ 			FaceEx* face = mesh->GetFace( i );
			IGameMaterial* mat = mesh->GetMaterialFromFace(face);

			if (mat == NULL)
			{
				continue;
			}

			matStruct matstr;
			matstr.pointer = (int)mat;

			int texnum = mat->GetNumberOfTextureMaps();
			for (int j = 0; j < texnum; ++j) 			{ 				IGameTextureMap* x = mat->GetIGameTextureMap(j);
				if (x == NULL)
				{
					continue;
				}
				std::string className(mat->GetIGameTextureMap(j)->GetClassName());
				matstr.textureClass.push_back( className );

				char* fname;
				if (className == "Bitmap")
				{
					fname = mat->GetIGameTextureMap(j)->GetBitmapFileName();
				}
				else
				{
					fname = 0;
				}

				std::vector token;
				if ( fname )
				{
					BitmapInfo bi( fname );
					BMMGetFullFilename( &bi );

					std::string tmp(bi.Name());

					Tokenize(tmp, token, "\\");

					//fname = (char*)bi.Name();
					//fname = token[token.size()-1];
					matstr.texturePath.push_back( token[token.size()-1] );
				}
				//matstr.texturePath.push_back( mat->GetIGameTextureMap(j)->GetBitmapFileName() );
				//matstr.texturePath.push_back( fname );

			}
			matstr.name = mat->GetMaterialName();

			addMaterial( matstr );
		}
	}

	void My3dsmaxExporter::extractFace(IGameNode* node, int materialId, int parentId, GMatrix worldtolocal, maxMesh* maxmesh, std::vector &str)
	{
		if (node->GetIGameObject()->GetIGameType() != IGameObject::IGAME_MESH) return;
		IGameMesh* mesh = (IGameMesh*)(node->GetIGameObject());
		mesh->InitializeData();

		IGameSkin* tmpSkin;
		maxmesh->isSkin = false;

		int numface = mesh->GetNumberOfFaces();
		int nummod = mesh->GetNumModifiers();

		// one mesh has one skin mod
		for (int i = 0; i < nummod; ++i) 		{ 			IGameModifier* tmpMod = mesh->GetIGameModifier(i);
			if (tmpMod->IsSkin())
			{
				tmpSkin = reinterpret_cast(tmpMod);
				maxmesh->isSkin = true;
			}
		}

		for (int i = 0; i < numface; ++i) 		{ 			FaceEx* tmpFace = mesh->GetFace(i);
			if (matArr[materialId].pointer == (int)(mesh->GetMaterialFromFace(tmpFace)))
			{
				// extract everything from face with unique material
				for (int j = 0; j < 3; ++j) 				{ 					if (maxmesh->isSkin)
					{
						// extract skin mesh
						int vertIdx = tmpFace->vert[j];
						int numbone = tmpSkin->GetNumberOfBones(vertIdx);

						SkinVertex tmpSkinVert;

						// calc weight
						std::vector tmpweightarr;
						for (int k = 0; k < numbone; ++k) 						{ 							float weight = tmpSkin->GetWeight(vertIdx, k);
							IGameNode* tmpBone = tmpSkin->GetIGameBone(vertIdx, k);

							weightCompare tmpweightcomp;
							tmpweightcomp.weight = weight;
							tmpweightcomp.boneId = getTransformId(tmpBone);
							GMatrix worldTrans = tmpBone->GetWorldTM(0).Inverse();

							// get matrix
							mat16f tmpMat16f;
							for (int row = 0; row < 4; ++row)
							{
								// construct mat16f
								Point4 matrow = worldTrans.GetRow(row);
								tmpMat16f.data[4*row + 0] = matrow.x;
								tmpMat16f.data[4*row + 1] = matrow.y;
								tmpMat16f.data[4*row + 2] = matrow.z;
								tmpMat16f.data[4*row + 3] = matrow.w;
							}
							tmpweightcomp.rotTransBone = tmpMat16f;

							Point3 tmpPoint;
							tmpPoint = worldTrans.Translation();
							tmpweightcomp.posTransBone = vec3f(tmpPoint.x, tmpPoint.y, tmpPoint.z);
							tmpPoint = worldTrans.Scaling();
							tmpweightcomp.scaleTransBone = vec3f(tmpPoint.x, tmpPoint.y, tmpPoint.z);

							tmpweightarr.push_back(tmpweightcomp);
						}
						std::sort( tmpweightarr.begin(), tmpweightarr.end() );

						// add 4 min weight
						float sumWeight = 0;
						for (int l = 0; l < 3; ++l)
						{
							if (l < tmpweightarr.size())
							{
								int index = addBoneList(tmpweightarr[l], maxmesh);
								tmpSkinVert.weights[l] = tmpweightarr[l].weight;
								tmpSkinVert.indices[l] = index;
							}
							else
							{
								tmpSkinVert.weights[l] = 0;
								tmpSkinVert.indices[l] = 0;
							}
							sumWeight += tmpSkinVert.weights[l];
						}

						// add 4 min reblending weight
						for (int l = 0; l < 3; ++l) 						{ 							tmpSkinVert.weights[l] = tmpSkinVert.weights[l]/sumWeight; 						} 						// add vert data 						Point3 tmpVert = mesh->GetVertex( tmpFace->vert[j] );
						Point2 tmpTexCoord = mesh->GetTexVertex( tmpFace->texCoord[j] );
						Point3 tmpNorm = mesh->GetNormal( tmpFace->norm[j] );

						tmpSkinVert.position.x = tmpVert.x;
						tmpSkinVert.position.y = tmpVert.y;
						tmpSkinVert.position.z = tmpVert.z;

						tmpSkinVert.texCoord.x = tmpTexCoord.x;
						tmpSkinVert.texCoord.y = 1-tmpTexCoord.y;

						tmpSkinVert.normal.x = tmpNorm.x;
						tmpSkinVert.normal.y = tmpNorm.y;
						tmpSkinVert.normal.z = tmpNorm.z;

						int index = addSkinVertex(tmpSkinVert, maxmesh);
						maxmesh->skinvertIndices[materialId].push_back(index);
					}
					else
					{
						// extract normal mesh
						ModelVertex tmpModelVert;

						Point3 tmpVert = mesh->GetVertex( tmpFace->vert[j] );
						tmpVert = worldtolocal.ExtractMatrix3() * tmpVert; // change to local

						Point2 tmpTexCoord = mesh->GetTexVertex( tmpFace->texCoord[j] );
						Point3 tmpNorm = mesh->GetNormal( tmpFace->norm[j] );
						Matrix3 noTrans = worldtolocal.ExtractMatrix3();
						noTrans.NoTrans();
						tmpNorm = noTrans * tmpNorm; // change to local

						tmpModelVert.position.x = tmpVert.x;
						tmpModelVert.position.y = tmpVert.y;
						tmpModelVert.position.z = tmpVert.z;

						tmpModelVert.texCoord.x = tmpTexCoord.x;
						tmpModelVert.texCoord.y = 1-tmpTexCoord.y;

						tmpModelVert.normal.x = tmpNorm.x;
						tmpModelVert.normal.y = tmpNorm.y;
						tmpModelVert.normal.z = tmpNorm.z;

						int index = addModelVertex(tmpModelVert, maxmesh);
						maxmesh->vertIndices[materialId].push_back(index);
					}

				}
			}
		}
	}

	void My3dsmaxExporter::extractSkin(IGameNode* node)
	{

	}

	void My3dsmaxExporter::recurseNode(IGameNode* node, int level, int parentId, int materialId, std::vector &print)
	{
		IGameObject* x = node->GetIGameObject();

		if (materialId == -1)
		{
			// extract material first

			// add animation data
			maxNode tmpMaxNode;
			tmpMaxNode.pointer = node;
			tmpMaxNode.parentId = parentId;
			for (int frameCounter = sceneStart; frameCounter < sceneEnd; frameCounter += 30) 			{ 				GMatrix localtrans; 				if (node->GetNodeParent() != 0) // check no parent
				{
					localtrans = node->GetWorldTM(frameCounter) * node->GetNodeParent()->GetWorldTM(frameCounter).Inverse();
				}
				else
				{
					localtrans = node->GetWorldTM(frameCounter);
				}

				// get matrix
				mat16f tmpMat16f;
				for (int row = 0; row < 4; ++row)
				{
					// construct mat16f
					Point4 matrow = localtrans.GetRow(row);
					tmpMat16f.data[4*row + 0] = matrow.x;
					tmpMat16f.data[4*row + 1] = matrow.y;
					tmpMat16f.data[4*row + 2] = matrow.z;
					tmpMat16f.data[4*row + 3] = matrow.w;
				}
				tmpMaxNode.localTMAnim.rot.push_back(tmpMat16f);

				Point3 tmpPoint;
				tmpPoint = localtrans.Translation();
				tmpMaxNode.localTMAnim.pos.push_back( vec3f(tmpPoint.x, tmpPoint.y, tmpPoint.z) );
				tmpPoint = localtrans.Scaling();
				tmpMaxNode.localTMAnim.scale.push_back( vec3f(tmpPoint.x, tmpPoint.y, tmpPoint.z) );
			}
			maxNodeArr.push_back(tmpMaxNode);

			extractMaterial(node);

			int thisNodeId = maxNodeArr.size()-1;
			// recurse through child
			for (int i = 0; i < node->GetChildCount(); ++i)
			{
				IGameNode* tmp = node->GetNodeChild(i);
				recurseNode(tmp, level+1, thisNodeId, materialId, print);
			}
		}
		else
		{
			// extract face detail
			maxMesh tmpMesh;
			tmpMesh.transformationId = getTransformId(node);

			// add vertices data
			GMatrix worldToLocal = node->GetWorldTM().Inverse();
			for (int i = 0; i < matArr.size(); ++i)
			{
				std::vector tmp;
				tmpMesh.vertIndices.push_back(tmp);
				tmpMesh.skinvertIndices.push_back(tmp);
				tmpMesh.matArr.push_back(i);
				if( (IGameMesh*)(node->GetIGameObject()) == 0 ) break;
				extractFace( node, i, parentId, worldToLocal, &tmpMesh, print );
			}
			if( (IGameMesh*)(node->GetIGameObject()) != 0 )
			maxMeshArr.push_back(tmpMesh);

			int thisNodeId = maxMeshArr.size()-1;
			// recurse through child
			for (int i = 0; i < node->GetChildCount(); ++i)
			{
				IGameNode* tmp = node->GetNodeChild(i);
				recurseNode(tmp, level+1, thisNodeId, materialId, print);
			}
		}
	}

	int My3dsmaxExporter::DoExport( const TCHAR* name, ExpInterface* ei, Interface* i, BOOL suppressprompts, DWORD options )
	{
		try
		{
			const bool exportselected = 0 != (options & SCENE_EXPORT_SELECTED); // export only selected objects

			std::ofstream m_fh(name, std::ios::binary);

			if ( !m_fh )
				throw std::exception( "Failed to open file for writing" );

			// initialize IGame
			m_igame = GetIGameInterface();
			if ( !m_igame )
				throw std::exception( "Failed to initialize IGame interface" );

			IGameConversionManager* cm = GetConversionManager();
			cm->SetCoordSystem(IGameConversionManager::IGAME_OGL);
			m_igame->InitialiseIGame( exportselected );

			sceneStart = m_igame->GetSceneStartTime();
			sceneEnd = m_igame->GetSceneEndTime();

			// TODO: export stuff
			///// global variable /////
			std::vector nodename;
			std::vector faceArr;
			///// end of global variable /////

			int topCount = m_igame->GetTopLevelNodeCount();
			// get material first
			for (int i = 0; i < topCount; ++i) 			{ 				IGameNode* tmp = m_igame->GetTopLevelNode(i);

				recurseNode(tmp, 0, -1, -1, nodename);
			}

			// get face details
			for (int i = 0; i < topCount; ++i) 			{ 				{ 					IGameNode* tmp = m_igame->GetTopLevelNode(i);

					recurseNode(tmp, 0, -1, 0, nodename);
				}
			}

			for (int i = 0; i < maxNodeArr.size(); ++i)
			{
				// optimize pos, rot, scal
				maxNodeArr[i].calcMaxNodeRot();
				maxNodeArr[i].optimize();

				nodeDesc tmpdesc;
				tmpdesc.numOfPos = maxNodeArr[i].localTMAnim.pos.size();
				tmpdesc.numOfRot = maxNodeArr[i].localTMAnim.rot.size();
				tmpdesc.numOfScal = maxNodeArr[i].localTMAnim.scale.size();
				nodeDescArr.push_back(tmpdesc);
			}

			for (int i = 0; i < maxMeshArr.size(); ++i)
			{
				meshDesc tmpdesc;

				tmpdesc.numOfMaterial = maxMeshArr[i].matArr.size();
				tmpdesc.numOfVert = maxMeshArr[i].vertices.size();
				tmpdesc.numOfIdxArr = maxMeshArr[i].vertIndices.size();
				tmpdesc.numOfBoneList = maxMeshArr[i].boneList.size();
				tmpdesc.numOfSkinVert = maxMeshArr[i].skinvertices.size();
				tmpdesc.numOfSkinIdxArr = maxMeshArr[i].skinvertIndices.size();
				//tmpdesc.numOfKeyframe = maxMeshArr[i].localTMAnim.pos.size();

				for (int j = 0; j < tmpdesc.numOfIdxArr; ++j)
				{
					tmpdesc.idxArr.push_back( maxMeshArr[i].vertIndices[j].size() );
				}
				for (int j = 0; j < tmpdesc.numOfSkinIdxArr; ++j)
				{
					tmpdesc.skinIdxArr.push_back( maxMeshArr[i].skinvertIndices[j].size() );
				}

				meshDescArr.push_back(tmpdesc);
			}

			///// real write
			// header
			std::string header;
			header.append("version*2.0");
			m_fh.write(header.c_str(), header.size());
			m_fh.write("\n", 1);

			// material
			for (unsigned int i = 0; i < matArr.size(); ++i)
			{
				//if (vertIndices[i].size() != 0)
				{
					m_fh.write("mat*", 4);

					std::string matPath;
					if (matArr[i].texturePath.size() != 0)
					{
						matPath = matArr[i].texturePath[0];
					}
					else
					{
						matPath = "-";
					}

					std::string matName = matArr[i].name;
					if (matArr[i].texturePath.size() != 0)
					{
						matName = matArr[i].name[0];
					}
					else
					{
						matName = "-";
					}

					m_fh.write(matPath.c_str(), matPath.size());
					m_fh.write("*", 1);
					m_fh.write(matName.c_str(), matName.size());
					m_fh.write("\n", 1);
				}

			}

			for (int i = 0; i < maxNodeArr.size(); ++i)
			{
				// write file desc
				std::string numPos = lexical_cast(nodeDescArr[i].numOfPos);
				std::string numRot = lexical_cast(nodeDescArr[i].numOfRot);
				std::string numScal = lexical_cast(nodeDescArr[i].numOfScal);

				m_fh.write("nodedesc*", 9);
				m_fh.write(numPos.c_str(), numPos.size());
				m_fh.write("*", 1);
				m_fh.write(numRot.c_str(), numRot.size());
				m_fh.write("*", 1);
				m_fh.write(numScal.c_str(), numScal.size());
				m_fh.write("\n", 1);

				m_fh.write("maxNode*", 8);
				m_fh.write("\n", 1);

				std::string parentId = lexical_cast(maxNodeArr[i].parentId);
				m_fh.write("parentId*", 9);
				m_fh.write(parentId.c_str(), parentId.size());
				m_fh.write("\n", 1);
				// keyframe container
				//std::string framerate = lexical_cast(maxNodeArr[i].localTMAnim.framesPerSecond);
				std::string framerate = lexical_cast(30);
				m_fh.write("fr*", 3);
				m_fh.write(framerate.c_str(), framerate.size());
				m_fh.write("\n", 1);

				m_fh.write("animPos*", 8);
				m_fh.write(reinterpret_cast(maxNodeArr[i].localTMAnim.pos.data()), maxNodeArr[i].localTMAnim.pos.size() * sizeof(vec3f));
				m_fh.write("\n", 1);

				m_fh.write("animRot*", 8);
				m_fh.write(reinterpret_cast(maxNodeArr[i].localTMAnim.rot.data()), maxNodeArr[i].localTMAnim.rot.size() * sizeof(mat16f));
				m_fh.write("\n", 1);

				m_fh.write("animScal*", 9);
				m_fh.write(reinterpret_cast(maxNodeArr[i].localTMAnim.scale.data()), maxNodeArr[i].localTMAnim.scale.size() * sizeof(vec3f));
				m_fh.write("\n", 1);
			}

			//int offset = 0;
			for (int i = 0; i < maxMeshArr.size(); ++i)
			{
				// write file desc
				std::string numMat = lexical_cast(meshDescArr[i].numOfMaterial);
				std::string numVert = lexical_cast(meshDescArr[i].numOfVert);
				std::string numIdx = lexical_cast(meshDescArr[i].numOfIdxArr);
				std::string numBoneList = lexical_cast(meshDescArr[i].numOfBoneList);
				std::string numSkinVert = lexical_cast(meshDescArr[i].numOfSkinVert);
				std::string numSkinIdx = lexical_cast(meshDescArr[i].numOfSkinIdxArr);

				m_fh.write("meshdesc*", 9);
				m_fh.write(numMat.c_str(), numMat.size());
				m_fh.write("*", 1);
				m_fh.write(numVert.c_str(), numVert.size());
				m_fh.write("*", 1);
				m_fh.write(numIdx.c_str(), numIdx.size());
				m_fh.write("*", 1);
				m_fh.write(numBoneList.c_str(), numBoneList.size());
				m_fh.write("*", 1);
				m_fh.write(numSkinVert.c_str(), numSkinVert.size());
				m_fh.write("*", 1);
				m_fh.write(numSkinIdx.c_str(), numSkinIdx.size());
				m_fh.write("*", 1);
				m_fh.write("\n", 1);

				m_fh.write("idxArr*", 7);
				for (int j = 0; j < meshDescArr[i].numOfIdxArr; ++j)
				{
					std::string idx = lexical_cast(meshDescArr[i].idxArr[j]);
					m_fh.write(idx.c_str(), idx.size());

					if (j == meshDescArr[i].numOfIdxArr-1)
					{
						break;
					}

					m_fh.write("*", 1);
				}
				m_fh.write("\n", 1);

				m_fh.write("SkinIdxArr*", 11);
				for (int j = 0; j < meshDescArr[i].numOfSkinIdxArr; ++j)
				{
					std::string idx = lexical_cast(meshDescArr[i].skinIdxArr[j]);
					m_fh.write(idx.c_str(), idx.size());

					if (j == meshDescArr[i].numOfSkinIdxArr-1)
					{
						break;
					}

					m_fh.write("*", 1);
				}
				m_fh.write("\n", 1);

				// end file desc

				m_fh.write("maxMesh*", 8);
				m_fh.write("\n", 1);

				std::string isskin = lexical_cast(maxMeshArr[i].isSkin ? 1 : 0);
				m_fh.write("isSkin*", 7);
				m_fh.write(isskin.c_str(), isskin.size());
				m_fh.write("\n", 1);

				std::string parent = lexical_cast(maxMeshArr[i].transformationId);
				m_fh.write("transformId*", 12);
				m_fh.write(parent.c_str(), parent.size());
				m_fh.write("\n", 1);

				int matArrSize = maxMeshArr[i].matArr.size() * sizeof(unsigned int);
				m_fh.write("matArr*", 7);
				m_fh.write(reinterpret_cast(maxMeshArr[i].matArr.data()), matArrSize);
				m_fh.write("\n", 1);

				int verticesSize = maxMeshArr[i].vertices.size() * sizeof(ModelVertex);
				m_fh.write("vertArr*", 8);
				m_fh.write(reinterpret_cast(maxMeshArr[i].vertices.data()), verticesSize);
				m_fh.write("\n", 1);
				for (int j = 0; j < maxMeshArr[i].vertIndices.size(); ++j)
				{
					m_fh.write("vertIdxArr*", 11);
					m_fh.write(reinterpret_cast(maxMeshArr[i].vertIndices[j].data()), maxMeshArr[i].vertIndices[j].size() * sizeof(unsigned int));
					m_fh.write("\n", 1);
				}

				int boneSize = maxMeshArr[i].boneList.size() * sizeof(weightCompare);
				m_fh.write("boneList*", 9);
				m_fh.write(reinterpret_cast(maxMeshArr[i].boneList.data()), boneSize);
				m_fh.write("\n", 1);

				int skinVerticesSize = maxMeshArr[i].skinvertices.size() * sizeof(SkinVertex);
				m_fh.write("skinVertArr*", 12);
				m_fh.write(reinterpret_cast(maxMeshArr[i].skinvertices.data()), skinVerticesSize);
				m_fh.write("\n", 1);
				for (int j = 0; j < maxMeshArr[i].skinvertIndices.size(); ++j)
				{
					m_fh.write("skinVertIdxArr*", 15);
					m_fh.write(reinterpret_cast(maxMeshArr[i].skinvertIndices[j].data()), maxMeshArr[i].skinvertIndices[j].size() * sizeof(unsigned int));
					m_fh.write("\n", 1);
				}
			}

			// release initialized stuff
			deinit();
		}
		catch ( std::exception& e )
		{
			TSTR tstr( e.what() );
			MessageBox( i->GetMAXHWnd(), tstr, _T("Export Error"), MB_OK|MB_ICONERROR );
			deinit();
		}
		return TRUE;
	}

	void My3dsmaxExporter::ShowAbout( HWND hwnd )
	{
	}

	int My3dsmaxExporter::ExtCount()
	{
		return 1;
	}

	const TCHAR* My3dsmaxExporter::Ext( int /*n*/ )
	{
		return _T("my3d");
	}

	const TCHAR* My3dsmaxExporter::LongDesc()
	{
		return _T("My 3dsmax Exporter");
	}

	const TCHAR* My3dsmaxExporter::ShortDesc()
	{
		return _T("My3dsmaxExporter");
	}

	const TCHAR* My3dsmaxExporter::AuthorName()
	{
		return _T("");
	}

	const TCHAR* My3dsmaxExporter::CopyrightMessage()
	{
		return _T("Copyright (C)");
	}

	const TCHAR* My3dsmaxExporter::OtherMessage1()
	{
		return _T("");
	}

	const TCHAR* My3dsmaxExporter::OtherMessage2()
	{
		return _T("");
	}

	unsigned int My3dsmaxExporter::Version()
	{
		return 1;
	}

	BOOL My3dsmaxExporter::SupportsOptions( int ext, DWORD options )
	{
		return TRUE;
	}

}


Spy vs Spy AI

     A class project involving A-Star (for logic decision) and Path finding reminded me of an old game called Spy vs Spy I played a long time ago. - http://www.youtube.com/watch?v=LJC7DIP5JIw). So I decided to try making something fun based on it. I also ended up adding more types of traps in the game.

     A-Star is an algorithm widely used in path finding. It is the process of plotting an efficiently traversable path between points, called nodes. Noted for its performance and accuracy, it enjoys widespread use. A-Star uses a best-first search, which is determined by the actual cost together with the estimation cost. We can define how to calculate the estimation cost by ourselves and make the AI behave as we want. The same line of thought is applied to GOAP (Goal-Oriented Action Planning), which uses the same logic – but instead of finding paths to a destination, it finds actions to achieve the objective.

     With A-Star and GOAP combined, I can make an AI which can decide what it needs to do and find the “way” to reach its destination in the level. Other fun byproducts are how the AI can predict its enemies’ path and then lay the appropriate traps for each situation.

Genre : AI

Tech used : C++, DirectX

Keyword : AStar, A*, Pathfinding, AI

Code Sample

#pragma once
#include
#include
#include
#include

#include "aNode.h"

using namespace std;

class aStar
{
public:
	aStar(void);
	~aStar(void);

	std::string result;

	void init(std::string filename);
	void reCalc(vec2f& startLoc, vec2f& destLoc);
	void calcPath(int step);
	void calcResult();
	string getResult();
	void selectedPath(aNode* node);
	void clearSelectedNode();

	void addNeighbor(aNode* node, aNode* parent);

	///// aStar Variable
	vector selectedNode;

	vector<vector> row;
	vector openList;
	aNode* endPoint;
	int current;
};

#include "StdAfx.h"
#include "aStar.h"

#ifdef _DEBUG

#define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
#define new DEBUG_NEW

#endif

aStar::aStar(void)
{
}

aStar::~aStar(void)
{
	selectedNode.clear();
	openList.clear();
	row.clear();
}

void aStar::init(std::string filename)
{
	// read map file
	string line;
	ifstream myfile (filename);

	if (myfile.is_open())
	{
		while ( myfile.good() )
		{
			getline (myfile,line);

			if (line.length() == 0) continue;
			///// init something here
			vector tmpRow;
			for (unsigned int i = 0; i < line.length(); ++i) 			{ 				aNode tmpNode;// = new aNode(); 				tmpNode.status = S_NEUTRAL_NODE; 				char tmp = line.at(i); 				if (tmp == '.') 				{ 					tmpNode.gridType = G_NORMAL; 				} 				else if (tmp == 'S') 				{ 					tmpNode.gridType = G_START; 				} 				else if (tmp == 'E') 				{ 					tmpNode.gridType = G_EXIT; 				} 				else if (tmp == '#') 				{ 					tmpNode.gridType = G_BLOCKER; 				} 				else if (tmp == '/') 				{ 					tmpNode.gridType = G_SEMIBLOCKER; 				} 				else if (tmp == '1') 				{ 					tmpNode.gridType = G_SLOWPICKUP; 				} 				else if (tmp == '2') 				{ 					tmpNode.gridType = G_STUNPICKUP; 				} 				else if (tmp == '3') 				{ 					tmpNode.gridType = G_BLOCKPICKUP; 				} 				else if (tmp == 'C') 				{ 					tmpNode.gridType = G_CHOKEPOINT; 				} 				else if (tmp == 'M') 				{ 					tmpNode.gridType = G_MONEY; 				} 				else if (tmp == 'G') 				{ 					tmpNode.gridType = G_GENSTORE; 				} 				else 				{ 					// invalid token 					tmpNode.gridType = G_INVALID; 				} 				tmpNode.location = vec2f((float)i, (float)(row.size())); 				tmpRow.push_back(tmpNode); 			} 			row.push_back(tmpRow); 		} 		myfile.close(); 	} 	else result.append("Unable to open file"); } void aStar::addNeighbor(aNode* node, aNode* parent) { 	if (node->status == S_NEUTRAL_NODE && node->gridType != G_BLOCKER)
	{
		node->parent = parent;
		node->status = S_OPEN_NODE;
		openList.push_back(node);
	}
}

void aStar::selectedPath(aNode* node)
{
	selectedNode.push_back(node);
	node->status = S_SELECTED_NODE;
	if (node->parent != 0)
	{
		selectedPath(node->parent);
	}
}

void aStar::clearSelectedNode()
{
	for (unsigned int i = 0; i < selectedNode.size(); ++i) 	{ 		selectedNode[i]->status = S_NEUTRAL_NODE;
	}
	selectedNode.clear();
}

void aStar::reCalc(vec2f& startLoc, vec2f& destLoc)
{
	for (unsigned int i = 0; i < row.size(); ++i)
	{
		for (unsigned int j = 0; j < row[i].size(); ++j)
		{
			row[i][j].parent = 0;
			row[i][j].status = S_NEUTRAL_NODE;
		}
	}
	selectedNode.clear();
	openList.clear();

	row[startLoc.y][startLoc.x].status = S_OPEN_NODE;
	openList.push_back(&row[startLoc.y][startLoc.x]);

	endPoint = &row[destLoc.y][destLoc.x];
}

void aStar::calcPath(int step)
{
	float min = 99999;
	current = 0;

	int round = 0;
	while (openList[current] != endPoint && (round location;
		if (location.y-1 >= 0) addNeighbor(&row[location.y-1][location.x], openList[current]);
		if (location.y+1 < row.size()) addNeighbor(&row[location.y+1][location.x], openList[current]); 		if (location.x-1 >= 0) addNeighbor(&row[location.y][location.x-1], openList[current]);
		if (location.x+1 < row[location.y].size()) addNeighbor(&row[location.y][location.x+1], openList[current]); 		openList[current]->status = S_CLOSED_NODE;
		openList.erase(openList.begin()+current);

		// find min
		for (unsigned int i = 0; i < openList.size(); ++i) 		{ 			openList[i]->calcG();
			openList[i]->calcDest( endPoint->location );

			float sumGH = openList[i]->G + openList[i]->H;
			if (sumGH < min)
			{
				current = i;
				min = sumGH;
			}
		}
		// got best current
		round++;
	}

	// pin down selected path
	if (openList[current] == endPoint)
	selectedPath(openList[current]);
	reverse(selectedNode.begin(), selectedNode.end());
}

void aStar::calcResult()
{
	result.clear();
	for (unsigned int i = 0; i < row.size(); ++i)
	{
		for (unsigned int j = 0; j < row[i].size(); ++j) 		{ 			aNode* tmp = &row[i][j]; 			string tmpStr; 			if (tmp->gridType == G_BLOCKER)
			{
				tmpStr = "#";
			}
			else if (tmp->status == S_OPEN_NODE)
			{
				tmpStr = "O";
			}
			else if (tmp->status == S_CLOSED_NODE)
			{
				tmpStr = "X";
			}
			else if (tmp->gridType == G_SEMIBLOCKER)
			{
				tmpStr = "/";
			}
			else if (tmp->gridType == G_NORMAL)
			{
				tmpStr = ".";
			}

			if (tmp->status == S_SELECTED_NODE) tmpStr = "P";
			if (tmp == openList[current]) tmpStr = "H";
			result.append(tmpStr);
		}
		result.append("\n");
	}
}

string aStar::getResult()
{
	calcResult();
	return result;
}


#pragma once
#include <string>
#include <fstream>
#include <iostream>
#include <vector>
#include <map>

#include "goapState.h"

using namespace std;

struct goapMapStruct
{
	std::vector<int> dimension;
};

struct compareStruct
{
	bool operator()(const goapMapStruct& a, const goapMapStruct& b) const
	{
		int tmpa = -1;
		int tmpb = -1;
		bool mina = true;
		bool minb = true;
		for (unsigned int i = 0; i < a.dimension.size(); ++i)
		{
			if (a.dimension[i] < b.dimension[i] && mina)
			{
				tmpa = i;
				mina = false;
			}
			if (b.dimension[i] < a.dimension[i] && minb)
			{
				tmpb = i;
				minb = false;
			}
		}
		return tmpa < tmpb;
	}
};

class goapStar
{
public:
	goapStar(void);
	~goapStar(void);

	std::string result;
	vector<int> actionChain;

	void init(goapState start, goapState end);
	void calcPath(int step);
	void calcResult();
	string getResult();

	void addAction(goapState s, std::string name);
	void preCondition();
	void addOpenList(goapState* childState, goapState* parentState, int actionIdx);
	void selectedPath(goapState* state);

	///// goapStar Variable
	vector<goapState*> selectedState;

	vector<std::string> actionName;
	vector<goapState> actionList;

	//vector<vector<goapNode*>> row;
	map<goapMapStruct, goapState, compareStruct> globalState;
	vector<goapState*> openList;
	goapState startPoint;
	goapState endPoint;
	int current;
};

#include "StdAfx.h"
#include "goapStar.h"

#ifdef _DEBUG

#define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
#define new DEBUG_NEW

#endif

void makeGoapMapStruct(goapState* a, goapMapStruct &b)
{
	b.dimension = a->dimension;
}

goapStar::goapStar(void)
{
}

goapStar::~goapStar(void)
{

}

void goapStar::init(goapState start, goapState end)
{
	selectedState.clear();
	actionChain.clear();
	openList.clear();
	globalState.clear();
	startPoint = start;
	openList.push_back(&startPoint);
	endPoint = end;
}

void goapStar::addOpenList(goapState* childState, goapState* parentState, int actionIdx)
{
	if (childState == 0) return;
	goapMapStruct tmp;
	makeGoapMapStruct(childState, tmp);

	if (globalState.find(tmp) != globalState.end())
	{
		// found
		childState = &globalState[tmp];
		if (globalState[tmp].status != S_CLOSED_NODE)
		{
			if (globalState[tmp].status == S_OPEN_NODE)
			{
				// re-parent
				goapState* tmpParent = globalState[tmp].parent;
				float tmpG = globalState[tmp].G;
				globalState[tmp].parent = parentState;
				globalState[tmp].calcG();
				if (tmpG > globalState[tmp].G)
				{
					// turn back to old parent
					globalState[tmp].parent = tmpParent;
				}
				else
				{
					// use new parent
					globalState[tmp].parentActionIdx = actionIdx;
				}
			}
			else
			{
				// normally add
				childState->parent = parentState;
				childState->parentActionIdx = actionIdx;
				childState->status = S_OPEN_NODE;
				openList.push_back(childState);
			}
		}
	}
	else
	{
		// not found
		globalState[tmp] = *childState;
		globalState[tmp].parent = parentState;
		globalState[tmp].parentActionIdx = actionIdx;
		globalState[tmp].status = S_OPEN_NODE;
		openList.push_back(&globalState[tmp]);
	}

}

void goapStar::addAction(goapState s, std::string name)
{
	actionList.push_back(s);
	actionName.push_back(name);
}

void goapStar::preCondition()
{
	goapState* state = openList[current];
	for (unsigned int i = 0; i < actionList.size(); ++i) 	{ 		goapState newState; 		bool canAction = openList[current]->takeAction(actionList[i], newState);
		if (canAction)
		{
			addOpenList(&newState, openList[current], i);
		}

	}
}

void goapStar::selectedPath(goapState* state)
{
	selectedState.push_back(state);
	if (state->parent != 0)
	{
		selectedPath(state->parent);
	}
}

bool chkEndState(goapState* a, goapState* end)
{
	bool eq = true;
	for (unsigned int i = 0; i < end->dimensionIdx.size(); ++i)
	{
		if (end->dimension[end->dimensionIdx[i]] != a->dimension[end->dimensionIdx[i]])
		{
			eq = false;
		}
	}

	return eq;
}

void goapStar::calcPath(int step)
{
	float min = 99999;
	current = 0;

	int round = 0;
	while (!chkEndState(openList[current], &endPoint) && (round status = S_CLOSED_NODE;
		openList.erase(openList.begin()+current);

		// find min
		for (unsigned int i = 0; i < openList.size(); ++i) 		{ 			openList[i]->calcG();
			openList[i]->calcDest( &endPoint );

			float sumGH = openList[i]->G + openList[i]->H;
			if (sumGH < min) 			{ 				current = i; 				min = sumGH; 			} 		} 		// got best current 		round++; 		int x = 0; 		if (openList.size() == 0) break; 	} 	// pin down selected path 	if (openList.size() != 0) 	{ 		if (chkEndState(openList[current], &endPoint)) 		{ 			selectedPath(openList[current]); 		} 	} } void goapStar::calcResult() { 	result.clear(); 	result.append("START->");
	if (selectedState.size() == 0)
	{
		result.append("HARAKIRI");
		return;
	}
	for (int i = (int)selectedState.size()-2; i >= 0; --i)
	{
		int idx = selectedState[i]->parentActionIdx;
		if (idx != -1)
		{
			result.append(actionName[idx]);
			actionChain.push_back(idx);
		}
		else
		{
			result.append("UNKNOWN_ACTION");
		}
		if (i == 0) break;
		result.append("->");
	}
}

string goapStar::getResult()
{
	calcResult();
	return result;
}

#pragma once
#include
#include "goapUtils.h"

///// start goapClass
//enum status_type
//{
//	S_OPEN_NODE,
//	S_CLOSED_NODE,
//	S_SELECTED_NODE,
//	S_NEUTRAL_NODE
//};

class goapState
{
public:
	goapState();
	goapState(int numOfVariable);
	~goapState(void);

	goapState* parent;
	int parentActionIdx;
	std::vector dimension;
	std::vector dimensionIdx;

	status_type status;
	float G;
	float H;

	void calcG();
	void calcDest(goapState* destination);

	void addVariable(int val, int idx)
	{
		dimension[idx] = val;
		dimensionIdx.push_back(idx);
	}

	bool takeAction(goapState& action, goapState& out)
	{
		bool canAction = true;
		for (unsigned int i = 0; i < action.dimensionIdx.size(); ++i) 		{ 			if (this->dimension[action.dimensionIdx[i]] + action.dimension[action.dimensionIdx[i]] >= 0)
			{
				//newState->dimension[action.dimensionIdx[i]] += action.dimension[action.dimensionIdx[i]];
			}
			else
			{
				canAction = false;
				break;
			}
		}

		if (canAction)
		{
			//goapState* newState = new goapState(*this);
			goapState newState = *this;
			newState.status = S_NEUTRAL_NODE;
			for (unsigned int i = 0; i < action.dimensionIdx.size(); ++i)
			{
				newState.dimension[action.dimensionIdx[i]] += action.dimension[action.dimensionIdx[i]];
			}

			//return newState;
			out = newState;
			return true;
		}
		else return false;
	}

};

#include "StdAfx.h"
#include "goapState.h"

#ifdef _DEBUG

#define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
#define new DEBUG_NEW

#endif

goapState::goapState(int numOfVariable)
{
	parent = 0;
	parentActionIdx = -1;
	G = 0;

	for (int i = 0; i < numOfVariable; ++i) 	{ 		dimension.push_back(0); 	} } goapState::goapState(void) { 	parent = 0; 	parentActionIdx = -1; 	G = 0; } goapState::~goapState(void) { } void goapState::calcG() { 	if (parent != 0) 	{ 		float obs = 1; 		 		G = parent->G + obs;
	}
	else
	{
		G = 0;
	}
}

void goapState::calcDest(goapState* destination)
{
	H = 0;
	for (unsigned int i = 0; i < destination->dimensionIdx.size(); ++i)
	{
		H += abs(dimension[destination->dimensionIdx[i]] - destination->dimension[destination->dimensionIdx[i]]);
	}
}


Mini C++ Compiler

     A compiler made in C++ consists of Lexical analyzer, Parser, Byte-code generator and Virtual machine. My compiler allows the user to see the results of each step easily.

     Each of these parts gives me interesting challenges. For me, the hardest part is obviously lexical analyzer. Not only do I need to know the structure for the whole language, I also need to know how to make other steps after that work too. For example, if I planned it wrong in lexical analyzer, my parser will be bad and generating the byte code from the parser is almost impossible (or ridiculously bad). Another hard part is the Virtual machine. Not because the VM is hard, but trying to connect this compiler with the real engine is hard because it has to connect to other applications by function pointers.

Genre : Compiler

Tech used : C++

Keyword : Compiler, C++, Virtual Machine

Code Sample

give(x)
{
	return x+7;
}

perm (arr, k, m)
{
	if (k == m)
	{
		printArr(arr, m+1);
		print "";
	}
	else
	{
		var i = k;
		while (i < m+1)
		{
			swap(arr, k, i);
			perm(arr, k+1, m);
			swap(arr, k, i);
			i = i+1;
		}
	}
}
swap (arr, a, b)
{
	var temp = arr[a];
	arr[a] = arr[b];
	arr[b] = temp;
}

main()
{
	var floattest = 44.55;
	print "testFloat";
	print floattest;

	print "factorial 7";
	print fac(give(0));

	print "myFactorial(7)";
	print myFac(give(0));

	print "testwhile";
	print testWhile();

	var sortarr = [1, 12, 5, 26, 88, 14, 3, 7, 2];
	print "==printArr Before sort==";
	printArr(sortarr, 9);

	quickSort(sortarr, 0, give(1));

	print "==printArr After sort==";
	printArr(sortarr, 9);

	print "==printArr Permutation==";
	var v = [1,"str 2",3,"str4"];
	perm(v, 0, 3);
}

printArr(arr, arrsize)
{
	var i = 0;
	while (i < arrsize)
	{
		print arr[i];
		i = i+1;
	}
}

testWhile()
{
	var n = 0;
	var total = 0;
	while(n < 6)
	{
		total = total + n;
		n = n+1;
	}
	return total;
}

badRecursion( val )
{
	if ( val < 5500000 )
	return badRecursion( val + 1 );
	else
	return val / 1000000;
}

myFac(n)
{
	if (n > 1)
	{
		return myFac(n-1)*n;
	}
	else
	{
		return 1;
	}
}

fac(n) { if(n==0) return 1; else return n*fac(n-1); }

quickSort(arr, left, right)
{
      var i = left; var j = right;
      var tmp;
      var pivot = arr[(left + right) / 2];

      while (i < j+1) {
            while (arr[i] < pivot)
                  i = i+1;
            while (arr[j] > pivot)
                  j = j-1;
            if (i < j+1) {
                  tmp = arr[i];
                  arr[i] = arr[j];
                  arr[j] = tmp;
                  i = i+1;
                  j = j-1;
            }
      }

      if (left < j)
            quickSort(arr, left, j);
      if (i < right)
            quickSort(arr, i, right);
}
=====PRINT TOKEN TREE=====
0:INVALID_TOKEN
	1:IDEN#give#
		2:IDEN#x#
		2:{
			3:RET
				4:+
					5:IDEN#x#
					5:INT#7#
			3:}
	1:IDEN#perm#
		2:IDEN#arr#
		2:IDEN#k#
		2:IDEN#m#
		2:{
			3:IF
				4:==
					5:IDEN#k#
					5:IDEN#m#
				4:{
					5:IDEN#printArr#
						6:(
						6:IDEN#arr#
						6:+
							7:IDEN#m#
							7:INT#1#
						6:)
					5:PRINT
						6:STRING##
					5:}
				4:{
					5:VAR
						6:=
							7:IDEN#i#
							7:IDEN#k#
					5:WHILE
						6:<
							7:IDEN#i#
							7:+
								8:IDEN#m#
								8:INT#1#
						6:{
							7:IDEN#swap#
								8:(
								8:IDEN#arr#
								8:IDEN#k#
								8:IDEN#i#
								8:)
							7:IDEN#perm#
								8:(
								8:IDEN#arr#
								8:+
									9:IDEN#k#
									9:INT#1#
								8:IDEN#m#
								8:)
							7:IDEN#swap#
								8:(
								8:IDEN#arr#
								8:IDEN#k#
								8:IDEN#i#
								8:)
							7:=
								8:IDEN#i#
								8:+
									9:IDEN#i#
									9:INT#1#
							7:}
					5:}
			3:}
	1:IDEN#swap#
		2:IDEN#arr#
		2:IDEN#a#
		2:IDEN#b#
		2:{
			3:VAR
				4:=
					5:IDEN#temp#
					5:IDEN#arr#
						6:[
						6:IDEN#a#
						6:]
			3:=
				4:IDEN#arr#
					5:[
					5:IDEN#a#
					5:]
				4:IDEN#arr#
					5:[
					5:IDEN#b#
					5:]
			3:=
				4:IDEN#arr#
					5:[
					5:IDEN#b#
					5:]
				4:IDEN#temp#
			3:}
	1:IDEN#main#
		2:{
			3:VAR
				4:=
					5:IDEN#floattest#
					5:INT#44.55#
			3:PRINT
				4:STRING#testFloat#
			3:PRINT
				4:IDEN#floattest#
			3:PRINT
				4:STRING#factorial 7#
			3:PRINT
				4:IDEN#fac#
					5:(
					5:IDEN#give#
						6:(
						6:INT#0#
						6:)
					5:)
			3:PRINT
				4:STRING#myFactorial(7)#
			3:PRINT
				4:IDEN#myFac#
					5:(
					5:IDEN#give#
						6:(
						6:INT#0#
						6:)
					5:)
			3:PRINT
				4:STRING#testwhile#
			3:PRINT
				4:IDEN#testWhile#
					5:(
					5:)
			3:VAR
				4:=
					5:IDEN#sortarr#
					5:[
						6:INT#1#
						6:INT#12#
						6:INT#5#
						6:INT#26#
						6:INT#88#
						6:INT#14#
						6:INT#3#
						6:INT#7#
						6:INT#2#
						6:]
			3:PRINT
				4:STRING#==printArr Before sort==#
			3:IDEN#printArr#
				4:(
				4:IDEN#sortarr#
				4:INT#9#
				4:)
			3:IDEN#quickSort#
				4:(
				4:IDEN#sortarr#
				4:INT#0#
				4:IDEN#give#
					5:(
					5:INT#1#
					5:)
				4:)
			3:PRINT
				4:STRING#==printArr After sort==#
			3:IDEN#printArr#
				4:(
				4:IDEN#sortarr#
				4:INT#9#
				4:)
			3:PRINT
				4:STRING#==printArr Permutation==#
			3:VAR
				4:=
					5:IDEN#v#
					5:[
						6:INT#1#
						6:STRING#str 2#
						6:INT#3#
						6:STRING#str4#
						6:]
			3:IDEN#perm#
				4:(
				4:IDEN#v#
				4:INT#0#
				4:INT#3#
				4:)
			3:}
	1:IDEN#printArr#
		2:IDEN#arr#
		2:IDEN#arrsize#
		2:{
			3:VAR
				4:=
					5:IDEN#i#
					5:INT#0#
			3:WHILE
				4:<
					5:IDEN#i#
					5:IDEN#arrsize#
				4:{
					5:PRINT
						6:IDEN#arr#
							7:[
							7:IDEN#i#
							7:]
					5:=
						6:IDEN#i#
						6:+
							7:IDEN#i#
							7:INT#1#
					5:}
			3:}
	1:IDEN#testWhile#
		2:{
			3:VAR
				4:=
					5:IDEN#n#
					5:INT#0#
			3:VAR
				4:=
					5:IDEN#total#
					5:INT#0#
			3:WHILE
				4:<
					5:IDEN#n#
					5:INT#6#
				4:{
					5:=
						6:IDEN#total#
						6:+
							7:IDEN#total#
							7:IDEN#n#
					5:=
						6:IDEN#n#
						6:+
							7:IDEN#n#
							7:INT#1#
					5:}
			3:RET
				4:IDEN#total#
			3:}
	1:IDEN#badRecursion#
		2:IDEN#val#
		2:{
			3:IF
				4:<
					5:IDEN#val#
					5:INT#5.5e+006#
				4:RET
					5:IDEN#badRecursion#
						6:(
						6:+
							7:IDEN#val#
							7:INT#1#
						6:)
				4:RET
					5:/
						6:IDEN#val#
						6:INT#1e+006#
			3:}
	1:IDEN#myFac#
		2:IDEN#n#
		2:{
			3:IF
				4:>
					5:IDEN#n#
					5:INT#1#
				4:{
					5:RET
						6:*
							7:IDEN#myFac#
								8:(
								8:-
									9:IDEN#n#
									9:INT#1#
								8:)
							7:IDEN#n#
					5:}
				4:{
					5:RET
						6:INT#1#
					5:}
			3:}
	1:IDEN#fac#
		2:IDEN#n#
		2:{
			3:IF
				4:==
					5:IDEN#n#
					5:INT#0#
				4:RET
					5:INT#1#
				4:RET
					5:*
						6:IDEN#n#
						6:IDEN#fac#
							7:(
							7:-
								8:IDEN#n#
								8:INT#1#
							7:)
			3:}
	1:IDEN#quickSort#
		2:IDEN#arr#
		2:IDEN#left#
		2:IDEN#right#
		2:{
			3:VAR
				4:=
					5:IDEN#i#
					5:IDEN#left#
			3:VAR
				4:=
					5:IDEN#j#
					5:IDEN#right#
			3:VAR
				4:IDEN#tmp#
			3:VAR
				4:=
					5:IDEN#pivot#
					5:IDEN#arr#
						6:[
						6:/
							7:+
								8:IDEN#left#
								8:IDEN#right#
							7:INT#2#
						6:]
			3:WHILE
				4:<
					5:IDEN#i#
					5:+
						6:IDEN#j#
						6:INT#1#
				4:{
					5:WHILE
						6:<
							7:IDEN#arr#
								8:[
								8:IDEN#i#
								8:]
							7:IDEN#pivot#
						6:=
							7:IDEN#i#
							7:+
								8:IDEN#i#
								8:INT#1#
					5:WHILE
						6:>
							7:IDEN#arr#
								8:[
								8:IDEN#j#
								8:]
							7:IDEN#pivot#
						6:=
							7:IDEN#j#
							7:-
								8:IDEN#j#
								8:INT#1#
					5:IF
						6:<
							7:IDEN#i#
							7:+
								8:IDEN#j#
								8:INT#1#
						6:{
							7:=
								8:IDEN#tmp#
								8:IDEN#arr#
									9:[
									9:IDEN#i#
									9:]
							7:=
								8:IDEN#arr#
									9:[
									9:IDEN#i#
									9:]
								8:IDEN#arr#
									9:[
									9:IDEN#j#
									9:]
							7:=
								8:IDEN#arr#
									9:[
									9:IDEN#j#
									9:]
								8:IDEN#tmp#
							7:=
								8:IDEN#i#
								8:+
									9:IDEN#i#
									9:INT#1#
							7:=
								8:IDEN#j#
								8:-
									9:IDEN#j#
									9:INT#1#
							7:}
					5:}
			3:IF
				4:<
					5:IDEN#left#
					5:IDEN#j#
				4:IDEN#quickSort#
					5:(
					5:IDEN#arr#
					5:IDEN#left#
					5:IDEN#j#
					5:)
			3:IF
				4:<
					5:IDEN#i#
					5:IDEN#right#
				4:IDEN#quickSort#
					5:(
					5:IDEN#arr#
					5:IDEN#i#
					5:IDEN#right#
					5:)
			3:}

=====PRINT BYTECODE=====
0: CALLFUNC 285 3
9: PUSHVAR -2
14: PUSHINT 7
19: ADD
20: RETURN 1
25: PUSHINT 0
30: RETURN 1
35: PUSHVAR -3
40: PUSHVAR -2
45: EQUAL
46: JUMPCON 84
51: PUSHVAR -4
56: PUSHVAR -2
61: PUSHINT 1
66: ADD
67: CALLFUNC 687 1
76: PUSHSTR
78: PRINT
79: JUMP 215
84: PUSHVAR -3
89: ASSIGN 1
94: PUSHVAR 1
99: PUSHVAR -2
104: PUSHINT 1
109: ADD
110: LESS
111: JUMPCON 215
116: PUSHVAR -4
121: PUSHVAR -3
126: PUSHVAR 1
131: CALLFUNC 225 1
140: PUSHVAR -4
145: PUSHVAR -3
150: PUSHINT 1
155: ADD
156: PUSHVAR -2
161: CALLFUNC 35 1
170: PUSHVAR -4
175: PUSHVAR -3
180: PUSHVAR 1
185: CALLFUNC 225 1
194: PUSHVAR 1
199: PUSHINT 1
204: ADD
205: ASSIGN 1
210: JUMP 94
215: PUSHINT 0
220: RETURN 3
225: PUSHVAR -3
230: PUSHVARIDX -4
235: ASSIGN 1
240: PUSHVAR -2
245: PUSHVARIDX -4
250: PUSHVAR -3
255: ASSIGNIDX -4
260: PUSHVAR 1
265: PUSHVAR -2
270: ASSIGNIDX -4
275: PUSHINT 0
280: RETURN 3
285: PUSHINT 44.55
290: ASSIGN 1
295: PUSHSTR testFloat
306: PRINT
307: PUSHVAR 1
312: PRINT
313: PUSHSTR factorial 7
326: PRINT
327: PUSHINT 0
332: CALLFUNC 9 0
341: CALLFUNC 992 0
350: PRINT
351: PUSHSTR myFactorial(7)
367: PRINT
368: PUSHINT 0
373: CALLFUNC 9 0
382: CALLFUNC 920 0
391: PRINT
392: PUSHSTR testwhile
403: PRINT
404: CALLFUNC 755 2
413: PRINT
414: PUSHINT 1
419: PUSHINT 12
424: PUSHINT 5
429: PUSHINT 26
434: PUSHINT 88
439: PUSHINT 14
444: PUSHINT 3
449: PUSHINT 7
454: PUSHINT 2
459: NEWVEC 9
464: ASSIGN 2
469: PUSHSTR ==printArr Before sort==
495: PRINT
496: PUSHVAR 2
501: PUSHINT 9
506: CALLFUNC 687 1
515: PUSHVAR 2
520: PUSHINT 0
525: PUSHINT 1
530: CALLFUNC 9 0
539: CALLFUNC 1064 4
548: PUSHSTR ==printArr After sort==
573: PRINT
574: PUSHVAR 2
579: PUSHINT 9
584: CALLFUNC 687 1
593: PUSHSTR ==printArr Permutation==
619: PRINT
620: PUSHINT 1
625: PUSHSTR str 2
632: PUSHINT 3
637: PUSHSTR str4
643: NEWVEC 4
648: ASSIGN 3
653: PUSHVAR 3
658: PUSHINT 0
663: PUSHINT 3
668: CALLFUNC 35 1
677: PUSHINT 0
682: RETURN 0
687: PUSHINT 0
692: ASSIGN 1
697: PUSHVAR 1
702: PUSHVAR -2
707: LESS
708: JUMPCON 745
713: PUSHVAR 1
718: PUSHVARIDX -3
723: PRINT
724: PUSHVAR 1
729: PUSHINT 1
734: ADD
735: ASSIGN 1
740: JUMP 697
745: PUSHINT 0
750: RETURN 2
755: PUSHINT 0
760: ASSIGN 1
765: PUSHINT 0
770: ASSIGN 2
775: PUSHVAR 1
780: PUSHINT 6
785: LESS
786: JUMPCON 828
791: PUSHVAR 2
796: PUSHVAR 1
801: ADD
802: ASSIGN 2
807: PUSHVAR 1
812: PUSHINT 1
817: ADD
818: ASSIGN 1
823: JUMP 775
828: PUSHVAR 2
833: RETURN 0
838: PUSHINT 0
843: RETURN 0
848: PUSHVAR -2
853: PUSHINT 5.5e+006
858: LESS
859: JUMPCON 894
864: PUSHVAR -2
869: PUSHINT 1
874: ADD
875: CALLFUNC 848 0
884: RETURN 1
889: JUMP 910
894: PUSHVAR -2
899: PUSHINT 1e+006
904: DIVIDE
905: RETURN 1
910: PUSHINT 0
915: RETURN 1
920: PUSHVAR -2
925: PUSHINT 1
930: GREATER
931: JUMPCON 972
936: PUSHVAR -2
941: PUSHINT 1
946: SUBTRACT
947: CALLFUNC 920 0
956: PUSHVAR -2
961: MULTIPLY
962: RETURN 1
967: JUMP 982
972: PUSHINT 1
977: RETURN 1
982: PUSHINT 0
987: RETURN 1
992: PUSHVAR -2
997: PUSHINT 0
1002: EQUAL
1003: JUMPCON 1023
1008: PUSHINT 1
1013: RETURN 1
1018: JUMP 1054
1023: PUSHVAR -2
1028: PUSHVAR -2
1033: PUSHINT 1
1038: SUBTRACT
1039: CALLFUNC 992 0
1048: MULTIPLY
1049: RETURN 1
1054: PUSHINT 0
1059: RETURN 1
1064: PUSHVAR -3
1069: ASSIGN 1
1074: PUSHVAR -2
1079: ASSIGN 2
1084: PUSHVAR -3
1089: PUSHVAR -2
1094: ADD
1095: PUSHINT 2
1100: DIVIDE
1101: PUSHVARIDX -4
1106: ASSIGN 4
1111: PUSHVAR 1
1116: PUSHVAR 2
1121: PUSHINT 1
1126: ADD
1127: LESS
1128: JUMPCON 1326
1133: PUSHVAR 1
1138: PUSHVARIDX -4
1143: PUSHVAR 4
1148: LESS
1149: JUMPCON 1175
1154: PUSHVAR 1
1159: PUSHINT 1
1164: ADD
1165: ASSIGN 1
1170: JUMP 1133
1175: PUSHVAR 2
1180: PUSHVARIDX -4
1185: PUSHVAR 4
1190: GREATER
1191: JUMPCON 1217
1196: PUSHVAR 2
1201: PUSHINT 1
1206: SUBTRACT
1207: ASSIGN 2
1212: JUMP 1175
1217: PUSHVAR 1
1222: PUSHVAR 2
1227: PUSHINT 1
1232: ADD
1233: LESS
1234: JUMPCON 1321
1239: PUSHVAR 1
1244: PUSHVARIDX -4
1249: ASSIGN 3
1254: PUSHVAR 2
1259: PUSHVARIDX -4
1264: PUSHVAR 1
1269: ASSIGNIDX -4
1274: PUSHVAR 3
1279: PUSHVAR 2
1284: ASSIGNIDX -4
1289: PUSHVAR 1
1294: PUSHINT 1
1299: ADD
1300: ASSIGN 1
1305: PUSHVAR 2
1310: PUSHINT 1
1315: SUBTRACT
1316: ASSIGN 2
1321: JUMP 1111
1326: PUSHVAR -3
1331: PUSHVAR 2
1336: LESS
1337: JUMPCON 1366
1342: PUSHVAR -4
1347: PUSHVAR -3
1352: PUSHVAR 2
1357: CALLFUNC 1064 4
1366: PUSHVAR 1
1371: PUSHVAR -2
1376: LESS
1377: JUMPCON 1406
1382: PUSHVAR -4
1387: PUSHVAR 1
1392: PUSHVAR -2
1397: CALLFUNC 1064 4
1406: PUSHINT 0
1411: RETURN 3

=====PRINT VIRTUAL MACHINE=====
testFloat
44.55
factorial 7
5040
myFactorial(7)
5040
testwhile
15
==printArr Before sort==
1
12
5
26
88
14
3
7
2
==printArr After sort==
1
2
3
5
7
12
14
26
88
==printArr Permutation==
1
str 2
3
str4

1
str 2
str4
3

1
3
str 2
str4

1
3
str4
str 2

1
str4
3
str 2

1
str4
str 2
3

str 2
1
3
str4

str 2
1
str4
3

str 2
3
1
str4

str 2
3
str4
1

str 2
str4
3
1

str 2
str4
1
3

3
str 2
1
str4

3
str 2
str4
1

3
1
str 2
str4

3
1
str4
str 2

3
str4
1
str 2

3
str4
str 2
1

str4
str 2
3
1

str4
str 2
1
3

str4
3
str 2
1

str4
3
1
str 2

str4
1
3
str 2

str4
1
str 2
3

=====PRINT MEM ALLOC ON VM=====
STRING ALLOC=33
STRING DEALLOC=33
VECTOR ALLOC=2
VECTOR DEALLOC=2
REFCOUNT ALLOC=35
REFCOUNT DEALLOC=35