Jump to content

  • Log In with Google      Sign In   
  • Create Account

FREE SOFTWARE GIVEAWAY

We have 4 x Pro Licences (valued at $59 each) for 2d modular animation software Spriter to give away in this Thursday's GDNet Direct email newsletter.


Read more in this forum topic or make sure you're signed up (from the right-hand sidebar on the homepage) and read Thursday's newsletter to get in the running!


molehill mountaineer

Member Since 24 Apr 2012
Offline Last Active Aug 10 2014 10:04 AM

Topics I've Started

Separating API specific code from the rest of the framework

10 September 2013 - 05:37 AM

Hi guys,

 

So for the past few months I've been developing a little "pong meets arkanoid" game, learning directx and building a little framework as I go along. (I hesitate to call it an engine) 

 

Though frustrating at times, I do find it immensely satisfying and educational to work with something I built, so I hope to keep exanding (and correcting) this code for future projects, which brings me to my question: I would like to make  the code less dependent on API specific stuff (directx  in particular) so that I could, for example, render the game in openGL should I wish to do so.

 

The trouble is that I'm not quite sure how to seperate the rendering code from the game objects.

I gather I would have a class called "render manager" or something like that, with a pointer to a "renderer" base class - similar to the 'strategy' design pattern. How does one isolate the actual drawing of the object from the game code? Right now my game objects all contain a pointer to a sprite object, which is rife with directX stuff.

 

I'm also now quite sure how to phrase this in a google query so I thought I would ask you guys.

 

Thanks for reading


[dx10/11] Weird problem with gigantic texture when drawing paddle

12 June 2013 - 11:52 AM

Hi folks,

 

I'm working on a simple 2D pong/arkanoid game in dx11 so I can learn the API (which is much more difficult than I had anticipated!).

 

To render the objects I'm creating quads and putting a simple texture on them - this worked fine in my previous version of the project where the Game class has a few game objects (bricks, balls & 2 paddles) and renders them in the Game::Draw() method.

 

In this newer version, I've introducted a  class "player" which contains the balls a player has, the paddle he's controlling and a number of powerups. So naturally I wanted to move the code for rendering the paddle and balls to this new class since it's the owner of these objects. The idea being that Game::Draw() then calls humanPlayer::Draw() and AIPlayer::Draw() which both inherit this method from the base class player.

 

Unfortunately the paddle I'm drawing is HUGE and for some reason my game now runs at a speed of 1FPS. This is really weird considering that the code for drawing the paddle is exactly the same in the version that works and the worldViewProjection matrix I'm using to draw the paddle is as well. When I uncomment the old code it still works (albeit very slowly).

 

This is the code that works (with severe performance issues) in Game::Draw()

XMMATRIX view = XMMatrixIdentity();
XMMATRIX projection = XMMatrixOrthographicOffCenterLH(0.0f, SOLIPSIST.getWindowWidth(), 0.0f,     SOLIPSIST.getWindowHeight(), 0.1f, 100.0f);

XMMATRIX viewProjection = XMMatrixMultiply(view, projection);
XMMATRIX world;
XMMATRIX wvp;

//----------------player paddle matrices-------------------
Paddle* m_pPlayerPaddle = m_pHumanPlayer->getPaddle();

world = m_pPlayerPaddle->getBitmap()->getWorldMatrix();
wvp = XMMatrixMultiply(world, viewProjection);
		
//transpose wvp to feed to shader
wvp = XMMatrixTranspose(wvp);

m_pParentProject->getGraphics()->getContext()->UpdateSubresource(m_pConstantBuffer, NULL, NULL, &wvp, NULL, NULL);

m_pParentProject->getGraphics()->getContext()->VSSetConstantBuffers(0, 1, &m_pConstantBuffer);

	


//draw the paddle
m_pPlayerPaddle->getBitmap()->loadRenderSettings();
m_pPlayerPaddle->draw();

which results in:

30wxt3c.jpg

 

 

This is the code that doesn't in Player::Draw()

void Player::draw(XMMATRIX& p_viewProjection)
{
	if(m_bInitialized)
	{
		XMMATRIX world;
		XMMATRIX wvp; //world*view*projection
		//draw paddle
		if(m_pPaddle != NULL)
		{
			world = m_pPaddle->getBitmap()->getWorldMatrix();
			wvp = XMMatrixMultiply(world, p_viewProjection);

			//transpose to feed to shader
			XMMatrixTranspose(wvp);			
			m_pGraphics->getContext()->UpdateSubresource(m_pConstantBuffer, NULL, NULL, &wvp, NULL, NULL);
			m_pGraphics->getContext()->VSSetConstantBuffers(0, 1, &m_pConstantBuffer);

			m_pPaddle->getBitmap()->loadRenderSettings();
			m_pPaddle->draw();
		}

which results in:

 

10rmrzk.png

 

 

Obviously I'm making some big mistakes here - anybody care to enlighten me? Also any good articles or advice on how to properly debug memory leaks and finetune performance would be welcome.

 

 

 

EDIT: These are the full source files for Player & Game. You'll see a few things like "SOLIPSIST" because I'm making a little framework so 

I can use the code for my next game. Maybe that was a mistake since I clearly need some more DirectX practice

 

Player.cpp

#include "Player.h"


Player::Player()
	: m_pPaddle(NULL),
	  m_pPowerUpChain(NULL),
	  m_bInitialized(false),
	  m_ballsLeft(3)
{
}


Player::~Player()
{
	cleanup();
}

void Player::cleanup()
{
	if(m_pPaddle)
		delete m_pPaddle;

	if(m_pPowerUpChain)
		delete m_pPowerUpChain;

	if(m_balls.size() != 0)
	{
		std::vector<Ball*>::iterator it = m_balls.begin();
		while(it != m_balls.end())
		{
			delete *it;
			++it;
		}

		m_balls.clear();
	}

}




void Player::draw(XMMATRIX& p_viewProjection)
{
	if(m_bInitialized)
	{
		XMMATRIX world;
		XMMATRIX wvp; //world*view*projection
		//draw paddle
		if(m_pPaddle != NULL)
		{
			world = m_pPaddle->getBitmap()->getWorldMatrix();
			wvp = XMMatrixMultiply(world, p_viewProjection);

			//transpose to feed to shader
			XMMatrixTranspose(wvp);			
			m_pGraphics->getContext()->UpdateSubresource(m_pConstantBuffer, NULL, NULL, &wvp, NULL, NULL);
			m_pGraphics->getContext()->VSSetConstantBuffers(0, 1, &m_pConstantBuffer);

			m_pPaddle->getBitmap()->loadRenderSettings();
			m_pPaddle->draw();
		}
	    //draw balls
		if(m_balls.size() != 0)
		{
			std::vector<Ball*>::iterator it = m_balls.begin();

			for(it; it != m_balls.end(); ++it)
			{
				if((*it) != NULL)
				{
					(*it)->getBitmap()->loadRenderSettings();


					world = (*it)->getWorldMatrix();
					wvp = XMMatrixMultiply(world, p_viewProjection);

					//transpose to feed to shader
					wvp = XMMatrixTranspose(wvp);

					m_pGraphics->getContext()->UpdateSubresource(m_pConstantBuffer, NULL, NULL, &wvp, NULL, NULL);
					m_pGraphics->getContext()->VSSetConstantBuffers(0,1,&m_pConstantBuffer);

					//draw ball
					(*it)->draw();
				}
			}
		}
	}
}



void Player::tick(float p_dtime)
{
	if(m_bInitialized)
	{
		m_pPaddle->tick(p_dtime);

		for(std::vector<Ball*>::iterator ballIt = m_balls.begin(); 
			ballIt != m_balls.end(); ++ballIt)
		{
			(*ballIt)->tick(p_dtime);
		}
	}
}



Paddle* Player::getPaddle()
{
	return m_pPaddle;
}


PowerUpChain* Player::getPowerUpChain()
{
	return m_pPowerUpChain;
}

std::vector<Ball*>& Player::getBalls()
{
	return m_balls;
}

unsigned short Player::getScore()
{
	return m_score;
}

unsigned short Player::getReserveBallCount()
{
	return m_ballsLeft;
}
#include "PongPrototype.h"
#include "defines.h"

PongPrototype::PongPrototype()
	: m_pHumanPlayer(NULL),
	  m_pAIPlayer(NULL),
	  m_pConstantBuffer(NULL),
	  m_pSpriteBatcher(NULL),
	  m_enemyScore(0),
	  m_playerScore(0)
{
}


PongPrototype::~PongPrototype()
{
	cleanup();
}


void PongPrototype::cleanup()
{
	//delete paddles
	if(m_pHumanPlayer)
		delete m_pHumanPlayer;
	if(m_pAIPlayer)
		delete m_pAIPlayer;

	////delete balls
	//std::vector<Ball*>::iterator ballIt = m_balls.begin();
	//for(ballIt; ballIt != m_balls.end(); ++ballIt)
	//{
	//	if(*ballIt != NULL)
	//	{
	//		delete *ballIt;
	//	}
	//}
	//m_balls.clear();


	//delete remaining bricks
	std::vector<PowerupBrick*>::iterator brickIt = m_bricks.begin();
	for(brickIt; brickIt != m_bricks.end(); ++brickIt)
	{
		if(*brickIt != NULL)
		{
			delete *brickIt;
		}
	}
	m_bricks.clear();


	//delete constantbuffer (used to interact with shader)
	if(m_pConstantBuffer)
		m_pConstantBuffer->Release();


	if(m_pSpriteBatcher)
		delete m_pSpriteBatcher;
	
}




bool PongPrototype::initialize(Graphics* p_graphics)
{

	m_pSpriteBatcher = new SpriteBatcher(p_graphics);

	//setup constant buffer (to feed to shader)
	D3D11_BUFFER_DESC bufferDescriptor;
	ZeroMemory(&bufferDescriptor, sizeof(bufferDescriptor));
	bufferDescriptor.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bufferDescriptor.Usage = D3D11_USAGE_DEFAULT;
	bufferDescriptor.ByteWidth = sizeof(XMMATRIX);

	m_pParentProject->getGraphics()->getDevice()->CreateBuffer(&bufferDescriptor, NULL, &m_pConstantBuffer);



	//scene positions for paddles
	XMFLOAT2 position1((float)SOLIPSIST.getWindowWidth() / 2 ,50.0f);
	XMFLOAT2 position2((float)SOLIPSIST.getWindowWidth() / 2, (float)SOLIPSIST.getWindowHeight() - 50);



	m_pHumanPlayer = new HumanPlayer();
	if(!m_pHumanPlayer->initialize(m_pParentProject->getGraphics(),
									 m_pConstantBuffer,
									 m_pSpriteBatcher,
									 position1))
	{
		m_pParentProject->log(_T("could not initialize human player object!"));
		return false;
	}

	m_pAIPlayer = new AIPlayer();
	if(!m_pAIPlayer->initialize(m_pParentProject->getGraphics(),
								m_pConstantBuffer,
								m_pSpriteBatcher,
								position2))
	{
		m_pParentProject->log(_T("could not initialize AI player object!"));
		return false;
	}

	//-------------------------------------INITIALIZE PADDLES---------------------------------
	////initialize player paddle
	//m_pPlayerPaddle = new Paddle();
	//if(!m_pPlayerPaddle->initialize(p_graphics, _T("Graphics/Sprites/playerPaddle.dds")))
	//	return false;
	//else //paddle initialized, place in scene
	//{
	//	m_pPlayerPaddle->move(position1);
	//	m_pPlayerPaddle->setFriendly(true);
	//}

	////initialize enemy paddle
	//m_pEnemyPaddle = new Paddle();
	//if(!m_pEnemyPaddle->initialize(p_graphics, _T("Graphics/Sprites/enemyPaddle.dds")))
	//	return false;
	//else
	//{
	//	m_pEnemyPaddle->move(position2);
	//	m_pEnemyPaddle->setFriendly(false);
	//}
	//-----------------------------------------------------------------------------------------
	//-------------------------------------INITIALIZE BALLS------------------------------------
	//position1.y += 50;
	//position2.y -= 50;
	//Ball* playerBall = new Ball();
	//playerBall->setFriendly(true);

	//Ball* enemyBall = new Ball();
	//enemyBall->setFriendly(false);

	//player ball initialization
	/*
	//initialize bitmap
	m_pBitmap = new Sprite(p_graphics);
	if(!m_pBitmap->initialize(p_texture))
		return false;
	*/
	
	//add player ball texture to sprite batcher
	//if(!m_pSpriteBatcher->addSprite(_T("playerBall"),  _T("Graphics/Sprites/playerBall.dds")))
	//	return false;
	//else
	//{
	//	playerBall->setSprite(*(m_pSpriteBatcher->getSprite(_T("playerBall"))));
	//	playerBall->initialize();
	//	playerBall->move(position1); //position on map
	//	playerBall->moveTowards(position2);
		/*//------------------------set direction (DEBUG STUFF)----------------------------
		XMFLOAT2 ballDirection; 
		ballDirection.x = 0;
		ballDirection.y = 300;
		m_pPlayerBall->moveTowards(ballDirection);
		//-----------------------------------------------------------------------------------*/
	//}


	////enemy ball initialization
	//if(!m_pSpriteBatcher->addSprite(_T("enemyBall"),  _T("Graphics/Sprites/enemyBall.dds")))
	//	return false;
	//else
	//{
	//	enemyBall->setSprite(*(m_pSpriteBatcher->getSprite(_T("enemyBall"))));
	//	enemyBall->initialize();
	//	enemyBall->move(position2);
	//	//------------------------set direction (DEBUG STUFF)----------------------------
	//	enemyBall->moveTowards(position1); //move enemy ball towards player
	//	//-----------------------------------------------------------------------------------
	//}

	////add balls to ballvector
	//m_balls.push_back(playerBall);
	//m_balls.push_back(enemyBall);
	//----------------------------------------------------------------------------------------------


	//add brick texture to spriteBatcher
	if(!m_pSpriteBatcher->addSprite(_T("brick"), _T("Graphics/Sprites/powerUpBrick.dds")))
		return false;

	//place brick in scene
	if(!setupBricks())
		return false;


	//all checks passed, initialization complete
	m_pParentProject->log(_T("[pong prototype initialization] all checks passed, prototype initialized"));




	m_bInitialized = true;
	return true;
}


bool PongPrototype::setupBricks()
{
	//-----------------------------------------INITIALIZE BRICKS-------------------------------------
	int brickRowNumber = SOLIPSIST.getWindowWidth() / 50; //window width divided by brick width, the amount of bricks in a single row
	float startingY = (SOLIPSIST.getWindowHeight() / 2) + 30; //brick is 20 pixels high, hence +30 for three rows
	for(int line = 0; line < 3; ++line)
	{
		float startingX = (((SOLIPSIST.getWindowWidth()) % brickRowNumber) / 2) + 25;
		for(int kolom = 0; kolom < brickRowNumber; ++kolom)
		{
			PowerupBrick* brick = new PowerupBrick();
			if(!brick->initialize(m_pSpriteBatcher->getSprite(_T("brick"))))
				return false;
			else
			{
				brick->move(XMFLOAT2(startingX ,startingY));
			}
			m_bricks.push_back(brick);
			startingX += 50;
		}
		startingY -= 40;
	}

	return true;
	//-----------------------------------------------------------------------------------------------
}


void PongPrototype::tick()
{

		float accumulator = 0.0f;

		double frameTime = m_pParentProject->getDeltaTime();
		if(frameTime > 0.25f)
			frameTime = 0.25f;

		double fps = m_pParentProject->getFps();
		float dt = 0.01f;


		accumulator += frameTime;
		while(accumulator >= dt)
		{
			accumulator -= dt;

			m_pHumanPlayer->tick(dt);
			m_pAIPlayer->tick(dt);



			////move balls
			//for(std::vector<Ball*>::iterator ballIt = m_balls.begin(); ballIt != m_balls.end(); ++ballIt)
			//{
			//	if((*ballIt) != NULL)
			//		(*ballIt)->tick(dt);
			//}



			//m_pPlayerPaddle->tick(dt);
			//m_pEnemyPaddle->tick(dt);

		
		}
		//check for collisions
		performCollisionChecks();

		//tick bricks
		for(std::vector<PowerupBrick*>::iterator brickIt = m_bricks.begin(); brickIt != m_bricks.end(); ++brickIt)
		{
			//brick has not yet been deleted yet and hitcounter is smaller or equal to zero
			if(((*brickIt) != NULL) && (*brickIt)->getHitCounter() <= 0)
			{
				delete (*brickIt);
				(*brickIt) = 0;
			}
		}


		//reset directions for paddles
		XMFLOAT2 blancDirection(0.0f, 0.0f);
		m_pHumanPlayer->getPaddle()->setDirection(blancDirection);
		m_pAIPlayer->getPaddle()->setDirection(blancDirection);

}



void PongPrototype::performCollisionChecks()
{
	std::vector<Ball*> balls;
	balls.insert(balls.end(), m_pHumanPlayer->getBalls().begin(), m_pHumanPlayer->getBalls().end());
	balls.insert(balls.end(), m_pAIPlayer->getBalls().begin(), m_pAIPlayer->getBalls().end());


	HitRegion* playerRegion			= m_pHumanPlayer->getPaddle()->getHitRegion();
	HitRegion* enemyRegion			= m_pAIPlayer->getPaddle()->getHitRegion();
	
	HitRegion* ballRegion			= NULL;
	HitRegion* brickRegion			= NULL;
	

	//check player collisions against level border and correct if necessary
	if(checkBorderCollision(playerRegion))
	{
		correctPaddleOverschoot(m_pHumanPlayer->getPaddle());
	}
	if(checkBorderCollision(enemyRegion))
	{
		correctPaddleOverschoot(m_pAIPlayer->getPaddle());
	}


	//iterate over balls and perform collision checks 
	std::vector<Ball*>::iterator it = m_balls.begin();
	for(it; it != m_balls.end(); ++it)
	{
		if((*it) != NULL)  //is there a ball to check?
		{

			ballRegion = (*it)->getHitRegion();

			//check ball collision against border 
			if(checkBorderCollision(ballRegion))
			{
				correctBallovershoot((*it));
				bounceBallOffWall((*it));
			}


			
			//it's important that we check the ball against paddle (in checkRegionCollision) and not the other way around
			//-> because of the way that the collision is calculated a larger hitregion checked against a 
			//smaller one will not return true. 
			//check ball collision against players
			if(m_pParentProject->checkRegionCollision(ballRegion, playerRegion)) //did ball hit player paddle?
			{
				bounceBallOffPaddle((*it), m_pHumanPlayer->getPaddle());
			}
			if(m_pParentProject->checkRegionCollision(ballRegion, enemyRegion)) //did ball hit player paddle?
			{
				bounceBallOffPaddle((*it), m_pAIPlayer->getPaddle());
			}


			//iterate over bricks and perform checks


			//iterate over bricks and perform collision checks 
			std::vector<PowerupBrick*>::iterator brickIt = m_bricks.begin();
			for(brickIt; brickIt != m_bricks.end(); ++brickIt)
			{
				if((*brickIt) != NULL)  //is there a brick to check?
				{
					brickRegion = (*brickIt)->getHitRegion();
					if(m_pParentProject->checkRegionCollision(ballRegion, brickRegion))
					{
						bounceBallOffBrick((*brickIt), (*it));
					}
				}
			}
	
			//finally iterate over entire ball vector to perform collision checks with other balls
			std::vector<Ball*>::iterator it2 = m_balls.begin();
			for(it2; it2 != m_balls.end(); ++it2)
			{
				//we've got to make sure that there is a ball to collide and that 
				//we don't "collide" with ourself
				if((*it2 != NULL) && (it2 != it))
				{
					HitRegion* otherBallRegion = (*it2)->getHitRegion();

					//did the balls hit each other?
					if(m_pParentProject->checkRegionCollision(ballRegion, otherBallRegion))
						bounceBallsOffEachOther(*it, *it2);
				}
			}
		}
	}

}


void PongPrototype::correctBallovershoot(Ball* p_ball)
{
	
	floatRect region = p_ball->getHitRegion()->getRegion();
	
	XMFLOAT2 offset;
	offset.x = 0.0f;
	offset.y = 0.0f;

	if(region.left <= 0)
		offset.x -= region.left; //negative number so we subtract

	if(region.right > SOLIPSIST.getWindowWidth())
		offset.x -= (region.right - SOLIPSIST.getWindowWidth());

	//hit top or bottom? adjust overshoot and flip y direction
	if(region.bottom <= 0)
		offset.y -= region.bottom;

	if(region.top > SOLIPSIST.getWindowHeight())
		offset.y -= (region.top - SOLIPSIST.getWindowHeight());


	//adjust overshoot
	p_ball->move(offset);

}





void PongPrototype::bounceBallsOffEachOther(Ball* p_ballOne, Ball* p_ballTwo)
{
	
	//hitregion is a bit smaller than the actual ball so we add a margin
	int ballOneRadius = (p_ballOne->getHitRegion()->getRegionWidth()  / 2) + 2;
	int ballTwoRadius = (p_ballTwo->getHitRegion()->getRegionWidth()  / 2) + 2;

	//find out the normal to the collision plane (by normalizing the distance between the centerpoints
	//position = centerpoint
	XMFLOAT2 ballOnePosition = p_ballOne->getBitmap()->getPosition();
	XMFLOAT2 ballTwoPosition = p_ballTwo->getBitmap()->getPosition();

	XMFLOAT2 normalVector;
	normalVector.x = ballOnePosition.x - ballTwoPosition.x;
	normalVector.y = ballOnePosition.y - ballTwoPosition.y;

	//find out magnitude (=length) of directional vector by taking square root of squared components
	//(brush up on vector math if you don't understand this bit)
	float normalMagnitude = sqrt((normalVector.x * normalVector.x) + (normalVector.y * normalVector.y));


	if(normalMagnitude <= (ballOneRadius + ballTwoRadius)) //collision? 
	{
		//move balls back a little to correct any overshoot so the hitregions don't get stuck inside eachother
		float overshoot =  (ballOneRadius + ballTwoRadius) - normalMagnitude;

		XMFLOAT2 overshootCorrection;
		overshootCorrection.x = (-p_ballOne->getDirection().x * overshoot);
		overshootCorrection.y = (-p_ballOne->getDirection().y * overshoot);
		p_ballOne->move(overshootCorrection);

		overshootCorrection.x = (-p_ballTwo->getDirection().x * overshoot);
		overshootCorrection.y = (-p_ballTwo->getDirection().y * overshoot);
		p_ballTwo->move(overshootCorrection);



		//make normal to collision plane a unit vector 
		XMFLOAT2 unitNormal;
		unitNormal.x = normalVector.x / normalMagnitude;
		unitNormal.y = normalVector.y / normalMagnitude;


		//find unit tangent to collision plane
		//since tangent is perpendicular to collision plane we can 
		//get the unit tangent from the unit normal 
		XMFLOAT2 unitTangent;
		unitTangent.x = -unitNormal.y;
		unitTangent.y = unitNormal.x;

	
		//project velocity onto normal and tangent (= decomposing the velocity vector into tangent & normal)
		XMFLOAT2 ball1Velocity;
		ball1Velocity.x = p_ballOne->getDirection().x * p_ballOne->getSpeed();
		ball1Velocity.y = p_ballOne->getDirection().y * p_ballOne->getSpeed();

		float normalSpeed1, tangentSpeed1;
		float xDot, yDot;

		//project velocity onto normal
		xDot = ball1Velocity.x * unitNormal.x;
		yDot = ball1Velocity.y * unitNormal.y;
		normalSpeed1 = xDot + yDot;

		//project velocity onto tangent
		xDot = ball1Velocity.x * unitTangent.x;
		yDot = ball1Velocity.y * unitTangent.y;
		tangentSpeed1 = xDot + yDot;


		//preserve tangent and normal velocities (unlike pool, where normal velocity would be affected from collision)
		XMFLOAT2 newDirection1;
		newDirection1.x =  -(unitNormal.x * normalSpeed1) + (unitTangent.x * tangentSpeed1);
		newDirection1.y = -(unitNormal.y * normalSpeed1) + (unitTangent.y * tangentSpeed1);
		float directionalMagnitude = sqrt((newDirection1.x * newDirection1.x) + (newDirection1.y * newDirection1.y));
		newDirection1.x /= directionalMagnitude;
		newDirection1.y /= directionalMagnitude;
		p_ballOne->setDirection(newDirection1);






		//project velocity onto normal and tangent (= decomposing the velocity vector into tangent & normal)
		XMFLOAT2 ball2Velocity;
		ball2Velocity.x = p_ballTwo->getDirection().x * p_ballTwo->getSpeed();
		ball2Velocity.y = p_ballTwo->getDirection().y * p_ballTwo->getSpeed();

		float normalSpeed2, tangentSpeed2;

		//project velocity onto normal
		xDot = ball2Velocity.x * unitNormal.x;
		yDot = ball2Velocity.y * unitNormal.y;
		normalSpeed2 = xDot + yDot;

		//project velocity onto tangent
		xDot = ball2Velocity.x * unitTangent.x;
		yDot = ball2Velocity.y * unitTangent.y;
		tangentSpeed2 = xDot + yDot;


		//preserve tangent and normal velocities (unlike pool, where normal velocity would be affected from collision)
		XMFLOAT2 newDirection2;
		newDirection2.x =  (unitNormal.x * normalSpeed1) + (unitTangent.x * tangentSpeed2);
		newDirection2.y = (unitNormal.y * normalSpeed1) + (unitTangent.y * tangentSpeed2);
		directionalMagnitude = sqrt((newDirection2.x * newDirection2.x) + (newDirection2.y * newDirection2.y));
		newDirection2.x /= directionalMagnitude;
		newDirection2.y /= directionalMagnitude;


		p_ballTwo->setDirection(newDirection2);
	
	}

}


void PongPrototype::bounceBallOffWall(Ball* &p_ball)
{
	bool markedForDeletion = false;

	XMFLOAT2 direction = p_ball->getDirection();
	floatRect region = p_ball->getHitRegion()->getRegion();
	
	//hit left or right? adjust overshoot and flip x direction
	if(region.left <= 0)
	{
		direction.x = -direction.x;
	}

	if(region.right >= SOLIPSIST.getWindowWidth())
	{
		direction.x = -direction.x;
	}

	//hit top or bottom? adjust overshoot and flip y direction + check scoring conditions
	if(region.bottom <= 0)
	{
		//player ball bounces of bottom, otherwise score
		if(p_ball->isFriendly())
			direction.y = -direction.y;
		else //ball wasn't friendly, score ball
		{
			m_enemyScore++;

			markedForDeletion = true;
		}
	}
	if(region.top >= SOLIPSIST.getWindowHeight())
	{
		if(p_ball->isFriendly())
		{
			m_playerScore++;

			markedForDeletion = true;
		}
		else
		{
			direction.y = -direction.y;
		}
	}

	//change direction
	p_ball->setDirection(direction);

	if(markedForDeletion)
	{
		//TODO: let pass pass through hitregion before deleting
		delete p_ball;
		p_ball = 0;
	}
}

//this function is only called when a collision is already detected, so 
//we just have to check which side of the paddle was hit
void PongPrototype::bounceBallOffPaddle(Ball* p_ball, Paddle* p_paddle)
{
	//these variables are used to nudge the ball when it comes into contact with the paddle
	//otherwise the hitregions might get stuck inside each other
	XMFLOAT2 overshootNudge;
	overshootNudge.x = 0.0f;
	overshootNudge.y = 0.0f;
	float ballRadius = p_ball->getHitRegion()->getRegionWidth() / 2;


	//centerpoints of colliding objects
	XMFLOAT2 paddlePosition = p_paddle->getBitmap()->getPosition();
	XMFLOAT2 ballPosition = p_ball->getBitmap()->getPosition();

	//direction of ball after hitting paddle (we "flip" these values)
	XMFLOAT2 ballDirectionPrime;
	ballDirectionPrime.x = p_ball->getDirection().x;
	ballDirectionPrime.y = p_ball->getDirection().y; 

	//vector from center of paddle to center of ball
	XMFLOAT2 impactDirection;
	impactDirection.x = ballPosition.x - paddlePosition.x;
	impactDirection.y = ballPosition.y - paddlePosition.y;

	//upper right corner of paddle in vector form
	XMFLOAT2 urCorner;
	urCorner.x = (paddlePosition.x + p_paddle->getHitRegion()->getRegionWidth() /2) - paddlePosition.x;
	urCorner.y = (paddlePosition.y + p_paddle->getHitRegion()->getRegionHeight() / 2) - paddlePosition.y;

	//lower left corner of paddle in vector form
	XMFLOAT2 llCorner;
	llCorner.x = (paddlePosition.x - p_paddle->getHitRegion()->getRegionWidth() /2) - paddlePosition.x;
	llCorner.y = (paddlePosition.y - p_paddle->getHitRegion()->getRegionHeight() / 2) - paddlePosition.y;


	bool collisionOccured = false;
	bool noFlip = true;

	//check if ball hit sides of paddle
	if( urCorner.y >= impactDirection.y && llCorner.y <= impactDirection.y)
	{
		collisionOccured = true;
		noFlip = false;
		ballDirectionPrime.x = -ballDirectionPrime.x;	
		

		//calculate amount of overlapping x-axis pixels and reverse direction
		float disparity = (paddlePosition.x - ballPosition.x);
		float regionArea = (urCorner.y + ballRadius + 2); // (2 is an extra "nudge" value to ensure we left the hitregion)
		float nudge = 0.0f;
		if(disparity > 0) //collision is upwards so nudge downwards
			nudge = (disparity - regionArea);
		else
			nudge = (disparity + regionArea);

		overshootNudge.x = nudge;


	}

	//check if ball hit top or bottom of padddle
	if(llCorner.x <= impactDirection.x && urCorner.x >= impactDirection.x)
	{
		collisionOccured = true;
		noFlip = false;
		ballDirectionPrime.y = -ballDirectionPrime.y;



		//calculate amount of overlapping y-axis pixels and reverse direction 
		float disparity = (paddlePosition.y - ballPosition.y);
		float regionArea = (urCorner.y + ballRadius + 2); // (2 is an extra "nudge" value to ensure we left the hitregion)
		float nudge = 0.0f;
		if(disparity > 0) //collision is upwards so nudge downwards
			nudge = (disparity - regionArea);
		else
			nudge = (disparity + regionArea);

		overshootNudge.y = nudge;
	}

	if(noFlip)
	{
		collisionOccured = true;

		//this happens when the corner is hit, switch axis unless you want funky behaviour
		if((impactDirection.x < 0 && impactDirection.y < 0) || 
			(impactDirection.x > 0 && impactDirection.y > 0))
		{
			ballDirectionPrime.x = -ballDirectionPrime.y;
			ballDirectionPrime.y = ballDirectionPrime.x;
		}
		else //same thing, but the axii are flipped (draw a picture of this if you want to understand it)
		{
			ballDirectionPrime.x = ballDirectionPrime.y;
			ballDirectionPrime.y = ballDirectionPrime.x;
		}
	}
	p_ball->move(overshootNudge);
	p_ball->setDirection(ballDirectionPrime);


	//ball becomes friendly or enemy depending on wether the other side's paddle touched it 
	//(second check is to ensure that a ball doesn't flip if it gets touched by its owner)
	if(collisionOccured && (p_paddle->isFriendly() != p_ball->isFriendly()))
	{

		if(p_ball->isFriendly())
			p_ball->setSprite(*(m_pSpriteBatcher->getSprite(_T("enemyBall"))));
		else
			p_ball->setSprite(*(m_pSpriteBatcher->getSprite(_T("playerBall"))));

		p_ball->flipFriendly();
	}
}


//TODO: dit heeft eigenlijk geen aparte functie nodig
void PongPrototype::bounceBallOffBrick(PowerupBrick* p_brick, Ball* p_ball)
{
	XMFLOAT2 overshootNudge;
	overshootNudge.x = 0.0f;
	overshootNudge.y = 0.0f;
	float ballRadius = p_ball->getHitRegion()->getRegionWidth() / 2;

	//centerpoints of colliding objects
	XMFLOAT2 brickPosition = p_brick->getPosition();
	XMFLOAT2 ballPosition = p_ball->getBitmap()->getPosition();

	//direction of ball after hitting paddle (we "flip" these values)
	XMFLOAT2 ballDirectionPrime;
	ballDirectionPrime.x = p_ball->getDirection().x;
	ballDirectionPrime.y = p_ball->getDirection().y; 

	//vector from center of paddle to center of ball
	XMFLOAT2 impactDirection;
	impactDirection.x = ballPosition.x - brickPosition.x;
	impactDirection.y = ballPosition.y - brickPosition.y;

	//upper right corner of paddle in vector form
	XMFLOAT2 urCorner;
	urCorner.x = (brickPosition.x + p_brick->getHitRegion()->getRegionWidth() /2) - brickPosition.x;
	urCorner.y = (brickPosition.y + p_brick->getHitRegion()->getRegionHeight() / 2) - brickPosition.y;

	//lower left corner of paddle in vector form
	XMFLOAT2 llCorner;
	llCorner.x = (brickPosition.x - p_brick->getHitRegion()->getRegionWidth() /2) - brickPosition.x;
	llCorner.y = (brickPosition.y - p_brick->getHitRegion()->getRegionHeight() / 2) - brickPosition.y;



	bool noFlip = true;

	//check if ball hit sides of brick
	if( urCorner.y >= impactDirection.y && llCorner.y <= impactDirection.y)
	{
		noFlip = false;
		ballDirectionPrime.x = -ballDirectionPrime.x;	

		//calculate amount of overlapping x-axis pixels and reverse direction
		float disparity = (brickPosition.x - ballPosition.x);
		float regionArea = (urCorner.y + ballRadius + 2); // (2 is an extra "nudge" value to ensure we left the hitregion)
		float nudge = 0.0f;
		if(disparity > 0) //collision is upwards so nudge downwards
			nudge = (disparity - regionArea);
		else
			nudge = (disparity + regionArea);

		overshootNudge.x = nudge;
	}

	//check if ball hit top or bottom of padddle
	if(llCorner.x <= impactDirection.x && urCorner.x >= impactDirection.x)
	{
		noFlip = false;
		ballDirectionPrime.y = -ballDirectionPrime.y;
		
		//calculate amount of overlapping y-axis pixels and reverse direction 
		float disparity = (brickPosition.y - ballPosition.y);
		float regionArea = (urCorner.y + ballRadius + 2); // (2 is an extra "nudge" value to ensure we left the hitregion)
		float nudge = 0.0f;
		if(disparity > 0) //collision is upwards so nudge downwards
			nudge = (disparity - regionArea);
		else
			nudge = (disparity + regionArea);

		overshootNudge.y = nudge;
	}

	if(noFlip)
	{
		//this happens when the corner is hit, switch axis unless you want funky behaviour
		if((impactDirection.x < 0 && impactDirection.y < 0) || 
			(impactDirection.x > 0 && impactDirection.y > 0))
		{
			ballDirectionPrime.x = -ballDirectionPrime.y;
			ballDirectionPrime.y = ballDirectionPrime.x;
		}
		else //same thing, but the axii are flipped (draw a picture of this if you want to understand it)
		{
			ballDirectionPrime.x = ballDirectionPrime.y;
			ballDirectionPrime.y = ballDirectionPrime.x;
		}
	}


	p_ball->move(overshootNudge);
	p_ball->setDirection(ballDirectionPrime);
	p_brick->decreaseHitCounter();
}





//The purpose of this function is to keep the paddle within the confines of the WIN32 window 
//and prevent the player from moving his paddle out of sight
void PongPrototype::correctPaddleOverschoot(Paddle* p_paddle)
{
	XMFLOAT2 offset;
	offset.x = 0.0f;
	offset.y = 0.0f;

	int windowWidth = SOLIPSIST.getWindow()->getWidth();
	int windowHeight = SOLIPSIST.getWindow()->getHeight();
	floatRect region = p_paddle->getHitRegion()->getRegion();



	//store overshoot corrections
	if(region.left < 0)
		offset.x -= region.left; //subtract because region.left is a negative value

	if(region.bottom < 0)
		offset.y -= region.bottom; //same here, subtract because it's a negative value

	if(region.right > windowWidth)
		offset.x -= (region.right - windowWidth); 
	
	if(region.top > windowHeight)
		offset.y -= (region.top - windowHeight);


	p_paddle->move(offset);

}



//check collision against edges of window 
bool PongPrototype::checkBorderCollision(HitRegion* p_region)
{	
	bool collision = false;
	floatRect rect = p_region->getRegion(); //retrieve the location of the hitregion (HitRegion is a wrapper for floatRect)

	//check the border of the map and adjust any overshoots
	if(	rect.bottom <= 0 || rect.top >= SOLIPSIST.getWindow()->getHeight() 
		|| rect.left <= 0 || rect.right >= SOLIPSIST.getWindow()->getWidth())
		collision = true;
	
	return collision;
}




void PongPrototype::spawnBall()
{
	if(m_pHumanPlayer)
	{
		POINT mousePosition;
		GetCursorPos(&mousePosition);
		ScreenToClient(SOLIPSIST.getWindow()->getHandle(), &mousePosition);

		m_pHumanPlayer->spawnBall(mousePosition.x, mousePosition.y, m_pSpriteBatcher);
	}
}

void PongPrototype::keyDown()
{
	XMFLOAT2 direction;
	direction.x = 0.0f;
	direction.y = 0.0f;

	//move player paddle according to directional keys
	if(m_bInitialized)
	{
		if(GetAsyncKeyState(0x5A ) || GetAsyncKeyState(VK_UP)) //Z or up
			direction.y = 1.0f;

		if(GetAsyncKeyState(0x53) || GetAsyncKeyState(VK_DOWN))//S or down
			direction.y = -1.0f;

		if(GetAsyncKeyState(0x51) || GetAsyncKeyState(VK_LEFT))//Q or left
			direction.x = -1.0f;

		if(GetAsyncKeyState(0x44) || GetAsyncKeyState(VK_RIGHT))//D or right
			direction.x = 1.0f;


		m_pHumanPlayer->getPaddle()->setDirection(direction);



		
	}
}


void PongPrototype::mouseButtonDown(UINT p_button)
{
	if(p_button == WM_LBUTTONUP)
		spawnBall();
}

void PongPrototype::draw()
{
	if(m_bInitialized)
	{


		//clear background
		SOLIPSIST.getGraphics()->clearRenderTarget(0.2f, 0.22f, 0.24f, 1.0f);

		tstringstream message;
		message << _T("PLAYER SCORE") << m_playerScore;
		SOLIPSIST.drawString(message.str(), -1.0f, 0.9f);
		message.str(_T(""));
		message << _T("CPU SCORE ") << m_enemyScore;
		SOLIPSIST.drawString(message.str(), 0.55f, 0.9f);
		message.str(_T(""));
		message << _T("FPS ") << m_pParentProject->getFps();
		SOLIPSIST.drawString(message.str(), -1.0f, 0.8f);
		message.str(_T(""));
		message << _T("FRAME TIME ") << m_pParentProject->getDeltaTime() << "SECONDS";
		SOLIPSIST.drawString(message.str(), -1.0f, 0.7f);
		message.str(_T(""));
		/*message << _T("GAME TIME ") << (int) m_pParentProject->getGameTime() << " SECONDS"; 
		SOLIPSIST.drawString(message.str(), -1.0f, -0.9f);
	    message.str(_T(""));*/
		message  << m_pHumanPlayer->getReserveBallCount() << _T("BALLS LEFT "); 
		SOLIPSIST.drawString(message.str(), -1.0f, -0.6f);
		
		//------------------------------------DISPLAYING DEBUG STUFF-------------------------------------
		/*
		tstringstream playerInfo;
		playerInfo << "X " << m_pEnemyBall->getBitmap()->getPosition().x;
		playerInfo << "Y " << m_pEnemyBall->getBitmap()->getPosition().y;
		m_pParentProject->drawString(playerInfo.str(),-1.0f, 0.9f);
		playerInfo.str(_T("")); //empty filestream

		playerInfo  << "REGBOT " << m_pEnemyPaddle->getHitRegion()->getRegion().bottom;
		m_pParentProject->drawString(playerInfo.str(),-1.0f, 0.8f);
		playerInfo.str(_T(""));
	
		playerInfo  << "REGTOP " << m_pEnemyPaddle->getHitRegion()->getRegion().top;
		m_pParentProject->drawString(playerInfo.str(),-1.0f, 0.7f);
		playerInfo.str(_T(""));
	
		playerInfo  << "REGLEFT " << m_pEnemyPaddle->getHitRegion()->getRegion().left;
		m_pParentProject->drawString(playerInfo.str(),-1.0f, 0.6f);
		playerInfo.str(_T(""));
	
		playerInfo <<  "REGRIGHT " << m_pEnemyPaddle->getHitRegion()->getRegion().right;
		m_pParentProject->drawString(playerInfo.str(),-1.0f, 0.5f);
		playerInfo.str(_T(""));
		*/
		//-------------------------------------------------------------------------------------------------
		


		//-----------------------------------setup shader matrices-----------------------------------------


		XMMATRIX view = XMMatrixIdentity();
		XMMATRIX projection = XMMatrixOrthographicOffCenterLH(0.0f, SOLIPSIST.getWindowWidth(), 0.0f, SOLIPSIST.getWindowHeight(), 0.1f, 100.0f);
		XMMATRIX viewProjection = XMMatrixMultiply(view, projection);
		XMMATRIX world;
		XMMATRIX wvp;

		//----------------player paddle matrices-------------------
		Paddle* m_pPlayerPaddle = m_pHumanPlayer->getPaddle();

		world = m_pPlayerPaddle->getBitmap()->getWorldMatrix();
		wvp = XMMatrixMultiply(world, viewProjection);
		
		//transpose wvp to feed to shader
		wvp = XMMatrixTranspose(wvp);

		m_pParentProject->getGraphics()->getContext()->UpdateSubresource(m_pConstantBuffer, NULL, NULL, &wvp, NULL, NULL);
		m_pParentProject->getGraphics()->getContext()->VSSetConstantBuffers(0, 1, &m_pConstantBuffer);

	


		//draw the paddle
		m_pPlayerPaddle->getBitmap()->loadRenderSettings();
		m_pPlayerPaddle->draw();


		////----------------enemy paddle matrices-------------------
		//world = m_pEnemyPaddle->getBitmap()->getWorldMatrix();
		//wvp = XMMatrixMultiply(world, viewProjection);
		//
		////transpose wvp to feed to shader
		//wvp = XMMatrixTranspose(wvp);

		//m_pParentProject->getGraphics()->getContext()->UpdateSubresource(m_pConstantBuffer, NULL, NULL, &wvp, NULL, NULL);
		//m_pParentProject->getGraphics()->getContext()->VSSetConstantBuffers(0, 1, &m_pConstantBuffer);


		////draw paddle
		//m_pEnemyPaddle->getBitmap()->loadRenderSettings();
		//m_pEnemyPaddle->draw();
		//



		////-----------------------------------------------DRAW BALLS------------------------------------
		//std::vector<Ball*>::iterator it = m_balls.begin();

		//

		//for(it; it != m_balls.end(); ++it)
		//{
		//	if((*it) != NULL)
		//	{
		//		(*it)->getBitmap()->loadRenderSettings();


		//		world = (*it)->getWorldMatrix();
		//		wvp = XMMatrixMultiply(world, viewProjection);

		//		//transpose to feed to shader
		//		wvp = XMMatrixTranspose(wvp);

		//		m_pParentProject->getGraphics()->getContext()->UpdateSubresource(m_pConstantBuffer, NULL, NULL, &wvp, NULL, NULL);
		//		m_pParentProject->getGraphics()->getContext()->VSSetConstantBuffers(0,1,&m_pConstantBuffer);

		//		//draw ball
		//		(*it)->draw();
		//	}
		//}

		m_pHumanPlayer->draw(viewProjection);
		//m_pAIPlayer->draw(viewProjection);

		//----------------bricks matrices-------------------
		std::vector<PowerupBrick*>::iterator brickIt = m_bricks.begin();

		for(brickIt; brickIt != m_bricks.end(); ++brickIt)
		{
			if((*brickIt) != NULL)
			{
				(*brickIt)->getBitmap()->loadRenderSettings();


				//get brick matrixes
				world = (*brickIt)->getWorldMatrix();
				wvp = XMMatrixMultiply(world, viewProjection);

				//transpose to feed to shader
				wvp = XMMatrixTranspose(wvp);

				m_pParentProject->getGraphics()->getContext()->UpdateSubresource(m_pConstantBuffer, NULL, NULL, &wvp, NULL, NULL);
				m_pParentProject->getGraphics()->getContext()->VSSetConstantBuffers(0,1,&m_pConstantBuffer);

				//draw brick
				(*brickIt)->draw();



				//display number of hits left over brick
				XMFLOAT2 position = (*brickIt)->getPosition();
				//center numberquad on brick bitmap
				position.x -= 10;
				position.y -= 8;
				//transform to screen coordinates (map to [-1;1])
				position.x /= (SOLIPSIST.getWindowWidth() / 2);
				position.y /= (SOLIPSIST.getWindowHeight() / 2);
				position.x -= 1; 
				position.y -= 1;

				unsigned short hitcount = (*brickIt)->getHitCounter();
				tstringstream number;
				number << hitcount;
				m_pParentProject->drawString(number.str(), position.x, position.y);
			}
		}


	}
}


Frame independent movement - problem calculating speedFactor

05 December 2012 - 01:48 PM

Hi folks,

I'm working on a simple game (basically pong meets breakout) and am trying to implement framerate-independant movement.
I'm polling my Timer object for a speedfactor (= seconds per frame) and multiplying my movement with this number. This causes my objects to move VERY slowly - obviously I'm not doing this right. I used to multiply with "deltaTime" (= seconds since last frame) but the results are the same.
Is this a rounding error or something - what am I not getting here?

Timer object (polled function is getSpf())
[source lang="cpp"]#include "Timer.h"#include "defines.h"/* double m_secondsPerCount; double m_deltaTime; __int64 m_baseTime; __int64 m_pausedTime; __int64 m_stopTime; __int64 m_prevTime; __int64 m_currTime; boom m_bStopped; */Timer::Timer() : m_secondsPerCount(0.0), m_deltaTime(-1.0), m_baseTime(0), m_pausedTime(0), m_prevTime(0), m_currTime(0), m_stopTime(0), m_bStopped(false){ __int64 countsPerSecond; QueryPerformanceFrequency((LARGE_INTEGER*) &countsPerSecond); m_secondsPerCount = 1.0 / (double)countsPerSecond;}Timer::~Timer(){}void Timer::tick(){ if(m_bStopped) { m_deltaTime = 0.0; return; } //get the time this frame __int64 currentTime; QueryPerformanceCounter((LARGE_INTEGER*) &currentTime); m_currTime = currentTime; //time difference between this frame and the previous frame in seconds m_deltaTime = (m_currTime - m_prevTime) * m_secondsPerCount; //prepare for the next frame m_prevTime = m_currTime; //threading or power save mode can cause non-negative values for delta time //we correct this here if(m_deltaTime < 0.0) m_deltaTime = 0.0;}float Timer::getGameTime() const{ //if we're stopped, get frequency difference between start and stop //then translate the difference into seconds if(m_bStopped) return (float)((m_stopTime - m_baseTime) * m_secondsPerCount); else //same idea but we need to remove the amount of paused time return (float)(((m_currTime - m_pausedTime) - m_baseTime) * m_secondsPerCount);}double Timer::getDeltaTime() const{ return m_deltaTime;}double Timer::getSpf() const{ return 1.0 / getFps();}short Timer::getFps() const{ return (short) 1.0/m_deltaTime;}void Timer::reset(){ __int64 currentTime; QueryPerformanceCounter((LARGE_INTEGER*) &currentTime); m_baseTime = currentTime; m_prevTime = currentTime; m_stopTime = 0; m_bStopped = false;}void Timer::stop(){ //if we're already stopped we don't need to do anything if(!m_bStopped) { __int64 currTime; QueryPerformanceCounter((LARGE_INTEGER*) &currTime); //save the time at the moment we stopped m_stopTime = m_currTime; m_bStopped = true; }}//resume timer from a stopped statevoid Timer::start(){ __int64 startTime; QueryPerformanceFrequency((LARGE_INTEGER*) &startTime); //if we're resuming the timer from a stopped state we need to do a few calculations if(m_bStopped) { //accumumlate paused time m_pausedTime += (startTime - m_stopTime); m_prevTime = startTime; m_stopTime = 0; m_bStopped = false; }}[/source]

This number is fed into a tick() function for the objects which need to move - move() is then called to perform the actual adjustments
First I multiply the direction with the speed to get the amount of pixels we move (the direction is a normalized vector). Then I multiply this result
with the speedFactor (= secondsPerFrame) in order to adjust to the frame rate. That's the theory anyway :/
[source lang="cpp"]void Ball::tick(const double& p_secondsPerFrame){ XMFLOAT2 movement; movement.x = (m_direction.x * m_speed) * p_secondsPerFrame; movement.y = (m_direction.y * m_speed) * p_secondsPerFrame; move(movement);}void Ball::move(XMFLOAT2& p_movement){if(m_bInitialized){ m_pHitRegion->move(p_movement); m_pBitmap->move(p_movement);}}[/source]
This is the movement itself, just an addition
[source lang="cpp"]void Sprite::move(XMFLOAT2& p_movement){ m_position.x += p_movement.x; m_position.y += p_movement.y;}[/source]

Thanks for reading.

directx 11 problem switching textures in simple font engine

18 November 2012 - 04:22 PM

Hi folks,

I'm trying to display the amount of seconds a program has been running using a simple font engine which samples 2 textures (one for numbers, one for letters). Displaying letters works fine but the numbers come out wrong. By changing the background colors of the textures I've come to the conclusion that I'm not switching shader resources correctly. Can somebody tell me what I'm doing wrong here? Relevant code will most likely be in the DrawString() method.

EDIT: I figure I'm not supposed to switch shader resources before callin Draw(). Comments & suggestion are welcome

FontEngine.cpp

[source lang="cpp"]#include "FontEngine.h"#include "Graphics.h"#include "D3DX11.h"#include "xnamath.h"struct vertexPos{XMFLOAT3 position;XMFLOAT2 texCoord;};FontEngine::FontEngine(Graphics* p_parent): m_bInitialized(false), m_pGraphics(p_parent), m_pInputLayout(NULL), m_pVertexShader(NULL), m_pPixelShader(NULL), m_pLetterMap(NULL), m_pNumberMap(NULL), m_pSampler(NULL), m_pDynamicVertexBuffer(NULL){}FontEngine::~FontEngine(){cleanup();}//remove member objectsvoid FontEngine::cleanup(){m_pGraphics->log(_T("[CLEANING] subsystem FontEngine"));if(m_pInputLayout) m_pInputLayout->Release();if(m_pVertexShader) m_pVertexShader->Release();if(m_pPixelShader) m_pPixelShader->Release();if(m_pLetterMap) m_pLetterMap->Release();if(m_pNumberMap) m_pNumberMap->Release();if(m_pSampler) m_pSampler->Release();if(m_pDynamicVertexBuffer) m_pDynamicVertexBuffer->Release();}bool FontEngine::initialize(){ID3DBlob* vertexShaderBuffer = NULL;bool compileResult = m_pGraphics->CompileD3DShader(_T("Effects/FontTextureMap.fx"), "VS_Main", "vs_4_0", &amp;amp;vertexShaderBuffer);if(!compileResult) //texture shader compilation failed{ MessageBox(NULL, _T("Failed to compile font engine vertex shader"), _T("Fatal Error"), MB_OK); return false;}HRESULT d3dResult;d3dResult = m_pGraphics->getDevice()->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &amp;amp;m_pVertexShader);if(FAILED(d3dResult)){ MessageBox(NULL, _T("Failed to create font vertex shader"), _T("Fatal Error"), MB_OK); return false;}D3D11_INPUT_ELEMENT_DESC solidColorLayout[] ={ { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}};unsigned int totalLayoutElements = ARRAYSIZE( solidColorLayout );d3dResult = m_pGraphics->getDevice()->CreateInputLayout(solidColorLayout, totalLayoutElements, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &amp;amp;m_pInputLayout);vertexShaderBuffer->Release();if(FAILED(d3dResult)){ MessageBox(NULL, _T("Failed to create input layout in fontengine"), _T("Fatal Error"), MB_OK); return false;}//compile and create pixel shaderID3DBlob* pixelShaderBuffer;compileResult = m_pGraphics->CompileD3DShader(_T("Effects/FontTextureMap.fx"), "PS_Main", "ps_4_0", &amp;amp;pixelShaderBuffer);if(!compileResult) //compilation of pixel shader failed{ MessageBox(NULL, _T("Failed to compile pixel shader in fontengine"), _T("Fatal Error"), MB_OK); return false;}d3dResult = m_pGraphics->getDevice()->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), 0, &amp;amp;m_pPixelShader);pixelShaderBuffer->Release();//warn user if pixel shader could not be createdif(FAILED(d3dResult)){ MessageBox(NULL, _T("Failed to create pixel shader in fontengine"), _T("Fatal Error"), MB_OK); return false;}//create shader resource view for lettersd3dResult = D3DX11CreateShaderResourceViewFromFile(m_pGraphics->getDevice(), _T("Graphics/Fonts/testFont2.dds"), 0, 0, &amp;amp;m_pLetterMap, 0);if(FAILED(d3dResult)){ MessageBox(NULL, _T("Failed to create shader resource view for letters in fontengine"), _T("Fatal Error"), MB_OK); return false;}//create shader resource view for numbersd3dResult = D3DX11CreateShaderResourceViewFromFile(m_pGraphics->getDevice(), _T("Graphics/Fonts/numbersArial.dds"), 0, 0, &amp;amp;m_pNumberMap, 0);if(FAILED(d3dResult)){ MessageBox(NULL, _T("Failed to create shader resource view for numbers in fontengine"), _T("Fatal Error"), MB_OK); return false;}//create sampler stateD3D11_SAMPLER_DESC colorMapDesc;ZeroMemory(&amp;amp;colorMapDesc, sizeof(colorMapDesc)); colorMapDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; colorMapDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; colorMapDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; colorMapDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; colorMapDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; colorMapDesc.MaxLOD = D3D11_FLOAT32_MAX;d3dResult = m_pGraphics->getDevice()->CreateSamplerState(&amp;amp;colorMapDesc, &amp;amp;m_pSampler);if(FAILED(d3dResult)){ MessageBox(NULL, _T("Failed to create sampler state in fontengine"), _T("Fatal Error"), MB_OK); return false;}D3D11_BUFFER_DESC vertexDesc;ZeroMemory(&amp;amp;vertexDesc, sizeof(vertexDesc));vertexDesc.Usage = D3D11_USAGE_DYNAMIC;vertexDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;vertexDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;const int sizeOfSprite = sizeof(vertexPos) * 6; //six points to a quadconst int maxLetters = 45; //45 quads to a stringvertexDesc.ByteWidth =sizeOfSprite * maxLetters;//create dynamic bufferd3dResult = m_pGraphics->getDevice()->CreateBuffer(&amp;amp;vertexDesc, NULL, &amp;amp;m_pDynamicVertexBuffer);if(FAILED(d3dResult)){ MessageBox(NULL, _T("Failed to create dynamic buffer in fontengine"), _T("Fatal Error"), MB_OK); return false;}//all checks passed - return truem_bInitialized = true;return true;}void FontEngine::drawString(tstring p_message, float p_xPosition, float p_yPosition){//TODO datamembers?//size (in bytes) of a single spriteconst int sizeOfSprite = sizeof(vertexPos) * 6;const int maxLetters = 45;int length = p_message.length();//clamp strings that are too longif(length > maxLetters) length = maxLetters;//per quad two triangles, per triangle three vertices (3*2=6)const int verticesPerLetter = 6;D3D11_MAPPED_SUBRESOURCE mapResource;HRESULT d3dResult = m_pGraphics->getContext()->Map(m_pDynamicVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &amp;amp;mapResource);if(FAILED(d3dResult)){ MessageBox(NULL, _T("Failed to map dynamic buffer in fontengine"), _T("Fatal Error"), MB_OK); return;}vertexPos* spritePtr = (vertexPos*) mapResource.pData;//convert to array of charactersconst wchar_t* cString = p_message.c_str();const int indexA = static_cast<char>('A');const int indexZ = static_cast<char>('Z');const int index0 = static_cast<char>('0');const int index9 = static_cast<char>('9');for(int i = 0; i < length; ++i){ //TODO hardcoded! float charWidth = 32.0f / 800.0f; // Char's height on screen. float charHeight = 32.0f / 640.0f; // Char's texel width. float texelWidth = 32.0f / 864.0f; int texLookup = 0; //the "index" of the character in the texture int letter = static_cast<char>( cString[i] ); //the "index" of the character in the ASCII table //select letter texture by default because we use space as default when the character isn't found on the fontmap m_pGraphics->getContext()->PSSetShaderResources( 0, 1, &amp;amp;m_pLetterMap); if( letter < indexA || letter > indexZ ) //not an uppercase letter? { if(letter < index0 || letter > index9) //not a number? texLookup = ( indexZ - indexA ) + 1; // Grab one index past Z, which is a blank space in the texture. else //it's a number { // Char's texel width. texelWidth = 32.0f / 333.0f; texLookup = (letter - index0); m_pGraphics->getContext()->PSSetShaderResources(1,1, &amp;amp;m_pNumberMap); } } else //uppercase letter { //select letter texture by default because we use space as kind of an error character m_pGraphics->getContext()->PSSetShaderResources( 0, 1, &amp;amp;m_pLetterMap); // A = 0, B = 1, Z = 25, etc. texLookup = ( letter - indexA ); } float thisStartX = p_xPosition + ( charWidth * static_cast<float>( i ) ); float thisEndX = thisStartX + charWidth; float thisEndY = p_yPosition + charHeight; spritePtr[0].position = XMFLOAT3( thisEndX, thisEndY, 0.01f ); spritePtr[1].position = XMFLOAT3( thisEndX, p_yPosition, 0.01f ); spritePtr[2].position = XMFLOAT3( thisStartX, p_yPosition, 0.01f ); spritePtr[3].position = XMFLOAT3( thisStartX, p_yPosition, 0.01f ); spritePtr[4].position = XMFLOAT3( thisStartX, thisEndY, 0.01f ); spritePtr[5].position = XMFLOAT3( thisEndX, thisEndY, 0.01f ); float tuStart = 0.0f + ( texelWidth * static_cast<float>( texLookup ) ); float tuEnd = tuStart + texelWidth; spritePtr[0].texCoord = XMFLOAT2( tuEnd, 0.0f ); spritePtr[1].texCoord = XMFLOAT2( tuEnd, 1.0f ); spritePtr[2].texCoord = XMFLOAT2( tuStart, 1.0f ); spritePtr[3].texCoord = XMFLOAT2( tuStart, 1.0f ); spritePtr[4].texCoord = XMFLOAT2( tuStart, 0.0f ); spritePtr[5].texCoord = XMFLOAT2( tuEnd, 0.0f ); //move forward the size of a single quad (6 vertices per quad) spritePtr += 6; }m_pGraphics->getContext()->Unmap(m_pDynamicVertexBuffer, 0 ); m_pGraphics->getContext()->Draw( 6 * length, 0 );}void FontEngine::setupRender(){ID3D11DeviceContext* context = m_pGraphics->getContext();if(context == 0 ) return; unsigned int stride = sizeof( vertexPos ); unsigned int offset = 0; context->IASetInputLayout( m_pInputLayout ); context->IASetVertexBuffers( 0, 1, &amp;amp;m_pDynamicVertexBuffer, &amp;amp;stride, &amp;amp;offset ); context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); context->VSSetShader( m_pVertexShader, 0, 0 ); context->PSSetShader( m_pPixelShader, 0, 0 ); context->PSSetSamplers( 0, 1, &amp;amp;m_pSampler );}bool FontEngine::isInitialized(){return m_bInitialized;}[/source]

[Win32] [solved] CreateWindowEx returns error 1407

27 September 2012 - 04:02 AM

Hello fellow coders,

Can any of you spot what I'm missing here? I'm trying to create a win32 window but falling flat on my face, it's kind of embarrassing really Posted Image
After the call to CreateWindowEx() I'm getting error 1407 which stands for "Cannot find window class".
This leads me to believe that I'm filling in the class structure incorrectly, even though registerClass() doesn't seem to fail.

EDIT: parameters were in the wrong order

bool Window::initialize(HINSTANCE p_instance, UINT p_showCommand)
{
tstring className = _T("solipsistWindow");
//define and register window class
WNDCLASSEX windowClass;
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = WindowProc;
windowClass.cbClsExtra = NULL;
windowClass.cbWndExtra = NULL;
windowClass.hInstance = p_instance;
windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
windowClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
windowClass.hCursor = LoadCursor(NULL, IDC_CROSS);
windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); //+1 to differentiate between background and scrollbar
windowClass.lpszClassName = className.c_str();
windowClass.lpszMenuName = NULL;
if(!RegisterClassEx(&windowClass))
{
  MessageBox(NULL, _T("failed to register window class"), _T("Fatal error"), MB_OK);
  return false;
}

m_windowHandle = CreateWindowEx(WS_EX_CLIENTEDGE,
		 m_windowTitle.c_str(),
		 className.c_str(), //window class
		 WS_OVERLAPPEDWINDOW, //window style
		 CW_USEDEFAULT,   //x
		 CW_USEDEFAULT,   //y
		 m_width,  
		 m_height,
		 NULL,	 //parent
		 NULL,	 //window
		 p_instance,  
		 NULL);	 //lParam

if(m_windowHandle == NULL)
{
  DWORD errorCode = GetLastError();
  tstringstream errorMessage;
  errorMessage << _T("Failed to create window after registering window class. Error code ");
  errorMessage << errorCode;

  MessageBox(NULL, errorMessage.str().c_str() , _T("Fatal Error"), MB_OK);
  return false;
}
ShowWindow(m_windowHandle, p_showCommand);
UpdateWindow(m_windowHandle);
setInitialized(true);
return true;
}

PARTNERS