Ok, i am a self taught programmer, and i am trying to use lwjgl and slik-utils to make a library to provide tools to make games. i have been trying to make a spritesheet, and i am using glTexCoord() to try to get only a part of the image. But to my best efforts, i thas not worked. here is the draw code.
public SpriteSheet draw(int x, int y, Point2D p)
{
GL11.glPushMatrix();
float x1 = p.posX * size + (size / 2F);
float y1 = p.posY * size + (size / 2F);
float d = 1F / texture.getImageHeight();
int i = size / 2;
//Texture centers for coords
float x2 = x1 * d;
float y2 = y1 * d;
float d1 = i * d;
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture.getTextureID());
GL11.glBegin(GL11.GL_QUADS);
GL11.glTexCoord2f(x2 - d1, y2 - d1);
//GL11.glTexCoord2f(0, 0);
GL2D.vertex(x - i, y - i);
GL11.glTexCoord2f(x2 - d1, y2 + d1);
//GL11.glTexCoord2f(0, 1);
GL2D.vertex(x - i, y + i);
GL11.glTexCoord2f(x2 + d1, y2 + d1);
//GL11.glTexCoord2f(1, 1);
GL2D.vertex(x + i, x + i);
GL11.glTexCoord2f(x2 + d1, y2 - d1);
//GL11.glTexCoord2f(1, 0);
GL2D.vertex(x - i, y + i);
GL11.glEnd();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GL11.glPopMatrix();
//TexturedQuad2D t = new TexturedQuad2D(32, 32, Color.black, texture);
//t.draw(x, y);
return this;
}
Yes, You are doing it wrong :)
your x and y are your world space coordinates and not your texture coordinate space, imagine your x and y being on coordinates 700, 800 and your texture size is 512. 700 / 512 = 1.4, texture coordinate go from 0 to 1.
so as first step try to set your texture coordinates with 0 and 1.
GL11.glTexCoord2f(0, 0);
GL11.glTexCoord2f(0, 1);
GL11.glTexCoord2f(1, 1);
GL11.glTexCoord2f(1, 0);
now as second step start to figure out how to calculate a portion of your texture coordinates
so if we have a sprite with 2 frames we go from 0 to 0.5 for first frame and 0.5 to 1 for second
//first frame would be
GL11.glTexCoord2f(0, 0);
GL11.glTexCoord2f(0, 1);
GL11.glTexCoord2f(0.5, 1);
GL11.glTexCoord2f(0.5, 0);
//second frame would be
GL11.glTexCoord2f(0.5, 0);
GL11.glTexCoord2f(0.5, 1);
GL11.glTexCoord2f(1, 1);
GL11.glTexCoord2f(1, 0);
Now as third step write a sprite class to calculate this coordinates for you!
ps. on second notice are you enabling textures for OpenGL anywhere GL11.glEnable(GL11.GL_TEXTURE_2D); to get any texture at all?
Related
I am working on an Android application using OpenGL.
In a database, I store the rotation of objects using local Euler rotation, x, y, then z, but in the editor, I would like to apply a global rotation by the x, y or z global axis. I took two approaches, outlined below.
I've simplified these methods to remove irrelevant Android code.
I've tried taking the matrix approach, but the object appears to rotate in an axis not aligned with the global x, y or z after calling the method a second time. I've read somewhere that the floating point error builds up over time making the rotation matrix "numerically unstable", which I assume is what's happening in the first method.
// rotAxis = 0 means rotation around the X global axis
// rotAxis = 1 means rotation around the Y global axis
// rotAxis = 2 means rotation around the Z global axis
public void executeRotationWithMatrix(float rotAngle, int rotAxis){
float[] rotationMatrix = new float[16];
// Matrix class is in android.opengl
Matrix.setIdentityM(rotationMatrix, 0);
switch (rotAxis){
case 0:
Matrix.rotateM(rotationMatrix, 0, rotAngle, 1.f, 0.f, 0.f);
break;
case 1:
Matrix.rotateM(rotationMatrix, 0, rotAngle, 0.f, 1.f, 0.f);
break;
case 2:
Matrix.rotateM(rotationMatrix, 0, rotAngle, 0.f, 0.f, 1.f);
break;
}
float rotx = getLocalRotationOfObjectOnX(); // Pseudocode
float roty = getLocalRotationOfObjectOnY(); // Pseudocode
float rotz = getLocalRotationOfObjectOnZ(); // Pseudocode
Matrix.rotateM(rotationMatrix, 0, rotx, 1.f, 0.f, 0.f);
Matrix.rotateM(rotationMatrix, 0, roty, 0.f, 1.f, 0.f);
Matrix.rotateM(rotationMatrix, 0, rotz, 0.f, 0.f, 1.f);
Vector3f rotationVector = rotationMatrixToEulerAngles(rotationMatrix);
saveLocalRotationOfObjectOnX(rotationVector.x); // Pseudocode
saveLocalRotationOfObjectOnY(rotationVector.y); // Pseudocode
saveLocalRotationOfObjectOnZ(rotationVector.z); // Pseudocode
}
In the second method, I tried to take the rotation quaternion approach by applying the rotations, but I get even weirder results whenever I try to use this method.
// rotAxis = 0 means rotation around the X global axis
// rotAxis = 1 means rotation around the Y global axis
// rotAxis = 2 means rotation around the Z global axis
public void executeRotationWithQuat(float rotAngle, int rotAxisInd){
Quat4f rotationQuat = new Quat4f(0, 0, 0, 1);
Quat4f tempQuat = new Quat4f(0, 0, 0, 1);
switch (rotAxisInd){
case 0:
QuaternionUtil.setRotation(tempQuat, new Vector3f(1, 0, 0), rotAngle);
break;
case 1:
QuaternionUtil.setRotation(tempQuat, new Vector3f(0, 1, 0), rotAngle);
break;
case 2:
QuaternionUtil.setRotation(tempQuat, new Vector3f(0, 0, 1), rotAngle);
break;
}
tempQuat.normalize();
rotationQuat.mul(tempQuat);
rotationQuat.normalize();
float rotx = getLocalRotationOfObjectOnX(); // Pseudocode
float roty = getLocalRotationOfObjectOnY(); // Pseudocode
float rotz = getLocalRotationOfObjectOnZ(); // Pseudocode
QuaternionUtil.setRotation(tempQuat, new Vector3f(1, 0, 0), rotx); tempQuat.normalize();
rotationQuat.mul(tempQuat);
rotationQuat.normalize();
QuaternionUtil.setRotation(tempQuat, new Vector3f(0, 1, 0), roty); tempQuat.normalize();
rotationQuat.mul(tempQuat);
rotationQuat.normalize();
QuaternionUtil.setRotation(tempQuat, new Vector3f(0, 0, 1), rotz); tempQuat.normalize();
rotationQuat.mul(tempQuat);
rotationQuat.normalize();
float qw = rotationQuat.w;
float qx = rotationQuat.x;
float qy = rotationQuat.y;
float qz = rotationQuat.z;
float[] rotationMatrix = new float[]{
1.0f - 2.0f*qy*qy - 2.0f*qz*qz, 2.0f*qx*qy - 2.0f*qz*qw, 2.0f*qx*qz + 2.0f*qy*qw, 0.0f,
2.0f*qx*qy + 2.0f*qz*qw, 1.0f - 2.0f*qx*qx - 2.0f*qz*qz, 2.0f*qy*qz - 2.0f*qx*qw, 0.0f,
2.0f*qx*qz - 2.0f*qy*qw, 2.0f*qy*qz + 2.0f*qx*qw, 1.0f - 2.0f*qx*qx - 2.0f*qy*qy, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
Vector3f rotationVector = rotationMatrixToEulerAngles(rotationMatrix);
saveLocalRotationOfObjectOnX(rotationVector.x); // Pseudocode
saveLocalRotationOfObjectOnY(rotationVector.y); // Pseudocode
saveLocalRotationOfObjectOnZ(rotationVector.z); // Pseudocode
}
The following are helper methods used in the above two methods.
public Vector3f rotationMatrixToEulerAngles(float[] m){
float sy = (float)Math.sqrt(m[6]*m[6] + m[10]*m[10]);
float x, y, z;
x = (float)Math.atan2(m[6], m[10]);
y = (float)Math.atan2(-m[2], sy);
z = (float)Math.atan2(m[1], m[0]);
//convert angles from radians to degrees
float conFactor = (float)(180/Math.PI);
x *= conFactor;
y *= conFactor;
z *= conFactor;
return new Vector3f(x, y, z);
}
public class QuaternionUtil {
public static void setRotation(Quat4f q, Vector3f axis, float angle) {
float d = axis.length();
assert (d != 0f);
float s = (float)Math.sin(angle * 0.5f) / d;
q.set(axis.x * s, axis.y * s, axis.z * s, (float) Math.cos(angle * 0.5f));
}
}
public class Vector3f{
public final float length() {
return (float)Math.sqrt((double)(this.x * this.x + this.y * this.y + this.z * this.z));
}
}
Any help would be greatly appreciated!
When I draw a texture with transparency(in file) over ShapeRenderer any shape isn't being updating. When I set batch.setColor(1f, 1f, 1f, 0.5f) result is almost the same: I see stuck shapes with 50% transparency and also see the same animated shapes underneath.
I've tried to use Gdx.gl.glEnable(GL20.GL_BLEND) but it didn't help.
shape.begin(ShapeRenderer.ShapeType.Filled);
shape.setColor(0f / 255f, 7f / 255f, 32f / 255f, 1f);
shape.rect(0,0, width, height);
for(Star star : stars) {
star.render(shape);
star.update(dx, dy, delta);
}
shape.end();
batch.begin();
batch.draw(overlay, 0, 0, width, height);
app.batch.end();
render method inside the Star class:
public void render(ShapeRenderer shape) {
r = (position.z / max_depth);
g = (position.z / max_depth);
b = (position.z / max_depth);
a = 1.f;
if(r < 0) r = 0;
if(g < 7f / 255f) g = 7f / 255f;
if (b < 32f / 255f) b = 32f / 255f
float radius = (position.z / max_depth) * maxRadius;
if(radius < 1) radius = 1;
shape.setColor(r, g, b, a);
shape.circle(position.x, position.y, radius);
}
You need to set BlendFunction to batch according to your desired blend output. By default it is enabled with GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA
so try to change to :
batch.setBlendFunction(GL20.GL_SRC_ALPHA,GL20.GL_DST_COLOR);
May be this not fit your requirement so choose appropriate one, according to your requirement.
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 have written an implementation of Bresenham's circle drawing algorithm. This algorithms takes advantage of the highly symmetrical properties of a circle (it only computes points from the 1st octant and draws the other points by taking advantage of symmetry). Therefore I was expecting it to be very fast. The Graphics programming black book, chapter #35 was titled "Bresenham is fast, and fast is good", and though it was about the line drawing algorithm, I could reasonably expect the circle drawing algorithm to also be fast (since the principle is the same).
Here is my java, swing implementation
public static void drawBresenhamsCircle(int r, double width, double height, Graphics g) {
int x,y,d;
y = r;
x = 0;
drawPoint(x, y, width, height,g);
d = (3-2*(int)r);
while (x <= y) {
if (d <= 0) {
d = d + (4*x + 6);
} else {
d = d + 4*(x-y) + 10;
y--;
}
x++;
drawPoint(x, y, width, height,g);
drawPoint(-x, y, width, height,g);
drawPoint(x, -y, width, height,g);
drawPoint(-x, -y, width, height,g);
drawPoint(y, x, width, height,g);
drawPoint(-y, x, width, height,g);
drawPoint(y, -x, width, height,g);
drawPoint(-y, -x, width, height,g);
}
}
This method uses the following drawPointmethod:
public static void drawPoint(double x, double y,double width,double height, Graphics g) {
double nativeX = getNativeX(x, width);
double nativeY = getNativeY(y, height);
g.fillRect((int)nativeX, (int)nativeY, 1, 1);
}
The two methods getNativeX and getNativeY are used to switch coordinates from originating in the upper left corner of the screen to a system that has it origin in the center of the panel with a more classic axis orientation.
public static double getNativeX(double newX, double width) {
return newX + (width/2);
}
public static double getNativeY(double newY, double height) {
return (height/2) - newY;
}
I have also created an implementation of a circle drawing algorithm based on trigonometrical formulaes (x=R*Math.cos(angle)and y= R*Math.sin(angle)) and a third implementation using a call to the standard drawArc method (available on the Graphics object). These additional implementations are for the sole purpose of comparing Bresenham's algorithm to them.
I then created methods to draw a bunch of circles in order to be able to get good measures of the spent time. Here is the method I use to draw a bunch of circles using Bresenham's algorithm
public static void drawABunchOfBresenhamsCircles(int numOfCircles, double width, double height, Graphics g) {
double r = 5;
double step = (300.0-5.0)/numOfCircles;
for (int i = 1; i <= numOfCircles; i++) {
drawBresenhamsCircle((int)r, width, height, g);
r += step;
}
}
Finally I override the paint method of the JPanel I am using, to draw the bunch of circles and to measure the time it took each type to draw. Here is the paint method:
public void paint(Graphics g) {
Graphics2D g2D = (Graphics2D)g;
g2D.setColor(Color.RED);
long trigoStartTime = System.currentTimeMillis();
drawABunchOfTrigonometricalCircles(1000, this.getWidth(), this.getHeight(), g);
long trigoEndTime = System.currentTimeMillis();
long trigoDelta = trigoEndTime - trigoStartTime;
g2D.setColor(Color.BLUE);
long bresenHamsStartTime = System.currentTimeMillis();
drawABunchOfBresenhamsCircles(1000, this.getWidth(), this.getHeight(), g);
long bresenHamsEndTime = System.currentTimeMillis();
long bresenDelta = bresenHamsEndTime - bresenHamsStartTime;
g2D.setColor(Color.GREEN);
long standardStarTime = System.currentTimeMillis();
drawABunchOfStandardCircles(1000, this.getWidth(), this.getHeight(),g);
long standardEndTime = System.currentTimeMillis();
long standardDelta = standardEndTime - standardStarTime;
System.out.println("Trigo : " + trigoDelta + " milliseconds");
System.out.println("Bresenham :" + bresenDelta + " milliseconds");
System.out.println("Standard :" + standardDelta + " milliseconds");
}
Here is the kind of rendering it would generate (drawing 1000 circles of each type)
Unfortunately my Bresenham's implementation is very slow. I took many comparatives measures, and the Bresenham's implementation is not only slower than the Graphics.drawArcbut also slower than the trigonometrical approach. Take a look at the following measures for a various number of circles drawn.
What part of my implementation is more time-consuming? Is there any workaround I could use to improve it? Thanks for helping.
[EDITION]: as requested by #higuaro, here is my trigonometrical algorithm for drawing a circle
public static void drawTrigonometricalCircle (double r, double width, double height, Graphics g) {
double x0 = 0;
double y0 = 0;
boolean isStart = true;
for (double angle = 0; angle <= 2*Math.PI; angle = angle + Math.PI/36) {
double x = r * Math.cos(angle);
double y = r * Math.sin(angle);
drawPoint((double)x, y, width, height, g);
if (!isStart) {
drawLine(x0, y0, x, y, width, height, g);
}
isStart = false;
x0 = x;
y0 = y;
}
}
And the method used to draw a bunch of trigonometrical circles
public static void drawABunchOfTrigonometricalCircles(int numOfCircles, double width, double height, Graphics g) {
double r = 5;
double step = (300.0-5.0)/numOfCircles;
for (int i = 1; i <= numOfCircles; i++) {
drawTrigonometricalCircle(r, width, height, g);
r += step;
}
}
Your Bresenham method isn't slow per se, it's just comparatively slow.
Swing's drawArc() implementation is machine-dependent, using native code. You'll never beat it using Java, so don't bother trying. (I'm actually surprised the Java Bresenham method is as fast as it is compared to drawArc(), a testament to the quality of the virtual machine executing the Java bytecode.)
Your trigonometric method, however, is unnecessarily fast, because you're not comparing it to Bresenham on an equal basis.
The trig method has a set angular resolution of PI/36 (~4.7 degrees), as in this operation at the end of the for statement:
angle = angle + Math.PI/36
Meanwhile, your Bresenham method is radius-dependent, computing a value at each pixel change. As each octant produces sqrt(2) points, multiplying that by 8 and dividing by 2*Pi will give you the equivalent angular resolution. So to be on equal footing with the Bresenham method, your trig method should therefore have:
resolution = 4 * r * Math.sqrt(2) / Math.PI;
somewhere outside the loop, and increment your for by it as in:
angle += resolution
Since we will now be back to pixel-level resolutions, you can actually improve the trig method and cut out the subsequent drawline call and assignments to x0 and y0, eliminate unnecessarily casts, and furthermore reduce calls to Math. Here's the new method in its entirety:
public static void drawTrigonometricalCircle (double r, double width, double height,
Graphics g) {
double localPi = Math.PI;
double resolution = 4 * r * Math.sqrt(2) / Math.PI;
for (double angle = 0; angle <= localPi; angle += resolution) {
double x = r * Math.cos(angle);
double y = r * Math.sin(angle);
drawPoint(x, y, width, height, g);
}
}
The trig method will now be executing more often by several orders of magnitude depending on the size of r.
I'd be interested to see your results.
Your problem lies in that Bresenham's algorithm does a variable number of iterations depending on the size of the circle whereas your trigonometric approach always does a fixed number of iterations.
This also means that Bresenham's algorithm will always produce a smooth looking circle whereas your trigonometric approach will produce worse looking circles as the radius increases.
To make it more even, change the trigonometric approach to produce approximately as many points as the Bresenham implementation and you'll see just how much faster it is.
I wrote some code to benchmark this and also print the number of points produced and here are the initial results:
Trigonometric: 181 ms, 73 points average
Bresenham: 120 ms, 867.568 points average
After modifying your trigonometric class to do more iterations for smoother circles:
int totalPoints = (int)Math.ceil(0.7 * r * 8);
double delta = 2 * Math.PI / totalPoints;
for (double angle = 0; angle <= 2*Math.PI; angle = angle + delta) {
These are the results:
Trigonometric: 2006 ms, 854.933 points average
Bresenham: 120 ms, 867.568 points average
I lately wrote a bresenham circle drawing implemenation myself for a sprite rasterizer and tried to optimize it a bit. I'm not sure if it will be faster or slower than what you did but i think it should have a pretty decent execution time.
Also unfortunately it is written in C++. If i have time tomorrow i might edit my answer with a ported Java version and an example picture for the result but for now you'd have to do it yourself if you want (or someone else who would want to take his time and edit it.)
Bascically, what it does is use the bresenham algorithm to aquire the positions for the outer edges of the circle, then perform the algorithm for 1/8th of the circle and mirror that for the the remaining 7 parts by drawing straight lines from the center to the outer edge.
Color is just an rgba value
Color* createCircleColorArray(const int radius, const Color& color, int& width, int& height) {
// Draw circle with custom bresenham variation
int decision = 3 - (2 * radius);
int center_x = radius;
int center_y = radius;
Color* data;
// Circle is center point plus radius in each direction high/wide
width = height = 2 * radius + 1;
data = new Color[width * height];
// Initialize data array for transparency
std::fill(data, data + width * height, Color(0.0f, 0.0f, 0.0f, 0.0f));
// Lambda function just to draw vertical/horizontal straight lines
auto drawLine = [&data, width, height, color] (int x1, int y1, int x2, int y2) {
// Vertical
if (x1 == x2) {
if (y2 < y1) {
std::swap(y1, y2);
}
for (int x = x1, y = y1; y <= y2; y++) {
data[(y * width) + x] = color;
}
}
// Horizontal
if (y1 == y2) {
if (x2 < x1) {
std::swap(x1, x2);
}
for (int x = x1, y = y1; x <= x2; x++) {
data[(y * width) + x] = color;
}
}
};
// Lambda function to draw actual circle split into 8 parts
auto drawBresenham = [color, drawLine] (int center_x, int center_y, int x, int y) {
drawLine(center_x + x, center_y + x, center_x + x, center_y + y);
drawLine(center_x - x, center_y + x, center_x - x, center_y + y);
drawLine(center_x + x, center_y - x, center_x + x, center_y - y);
drawLine(center_x - x, center_y - x, center_x - x, center_y - y);
drawLine(center_x + x, center_y + x, center_x + y, center_y + x);
drawLine(center_x - x, center_y + x, center_x - y, center_y + x);
drawLine(center_x + x, center_y - x, center_x + y, center_y - x);
drawLine(center_x - x, center_y - x, center_x - y, center_y - x);
};
for (int x = 0, y = radius; y >= x; x++) {
drawBresenham(center_x, center_y, x, y);
if (decision > 0) {
y--;
decision += 4 * (x - y) + 10;
}
else {
decision += 4 * x + 6;
}
}
return data;
}
//Edit
Oh wow, I just realized how old this question is.
I am making a brick breaker game in Java for fun. In this game the bat is a curved arc that goes around the circumference of a circle. I am struggling to make the bat behave properly.
I am drawing an arc that comes from 2 points on the circle:
public void update(){
if(dir == 1){
angle += 0.05;
}else if(dir == 0){
angle -= 0.05;
}
x0 = a + r * Math.cos(angle);
y0 = b + r * Math.sin(angle);
x1 = a + r * Math.cos(angle - 0.1);
y1 = b + r * Math.sin(angle - 0.1);
}
public void draw(Graphics2D g){
g.setColor(Color.black);
g.fillRect(0, 0, GamePanel.WIDTH, GamePanel.HEIGHT);
int tr = (int)Math.sqrt((x0-a)*(x0-a) + (y0-b)*(y0-b));
int x = (int) (a - tr);
int y = (int) (a - tr);
int width = 2*tr;
int height = 2*tr;
int startAngle = (int) (180/Math.PI*Math.atan2(y0-b, x0-a));
int endAngle = (int) (180/Math.PI*Math.atan2(y1-b, x1-a));
g.setColor(Color.white);
g.drawArc(x, y, width, height, startAngle, endAngle);
}
In theory this should work, the second points being generated from the angle going slightly further, but the length of the arc keeps varying in size...? That is where the problem lies.
This here statement breaks the pattern:
int y = (int) (a - tr);
It would make more sense to use
int y = (int) (b - tr);
And then there is the way g.drawArc is being called:
g.drawArc(x, y, width, height, startAngle, endAngle);
The last parameter is the angle of the arc, so I think you want
g.drawArc(x, y, width, height, startAngle, endAngle - startAngle );
possibly even
g.drawArc(x, y, width, height, startAngle, Math.abs(endAngle - startAngle) );