I'm creating a game in LibGDX and am using Tiled as my map system.
I'm trying to contain an OrthographicCamera within the bounds of my TiledMap. I use MathUtils.clamp to achieve this. When the camera is at a normal zoom of 1.0f, it works perfectly. However when the camera is zoomed in further, to lets say .75f, the camera is clamped to the wrong location because it has no information of the zoom value.
position.x = MathUtils.clamp(position.x * (gameScreen.gameCamera.camera.zoom), gameScreen.gameCamera.camera.viewportWidth / 2, gameScreen.mapHandler.mapPixelWidth - (gameScreen.gameCamera.camera.viewportWidth / 2));
position.y = MathUtils.clamp(position.y * (gameScreen.gameCamera.camera.zoom), (gameScreen.gameCamera.camera.viewportHeight / 2), gameScreen.mapHandler.mapPixelHeight - (gameScreen.gameCamera.camera.viewportHeight / 2));
My question: How do I include the zoom value in my clamp code so the camera is correctly clamped? Any ideas?
Thank you!
- Jake
You should multiply by zoom the world size, not the camera position:
float worldWidth = gameScreen.mapHandler.mapPixelWidth;
float worldHeight = gameScreen.mapHandler.mapPixelHeight;
float zoom = gameScreen.gameCamera.camera.zoom;
float zoomedHalfWorldWidth = zoom * gameScreen.gameCamera.camera.viewportWidth / 2;
float zoomedHalfWorldHeight = zoom * gameScreen.gameCamera.camera.viewportHeight / 2;
//min and max values for camera's x coordinate
float minX = zoomedHalfWorldWidth;
float maxX = worldWidth - zoomedHalfWorldWidth;
//min and max values for camera's y coordinate
float minY = zoomedHalfWorldHeight;
float maxY = worldHeight - zoomedHalfWorldHeight;
position.x = MathUtils.clamp(position.x, minX, maxX);
position.y = MathUtils.clamp(position.y, minY, maxY);
Note, that if a visible area can be smaller than the world size, then you must handle such situations differently:
if (maxX <= minX) {
//visible area width is bigger than the worldWidth -> set the camera at the world centerX
position.x = worldWidth / 2;
} else {
position.x = MathUtils.clamp(position.x, minX, maxX);
}
if (maxY <= minY) {
//visible area height is bigger than the worldHeight -> set the camera at the world centerY
position.y = worldHeight / 2;
} else {
position.y = MathUtils.clamp(position.y, minY, maxY);
}
Related
gif
Creating View, Porjection Matrix
void CameraSystem::CreateMatrix()
{
camera->aspect = viewport->Width / viewport->Height;
projection_matrix = XMMatrixPerspectiveFovLH(camera->fov, camera->aspect, camera->near_z, camera->far_z);
XMVECTOR s, o, q, t;
XMFLOAT3 position(camera->position.m128_f32[0], camera->position.m128_f32[1], camera->position.m128_f32[2]);
s = XMVectorReplicate(1.0f);
o = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
q = XMQuaternionRotationRollPitchYaw(camera->pitch, camera->yaw, camera->roll);
t = XMLoadFloat3(&position);
world_matrix = XMMatrixAffineTransformation(s, o, q, t);
view_matrix = XMMatrixInverse(0, world_matrix);
camera->look = XMVector3Normalize(XMMatrixTranspose(view_matrix).r[2]);
camera->right = XMVector3Normalize(XMMatrixTranspose(view_matrix).r[0]);
camera->up = XMVector3Normalize(XMMatrixTranspose(view_matrix).r[1]);
camera->position = world_matrix.r[3];
cb_viewproj.data.view_matrix = XMMatrixTranspose(view_matrix);
cb_viewproj.data.projection_matrix = XMMatrixTranspose(projection_matrix);
}
this code creating projection matrix with aspect, fov, near, far.
and create world, view with camera transform. and these work perfect for rendering but maybe not for create ray.
Creating Mouse Ray
MouseRay CameraSystem::CreateMouseRay()
{
MouseRay mouse_ray;
POINT cursor_pos;
GetCursorPos(&cursor_pos);
ScreenToClient(ENGINE->GetWindowHandle(), &cursor_pos);
// Convert the mouse position to a direction in world space
float mouse_x = static_cast<float>(cursor_pos.x);
float mouse_y = static_cast<float>(cursor_pos.y);
float ndc_x = 2.0f * mouse_x / (float)ENGINE->GetWindowSize().x - 1.0f;
float ndc_y = (2.0f * mouse_y / (float)ENGINE->GetWindowSize().y - 1.0f) * -1.0f;
float ndc_z = 1.0f;
ndc.x = ndc_x;
ndc.y = ndc_y;
XMMATRIX inv_view = XMMatrixInverse(nullptr, view_matrix);
XMMATRIX inv_world = XMMatrixInverse(nullptr, XMMatrixIdentity());
XMVECTOR ray_dir;
XMVECTOR ray_origin;
ndc_x /= projection_matrix.r[0].m128_f32[0];
ndc_y /= projection_matrix.r[1].m128_f32[1];
ray_dir.m128_f32[0] = (ndc_x * inv_view.r[0].m128_f32[0]) + (ndc_y * inv_view.r[1].m128_f32[0]) + (ndc_z * inv_view.r[2].m128_f32[0]) + inv_view.r[0].m128_f32[3];
ray_dir.m128_f32[1] = (ndc_x * inv_view.r[0].m128_f32[1]) + (ndc_y * inv_view.r[1].m128_f32[1]) + (ndc_z * inv_view.r[2].m128_f32[1]) + inv_view.r[1].m128_f32[3];
ray_dir.m128_f32[2] = (ndc_x * inv_view.r[0].m128_f32[2]) + (ndc_y * inv_view.r[1].m128_f32[2]) + (ndc_z * inv_view.r[2].m128_f32[2]) + inv_view.r[2].m128_f32[3];
ray_origin = XMVector3TransformCoord(camera->position, inv_world);
ray_dir = XMVector3TransformNormal(ray_dir, inv_world);
ray_dir = XMVector3Normalize(ray_dir);
XMtoRP(ray_origin, mouse_ray.start_point);
XMtoRP(ray_dir * 1000.f, mouse_ray.end_point);
return mouse_ray;
}
get cursor pos, and converto ndc, it's perpect, i guess 'ndc, porjection calulate' has some propelm, but i couldn't find any other code.
inversed view matrix to ray direction, and i guess this code also has problem.
As you can see, the mouse does not seem to generate a ray in the exact direction of the pointed mouse. The frustum feels narrower than it really is, and the further away from the map, the worse it gets.
It seems as if the camera position is fixed, but the camera position is being updated from the world matrix as shown in the properties window.
The fact that views and projection matrices are also honestly generated can be inferred from the fact that the rest except for raycasting are rendered correctly.
I assume that the ray direction vector is miscalculated.
I don't know what more calculations can be made in the next code.
raycast test by reactphysics3d library.
Currently, I am working with a ray tracer that takes an iterative approach towards developing the scenes. My goal is to turn it into a recursive ray tracer.
At the moment, I have a ray tracer defined to do the following operation to create the bitmap it is stored in:
int WIDTH = 640;
int HEIGHT = 640;
BMP Image(WIDTH, HEIGHT); // create new bitmap
// Slightly shoot rays left of right camera direction
double xAMT, yAMT;
*/
Color blue(0.1, 0.61, 0.76, 0);
for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
if (WIDTH > HEIGHT) {
xAMT = ((x + 0.5) / WIDTH) * aspectRatio - (((WIDTH - HEIGHT) / (double)HEIGHT) / 2);
yAMT = ((HEIGHT - y) + 0.5) / HEIGHT;
}
else if (HEIGHT > WIDTH) {
xAMT = (x + 0.5) / WIDTH;
yAMT = (((HEIGHT - y) + 0.5) / HEIGHT) / aspectRatio - (((HEIGHT - WIDTH) / (double)WIDTH) / 2);
}
else {
xAMT = (x + 0.5) / WIDTH;
yAMT = ((HEIGHT - y) + 0.5) / HEIGHT;
}
..... // calculate intersections, shading, reflectiveness.... etc
Image.setPixel(x, y, blue); // this is here just as an example
}
}
Is there another approach to calculating the reflective and refractive child rays outside the double for-loop?
Are the for-loops necessary? // yes because of the bitmap?
What approaches can be taken to minimize/optimize an iterative ray tracer?
I have a problem with collision detection of a circle and a rectangle. I have tried to solve the problem with the Pythagorean Theorem. But none of the queries works. The rectangle collides with the rectangular bounding box of the circle.
if (CGRectIntersectsRect(player.frame, visibleEnemy.frame)) {
if (([visibleEnemy spriteTyp] == jumper || [visibleEnemy spriteTyp] == wobble )) {
if ((visibleEnemy.center.x - player.frame.origin.x) * (visibleEnemy.center.x - player.frame.origin.x) +
(visibleEnemy.center.y - player.frame.origin.y) * (visibleEnemy.center.y - player.frame.origin.y) <=
(visibleEnemy.bounds.size.width/2 * visibleEnemy.bounds.size.width/2)) {
NSLog(#"Check 1");
normalAction = NO;
}
if ((visibleEnemy.center.x - (player.frame.origin.x + player.bounds.size.width)) *
(visibleEnemy.center.x - (player.frame.origin.x + player.bounds.size.width)) +
(visibleEnemy.center.y - player.frame.origin.y) * (visibleEnemy.center.y - player.frame.origin.y) <=
(visibleEnemy.bounds.size.width/2 * visibleEnemy.bounds.size.width/2)) {
NSLog(#"Check 2");
normalAction = NO;
}
else {
NSLog(#"Check 3");
normalAction = NO;
}
}
}
Here is how I did it in one of my small gaming projects. It gave me best results and it's simple. My code detects if there is a collision between circle and the line. So you can easily adopt it to circle - rectangle collision detection by checking all 4 edges of the rectangle.
Let's say that a ball has a ballRadius, and location (xBall, yBall). The line is defined with two points (xStart, yStart) and (xEnd, yEnd).
Implementation of a simple collision detection:
float ballRadius = ...;
float x1 = xStart - xBall;
float y1 = yStart - yBall;
float x2 = xEnd - xBall;
float y2 = yEnd - yBall;
float dx = x2 - x1;
float dy = y2 - y1;
float dr = sqrtf(powf(dx, 2) + powf(dy, 2));
float D = x1*y2 - x2*y1;
float delta = powf(ballRadius*0.9,2)*powf(dr,2) - powf(D,2);
if (delta >= 0)
{
// Collision detected
}
If delta is greater than zero there are two intersections between ball (circle) and line. If delta is equal to zero there is one intersection – perfect collision.
I hope it will help you.
I have a plane/board, with a grid, that is about 1100 x 1100. I have panning, zooming, and rotating working except for the fact that the board moves back to the center of the screen after it has been panned. So, if I don't pan the board at all then everything works. After I pan the board it moves back to the center of the screen when I try to rotate it. I cannot figure out how to change the origin of the camera so that it rotates around the center of the board. It seems like it rotates around the center of the camera.
var radius = 1500, theta = 45 * 0.5, onMouseDownTheta = 45 * 0.5;
var fov = 45;
var mouse2D = new THREE.Vector3(0, 10000, 0.5);
cameraX = radius * Math.sin(THREE.Math.degToRad(theta));
cameraY = 1000;
cameraZ = radius * Math.cos(THREE.Math.degToRad(theta));
camera = new THREE.PerspectiveCamera(fov, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(cameraX, cameraY, cameraZ);
scene = new THREE.Scene();
camera.lookAt(scene.position);
render();
ThreeBoard.prototype.render = function() {
mouse2D.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse2D.y = - (event.clientY / window.innerHeight) * 2 + 1;
// rotate
if (isMouseDown && isShiftPressed && !isCtrlPressed) {
theta = ((event.pageX - mouse2D.x) * 0.5) + onMouseDownTheta;
cameraX = radius * Math.sin(THREE.Math.degToRad(theta));
cameraZ = radius * Math.cos(THREE.Math.degToRad(theta));
camera.position.set(cameraX, cameraY, cameraZ);
camera.lookAt(scene.position);
}
// pan
if (isMouseDown && isShiftPressed && isCtrlPressed) {
theta = ((event.pageX - mouse2D.x) * 0.5) + onMouseDownTheta;
cameraX += 10 * mouse2D.x;
// cameraY += 10;
cameraZ -= 10 * mouse2D.y;
camera.position.set(cameraX, cameraY, cameraZ);
// camera.lookAt(scene.position);
}
renderer.render(scene, camera);
};
I have some problems figuring out where my error is. I got the following:
Have an image and corresponding GPS coordinates of its top-left and bottom-right vertices.
E.g:
topLeft.longitude = 8.235128;
topLeft.latitude = 49.632383;
bottomRight.longitude = 8.240547;
bottomRight.latitude = 49.629808;
Now a have an Point that lies in that map:
p.longitude = 8.238567;
p.latitude = 49.630664;
I draw my image in landscape fullscreen (1024*748).
Now I want to calculate the exact Pixel position (x,y) of my point.
For doing that I am trying to use the great circle distance approach from here: Link.
CGFloat DegreesToRadians(CGFloat degrees)
{
return degrees * M_PI / 180;
};
- (float) calculateDistanceP1:(CLLocationCoordinate2D)p1 andP2:(CLLocationCoordinate2D)p2 {
double circumference = 40000.0; // Erdumfang in km am Äquator
double distance = 0.0;
double latitude1Rad = DegreesToRadians(p1.latitude);
double longitude1Rad = DegreesToRadians(p1.longitude);
double latititude2Rad = DegreesToRadians(p2.latitude);
double longitude2Rad = DegreesToRadians(p2.longitude);
double logitudeDiff = fabs(longitude1Rad - longitude2Rad);
if (logitudeDiff > M_PI)
{
logitudeDiff = 2.0 * M_PI - logitudeDiff;
}
double angleCalculation =
acos(sin(latititude2Rad) * sin(latitude1Rad) + cos(latititude2Rad) * cos(latitude1Rad) * cos(logitudeDiff));
distance = circumference * angleCalculation / (2.0 * M_PI);
NSLog(#"%f",distance);
return distance;
}
Here is my code for getting the Pixel position:
- (CGPoint) calculatePoint:(CLLocationCoordinate2D)point {
float x_coord;
float y_coord;
CLLocationCoordinate2D x1;
CLLocationCoordinate2D x2;
x1.longitude = p.longitude;
x1.latitude = topLeft.latitude;
x2.longitude = p.longitude;
x2.latitude = bottomRight.latitude;
CLLocationCoordinate2D y1;
CLLocationCoordinate2D y2;
y1.longitude = topLeft.longitude;
y1.latitude = p.latitude;
y2.longitude = bottomRight.longitude;
y2.latitude = p.latitude;
float distanceX = [self calculateDistanceP1:x1 andP2:x2];
float distanceY = [self calculateDistanceP1:y1 andP2:y2];
float distancePX = [self calculateDistanceP1:x1 andP2:p];
float distancePY = [self calculateDistanceP1:y1 andP2:p];
x_coord = fabs(distancePX * (1024 / distanceX))-1;
y_coord = fabs(distancePY * (748 / distanceY))-1;
return CGPointMake(x_coord,y_coord);
}
x1 and x2 are the points on the longitude of p and with latitude of topLeft and bottomRight.
y1 and y2 are the points on the latitude of p and with longitude of topLeft and bottomRight.
So I got the distance between left and right on longitude of p and distance between top and bottom on latitude of p. (Needed for calculate the pixel position)
Now I calculate the distance between x1 and p (my distance between x_0 and x_p) after that I calculate the distance between y1 and p (distance between y_0 and y_p)
Last but not least the Pixel position is calculated and returned.
The Result is, that my point is on the red and NOT on the blue position:
Maybe you find any mistakes or have any suggestions for improving the accuracy.
Maybe I didn't understand your question, but shouldn't you be using the Converting Map Coordinates methods of MKMapView?
See this image
I used your co-ordinates, and simply did the following:
x_coord = 1024 * (p.longitude - topLeft.longitude)/(bottomRight.longitude - topLeft.longitude);
y_coord = 748 - (748 * (p.latitude - bottomRight.latitude)/(topLeft.latitude - bottomRight.latitude));
The red dot markes this point. For such small distances you don't really need to use great circles, and your rounding errors will be making things much more inaccurate