I've got the following code to display an image/texture in Opengl. The method is supposed to display the image in its correct aspect ratio and zoom in/out.
The image does not seem to maintain its aspect ratio on the horizontal axis. Why?
(NB: The OpenGL viewing width is from -1 to 0 and height from 1 to -1).
private void renderImage(Rectangle dst, float magnification) {
float width, height;
float horizontalOffset, verticalOffset;
// Default: Fill screen horizontally
width = 1f;
height = dst.getHeight()/(float) dst.getWidth();
// magnification
width *= magnification;
height *= magnification;
// Offsets
horizontalOffset = width/2f;
verticalOffset = height/2f;
// Do the actual OpenGL rendering
glBegin (GL_QUADS);
// Right top
glTexCoord2f(0.0f, 0.0f);
glVertex2f(-0.5f + horizontalOffset, verticalOffset);
// Right bottom
glTexCoord2f(0.0f, 1.0f);
glVertex2f(-0.5f + horizontalOffset, -verticalOffset);
// Left bottom
glTexCoord2f(1.0f,1.0f);
glVertex2f(-0.5f - horizontalOffset, -verticalOffset);
// Left top
glTexCoord2f(1.0f, 0.0f);
glVertex2f(-0.5f - horizontalOffset, verticalOffset);
glEnd();
}
I don't have any experience with OpenGL but from looking at your code it seems that there is something fishy about your default fill.
// Default: Fill screen horizontally
width = 1f;
height = dst.getHeight()/(float) dst.getWidth();
This is setting your "width" variable to a constant value of 1 whilst the "height" variable relies on the height and width of the rectangle that you are passing in, which is then being used to calculate the offsets
// Offsets
horizontalOffset = width/2f;
verticalOffset = height/2f;
in my experience this can cause the problems you have stated, first try stepping through this function in the debugger to analyse the values that the width and height variables hold, or try changing
// Default: Fill screen horizontally
width = 1f;
height = dst.getHeight()/(float) dst.getWidth();
to
// Default: Fill screen horizontally
width = 1f;
height = 1f;
and rerun this to see if it has had an effect on your output
Related
I need to calculate the camera view bounds in 2d, x y width and height
In this screen shot the grid is each 1 unit, I need to calculate the bounding box of the 3d view, in the example image above, the results should be:
float x = -3;
float y = 0;
float width = 14;
float height = 6;
Assuming your camera is always going to be looking down at your world so the horizon is not visible, and that the camera is always looking parallel to the Y axis of the map so the horizontal lines are never crooked, I think this is a matter of calculating the width of the farthest away line that is within view, because that is where perspective will be showing you the most tiles horizontally. This will get you a rectangular area of the tiled map that fully covers the frustum, although you'll be drawing some extra tiles at the near corners.
private final Ray tmpRay = new Ray();
private final Vector3 tmpVec = new Vector3();
private final Rectangle visibleTilesRegion = new Rectangle();
private void updateVisibleTilesRegion (){
// Define a ray that is a projection of the direction the camera is looking onto the
// tile plane (assuming it is a Z=0 plane).
tmpRay.origin.set(camera.position.x, camera.position.y, 0f);
tmpRay.direction.set(0f, 1f, 0f);
//Find top and bottom
Intersector.intersectRayPlane(tmpRay, camera.frustum.planes[4], tmpVec);
float yTop = tmpVec.y;
Intersector.intersectRayPlane(tmpRay, camera.frustum.planes[5], tmpVec);
float yBottom = tmpVec.y;
// Find left and right at the top of the screen by intersecting that line with the left
// and right planes
tmpRay.origin.set(camera.position.x, yTop, 0f);
tmpRay.direction.set(-1f, 0f, 0f);
Intersector.intersectRayPlane(tmpRay, camera.frustum.planes[2], tmpVec);
float xLeft = tmpVec.x;
tmpRay.direction.set(1f, 0f, 0f);
Intersector.intersectRayPlane(tmpRay, camera.frustum.planes[3], tmpVec);
float xRight = tmpVec.x;
visibleTilesRegion.set(xLeft, yBottom, xRight - xLeft, yTop - yBottom);
}
I have a rectangle that I want to draw text inside. I want the text to be centered vertically and horizontally and I need the text size to changed to fit all characters inside the rectangle. Here is my code:
#Override
public void drawFixedText(String text, Rect rect, Paint paint) {
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
int cX = rect.left;
int cY = rect.top;
float textSize = paint.getTextSize();
paint.setTextSize(textSize);
float textWidth = paint.measureText(text);
while (textWidth > rect.width()) {
textSize--;
paint.setTextSize(textSize);
}
//if cX and cY are the origin coordinates of the your rectangle
//cX-(textWidth/2) = The x-coordinate of the origin of the text being drawn
//cY+(textSize/2) = The y-coordinate of the origin of the text being drawn
canvas.drawText(text, cX-(textWidth/2), cY+(textSize/2), paint);
}
I tried to combine the answers from Calculate text size according to width of text area and Android draw text into rectangle on center and crop it if needed
But it didn't work in that the text is placed to the left of the rectangle instead of inside of it. How can I fix this?
First, you need to measure the text width after each time setting its size. Otherwise, you'll end up with an infinite loop if the text starts out wider than the Rect.
while (textWidth > rect.width()) {
textSize--;
paint.setTextSize(textSize);
textWidth = paint.measureText(text);
}
Then, to center the text horizontally, you want to subtract half of the text width from the horizontal midpoint of the Rect, not from its left edge, which is what cX will be in your snippet. That is, replace cX - (textWidth / 2) with rect.centerX() - (textWidth / 2) in the drawText() call.
Furthermore, to center the text vertically, we'll need to do something similar with the y-coordinate. However, using the text size for that will not give the correct result. We need to measure the text's actual height and offset from baseline, which we can do using the Paint#getTextBounds() method.
Altogether, these changes would give something like:
public void drawFixedText(String text, Rect rect, Paint paint) {
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
float textSize = paint.getTextSize();
float textWidth = paint.measureText(text);
while (textWidth > rect.width()) {
textSize--;
paint.setTextSize(textSize);
textWidth = paint.measureText(text);
}
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
canvas.drawText(text,
rect.centerX() - textWidth / 2,
rect.centerY() - (bounds.top + bounds.bottom) / 2,
paint);
}
Please note that this assumes a default Paint instance. That is, any properties that affect text alignment have their default values going into this method.
I try to transform the window mouse coordinates (0/0 is the upper left corner) into world space coordinates. I just tried to solve it by this description. Here is my code:
public void showMousePosition(float mx, float my){
Matrix4f projectionMatrix = camera.getProjectionMatrix();
Matrix4f viewMatrix = camera.getViewMatrix();
Matrix4f projMulView = projectionMatrix.mul(viewMatrix);
projMulView.invert();
float px = ((2*mx)/650)-1;
float py = ((2*my)/650)-1;
Vector4f vec4 = new Vector4f(px, py*(-1), 0.0f, 1.0f);
vec4.mul(projMulView);
vec4.w = 1.0f / vec4.w;
vec4.x *= vec4.w;
vec4.y *= vec4.w;
vec4.z *= vec4.w;
System.out.println(vec4.x + ", " + vec4.y);
}
But thats not 100% correct. I have an Object on 0/-11 on world space and when I move my mouse to this point, my function say 0/9,8. And when I go to the left side of my window the x value is 5,6 but it should be something like 28.
Someone know what is wrong on my code?
First of all, your code says that your windows size is always width=650, height=650.
Then you are getting the position when z=0. But this z is in screen space and therefore it changes as you change the camera position and orientation. Normally, you get this information from the depth buffer, using glReadPixel. You should do it in this case.
However, there is another way to do this also. In the code I will share, I am looking for the intersection between a ray (generated from the mouse position) and the plane (0,0,0) with normal (0,1,0), I hope this helps.
/*Given the inverse PV (projection*view) matrix, the position of the mouse on screen and the size of the screen, transforms the screen coordinates to world coordinates*/
glm::vec3 Picking::OnWorld(glm::mat4 const& m_inv, glm::vec2 const & spos,size_t width, size_t height) {
float x = spos.x;
float y = spos.y;
y = height - y;
//InputOrigin, start of the ray for intersection with plane
glm::vec4 inputO = glm::vec4(x / width*2.0f - 1.0f, y / height*2.0f - 1.0f, -1.0f, 1.0f); //transforms screen position to the unit cube range
glm::vec4 resO = m_inv*inputO; //transforms to world space
if (resO.w == 0.0f)
return glm::vec3(-1); //return an invalid value to show a problem during a calculation, normally this means that the m_inv matrix was incorrect
resO /= resO.w; //homogeneous division
glm::vec4 inputE = inputO; //inputEnd, the end of the ray
inputE.z = 1.0;
//End of ray to world space
glm::vec4 resE = m_inv*inputE;
//checks that the coordinates are correct
if (resE.w == 0.0f)
return glm::vec3(-1); //return an invalid value to show a problem during a calculation, normally this means that the m_inv matrix was incorrect
resE /= resE.w;
//ray for intersection
glm::vec3 ray = glm::vec3(resE - resO); //vector between z=-1 and z=1
glm::vec3 normalRay = glm::normalize(ray);
glm::vec3 normalPlane = glm::vec3(0, 1, 0); //detects collision with plane 0, normal 1
float denominator = glm::dot(normalRay, normalPlane);
if (denominator == 0)
return glm::vec3(-1); //return an invalid value to show a problem during a calculation, normally this means that the m_inv matrix was incorrect
float numerator = glm::dot(glm::vec3(resO), normalPlane);
//intersection between ray and plane
glm::vec3 result = glm::vec3(resO) - normalRay*(numerator / denominator);
return result;
}
The math for the intersection can be read from this link:
https://www.cs.princeton.edu/courses/archive/fall00/cs426/lectures/raycast/sld017.htm
I am making a game in LibGDX. I'm using the orthographic camera class to scale the image and it works great. My only issue is with the black border. Since opengl draws the image at 0,0 which is the bottom left corner, the black border when the screen is scaled improperly is either on the top or the right. Ex:
Border on right:
Border on top:
What I want to do is make them like this:
Border used to be on right, now it's on right and left, perfectly symmetrical:
Same for this one. It used to just be on top but now it's evenly on top and bottom:
Anyone know how to do this? I think it would look muuuch nicer and more professional. Thanks in advance.
EDIT: This is what I tried so far:
Here is what I did so far:
double ratio = 1280 / 720;
double newRatio = width / height;
System.out.println("" + newRatio + " " + ratio);
System.out.println("" + width + " " + height);
if(newRatio < ratio){
System.out.println("herro");
double x = width / ratio;
double dif = height - x;
cam.setPosition(0f, (float)(dif / 2));
}
you will have to center the image at first it would look something like:
image_x = screen_width/2 - image_width/2;
image_y = screen_height/2 - image_height/2;
then in the resize methode provided by LibGDX you will have to change the viewport, it would look like:
public void resize(int newWidth, int newHeight) {
Gdx.GL30.glViewport(0, 0, newWidth, newHeight);
}
the version of the GL depends on your config.
So im trying to get the whole area of the viewport and then use the coordinates to draw images in the camera's viewport. So if the camera moves I can fill it with one image the length and the width of the viewport:
I just want the take the gray area in the picture (which is the camera's viewport) and fill it with this image.
So no matter what the camera's x and y coordinates are it will continue drawing the bottom image giving the effect of an infinite space and also the effect of moving.
EDIT:
Code I've tried so far
TextureRegion bg = ...;
int imageWidth = bg.getRegionWidth();
int imageHeight = bg.getRegionHeight();
batch.begin();
for (int x = 0;x<(camera.viewportWidth / imageWidth) + 2;x++) {
for (int y = 0;y<(camera.viewportHeight / imageHeight) + 2;y++) {
batch.draw(bg, ((camera.position.x - camera.viewportWidth / 2) + ((x - 1) * imageWidth)), ((camera.position.y - camera.viewportHeight / 2) + ((y - 1) * imageHeight)));
}
}
batch.end();
EDIT 2:
Thinking about getting the viewport area of the camera and adding tiles to a list that are viewable in the viewport area. Then as the camera moves remove the ones out of the list that are out of view and when a new area comes into view add new tiles. Then render all the tiles in the list.