Convert a Vector3f x and y to exact pixel size - lwjgl

I am currently playing a bit around with a jave game engine. ( Developed by a Friend using LWJGL ). This Engine uses Vector3f Positions to draw a picture on the screen. I want to draw Pictures with an exact pixel position... so I created a Vector3f and, somehow, have to convert the x and y values to a pixel position now. But how?

Assuming the components of the Vector3f use OpenGL screen coordinates (origin in the center of the screen, x goes right, y goes up and the axes have values of -1 to 1). The resulting pixel coordinate uses LWJGL coordinate system, i.e. origin is in bottom left corner.
import org.lwjgl.opengl.Display;
import org.lwjgl.util.Point;
import org.lwjgl.util.vector.Vector3f;
public static Point toPixelPos(Vector3f v) {
int width = Display.getWidth();
int height = Display.getHeight();
int x = (int) (width * (v.x + 1) / 2);
int y = (int) (height * (v.y + 1) / 2);
return new Point(x, y);
}

Related

3D ball rolling on wrong axis LibGDX

Hello I'm trying to make a billiards game using libgdx. I'm using 3d models for the balls and an Orthographic Camera to view them. I am having trouble getting them to roll correctly after rolling on a different axis. Here is a clip of what they look like when they're rolling.
As you can see they appear to be rotating as if they were on their starting axis. Is there any way to rotate it so that it looks like it's actually rolling. I am also not very familiar with transformation matrices or quaternions so im not too sure where to go.
Edit: Updated for clarity
Here is the code I use to update the rotation
public boolean update() {
if (!visible) {
return false;
}
Vector2 vBall = ballBody.getLinearVelocity();
float vAngle = ballBody.getAngularVelocity();
isMoving = true;
float x = ballBody.getPosition().x * SCALE;
float y = ballBody.getPosition().y * SCALE;
Vector2 axisInPlane = new Vector2(y - center.y, x - center.x).rotateRad(Math.PI/2f);
Vector3 axis3D = new Vector3(axisInPlane.x,axisInPlane.y,0f);
ball3D.transform.rotate(axis, (float) Math.toDegrees(dist / RADIUS_PX));
ball3D.transform.setTranslation(mapX(x), mapY(y), 0);
Just to be sure, center is an arbitrary fixed point where there is no rotation and all rotations are derived from this distance/angle as the ball has no slippage.
So you can directly get the axis with
Vector2 axisInPlane = new Vector2(y - center.y, x - center.x).rotateRad(Math.PI/2f);
Vector3 axis3D = new Vector3(axisInPlane.x,axisInPlane.y,0f);
Also Math.toDegree takes radians as an argument, not a float, so dist/RADIUS_PX will be off, you have to supply as a fraction of 2PI (360 degs in radians). Also this should be the circumference of the ball not the radius. I don't know what class ball3D is but I would check that ball3D.transform.rotate does take degrees as an argument and if it does replace that line with
float rotateRadians =(float) Math.toDegrees((dist/CIRCUMFERENCE_PX)*Math.PI*2f);
ball3D.transform.rotate(axis, rotateRadians );

java.awt drawRect() wrongly interprets "width" and "height" (produces +1 pixel right and down)

I have started exercising with Swing programming and found one really weird thing which I consider as a bug in the basics.
The below example shows that "width" and "height" values are treated differently when we create a Rectangle and when we display it.
import java.awt.*;
// I create a rectangle 2 x 2 with height=2, width=2
Rectangle rec = new Rectangle(1, 1, 2, 2);
// I make sure that it contains only 4 points: (1,1), (1,2), (2,1), (2,2)
println("- " + rec.contains(1, 1) + ", " + rec.contains(1, 2) + ", " + rec.contains(1, 3));
println("- " + rec.contains(2, 1) + ", " + rec.contains(2, 2) + ", " + rec.contains(2, 3));
println("- " + rec.contains(3, 1) + ", " + rec.contains(3, 2) + ", " + rec.contains(3, 3));
Output:
true, true, false
true, true, false
false, false, false
Then:
// I make sure again that the right-bottom point of my rectangle is (2,2)
// by checking the intersection result with another Rectangle
println("intersect (2,2): " + rec.intersects(new Rectangle(2, 2, 5, 5)));
println("intersect (3,3): " + rec.intersects(new Rectangle(3, 3, 5, 5)));
Output:
intersect (2,2): true
intersect (3,3): false
So from math point of view this is really rectangle 2 x 2 which has 4 integer points (that is 4 pixels): (1,1), (1,2), (2,1), (2,2). But what if we try to render it?
// Let's check what is get drawn
g.drawRect(1, 1, 2, 2); // displays 3 x 3 (as if width=3, height=3) !!
// Try it different way
((Graphics2D)g).draw(rec); // displays 3 x 3 again (as if width=3, height=3) !!
The yellow small rectangle is shown as 3 x 3:
Conclusion: Rectangle() class constructor considers width/height as a real width/height of the shape in pixels, while drawing method drawRect() consider width/height as a shift relative to the left-top pixel.
So java.awt represents the same rectangle as N x M for calculations, but as (N+1) x (M+1) when it is rendered with drawRect().
I find this totally wrong, because the default human logic is to suppose that we draw exactly what we have!
I wonder if someone else knows about this issue and can give some references to bug trackers or/and some logical explanations of such behavior. Just Google search gives me nothing about.
I wonder also how another programmers solve this problem. Use wrappers to drawRect pushing (x, y, width - 1, height -1)? I just can't believe that it is OK for everyone that it is working so weird like this.
UPDATE 1: Posting the "minimal" code snippet which reproduces the problem:
import javax.swing.*;
import java.awt.*;
public class Main {
public static void main(String args[]) {
JFrame jFrame = new JFrame("Bug of drawRect() demo");
JPanel jPanel = new JPanel() {
#Override
public void paintComponent(Graphics g) {
g.drawRect(1,1,2,2);
}
};
jFrame.add(jPanel);
jFrame.setSize(400, 100);
jFrame.setLocationRelativeTo(null);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
}
UPDATE 2: I am using OpenJDK 8 from https://github.com/ojdkbuild/ojdkbuild/releases. If I use "Go To -> Implementation(s)" in my IDE I see the drawRect() implementation here: C:\Program Files\ojdkbuild\java-1.8.0-openjdk-1.8.0.181-1\src.zip!\java\awt\Graphics.java.
public void drawRect(int x, int y, int width, int height) {
if ((width < 0) || (height < 0)) {
return;
}
if (height == 0 || width == 0) {
drawLine(x, y, x + width, y + height);
} else {
drawLine(x, y, x + width - 1, y);
drawLine(x + width, y, x + width, y + height - 1);
drawLine(x + width, y + height, x + 1, y + height);
drawLine(x, y + height, x, y + 1);
}
}
In this implementation I see that it draws the second and the third line at "x + width" instead of "x + width - 1" which causes the bug.
As I ran into the same problem, with the code below (it is more complex than that), it took me some time to understand what I was doing wrong (I put emphasis here, like you, I think the API is wrong: you draw rectangle of 100x100, it should result in 100x100, not 101x101.):
public class MyComponent extends JComponent {
#Override
public void paintComponent(final Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
g.fillRect(0, 0, 100, getHeight());
g.setColor(Color.WHITE);
g.drawRect(0, 0, 100, 100);
}
public static void main(final String[] args) {
final JFrame frame = new JFrame("Test");
frame.setSize(150, +156);
frame.getContentPane().add(new MyComponent());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
This result in the sample window, where we can see the drawn rectangle having one additional pixels on width/height.
This is problematic in my case, because I am calling repaint with the region in which I'm drawing the rectangle. So:
I am calling repaint(0, 0, 100, 100)
Swing repaint the 100x100 square
The white line is still there because it was not repaint.
The problem is that the Graphic class is not homogeneous in how it use the width and height, as one can see in the below Javadoc extract:
From drawRect javadoc:
Draws the outline of the specified rectangle. The left and right edges
of the rectangle are at x and x + width. The top and bottom edges are
at y and y + height. The rectangle is drawn using the graphics
context's current color.
From fillRect javadoc:
Fills the specified rectangle. The left and right edges of the
rectangle are at x and x + width - 1. The top and bottom edges are at
y and y + height - 1. The resulting rectangle covers an area width
pixels wide by height pixels tall. The rectangle is filled using the
graphics context's current color.
Fixing such an API is hard because code depending on it would fail (because to fix the actual problem with code above, I would pass 99x99 to have 100x100 rectangle: fixing the API would result in a 99x99 rectangle).
You may expect this to never be fixed unless the class is retrofitted into a new package (probably merging Graphics2D along the way).
TL;DR: you should probably create a static method drawRect doing the same as fillRect, like below:
public static void drawRect(Graphics g, int x, int y, int w, int h) {
g.drawRect(x, y, w - 1, h - 1);
}

Libgdx Tiled map object to box2d body location

A Tiled map object hat a position x, y in pixels and a rotation in degrees.
I am loading the coordinates and the rotation from the map and trying to assign them to a box2d Body. There are a couple of differences between the location models, for example Tiled object rotation is in degrees and box2d body angle is in radians.
How do I convert the location to the BodyDef coordinates x, y and angle so that the body will be created at the correct position?
Background:
Using the code:
float angle = -rotation * MathUtils.degreesToRadians;
bodyDef.angle = angle;
bodyDef.position.set(x, y);
Works when the rotation is 0, but the body is not positioned correctly when rotation is different than 0.
I found a couple of hints here:
http://www.tutorialsface.com/2015/12/qu ... dx-solved/
and here:
https://github.com/libgdx/libgdx/issues/2742
That seem to tackle this exact problem, however neither solution worked for me, the body objects are still positioned wrong after applying those transformations. By positioned wrong I mean that the body is positioned in the area of the map where it should be but slightly off depending on its rotation.
I feel that it should be pretty simple but I do not know how to mediate the differences between Tiled and box2d locations.
For reference these are the two solutions I tried from the links above (after transforming the values x, y, width, height from pixels to world units):
float angle = rotation * MathUtils.degreesToRadians;
bodyDef.angle = -angle;
Vector2 correctionPosition = new Vector2(
height * MathUtils.cosDeg(-rotation - 90),
height + height * MathUtils.sinDeg(-rotation - 90));
bodyDef.position.set(x, y).add(correctionPosition);
and
float angle = rotation * MathUtils.degreesToRadians;
bodyDef.angle = -angle;
// Top left corner of object
Vector2 correctedPosition = new Vector2(x, y + height);
// half of diagonal for rectangular object
float radius = (float)Math.sqrt(width * width + height * height) / 2.0f;
// Angle at diagonal of rectangular object
float theta = (float)Math.tanh(height / width) * MathUtils.degreesToRadians;
// Finding new position if rotation was with respect to top-left corner of object.
// X=x+radius*cos(theta-angle)+(h/2)cos(90+angle)
// Y=y+radius*sin(theta-angle)-(h/2)sin(90+angle)
correctedPosition = correctedPosition
.add(
radius * MathUtils.cos(theta - angle),
radius * MathUtils.sin(theta - angle))
.add(
((height / 2) * MathUtils.cos(MathUtils.PI2 + angle)),
(-(height / 2) * MathUtils.sin(MathUtils.PI2 + angle)));
bodyDef.position.set(correctedPosition);
Any hint would be highly welcomed.
Found the correct solution, lost about 1 day of my life :)
The information from above links is incorrect and/or outdated. Currently Tiled saves the object position depending on it's type. For an image is relative to bottom-left position.
Box2d doesn't really have an "origin" point, but you can consider is its center and the shapes of the fixtures attached to the body should be positioned relative to (0,0).
Step 1: Read tiled properties
float rotation = textureMapObject.getRotation();
float x = textureMapObject.getX();
float y = textureMapObject.getY();
float width = textureMapObject.getProperties()
.get("width", Float.class).floatValue();
float height = textureMapObject.getProperties()
.get("height", Float.class).floatValue();
Step 2: Scale these acording to your box2d world size, for example x = x * 1/25; etc.
Step 3: Create a body without any position or angle.
Step 4: Transform body position and angle with:
private void applyTiledLocationToBody(Body body,
float x, float y,
float width, float height,
float rotation) {
// set body position taking into consideration the center position
body.setTransform(x + width / 2, y + height / 2, 0);
// bottom left position in local coordinates
Vector2 localPosition = new Vector2(-width / 2, -height / 2);
// save world position before rotation
Vector2 positionBefore = body.getWorldPoint(localPosition).cpy();
// calculate angle in radians
float angle = -rotation * MathUtils.degreesToRadians;
// set new angle
body.setTransform(body.getPosition(), angle);
// save world position after rotation
Vector2 positionAfter = body.getWorldPoint(localPosition).cpy();
// adjust position with the difference (before - after)
// so that the bottom left position remains unchanged
Vector2 newPosition = body.getPosition()
.add(positionBefore)
.sub(positionAfter);
body.setTransform(newPosition, angle);
}
Hope it helps.

maintaining relative position when changing scaleX and scaleY?

I am trying to implement a simple "zoom" function in a map presentation type app. The user interacts with a NumericStepper to dial in a scale value and I then use that value to set the scaleX and scaleY properties of my map sprite. The parent of the map sprite has a scrollRect defined so the map is cropped as it scales. That all seems to work fine.
Naturally when I change the scale, the visible content shifts as the sprite becomes larger or smaller. I would like to keep the content in relatively the same screen location. I've taken a first pass at it below but it's not quite right.
Question: Am I on the right track here thinking that I can determine how much to shift the x/y by comparing the change in the width/height of the sprite after scaling? (as I write this I am thinking I can determine the center of the sprite before scaling, then reposition it so it stays centered over that point. Hmm. . .).
protected function scaleStepper_changeHandler(event:Event):void
{
var cX:Number = wrapper.x + (wrapper.width /2);
var cY:Number = wrapper.y + (wrapper.height /2);
wrapper.scaleX = scaleStepper.value;
wrapper.scaleY = scaleStepper.value;
wrapper.x = cX - (wrapper.width /2);
wrapper.y = cY - (wrapper.height /2);
}
You are on the right track, but for a better solution you should use a matrix to transform your Sprite. Use the following code below to achieve what you need:
private var originalMatrix:Matrix;
private function scaleAroundPoint(target:Sprite, scalePoint:Point, scaleFactor:Number):void
{
if(originalMatrix == null)
originalMatrix = target.transform.matrix;
var matrix:Matrix = originalMatrix.clone();
matrix.translate(-scalePoint.x, -scalePoint.y);
matrix.scale(scaleFactor, scaleFactor);
matrix.translate(scalePoint.x, scalePoint.y);
target.transform.matrix = matrix;
}
You can call this method like this:
scaleAroundPoint(wrapper, new Point(yourWidth/2, yourHeight/2), scaleStepper.value);
Hope this helps and solves your problem.
Am I on the right track here thinking that I can determine how much to shift the x/y by comparing the change in the width/height of the sprite after scaling?
Yes. As all values are known, you don't really have to "test" after scaling. You basically want to distribute the movement of the bounding box borders evenly.
Here's an example in one dimension, scaling factor 2, X is the registration point, | a boundary:
before scaling |--X--|
after scaling |----X----|
No problem there. Now what if the registration point is not in the middle?
before scaling |-X---|
after scaling |--X------|
As a last example, the edge case with the registration point on the boundary:
before scaling |X----|
after scaling |X--------|
Note how the boundaries of all 3 examples are equal before scaling and within each example, the registration point remains constant.
The problem is clearly identified. Now how to solve this?
We do know how much the width changes
before scaling width
after scaling width * scaleFactor
and from the first example we can determine where the left boundary should be after scaling (assuming that the registration point is at 0, so the object is centered):
before scaling -width * 0.5
after scaling -width * 0.5 * scaleFactor
This value depends on where the registration point of course is within the display object relative to the left boarder. To circumvent this dependency, subtract the values from each other to know how much the left boundary is moved to the left after scaling while keeping the object centered:
boundary shift width * 0.5 * (scaleFactor - 1)
Comparing before and after scaling, the left boundary should be further to the left by that amount and the right boundary should be further to the right by that amount.
The problem is that you cannot really set the left or right boundary directly.
You have to set the registration point, which will influence where the boundaries are. To know how far you should move the registration point, imagine both edge cases:
before scaling |X----|
after scaling |X--------|
corrected, |X--------|
before scaling |----X|
after scaling |--------X|
corrected, |--------X|
In both cases, the registration point has to be moved by the amount which the boundary should move, because essentially, the registration point is on the boundary and thus behaves the same way.
Any value in between can be found by linearly interpolating between both cases:
-[width * 0.5 * (scaleFactor - 1)] <= value <= +[width * 0.5 * (scaleFactor - 1)]
-[width * 0.5 * (scaleFactor - 1)] * (1-t) + [width * 0.5 * (scaleFactor - 1)] * t
To find the interpolation value t, which is 0 if X is on the left and 1 when on the right:
t = (X - L) / width
Add -[width * 0.5 * (scaleFactor - 1)] * (1-t) + [width * 0.5 * (scaleFactor - 1)] * t to the x position of the registration point and the scale the object.
Do the same for y in a similar fashion.

Setting X Coordinate from Mouse Location

i have a darkBlueRect rectangle sprite and a copy of the same sprite with a larger scale and a lighter color - lightBlueRect.
i'm attempting to shift the location of the lightBlueRect according to the mouse.x location when the mouse is moving over the darkBlueRect. this way if the mouse is on the right of the darkBlueRect than the location of the scaled up lightBlueRect will be on the opposite side and shifted towards the left proportionate to the mouse position and scale. in addition, the lightBlueRect must appear "locked" to the darkBlueRect so lightBlueRect.x must never be more than darkBlueRect.x and lightBlueRect.x + lightBlueRect.width must never be less than darkBlueRect.x + darkBlueRect.width.
the image below depicts 3 states of what i'm attempting to accomplish:
State A: mouse.x is over darkBlueRect.x = 1 and both sprites are aligned to the left.
State B: mouse.x is in the middle of darkBlueRect and both sprites are aligned to the middle.
State C: mouse.x is on the last pixel of darkBlueRect and both sprites are aligned to the right.
for this example, the darkBlueRect.width is equal to 170 and the lightBlueRect.width is equal to 320, or a scale of 1.89.
each time the mouse changes it's x position over darkBlueRect the following is called. however, while my current code works for the most part, it's not exactly correct. when the mouse.x is over darkBlueRect.x = 1, as shown in State A, the lightBlueRect.x is not property aligned with darkBlueRect and appears less than darkBlueRect.x.
var scale:Number = 1.89;
lightBlueRect.x = darkBlueRect.x - Math.round((mouse.x * scale) / darkBlueRect.width * (lightBlueRect.width - darkBlueRect.width));
what equation can i use so that no matter the scale of the lightBlueRect it's first position (mouse over first pixel) and last position (mouse over last pixel) will result in the 2 sprites being aligned as well as property proportionate positioning in between?
[EDIT] the coordinates of the darkBlueRect is {0, 0}, so when the lightBlueRect moves towards the left it is moving into the negative. i could have simply written my code (what doesn't work) like this instead:
var scale:Number = 1.89;
lightBlueRect.x = 0 - Math.round((mouse.x * scale) / darkBlueRect.width * (lightBlueRect.width - darkBlueRect.width));
[EDIT 2]
when the display objects are small, the problem is difficult to notice. however, when they are large the problem becomes move obvious. the problem, here, being that the objects on the left side are misaligned.
also the problem is probably exasperated by the fact that both the lightBlueRect and darkBlueRect are scalable. darkBlueRect is scaled down and lightBlueRect is scaled up.
here is a link to the test displaying the problem. mousing over the shape quickly will obviously result in inaccurate alignment since it's based on frame rate speed, but this is not my concern. still, when you slowly mouse over the shape it will not align correctly on the left side when the mouse is over the first pixel of darkBlueRect: http://www.geoffreymattie.com/test/test.html
[SWF(width = "1000", height = "600", backgroundColor = "0xCCCCCC")]
import flash.display.Sprite;
import flash.events.MouseEvent;
var downScale:Number = 0.48;
var upScale:Number = 2.64;
var darkBlueRect:Sprite = createSprite();
darkBlueRect.scaleX = darkBlueRect.scaleY = downScale;
darkBlueRect.x = stage.stageWidth / 2 - darkBlueRect.width / 2;
darkBlueRect.y = stage.stageHeight / 2 - darkBlueRect.height / 2;
addChild(darkBlueRect);
var lightBlueRect:Sprite = createSprite();
lightBlueRect.scaleX = lightBlueRect.scaleY = upScale;
lightBlueRect.y = stage.stageHeight / 2 - lightBlueRect.height / 2;
lightBlueRect.x = stage.stageWidth;
lightBlueRect.mouseEnabled = false;
addChild(lightBlueRect);
darkBlueRect.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveEventHandler);
function mouseMoveEventHandler(evt:MouseEvent):void
{
lightBlueRect.x = darkBlueRect.x + Math.max(0.0, Math.min(darkBlueRect.mouseX / darkBlueRect.width * downScale, 1.0)) * (darkBlueRect.width - lightBlueRect.width);
}
function createSprite():Sprite
{
var result:Sprite = new Sprite();
result.graphics.beginFill(0x0000FF, 0.5);
result.graphics.drawRect(0, 0, 700, 200);
result.graphics.endFill();
return result;
}
i believe the problem is that the scaling of the shapes.
Assuming you have a Clamp function handy, and that width is floating-point so that division works as expected:
lBR.x = dBR.x + Clamp((mouse.x - dBR.x) / dBR.width, 0, 1) * (dBR.width - lBR.width);
(You can define Clamp(x, m, M) = min(max(x, m), M) if you don't have one.)