I am working on a sample project in Java, specifically Android, with OpenGL ES 2.0. I am looking to create a terrain for a player to move across. The player needs to move up and down the terrains hieght following the heightmap. The terrain is not flat. I am using Blender to create the terrain and heightmap. The terrain is being imported as an obj file.
The import and the drawing is working fine but i am having issues making the coordinates follow the heightmap. It doesn't seem to follow it 100%. The player moves up or down too soon every so often or he drops below the terrain.
What I Have So Far:
1)What I am doing is importing the OBJ file into project. Reading the farthest point from terrain object and using this as the object length ( this value * 2 ). This tells me how many vertices are in the object along one side.
2)I then get the difference in the players position to terrain origin in world coordinates. I take the x and z values and divide each by the OBJ object length*2 to give the region of the terrain the object is in. This will give me the percentage of where the x and z position the player is in reference to the terrain.
3) I open the heightmap and multiply the percentages above to the image of the heightmap to get the x and y position on the heightmap to read the height from.
Below is what I am working with.
public float getHeightOfTerrain(float worldX, float worldZ){
// worldX and worldZ are the players position
// Currentmatrix is the matrix used for positioning the terrain
float terrainX = worldX - CurrentMatrix[12] + objLength;
float terrainZ = worldZ - CurrentMatrix[14] + objLength;
return getHeight(terrainX, terrainZ);
}
................
float MAX_HEIGHT = 20;
float MAX_PIXEL_COLOR = 256*256*256;
................
public float getHeight(float x, float z){
float posX = x / (objLength*2);
float posZ = z / (objLength*2);
if(posX < 0 || posX > 1 || posZ < 0 || posZ > 1){ // if over 1 which is 100% of terrain then return
return 0;
}
float imgX = posX * bitmapOfMap.getWidth();
float imgZ = bitmapOfMap.getHeight() - (posZ * bitmapOfMap.getHeight()); // subtract BC bitmap y reads in reverse
if(imgX < 0 || imgX >= bitmapOfMap.getWidth() || imgZ < 0 || imgZ >= bitmapOfMap.getHeight()){ // if over then return
return 0;
}
float height = bitmapOfMap.getPixel((int)imgX, (int)imgZ);
height += MAX_PIXEL_COLOR/2f;
height /= MAX_PIXEL_COLOR/2f;
height *= MAX_HEIGHT;
return height;
}
The smaller the terrain height, the better this setup works. The bigger change in terrain height, the worse this system works. I have tried different obj and heightmaps, recreating over and over thinking this may be the issue but the same problems exist.
Am I doing this completely wrong? Any help would be appreciated. Anyone know a better way? I really want to keep my OBJ file import and read from that for customization.
I went ahead and created the terrain just by the heightmap, no OBJ import. Then applied my code above and had the same exact issue. So I dug a bit deeper in what is exactly going on step by step. It all started from the way I was keep track of the terrains location on x, y, and z then subtract that from the players position. I was tracking the players position from a seperate array of x, y, z and then terrains position from the matrix[12], matrix[13], matrix[14] as x, y, z.
This was the issue. I can't use the floats in the matrices as positioning relative values. I had to keep a seperate x, y, z for the terrain as well.
So from.....
float terrainX = worldX - CurrentMatrix[12] + objLength;
float terrainZ = worldZ - CurrentMatrix[14] + objLength;
to
float x, y, z = 0;
.....
float terrainX = z;
float terrainZ = x;
Now the model works perfect! .... moves up and down the terrain appropriately following the heightmap. I hope this helps anyone that may be having the same issue.
Related
I have a rectangle which when I hold down the mouse button I want that rectangle to move to that point following a strait line 1 pixel at a time.
This is my code so far (I put comments in it so you can understand)
float distanceX = finalX - x; //the number of pixels needed to get to destination on the X axis
float distanceY = finalY - y; // same as above but Y axis
float moveX = distanceX > 0 ? 1 : -1; // I only want it to move 1 pixel per render
float moveY = distanceY > 0 ? 1 : -1; // same as above
Array<Stuff> collidedX = new Array<Stuff>(); //saves collisions seperately for x and y
Array<Stuff> collidedY = new Array<Stuff>(); //because I want the square to move where the mouse is pointing even if it means only aligning one axis
for (Stuff s : collidables) {
if (overlapsT(s, x + moveX, y)) {
collidedX.add(s);
}
}
if (collidedX.size < 1) {
if (distanceX != 0)
x += moveX;
}
for (Stuff s : collidables) {
if (overlapsT(s, x, y + moveY)) {
collidedY.add(s);
}
}
if (collidedY.size < 1) {
if (distanceY != 0)
y += moveY;
}
right now the problem is it goes perfectly diagonal until it lines up with one of the axis and then moves up down left or right to the destination.
I don't want to move fractions of pixels. The way my custom physics engine works is each pixel matters, fractional pixels are no good so I am trying to figure out how to smooth the path or rather how to decide when to add 1 to x and then y.
Currently I can't comment, so I have to answer. I think the Bresenham's line algorithm will help you out. It's for drawing rasterize lines.
Bresenham
This is my first attempt at creating a 2D game, so my code probably isn't as efficient as it could be. Anyway, I tried creating a method to create circles out of my tiles. The point of this method is to create circular dirt patches across my screen. Here is a bit of my code:
private void generateDirt(int x, int y) {
int dirt = 3;
int radius = random.nextInt(7) + 3;
for (int i = radius; i > 1; i--) {
for (int angle = 0; angle < 360; angle++) {
double theta = Math.toRadians(angle);
// Broken Line to solve jutting blocks
// if (theta % Math.PI == 0) theta = 0;
tiles[(int) (x + radius * (Math.sin(theta) * Math.cos(theta)))
+ (int) (y + radius
* (Math.sin(theta) * Math.sin(theta))) * width] = dirt;
}
radius--;
}
}
If I comment out the part where I decrease the radius, and draw just a single circle outline (comment out the outermost for loop(int i = radius...) then the circle is drawn perfectly, except for these two strange tiles jutting out in the side. Sometimes the jutting block is on the right side (I thought it was when it was equal to pi / 2) and on the bottom side as well. But the main problem is that when I attempt to fill the circle by decreasing the radius, the circle...well... becomes a square. It loses its round shape and develops very rigid corners.
I worked on this pretty late, I'm not even sure if my math is correct. TBH, I just kinda threw in the trig functions at random and finally got something that looked like a circle. If you can help me identify what is wrong, or tell me a better way to do this, please let me know! Thanks for the help!
*Also, the radius is actually the diameter (I counted), I need to change the name...
Well I found the answer to my own question. It turns out I don't need to convert my angles to radians. In fact, that just messes up the coordinates. Just using the "angle" instead of "theta" variable fixes the problem.
I'm using the method of dividing the x and y coordinates by the z value, then rendering it just like you would a 2D game/thing to create 3D looking thing. The problem is when the z value is less then 0 it flips the quadrants for the coordinate and looks very weird. At the moment there is only movement if someone could help me fix the negative z value thing and show me how to rotate. I'm not using and matrices only vectors that take in x,y and z for the maths if that helps. I'm making this using Java with no extra libraries.
Thanks for the help.
I used the perspective matrix and multiplied it by my vector but it didn't work here is my code there might be something wrong with it. I just turned the vector into a 1 by 3 matrix and then did this.
public Matrix multiply(Matrix matrix)
{
Matrix result = new Matrix(getWidth(),getHeight());
for(int y = 0; y < getHeight()-1; y++)
{
for(int x = 0; x < getWidth()-1; x++)
{
float sum = 0.0f;
for(int e = 0; e < this.getWidth()-1; e++)
{
sum += this.matrix[e + y * getWidth()] * matrix.matrix[x + e * matrix.getWidth()];
}
result.matrix[x + y * getWidth()] = sum;
}
}
return result;
}
Just guessing here, but it sounds like you are trying to do a projection transform: You are modeling 3D objects (things with X, Y, and Z coordinates) and you want to project them onto a 2D window (i.e., the screen).
The meaning of Z in the naive projection transform is the distance between the point and a plane parallel to the screen, that passes through your eyeball. If you have points with -Z, those represent points that are behind your head.
Sounds like you need to translate the Z coordinates so that Z=0 is the plane of the screen, or a plane parallel to and behind the screen. (In other words, Add a constant to all of your Zs, so that none of them is negative.)
http://en.wikipedia.org/wiki/3D_projection
The applet used is like the first quadrant of a Cartisian Plane with the domain and range (0, 200). My assignment is to draw a house and a sun in this applet.
I am trying to draw the circle for the sun. I really have no idea where to start. We are learning about for loops and nested loops so it probably pertains to that. We haven't got to arrays and general functions like draw.circle do not exist for this applet. If it helps, here is how I drew my roof for the house (two right triangles): Notice it is drawn pixel by pixel. I suspect my teacher wants the same kind of thing for the circle.
//roof
//left side
double starty = 100;
for(double x = 16; x <= 63; x++){
for(int y = 100; y <= starty; y++){
img.set(x, y, JRaster.purple);
}
starty += 1;
}
//right side
double startx = 110;
for(int y = 100; y <= 147; y++){
for(double x = 63; x <= startx; x++){
img.set(x , y, JRaster.purple);
}
startx -= 1;
}
Here's how I would draw the north-east quarter of a circle, pixel by pixel. You can just repeat this with slight variations for the other three quarters. No trigonometry required!
Start by drawing the eastern most point of the circle. Then you'll draw more pixels, moving northwards and westwards, until you get to the northern most point of the circle.
Calculate the distance of the point you've just drawn from the centre. If it's more than the radius, then your next pixel will be one to the left, otherwise, your next pixel will be the one above.
Repeat the previous step till you get to the northern most point.
Post a comment if you get stuck, with converting this to Java, or with adjusting it for the other three quarters of the circle.
I won't give you code, but you should remember how a circle is made. Going from theta=0 to theta=2*pi, the circle is traced by x=cos x, y=sin x.
So, using a for loop that increments a double(here called theta) by something like 0.01 until 2*pi(2*Math.PI or roughly 6.28) plot off Math.cos(theta), Math.sin(theta).
Let's say I have an (x,y) that is always the same for the start point of a line and an (x,y) that changes for the end point of that same line. The line is also always 40px long. At the start of the program the line originates in a vertical orientation (lets call it 0 degrees). Based on a user input I need the line to be redrawn a specific number of degrees from its origin by changing only the end (x,y).
SOME MORE FOOD FOR THOUGHT IF YOU NEED IT:
I'm in a rut trying to calculate this and make it work in Java. I can make the math work to calculate the point based on the arc length of a circle segment, but I don't know how to make Java do it.
I think it would work easier based off a triangle angles since I will always know the length of two sides of a triangle (one side formed by the 40px long line and the other side formed by the start point of that line and the border of the JPanel) and the angle those two lines form. Still, my brain is mush from trying to figure it out. Any help would be much appreciated.
UPDATE:
#casablanca got me on the right track. I brushed up on my trig functions and here is how I made it work.
First off, I didn't realize that 90 degrees was straight up, but once I did realize that I made my solution reflect that fact. I was drawing my line starting at the bottom center of the frame going out. Since the opposite side of the triangle is on the right side of the screen when the angle given by my user is less than 90 degrees and is on the left side of the screen when the angle given by my user is greater than 90 degrees I had to adjust the formula to account for that fact, thus I have four methods, one for the x coordinate on the left side of the screen (when the user given angle is greater than 90 degrees), one for the y coordinate on the left side of the screen (when the user given angle is greater than 90 degrees) and the same thing for the right side of the screen when the user given angle is less than 90 degrees. The int length in all methods is the length of the hypotenuse. Thanks again for your help #casablanca!
public double leftSideX(double angle, int length){
double x = frameWidth/2 - (length * Math.cos(Math.toRadians(90-(Math.toDegrees(angle)-90))));
return x;
}
public double leftSideY(double angle, int length){
double y = frameHeight - (length * Math.sin(Math.toRadians(90-(Math.toDegrees(angle)-90))));
return y;
}
public double rightSideX(double angle, int length){
double x = frameWidth/2 + (length * Math.cos(angle));
return x;
}
public double rightSideY(double angle, int length){
double y = frameHeight - (length * Math.sin(angle));
return y;
}
Is this what you're looking for?
startX = x;
startY = y;
endX = x + 40 * Math.sin(angle);
endY = y + 40 * Math.cos(angle);
And draw a line from (startX, startY) to (endX, endY) in whatever API you're using.
Also note that angle is in radians. If you had it in degrees, you need to convert it first:
angle = angle * Math.PI / 180;