I am trying to have a JavaFX 3D Sphere, textured with a texture of the earth. The texture is this one (from Wikipedia, an equirectangular projection):
The sphere is rendered as follows:
You can clearly see that, at the poles, the texture is not preserving the proportions anymore. I found a bug files on the openJDK system, which I think is related to this behaviour: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8092112
Sadly, in 7 years nobody made the change that the person filing the bug requested. Do you know if there is an alternative way to properly render an equirectangular sphere projection on a JavaFX 3D Sphere?
Just for reference, the code that I using is:
Sphere earthSphere = new Sphere(EARTH_RADIUS, 256);
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(new Image(Main.class.getResourceAsStream("/images/earth2.jpg")));
earthSphere.setMaterial(material);
At the end I implement it myself using a mesh, for the purposes I needed for. Here is the code, in case you are interested (it will end up in a GitHub project anyway):
public static Group createEarthSphere() {
// Use triangular mesh
int latLevels = 90;
int lonLevels = 180;
TriangleMesh mesh = new TriangleMesh(VertexFormat.POINT_NORMAL_TEXCOORD);
double radius = EARTH_RADIUS;
double latIncAngle = (Math.PI/latLevels);
double lonIncAngle = (Math.PI * 2)/lonLevels;
double textLatIncr = 1.0/latLevels;
double textLonIncr = 1.0/lonLevels;
int currentPointOffset = 0;
int currentNormalOffset = 0;
int currentTextOffset = 0;
for(int i = 0; i < latLevels; ++i) {
for(int j = 0; j < lonLevels; ++j) {
// The point list is: top left - bottom left - bottom right - top right
// The faces-normal points are: (0,0) (1,1) (2,2) (0,3) (2,4) (3,5)
Point3D tp1 = new Point3D(0,radius * Math.cos(Math.PI - (i * latIncAngle)), radius * Math.sin(Math.PI - (i * latIncAngle)));
Point3D tp2 = new Point3D(0,radius * Math.cos(Math.PI - (i * latIncAngle + latIncAngle)), radius * Math.sin(Math.PI - (i * latIncAngle + latIncAngle)));
Point3D topLeft = new Rotate(Math.toDegrees(j * lonIncAngle), new Point3D(0, 1, 0)).transform(tp1);
Point3D bottomLeft = new Rotate(Math.toDegrees(j * lonIncAngle), new Point3D(0, 1, 0)).transform(tp2);
Point3D bottomRight = new Rotate(Math.toDegrees(j * lonIncAngle + lonIncAngle), new Point3D(0, 1, 0)).transform(tp2);
Point3D topRight = new Rotate(Math.toDegrees(j * lonIncAngle + lonIncAngle), new Point3D(0, 1, 0)).transform(tp1);
// Compute normals
Point3D topLeftNormal_1 = computeNormal(topLeft, bottomLeft, bottomRight); // 0
Point3D bottomLeftNormal_1 = computeNormal(bottomLeft, bottomRight, topLeft); // 1
Point3D bottomRightNormal_1 = computeNormal(bottomRight, topLeft, bottomLeft); // 2
Point3D topLeftNormal_2 = computeNormal(topLeft, bottomRight, topRight); // 3
Point3D bottomRightNormal_2 = computeNormal(bottomRight, topRight, topLeft); // 4
Point3D topRightNormal_2 = computeNormal(topRight, topLeft, bottomRight); // 5
// Add points
mesh.getPoints().addAll((float) topLeft.getX(), (float) topLeft.getY(), (float) topLeft.getZ()); // 0
mesh.getPoints().addAll((float) bottomLeft.getX(), (float) bottomLeft.getY(), (float) bottomLeft.getZ()); // 1
mesh.getPoints().addAll((float) bottomRight.getX(), (float) bottomRight.getY(), (float) bottomRight.getZ()); // 2
mesh.getPoints().addAll((float) topRight.getX(), (float) topRight.getY(), (float) topRight.getZ()); // 3
// Add normals
mesh.getNormals().addAll((float) topLeftNormal_1.getX(), (float) topLeftNormal_1.getY(), (float) topLeftNormal_1.getZ()); // 0
mesh.getNormals().addAll((float) bottomLeftNormal_1.getX(), (float) bottomLeftNormal_1.getY(), (float) bottomLeftNormal_1.getZ()); // 1
mesh.getNormals().addAll((float) bottomRightNormal_1.getX(), (float) bottomRightNormal_1.getY(), (float) bottomRightNormal_1.getZ()); // 2
mesh.getNormals().addAll((float) topLeftNormal_2.getX(), (float) topLeftNormal_2.getY(), (float) topLeftNormal_2.getZ()); // 3
mesh.getNormals().addAll((float) bottomRightNormal_2.getX(), (float) bottomRightNormal_2.getY(), (float) bottomRightNormal_2.getZ()); // 4
mesh.getNormals().addAll((float) topRightNormal_2.getX(), (float) topRightNormal_2.getY(), (float) topRightNormal_2.getZ()); // 5
// Add texture
float[] p0t = { (float) (i * textLatIncr), 1.0f - (float) (j * textLonIncr) };
float[] p1t = { (float) (i * textLatIncr + textLatIncr), 1.0f - (float) (j * textLonIncr) };
float[] p2t = { (float) (i * textLatIncr + textLatIncr), 1.0f - (float) (j * textLonIncr + textLonIncr) };
float[] p3t = { (float) (i * textLatIncr), 1.0f - (float) (j * textLonIncr + textLonIncr) };
mesh.getTexCoords().addAll(
p0t[1], p0t[0],
p1t[1], p1t[0],
p2t[1], p2t[0],
p3t[1], p3t[0]
);
// Add faces
mesh.getFaces().addAll(
currentPointOffset + 0, currentNormalOffset + 0, currentTextOffset + 0,
currentPointOffset + 2, currentNormalOffset + 2, currentTextOffset + 2,
currentPointOffset + 1, currentNormalOffset + 1, currentTextOffset + 1,
currentPointOffset + 0, currentNormalOffset + 3, currentTextOffset + 0,
currentPointOffset + 3, currentNormalOffset + 5, currentTextOffset + 3,
currentPointOffset + 2, currentNormalOffset + 4, currentTextOffset + 2
);
currentPointOffset += 4;
currentNormalOffset += 6;
currentTextOffset += 4;
}
}
MeshView meshView = new MeshView(mesh);
meshView.setCullFace(CullFace.BACK);
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(new Image(Main.class.getResourceAsStream("/images/earth.jpg")));
meshView.setMaterial(material);
return new Group(meshView);
}
private static Point3D computeNormal(Point3D p1, Point3D p2, Point3D p3) {
return (p3.subtract(p1).normalize()).crossProduct(p2.subtract(p1).normalize()).normalize();
}
The result is:
Now everything is exactly where it should be, and lat/lon are correctly matching the texture.
Related
I`m using Vertex Buffers in JOGL. I have a few hundred thousand triangles. Each triangle contains :
9 floats for the vertices - 3 for each edge
3 floats for the surface normal
3 floats for the colors.
I can`t seem to display the triangles or the colors. I know the normals are being calculated correctly.
This doesn`t work.
gl.glDrawArrays(GL2.GL_TRIANGLES, 0, vertxcnt);
But, the below snippet works - however I don`t see the colors. So, I know the points that are making up the triangles are correct.
gl.glDrawArrays(GL2.GL_POINTS, 0, vertxcnt);
So, if the points and the normals are correctly being calculated, I thinking is I`m going wrong in the render(gl) function. The code for that is below. What am I doing wrong? I cant post SSCCE now due to the complexity, but would like to know if anything is glaringly wrong.
private void render(GL2 gl) {
// VBO
// Enable Pointers
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, VBOVertices[0]); // Set Pointers To Our Data
gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); // Enable Vertex Arrays
gl.glVertexPointer(3, GL.GL_FLOAT, BufferUtil.SIZEOF_FLOAT * 15, 0); //15 = 9 vertices of triangles + 3 normal + 3 colors
gl.glEnableClientState(GL2.GL_NORMAL_ARRAY);
gl.glNormalPointer(GL.GL_FLOAT, BufferUtil.SIZEOF_FLOAT * 15, BufferUtil.SIZEOF_FLOAT * 9);
gl.glEnableClientState(GL2.GL_COLOR_ARRAY);
gl.glColorPointer(3, GL.GL_FLOAT, BufferUtil.SIZEOF_FLOAT * 15, BufferUtil.SIZEOF_FLOAT * 12);
// Render
// Draw All Of The Triangles At Once
gl.glPointSize(4);
gl.glDrawArrays(GL2.GL_POINTS, 0, vertxcnt);
// Disable Pointers
// Disable Vertex, Normals and Color Arrays
gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL2.GL_NORMAL_ARRAY);
gl.glDisableClientState(GL2.GL_COLOR_ARRAY);
}
Here is the init and display functions.
#Override
public void init(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
gl.glClearColor(0.0f, 0.0f, 6.0f, 0.5f);
gl.glClearDepth(1.0f); // Depth Buffer Setup
gl.glDepthFunc(GL.GL_LEQUAL); // The Type Of Depth Testing (Less Or
// Equal)
gl.glEnable(GL.GL_DEPTH_TEST); // Enable Depth Testing
gl.glDepthFunc(GL2.GL_LESS);
gl.glEnable(GL2.GL_LIGHTING);
gl.glEnable(GL2.GL_LIGHT0);
gl.glEnable(GL2.GL_AUTO_NORMAL);
gl.glEnable(GL2.GL_NORMALIZE);
gl.glEnable(GL2.GL_CULL_FACE);
gl.glFrontFace(GL2.GL_CCW);
gl.glCullFace(GL2.GL_BACK);
gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_NICEST);
gl.glShadeModel(GL2.GL_SMOOTH);
buildVBOs(gl);
}
#Override
public void display(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
gl.glClearColor(.0f, .0f, .2f, 0.9f);
gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
glu.gluLookAt(45, 0, 0, 0, 0, 0, 0.0, 1.0, 0.0);
float ma_x = (float) getMax(fx0);
float mi_x = (float) getMin(fx0);
float tr_x = (ma_x + mi_x) / 2;
float ma_y = (float) getMax(fy0);
float mi_y = (float) getMin(fy0);
float tr_y = (ma_y + mi_y) / 2;
float ma_z = (float) getMax(fz0);
float mi_z = (float) getMin(fz0);
float tr_z = (ma_z + mi_z) / 2;
gl.glScalef(scaleFac, scaleFac, scaleFac);
gl.glRotatef(rotFac, 0, 1, 0);
gl.glTranslatef(-tr_x, -tr_y, -tr_z);
for (int i = 0; i < 30; i++) {
render(gl);
gl.glRotatef(12, 0, 0, 1);
}
}
*/
private void createVects(double ang) {
int cnt = fx0.size();
for (int i = 0; i < cnt - 1; i++) {
// Triangle 1 and 2 [Top]
float x0 = (float) (fx0.get(i) * Math.cos(ang) - fy0.get(i) * Math.sin(ang));
float y0 = (float) (fx0.get(i) * Math.sin(ang) + fy0.get(i) * Math.cos(ang));
float z0 = fz0.get(i).floatValue();
Vect3D v0 = new Vect3D(x0, y0, z0);
fvert.add(v0); // 0
float x1 = (float) (fx0.get(i + 1) * Math.cos(ang) - fy0.get(i + 1) * Math.sin(ang));
float y1 = (float) (fx0.get(i + 1) * Math.sin(ang) + fy0.get(i + 1) * Math.cos(ang));
float z1 = fz0.get(i + 1).floatValue();
Vect3D v1 = new Vect3D(x1, y1, z1);
fvert.add(v1);// 1
float x2 = (float) (fx1.get(i + 1) * Math.cos(ang) - fy1.get(i + 1) * Math.sin(ang));
float y2 = (float) (fx1.get(i + 1) * Math.sin(ang) + fy1.get(i + 1) * Math.cos(ang));
float z2 = fz1.get(i + 1).floatValue();
Vect3D v2 = new Vect3D(x2, y2, z2);
fvert.add(v2);// 2
Vect3D n0 = calcNormal(v0, v1, v2);
fnorm.add(n0);
// VBO
vertices.put(x0); //vertices of the triangle
vertices.put(y0);
vertices.put(z0);
vertices.put(x1);
vertices.put(y1);
vertices.put(z1);
vertices.put(x2);
vertices.put(y2);
vertices.put(z2);
vertices.put(n0.x); // normals
vertices.put(n0.y);
vertices.put(n0.z);
vertices.put(0.5f); // colors // for now
vertices.put(0.0f);
vertices.put(0.0f);
}
}
I want to put some Sprites on path. I thought about calculate bezier path and then put sprites on calculated points. But I can't find in java method to do that.
SOLVED
This is my main method to draw line of images
private List<Sprite> drawStarLine(Point start, Point end, Point control1, Point control2, int starsCount,TextureRegion pTexture) {
ArrayList<Sprite> starsForReturn = new ArrayList<Sprite>();
ArrayList<Point> points = new ArrayList<Point>(generateBezierPath(start, end, control1, control2, starsCount));
for (int i = 0; i < points.size(); i++) {
Point p = points.get(i);
Sprite s = new Sprite((float) p.getX(), (float) p.getY(), pTexture, activity.getVertexBufferObjectManager());
s.setScale(mainScaleX / 2);
starsForReturn.add(s);
}
return starsForReturn;
}
This is how I calculate bezier path
private List<Point> generateBezierPath(Point origin, Point destination, Point control1, Point control2, int segments) {
ArrayList<Point> pointsForReturn = new ArrayList<Point>();
float t = 0;
for (int i = 0; i < segments; i++) {
Point p = new Point();
p.setX(Math.pow(1 - t, 3) * origin.x + 3.0f * Math.pow(1 - t, 2) * t * control1.x + 3.0f * (1 - t) * t * t * control2.x + t * t * t * destination.x);
p.setY(Math.pow(1 - t, 3) * origin.y + 3.0f * Math.pow(1 - t, 2) * t * control1.y + 3.0f * (1 - t) * t * t * control2.y + t * t * t * destination.y);
t += 1.0f / segments;
pointsForReturn.add(p);
}
pointsForReturn.add(destination);
return pointsForReturn;
}
Simply calculate the quadratic curve coordinates (for position) and tangent (for orientation). The code is simple.
double[] getPoint(double[] x, double[] y, double t) {
double mt = 1-t;
return new double[]{
x[0]*mt*mt + 2*x[1]*mt*t + x[2]*t*t,
y[0]*mt*mt + 2*y[1]*mt*t + y[2]*t*t
};
}
double[] getTangent(double[] x, double[] y, double t) {
double mt = t-1; // note: NOT 1-t
return new double[]{
2*(x[0]*mt - x[1]*(2*t-1) + x[2]*t),
2*(y[0]*mt - y[1]*(2*t-1) + y[2]*t)
};
}
Place your "thing" at the coordinate you get from getPoint, and then if you need it to follow your path, rotate your thing so that its axis of travel lines up with the tangent you get out of getTangent. Done.
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.
(Question is at bottom)Im learning opengl(using lwjgl) and done some drawing of flat shape through sending buffers. Now I need to draw many spheres in single buffer. In my last question, I was advised to use geometry instancing but I dont know how to use any shader language in java yet so I'm trying to make multiple objects in single buffer just like in the examples.
What I tried to generate two spheres by QUAD_STRIP style(using lwjgl's own GLU.Sphere() function to fill the buffers):
n=c1*(c2+1);
float rr=(float) Math.random();
float gg=(float) Math.random();
float bb=(float) Math.random();
float aa=(float) Math.random();
positions = new float[c1 * (c2+1) * 3*2 *2];
normals = new float[c1 * (c2+1) * 3*2 *2];
colors = new float[c1 * (c2+1) * 4*2 *2];
int counter=0;
float drho = 3.141593F / 32.0f;
float dtheta = 6.283186F / 32.0f;
float ds = 1.0F / 32.0f;
float dt = 1.0F / 32.0f;
float t = 1.0F;
/*first sphere*/
for (int i = 0; i < 32; i++) {
float rho = i * drho;
float s = 0.0F;
for (int j = 0; j <= 32; j++) {
float theta = j == 32 ? 0.0F : j * dtheta;
float x = (float) (-Math.sin(theta) * Math.sin(rho));
float y = (float) (Math.cos(theta) * Math.sin(rho));
float z = (float) (1.0f * Math.cos(rho));
normals[counter*3+0]=x*1.0f;normals[counter*3+1]=y*1.0f;normalscounter*3+2]=z*1.0f;
colors[counter*4+0]=rr;colors[counter*4+1]=gg;colors[counter*4+2]=bb;colors[counter*4+3]=1.0f/*aa*/;
positions[counter*3+0]=x*r;positions[counter*3+1]=y*r;positions[counter*3+2]=z*r;
counter++;
x = (float) (-Math.sin(theta) * Math.sin(rho + drho));
y = (float) (Math.cos(theta) * Math.sin(rho + drho));
z = (float) (1.0f * Math.cos(rho + drho));
normals[counter*3+0]=x*1.0f;normals[counter*3+1]=y*1.0f;normals[counter*3+2]=z*1.0f;
colors[counter*4+0]=rr;colors[counter*4+1]=gg;colors[counter*4+2]=bb;colors[counter*4+3]=1.0f/*aa*/;
positions[counter*3+0]=x*r;positions[counter*3+1]=y*r;positions[counter*3+2]=z*r;
counter++;
s += ds;
}
t -= dt;
}
/* first sphere end */
/* second sphere generation */
{
drho = 3.141593F / 32.0f;
dtheta = 6.283186F / 32.0f;
ds = 1.0F / 32.0f;
dt = 1.0F / 32.0f;
t = 1.0F;
for (int i = 0; i < 32; i++) {
float rho = i * drho;
float s = 0.0F;
for (int j = 0; j <= 32; j++) {
float theta = j == 32 ? 0.0F : j * dtheta;
float x = (float) (-Math.sin(theta) * Math.sin(rho));
float y = (float) (Math.cos(theta) * Math.sin(rho));
float z = (float) (1.0f * Math.cos(rho));
normals[counter*3+0]=x*1.0f;normals[counter*3+1]=y*1.0f;normals[counter*3+2]=z*1.0f;
colors[counter*4+0]=rr;colors[counter*4+1]=gg;colors[counter*4+2]=bb;colors[counter*4+3]=1.0f/*aa*/;
positions[counter*3+0]=x*r+1.0f;positions[counter*3+1]=y*r+1.0f;positions[counter*3+2]=z*r+1.0f;
counter++;
x = (float) (-Math.sin(theta) * Math.sin(rho + drho));
y = (float) (Math.cos(theta) * Math.sin(rho + drho));
z = (float) (1.0f * Math.cos(rho + drho));
normals[counter*3+0]=x*1.0f;normals[counter*3+1]=y*1.0f;normals[counter*3+2]=z*1.0f;
colors[counter*4+0]=rr;colors[counter*4+1]=gg;colors[counter*4+2]=bb;colors[counter*4+3]=1.0f/*aa*/;
positions[counter*3+0]=x*r+1.0f;positions[counter*3+1]=y*r+1.0f;positions[counter*3+2]=z*r+1.0f;
counter++;
s += ds;
}
t -= dt;
}
}
/*second sphere end*/
positionsBuf=BufferUtils.createFloatBuffer(c1 * (c2+1) * 3*2 *2);
positionsBuf.put(positions);
positionsBuf.rewind();
colorsBuf=BufferUtils.createFloatBuffer(c1 * (c2+1) * 4*2 *2);
colorsBuf.put(colors);
colorsBuf.rewind();
normalsBuf=BufferUtils.createFloatBuffer(c1 * (c2+1) * 3*2 *2);
normalsBuf.put(normals);
normalsBuf.rewind();
As you can see, below image shows how two spheres are drawn. There is an unwanted link between two.
Most probably the rope is caused by the last point of first sphere and first point of second sphere. Is there some kind of delimiter/drawing-hint to separate two drawings in the same buffer?
Here is how they are drawn:
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,sphereBufferCol.get(0));
GL11.glColorPointer(4, GL11.GL_FLOAT, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, sphereBufferPos.get(0));
GL11.glVertexPointer(3, GL11.GL_FLOAT, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, sphereBufferNormal.get(0));
GL11.glNormalPointer(GL11.GL_FLOAT, 0, 0);
GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GL11.glEnableClientState(GL11.GL_COLOR_ARRAY);
GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);
//Each sphere is generated 32 by 32 quadstriparray and each having two sets of two points and there are two spheres
GL11.glDrawArrays(GL11.GL_QUAD_STRIP, 0, 32*33*2 *2);
GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);
GL11.glDisableClientState(GL11.GL_NORMAL_ARRAY);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
Question: How can I make that rope-like thing disappear without decreasing performance? Maybe putting zero to last and first points' alpha value can make it invisible but wouldnt that cause two holes on each sphere and decrease performance because of many lines on the screen?
All vertex values will be altered by opencl interoperability so single drawing call is needed to draw whole 10000+ spheres.
There seem to be a number of options:
use multiple buffers
use quads instead of quad strips
use primitive restart
use degenerate triangles, i.e. add the last vertex twice
use [instanced rendering] (http://www.opengl.org/wiki/Vertex_Rendering#Instancing)
If you are on newer hardware and want to use quad strips, I'd prefer using primitive restart.
Please note that this is just the result of a quick assessment and anchecked (I personally don't use quad strips or even tri strips that often ;) ).
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)