Adding thickness to a 2D sprite when turning - java

In my game, my entities turn like a piece of paper, as shown here at half-speed: https://imgur.com/a/u2suen6
I want to give the entities a bit of thickness when they turn, making them more cardboard-thin than paper-thin.
I thought about using a Pixmap to detect and extend the edge pixels and give the image some Three-Dimensionality. I also considered duplicating the image along the x-axis to give the same effect. Of the two ideas, the Pixmap holds out the most promise in my mind. However, I'm wondering if there's a better solution.
I'm using a GLSL shader to give the entities highlights and shadows while turning, as you saw in the gif. I think that with the right knowledge, I could achieve what I'm going for using the same shader program.
My shader looks like this:
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform vec2 u_resolution;
uniform vec3 color;
void main()
{
vec4 col = vec4(color, 0.0);
gl_FragColor = texture2D(u_texture, v_texCoords) * v_color + col;
}
I think that one might be able to make calculations based on the uniform vec3 color that I pass it (with its values ranging from 0, 0, 0 to 1, 1, 1. 1's being highlight and 0's being shadow). Unfortunately, I don't have the understanding of shaders to do so.
If any of you have the know-how, could you steer me in the right direction? Or let me know if I should just stick to the Pixmap idea.
Edit: I'm trying to stay away from using a 3D model because I'm 6.4k lines of code deep using a 2d Orthographic Camera.
Edit 2: I figured that the reflection shader wouldn't look good if I tried making the sprite look 3D. I scrapped the shader, went with the Pixmap idea, and plan on implementing shadows and reflections to the pixmap without any shader. Though it looks good so far without reflections.

I ended up going with my pixmap idea. I want to share my code so that others can know how I got 2D thickness to work.
Please note that in the following code:
dir is a floating point value in the range -1.0 to 1.0. It tells the program where the sprite is in its turn. -1 means facing fully left. 1 meaning right. 0 means that it's 'facing' the camera.
right is a boolean that tells the program which direction the entity is turning. true means that the entity is turning from left to right. false means from right to left.
The Code:
private Texture getTurningImage(TextureRegion input, int thickness)
{
if(Math.abs(dir) < 0.1)
dir = (right ? 1 : -1) * 0.1f;
Texture texture = input.getTexture();
if (!texture.getTextureData().isPrepared())
{
texture.getTextureData().prepare();
}
Pixmap pixmap = texture.getTextureData().consumePixmap();
Pixmap p = new Pixmap(64, 64, Pixmap.Format.RGBA8888);
p.setFilter(Pixmap.Filter.NearestNeighbour);
Pixmap texCopy = new Pixmap(input.getRegionWidth(), input.getRegionHeight(), Pixmap.Format.RGBA8888);
// getting a texture out of the input region. I can't use input.getTexture()
// because it's an animated sprite sheet
for (int x = 0; x < input.getRegionWidth(); x++)
{
for (int y = 0; y < input.getRegionHeight(); y++)
{
int colorInt = pixmap.getPixel(input.getRegionX() + x, input.getRegionY() + y);
Color c = new Color(colorInt);
colorInt = Color.rgba8888(c);
texCopy.drawPixel(x, y, colorInt);
}
}
pixmap.dispose();
float offsetVal = Math.round(thickness/2.0) * (float) -Math.cos((dir * Math.PI)/2);
if(offsetVal > -1.23/Math.pow(10, 16))
{
offsetVal = 0;
}
// generate the pixel colors we'll use for the side view
Pixmap sideProfile = new Pixmap(1, 64, Pixmap.Format.RGBA8888);
for (int y = 0; y < texCopy.getHeight(); y++)
{
for (int x = 0; x < texCopy.getWidth(); x++)
{
int colorInt = texCopy.getPixel(x, y);
if(new Color(colorInt).a != 0 && new Color(texCopy.getPixel(x + 1, y)).a == 0)
{
Color c = new Color(colorInt);
c.mul(.8f); // darken the color
c.a = 1;
colorInt = Color.rgba8888(c);
sideProfile.drawPixel(0, y, colorInt);
continue;
}
}
}
// drawing the bottom layer
p.drawPixmap(texCopy, 0, 0, 64, 64, (int) (Math.round(-offsetVal) + (64 - texCopy.getWidth()*Math.abs(dir))/2), 0, (int)(64*Math.abs(dir)), 64);
// drawing the middle (connecting) layer
// based on the edge pixels of the bottom layer, then translated to be in the middle
for (int y = 0; y < p.getHeight(); y++)
{
int colorInt = sideProfile.getPixel(0, y);
for (int x = 0; x < p.getWidth(); x++)
{
if(new Color(p.getPixel(x, y)).a != 0 && new Color(p.getPixel(x + 1, y)).a == 0)
{
for(int i = 0; i <= 2 * Math.round(Math.abs(offsetVal)); i++) // the for the length between the top and bottom
{
p.drawPixel(x + i - 2 * (int)Math.round(Math.abs(offsetVal)), y, colorInt);
}
}
}
}
// drawing the top layer
p.drawPixmap(texCopy, 0, 0, 64, 64, (int) (Math.round(offsetVal) + (64 - texCopy.getWidth()*Math.abs(dir))/2), 0, (int)(64*Math.abs(dir)), 64);
// flip if facing left
if(dir < 0)
{
p = flipPixmap(p);
}
return new Texture(p);
}
My flipPixmap method looks like this (stolen from stack overflow):
private Pixmap flipPixmap(Pixmap src)
{
final int width = src.getWidth();
final int height = src.getHeight();
Pixmap flipped = new Pixmap(width, height, src.getFormat());
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
flipped.drawPixel(x, y, src.getPixel(width - x - 1, y));
}
}
return flipped;
}
Here's the result :D https://imgur.com/a/wGeHg9D

Related

How to create multicolor rasterized picture in processing

I followed this tutorial on Youtube, and I have successfully added colors to the black and white picture. However, my intention was to create a multi-color or gradient effect (like here or here) instead of switching colors when I move the cursor.
I very new at processing, and I have tried to play with the variable, with no success.
Here is the code snippet of the sketch:
`
PImage img;
void setup() {
size(598,336);
colorMode(HSB);
img = loadImage("picture-in-data-folder.jpg");
img.resize(598,336);
//ellipseMode(RADIUS);
frameRate(30);
}
void draw() {
background(255);
noStroke();
// fill(0);
float tiles = mouseX/10;
float tileSize = width/tiles;
// color section
fill(color(tiles, 255, 255));
tileSize++;
if (tiles > width / 2) {
tileSize = 0;
}
// end color section
translate(tileSize/2, tileSize/2);
for (int x = 0; x < tiles; x++) {
for (int y = 0; y < tiles; y++) {
color c = img.get(int(x*tileSize),int(y*tileSize));
float size = map(brightness(c), 0, 255, tileSize, 0);
ellipse(x*tileSize, y*tileSize, size, size);
// image(img, mouseX, mouseY);
}
}
}
I would be grateful if you had any hints, or if you could provide an advice.
Thanks.
Short answer: you need to put a fill() command inside the for loop.
Long answer:
Right now, your code is doing the following:
Define tiles based on mouseX
Set the fill color to (tiles, 255, 255)
Draw all the circles
I think what you want it to do is something like this:
Set the fill color to (21, 255, 255) (or whatever you want the first color to be)
draw the first circle
set the fill color to the next color in the gradient
draw the second circle
etc.
In order to do this, you need to put a command into the for loop which changes the fill color. Here is one way to do that:
for (int x = 0; x < tiles; x++) {
for (int y = 0; y < tiles; y++) {
color c = img.get(int(x*tileSize),int(y*tileSize));
float size = map(brightness(c), 0, 255, tileSize, 0);
fill(map(x, 0, tiles, 0, 255), 255, 255);
ellipse(x*tileSize, y*tileSize, size, size);
}
}
I just added that fill command as a function of x, but you can make it whatever you want. In order for it to be a gradient, it needs to vary somewhat with x or y.

How to draw lines pointing to mouse in Processing

I am trying to make a program where there are lines in a grid pointing towards the mouse like magnets. I am a beginner in Processing, can someone point me towards a tutorial on how to do that or give me some code and explain what it does?
int x1 = 0;
int x2 = 0;
int y1 = 0;
int y2 = 0;
void setup() {
size(200, 200);
}
void draw() {
background(255, 255, 0);
x1 = (mouseX + 100) / 2;
y1 = (mouseY + 100) / 2;
x2 = -1 * x1 + 200;
y2 = -1 * y1 + 200;
line(x1, y1, x2, y2);
}
There's plenty of solutions for this project. One of the easiest is to use Processing's PVector class.
The PVector class can be used for two or three dimensional vectors. A vector is an entity that has both magnitude and direction. The PVector class, however, stores the components of the vector (x,y for 2D, and x,y,z for 3D). The magnitude and direction are calculated from the components and can be accessed via the methods mag() and heading().
A two dimensional vector in Processing is defined through x and y components:
PVector v = new PVector(xComponent, yComponent);
With some mathematical formulae, you can determine magnitude and direction using the x- and y-components. But we don't need to determine these.
Below, I've attached completed solution code. Most of it should make sense to you. But it's worth understanding what is going on with PVector.
A nested for loop within void draw() contains x and y variables that represent the coordinates of each grid vertex.
We first define PVector v as a vector given by an x-component of mouseX - x, or the difference between the x-positions of the mouse and each grid point. Similarly, the y-component given by mouseY - y has the same difference.
Creating a variable PVector u initialized from v.setMag(15) holds a PVector that has the same direction as v, but with a length of just 15.
Now to draw the lines. Vectors represent an offset, not a position (in this case), so drawing a line from a grid point to an offset of a grid point is key.
Hence line(x, y, x + u.x, y + u.y), where u.x and u.y are the x- and y-components of the vector u.
void setup() {
size(600, 600); // Set the size of the canvas to 600x600.
}
void draw() {
background(255);
stroke(200); // Set the stroke color to black
int distVertLine = width / 10; // This variable defines the distance between each subsequent vertical line.
for(int i = 0; i < width; i += distVertLine) {
line(i, 0, i, height); // Draw a line at x=i starting at the top of the canvas (y=0) and going to the bottom (y=height)
}
int distHorizLine = height / 10; // This variable defines the distance between each subsequent vertical line.
for(int i = 0; i < width; i += distHorizLine) {
line(0, i, width, i); // Draw a line at y=i starting at the left of the canvas (x=0) and going to the right (x=width)
}
stroke(0); // Set the stroke to black.
// Use a nested for loop to iterate through all grid vertices.
for(int x = 0; x <= width; x += width/10) {
for(int y = 0; y <= height; y += height/10) {
PVector v = new PVector(mouseX - x, mouseY - y); // Define a vector that points in the direction of the mouse from each grid point.
PVector u = v.setMag(15); // Make the vector have a length of 15 units.
line(x, y, x + u.x, y + u.y); // Draw a line from the grid vertex to the terminal point given by the vector.
}
}
}
The answer already given by Ben Myers is excellent! The code below has a few small modifications:
the two for loops for the grid lines have been combined (since width and height are equal);
the construction of the vector is combined with setting the magnitude;
some minor changes to colors and comments.
Modified code:
void setup() {
// Set the size of the canvas to 600x600 pixels.
size(600, 600);
}
void draw() {
// There are 10x10 grid cells that each have a size of 60x60 pixels.
int gridSize = width / 10;
// Set the background color to anthracite and the stroke color to orange.
background(56, 62, 66);
stroke(235, 113, 52);
// Draw vertical and horizontal grid lines.
for (int lineIndex = 0; lineIndex < gridSize; lineIndex++) {
line(lineIndex * gridSize, 0, lineIndex * gridSize, height);
line(0, lineIndex * gridSize, width, lineIndex * gridSize);
}
// Set the stroke color to blue.
stroke(0, 139, 225);
// Use a nested for loop to iterate through all grid cells.
for (int x = 0; x <= width; x += gridSize) {
for (int y = 0; y <= height; y += gridSize) {
// Define a vector that points in the direction of the mouse from
// each grid point and set the vector length to 15 units.
PVector vector = new PVector(mouseX - x, mouseY - y).setMag(15);
// Draw a line from the grid point to the end point using the vector.
line(x, y, x + vector.x, y + vector.y);
}
}
}

Opengl terrain texture shading

I am currently working on an OpenGL (JOGL) project in java.
The goal is to create a terrain with textures and shading.
I'm creating a random simplex noise, using these values as a heightmap.
The heights are mapped to a 1D texture to simulate coloring based on height.
A material (ambient/diffuse/specular/shininess) is then used to simulate shading.
However; after adding shading to the terrain, 'stripes' appear on each 'column' (Y direction) of the terrain.
The following material is then applied:
TERRAIN(
new float[]{0.5f, 0.5f, 0.5f, 1.0f},
new float[]{0.7f, 0.7f, 0.7f, 1.0f},
new float[]{0.2f, 0.2f, 0.2f, 1.0f},
new float[]{100f})
The material enum constructor:
Material(float[] ambient, float[] diffuse, float[] specular, float[] shininess) {
this.ambient = ambient;
this.diffuse = diffuse;
this.specular = specular;
this.shininess = shininess;
}
I apply the material using the following method:
public void use(GL2 gl) {
// set the material properties
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_AMBIENT, ambient, 0);
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_DIFFUSE, diffuse, 0);
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_SPECULAR, specular, 0);
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_SHININESS, shininess, 0);
}
After creating a 2D 'noisearray' consistent of 0-1 values, an 2D vectorarray is created, consiting of X*Y vectors, where each vector represents a point in the plane/terrain.
Here is the method that draws triangles in between those points, where you can see I draw the plane per column (Y direction):
public void draw(GL2 gl, GLU glu, GLUT glut, Drawer drawer) {
Material.TERRAIN.use(gl);
texture.bind(gl);
if (showGrid)
gl.glPolygonMode( gl.GL_FRONT_AND_BACK, gl.GL_LINE );
ArrayList<Vector[]> normals = new ArrayList<>();
for(int i=1;i<vectors.length;i++) {
gl.glBegin(gl.GL_TRIANGLE_STRIP);
for (int j = 0; j < vectors[i].length; j++) {
Vector normalTopRight, normalBottomLeft;
//Calculate normals top right
Vector v1, v2, triangleCenterTR;
if (j < vectors[i].length - 1)
{
v1 = vectors[i-1][j].subtract(vectors[i][j]);
v2 = vectors[i][j+1].subtract(vectors[i][j]);
normalTopRight = v2.cross(v1).normalized();
// Get center (a+b+c)*(1/3)
triangleCenterTR = (vectors[i][j].add(vectors[i - 1][j]).add(vectors[i][j + 1])).scale(1.0 / 3);
} else {
v1 = vectors[i-1][j].subtract(vectors[i][j]);
v2 = vectors[i][j-1].subtract(vectors[i][j]);
normalTopRight = v1.cross(v2).normalized();
// Get center (a+b+c)*(1/3)
triangleCenterTR = (vectors[i][j].add(vectors[i-1][j]).add(vectors[i][j-1])).scale(1.0/3);
}
normals.add(new Vector[] {triangleCenterTR, triangleCenterTR.add(normalTopRight)});
if (j != 0)
{
v1 = vectors[i][j].subtract(vectors[i-1][j]);
v2 = vectors[i-1][j-1].subtract(vectors[i-1][j]);
normalBottomLeft = v2.cross(v1).normalized();
// Get center (a+b+c)*(1/3)
Vector triangleCenterBL = (vectors[i - 1][j].add(vectors[i][j]).add(vectors[i - 1][j - 1])).scale(1.0 / 3);
normals.add(new Vector[]{triangleCenterBL, triangleCenterBL.add(normalBottomLeft)});
} else {
normalBottomLeft = null; // If j==0, there is no bottom left triangle above
}
/**
* We have everything to start drawing
*/
// Set some color
if (j == 0) {
// Initialization vector
gl.glTexCoord1d(mapTextureToHeight(vectors[i][j].z));
drawer.glVertexV(vectors[i][j]);
} else {
drawer.glNormalV(normalBottomLeft);
}
// Shift left
gl.glTexCoord1d(mapTextureToHeight(vectors[i - 1][j].z));
drawer.glVertexV(vectors[i - 1][j]);
// Right down diagonally
if (j < vectors[i].length - 1) { // Skip if we are reached the end
gl.glTexCoord1d(mapTextureToHeight(vectors[i][j + 1].z));
drawer.glNormalV(normalTopRight);
drawer.glVertexV(vectors[i][j + 1]);
}
}
gl.glEnd();
}
if (showGrid)
gl.glPolygonMode( gl.GL_FRONT_AND_BACK, gl.GL_FILL );
if (drawNormals) {
for (Vector[] arrow : normals) {
if (yellowNormals)
Material.YELLOW.use(gl);
else
gl.glTexCoord1d(mapTextureToHeight(arrow[0].z));
drawer.drawArrow(arrow[0], arrow[1], 0.05);
}
}
texture.unbind(gl);
}
The most obvious reason for the stripes is the fact I draw the triangles per column, causing OpenGL to not be able to smoothen the shading on the polygons (GL_SMOOTH). Is there any way to fix this?
[Edit1] Copied from your comment by Spektre
I just finished calculating the average normals, I indeed have a smooth terrain now, but the lighting looks kind of dull (no depth)
Here is the new code that draws the terrain:
public void draw() {
if (showGrid)
gl.glPolygonMode( gl.GL_FRONT_AND_BACK, gl.GL_LINE);
texture.bind(gl);
Material.TERRAIN.use(gl);
for(int i=1;i<vectors.length;i++) {
gl.glBegin(gl.GL_TRIANGLE_STRIP);
for (int j = 0; j < vectors[i].length; j++) {
// Initialization vector
gl.glTexCoord1d(mapTextureToHeight(vectors[i][j].z));
drawer.glNormalV(normals.get(vectors[i][j]));
drawer.glVertexV(vectors[i][j]);
// Shift left
gl.glTexCoord1d(mapTextureToHeight(vectors[i - 1][j].z));
drawer.glNormalV(normals.get(vectors[i - 1][j]));
drawer.glVertexV(vectors[i - 1][j]);
}
gl.glEnd();
}
if (showGrid)
gl.glPolygonMode( gl.GL_FRONT_AND_BACK, gl.GL_FILL );
if (drawNormals)
drawFaceNormals();
texture.unbind(gl);
}
I cleaned it up, I am sure the normals are pointing the correct way using the drawnormals function and made sure OpenGL is seeing the top of the terrain as FRONT using (gl.GL_FRONT -> draws only above terrain, not below).
Here is the complete class: PasteBin
Thanks to #Spektre for helping me out.
After properly calculating the average normal of all surrounding faces on a vertex and using this normal for glNormal, the shading was correct.

Java LWJGL - Heightmap not rendering correctly

I am trying to implement a heightmap into my game following Oskar Veerhoek's youtube tutorials. I follow the steps and I get a few methods:
private static void setUpHeightmap() {
try {
// Load the heightmap-image from its resource file
BufferedImage heightmapImage = ImageIO.read(new File("res/img/heightmap.bmp"));
// Initialise the data array, which holds the heights of the heightmap-vertices, with the correct dimensions
data = new float[heightmapImage.getWidth()][heightmapImage.getHeight()];
// Lazily initialise the convenience class for extracting the separate red, green, blue, or alpha channels
// an int in the default RGB color model and default sRGB colourspace.
Color colour;
// Iterate over the pixels in the image on the x-axis
for (int z = 0; z < data.length; z++) {
// Iterate over the pixels in the image on the y-axis
for (int x = 0; x < data[z].length; x++) {
// Retrieve the colour at the current x-location and y-location in the image
colour = new Color(heightmapImage.getRGB(z, x));
// Store the value of the red channel as the height of a heightmap-vertex in 'data'. The choice for
// the red channel is arbitrary, since the heightmap-image itself only has white, gray, and black.
data[z][x] = colour.getRed();
}
}
// Create an input stream for the 'lookup texture', a texture that will used by the fragment shader to
// determine which colour matches which height on the heightmap
FileInputStream heightmapLookupInputStream = new FileInputStream("res/img/heightmap_lookup.png");
// Create a class that will give us information about the image file (width and height) and give us the
// texture data in an OpenGL-friendly manner
PNGDecoder decoder = new PNGDecoder(heightmapLookupInputStream);
// Create a ByteBuffer in which to store the contents of the texture. Its size is the width multiplied by
// the height and 4, which stands for the amount of bytes a float is in Java.
ByteBuffer buffer = BufferUtils.createByteBuffer(4 * decoder.getWidth() * decoder.getHeight());
// 'Decode' the texture and store its data in the buffer we just created
decoder.decode(buffer, decoder.getWidth() * 4, PNGDecoder.Format.RGBA);
// Make the contents of the ByteBuffer readable to OpenGL (and unreadable to us)
buffer.flip();
// Close the input stream for the heightmap 'lookup texture'
heightmapLookupInputStream.close();
// Generate a texture handle for the 'lookup texture'
lookupTexture = glGenTextures();
glBindTexture(GL_TEXTURE_2D, lookupTexture);
// Hand the texture data to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, buffer);
} catch (IOException e) {
e.printStackTrace();
}
// Use the GL_NEAREST texture filter so that the sampled texel (texture pixel) is not smoothed out. Usually
// using GL_NEAREST will make the textured shape appear pixelated, but in this case using the alternative,
// GL_LINEAR, will make the sharp transitions between height-colours ugly.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Generate a display list handle for the display list that will store the heightmap vertex data
heightmapDisplayList = glGenLists(1);
// TODO: Add alternative VBO rendering for pseudo-compatibility with version 3 and higher.
glNewList(heightmapDisplayList, GL_COMPILE);
// Scale back the display list so that its proportions are acceptable.
glScalef(0.2f, 0.06f, 0.2f);
// Iterate over the 'strips' of heightmap data.
for (int z = 0; z < data.length - 1; z++) {
// Render a triangle strip for each 'strip'.
glBegin(GL_TRIANGLE_STRIP);
for (int x = 0; x < data[z].length; x++) {
// Take a vertex from the current strip
glVertex3f(x, data[z][x], z);
// Take a vertex from the next strip
glVertex3f(x, data[z + 1][x], z + 1);
}
glEnd();
}
glEndList();
}
private static void setUpShaders() {
shaderProgram = ShaderLoader.loadShaderPair("res/shaders/landscape.vs", "res/shaders/landscape.fs");
glUseProgram(shaderProgram);
// The following call is redundant because the default value is already 0, but illustrates how you would use
// multiple textures
glUniform1i(glGetUniformLocation(shaderProgram, "lookup"), 0);
}
private static void setUpStates() {
camera.applyOptimalStates();
glPointSize(2);
// Enable the sorting of shapes from far to near
glEnable(GL_DEPTH_TEST);
// Set the background to a blue sky colour
glClearColor(0, 0.75f, 1, 1);
// Remove the back (bottom) faces of shapes for performance
//glEnable(GL_CULL_FACE);
}
When I run the game, the heightmap doesn't show, and my models on the screen get squished like this:
Im thinking it has something to do with it incorrectly rendering the heights and applying it to my models. But I really don't know as I am VERY foreign to 3D development. Some help would be amazing! Thanks!
To get the heightmap information from the image, you simply code:
try {
BufferedImage height = ImageIO.read(new File("utah.jpg"));
hm = new double[height.getHeight()][height.getWidth()];
for (int row = 0; row < height.getHeight(); row++) {
for (int col = 0; col < height.getWidth(); col++) {
hm[row][col] = (height.getRGB(col, row) & 255) / 255.0;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
To draw the points, you just do the following(translate from JOGL to LWJGL):
gl.glPushMatrix();
for (int x = 0; x < (257 - 1); x++) {
gl.glBegin(GL.GL_TRIANGLE_STRIP);
for (int z = 0; z < (257 - 1); z++) {
double[] row = hm[0];
double y = hm[(x * hm.length) % 10][(z + row.length) % 10]
* 5;
gl.glColor3d(1, 1, 1);
gl.glVertex3d(x, y, z);
//gl.glTexCoord3d(x, y, z);
y = hm[((x + 1) * hm.length) % 10][((z + 1) + row.length) % 10]
* 5;
gl.glColor3d(1, 1, 1);
gl.glVertex3d(x + 1, y, z + 1);
//gl.glTexCoord3d(x + 1, y, z + 1);
}
gl.glEnd();
}
gl.glPopMatrix();

Fill a shape with points

I try to draw a leaf looking thing on the screen, and try to fill it with a color. It's like drawing a circle, the difference is, that it's only 270 degrees, and the radius starts from 0 to 100. I first draw the left side, and on each degree I fill the inside. At the end I draw the right side.
Here is to code, maybe it's easier to understand:
canvas = new BufferedImage(SIZE, SIZE, BufferedImage.TYPE_INT_ARGB);
Color black = new Color(0,0,0);
Color green = new Color(0,130,0);
double j = 0.0; // radius
double max = 100.0; // max radius
for (int i = 0; i < 135; i++) { // left side (270 degree / 2)
j += max / 135.0;
// x, y coordinate
int x = (int)(Math.cos(Math.toRadians(i)) * j);
int y = (int)(Math.sin(Math.toRadians(i)) * j);
// draw a circle like thing with radius j
for (int l = i; l < 135 + (135 - i); l++) {
int ix = (int)(Math.cos(Math.toRadians(l)) * j);
int iy = (int)(Math.sin(Math.toRadians(l)) * j);
canvas.setRGB(ix + 256, iy + 256, green.getRGB());
}
canvas.setRGB(x + 256, y + 256, black.getRGB());
}
// draw the right side
for (int i = 135; i < 270; i++) {
j -= max / 135.0;
int x = (int)(Math.cos(Math.toRadians(i)) * j);
int y = (int)(Math.sin(Math.toRadians(i)) * j);
canvas.setRGB(x + 256, y + 256, black.getRGB());
}
This is the result:
As you can see, where the radius is bigger, the leaf is not filled completely.
If I change i to 1350, then divide it with 10 where I calculate x, y, then it's filled, but it's much slower. Is there a better way to properly fill my shape?
Later I also would like to fill my shape with a gradient, so from green to a darker green, then back to green. With my method this is easy, but super slow.
Thanks in advance!
I think that for you the best solution is to use a flood fill algorithm, it's easy to implement in Java and efficient in your case, like you have a simple shape.
Here is a wikipedia article that is really complet : http://en.wikipedia.org/wiki/Flood_fill
Here is a simple suggestion: Instead of drawing the leaf, just put the points that create the outline into an array. The array should run from xMin (smallest X coordiate of the leaf outline) to xMax. Each element is two ints: yMin and yMax.
After rendering all the points, you can just draw vertical lines to fill the space between yMin/yMax for each X coordinate.
If you have gaps in the array, fill them by interpolating between the neighboring points.
An alternative would be to sort the points clockwise or counter-clockwise and use them as the outline for a polygon.

Categories