How can i flip, ot mirror up/left of the image OBJECT, not the screen in the onDraw method?
I tried scale(1, -1, 1) that not worked
my code:
opengl.selectVertex(vertexname)
.translate(x-1, y, -9);
if (opengl.getPathModifier(vertexname).getAngle()>-180 &&
opengl.getPathModifier(vertexname).getAngle()<0 ) {
opengl.selectVertex(vertexname).scale(-scale,scale,1);
} else {
opengl.selectVertex(vertexname).scale(scale,scale,1);
}
opengl.rotate(opengl.getPathModifier(vertexname).getAngle()+90, 0, 0, 1);
Not 100% sure what you're asking here, but I think what you want to do is transform the projection matrix. If you want to flip so that things on the left appear on the right then you need to Scale the projection matrix by (-1, 1, 1). If you want things at the top to appear at the bottom you need to scale by (1,-1, 1) and if you want both you can scale (-1, -1, 1).
Edit based on extra information: If all you want to do is display the object exactly the same, but with texture flipped, you need to change the texture coordinates of the vertices in the objects - flip the texture coordinates by replacing the old u texture coordinate with 1-u.
If you are using glFrustum(l,r,b,t,n,f) then change it yo glFrustum(l,r,t,b,n,f), in case you want a vertical flip. This apply also to glOrtho function.
Related
I am trying to implement a logic where, on mouse click, a shot is fired at an object.To do so, I did the following,
I first considered the .obj file of my model and found the region (list of coordinates) that the shot works on (a particular weak point of the body).
I then considered the least and largest x,y and z values present in the file for that particular region (xmin,ymin,zmin and xmax,ymax,zmax).
To figure out whether the shot has landed on the weak point, I considered the assumption that a shot would land on the weak point, if the coordinates of the shot lie between (xmin,ymin,zmin) and (xmax,ymax,zmax).
I assumed the coordinates from the .obj file to be the actual coordinates of the model, since the assimp code I have directly loads in the coordinates of the model. Considering (xmin,ymin,zmin) and (xmax,ymax,zmax), I converted the coordinates to window coordinates via gluProject().
I then considered the current cursor position and checked if the cursor position lies between (xmin,ymin,zmin) and (xmax,ymax,zmax).
The problems I now face are:
The object coordinates provided in the .obj file range between -4 to 4, which then lie around 1.0 after gluProject(), whereas the cursor position lies between (0,0) and (1280,720).
After gluProject(), (xmin,ymin) and (xmax,ymax) are either (0,1) or (1,0) whereas the zmin and zmax values seem fine.
How can I get my logic working ?
Here is the code:
// Call shader to draw and acquire necessary information for gluProject()
modelShader.use();
modelShader.setMat4("projection", projection);
modelShader.setMat4("view", view);
glm::mat4 model_dragon;
double time=glfwGetTime();
model_dragon=glm::translate(model_dragon, glm::vec3(cos((360.0-time)/2.0)*60.0,cos(((360.0-time)/2.0))*(-2.5),sin((360-time)/1.0)*60.0));
model_dragon=glm::rotate(model_dragon,(float)(glm::radians(30.0)),glm::vec3(0.0,0.0,1.0));
model_dragon=glm::scale(model_dragon,glm::vec3(1.4,1.4,1.4));
modelShader.setMat4("model", model_dragon);
collision_model=model_dragon;collision_view=view;collision_proj=projection; //so that I can provide the view,model and projection required for gluProject()
ourModel.Draw(modelShader);
Mouse button callback
// Note: dragon_min and dragon_max variables hold the constant position of the min and max coordinates.
void mouse_button_callback(GLFWwindow* window,int button,int action,int mods){
if(button==GLFW_MOUSE_BUTTON_LEFT && action==GLFW_PRESS){
Mix_PlayChannel( -1, shot, 0 ); //Play sound
GLdouble x,y,xmin,ymin,zmin,xmax,ymax,zmax,dmodel[16],dproj[16];
GLint dview[16];
float *model = (float*)glm::value_ptr(collision_model);
float *proj = (float*)glm::value_ptr(collision_proj);
float *view = (float*)glm::value_ptr(collision_view);
for (int i = 0; i < 16; ++i){dmodel[i]=model[i];dproj[i]=proj[i];dview[i]=(int)view[i];} // Convert mat4 to double array
glfwGetCursorPos(window,&x,&y);
gluProject(dragon_min_x,dragon_min_y,dragon_min_z,dmodel,dproj,dview,&xmin,&ymin,&zmin);
gluProject(dragon_max_x,dragon_max_y,dragon_max_z,dmodel,dproj,dview,&xmax,&ymax,&zmax);
if((x>=xmin && x<=xmax) && (y>=ymin && y<=ymax)){printf("Hit\n");defense--;}
The .obj coordinates have eg. values as shown:
0.032046 1.533727 4.398055
You are confusing the parameters of gluProject, especially the view parameter. This parameter should contain 4 integers which describe the viewport (x,y,width,height) and not the view matrix.
gluProject (and a lot of other glu functions) are tailored towards the fixed function pipeline and their matrix stacks. Due to this, you have to pass the following information:
model: The modelview matrix, as returned by glGetDoublev( GL_MODELVIEW_MATRIX, ...)).
proj: The projection matrix, as returned by glGetDoublev( GL_PROJECTION_MATRIX, ...).
view: The current viewport, as returned by glGetIntegerv( GL_VIEWPORT, ...)
As you see, the view matrix is packed together with the model matrix and view contains the viewport.
I'd strongly advice not to use glu functions at all when working with modern OpenGL. Especially when the matrices are already stored in glm, it would be better to use glm::project.
Note1: Converting a floating point matrix to an integer matrix by casting each element almost never results in anything meaningful.
Note2: When projecting a bounding rectangle to screenspace, the result will in general not be a rectangle anymore. During projection, angles are not preserved, thus the result is a general four cornered polygon and not a rectangle anymore. Same goes for bounding boxes: You can't even guarantee that the projected box is contained in the screen-space rectangle defined by projecting [x_min, y_min, z_min] and [x_max, y_max, z_max].
I have a question about applying transformations to textures/vertices (I'm an OpenGL learner). I've got a situation where I'm using SharpGL's DrawText function. You can specify the X and Y coordinate but I want to rotate the text so that it's oriented vertically. The function doesn't take an angle though. The way that SharpGL writes text is that it gets an array of bitmaps that it writes to the screen.
No matter what I try, my text always comes out horizontal.
The gl.DrawText function does the following (I dug into the source code).
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.PushMatrix();
gl.LoadIdentity();
int[] viewport = new int[4];
gl.GetInteger(OpenGL.GL_VIEWPORT, viewport);
gl.Ortho(0, width, 0, height, -1, 1);
// Create the appropriate modelview matrix.
gl.MatrixMode(OpenGL.GL_MODELVIEW);
gl.PushMatrix();
gl.LoadIdentity();
gl.Color(r, g, b);
gl.RasterPos(x, y);
gl.PushAttrib(OpenGL.GL_LIST_BIT | OpenGL.GL_CURRENT_BIT |
OpenGL.GL_ENABLE_BIT | OpenGL.GL_TRANSFORM_BIT);
gl.Color(r, g, b);
gl.Disable(OpenGL.GL_LIGHTING);
gl.Disable(OpenGL.GL_TEXTURE_2D);
gl.Disable(OpenGL.GL_DEPTH_TEST);
gl.RasterPos(x, y);
// Set the list base.
gl.ListBase(fontBitmapEntry.ListBase);
// Create an array of lists for the glyphs.
var lists = text.Select(c => (byte) c).ToArray();
// Call the lists for the string.
gl.CallLists(lists.Length, lists);
gl.Flush();
Is there a way that I can make transformation calls apply to any drawing that has already been made and had the associated matrices popped off?
If SharpGL is using glBitmap() behind the scenes you'll have to render the text to a texture (glReadPixels()/PBO/FBO) then render a (rotated) quad with that texture.
I have managed to rotate a rectangle in OpenGL (C++) just fine. I am making a program that tests two rectangles for collision using the "separated axis theorem". To use the theorem, I need the x and y coordinates of each corner of the rectangle, but my problem is that, although I call glRotatef(...), the coordinates of the corners of the rectangle are not changed to the values that they are rotated too, but the rectangle rotates as it should. How can I update the coordinates of my rectangle after glRotatef is called?
Code:
// float r1.x[4] and r1.y[4] hold the x and y position of each of the 4 corners, starting with the upper left (r1.x[0], r1.y[0])
glLoadIdentity();
glTranslatef((r1.x[0] + r1.x[2]) / 2, (r1.y[1] + r1.y[3]) / 2, 0); // Translates matrix to center of rectangle
glRotatef(r1.angle, 0, 0, 1);
glTranslatef(-((r1.x[0] + r1.x[2]) / 2), -((r1.y[1] + r1.y[3]) / 2), 0); // Translates back
r1.angle++;
glBegin(GL_QUADS);
glVertex2f(r1.x[0], r1.y[0]);
glVertex2f(r1.x[1], r1.y[1]);
glVertex2f(r1.x[2], r1.y[2]);
glVertex2f(r1.x[3], r1.y[3]);
glEnd();
Transformation calls in OpenGL (like glTranslatef and glRotatef) update an internal transformation matrix that get multiplied by the points you provide before getting drawn on the screen. OpenGL does not at all touch your data.
In general, that is what you want. You have a model that is constant in time, but it gets transformed around.
If however, you do need to update your data, you need to create your own transformation matrix, manually multiply it and then draw with a clean transformation matrix (with glLoadIdentity)
You could get a little help from OpenGL though, as you can get the transformation matrix from OpenGL, but I don't recommend this. The math is not that hard and you'd appreciate learning how to do it.
Hey so I'm integrating box2d and SFML, and box2D has the same odd, mirrored Y-axis coordinate system as SFML, meaning everything is rendered upside down. Is there some kind of function or short amount of code I can put that simply mirrors the window's render contents?
I'm thinking I can put something in sf::view to help with this...
How can i easily flip the Y-axis easily, for rendering purposes, not effecting the bodies dimensions/locations?
I don't know what is box2d but when I wanted to flip Y axis using openGL, I just applied negative scaling factor to projection matrix, like:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glScalef(1.0f, -1.0f, 1.0f);
If you want to do it independent of openGL simply apply a sf::View with a negative x value.
It sounds like your model uses a conventional coordinate system (positive y points up), and you need to translate that to the screen coordinate system (positive y points down).
When copying model/Box2D position data to any sf::Drawable, manually transform between the model and screen coordinate systems:
b2Vec2 position = body->GetPosition();
sprite.SetPosition( position.x, window.GetHeight() - position.y )
You can hide this in a wrapper class or function, but it needs to sit between the model and renderer as a pre-render transform. I don't see a place to set that in SFML.
I think Box2D has the coordinate system you want; just set the gravity vector based on your model (0, -10) instead of the screen.
How can i easily flip the Y-axis easily, for rendering purposes, not effecting the bodies dimensions/locations?
By properly applying transforms. First, you can apply a transform that sets the window's bottom-left corner as the origin. Then, scale the Y axis by a factor of -1 to flip it as the second transform.
For this, you can use sf::Transformable to specify each transformation individually (i.e., the setting of the origin and the scaling) and then – by calling sf::Transformable::getTransform() – obtain an sf::Transform object that corresponds to the composed transform.
Finally, when rendering the corresponding object, pass this transform object to the sf::RenderTarget::draw() member function as its second argument. An sf::Transform object implicitly converts to a sf::RenderStates which is the second parameter type of the corresponding sf::RenderTarget::draw() overload.
As an example:
#include <SFML/Graphics.hpp>
auto main() -> int {
auto const width = 300, height = 300;
sf::RenderWindow win(sf::VideoMode(width, height), "Transformation");
win.setFramerateLimit(60);
// create the composed transform object
const sf::Transform transform = [height]{
sf::Transformable transformation;
transformation.setOrigin(0, height); // 1st transform
transformation.setScale(1.f, -1.f); // 2nd transform
return transformation.getTransform();
}();
sf::RectangleShape rect({30, 30});
while (win.isOpen()) {
sf::Event event;
while (win.pollEvent(event))
if (event.type == sf::Event::Closed)
win.close();
// update rectangle's position
rect.move(0, 1);
win.clear();
rect.setFillColor(sf::Color::Blue);
win.draw(rect); // no transformation applied
rect.setFillColor(sf::Color::Red);
win.draw(rect, transform); // transformation applied
win.display();
}
}
There is a single sf::RectangleShape object that is rendered twice with different colors:
Blue: no transform was applied.
Red: the composed transform was applied.
They move in opposite directions as a result of flipping the Y axis.
Note that the object space position coordinates remain the same. Both rendered rectangles correspond to the same object, i.e., there is just a single sf::RectangleShape object, rect – only the color is changed. The object space position is rect.getPosition().
What is different for these two rendered rectangles is the coordinate reference system. Therefore, the absolute space position coordinates of these two rendered rectangles also differ.
You can use this approach in a scene tree. In such a tree, the transforms are applied in a top-down manner from the parents to their children, starting from the root. The net effect is that children's coordinates are relative to their parent's absolute position.
I'm currently calling Trace (method below) from a game loop. Right now all I'm trying to do is get the world coordinates from the screen mouse so I can move objects around in the world space. The values I'm getting from gluUnProject are however; puzzling me.
I was using glReadPixel(...) to get the Z value but that produced little to no movement in the object I was drawing and the resulting vector ended up being the same as my cameras location (except for the tiny decimal changes due to mouse movement), so I decided to get rid of the call and replace the Z value with 1.
My question is: Does the following code look right to you? Every example I've seen thusfar is either identical or -very- similar but I can't seem to produce correct results, even if I lock down the Y axis. If the code is correct, then I'm guessing that I'm just not using the resulting vector properly. Should I not be able to draw an object or point directly with the resulting vector or do I have to do something else with it, like normalize?
The current render mode is GL_RENDER and I am using glFrustum with a NearZ value of 1 and FarZ value of 2048, to create a perspective. There is also a series of viewports created along with scissors, with a size and width of 512x768 and positioned in each corner of a 1024x768 window. Trace(...) is called in between rendering of the upper left viewport and is the only perspective projection, while the other viewports are orthographic. FOV is set to 45.
void VideoWindow::Trace(int cursorX, int cursorY)
{
double objX, objY, objZ;//holder for world coordinates
GLint view[4];//viewport dimensions+pos
GLdouble p[16];//projection matrix
GLdouble m[16];//modelview matrix
GLdouble z;//Z-Buffer Value?
glGetDoublev (GL_MODELVIEW_MATRIX, m);
glGetDoublev (GL_PROJECTION_MATRIX,p);
glGetIntegerv( GL_VIEWPORT, view );
//view[3]-cursorY = conversion from upper left (0,0) to lower left (0,0)
//Unproject 2D Screen coordinates into wonderful world coordinates
gluUnProject(cursorX, view[3]-cursorY, 1, m, p, view, &objX, &objY, &objZ);
//Do something useful here???
}
Any ideas?
Edit: I've changed the winZ value to 0.5 instead of 1 which gives a vector thats more reasonable but drawing a point still wasn't matching the mouse. I found out that the value of view[3] was 384 which is correct for the viewport I'm using but I replaced it with 768 (the actual window size) and the point followed the mouse 100%. Further experimentation reveals that I can't use the coordinates to move around a 3D object in the perspective world space using this these coordinates however moving around 3D object in Orthographic space works fine.
The winz argument to gluUnproject specifies the depth from the camera at which you're "picking" your points. As you've stated this coordinate should be in the [0, 1] range.
Some tutorials like NeHes read out the z coordinate from the depth buffer so that you "pick" at the right depth, of course for this to work you'll have to do the gluUnproject after you've rendered everything else.
Regardless, if you set winz to 0.5 or something (not 0 or 1 or the point will end up on the near or far clip plane, and maybe culled) and do the following:
gluUnProject(cursorX, view[3]-cursorY, 0.5, m, p, view, &objX, &objY, &objZ);
//Do something useful here???
glPointSize(10);
glBegin(GL_POINTS);
glColor3f(1, 0, 0);
glVertex3f(objX, objY, objZ);
glEnd();
You should end up with a red blob at the mouse pointer (provided nothing else overdraws it afterwards and you don't have any funny render states which renders the point invisible).
just a thought, but if the third argument to gluUnProject is the z distance to the camera, wouldn't any point you draw at that location be on the near clipping plane of your frustum?
Better make that z value a bit higher.