PDA

View Full Version : Rotating object after transformation in modern OpenGL?



eratic-magician
03-16-2017, 04:09 PM
I've seen a lot of posts that explain how to rotate an object around a point rather than around its own axis, via translating the object first and then applying the rotation. However, almost every post I see uses old functions like translatef and rotatef.

How would this be achieved in modern OpenGL, using the MVP (Model, View and Projection) framework?

For example, say I have this:


glm::mat4 RotationMatrix = glm::toMat4(myRotation);
glm::mat4 TranslationMatrix = translate(mat4(), myTranslation);
glm::mat4 ScaleMatrix = scale(mat4(), glm::vec3(0.1f, 0.1f, 0.1f));
glm::mat4 ModelMatrix = TranslationMatrix * RotationMatrix * ScaleMatrix;
glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;

This appears to move the object and rotate it along its own axis.

How can I move the object and THEN rotate it with this style of OpenGL? E.g. moving an object in front of the camera position, then rotating it around the camera position using the camera's orientation (so that wherever you look, the object is drawn in front of you).

BBeck1
03-16-2017, 06:51 PM
It's all in the order of multiplication. This is the foundation of rigid animation. For example, if you have a car with hub caps that spin backwards regardless of how the wheel spins, then the car body would be a parent, the wheel would be a child of the parent, and the hub cap would be a child of the wheel. Each would have their own object/world matrix to contain their position and orientation. But you don't use the wheel or the hub's matrix directly. Instead you multiply the child by the parent and use the result to position and orient it. This allows the child to be attached to the parent and relative to the parent but move freely on it's own.

I would challenge you to not build your model matrix every frame, but rather let the model matrix store the position and orientation of the model from one frame to another. Let it be the ultimate place to store the position and orientation of the object. You can decompose it when you need and get the position. (I think it's the last column in the matrix, or is it last row, I can never remember). You can also get a forward, up, or right vector out of it as needed.

If you do it every frame, you have to move the model back to the origin to rotate or scale and then move it back to where it was within a single frame. No one will know it happened. Otherwise, scale will be skewed, and rotation will orbit the origin. You could do your translations last so that things would occur at the origin. When you're at the origin, the object's center and the origin match and so you rotate around the object's center. If you translate first, they don't match and rotations become orbits around the origin. Scaling is even uglier.

Here's how I do a rotation:

Triangle.Transform(glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f)));

I pass an empty identity matrix to glm::rotate and a rotation giving me a rotation matrix. The Transform method is as follows:


void HandCodedObjectClass::Transform(glm::mat4 TransformationMatrix)
{
WorldMatrix *= TransformationMatrix;
}


So, I'm just combining a rotation with whatever is already in the object's world matrix. The bad part of this is that the order of multiplication is hard coded. I think like this it's a rotation, but you might want an orbit instead and then you would have to reverse the order of multiplication/combination.

For an example of how to rotate relative to another object I have this:


Cube2Pivot = Cube2Pivot * glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f));
Cube2.WorldMatrix = Cube.WorldMatrix * Cube2Pivot * Cube2World;


Here I have one cube orbiting another cube that you can move through the scene. Cube2Pivot is a matrix that determines the position and orientation of the second cube relative to the first (Cube).

If you are going to use the camera, realize that the camera is just like any other object's world matrix except it is inversed. To turn it into a regular world matrix, you must invert it. Then you can treat it like any other object's world matrix.

You might download my basic engine code on my website (http://virtuallyprogramming.com/OGL45/OGL45.html). The code in that project is pretty basic. There's a video on that web page that shows what the code does. So you can see what it does without downloading the code and see whether it's worth your time to download. The code above is from a slightly more complex version of the same code with these changes:



#include "Game.h"

using namespace OGLGameNameSpace;


Game::Game(void)
{
}


Game::~Game(void)
{
}


//================================================== ================================================== =================
// Game::Initialize()
//
// Purpose:
// To allow any code you want to run once at startup not associated with art assets.
//
// Input:
// None.
//
// Output:
// bool - The program will close if it returns false assuming a catastrophic error has occured.
//
// Notes:
//
//
//================================================== ================================================== =================
bool Game::Initialize()
{
bool GameObjectInitializedProperly = false; //Must be set to true to keep the program from closing.


CameraHeight = 1.68f; //Roughly the average eye height in meters of an average man. Our camera will stay at this level to make it feel like we are in the scene.
CameraTilt = 0.0f; //Will tilt the camera up and down.

GameObjectInitializedProperly = true; //This should probably be set by error checking, but we don't have any error checking here.
View = glm::lookAt(glm::vec3(0.0f, CameraHeight, 2.0f), glm::vec3(0.0f, CameraHeight, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); //1.63 meters is roughly the height of the average man's eyes.
//Projection = glm::perspective(0.96f, 1.770833f, 0.1f, 700.0f); //0.96 is 55 degrees and 1.7708333 is the width to height ratio on my computer.
Projection = glm::perspective(0.96f, OperatingSystem.AspectRatio(), 0.1f, 700.0f); //0.96 is 55 degrees and 1.7708333 is the width to height ratio on my computer.

glEnable(GL_CULL_FACE); //Turn back face culling on.
glCullFace(GL_BACK); //Set back face culling to remove the back face rather than the front face. Back is default. Included here for clarity.
glEnable(GL_DEPTH_TEST); //Enable the depth buffer so that things are drawn correctly.
glEnable(GL_FRAMEBUFFER_SRGB); //Needed to prepare frame buffer for sRGB color.


DiffuseLightDirection = glm::normalize(glm::vec3(1.0f, -1.0f, -1.0f)); //Direction that the primary light of the scene is "shining" in.
AmbientLightColor = glm::vec4(0.05f, 0.05f, 0.1f, 1.0f); //Light color in the "shadows".
DiffuseLightColor = glm::vec4(1.0f, 1.0f, 0.9f, 1.0f); //Direct light color.

return GameObjectInitializedProperly;
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Game::LoadContent()
//
// Purpose:
// To allow any code you want to run once at startup associated with art assets.
//
// Input:
// None.
//
// Output:
// bool - The program will close if it returns false assuming a catastrophic error has occured.
//
// Notes:
// Here we are creating all the objects in the scene. The switch statement makes it easy to manage this and add more
// objects of your own. You merely define all the objects vertices it will need to be drawn and then list the index order
// of every vertex to specify what order it will be drawn in. These examples merely count the number of vertices for the
// index which is largely pointless. However, the design means you "could" reuse vertices or specify them out of order and
// use the index buffer to specify what order to draw them in.
//
// You'll quickly notice that the number of vertices you have to specify for even the most simple of objects is nearly
// unacceptable. There are better ways, but we're learning the most straight forward way here which we will build on later.
//
// Textures were created with Paint.Net by saving as .DDS files with mip maps.
//
//================================================== ================================================== =================
bool Game::LoadContent()
{
bool NoCatastrophicFailuresOccured = false; //Close the program if even one mesh fails to initialize correctly.
float ScaleFactor = 200.0f;
float TerrainSize = 500.0f;


//GrassTexture.Load("Textures/Grass.dds", true);
GrassTexture.Load("Textures/FloorTiles.jpg", true);
//GrassTexture.Load("Textures/MudWall.jpg", true);

GrassTexture.Bind(0);

NoCatastrophicFailuresOccured = Shader.LoadShader("Shaders/BlinnPhong.vrt", "Shaders/BlinnPhong.frg");

glClearColor(0.392156862745098f, 0.5843137254901961f, 0.9294117647058824f, 1.0f); //XNA's "Cornflower Blue"


GLfloat TriangleVertexBuffer[] = {
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.45f, -0.5, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-0.45f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f
};

GLuint TriangleIndices[] = {
0,1,2
};
Triangle.DefineMesh(3, TriangleVertexBuffer, 3, TriangleIndices, nullptr);


GLfloat CubeVertexBuffer[] = {
//Front Quad.
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,

0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,

//Right Quad.
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

//Bottom Quad.
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

//Back Quad.
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,

-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,

//Left Quad.
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

//Top Quad.
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
};

GLuint CubeIndices[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 };
Cube.DefineMesh(VERTICESINCUBE, CubeVertexBuffer, VERTICESINCUBE, CubeIndices, nullptr);
Cube2.DefineMesh(VERTICESINCUBE, CubeVertexBuffer, VERTICESINCUBE, CubeIndices, nullptr);

GLfloat GroundVertexBuffer[] = {
-TerrainSize, 0.0f, TerrainSize, 0.0f, ScaleFactor, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
TerrainSize, 0.0f, TerrainSize, ScaleFactor, ScaleFactor, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
TerrainSize, 0.0f, -TerrainSize, ScaleFactor, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-TerrainSize, 0.0f, -TerrainSize, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f
};

GLuint GroundIndices[] = {
0,1,2,
0,2,3
};
Ground.DefineMesh(4, GroundVertexBuffer, 6, GroundIndices, &GrassTexture);

Cube2Pivot = glm::mat4();
Triangle.Transform(glm::translate(glm::mat4(), glm::vec3(0.0f, 1.63f, 0.0f)));
Cube.Transform(glm::translate(glm::mat4(), glm::vec3(-3.0f, 2.5f, -3.0f)));
//Cube2.Transform(glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, 1.0f)));
Cube2World = glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, 3.0f));

return NoCatastrophicFailuresOccured;
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Game::UnloadContent()
//
// Purpose:
// To allow any code you want to run as the program closes in order to cleanup.
//
// Input:
// None.
//
// Output:
// None.
//
// Notes:
// All the game objects you create need their Shutdown() methods called to release the associate vertex and index buffers.
// Since Shaders are owned by this class, it is also responsible for calling the Shutdown() method of any shaders created.
//
//================================================== ================================================== =================
void Game::UnloadContent()
{
//glDeleteVertexArrays(1, &vao);
//glDeleteBuffers(1, &vbo);
//glDeleteBuffers(1, &ibo);
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Game::Update()
//
// Purpose:
// To do everything that needs to be done in one frame except draw stuff to the screen.
//
// Input:
// float TimeDelta - Amount of time that has passed since the last frame occured in milliseconds.
//
// Output:
// None.
//
// Notes:
// This is where most of your game code will be. It gets called every frame to change anything that needs to be changed
// or done during that frame. It runs in a loop that needs to be called at least 30 times per second but there's nothing
// to control how often it gets called. Things would move at unpredictable rates if we did not use TimeDelta to take in to
// account the amount of time that has passed since the last frame.
//
// We start out by processing the keyboard and game controller input to change the camera's position and direction. You
// can also toggle full screen on and off.
//
// The camera movement this frame is stored as a 3D vector that we treat more like a 2D vector. The facing normal should
// point in the direction we want the camera to face. And as a normal should have a length of 1. Any movement during the
// frame is cumulative from the various controls. When you move it uses either the CameraFacingNormal or a normal rotated 90
// degrees away from the camera facing. It's basic vector addition to add the movement to the camera position.
//
// XMMatrixLookAtRH is used to create a view matrix to simulate the camera every frame. Generally, I would say it is a
// good idea to not continuously recreate the view matrix but it's easier then maintaining a view matrix between frames and
// this is really early in this tutorial series.
//
// Finally some very simple rigid animation is thrown in to show you not only how to do it, but that it can be done and how
// easy it is to do. Experiment by turning the rotations off and on and changing their directions and speed.
//
// The scene is lit with a simple Blinn-Phong shader that has "directional" lighting as opposed to point lights or
// spot lights. Directional lighting is nothing more than a direction that the light shines in. It is a normalized vector
// describing a direction and it has a color. That's all it is. Look at the shader for more detail. By rotating that direction
// the light source seems to orbit the scene similar to a day and night cycle except the light shines through solid objects.
//
//================================================== ================================================== =================
void Game::Update()
{
const float MaxTiltAngle = glm::radians(45.0);
const unsigned char* Buttons;
int JoyStick1Present = false;
int NumberOfJoyStickAxes = 0;
int NumberOfJoyStickButtons = 0;
const float* AxesArray = nullptr;
float LeftThumbStickY = 0.0f;
float LeftThumbStickX = 0.0f;
float Triggers = 0.0f; //XBox 360 controller triggers are a single axis for both triggers. Positive = Left. Negative = Right.
float RightThumbStickY = 0.0f;
float RightThumbStickX = 0.0f;
bool AButton = false;
bool BButton = false;


if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_ESCAPE && OperatingSystem.Keyboard.ActionPressed == GLFW_PRESS) OperatingSystem.ShutDown();

JoyStick1Present = glfwJoystickPresent(GLFW_JOYSTICK_1);
if (JoyStick1Present)
{
AxesArray = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &NumberOfJoyStickAxes);
Buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &NumberOfJoyStickButtons);
LeftThumbStickY = AxesArray[0];
LeftThumbStickX = AxesArray[1];
Triggers = AxesArray[2];
RightThumbStickY = AxesArray[3];
RightThumbStickX = AxesArray[4];

//Camera Controls with XBox 360 controller.
if (RightThumbStickX > 0.2 || RightThumbStickX < -0.2) View = glm::rotate(glm::mat4(), RightThumbStickX *0.06f, glm::vec3(0.0f, 1.0f, 0.0f)) * View;
if (LeftThumbStickX > 0.2 || LeftThumbStickX < -0.2) View = glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -LeftThumbStickX * 0.1f)) * View; //*0.1f to slow it down. Negative to flip the axis. -0.2 for deadzone.

if (RightThumbStickY > 0.2 || RightThumbStickY < -0.2) CameraTilt += 0.03 * RightThumbStickY;
if (LeftThumbStickY > 0.2 || LeftThumbStickY < -0.2) View = glm::translate(glm::mat4(), glm::vec3(-LeftThumbStickY * 0.1f, 0.0f, 0.0f)) * View;

if (Triggers > 0.2 || Triggers < -0.2) View = glm::translate(glm::mat4(), glm::vec3(0.0f, Triggers*0.1f, 0.0f)) * View;

if (Buttons[0] == '\x1') AButton = true;
if (Buttons[1] == '\x1') BButton = true;
if (Buttons[6] == '\x1') OperatingSystem.ShutDown();
}


//Camera Controls with keyboard.
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_W && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
View = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.05f)) * View;
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_S && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
View = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -0.05f)) * View;
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_E && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
CameraTilt += 0.1;
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_Q && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
CameraTilt -= 0.1;
if (OperatingSystem.Keyboard.ModePressed == GLFW_MOD_SHIFT)
{
//Keys while Shift keys are also held down.
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_A && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
View = glm::translate(glm::mat4(), glm::vec3(0.1f, 0.0f, 0.0f)) * View;
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_D && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
View = glm::translate(glm::mat4(), glm::vec3(-0.1f, 0.0f, 0.0f)) * View;
}
else
{
//Keys when shift keys are not being held down.
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_D && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
View = glm::rotate(glm::mat4(1.0f), 0.05f, glm::vec3(0.0f, 1.0f, 0.0f)) * View;
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_A && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
View = glm::rotate(glm::mat4(1.0f), -0.05f, glm::vec3(0.0f, 1.0f, 0.0f)) * View;
}


//Yellow cube controls.
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_I && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
Cube.Transform(glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, 0.05f)));
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_K && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
Cube.Transform(glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -0.05f)));
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_L && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
Cube.Transform(glm::rotate(glm::mat4(), glm::radians<float>(-1), glm::vec3(0.0f, 1.0f, 0.0f)));
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_J && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
Cube.Transform(glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f)));


if (CameraTilt > MaxTiltAngle) CameraTilt = MaxTiltAngle;
if (CameraTilt < -MaxTiltAngle) CameraTilt = -MaxTiltAngle;

if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_Y && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
Cube2Pivot = glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 0.0f, 1.0f)) * Cube2Pivot;
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_H && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
Cube2Pivot = glm::rotate(glm::mat4(), glm::radians<float>(-1), glm::vec3(0.0f, 0.0f, 1.0f)) * Cube2Pivot;

Triangle.Transform(glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f)));
Cube2Pivot = Cube2Pivot * glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f));
Cube2.WorldMatrix = Cube.WorldMatrix * Cube2Pivot * Cube2World;


}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Game::Draw()
//
// Purpose:
// To do draw a single frame on the computer screen.
//
// Input:
// float TimeDelta - Amount of time that has passed since the last frame occured in milliseconds.
//
// Output:
// None.
//
// Notes:
// Since all the drawing code is tucked away neatly elsewhere, this method ends up being very short and sweet. There's
// basically nothing to it. The first two lines clear the backbuffer in corn flower blue which is a constant. And then
// it clears the depth stencil. You can clear the screen in any color you like.
//
// Then each game object's Draw() method needs to be called to tell it to draw it self to the back buffer. The parameters
// for the shader have to be sent here.
//
// Finaly, the swapchain is told to make the backbuffer the frontbuffer and draw it to the screen by presenting it and
// the image appears on the computer monitor.
//
//================================================== ================================================== =================
void Game::Draw()
{
glm::mat4 TiltedView = glm::rotate(glm::mat4(), CameraTilt, glm::vec3(1.0, 0.0, 0.0)) * View;


glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

Triangle.Draw(TiltedView, Projection, &Shader, DiffuseLightDirection, AmbientLightColor, DiffuseLightColor);
Cube.Draw(TiltedView, Projection, &Shader, DiffuseLightDirection, AmbientLightColor, DiffuseLightColor);
Cube2.Draw(TiltedView, Projection, &Shader, DiffuseLightDirection, AmbientLightColor, DiffuseLightColor);
Ground.Draw(TiltedView, Projection, &Shader, DiffuseLightDirection, AmbientLightColor, DiffuseLightColor);

}
//================================================== ================================================== =================




The comments were from a previous version that doesn't exactly match the current code.

john_connor
03-17-2017, 02:35 AM
How would this be achieved in modern OpenGL, using the MVP (Model, View and Projection) framework?

...

This appears to move the object and rotate it along its own axis.

i also had this problem and it took me any hours to figure it out once and for all: my approach would be wrapping it up into a class, hiding the math behind it and only use class methods to operate with the data

what data ?
my solution is to store only vec3 position to represent a worldspace position, and a quaternion to represent the rotation, scaling is another issue and has nothing to do with and "orientation". afaik BBeck1's version is to store the complete matrix, that way you dont need to rebuild the "model-to-world" matrix later.
however, derived from that "orientation" class, i can easily create another "camera" class or an "AbstractSceneObject" class (whatever that should be, if it needs a "model-to-world" matrix later somewhere, i'll derive that class from "orientation")

take a look at the "orientation" class:
https://sites.google.com/site/john87connor/home/3-2-scene-orientation-camera

first you need to define a (right-handed) coordinate system, that "no rotation at all" will represent, my favorite way is to use the screens x-axis for "right", the screen y-axis for "up" ( because thats how the window appears to me) and the z-axis for "forward"


#define DEFAULT_DIRECTION_FORWARD (glm::vec3(0, 0, 1))
#define DEFAULT_DIRECTION_UP (glm::vec3(0, 1, 0))
#define DEFAULT_DIRECTION_RIGHT (glm::vec3(1, 0, 0))


based on these values, if you want a point in space to rotate (using a rotation matrix), you need to define that rotation around that coordinate system, for example you want to "roll" an spaceship (that means rotation around its "forward" direction), you need to create the rotation matrix around the constant "forward" axis (which is in my case vec3(0, 0, 1)), and rotate the the spaceship using that rotation matrix


void Orientation::Roll(float angle)
{
m_rotation = glm::rotate(m_rotation, angle, DEFAULT_DIRECTION_FORWARD);
}

here i rotate the "orientation"s quaternion member "m_rotation" (positive angle will rotate "to the right") around a constant axis, so that the rotation will be "added" to previous rotation

thats all

later if you want to build the "model-to-world" matrix including scaling, you can use:


glm::mat4 Orientation::Transformation(const glm::vec3& scalefactor) const
{
return glm::translate(m_position) * glm::toMat4(m_rotation) * glm::scale(scalefactor);
}

"vec3 scalefactor" is not part of an orientation, so it has to come from somewhere else ... (if needed, in the headder i've set it to vec3(1, 1, 1) if none is provided)

eratic-magician
03-17-2017, 12:45 PM
It's all in the order of multiplication. This is the foundation of rigid animation. For example, if you have a car with hub caps that spin backwards regardless of how the wheel spins, then the car body would be a parent, the wheel would be a child of the parent, and the hub cap would be a child of the wheel. Each would have their own object/world matrix to contain their position and orientation. But you don't use the wheel or the hub's matrix directly. Instead you multiply the child by the parent and use the result to position and orient it. This allows the child to be attached to the parent and relative to the parent but move freely on it's own.

I would challenge you to not build your model matrix every frame, but rather let the model matrix store the position and orientation of the model from one frame to another. Let it be the ultimate place to store the position and orientation of the object. You can decompose it when you need and get the position. (I think it's the last column in the matrix, or is it last row, I can never remember). You can also get a forward, up, or right vector out of it as needed.

If you do it every frame, you have to move the model back to the origin to rotate or scale and then move it back to where it was within a single frame. No one will know it happened. Otherwise, scale will be skewed, and rotation will orbit the origin. You could do your translations last so that things would occur at the origin. When you're at the origin, the object's center and the origin match and so you rotate around the object's center. If you translate first, they don't match and rotations become orbits around the origin. Scaling is even uglier.

Here's how I do a rotation:

Triangle.Transform(glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f)));

I pass an empty identity matrix to glm::rotate and a rotation giving me a rotation matrix. The Transform method is as follows:


void HandCodedObjectClass::Transform(glm::mat4 TransformationMatrix)
{
WorldMatrix *= TransformationMatrix;
}


So, I'm just combining a rotation with whatever is already in the object's world matrix. The bad part of this is that the order of multiplication is hard coded. I think like this it's a rotation, but you might want an orbit instead and then you would have to reverse the order of multiplication/combination.

For an example of how to rotate relative to another object I have this:


Cube2Pivot = Cube2Pivot * glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f));
Cube2.WorldMatrix = Cube.WorldMatrix * Cube2Pivot * Cube2World;


Here I have one cube orbiting another cube that you can move through the scene. Cube2Pivot is a matrix that determines the position and orientation of the second cube relative to the first (Cube).

If you are going to use the camera, realize that the camera is just like any other object's world matrix except it is inversed. To turn it into a regular world matrix, you must invert it. Then you can treat it like any other object's world matrix.

.

Your entire post is very helpful, cheers. I just have a few questions:


If you do it every frame, you have to move the model back to the origin to rotate or scale and then move it back to where it was within a single frame.
Which origin do you mean? The world's origin (where the model would render without any transformations) or the model's origin? I'm leaning more towards this approach, I'm just unsure about how you'd alter this code:


// Rotate bullet based on camera angles
glm::mat4 BulletRotationMatrix = glm::toMat4(bulletRotations[i]);
// Translate it by getPosition(), the camera's x, y and z, -1 on the z-axis
glm::mat4 BulletTranslationMatrix = translate(mat4(), bulletPositions[i]);
glm::mat4 BulletScaleMatrix = scale(mat4(), glm::vec3(0.1f, 0.1f, 0.1f));
glm::mat4 ModelMatrix2 = BulletTranslationMatrix * BulletRotationMatrix * BulletScaleMatrix;
glm::mat4 MVP2 = ProjectionMatrix * ViewMatrix * ModelMatrix2;

to do what you said. Would I translate to the world origin first, -bulletPositions[i] first etc.

Second, you mentioned:

You can decompose it [the matrix] when you need and get the position. (I think it's the last column in the matrix, or is it last row, I can never remember)
Do you know how exactly you'd get the last row/column? Say my object is being rotated as planned, I'm guessing I'd need the last row/column of the world-matrix (MVP = ProjectionMatrix * ViewMatrix * ModelMatrix) or the ModelMatrix. I need this in the form of a glm::vec3.

Hopefully the answers to those will help me out a lot, cheers.

john_connor
03-17-2017, 03:19 PM
if you want to apply a rotation to a "model-to-world" transformation matrix, you have to do that in "model space", otherwise you rotate the translation part also, which will result in the bullet being at another position

example:
vertex position = (0, 0, 0)

1. we create a simple transformation:
-- translation to (1, 0, 0)
-- rotation around y axis (90)
translated_vertex_position = (1, 0, 0)

2. apply another rotation
-- rotation around y axis (90)
==> the result wont be (as one might expect) rotation = 180, but also the position has changed, it'll be (0, 0, -1)

read more about that here: (the "in practice" part)
https://learnopengl.com/#!Getting-started/Transformations

to fix that, you have to temporarily translate the "model-to-world" transformation matrix back to the origin (the "world space" origin), then apply the rotation, finally translating the result matrix back to its former position (in "world space")

BBeck1
03-18-2017, 08:39 AM
Which origin do you mean? The world's origin (where the model would render without any transformations) or the model's origin? I'm leaning more towards this approach, I'm just unsure about how you'd alter this code:
to do what you said. Would I translate to the world origin first, -bulletPositions[i] first etc.

Second, you mentioned:

Do you know how exactly you'd get the last row/column? Say my object is being rotated as planned, I'm guessing I'd need the last row/column of the world-matrix (MVP = ProjectionMatrix * ViewMatrix * ModelMatrix) or the ModelMatrix. I need this in the form of a glm::vec3.

Hopefully the answers to those will help me out a lot, cheers.

It's "probably" the world origin. Because you are keeping it pretty simple here, you probably don't have cases where the origin becomes any more complicated than that. The reason I hesitate to just proclaim it as the world origin is that when you get into rigid animation and you start chaining these matrices into parent/child relationships that are 4 or 5 generations deep, each parent's position and orientation is the "origin" for each child. So, you have origins that are not at the world origin. Also, when you reverse the order of multiplication of the matrices, you are changing back and forth between the world axis and the local axis of the object, and thus your origin is changing as well.

But in the case of using the "trick" to move the object to the origin, rotate it, and then moving it back, we're probably talking about the world origin in this case. Basically, you just want to extract the position out of the matrix, translate by the negated position to reduce the position to 0,0,0, do your rotation, and then add that same position back to it (because you stored it before negating it) so that it returns to the new position in a new orientation. If you do all that in a single frame, it all is concluded before the draw call and so the move to the origin is never actually drawn to the screen and the user has no knowledge that it happened, yet the object rotates instead of orbits.

You can probably achieve the same results by simply changing this line to:
glm::mat4 ModelMatrix2 = BulletRotationMatrix * BulletScaleMatrix * BulletTranslationMatrix;

If you rotate and scale before you translate for the first time, you were already at the origin and thus did not need to move it there.

As far as how to decompose and get the position out of the object's matrix here is an example where I did it in my engine code (http://virtuallyprogramming.com/OGL45/OGL45.html):


ConversionMatrix = glm::inverse(View);
CameraPos = (glm::vec3) ConversionMatrix[3]; //Get the camera position from the view matrix.
Shader->SetUniform("CameraPosition", CameraPos);


In the above case, I need to know the position of the camera. In my code, I store the camera's position and orientation in the View matrix at all times and so the value remains from frame to frame. I decompose it to answer questions such as where it is positioned as shown above. The view matrix is just like an object's world matrix except it is inversed which means that it is backwards. I first create an inversed version of the view matrix to make it no different from any object's world matrix. Then with that inversed view matrix, I grab the last column I believe it is with "ConversionMatrix[3]". Here I've viewing the matrix as 4 columns (numbered 0 through 3) of glm::vec4's. I convert it to a Vec3 in order to turn it into a 3D vector value of x,y, and z and then put it to use. In this particular case, I think I'm feeding the camera position as a vec3 to the shader to calculate specular reflection off the object. But this position info could be used for anything. If you are working with a world matrix of an object, you don't need to worry about the inverse part; you just grab that 4th column and turn it into a vec3. I think the first 3 columns will give you a "Forward", "Up", and "Right" vector (x,y,z) that you could grab in the same way, but may want to normalize. Scaling may interfere with some of the math here, I'm not sure. I rarely scale and certainly don't scale with a view matrix. I generally try and make things to scale when I'm in Blender before I import into my program.

I was looking for an example of moving the object back to the origin to rotate it in my code and didn't find one in a quick look. I've really been avoiding this method for the past couple of years. I used to use it for everything and thought it was the only way it could be done in the early days. Now I avoid it in favor of storing orientation and position in the object's matrix. (You can store the orientation as a quaternion as well, which may have some advantages in some cases, but I prefer a matrix because the shader will probably want a matrix rather than a quaternion and I find a matrix works as well if not better than a quaternion for most of my uses and thus avoids having to convert it to a matrix before sending it to a shader. A quaternion is very similar to a 3by3 matrix and by using a 4by4 matrix you can store position and scale as well as orientation, which a quaternion cannot do. Quaternions certainly have their place though.)

Anyway with your code, I don't think you need to translate to the origin but rather just need to do the translation after the rotation and scale. Otherwise it would look something like this:


// Rotate bullet based on camera angles
glm::mat4 BulletRotationMatrix = glm::toMat4(bulletRotations[i]);
// Translate it by getPosition(), the camera's x, y and z, -1 on the z-axis
glm::mat4 BulletTranslationMatrix = translate(mat4(), bulletPositions[i]);
glm::mat4 BulletScaleMatrix = scale(mat4(), glm::vec3(0.1f, 0.1f, 0.1f));
glm::mat4 ModelMatrix2 = BulletTranslationMatrix
ModelMatrix2 = ModelMatrix2 * translate(mat4(), (glm:vec3) -ModelMatrix2[3]); //Undoing the previous line
ModelMatrix2 = ModelMatrix2 * BulletRotationMatrix * BulletScaleMatrix;
ModelMatrix2 = ModelMatrix2 * BulletTranslationMatrix; //Restoring it to its previous position.
glm::mat4 MVP2 = ProjectionMatrix * ViewMatrix * ModelMatrix2;


But for this code, that doesn't make much sense since you can do the same thing like this by doing the translation last:


// Rotate bullet based on camera angles
glm::mat4 BulletRotationMatrix = glm::toMat4(bulletRotations[i]);
// Translate it by getPosition(), the camera's x, y and z, -1 on the z-axis
glm::mat4 BulletTranslationMatrix = translate(mat4(), bulletPositions[i]);
glm::mat4 BulletScaleMatrix = scale(mat4(), glm::vec3(0.1f, 0.1f, 0.1f));
glm::mat4 ModelMatrix2 = BulletRotationMatrix * BulletScaleMatrix * BulletTranslationMatrix;
glm::mat4 MVP2 = ProjectionMatrix * ViewMatrix * ModelMatrix2;


It's basically a mantra that you will hear all over the internet that you need to rotate and scale before you translate.

https://msdn.microsoft.com/en-us/library/eews39w7(v=vs.110).aspx

I don't really think of it this way anymore. Because I always store the position and orientation info in the matrix, I have to do the operations in whatever order is called for at the time. I don't have the luxury of choosing the order. If the object needs to be moved and then rotated I can't choose to rotate it and then move it. Not to mention the order is constantly changing and reversing. Instead, I learn to control which axis is being used (world or local) by reversing the order of multiplication. Then it doesn't matter what came before. All I'm doing is that one operation, which is mostly unaffected by what came before.

It's a bit difficult to get comfortable with matrices. I wasn't at first. It took me many years to get where I am now. At first, I did everything with vectors because I had figured out vectors and fell in love with them. Then I started using matrices more and more and understanding them better as I went along. Eventually, I came to choose matrices over not only vectors but quaternions as well (in most cases but not all). I eventually learned to have faith that the matrices could do their job of holding positions and orientations quite well and they didn't need me to micro-manage them or to even know what was going on inside of them. You eventually have to come to this place for animation. Matrices are the core of animation. You can use quaternions instead and for some things like SLERP quaternions are superior, but even then the quaternions are being used much in the same way that matrices would be. For rigid animation I would still generally prefer matrices, but for skinned animation you need to interpolate frames in between your key frames, which is where quaternions really pull ahead of matrices in their abilities. But my point is that getting comfortable using matrices is something you will want to strive for because you will be using them a lot if you code your own animation stuff.

You might study rigid animation to get a better idea of how I see matrices. Learning to link them will give you a greater understanding.

eratic-magician
03-18-2017, 04:39 PM
Thanks for all the help you guys, both posts are extremely useful, I've honestly understood more from these posts about matrices than days of reading so thanks.


You can probably achieve the same results by simply changing this line to:
glm::mat4 ModelMatrix2 = BulletRotationMatrix * BulletScaleMatrix * BulletTranslationMatrix;

Apologies if I'm wrong, but this doesn't seem to work. It repositions the bullet a bit but doesn't result in the bullet orbiting around the camera (which is what I'm going for, sorry if that wasn't clear).
After thoroughly reading through these posts and trying a few different things, it seems my bullet still won't do what I need. I'm basically trying to get it to appear as though it would if drawn without the ViewMatrix multiplication, in that it doesn't matter which way you rotate the camera, it'll always be attached to the camera. Only, I need this to be done in world space, hence the idea of the bullet model orbiting the camera (which is were I thought that the rotating AFTER translating would play in). It seems as though your suggestion is more about rotating an object on it's own axis even after a transformation. Again though, apologies if I'm wrong (I'm still figuring this all out).

Getting the position of my bullet worked perfectly though (as far as I can tell anyway), so thanks for explaining that. This seems to give me my bullet's world position:

glm::vec3 bulletCoordinates = (glm::vec3) MVP2[3];

john_connor
03-19-2017, 04:29 AM
do you remember what i posted a few days ago (in another topic (https://www.opengl.org/discussion_boards/showthread.php/199399-Model-not-staying-rendered-%28And-trying-to-get-it-s-world-position%29?p=1286321&viewfull=1#post1286321)) ? with that approach you'd have solved that problem ...

simulating the bullet(s) is easy, its just s(t) = v * t, the camera can be "moved" the same way or you could move the cam only in response to some input, like key pressing / joystick axis / etc ...

if you want to animate the camera to move around a flying bullet, create a new "animation" which has to be advanced each frame, you can wrap it into a "function object", using c++ 11 and <functional> (http://www.cplusplus.com/reference/functional/function/function/)

output = none
input = frametime, &scene

std::function<void(float, Scene&)> my_animation = ......... // lambda expression, code to rotate the camera around the bullet
to execute the code in the lambda expression:

float frametime = 1.0f / 60;
my_animation(frametime, myscene);

BBeck1
03-19-2017, 05:16 AM
I didn't realize you wanted the bullet to orbit the camera.

To orbit the camera, you need to do a little rigid animation. The camera becomes the parent to the child object orbiting it. To do that you multiply the parent times the child (or is it child times parent, flip it and try it the other way if it doesn't work) and use the result for the child's world matrix. Because the camera is the view matrix instead of a world matrix, you have to invert it. So, it becomes: inverted view matrix times object's world matrix equals the matrix you actually use for the object. So, you never use the object's world matrix directly. That will cause the object to be attached to the camera and if you rotate the object's world matrix (not the result matrix) then the result matrix will cause the object to orbit the camera. I think reversing the order of multiplication between the inverted camera and the object's matrix will determine if the rotation of the object is on it's own axis or orbiting the camera.

Other than that, everything should work pretty much the same. You could do a scale and rotate of the object's world matrix, then translate it relative to the camera, then multiply it by the inverted view matrix, and use that result as if it were the object's world matrix. You would still need to do your projection/world/view multiplication. So, the view data goes in twice, once inverted and once normal. That's because the object is to orbit the camera.


I usually do the opposite and have the camera orbit an object and thus become a "chase camera". Move the object and the camera remains attached. But there's no reason you can't have object's orbit the camera. The chase camera is done by taking an object's world matrix and multiplying the view matrix and using the result matrix as the view matrix to actually draw with. I'm not sure if the view matrix needs to be inverted for that; I would have to play with it a bit to find out. I haven't done a chase camera in awhile. I think the last one I did was in Unity. I doubt it needs to be inverted for that.

I don't know if this is helpful, but here's some code where I've got object's orbiting on command. The comments are a little off since some of this code was copied and pasted from another project and reworked. But it's basically my engine code on my website (http://virtuallyprogramming.com/OGL45/OGL45.html) except I've added a second cube that orbits the first cube. It orbits by itself, but keyboard commands can move the main cube. I think I actually had 3 matrices involved in that, the main cube, a pivot point, and the second cube. I think the pivot point allows the orbit to be changed of the second cube with the Y and H keys. The I and K keys move the main cube around and the second cube stays attached and continues to orbit.



#include "Game.h"

using namespace OGLGameNameSpace;


Game::Game(void)
{
}


Game::~Game(void)
{
}


//================================================== ================================================== =================
// Game::Initialize()
//
// Purpose:
// To allow any code you want to run once at startup not associated with art assets.
//
// Input:
// None.
//
// Output:
// bool - The program will close if it returns false assuming a catastrophic error has occured.
//
// Notes:
//
//
//================================================== ================================================== =================
bool Game::Initialize()
{
bool GameObjectInitializedProperly = false; //Must be set to true to keep the program from closing.


CameraHeight = 1.68f; //Roughly the average eye height in meters of an average man. Our camera will stay at this level to make it feel like we are in the scene.
CameraTilt = 0.0f; //Will tilt the camera up and down.

GameObjectInitializedProperly = true; //This should probably be set by error checking, but we don't have any error checking here.
View = glm::lookAt(glm::vec3(0.0f, CameraHeight, 2.0f), glm::vec3(0.0f, CameraHeight, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); //1.63 meters is roughly the height of the average man's eyes.
//Projection = glm::perspective(0.96f, 1.770833f, 0.1f, 700.0f); //0.96 is 55 degrees and 1.7708333 is the width to height ratio on my computer.
Projection = glm::perspective(0.96f, OperatingSystem.AspectRatio(), 0.1f, 700.0f); //0.96 is 55 degrees and 1.7708333 is the width to height ratio on my computer.

glEnable(GL_CULL_FACE); //Turn back face culling on.
glCullFace(GL_BACK); //Set back face culling to remove the back face rather than the front face. Back is default. Included here for clarity.
glEnable(GL_DEPTH_TEST); //Enable the depth buffer so that things are drawn correctly.
glEnable(GL_FRAMEBUFFER_SRGB); //Needed to prepare frame buffer for sRGB color.


DiffuseLightDirection = glm::normalize(glm::vec3(1.0f, -1.0f, -1.0f)); //Direction that the primary light of the scene is "shining" in.
AmbientLightColor = glm::vec4(0.05f, 0.05f, 0.1f, 1.0f); //Light color in the "shadows".
DiffuseLightColor = glm::vec4(1.0f, 1.0f, 0.9f, 1.0f); //Direct light color.

return GameObjectInitializedProperly;
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Game::LoadContent()
//
// Purpose:
// To allow any code you want to run once at startup associated with art assets.
//
// Input:
// None.
//
// Output:
// bool - The program will close if it returns false assuming a catastrophic error has occured.
//
// Notes:
// Here we are creating all the objects in the scene. The switch statement makes it easy to manage this and add more
// objects of your own. You merely define all the objects vertices it will need to be drawn and then list the index order
// of every vertex to specify what order it will be drawn in. These examples merely count the number of vertices for the
// index which is largely pointless. However, the design means you "could" reuse vertices or specify them out of order and
// use the index buffer to specify what order to draw them in.
//
// You'll quickly notice that the number of vertices you have to specify for even the most simple of objects is nearly
// unacceptable. There are better ways, but we're learning the most straight forward way here which we will build on later.
//
// Textures were created with Paint.Net by saving as .DDS files with mip maps.
//
//================================================== ================================================== =================
bool Game::LoadContent()
{
bool NoCatastrophicFailuresOccured = false; //Close the program if even one mesh fails to initialize correctly.
float ScaleFactor = 200.0f;
float TerrainSize = 500.0f;


//GrassTexture.Load("Textures/Grass.dds", true);
GrassTexture.Load("Textures/FloorTiles.jpg", true);
//GrassTexture.Load("Textures/MudWall.jpg", true);

GrassTexture.Bind(0);

NoCatastrophicFailuresOccured = Shader.LoadShader("Shaders/BlinnPhong.vrt", "Shaders/BlinnPhong.frg");

glClearColor(0.392156862745098f, 0.5843137254901961f, 0.9294117647058824f, 1.0f); //XNA's "Cornflower Blue"


GLfloat TriangleVertexBuffer[] = {
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.45f, -0.5, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-0.45f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f
};

GLuint TriangleIndices[] = {
0,1,2
};
Triangle.DefineMesh(3, TriangleVertexBuffer, 3, TriangleIndices, nullptr);


GLfloat CubeVertexBuffer[] = {
//Front Quad.
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,

0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,

//Right Quad.
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

//Bottom Quad.
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

//Back Quad.
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,

-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,

//Left Quad.
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

//Top Quad.
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,

0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
};

GLuint CubeIndices[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 };
Cube.DefineMesh(VERTICESINCUBE, CubeVertexBuffer, VERTICESINCUBE, CubeIndices, nullptr);
Cube2.DefineMesh(VERTICESINCUBE, CubeVertexBuffer, VERTICESINCUBE, CubeIndices, nullptr);

GLfloat GroundVertexBuffer[] = {
-TerrainSize, 0.0f, TerrainSize, 0.0f, ScaleFactor, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
TerrainSize, 0.0f, TerrainSize, ScaleFactor, ScaleFactor, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
TerrainSize, 0.0f, -TerrainSize, ScaleFactor, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-TerrainSize, 0.0f, -TerrainSize, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f
};

GLuint GroundIndices[] = {
0,1,2,
0,2,3
};
Ground.DefineMesh(4, GroundVertexBuffer, 6, GroundIndices, &GrassTexture);

Cube2Pivot = glm::mat4();
Triangle.Transform(glm::translate(glm::mat4(), glm::vec3(0.0f, 1.63f, 0.0f)));
Cube.Transform(glm::translate(glm::mat4(), glm::vec3(-3.0f, 2.5f, -3.0f)));
//Cube2.Transform(glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, 1.0f)));
Cube2World = glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, 3.0f));

return NoCatastrophicFailuresOccured;
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Game::UnloadContent()
//
// Purpose:
// To allow any code you want to run as the program closes in order to cleanup.
//
// Input:
// None.
//
// Output:
// None.
//
// Notes:
// All the game objects you create need their Shutdown() methods called to release the associate vertex and index buffers.
// Since Shaders are owned by this class, it is also responsible for calling the Shutdown() method of any shaders created.
//
//================================================== ================================================== =================
void Game::UnloadContent()
{
//glDeleteVertexArrays(1, &vao);
//glDeleteBuffers(1, &vbo);
//glDeleteBuffers(1, &ibo);
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Game::Update()
//
// Purpose:
// To do everything that needs to be done in one frame except draw stuff to the screen.
//
// Input:
// float TimeDelta - Amount of time that has passed since the last frame occured in milliseconds.
//
// Output:
// None.
//
// Notes:
// This is where most of your game code will be. It gets called every frame to change anything that needs to be changed
// or done during that frame. It runs in a loop that needs to be called at least 30 times per second but there's nothing
// to control how often it gets called. Things would move at unpredictable rates if we did not use TimeDelta to take in to
// account the amount of time that has passed since the last frame.
//
// We start out by processing the keyboard and game controller input to change the camera's position and direction. You
// can also toggle full screen on and off.
//
// The camera movement this frame is stored as a 3D vector that we treat more like a 2D vector. The facing normal should
// point in the direction we want the camera to face. And as a normal should have a length of 1. Any movement during the
// frame is cumulative from the various controls. When you move it uses either the CameraFacingNormal or a normal rotated 90
// degrees away from the camera facing. It's basic vector addition to add the movement to the camera position.
//
// XMMatrixLookAtRH is used to create a view matrix to simulate the camera every frame. Generally, I would say it is a
// good idea to not continuously recreate the view matrix but it's easier then maintaining a view matrix between frames and
// this is really early in this tutorial series.
//
// Finally some very simple rigid animation is thrown in to show you not only how to do it, but that it can be done and how
// easy it is to do. Experiment by turning the rotations off and on and changing their directions and speed.
//
// The scene is lit with a simple Blinn-Phong shader that has "directional" lighting as opposed to point lights or
// spot lights. Directional lighting is nothing more than a direction that the light shines in. It is a normalized vector
// describing a direction and it has a color. That's all it is. Look at the shader for more detail. By rotating that direction
// the light source seems to orbit the scene similar to a day and night cycle except the light shines through solid objects.
//
//================================================== ================================================== =================
void Game::Update()
{
const float MaxTiltAngle = glm::radians(45.0);
const unsigned char* Buttons;
int JoyStick1Present = false;
int NumberOfJoyStickAxes = 0;
int NumberOfJoyStickButtons = 0;
const float* AxesArray = nullptr;
float LeftThumbStickY = 0.0f;
float LeftThumbStickX = 0.0f;
float Triggers = 0.0f; //XBox 360 controller triggers are a single axis for both triggers. Positive = Left. Negative = Right.
float RightThumbStickY = 0.0f;
float RightThumbStickX = 0.0f;
bool AButton = false;
bool BButton = false;


if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_ESCAPE && OperatingSystem.Keyboard.ActionPressed == GLFW_PRESS) OperatingSystem.ShutDown();

JoyStick1Present = glfwJoystickPresent(GLFW_JOYSTICK_1);
if (JoyStick1Present)
{
AxesArray = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &NumberOfJoyStickAxes);
Buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &NumberOfJoyStickButtons);
LeftThumbStickY = AxesArray[0];
LeftThumbStickX = AxesArray[1];
Triggers = AxesArray[2];
RightThumbStickY = AxesArray[3];
RightThumbStickX = AxesArray[4];

//Camera Controls with XBox 360 controller.
if (RightThumbStickX > 0.2 || RightThumbStickX < -0.2) View = glm::rotate(glm::mat4(), RightThumbStickX *0.06f, glm::vec3(0.0f, 1.0f, 0.0f)) * View;
if (LeftThumbStickX > 0.2 || LeftThumbStickX < -0.2) View = glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -LeftThumbStickX * 0.1f)) * View; //*0.1f to slow it down. Negative to flip the axis. -0.2 for deadzone.

if (RightThumbStickY > 0.2 || RightThumbStickY < -0.2) CameraTilt += 0.03 * RightThumbStickY;
if (LeftThumbStickY > 0.2 || LeftThumbStickY < -0.2) View = glm::translate(glm::mat4(), glm::vec3(-LeftThumbStickY * 0.1f, 0.0f, 0.0f)) * View;

if (Triggers > 0.2 || Triggers < -0.2) View = glm::translate(glm::mat4(), glm::vec3(0.0f, Triggers*0.1f, 0.0f)) * View;

if (Buttons[0] == '\x1') AButton = true;
if (Buttons[1] == '\x1') BButton = true;
if (Buttons[6] == '\x1') OperatingSystem.ShutDown();
}


//Camera Controls with keyboard.
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_W && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
View = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.05f)) * View;
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_S && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
View = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -0.05f)) * View;
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_E && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
CameraTilt += 0.1;
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_Q && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
CameraTilt -= 0.1;
if (OperatingSystem.Keyboard.ModePressed == GLFW_MOD_SHIFT)
{
//Keys while Shift keys are also held down.
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_A && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
View = glm::translate(glm::mat4(), glm::vec3(0.1f, 0.0f, 0.0f)) * View;
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_D && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
View = glm::translate(glm::mat4(), glm::vec3(-0.1f, 0.0f, 0.0f)) * View;
}
else
{
//Keys when shift keys are not being held down.
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_D && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
View = glm::rotate(glm::mat4(1.0f), 0.05f, glm::vec3(0.0f, 1.0f, 0.0f)) * View;
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_A && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
View = glm::rotate(glm::mat4(1.0f), -0.05f, glm::vec3(0.0f, 1.0f, 0.0f)) * View;
}


//Yellow cube controls.
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_I && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
Cube.Transform(glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, 0.05f)));
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_K && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
Cube.Transform(glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -0.05f)));
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_L && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
Cube.Transform(glm::rotate(glm::mat4(), glm::radians<float>(-1), glm::vec3(0.0f, 1.0f, 0.0f)));
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_J && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
Cube.Transform(glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f)));


if (CameraTilt > MaxTiltAngle) CameraTilt = MaxTiltAngle;
if (CameraTilt < -MaxTiltAngle) CameraTilt = -MaxTiltAngle;

if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_Y && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
Cube2Pivot = glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 0.0f, 1.0f)) * Cube2Pivot;
if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_H && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
Cube2Pivot = glm::rotate(glm::mat4(), glm::radians<float>(-1), glm::vec3(0.0f, 0.0f, 1.0f)) * Cube2Pivot;

Triangle.Transform(glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f)));
Cube2Pivot = Cube2Pivot * glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f));
Cube2.WorldMatrix = Cube.WorldMatrix * Cube2Pivot * Cube2World;


}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Game::Draw()
//
// Purpose:
// To do draw a single frame on the computer screen.
//
// Input:
// float TimeDelta - Amount of time that has passed since the last frame occured in milliseconds.
//
// Output:
// None.
//
// Notes:
// Since all the drawing code is tucked away neatly elsewhere, this method ends up being very short and sweet. There's
// basically nothing to it. The first two lines clear the backbuffer in corn flower blue which is a constant. And then
// it clears the depth stencil. You can clear the screen in any color you like.
//
// Then each game object's Draw() method needs to be called to tell it to draw it self to the back buffer. The parameters
// for the shader have to be sent here.
//
// Finaly, the swapchain is told to make the backbuffer the frontbuffer and draw it to the screen by presenting it and
// the image appears on the computer monitor.
//
//================================================== ================================================== =================
void Game::Draw()
{
glm::mat4 TiltedView = glm::rotate(glm::mat4(), CameraTilt, glm::vec3(1.0, 0.0, 0.0)) * View;


glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

Triangle.Draw(TiltedView, Projection, &Shader, DiffuseLightDirection, AmbientLightColor, DiffuseLightColor);
Cube.Draw(TiltedView, Projection, &Shader, DiffuseLightDirection, AmbientLightColor, DiffuseLightColor);
Cube2.Draw(TiltedView, Projection, &Shader, DiffuseLightDirection, AmbientLightColor, DiffuseLightColor);
Ground.Draw(TiltedView, Projection, &Shader, DiffuseLightDirection, AmbientLightColor, DiffuseLightColor);

}
//================================================== ================================================== =================




Also, it might be worth noting that I don't use the View matrix to draw. I use it to control the camera, but I wanted to limit the angle at which the camera can pitch up and down. So, I never allow the View matrix to pitch but rather maintain that as a separate float which I apply right before drawing and then use the result (TiltedView) to actually draw with. So, that's an example of not using the View to draw directly, but rather using a modified View to draw. I just build and apply a rotation matrix to the View, for the pitch, right before drawing.