Strange PolyBatch Positioning in libGDX - java

Excuse the basic question, just getting into the guts of LibGDX
I'm creating a radial bar to show my countdown timer
I've found some code that does what I need it to, the problem is the radial sprite's positioning. I can't seem to get it to center in the Image object (Since it seems to be ignoring the Image's local coordinates and is defaulting to the stage's) so 0,0 places it close to the bottom left of my screen.
I've tried using a localtoStage and vice versa to calculate the correct positions, but that doesn't seem to give me the right values either.
Please advise
package com.goplayplay.klpoker.CSS.Classes;
import com.badlogic.gdx.graphics.g2d.*;
import com.badlogic.gdx.math.EarClippingTriangulator;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.utils.ShortArray;
public class ProgressCircle extends Image {
TextureRegion texture;
PolygonSpriteBatch polyBatch;
Vector2 center;
Vector2 centerTop;
Vector2 leftTop;
Vector2 leftBottom;
Vector2 rightBottom;
Vector2 rightTop;
Vector2 progressPoint;
float[] fv;
IntersectAt intersectAt;
public ProgressCircle(TextureRegion region, PolygonSpriteBatch polyBatch) {
super(region);
this.texture = region;
this.polyBatch = polyBatch;
center = new Vector2(this.getWidth() / 2, this.getHeight() / 2);
centerTop = new Vector2(this.getWidth() / 2, this.getHeight());
leftTop = new Vector2(0, this.getHeight());
leftBottom = new Vector2(0, 0);
rightBottom = new Vector2(this.getWidth(), 0);
rightTop = new Vector2(this.getWidth(), this.getHeight());
progressPoint = new Vector2(this.getWidth() / 2, this.getHeight() / 2);
setPercentage(0);
}
private Vector2 IntersectPoint(Vector2 line) {
Vector2 v = new Vector2();
boolean isIntersect;
//check top
isIntersect = Intersector.intersectSegments(leftTop, rightTop, center, line, v);
//check bottom
if (isIntersect) {
intersectAt = IntersectAt.TOP;
return v;
} else isIntersect = Intersector.intersectSegments(leftBottom, rightBottom, center, line, v);
//check left
if (isIntersect) {
intersectAt = IntersectAt.BOTTOM;
return v;
} else isIntersect = Intersector.intersectSegments(leftTop, leftBottom, center, line, v);
//check bottom
if (isIntersect) {
intersectAt = IntersectAt.LEFT;
return v;
} else isIntersect = Intersector.intersectSegments(rightTop, rightBottom, center, line, v);
if (isIntersect) {
intersectAt = IntersectAt.RIGHT;
return v;
} else {
intersectAt = IntersectAt.NONE;
return null;
}
}
public void setPercentage(float percent) {
//100 % = 360 degree
//==> percent % => (percent * 360 / 100) degree
float angle = convertToRadians(90); //percent = 0 => angle = -90
angle -= convertToRadians(percent * 360 / 100);
float len = this.getWidth() > this.getHeight() ? this.getWidth() : this.getHeight();
float dy = (float) (Math.sin(angle) * len);
float dx = (float) (Math.cos(angle) * len);
Vector2 line = new Vector2(center.x + dx, center.y + dy);
Vector2 v = IntersectPoint(line);
if (intersectAt == IntersectAt.TOP) {
if (v.x >= this.getWidth() / 2)
{
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
leftTop.x,
leftTop.y,
leftBottom.x,
leftBottom.y,
rightBottom.x,
rightBottom.y,
rightTop.x,
rightTop.y,
v.x,
v.y
};
} else {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
v.x,
v.y
};
}
} else if (intersectAt == IntersectAt.BOTTOM) {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
leftTop.x,
leftTop.y,
leftBottom.x,
leftBottom.y,
v.x,
v.y
};
} else if (intersectAt == IntersectAt.LEFT) {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
leftTop.x,
leftTop.y,
v.x,
v.y
};
} else if (intersectAt == IntersectAt.RIGHT) {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
leftTop.x,
leftTop.y,
leftBottom.x,
leftBottom.y,
rightBottom.x,
rightBottom.y,
v.x,
v.y
};
} else // if (intersectAt == IntersectAt.NONE)
{
fv = null;
}
}
//
#Override
public void draw(Batch batch, float parentAlpha) {
// super.draw(batch, parentAlpha);
if (fv == null) return;
batch.end();
drawMe();
batch.begin();
}
public void drawMe() {
Vector2 acc = new Vector2();
acc.set(getWidth() / 2, getHeight() / 2);
localToStageCoordinates(acc);
EarClippingTriangulator e = new EarClippingTriangulator();
ShortArray sv = e.computeTriangles(fv);
PolygonRegion polyReg = new PolygonRegion(texture, fv, sv.toArray());
PolygonSprite poly = new PolygonSprite(polyReg);
poly.setOrigin(this.getOriginX(), this.getOriginY());
poly.setPosition(this.getX(), this.getY());
// poly.setPosition(acc.x, acc.y); //Attempting to calculate correct positioning - Doesnt work
poly.setRotation(this.getRotation());
poly.setColor(this.getColor());
polyBatch.begin();
poly.draw(polyBatch);
polyBatch.end();
}
float convertToDegrees(float angleInRadians) {
float angleInDegrees = angleInRadians * 57.2957795f;
return angleInDegrees;
}
//-----------------------------------------------------------------
float convertToRadians(float angleInDegrees) {
float angleInRadians = angleInDegrees * 0.0174532925f;
return angleInRadians;
}
public enum IntersectAt {
NONE, TOP, BOTTOM, LEFT, RIGHT
}
}

You forgot to set the camera's projection matrix on the polygon batch. You can get a copy of it from the Batch that's passed in:
public void draw(Batch batch, float parentAlpha) {
// super.draw(batch, parentAlpha);
if (fv == null) return;
batch.end();
drawMe(batch.getProjectionMatrix());
batch.begin();
}
public void drawMe(Matrix4 projection) {
polyBatch.setProjectionMatrix(projection);
//...
}
Or more simply, you can use a PolygonBatch as your Stage's batch, so you don't have to be swapping batches:
stage = new Stage(myViewport, new PolygonBatch());
//...
public void draw(Batch batch, float parentAlpha) {
// super.draw(batch, parentAlpha);
if (fv == null) return;
//don't need to call begin and end on the batch
drawMe((PolygonBatch)batch);
}
public void drawMe(PolygonBatch polyBatch) {
//...
//don't need to call begin or end on the batch
}
By the way, your drawMe method instantiates quite a few objects, some large. You should avoid this if you have more than a few actors that do this, or you'll get stutters from the GC. Try to instantiate objects only once in the constructor and reuse them.

Related

Transformation Hierarchy (Problem with modelling transformation of parent on children), OpenGL, LWJGL 3

I am currently building a scene graph in OpenGL and I trying to model parent transformations on their children, but it does not seem to be working. :(
My main problem is that the child's rotation does not follow the parent's rotation correctly. The child rotates around the parent object (center), which is what it is supposed to do, but the speed of it orbiting around the parent increases as the parent's rotation increase and slows back down once the parent does a full circle (when the parent's rotation reaches back to the rotation it started of at). Also I did have some problems with the correct translation of the child in relation to the parent, but I managed to temporarily fix them (I think). More info in the code. Sorry for the lengthy explanation, I would have definitely attached a video if I could, of the problem.
This is my transform class, where the transformations are set, particularly the getTransformation() method:
public class Transform {
private Vector3f position, lastPosition;
private Vector3f rotation, lastRotation;
private Vector3f scale, lastScale;
private Transform parent;
private Matrix4f parentMatrix;
public Transform() {
this(null, null, null);
}
public Transform(Vector3f position, Vector3f rotation, Vector3f scale) {
this.position = position != null ? position : new Vector3f(0, 0, 0);
this.rotation = rotation != null ? rotation : new Vector3f(0, 0, 0);
this.scale = scale != null ? scale : new Vector3f(1, 1, 1);
this.lastPosition = new Vector3f(0, 0, 0);
this.lastRotation = new Vector3f(0, 0, 0);
this.lastScale = new Vector3f(1, 1, 1);
parentMatrix = new Matrix4f().identity();
}
public boolean requireTMUpdate() { //checks if the matrix has changed and requires an update
if(parent != null) {
return parent.requireTMUpdate();
}
if(!position.equals(lastPosition)) {
lastPosition.set(position);
return true;
}
if(!rotation.equals(lastRotation)) {
lastRotation.set(rotation);
return true;
}
if(!scale.equals(lastScale)) {
lastScale.set(scale);
return true;
}
return false;
}
public Matrix4f getTransformation() {
if((parent != null) && (requireTMUpdate())) {
if(!(getParent().equals(getParent().getParent()))) {
parentMatrix.set(parent.getTransformation());
//The above line sets the updated parentMatrix
setPosition(parentMatrix.transformPosition(position));
//The above line sets the position to where child is supposed to be in
//relation the parent, otherwise once the key is stopped being pressed,
//it will return to the position at the start of the game / program.
}
}else {
parentMatrix.rotationXYZ(0, 0, 0).translation(0, 0, 0).scale(1);
// The above line is supposed to reset the matrix, otherwise the previous
//transformations or rotations add up each frame and the it just gets messed
//up.
}
//System.out.println(parentMatrix.toString());
return parentMatrix.mul(Matrices.transformationMatrix(position, rotation, scale))
// The transformationMatrix() method above from the Matrices class is
//supposed to return a worldMatrix.
}
public Vector3f getPosition() {
return position;
}
public void setPosition(Vector3f position) {
this.position = position;
}
public Vector3f getRotation() {
return rotation;
}
public void setRotation(Vector3f rotation) {
this.rotation = rotation;
}
public Vector3f getScale() {
return scale;
}
public void setScale(Vector3f scale) {
this.scale = scale;
}
public Transform getParent() {
return parent;
}
public void setParent(Transform parent) {
this.parent = parent;
}
}
Matrices class:
public class Matrices {
public static Matrix4f transformationMatrix(Vector3f pos, Vector3f rot, Vector3f scale) {
Matrix4f result = new Matrix4f();
result.identity();
result.translate(pos.x, pos.y, pos.z);
Quaternionf rotation =
new Quaternionf().
identity().
rotateX((float) Math.toRadians(rot.x)).
rotateY((float) Math.toRadians(rot.y)).
rotateZ((float) Math.toRadians(rot.z));
/*result.rotate((float) Math.toRadians(rot.getX()), 1, 0, 0);
result.rotate((float) Math.toRadians(rot.getY()), 0, 1, 0);
result.rotate((float) Math.toRadians(rot.getZ()), 0, 0, 1);*/
result.rotate(rotation);
result.scale(scale.x, scale.y, scale.z);
return result;
}
public static Matrix4f transformationMatrix(Vector3f pos, Quaternionf rotation, Vector3f scale) {
Matrix4f result = new Matrix4f();
result.identity();
result.translate(pos.x, pos.y, pos.z);
/*Quaternionf rotation =
new Quaternionf().
identity().
rotateX((float) Math.toRadians(rot.x)).
rotateY((float) Math.toRadians(rot.y)).
rotateZ((float) Math.toRadians(rot.z));*/
/*result.rotate((float) Math.toRadians(rot.getX()), 1, 0, 0);
result.rotate((float) Math.toRadians(rot.getY()), 0, 1, 0);
result.rotate((float) Math.toRadians(rot.getZ()), 0, 0, 1);*/
result.rotate(rotation);
result.scale(scale.x, scale.y, scale.z);
return result;
}
public static Matrix4f viewMatrix(Vector3f pos, Vector3f rot) {
Matrix4f result = new Matrix4f();
result.identity();
Quaternionf rotation =
new Quaternionf().
identity().
rotateX((float) Math.toRadians(rot.x)).
rotateY((float) Math.toRadians(rot.y)).
rotateZ((float) Math.toRadians(rot.z));
/*result.rotate((float) Math.toRadians(rot.getX()), new org.joml.Vector3f(1, 0, 0));
result.rotate((float) Math.toRadians(rot.getY()), new org.joml.Vector3f(0, 1, 0));*/
result.rotate(rotation);
result.translate(-pos.x, -pos.y, -pos.z);
return result;
}
public static Matrix4f viewMatrix(Vector3f pos, float pitch, float yaw, float roll) {
Matrix4f result = new Matrix4f();
result.identity();
Quaternionf rotation =
new Quaternionf().
identity().
rotateX((float) Math.toRadians(pitch)).
rotateY((float) Math.toRadians(yaw)).
rotateZ((float) Math.toRadians(roll));
/*result.rotate((float) Math.toRadians(rot.getX()), new org.joml.Vector3f(1, 0, 0));
result.rotate((float) Math.toRadians(rot.getY()), new org.joml.Vector3f(0, 1, 0));*/
result.rotate(rotation);
result.translate(-pos.x, -pos.y, -pos.z);
return result;
}
public static Matrix4f projectionMatrix(float FOV, float aspectRatio,
float NearPlaneDis, float FarPlaneDis) {
Matrix4f result = new Matrix4f();
result.identity();
result.perspective(FOV, aspectRatio, NearPlaneDis, FarPlaneDis);
/*float y_scale = (float) ((1f / Math.tan(Math.toRadians(FOV / 2f))) * aspectRatio);
float x_scale = y_scale / aspectRatio;
float frustum_length = FarPlaneDis - NearPlaneDis;
result.m00(x_scale);
result.m11(y_scale);
result.m22(-((FarPlaneDis + NearPlaneDis) / frustum_length));
result.m23(-1);
result.m32(-((2 * NearPlaneDis * FarPlaneDis) / frustum_length));
result.m33(0);*/
return result;
}
public static Matrix4f translate(Vector3f vec, Matrix4f src, Matrix4f dest) {
if (dest == null)
dest = new Matrix4f();
dest.m30(dest.m30() + src.m00() * vec.x + src.m10() * vec.y + src.m20() * vec.z);
dest.m31(dest.m31() + src.m01() * vec.x + src.m11() * vec.y + src.m21() * vec.z);
dest.m32(dest.m32() + src.m02() * vec.x + src.m12() * vec.y + src.m22() * vec.z);
dest.m33(dest.m33() + src.m03() * vec.x + src.m13() * vec.y + src.m23() * vec.z);
return dest;
}
public static Vector4f transform(Matrix4f left, Vector4f right, Vector4f dest) {
if (dest == null)
dest = new Vector4f(0, 0, 0, 0);
float x = left.m00() * right.x + left.m10() * right.y + left.m20() * right.z + left.m30() * right.w;
float y = left.m01() * right.x + left.m11() * right.y + left.m21() * right.z + left.m31() * right.w;
float z = left.m02() * right.x + left.m12() * right.y + left.m22() * right.z + left.m32() * right.w;
float w = left.m03() * right.x + left.m13() * right.y + left.m23() * right.z + left.m33() * right.w;
dest.x = x;
dest.y = y;
dest.z = z;
dest.w = w;
return dest;
}
public static Vector3f scale(Vector3f vector, float scale) {
vector.x *= scale;
vector.y *= scale;
vector.z *= scale;
return vector;
}
public static float[] getAll(Matrix4f matrix) {
float[] f = new float[16];
return matrix.get(f);
}
public static float barryCentric(Vector3f p1, Vector3f p2, Vector3f p3, Vector2f pos) {
float det = (p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z);
float l1 = ((p2.z - p3.z) * (pos.x - p3.x) + (p3.x - p2.x) * (pos.y - p3.z)) / det;
float l2 = ((p3.z - p1.z) * (pos.x - p3.x) + (p1.x - p3.x) * (pos.y - p3.z)) / det;
float l3 = 1.0f - l1 - l2;
return l1 * p1.y + l2 * p2.y + l3 * p3.y;
}
}
Finally, the Renderer class, where the objects are initialized:
public class Renderer {
private Model model;
private Model model2;
private GameObject root;
private GameObject child;
public Camera camera;
private float time = 0;
/*private float vertices[] = {
0.5f, 0.5f, 0f, //0 - top right
0.5f, -0.5f, 0f, //1 - bottom right
-0.5f, -0.5f, 0f, //2 - bottom left
-0.5f, 0.5f, 0f //3 - top left
};
private int indices[] = {
0, 1, 3, // first triangle
3, 1, 2 // second triangle
};*/
public Renderer() {
model = new Model("/backpack.obj");
model2 = new Model("/backpack.obj");
camera = new Camera(new Vector3f(0, 0, 0), new Vector3f(0, 0, 0));
root = new GameObject();
child = new GameObject();
root.addComponent(camera);
root.addComponent(model);
child.addComponent(model2);
root.addChild(child);
model.getTransform().setPosition(new Vector3f(0, 0, 0));
model2.getTransform().setPosition(new Vector3f(10, 0, 10));
}
public void render(Shader shader) {
if(Input.isKeyDown(GLFW.GLFW_KEY_RIGHT)) {
model.getTransform().setRotation(new Vector3f(0, time += 1f, 0));
}
//model2.getTransform().setRotation(new Vector3f(0, time += 0.01f, 0));
shader.bind();
root.input();
root.update();
root.render(shader, camera);
shader.unbind();
}
public void cleanUp() {
for(Mesh mesh: model.getMeshes()) {
mesh.cleanUp();
}
}
}
What exactly am I doing wrong here? Any help is appreciated!
Nvm, I found the solution after a day's worth of debugging:
This line:
return parentMatrix.mul(Matrices.transformationMatrix(position, rotation, scale));
should actually be:
return new Matrix4f(parentMatrix).mul(Matrices.transformationMatrix(position, rotation, scale));.
Turns out the mul method in the JOML Matrix4f class does not return a new Matrix4f() class of the worldMatrix that should be outputted instead multiplies it to the current matrix (parentMatrix here) itself, creating a completely different parentMatrix for the next frame.

Raycasting giving erratic values

Using LWJGL3 and JOML.
I am trying to work out how to get the point on the terrain using a raycast system. I use the point to set a characters position to see what point is being outputted.
I don't think it is my terrain causing the issue since moving the character (with the keyboard or by just adding a value each frame) and attaching it to the terrain works fine.
The issues I get:
Inverting the projection matrix causes the point to flicker between the correct position and some other point but I am not sure about the relationship with this other value.
Not inverting the projection matrix stops the flickering but now the point moves away from the mouse position exponentially.
Near the center of the screen the 2 positions will merge together.
If I print out the terrain point vector it comes out in scientific notation for some reason:
( 5.335E+1 3.849E-2 -9.564E+1)
( 8.804E+1 -6.256E-3 -2.815E+2)
( 5.335E+1 3.849E-2 -9.564E+1)
( 8.804E+1 -6.256E-3 -2.815E+2)
( 5.335E+1 3.849E-2 -9.564E+1)
If I print out each of the x, y and z values individually it is actually showing the correct position but is also flicking to another (the difference between which increases the further from the center of the screen that the mouse moves):
5.8912144, 0.016174316, -7.771721
6.1992702, 0.01574707, -11.79966
5.8912144, 0.016174316, -7.771721
6.1992702, 0.01574707, -11.79966
6.609352, 0.01815033, -8.793705
Raycasting class:
public class Raycast
{
private static final int RECURSION_COUNT = 200;
private static final float RAY_RANGE = 600;
private Input input;
private Vector3f currentRay = new Vector3f();
private Matrix4f projectionMatrix;
private Matrix4f viewMatrix;
private Camera camera;
private Terrain terrain;
private Vector3f currentTerrainPoint;
public Raycast(Camera camera, Matrix4f projectionMatrix, Terrain terrain, Input input) {
this.camera = camera;
this.projectionMatrix = projectionMatrix;
this.input = input;
this.viewMatrix = MathUtils.createViewMatrix(camera);
this.terrain = terrain;
}
public void update()
{
viewMatrix = MathUtils.createViewMatrix(camera);
currentRay = calculateRay();
if (intersectionInRange(0, RAY_RANGE, currentRay)) {
currentTerrainPoint = binarySearch(0, 0, RAY_RANGE, currentRay);
} else {
currentTerrainPoint = null;
}
}
private Vector3f calculateRay()
{
float mouseX = (float) input.getMouseDx();
float mouseY = (float) input.getMouseDy();
Vector2f deviceCoords = getNormalizedDeviceCoordinates(mouseX, mouseY);
//System.out.println(deviceCoords.x+", "+deviceCoords.y);
Vector4f clipCoords = new Vector4f(deviceCoords.x, deviceCoords.y, -1f, 1f);
Vector4f eyeCoords = toEyeCoords(clipCoords);
Vector3f worldRay = toWorldCoords(eyeCoords);
return worldRay;
}
private Vector3f toWorldCoords(Vector4f eyeCoords)
{
Matrix4f invertedView = viewMatrix.invert();
Vector4f rayWorld = invertedView.transform(eyeCoords);
Vector3f mouseRay = new Vector3f(rayWorld.x, rayWorld.y, rayWorld.z);
mouseRay.normalize();
return mouseRay;
}
private Vector4f toEyeCoords(Vector4f clipCoords)
{
Matrix4f invertedProjection = projectionMatrix.invert();
Vector4f eyeCoords = invertedProjection.transform(clipCoords);
return new Vector4f(eyeCoords.x, eyeCoords.y, -1f, 0f);
}
private Vector2f getNormalizedDeviceCoordinates(float mouseX, float mouseY)
{
float x = (2f * mouseX) / Constants.DISPLAY_WIDTH - 1f;
float y = (2f * mouseY) / Constants.DISPLAY_HEIGHT - 1f;
return new Vector2f(x, -y);
}
private Vector3f getPointOnRay(Vector3f ray, float distance) {
//Vector3f camPos = new Vector3f(camera.getPosX(), camera.getPosY(), camera.getPosZ());
Vector3f start = new Vector3f(camera.getPosX(), camera.getPosY(), camera.getPosZ());
Vector3f scaledRay = new Vector3f(ray.x * distance, ray.y * distance, ray.z * distance);
return start.add(scaledRay);
}
private Vector3f binarySearch(int count, float start, float finish, Vector3f ray) {
float half = start + ((finish - start) / 2f);
if (count >= RECURSION_COUNT) {
Vector3f endPoint = getPointOnRay(ray, half);
Terrain terrain = getTerrain(endPoint.x, endPoint.z);
if (terrain != null) {
return endPoint;
} else {
return null;
}
}
if (intersectionInRange(start, half, ray)) {
return binarySearch(count + 1, start, half, ray);
} else {
return binarySearch(count + 1, half, finish, ray);
}
}
private boolean intersectionInRange(float start, float finish, Vector3f ray) {
Vector3f startPoint = getPointOnRay(ray, start);
Vector3f endPoint = getPointOnRay(ray, finish);
if (!isUnderGround(startPoint) && isUnderGround(endPoint)) {
return true;
} else {
return false;
}
}
private boolean isUnderGround(Vector3f testPoint) {
Terrain terrain = getTerrain(testPoint.x, testPoint.z);
float height = 0;
if (terrain != null) {
height = terrain.getTerrainHeight(testPoint.x, testPoint.z);
}
if (testPoint.y < height) {
return true;
} else {
return false;
}
}
private Terrain getTerrain(float worldX, float worldZ) {
return terrain;
}
public Vector3f getCurrentTerrainPoint() {
return currentTerrainPoint;
}
public Vector3f getCurrentRay() {
return currentRay;
}
}
MathUtils class with view and projection matrices:
public class MathUtils {
public static float baryCentric(Vector3f p1, Vector3f p2, Vector3f p3, Vector2f pos) {
float det = (p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z);
float l1 = ((p2.z - p3.z) * (pos.x - p3.x) + (p3.x - p2.x) * (pos.y - p3.z)) / det;
float l2 = ((p3.z - p1.z) * (pos.x - p3.x) + (p1.x - p3.x) * (pos.y - p3.z)) / det;
float l3 = 1.0f - l1 - l2;
return l1 * p1.y + l2 * p2.y + l3 * p3.y;
}
public static Matrix4f createTransformationMatrix(Vector2f translation, Vector2f scale) {
Matrix4f matrix = new Matrix4f();
matrix.identity();
matrix.translate(translation.x,translation.y,0f);
matrix.scale(scale.x,scale.y,1f);
return matrix;
}
public static Matrix4f createTransformationMatrix(Vector3f translation, float rx, float ry, float rz, float scale) {
Matrix4f transformationMatrix = new Matrix4f();
transformationMatrix.identity();
transformationMatrix.translate(translation);
transformationMatrix.rotate((float) Math.toRadians(rx), 1,0,0);
transformationMatrix.rotate((float) Math.toRadians(ry), 0,1,0);
transformationMatrix.rotate((float) Math.toRadians(rz), 0,0,1);
transformationMatrix.scale(scale);
return transformationMatrix;
}
public static Matrix4f createViewMatrix(Camera camera) {
Matrix4f viewMatrix = new Matrix4f();
viewMatrix.identity();
viewMatrix = viewMatrix.rotate((float) Math.toRadians(camera.getPitch()), 1,0,0);//((float) Math.toRadians(camera.getPitch()), new Vector3f(1, 0, 0), viewMatrix);
viewMatrix = viewMatrix.rotate((float) Math.toRadians(camera.getYaw()),0, 1, 0);
Vector3f cameraPos = new Vector3f(camera.getPosX(), camera.getPosY(), camera.getPosZ());
Vector3f negativeCameraPos = new Vector3f(-cameraPos.x, -cameraPos.y, -cameraPos.z);
viewMatrix = viewMatrix.translate(negativeCameraPos);
return viewMatrix;
}
public static Matrix4f createProjectionMatrix() {
Matrix4f projectionMatrix = new Matrix4f();
float aspectRatio = (float) Constants.DISPLAY_WIDTH / (float) Constants.DISPLAY_HEIGHT;
float fov = Constants.FOV;
float near = Constants.NEAR_PLANE;
float far = Constants.FAR_PLANE;
projectionMatrix = projectionMatrix.perspective((float) java.lang.Math.toRadians(fov), aspectRatio, near, far);
return projectionMatrix;
}
A coordinate in view space is a Cartesian coordinates with 3 components x, y and z. The projection matrix transforms from view space to clip space. Clip space coordinates are Homogeneous coordinates with 4 components x, y, z and w.
Clip space coordinates can be transformed to normalized device coordinates by a Perspective divide.
This means the x, y and z component is divided by w component.
If you want to transform from normalized device space to view space, then you've to do the inverse operation. this means you've to transform by the inverse projection matrix and to divide the x, y and z component of the result by the w component of the result.
private Vector4f toEyeCoords(Vector4f ndcCoords)
{
Matrix4f invertedProjection = projectionMatrix.invert(new Matrix4f());
Vector4f eyeCoords = invertedProjection.transform(clipCoords);
return new Vector4f(eyeCoords.x/eyeCoords.w, eyeCoords.y/eyeCoords.w, eyeCoords.z/eyeCoords.w, 0.0f);
}

Java libGDX different sizes of animation frames

How to deal with different sizes of frames? If I setBounds(0, 0, 100, 100) in constructor then some frames are smaller but if I update it then frames are moved to left and up. How to make that every frame will be in the same place?
mayby you can see it
http://imgur.com/a/AN8Gc
public class Player extends Sprite{
//floats
private float animationTimer;
//box2d variables
public World world;
public Body body;
//enums
public enum State{STANDING, MOVING, SHOOTING, RELOAD, MALEE_ATTACK}
public State currentState;
public State previousState;
//booleans
boolean shoot;
boolean reload;
boolean maleeAttack;
private TextureRegion region;
public PlayScreen playScreen;
public Player(PlayScreen playScreen){
this.playScreen = playScreen;
this.world = playScreen.getWorld();
definePlayer();
animationTimer = 0;
region = Assets.instance.playerAssets.idleAniamtion.getKeyFrame(animationTimer);
setRegion(region);
setBounds(0, 0, getRegionWidth() / Constants.PPM, getRegionHeight() / Constants.PPM);
setPosition(0, 0);
currentState = State.STANDING;
previousState = State.STANDING;
shoot = false;
reload = false;
maleeAttack = false;
}
public void definePlayer(){
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(100 / Constants.PPM, 100 / Constants.PPM);
bodyDef.type = BodyDef.BodyType.DynamicBody;
body = world.createBody(bodyDef);
FixtureDef fixtureDef = new FixtureDef();
CircleShape shape = new CircleShape();
shape.setRadius(50 / Constants.PPM);
fixtureDef.shape = shape;
body.createFixture(fixtureDef).setUserData(this);
body.setLinearDamping(Constants.LINEAR_DAMPING);
}
public void update(float delta){
region = Assets.instance.playerAssets.idleAniamtion.getKeyFrame(animationTimer);
setRegion(getFrame(delta));
moving();
//rotate();
}
public void moving(){
setPosition(body.getPosition().x - getWidth() / 2, body.getPosition(). y - getHeight() / 2);
//setBounds(0, 0, getRegionWidth() / Constants.PPM, getRegionHeight() / Constants.PPM); update bounds
if (input.isKeyPressed(Input.Keys.W) && body.getLinearVelocity().y < 5){
body.applyLinearImpulse(new Vector2(0, 1), body.getWorldCenter(), true);
}
if (input.isKeyPressed(Input.Keys.S) && body.getLinearVelocity().y > -5){
body.applyLinearImpulse(new Vector2(0, -1), body.getWorldCenter(), true);
}
if (input.isKeyPressed(Input.Keys.D) && body.getLinearVelocity().x < 5){
body.applyLinearImpulse(new Vector2(1, 0), body.getWorldCenter(), true);
}
if (input.isKeyPressed(Input.Keys.A) && body.getLinearVelocity().x > -5){
body.applyLinearImpulse(new Vector2(-1, 0), body.getWorldCenter(), true);
}
if (Gdx.input.isKeyPressed(Input.Keys.R)){
reload = true;
}
if (Gdx.input.isKeyPressed(Input.Keys.F)){
maleeAttack = true;
}
if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)){
shoot = true;
}
}
public TextureRegion getFrame(float delta){
TextureRegion region;
currentState = getState();
switch (currentState){
case MOVING:
region = Assets.instance.playerAssets.moveAnimation.getKeyFrame(animationTimer);
break;
case SHOOTING:
maleeAttack = false;
region = Assets.instance.playerAssets.shootAniamtion.getKeyFrame(animationTimer);
if (Assets.instance.playerAssets.shootAniamtion.isAnimationFinished(animationTimer)){
shoot = false;
}
break;
case RELOAD:
region = Assets.instance.playerAssets.reloadAnimation.getKeyFrame(animationTimer);
if (Assets.instance.playerAssets.reloadAnimation.isAnimationFinished(animationTimer)){
reload = false;
}
break;
case MALEE_ATTACK:
region = Assets.instance.playerAssets.maleeAttackAnimation.getKeyFrame(animationTimer);
if (Assets.instance.playerAssets.maleeAttackAnimation.isAnimationFinished(animationTimer)) {
maleeAttack = false;
}
break;
default:
region = Assets.instance.playerAssets.idleAniamtion.getKeyFrame(animationTimer);
break;
}
animationTimer = currentState == previousState ? animationTimer + delta : 0;
previousState = currentState;
return region;
}
public State getState(){
if ((body.getLinearVelocity().x > 1 || body.getLinearVelocity().x < -1 || body.getLinearVelocity().y > 1 || body.getLinearVelocity().y < - 1) && !reload && !shoot && !maleeAttack){
return State.MOVING;
}
if (shoot && !reload){
return State.SHOOTING;
}
if (reload && !maleeAttack){
return State.RELOAD;
}
if (maleeAttack){
return State.MALEE_ATTACK;
}
else {
return State.STANDING;
}
}
public Vector2 getMousePosition(){
Vector2 mousePosition;
mousePosition = playScreen.getViewport().unproject(new Vector2(Gdx.input.getX(), Gdx.input.getY()));
return mousePosition;
}
public float getMouseAngle(){
float angle = (float) Math.atan2(getMousePosition().y - body.getPosition().y, getMousePosition().x - body.getPosition().x);
return angle;
}
public void rotate(){
setOrigin(getWidth() / 2, getHeight() / 2 );
setRotation((float) (getMouseAngle() * (180/Math.PI)));
body.setTransform(body.getPosition(), getMouseAngle());
}
}
To avoid the resizing of the texture just create a variable size used to set the bounds. The bound needs to set every frame.
public class Player extends Sprite {
private Body body;
private Vector2 size;
public Player(){
this.size = new Vector2( getRegionWidth() / Constants.PPM, getRegionHeight() / Constants.PPM );
}
public void update( float delta ){
Vector2 position = body.getPosition();
setRegion( getFrame( delta ) );
setRotation( MathUtils.radDeg * body.getAngle() );
setBounds( position.x, position.y, size.x, size.y );
setOriginCenter();
}
public void rotate(){
this.body.setTransform( body.getPosition(), getMouseAngle() );
}
}
You need to use the additional data information a TextureAtlas provides as an AtlasRegion.
One complete animation should have the same (width/height) size for each keyframe. Pick a size where the biggest keyfarme fits into. Also be aware of positioning correctly. Have the same pivot point for each keyframe.
To avoid wasting space when packing your TextureAtlas enable "trim" function. Should be supported by any TexturePacker Tool out there.
The texture data file (for libGDX) then has entries like this:
walk_animation
rotate: false
xy: 794, 235
size: 86, 109
orig: 160, 170
offset: 37, 22
index: 5
Use the AtlasRegion to draw things at the correct size and position:
float width = 400; // pick your size here
float height = 300;
float offsetPctX = atlasRegion.offsetX / atlasRegion.originalWidth;
float offsetPctY = atlasRegion.offsetY / atlasRegion.originalHeight;
float scaleWidth = (float) atlasRegion.packedWidth / atlasRegion.originalWidth;
float scaleHeight = (float) atlasRegion.packedHeight / atlasRegion.originalHeight;
float drawWidth = width * scaleWidth;
float drawHeight = height * scaleHeight;
float regionOffsetX = offsetPctX * width;
float regionOffsetY = offsetPctY * height;
float drawScaleX = 1; // adjust to your needs
float drawScaleY = 1;
float drawOriginX = 0; // adjust to tour needs
float drawOriginY = 0;
float drawRotation = false;
float x = 100 + offsetX + regionOffsetX; // adjust to your needs
float y = 100 + offsetY + regionOffsetY;
spriteBatch.draw(atlasRegion, x, y, drawOriginX, drawOriginY, drawWidth, drawHeight, drawScaleX, drawScaleY, drawRotation);

How to make sprite bounce of the sides of the screen

I'm using libgdx in java android studio. i have just started. i'm working on android phone. i not using any cameras. all i want is a sprite bounce of all four sides of the screen without tapping. i tried many codes i thought would work but nope. i hope u guys can help me. I'm expecting an answer soon as possible. Thanks
this is what i have:
SpriteBatch batch;
Texture background;
Sprite backgroundsprite;
Sprite ballsprite;
Texture line;
Texture ballimg;
BitmapFont credits;
BitmapFont input;
BitmapFont play;
float dt;
String string = "";
float ballx;
float bally;
float speedx;
float speedy;
Rectangle screenrect;
Rectangle ballrect;
float screenLeft ;
float screenBottom ;
float screenTop ;
float screenRight ;
#Override
public void create() {
batch = new SpriteBatch();
speedx = 5f * dt;
speedy = 5f * dt;
createsprite();
createbackground();
createtext();
ballx = ballsprite.getX();
bally = ballsprite.getY();
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
dt = Gdx.graphics.getDeltaTime();
ballsprite.setPosition(ballx + speedx,ballsprite.getY());
ballsprite.translateX(speedx);
float left = ballrect.getX();
float bottom = ballrect.getY();
float top = bottom + ballrect.getHeight();
float right = left + ballrect.getWidth();
if(left < screenLeft) {
string = "left";
speedx = 5f*dt;
}
if(right > screenRight)
{
string = "right";
speedx = -5f*dt;
}
if(bottom < screenBottom)
{
string = "bottom";
}
if(top > screenTop)
{
string = "top";
}
batch.begin();
backgroundsprite.draw(batch);
ballsprite.draw(batch);
rendertext();
batch.end();
}
public void createbackground() {
background = new Texture("images/BackgroundGodwin.jpg");
backgroundsprite = new Sprite(background);
backgroundsprite.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
screenrect = new Rectangle(0,0,Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
screenLeft = screenrect.getX();
screenBottom = screenrect.getY();
screenTop = screenBottom + screenrect.getHeight();
screenRight = screenLeft + screenrect.getWidth();
}
public void createsprite() {
ballimg = new Texture("images/SpriteGodwin.png");
ballsprite = new Sprite(ballimg);
ballsprite.setScale(0.65f);
ballsprite.setPosition(Gdx.graphics.getWidth()/3,Gdx.graphics.getHeight()/2);
ballrect = new Rectangle(ballsprite.getBoundingRectangle());
}
#Override
public void dispose() {
batch.dispose();
ballimg.dispose();
background.dispose();
credits.dispose();
play.dispose();
input.dispose();
line.dispose();
}
public void createtext(){
play = new BitmapFont(Gdx.files.internal("fonts/realfont.fnt"));
play.setColor(com.badlogic.gdx.graphics.Color.GOLD);
credits = new BitmapFont(Gdx.files.internal("fonts/realfont.fnt"));
credits.setColor(com.badlogic.gdx.graphics.Color.GOLD);
input = new BitmapFont(Gdx.files.internal("fonts/realfont.fnt"));
input.setColor(com.badlogic.gdx.graphics.Color.OLIVE);
}
public void rendertext() {
credits.draw(batch, "Maded", Gdx.graphics.getWidth() / 7 - 50, Gdx.graphics.getHeight() / 9);
play.draw(batch, "Touch the Screen to play!!", Gdx.graphics.getWidth() / 2 - 175, Gdx.graphics.getHeight() - 80);
input.draw(batch, string, Gdx.graphics.getWidth() / 2 - 160, Gdx.graphics.getHeight() - 120);
}
}
I made a very simple version of what you want:
public class BouncyGame extends ApplicationAdapter {
SpriteBatch batch;
Texture ball;
float speedX = 3f;
float speedY = 3f;
int x;
int y;
#Override
public void create () {
batch = new SpriteBatch();
ball = new Texture("ball.png");
x = Gdx.graphics.getWidth()/2;
y = Gdx.graphics.getHeight()/2;
}
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//When the ball's x position is on either side of the screen.
//The width of the sprite is taken into account.
if (x > Gdx.graphics.getWidth() - ball.getWidth()/2 || x < 0 + ball.getWidth()/2) {
//Here we flip the speed, so it bonces the other way.
speedX = -speedX;
}
//Same as above, but with on the y-axis.
if (y > Gdx.graphics.getHeight() - ball.getHeight()/2 || y < 0 + ball.getHeight()/2) {
speedY = -speedY;
}
//Move the ball according to the speed.
x += speedX;
y += speedY;
batch.begin();
//Draw the ball so the center is at x and y. Normally it would be drawn from the lower left corner.
batch.draw(ball, x - ball.getWidth()/2, y - ball.getHeight()/2);
batch.end();
}
}
It will result in the following:
http://gfycat.com/TatteredCarefreeHapuku
There are numerous ways to improve this code, you could for example use vectors, and I wouldn't recommend using it in your final product, but it might help you figure out how to do something like this for your own project.

How to unbind a sprite in an updating vector?

How to unbind an updating vector to a sprite? I am using Libgdx framework. here is my code
public class VectorSample extends GDX_TEST implements InputProcessor {
Texture ball,bullet,guider;
OrthographicCamera camera;
Vector2 vector = new Vector2();
Vector2 vectoralt = new Vector2();
long lastDropTime;
SpriteBatch batch;
Rectangle rect = new Rectangle();
float angle = 0,anglealt = 0;
private ShapeRenderer renderer;
TextureRegion tr;
Sprite sprite,sprite2;
ShapeRenderer sr;
Ship ship;
BallShit shit;
Vector2 nvec;
protected Vector2 center = new Vector2();
Array<Vector2> bullets;
Array<Rectangle> bulletsRect;
Boolean fire = false;
Rectangle tmpRect = new Rectangle();
#Override
public void create() {
renderer = new ShapeRenderer();
ball = new Texture(Gdx.files.internal("data/player.png"));
bullet = new Texture("data/ball_black.png");
tr = new TextureRegion(ball);
camera = new OrthographicCamera();
camera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
ship = new Ship(new Vector2(5, 50), 1, 1, 0, 5f);
sr = new ShapeRenderer();
batch = new SpriteBatch();
Gdx.input.setInputProcessor(this);
rect.width = ball.getWidth();
rect.height = ball.getHeight();
vector.add(200,200);
sprite = new Sprite(ball);
vectoralt.add(200 + sprite.getWidth(),200);
shit = new BallShit(new Vector2(10,0),50f,50f);
bullets = new Array<Vector2>();
getCenter();
bulletsRect = new Array<Rectangle>();
fireatwill2();
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
{
batch.draw(sprite,vector.x,vector.y,sprite.getWidth()/2,sprite.getHeight()/2,sprite.getWidth(),sprite.getHeight(),1,1,angle);
if(fire == true){
for(Rectangle raindrop: bulletsRect) {
batch.draw(bullet,raindrop.x,raindrop.y);
}
}
}
batch.end();
if(Gdx.input.isKeyPressed(Input.Keys.D)){
vector.add(1,0);
if(vector.x > Gdx.graphics.getWidth()){
Gdx.app.log("x","x");
}
}
if(Gdx.input.isKeyPressed(Input.Keys.A)){
vector.add(-1,0);
if(vector.x < 0){
Gdx.app.log("x","-x");
}
}
float w = camera.frustum.planePoints[1].x - camera.frustum.planePoints[0].x;
float distance = w - (vector.x + sprite.getWidth());
if(distance <=0){
vector.x = w - sprite.getWidth();
}
Gdx.app.log("camera x" , " " + distance);
if(distance >= Gdx.graphics.getWidth() - sprite.getWidth()) {
vector.x = 0;
}
if(Gdx.input.isButtonPressed(Input.Buttons.LEFT)){
fire = true;
if(TimeUtils.nanoTime() - lastDropTime > 100000000) fireatwill2();
}
Iterator<Rectangle> iter = bulletsRect.iterator();
while(iter.hasNext()) {
Rectangle raindrop = iter.next();
double angletry = getAngle() * MathUtils.degreesToRadians;
float speed = 5;
float scale_x = (float)Math.cos(angletry);
float scale_y = (float)Math.sin(angletry);
float velocity_x = (speed* scale_x);
float velocity_y = (speed* scale_y);
raindrop.x += velocity_x;
raindrop.y += velocity_y;
if(raindrop.y < 0) iter.remove();
if(raindrop.y > Gdx.graphics.getHeight() || raindrop.x > Gdx.graphics.getWidth()) iter.remove();
}
//getting the angle
float angle = findAngle(Gdx.input.getX(),
Gdx.graphics.getHeight() - Gdx.input.getY());
this.angle = angle % 360;
}
private float getAngle(){
float angle = findAngle(Gdx.input.getX(),
Gdx.graphics.getHeight() - Gdx.input.getY());
return angle;
}
private void fireatwill2() {
Rectangle raindrop = new Rectangle();
raindrop.x = vector.x + sprite.getWidth() / 2 - (bullet.getWidth() / 2);
raindrop.y = vector.y + sprite.getHeight() / 2 - (bullet.getHeight() / 2);
bulletsRect.add(raindrop);
lastDropTime = TimeUtils.nanoTime();
}
public Vector2 getCenter() {
center.x = vector.x + sprite.getWidth() / 2;
center.y = vector.y + sprite.getHeight() / 2;
return center.cpy();
}
#Override
public void pause() {
super.pause(); //To change body of overridden methods use File | Settings | File Templates.
}
#Override
public void resume() {
super.resume(); //To change body of overridden methods use File | Settings | File Templates.
}
public float findAngle(float x1, float y1) {
Vector2 center = getCenter();
float x0 = center.x;
float y0 = center.y;
float a = MathUtils.atan2(y1 - y0, x1 - x0);
return a * MathUtils.radiansToDegrees;
}
}
and here is the running demo and source is here
I cant explain it well but if you could run the demo u will get what i am saying.
Im really stuck in here.. Thanks.

Categories