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.)
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);
}
}
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;
}
I didn't find any examples in openCV to convert a flat image to cylindrical in java, I want it to render the image in 2d not 3d, also didn’t find any example code or book on it. Below is the image of pictures which I want to warp around a cup.
A good book and example code will be much appreciated.
This i have done so far. suggested my #Amitay to make image concave, using this example Wrap image around cylinder but stuck on convertion.
import java.io.File;
import org.bytedeco.javacpp.indexer.UByteBufferIndexer;
import org.bytedeco.javacpp.opencv_core.Mat;
import org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_highgui.imshow;
import static org.bytedeco.javacpp.opencv_highgui.waitKey;
import static org.bytedeco.javacpp.opencv_imgcodecs.CV_LOAD_IMAGE_COLOR;
import static org.bytedeco.javacpp.opencv_imgcodecs.imread;
/**
*
* #author BTACTC
*/
public class CupWrapping {
Mat image;
Mat dstImage;
int width;
int height;
public CupWrapping(File imageFile) {
image = imread(imageFile.getAbsolutePath(), CV_LOAD_IMAGE_COLOR);
width = image.size().width();
height = image.size().height();
dstImage = new Mat(width, height, image.type());
UByteBufferIndexer sI = image.createIndexer();
UByteBufferIndexer sD = dstImage.createIndexer();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Point2f current_pos = new Point2f(x, y);
current_pos = convert_pt(current_pos, width, height);
Point top_left = new Point((int) current_pos.x(), (int) current_pos.y()); //top left because of integer rounding
//make sure the point is actually inside the original image
if (top_left.x() < 0
|| top_left.x() > width - 2
|| top_left.y() < 0
|| top_left.y() > height - 2) {
continue;
}
//bilinear interpolation
float dx = current_pos.x() - top_left.x();
float dy = current_pos.y() - top_left.y();
float weight_tl = (float) ((1.0 - dx) * (1.0 - dy));
float weight_tr = (float) ((dx) * (1.0 - dy));
float weight_bl = (float) ((1.0 - dx) * (dy));
float weight_br = (dx) * (dy);
byte value = (byte) (weight_tl * sI.get(top_left.y(), top_left.x())
+ weight_tr * sI.get(top_left.y(), top_left.x() + 1)
+ weight_bl * sI.get(top_left.y() + 1, top_left.x())
+ weight_br * sI.get(top_left.y() + 1, top_left.x() + 1));
sD.put(y, x,value);
}
}
imshow("", dstImage);
waitKey(0);
}
public Point2f convert_pt(Point2f point, int w, int h) {
//center the point at 0,0
Point2f pc = new Point2f(point.x() - w / 2, point.y() - h / 2);
//these are your free parameters
float f = w;
float r = w;
float omega = w / 2;
float z0 = (float) (f - Math.sqrt(r * r - omega * omega));
float zc = (float) ((2 * z0 - Math.sqrt(4 * z0 * z0 - 4 * (pc.x() * pc.x() / (f * f) + 1) * (z0 * z0 - r * r))) / (2 * (pc.x() * pc.x() / (f * f) + 1)));
Point2f final_point = new Point2f(pc.x() * zc / f, pc.y() * zc / f);
final_point.x() = final_point.x() + w / 2;
final_point.y() += h / 2;
return final_point;
}
public static void main(String[] args) {
File imageFile = new File("image/C13.jpg");
CupWrapping wrap = new CupWrapping(imageFile);
}
}
Look at this answer in Warp Image to Appear in Cylindrical Projection
You only need to change two things:
Because you want convex projection and not concave you need to change the line of code in the function convert_pt.
From:
float zc = (2*z0+sqrt(4*z0*z0-4*(pc.x*pc.x/(f*f)+1)*(z0*z0-r*r)))/(2* (pc.x*pc.x/(f*f)+1));
To
float zc = (2*z0-sqrt(4*z0*z0-4*(pc.x*pc.x/(f*f)+1)*(z0*z0-r*r)))/(2* (pc.x*pc.x/(f*f)+1));
Convert the all the rest of the code form c++ to java.
Good luck
I'm trying to draw a circle in LWJGL, but when I draw I try to draw it, it makes a shape that's more like an oval rather than a circle. Also, when I change my circleVertexCount 350+, the shape like flips out. I'm really not sure how the code works that creates the vertices(I have taken Geometry and I know the basic trig ratios). I haven't really found that good of tutorials on creating circles. Here's my code:
public class Circles {
// Setup variables
private int WIDTH = 800;
private int HEIGHT = 600;
private String title = "Circle";
private float fXOffset;
private int vbo = 0;
private int vao = 0;
int circleVertexCount = 300;
float[] vertexData = new float[(circleVertexCount + 1) * 4];
public Circles() {
setupOpenGL();
setupQuad();
while (!Display.isCloseRequested()) {
loop();
adjustVertexData();
Display.update();
Display.sync(60);
}
Display.destroy();
}
public void setupOpenGL() {
try {
Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
Display.setTitle(title);
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(-1);
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}
public void setupQuad() {
float r = 0.1f;
float x;
float y;
float offSetX = 0f;
float offSetY = 0f;
double theta = 2.0 * Math.PI;
vertexData[0] = (float) Math.sin(theta / circleVertexCount) * r + offSetX;
vertexData[1] = (float) Math.cos(theta / circleVertexCount) * r + offSetY;
for (int i = 2; i < 400; i += 2) {
double angle = theta * i / circleVertexCount;
x = (float) Math.cos(angle) * r;
vertexData[i] = x + offSetX;
}
for (int i = 3; i < 404; i += 2) {
double angle = Math.PI * 2 * i / circleVertexCount;
y = (float) Math.sin(angle) * r;
vertexData[i] = y + offSetY;
}
FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(vertexData.length);
vertexBuffer.put(vertexData);
vertexBuffer.flip();
vao = glGenVertexArrays();
glBindVertexArray(vao);
vbo = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER,vertexBuffer, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
public void loop() {
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glDrawArrays(GL_TRIANGLE_FAN, 0, vertexData.length / 2);
glDisableVertexAttribArray(0);
glBindVertexArray(0);
}
public static void main(String[] args) {
new Circles();
}
private void adjustVertexData() {
float newData[] = new float[vertexData.length];
System.arraycopy(vertexData, 0, newData, 0, vertexData.length);
if(Keyboard.isKeyDown(Keyboard.KEY_W)) {
fXOffset += 0.05f;
} else if(Keyboard.isKeyDown(Keyboard.KEY_S)) {
fXOffset -= 0.05f;
}
for(int i = 0; i < vertexData.length; i += 2) {
newData[i] += fXOffset;
}
FloatBuffer newDataBuffer = BufferUtils.createFloatBuffer(newData.length);
newDataBuffer.put(newData);
newDataBuffer.flip();
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, newDataBuffer);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
300 Vertex Count(This is my main problem)
400 Vertex Count - I removed this image, it's bugged out, should be a tiny sliver cut out from the right, like a secant
500 Vertex Count
Each 100, it removes more and more of the circle, and so on.
One of your problems is this:
for (int i = 2; i < 400; i += 2) {
double angle = theta * i / circleVertexCount;
x = (float) Math.cos(angle) * r;
vertexData[i] = x + offSetX;
}
for (int i = 3; i < 404; i += 2) {
double angle = Math.PI * 2 * i / circleVertexCount;
y = (float) Math.sin(angle) * r;
vertexData[i] = y + offSetY;
}
You are using a different value for angle for the x and y position of each vertex.
You could try this instead:
for (int i = 0; i <= circleVertexCount; i++) {
double angle = i * theta / circleVertexCount;
x = (float) Math.cos(angle) * r;
y = (float) Math.sin(angle) * r;
vertexData[i * 2] = x + offSetX;
vertexData[i * 2 + 1] = y + offSetY;
}
The reason part of your circle was being cut out at higher vertex counts was the i < 400 in your for loops, so I have changed it to i <= circleVertexCount.
Another problem is that your window is not square, and you are not using a shader (or the deprecated built in matrices) to correct this. This means that one unit up looks a different length than one unit right, resulting in an oval instead of a circle. To fix this you could multiply your vertex x position by your display height divided by your display width, preferably in a shader.
I'm not good in math.
I have 2 points, A(x1, y1) and B(x2, y2) in 2D.
I need to create a virtual path from point A to B curved at R(radius), and then return an array of points which are describing this curved path, not all maybe every D(distance) from each other.
In Java I need a method like this:
private ArrayList<PointF> generateCurve(PointF pFrom,PointF pTo,float pRadius,float pMinDistance){
ArrayList<PointF> pOutPut = new ArrayList<PointF>();
// ...generate result to pOutPut
return pOutPut;
}
How to do this ?
I didn't gave up and I've been working on it for a few more hours. And here is the result:
I created a method where you can specify if you want the shortest of the longest arc between the points.
Here are some calls to it, with the produced output:
generateCurve(pFrom, pTo, 100f, 7f, false, false);
generateCurve(pFrom, pTo, 100f, 7f, true, false);
generateCurve(pFrom, pTo, 100f, 7f, false, true);
generateCurve(pFrom, pTo, 100f, 7f, true, true);
As you can see, it is working like a charm. Here is the code:
package curve;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
/**
*
* #author martijn
*/
public class Main
{
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException
{
PointF pFrom = new PointF(-10f, 30.0f);
PointF pTo = new PointF(-100f, 0.0f);
List<PointF> points = generateCurve(pFrom, pTo, 100f, 7f, true, true);
System.out.println(points);
// Calculate the bounds of the curve
Rectangle2D.Float bounds = new Rectangle2D.Float(points.get(0).x, points.get(0).y, 0, 0);
for (int i = 1; i < points.size(); ++i) {
bounds.add(points.get(i).x, points.get(i).y);
}
bounds.add(pFrom.x, pFrom.y);
bounds.add(pTo.x, pTo.y);
BufferedImage img = new BufferedImage((int) (bounds.width - bounds.x + 50), (int) (bounds.height - bounds.y + 50), BufferedImage.TYPE_4BYTE_ABGR_PRE);
Graphics2D g = img.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.translate(25.0f - bounds.getX(), 25.0f - bounds.getY());
g.setStroke(new BasicStroke(1.0f));
g.setColor(Color.DARK_GRAY);
g.drawLine(-1000, 0, 1000, 0);
g.drawLine(0, -1000, 0, 1000);
g.setColor(Color.RED);
for (int i = 0; i < points.size(); ++i) {
if (i > 0) {
Line2D.Float f = new Line2D.Float(points.get(i - 1).x, points.get(i - 1).y, points.get(i).x, points.get(i).y);
System.out.println("Dist : " + f.getP1().distance(f.getP2()));
// g.draw(f);
}
g.fill(new Ellipse2D.Float(points.get(i).x - 0.8f, points.get(i).y - 0.8f, 1.6f, 1.6f));
}
g.setColor(Color.BLUE);
g.fill(new Ellipse2D.Float(pFrom.x - 1, pFrom.y - 1, 3, 3));
g.fill(new Ellipse2D.Float(pTo.x - 1, pTo.y - 1, 3, 3));
g.dispose();
ImageIO.write(img, "PNG", new File("result.png"));
}
static class PointF
{
public float x, y;
public PointF(float x, float y)
{
this.x = x;
this.y = y;
}
#Override
public String toString()
{
return "(" + x + "," + y + ")";
}
}
private static List<PointF> generateCurve(PointF pFrom, PointF pTo, float pRadius, float pMinDistance, boolean shortest, boolean side)
{
List<PointF> pOutPut = new ArrayList<PointF>();
// Calculate the middle of the two given points.
PointF mPoint = new PointF(pFrom.x + pTo.x, pFrom.y + pTo.y);
mPoint.x /= 2.0f;
mPoint.y /= 2.0f;
System.out.println("Middle Between From and To = " + mPoint);
// Calculate the distance between the two points
float xDiff = pTo.x - pFrom.x;
float yDiff = pTo.y - pFrom.y;
float distance = (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff);
System.out.println("Distance between From and To = " + distance);
if (pRadius * 2.0f < distance) {
throw new IllegalArgumentException("The radius is too small! The given points wont fall on the circle.");
}
// Calculate the middle of the expected curve.
float factor = (float) Math.sqrt((pRadius * pRadius) / ((pTo.x - pFrom.x) * (pTo.x - pFrom.x) + (pTo.y - pFrom.y) * (pTo.y - pFrom.y)) - 0.25f);
PointF circleMiddlePoint = new PointF(0, 0);
if (side) {
circleMiddlePoint.x = 0.5f * (pFrom.x + pTo.x) + factor * (pTo.y - pFrom.y);
circleMiddlePoint.y = 0.5f * (pFrom.y + pTo.y) + factor * (pFrom.x - pTo.x);
} else {
circleMiddlePoint.x = 0.5f * (pFrom.x + pTo.x) - factor * (pTo.y - pFrom.y);
circleMiddlePoint.y = 0.5f * (pFrom.y + pTo.y) - factor * (pFrom.x - pTo.x);
}
System.out.println("Middle = " + circleMiddlePoint);
// Calculate the two reference angles
float angle1 = (float) Math.atan2(pFrom.y - circleMiddlePoint.y, pFrom.x - circleMiddlePoint.x);
float angle2 = (float) Math.atan2(pTo.y - circleMiddlePoint.y, pTo.x - circleMiddlePoint.x);
// Calculate the step.
float step = pMinDistance / pRadius;
System.out.println("Step = " + step);
// Swap them if needed
if (angle1 > angle2) {
float temp = angle1;
angle1 = angle2;
angle2 = temp;
}
boolean flipped = false;
if (!shortest) {
if (angle2 - angle1 < Math.PI) {
float temp = angle1;
angle1 = angle2;
angle2 = temp;
angle2 += Math.PI * 2.0f;
flipped = true;
}
}
for (float f = angle1; f < angle2; f += step) {
PointF p = new PointF((float) Math.cos(f) * pRadius + circleMiddlePoint.x, (float) Math.sin(f) * pRadius + circleMiddlePoint.y);
pOutPut.add(p);
}
if (flipped ^ side) {
pOutPut.add(pFrom);
} else {
pOutPut.add(pTo);
}
return pOutPut;
}
}
Enjoy!
PS: I created two questions on Mathematics to solve your question:
Analytic Geometry: Point coordinates, same distance from two points.
Trigonometry: Solve (1−cosα)2+sin2α=d2 for α
This works:
private static double GetAngle(Point2D x, Point2D o, double R){
double cosa = (x.getX()-o.getX())/R;
double sina = (x.getY()-o.getY())/R;
double angle = Math.acos(cosa);
return Math.sin(angle)*sina >= 0 ? angle : 2*Math.PI - angle;
}
private static ArrayList<Point2D> generateCurve(Point2D pFrom,Point2D pTo,float pRadius,float pMinDistance){
ArrayList<Point2D> pOutPut = new ArrayList<Point2D>();
double dist = pFrom.distance(pTo);
double h = Math.sqrt(pRadius * pRadius - (dist * dist / 4.0));
double angleStep = pMinDistance/pRadius;
if(2*pRadius <= dist)
throw new Error("Radius is too small");
//find center
double x1 = pFrom.getX(), x2 = pFrom.getY();
double y1 = pTo.getX(), y2 = pTo.getY();
double m1 = (x1+y1)/2, m2 = (x2+y2)/2;
double u1 = - (y2-x2)/dist, u2 = (y1-x1)/dist;
double o1 = m1 + h * u1, o2 = m2 + h * u2;
Point2D o = new Point2D.Double(o1, o2);
double startAngle = GetAngle(pFrom, o, pRadius);
double endAngle = GetAngle(pTo, o, pRadius);
if(endAngle < startAngle)
endAngle += 2 * Math.PI;
for(double a = startAngle; a < endAngle; a+=angleStep){
pOutPut.add(new Point2D.Double(o1+pRadius*Math.cos(a), o2+pRadius*Math.sin(a)));
}
pOutPut.add(pTo);
return pOutPut;
}
Here is what I get when I call it like this: generateCurve(new Point2D.Double(10,10), new Point2D.Double(400, 400), 300, 15)