Right now I'm rotating an object, lets call it 'mainBody' but attached to the mainBody are several attachments and when I rotate my mainBody they are supposed to rotate along as well, but right now they don't. I'm not using a parent/child system. The main body has an array of it's attachments and draws them in the mainBody draw function.
The glPopMatrix() from the main body is done after the equipment items are drawn.
I know I managed to do this in the past with pushMatrix(); and popMatrix(); But now it doesn't seem to work.
I'm using c++, opengl, and glm.
Here is some code that shows you what I have right now:
{
setupStartModelMatrix(); //<---- Has glPushMatrix();
setupModelviewMatrix(); //<--- has all the gml stuff
drawMainBody();
}
if(mNumberOfEquipementOwned != 0)
{
for(int i = 0; i < mNumberOfEquipementOwned; i++)
{
//obj is retrieved with a function
obj->render();
}
}
setupEndModelMatrix(); // <--- Has glPopMatrix();
}
And the glm code
void GameObject::setupModelviewMatrix()
{
glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.f);
glm::mat4 Model = glm::scale( glm::mat4(1.0f), glm::vec3(1.0f));
glm::mat4 ViewTranslate = glm::translate( glm::mat4(1.0f), glm::vec3(mPosition.x, mPosition.y, mPosition.z));
glm::mat4 ViewRotateX = glm::rotate( ViewTranslate, mRotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 ViewRotateY = glm::rotate( ViewRotateX, mRotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 View = glm::rotate( ViewRotateY, mRotation.z, glm::vec3(0.0f, 0.0f, 1.0f));
glm::mat4 MVP = View * Model;
glUniformMatrix4fv( 0, 1, GL_FALSE, glm::value_ptr(MVP));
glLoadMatrixf(glm::value_ptr(MVP));
}
And the draw equipement code
void Gun::render()
{
glPushMatrix();
setupModelviewMatrix();
mMesh->render();
glPopMatrix();
}
My guess is that you are calling glPushMatrix(), rotating, drawing your main body, calling glPopMatrix(), then drawing the attachments. If this is true, move the glPopMatrix() call to after drawing the attachments.
Because of Tim I noticed my matrix wasn't passed trough too the others. When I finally understood what he was saying it's a easy fix so 100% of the props go to him.
Now the code for anyone who might need it.
{
glm::mat4 startingModel(1.0);
setupStartModelMatrix();
startingModel = continuedModelvieuwMatrix(startingModel);
drawMainBody();
}
if(mNumberOfEquipementOwned != 0)
{
for(int i = 0; i < mNumberOfEquipementOwned; i++)
{
//obj is retrieved with a function
obj->render(startingModel);
}
}
setupEndModelMatrix(); // <--- Has glPopMatrix();
}
And the glm code
glm::mat4 GameObject::continuedModelvieuwMatrix(glm::mat4 startingModel)
{
glm::mat4 Model = glm::scale( glm::mat4(1.0f), glm::vec3(1.0f));
glm::mat4 ViewTranslate = glm::translate( startingModel, glm::vec3(mPosition.x, mPosition.y, mPosition.z));
glm::mat4 ViewRotateX = glm::rotate( ViewTranslate, mRotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 ViewRotateY = glm::rotate( ViewRotateX, mRotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 View = glm::rotate( ViewRotateY, mRotation.z, glm::vec3(0.0f, 0.0f, 1.0f));
glm::mat4 MVP = View * Model;
glUniformMatrix4fv( 0, 1, GL_FALSE, glm::value_ptr(MVP));
glLoadMatrixf(glm::value_ptr(MVP));
return MVP;
}
void GameObject::setupStartModelMatrix()
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
And the draw equipement code
void Gun::render(glm::mat4 startingModel)
{
glm::math4 tempModel;
tempModel = continuedModelvieuwMatrix(startingModel);
mMesh->render();
}
And thats about it.
I'm not 100% sure if this code works as I had to fix it for my self in a different way because of my object construction but this should at least give a good head start for anyone with the same problem.
Also no glPopMatrix() or glPushMatrix() is required to make this work.
Related
First for the part that does work using XMMATH where data.model is a XMMatrix:
static auto model_matrix = DirectX::XMMatrixIdentity();
static auto pos = DirectX::XMVectorSet(0.0f, 0.0f, -10.0f, 0.0f);
static auto focus = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
static auto up = DirectX::XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
static auto view_matrix = DirectX::XMMatrixLookAtLH(pos, focus, up);
static auto proj_matrix = DirectX::XMMatrixPerspectiveFovLH(glm::radians(45.0f), 16.0f / 9.0f, 0.1f, 10000.0f);
building the mvp:
data.model = model_matrix * view_matrix * proj_matrix;
data.model = DirectX::XMMatrixTranspose(data.model);
when I hand the data.model over to my HLSL shader, everything works fine and and I can change the pos vector to look at my cube from different angles. HLSL vertex shader:
cbuffer myCbuffer : register(b0) {
float4x4 mat;
}
float4 main(float3 pos : POSITION) : SV_POSITION
{
return mul(float4(pos, 1), mat);
}
Now when I try to make something similar using GLM (I changed the type of data.model to be a glm::mat4 ):
auto gl_m = glm::mat4(1.0f);
static auto gl_pos = glm::vec3(0.0f, 0.0f, -10.0f);
static auto gl_focus = glm::vec3(0.0f, 0.0f, 0.0f);
static auto gl_up = glm::vec3(0.0f, 1.0f, 0.0f);
auto gl_v = glm::lookAtLH(gl_pos, gl_focus, gl_up);
auto gl_p = glm::perspectiveFovLH_ZO(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f);
building the MVP:
data.model = gl_m * gl_v * gl_p;
Now when I pass this to data.model the cube does get rendered but the whole screen is filled black. (my cube is black and the clear color is light-blue, so I think it's rendering the cube but really close or inside it).
I don't know where to look on how to fix this, the projection matrix should be in the correct clipping space since I'm using perspectiveFovLH_ZO, the ZO fixes the clipping space to [0..1]. It could be how the HLSL shader float4x4 deals with the glm::mat4, but both are column major I believe so no need to transpose.
It might have something to do with the rasterizer culling settings and the FrontCounterClockwise setting, but I'm fairly new to DirectX and don't know what it does exactly.
D3D11_RASTERIZER_DESC raster_desc = {0};
raster_desc.FillMode = D3D11_FILL_MODE::D3D11_FILL_SOLID;
raster_desc.CullMode = D3D11_CULL_MODE::D3D11_CULL_NONE;
raster_desc.FrontCounterClockwise = false;
d3device->CreateRasterizerState(&raster_desc, rasterize_state.GetAddressOf());
Any help is appreciated, let me know if I forgot anything.
Managed to fix it (seems like a bandaid fix but I'll work with it for now).
After I build the mvp I added:
gl_mvp[3][3] = -10.0f;
gl_mvp = glm::transpose(gl_mvp);
the -10 is the z coordinate of the camera position (the same one you pass as the first argument to glm::lookAtLH). I thought HLSL/DirectX matched the column-major GLM matrices but apparently not, it needed the extra transpose call. I'm not sure why that is and what the bottom left element of the MVP is that it has to match the positional z, maybe someone with a better understanding of the math behind it can clarify.
Removed gl_mvp[3][3] = -10.0f; since it's a bad fix, instead I got this now:
changed both the lookAtLH and PerspectiveFovLH_ZO to their RH variants.
Also changed the order of building the MVP from M * V * P to P * V * M.
New code that seems to work well (even flying around using my Camera class):
auto gl_m = glm::mat4(1.0f);
auto gl_v = glm::lookAtRH(position, position + dir, {0, 1, 0});
auto gl_p = glm::perspectiveFovRH_ZO(glm::radians(FOV), 1280.0f, 720.0f, 0.1f, 10000.0f);
glm::mat4 gl_mvp = gl_p * gl_v * gl_m;
return glm::transpose(gl_mvp);
It's a bit different from the previous code cause this is inside my Camera class, so position, dir and FOV are variables I keep track off but you get the idea. Passed this return result to my HLSL shader and all seems well so far.
I'm trying to rotate my camera with the purpose of see an object rotating around my cam with a rotation Matrix that I develop the problem is that it doesn't works.
So I try with the glm::rotation matrix and put the values
m_View = glm::rotate(m_View, a * glm::radians(180.0f), glm::vec3(0.0f, 1.0f, 0.0f))
but it does not works either:
void CCam::setView()
{
Front = glm::normalize(Eye - At);
Right = glm::normalize(glm::cross(Up, Front));
up = glm::cross(Front, Right); // Up Verdadero
m_View = glm::lookAt(
Eye, // Camera Position
(Eye + Front), // Where the camera looks
up // This is another way to say camera is not rotated
);
newAt = glm::vec4(At, 1.0f);
//m_View = m_View * GLMatrixRotationY(a);
m_View = glm::rotate(m_View, a * glm::radians(180.0f), glm::vec3(0.0f, 1.0f, 0.0f));
}
glm::mat4 CCam::GLMatrixRotationX(float Angle)
{
matrizRotacionX = glm::mat4(
1, 0, 0, 0,
0, cos(Angle), -sin(Angle), 0,
0, sin(Angle), cos(Angle), 0,
0, 0, 0, 1
);
return matrizRotacionX;
}
I expect to see my mesh rotating around the camera but I only got the cam rotating around the mesh.
I've been working with this GameObject(which utilizes Box2D) and Renderer2D class for some time now, and I haven't had any problems. That was till I decided to add the option to rotate an object. All works fine (or that seems to be) with box 2D, but the object when seen on screen moves in the wrong axis, it becomes smaller when moving Up and bigger when moving down (or closer and further away from the camera).
The only three lines I have added to the Renderer2D script are this:
model = math::translate(model, iVec3(0.5f * size.x, 0.5f * size.y, 0.0f));
model = math::rotate(model, rotation, iVec3(0.0f, 0.0f, 1.0f));
model = math::translate(model, iVec3(-0.5f * size.x, -0.5f * size.y, 0.0f));
If I just remove those lines everything works fine, but I want the object to rotate so I can't remove them.
This is the whole Rendering Function:
void Renderer2D::Render(Texture & texture, iVec2 position, iVec2 size, float rotation, Color color, iVec2 tiling) {
this->shader.Use();
iMat4x4 model;
model = math::translate(model, iVec3(position, 0.0f));
model = math::translate(model, iVec3(0.5f * size.x, 0.5f * size.y, 0.0f));
model = math::rotate(model, rotation, iVec3(0.0f, 0.0f, 1.0f));
model = math::translate(model, iVec3(-0.5f * size.x, -0.5f * size.y, 0.0f));
model = math::scale(model, iVec3(size, 1.0f));
this->shader.SetMatrix4("model2D", model);
this->shader.SetVector3f("spriteColor", iVec3(color.r, color.g, color.b));
this->shader.SetVector2f("tiling", tiling);
glActiveTexture(GL_TEXTURE0);
texture.Bind();
glBindVertexArray(this->QuadVAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
}
I move the GameObject with the Velocity variable of Box2D, like this:
float horizontal, vertical;
if (Input::GetKeyDown(Input::Key.A) || Input::GetKeyDown(Input::Key.D)) {
if (Input::GetKeyDown(Input::Key.A))
horizontal = -1;
else if (Input::GetKeyDown(Input::Key.D))
horizontal = +1;
}
else horizontal = 0;
if (Input::GetKeyDown(Input::Key.W) || Input::GetKeyDown(Input::Key.S)) {
if (Input::GetKeyDown(Input::Key.W))
vertical = -1;
else if (Input::GetKeyDown(Input::Key.S))
vertical = +1;
}
else vertical = 0;
Rigidbody2D->Velocity = iVec2(horizontal, vertical) * speed;
I really have tried everything and still don't know if it's a Camera problem or a Renderer or a Box2D. Tried altering the far and near values of the view matrix, still the same result.
Solved the main problem, after printing the matrix on the console I realized that the fourth column was been replaced from x y 0 1 to 0 x 0 y.
Changed the code from this:
model = math::translate(model, iVec3(0.5f * size.x, 0.5f * size.y, 0.0f));
model = math::rotate(model, rotation, iVec3(0.0f, 0.0f, 1.0f));
model = math::translate(model, iVec3(-0.5f * size.x, -0.5f * size.y, 0.0f));
to this and it's done!
iMat4x4 tempModel;
iMat4x4 tempModel2;
tempModel = math::translate(tempModel, iVec3(0.5f * size.x, 0.5f * size.y, 0.0f));
model[3].z = model[3].w = 0;
model[3] = model[3] + tempModel[3];
model = math::rotate(model, rotation, iVec3(0.0f, 0.0f, 1.0f));
tempModel2 = math::translate(tempModel2, iVec3(-0.5f * size.x, -0.5f * size.y, 0.0f));
model[3].z = model[3].w = 0;
model[3] = model[3] + tempModel2[3];
Still didn't get the rotation I wanted, but at least other functions work.
What must be changed to let me see the impression of flying around the whole fixed scene? My current code just lets me look from a fixed viewpoint at objects each one rotating around itself. Enabling glLoadIdentity() just stops their rotation. Note that 3dWidget::paintGL() is permanently called by a timer every 20ms.
void 3dWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTranslatef(0.5f, 0.5f, 0.5f);
glRotatef(3.0f, 1.0f, 1.0f, 1.0f);
glTranslatef(-0.5f, -0.5f, -0.5f);
glPushMatrix();
//glLoadIdentity();
for (int i = 0; i < m_cubes.count(); i++) {
m_cubes[i]->render();
}
glPopMatrix();
}
void Cube::render() {
glTranslatef(m_x, m_y, m_z); // local position of this object
glCallList(m_cubeId); // render code is in createRenderCode()
glTranslatef(-m_x, -m_y, -m_z);
}
void Cube::createRenderCode(int cubeId) {
m_cubeId = cubeId;
glVertexPointer(3, GL_FLOAT, 0, m_pCubePoints);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, m_pCubeColors);
glNewList(m_cubeId, GL_COMPILE);
{
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLE_STRIP, 0, m_numPoints);
glDisableClientState(GL_COLOR_ARRAY);
}
glEndList();
}
void 3dWidget::init(int w, int h)
{
...
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float aspect = w/(float)(h ? h : 1);
glFrustum(-aspect, aspect, -1, 1, 10, 100);
glTranslatef(0., 0., -12);
glMatrixMode(GL_MODELVIEW);
}
EDIT: It seems it's important to know that 2 cubes are created with the following 3D position coordinates (m_x, m_y, m_z):
void 3dWidget::createScene()
{
Cube* pCube = new Cube;
pCube->create(0.5 /*size*/, -0.5 /*m_x*/, -0.5 /*m_y*/, -0.5 /*m_z*/);
pCube = new Cube;
pCube->create(0.5 /*size*/, +0.5 /*m_x*/, +0.5 /*m_y*/, +0.5 /*m_z*/);
}
Use gluLookAt to position the camera. You apply it to the modelview matrix before any object transforms.
Obviously, you'll have to figure out a path for the camera to follow. That's up you and how you want the "flight" to proceed.
EDIT: Just to be clear, there's no camera concept, as such, in OpenGL. gluLookAt is just another transform that (when applied to the modelview matrix) has the effect of placing a camera at the prescribed location.
If you really are just trying to rotate the world, your code seems to perform the transforms in a reasonable order. I can't see why your objects rotate around themselves rather than as a group. It might help to present a SSCCE using glut.
Now I've found the reason by myself. It works as soon as I change method paintGL() to
void 3dWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#if 0 // not working
glTranslatef(0.5f, 0.5f, 0.5f);
glRotatef(3.0f, 1.0f, 1.0f, 1.0f);
glTranslatef(-0.5f, -0.5f, -0.5f);
#else // this works properly, they rotate horizontally around (0,0,0)
glRotatef(3.0f, 0.0f, 1.0f, 0.0f);
#endif
for (int i = 0; i < m_cubes.count(); i++) {
m_cubes[i]->render();
}
}
I don't get it exactly why, but it obviously appeared that some transformations had compensated in a way that the objects just rotate around itself. Thanks for your help anyway.
I think it's always better to let the scene rotate than to move by gluLookAt (beside the issue that finding the right formula for the angle of view is more difficult).
I have a plane in 3D space, which is facing the camera that I want to be able to place at the same position as where I click. However, the position of the plane overshoots the mouse cursor. This is for a dynamic GUI that I want to be able to move about and interact with the widgets on the UI.
void mouse::unProjectMouse(float width, float height, camera* viewportCamera)
{
if(NULL == viewportCamera)
{
std::cout<<CNTRLCALL<<"camera failed! failed to un-project mouse";
} else {
glm::vec4 viewport = glm::vec4(0, 0, width, height);
glm::mat4 tmpView = viewportCamera->updateView();
glm::mat4 tmpProj = viewportCamera->updateProjection();
glm::vec3 screenPos = glm::vec3(mouseX, height-mouseY - 1.0f, 1.0f);
glm::vec3 worldPos = glm::unProject(screenPos, tmpView, tmpProj, viewport);
worldPos = worldPos / (worldPos.z * -1.0f);
mouseWorldX = worldPos.x;
mouseWorldY = worldPos.y;
mouseWorldZ = worldPos.z;
}
}
I am at a loss here as to why the plane is not aligning correctly with the mouse.
This is the fix:
void mouse::unProjectMouse(float width, float height, camera* viewportCamera)
{
if(NULL == viewportCamera)
{
std::cout<<CNTRLCALL<<"camera failed! failed to un-project mouse";
} else {
glm::vec4 viewport = glm::vec4(0.0f, 0.0f, width, height);
glm::mat4 tmpView = glm::lookAt(glm::vec3(viewportCamera->getCameraPosX(),viewportCamera->getCameraPosY(),viewportCamera->getCameraPosZ()),
glm::vec3(viewportCamera->getForward().x,viewportCamera->getForward().y,viewportCamera->getForward().z),glm::vec3(0,1,0));
glm::mat4 tmpProj = glm::perspective( 90.0f, width/height, 0.1f, 1000.0f);
glm::vec3 screenPos = glm::vec3(mouseX, height-mouseY - 1, 0.0f);
glm::vec3 worldPos = glm::unProject(screenPos, tmpView, tmpProj, viewport);
mouseWorldX = worldPos.x;
mouseWorldY = worldPos.y;
mouseWorldZ = worldPos.z;
}
}
And then to align the object to the cursors positon, the Z element of the camera had to be exact:
UIcamera->translateCameraX(0.0f);
UIcamera->translateCameraY(0.0f);
UIcamera->translateCameraZ(0.744f);