OpenGL prevent stretching on window resize - vb.net

I have a ortho set up at the moment for 2D, when I resize the window it stretches anything that is drawn in the window, is there a way to either just have black bars show when the window is resized or at least maintain the aspect ratio of the contents, so they dont stretch at all. I have tried a few implementations that I have seen on here, but nothing really works.
EDIT: Sorry guys had a bit of a blonde moment
Protected Overrides Sub OnResize(ByVal e As EventArgs)
MyBase.OnResize(e)
GL.Viewport(0, 0, Width, Height)
GL.MatrixMode(MatrixMode.Projection)
GL.LoadIdentity()
GL.Ortho(-1.0, testvalue, testvalue , 1.0, 0.0, 4.0)
End Sub
testvalue at the moment is 5000, window size is 800x800

I figured out what I had to do. Rather than utilizing the aspect ratio on the Ortho part, I used it on the view port as such:
Dim ar As Single = Width/Height
GL.Viewport( 0, 0, 800 * ar, 800 * ar)
This prevents all stretching and simply places a black bar on the right hand side when the width is greater than height.

When you resize, Windows creates its own message pump to handle events, bypassing your message pump. There are work-arounds (hacks) to get it to render whilst sizing, including running your update/rendering on a thread. Note that this is a problem for D3D as well as OpenGL.
There's a discussion here on an old Gamedev thread.

the ortho command comprises of: the following parameters: left, right, bottom, top, zNear, zFar further info: http://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml
You will want to plug in your 'Width' into right and your 'Height' into bottom and ensure the Width and Height values reflect the new window size.
E.g.:
GL.Viewport(0, 0, Width, Height)
GL.MatrixMode(MatrixMode.Projection)
GL.LoadIdentity()
GL.Ortho(0.0, Width, Height, 0.0, 1.0, 2.0)
Since you're only after 2D your zNear and zFar can be quite small, just make sure to render between zNear and zFar and do not use 0.0 as your zNear, I would recommend using 0.1 or larger for your zNear.

Related

How does the viewport work in libgdx and how to set it up correctly?

I am learning the use of libgdx and I got confused by the viewport and how objects are arranged on the screen. Let's assume my 2D world is 2x2 units wide and high. Now I create a camera which viewport is 1x1. So I should see 25% of my world. Usually displays are not square shaped. So I would expect libgdx to squish and stretch this square to fit the display.
For a side scroller you would set the viewport height the same as the world height and adjust the viewport width according to the aspect ratio. Independent of the aspect ratio of your display you always see the full height of the world but different expansions on the x-axis. Somebody with a wider than high display could look further on the x-axis than somebody with a square shaped display. But proportions will be maintained and there is no distortion. So far I thought I mastered how the viewport logic works.
I am working with the book "Learning LibGDX Game Development" in which you develop the game "canyon bunny". The source code can be found here:
Canyon Bunny - GitHub
In the WorldRenderer Class you find the initilization of the camera:
private void init() {
batch = new SpriteBatch();
camera = new OrthographicCamera(Constants.VIEWPORT_WIDTH, Constants.VIEWPORT_HEIGHT);
camera.position.set(0, 0, 0);
camera.update();
}
The viewport constants are saved in a separate Constants-Class:
public class Constants {
// Visible game world is 5 meters wide
public static final float VIEWPORT_WIDTH = 5.0f;
// Visible game world is 5 meters tall
public static final float VIEWPORT_HEIGHT = 5.0f;
}
As you can see the viewport is 5x5. But the game objects have the right proportion on my phone (16:9) and even on a desktop when you change the windows size the game maintains the correct proportions. I don't understand why. I would expect that the game tries to paint a square shaped cutout of the world onto a rectangle shaped display which would lead to distortion. Why is that not the case? And why don't you need the adaption of width or height of the viewport to the aspect ratio?
The line:
cameraGUI.setToOrtho(true);
Overrides the values you gave when you called:
cameraGUI = new OrthographicCamera(Constants.VIEWPORT_GUI_WIDTH, Constants.VIEWPORT_GUI_HEIGHT);
Here's the LibGDX code that shows why/how the viewport sizes you set were ignored:
/** Sets this camera to an orthographic projection using a viewport fitting the screen resolution, centered at
* (Gdx.graphics.getWidth()/2, Gdx.graphics.getHeight()/2), with the y-axis pointing up or down.
* #param yDown whether y should be pointing down */
public void setToOrtho (boolean yDown) {
setToOrtho(yDown, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
/** Sets this camera to an orthographic projection, centered at (viewportWidth/2, viewportHeight/2), with the y-axis pointing up
* or down.
* #param yDown whether y should be pointing down.
* #param viewportWidth
* #param viewportHeight */
public void setToOrtho (boolean yDown, float viewportWidth, float viewportHeight) {
if (yDown) {
up.set(0, -1, 0);
direction.set(0, 0, 1);
} else {
up.set(0, 1, 0);
direction.set(0, 0, -1);
}
position.set(zoom * viewportWidth / 2.0f, zoom * viewportHeight / 2.0f, 0);
this.viewportWidth = viewportWidth;
this.viewportHeight = viewportHeight;
update();
}
So you would need to do this instead:
cameraGUI.setToOrtho(true, Constants.VIEWPORT_GUI_WIDTH, Constants.VIEWPORT_GUI_HEIGHT);
Also don't forget to call update() right after wherever you change the position or viewport dimensions of your camera (Or other properties)
I found the reason. If you take a look on the worldRenderer class there is a method resize(). In this method the viewport is adapted to the aspect ratio. I am just wondering because until now I thought the resize method is only called when resizing the window. Apparently it's also called at start up. Can anybody clarify?

Qt5 QtChart drop vertical lines while using QScatterSeries

When I am using QScatterSeries, I can very easily draw point at (x, y). However, instead of points I would like to draw short lines, like in the figure below. How can I get about doing so?
I tried using RectangleMarker, but it just draws a fat square. I would prefer a thin line about 2px wide and 20px in height.
Is there a way I can add custom marker shapes?
Here are the code and the settings I use to transform my points into lines :
//create scatter series to draw point
m_pSeries1 = new QtCharts::QScatterSeries();
m_pSeries1->setName("trig");
m_pSeries1->setMarkerSize(100.0);
//draw a thin rectangle (50 to 50)
QPainterPath linePath;
linePath.moveTo(50, 0);
linePath.lineTo(50, 100);
linePath.closeSubpath();
//adapt the size of the image with the size of your rectangle
QImage line1(100, 100, QImage::Format_ARGB32);
line1.fill(Qt::transparent);
QPainter painter1(&line1);
painter1.setRenderHint(QPainter::Antialiasing);
painter1.setPen(QColor(0, 0, 0));
painter1.setBrush(painter1.pen().color());
painter1.drawPath(linePath);
//attach your image of rectangle to your series
m_pSeries1->setBrush(line1);
m_pSeries1->setPen(QColor(Qt::transparent));
//then use the classic QtChart pipeline...
You can play the marker size, the dimension of the image and the drawing pattern in the painter to adapt the size and shape of the rectangle to obtain a line.
In the picture, it's the black line. As you can see you can repeat the process for other series.
Keep in mind that you cannot use the openGL acceleration:
m_pSeries0->setUseOpenGL(true);
My work is based on the QtCharts/QScatterSeries example : QScatterSeries example
Hope it will help you.
Florian

How to use mask with transparency on QWidget?

I am trying to use mask on my QWidget. I want to overlay existing widget with row of buttons - similar to Skype
Notice that these buttons don't have jagged edges - they are nicely antialiased and widget below them is still visible.
I tried to accomplish that using Qt Stylesheets but on pixels that should be "masked out" was just black colour - it was round button on black, rectangular background.
Then I tried to do this using QWidget::mask(). I used following code
QImage alpha_mask(QSize(50, 50), QImage::Format_ARGB32);
alpha_mask.fill(Qt::transparent);
QPainter painter(&alpha_mask);
painter.setBrush(Qt::black);
painter.setRenderHint(QPainter::Antialiasing);
painter.drawEllipse(QPoint(25,25), 24, 24);
QPixmap mask = QPixmap::fromImage(alpha_mask);
widget.setMask(mask.mask());
Sadly, it results in following effect
"Edges" are jagged, where they should be smooth. I saved generated mask so I could investigate if it was the problem
it wasn't.
I know that Linux version of Skype does use Qt so it should be possible to reproduce. But how?
One possible approach I see is the following.
Prepare a nice high resolution pixmap with the circular button icon over transparent background.
Paint the pixmap on a square widget.
Then mask the widget leaving just a little bit of margin beyond the border of the circular icon so that the widget mask jaggedness won't touch the smooth border of the icon.
I managed to get a nice circular button with not so much code.
Here is the constructor of my custom button:
Button::Button(Type t, QWidget *parent) : QPushButton(parent) {
setIcon(getIcon(t));
resize(30,30);
setMouseTracking(true);
// here I apply a centered mask and 2 pixels bigger than the button
setMask(QRegion(QRect(-1,-1,32,32),QRegion::Ellipse));
}
and in the style sheet I have the following:
Button {
border-radius: 15px;
background-color: rgb(136, 0, 170);
}
With border-radius I get the visual circle and the mask doesn't corrupt the edges because it is 1 pixel away.
You are using the wrong approach for generating masks. I would generate them from the button images themselves:
QImage image(widget.size(), QImage::Format_Alpha8);
widget.render(&image);
widget.setMask(QBitmap::fromImage(image.createMaskFromColor(qRgba(0, 0, 0, 0))));

Keeping an object made in OpenGL within the boundaries a window

I have been working on a game using objective c and OpenGL. I know how to create the object and how to make it move the way I want, but I cannot keep it within the window. How do you keep the object within the window?
OpenGL FAQ, section 8.070: How can I automatically calculate a view that displays my entire model?:
The following is from a posting by Dave Shreiner on setting up a basic
viewing system:
First, compute a bounding sphere for all objects in your scene. This
should provide you with two bits of information: the center of the
sphere (let ( c.x, c.y, c.z ) be that point) and its diameter (call it
"diam").
Next, choose a value for the zNear clipping plane. General guidelines
are to choose something larger than, but close to 1.0. So, let's say
you set:
zNear = 1.0;
zFar = zNear + diam;
Structure your matrix calls in this order (for an Orthographic projection):
GLdouble left = c.x - diam;
GLdouble right = c.x + diam;
GLdouble bottom c.y - diam;
GLdouble top = c.y + diam;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(left, right, bottom, top, zNear, zFar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
This approach should center your objects in the middle of the window and stretch them to fit (i.e., its assuming that you're using a
window with aspect ratio = 1.0). If your window isn't square, compute
left, right, bottom, and top, as above, and put in the following logic
before the call to glOrtho():
GLdouble aspect = (GLdouble) windowWidth / windowHeight;
if ( aspect < 1.0 ) { // window taller than wide
bottom /= aspect;
top /= aspect;
} else {
left *= aspect;
right *= aspect;
}
The above code should position the objects in your scene appropriately. If you intend to manipulate (i.e. rotate, etc.), you
need to add a viewing transform to it.
A typical viewing transform will go on the ModelView matrix and might
look like this:
gluLookAt(0., 0., 2.*diam,
c.x, c.y, c.z,
0.0, 1.0, 0.0);
a "look at" camera is a very convenient technique to make a viewer track an object (hence however the object or camera point moves, the object will stay onscreen). see 'gluLookAt' for a commonly provided implementation, most 3d helper code libraries will have one. You give it a desired camera point, object of interest, and 'up-vector', and it will create an appropriate world (camera transformation) matrix.
Otherwise if you're in control of the object, just don't move it outside of the initial frustum.

VB.NET custom user control graphics rotation

this is my first question on here.
I'm trying to build a dial control as a custom user control in VB.NET. I'm using VS2008.
so far I have managed to rotate image using graphics.rotatetransform . however, this rotate everything. Now I have a Bitmap for the dial which should stay stable and another Bitmap for the needle which I need to rotate.
so far i've tried this:
Dim gL As Graphics = Graphics.FromImage(bmpLongNeedle)
gL.TranslateTransform(bmpLongNeedle.Width / 2, bmpLongNeedle.Height * 0.74)
gL.RotateTransform(angleLongNeedle)
gL.TranslateTransform(-bmpLongNeedle.Width / 2, -bmpLongNeedle.Height * 0.74)
gL.DrawImage(bmpLongNeedle, 0, 0)
As I understand it, the image of the needle should be rotated at angle "angleLongNeedle" although i'm placing the rotated image at 0,0. However, the result is that the Needle doesn't get drawn on the control.
any pointers as to where I might be going wrong or something else I should be doing?
Thanks in advance
First of all, why do you allocate the Graphics object from a bitmap that you then proceed to draw onto the graphics? That doesn’t make sense.
Dim gL As Graphics = Graphics.FromImage(bmpLongNeedle)
' … '
gL.DrawImage(bmpLongNeedle, 0, 0)
What you probably want is a graphics context for the whole image. You then apply the transformations to it and finally draw the bmpLongNeedle image.
Secondly, your translations look inversed: in the first step, you need to move the image to the origin (0, 0); then you rotate it, and then move it back. So the transformation should look like this:
gL.TranslateTransform(-bmpLongNeedle.Width * 0.5, -bmpLongNeedle.Height * 0.5)
gL.RotateTransform(angleLongNeedle)
gL.TranslateTransform(bmpLongNeedle.Width * 0.5, bmpLongNeedle.Height * 0.5)
Notice the inversed order of the TranslateTransforms. Also, why did you translate by 0.74 times the height, instead of half?
oh the bitmap for needle has the pivot point at 0.74 * height.
may be I should have posted this before. but this is what i've done.
Public Class Altimeter
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim bmpBezel As New Bitmap("{path}\Altimeter_Background.bmp")
Dim bmpLongNeedle As New Bitmap("{path}\LongNeedle.bmp")
Dim rect2 As New Rectangle(e.ClipRectangle.X, e.ClipRectangle.Y, e.ClipRectangle.Width, e.ClipRectangle.Height)
'make transparent
bmpBezel.MakeTransparent(Color.Yellow)
bmpLongNeedle.MakeTransparent(Color.Yellow)
Dim angleLongNeedle As Single = (Altitude / 50) * 360
'draw bezel
e.Graphics.DrawImage(bmpBezel, rect2)
'rotate long needle
Dim gL As Graphics = Graphics.FromImage(bmpLongNeedle)
gL.TranslateTransform(bmpLongNeedle.Width / 2, bmpLongNeedle.Height * 0.74)
gL.RotateTransform(angleLongNeedle)
gL.TranslateTransform(-bmpLongNeedle.Width / 2, -bmpLongNeedle.Height * 0.74)
gL.DrawImage(bmpLongNeedle, 0, 0)
MyBase.OnPaint(e)
End Sub
i use e.graphics.drawimage to paint the whole image. i don't really understand what you said about having graphics object for all the images and then drawing the needle? do you have any pseudo code?
thanks