Procedurally Generated Voronoi Roads - java

i'm using a noise function inspired by libnoiseforjava to try and generate roads. (See below)
public class VoronoiNoise {
private static final double SQRT_2 = 1.4142135623730950488;
private static final double SQRT_3 = 1.7320508075688772935;
private long seed;
private short distanceMethod;
private final double frequency;
public VoronoiNoise(long seed, double frequency, short distanceMethod) {
this.seed = seed;
this.distanceMethod = distanceMethod;
this.frequency = frequency;
}
private double getDistance(double xDist, double zDist) {
switch(distanceMethod) {
case 0: //EUCLIDIAN - Length
return Math.sqrt(xDist * xDist + zDist * zDist) / SQRT_2;
case 1: //???
return xDist + zDist;
case 2: //???
return Math.pow(Math.E, Math.sqrt(xDist * xDist + zDist * zDist) / SQRT_2)/Math.E;
case 3: //MANHATTAN
return Math.abs(xDist) + Math.abs(zDist);
case 4: //CHEBYCHEV
return Math.max(Math.abs(xDist), Math.abs(zDist));
case 5: //MINKOVSKI
return Math.pow(Math.pow(Math.abs(xDist), Math.PI) + Math.pow(Math.abs(zDist), Math.PI), (1 / Math.PI));
case 6: //MINKOVSKI4
return Math.pow(xDist*xDist*xDist*xDist+zDist*zDist*zDist*zDist,0.25);
default:
return 1.0;
}
}
private double getDistance(double xDist, double yDist, double zDist) {
switch(distanceMethod) {
case 0:
return Math.sqrt(xDist * xDist + yDist * yDist + zDist * zDist) / SQRT_3;
case 1:
return xDist + yDist + zDist;
default:
return 1.0;
}
}
public short getDistanceMethod() {
return distanceMethod;
}
public long getSeed() {
return seed;
}
public double noise(double x, double z) {
x *= frequency;
z *= frequency;
int xInt = (x > .0? (int)x: (int)x - 1);
int zInt = (z > .0? (int)z: (int)z - 1);
double minDist = 32000000.0;
double xCandidate = 0;
double zCandidate = 0;
double xCandidate2 = 0;
double zCandidate2 = 0;
Random random = new Random(seed);
long xSeed = random.nextLong();
long zSeed = random.nextLong();
for(int zCur = zInt - 2; zCur <= zInt + 2; zCur++) {
for(int xCur = xInt - 2; xCur <= xInt + 2; xCur++) {
double xPos = xCur + valueNoise2D(xCur, zCur, xSeed);
double zPos = zCur + valueNoise2D(xCur, zCur, zSeed);
double xDist = xPos - x;
double zDist = zPos - z;
double dist = xDist * xDist + zDist * zDist;
if(dist < minDist) {
xCandidate2 = xCandidate;
zCandidate2 = zCandidate;
xCandidate = xPos;
zCandidate = zPos;
minDist = dist;
}
}
}
double xDist = xCandidate - x;
double zDist = zCandidate - z;
//return getDistance(xDist, zDist);
return getDistance(xCandidate2 - x, zCandidate2 - z) - getDistance(xCandidate - x, zCandidate - z);
}
public double noise(double x, double y, double z) {
x *= frequency;
y *= frequency;
z *= frequency;
int xInt = (x > .0? (int)x: (int)x - 1);
int yInt = (y > .0? (int)y: (int)y - 1);
int zInt = (z > .0? (int)z: (int)z - 1);
double minDist = 32000000.0;
double xCandidate = 0;
double yCandidate = 0;
double zCandidate = 0;
Random rand = new Random(seed);
for(int zCur = zInt - 2; zCur <= zInt + 2; zCur++) {
for(int yCur = yInt - 2; yCur <= yInt + 2; yCur++) {
for(int xCur = xInt - 2; xCur <= xInt + 2; xCur++) {
double xPos = xCur + valueNoise3D (xCur, yCur, zCur, seed);
double yPos = yCur + valueNoise3D (xCur, yCur, zCur, rand.nextLong());
double zPos = zCur + valueNoise3D (xCur, yCur, zCur, rand.nextLong());
double xDist = xPos - x;
double yDist = yPos - y;
double zDist = zPos - z;
double dist = xDist * xDist + yDist * yDist + zDist * zDist;
if(dist < minDist) {
minDist = dist;
xCandidate = xPos;
yCandidate = yPos;
zCandidate = zPos;
}
}
}
}
double xDist = xCandidate - x;
double yDist = yCandidate - y;
double zDist = zCandidate - z;
return getDistance(xDist, yDist, zDist);
}
public void setDistanceMethod(short distanceMethod) {
this.distanceMethod = distanceMethod;
}
public void setSeed(long seed) {
this.seed = seed;
}
public static double valueNoise2D (int x, int z, long seed) {
long n = (1619 * x + 6971 * z + 1013 * seed) & 0x7fffffff;
n = (n >> 13) ^ n;
return 1.0 - ((double)((n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff) / 1073741824.0);
}
public static double valueNoise3D (int x, int y, int z, long seed) {
long n = (1619 * x + 31337 * y + 6971 * z + 1013 * seed) & 0x7fffffff;
n = (n >> 13) ^ n;
return 1.0 - ((double)((n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff) / 1073741824.0);
}
}
As you can see i've been calculating the Distance21 - the distance of the 1st minus the 2nd closest point. I've been using this along with Euclidian distance calculation to generate a noise map. Here is what I get:
I've then been checking whether the noise is below a certain value (black colours) and using that to place roads. One problem is that some of the cells are black, some blacker than the roads causing a mass of roads. Also, the roads are more than 1 unit in width which is unacceptable for my purposes.
Here is a chart of the different types of voronoi noise.
I was wondering if there is an algorithm for procedurally generating noise like this
Except that given a seed, a frequency and a point (x,y) it would return true or false whether or not the vertices pass through that point or not.
Creating lines
As a last resort I will use a repeating, static, image.
EDIT: I managed to do what i set out to do by checking if the 4 points around the point have a different closest point
I would still like to know if this is possible some other less intensive way.

Related

Zooming relative to mouse doesnt work in JavaFX

I had a working code, which used ParallelCamera when zooming, and it worked perfectly.
Here's it:
double s = camera.getScaleX();
double d = e.getDeltaY();
double f = 1.2;
double x = e.getSceneX();
double y = e.getSceneY();
if (d < 0) {
f = 2;
} else if (d > 0) {
f = 0.5;
}
double ps = s;
/*if (s * f > 1) {
camera.setScaleX(1);
camera.setScaleY(1);
camera.setScaleZ(1);
camera.setTranslateX(0);
camera.setTranslateY(0);
return;
}
camera.setScaleX(s * f);
camera.setScaleY(s * f);
camera.setScaleZ(s * f);
s = camera.getScaleX();
double dx = ps * (1 - f) * x;
double dy = ps * (1 - f) * y;
System.out.println(dx);
double width = primaryScene.getWidth();
double height = primaryScene.getHeight();
if (camera.getTranslateX() + s * width + dx > width) {
camera.setTranslateX(width - s * width);
} else if ((camera.getTranslateX() + dx < 0)) {
camera.setTranslateX(0);
} else {
camera.setTranslateX(camera.getTranslateX() + dx);
}
if (camera.getTranslateY() + s * height + dy > height) {
camera.setTranslateY(height - s * height);
} else if ((camera.getTranslateY() + dy < 0)) {
camera.setTranslateY(0);
} else {
camera.setTranslateY(camera.getTranslateY() + dy);
}
But I wanted to not have the quality loss, so I rewrote it.
Now it scales the Pane, and not the camera.
I followed exactly the same code, only this time the scaling is reversed.
And when I calculate the position, I first set an offset tomimic my original code.
(The camera's pivot point is at 0,0, but the Pane's is in the center)
The quality loss is gone, but it doesnt really work:
double s = mapLayer.getScaleX();
double d = e.getDeltaY();
double f = 1.2;
double width = primaryScene.getWidth();
double height = primaryScene.getHeight();
double x = (e.getSceneX());
double y = (e.getSceneY());
if (d > 0) {
f = 2;
} else{
f = 0.5;
}
double ps = s;
mapLayer.setScaleX(s * f);
mapLayer.setScaleY(s * f);
mapLayer.setScaleZ(s * f);
s = mapLayer.getScaleX();
double dx = ps * (1 - f) * x;
double dy = ps * (1 - f) * y;
mapLayer.setTranslateX(mapLayer.getTranslateX()+ps*(width/2)*(f-1));
mapLayer.setTranslateY(mapLayer.getTranslateY()+ps*(height/2)*(f-1));
mapLayer.setTranslateX(mapLayer.getTranslateX()+dx);
mapLayer.setTranslateY(mapLayer.getTranslateY()+dy);
I found this, and it worked like a charm:
JavaFX 8 - Zooming Relative to Mouse Pointer. The solution now is this (without clamping):
double delta = 1.2;
double scale = mapLayer.getScaleX(); // currently we only use Y, same value is used for X
double oldScale = scale;
if (e.getDeltaY() < 0) {
scale /= delta;
} else {
scale *= delta;
}
double f = (scale / oldScale) - 1;
double dx = (e.getSceneX()
- (mapLayer.getBoundsInParent().getWidth() / 2
+ mapLayer.getBoundsInParent().getMinX()));
double dy = (e.getSceneY()
- (mapLayer.getBoundsInParent().getHeight() / 2
+ mapLayer.getBoundsInParent().getMinY()));
mapLayer.setScaleX(scale);
mapLayer.setScaleY(scale);
mapLayer.setScaleZ(scale);
mapLayer.setTranslateX(mapLayer.getTranslateX() - f * dx);
mapLayer.setTranslateY(mapLayer.getTranslateY() - f * dy);

Perlin noise glitch

I have an infinite map generator. It works well with positive coordinates.
Positive coordinates generation 1
But on negative coordinates I have this trash:
Negative coordinates broken generation
2
void generateChunk(int x0, int y0) {
Chunk chunk = new Chunk(x0, y0);
for(int yTile = 0; yTile < Chunk.CHUNK_SIZE; yTile++) {
for(int xTile = 0; xTile < Chunk.CHUNK_SIZE; xTile++) {
int pX = chunk.x0 + xTile;
int pY = chunk.y0 + yTile;
double perlinNoiseHeight = floorPerlinNoise.getHeight(pX, pY);;
if(perlinNoiseHeight > BEACH_AREA) {
chunk.setFloor(GrassTile.ID, xTile, yTile);
} else if(perlinNoiseHeight > 0) {
chunk.setFloor(SandTile.ID, xTile, yTile);
} else {
chunk.setFloor(WaterTile.ID, xTile, yTile);
}
}
}
saveChunk(chunk);
}
Perlin noise class:
package org.ixnomad.game.level.generation;
public class PerlinNoise {
private double persistence, frequency, amplitude;
private int octaves;
private long seed;
public PerlinNoise(double persistence, double frequency, double amplitude, int octaves, long seed) {
this.persistence = persistence;
this.amplitude = amplitude;
this.frequency = frequency;
this.octaves = octaves;
this.seed = seed;
}
public double getHeight(double x, double y) {
return amplitude * total(x, y) + 0.4;
}
private double total(double i, double j) {
double total = 0.0d;
double _ampl = 1;
double _freq = frequency;
for(int k = 0; k < octaves; k++) {
total += getValue(j * _freq + seed, i * _freq + seed) * _ampl;
_ampl *= persistence;
_freq *= 2;
}
return total;
}
private double getValue(double x, double y) {
int xInt = (int) x;
int yInt = (int) y;
double xFrac = x - xInt;
double yFrac = y - yInt;
double n01 = noise(xInt-1, yInt-1);
double n02 = noise(xInt+1, yInt-1);
double n03 = noise(xInt-1, yInt+1);
double n04 = noise(xInt+1, yInt+1);
double n05 = noise(xInt-1, yInt );
double n06 = noise(xInt+1, yInt );
double n07 = noise(xInt , yInt-1);
double n08 = noise(xInt , yInt+1);
double n09 = noise(xInt , yInt );
double n12 = noise(xInt+2, yInt-1);
double n14 = noise(xInt+2, yInt+1);
double n16 = noise(xInt+2, yInt );
double n23 = noise(xInt-1, yInt+2);
double n24 = noise(xInt+1, yInt+2);
double n28 = noise(xInt , yInt+2);
double n34 = noise(xInt+2, yInt+2);
double x0y0 = 0.0625*(n01+n02+n03+n04) + 0.125*(n05+n06+n07+n08) + 0.25*(n09);
double x1y0 = 0.0625*(n07+n12+n08+n14) + 0.125*(n09+n16+n02+n04) + 0.25*(n06);
double x0y1 = 0.0625*(n05+n06+n23+n24) + 0.125*(n03+n04+n09+n28) + 0.25*(n08);
double x1y1 = 0.0625*(n09+n16+n28+n34) + 0.125*(n08+n14+n06+n24) + 0.25*(n04);
double v1 = interpolate(x0y0, x1y0, xFrac);
double v2 = interpolate(x0y1, x1y1, xFrac);
return interpolate(v1, v2, yFrac);
}
private double interpolate(double x, double y, double frac) {
double negFrac = 1.0 - frac;
double negFraqSqr = negFrac * negFrac;
double fac1 = 3.0 * negFraqSqr - 2.0 * negFraqSqr * negFrac;
double fracSqr = frac * frac;
double fac2 = 3.0 * fracSqr - 2.0 * fracSqr * frac;
return x * fac1 + y * fac2;
}
private double noise(int x, int y) {
int n = x + y * 57;
n = (n << 13) ^ n;
int t = (n * (n * n * 15713 + 789221) + 1376312589) & 0x7fffffff;
return 1.0d - (double) (t * 0.931322574615478515625e-9);
}
}
I have tried to solve this problem for a whole day, but I don't know, what I'm doing wrong. I thought that the problem is in bit shift (private double noise(double x, double y)), but the problem doesn't depend on it.
I tried to use this Perlin's algoryth: https://mrl.nyu.edu/~perlin/noise/
But effect is the same.
Good morning, I probably have solved the problem :D Jush should sleep and reload brains)
Just use the max value the integer can bring and move generate coordinates to half :D
int maxint = 0x07fffffff;
double perlinNoiseHeight = floorPerlinNoise.getHeight(pX + (maxint >> 1), pY + (maxint >> 1));
That is a simple solution, but it works)
Negative coordinates

Find out if the user in specific region using GPS data

I want to find out if the user in specific region using GPS data and consider with accuracy info to reduce error, because the program will prompt an alert if user definitely out of the region.
GPS sensor return latitude, longitude and accuracy(in meter), I can draw a circle using those data:
135.500908,34.661964,30.0
There're array of coordinates in sequence represent the specific region:
135.500350,34.667011
135.506101,34.666853
135.505972,34.663076
135.505135,34.663111
135.504942,34.662387
135.504084,34.662440
135.504062,34.663146
135.502968,34.663217
135.502689,34.663764
135.502431,34.664205
135.502110,34.664646
135.501680,34.665105
135.501509,34.665246
135.500844,34.665229
135.500371,34.665511
My idea is to find out if any collisions of the polygon with the circle by using line-circle collisions detection algorithm, but it looks something wrong in my code, and seems I can't directly use that info due to radius/degree, could anyone help me out? Or let me know if any more simple solution?
public static boolean possiblyInside(List<Double> arrayX, List<Double> arrayY, double locationX, double locationY, double locationAccuracy) {
if (arrayX.size() != arrayY.size()) {
throw new IllegalArgumentException("Array length not equal");
}
boolean anyCircleLineIntersection = false;
if (arrayX.size() > 1) {
for (int i = 0; i < arrayX.size(); i++) {
double p1x = i == 0 ? arrayX.get(arrayX.size() - 1) : arrayX.get(i - 1);
double p1y = i == 0 ? arrayY.get(arrayY.size() - 1) : arrayY.get(i - 1);
double p2x = arrayX.get(i);
double p2y = arrayY.get(i);
if (circleLineIntersection(p1x, p1y, p2x, p2y, locationX, locationY, locationAccuracy)) {
anyCircleLineIntersection = true;
break;
}
}
}
return anyCircleLineIntersection;
}
private static boolean circleLineIntersection(double p1X, double p1Y, double p2X, double p2Y, double centerX, double centerY, double locationAccuracy) {
double rad = (180 / Math.PI);
double r = (locationAccuracy / 1000);
p1X = p1X * rad;
p1Y = p1Y * rad;
p2X = p2X * rad;
p2Y = p2Y * rad;
centerX = centerX * rad;
centerY = centerY * rad;
// Transform to local coordinates
double localP1X = p1X - centerX;
double localP1Y = p1Y - centerY;
double localP2X = p2X - centerX;
double localP2Y = p2Y - centerY;
// Pre-calculate this value. We use it often
double pDiffX = localP2X - localP1X;
double pDiffY = localP2Y - localP1Y;
double a = (pDiffX) * (pDiffX) + (pDiffY) * (pDiffY);
double b = 2 * ((pDiffX * localP1X) + (pDiffY * localP1Y));
double c = (localP1X * localP1X) + (localP1Y * localP1Y) - (r * r);
double delta = b * b - (4 * a * c);
return delta >= 0.0;
}
There is a method called Geofencing. Google already provides such functionality for you. And you don't have to deal with all these complex calculations.
You can fire events when the user entered a specific area / exited a specific area / after staying for some time in a specific area. Or you can make different combinations.
Here is an article of how you can use Geofencing. It consists of 4 separated articles.
Thank you for Todor Kostov's answer.
I know Android has provided Geofencing API, but it is not a perfect fit for my situation due to its implementation and limitations, and I would like to sync the algorithm with iOS version app as well. (Even I know the algorithm are not good as iOS or Android provided, and it also looks a bit silly).
Finally, I solved the problem in this way:
Ensure current location not inside the polygon (use point-in-polygon algorithm)
Loop through all line segment of the region polygon, find out the
closest coordinate(PointA) to the current location(PointB)
Calculate the distance between PointA and PointB, convert it to meter(X)
If X > location accuracy (also in meter), the user is definitely out of the particular region
p.s. I'm not good at math and geolocation, point out if any incorrect
public static boolean possiblyInside(List<Double> arrayX, List<Double> arrayY, double locationX, double locationY, double locationAccuracy) {
if (arrayX.size() != arrayY.size()) {
throw new IllegalArgumentException("Array length not equal");
}
if (arrayX.size() < 3) {
return false;
}
double minimumDistance = Double.MAX_VALUE;
for (int i = 0; i < arrayX.size(); i++) {
double p1x = i == 0 ? arrayX.get(arrayX.size() - 1) : arrayX.get(i - 1);
double p1y = i == 0 ? arrayY.get(arrayY.size() - 1) : arrayY.get(i - 1);
double p2x = arrayX.get(i);
double p2y = arrayY.get(i);
Coordinate closest = getClosestPointOnLine(p1x, p1y, p2x, p2y, locationX, locationY);
double currentDistance = distanceMeterBetweenPoints(closest.latitude, closest.longitude, locationX, locationY);
if (currentDistance < minimumDistance) {
minimumDistance = currentDistance;
}
}
return (minimumDistance <= locationAccuracy);
}
private static Coordinate getClosestPointOnLine(double sx1, double sy1, double sx2, double sy2, double px, double py) {
double xDelta = sx2 - sx1;
double yDelta = sy2 - sy1;
if ((xDelta == 0) && (yDelta == 0)) {
throw new IllegalArgumentException("Line start equals line end");
}
double u = ((px - sx1) * xDelta + (py - sy1) * yDelta) / (xDelta * xDelta + yDelta * yDelta);
final Coordinate closestPoint;
if (u < 0.0) {
closestPoint = new Coordinate(sx1, sy1);
} else if (u > 1.0) {
closestPoint = new Coordinate(sx2, sy2);
} else {
closestPoint = new Coordinate((int) Math.round(sx1 + u * xDelta), (int) Math.round(sy1 + u * yDelta));
}
return closestPoint;
}
public static double distanceMeterBetweenPoints(double aX, double aY, double bX, double bY) {
double rad = Math.PI / 180;
int r = 6371;
double dLat = (aX - bX) * rad;
double dLng = (aY - bY) * rad;
double x = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(aX * rad) * Math.cos(bX * rad) * Math.pow(Math.sin(dLng / 2), 2);
double y = 2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x));
return r * y * 1000;
}

Result of Quaternion Multiplication not yielding expected Rotation of local coordinate system

I'm getting unexpected results when multiplying two quaternions and applying the resulting rotation to my local right-handed coordinate system. (X pointing forward, Y to the right and Z downward).
(See my Java SCCE below)
So I am trying to first apply a Z rotation by 90 degrees (yaw) and then a rotation of 90 degrees around the local X axis (roll).
I am trying to accomplish this by multiplying two quaternions representing these two rotations, creating a rotation Matrix from the result and applying it to the 3 unit vectors of my coordinate system but the results I am getting do not make sense. (i.e. they do not represent the coordinate system you should get from these two rotations.)
I have tried changing the quaternion multiplication order which did not help (see code lines that were commented out in the main method of the SCCE).
I have also tried creating the quaternion for the second rotation from global Y to simulate that it was created from the resulting local coordinate system after the first rotation.
For reference I am also calculating the result by applying the two individual rotation matrices (which works as expected).
What am I doing wrong?
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class Quaternion {
public static final double NORMALIZATION_LOWER_TOLERANCE = 1 - 1e-4;
public static final double NORMALIZATION_UPPER_TOLERANCE = 1 + 1e-4;
private double w = 1.0;
private double x = 0.0;
private double y = 0.0;
private double z = 0.0;
public static void main(String[] args) {
Vector3D xVect = new Vector3D(1,0,0);
Vector3D yVect = new Vector3D(0,1,0);
Vector3D zVect = new Vector3D(0,0,1);
System.out.println("Initial Local Coordinate System: X:"+xVect+" / Y:"+yVect+ " / Z:"+zVect);
Quaternion rotZ = new Quaternion(Math.PI/2, zVect); // Yaw +90 deg
Quaternion rotY = new Quaternion(Math.PI/2, yVect); // Yaw +90 deg
Quaternion rotX = new Quaternion(Math.PI/2, xVect); // Then roll +90 deg
Matrix rotationMatrixZ = new Matrix(rotZ);
Vector3D localX = xVect.rotate(rotationMatrixZ);
Vector3D localY = yVect.rotate(rotationMatrixZ);
Vector3D localZ = zVect.rotate(rotationMatrixZ);
System.out.println("New Local Coordinate System after Yaw: X:"+localX+" / Y:"+localY+ " / Z:"+localZ); // Gives expected result
Quaternion localRotX = new Quaternion(Math.PI/2, localX);
Matrix localRotXMatrix = new Matrix(localRotX);
Vector3D rotatedX = localX.rotate(localRotXMatrix);
Vector3D rotatedY = localY.rotate(localRotXMatrix);
Vector3D rotatedZ = localZ.rotate(localRotXMatrix);
System.out.println("New Local Coordinate System two local rotations: X:"+rotatedX+" / Y:"+rotatedY+ " / Z:"+rotatedZ); // Gives expected result
Quaternion rotZX = rotZ.multiply(rotX);
// Quaternion rotZX = rotX.multiply(rotZ); // Tried both orders
// Quaternion rotZX = rotZ.multiply(rotY); // rotY is in fact the local rotX
// Quaternion rotZX = rotZ.multiply(rotY); // rotY is in fact the local rotX, tried both orders
rotZX.normalizeIfNeeded();
Matrix rotationXMatrixZX = new Matrix(rotZX);
rotatedX = xVect.rotate(rotationXMatrixZX);
rotatedY = localY.rotate(rotationXMatrixZX);
rotatedZ = localZ.rotate(rotationXMatrixZX);
System.out.println("New Local Coordinate System Quaternion Multiplication: X:"+rotatedX+" / Y:"+rotatedY+ " / Z:"+rotatedZ); // Expect same as above
}
public Quaternion() {
}
public Quaternion(double w, double x, double y, double z) {
this.w = w;
this.x = x;
this.y = y;
this.z = z;
}
public Quaternion(double angle, Vector3D vector){
double halfAngle = angle / 2;
double sin = Math.sin(halfAngle);
this.w = Math.cos(halfAngle);
this.x = vector.getX()*sin;
this.y = vector.getY()*sin;
this.z = vector.getZ()*sin;
}
public boolean normalizeIfNeeded() {
double sum = w * w + x * x + y * y + z * z;
if (NORMALIZATION_LOWER_TOLERANCE < sum && sum < NORMALIZATION_UPPER_TOLERANCE) {
return false;
}
double magnitude = Math.sqrt(sum);
w /= magnitude;
x /= magnitude;
y /= magnitude;
z /= magnitude;
return true;
}
public Quaternion multiply(Quaternion q2) {
Quaternion result = new Quaternion();
result.w = w * q2.w - x * q2.x - y * q2.y - z * q2.z;
result.x = w * q2.x + x * q2.w + y * q2.z - z * q2.y;
result.y = w * q2.y - x * q2.z + y * q2.w + z * q2.x;
result.z = w * q2.z + x * q2.y - y * q2.x + z * q2.w;
return result;
}
public Quaternion conjugate() {
return new Quaternion(w, -x, -y, -z);
}
public double getW() {
return w;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getZ() {
return z;
}
#Override
public String toString() {
return "Quaternion [w=" + w + ", x=" + x + ", y=" + y + ", z=" + z + "]";
}
static class Vector3D {
double x=0;
double y=0;
double z=0;
public Vector3D(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Vector3D rotate(Matrix rotationMatrix){
return rotationMatrix.multiply(this);
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getZ() {
return z;
}
#Override
public String toString() {
NumberFormat df = DecimalFormat.getNumberInstance();
return "[x=" + df.format(x) + ", y=" + df.format(y) + ", z=" + df.format(z) + "]";
}
}
static class Matrix {
private double[][] values;
public Matrix(int rowCount, int colCount) {
values = new double[rowCount][colCount];
}
public Matrix(Quaternion quaternionForRotationMatrix) {
this(3,3);
double w = quaternionForRotationMatrix.getW();
double x = quaternionForRotationMatrix.getX();
double y = quaternionForRotationMatrix.getY();
double z = quaternionForRotationMatrix.getZ();
double ww = w*w;
double wx = w*x;
double xx = x*x;
double xy = x*y;
double xz = x*z;
double wy = w*y;
double yy = y*y;
double yz = y*z;
double wz = w*z;
double zz = z*z;
values[0][0] = ww + xx - yy - zz;
values[0][1] = 2 * xy - 2 * wz;
values[0][2] = 2 * xz + 2 * wy;
values[1][0] = 2 * xy + 2 * wz;
values[1][1] = ww - xx + yy - zz;
values[1][2] = 2 * yz + 2 * wx;
values[2][0] = 2 * xz - 2 * wy;
values[2][1] = 2 * yz - 2 * wx;
values[2][2] = ww - xx - yy + zz;
}
public Vector3D multiply(Vector3D vector){
double [][] vect = new double [3][1];
vect[0][0] = vector.getX();
vect[1][0] = vector.getY();
vect[2][0] = vector.getZ();
double [][] result = multiplyMatrices(values, vect);
return new Vector3D(result[0][0], result[1][0], result[2][0]);
}
private double[][] multiplyMatrices(double[][] m1, double[][] m2) {
double[][] result = null;
if (m1[0].length == m2.length) {
int rowCount1 = m1.length;
int colCount1 = m1[0].length;
int rowCount2 = m2[0].length;
result = new double[rowCount1][rowCount2];
for (int i = 0; i < rowCount1; i++) {
for (int j = 0; j < rowCount2; j++) {
result[i][j] = 0;
for (int k = 0; k < colCount1; k++) {
result[i][j] += m1[i][k] * m2[k][j];
}
}
}
} else {
int rowCount = m1.length;
int colCount = m1[0].length;
result = new double[rowCount][colCount];
for (int i = 0; i < m1.length; i++) {
for (int j = 0; j < m1[0].length; j++) {
result[i][j] = 0;
}
}
}
return result;
}
#Override
public String toString() {
StringBuffer sb = new StringBuffer("Matrix = ");
for(int row = 0 ; row<values.length; row++){
sb.append ("[ ");
for(int col = 0 ; col<values[0].length; col++){
sb.append(Double.toString(values[row][col]));
if(col<values.length-1){
sb.append(" | ");
}
}
sb.append("] ");
}
return sb.toString();
}
}
}
Nevermind. Found it. I had an error in the formulas to build the rotation matrix. It now works as expected.
I am making a mental note to use formulas from Wikipedia in the future and not some random other site.
The respective part should be
values[0][0] = ww + xx - yy - zz;
values[0][1] = 2 * xy - 2 * wz;
values[0][2] = 2 * xz + 2 * wy;
values[1][0] = 2 * xy + 2 * wz;
values[1][1] = ww - xx + yy - zz;
values[1][2] = 2 * yz - 2 * wx; //CORRECTED SIGN
values[2][0] = 2 * xz - 2 * wy;
values[2][1] = 2 * yz + 2 * wx; //CORRECTED SIGN
values[2][2] = ww - xx - yy + zz;
At the end of the main method I was also using the wrong vectors for y and z:
Matrix rotationXMatrixZX = new Matrix(rotZX);
rotatedX = xVect.rotate(rotationXMatrixZX);
rotatedY = yVect.rotate(rotationXMatrixZX); // Corrected used y-vector
rotatedZ = zVect.rotate(rotationXMatrixZX); // Corrected used z-vector

Java, Colours of a buffered image are completely different to the original image

I am trying to add some texture to my game. I am running into some problems getting the image to display properly.
This is what the texture should look like, just a boring black square:
And this is what I get. A little bit of black with blue lines.
This is the code I used to import the image. The BufferedImage is set to Type_INT_RGB:
package com.mime.minefront.graphics;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class Texture {
public static Render floor = loadBitmap("/textures/floorb.png");
public static Render loadBitmap(String fileName) {
try {
BufferedImage image = ImageIO.read(Texture.class.getResource(fileName));
int width = image.getWidth();
int height = image.getHeight();
Render result = new Render(width, height);
image.getRGB(0, 0, width, height, result.pixels, 0, width);
return result;
} catch (Exception e) {
System.out.println("CRASH!");
throw new RuntimeException(e);
}
}
}
Any help or advice would be great. I have tried to search for the answer but with no luck.
This is my Render class.
package com.mime.minefront.graphics;
public class Render {
public final int width;
public final int height;
public final int[] pixels;
public Render(int width, int height) {
this.width = width;
this.height = height;
pixels = new int[width * height];
}
public void draw(Render render, int xOffset, int yOffset) {
for (int y = 0; y < render.height; y++) {
int yPix = y + yOffset;
if (yPix < 0 || yPix >= height) {
continue;
}
for (int x = 0; x < render.width; x++) {
int xPix = x + xOffset;
if (xPix < 0 || xPix >= width) {
continue;
}
int aplha = render.pixels[x + y * render.width];
if (aplha > 0) {
pixels[xPix + yPix * width] = aplha;
}
}
}
}
}
and this is my Render3D class
package com.mime.minefront.graphics;
import com.mime.minefront.Game;
import com.mimi.minefront.input.Controller;
import com.mimi.minefront.input.InputHandler;
import java.awt.Robot;
import java.util.Random;
public class Render3D extends Render {
public double[] zBuffer;
private double renderDistance = 5000;
private double forward, right, up, cosine, sine;
public Render3D(int width, int height) {
super(width, height);
zBuffer = new double[width * height];
}
public void floor(Game game) {
double floorPosition = 8;
double cellingPosition = 8;
forward = game.controls.z;
right = game.controls.x;
up = game.controls.y;
double walking = Math.sin(game.time / 6.0) * 0.5;
if (Controller.crouchWalk) {
walking = Math.sin(game.time / 6.0) * 0.25;
}
if (Controller.runWalk) {
walking = Math.sin(game.time / 6.0) * 0.8;
}
double rotation = 0;//Math.sin(game.time / 20) * 0.5; //game.controls.rotation;
cosine = Math.cos(rotation);
sine = Math.sin(rotation);
for (int y = 0; y < height; y++) {
double celling = (y - height / 2.0) / height;
double z = (floorPosition + up) / celling;
if (Controller.walk) {
z = (floorPosition + up + walking) / celling;
}
if (celling < 0) {
z = (cellingPosition - up) / -celling;
if (Controller.walk) {
z = (cellingPosition - up - walking) / -celling;
}
}
for (int x = 0; x < width; x++) {
double depth = (x - width / 2.0) / height;
depth *= z;
double xx = depth * cosine + z * sine;
double yy = z * cosine - depth * sine;
int xPix = (int) (xx + right);
int yPix = (int) (yy + forward);
zBuffer[x + y * width] = z;
pixels[x + y * width] = //((xPix & 15) * 16 | ((yPix % 15) * 16) << 8);
Texture.floor.pixels[xPix & 7] + (yPix & 7) * 8;
if (z > 500) {
pixels[x + y * width] = 0;
}
}
}
}
public void renderWall(double xLeft, double xRight, double zDistance, double yHeight) {
double xcLeft = ((xLeft) - right) * 2;
double zcLeft = ((zDistance) - forward) * 2;
double rotLeftSideX = xcLeft * cosine - zcLeft * sine;
double yCornerTL = ((-yHeight) - up) * 2;
double yCornerBL = ((+0.5 - yHeight) - up) * 2;
double rotLeftSideZ = zcLeft * cosine + xcLeft * sine;
double xcRight = ((xRight) - right) * 2;
double zcRight = ((zDistance) - forward) * 2;
double rotRightSideX = xcRight * cosine - zcLeft * sine;
double yCornerTR = ((-yHeight) - up) * 2;
double yCornerBR = ((+0.5 - yHeight) - up) * 2;
double rotRightSideZ = zcRight * cosine + xcRight * sine;
double xPixelLeft = (rotLeftSideX / rotLeftSideZ * height + width / 2);
double xPixelRight = (rotRightSideX / rotRightSideZ * height + width / 2);
if (xPixelLeft >= xPixelRight) {
return;
}
int xPixelLeftInt = (int) (xPixelLeft);
int xPixelRightInt = (int) (xPixelRight);
if (xPixelLeftInt < 0) {
xPixelLeftInt = 0;
}
if (xPixelRightInt > width) {
xPixelRightInt = width;
}
double yPixelLeftTop = (yCornerTL / rotLeftSideZ * height + height / 2);
double yPixelLeftBottom = (yCornerBL / rotLeftSideZ * height + height / 2);
double yPixelRightTop = (yCornerTR / rotRightSideZ * height + height / 2);
double yPixelRightBottom = (yCornerBR / rotRightSideZ * height + height / 2);
double tex1 = 1 / rotLeftSideZ;
double tex2 = 1 / rotRightSideZ;
double tex3 = 0 / rotLeftSideZ;
double tex4 = 8 / rotRightSideZ - tex3;
for (int x = xPixelLeftInt; x < xPixelRightInt; x++) {
double pixelRotation = (x - xPixelLeft) / (xPixelRight - xPixelLeft);
double xTexture= (int) ((tex3+tex4*pixelRotation)/tex1+(tex2-tex1)*pixelRotation);
double yPixelTop = yPixelLeftTop + (yPixelRightTop - yPixelLeftTop) * pixelRotation;
double yPixelBottom = yPixelLeftBottom + (yPixelRightBottom - yPixelLeftBottom) * pixelRotation;
int yPixelTopInt = (int) (yPixelTop);
int yPixelBottomInt = (int) (yPixelBottom);
if (yPixelTopInt < 0) {
yPixelTopInt = 0;
}
if (yPixelBottomInt > height) {
yPixelBottomInt = height;
}
for (int y = yPixelTopInt; y < yPixelBottomInt; y++) {
pixels[x + y * width] = (int) xTexture*100;
zBuffer[x + y * width] = 0;
}
}
}
public void renderDistanceLimiter() {
for (int i = 0; i < width * height; i++) {
int colour = pixels[i];
int brightness = (int) (renderDistance / (zBuffer[i]));
if (brightness < 0) {
brightness = 0;
}
if (brightness > 255) {
brightness = 255;
}
int r = (colour >> 16) & 0xff;
int g = (colour >> 8) & 0xff;
int b = (colour) & 0xff;
r = r * brightness / 255;
g = g * brightness / 255;
b = b * brightness / 255;
pixels[i] = r << 16 | g << 8 | b;
}
}
}
From getRGB() :
Returns an array of integer pixels in the default RGB color model
(TYPE_INT_ARGB) and default sRGB color space, from a portion of the
image data. Color conversion takes place if the default model does not
match the image ColorModel
See if using TYPE_INT_ARGB instead of TYPE_INT_RGB works.

Categories