this uses the lwjgl library.
in this 3D engine, I created a camera, it moves around fine, but the rotation is really bad. First off, left and right move diagonally down left or right, and up and down make the object stretch.
all of my rotation code to do with the camera:
public Vector3f rotate(float angle, Vector3f axis)
{
float sinHalfAngle = (float)Math.sin(Math.toRadians(angle / 2));
float cosHalfAngle = (float)Math.cos(Math.toRadians(angle / 2));
float rX = axis.getX() * sinHalfAngle;
float rY = axis.getY() * sinHalfAngle;
float rZ = axis.getZ() * sinHalfAngle;
float rW = cosHalfAngle;
Quaternion rotation = new Quaternion(rX, rY, rZ, rW);
Quaternion conjugate = rotation.conjugate();
Quaternion w = rotation.mul(this).mul(conjugate);
x = w.getX();
y = w.getY();
z = w.getZ();
return this;
}
can't remember if this has to do with rotation, but it's for the camera
public Matrix4f ititProjection(float fov, float width, float height, float zNear, float zFar)
{
float ar = width/height;
float tanHalfFOV = (float)Math.tan(Math.toRadians(fov / 2));
float zRange = zNear - zFar;
m[0][0] = 1.0f / (tanHalfFOV * ar); m[0][1] = 0; m[0][2] = 0; m[0][3] = 0;
m[1][0] = 0; m[1][1] = 1.0f / tanHalfFOV; m[1][2] = 0; m[1][3] = 0;
m[2][0] = 0; m[2][1] = 0; m[2][2] = (-zNear - zFar)/zRange; m[2][3] = 2 * zFar * zNear / zRange;
m[3][0] = 0; m[3][1] = 0; m[3][2] = 1; m[3][3] = 0;
return this;
}
public Matrix4f initCamera(Vector3f forward, Vector3f up)
{
Vector3f f = forward;
f.normalize();
Vector3f r = up;
r.normalize();
r = r.cross(f);
Vector3f u = f.cross(r);
m[0][0] = r.getX(); m[0][1] = r.getY(); m[0][2] = r.getZ(); m[0][3] = 0;
m[1][0] = u.getX(); m[1][1] = u.getY(); m[1][2] = r.getZ(); m[1][3] = 0;
m[2][0] = f.getX(); m[2][1] = f.getY(); m[2][2] = f.getZ(); m[2][3] = 0;
m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1;
return this;
}
Related
First I want to say that I am not using the AffineTransform class that Oracle made. I am doing my "own" AffineTransform. Why? I am making a pixel engine and I couldn't find any other solution. So I began looking a bit at Wikipedia and how matrices work. I then watched javidx9's video at how to implement it. I got it working. Rotation,reflection,shear etc worked fine but I got a pixel gap. Javidx9 mentioned this in his video and he solved the problem. I tried his solution but I got an "ArrayOutOfIndex error" when doing reflection,rotation and shear. I have been trying to solve the issue but I just can't understand what I am doing due to the complex code. That's why I am here! I need your help!
I got it working like this (with gaps):
GIF
The code I use for the transformation with gaps is the following:
Render the image with a matrix
for (int x = 0; x < image2.getWidth(); x++)
{
for (int y = 0; y < image2.getHeight(); y++)
{
matrixLast.forward(x,y);
renderer.setPixel((int)matrixLast.getNx(),(int) matrixLast.getNy(), image2.getPixels()[x+y*image2.getWidth()], null, 255);
}
}
However as you can see you need more information than that. I also made a "Matrix3x3" class, here are the important methods/functions:
public static void Identity(Matrix3x3 mat)
{
mat.matrix[0][0] = 1.0f; mat.matrix[1][0] = 0.0f; mat.matrix[2][0] = 0.0f;
mat.matrix[0][1] = 0.0f; mat.matrix[1][1] = 1.0f; mat.matrix[2][1] = 0.0f;
mat.matrix[0][2] = 0.0f; mat.matrix[1][2] = 0.0f; mat.matrix[2][2] = 1.0f;
}
public static void Translate(Matrix3x3 mat, float ox, float oy)
{
mat.matrix[0][0] = 1.0f; mat.matrix[1][0] = 0.0f; mat.matrix[2][0] = ox;
mat.matrix[0][1] = 0.0f; mat.matrix[1][1] = 1.0f; mat.matrix[2][1] = oy;
mat.matrix[0][2] = 0.0f; mat.matrix[1][2] = 0.0f; mat.matrix[2][2] = 1.0f;
}
public static void Rotate(Matrix3x3 mat, float degrees)
{
double fTheta = Math.toRadians(degrees);
mat.matrix[0][0] = (float)Math.cos(fTheta); mat.matrix[1][0] = (float)-Math.sin(fTheta); mat.matrix[2][0] = 0.0f;
mat.matrix[0][1] = (float)Math.sin(fTheta); mat.matrix[1][1] = (float)Math.cos(fTheta); mat.matrix[2][1] = 0.0f;
mat.matrix[0][2] = 0.0f; mat.matrix[1][2] = 0.0f; mat.matrix[2][2] = 1.0f;
}
public static void Scale(Matrix3x3 mat, float sx, float sy)
{
mat.matrix[0][0] = sx; mat.matrix[1][0] = 0.0f; mat.matrix[2][0] = 0.0f;
mat.matrix[0][1] = 0.0f; mat.matrix[1][1] = sy; mat.matrix[2][1] = 0.0f;
mat.matrix[0][2] = 0.0f; mat.matrix[1][2] = 0.0f; mat.matrix[2][2] = 1.0f;
}
public static void Shear(Matrix3x3 mat, float sx, float sy)
{
mat.matrix[0][0] = 1.0f; mat.matrix[1][0] = sx; mat.matrix[2][0] = 0.0f;
mat.matrix[0][1] = sy; mat.matrix[1][1] = 1.0f; mat.matrix[2][1] = 0.0f;
mat.matrix[0][2] = 0.0f; mat.matrix[1][2] = 0.0f; mat.matrix[2][2] = 1.0f;
}
public static void Reflection(Matrix3x3 mat) {
mat.matrix[0][0] = -1.0f; mat.matrix[1][0] = 0; mat.matrix[2][0] = 0.0f;
mat.matrix[0][1] = 0; mat.matrix[1][1] = 1.0f; mat.matrix[2][1] = 0.0f;
mat.matrix[0][2] = 0.0f; mat.matrix[1][2] = 0.0f; mat.matrix[2][2] = 1.0f;
}
public void forward(float inX, float inY) { //Ingen aning vad denna gör
float outX,outY;
outX = (inX * this.matrix[0][0]) + (inY * this.matrix[1][0]) + this.matrix[2][0];
outY = (inX * this.matrix[0][1]) + (inY * this.matrix[1][1]) + this.matrix[2][1];
this.ny = outY;
this.nx = outX;
}
In the "render" code as you can see above you see that there is a "matrixLast" in there. Here is how I got that one (they are defined elsewhere as for example: "Matrix3x3 name;"):
Matrix3x3.Translate(matrixFinal3, -image2.getWidth() / 2, -image2.getHeight() / 2);
Matrix3x3.Rotate(matrixFinal2,rotation); //Rotation och shear är det något fel på.
matrixSum = Matrix3x3.multiplyMatrix(matrixFinal2, matrixFinal3);
Matrix3x3.Translate(matrixFinal, 700, 600);
matrixLast = Matrix3x3.multiplyMatrix(matrixFinal, matrixSum);
matrixInv = Matrix3x3.invert(matrixLast);
The issue I am having is basically that I am trying to fix the "gaps". I tried Javidx9's solution which changes the render code to the following (Utils.getMaximum() returns the largest number and the opposite for Utils.getMinimum() ):
float ex,ey,sx,sy;
matrixLast.forward(0f, 0f);
sx = matrixLast.getNx();
ex = matrixLast.getNx();
sy = matrixLast.getNy();
ey = matrixLast.getNy();
matrixLast.forward((float) image2.getWidth(), (float) image2.getHeight());
sx = Utils.getMinimum(sx, matrixLast.getNx()); sy = Utils.getMinimum(sy, matrixLast.getNy());
ex = Utils.getMaximum(ex, matrixLast.getNx()); ey = Utils.getMaximum(ey, matrixLast.getNy());
matrixLast.forward(0f, (float) image2.getHeight());
sx = Utils.getMinimum(sx, matrixLast.getNx()); sy = Utils.getMinimum(sy, matrixLast.getNy());
ex = Utils.getMaximum(ex, matrixLast.getNx()); ey = Utils.getMaximum(ey, matrixLast.getNy());
matrixLast.forward((float) image2.getWidth(), 0f);
sx = Utils.getMinimum(sx, matrixLast.getNx()); sy = Utils.getMinimum(sy, matrixLast.getNy());
ex = Utils.getMaximum(ex, matrixLast.getNx()); ey = Utils.getMaximum(ey, matrixLast.getNy());
for(int x = (int)sx; x < (int)ex; x++) {
for(int y = (int)sy; y < (int)ey; y++) {
matrixInv.forward(x,y);
renderer.setPixel(x, y, image2.getPixels()[(int)(matrixInv.getNx() + 0.5) + (int)(matrixInv.getNy() + 0.5) * image2.getWidth()], null, 255);
}
}
He also added an invert method:
public static Matrix3x3 invert(Matrix3x3 matIn) {
float det = matIn.matrix[0][0] * (matIn.matrix[1][1] * matIn.matrix[2][2] - matIn.matrix[1][2] * matIn.matrix[2][1]) -
matIn.matrix[1][0] * (matIn.matrix[0][1] * matIn.matrix[2][2] - matIn.matrix[2][1] * matIn.matrix[0][2]) +
matIn.matrix[2][0] * (matIn.matrix[0][1] * matIn.matrix[1][2] - matIn.matrix[1][1] * matIn.matrix[0][2]);
float idet = 1.0f / det;
Matrix3x3 matOut = new Matrix3x3();
matOut.matrix[0][0] = (matIn.matrix[1][1] * matIn.matrix[2][2] - matIn.matrix[1][2] * matIn.matrix[2][1]) * idet;
matOut.matrix[1][0] = (matIn.matrix[2][0] * matIn.matrix[1][2] - matIn.matrix[1][0] * matIn.matrix[2][2]) * idet;
matOut.matrix[2][0] = (matIn.matrix[1][0] * matIn.matrix[2][1] - matIn.matrix[2][0] * matIn.matrix[1][1]) * idet;
matOut.matrix[0][1] = (matIn.matrix[2][1] * matIn.matrix[0][2] - matIn.matrix[0][1] * matIn.matrix[2][2]) * idet;
matOut.matrix[1][1] = (matIn.matrix[0][0] * matIn.matrix[2][2] - matIn.matrix[2][0] * matIn.matrix[0][2]) * idet;
matOut.matrix[2][1] = (matIn.matrix[0][1] * matIn.matrix[2][0] - matIn.matrix[0][0] * matIn.matrix[2][1]) * idet;
matOut.matrix[0][2] = (matIn.matrix[0][1] * matIn.matrix[1][2] - matIn.matrix[0][2] * matIn.matrix[1][1]) * idet;
matOut.matrix[1][2] = (matIn.matrix[0][2] * matIn.matrix[1][0] - matIn.matrix[0][0] * matIn.matrix[1][2]) * idet;
matOut.matrix[2][2] = (matIn.matrix[0][0] * matIn.matrix[1][1] - matIn.matrix[0][1] * matIn.matrix[1][0]) * idet;
return matOut;
}
Here is when he totally lost me, it got too complex and I don't know how to troubleshoot it due to me not knowing exactly what the code does. His solution gave me an ArrayOutOfIndexerror but only when doing reflections,rotations and shear:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index -9 out of bounds for length 589824
at com.dubstepzedd.gameengine.gamemanager.GameManager.render(GameManager.java:112)
at com.dubstepzedd.gameengine.GameContainer.loop(GameContainer.java:94)
at com.dubstepzedd.gameengine.GameContainer.run(GameContainer.java:48)
at java.base/java.lang.Thread.run(Thread.java:832)
at com.dubstepzedd.gameengine.GameContainer.start(GameContainer.java:40)
at com.dubstepzedd.gameengine.gamemanager.GameManager.main(GameManager.java:178)
I have no clue why this is happening. If someone has any advice on how to approach the "gap" situation differently or what is wrong with the code please comment or post a question
UPDATE
Here is the how it acts when I added getPixel():
Link to GIF
The code added to the Image class:
public int getPixel(int x, int y) {
if(0 <= x && x < this.width && 0 <= y && y < this.height) {
return pixels[x+y*this.width];
}
else {
return Color.black.getRGB();
}
}
You are trying to access a pixel that's outside the original image here:
image2.getPixels()[(int)(matrixInv.getNx() + 0.5) + (int)(matrixInv.getNy() + 0.5) * image2.getWidth()]
I would recommend adding a method to your image class that returns the pixel at a x/y position, if x and y are in range, and a default color if not:
// add to image class:
int getPixel(int x, int y) {
if (0 <= x && x < width && 0 <= y && y < height) {
return pixelData[x + width*y];
} else {
return backgroundColor;
}
}
Use this method instead of array lookups - for example
for(int x = (int)sx; x < (int)ex; x++) {
for(int y = (int)sy; y < (int)ey; y++) {
matrixInv.forward(x,y);
int pixel = image2.getPixel((int)(matrixInv.getNx() + 0.5, (int)(matrixInv.getNy() + 0.5))
renderer.setPixel(x, y, pixel);
}
}
I'm trying to make a 3d game with java from scratch but a have a problem with rendering a triangle after I multiply each vertices with projection matrix
I already try to use the projected vertices x and y but the result is all the vertices the in the same X, so I try to rotate the triangle X or Y or Z axis but the result is the same.
The result of rendering (draw in paint):
I know that the triangle is align with the camera but I tried to move the vertex that is overlapping out by changing its X or Y or Z coordinate but It didn't work
import java.awt.Color;
import java.awt.Graphics;
import measurement.MatrixF;
import measurement.Vector3f;
import model.Mesh;
import model.Triangle;
import toolbox.GE;
import toolbox.Matrix;
import toolbox.Vector;
public class MeshRenderer {
private int width, height;
private float fNear, fFar;
private float fov;
private float fAspectRatio;
private float fovRad;
private float theta;
private MatrixF projectionMatrix;
private MatrixF rotXMatrix;
private MatrixF rotYMatrix;
private MatrixF rotZMatrix;
private Vector3f globalTranslation;
public MeshRenderer(float fNear, float fFar, float fov, int width, int height) {
this.fNear = fNear;
this.fFar = fFar;
this.fov = fov;
this.fAspectRatio = height / width;
this.width = width;
this.height = height;
this.fovRad = (float) (1.0f / Math.tan(Math.toRadians(fov / 2)));
projectionMatrix = new MatrixF(4, 4);
rotXMatrix = new MatrixF(4, 4);
rotYMatrix = new MatrixF(4, 4);
rotZMatrix = new MatrixF(4, 4);
projectionMatrix.m[0][0] = fAspectRatio * fovRad;
projectionMatrix.m[1][1] = fovRad;
projectionMatrix.m[2][2] = (-(fFar + fNear)) / (fFar - fNear);
projectionMatrix.m[3][2] = (-2 * fFar * fNear) / (fFar - fNear);
projectionMatrix.m[2][3] = -1.0f;
projectionMatrix.m[3][3] = 0.0f;
rotXMatrix.m[0][0] = 1;
rotXMatrix.m[1][1] = (float) Math.cos(theta);
rotXMatrix.m[2][1] = (float) -Math.sin(theta);
rotXMatrix.m[1][2] = (float) Math.sin(theta);
rotXMatrix.m[2][2] = (float) Math.cos(theta);
rotYMatrix.m[0][0] = (float) Math.cos(theta);
rotYMatrix.m[2][0] = (float) Math.sin(theta);
rotYMatrix.m[1][1] = (float) 1.0;
rotYMatrix.m[0][2] = (float) -Math.sin(theta);
rotYMatrix.m[2][2] = (float) Math.cos(theta);
rotXMatrix.m[2][2] = 1;
rotXMatrix.m[0][0] = (float) Math.cos(theta);
rotXMatrix.m[1][0] = (float) -Math.sin(theta);
rotXMatrix.m[0][1] = (float) Math.sin(theta);
rotXMatrix.m[1][1] = (float) Math.cos(theta);
//projectionMatrix = Matrix.transpose(projectionMatrix);
globalTranslation = new Vector3f(0.0f, 0.0f, 0.0f);
}
public void renderMesh(Mesh mesh, Graphics g) {
for(int i = 0; i < mesh.tris.length; i++) {
Triangle tri = new Triangle(mesh.tris[i].p[0], mesh.tris[i].p[1], mesh.tris[i].p[2]);
Triangle translatedTri = tri;
Triangle projectedTri = new Triangle();
theta += 0.0001;
this.calculateRotationMatrix(theta);
translatedTri.p[0] = Matrix.multiplyMatrixVector(tri.p[0], rotYMatrix);
translatedTri.p[1] = Matrix.multiplyMatrixVector(tri.p[1], rotYMatrix);
translatedTri.p[2] = Matrix.multiplyMatrixVector(tri.p[2], rotYMatrix);
translatedTri.p[0].z = tri.p[0].z + globalTranslation.z;
translatedTri.p[1].z = tri.p[1].z + globalTranslation.z;
translatedTri.p[2].z = tri.p[2].z + globalTranslation.z;
projectedTri.p[0] = Matrix.multiplyMatrixVector(translatedTri.p[0], projectionMatrix);
projectedTri.p[1] = Matrix.multiplyMatrixVector(translatedTri.p[1], projectionMatrix);
projectedTri.p[2] = Matrix.multiplyMatrixVector(translatedTri.p[2], projectionMatrix);
projectedTri.p[0].x += 1.0f; projectedTri.p[0].y += 1.0f;
projectedTri.p[1].x += 1.0f; projectedTri.p[1].y += 1.0f;
projectedTri.p[2].x += 1.0f; projectedTri.p[2].y += 1.0f;
float scale = 0.5f;
projectedTri.p[0].x *= scale * width;
projectedTri.p[0].y *= scale * height;
projectedTri.p[1].x *= scale * width;
projectedTri.p[1].y *= scale * height;
projectedTri.p[2].x *= scale * width;
projectedTri.p[2].y *= scale * height;
GE.drawTriangle(projectedTri.p[0].x, projectedTri.p[0].y, projectedTri.p[1].x, projectedTri.p[1].y, projectedTri.p[2].x, projectedTri.p[2].y, Color.WHITE, g);
for(int j = 0; j < projectedTri.p.length; j++) {
g.setColor(new Color(255, 0, (j * 50)));
g.fillRect((int)projectedTri.p[j].x - 8, (int)projectedTri.p[j].y - 8, 16 - j, 16 - j);
}
translatedTri.p[0].z = tri.p[0].z - globalTranslation.z;
translatedTri.p[1].z = tri.p[1].z - globalTranslation.z;
translatedTri.p[2].z = tri.p[2].z - globalTranslation.z;
}
}
private void calculateRotationMatrix(float theta) {
rotXMatrix.m[0][0] = 1;
rotXMatrix.m[1][1] = (float) Math.cos(theta);
rotXMatrix.m[2][1] = (float) -Math.sin(theta);
rotXMatrix.m[1][2] = (float) Math.sin(theta);
rotXMatrix.m[2][2] = (float) Math.cos(theta);
rotYMatrix.m[0][0] = (float) Math.cos(theta);
rotYMatrix.m[2][0] = (float) Math.sin(theta);
rotYMatrix.m[1][1] = (float) 1.0;
rotYMatrix.m[0][2] = (float) -Math.sin(theta);
rotYMatrix.m[2][2] = (float) Math.cos(theta);
rotXMatrix.m[2][2] = 1;
rotXMatrix.m[0][0] = (float) Math.cos(theta);
rotXMatrix.m[1][0] = (float) -Math.sin(theta);
rotXMatrix.m[0][1] = (float) Math.sin(theta);
rotXMatrix.m[1][1] = (float) Math.cos(theta);
}
public Vector3f getTranslation() {
return globalTranslation;
}
public float getfNear() {
return fNear;
}
public float getfFar() {
return fFar;
}
public float getFov() {
return fov;
}
public float getfAspectRatio() {
return fAspectRatio;
}
public float getFovRad() {
return fovRad;
}
}
The matrix (4x4) multiply with vector3 function just in case:
Vector3f o = new Vector3f(0, 0, 0);
o.x = (i.x * m.m[0][0]) + (i.y * m.m[1][0]) + (i.z * m.m[2][0]) + m.m[3][0];
o.y = (i.x * m.m[0][1]) + (i.y * m.m[1][1]) + (i.z * m.m[2][1]) + m.m[3][1];
o.z = (i.x * m.m[0][2]) + (i.y * m.m[1][2]) + (i.z * m.m[2][2]) + m.m[3][2];
float w = (i.x * m.m[0][3]) + (i.y * m.m[1][3]) + (i.z * m.m[2][3]) + m.m[3][3];
if (w != 0.0f)
{
o.x /= w; o.y /= w; o.z /= w;
}
return o;
}
Without seeing exactly how this class is being used, it's hard to say exactly what the problem is, but FWIW I'm not seeing too much wrong with the math:
There are a couple places where you probably intended to initialize rotZMatrix instead of reinitializing rotXMatrix, but the code is not actually using either.
When adding in globalTranslation, you are overwriting the rotated z coordinate with the pre-rotated z coordinate, when you probably just want to update the rotated coordinate.
It's not apparent whether MatrixF is initialized to the identity or to zeros -- but if the latter, you probably should be populating the m[3][3] element of the rotation matrices with 1.0.
Naturally, you probably want to lift the theta increment and rotation calculation outside of the triangle loop, once you have more than one triangle.
I'm guessing that the problem is that you are leaving globalTranslation at zero and that the mesh is near the origin -- hence the transformed geometry is on the wrong side of the near plane and outside of the view frustum. Most graphic engines would cull such geometry, since the post-transform results will lie outside of clip space and will look increasingly anomalous around and behind the eye point.
I'd recommend trying to adjust globalTranslation.z to ensure 0 < fNear < translatedTri.p[i].z < fFar, for all the translated points.
(You could also try temporarily swapping the perspective matrix with a orthographic projection matrix, to determine whether the problem is in the projection/homogenization math or elsewhere.)
So right now I am copying tiles that are within the camera's viewport, and pasting them to the top of the TiledMap using the following function:
public TiledMapTile[][] copyRegion(TiledMapTileLayer layer, int x, int y, int width, int height) {
TiledMapTile[][] region = new TiledMapTile[width][height];
for (int ix = x; ix < x + width; ix++)
for (int iy = y; iy < y + height; iy++) {
Cell cell = layer.getCell(ix, iy);
if(cell == null)
continue;
region[ix - x][iy - y] = cell.getTile();
}
return region;
}
My problem is with this double for loop, its searching through each x and y value of the viewport, which is thousands of loops which is obviously my problem. Before this I was setting the width and height to the TiledMapTileLayer's layer.getTileHeight and layer.getTileWidth and it ran smoothly. This is because the layers x and y values go by the Tiles, so it was only really checking about 100 tiles, which is much faster. How would I go about "merging" the 2 together to only copy the tiles within the viewport, instead of the x and y values within the viewport?
I am calling this function using the following:
clipboard = TileMapCopier.copyRegion(layer, 0, 0, (int) camera.viewportWidth, (int) camera.viewportHeight);
TileMapCopier.pasteRegion(layer, clipboard, 0, layer.getHeight() / 2);
Thanks!
EDIT: I was told to look at how the orthogonal tiled map renderer renders based on viewport. I dont really know how to do this :P Im still new to this.
#Override
public void renderTileLayer (TiledMapTileLayer layer) {
final Color batchColor = spriteBatch.getColor();
final float color = Color.toFloatBits(batchColor.r, batchColor.g, batchColor.b, batchColor.a * layer.getOpacity());
final int layerWidth = layer.getWidth();
final int layerHeight = layer.getHeight();
final float layerTileWidth = layer.getTileWidth() * unitScale;
final float layerTileHeight = layer.getTileHeight() * unitScale;
final int col1 = Math.max(0, (int)(viewBounds.x / layerTileWidth));
final int col2 = Math.min(layerWidth, (int)((viewBounds.x + viewBounds.width + layerTileWidth) / layerTileWidth));
final int row1 = Math.max(0, (int)(viewBounds.y / layerTileHeight));
final int row2 = Math.min(layerHeight, (int)((viewBounds.y + viewBounds.height + layerTileHeight) / layerTileHeight));
float y = row1 * layerTileHeight;
float xStart = col1 * layerTileWidth;
final float[] vertices = this.vertices;
for (int row = row1; row < row2; row++) {
float x = xStart;
for (int col = col1; col < col2; col++) {
final TiledMapTileLayer.Cell cell = layer.getCell(col, row);
if (cell == null) {
x += layerTileWidth;
continue;
}
final TiledMapTile tile = cell.getTile();
if (tile != null) {
final boolean flipX = cell.getFlipHorizontally();
final boolean flipY = cell.getFlipVertically();
final int rotations = cell.getRotation();
TextureRegion region = tile.getTextureRegion();
float x1 = x;
float y1 = y;
float x2 = x1 + region.getRegionWidth() * unitScale;
float y2 = y1 + region.getRegionHeight() * unitScale;
float u1 = region.getU();
float v1 = region.getV2();
float u2 = region.getU2();
float v2 = region.getV();
vertices[X1] = x1;
vertices[Y1] = y1;
vertices[C1] = color;
vertices[U1] = u1;
vertices[V1] = v1;
vertices[X2] = x1;
vertices[Y2] = y2;
vertices[C2] = color;
vertices[U2] = u1;
vertices[V2] = v2;
vertices[X3] = x2;
vertices[Y3] = y2;
vertices[C3] = color;
vertices[U3] = u2;
vertices[V3] = v2;
vertices[X4] = x2;
vertices[Y4] = y1;
vertices[C4] = color;
vertices[U4] = u2;
vertices[V4] = v1;
if (flipX) {
float temp = vertices[U1];
vertices[U1] = vertices[U3];
vertices[U3] = temp;
temp = vertices[U2];
vertices[U2] = vertices[U4];
vertices[U4] = temp;
}
if (flipY) {
float temp = vertices[V1];
vertices[V1] = vertices[V3];
vertices[V3] = temp;
temp = vertices[V2];
vertices[V2] = vertices[V4];
vertices[V4] = temp;
}
if (rotations != 0) {
switch (rotations) {
case Cell.ROTATE_90: {
float tempV = vertices[V1];
vertices[V1] = vertices[V2];
vertices[V2] = vertices[V3];
vertices[V3] = vertices[V4];
vertices[V4] = tempV;
float tempU = vertices[U1];
vertices[U1] = vertices[U2];
vertices[U2] = vertices[U3];
vertices[U3] = vertices[U4];
vertices[U4] = tempU;
break;
}
case Cell.ROTATE_180: {
float tempU = vertices[U1];
vertices[U1] = vertices[U3];
vertices[U3] = tempU;
tempU = vertices[U2];
vertices[U2] = vertices[U4];
vertices[U4] = tempU;
float tempV = vertices[V1];
vertices[V1] = vertices[V3];
vertices[V3] = tempV;
tempV = vertices[V2];
vertices[V2] = vertices[V4];
vertices[V4] = tempV;
break;
}
case Cell.ROTATE_270: {
float tempV = vertices[V1];
vertices[V1] = vertices[V4];
vertices[V4] = vertices[V3];
vertices[V3] = vertices[V2];
vertices[V2] = tempV;
float tempU = vertices[U1];
vertices[U1] = vertices[U4];
vertices[U4] = vertices[U3];
vertices[U3] = vertices[U2];
vertices[U2] = tempU;
break;
}
}
}
spriteBatch.draw(region.getTexture(), vertices, 0, 20);
}
x += layerTileWidth;
}
y += layerTileHeight;
}
}
}
I am rendering triangle strips with glpolygonMode(GL_FRONT_AND_BACK, GL_LINE).
Then I enable the line offset to this setting glPolygonOffset (-0.8f, -1.0f).
No matter what setting I use for the offset I end up getting random lines that flash
in and out when moving around. Here is a gif. of what it looks like. If anyone could help
give ideas as what they think is wrong here that would be great.
Here is my method where I render the lines if anyone needs to look at it.
public void lines() {
GL11.glColor3f(0.0f, 0.0f, 0.0f);
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
Triangle currentTri = new Triangle();
List<Triangle> triList = new ArrayList<Triangle>();
boolean xSwitch = false;
for (int z=0; z<=100; z+=2) {
GL11.glEnable(GL11.GL_POLYGON_OFFSET_LINE);
GL11.glPolygonOffset (-0.8f, -1.0f);
GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
int triPointIndex = 0;
boolean zSwitch = true;
xSwitch = !xSwitch;
float zVal;
float xVal;
for (int x=0; x<100; x+=1) {
xVal = x;
float randY;
zSwitch = !zSwitch;
// this is what determines the xSwitch and zSwitch outcomes.
// changes the zVal and randY to match what must alternate.
if (xSwitch) {
if (zSwitch) {
zVal = z;
randY = randYList[z][x];
} else {
zVal = z + 2.0f;
randY = randYList[z+2][x];
}
} else {
if (zSwitch) {
zVal = z + 2.0f;
randY = randYList[z+2][x];
} else {
zVal = z;
randY = randYList[z][x];
}
}
// set up the currentVert within the currentTri so that we can use it for computing normals.
Vert currentVert = currentTri.triVerts[triPointIndex];
currentVert.x = xVal;
currentVert.y = randY;
currentVert.z = zVal;
triPointIndex++;
if (triPointIndex == 3) {
triList.add(currentTri);
Triangle nextTri = new Triangle();
Vector3f normal = new Vector3f();
float Ux; float Uy; float Uz;
float Vx; float Vy; float Vz;
if (xSwitch) {
if (triList.indexOf(currentTri) % 2 == 0) {
Vx = currentTri.triVerts[1].x - currentTri.triVerts[0].x;
Vy = currentTri.triVerts[1].y - currentTri.triVerts[0].y;
Vz = currentTri.triVerts[1].z - currentTri.triVerts[0].z;
Ux = currentTri.triVerts[2].x - currentTri.triVerts[0].x;
Uy = currentTri.triVerts[2].y - currentTri.triVerts[0].y;
Uz = currentTri.triVerts[2].z - currentTri.triVerts[0].z;
} else {
Ux = currentTri.triVerts[1].x - currentTri.triVerts[0].x;
Uy = currentTri.triVerts[1].y - currentTri.triVerts[0].y;
Uz = currentTri.triVerts[1].z - currentTri.triVerts[0].z;
Vx = currentTri.triVerts[2].x - currentTri.triVerts[0].x;
Vy = currentTri.triVerts[2].y - currentTri.triVerts[0].y;
Vz = currentTri.triVerts[2].z - currentTri.triVerts[0].z;
}
} else {
if (triList.indexOf(currentTri) % 2 == 0) {
Ux = currentTri.triVerts[1].x - currentTri.triVerts[0].x;
Uy = currentTri.triVerts[1].y - currentTri.triVerts[0].y;
Uz = currentTri.triVerts[1].z - currentTri.triVerts[0].z;
Vx = currentTri.triVerts[2].x - currentTri.triVerts[0].x;
Vy = currentTri.triVerts[2].y - currentTri.triVerts[0].y;
Vz = currentTri.triVerts[2].z - currentTri.triVerts[0].z;
} else {
Vx = currentTri.triVerts[1].x - currentTri.triVerts[0].x;
Vy = currentTri.triVerts[1].y - currentTri.triVerts[0].y;
Vz = currentTri.triVerts[1].z - currentTri.triVerts[0].z;
Ux = currentTri.triVerts[2].x - currentTri.triVerts[0].x;
Uy = currentTri.triVerts[2].y - currentTri.triVerts[0].y;
Uz = currentTri.triVerts[2].z - currentTri.triVerts[0].z;
}
}
normal.x = (Uy * Vz) - (Uz * Vy);
normal.y = (Uz * Vx) - (Ux * Vz);
normal.z = (Ux * Vy) - (Uy * Vx);
GL11.glNormal3f(normal.x, normal.y, normal.z);
nextTri.triVerts[0] = currentTri.triVerts[1];
nextTri.triVerts[1] = currentTri.triVerts[2];
currentTri = nextTri;
triPointIndex = 2;
} // close triPointIndex == 3
GL11.glVertex3f(xVal, randY, zVal);
} // close x loop
GL11.glEnd();
} // close z loop
GL11.glDisable(GL11.GL_POLYGON_OFFSET_LINE);
}
android uses the following code to calculate rotation matrix:
float Ax = gravity[0];
float Ay = gravity[1];
float Az = gravity[2];
final float Ex = geomagnetic[0];
final float Ey = geomagnetic[1];
final float Ez = geomagnetic[2];
float Hx = Ey*Az - Ez*Ay;
float Hy = Ez*Ax - Ex*Az;
float Hz = Ex*Ay - Ey*Ax;
final float normH = (float)Math.sqrt(Hx*Hx + Hy*Hy + Hz*Hz);
if (normH < 0.1f) {
// device is close to free fall (or in space?), or close to
// magnetic north pole. Typical values are > 100.
return false;
}
final float invH = 1.0f / normH;
Hx *= invH;
Hy *= invH;
Hz *= invH;
final float invA = 1.0f / (float)Math.sqrt(Ax*Ax + Ay*Ay + Az*Az);
Ax *= invA;
Ay *= invA;
Az *= invA;
final float Mx = Ay*Hz - Az*Hy;
final float My = Az*Hx - Ax*Hz;
final float Mz = Ax*Hy - Ay*Hx;
if (R != null) {
if (R.length == 9) {
R[0] = Hx; R[1] = Hy; R[2] = Hz;
R[3] = Mx; R[4] = My; R[5] = Mz;
R[6] = Ax; R[7] = Ay; R[8] = Az;
} else if (R.length == 16) {
R[0] = Hx; R[1] = Hy; R[2] = Hz; R[3] = 0;
R[4] = Mx; R[5] = My; R[6] = Mz; R[7] = 0;
R[8] = Ax; R[9] = Ay; R[10] = Az; R[11] = 0;
R[12] = 0; R[13] = 0; R[14] = 0; R[15] = 1;
}
}
I would like to know what the logic behind this is. How should I use an accelerometer and magnetometer to obtain a rotation matrix?
Annotated, with corner case handing removed:
// Down vector
float Ax = gravity[0];
float Ay = gravity[1];
float Az = gravity[2];
// North vector
final float Ex = geomagnetic[0];
final float Ey = geomagnetic[1];
final float Ez = geomagnetic[2];
H is perpendicular to both E and A
// H = E x A
float Hx = Ey*Az - Ez*Ay;
float Hy = Ez*Ax - Ex*Az;
float Hz = Ex*Ay - Ey*Ax;
final float normH = (float)Math.sqrt(Hx*Hx + Hy*Hy + Hz*Hz);
Each column in the matrix should have length 1
// Force H to unit length
final float invH = 1.0f / normH;
Hx *= invH;
Hy *= invH;
Hz *= invH;
// Force A to unit length
final float invA = 1.0f / (float)Math.sqrt(Ax*Ax + Ay*Ay + Az*Az);
Ax *= invA;
Ay *= invA;
Az *= invA;
Since A is perpendicular to H, and both are of unit length, M must also have unit length, so no normalization is needed here.
// M = A x H
// Forward vector
final float Mx = Ay*Hz - Az*Hy;
final float My = Az*Hx - Ax*Hz;
final float Mz = Ax*Hy - Ay*Hx;
H, M, and A are mutually perpendicular, so we have a rotation matrix
R[0] = Hx; R[1] = Hy; R[2] = Hz;
R[3] = Mx; R[4] = My; R[5] = Mz;
R[6] = Ax; R[7] = Ay; R[8] = Az;