It seems that this is quite a common question, but I can't find a person with my same circumstances. The closest seems to be: OpenGL: scale then translate? and how?.
The problem I'd really like some help with is to do with moving around while zoomed into (and out of) a 2d scene using OpenGl. The code for zooming out is pretty simple:
void RefMapGLScene::zoomOut(){
currentScale = currentScale-zoomFactor;
double xAdjust = (((get_width())*zoomFactor/2));
double yAdjust = ((get_height()*zoomFactor/2));
zoomTranslateX -= xAdjust;
zoomTranslateY -= yAdjust;
}
The code for zooming in is basically the same (add the zoomFactor to currentScale, and increment zoomTranslateX and Y).
The code for rending everything is also simple:
glPushMatrix();
glTranslated(-zoomTranslateX, -zoomTranslateY, 0);
glScaled(currentScale, currentScale, 1);
glTranslated(totalMovedX, totalMovedY, 0);
graph->draw();
glPopMatrix();
Essentially, zoomTranslate stores an adjustment needed to make the screen move a little towards the middle when zooming. I don't do anything nice like move to where the mouse is pointing, I just move to the middle (ie, to the right and up/down depending on your co-ordinate system). TotalMovedX and Y store the mouse movement as follows:
if (parent->rightButtonDown){
totalMovedX += (-(mousex-curx))/currentScale;
totalMovedY += (-(mousey-cury))/currentScale;
}
Dragging while not zoomed in or out works great. Zooming works great. Dragging while zoomed in/out does not work great :) Essentially, when zoomed in, the canvas moves a lot slower than the mouse. The opposite for when zoomed out.
I've tried everything I can think of, and have read a lot of this site about people with similar issues. I also tried reimplementing my code using glOrtho to handle the zooms, but ended up facing other problems, so came back to this way. Could anybody please suggest how I handle these dragging events?
The order of operations matter. Operations on matrices are applied in the reverse order in which you multiplied the matrices. In your case you apply the canvas movement before the scaling, so your mouse drag is also zoomed.
Change your code to this:
glPushMatrix();
glTranslated(-zoomTranslateX, -zoomTranslateY, 0);
glTranslated(totalMovedX, totalMovedY, 0);
glScaled(currentScale, currentScale, 1);
graph->draw();
glPopMatrix();
Also after changing that order you don't have to scale your mouse moves, so you can omit that division by currentScale
if (parent->rightButtonDown){
totalMovedX += (-(mousex-curx));
totalMovedY += (-(mousey-cury));
}
Related
While my problem lies strictly in the opacity of the tristrip, I'd like to give some context first.
Recently I started developing a game through LibGdx which involves 2D circles which bounce around the screen. So as to provide a neat graphical effect, I created a small system that would provide a "tail" to the actors, which would fade over time. Visually, it looks like this:
Nice Trail Example
Now that ended up looking satisfactory. My problem, however, lies in situation where parts of the "trail" effect overlap, creating an ugly artifact which I would guess is the sum of the opacities of the points.
Ugly Trail Example
I believe this problem lies in the way with which the tristrip is drawn, specifically with the blending methods used.
The code used to generate the trail is as follows:
Array<Vector2> tristrip = new Array<Vector2>(); //Contains the vector information for OpenGL to build the strip.
Array<Vector2> texcoord = new Array<Vector2>(); //Contains the opacity information for the corresponding tristrip point.
// ... Code Here.... //
gl20.begin(camera.combined, GL20.GL_TRIANGLE_STRIP);
for (int i = 0; i < tristrip.size; i++) {
if (i == batchSize) {
gl20.end();
gl20.begin(camera.combined, GL20.GL_TRIANGLE_STRIP);
}
Vector2 point = tristrip.get(i);
Vector2 textcoord = texcoord.get(i);
gl20.color(color.r, color.g, color.b, color.a); // Color.WHITE
gl20.texCoord(textcoord.x, 0f);
gl20.vertex(point.x, point.y, 0);
}
gl20.end();
It is also important to note that the draw function for the strip is called within another class, in this fashion:
private void renderFX() {
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Array<Ball> balls = mainstage.getBalls();
for (int i = 0; i < balls.size; i++) { //Draws the trails for each actor
balls.get(i).drawFX();
}
}
Is this problem a rookie mistake on my part, or was my implementation of the drawing of the vector array tristrip flawed from the start? How can I fix the blending issue in order to create smoother trails even in the presence of sharp curves?
Thanks in advance...
Edit: Since originally asking this question, I've experimented with some possible solutions, also implementing Deniz Yılmaz's suggestion of using a FBO to facilitate blending. Given that, my render function currently looks like this:
private void renderFX() {
frameBuffer.begin();
Gdx.gl20.glDisable(GL20.GL_BLEND);
Gdx.gl20.glClearColor(0f, 0f, 0f, 0);
Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.gl20.glEnable(GL20.GL_STENCIL_TEST);
Gdx.gl20.glStencilOp(GL20.GL_KEEP, GL20.GL_INCR, GL20.GL_INCR);
Gdx.gl20.glStencilMask(0xFF);
Gdx.gl20.glClear(GL20.GL_STENCIL_BUFFER_BIT);
Array<Ball> balls = mainstage.getBalls();
for (int i = 0; i < balls.size; i++) {
Gdx.gl20.glStencilFunc(GL20.GL_EQUAL, 0, 0xFF);
balls.get(i).drawFX(1f, Color.RED);
}
frameBuffer.end();
}
As shown, I've also experimented with stencils so as to try and mask the overlapping portion of the trail. This approach, however, results in the following visuals:
Stenciled Version
Again, this is not ideal, and has made me realize that approaching this problem by masking is not a good idea, as the opacity gradient will never be smooth in the corners as there will always be a sharp line between the two overlapping opacity values, even if somehow the logic prevents blending.
Given that, how else could I approach this problem? Should I scrap this method entirely if I plan to achieve a smooth gradient for this trail effect?
Thanks again.
glBlendFunc() is useless in this case because by default the values calculated based on the blend function are added.
So something like glBlendEquation(GL_MAX) needed
BUT
blending alone won't work, since it can't tell the difference between what is the background and what is the overlapping shapes.
Instead use FrameBuffer to draw trail with a glBlendEquation.
https://github.com/mattdesl/lwjgl-basics/wiki/FrameBufferObjects
I am trying to rotate am image around its origin(center) in QT using QWidgts in C++. I experimented a lot of things here, but no matter what I do, the image keeps rotating around some arbitrary position I have no clue of. Kindly, help me out here. I am new to QT.
void gaugeWithRedZoneImage::rotate()
{
QPixmap pixmap(*gaugeMainScreen->pixmap());
QMatrix rm;
rm.translate(0, 0);
rm.rotate(-360);
pixmap = pixmap.transformed(rm);
gaugeMainScreen->setPixmap(pixmap);
/*QTransform rotate_disc;
rotate_disc.translate(pixmap.width()/2.0 , pixmap.height()/2.0);
rotate_disc.rotate(-60);
rotate_disc.translate(-(pixmap.width()/2.0) , -(pixmap.height()/2.0));
pixmap = pixmap.transformed(rotate_disc);
gaugeMainScreen->setPixmap(pixmap);*/
}
Form the documentation of QPixmap::transformed():
The transformation transform is internally adjusted to compensate for unwanted translation; i.e. the pixmap produced is the smallest pixmap that contains all the transformed points of the original pixmap.
This means that the method ensures no clipping takes place by appending the canvas. No matter what your rotation center was, the automatic extension of canvas will almost always result in a perceived shift.
Image examples might help to further diagnose the problem.
As ypnos said, your problem isn't the rotation center. When you rotate your image, its width and height will most likely change and no longer fit your container (gaugeMainScreen) dimensions.
You have some possibilities to overcome this problem. One of them is to set your container to scale its contents (you can use the method setScaledContents()). In this case, you have to keep the original image around and use it whenever you apply a rotation, otherwise your image will appear increasingly smaller.
1. Goal
My colleague and I have been trying to render rotated ellipsoids in Qt. The typical solution approach, as we understand it, consists of shifting the center of the ellipsoids to the origin of the coordinate system, doing the rotation there, and shifting back:
http://qt-project.org/doc/qt-4.8/qml-rotation.html
2. Sample Code
Based on the solution outlined in the link above, we came up with the following sample code:
// Constructs and destructors
RIEllipse(QRect rect, RIShape* parent, bool isFilled = false)
: RIShape(parent, isFilled), _rect(rect), _angle(30)
{}
// Main functionality
virtual Status draw(QPainter& painter)
{
const QPen& prevPen = painter.pen();
painter.setPen(getContColor());
const QBrush& prevBrush = painter.brush();
painter.setBrush(getFillBrush(Qt::SolidPattern));
// Get rectangle center
QPoint center = _rect.center();
// Center the ellipse at the origin (0,0)
painter.translate(-center.x(), -center.y());
// Rotate the ellipse around its center
painter.rotate(_angle);
// Move the rotated ellipse back to its initial location
painter.translate(center.x(), center.y());
// Draw the ellipse rotated around its center
painter.drawEllipse(_rect);
painter.setBrush(prevBrush);
painter.setPen(prevPen);
return IL_SUCCESS;
}
As you can see, we have hard coded the rotation angle to 30 degrees in this test sample.
3. Observations
The ellipses come out at wrong positions, oftentimes outside the canvas area.
4. Question
What is wrong about the sample code above?
Best regards,
Baldur
P.S. Thanks in advance for any constructive response?
P.P.S. Prior to posting this message, we searched around quite a bit on stackoverflow.com.
Qt image move/rotation seemed to reflect a solution approach similar to the link above.
In painter.translate(center.x(), center.y()); you shift your object by the amount of current coordinate which makes (2*center.x(), 2*center.y()) as a result. You may need:
painter.translate(- center.x(), - center.y());
The theory of moving an object back to its origin, rotating and then replacing the object's position is correct. However, the code you've presented is not translating and rotating the object at all, but translating and rotating the painter. In the example question that you've referred to, they're wanting to rotate the whole image about an object, which is why they move the painter to the object's centre before rotating.
The easiest way to do rotations about a GraphicsItem is to initially define the item with its centre in the centre of the object, rather than in its top left corner. That way, any rotation will automatically be about the objects centre, without any need to translate the object.
To do this, you'd define the item with a bounding rect for x,y,width,height with (-width/2, -height/2, width, height).
Alternatively, assuming your item is inherited from QGraphicsItem or QGraphicsObject, you can use the function setTransformOriginPoint before any rotation.
I am using OpenGL to create the 3D space.
I have a spaceship which can fire lasers.
Up until now I have had it so that the lasers will simply to deeper into the Z-axis once fired.
But I am attempting to make a proper aiming system with crosshairs so that you can aim and shoot in any direction, but I have not been successfull in trying to update the laser's path.
I have a directional vector based off the lasers end tip and start tip, which is gotten from the aiming.
How should I update the laser's X,Y,Z values (or vectors) properly so that it looks natural?
I think I see.
Let's say you start with the aiming direction as a 3D vector, call it "aimDir". Then in your update loop add all 3 (x, y and z) to the projectile "position". (OK, at the speed of light you wouldn't actually see any movement, but I think I see what you're going for here).
void OnUpdate( float deltaT )
{
// "move" the laser in the aiming direction, scaled by the amount of time elapsed
// since our last update (you probably want another scale factor here to control
// how "fast" the laser appears to move)
Vector3 deltaLaser = deltaT * aimDir; // calc 3d offset for this frame
laserEndpoint += deltaLaser; // add it to the end of the laser
}
then in the render routine draw the laser from the firing point to the new endpoint:
void OnRender()
{
glBegin(GL_LINES);
glVertex3f( gunPos.x, gunPos.Y, gunPos.z );
glVertex3f( laserEndPoint.x, laserEndPoint.y, laserEndPoint.z );
glEnd();
}
I'm taking some liberties because I don't know if you're using glut, sdl or what. But I'm sure you have at least an update function and a render function.
Warning, just drawing a line from the gun to the end of the laser might be disappointing visually, but it will be a critical reference for adding better effects (particle systems, bloom filter, etc.). A quick improvement might be to make the front of the laser (line) a bright color and the back black. And/or make multiple lines like a machine gun. Feel free to experiment ;-)
Also, if the source of the laser is directly in front of the viewer you will just see a dot! So you may want to cheat a bit and fire from just below or to the right of the viewer and then have in fire slightly up or in. Especially if you have one one each side (wing?) that appear to converge as in conventional machine guns.
Hope that's helpful.
I'm really going crazy trying to figure this out, so any help would be really appreciated. I'm trying to hide most of a sprite and show it gradually. This works fine if I only work with rectangles. For example, I found someone's implementation of the "ClippingNode" class and it worked well, namely, this part of the code:
-(void) visit
{
glPushMatrix();
glEnable(GL_SCISSOR_TEST);
glScissor(clippingRegion.origin.x + positionInPixels_.x, clippingRegion.origin.y + positionInPixels_.y, clippingRegion.size.width, clippingRegion.size.height);
[super visit];
glDisable(GL_SCISSOR_TEST);
glPopMatrix();
}
The problem is I need an irregular shape, not just a rectangle. I was hoping I could stack calls to glScissor and create a shape with many smaller rectangles, but unfortunately glScissor only works once (the last time it was called).
It seems that cocos2d doesn't support OpenGLs stencil buffer (does it?) and even if it did, I find OpenGL so hard to understand, I'd still need someone to explain it to me. If I could set a bezier path on the sprite as a mask (which I think you can do in Quartz), that would be great, but it doesn't seem like that's supported.
Please, if anyone has any bit of wisdom here, that'd be great!
Figured it out. You can call glScissor multiple times, you just also need to draw that scissored shape each time:
-(void) visit
{
NSEnumerator *enumerator;
NSValue *val;
CGRect aRegion;
glPushMatrix();
glEnable(GL_SCISSOR_TEST);
enumerator = [regions objectEnumerator];
while ((val = (NSValue *)[enumerator nextObject])) {
aRegion = [val CGRectValue];
glScissor(aRegion.origin.x, aRegion.origin.y,
aRegion.size.width, aRegion.size.height);
[super visit];
}
glDisable(GL_SCISSOR_TEST);
glPopMatrix();
}
It isn't possible with glScissor, but you could easily achieve this effect using the stencil buffer. Here is the documentation:
http://www.opengl.org/resources/code/samples/sig99/advanced99/notes/node117.html
There is also a NeHe tutorial on the stencil buffer, but it is in C++, not Objective C (though it should be easy to translate into whatever application you need):
http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=26
EDIT: This is based on the assumption that you want to clip it to some arbitrary shape, for example a star, smiley whatever, instead of just a rectangle.