OpenGL move object and keep transformation - opengl

I've a object, which is transfomred (rotated at 45deg on the Y axis).
The target is to move (translate) the object on the x and y axis and keep the transformation effect as it is.
Its very hard to explain, so I made a picture:
I know the concept of the camera in opengl and i know i cant really move the camera but in fact everything is moving around the camera. Does someone actually know how to achieve this?
My code:
//set mvp
matrixProj = new PerspectiveProjectionMatrix(fovy, aspect, near, far);
matrixView = new ModelMatrix();
matrixView.LookAtTarget(new Vertex3f(0, 0, 2), new Vertex3f(0, 0, 0), new Vertex3f(0, 1, 0));
matrixModel = new ModelMatrix();
matrixModel.SetIdentity();
matrixModel.RotateY(45);
matrixModel.Translate(-2, -2, 0);
Matrix4x4 mvp = matrixProj * matrixView * matrixModel;
Gl.UniformMatrix4(Gl.GetUniformLocation(shaderProgram, "MVP"), 1, false, mvp.ToArray());
//draw quad
Gl.Begin(PrimitiveType.Quads);
Gl.Vertex3(-2, 2, 0);
Gl.Vertex3(2, 2, 0);
Gl.Vertex3(2, -2, 0);
Gl.Vertex3(-2, -2, 0);
Gl.End();

You have to change the order of the instructions. A rotation around the axis of the object is performed, by multiplying the translation matrix of the object by the rotation matrix.
This means you have to do the translation first and then the rotation.
matrixModel = new ModelMatrix();
matrixModel.SetIdentity();
matrixModel.Translate(-2, -2, 0);
matrixModel.RotateY(45);
Note, the translation matrix looks like this:
Matrix4x4 translate;
translate[0] : ( 1, 0, 0, 0 )
translate[1] : ( 0, 1, 0, 0 )
translate[2] : ( 0, 0, 1, 0 )
translate[3] : ( tx, ty, tz, 1 )
And the rotation matrix around Y-Axis looks like this:
Matrix4x4 rotate;
float angle;
rotate[0] : ( cos(angle), 0, sin(angle), 0 )
rotate[1] : ( 0, 1, 0, 0 )
rotate[2] : ( -sin(angle), 0, cos(angle), 0 )
rotate[3] : ( 0, 0, 0, 1 )
A matrix multiplication works like this:
Matrix4x4 A, B, C;
// C = A * B
for ( int k = 0; k < 4; ++ k )
for ( int l = 0; l < 4; ++ l )
C[k][l] = A[0][l] * B[k][0] + A[1][l] * B[k][1] + A[2][l] * B[k][2] + A[3][l] * B[k][3];
The result of translate * rotate is this:
model[0] : ( cos(angle), 0, sin(angle), 0 )
model[1] : ( 0, 1, 0, 0 )
model[2] : ( -sin(angle), 0, cos(angle), 0 )
model[3] : ( tx, ty, tz, 1 )
Note, the result of rotate * translate would be:
model[0] : ( cos(angle), 0, sin(angle), 0 )
model[1] : ( 0, 1, 0, 0 )
model[2] : ( -sin(angle), 0, cos(angle), 0 )
model[3] : ( cos(angle)*tx - sin(angle)*tx, ty, sin(angle)*tz + cos(angle)*tz, 1 )
Extension to the answer:
A perspective projection matrix looks like this:
r = right, l = left, b = bottom, t = top, n = near, f = far
2*n/(r-l) 0 0 0
0 2*n/(t-b) 0 0
(r+l)/(r-l) (t+b)/(t-b) -(f+n)/(f-n) -1
0 0 -2*f*n/(f-n) 0
where :
r = w / h
ta = tan( fov_y / 2 );
2*n / (r-l) = 1 / (ta*a) ---> 1/(r-l) = 1/(ta*a) * 1/(2*n)
2*n / (t-b) = 1 / ta ---> 1/(t-b) = 1/ta * 1/(2*n)
If you want to displace the filed of view by an offset (x, y), then you have to do it like this:
x_disp = 1/(ta*a) * x/(2*n)
y_disp = 1/ta * y/(2*n)
1/(ta*a) 0 0 0
0 1/t 0 0
x_disp y_disp -(f+n)/(f-n) -1
0 0 - 2*f*n/(f-n) 0
Set up the perspective projection matrix like this:
float x = ...;
float y = ...;
matrixProj = new PerspectiveProjectionMatrix(fovy, aspect, near, far);
matrixProj[2][0] = x * matrixProj[0][0] / (2.0 * near);
matrixProj[2][1] = y * matrixProj[1][1] / (2.0 * near);
To glFrustum, a pixel offset, can be applied like this:
float x_pixel = .....;
float y_pixel = .....;
float x_dipl = (right - left) * x_pixel / width_pixel;
float y_dipl = (top - bottom) * y_pixel / height_pixel;
glFrustum( left + x_dipl, right + x_dipl, top + y_dipl, bottom + y_dipl, near, far);

Related

Problem converting screen coordinates to OpenGL world coordinates

I'm using C++/Qt/OpenGL 4.3 to implement an OpenGL viewer and I'm stuck on converting mouse coordinates to world coordinates.
Update: After reading the comments and answer, I found this code to work correctly:
float depth;
double mouseX;
double mouseY;
_app->getCurPos(mouseX, mouseY);
glReadPixels(mouseX, _h - mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, (void *)&depth);
float x = (2.0f * mouseX) / _w - 1.0f;
float y = 1.0f - (2.0f * mouseY) / _h;
float z = depth * 2.0 - 1.0; // convert to NDC
QVector4D pos(x, y, z, 1.0);
QMatrix4x4 invVM(_app->camera()->ViewMatrix().inverted());
QMatrix4x4 invPM(_app->camera()->ProjectionMatrix().inverted());
QVector4D world = invVM * invPM * pos;
world /= world.w();
qDebug() << "world" << world;
(from the original question...)
Where does this code go wrong? Its output is included below.
Code:
// mouse coordinates to world coordinates
QVector3D GLCamera::transformScreen(float mouseX, float mouseY)
{
// our 3D view has Y as "UP"
float x = (2.0f * mouseX) / _w - 1.0f;
float y = 0.0f;
float z = 1.0f - (2.0f * mouseY) / _h;
// hard code NDC to upper-right of screen
x = 1.0;
y = 0.0;
z = 1.0;
QVector4D ndc = QVector4D(x, y, z, 1);
QVector4D point1 = ndc * mProjectionMatrix.inverted();
qDebug() << point1;
QVector3D point3D = QVector3D(point1) / point1.w();
qDebug() << point3D;
QVector3D point2 = point3D * mViewMatrix.inverted();
qDebug() << point2;
qDebug() << "mViewMatrix: " << mViewMatrix;
qDebug() << "inv : " << mViewMatrix.inverted();
qDebug() << "mProjMatrix: " << mProjectionMatrix;
qDebug() << "inv : " << mProjectionMatrix.inverted();
return point2;
}
Output:
qDebug: QVector4D(1, 0, 1, 1)
qDebug: QVector4D(0.742599, 0, -49.995, 49.005)
qDebug: QVector3D(0.0151535, 0, -1.0202)
# This is the returned value x seems so much smaller than z
qDebug: QVector3D(-0.000938916, -0.0418856, 0.0473429)
qDebug: mViewMatrix: QMatrix4x4(type:Translation,Rotation
1 0 0 0
0 0.748955 -0.662621 -0.626549
0 0.662621 0.748955 -22.9856
0 0 0 1
)
qDebug: inv : QMatrix4x4(type:Translation,Rotation
1 0 0 0
0 0.748955 0.662621 15.7
0 -0.662621 0.748955 16.8
0 0 0 1
)
qDebug: mProjMatrix: QMatrix4x4(type:General
1.34662 0 0 0
0 2.41421 0 0
0 0 -1.0002 -0.020002
0 0 -1 0
)
qDebug: inv : QMatrix4x4(type:General
0.742599 0 0 0
0 0.414214 0 0
0 0 0 -1
0 0 -49.995 50.005
)
The top right corner of the screen is (1, 1, depth) in NDC. You need a reasonable depth value to get the point that you want. Otherwise, you will only get a ray.
The matrices you show are supposed to be used for right-multiplication:
QVector4D point1 = mProjectionMatrix.inverted() * ndc;
QVector3D point2 = mViewMatrix.inverted() * point3D;
The perspective divide should happen at the very end and not in between. Up to this point, continue working with 4D vectors. Otherwise, passing the view-space position (point1) as a QVector3D will set a w-component of 0 and you will lose any translations. You can pass it as a QPoint3D, which would set w=1, but keeping the 4D vector is the safest choice.
QVector3D point3D = QVector3D(point1) / point1.w();
I would also suggest, naming the intermediate results reasonably (not just pointX). Name them pointClipSpace, pointViewSpace, pointWorldSpace or similar.
Again, deciding for an NDC depth is very important to get an interpretable result.

How to solve problem with lookat matrix on OpenGL/GLSL

I have the following code for my own look-at matrix(multiplication of matrices and cross product of vectors work perfectly, I checked it):
template<typename Type>
void setLookAt(Matrix4x4<Type>& matrix, const Vector3<Type> eye, const Vector3<Type> center, const Vector3<Type> up) noexcept
{
Math::Vector3f right = Math::cross(center, up).normalize();
Matrix4x4f lookAt({
right.getX(), right.getY(), right.getZ(), 0.0,
up.getX(), up.getY(), up.getZ(), 0.0,
center.getX(), center.getY(), center.getZ(), 0.0,
0.0, 0.0, 0.0, 1.0
});
Matrix4x4f additionalMatrix({
0.0, 0.0, 0.0, -(eye.getX()),
0.0, 0.0, 0.0, -(eye.getY()),
0.0, 0.0, 0.0, -(eye.getZ()),
0.0, 0.0, 0.0, 1.0
});
lookAt.mul(additionalMatrix);
matrix = lookAt;
}
template<typename Type>
void setPerspectiveMatrix(Matrix4x4<Type>& matrix, Type fov, Type aspect, Type znear, Type zfar) noexcept
{
const Type yScale = static_cast<Type>(1.0 / tan(RADIANS_PER_DEGREE * fov / 2));
const Type xScale = yScale / aspect;
const Type difference = znear - zfar;
matrix = {
xScale, 0, 0, 0,
0, yScale, 0, 0,
0, 0, (zfar + znear) / difference, 2 * zfar * znear / difference,
0, 0, -1, 0
};
}
Matrix multiplication implementation:
// static const std::uint8_t ROW_SIZE = 4;
// static const std::uint8_t MATRIX_SIZE = ROW_SIZE * ROW_SIZE;
// static const std::uint8_t FIRST_ROW = 0;
// static const std::uint8_t SECOND_ROW = ROW_SIZE;
// static const std::uint8_t THIRD_ROW = ROW_SIZE + ROW_SIZE;
// static const std::uint8_t FOURTH_ROW = ROW_SIZE + ROW_SIZE + ROW_SIZE;
template<class Type>
void Matrix4x4<Type>::mul(const Matrix4x4& anotherMatrix) noexcept
{
Type currentElements[MATRIX_SIZE];
std::copy(std::begin(mElements), std::end(mElements), currentElements);
const Type* otherElements = anotherMatrix.mElements;
for (std::uint8_t i = 0; i < MATRIX_SIZE; i += ROW_SIZE)
{
mElements[i] = currentElements[i] * otherElements[FIRST_ROW] +
currentElements[i + 1] * otherElements[SECOND_ROW] +
currentElements[i + 2] * otherElements[THIRD_ROW] +
currentElements[i + 3] * otherElements[FOURTH_ROW];
mElements[i + 1] = currentElements[i] * otherElements[FIRST_ROW + 1] +
currentElements[i + 1] * otherElements[SECOND_ROW + 1] +
currentElements[i + 2] * otherElements[THIRD_ROW + 1] +
currentElements[i + 3] * otherElements[FOURTH_ROW + 1];
mElements[i + 2] = currentElements[i] * otherElements[FIRST_ROW + 2] +
currentElements[i + 1] * otherElements[SECOND_ROW + 2] +
currentElements[i + 2] * otherElements[THIRD_ROW + 2] +
currentElements[i + 3] * otherElements[FOURTH_ROW + 2];
mElements[i + 3] = currentElements[i] * otherElements[FIRST_ROW + 3] +
currentElements[i + 1] * otherElements[SECOND_ROW + 3] +
currentElements[i + 2] * otherElements[THIRD_ROW + 3] +
currentElements[i + 3] * otherElements[FOURTH_ROW + 3];
}
}
Cross product implementation:
template<typename Type>
Math::Vector3<Type> cross(Vector3<Type> vector, Vector3<Type> anotherVector) noexcept
{
const Type x = vector.getY()*anotherVector.getZ() - vector.getZ()*anotherVector.getY();
const Type y = -(vector.getX()*anotherVector.getZ() - vector.getZ()*anotherVector.getX());
const Type z = vector.getX()*anotherVector.getY() - vector.getY()*anotherVector.getX();
return { x, y, z };
}
Using it:
// OpenGL
glUseProgram(mProgramID);
Matrix4x4f lookAt;
setLookAt(lookAt, { 0.0f, 0.0f, 3.0f }, { 0.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f });
glUniformMatrix4fv(glGetAttribLocation(mProgramID, "viewMatrix"), 1, GL_TRUE, lookAt);
Matrix4x4f projection;
setPerspectiveMatrix(projection, 45.0f, width / height, -0.1, 100.0f);
glUniformMatrix4fv(glGetAttribLocation(mProgramID, "projectionMatrix "), 1, GL_TRUE, projection);
// GLSL
layout (location = 0) in vec3 position;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
void main()
{
gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0f);
}
After using this code, I get a blank screen, although I would have to draw a cube. The problem is in the matrix itself, so other matrices work fine(offset, rotation, ...), but I can understand exactly where. Can you tell me what could be the problem?
"projectionMatrix" and "viewMatrix" are uniform variables. The uniform location can be get by glGetUniformLocation rather than glGetAttribLocation, which would return the attribute index of an active attribute:
GLint projLoc = glGetUniformLocation( mProgramID, "projectionMatrix" );
GLint viewLoc = glGetUniformLocation( mProgramID, "viewMatrix" );
At Perspective Projection the projection matrix describes the mapping from 3D points in the world as they are seen from of a pinhole camera, to 2D points of the viewport.
The eye space coordinates in the camera frustum (a truncated pyramid) are mapped to a cube (the normalized device coordinates).
At perspective projection the view space (volume) is defined by a frustum (a truncated pyramid), where the top of the pyramid is the viewer's position.
The direction of view (line of sight) and the near and the far distance define the planes which truncated the pyramid to a frustum (the direction of view is the normal vector of this planes).
This means both values, the distance to the near plane and the distance to the far plane have to be positive values:
Matrix4x4f lookAt;
setLookAt(lookAt, { 0.0f, 0.0f, 3.0f }, { 0.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f });
glUniformMatrix4fv(viewLoc, 1, GL_TRUE, lookAt);
Matrix4x4f projection;
setPerspectiveMatrix(projection, 45.0f, width / height, 0.1f, 100.0f); // 0.1f instead of -0.1f
glUniformMatrix4fv(projLoc, 1, GL_TRUE, projection);
The view space is the local system which is defined by the point of view onto the scene.
The position of the view, the line of sight and the upwards direction of the view, define a coordinate system relative to the world coordinate system.
The view matrix has to transform from world space to view space, so the view matrix is the inverse matrix of the view coordinate system.
If the coordinate system of the view space is a Right-handed system, where the X-axis points to the left and the Y-axis points up, then the Z-axis points out of the view (Note in a right hand system the Z-Axis is the cross product of the X-Axis and the Y-Axis).
The z-axis line of sight is the vector from the point of view eye to the traget center:
template<typename Type>
void setLookAt(Matrix4x4<Type>& matrix, const Vector3<Type> eye, const Vector3<Type> center, const Vector3<Type> up) noexcept
{
Vector3f mz( { eye.getX()-center.getX(), eye.getY()-center.getY(), eye.getZ()-center.getZ() } );
mz = mz.normalize();
Vector3f my = up.normalize();
Vector3f mx = cross(my, mz).normalize();
Type tx = dot( mx, eye );
Type ty = dot( my, eye );
Type tz = -dot( mz, eye );
matrix = {
mx.getX(), mx.getY(), mx.getZ(), tx,
my.getX(), my.getY(), my.getZ(), ty,
mz.getX(), mz.getY(), mz.getZ(), tz,
0.0, 0.0, 0.0, 1.0
};
}
template<typename Type>
Vector3<Type> cross(Vector3<Type> vector, Vector3<Type> anotherVector) noexcept
{
const Type x = vector.getY()*anotherVector.getZ() - vector.getZ()*anotherVector.getY();
const Type y = -(vector.getX()*anotherVector.getZ() - vector.getZ()*anotherVector.getX());
const Type z = vector.getX()*anotherVector.getY() - vector.getY()*anotherVector.getX();
return { x, y, z };
}
template<typename Type>
Vector3<Type> Vector3<Type>::normalize(void) const
{
Type len = std::sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
return { mV[0] / len, mV[1] / len, mV[2] / len };
}
template<typename Type>
Type dot(Vector3<Type> vector, Vector3<Type> anotherVector) noexcept
{
Type ax = vector.getX(), ay = vector.getY(), az = vector.getZ();
Type bx = anotherVector.getX(), by = anotherVector.getY(), bz = anotherVector.getZ();
return ax*bx + ay*by + az*bz;
}
A perspective projection matrix can be defined by a frustum.
The distances left, right, bottom and top, are the distances from the center of the view to the side faces of the frustum, on the near plane. near and far specify the distances to the near and far plane on the frustum.
r = right, l = left, b = bottom, t = top, n = near, f = far
x y z t
2*n/(r-l) 0 (r+l)/(r-l) 0
0 2*n/(t-b) (t+b)/(t-b) 0
0 0 -(f+n)/(f-n) -2*f*n/(f-n)
0 0 -1 0
If the projection is symmetric, where the line of sight is axis of symmetry of the view frustum, then the matrix can be simplified:
x y z t
1/(ta*a) 0 0 0
0 1/ta 0 0
0 0 -(f+n)/(f-n) -2*f*n/(f-n)
0 0 -1 0
where:
a = w / h
ta = tan( fov_y / 2 );
2 * n / (r-l) = 1 / (ta * a)
2 * n / (t-b) = 1 / ta
Further the projection matrix switches from an right-handed system to an left-handed system, because the z axis is turned.
template<typename Type>
void setPerspectiveMatrix(Matrix4x4<Type>& matrix, Type fov, Type aspect, Type znear, Type zfar) noexcept
{
const Type yScale = static_cast<Type>(1.0 / tan(RADIANS_PER_DEGREE * fov / 2));
const Type xScale = yScale / aspect;
const Type difference = zfar - znear;
matrix = {
xScale, 0, 0, 0,
0, yScale, 0, 0,
0, 0, -(zfar + znear) / difference, -2 * zfar * znear / difference,
0, 0, -1, 0
};
}

How can I move a 3D box in order to stay on a rotating plane?

I have a rotating plane and a box on it. When the plane rotates and tilts at an angle, I want the box to stay on the same position on the plane.
Here the plane is not tilted,
while here the plane is tilted,
but the box does not follow the plane downwards.
At every update I render the box, and translate it by the given vec3 in glm::translate:
{
glm::mat4 modelMatrix = glm::mat4(1);
modelMatrix = glm::translate(modelMatrix, glm::vec3(boxOX, boxOY, boxOZ));
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOX), glm::vec3(1, 0, 0));
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOY), glm::vec3(0, 1, 0));
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOZ), glm::vec3(0, 0, 1));
modelMatrix = glm::scale(modelMatrix, glm::vec3(0.2f));
RenderSimpleMesh(meshes["box"], shaders["ShaderLab8"], modelMatrix, glm::vec3(1, 0, 0));
}
The plane moves by hitting WASD keys:
if (window->KeyHold(GLFW_KEY_W) && (anglePlaneOX > -90.0f)) {
anglePlaneOX -= deltaTime * DELTA_SLOPE;
}
if (window->KeyHold(GLFW_KEY_S) && (anglePlaneOX < 90.0f)) {
anglePlaneOX += deltaTime * DELTA_SLOPE;
}
if (window->KeyHold(GLFW_KEY_D) && (anglePlaneOZ > -90.0f)) {
anglePlaneOZ -= deltaTime * DELTA_SLOPE;
}
if (window->KeyHold(GLFW_KEY_A) && (anglePlaneOZ < 90.0f)) {
anglePlaneOZ += deltaTime * DELTA_SLOPE;
}
I tried the following
when pressing the A key:
boxOY += deltaTime * (1 - (float)cos((double)anglePlaneOZ * PI / 180));
when pressing the W key:
boxOX -= deltaTime * sinf(anglePlaneOX * PI / 180);
But none of those seem to work.
What are the mathematical relations in order to move the box accordingly to the plane?
You have to do the translation "before" the rotation:
glm::mat4 modelMatrix = glm::mat4(1);
modelMatrix = glm::translate(modelMatrix, glm::vec3(boxOX, boxOY, boxOZ));
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOX), glm::vec3(1, 0, 0));
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOY), glm::vec3(0, 1, 0));
modelMatrix = glm::rotate(modelMatrix, RADIANS(anglePlaneOZ), glm::vec3(0, 0, 1));
modelMatrix = glm::translate(modelMatrix, glm::vec3(boxOX, boxOY, boxOZ)); // translate here
modelMatrix = glm::scale(modelMatrix, glm::vec3(0.2f));
RenderSimpleMesh(meshes["box"], shaders["ShaderLab8"], modelMatrix, glm::vec3(1, 0, 0));
Explanation:
The translation matrix looks like this:
glm::mat4 translate;
translate[0] : ( 1, 0, 0, 0 )
translate[1] : ( 0, 1, 0, 0 )
translate[2] : ( 0, 0, 1, 0 )
translate[3] : ( tx, ty, tz, 1 )
And the rotation matrix around Y-Axis looks like this:
mat4 rotate;
float angle;
rotate[0] : ( cos(angle), 0, sin(angle), 0 )
rotate[1] : ( 0, 1, 0, 0 )
rotate[2] : ( -sin(angle), 0, cos(angle), 0 )
rotate[3] : ( 0, 0, 0, 1 )
The result of translate * rotate is this:
model[0] : ( cos(angle), 0, sin(angle), 0 )
model[1] : ( 0, 1, 0, 0 )
model[2] : ( -sin(angle), 0, cos(angle), 0 )
model[3] : ( tx, ty, tz, 1 )
Note, the result of rotate * translate would be:
model[0] : ( cos(angle), 0, sin(angle), 0 )
model[1] : ( 0, 1, 0, 0 )
model[2] : ( -sin(angle), 0, cos(angle), 0 )
model[3] : ( cos(angle)*tx - sin(angle)*tx, ty, sin(angle)*tz + cos(angle)*tz, 1 )

How do you implement the rotation of the shape in place and the movement in a rotated state in OpenGL?

How do you implement the rotation of the shape in place and the movement in a rotated state?
I want to move the rotated shape towards the x and y axes of the screen I see.
I have already made the shape move in the direction of the x-axis and y-axis when I press the key.
But I can't move the way I want to. It's weird.
How do I set up the code to move the way I want to?
I'll put the code up for now.
glTranslatef(2.5 + puzX1, 2 + puzY1, 0);
glRotatef(100, 0.0, 0.0, 1.0);
glRotatef(puzang1, 0.0, 0.0, 1.0);
glTranslatef(-2.5+puzX1, -2+puzY1, 0);
I wrote the code to make it spin in place.
The code at the top and bottom of the code is a code that moves to the center of the shape and then returns it to its place.
What should I add?
Which part should be modified?
I am not good at English.
i'm so sorry
Can you help me?
Note, that drawing by glBegin/glEnd sequences, the fixed function pipeline matrix stack, is deprecated since decades.
Read about Fixed Function Pipeline and see Vertex Specification and Shader for a state of the art way of rendering.
How do you implement the rotation of the shape in place?
If the shade should rotate on its local axis, then you have to do the rotation before the translation. In the code this means the rotation instruction has to be after the translation instruction:
glTranslatef(2.5 + puzX1, 2 + puzY1, 0);
glRotatef(puzang1, 0.0, 0.0, 1.0);
glRotatef(100, 0.0, 0.0, 1.0);
See also OpenGL translation before and after a rotation
Explanation:
Translation: See the documentation of glTranslate:
glTranslate produces a translation by x y z . The current matrix (see glMatrixMode) is multiplied by this translation matrix, with the product replacing the current matrix.
Rotation: See the documentation of glRotate:
glRotate produces a rotation of angle degrees around the vector x y z . The current matrix (see glMatrixMode) is multiplied by a rotation matrix with the product replacing the current matrix.
The translation matrix looks like this:
Matrix4x4 translate;
translate[0] : ( 1, 0, 0, 0 )
translate[1] : ( 0, 1, 0, 0 )
translate[2] : ( 0, 0, 1, 0 )
translate[3] : ( tx, ty, tz, 1 )
And the rotation matrix around Z-Axis looks like this:
Matrix4x4 rotate;
float angle;
rotate[0] : ( cos(angle), sin(angle), 0, 0 )
rotate[1] : ( -sin(angle), cos(angle), 0, 0 )
rotate[2] : ( 0, 0, 1, 0 )
rotate[3] : ( 0, 0, 0, 1 )
The result of translate * rotate is this:
glTranslate( ..... );
glRotate( ..... );
model[0] : ( cos(angle), sin(angle), 0, 0 )
model[1] : ( -sin(angle), cos(angle), 0, 0 )
model[2] : ( 0, 1, 0, 0 )
model[3] : ( tx, ty, tz, 1 )
The result of rotate * translate is:
glRotate( ..... );
glTranslate( ..... );
model[0] : ( cos(angle), sin(angle), 0, 0 )
model[1] : ( -sin(angle), cos(angle), 0, 0 )
model[2] : ( 0, 0, 1, 0 )
model[3] : ( cos(angle)*tx - sin(angle)*tx, sin(angle)*ty + cos(angle)*ty, tz, 1 )

gluDisk rotation for mapping

I'm trying to create sub-cursor for terrain mapping.
Basic by code: (old image, but rotation is same)
image http://www.sdilej.eu/pics/274a90360f9c46e2eaf94e095e0b6223.png
This is when i testing change glRotate ax to my numbers:
image2 http://www.sdilej.eu/pics/146bda9dc51708da54b9249706f874fc.png
What i want:
image3 http://www.sdilej.eu/pics/69721aa237608b423b635945d430e561.png
My code:
void renderDisk(float x1, float y1, float z1, float x2, float y2, float z2, float radius, int subdivisions, GLUquadricObj* quadric)
{
float vx = x2 - x1;
float vy = y2 - y1;
float vz = z2 - z1;
//handle the degenerate case of z1 == z2 with an approximation
if( vz == 0.0f )
vz = .0001f;
float v = sqrt( vx*vx + vy*vy + vz*vz );
float ax = 57.2957795f * acos( vz/v );
if(vz < 0.0f)
ax = -ax;
float rx = -vy * vz;
float ry = vx * vz;
glPushMatrix();
glTranslatef(x1, y1, z1);
glRotatef(ax, rx, ry, 0.0);
gluQuadricOrientation(quadric, GLU_OUTSIDE);
gluDisk(quadric, radius - 0.25, radius + 5.0, subdivisions, 5);
glPopMatrix();
}
void renderDisk_convenient(float x, float y, float z, float radius, int subdivisions)
{
// Mouse opacity
glColor4f( 0.0f, 7.5f, 0.0f, 0.5f );
GLUquadricObj* quadric = gluNewQuadric();
gluQuadricDrawStyle(quadric, GLU_LINE);
gluQuadricNormals(quadric, GLU_SMOOTH);
gluQuadricTexture(quadric, GL_TRUE);
renderDisk(x, y, z, x, y, z, radius, subdivisions, quadric);
gluDeleteQuadric(quadric);
}
renderDisk_convenient(posX, posY, posZ, radius, 20);
This is a simple one. In your call to renderDisk() you supply bad arguments. Looks like you copied the function from some tutorial without understanding how it works. The first three parameters control the center position, and the other three parameters control rotation using a second position which the disk is always facing. If the two positions are equal (which is your case), this line is executed:
//handle the degenerate case of z1 == z2 with an approximation
if( vz == 0.0f )
vz = .0001f;
And setting z to nonzero makes the disc perpendicular to XZ plane, which is also the horizontal plane for your terrain. So ... to make it okay, you need to modify your function like this:
void renderDisk_convenient(float x, float y, float z, float radius, int subdivisions)
{
// Mouse opacity
glColor4f( 0.0f, 7.5f, 0.0f, 0.5f );
GLUquadricObj* quadric = gluNewQuadric();
gluQuadricDrawStyle(quadric, GLU_LINE);
gluQuadricNormals(quadric, GLU_SMOOTH);
gluQuadricTexture(quadric, GL_TRUE);
float upX = 0, upY = 1, upZ = 0; // up vector (does not need to be normalized)
renderDisk(x, y, z, x + upX, y + upY, z + upZ, radius, subdivisions, quadric);
gluDeleteQuadric(quadric);
}
This should turn the disc into the xz plane so it will be okay if the terrain is flat. But in other places, you actually need to modify the normal direction (the (upX, upY, upZ) vector). If your terrain is generated from a heightmap, then the normal can be calculated using code such as this:
const char *p_s_heightmap16 = "ps_height_1k.png";
const float f_terrain_height = 50; // terrain is 50 units high
const float f_terrain_scale = 1000; // the longer edge of terrain is 1000 units long
TBmp *p_heightmap;
if(!(p_heightmap = p_LoadHeightmap_HiLo(p_s_heightmap16))) {
fprintf(stderr, "error: failed to load heightmap (%s)\n", p_s_heightmap16);
return false;
}
// load heightmap
TBmp *p_normalmap = TBmp::p_Alloc(p_heightmap->n_width, p_heightmap->n_height);
// alloc normalmap
const float f_width_scale = f_terrain_scale / max(p_heightmap->n_width, p_heightmap->n_height);
// calculate the scaling factor
for(int y = 0, hl = p_normalmap->n_height, hh = p_heightmap->n_height; y < hl; ++ y) {
for(int x = 0, wl = p_normalmap->n_width, wh = p_heightmap->n_width; x < wl; ++ x) {
Vector3f v_normal(0, 0, 0);
{
Vector3f v_pos[9];
for(int yy = -1; yy < 2; ++ yy) {
for(int xx = -1; xx < 2; ++ xx) {
int sx = xx + x;
int sy = yy + y;
float f_height;
if(sx >= 0 && sy >= 0 && sx < wh && sy < hh)
f_height = ((const uint16_t*)p_heightmap->p_buffer)[sx + sy * wh] / 65535.0f * f_terrain_height;
else
f_height = 0;
v_pos[(xx + 1) + 3 * (yy + 1)] = Vector3f(xx * f_width_scale, f_height, yy * f_width_scale);
}
}
// read nine-neighbourhood
/*
0 1 2
+----------+----------+
|\ | /|
| \ | / |
| \ | / |
| \ | / |
3|_________\|/_________|5
| 4/|\ |
| / | \ |
| / | \ |
| / | \ |
|/ | \|
+----------+----------+
6 7 8
*/
const int p_indices[] = {
0, 1, //4,
1, 2, //4,
2, 5, //4,
5, 8, //4,
8, 7, //4,
7, 6, //4,
6, 3, //4,
3, 0 //, 4
};
for(int i = 0; i < 8; ++ i) {
Vector3f a = v_pos[p_indices[i * 2]];
Vector3f b = v_pos[p_indices[i * 2 + 1]];
Vector3f c = v_pos[4];
// triangle
Vector3f v_tri_normal = (a - c).v_Cross(b - c);
v_tri_normal.Normalize();
// calculate normals
v_normal += v_tri_normal;
}
v_normal.Normalize();
}
// calculate normal from the heightmap (by averaging the normals of eight triangles that share the current point)
uint32_t n_normalmap =
0xff000000U |
(max(0, min(255, int(v_normal.z * 127 + 128))) << 16) |
(max(0, min(255, int(v_normal.y * 127 + 128))) << 8) |
max(0, min(255, int(-v_normal.x * 127 + 128)));
// calculate normalmap color
p_normalmap->p_buffer[x + wl * y] = n_normalmap;
// use the lightmap bitmap to store the results
}
}
(note this contains some structures and functions that are not included here so you won't be able to use this code directly, but the basic concept is there)
Once you have the normals, you need to sample normal under location (x, z) and use that in your function. This will still make the disc intersect the terrain where there is a steep slope next to flat surface (where the second derivative is high). In order to cope with that, you can either lift the cursor up a bit (along the normal), or disable depth testing.
If your terrain is polygonal, you could use vertex normals just as well, just take triangle that is below (x, y, z) and interpolate it's vertices normals to get the normal for the disc.
I hope this helps, feel free to comment if you need further advice ...