My AffineTransformation has gaps between pixels - java

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);
}
}

Related

java-processing floating point rounding error, how to keep radians rational

I have modified this arcball class so that every call to arcball.rollforward(PI/180); rotates a matrix 1 degree.
I have tried to set it up so arcball.rollback() is called with the accumulated float rotatebywithincludedfloaterror but it has had the same degree error as rolling back 360 degrees without the float error.
this is how far it is off after 1000 full rotations, it should be a 1:1 reflection of the top cube over x
here is main function with a loop of 1 * 360 degree rotation and framerate for testing (set framerate to 900 for multiple rotations so it dose not take forever)
Arcball arcball;
int i;
//framecount
int fcount, lastm;
float frate;
int fint = 3;
boolean[] keys = new boolean[13];
final int w = 0;
void setup() {
size(900, 700, P3D);
frameRate(60);
noStroke();
arcball = new Arcball(width/2, height/2, 100); //100 is radius
}
void draw() {
lights();
background(255,160,122);
print(" \n degree = " + i );
i++;
if(i <= (360 * 1)) { arcball.rollforward(PI/180); }
else { print(" break"); }
if(keys[w]) { arcball.rollforward(PI/180); }
translate(width/2, height/2-100, 0);
box(50);
translate(0, 200, 0);
arcball.run();
box(50);
fcount += 1;
int m = millis();
if (m - lastm > 1000 * fint) {
frate = float(fcount) / fint;
fcount = 0;
lastm = m;
println("fps: " + frate);
}
}
void keyPressed() {
switch(key) {
case 119:
keys[w] = true;
break;
}
}
void keyReleased() {
switch(key) {
case 119:
keys[w] = false;
break;
}
}
and the arcball class
// Ariel and V3ga's arcball class with a couple tiny mods by Robert Hodgin and smaller mods by cubesareneat
class Arcball {
float center_x, center_y, radius;
Vec3 v_down, v_drag;
Quat q_now, q_down, q_drag;
Vec3[] axisSet;
int axis;
float mxv, myv;
float x, y;
float degreeW_count = 0;
float degreeS_count = 0;
float rotatebywithincludedfloaterror =0;
Arcball(float center_x, float center_y, float radius){
this.center_x = center_x;
this.center_y = center_y;
this.radius = radius;
v_down = new Vec3();
v_drag = new Vec3();
q_now = new Quat();
q_down = new Quat();
q_drag = new Quat();
axisSet = new Vec3[] {new Vec3(1.0f, 0.0f, 0.0f), new Vec3(0.0f, 1.0f, 0.0f), new Vec3(0.0f, 0.0f, 1.0f)};
axis = -1; // no constraints...
}
void rollforward(float radians2turn) {
rotatebywithincludedfloaterror = rotatebywithincludedfloaterror + (-1 * (((sin(radians2turn) * radius))/2));
if(degreeW_count >= 360) {
arcball.rollback(rotatebywithincludedfloaterror);
degreeW_count = 0;
rotatebywithincludedfloaterror = 0;
}
rollortilt(0, -1 * (((sin(radians2turn) * radius))/2));
degreeW_count = degreeW_count + 1; // need to edit this later to work with rotations other then 1 degree
}
void rollback(float radians2turn) {
rollortilt(0, ((sin(radians2turn) * radius))/2);
}
void rollortilt(float xtra, float ytra){
q_down.set(q_now);
v_down = XY_to_sphere(center_x, center_y);
q_down.set(q_now);
q_drag.reset();
v_drag = XY_to_sphere(center_x + xtra, center_y + ytra);
q_drag.set(Vec3.dot(v_down, v_drag), Vec3.cross(v_down, v_drag));
}
/*
void mousePressed(){
v_down = XY_to_sphere(mouseX, mouseY);
q_down.set(q_now);
q_drag.reset();
}
void mouseDragged(){
v_drag = XY_to_sphere(mouseX, mouseY);
q_drag.set(Vec3.dot(v_down, v_drag), Vec3.cross(v_down, v_drag));
}
*/
void run(){
q_now = Quat.mul(q_drag, q_down);
applyQuat2Matrix(q_now);
x += mxv;
y += myv;
mxv -= mxv * .01;
myv -= myv * .01;
}
Vec3 XY_to_sphere(float x, float y){
Vec3 v = new Vec3();
v.x = (x - center_x) / radius;
v.y = (y - center_y) / radius;
float mag = v.x * v.x + v.y * v.y;
if (mag > 1.0f){
v.normalize();
} else {
v.z = sqrt(1.0f - mag);
}
return (axis == -1) ? v : constrain_vector(v, axisSet[axis]);
}
Vec3 constrain_vector(Vec3 vector, Vec3 axis){
Vec3 res = new Vec3();
res.sub(vector, Vec3.mul(axis, Vec3.dot(axis, vector)));
res.normalize();
return res;
}
void applyQuat2Matrix(Quat q){
// instead of transforming q into a matrix and applying it...
float[] aa = q.getValue();
rotate(aa[0], aa[1], aa[2], aa[3]);
}
}
static class Vec3{
float x, y, z;
Vec3(){
}
Vec3(float x, float y, float z){
this.x = x;
this.y = y;
this.z = z;
}
void normalize(){
float length = length();
x /= length;
y /= length;
z /= length;
}
float length(){
return (float) Math.sqrt(x * x + y * y + z * z);
}
static Vec3 cross(Vec3 v1, Vec3 v2){
Vec3 res = new Vec3();
res.x = v1.y * v2.z - v1.z * v2.y;
res.y = v1.z * v2.x - v1.x * v2.z;
res.z = v1.x * v2.y - v1.y * v2.x;
return res;
}
static float dot(Vec3 v1, Vec3 v2){
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
static Vec3 mul(Vec3 v, float d){
Vec3 res = new Vec3();
res.x = v.x * d;
res.y = v.y * d;
res.z = v.z * d;
return res;
}
void sub(Vec3 v1, Vec3 v2){
x = v1.x - v2.x;
y = v1.y - v2.y;
z = v1.z - v2.z;
}
}
static class Quat{
float w, x, y, z;
Quat(){
reset();
}
Quat(float w, float x, float y, float z){
this.w = w;
this.x = x;
this.y = y;
this.z = z;
}
void reset(){
w = 1.0f;
x = 0.0f;
y = 0.0f;
z = 0.0f;
}
void set(float w, Vec3 v){
this.w = w;
x = v.x;
y = v.y;
z = v.z;
}
void set(Quat q){
w = q.w;
x = q.x;
y = q.y;
z = q.z;
}
static Quat mul(Quat q1, Quat q2){
Quat res = new Quat();
res.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
res.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
res.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z;
res.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x;
return res;
}
float[] getValue(){
// transforming this quat into an angle and an axis vector...
float[] res = new float[4];
float sa = (float) Math.sqrt(1.0f - w * w);
if (sa < EPSILON){
sa = 1.0f;
}
res[0] = (float) Math.acos(w) * 2.0f;
res[1] = x / sa;
res[2] = y / sa;
res[3] = z / sa;
return res;
}
}
keep track of the floating error margin to return same number of degrees arcball.rollforward()
void rollforward(float radians2turn) {
rotatebywithincludedfloaterror = rotatebywithincludedfloaterror + (-1 * (((sin(radians2turn) * radius))/2));
if(degreeW_count >= 360) {
arcball.rollback(rotatebywithincludedfloaterror);
degreeW_count = 0;
rotatebywithincludedfloaterror = 0;
}
rollortilt(0, -1 * (((sin(radians2turn) * radius))/2));
degreeW_count = degreeW_count + 1; // need to edit this later to work with rotations other then 1 degree
}
using my idea in the question to reset every 2*PI
if(keys[w]) {
arcball.rollforward(PI/180);
degreeW_count = degreeW_count + 1;
}
if(degreeW_count == 360) {
arcball = new Arcball(width/2, height/2, 100); // setset to original arcball at 0 degrees
degreeW_count = 0;
}
in arcball
void rollforward(float degrees2turn) {
rollortilt(0, -1 * (((sin(degrees2turn) * radius))/2)); // one degree forward 180/PI
}
this totally circumvents the any rounding error that would accumulate with any data type using irrational numbers and periodic functions!

circle collision causes circle to go supersonic

can anyone tell me why the circles i draw are getting stuck on each other or going super fast and flying off the window. all i want is for the circles to bounce off each other, but instead they get stuck 1/3 of the time, accelerate drastically another 1/3 of the time and only bounce the other 1/3. sorry if its messy, i've been trying a lot of different ideas and it kind of got out of hand. code uses maths that i got from gist.github/christopher4lis/f9ccb589ee8ecf751481f05a8e59b1dc. its horribly done but its the best i can do for now.
Please let me know if there is any other code that you need to help fix this.
public void bounce() {
collision = false;
for (int i = 0; i < size; i++) {
for (int a = 0; a < size; a++) {
float xVelDiff = dots[i][2] - dots[a][2];
float yVelDiff = dots[i][3] - dots[a][3];
float xDist = dots[i][0] - dots[a][0];
float yDist = dots[i][1] - dots[a][1];
if (xVelDiff * xDist + yVelDiff * yDist <= 0) {
angle = (float) -Math.atan2(dots[a][0] - dots[i][0], dots[a][1] - dots[i][1]);
float m1 = dots[i][7];
BigDecimal bd = new BigDecimal(m1).setScale(2, RoundingMode.HALF_UP);
m1 = bd.floatValue();
float m2 = dots[a][7];
float[] u1 = rotate(dots[i][2], dots[i][3], (float) angle);
float[] u2 = rotate(dots[a][2], dots[a][3], (float) angle);
float[] v1 = new float[2];
v1[0] = u1[0] * (m1 - m2) / (m1 + m2) + u2[0] * 2 * m2 / (m1 + m2);
v1[1] = u1[1];
float[] v2 = new float[2];
v2[0] = u2[0] * (m1 - m2) / (m1 + m2) + u1[0] * 2 * m2 / (m1 + m2);
v2[1] = u2[1];
float[] vFinal1 = rotate(v1[0], v1[1], (float) -angle);
float[] vFinal2 = rotate(v2[0], v2[1], (float) -angle);
if (a != i && !(dots[a][0] == 0 && dots[a][1] == 0)) {
boolean thisCollision = (dots[a][0] - dots[i][0]) * (dots[a][0] - dots[i][0]) + (dots[a][1] - dots[i][1]) * (dots[a][1] - dots[i][1]) <= (dots[a][4] + dots[i][4]) * (dots[a][4] + dots[i][4]);
// System.out.println("collision: "+collision+" i="+i+" a="+a);
if (thisCollision) {
System.out.println(vFinal2[0] + " " + vFinal2[1]);
collision = true;
dots[i][2] = vFinal1[0];
dots[i][3] = vFinal1[1];
dots[a][2] = vFinal2[0];
dots[a][3] = vFinal2[1];
return;
}
}
}
}
}
}
public float[] rotate(float velocityX, float velocityY, float angle) {
float x1 = (float) (velocityX * Math.cos(angle) - velocityY * Math.sin(angle));
float y1 = (float) (velocityX * Math.cos(angle) - velocityY * Math.sin(angle));
float vel[] = new float[2];
vel[0] = x1;
vel[1] = y1;
return vel;
}

circle collision practice, advice?

can anyone tell me what i'm doing wrong? i'm trying to get the circles to bounce off each other but they don't seem to be working.i keep making changes to fix the issue but that only makes more issues, whilst the main issue isn't resolved. have i used the wrong math's algorithm to check for collisions? or is it right and i have just made an error i cant seem to find? any help would be appreciated.
public float[][] CreateDots() {
if (first == true) {
for (int i = 0; i < dotNumber; i++) {
do{
dotX = r.nextInt(300);
dotY = r.nextInt(300);
dotWidth = r.nextFloat() * 50;
dotRadius = dotWidth / 2;
dotMass = r.nextFloat() / 10;
dotCentreX = dotX + dotRadius;
dotCentreY = dotY + dotRadius;
dotVelocityX = r.nextFloat();
dotVelocityY = r.nextFloat();
dots[i][0] = dotX;
dots[i][1] = dotY;
dots[i][2] = dotVelocityX;
dots[i][3] = dotVelocityY;
dots[i][4] = dotRadius;
dots[i][5] = dotCentreX;
dots[i][6] = dotCentreY;
dots[i][7] = dotMass;
dots[i][8] = dotWidth;
}while(collision == true);
}
first = false;
} else {
for (int i = 0; i < dotNumber; i++) {
dots[i][0] = dots[i][0] + dots[i][2];
dots[i][1] = dots[i][1] + dots[i][3];
if (dots[i][0] + dots[i][8] >= wallX) {
dots[i][2] = -dots[i][2];
}
if (dots[i][1] + dots[i][8] >= wallY) {
dots[i][3] = -dots[i][3];
}
if (dots[i][0] < 0) {
dots[i][2] = -dots[i][2];
}
if (dots[i][1] < 0) {
dots[i][3] = -dots[i][3];
}
}
}
repaint();
return dots;
}
public void bounce() {
collisionDot = false;
for (int i = 0; i < dotNumber; i++) {
for (int a = i + 1; a < dotNumber; a++) {
// difference between the x and y velocity of two dots
float xVelDiff = dots[i][2] - dots[a][2];
float yVelDiff = dots[i][3] - dots[a][3];
//difference between the centre x and y of two dots
float xDist = dots[i][5] - dots[a][5];
float yDist = dots[i][6] - dots[a][6];
System.out.println(xVelDiff + " * " + xDist + " + " + yVelDiff + " * " + yDist + " = "+ (xVelDiff * xDist + yVelDiff * yDist));
//not quite sure yet
if (xVelDiff * xDist + yVelDiff * yDist <= 0) {
angleCollision = (float) -Math.atan2(dots[a][0] - dots[i][0], dots[a][1] - dots[i][1]);
float mass = (dots[i][7] + dots[a][7]);
float mass2 = (dots[i][7] - dots[a][7]);
// x and y velocity and angle of collision for the two dots
float[] u1 = rotate(dots[i][2], dots[i][3], (float) angleCollision);
float[] u2 = rotate(dots[a][2], dots[a][3], (float) angleCollision);
//Velocity of dot 1
float[] v1 = new float[2];
v1[0] = u1[0] * mass2 / mass + u2[0] * 2 * dots[a][7] / (mass);
v1[1] = u1[1];
// velocity of dot 2
float[] v2 = new float[2];
v2[0] = u2[0] * mass2 / mass + u1[0] * 2 * dots[a][7] / (mass);
v2[1] = u2[1];
// final velocity of two colliding dots is:
float[] vFinal1 = rotate(v1[0], v1[1], (float) -angleCollision);;
float[] vFinal2 = rotate(v2[0], v2[1], (float) -angleCollision);;
if (a != i && !(dots[a][0] == 0 && dots[a][1] == 0)) {
// if the x and y distance between the two dots centres is less than their radii combined then the dots have collided
boolean thisCollision = Math.pow(xDist, 2) + Math.pow(yDist, 2) <= Math.pow((dots[a][4] + dots[i][4]), 2);
//if the dots collided, create new final velocity's from the angle of collision and the x and y velocitys at collision
if (thisCollision) {
collisionDot = true;
dots[i][2] = vFinal1[0];
dots[i][3] = vFinal1[1];
dots[a][2] = vFinal2[0];
dots[a][3] = vFinal2[1];
return;
}
}
}
}
}
}
public float[] rotate(float velocityX, float velocityY, float angle) {
float x1 = (float) (velocityX * Math.cos(angle) - velocityY * Math.sin(angle));
float y1 = (float) (velocityX * Math.sin(angle) - velocityY * Math.cos(angle));
float vel[] = new float[2];
vel[0] = x1;
vel[1] = y1;
return vel;
}

How can i render triangle after multiply by projection matrix

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.)

Camera rotation bug

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;
}

Categories

Resources