Calculating Angle/Curvature? - c++

I'm trying to apply the Gauss-Bonnet theorem to my C++ OpenGL application and compute the value of the interior angle at vertex Vi in the neighboring triangle Fi in my mesh.
I did some searching before making this post, and I know that to do this for a 2D model, one could use the function below to get the angles:
void angles(double points[][2], double angles[], int npoints){
for(int i = 0; i < npoints; i++){
int last = (i - 1 + npoints) % npoints;
int next = (i + 1) % npoints;
double x1 = points[i][0] - points[last][0];
double y1 = points[i][1] - points[last][1];
double x2 = points[next][0] - points[i][0];
double y2 = points[next][1] - points[i][1];
double theta1 = atan2(y1, x1)*180/3.1415926358979323;
double theta2 = atan2(y2, x2)*180/3.1415926358979323;
angles[i] = (180 + theta1 - theta2 + 360);
while(angles[i]>360)angles[i]-=360;
} }
But how can I find the angles with a 3D mesh (x, y, and z) vertices?

The analogous concept in 3D is called Gaussian curvature. The situation is much more complicated than 2D, and there is no single good way of calculating or estimating the Gaussian curvature for a mesh. There's a survey paper that may give you some ideas.

Related

Perlin Noise algorithm does not seem to produce gradient noise

I am attempting to implement Perlin Noise in c++.
Firstly, the problem (I think) is that the output is not what I expect. Currently I simply use the generated Perlin Noise values in a greyscaled image, and this is the results I get:
However, from my understanding, it's supposed to look more along the lines of:
That is, the noise I am producing currently seems to be more along the lines of "standard" irregular noise.
This is the Perlin Noise Algorithm I have implemented so far:
float perlinNoise2D(float x, float y)
{
// Find grid cell coordinates
int x0 = (x > 0.0f ? static_cast<int>(x) : (static_cast<int>(x) - 1));
int x1 = x0 + 1;
int y0 = (y > 0.0f ? static_cast<int>(y) : (static_cast<int>(y) - 1));
int y1 = y0 + 1;
float s = calculateInfluence(x0, y0, x, y);
float t = calculateInfluence(x1, y0, x, y);
float u = calculateInfluence(x0, y1, x, y);
float v = calculateInfluence(x1, y1, x, y);
// Local position in the grid cell
float localPosX = 3 * ((x - (float)x0) * (x - (float)x0)) - 2 * ((x - (float)x0) * (x - (float)x0) * (x - (float)x0));
float localPosY = 3 * ((y - (float)y0) * (y - (float)y0)) - 2 * ((y - (float)y0) * (y - (float)y0) * (y - (float)y0));
float a = s + localPosX * (t - s);
float b = u + localPosX * (v - u);
return lerp(a, b, localPosY);
}
The function calculateInfluence has the job of generating the random gradient vector and distance vector for one of the corner points of the current grid cell and returning the dot product of these. It is implemented as:
float calculateInfluence(int xGrid, int yGrid, float x, float y)
{
// Calculate gradient vector
float gradientXComponent = dist(rdEngine);
float gradientYComponent = dist(rdEngine);
// Normalize gradient vector
float magnitude = sqrt( pow(gradientXComponent, 2) + pow(gradientYComponent, 2) );
gradientXComponent = gradientXComponent / magnitude;
gradientYComponent = gradientYComponent / magnitude;
magnitude = sqrt(pow(gradientXComponent, 2) + pow(gradientYComponent, 2));
// Calculate distance vectors
float dx = x - (float)xGrid;
float dy = y - (float)yGrid;
// Compute dot product
return (dx * gradientXComponent + dy * gradientYComponent);
}
Here, dist is a random number generator from C++11:
std::mt19937 rdEngine(1);
std::normal_distribution<float> dist(0.0f, 1.0f);
And lerp is simply implemented as:
float lerp(float v0, float v1, float t)
{
return ( 1.0f - t ) * v0 + t * v1;
}
To implement the algorithm, I primarily made use of the following two resources:
Perlin Noise FAQ
Perlin Noise Pseudo Code
It's difficult for me to pinpoint exactly where I seem to be messing up. It could be that I am generating the gradient vectors incorrectly, as I'm not quite sure what type of distribution they should have. I have tried with a uniform distribution, however this seemed to generate repeating patterns in the texture!
Likewise, it could be that I am averaging the influence values incorrectly. It has been a bit difficult to discern exactly how it should be done from from the Perlin Noise FAQ article.
Does anyone have any hints as to what might be wrong with the code? :)
It seems like you are only generating a single octave of Perlin Noise. To get a result like the one shown, you need to generate multiple octaves and add them together. In a series of octaves, each octave should have a grid cell size double that of the last.
To generate multi-octave noise, use something similar to this:
float multiOctavePerlinNoise2D(float x, float y, int octaves)
{
float v = 0.0f;
float scale = 1.0f;
float weight = 1.0f;
float weightTotal = 0.0f;
for(int i = 0; i < octaves; i++)
{
v += perlinNoise2D(x * scale, y * scale) * weight;
weightTotal += weight;
// "ever-increasing frequencies and ever-decreasing amplitudes"
// (or conversely decreasing freqs and increasing amplitudes)
scale *= 0.5f;
weight *= 2.0f;
}
return v / weightTotal;
}
For extra randomness you could use a differently seeded random generator for each octave. Also, the weights given to each octave can be varied to adjust the aesthetic quality of the noise. If the weight variable is not adjusted each iteration, then the example above is "pink noise" (each doubling of frequency carries the same weight).
Also, you need to use a random number generator that returns the same value each time for a given xGrid, yGrid pair.

Missing vertices in sphere model

I'm learning OpenGL and I'm working on creating my own sphere model. I was able to draw a complete sphere, although with some puzzling results. I'm wondering if someone can explain (and possibly correct) my code.
The rationale: build triangles using carthesian coordinates calculated from polar coordinates. The number of subdivisions tells me the steps in phi or theta radians to generate the sphere point. From a particular point P(phi, theta), I build the other edges of the sector for [phi, delta_phi], [theta, delta_tetha], with phi varying from [0, pi] (180 degrees) and tetha from [0, 2*pi] (360 degrees).
This is the code I came up with (I'm using QT objects, but it should be pretty straitghforward):
QVector3D polarToCarthesian(float rho, float phi, float theta)
{
float r = qSin(phi) * rho;
float y = qCos(phi) * rho;
float x = r * qSin(theta);
float z = r * qCos(theta);
return QVector3D{x, y, z};
}
void make_sector(QVector<QVector3D>& mesh, float phi, float theta, float rho, float deltaPhi, float deltaTheta)
{
QVector3D p1 = polarToCarthesian(rho, phi, theta);
QVector3D p2 = polarToCarthesian(rho, phi, theta + deltaTheta);
QVector3D p3 = polarToCarthesian(rho, phi + deltaPhi, theta);
QVector3D p4 = polarToCarthesian(rho, phi + deltaPhi, theta + deltaTheta);
// First Triangle
mesh.push_back(p1);
mesh.push_back(p1); // Normal
mesh.push_back(p3);
mesh.push_back(p3); // Normal
mesh.push_back(p2);
mesh.push_back(p2); // Normal
// Second Triangle
mesh.push_back(p2);
mesh.push_back(p2); // Normal
mesh.push_back(p3);
mesh.push_back(p3); // Normal
mesh.push_back(p4);
mesh.push_back(p4); // Normal
}
void build_sphere(QVector<QVector3D>& mesh, int ndiv)
{
const float PHI_MAX = static_cast<float>(M_PI);
const float THETA_MAX = static_cast<float>(M_PI) * 2;
const float delta_phi = PHI_MAX / ndiv;
const float delta_theta = THETA_MAX / ndiv;
for (int i = 0; i < ndiv; ++i) {
float phi = i * delta_phi;
for (int j = 0; j < ndiv; ++j) {
float theta = j * delta_theta;
make_sector(mesh, phi, theta, 1.0f, delta_phi, delta_theta);
}
}
}
// Then I can generate the sphere with
build_sphere(sphere_mesh, 10);
However, I cannot get a complete sphere unless I change the iteration for phi from ndiv iterations to 3 * ndiv iterations. I don't understand why! Phi should vary from 0 to PI to cover the whole Y axis while Theta from 0 to 2 * pi should cover the XZ plane.
Can somebody explain what's happening and why 3 * ndiv works?
phi should go from -π/2 to +π/2, not from 0 to π:
float phi = i * delta_phi - (M_PI / 2);
Also, you appear to have your r and y calculations the wrong way around. You want r to be maximum at the equator (when phi == 0).
I think your code may have worked (albeit producing twice as many polygons as it should have) if you had stuck at 2 * ndiv. As it is, going from 0 to π only puts polygons in the northern hemisphere, so you have to keep going beyond that to have any polygons in the southern hemisphere.
p.s. there's no 'h' in cartesian ;)

interior angles of irregular polygon with angles > 180

I'm trying to calculate the values shown in the picture in red i.e. the interior angles.
I've got an array of the points where lines intersect and have tried using the dot-product but it only returns the smallest angles. I need the full range of internal angles (0-359) but can't seem to find much that meets this criteria.
Assuming your angles are in standard counterclockwise format, the following should work:
void angles(double points[][2], double angles[], int npoints){
for(int i = 0; i < npoints; i++){
int last = (i - 1 + npoints) % npoints;
int next = (i + 1) % npoints;
double x1 = points[i][0] - points[last][0];
double y1 = points[i][1] - points[last][1];
double x2 = points[next][0] - points[i][0];
double y2 = points[next][1] - points[i][1];
double theta1 = atan2(y1, x1)*180/3.1415926358979323;
double theta2 = atan2(y2, x2)*180/3.1415926358979323;
angles[i] = (180 + theta1 - theta2 + 360);
while(angles[i]>360)angles[i]-=360;
}
}
Obviously, if you are using some sort of data structure for your points, you will want to replace double points[][2] and references to it with references to your data structure.
You can obtain full angle range (-Pi..Pi) with atan2 function:
atan2(crossproduct, dotproduct)

two circles collision

I have been testing collision between two circles using the method:
Circle A = (x1,y1) Circle b = (x2,y2)
Radius A Radius b
x1 - x2 = x' * x'
y1 - y2 = y' * y'
x' + y' = distance
square root of distance - Radius A + Radius B
and if the resulting answer is a negative number it is intersecting.
I have used this method in a test but it doesn't seem to be very accurate at all.
bool circle::intersects(circle & test)
{
Vector temp;
temp.setX(centre.getX() - test.centre.getX());
temp.setY(centre.getY() - test.centre.getY());
float distance;
float temp2;
float xt;
xt = temp.getX();
temp2 = xt * xt;
temp.setX(temp2);
xt = temp.getY();
temp2 = xt * xt;
temp.setY(temp2);
xt = temp.getX() + temp.getY();
distance = sqrt(xt);
xt = radius + test.radius;
if( distance - xt < test.radius)
{
return true;
}
else return false;
}
This is the function using this method maybe I'm wrong here. I just wondered what other methods I could use. I know separating axis theorem is better , but I wouldn't know where to start.
if( distance - xt < test.radius)
{
return true;
}
distance - xt will evaluate to the blue line, the distance between the two disks. It also meets the condition of being less than the test radius, but there is no collision going on.
The solution:
if(distance <= (radius + test.radius) )
return true;
Where distance is the distance from the centres.
Given: xt = radius + test.radius;
The correct test is: if( distance < xt)
Here is an attempt to re-write the body for you: (no compiler, so may be errors)
bool circle::intersects(circle & test)
{
float x = this->centre.getX() - test.centre.getX()
float y = this->centre.getY() - test.centre.getY()
float distance = sqrt(x*x+y*y);
return distance < (this->radius + test.radius);
}
Based on Richard solution but comparing the squared distance. This reduce the computation errors and the computation time.
bool circle::intersects(circle & test)
{
float x = this->centre.getX() - test.centre.getX()
float y = this->centre.getY() - test.centre.getY()
float distance2 = x * x + y * y;
float intersect_distance2 = (this->radius + test.radius) * (this->radius + test.radius);
return distance <= intersect_distance2;
}
Use Pythagoras theorem to compute the distance between the centres
That is a straight line
If they have collided then that distance is shorter that the sum of the two radiuses

UV mapping for a dome?

I am trying to understand how can I change UV mapping of a dome, I need a different texture map projection than this one coded below:
protected final void createDome(final float radius) {
int lats=16;
int longs=16;
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textures2x4[0].getTextureID());
int i, j;
int halfLats = lats / 2;
for(i = 0; i <= halfLats; i++)
{
double lat0 = MathUtils.PI * (-0.5 + (double) (i - 1) / lats);
double z0 = Math.sin(lat0)* radius;
double zr0 = Math.cos(lat0)* radius;
double lat1 = MathUtils.PI * (-0.5 + (double) i / lats);
double z1 = Math.sin(lat1)* radius;
double zr1 = Math.cos(lat1)* radius;
GL11.glBegin(GL11.GL_QUAD_STRIP);
for(j = 0; j <= longs; j++)
{
double lng = 2 * MathUtils.PI * (double) (j - 1) / longs;
double x = Math.cos(lng);
double y = Math.sin(lng);
double s1, s2, t;
s1 = ((double) i) / halfLats;
s2 = ((double) i + 1) / halfLats;
t = ((double) j) / longs;
// HERE: I don't know how to calculate the UV mapping
GL11.glTexCoord2d(s1, t);
GL11.glNormal3d(x * zr0, y * zr0, z0);
GL11.glVertex3d(x * zr0, y * zr0, z0);
GL11.glTexCoord2d(s2, t);
GL11.glNormal3d(x * zr1, y * zr1, z1);
GL11.glVertex3d(x * zr1, y * zr1, z1);
}
GL11.glEnd();
}
}
I linked the output image and the original map. Pratically I need a UV mapping which places the Artic at the zenith/top of the dome, and the Antartic streched on the bottom side of the dome... the Artic/Antartic map is only used to figure out what I mean, my need it's not to fit a globe emisphere
Output image http://img831.imageshack.us/img831/3481/lwjgl.png
Source map http://img203.imageshack.us/img203/4930/earthc.png
Take a look at this function calls (disclaimer: untested - I haven't used LWJGL, but the concept should be identical):
GL11.glMatrixMode(GL11.GL_TEXTURE);
GL11.glRotate(90, 0, 0, 1); // (1) Here you transform texture space
GL11.glMatrixMode(GL11.GL_MODELVIEW);
// and so on
Basically, you need to rotate texture on object. And that's the way you do it - transform texture projection matrix. The line (1) rotates texture 90 degrees along Z axis (perpendicular to texture plane). It's Z axis, because the last argument is 1. Last three arguments denote X, Y and Z respectively (I'll leave the whole explanation for later if you're interested).
The best You can do is to grasp all the basic stuff (projection, texture space, normal vectors, triangulation, continuity, particle systems and a lot more) is to download some trial version of a 3d package and play with it. I learned a lot just out of playing with 3D Studio Max (trial version available, and many more for free). If you have some free time and will to learn something new I strongly advise to look into it. In the end, if You're really interested in 3D graphics You'll end up using one any way - be it 3d package or game engine level editor.
EDIT: After more reading I recognized my own code... Basically you could only swap some of the coordinates to reflect symmetrically along diagonal. You might end up upside down, but that can also be fixed with additional tweaking (or transforming the view axis). Here is my untested guess:
// tweaked to get pole right
s1 = ((double) j) / longs;
s2 = ((double) j + 1) / longs;
t = ((double) i) / halfLats;
Try swapping s1 with s2 if it's not right.