As I have it understood, a projection matrix scales a polygon depending on how far away or close it is from the camera. Though I might be completely wrong. My question is, how does the projection matrix "know" to show the sides of the following cube, as the camera moves, when the matrix is only supposed "scale" polygons?
Notice in the image, the cube is off to the right side of the screen, by moving the camera to the left. If the camera is moved in the opposite direction (to the right) in order to center the cube, the side of the cube will disappear as expected.
Here is my matrix code:
private void createProjectionMatrix(){
float aspectRatio = (float) Display.getWidth() / (float) Display.getHeight();
float y_scale = (float) ((1f / Math.tan(Math.toRadians(FOV/2f))) * aspectRatio);
float x_scale = y_scale / aspectRatio;
float frustum_length = FAR_PLANE - NEAR_PLANE;
projectionMatrix = new Matrix4f();
projectionMatrix.m00 = x_scale;
projectionMatrix.m11 = y_scale;
projectionMatrix.m22 = -((FAR_PLANE + NEAR_PLANE) / frustum_length);
projectionMatrix.m23 = -1;
projectionMatrix.m32 = -((2 * NEAR_PLANE * FAR_PLANE) / frustum_length);
projectionMatrix.m33 = 0;
}
The function of a projection matrix (in the context of graphics APIs, such as OpenGL) is to transform vertex positions from view-space into clip-space.
Clip space is generally a unit box (although in D3D it's a half-unit box). If a vertex position after being transformed into clip-space does not lie within that unit box, then it is clipped. This is essentially how the system "knows" the cube is visible on the screen.
Related
I create a 2d game similar to a classic diepio. I created a system for positioning the player's barrel in a specific direction. The updated angle is sent to the server. When the player clicks, the server creates a missile. This only works correctly when the barrel is attached to the center of the player's body. When I want to move the barrel away from the center of the body, there is a problem. I don't know how to update the server-side position where the projectile spawns.
In the image below, the barrel rotates around the center of the player's body. I marked the missile's flight path with the red line.
On this image, the barrels also have a rotation axis in the player's center, but have been shifted to the side. The green line marked the route the missile should take. Unfortunately, I don't know how to do it correctly.
How to update the projectile's spawn point by a given distance (e.g. 10) from the basic distance (if the barrel was not moved) based on the player's angle of rotation and his position?
Projectile spawn method:
float angle = (float) ((player.getRotation() + 90) * Math.PI / 180f);
float forceX = (float) Math.cos(angle);
float forceY = (float) Math.sin(angle);
spawnProjectile(player.getPosition().x + (forceX * 3f), player.getPosition().y + (forceY * 3f));
If I understood your question correctly, you want to find the two points marked in orange in the following image:
Since you know the direction in which the missiles should fly (the red line), the distance from the center position (e.g. 10) and you know that there is a 90° angle between the movement vector of the missile (red line) and the connection line between the two starting positions of the missiles (marked as black line in the image) you can calculate the resulting positions like this:
float angle = (float) ((player.getRotation() + 90) * Math.PI / 180f);
float forceX = (float) Math.cos(angle);
float forceY = (float) Math.sin(angle);
// the center point (start of the red line in the image)
float centerX = player.getPosition().x + (forceX * 3f);
float centerY = player.getPosition().y + (forceY * 3f);
float offsetFromCenterDistanceFactor = 1f; // increase this value to increase the distance between the center of the player and the starting position of the missile
// the vector towards one of the starting positions
float offsetX1 = forceY * offsetFromCenterDistanceFactor;
float offsetY1 = -forceX * offsetFromCenterDistanceFactor;
// the vector towards the other starting position
float offsetX2 = -offsetX1;
float offsetY2 = -offsetY1;
//spawn the upper missile
spawnProjectile(centerX + offsetX1, centerY + offsetY1);
//spawn the lower missile
spawnProjectile(centerX + offsetX2, centerY + offsetY2);
For more detail on the calculation of the orthogonal vectors see this answer.
I am struggling to draw this shape on an android canvas. I already used all what one could find in here. but it works on some angles and not on others.
Let's say that this shape can be drawn given a certain angle (in the example = 90°)
We also have the coordinates of the three points of the shape (A,B and C)
Here is the code I currently use :
// (cx,cy) is the point A
// (pos2LegX, pos2LegY) is the point C
// radDirection is the drawn example 90°
float radDirection = (float) (Math.toRadians(this.rad));
float pos2LegX = (float) (cx + radius * Math.sin(radDirection)) ;
float pos2LegY = (float) (cy - radius * Math.cos(radDirection)) ;
// (arcPosX, arcPosY) is the point B
float arcPosX = pos2LegX + (float) ((radius/2) * Math.abs(Math.cos(radDirection))) ;
float arcPosY = pos2LegY - (float) ((radius/2) * Math.abs(Math.sin(radDirection))) ;
// the rect to use with the drawers
final RectF oval = new RectF();
oval.set(pos2LegX , pos2LegY - radius/4, pos2LegX + radius/2, pos2LegY+ radius/4);
// draw the shape
// draw AC
canvas.drawLine(cx,cy,pos2LegX, pos2LegY ,paint);
// draw the arc CB
int startAngle = (int) (180 / Math.PI * Math.atan2(arcPosY - pos2LegY, arcPosX - pos2LegX));
canvas.drawArc(oval,startAngle,180,false,paint);
// draw BA
canvas.drawLine(arcPosX,arcPosY,cx,cy,paint);
This may work for example if radDirection = 180 but if radDirection = 000 it gives this :
But here, the shape should be in opposite direction with arc concave to the center cx, cy.
Any solution would be a big help for me.
Thanks in advance :)
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 got the origin and a direction vector but I have no clue how to follow the ray and check for collisions...
Here is a picture of the ray, it goes out about 6 blocks.
Vector3f cam = camera.getPosition();
Vector3f dir = getDirection();
dir.x *= 40;
dir.y *= 40;
dir.z *= 40;
Vector3f dest = new Vector3f(cam.x + dir.x, cam.y + dir.y, cam.z + dir.z);
public Vector3f getDirection()
{
Vector3f vector = new Vector3f();
float rotX = camera.yaw;
float rotY = camera.pitch;
vector.y = (float) -Math.sin(Math.toRadians(rotY));
float h = (float) Math.cos(Math.toRadians(rotY));
vector.x = (float) (h * Math.sin(Math.toRadians(rotX)));
vector.z = (float) (-h * Math.cos(Math.toRadians(rotX)));
return vector;
}
I have tried using gluUnProject and it worked a little bit but like when you picked a face of a block it wasn't very precise.
BTW: I am using display lists for the chunks and I am just rendering block quads inside that display list. I get 60 FPS. I have been searching and searching but I cannot find ANYTHING on ray tracing and or ray picking... Thanks!
Your question is very unprecise.
Since your scene seems to consist of a grid, I suggest you to look into "3D Digital Differential Analyzer":
http://www.cse.chalmers.se/edu/course/_MY_MISSING_COURSE_2012/_courses_2011/TDA361_Computer_Graphics/grid.pdf
and:
http://en.wikipedia.org/wiki/Digital_differential_analyzer_%28graphics_algorithm%29
I have the following Java class I've written for a LibGdx OpenGL project.
The camera keeps the aspect ratio of the screen no matter how you resize it by letterboxing either the top and bottom, or the sides. So far, so good.
The issue comes when I try to obtain the mouse x, y coordinates of a click, and the letterbox is involved for that axis.
First here is the class:
public class Camera {
private static float viewportWidth;
private static float viewportHeight;
private static float aspectRatio;
private static float barSize;
/**
* Creates an orthographic camera where the "play area" has the given viewport size. The viewport will be scaled to maintain the aspect ratio.
*
* #param virtualWidth the width of the game screen in virtual pixels.
* #param virtualHeight the height of the game screen in virtual pixels.
* #return the new camera.
*
*/
public static OrthographicCamera createCamera(float virtualWidth, float virtualHeight) {
aspectRatio = virtualWidth / virtualHeight;
float physicalWidth = Gdx.graphics.getWidth();
float physicalHeight = Gdx.graphics.getHeight();
if (physicalWidth / physicalHeight >= aspectRatio) {
// Letterbox left and right.
viewportHeight = virtualHeight;
viewportWidth = viewportHeight * physicalWidth / physicalHeight;
barSize = ????;
}
else {
// Letterbox above and below.
viewportWidth = virtualWidth;
viewportHeight = viewportWidth * physicalHeight / physicalWidth;
barSize = ????;
}
OrthographicCamera cam = new OrthographicCamera(viewportWidth , viewportHeight);
cam.position.set(virtualWidth / 2, virtualHeight / 2, 0);
cam.rotate(180, 1, 0, 0);
cam.update();
Gdx.app.log("BTLog", "barSize:"+barSize);
return cam;
}
public static float getViewportWidth() {
return viewportWidth;
}
public static float getViewportHeight() {
return viewportHeight;
}
}
LibGdx supplies me the x and y coordinates when an even happens, and I need to translate these raw coordinates into the scale of my camera (the virtual height and width).
When the screen is stretched (no letterboxing at all), it's pretty easy to obtain the x and y coordinates by using:
xRelative = (int) (x / (float)Gdx.graphics.getWidth() * Camera.getViewportWidth());
yRelative = (int) (y / (float)Gdx.graphics.getHeight() * Camera.getViewportHeight());
The problem is when the letterboxes come into play, it throws off the coordinate for that axis. I know I need to take into account the width of the letterboxing, but i'm having a hell of a time figuring how to calculate it.
Above where I have "barSize = ????;" my first instinct was to do this:
barSize = physicalHeight - viewportHeight; // to use height for example
Once I get the barSize, i'm fairly sure I can use this to get the right numbers (using the y axis for example):
yRelative = (int) (y / (float)Gdx.graphics.getHeight() * Camera.getViewportHeight() - Camera.getBarSize());
But the numbers don't match up. Any suggestions would be a really appreciated!
Ray ray = camera.getPickRay(x, y);
System.out.println(ray.origin.x);
System.out.println(ray.origin.y);