Quellcode durchsuchen

Chapter 8 initial add

Sanjay Madhav vor 8 Jahren
Ursprung
Commit
0b50a0802a
52 geänderte Dateien mit 4027 neuen und 72 gelöschten Zeilen
  1. 0 24
      Chapter05/Exercises/Exercise3.3/Assets/MapLayer1.csv
  2. 0 24
      Chapter05/Exercises/Exercise3.3/Assets/MapLayer2.csv
  3. 0 24
      Chapter05/Exercises/Exercise3.3/Assets/MapLayer3.csv
  4. BIN
      Chapter05/Exercises/Exercise3.3/Assets/Tiles.png
  5. 124 0
      Chapter08/Actor.cpp
  6. 74 0
      Chapter08/Actor.h
  7. BIN
      Chapter08/Assets/Asteroid.png
  8. BIN
      Chapter08/Assets/Laser.png
  9. BIN
      Chapter08/Assets/Ship.png
  10. BIN
      Chapter08/Assets/ShipWithThrust.png
  11. 46 0
      Chapter08/Asteroid.cpp
  12. 20 0
      Chapter08/Asteroid.h
  13. 25 0
      Chapter08/Chapter08-windows.sln
  14. 402 0
      Chapter08/Chapter6-mac.xcodeproj/project.pbxproj
  15. 7 0
      Chapter08/Chapter6-mac.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  16. 92 0
      Chapter08/Chapter6-mac.xcodeproj/xcshareddata/xcschemes/Game-mac.xcscheme
  17. 40 0
      Chapter08/CircleComponent.cpp
  18. 26 0
      Chapter08/CircleComponent.h
  19. 27 0
      Chapter08/Component.cpp
  20. 33 0
      Chapter08/Component.h
  21. 375 0
      Chapter08/Game.cpp
  22. 71 0
      Chapter08/Game.h
  23. 146 0
      Chapter08/Game.vcxproj
  24. 132 0
      Chapter08/Game.vcxproj.filters
  25. 47 0
      Chapter08/InputComponent.cpp
  26. 45 0
      Chapter08/InputComponent.h
  27. 61 0
      Chapter08/InputSystem.cpp
  28. 54 0
      Chapter08/InputSystem.h
  29. 56 0
      Chapter08/Laser.cpp
  30. 20 0
      Chapter08/Laser.h
  31. 21 0
      Chapter08/Main.cpp
  32. 240 0
      Chapter08/Math.cpp
  33. 1033 0
      Chapter08/Math.h
  34. 41 0
      Chapter08/MoveComponent.cpp
  35. 26 0
      Chapter08/MoveComponent.h
  36. 51 0
      Chapter08/Random.cpp
  37. 36 0
      Chapter08/Random.h
  38. 148 0
      Chapter08/Shader.cpp
  39. 41 0
      Chapter08/Shader.h
  40. 20 0
      Chapter08/Shaders/Basic.frag
  41. 23 0
      Chapter08/Shaders/Basic.vert
  42. 25 0
      Chapter08/Shaders/Sprite.frag
  43. 33 0
      Chapter08/Shaders/Sprite.vert
  44. 23 0
      Chapter08/Shaders/Transform.vert
  45. 50 0
      Chapter08/Ship.cpp
  46. 20 0
      Chapter08/Ship.h
  47. 60 0
      Chapter08/SpriteComponent.cpp
  48. 30 0
      Chapter08/SpriteComponent.h
  49. 69 0
      Chapter08/Texture.cpp
  50. 30 0
      Chapter08/Texture.h
  51. 51 0
      Chapter08/VertexArray.cpp
  52. 33 0
      Chapter08/VertexArray.h

+ 0 - 24
Chapter05/Exercises/Exercise3.3/Assets/MapLayer1.csv

@@ -1,24 +0,0 @@
-27,42,43,44,42,43,44,42,43,44,42,43,44,42,43,44,42,43,44,42,43,44,42,43,44,42,43,44,42,43,44,42
-21,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-29,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-37,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-21,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-29,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,48,49,52,53,54,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-27,11,12,13,14,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-27,43,44,45,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-29,-1,-1,-1,-1,-1,48,49,52,53,54,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-37,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,48,49,52,53,54,-1,-1,-1,48,49,50,51,52,50
-21,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-27,10,11,12,11,12,10,11,12,10,11,12,10,11,12,13,14,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-27,43,44,42,43,44,42,43,44,42,43,44,42,43,44,45,-1,-1,-1,-1,-1,-1,48,49,53,54,-1,-1,-1,-1,-1,-1
-21,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-29,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,56,57,58,59,57
-37,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,9,10,11,12,10,11,12,11,13,65,66,67,65
-21,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,41,42,42,43,44,42,43,44,45,-1,-1,-1,-1
-27,10,11,12,13,14,-1,-1,-1,-1,-1,-1,-1,48,49,53,54,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-20,26,27,28,18,11,12,13,14,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-28,34,35,36,34,35,36,27,11,12,13,14,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-36,42,43,44,44,42,43,44,42,43,44,52,50,51,52,50,51,52,50,51,52,53,54,-1,-1,-1,-1,-1,-1,-1,-1,-1
-37,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-21,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,48,49,52,53,54,-1,8,9,10
-29,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,25,26

+ 0 - 24
Chapter05/Exercises/Exercise3.3/Assets/MapLayer2.csv

@@ -1,24 +0,0 @@
-70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70
-70,70,70,70,70,70,70,70,70,70,70,61,62,63,70,70,70,70,70,70,70,70,70,61,63,61,62,62,63,70,61,62
-70,70,70,70,70,70,70,70,70,70,70,69,0,71,70,70,70,70,70,70,61,62,63,69,71,69,0,0,71,70,69,0
-70,70,70,70,70,70,70,70,70,70,70,69,0,75,62,63,70,70,70,70,69,0,71,69,71,69,0,0,71,70,69,0
-70,70,70,70,70,61,62,62,62,62,62,76,0,0,0,71,70,70,70,61,76,0,71,69,75,76,0,0,71,70,69,0
-70,70,70,70,70,69,0,0,0,0,0,0,0,0,0,71,70,70,70,69,0,0,71,69,0,0,0,0,71,70,69,0
-70,70,70,70,70,69,0,0,0,0,0,0,0,0,0,75,63,61,63,69,0,0,71,69,0,0,0,0,71,70,69,0
-70,70,70,70,70,69,0,0,0,0,0,0,0,0,0,0,75,76,71,69,0,0,71,69,0,0,0,0,71,70,69,0
-70,70,70,61,62,76,0,0,0,0,0,0,0,0,0,0,0,0,71,69,0,0,71,69,0,0,0,0,71,70,69,0
-70,70,61,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,69,0,0,71,69,0,0,0,0,71,70,69,0
-70,70,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,69,0,0,71,69,0,0,0,0,71,70,69,0
-70,70,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,69,0,0,71,69,0,0,0,0,71,70,69,0
-70,70,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,69,0,0,71,69,0,0,0,0,71,70,69,0
-70,70,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,79,69,0,0,71,69,0,0,0,0,71,70,69,0
-70,70,77,78,78,74,0,0,0,0,0,0,0,0,0,0,0,71,70,77,78,78,79,69,0,0,0,0,71,70,77,78
-70,70,70,70,70,77,78,78,78,74,0,0,0,0,73,78,78,79,70,70,70,70,70,69,0,0,0,0,71,70,70,70
-70,70,70,70,70,70,70,70,70,69,73,78,74,0,71,70,70,70,70,70,70,70,70,77,78,78,78,78,79,70,70,70
-70,70,70,70,70,70,70,70,70,77,79,70,77,78,79,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70
-70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70
-70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70
-70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70
-70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70
-70,70,70,70,70,70,70,70,70,70,70,70,70,70,61,63,61,62,62,63,61,63,70,70,70,70,70,70,70,70,70,70
-70,70,70,70,70,70,70,70,70,70,70,70,70,61,76,71,69,0,0,75,76,71,70,70,70,70,70,70,70,70,70,70

+ 0 - 24
Chapter05/Exercises/Exercise3.3/Assets/MapLayer3.csv

@@ -1,24 +0,0 @@
-96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97
-104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105
-112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113
-96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97
-104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105
-112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113
-96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97
-104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105
-112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113
-96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97
-104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105
-112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113
-96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97
-104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105
-112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113
-96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97
-104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105
-112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113
-96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97
-104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105
-112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113
-96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97,98,96,97
-104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105,106,104,105
-112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113,114,112,113

BIN
Chapter05/Exercises/Exercise3.3/Assets/Tiles.png


+ 124 - 0
Chapter08/Actor.cpp

@@ -0,0 +1,124 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "Actor.h"
+#include "Game.h"
+#include "Component.h"
+#include <algorithm>
+
+Actor::Actor(Game* game)
+	:mState(EActive)
+	,mPosition(Vector2::Zero)
+	,mScale(1.0f)
+	,mRotation(0.0f)
+	,mGame(game)
+	,mRecomputeWorldTransform(true)
+{
+	mGame->AddActor(this);
+}
+
+Actor::~Actor()
+{
+	mGame->RemoveActor(this);
+	// Need to delete components
+	// Because ~Component calls RemoveComponent, need a different style loop
+	while (!mComponents.empty())
+	{
+		delete mComponents.back();
+	}
+}
+
+void Actor::Update(float deltaTime)
+{
+	if (mState == EActive)
+	{
+		ComputeWorldTransform();
+
+		UpdateComponents(deltaTime);
+		UpdateActor(deltaTime);
+
+		ComputeWorldTransform();
+	}
+}
+
+void Actor::UpdateComponents(float deltaTime)
+{
+	for (auto comp : mComponents)
+	{
+		comp->Update(deltaTime);
+	}
+}
+
+void Actor::UpdateActor(float deltaTime)
+{
+}
+
+void Actor::ProcessInput(const uint8_t* keyState)
+{
+	if (mState == EActive)
+	{
+		// First process input for components
+		for (auto comp : mComponents)
+		{
+			comp->ProcessInput(keyState);
+		}
+
+		ActorInput(keyState);
+	}
+}
+
+void Actor::ActorInput(const uint8_t* keyState)
+{
+}
+
+void Actor::ComputeWorldTransform()
+{
+	if (mRecomputeWorldTransform)
+	{
+		mRecomputeWorldTransform = false;
+		// Scale, then rotate, then translate
+		mWorldTransform = Matrix4::CreateScale(mScale);
+		mWorldTransform *= Matrix4::CreateRotationZ(mRotation);
+		mWorldTransform *= Matrix4::CreateTranslation(Vector3(mPosition.x, mPosition.y, 0.0f));
+
+		// Inform components world transform updated
+		for (auto comp : mComponents)
+		{
+			comp->OnUpdateWorldTransform();
+		}
+	}
+}
+
+void Actor::AddComponent(Component* component)
+{
+	// Find the insertion point in the sorted vector
+	// (The first element with a order higher than me)
+	int myOrder = component->GetUpdateOrder();
+	auto iter = mComponents.begin();
+	for (;
+		iter != mComponents.end();
+		++iter)
+	{
+		if (myOrder < (*iter)->GetUpdateOrder())
+		{
+			break;
+		}
+	}
+
+	// Inserts element before position of iterator
+	mComponents.insert(iter, component);
+}
+
+void Actor::RemoveComponent(Component* component)
+{
+	auto iter = std::find(mComponents.begin(), mComponents.end(), component);
+	if (iter != mComponents.end())
+	{
+		mComponents.erase(iter);
+	}
+}

+ 74 - 0
Chapter08/Actor.h

@@ -0,0 +1,74 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+#include <vector>
+#include "Math.h"
+#include <cstdint>
+
+class Actor
+{
+public:
+	enum State
+	{
+		EActive,
+		EPaused,
+		EDead
+	};
+
+	Actor(class Game* game);
+	virtual ~Actor();
+
+	// Update function called from Game (not overridable)
+	void Update(float deltaTime);
+	// Updates all the components attached to the actor (not overridable)
+	void UpdateComponents(float deltaTime);
+	// Any actor-specific update code (overridable)
+	virtual void UpdateActor(float deltaTime);
+
+	// ProcessInput function called from Game (not overridable)
+	void ProcessInput(const uint8_t* keyState);
+	// Any actor-specific input code (overridable)
+	virtual void ActorInput(const uint8_t* keyState);
+
+	// Getters/setters
+	const Vector2& GetPosition() const { return mPosition; }
+	void SetPosition(const Vector2& pos) { mPosition = pos; mRecomputeWorldTransform = true; }
+	float GetScale() const { return mScale; }
+	void SetScale(float scale) { mScale = scale;  mRecomputeWorldTransform = true; }
+	float GetRotation() const { return mRotation; }
+	void SetRotation(float rotation) { mRotation = rotation;  mRecomputeWorldTransform = true; }
+	
+	void ComputeWorldTransform();
+	const Matrix4& GetWorldTransform() const { return mWorldTransform; }
+
+	Vector2 GetForward() const { return Vector2(Math::Cos(mRotation), Math::Sin(mRotation)); }
+
+	State GetState() const { return mState; }
+	void SetState(State state) { mState = state; }
+
+	class Game* GetGame() { return mGame; }
+
+
+	// Add/remove components
+	void AddComponent(class Component* component);
+	void RemoveComponent(class Component* component);
+private:
+	// Actor's state
+	State mState;
+
+	// Transform
+	Matrix4 mWorldTransform;
+	Vector2 mPosition;
+	float mScale;
+	float mRotation;
+	bool mRecomputeWorldTransform;
+
+	std::vector<class Component*> mComponents;
+	class Game* mGame;
+};

BIN
Chapter08/Assets/Asteroid.png


BIN
Chapter08/Assets/Laser.png


BIN
Chapter08/Assets/Ship.png


BIN
Chapter08/Assets/ShipWithThrust.png


+ 46 - 0
Chapter08/Asteroid.cpp

@@ -0,0 +1,46 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "Asteroid.h"
+#include "SpriteComponent.h"
+#include "MoveComponent.h"
+#include "Game.h"
+#include "Random.h"
+#include "CircleComponent.h"
+
+Asteroid::Asteroid(Game* game)
+	:Actor(game)
+	,mCircle(nullptr)
+{
+	// Initialize to random position/orientation
+	Vector2 randPos = Random::GetVector(Vector2(-512.0f, -384.0f),
+		Vector2(512.0f, 384.0f));
+	SetPosition(randPos);
+
+	SetRotation(Random::GetFloatRange(0.0f, Math::TwoPi));
+
+	// Create a sprite component
+	SpriteComponent* sc = new SpriteComponent(this);
+	sc->SetTexture(game->GetTexture("Assets/Asteroid.png"));
+
+	// Create a move component, and set a forward speed
+	MoveComponent* mc = new MoveComponent(this);
+	mc->SetForwardSpeed(150.0f);
+
+	// Create a circle component (for collision)
+	mCircle = new CircleComponent(this);
+	mCircle->SetRadius(40.0f);
+
+	// Add to mAsteroids in game
+	game->AddAsteroid(this);
+}
+
+Asteroid::~Asteroid()
+{
+	GetGame()->RemoveAsteroid(this);
+}

+ 20 - 0
Chapter08/Asteroid.h

@@ -0,0 +1,20 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+#include "Actor.h"
+class Asteroid : public Actor
+{
+public:
+	Asteroid(class Game* game);
+	~Asteroid();
+	
+	class CircleComponent* GetCircle() { return mCircle; }
+private:
+	class CircleComponent* mCircle;
+};

+ 25 - 0
Chapter08/Chapter08-windows.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27004.2008
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Game", "Game.vcxproj", "{BC508D87-495F-4554-932D-DD68388B63CC}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{BC508D87-495F-4554-932D-DD68388B63CC}.Debug|Win32.ActiveCfg = Debug|Win32
+		{BC508D87-495F-4554-932D-DD68388B63CC}.Debug|Win32.Build.0 = Debug|Win32
+		{BC508D87-495F-4554-932D-DD68388B63CC}.Release|Win32.ActiveCfg = Release|Win32
+		{BC508D87-495F-4554-932D-DD68388B63CC}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {84365DE3-728A-494A-A8A1-96281CA6772F}
+	EndGlobalSection
+EndGlobal

+ 402 - 0
Chapter08/Chapter6-mac.xcodeproj/project.pbxproj

@@ -0,0 +1,402 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		9203E9F01F0DD69900F9FFC2 /* Tower.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9203E9EE1F0DD69900F9FFC2 /* Tower.cpp */; };
+		9203E9F31F0DE24000F9FFC2 /* Enemy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9203E9F11F0DE24000F9FFC2 /* Enemy.cpp */; };
+		9203E9F61F0DF13600F9FFC2 /* NavComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9203E9F41F0DF13600F9FFC2 /* NavComponent.cpp */; };
+		9203E9F91F0F12FE00F9FFC2 /* Bullet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9203E9F71F0F12FE00F9FFC2 /* Bullet.cpp */; };
+		9206FDC61F140707005078A2 /* Texture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9206FDC41F140707005078A2 /* Texture.cpp */; };
+		9206FDC91F140D40005078A2 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9206FDC71F140D40005078A2 /* Shader.cpp */; };
+		9223C4781F009428009A94D7 /* Game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9223C4671F009428009A94D7 /* Game.cpp */; };
+		9223C4791F009428009A94D7 /* Actor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9223C4681F009428009A94D7 /* Actor.cpp */; };
+		9223C47C1F009428009A94D7 /* Component.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9223C46E1F009428009A94D7 /* Component.cpp */; };
+		9223C47D1F009428009A94D7 /* Main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9223C4711F009428009A94D7 /* Main.cpp */; };
+		9223C47E1F009428009A94D7 /* Math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9223C4721F009428009A94D7 /* Math.cpp */; };
+		9223C4801F009428009A94D7 /* SpriteComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9223C4761F009428009A94D7 /* SpriteComponent.cpp */; };
+		9223C48B1F0CA3CE009A94D7 /* MoveComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9223C48A1F0CA3CE009A94D7 /* MoveComponent.cpp */; };
+		9223C48F1F0CA67A009A94D7 /* Tile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9223C48D1F0CA67A009A94D7 /* Tile.cpp */; };
+		9223C4941F0CA766009A94D7 /* CollisionComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9223C4901F0CA766009A94D7 /* CollisionComponent.cpp */; };
+		9223C4951F0CA766009A94D7 /* CircleComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9223C4911F0CA766009A94D7 /* CircleComponent.cpp */; };
+		9223C4981F0DBD69009A94D7 /* Grid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9223C4961F0DBD69009A94D7 /* Grid.cpp */; };
+		92CF0D791F3BBF140086A0F3 /* VertexArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 92CF0D771F3BBF140086A0F3 /* VertexArray.cpp */; };
+		92D324FB1B697389005A86C7 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92D324FA1B697389005A86C7 /* CoreFoundation.framework */; };
+		92E46E941B6353E50035CD21 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92E46E931B6353E50035CD21 /* OpenGL.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		9203E9EE1F0DD69900F9FFC2 /* Tower.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tower.cpp; sourceTree = "<group>"; };
+		9203E9EF1F0DD69900F9FFC2 /* Tower.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tower.h; sourceTree = "<group>"; };
+		9203E9F11F0DE24000F9FFC2 /* Enemy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Enemy.cpp; sourceTree = "<group>"; };
+		9203E9F21F0DE24000F9FFC2 /* Enemy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Enemy.h; sourceTree = "<group>"; };
+		9203E9F41F0DF13600F9FFC2 /* NavComponent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NavComponent.cpp; sourceTree = "<group>"; };
+		9203E9F51F0DF13600F9FFC2 /* NavComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NavComponent.h; sourceTree = "<group>"; };
+		9203E9F71F0F12FE00F9FFC2 /* Bullet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Bullet.cpp; sourceTree = "<group>"; };
+		9203E9F81F0F12FE00F9FFC2 /* Bullet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Bullet.h; sourceTree = "<group>"; };
+		9206FDC31F13F7E8005078A2 /* Shaders */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Shaders; sourceTree = "<group>"; };
+		9206FDC41F140707005078A2 /* Texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Texture.cpp; sourceTree = "<group>"; };
+		9206FDC51F140707005078A2 /* Texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Texture.h; sourceTree = "<group>"; };
+		9206FDC71F140D40005078A2 /* Shader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Shader.cpp; sourceTree = "<group>"; };
+		9206FDC81F140D40005078A2 /* Shader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Shader.h; sourceTree = "<group>"; };
+		9223C4671F009428009A94D7 /* Game.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Game.cpp; sourceTree = "<group>"; };
+		9223C4681F009428009A94D7 /* Actor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Actor.cpp; sourceTree = "<group>"; };
+		9223C4691F009428009A94D7 /* Actor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Actor.h; sourceTree = "<group>"; };
+		9223C46E1F009428009A94D7 /* Component.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Component.cpp; sourceTree = "<group>"; };
+		9223C46F1F009428009A94D7 /* Component.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Component.h; sourceTree = "<group>"; };
+		9223C4701F009428009A94D7 /* Game.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Game.h; sourceTree = "<group>"; };
+		9223C4711F009428009A94D7 /* Main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Main.cpp; sourceTree = "<group>"; };
+		9223C4721F009428009A94D7 /* Math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Math.cpp; sourceTree = "<group>"; };
+		9223C4731F009428009A94D7 /* Math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Math.h; sourceTree = "<group>"; };
+		9223C4761F009428009A94D7 /* SpriteComponent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpriteComponent.cpp; sourceTree = "<group>"; };
+		9223C4771F009428009A94D7 /* SpriteComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpriteComponent.h; sourceTree = "<group>"; };
+		9223C48A1F0CA3CE009A94D7 /* MoveComponent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MoveComponent.cpp; sourceTree = "<group>"; };
+		9223C48C1F0CA3D4009A94D7 /* MoveComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MoveComponent.h; sourceTree = "<group>"; };
+		9223C48D1F0CA67A009A94D7 /* Tile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tile.cpp; sourceTree = "<group>"; };
+		9223C48E1F0CA67A009A94D7 /* Tile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tile.h; sourceTree = "<group>"; };
+		9223C4901F0CA766009A94D7 /* CollisionComponent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CollisionComponent.cpp; sourceTree = "<group>"; };
+		9223C4911F0CA766009A94D7 /* CircleComponent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CircleComponent.cpp; sourceTree = "<group>"; };
+		9223C4921F0CA766009A94D7 /* CollisionComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollisionComponent.h; sourceTree = "<group>"; };
+		9223C4931F0CA766009A94D7 /* CircleComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircleComponent.h; sourceTree = "<group>"; };
+		9223C4961F0DBD69009A94D7 /* Grid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Grid.cpp; sourceTree = "<group>"; };
+		9223C4971F0DBD69009A94D7 /* Grid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Grid.h; sourceTree = "<group>"; };
+		92CF0D771F3BBF140086A0F3 /* VertexArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VertexArray.cpp; sourceTree = "<group>"; };
+		92CF0D781F3BBF140086A0F3 /* VertexArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VertexArray.h; sourceTree = "<group>"; };
+		92D324FA1B697389005A86C7 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
+		92E46DF71B634EA30035CD21 /* Game-mac */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Game-mac"; sourceTree = BUILT_PRODUCTS_DIR; };
+		92E46E931B6353E50035CD21 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		92E46DF41B634EA30035CD21 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				92D324FB1B697389005A86C7 /* CoreFoundation.framework in Frameworks */,
+				92E46E941B6353E50035CD21 /* OpenGL.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		92E46DEE1B634EA30035CD21 = {
+			isa = PBXGroup;
+			children = (
+				92CF0D771F3BBF140086A0F3 /* VertexArray.cpp */,
+				92CF0D781F3BBF140086A0F3 /* VertexArray.h */,
+				9223C4681F009428009A94D7 /* Actor.cpp */,
+				9223C4691F009428009A94D7 /* Actor.h */,
+				9203E9F71F0F12FE00F9FFC2 /* Bullet.cpp */,
+				9203E9F81F0F12FE00F9FFC2 /* Bullet.h */,
+				9223C46E1F009428009A94D7 /* Component.cpp */,
+				9223C46F1F009428009A94D7 /* Component.h */,
+				9223C4911F0CA766009A94D7 /* CircleComponent.cpp */,
+				9223C4931F0CA766009A94D7 /* CircleComponent.h */,
+				9223C4901F0CA766009A94D7 /* CollisionComponent.cpp */,
+				9223C4921F0CA766009A94D7 /* CollisionComponent.h */,
+				9203E9F11F0DE24000F9FFC2 /* Enemy.cpp */,
+				9203E9F21F0DE24000F9FFC2 /* Enemy.h */,
+				9223C4671F009428009A94D7 /* Game.cpp */,
+				9223C4701F009428009A94D7 /* Game.h */,
+				9223C4961F0DBD69009A94D7 /* Grid.cpp */,
+				9223C4971F0DBD69009A94D7 /* Grid.h */,
+				9223C4711F009428009A94D7 /* Main.cpp */,
+				9223C4721F009428009A94D7 /* Math.cpp */,
+				9223C4731F009428009A94D7 /* Math.h */,
+				9223C48A1F0CA3CE009A94D7 /* MoveComponent.cpp */,
+				9223C48C1F0CA3D4009A94D7 /* MoveComponent.h */,
+				9203E9F41F0DF13600F9FFC2 /* NavComponent.cpp */,
+				9203E9F51F0DF13600F9FFC2 /* NavComponent.h */,
+				9206FDC71F140D40005078A2 /* Shader.cpp */,
+				9206FDC81F140D40005078A2 /* Shader.h */,
+				9223C4761F009428009A94D7 /* SpriteComponent.cpp */,
+				9223C4771F009428009A94D7 /* SpriteComponent.h */,
+				9206FDC41F140707005078A2 /* Texture.cpp */,
+				9206FDC51F140707005078A2 /* Texture.h */,
+				9223C48D1F0CA67A009A94D7 /* Tile.cpp */,
+				9223C48E1F0CA67A009A94D7 /* Tile.h */,
+				9203E9EE1F0DD69900F9FFC2 /* Tower.cpp */,
+				9203E9EF1F0DD69900F9FFC2 /* Tower.h */,
+				9206FDC31F13F7E8005078A2 /* Shaders */,
+				92E46DF81B634EA30035CD21 /* Products */,
+				92D324FA1B697389005A86C7 /* CoreFoundation.framework */,
+				92E46E931B6353E50035CD21 /* OpenGL.framework */,
+			);
+			sourceTree = "<group>";
+		};
+		92E46DF81B634EA30035CD21 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				92E46DF71B634EA30035CD21 /* Game-mac */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		92E46DF61B634EA30035CD21 /* Game-mac */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 92E46DFE1B634EA40035CD21 /* Build configuration list for PBXNativeTarget "Game-mac" */;
+			buildPhases = (
+				92E46DF31B634EA30035CD21 /* Sources */,
+				92E46DF41B634EA30035CD21 /* Frameworks */,
+				92E46EA11B63615B0035CD21 /* ShellScript */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "Game-mac";
+			productName = "Game-mac";
+			productReference = 92E46DF71B634EA30035CD21 /* Game-mac */;
+			productType = "com.apple.product-type.tool";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		92E46DEF1B634EA30035CD21 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0830;
+				ORGANIZATIONNAME = "Sanjay Madhav";
+				TargetAttributes = {
+					92E46DF61B634EA30035CD21 = {
+						CreatedOnToolsVersion = 6.4;
+					};
+				};
+			};
+			buildConfigurationList = 92E46DF21B634EA30035CD21 /* Build configuration list for PBXProject "Chapter6-mac" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = 92E46DEE1B634EA30035CD21;
+			productRefGroup = 92E46DF81B634EA30035CD21 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				92E46DF61B634EA30035CD21 /* Game-mac */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		92E46EA11B63615B0035CD21 /* ShellScript */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "if [ -d \"$BUILD_DIR/Debug\" ]; then\n    cp \"$SRCROOT\"/../external/GLEW/lib/mac/*.dylib $BUILD_DIR/Debug\n    cp \"$SRCROOT\"/../external/SDL/lib/mac/*.dylib $BUILD_DIR/Debug\nfi\n\nif [ -d \"$BUILD_DIR/Release\" ]; then\n    cp \"$SRCROOT\"/../external/GLEW/lib/mac/*.dylib $BUILD_DIR/Release\n    cp \"$SRCROOT\"/../external/SDL/lib/mac/*.dylib $BUILD_DIR/Release\nfi";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		92E46DF31B634EA30035CD21 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9223C4941F0CA766009A94D7 /* CollisionComponent.cpp in Sources */,
+				9223C47D1F009428009A94D7 /* Main.cpp in Sources */,
+				9223C47E1F009428009A94D7 /* Math.cpp in Sources */,
+				9203E9F01F0DD69900F9FFC2 /* Tower.cpp in Sources */,
+				9223C4781F009428009A94D7 /* Game.cpp in Sources */,
+				9203E9F61F0DF13600F9FFC2 /* NavComponent.cpp in Sources */,
+				9223C4801F009428009A94D7 /* SpriteComponent.cpp in Sources */,
+				92CF0D791F3BBF140086A0F3 /* VertexArray.cpp in Sources */,
+				9223C48B1F0CA3CE009A94D7 /* MoveComponent.cpp in Sources */,
+				9203E9F31F0DE24000F9FFC2 /* Enemy.cpp in Sources */,
+				9206FDC91F140D40005078A2 /* Shader.cpp in Sources */,
+				9223C4791F009428009A94D7 /* Actor.cpp in Sources */,
+				9203E9F91F0F12FE00F9FFC2 /* Bullet.cpp in Sources */,
+				9223C4951F0CA766009A94D7 /* CircleComponent.cpp in Sources */,
+				9223C47C1F009428009A94D7 /* Component.cpp in Sources */,
+				9206FDC61F140707005078A2 /* Texture.cpp in Sources */,
+				9223C48F1F0CA67A009A94D7 /* Tile.cpp in Sources */,
+				9223C4981F0DBD69009A94D7 /* Grid.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		92E46DFC1B634EA40035CD21 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = macosx;
+			};
+			name = Debug;
+		};
+		92E46DFD1B634EA40035CD21 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = macosx;
+			};
+			name = Release;
+		};
+		92E46DFF1B634EA40035CD21 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+				FRAMEWORK_SEARCH_PATHS = "";
+				GCC_ENABLE_CPP_RTTI = YES;
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"$(SRCROOT)/../external/SDL/include",
+					"$(SRCROOT)/../external/GLEW/include",
+					"$(SRCROOT)/../external/SOIL/include",
+				);
+				LIBRARY_SEARCH_PATHS = (
+					"$(SRCROOT)/../external/GLEW/lib/mac",
+					"$(SRCROOT)/../external/SDL/lib/mac",
+					"$(SRCROOT)/../external/SOIL/lib/mac",
+				);
+				OTHER_LDFLAGS = (
+					"-lGLEW.2.1.0",
+					"-lSDL2-2.0.0",
+					"-lSDL2_mixer-2.0.0",
+					"-lSDL2_ttf-2.0.0",
+					"-lSOIL",
+					"-lSDL2_image-2.0.0",
+				);
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		92E46E001B634EA40035CD21 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+				FRAMEWORK_SEARCH_PATHS = "";
+				GCC_ENABLE_CPP_RTTI = YES;
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"$(SRCROOT)/../external/SDL/include",
+					"$(SRCROOT)/../external/GLEW/include",
+					"$(SRCROOT)/../external/SOIL/include",
+				);
+				LIBRARY_SEARCH_PATHS = (
+					"$(SRCROOT)/../external/GLEW/lib/mac",
+					"$(SRCROOT)/../external/SDL/lib/mac",
+					"$(SRCROOT)/../external/SOIL/lib/mac",
+				);
+				OTHER_LDFLAGS = (
+					"-lGLEW.2.1.0",
+					"-lSDL2-2.0.0",
+					"-lSDL2_mixer-2.0.0",
+					"-lSDL2_ttf-2.0.0",
+					"-lSOIL",
+					"-lSDL2_image-2.0.0",
+				);
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		92E46DF21B634EA30035CD21 /* Build configuration list for PBXProject "Chapter6-mac" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				92E46DFC1B634EA40035CD21 /* Debug */,
+				92E46DFD1B634EA40035CD21 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		92E46DFE1B634EA40035CD21 /* Build configuration list for PBXNativeTarget "Game-mac" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				92E46DFF1B634EA40035CD21 /* Debug */,
+				92E46E001B634EA40035CD21 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 92E46DEF1B634EA30035CD21 /* Project object */;
+}

+ 7 - 0
Chapter08/Chapter6-mac.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:/Users/Sanjay/gameprogcpp-solutions/Chapter06-new/Chapter6-mac.xcodeproj">
+   </FileRef>
+</Workspace>

+ 92 - 0
Chapter08/Chapter6-mac.xcodeproj/xcshareddata/xcschemes/Game-mac.xcscheme

@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0830"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "92E46DF61B634EA30035CD21"
+               BuildableName = "Game-mac"
+               BlueprintName = "Game-mac"
+               ReferencedContainer = "container:Chapter6-mac.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "92E46DF61B634EA30035CD21"
+            BuildableName = "Game-mac"
+            BlueprintName = "Game-mac"
+            ReferencedContainer = "container:Chapter6-mac.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "YES"
+      customWorkingDirectory = "$(SRCROOT)"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "92E46DF61B634EA30035CD21"
+            BuildableName = "Game-mac"
+            BlueprintName = "Game-mac"
+            ReferencedContainer = "container:Chapter6-mac.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "92E46DF61B634EA30035CD21"
+            BuildableName = "Game-mac"
+            BlueprintName = "Game-mac"
+            ReferencedContainer = "container:Chapter6-mac.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 40 - 0
Chapter08/CircleComponent.cpp

@@ -0,0 +1,40 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "CircleComponent.h"
+#include "Actor.h"
+
+CircleComponent::CircleComponent(class Actor* owner)
+:Component(owner)
+,mRadius(0.0f)
+{
+	
+}
+
+const Vector2& CircleComponent::GetCenter() const
+{
+	return mOwner->GetPosition();
+}
+
+float CircleComponent::GetRadius() const
+{
+	return mOwner->GetScale() * mRadius;
+}
+
+bool Intersect(const CircleComponent& a, const CircleComponent& b)
+{
+	// Calculate distance squared
+	Vector2 diff = a.GetCenter() - b.GetCenter();
+	float distSq = diff.LengthSq();
+
+	// Calculate sum of radii squared
+	float radiiSq = a.GetRadius() + b.GetRadius();
+	radiiSq *= radiiSq;
+
+	return distSq <= radiiSq;
+}

+ 26 - 0
Chapter08/CircleComponent.h

@@ -0,0 +1,26 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+#include "Component.h"
+#include "Math.h"
+
+class CircleComponent : public Component
+{
+public:
+	CircleComponent(class Actor* owner);
+	
+	void SetRadius(float radius) { mRadius = radius; }
+	float GetRadius() const;
+	
+	const Vector2& GetCenter() const;
+private:
+	float mRadius;
+};
+
+bool Intersect(const CircleComponent& a, const CircleComponent& b);

+ 27 - 0
Chapter08/Component.cpp

@@ -0,0 +1,27 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "Component.h"
+#include "Actor.h"
+
+Component::Component(Actor* owner, int updateOrder)
+	:mOwner(owner)
+	,mUpdateOrder(updateOrder)
+{
+	// Add to actor's vector of components
+	mOwner->AddComponent(this);
+}
+
+Component::~Component()
+{
+	mOwner->RemoveComponent(this);
+}
+
+void Component::Update(float deltaTime)
+{
+}

+ 33 - 0
Chapter08/Component.h

@@ -0,0 +1,33 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+#include <cstdint>
+
+class Component
+{
+public:
+	// Constructor
+	// (the lower the update order, the earlier the component updates)
+	Component(class Actor* owner, int updateOrder = 100);
+	// Destructor
+	virtual ~Component();
+	// Update this component by delta time
+	virtual void Update(float deltaTime);
+	// Process input for this component
+	virtual void ProcessInput(const uint8_t* keyState) {}
+	// Called when world transform changes
+	virtual void OnUpdateWorldTransform() { }
+
+	int GetUpdateOrder() const { return mUpdateOrder; }
+protected:
+	// Owning actor
+	class Actor* mOwner;
+	// Update order of component
+	int mUpdateOrder;
+};

+ 375 - 0
Chapter08/Game.cpp

@@ -0,0 +1,375 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "Game.h"
+#include <GL/glew.h>
+#include "Texture.h"
+#include "VertexArray.h"
+#include "Shader.h"
+#include <algorithm>
+#include "Actor.h"
+#include "SpriteComponent.h"
+#include "Actor.h"
+#include "Ship.h"
+#include "Asteroid.h"
+#include "Random.h"
+
+Game::Game()
+:mWindow(nullptr)
+,mSpriteShader(nullptr)
+,mIsRunning(true)
+,mUpdatingActors(false)
+{
+	
+}
+
+bool Game::Initialize()
+{
+	if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0)
+	{
+		SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
+		return false;
+	}
+	
+	// Set OpenGL attributes
+	// Use the core OpenGL profile
+	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+	// Specify version 3.3
+	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
+	// Request a color buffer with 8-bits per RGBA channel
+	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+	// Enable double buffering
+	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+	// Force OpenGL to use hardware acceleration
+	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
+	
+	mWindow = SDL_CreateWindow("Game Programming in C++ (Chapter 5)", 100, 100,
+							   1024, 768, SDL_WINDOW_OPENGL);
+	if (!mWindow)
+	{
+		SDL_Log("Failed to create window: %s", SDL_GetError());
+		return false;
+	}
+	
+	// Create an OpenGL context
+	mContext = SDL_GL_CreateContext(mWindow);
+	
+	// Initialize GLEW
+	glewExperimental = GL_TRUE;
+	if (glewInit() != GLEW_OK)
+	{
+		SDL_Log("Failed to initialize GLEW.");
+		return false;
+	}
+	
+	// On some platforms, GLEW will emit a benign error code,
+	// so clear it
+	glGetError();
+	
+	// Make sure we can create/compile shaders
+	if (!LoadShaders())
+	{
+		SDL_Log("Failed to load shaders.");
+		return false;
+	}
+
+	// Create quad for drawing sprites
+	CreateSpriteVerts();
+
+	LoadData();
+
+	mTicksCount = SDL_GetTicks();
+	
+	return true;
+}
+
+void Game::RunLoop()
+{
+	while (mIsRunning)
+	{
+		ProcessInput();
+		UpdateGame();
+		GenerateOutput();
+	}
+}
+
+void Game::ProcessInput()
+{
+	SDL_Event event;
+	while (SDL_PollEvent(&event))
+	{
+		switch (event.type)
+		{
+			case SDL_QUIT:
+				mIsRunning = false;
+				break;
+		}
+	}
+	
+	const Uint8* keyState = SDL_GetKeyboardState(NULL);
+	if (keyState[SDL_SCANCODE_ESCAPE])
+	{
+		mIsRunning = false;
+	}
+
+	mUpdatingActors = true;
+	for (auto actor : mActors)
+	{
+		actor->ProcessInput(keyState);
+	}
+	mUpdatingActors = false;
+}
+
+void Game::UpdateGame()
+{
+	// Compute delta time
+	// Wait until 16ms has elapsed since last frame
+	while (!SDL_TICKS_PASSED(SDL_GetTicks(), mTicksCount + 16))
+		;
+
+	float deltaTime = (SDL_GetTicks() - mTicksCount) / 1000.0f;
+	if (deltaTime > 0.05f)
+	{
+		deltaTime = 0.05f;
+	}
+	mTicksCount = SDL_GetTicks();
+
+	// Update all actors
+	mUpdatingActors = true;
+	for (auto actor : mActors)
+	{
+		actor->Update(deltaTime);
+	}
+	mUpdatingActors = false;
+
+	// Move any pending actors to mActors
+	for (auto pending : mPendingActors)
+	{
+		pending->ComputeWorldTransform();
+		mActors.emplace_back(pending);
+	}
+	mPendingActors.clear();
+
+	// Add any dead actors to a temp vector
+	std::vector<Actor*> deadActors;
+	for (auto actor : mActors)
+	{
+		if (actor->GetState() == Actor::EDead)
+		{
+			deadActors.emplace_back(actor);
+		}
+	}
+
+	// Delete dead actors (which removes them from mActors)
+	for (auto actor : deadActors)
+	{
+		delete actor;
+	}
+}
+
+void Game::GenerateOutput()
+{
+	// Set the clear color to grey
+	glClearColor(0.86f, 0.86f, 0.86f, 1.0f);
+	// Clear the color buffer
+	glClear(GL_COLOR_BUFFER_BIT);
+	
+	// Draw all sprite components
+	// Enable alpha blending on the color buffer
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+	
+	// Set shader/vao as active
+	mSpriteShader->SetActive();
+	mSpriteVerts->SetActive();
+	for (auto sprite : mSprites)
+	{
+		sprite->Draw(mSpriteShader);
+	}
+
+	// Swap the buffers
+	SDL_GL_SwapWindow(mWindow);
+}
+
+bool Game::LoadShaders()
+{
+	mSpriteShader = new Shader();
+	if (!mSpriteShader->Load("Shaders/Sprite.vert", "Shaders/Sprite.frag"))
+	{
+		return false;
+	}
+
+	mSpriteShader->SetActive();
+	// Set the view-projection matrix
+	Matrix4 viewProj = Matrix4::CreateSimpleViewProj(1024.f, 768.f);
+	mSpriteShader->SetMatrixUniform("uViewProj", viewProj);
+	return true;
+}
+
+void Game::CreateSpriteVerts()
+{
+	float vertices[] = {
+		-0.5f,  0.5f, 0.f, 0.f, 0.f, // top left
+		 0.5f,  0.5f, 0.f, 1.f, 0.f, // top right
+		 0.5f, -0.5f, 0.f, 1.f, 1.f, // bottom right
+		-0.5f, -0.5f, 0.f, 0.f, 1.f  // bottom left
+	};
+
+	unsigned int indices[] = {
+		0, 1, 2,
+		2, 3, 0
+	};
+
+	mSpriteVerts = new VertexArray(vertices, 4, indices, 6);
+}
+
+void Game::LoadData()
+{
+	// Create player's ship
+	mShip = new Ship(this);
+	mShip->SetRotation(Math::PiOver2);
+
+	// Create asteroids
+	const int numAsteroids = 20;
+	for (int i = 0; i < numAsteroids; i++)
+	{
+		new Asteroid(this);
+	}
+}
+
+void Game::UnloadData()
+{
+	// Delete actors
+	// Because ~Actor calls RemoveActor, have to use a different style loop
+	while (!mActors.empty())
+	{
+		delete mActors.back();
+	}
+
+	// Destroy textures
+	for (auto i : mTextures)
+	{
+		i.second->Unload();
+		delete i.second;
+	}
+	mTextures.clear();
+}
+
+Texture* Game::GetTexture(const std::string& fileName)
+{
+	Texture* tex = nullptr;
+	auto iter = mTextures.find(fileName);
+	if (iter != mTextures.end())
+	{
+		tex = iter->second;
+	}
+	else
+	{
+		tex = new Texture();
+		if (tex->Load(fileName))
+		{
+			mTextures.emplace(fileName, tex);
+		}
+		else
+		{
+			delete tex;
+			tex = nullptr;
+		}
+	}
+	return tex;
+}
+
+void Game::AddAsteroid(Asteroid* ast)
+{
+	mAsteroids.emplace_back(ast);
+}
+
+void Game::RemoveAsteroid(Asteroid* ast)
+{
+	auto iter = std::find(mAsteroids.begin(),
+		mAsteroids.end(), ast);
+	if (iter != mAsteroids.end())
+	{
+		mAsteroids.erase(iter);
+	}
+}
+
+void Game::Shutdown()
+{
+	UnloadData();
+	delete mSpriteVerts;
+	mSpriteShader->Unload();
+	delete mSpriteShader;
+	SDL_GL_DeleteContext(mContext);
+	SDL_DestroyWindow(mWindow);
+	SDL_Quit();
+}
+
+void Game::AddActor(Actor* actor)
+{
+	// If we're updating actors, need to add to pending
+	if (mUpdatingActors)
+	{
+		mPendingActors.emplace_back(actor);
+	}
+	else
+	{
+		mActors.emplace_back(actor);
+	}
+}
+
+void Game::RemoveActor(Actor* actor)
+{
+	// Is it in pending actors?
+	auto iter = std::find(mPendingActors.begin(), mPendingActors.end(), actor);
+	if (iter != mPendingActors.end())
+	{
+		// Swap to end of vector and pop off (avoid erase copies)
+		std::iter_swap(iter, mPendingActors.end() - 1);
+		mPendingActors.pop_back();
+	}
+
+	// Is it in actors?
+	iter = std::find(mActors.begin(), mActors.end(), actor);
+	if (iter != mActors.end())
+	{
+		// Swap to end of vector and pop off (avoid erase copies)
+		std::iter_swap(iter, mActors.end() - 1);
+		mActors.pop_back();
+	}
+}
+
+void Game::AddSprite(SpriteComponent* sprite)
+{
+	// Find the insertion point in the sorted vector
+	// (The first element with a higher draw order than me)
+	int myDrawOrder = sprite->GetDrawOrder();
+	auto iter = mSprites.begin();
+	for (;
+		iter != mSprites.end();
+		++iter)
+	{
+		if (myDrawOrder < (*iter)->GetDrawOrder())
+		{
+			break;
+		}
+	}
+
+	// Inserts element before position of iterator
+	mSprites.insert(iter, sprite);
+}
+
+void Game::RemoveSprite(SpriteComponent* sprite)
+{
+	auto iter = std::find(mSprites.begin(), mSprites.end(), sprite);
+	mSprites.erase(iter);
+}

+ 71 - 0
Chapter08/Game.h

@@ -0,0 +1,71 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+#include "SDL/SDL.h"
+#include <unordered_map>
+#include <string>
+#include <vector>
+#include "Math.h"
+
+class Game
+{
+public:
+	Game();
+	bool Initialize();
+	void RunLoop();
+	void Shutdown();
+
+	void AddActor(class Actor* actor);
+	void RemoveActor(class Actor* actor);
+
+	void AddSprite(class SpriteComponent* sprite);
+	void RemoveSprite(class SpriteComponent* sprite);
+	
+	class Texture* GetTexture(const std::string& fileName);
+	
+	// Game-specific (add/remove asteroid)
+	void AddAsteroid(class Asteroid* ast);
+	void RemoveAsteroid(class Asteroid* ast);
+	std::vector<class Asteroid*>& GetAsteroids() { return mAsteroids; }
+private:
+	void ProcessInput();
+	void UpdateGame();
+	void GenerateOutput();
+	bool LoadShaders();
+	void CreateSpriteVerts();
+	void LoadData();
+	void UnloadData();
+	
+	// Map of textures loaded
+	std::unordered_map<std::string, class Texture*> mTextures;
+
+	// All the actors in the game
+	std::vector<class Actor*> mActors;
+	// Any pending actors
+	std::vector<class Actor*> mPendingActors;
+
+	// All the sprite components drawn
+	std::vector<class SpriteComponent*> mSprites;
+
+	// Sprite shader
+	class Shader* mSpriteShader;
+	// Sprite vertex array
+	class VertexArray* mSpriteVerts;
+
+	SDL_Window* mWindow;
+	SDL_GLContext mContext;
+	Uint32 mTicksCount;
+	bool mIsRunning;
+	// Track if we're updating actors right now
+	bool mUpdatingActors;
+
+	// Game-specific
+	class Ship* mShip;
+	std::vector<class Asteroid*> mAsteroids;
+};

+ 146 - 0
Chapter08/Game.vcxproj

@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="Actor.cpp" />
+    <ClCompile Include="Asteroid.cpp" />
+    <ClCompile Include="CircleComponent.cpp" />
+    <ClCompile Include="Component.cpp" />
+    <ClCompile Include="Game.cpp" />
+    <ClCompile Include="InputComponent.cpp" />
+    <ClCompile Include="InputSystem.cpp" />
+    <ClCompile Include="Laser.cpp" />
+    <ClCompile Include="Main.cpp" />
+    <ClCompile Include="Math.cpp" />
+    <ClCompile Include="MoveComponent.cpp" />
+    <ClCompile Include="Random.cpp" />
+    <ClCompile Include="Shader.cpp" />
+    <ClCompile Include="Ship.cpp" />
+    <ClCompile Include="SpriteComponent.cpp" />
+    <ClCompile Include="Texture.cpp" />
+    <ClCompile Include="VertexArray.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="Actor.h" />
+    <ClInclude Include="Asteroid.h" />
+    <ClInclude Include="CircleComponent.h" />
+    <ClInclude Include="Component.h" />
+    <ClInclude Include="Game.h" />
+    <ClInclude Include="InputComponent.h" />
+    <ClInclude Include="InputSystem.h" />
+    <ClInclude Include="Laser.h" />
+    <ClInclude Include="Math.h" />
+    <ClInclude Include="MoveComponent.h" />
+    <ClInclude Include="Random.h" />
+    <ClInclude Include="Shader.h" />
+    <ClInclude Include="Ship.h" />
+    <ClInclude Include="SpriteComponent.h" />
+    <ClInclude Include="Texture.h" />
+    <ClInclude Include="VertexArray.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Shaders\Basic.frag" />
+    <None Include="Shaders\Basic.vert" />
+    <None Include="Shaders\Sprite.frag" />
+    <None Include="Shaders\Sprite.vert" />
+    <None Include="Shaders\Transform.vert" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{BC508D87-495F-4554-932D-DD68388B63CC}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>Game</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>..\external\SDL\include;..\external\GLEW\include;..\external\SOIL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <RuntimeTypeInfo>false</RuntimeTypeInfo>
+      <ExceptionHandling>Sync</ExceptionHandling>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>..\external\SDL\lib\win\x86;..\external\GLEW\lib\win\x86;..\external\SOIL\lib\win\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>opengl32.lib;SDL2.lib;SDL2main.lib;SDL2_ttf.lib;SDL2_mixer.lib;SDL2_image.lib;glew32.lib;SOIL.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalOptions>/NODEFAULTLIB:msvcrt.lib %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+    <PostBuildEvent>
+      <Command>xcopy "$(ProjectDir)\..\external\SDL\lib\win\x86\*.dll" "$(OutDir)" /i /s /y
+xcopy "$(ProjectDir)\..\external\GLEW\lib\win\x86\*.dll" "$(OutDir)" /i /s /y</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>..\external\SDL\include;..\external\GLEW\include;..\external\SOIL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <RuntimeTypeInfo>false</RuntimeTypeInfo>
+      <ExceptionHandling>Sync</ExceptionHandling>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>..\external\SDL\lib\win\x86;..\external\GLEW\lib\win\x86;..\external\SOIL\lib\win\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>opengl32.lib;SDL2.lib;SDL2main.lib;SDL2_ttf.lib;SDL2_mixer.lib;SDL2_image.lib;glew32.lib;SOIL.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>xcopy "$(ProjectDir)\..\external\SDL\lib\win\x86\*.dll" "$(OutDir)" /i /s /y
+xcopy "$(ProjectDir)\..\external\GLEW\lib\win\x86\*.dll" "$(OutDir)" /i /s /y</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 132 - 0
Chapter08/Game.vcxproj.filters

@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Shaders">
+      <UniqueIdentifier>{12a47348-9a6e-404a-8d7e-2ffa91eb59f6}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="Actor.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Component.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Game.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Main.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Math.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="SpriteComponent.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="CircleComponent.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="MoveComponent.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Shader.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Texture.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="VertexArray.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Asteroid.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Laser.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Ship.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="InputComponent.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Random.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="InputSystem.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="Actor.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Component.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Game.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Math.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="SpriteComponent.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="CircleComponent.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="MoveComponent.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Shader.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Texture.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="VertexArray.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Asteroid.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Laser.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Ship.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="InputComponent.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Random.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="InputSystem.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Shaders\Sprite.frag">
+      <Filter>Shaders</Filter>
+    </None>
+    <None Include="Shaders\Sprite.vert">
+      <Filter>Shaders</Filter>
+    </None>
+    <None Include="Shaders\Basic.frag">
+      <Filter>Shaders</Filter>
+    </None>
+    <None Include="Shaders\Basic.vert">
+      <Filter>Shaders</Filter>
+    </None>
+    <None Include="Shaders\Transform.vert">
+      <Filter>Shaders</Filter>
+    </None>
+  </ItemGroup>
+</Project>

+ 47 - 0
Chapter08/InputComponent.cpp

@@ -0,0 +1,47 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "InputComponent.h"
+#include "Actor.h"
+
+InputComponent::InputComponent(class Actor* owner)
+:MoveComponent(owner)
+,mForwardKey(0)
+,mBackKey(0)
+,mClockwiseKey(0)
+,mCounterClockwiseKey(0)
+{
+	
+}
+
+void InputComponent::ProcessInput(const uint8_t* keyState)
+{
+	// Calculate forward speed for MoveComponent
+	float forwardSpeed = 0.0f;
+	if (keyState[mForwardKey])
+	{
+		forwardSpeed += mMaxForwardSpeed;
+	}
+	if (keyState[mBackKey])
+	{
+		forwardSpeed -= mMaxForwardSpeed;
+	}
+	SetForwardSpeed(forwardSpeed);
+
+	// Calculate angular speed for MoveComponent
+	float angularSpeed = 0.0f;
+	if (keyState[mClockwiseKey])
+	{
+		angularSpeed += mMaxAngularSpeed;
+	}
+	if (keyState[mCounterClockwiseKey])
+	{
+		angularSpeed -= mMaxAngularSpeed;
+	}
+	SetAngularSpeed(angularSpeed);
+}

+ 45 - 0
Chapter08/InputComponent.h

@@ -0,0 +1,45 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+#include "MoveComponent.h"
+#include <cstdint>
+
+class InputComponent : public MoveComponent
+{
+public:
+	// Lower update order to update first
+	InputComponent(class Actor* owner);
+
+	void ProcessInput(const uint8_t* keyState) override;
+	
+	// Getters/setters for private variables
+	float GetMaxForward() const { return mMaxForwardSpeed; }
+	float GetMaxAngular() const { return mMaxAngularSpeed; }
+	int GetForwardKey() const { return mForwardKey; }
+	int GetBackKey() const { return mBackKey; }
+	int GetClockwiseKey() const { return mClockwiseKey; }
+	int GetCounterClockwiseKey() const { return mCounterClockwiseKey; }
+
+	void SetMaxForwardSpeed(float speed) { mMaxForwardSpeed = speed; }
+	void SetMaxAngularSpeed(float speed) { mMaxAngularSpeed = speed; }
+	void SetForwardKey(int key) { mForwardKey = key; }
+	void SetBackKey(int key) { mBackKey = key; }
+	void SetClockwiseKey(int key) { mClockwiseKey = key; }
+	void SetCounterClockwiseKey(int key) { mCounterClockwiseKey = key; }
+private:
+	// The maximum forward/angular speeds
+	float mMaxForwardSpeed;
+	float mMaxAngularSpeed;
+	// Keys for forward/back movement
+	int mForwardKey;
+	int mBackKey;
+	// Keys for angular movement
+	int mClockwiseKey;
+	int mCounterClockwiseKey;
+};

+ 61 - 0
Chapter08/InputSystem.cpp

@@ -0,0 +1,61 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "InputSystem.h"
+
+bool KeyboardState::GetKeyValue(SDL_Scancode code) const
+{
+	return mCurrState[code] == 1;
+}
+
+ButtonState KeyboardState::GetKeyState(SDL_Scancode code) const
+{
+	if (mCurrState[code] == 0)
+	{
+		if (mPrevState[code] == 0)
+		{
+			return ENone;
+		}
+		else
+		{
+			return EReleased;
+		}
+	}
+	else // must be 1
+	{
+		if (mPrevState[code] == 0)
+		{
+			return EPressed;
+		}
+		else
+		{
+			return EHeld;
+		}
+	}
+}
+
+InputSystem::InputSystem()
+{
+}
+
+bool InputSystem::Initialize()
+{
+	return true;
+}
+
+void InputSystem::Shutdown()
+{
+}
+
+void InputSystem::PrepareForUpdate()
+{
+}
+
+void InputSystem::Update()
+{
+}

+ 54 - 0
Chapter08/InputSystem.h

@@ -0,0 +1,54 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+#include <SDL/SDL_scancode.h>
+
+// The different button states
+enum ButtonState
+{
+	ENone,
+	EPressed,
+	EReleased,
+	EHeld
+};
+
+// Helper for Keyboard input
+class KeyboardState
+{
+public:
+	friend class InputSystem;
+	bool GetKeyValue(SDL_Scancode code) const;
+	ButtonState GetKeyState(SDL_Scancode code) const;
+private:
+	const Uint8* mCurrState;
+	const Uint8 mPrevState[SDL_NUM_SCANCODES];
+};
+
+// Wrapper that contains current state of input
+struct InputState
+{
+	KeyboardState Keyboard;
+};
+
+class InputSystem
+{
+public:
+	InputSystem();
+	bool Initialize();
+	void Shutdown();
+
+	// Called right before SDL_PollEvents loop
+	void PrepareForUpdate();
+	// Called after SDL_PollEvents loop
+	void Update();
+
+	const InputState& GetState() const { return mState; }
+private:
+	InputState mState;
+};

+ 56 - 0
Chapter08/Laser.cpp

@@ -0,0 +1,56 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "Laser.h"
+#include "SpriteComponent.h"
+#include "MoveComponent.h"
+#include "Game.h"
+#include "CircleComponent.h"
+#include "Asteroid.h"
+
+Laser::Laser(Game* game)
+	:Actor(game)
+	,mDeathTimer(1.0f)
+{
+	// Create a sprite component
+	SpriteComponent* sc = new SpriteComponent(this);
+	sc->SetTexture(game->GetTexture("Assets/Laser.png"));
+
+	// Create a move component, and set a forward speed
+	MoveComponent* mc = new MoveComponent(this);
+	mc->SetForwardSpeed(800.0f);
+
+	// Create a circle component (for collision)
+	mCircle = new CircleComponent(this);
+	mCircle->SetRadius(11.0f);
+}
+
+void Laser::UpdateActor(float deltaTime)
+{
+	// If we run out of time, laser is dead
+	mDeathTimer -= deltaTime;
+	if (mDeathTimer <= 0.0f)
+	{
+		SetState(EDead);
+	}
+	else
+	{
+		// Do we intersect with an asteroid?
+		for (auto ast : GetGame()->GetAsteroids())
+		{
+			if (Intersect(*mCircle, *(ast->GetCircle())))
+			{
+				// The first asteroid we intersect with,
+				// set ourselves and the asteroid to dead
+				SetState(EDead);
+				ast->SetState(EDead);
+				break;
+			}
+		}
+	}
+}

+ 20 - 0
Chapter08/Laser.h

@@ -0,0 +1,20 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+#include "Actor.h"
+class Laser : public Actor
+{
+public:
+	Laser(class Game* game);
+
+	void UpdateActor(float deltaTime) override;
+private:
+	class CircleComponent* mCircle;
+	float mDeathTimer;
+};

+ 21 - 0
Chapter08/Main.cpp

@@ -0,0 +1,21 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "Game.h"
+
+int main(int argc, char** argv)
+{
+	Game game;
+	bool success = game.Initialize();
+	if (success)
+	{
+		game.RunLoop();
+	}
+	game.Shutdown();
+	return 0;
+}

+ 240 - 0
Chapter08/Math.cpp

@@ -0,0 +1,240 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "Math.h"
+
+const Vector2 Vector2::Zero(0.0f, 0.0f);
+const Vector2 Vector2::UnitX(1.0f, 0.0f);
+const Vector2 Vector2::UnitY(0.0f, 1.0f);
+const Vector2 Vector2::NegUnitX(-1.0f, 0.0f);
+const Vector2 Vector2::NegUnitY(0.0f, -1.0f);
+
+const Vector3 Vector3::Zero(0.0f, 0.0f, 0.f);
+const Vector3 Vector3::UnitX(1.0f, 0.0f, 0.0f);
+const Vector3 Vector3::UnitY(0.0f, 1.0f, 0.0f);
+const Vector3 Vector3::UnitZ(0.0f, 0.0f, 1.0f);
+const Vector3 Vector3::NegUnitX(-1.0f, 0.0f, 0.0f);
+const Vector3 Vector3::NegUnitY(0.0f, -1.0f, 0.0f);
+const Vector3 Vector3::NegUnitZ(0.0f, 0.0f, -1.0f);
+const Vector3 Vector3::Infinity(Math::Infinity, Math::Infinity, Math::Infinity);
+const Vector3 Vector3::NegInfinity(Math::NegInfinity, Math::NegInfinity, Math::NegInfinity);
+
+static float m3Ident[3][3] =
+{
+	{ 1.0f, 0.0f, 0.0f },
+	{ 0.0f, 1.0f, 0.0f },
+	{ 0.0f, 0.0f, 1.0f }
+};
+const Matrix3 Matrix3::Identity(m3Ident);
+
+static float m4Ident[4][4] =
+{
+	{ 1.0f, 0.0f, 0.0f, 0.0f },
+	{ 0.0f, 1.0f, 0.0f, 0.0f },
+	{ 0.0f, 0.0f, 1.0f, 0.0f },
+	{ 0.0f, 0.0f, 0.0f, 1.0f }
+};
+
+const Matrix4 Matrix4::Identity(m4Ident);
+
+const Quaternion Quaternion::Identity(0.0f, 0.0f, 0.0f, 1.0f);
+
+Vector2 Vector2::Transform(const Vector2& vec, const Matrix3& mat, float w /*= 1.0f*/)
+{
+	Vector2 retVal;
+	retVal.x = vec.x * mat.mat[0][0] + vec.y * mat.mat[1][0] + w * mat.mat[2][0];
+	retVal.y = vec.x * mat.mat[0][1] + vec.y * mat.mat[1][1] + w * mat.mat[2][1];
+	//ignore w since we aren't returning a new value for it...
+	return retVal;
+}
+
+Vector3 Vector3::Transform(const Vector3& vec, const Matrix4& mat, float w /*= 1.0f*/)
+{
+	Vector3 retVal;
+	retVal.x = vec.x * mat.mat[0][0] + vec.y * mat.mat[1][0] +
+		vec.z * mat.mat[2][0] + w * mat.mat[3][0];
+	retVal.y = vec.x * mat.mat[0][1] + vec.y * mat.mat[1][1] +
+		vec.z * mat.mat[2][1] + w * mat.mat[3][1];
+	retVal.z = vec.x * mat.mat[0][2] + vec.y * mat.mat[1][2] +
+		vec.z * mat.mat[2][2] + w * mat.mat[3][2];
+	//ignore w since we aren't returning a new value for it...
+	return retVal;
+}
+
+// This will transform the vector and renormalize the w component
+Vector3 Vector3::TransformWithPerspDiv(const Vector3& vec, const Matrix4& mat, float w /*= 1.0f*/)
+{
+	Vector3 retVal;
+	retVal.x = vec.x * mat.mat[0][0] + vec.y * mat.mat[1][0] +
+		vec.z * mat.mat[2][0] + w * mat.mat[3][0];
+	retVal.y = vec.x * mat.mat[0][1] + vec.y * mat.mat[1][1] +
+		vec.z * mat.mat[2][1] + w * mat.mat[3][1];
+	retVal.z = vec.x * mat.mat[0][2] + vec.y * mat.mat[1][2] +
+		vec.z * mat.mat[2][2] + w * mat.mat[3][2];
+	float transformedW = vec.x * mat.mat[0][3] + vec.y * mat.mat[1][3] +
+		vec.z * mat.mat[2][3] + w * mat.mat[3][3];
+	if (!Math::NearZero(Math::Abs(transformedW)))
+	{
+		transformedW = 1.0f / transformedW;
+		retVal *= transformedW;
+	}
+	return retVal;
+}
+
+// Transform a Vector3 by a quaternion
+Vector3 Vector3::Transform(const Vector3& v, const Quaternion& q)
+{
+	// v + 2.0*cross(q.xyz, cross(q.xyz,v) + q.w*v);
+	Vector3 qv(q.x, q.y, q.z);
+	Vector3 retVal = v;
+	retVal += 2.0f * Vector3::Cross(qv, Vector3::Cross(qv, v) + q.w * v);
+	return retVal;
+}
+
+void Matrix4::Invert()
+{
+	// Thanks slow math
+	float tmp[12]; /* temp array for pairs */
+	float src[16]; /* array of transpose source matrix */
+	float dst[16]; /* storage */
+	float det; /* determinant */
+	/* transpose matrix */
+
+	// row 1 to col 1
+	src[0] = mat[0][0];
+	src[4] = mat[0][1];
+	src[8] = mat[0][2];
+	src[12] = mat[0][3];
+
+	// row 2 to col 2
+	src[1] = mat[1][0];
+	src[5] = mat[1][1];
+	src[9] = mat[1][2];
+	src[13] = mat[1][3];
+
+	// row 3 to col 3
+	src[2] = mat[2][0];
+	src[6] = mat[2][1];
+	src[10] = mat[2][2];
+	src[14] = mat[2][3];
+
+	// row 4 to col 4
+	src[3] = mat[3][0];
+	src[7] = mat[3][1];
+	src[11] = mat[3][2];
+	src[15] = mat[3][3];
+
+	// 	for (int i = 0; i < 4; i++) {
+	// 		src[i] = mat[i*4];
+	// 		src[i + 4] = mat[i*4 + 1];
+	// 		src[i + 8] = mat[i*4 + 2];
+	// 		src[i + 12] = mat[i*4 + 3];
+	// 	}
+	/* calculate pairs for first 8 elements (cofactors) */
+	tmp[0] = src[10] * src[15];
+	tmp[1] = src[11] * src[14];
+	tmp[2] = src[9] * src[15];
+	tmp[3] = src[11] * src[13];
+	tmp[4] = src[9] * src[14];
+	tmp[5] = src[10] * src[13];
+	tmp[6] = src[8] * src[15];
+	tmp[7] = src[11] * src[12];
+	tmp[8] = src[8] * src[14];
+	tmp[9] = src[10] * src[12];
+	tmp[10] = src[8] * src[13];
+	tmp[11] = src[9] * src[12];
+	/* calculate first 8 elements (cofactors) */
+	dst[0] = tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7];
+	dst[0] -= tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7];
+	dst[1] = tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7];
+	dst[1] -= tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7];
+	dst[2] = tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7];
+	dst[2] -= tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7];
+	dst[3] = tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6];
+	dst[3] -= tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6];
+	dst[4] = tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3];
+	dst[4] -= tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3];
+	dst[5] = tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3];
+	dst[5] -= tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3];
+	dst[6] = tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3];
+	dst[6] -= tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3];
+	dst[7] = tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2];
+	dst[7] -= tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2];
+	/* calculate pairs for second 8 elements (cofactors) */
+	tmp[0] = src[2] * src[7];
+	tmp[1] = src[3] * src[6];
+	tmp[2] = src[1] * src[7];
+	tmp[3] = src[3] * src[5];
+	tmp[4] = src[1] * src[6];
+	tmp[5] = src[2] * src[5];
+	tmp[6] = src[0] * src[7];
+	tmp[7] = src[3] * src[4];
+	tmp[8] = src[0] * src[6];
+	tmp[9] = src[2] * src[4];
+	tmp[10] = src[0] * src[5];
+	tmp[11] = src[1] * src[4];
+	/* calculate second 8 elements (cofactors) */
+	dst[8] = tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15];
+	dst[8] -= tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15];
+	dst[9] = tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15];
+	dst[9] -= tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15];
+	dst[10] = tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15];
+	dst[10] -= tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15];
+	dst[11] = tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14];
+	dst[11] -= tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14];
+	dst[12] = tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9];
+	dst[12] -= tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10];
+	dst[13] = tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10];
+	dst[13] -= tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8];
+	dst[14] = tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8];
+	dst[14] -= tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9];
+	dst[15] = tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9];
+	dst[15] -= tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8];
+	/* calculate determinant */
+	det = src[0] * dst[0] + src[1] * dst[1] + src[2] * dst[2] + src[3] * dst[3];
+	/* calculate matrix inverse */
+	det = 1 / det;
+	for (int j = 0; j < 16; j++)
+		dst[j] *= det;
+
+	// Set it back
+	for (int i = 0; i < 4; i++)
+	{
+		for (int j = 0; j < 4; j++)
+		{
+			mat[i][j] = dst[i * 4 + j];
+		}
+	}
+}
+
+Matrix4 Matrix4::CreateFromQuaternion(const class Quaternion& q)
+{
+	float mat[4][4];
+	
+	mat[0][0] = 1.0f - 2.0f * q.y * q.y - 2.0f * q.z * q.z;
+	mat[0][1] = 2.0f * q.x * q.y + 2.0f * q.w * q.z;
+	mat[0][2] = 2.0f * q.x * q.z - 2.0f * q.w * q.y;
+	mat[0][3] = 0.0f;
+
+	mat[1][0] = 2.0f * q.x * q.y - 2.0f * q.w * q.z;
+	mat[1][1] = 1.0f - 2.0f * q.x * q.x - 2.0f * q.z * q.z;
+	mat[1][2] = 2.0f * q.y * q.z + 2.0f * q.w * q.x;
+	mat[1][3] = 0.0f;
+
+	mat[2][0] = 2.0f * q.x * q.z + 2.0f * q.w * q.y;
+	mat[2][1] = 2.0f * q.y * q.z - 2.0f * q.w * q.x;
+	mat[2][2] = 1.0f - 2.0f * q.x * q.x - 2.0f * q.y * q.y;
+	mat[2][3] = 0.0f;
+
+	mat[3][0] = 0.0f;
+	mat[3][1] = 0.0f;
+	mat[3][2] = 0.0f;
+	mat[3][3] = 1.0f;
+
+	return Matrix4(mat);
+}

+ 1033 - 0
Chapter08/Math.h

@@ -0,0 +1,1033 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+
+#include <cmath>
+#include <memory.h>
+#include <limits>
+
+namespace Math
+{
+	const float Pi = 3.1415926535f;
+	const float TwoPi = Pi * 2.0f;
+	const float PiOver2 = Pi / 2.0f;
+	const float Infinity = std::numeric_limits<float>::infinity();
+	const float NegInfinity = -std::numeric_limits<float>::infinity();
+
+	inline float ToRadians(float degrees)
+	{
+		return degrees * Pi / 180.0f;
+	}
+
+	inline float ToDegrees(float radians)
+	{
+		return radians * 180.0f / Pi;
+	}
+
+	inline bool NearZero(float val, float epsilon = 0.001f)
+	{
+		if (fabs(val) <= epsilon)
+		{
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+	}
+
+	template <typename T>
+	T Max(const T& a, const T& b)
+	{
+		return (a < b ? b : a);
+	}
+
+	template <typename T>
+	T Min(const T& a, const T& b)
+	{
+		return (a < b ? a : b);
+	}
+
+	template <typename T>
+	T Clamp(const T& value, const T& lower, const T& upper)
+	{
+		return Min(upper, Max(lower, value));
+	}
+
+	inline float Abs(float value)
+	{
+		return fabs(value);
+	}
+
+	inline float Cos(float angle)
+	{
+		return cosf(angle);
+	}
+
+	inline float Sin(float angle)
+	{
+		return sinf(angle);
+	}
+
+	inline float Tan(float angle)
+	{
+		return tanf(angle);
+	}
+
+	inline float Acos(float value)
+	{
+		return acosf(value);
+	}
+	
+	inline float Atan2(float y, float x)
+	{
+		return atan2f(y, x);
+	}
+
+	inline float Cot(float angle)
+	{
+		return 1.0f / Tan(angle);
+	}
+
+	inline float Lerp(float a, float b, float f)
+	{
+		return a + f * (b - a);
+	}
+
+	inline float Sqrt(float value)
+	{
+		return sqrtf(value);
+	}
+	
+	inline float Fmod(float numer, float denom)
+	{
+		return fmod(numer, denom);
+	}
+}
+
+// 2D Vector
+class Vector2
+{
+public:
+	float x;
+	float y;
+
+	Vector2()
+		:x(0.0f)
+		,y(0.0f)
+	{}
+
+	explicit Vector2(float inX, float inY)
+		:x(inX)
+		,y(inY)
+	{}
+
+	// Set both components in one line
+	void Set(float inX, float inY)
+	{
+		x = inX;
+		y = inY;
+	}
+
+	// Vector addition (a + b)
+	friend Vector2 operator+(const Vector2& a, const Vector2& b)
+	{
+		return Vector2(a.x + b.x, a.y + b.y);
+	}
+
+	// Vector subtraction (a - b)
+	friend Vector2 operator-(const Vector2& a, const Vector2& b)
+	{
+		return Vector2(a.x - b.x, a.y - b.y);
+	}
+
+	// Component-wise multiplication
+	// (a.x * b.x, ...)
+	friend Vector2 operator*(const Vector2& a, const Vector2& b)
+	{
+		return Vector2(a.x * b.x, a.y * b.y);
+	}
+
+	// Scalar multiplication
+	friend Vector2 operator*(const Vector2& vec, float scalar)
+	{
+		return Vector2(vec.x * scalar, vec.y * scalar);
+	}
+
+	// Scalar multiplication
+	friend Vector2 operator*(float scalar, const Vector2& vec)
+	{
+		return Vector2(vec.x * scalar, vec.y * scalar);
+	}
+
+	// Scalar *=
+	Vector2& operator*=(float scalar)
+	{
+		x *= scalar;
+		y *= scalar;
+		return *this;
+	}
+
+	// Vector +=
+	Vector2& operator+=(const Vector2& right)
+	{
+		x += right.x;
+		y += right.y;
+		return *this;
+	}
+
+	// Vector -=
+	Vector2& operator-=(const Vector2& right)
+	{
+		x -= right.x;
+		y -= right.y;
+		return *this;
+	}
+
+	// Length squared of vector
+	float LengthSq() const
+	{
+		return (x*x + y*y);
+	}
+
+	// Length of vector
+	float Length() const
+	{
+		return (Math::Sqrt(LengthSq()));
+	}
+
+	// Normalize this vector
+	void Normalize()
+	{
+		float length = Length();
+		x /= length;
+		y /= length;
+	}
+
+	// Normalize the provided vector
+	static Vector2 Normalize(const Vector2& vec)
+	{
+		Vector2 temp = vec;
+		temp.Normalize();
+		return temp;
+	}
+
+	// Dot product between two vectors (a dot b)
+	static float Dot(const Vector2& a, const Vector2& b)
+	{
+		return (a.x * b.x + a.y * b.y);
+	}
+
+	// Lerp from A to B by f
+	static Vector2 Lerp(const Vector2& a, const Vector2& b, float f)
+	{
+		return Vector2(a + f * (b - a));
+	}
+	
+	// Reflect V about (normalized) N
+	static Vector2 Reflect(const Vector2& v, const Vector2& n)
+	{
+		return v - 2.0f * Vector2::Dot(v, n) * n;
+	}
+
+	// Transform vector by matrix
+	static Vector2 Transform(const Vector2& vec, const class Matrix3& mat, float w = 1.0f);
+
+	static const Vector2 Zero;
+	static const Vector2 UnitX;
+	static const Vector2 UnitY;
+	static const Vector2 NegUnitX;
+	static const Vector2 NegUnitY;
+};
+
+// 3D Vector
+class Vector3
+{
+public:
+	float x;
+	float y;
+	float z;
+
+	Vector3()
+		:x(0.0f)
+		,y(0.0f)
+		,z(0.0f)
+	{}
+
+	explicit Vector3(float inX, float inY, float inZ)
+		:x(inX)
+		,y(inY)
+		,z(inZ)
+	{}
+
+	// Cast to a const float pointer
+	const float* GetAsFloatPtr() const
+	{
+		return reinterpret_cast<const float*>(&x);
+	}
+
+	// Set all three components in one line
+	void Set(float inX, float inY, float inZ)
+	{
+		x = inX;
+		y = inY;
+		z = inZ;
+	}
+
+	// Vector addition (a + b)
+	friend Vector3 operator+(const Vector3& a, const Vector3& b)
+	{
+		return Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
+	}
+
+	// Vector subtraction (a - b)
+	friend Vector3 operator-(const Vector3& a, const Vector3& b)
+	{
+		return Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
+	}
+
+	// Component-wise multiplication
+	friend Vector3 operator*(const Vector3& left, const Vector3& right)
+	{
+		return Vector3(left.x * right.x, left.y * right.y, left.z * right.z);
+	}
+
+	// Scalar multiplication
+	friend Vector3 operator*(const Vector3& vec, float scalar)
+	{
+		return Vector3(vec.x * scalar, vec.y * scalar, vec.z * scalar);
+	}
+
+	// Scalar multiplication
+	friend Vector3 operator*(float scalar, const Vector3& vec)
+	{
+		return Vector3(vec.x * scalar, vec.y * scalar, vec.z * scalar);
+	}
+
+	// Scalar *=
+	Vector3& operator*=(float scalar)
+	{
+		x *= scalar;
+		y *= scalar;
+		z *= scalar;
+		return *this;
+	}
+
+	// Vector +=
+	Vector3& operator+=(const Vector3& right)
+	{
+		x += right.x;
+		y += right.y;
+		z += right.z;
+		return *this;
+	}
+
+	// Vector -=
+	Vector3& operator-=(const Vector3& right)
+	{
+		x -= right.x;
+		y -= right.y;
+		z -= right.z;
+		return *this;
+	}
+
+	// Length squared of vector
+	float LengthSq() const
+	{
+		return (x*x + y*y + z*z);
+	}
+
+	// Length of vector
+	float Length() const
+	{
+		return (Math::Sqrt(LengthSq()));
+	}
+
+	// Normalize this vector
+	void Normalize()
+	{
+		float length = Length();
+		x /= length;
+		y /= length;
+		z /= length;
+	}
+
+	// Normalize the provided vector
+	static Vector3 Normalize(const Vector3& vec)
+	{
+		Vector3 temp = vec;
+		temp.Normalize();
+		return temp;
+	}
+
+	// Dot product between two vectors (a dot b)
+	static float Dot(const Vector3& a, const Vector3& b)
+	{
+		return (a.x * b.x + a.y * b.y + a.z * b.z);
+	}
+
+	// Cross product between two vectors (a cross b)
+	static Vector3 Cross(const Vector3& a, const Vector3& b)
+	{
+		Vector3 temp;
+		temp.x = a.y * b.z - a.z * b.y;
+		temp.y = a.z * b.x - a.x * b.z;
+		temp.z = a.x * b.y - a.y * b.x;
+		return temp;
+	}
+
+	// Lerp from A to B by f
+	static Vector3 Lerp(const Vector3& a, const Vector3& b, float f)
+	{
+		return Vector3(a + f * (b - a));
+	}
+	
+	// Reflect V about (normalized) N
+	static Vector3 Reflect(const Vector3& v, const Vector3& n)
+	{
+		return v - 2.0f * Vector3::Dot(v, n) * n;
+	}
+
+	static Vector3 Transform(const Vector3& vec, const class Matrix4& mat, float w = 1.0f);
+	// This will transform the vector and renormalize the w component
+	static Vector3 TransformWithPerspDiv(const Vector3& vec, const class Matrix4& mat, float w = 1.0f);
+
+	// Transform a Vector3 by a quaternion
+	static Vector3 Transform(const Vector3& v, const class Quaternion& q);
+
+	static const Vector3 Zero;
+	static const Vector3 UnitX;
+	static const Vector3 UnitY;
+	static const Vector3 UnitZ;
+	static const Vector3 NegUnitX;
+	static const Vector3 NegUnitY;
+	static const Vector3 NegUnitZ;
+	static const Vector3 Infinity;
+	static const Vector3 NegInfinity;
+};
+
+// 3x3 Matrix
+class Matrix3
+{
+public:
+	float mat[3][3];
+
+	Matrix3()
+	{
+		*this = Matrix3::Identity;
+	}
+
+	explicit Matrix3(float inMat[3][3])
+	{
+		memcpy(mat, inMat, 9 * sizeof(float));
+	}
+
+	// Cast to a const float pointer
+	const float* GetAsFloatPtr() const
+	{
+		return reinterpret_cast<const float*>(&mat[0][0]);
+	}
+
+	// Matrix multiplication
+	friend Matrix3 operator*(const Matrix3& left, const Matrix3& right)
+	{
+		Matrix3 retVal;
+		// row 0
+		retVal.mat[0][0] = 
+			left.mat[0][0] * right.mat[0][0] +
+			left.mat[0][1] * right.mat[1][0] +
+			left.mat[0][2] * right.mat[2][0];
+
+		retVal.mat[0][1] = 
+			left.mat[0][0] * right.mat[0][1] +
+			left.mat[0][1] * right.mat[1][1] +
+			left.mat[0][2] * right.mat[2][1];
+
+		retVal.mat[0][2] = 
+			left.mat[0][0] * right.mat[0][2] +
+			left.mat[0][1] * right.mat[1][2] +
+			left.mat[0][2] * right.mat[2][2];
+		
+		// row 1
+		retVal.mat[1][0] = 
+			left.mat[1][0] * right.mat[0][0] +
+			left.mat[1][1] * right.mat[1][0] +
+			left.mat[1][2] * right.mat[2][0];
+
+		retVal.mat[1][1] = 
+			left.mat[1][0] * right.mat[0][1] +
+			left.mat[1][1] * right.mat[1][1] +
+			left.mat[1][2] * right.mat[2][1];
+
+		retVal.mat[1][2] = 
+			left.mat[1][0] * right.mat[0][2] +
+			left.mat[1][1] * right.mat[1][2] +
+			left.mat[1][2] * right.mat[2][2];
+		
+		// row 2
+		retVal.mat[2][0] = 
+			left.mat[2][0] * right.mat[0][0] +
+			left.mat[2][1] * right.mat[1][0] +
+			left.mat[2][2] * right.mat[2][0];
+
+		retVal.mat[2][1] =
+			left.mat[2][0] * right.mat[0][1] +
+			left.mat[2][1] * right.mat[1][1] +
+			left.mat[2][2] * right.mat[2][1];
+
+		retVal.mat[2][2] = 
+			left.mat[2][0] * right.mat[0][2] +
+			left.mat[2][1] * right.mat[1][2] +
+			left.mat[2][2] * right.mat[2][2];
+
+		return retVal;
+	}
+
+	Matrix3& operator*=(const Matrix3& right)
+	{
+		*this = *this * right;
+		return *this;
+	}
+
+	// Create a scale matrix with x and y scales
+	static Matrix3 CreateScale(float xScale, float yScale)
+	{
+		float temp[3][3] =
+		{
+			{ xScale, 0.0f, 0.0f },
+			{ 0.0f, yScale, 0.0f },
+			{ 0.0f, 0.0f, 1.0f },
+		};
+		return Matrix3(temp);
+	}
+
+	static Matrix3 CreateScale(const Vector2& scaleVector)
+	{
+		return CreateScale(scaleVector.x, scaleVector.y);
+	}
+
+	// Create a scale matrix with a uniform factor
+	static Matrix3 CreateScale(float scale)
+	{
+		return CreateScale(scale, scale);
+	}
+
+	// Create a rotation matrix about the Z axis
+	// theta is in radians
+	static Matrix3 CreateRotation(float theta)
+	{
+		float temp[3][3] =
+		{
+			{ Math::Cos(theta), Math::Sin(theta), 0.0f },
+			{ -Math::Sin(theta), Math::Cos(theta), 0.0f },
+			{ 0.0f, 0.0f, 1.0f },
+		};
+		return Matrix3(temp);
+	}
+
+	// Create a translation matrix (on the xy-plane)
+	static Matrix3 CreateTranslation(const Vector2& trans)
+	{
+		float temp[3][3] =
+		{
+			{ 1.0f, 0.0f, 0.0f },
+			{ 0.0f, 1.0f, 0.0f },
+			{ trans.x, trans.y, 1.0f },
+		};
+		return Matrix3(temp);
+	}
+
+	static const Matrix3 Identity;
+};
+
+// 4x4 Matrix
+class Matrix4
+{
+public:
+	float mat[4][4];
+
+	Matrix4()
+	{
+		*this = Matrix4::Identity;
+	}
+
+	explicit Matrix4(float inMat[4][4])
+	{
+		memcpy(mat, inMat, 16 * sizeof(float));
+	}
+
+	// Cast to a const float pointer
+	const float* GetAsFloatPtr() const
+	{
+		return reinterpret_cast<const float*>(&mat[0][0]);
+	}
+
+	// Matrix multiplication (a * b)
+	friend Matrix4 operator*(const Matrix4& a, const Matrix4& b)
+	{
+		Matrix4 retVal;
+		// row 0
+		retVal.mat[0][0] = 
+			a.mat[0][0] * b.mat[0][0] + 
+			a.mat[0][1] * b.mat[1][0] + 
+			a.mat[0][2] * b.mat[2][0] +
+			a.mat[0][3] * b.mat[3][0];
+
+		retVal.mat[0][1] = 
+			a.mat[0][0] * b.mat[0][1] + 
+			a.mat[0][1] * b.mat[1][1] + 
+			a.mat[0][2] * b.mat[2][1] + 
+			a.mat[0][3] * b.mat[3][1];
+
+		retVal.mat[0][2] = 
+			a.mat[0][0] * b.mat[0][2] + 
+			a.mat[0][1] * b.mat[1][2] + 
+			a.mat[0][2] * b.mat[2][2] + 
+			a.mat[0][3] * b.mat[3][2];
+		
+		retVal.mat[0][3] = 
+			a.mat[0][0] * b.mat[0][3] + 
+			a.mat[0][1] * b.mat[1][3] + 
+			a.mat[0][2] * b.mat[2][3] + 
+			a.mat[0][3] * b.mat[3][3];
+
+		// row 1
+		retVal.mat[1][0] = 
+			a.mat[1][0] * b.mat[0][0] + 
+			a.mat[1][1] * b.mat[1][0] + 
+			a.mat[1][2] * b.mat[2][0] + 
+			a.mat[1][3] * b.mat[3][0];
+
+		retVal.mat[1][1] = 
+			a.mat[1][0] * b.mat[0][1] + 
+			a.mat[1][1] * b.mat[1][1] + 
+			a.mat[1][2] * b.mat[2][1] + 
+			a.mat[1][3] * b.mat[3][1];
+
+		retVal.mat[1][2] = 
+			a.mat[1][0] * b.mat[0][2] + 
+			a.mat[1][1] * b.mat[1][2] + 
+			a.mat[1][2] * b.mat[2][2] + 
+			a.mat[1][3] * b.mat[3][2];
+
+		retVal.mat[1][3] = 
+			a.mat[1][0] * b.mat[0][3] +
+			a.mat[1][1] * b.mat[1][3] +
+			a.mat[1][2] * b.mat[2][3] +
+			a.mat[1][3] * b.mat[3][3];
+
+		// row 2
+		retVal.mat[2][0] = 
+			a.mat[2][0] * b.mat[0][0] +
+			a.mat[2][1] * b.mat[1][0] +
+			a.mat[2][2] * b.mat[2][0] +
+			a.mat[2][3] * b.mat[3][0];
+
+		retVal.mat[2][1] = 
+			a.mat[2][0] * b.mat[0][1] + 
+			a.mat[2][1] * b.mat[1][1] + 
+			a.mat[2][2] * b.mat[2][1] + 
+			a.mat[2][3] * b.mat[3][1];
+
+		retVal.mat[2][2] = 
+			a.mat[2][0] * b.mat[0][2] +
+			a.mat[2][1] * b.mat[1][2] + 
+			a.mat[2][2] * b.mat[2][2] + 
+			a.mat[2][3] * b.mat[3][2];
+
+		retVal.mat[2][3] = 
+			a.mat[2][0] * b.mat[0][3] + 
+			a.mat[2][1] * b.mat[1][3] + 
+			a.mat[2][2] * b.mat[2][3] + 
+			a.mat[2][3] * b.mat[3][3];
+
+		// row 3
+		retVal.mat[3][0] = 
+			a.mat[3][0] * b.mat[0][0] + 
+			a.mat[3][1] * b.mat[1][0] + 
+			a.mat[3][2] * b.mat[2][0] + 
+			a.mat[3][3] * b.mat[3][0];
+
+		retVal.mat[3][1] = 
+			a.mat[3][0] * b.mat[0][1] + 
+			a.mat[3][1] * b.mat[1][1] + 
+			a.mat[3][2] * b.mat[2][1] + 
+			a.mat[3][3] * b.mat[3][1];
+
+		retVal.mat[3][2] = 
+			a.mat[3][0] * b.mat[0][2] +
+			a.mat[3][1] * b.mat[1][2] +
+			a.mat[3][2] * b.mat[2][2] +
+			a.mat[3][3] * b.mat[3][2];
+
+		retVal.mat[3][3] = 
+			a.mat[3][0] * b.mat[0][3] +
+			a.mat[3][1] * b.mat[1][3] +
+			a.mat[3][2] * b.mat[2][3] +
+			a.mat[3][3] * b.mat[3][3];
+		
+		return retVal;
+	}
+
+	Matrix4& operator*=(const Matrix4& right)
+	{
+		*this = *this * right;
+		return *this;
+	}
+
+	// Invert the matrix - super slow
+	void Invert();
+
+	// Get the translation component of the matrix
+	Vector3 GetTranslation() const
+	{
+		return Vector3(mat[3][0], mat[3][1], mat[3][2]);
+	}
+	
+	// Get the X axis of the matrix (forward)
+	Vector3 GetXAxis() const
+	{
+		return Vector3::Normalize(Vector3(mat[0][0], mat[0][1], mat[0][2]));
+	}
+
+	// Get the Y axis of the matrix (left)
+	Vector3 GetYAxis() const
+	{
+		return Vector3::Normalize(Vector3(mat[1][0], mat[1][1], mat[1][2]));
+	}
+
+	// Get the Z axis of the matrix (up)
+	Vector3 GetZAxis() const
+	{
+		return Vector3::Normalize(Vector3(mat[2][0], mat[2][1], mat[2][2]));
+	}
+
+	// Extract the scale component from the matrix
+	Vector3 GetScale() const
+	{
+		Vector3 retVal;
+		retVal.x = Vector3(mat[0][0], mat[0][1], mat[0][2]).Length();
+		retVal.y = Vector3(mat[1][0], mat[1][1], mat[1][2]).Length();
+		retVal.z = Vector3(mat[2][0], mat[2][1], mat[2][2]).Length();
+		return retVal;
+	}
+
+	// Create a scale matrix with x, y, and z scales
+	static Matrix4 CreateScale(float xScale, float yScale, float zScale)
+	{
+		float temp[4][4] =
+		{
+			{ xScale, 0.0f, 0.0f, 0.0f },
+			{ 0.0f, yScale, 0.0f, 0.0f },
+			{ 0.0f, 0.0f, zScale, 0.0f },
+			{ 0.0f, 0.0f, 0.0f, 1.0f }
+		};
+		return Matrix4(temp);
+	}
+
+	static Matrix4 CreateScale(const Vector3& scaleVector)
+	{
+		return CreateScale(scaleVector.x, scaleVector.y, scaleVector.z);
+	}
+
+	// Create a scale matrix with a uniform factor
+	static Matrix4 CreateScale(float scale)
+	{
+		return CreateScale(scale, scale, scale);
+	}
+
+	// Rotation about x-axis
+	static Matrix4 CreateRotationX(float theta)
+	{
+		float temp[4][4] =
+		{
+			{ 1.0f, 0.0f, 0.0f , 0.0f },
+			{ 0.0f, Math::Cos(theta), Math::Sin(theta), 0.0f },
+			{ 0.0f, -Math::Sin(theta), Math::Cos(theta), 0.0f },
+			{ 0.0f, 0.0f, 0.0f, 1.0f },
+		};
+		return Matrix4(temp);
+	}
+
+	// Rotation about y-axis
+	static Matrix4 CreateRotationY(float theta)
+	{
+		float temp[4][4] =
+		{
+			{ Math::Cos(theta), 0.0f, -Math::Sin(theta), 0.0f },
+			{ 0.0f, 1.0f, 0.0f, 0.0f },
+			{ Math::Sin(theta), 0.0f, Math::Cos(theta), 0.0f },
+			{ 0.0f, 0.0f, 0.0f, 1.0f },
+		};
+		return Matrix4(temp);
+	}
+
+	// Rotation about z-axis
+	static Matrix4 CreateRotationZ(float theta)
+	{
+		float temp[4][4] =
+		{
+			{ Math::Cos(theta), Math::Sin(theta), 0.0f, 0.0f },
+			{ -Math::Sin(theta), Math::Cos(theta), 0.0f, 0.0f },
+			{ 0.0f, 0.0f, 1.0f, 0.0f },
+			{ 0.0f, 0.0f, 0.0f, 1.0f },
+		};
+		return Matrix4(temp);
+	}
+
+	// Create a rotation matrix from a quaternion
+	static Matrix4 CreateFromQuaternion(const class Quaternion& q);
+
+	static Matrix4 CreateTranslation(const Vector3& trans)
+	{
+		float temp[4][4] =
+		{
+			{ 1.0f, 0.0f, 0.0f, 0.0f },
+			{ 0.0f, 1.0f, 0.0f, 0.0f },
+			{ 0.0f, 0.0f, 1.0f, 0.0f },
+			{ trans.x, trans.y, trans.z, 1.0f }
+		};
+		return Matrix4(temp);
+	}
+
+	static Matrix4 CreateLookAt(const Vector3& eye, const Vector3& target, const Vector3& up)
+	{
+		Vector3 zaxis = Vector3::Normalize(target - eye);
+		Vector3 xaxis = Vector3::Normalize(Vector3::Cross(up, zaxis));
+		Vector3 yaxis = Vector3::Normalize(Vector3::Cross(zaxis, xaxis));
+		Vector3 trans;
+		trans.x = -Vector3::Dot(xaxis, eye);
+		trans.y = -Vector3::Dot(yaxis, eye);
+		trans.z = -Vector3::Dot(zaxis, eye);
+
+		float temp[4][4] =
+		{
+			{ xaxis.x, yaxis.x, zaxis.x, 0.0f },
+			{ xaxis.y, yaxis.y, zaxis.y, 0.0f },
+			{ xaxis.z, yaxis.z, zaxis.z, 0.0f },
+			{ trans.x, trans.y, trans.z, 1.0f }
+		};
+		return Matrix4(temp);
+	}
+
+	static Matrix4 CreateOrtho(float width, float height, float near, float far)
+	{
+		float temp[4][4] =
+		{
+			{ 2.0f / width, 0.0f, 0.0f, 0.0f },
+			{ 0.0f, 2.0f / height, 0.0f, 0.0f },
+			{ 0.0f, 0.0f, 1.0f / (far - near), 0.0f },
+			{ 0.0f, 0.0f, near / (near - far), 1.0f }
+		};
+		return Matrix4(temp);
+	}
+
+	static Matrix4 CreatePerspectiveFOV(float fovY, float width, float height, float near, float far)
+	{
+		float yScale = Math::Cot(fovY / 2.0f);
+		float xScale = yScale * height / width;
+		float temp[4][4] =
+		{
+			{ xScale, 0.0f, 0.0f, 0.0f },
+			{ 0.0f, yScale, 0.0f, 0.0f },
+			{ 0.0f, 0.0f, far / (far - near), 1.0f },
+			{ 0.0f, 0.0f, -near * far / (far - near), 0.0f }
+		};
+		return Matrix4(temp);
+	}
+
+	// Create "Simple" View-Projection Matrix from Chapter 6
+	static Matrix4 CreateSimpleViewProj(float width, float height)
+	{
+		float temp[4][4] =
+		{
+			{ 2.0f/width, 0.0f, 0.0f, 0.0f },
+			{ 0.0f, 2.0f/height, 0.0f, 0.0f },
+			{ 0.0f, 0.0f, 1.0f, 0.0f },
+			{ 0.0f, 0.0f, 1.0f, 1.0f }
+		};
+		return Matrix4(temp);
+	}
+	
+	static const Matrix4 Identity;
+};
+
+// (Unit) Quaternion
+class Quaternion
+{
+public:
+	float x;
+	float y;
+	float z;
+	float w;
+
+	Quaternion()
+	{
+		*this = Quaternion::Identity;
+	}
+
+	// This directly sets the quaternion components --
+	// don't use for axis/angle
+	explicit Quaternion(float inX, float inY, float inZ, float inW)
+	{
+		Set(inX, inY, inZ, inW);
+	}
+
+	// Construct the quaternion from an axis and angle
+	// It is assumed that axis is already normalized,
+	// and the angle is in radians
+	explicit Quaternion(const Vector3& axis, float angle)
+	{
+		float scalar = Math::Sin(angle / 2.0f);
+		x = axis.x * scalar;
+		y = axis.y * scalar;
+		z = axis.z * scalar;
+		w = Math::Cos(angle / 2.0f);
+	}
+
+	// Directly set the internal components
+	void Set(float inX, float inY, float inZ, float inW)
+	{
+		x = inX;
+		y = inY;
+		z = inZ;
+		w = inW;
+	}
+
+	void Conjugate()
+	{
+		x *= -1.0f;
+		y *= -1.0f;
+		z *= -1.0f;
+	}
+
+	float LengthSq() const
+	{
+		return (x*x + y*y + z*z + w*w);
+	}
+
+	float Length() const
+	{
+		return Math::Sqrt(LengthSq());
+	}
+
+	void Normalize()
+	{
+		float length = Length();
+		x /= length;
+		y /= length;
+		z /= length;
+		w /= length;
+	}
+
+	// Normalize the provided quaternion
+	static Quaternion Normalize(const Quaternion& q)
+	{
+		Quaternion retVal = q;
+		retVal.Normalize();
+		return retVal;
+	}
+
+	// Linear interpolation
+	static Quaternion Lerp(const Quaternion& a, const Quaternion& b, float f)
+	{
+		Quaternion retVal;
+		retVal.x = Math::Lerp(a.x, b.x, f);
+		retVal.y = Math::Lerp(a.y, b.y, f);
+		retVal.z = Math::Lerp(a.z, b.z, f);
+		retVal.w = Math::Lerp(a.w, b.w, f);
+		retVal.Normalize();
+		return retVal;
+	}
+
+	static float Dot(const Quaternion& a, const Quaternion& b)
+	{
+		return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
+	}
+
+	// Spherical Linear Interpolation
+	static Quaternion Slerp(const Quaternion& a, const Quaternion& b, float f)
+	{
+		float rawCosm = Quaternion::Dot(a, b);
+
+		float cosom = -rawCosm;
+		if (rawCosm >= 0.0f)
+		{
+			cosom = rawCosm;
+		}
+
+		float scale0, scale1;
+
+		if (cosom < 0.9999f)
+		{
+			const float omega = Math::Acos(cosom);
+			const float invSin = 1.f / Math::Sin(omega);
+			scale0 = Math::Sin((1.f - f) * omega) * invSin;
+			scale1 = Math::Sin(f * omega) * invSin;
+		}
+		else
+		{
+			// Use linear interpolation if the quaternions
+			// are collinear
+			scale0 = 1.0f - f;
+			scale1 = f;
+		}
+
+		if (rawCosm < 0.0f)
+		{
+			scale1 = -scale1;
+		}
+
+		Quaternion retVal;
+		retVal.x = scale0 * a.x + scale1 * b.x;
+		retVal.y = scale0 * a.y + scale1 * b.y;
+		retVal.z = scale0 * a.z + scale1 * b.z;
+		retVal.w = scale0 * a.w + scale1 * b.w;
+		retVal.Normalize();
+		return retVal;
+	}
+
+	// Concatenate
+	// Rotate by q FOLLOWED BY p
+	static Quaternion Concatenate(const Quaternion& q, const Quaternion& p)
+	{
+		Quaternion retVal;
+
+		// Vector component is:
+		// ps * qv + qs * pv + pv x qv
+		Vector3 qv(q.x, q.y, q.z);
+		Vector3 pv(p.x, p.y, p.z);
+		Vector3 newVec = p.w * qv + q.w * pv + Vector3::Cross(pv, qv);
+		retVal.x = newVec.x;
+		retVal.y = newVec.y;
+		retVal.z = newVec.z;
+
+		// Scalar component is:
+		// ps * qs - pv . qv
+		retVal.w = p.w * q.w - Vector3::Dot(pv, qv);
+
+		return retVal;
+	}
+
+	static const Quaternion Identity;
+};
+
+namespace Color
+{
+	static const Vector3 Black(0.0f, 0.0f, 0.0f);
+	static const Vector3 White(1.0f, 1.0f, 1.0f);
+	static const Vector3 Red(1.0f, 0.0f, 0.0f);
+	static const Vector3 Green(0.0f, 1.0f, 0.0f);
+	static const Vector3 Blue(0.0f, 0.0f, 1.0f);
+	static const Vector3 Yellow(1.0f, 1.0f, 0.0f);
+	static const Vector3 LightYellow(1.0f, 1.0f, 0.88f);
+	static const Vector3 LightBlue(0.68f, 0.85f, 0.9f);
+	static const Vector3 LightPink(1.0f, 0.71f, 0.76f);
+	static const Vector3 LightGreen(0.56f, 0.93f, 0.56f);
+}

+ 41 - 0
Chapter08/MoveComponent.cpp

@@ -0,0 +1,41 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "MoveComponent.h"
+#include "Actor.h"
+
+MoveComponent::MoveComponent(class Actor* owner, int updateOrder)
+:Component(owner, updateOrder)
+,mAngularSpeed(0.0f)
+,mForwardSpeed(0.0f)
+{
+	
+}
+
+void MoveComponent::Update(float deltaTime)
+{
+	if (!Math::NearZero(mAngularSpeed))
+	{
+		float rot = mOwner->GetRotation();
+		rot += mAngularSpeed * deltaTime;
+		mOwner->SetRotation(rot);
+	}
+	
+	if (!Math::NearZero(mForwardSpeed))
+	{
+		Vector2 pos = mOwner->GetPosition();
+		pos += mOwner->GetForward() * mForwardSpeed * deltaTime;
+
+		// Screen wrapping (for asteroids)
+		if (pos.x < -512.0f) { pos.x = 510.0f; }
+		else if (pos.x > 512.0f) { pos.x = -510.0f; }
+		if (pos.y < -384.0f) { pos.y = 382.0f; }
+		else if (pos.y > 384.0f) { pos.y = -382.0f; }
+		mOwner->SetPosition(pos);
+	}
+}

+ 26 - 0
Chapter08/MoveComponent.h

@@ -0,0 +1,26 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+#include "Component.h"
+
+class MoveComponent : public Component
+{
+public:
+	// Lower update order to update first
+	MoveComponent(class Actor* owner, int updateOrder = 10);
+	void Update(float deltaTime) override;
+	
+	float GetAngularSpeed() const { return mAngularSpeed; }
+	float GetForwardSpeed() const { return mForwardSpeed; }
+	void SetAngularSpeed(float speed) { mAngularSpeed = speed; }
+	void SetForwardSpeed(float speed) { mForwardSpeed = speed; }
+private:
+	float mAngularSpeed;
+	float mForwardSpeed;
+};

+ 51 - 0
Chapter08/Random.cpp

@@ -0,0 +1,51 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "Random.h"
+
+void Random::Init()
+{
+	std::random_device rd;
+	Random::Seed(rd());
+}
+
+void Random::Seed(unsigned int seed)
+{
+	sGenerator.seed(seed);
+}
+
+float Random::GetFloat()
+{
+	return GetFloatRange(0.0f, 1.0f);
+}
+
+float Random::GetFloatRange(float min, float max)
+{
+	std::uniform_real_distribution<float> dist(min, max);
+	return dist(sGenerator);
+}
+
+int Random::GetIntRange(int min, int max)
+{
+	std::uniform_int_distribution<int> dist(min, max);
+	return dist(sGenerator);
+}
+
+Vector2 Random::GetVector(const Vector2& min, const Vector2& max)
+{
+	Vector2 r = Vector2(GetFloat(), GetFloat());
+	return min + (max - min) * r;
+}
+
+Vector3 Random::GetVector(const Vector3& min, const Vector3& max)
+{
+	Vector3 r = Vector3(GetFloat(), GetFloat(), GetFloat());
+	return min + (max - min) * r;
+}
+
+std::mt19937 Random::sGenerator;

+ 36 - 0
Chapter08/Random.h

@@ -0,0 +1,36 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma  once
+#include <random>
+#include "Math.h"
+
+class Random
+{
+public:
+	static void Init();
+
+	// Seed the generator with the specified int
+	// NOTE: You should generally not need to manually use this
+	static void Seed(unsigned int seed);
+
+	// Get a float between 0.0f and 1.0f
+	static float GetFloat();
+	
+	// Get a float from the specified range
+	static float GetFloatRange(float min, float max);
+
+	// Get an int from the specified range
+	static int GetIntRange(int min, int max);
+
+	// Get a random vector given the min/max bounds
+	static Vector2 GetVector(const Vector2& min, const Vector2& max);
+	static Vector3 GetVector(const Vector3& min, const Vector3& max);
+private:
+	static std::mt19937 sGenerator;
+};

+ 148 - 0
Chapter08/Shader.cpp

@@ -0,0 +1,148 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "Shader.h"
+#include "Texture.h"
+#include <SDL/SDL.h>
+#include <fstream>
+#include <sstream>
+
+Shader::Shader()
+	: mShaderProgram(0)
+	, mVertexShader(0)
+	, mFragShader(0)
+{
+	
+}
+
+Shader::~Shader()
+{
+
+}
+
+bool Shader::Load(const std::string& vertName, const std::string& fragName)
+{
+	// Compile vertex and pixel shaders
+	if (!CompileShader(vertName,
+		GL_VERTEX_SHADER,
+		mVertexShader) ||
+		!CompileShader(fragName,
+			GL_FRAGMENT_SHADER,
+			mFragShader))
+	{
+		return false;
+	}
+	
+	// Now create a shader program that
+	// links together the vertex/frag shaders
+	mShaderProgram = glCreateProgram();
+	glAttachShader(mShaderProgram, mVertexShader);
+	glAttachShader(mShaderProgram, mFragShader);
+	glLinkProgram(mShaderProgram);
+	
+	// Verify that the program linked successfully
+	if (!IsValidProgram())
+	{
+		return false;
+	}
+	
+	return true;
+}
+
+void Shader::Unload()
+{
+	// Delete the program/shaders
+	glDeleteProgram(mShaderProgram);
+	glDeleteShader(mVertexShader);
+	glDeleteShader(mFragShader);
+}
+
+void Shader::SetActive()
+{
+	// Set this program as the active one
+	glUseProgram(mShaderProgram);
+}
+
+void Shader::SetMatrixUniform(const char* name, const Matrix4& matrix)
+{
+	// Find the uniform by this name
+	GLuint loc = glGetUniformLocation(mShaderProgram, name);
+	// Send the matrix data to the uniform
+	glUniformMatrix4fv(loc, 1, GL_TRUE, matrix.GetAsFloatPtr());
+}
+
+bool Shader::CompileShader(const std::string& fileName,
+	GLenum shaderType,
+	GLuint& outShader)
+{
+	// Open file
+	std::ifstream shaderFile(fileName);
+	if (shaderFile.is_open())
+	{
+		// Read all the text into a string
+		std::stringstream sstream;
+		sstream << shaderFile.rdbuf();
+		std::string contents = sstream.str();
+		const char* contentsChar = contents.c_str();
+		
+		// Create a shader of the specified type
+		outShader = glCreateShader(shaderType);
+		// Set the source characters and try to compile
+		glShaderSource(outShader, 1, &(contentsChar), nullptr);
+		glCompileShader(outShader);
+		
+		if (!IsCompiled(outShader))
+		{
+			SDL_Log("Failed to compile shader %s", fileName.c_str());
+			return false;
+		}
+	}
+	else
+	{
+		SDL_Log("Shader file not found: %s", fileName.c_str());
+		return false;
+	}
+	
+	return true;
+}
+
+bool Shader::IsCompiled(GLuint shader)
+{
+	GLint status;
+	// Query the compile status
+	glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+	
+	if (status != GL_TRUE)
+	{
+		char buffer[512];
+		memset(buffer, 0, 512);
+		glGetShaderInfoLog(shader, 511, nullptr, buffer);
+		SDL_Log("GLSL Compile Failed:\n%s", buffer);
+		return false;
+	}
+	
+	return true;
+}
+
+bool Shader::IsValidProgram()
+{
+	
+	GLint status;
+	// Query the link status
+	glGetProgramiv(mShaderProgram, GL_LINK_STATUS, &status);
+	if (status != GL_TRUE)
+	{
+		char buffer[512];
+		memset(buffer, 0, 512);
+		glGetProgramInfoLog(mShaderProgram, 511, nullptr, buffer);
+		SDL_Log("GLSL Link Status:\n%s", buffer);
+		return false;
+	}
+	
+	return true;
+}

+ 41 - 0
Chapter08/Shader.h

@@ -0,0 +1,41 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+#include <GL/glew.h>
+#include <string>
+#include "Math.h"
+
+class Shader
+{
+public:
+	Shader();
+	~Shader();
+	// Load the vertex/fragment shaders with the given names
+	bool Load(const std::string& vertName, const std::string& fragName);
+	void Unload();
+	// Set this as the active shader program
+	void SetActive();
+	// Sets a Matrix uniform
+	void SetMatrixUniform(const char* name, const Matrix4& matrix);
+private:
+	// Tries to compile the specified shader
+	bool CompileShader(const std::string& fileName,
+					   GLenum shaderType,
+					   GLuint& outShader);
+	
+	// Tests whether shader compiled successfully
+	bool IsCompiled(GLuint shader);
+	// Tests whether vertex/fragment programs link
+	bool IsValidProgram();
+private:
+	// Store the shader object IDs
+	GLuint mVertexShader;
+	GLuint mFragShader;
+	GLuint mShaderProgram;
+};

+ 20 - 0
Chapter08/Shaders/Basic.frag

@@ -0,0 +1,20 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+// Request GLSL 3.3
+#version 330
+
+// This corresponds to the output color
+// to the color buffer
+out vec4 outColor;
+
+void main()
+{
+	// RGBA of 100% blue, 100% opaque
+    outColor = vec4(0.0, 0.0, 1.0, 1.0);
+}

+ 23 - 0
Chapter08/Shaders/Basic.vert

@@ -0,0 +1,23 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+// Request GLSL 3.3
+#version 330
+
+// This should correspond to the data stored
+// for each vertex in the vertex buffer.
+// For now, just a position.
+in vec3 inPosition;
+
+void main()
+{
+	// The vertex shader needs to output a 4D
+	// coordinate.
+	// For now set the 4th coordinate to 1.0
+	gl_Position = vec4(inPosition, 1.0);
+}

+ 25 - 0
Chapter08/Shaders/Sprite.frag

@@ -0,0 +1,25 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+// Request GLSL 3.3
+#version 330
+
+// Tex coord input from vertex shader
+in vec2 fragTexCoord;
+
+// This corresponds to the output color to the color buffer
+out vec4 outColor;
+
+// This is used for the texture sampling
+uniform sampler2D uTexture;
+
+void main()
+{
+	// Sample color from texture
+	outColor = texture(uTexture, fragTexCoord);
+}

+ 33 - 0
Chapter08/Shaders/Sprite.vert

@@ -0,0 +1,33 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+// Request GLSL 3.3
+#version 330
+
+// Uniforms for world transform and view-proj
+uniform mat4 uWorldTransform;
+uniform mat4 uViewProj;
+
+// Attribute 0 is position, 1 is tex coords.
+layout(location = 0) in vec3 inPosition;
+layout(location = 1) in vec2 inTexCoord;
+
+// Add texture coordinate as output
+out vec2 fragTexCoord;
+
+void main()
+{
+	// Convert position to homogeneous coordinates
+	vec4 pos = vec4(inPosition, 1.0);
+	// Transform position to world space, then clip space
+	gl_Position = pos * uWorldTransform * uViewProj;
+
+	// Transform
+	// Pass along the texture coordinate to frag shader
+	fragTexCoord = inTexCoord;
+}

+ 23 - 0
Chapter08/Shaders/Transform.vert

@@ -0,0 +1,23 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+// Request GLSL 3.3
+#version 330
+
+// Uniforms for world transform and view-proj
+uniform mat4 uWorldTransform;
+uniform mat4 uViewProj;
+
+// Vertex attributes
+in vec3 inPosition;
+
+void main()
+{
+	vec4 pos = vec4(inPosition, 1.0);
+	gl_Position = pos * uWorldTransform * uViewProj;
+}

+ 50 - 0
Chapter08/Ship.cpp

@@ -0,0 +1,50 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "Ship.h"
+#include "SpriteComponent.h"
+#include "InputComponent.h"
+#include "Game.h"
+#include "Laser.h"
+
+Ship::Ship(Game* game)
+	:Actor(game)
+	,mLaserCooldown(0.0f)
+{
+	// Create a sprite component
+	SpriteComponent* sc = new SpriteComponent(this, 150);
+	sc->SetTexture(game->GetTexture("Assets/Ship.png"));
+
+	// Create an input component and set keys/speed
+	InputComponent* ic = new InputComponent(this);
+	ic->SetForwardKey(SDL_SCANCODE_W);
+	ic->SetBackKey(SDL_SCANCODE_S);
+	ic->SetClockwiseKey(SDL_SCANCODE_A);
+	ic->SetCounterClockwiseKey(SDL_SCANCODE_D);
+	ic->SetMaxForwardSpeed(300.0f);
+	ic->SetMaxAngularSpeed(Math::TwoPi);
+}
+
+void Ship::UpdateActor(float deltaTime)
+{
+	mLaserCooldown -= deltaTime;
+}
+
+void Ship::ActorInput(const uint8_t* keyState)
+{
+	if (keyState[SDL_SCANCODE_SPACE] && mLaserCooldown <= 0.0f)
+	{
+		// Create a laser and set its position/rotation to mine
+		Laser* laser = new Laser(GetGame());
+		laser->SetPosition(GetPosition());
+		laser->SetRotation(GetRotation());
+
+		// Reset laser cooldown (half second)
+		mLaserCooldown = 0.5f;
+	}
+}

+ 20 - 0
Chapter08/Ship.h

@@ -0,0 +1,20 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+#include "Actor.h"
+class Ship : public Actor
+{
+public:
+	Ship(class Game* game);
+
+	void UpdateActor(float deltaTime) override;
+	void ActorInput(const uint8_t* keyState) override;
+private:
+	float mLaserCooldown;
+};

+ 60 - 0
Chapter08/SpriteComponent.cpp

@@ -0,0 +1,60 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "SpriteComponent.h"
+#include "Texture.h"
+#include "Shader.h"
+#include "Actor.h"
+#include "Game.h"
+
+SpriteComponent::SpriteComponent(Actor* owner, int drawOrder)
+	:Component(owner)
+	,mTexture(nullptr)
+	,mDrawOrder(drawOrder)
+	,mTexWidth(0)
+	,mTexHeight(0)
+{
+	mOwner->GetGame()->AddSprite(this);
+}
+
+SpriteComponent::~SpriteComponent()
+{
+	mOwner->GetGame()->RemoveSprite(this);
+}
+
+void SpriteComponent::Draw(Shader* shader)
+{
+	if (mTexture)
+	{
+		// Scale the quad by the width/height of texture
+		Matrix4 scaleMat = Matrix4::CreateScale(
+			static_cast<float>(mTexWidth),
+			static_cast<float>(mTexHeight),
+			1.0f);
+		
+		Matrix4 world = scaleMat * mOwner->GetWorldTransform();
+		
+		// Since all sprites use the same shader/vertices,
+		// the game first sets them active before any sprite draws
+		
+		// Set world transform
+		shader->SetMatrixUniform("uWorldTransform", world);
+		// Set current texture
+		mTexture->SetActive();
+		// Draw quad
+		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
+	}
+}
+
+void SpriteComponent::SetTexture(Texture* texture)
+{
+	mTexture = texture;
+	// Set width/height
+	mTexWidth = texture->GetWidth();
+	mTexHeight = texture->GetHeight();
+}

+ 30 - 0
Chapter08/SpriteComponent.h

@@ -0,0 +1,30 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+#include "Component.h"
+#include "SDL/SDL.h"
+class SpriteComponent : public Component
+{
+public:
+	// (Lower draw order corresponds with further back)
+	SpriteComponent(class Actor* owner, int drawOrder = 100);
+	~SpriteComponent();
+
+	virtual void Draw(class Shader* shader);
+	virtual void SetTexture(class Texture* texture);
+
+	int GetDrawOrder() const { return mDrawOrder; }
+	int GetTexHeight() const { return mTexHeight; }
+	int GetTexWidth() const { return mTexWidth; }
+protected:
+	class Texture* mTexture;
+	int mDrawOrder;
+	int mTexWidth;
+	int mTexHeight;
+};

+ 69 - 0
Chapter08/Texture.cpp

@@ -0,0 +1,69 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "Texture.h"
+#include <SOIL/SOIL.h>
+#include <GL/glew.h>
+#include <SDL/SDL.h>
+
+Texture::Texture()
+:mTextureID(0)
+,mWidth(0)
+,mHeight(0)
+{
+	
+}
+
+Texture::~Texture()
+{
+	
+}
+
+bool Texture::Load(const std::string& fileName)
+{
+	int channels = 0;
+	
+	unsigned char* image = SOIL_load_image(fileName.c_str(),
+										   &mWidth, &mHeight, &channels, SOIL_LOAD_AUTO);
+	
+	if (image == nullptr)
+	{
+		SDL_Log("SOIL failed to load image %s: %s", fileName.c_str(), SOIL_last_result());
+		return false;
+	}
+	
+	int format = GL_RGB;
+	if (channels == 4)
+	{
+		format = GL_RGBA;
+	}
+	
+	glGenTextures(1, &mTextureID);
+	glBindTexture(GL_TEXTURE_2D, mTextureID);
+	
+	glTexImage2D(GL_TEXTURE_2D, 0, format, mWidth, mHeight, 0, format,
+				 GL_UNSIGNED_BYTE, image);
+	
+	SOIL_free_image_data(image);
+	
+	// Enable bilinear filtering
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	
+	return true;
+}
+
+void Texture::Unload()
+{
+	glDeleteTextures(1, &mTextureID);
+}
+
+void Texture::SetActive()
+{
+	glBindTexture(GL_TEXTURE_2D, mTextureID);
+}

+ 30 - 0
Chapter08/Texture.h

@@ -0,0 +1,30 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include <string>
+
+class Texture
+{
+public:
+	Texture();
+	~Texture();
+	
+	bool Load(const std::string& fileName);
+	void Unload();
+	
+	void SetActive();
+	
+	int GetWidth() const { return mWidth; }
+	int GetHeight() const { return mHeight; }
+private:
+	// OpenGL ID of this texture
+	unsigned int mTextureID;
+	// Width/height of the texture
+	int mWidth;
+	int mHeight;
+};

+ 51 - 0
Chapter08/VertexArray.cpp

@@ -0,0 +1,51 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#include "VertexArray.h"
+#include <GL/glew.h>
+
+VertexArray::VertexArray(const float* verts, unsigned int numVerts,
+	const unsigned int* indices, unsigned int numIndices)
+	:mNumVerts(numVerts)
+	,mNumIndices(numIndices)
+{
+	// Create vertex array
+	glGenVertexArrays(1, &mVertexArray);
+	glBindVertexArray(mVertexArray);
+
+	// Create vertex buffer
+	glGenBuffers(1, &mVertexBuffer);
+	glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
+	glBufferData(GL_ARRAY_BUFFER, numVerts * 5 * sizeof(float), verts, GL_STATIC_DRAW);
+
+	// Create index buffer
+	glGenBuffers(1, &mIndexBuffer);
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
+	glBufferData(GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof(unsigned int), indices, GL_STATIC_DRAW);
+
+	// Specify the vertex attributes
+	// (For now, assume one vertex format)
+	// Position is 3 floats starting at offset 0
+	glEnableVertexAttribArray(0);
+	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, 0);
+	glEnableVertexAttribArray(1);
+	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 5,
+		reinterpret_cast<void*>(sizeof(float) * 3));
+}
+
+VertexArray::~VertexArray()
+{
+	glDeleteBuffers(1, &mVertexBuffer);
+	glDeleteBuffers(1, &mIndexBuffer);
+	glDeleteVertexArrays(1, &mVertexArray);
+}
+
+void VertexArray::SetActive()
+{
+	glBindVertexArray(mVertexArray);
+}

+ 33 - 0
Chapter08/VertexArray.h

@@ -0,0 +1,33 @@
+// ----------------------------------------------------------------
+// From Game Programming in C++ by Sanjay Madhav
+// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
+// 
+// Released under the BSD License
+// See LICENSE in root directory for full details.
+// ----------------------------------------------------------------
+
+#pragma once
+class VertexArray
+{
+public:
+	VertexArray(const float* verts, unsigned int numVerts,
+		const unsigned int* indices, unsigned int numIndices);
+	~VertexArray();
+
+	// Activate this vertex array (so we can draw it)
+	void SetActive();
+
+	unsigned int GetNumIndices() const { return mNumIndices; }
+	unsigned int GetNumVerts() const { return mNumVerts; }
+private:
+	// How many vertices in the vertex buffer?
+	unsigned int mNumVerts;
+	// How many indices in the index buffer
+	unsigned int mNumIndices;
+	// OpenGL ID of the vertex buffer
+	unsigned int mVertexBuffer;
+	// OpenGL ID of the index buffer
+	unsigned int mIndexBuffer;
+	// OpenGL ID of the vertex array object
+	unsigned int mVertexArray;
+};