Related
I have built a Bezier Curve tool and each time the coordinates for each curve segment are calculated they are stored in a vector. Every single frame I am adding the entire curve's points on it over and over again. That is, frame 1, might have { p1, p2, p3 } then frame 2, it would have { p1, p2, p3, p1, p2, p3 } and so on. This would cause the line to loop back on itself when the loop at the end of the render function draws lines between the points p3 and p1. I am struggling to find where and how I should clear my BezierCurve vector between frames. Clearing after glClear() or before glSwapBuffers() only shows the previously drawn curve segment and has a straight line between points.
I basically want the straight line in between points to go away and I know why it is happening. My code is below:
#include <iostream>
#include <stdlib.h>
#include <GL/glut.h>
#include <vector>
#include <math.h>
using namespace std;
//Point class for taking the points
class Point {
public:
float x, y;
void setxy(float x2, float y2)
{
x = x2; y = y2;
}
//operator overloading for '=' sign
const Point& operator=(const Point& rPoint)
{
x = rPoint.x;
y = rPoint.y;
return *this;
}
};
int SCREEN_HEIGHT = 500;
vector<Point> Points;
Point Tangent;
Point inverseTangent;
Point cursorLocationLive;
int TangentsSize = 0;
vector<Point> Tangents(TangentsSize);
vector<Point> inverseTangents(TangentsSize);
vector<Point> BezierCurve;
bool MouseReleased = false;
void drawDot(Point p1)
{
glBegin(GL_POINTS);
glVertex2i(p1.x, p1.y);
glEnd();
}
void drawLine(Point p1, Point p2)
{
glBegin(GL_LINE_STRIP);
glVertex2f(p1.x, p1.y);
glVertex2f(p2.x, p2.y);
glEnd();
}
float interpolate(float n1, float n2, float perc)
{
float diff = n2 - n1;
return n1 + (diff * perc);
}
void myMouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON)
{
if (state == GLUT_DOWN)
{
MouseReleased = false;
// Store points into Points vector on click
Point point;
point.setxy(x, SCREEN_HEIGHT - y);
Points.push_back(point);
// Tangents are set to the cursor position
Tangent.setxy(x, SCREEN_HEIGHT - y);
inverseTangent.x = (2 * Points[Points.size() - 1].x) - Tangent.x;
inverseTangent.y = (2 * Points[Points.size() - 1].y) - Tangent.y;
/*Add new element to Tangent & inverseTangent so when we draw the curve
the tangents are accessed at the right index*/
TangentsSize++;
}
else if (state == GLUT_UP)
{
MouseReleased = true;
// Upon mouse release store tangent and inverse tangent into separate vectors
Tangents.push_back(Tangent);
inverseTangents.push_back(inverseTangent);
}
}
}
void passiveMotion(int x, int y)
{
// Sets the location of cursor while moving with no buttons pressed
cursorLocationLive.setxy(x, SCREEN_HEIGHT - y);
}
void motion(int x, int y)
{
// Sets the coordinates of the tangents when mouse moves with a button held down
Tangent.setxy(x, SCREEN_HEIGHT - y);
inverseTangent.x = (2 * Points[Points.size() - 1].x) - Tangent.x;
inverseTangent.y = (2 * Points[Points.size() - 1].y) - Tangent.y;
}
void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
// Draw main points in red
glColor3f(255, 0, 0);
for (int i = 0; i < Points.size(); i++)
{
drawDot(Points[i]);
}
// If there is a starting point draw a line to cursor from last drawn point in passive motion
if (Points.size() > 0)
{
glColor3f(0, 0, 0);
drawLine(Points[Points.size() - 1], cursorLocationLive);
}
// Draw live tangent dots in green
glColor3f(0, 255, 0);
drawDot(Tangent);
drawDot(inverseTangent);
// Draw live tangent lines in blue
glColor3f(0, 0, 255);
drawLine(Tangent, inverseTangent);
for (int i = 0; i < Tangents.size(); i++)
{
// Draw stored tangent dots in green
glColor3f(0, 255, 0);
drawDot(Tangents[i]);
drawDot(inverseTangents[i]);
// Draw stored tangent lines in blue
glColor3f(0, 0, 255);
drawLine(Tangents[i], inverseTangents[i]);
}
// Loop through all points
for (int i = 0; i < Points.size(); i++)
{
// If there are two points draw the first segment
if (Points.size() == 2)
{
// p1 is the start of the curve set at first point
Point p1;
p1 = Points[0];
float i;
// Calculate curve coordinates
for (float j = 0; j <= 100; j++)
{
i = j / 100;
// The Green Lines
float xa = interpolate(Points[0].x, inverseTangents[0].x, i);
float ya = interpolate(Points[0].y, inverseTangents[0].y, i);
float xb = interpolate(inverseTangents[0].x, inverseTangent.x, i);
float yb = interpolate(inverseTangents[0].y, inverseTangent.y, i);
float xc = interpolate(inverseTangent.x, Points[1].x, i);
float yc = interpolate(inverseTangent.y, Points[1].y, i);
// The Blue Line
float xm = interpolate(xa, xb, i);
float ym = interpolate(ya, yb, i);
float xn = interpolate(xb, xc, i);
float yn = interpolate(yb, yc, i);
// The Black Dot
float x2 = interpolate(xm, xn, i);
float y2 = interpolate(ym, yn, i);
Point p2;
p2.setxy(x2, y2);
drawLine(p1, p2);
p1 = p2;
// Prevents curves generated during mouse motion from being stored
if (MouseReleased)
{
// Store curvature into Bezier Points
BezierCurve.push_back(p2);
}
}
}
// Second segment onwards
else if (Points.size() > 2)
{
// p1 is the start of the curve set to second last point
Point p1;
p1 = Points[Points.size() - 2];
float i;
// Calculate curve coordinates
for (float j = 0; j <= 100; j++)
{
i = j / 100;
// The Green Lines
float xa = interpolate(Points[Points.size() - 2].x, Tangents[TangentsSize - 2].x, i);
float ya = interpolate(Points[Points.size() - 2].y, Tangents[TangentsSize - 2].y, i);
float xb = interpolate(Tangents[TangentsSize - 2].x, inverseTangent.x, i);
float yb = interpolate(Tangents[TangentsSize - 2].y, inverseTangent.y, i);
float xc = interpolate(inverseTangent.x, Points[Points.size() - 1].x, i);
float yc = interpolate(inverseTangent.y, Points[Points.size() - 1].y, i);
// The Blue Line
float xm = interpolate(xa, xb, i);
float ym = interpolate(ya, yb, i);
float xn = interpolate(xb, xc, i);
float yn = interpolate(yb, yc, i);
// The Black Dot
float x2 = interpolate(xm, xn, i);
float y2 = interpolate(ym, yn, i);
Point p2;
p2.setxy(x2, y2);
drawLine(p1, p2);
p1 = p2;
// Prevents curves generated during mouse motion from being stored
if (MouseReleased)
{
// Store curvature into Bezier Points
BezierCurve.push_back(p2);
}
}
}
}
// Draw all bezier curvature
for (int i = 1; i < BezierCurve.size(); i++)
{
drawLine(BezierCurve[i - 1], BezierCurve[i]);
}
glutSwapBuffers();
}
void timer(int)
{
glutTimerFunc(1000 / 60, timer, 0);
glutPostRedisplay();
}
int main(int argc, char* argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(640, 500);
glutInitWindowPosition(100, 150);
glutCreateWindow("Bezier Curve");
glutDisplayFunc(myDisplay);
glutIdleFunc(myDisplay);
glutTimerFunc(0, timer, 0);
glutMouseFunc(myMouse);
glutPassiveMotionFunc(passiveMotion);
glutMotionFunc(motion);
glClearColor(255, 255, 255, 0.0);
glPointSize(3);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 640.0, 0.0, 500.0);
glutMainLoop();
return 0;
}
You will want to detect when mouse click changes from down to up:
bool prevMouse;
...
// at the end of Display()
prevMouse = MouseReleased;
Then we check when mouse click changes from pressed to released and add lines to BezierCurve:
if (PrevMouse == 0 && MouseReleased)
{
// Store curvature into Bezier Points
BezierCurve.push_back(p2);
}
The two code paths, if (Points.size() == 2), and else if (Points.size() > 2) inside the for loop can be simplified to if (Points.size() >= 2) and for that matter the for loop is extraneous, we don't need to update the bezier curve for any of the previous points, just the curve between the two newest points, Points[Points.size() - 2] and Points[Points.size() - 1].
The final code:
#include <iostream>
#include <stdlib.h>
#include <GL/glut.h>
#include <vector>
#include <math.h>
using namespace std;
//Point class for taking the points
class Point {
public:
float x, y;
void setxy(float x2, float y2)
{
x = x2; y = y2;
}
//operator overloading for '=' sign
const Point& operator=(const Point& rPoint)
{
x = rPoint.x;
y = rPoint.y;
return *this;
}
};
int SCREEN_HEIGHT = 500;
vector<Point> Points;
Point Tangent;
Point inverseTangent;
Point cursorLocationLive;
int TangentsSize = 0;
vector<Point> Tangents(TangentsSize);
vector<Point> inverseTangents(TangentsSize);
vector<Point> BezierCurve;
bool MouseReleased = false;
bool PrevMouse = false;
void drawDot(Point p1)
{
glBegin(GL_POINTS);
glVertex2i(p1.x, p1.y);
glEnd();
}
void drawLine(Point p1, Point p2)
{
glBegin(GL_LINE_STRIP);
glVertex2f(p1.x, p1.y);
glVertex2f(p2.x, p2.y);
glEnd();
}
float interpolate(float n1, float n2, float perc)
{
float diff = n2 - n1;
return n1 + (diff * perc);
}
void myMouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON)
{
if (state == GLUT_DOWN)
{
MouseReleased = false;
// Store points into Points vector on click
Point point;
point.setxy(x, SCREEN_HEIGHT - y);
Points.push_back(point);
// Tangents are set to the cursor position
Tangent.setxy(x, SCREEN_HEIGHT - y);
inverseTangent.x = (2 * Points[Points.size() - 1].x) - Tangent.x;
inverseTangent.y = (2 * Points[Points.size() - 1].y) - Tangent.y;
/*Add new element to Tangent & inverseTangent so when we draw the curve
the tangents are accessed at the right index*/
TangentsSize++;
}
else if (state == GLUT_UP)
{
MouseReleased = true;
// Upon mouse release store tangent and inverse tangent into separate vectors
Tangents.push_back(Tangent);
inverseTangents.push_back(inverseTangent);
}
}
}
void passiveMotion(int x, int y)
{
// Sets the location of cursor while moving with no buttons pressed
cursorLocationLive.setxy(x, SCREEN_HEIGHT - y);
}
void motion(int x, int y)
{
// Sets the coordinates of the tangents when mouse moves with a button held down
Tangent.setxy(x, SCREEN_HEIGHT - y);
inverseTangent.x = (2 * Points[Points.size() - 1].x) - Tangent.x;
inverseTangent.y = (2 * Points[Points.size() - 1].y) - Tangent.y;
}
void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
// Draw main points in red
glColor3f(255, 0, 0);
for (int i = 0; i < Points.size(); i++)
{
drawDot(Points[i]);
}
// If there is a starting point draw a line to cursor from last drawn point in passive motion
if (Points.size() > 0)
{
glColor3f(0, 0, 0);
drawLine(Points[Points.size() - 1], cursorLocationLive);
}
// Draw live tangent dots in green
glColor3f(0, 255, 0);
drawDot(Tangent);
drawDot(inverseTangent);
// Draw live tangent lines in blue
glColor3f(0, 0, 255);
drawLine(Tangent, inverseTangent);
for (int i = 0; i < Tangents.size(); i++)
{
// Draw stored tangent dots in green
glColor3f(0, 255, 0);
drawDot(Tangents[i]);
drawDot(inverseTangents[i]);
// Draw stored tangent lines in blue
glColor3f(0, 0, 255);
drawLine(Tangents[i], inverseTangents[i]);
}
// Loop through all points
if (Points.size() >= 2)
{
// p1 is the start of the curve set to second last point
Point p1;
p1 = Points[Points.size() - 2];
float i;
// Calculate curve coordinates
for (float j = 0; j <= 100; j++)
{
i = j / 100;
// The Green Lines
float xa = interpolate(Points[Points.size() - 2].x, Tangents[TangentsSize - 2].x, i);
float ya = interpolate(Points[Points.size() - 2].y, Tangents[TangentsSize - 2].y, i);
float xb = interpolate(Tangents[TangentsSize - 2].x, inverseTangent.x, i);
float yb = interpolate(Tangents[TangentsSize - 2].y, inverseTangent.y, i);
float xc = interpolate(inverseTangent.x, Points[Points.size() - 1].x, i);
float yc = interpolate(inverseTangent.y, Points[Points.size() - 1].y, i);
// The Blue Line
float xm = interpolate(xa, xb, i);
float ym = interpolate(ya, yb, i);
float xn = interpolate(xb, xc, i);
float yn = interpolate(yb, yc, i);
// The Black Dot
float x2 = interpolate(xm, xn, i);
float y2 = interpolate(ym, yn, i);
Point p2;
p2.setxy(x2, y2);
drawLine(p1, p2);
p1 = p2;
// Prevents curves generated during mouse motion from being stored
if (PrevMouse == 0 && MouseReleased)
{
// Store curvature into Bezier Points
BezierCurve.push_back(p2);
}
}
}
std::cout << BezierCurve.size() << std::endl;
PrevMouse = MouseReleased;
// Draw all bezier curvature
for (int i = 1; i < BezierCurve.size(); i++)
{
drawLine(BezierCurve[i - 1], BezierCurve[i]);
}
glutSwapBuffers();
}
void timer(int)
{
glutTimerFunc(1000 / 60, timer, 0);
glutPostRedisplay();
}
int main(int argc, char* argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(640, 500);
glutInitWindowPosition(100, 150);
glutCreateWindow("Bezier Curve");
glutDisplayFunc(myDisplay);
glutIdleFunc(myDisplay);
glutTimerFunc(0, timer, 0);
glutMouseFunc(myMouse);
glutPassiveMotionFunc(passiveMotion);
glutMotionFunc(motion);
glClearColor(255, 255, 255, 0.0);
glPointSize(3);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 640.0, 0.0, 500.0);
glutMainLoop();
return 0;
}
I am making a 3D game using SFML. I want to use normals to check if the triangle need to be draw in a terrain (triangle) mesh.Here is my code:
vec3d line1, line2, normal;
line1.x = terrain.tris[i].p[0].x - terrain.tris[i].p[1].x;
line1.y = terrain.tris[i].p[0].y - terrain.tris[i].p[1].y;
line1.z = terrain.tris[i].p[0].z - terrain.tris[i].p[1].z;
line2.x = terrain.tris[i].p[1].x - terrain.tris[i].p[2].x;
line2.y = terrain.tris[i].p[1].y - terrain.tris[i].p[2].y;
line2.z = terrain.tris[i].p[1].z - terrain.tris[i].p[2].z;
normal.x = line1.y * line2.z - line1.z * line2.y;
normal.y = line1.z * line2.x - line1.x * line2.z;
normal.z = line1.x * line2.y - line1.y * line2.x;
vec3d vCameraRay = Vector_Sub(terrain.tris[i].p[0], cam.pos);
if (Vector_DotProduct(normal, vCameraRay) < 0.0f){
do_something();
}
Vector_DotProduct:
float Vector_DotProduct(vec3d& v1, vec3d& v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
Vector_sub:
vec3d Vector_Sub(vec3d& v1, vec3d& v2) {
return { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
}
And the vec3d is just a struct that contains
float x, y, z;
But whenever I run the program, I always get this
problem
The triangles that should be displayed was considered "Not visable" by my program(The normal of it is wrong), but the calculation seems right to me!
The code for producing triangle grid:
for (int i = 0; i < 2; i++) {
for (int y = 0; y < wds; y++) {
for (int x = 0; x < wds; x++) {
if (x + 1 < wds && y + 1 < wds) {
vec3d point[3];
switch (i) {
case 0:
point[0] = { (float)(y + 1) * scl + p.x, height * h[y + 1][x + 1], (float)(x + 1) * scl + p.z };
point[1] = { (float)y * scl + p.x, height * h[y][x], (float)x * scl + p.z };
point[2] = { (float)y * scl + p.x, height * h[y][x + 1], (float)(x + 1) * scl + p.z };
break;
case 1:
point[0] = { (float)(y + 1) * scl + p.x, height * h[y + 1][x + 1], (float)(x + 1) * scl + p.z };
point[2] = { (float)y * scl + p.x, height * h[y][x], (float)x * scl + p.z };
point[1] = { (float)(y + 1) * scl + p.x, height * h[y + 1][x], (float)x * scl + p.z };
break;
};
triangle out = { point[0], point[1], point[2] };
tris.push_back(out);
}
}
}
}
The wds is for the size of the grid(side length), the scl is the size of per grid, the h is the height map(two dimentions), and p is the position of the upper left corner.
My 3D point to camera point code:
float mx = p.x - pos.x;
float my = p.y - pos.y;
float mz = p.z - pos.z;
float dx = cos(rot.y) * (sin(rot.z) * my + cos(rot.z) * mx) - sin(rot.y) * mz;
float dy = sin(rot.x) * (cos(rot.y) * mz + sin(rot.y) * (sin(rot.z) * my + cos(rot.z) * mx)) + cos(rot.x) * (cos(rot.z) * my + sin(rot.z) * mx);
float dz = cos(rot.x) * (cos(rot.y) * mz + sin(rot.y) * (sin(rot.z) * my + cos(rot.z) * mx)) - sin(rot.x) * (cos(rot.z) * my + sin(rot.z) * mx);
return { dx, dy, dz };
The rot is the rotation of the camera, p is the position of the camera, and the pos is the 3D point I want to transfer to camera point.
I have been working on this problem for almost a week, but nothing seems to work.It will be a lot of help if you guys can find the problem. Thanks in advance!
Full Code
Init.h:
#ifndef _INIT_H_
#define _INIT_H_
#define WIDTH 1200
#define HEIGHT 800
#endif
Noise.h: noice function
#pragma once
#ifndef _NOISE_H_
#define _NOISE_H_
extern int primeIndex;
extern int numOctaves;
extern double persistence;
extern int primes[10][3];
#endif
#include <math.h>
float Noise(int i, int x, int y);
float SmoothedNoise(int i, int x, int y);
float Interpolate(float a, float b, float x);
float InterpolatedNoise(int i, float x, float y);
float noise(float x, float y);
Noise.cpp:
#include "Noise.h"
int primeIndex = 0;
int numOctaves = 7;
double persistence = 0.5;
int primes[10][3] = {
{ 995615039, 600173719, 701464987 },
{ 831731269, 162318869, 136250887 },
{ 174329291, 946737083, 245679977 },
{ 362489573, 795918041, 350777237 },
{ 457025711, 880830799, 909678923 },
{ 787070341, 177340217, 593320781 },
{ 405493717, 291031019, 391950901 },
{ 458904767, 676625681, 424452397 },
{ 531736441, 939683957, 810651871 },
{ 997169939, 842027887, 423882827 }
};
float Noise(int i, int x, int y) {
int n = x + y * 57;
n = (n << 13) ^ n;
int a = primes[i][0], b = primes[i][1], c = primes[i][2];
int t = (n * (n * n * a + b) + c) & 0x7fffffff;
return 1.0 - (float)(t) / 1073741824.0;
}
float SmoothedNoise(int i, int x, int y) {
float corners = (Noise(i, x - 1, y - 1) + Noise(i, x + 1, y - 1) +
Noise(i, x - 1, y + 1) + Noise(i, x + 1, y + 1)) / 16,
sides = (Noise(i, x - 1, y) + Noise(i, x + 1, y) + Noise(i, x, y - 1) +
Noise(i, x, y + 1)) / 8,
center = Noise(i, x, y) / 4;
return corners + sides + center;
}
float Interpolate(float a, float b, float x) {
float ft = x * 3.1415927,
f = (1 - cos(ft)) * 0.5;
return a * (1 - f) + b * f;
}
float InterpolatedNoise(int i, float x, float y) {
int integer_X = x;
float fractional_X = x - integer_X;
int integer_Y = y;
float fractional_Y = y - integer_Y;
float v1 = SmoothedNoise(i, integer_X, integer_Y),
v2 = SmoothedNoise(i, integer_X + 1, integer_Y),
v3 = SmoothedNoise(i, integer_X, integer_Y + 1),
v4 = SmoothedNoise(i, integer_X + 1, integer_Y + 1),
i1 = Interpolate(v1, v2, fractional_X),
i2 = Interpolate(v3, v4, fractional_X);
return Interpolate(i1, i2, fractional_Y);
}
float noise(float x, float y) {
float total = 0,
frequency = pow(2, numOctaves),
amplitude = 1;
for (int i = 0; i < numOctaves; ++i) {
frequency /= 2;
amplitude *= persistence;
total += InterpolatedNoise((primeIndex + i) % 10,
x / frequency, y / frequency) * amplitude;
}
return total / frequency;
}
Struct.h:
#pragma once
#ifndef _STRUCT_H_
#define _STRUCT_H_
#endif
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <list>
#include "Init.h"
struct vec3d {
float x = 0;
float y = 0;
float z = 0;
};
struct vec2d {
float x = 0;
float y = 0;
};
struct triangle {
vec3d p[3];
int color[3] = { 255, 255, 255 };
vec3d normal;
};
Terrain.h: terrain generation
#pragma once
#ifndef _TERRAIN_H_
#define _TERRAIN_H_
#endif
#include <vector>
#include "Struct.h"
#include "Noise.h"
#define wds 50
#define scl 20
#define width 1000
#define height 120
struct Terrain {
public:
std::vector<triangle> tris;
vec3d p = { -width / 2, 0.0f, -width / 2 };
float h[wds][wds];
void triangle_Strip();
};
Terrain.cpp:
#include "Terrain.h"
void Terrain::make_value() {
for (int y = 0; y < wds; y++) {
for (int x = 0; x < wds; x++) {
int a = abs(p.z / scl + x), b = abs(p.x / scl + y);
h[y][x] = noise(a, b) * 30;
}
}
}
void Terrain::triangle_Strip() {
tris.clear();
for (int i = 0; i < 2; i++) {
for (int y = 0; y < wds; y++) {
for (int x = 0; x < wds; x++) {
if (x + 1 < wds && y + 1 < wds) {
vec3d point[3];
switch (i) {
case 0:
point[0] = { (float)(y + 1) * scl + p.x, height * h[y + 1][x + 1], (float)(x + 1) * scl + p.z };
point[1] = { (float)y * scl + p.x, height * h[y][x], (float)x * scl + p.z };
point[2] = { (float)y * scl + p.x, height * h[y][x + 1], (float)(x + 1) * scl + p.z };
break;
case 1:
point[0] = { (float)(y + 1) * scl + p.x, height * h[y + 1][x + 1], (float)(x + 1) * scl + p.z };
point[2] = { (float)y * scl + p.x, height * h[y][x], (float)x * scl + p.z };
point[1] = { (float)(y + 1) * scl + p.x, height * h[y + 1][x], (float)x * scl + p.z };
break;
};
triangle out = { point[0], point[1], point[2] };
tris.push_back(out);
}
}
}
}
}
Camera.h: camera class, get3dcoord which is get camera point, get2dcoord which is get screen point
#pragma once
#ifndef _CAMERA_H_
#define _CAMERA_H_
#endif
#include "Mat.h"
#include "Init.h"
#include "Struct.h"
class Cam {
public:
vec3d pos;
vec3d rot;
float fov;
float speed;
Cam(vec3d p, vec3d r, float f, float s);
vec3d get3dcoord(vec3d p);
vec3d get2dcoord(vec3d p);
};
Camera.cpp:
#include "Camera.h"
Cam::Cam(vec3d p, vec3d r, float f, float s) {
pos = p;
rot = r;
fov = f;
speed = s;
}
vec3d Cam::get3dcoord(vec3d p) {
float mx = p.x - pos.x;
float my = p.y - pos.y;
float mz = p.z - pos.z;
float dx = cos(rot.y) * (sin(rot.z) * my + cos(rot.z) * mx) - sin(rot.y) * mz;
float dy = sin(rot.x) * (cos(rot.y) * mz + sin(rot.y) * (sin(rot.z) * my + cos(rot.z) * mx)) + cos(rot.x) * (cos(rot.z) * my + sin(rot.z) * mx);
float dz = cos(rot.x) * (cos(rot.y) * mz + sin(rot.y) * (sin(rot.z) * my + cos(rot.z) * mx)) - sin(rot.x) * (cos(rot.z) * my + sin(rot.z) * mx);
return { dx, dy, dz };
}
vec3d Cam::get2dcoord(vec3d p) {
float e = (float)tan(fov / 2) * (float)(WIDTH / 2);
float x = (WIDTH / 2) + (e * p.x) / p.z;
float y = (HEIGHT / 2) + (e * p.y) / p.z;
return { x, y, 0 };
}
3D engine.h: main
#pragma once
#ifndef _3D_ENGINE_H_
#define _3D_ENGINE_H_
#endif
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <iostream>
#include <stdlib.h>
#include <sstream>
#include <list>
#include "Struct.h"
#include "Camera.h"
#include "Init.h"
#include "Noise.h"
#include "Terrain.h"
#define endl "\n"
void draw_triangle(vec3d p1, vec3d p2, vec3d p3, int color[]);
vec3d Vector_Sub(vec3d& v1, vec3d& v2);
float Vector_DotProduct(vec3d& v1, vec3d& v2);
3D engine.cpp:
#include "3D engine.h"
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "3D game in progress");
const sf::Vector2i windowCenter(WIDTH / 2, HEIGHT / 2);
Cam cam({ 0.0f, -40.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, 90, 2.0f);
Terrain terrain;
sf::VertexArray TriangleToDraw(sf::Triangles);
void draw_triangle(vec3d p1, vec3d p2, vec3d p3, int color[]) {
sf::VertexArray tri(sf::Triangles, 3);
tri[0].position = sf::Vector2f(p1.x, p1.y);
tri[1].position = sf::Vector2f(p2.x, p2.y);
tri[2].position = sf::Vector2f(p3.x, p3.y);
tri[0].color = sf::Color((int)color[0], (int)color[1], (int)color[2]);
tri[1].color = sf::Color((int)color[0], (int)color[1], (int)color[2]);
tri[2].color = sf::Color((int)color[0], (int)color[1], (int)color[2]);
TriangleToDraw.append(tri[0]);
TriangleToDraw.append(tri[1]);
TriangleToDraw.append(tri[2]);
}
vec3d Vector_Sub(vec3d& v1, vec3d& v2) {
return { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
}
float Vector_DotProduct(vec3d& v1, vec3d& v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
int main() {
window.setMouseCursorVisible(false);
sf::Mouse::setPosition(windowCenter, window);
terrain.make_value();
terrain.triangle_Strip();
while (window.isOpen()) {
TriangleToDraw.clear();
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
if ((event.type == sf::Event::MouseLeft || event.type == sf::Event::MouseMoved) && sf::Mouse::getPosition(window) != windowCenter) {
sf::Vector2i pos = sf::Mouse::getPosition(window);
int x_a = pos.x;
int y_a = pos.y;
float movex = (float)(x_a - windowCenter.x) / 500.0f;
float movey = (float)(y_a - windowCenter.y) / 500.0f;
cam.rot.x -= movey;
cam.rot.y += movex;
sf::Mouse::setPosition(windowCenter, window);
}
}
float x = sin(cam.rot.y) * cam.speed; float z = cos(cam.rot.y) * cam.speed;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) { cam.pos.x -= x; cam.pos.z -= z; /*terrain.p.x -= x; terrain.p.z -= z;*/ }
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) { cam.pos.x += x; cam.pos.z += z; /*terrain.p.x += x; terrain.p.z += z;*/ }
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) { cam.pos.x += z; cam.pos.z -= x; /*terrain.p.x += z; terrain.p.z -= x;*/ }
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) { cam.pos.x -= z; cam.pos.z += x; /*terrain.p.x -= z; terrain.p.z += x;*/ }
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space)) cam.pos.y += cam.speed;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LSHIFT)) cam.pos.y -= cam.speed;
window.clear(sf::Color(0, 0, 0));
std::vector<triangle> triangles;
for (int i = 0, len = terrain.tris.size(); i < len; i++) {
std::vector<vec3d> projected(3);
for (int r = 0; r < 3; r++) projected[r] = cam.get3dcoord(terrain.tris[i].p[r]);
vec3d line1, line2, normal;
line1.x = projected[0].x - projected[1].x;
line1.y = projected[0].y - projected[1].y;
line1.z = projected[0].z - projected[1].z;
line2.x = projected[1].x - projected[2].x;
line2.y = projected[1].y - projected[2].y;
line2.z = projected[1].z - projected[2].z;
normal.x = line1.y * line2.z - line1.z * line2.y;
normal.y = line1.z * line2.x - line1.x * line2.z;
normal.z = line1.x * line2.y - line1.y * line2.x;
float l = sqrtf(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
normal.x /= l; normal.y /= l; normal.z /= l;
vec3d vCameraRay1 = Vector_Sub(projected[0], cam.pos);
if (Vector_DotProduct(normal, vCameraRay1) < 0.0f && projected[0].z < 0.0f && projected[1].z < 0.0f && projected[2].z < 0.0f/*avoid points behind the camera to be projected*/) {
vec3d light = { 0.0f, 0.0f, 1.0f };
float lNormal = sqrtf(powf(light.x, 2) + powf(light.y, 2) + powf(light.z, 2));
light.x /= lNormal; light.y /= lNormal; light.z /= lNormal;
float dp = std::max(0.3f, Vector_DotProduct(light, normal));
int c = 255 * dp;
triangles.push_back({projected[0], projected[1], projected[2], {c, c, c}});
}
}
std::sort(triangles.begin(), triangles.end(), [](triangle& t1, triangle& t2)
{
float z1 = (t1.p[0].z + t1.p[1].z + t1.p[2].z) / 3.0f;
float z2 = (t2.p[0].z + t2.p[1].z + t2.p[2].z) / 3.0f;
return z1 < z2;
});
for (triangle tri : triangles) {
draw_triangle(cam.get2dcoord(tri.p[0]), cam.get2dcoord(tri.p[1]), cam.get2dcoord(tri.p[2]), tri.color);
}
window.draw(TriangleToDraw);
window.display();
}
return 0;
}
One of the triangle that had the wrong normal:
Normal: -0.08
vCameraRay: -588.2, 19.0, -662.5
Vector Dotproduct: -74.7
Triangle Point1: 19.03, -35.10, -75.69
Triangle Point2: -1.28, -27.57, -92.94
Triangle Point3: -0.96, -25.79, -71.35
Camera position: 2.20, 627.26, 0.03
One of the triangle that had the wrong Dot Product:
Normal: 0.59
vCameraRay: 468.41, 13.59, -634.75
Vector DotProduct: -55.05
Triangle Point1: 13.59, -7.29, -55.05
Triangle Point2: 19.19, 7.04, -37.72
Trianlge Point3: 0.00, 9.75, -28.36
Camera pos: 0.00, 627.45, 0.00
I've done some research and there are many methods to doing this if the vertices and points are predetermined as seen here. However in my case, everything is specified by the user. The code I have put together (with the help of others), allows the user to create the polygon, and place points. I wrote functions to try and create vectors from the point to the vertices and then compute the angle. If it comes to 360 it should be inside and it should be colored green. Else it should be outside and red.
This is what I've been working on, but I cant seem to figure it out:
(Edit: Included my entire code)
GLint vert[100][2];
int width = 400, height = 600, n = 0, m = 0, type = GL_LINE_STRIP, v;
bool rubberbanding = false;
bool closed = false;
double testx, testy;
bool isitin;
double dotp(double x1, double y1, double x2, double y2) {
double a;
a = (x1 * x2) + (y1 * y2);
return a;
}
double mag(double x1, double y1, double x2, double y2) {
double a;
double x = (x2 - x1);
double y = (y2 - y1);
a = sqrt((x*x) + (y*y));
return a;
}
bool inpoly(int numv, GLint vx[][2], GLint vy[][2], double tx, double ty) {
double angle = 0.0;
int n = 0;
while (n != numv) {
//vector from point to vertex
double newv1x = vx[n][0] - tx;
double newv1y = vy[n][1] - ty;
double magv1 = mag(tx, ty, vx[n][0], vy[n][1]); //magnitude of vector
//vector from point to next vertex
double newv2x = vx[n + 1][0] - tx;
double newv2y = vy[n + 1][1] - ty;
double magv2 = mag(tx, ty, vx[n+1][0], vy[n+1][1]);//magnitude of vector
//dot product between the two vectors
double dp = dotp(newv1x, newv1y, newv2x, newv2y);
//angle between two vectors
double vang = acos(dp / (magv1*magv2));
angle += vang;
n++;
}
//vector from point to last vertex
double newv1x = vx[numv][0] - tx;
double newv1y = vy[numv][1] - ty;
double magv1 = mag(tx, ty, vx[numv][0], vy[numv][1]); //magnitude of vector
//vector from point to first vertex
double newv2x = vx[0][0] - tx;
double newv2y = vy[0][1] - ty;
double magv2 = mag(tx, ty, vx[0][0], vy[0][1]);//magnitude of vector
//dot product between the two vectors
double dp = dotp(newv1x, newv1y, newv2x, newv2y);
//angle between two vectors
double vang = acos(dp / (magv1*magv2));
angle += vang;
if (angle == 360.0) return true;
return false;
}
void display(){
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 1, 0);
glBegin(closed ? GL_LINE_LOOP : GL_LINE_STRIP);
for(int i = 0; i < m; i++){
glVertex2iv(vert[i]);
}
glEnd();
/*
glColor3f(0, 0, 1);
glBegin(GL_POINTS);
for (int i = m; i < n; i++) {
glVertex2iv(vert[i]);
}
*/
isitin = inpoly(m, vert, vert, testx, testy);
if (isitin == true) {
glColor3f(0, 1, 0);
glBegin(GL_POINTS);
for (int i = m; i < n; i++) {
glVertex2iv(vert[i]);
}
}
else {
glColor3f(1, 0, 0);
glBegin(GL_POINTS);
for (int i = m; i < n; i++) {
glVertex2iv(vert[i]);
}
}
glEnd();
glutSwapBuffers();
glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y){
switch(key){
case 'r': n = 0; m = 0; closed = false; break;
case 'c': closed = true; break;
}
glutPostRedisplay();
}
int findVertex(int x, int y){
int dx, dy;
for(int i = 0; i < n; i++){
dx = vert[i][0] - x;
dy = vert[i][1] - y;
if(dx*dx + dy*dy < 16) return i;
}
return - 1;
}
void mousemove(int x, int y)
{
testx = x;
testy = height - 1 - y;
}
void mouse(int button, int state, int x, int y){
switch(button){
case GLUT_LEFT_BUTTON:
if(state == GLUT_DOWN){
if (n < 100) {
v = n++;
vert[v][0] = x;
vert[v][1] = height - 1 - y;
// n++;
rubberbanding = true;
glutPostRedisplay();
if (!closed) m = n;
}
}
else{
rubberbanding = false;
}
break;
}
}
void motion(int x, int y){
if(rubberbanding){
vert[v][0] = x;
vert[v][1] = height - 1 - y;
glutPostRedisplay();
}
}
void main(int argc, char** argv){
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE);
glutInitWindowSize(width,height);
glutInitWindowPosition(50,100);
glutCreateWindow("Project 3");
glClearColor(0.0,0.0,0.0,0.0);
glColor3f( 1, 1, 0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, 0, height);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutMouseFunc(mouse);
glutPassiveMotionFunc(mousemove);
glutMotionFunc(motion);
glutMainLoop();
}
When I run the program in Visual Studio, I can draw the polygon, and I can specify
points, but for some reason all the points appear red. If anyone has any ideas on how to fix this, it would be greatly appreciated.
Probably your error is that acos returns in radians, and you're testing if the sum equals 360 degrees.
Also, you shouldn't compare doubles that way, since that calculation is probably adding rounding error in each sum. See here for more information.
It also draws multiply triangles, they are all displaced and in wrong scale.
I am trying to make my own implementation of triangle rasterizer found at:
https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/rasterization-stage
I have no idea what is wrong with my code.
#include<fstream>
#include<cmath>
class Vertice
{
public:
float x, y;
Vertice(float x, float y)
{
this->x = x;
this->y = y;
}
void fitToImage(int imageWidth, int imageHeight)
{
x = (x * (imageWidth / 2)) + (imageWidth / 2);
y = (-y * (imageHeight / 2)) + (imageHeight / 2);
}
};
class Image
{
public:
int imageWidth, imageHeight;
unsigned char* pixels;
Image(int imageWidth, int imageHeight)
{
this->imageWidth = imageWidth;
this->imageHeight = imageHeight;
pixels = new unsigned char[imageWidth * imageHeight * 3];
}
~Image()
{
delete[] pixels;
}
void setPixel(int x, int y, int red, int green, int blue)
{
int help_var = ((y * imageHeight) + x) * 3;
pixels[help_var + 0] = (char)red;
pixels[help_var + 1] = (char)green;
pixels[help_var + 2] = (char)blue;
}
void fillPixels(int red, int green, int blue)
{
int help_var = imageWidth * imageHeight * 3;
for (int i = 0; i < help_var; i += 3) {
pixels[i + 0] = (char)red;
pixels[i + 1] = (char)green;
pixels[i + 2] = (char)blue;
}
}
//-------------------BARYCENTRIC TRIANGLE RASTERISATION------------------------
float edgeFunction(const Vertice& A, const Vertice& B, const Vertice& P)
{
return ((P.x - A.x)*(B.y - A.y) + (P.y - A.y)*(B.x - A.x));
}
void fillTriangleBarycentric(const Vertice& v0, const Vertice& v1, const Vertice& v2)
{
Vertice p(0.0f, 0.0f);
for (int x = 0; x < imageWidth; x++) {
for (int y = 0; y < imageHeight; y++) {
p.x = x + 0.5f; p.y = y + 0.5f;
float w0 = edgeFunction(v1, v2, p);
float w1 = edgeFunction(v2, v0, p);
float w2 = edgeFunction(v0, v1, p);
if (w0 >= 0 && w1 >= 0 && w2 >= 0) {
setPixel(x, y, 0, 0, 255);
}
}
}
}
//-----------------------------------------------------------------------------
};
int main()
{
Image image(800, 600);
image.fillPixels(255, 255, 255);
Vertice a(0.2f, 0.5f);
Vertice b(-0.5f, 0.0f);
Vertice c(0.5f, -0.5f);
a.fitToImage(image.imageWidth, image.imageHeight);
b.fitToImage(image.imageWidth, image.imageHeight);
c.fitToImage(image.imageWidth, image.imageHeight);
image.fillTriangleBarycentric(a, b, c);
std::ofstream imageFile;
imageFile.open("./drawing_triangle_test_image.ppm");
imageFile << "P6\n" << image.imageWidth << " " << image.imageHeight << "\n255\n";
imageFile.write((char*)image.pixels, image.imageWidth * image.imageHeight * 3);
imageFile.close();
return 0;
}
Here is the image I get after running my program.
Thanks for any help!
Here is the better result (where setPixel is using imageWidth instead of imageHeight):
y * imageHeight
Is definitely the type of error your code has (might have multiple instances). You need to multiply the y position by the width. Otherwise, you'll end up interlacing the triangle at random x positions.
The fact that you get four triangles relates to 800/600 simplifying to 4/3. Had you rendered to 797 by 603, you'd probably would have gotten some random mess of horizontal lines.
In addition to #Jeffrey's correction your edge function is also not quite right. It should be
float edgeFunction(const Vertice& A, const Vertice& B, const Vertice& P)
{
return ((P.x - A.x)*(B.y - A.y) - (P.y - A.y)*(B.x - A.x));
}
i.e. there should be a negative sign between the two terms (because it is the cross product of the two position vectors AB and AP).
I'm trying to make Bezier curve by control point.
I have some examples and followed it. But it did not work.
It shows the line is going to (0, 0) points during working.
I still don't get it why.
Here's the code in the C++ language, using OpenGL.
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <gl/glut.h>
#include <gl/GLU.h>
#include <gl/GL.h>
#include <math.h>
#define CTRL_COUNT 100
void display();
void init();
float getNextBezierPointX(float t);
float getNextBezierPointY(float t);
void drawline();
int ctrlPointsCount;
int ctrlPointsX[CTRL_COUNT], ctrlPointsY[CTRL_COUNT];
int X1[20] = { 10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100,105 };
int Y1[20] = { 50,60,40,70,40,60,35,80,45,55,30,60,40,60,40,55,35,70,40,50 };
void main(int argc, char *argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(500, 300);
glutCreateWindow("Bezier");
glutDisplayFunc(display);
init();
glutMainLoop();
}
void display() {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
ctrlPointsCount = 20;
for (int i = 0; i < 20; i++) {
ctrlPointsX[i] = X1[i];
ctrlPointsY[i] = Y1[i];
}
drawline();
glFlush();
}
void init() {
glClearColor(0.0, 0.0, 0.0, 0.0);
glColor3f(1.0, 0.0, 0.0);
glPointSize(8.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 128.0, 0.0, 96.0);
}
float getNextBezierPointX(float t) {
float x = 0.0;
int c;
for (int i = 0; i < ctrlPointsCount; i ++) {
if (i == 0 || i == ctrlPointsCount - 1)
c = 1;
else
c = ctrlPointsCount - 1;
x = x + c * pow(t, i) * pow(1 - t, ctrlPointsCount - 1 - i) * ctrlPointsX[i];
}
return x;
}
float getNextBezierPointY(float t) {
float y = 0.0;
int c;
for (int i = 0; i < ctrlPointsCount; i ++) {
if (i == 0 || i == ctrlPointsCount - 1)
c = 1;
else
c = ctrlPointsCount - 1;
y = y + c * pow(t, i) * pow(1 - t, ctrlPointsCount - 1 - i) * ctrlPointsY[i];
}
return y;
}
void drawline() {
float x, y;
for (int i = 0; i < 20; i++) {
glBegin(GL_POINTS);
glVertex2i(ctrlPointsX[i], ctrlPointsY[i]);
glEnd();
glFlush();
}
float oldX = ctrlPointsX[0], oldY = ctrlPointsY[0];
for (double t = 0.0; t <= 1.0; t += 0.01) {
x = getNextBezierPointX(t);
y = getNextBezierPointY(t);
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
glVertex2f(oldX, oldY);
glVertex2f(x, y);
glEnd();
glFlush();
oldX = x;
oldY = y;
}
}
For a bezier curve like this you have to calculate Bernstein polynomials:
double factorial(int n)
{
double x = 1.0;
for (int i = 1; i <= n; i++)
x *= (double)i;
return x;
}
double Ni(int n, int i)
{
double a1 = factorial(n);
double a2 = factorial(i);
double a3 = factorial(n - i);
double ni = a1/ (a2 * a3);
return ni;
}
double Bernstein(int n, int i, double t)
{
double ti = (t == 0.0 && i == 0) ? 1.0 : pow(t, i); /* t^i */
double tni = (n == i && t == 1.0) ? 1.0 : pow((1 - t), (n - i)); /* (1 - t)^i */
double basis = Ni(n, i) * ti * tni; //Bernstein basis
return basis;
}
The code to create the curve may look like this:
struct vec2
{
double x, y;
};
vec2 getBezierPoint(float t, int n, int x[], int y[] )
{
vec2 pt{ 0.0, 0.0 };
for (int i = 0; i < n; i ++) {
double b = Bernstein( n - 1, i, t );
pt.x += b * x[i];
pt.y += b * y[i];
}
return pt;
}
float oldX = ctrlPointsX[0], oldY = ctrlPointsY[0];
for (double t = 0.0; t <= 1.0; t += 0.01)
{
vec2 pt = getBezierPoint( t, ctrlPointsCount, ctrlPointsX, ctrlPointsY );
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
glVertex2f(oldX, oldY);
glVertex2f((float)pt.x, (float)pt.y);
glEnd();
glFlush();
oldX = (float)pt.x; oldY = (float)pt.y;
}
An other solution is provided in the answer to the Stack Overflow question How do I implement a Bézier curve in C++?:
#include <vector>
vec2 getBezierPoint2( float t, int n, int x[], int y[] )
{
std::vector<double> tmpx( n ), tmpy( n );
for ( int i=0; i<n; ++ i )
{
tmpx[i] = x[i];
tmpy[i] = y[i];
}
int i = n - 1;
while (i > 0)
{
for (int k = 0; k < i; k++)
{
tmpx[k] = tmpx[k] + t * ( tmpx[k+1] - tmpx[k] );
tmpy[k] = tmpy[k] + t * ( tmpy[k+1] - tmpy[k] );
}
i--;
}
return { tmpx[0], tmpy[0] };
}
But may be the result is not what you expect it to be, because you create a Higher-order Bézier curve which results in a very flat curve:
Maybe you want to connect any number of points with square bezier curves: Quadratic Bézier Curve: Calculate Points