How to draw a 45x45 grid with SDL - c++

I am trying to make a grid of 45x45 in SDL but I am not so sure how to do it. Should I use SDL_RenderDrawLine? If so how should I use it inside my void ParticleManager::renderGrid(SDL_Renderer* renderer) function?
#include <iostream>
#include <vector>
#include <string>
#include <cassert>
#include <SDL.h>
#include "Constants.h"
#include "ParticleManager.h"
using namespace std;
Particle::Particle(double _x, double _y) :
x(_x), y(_y),
vx(0.f), vy(0.f),
fx(0.f), fy(0.f),
rho(0.f), p(0.f)
{
}
ParticleManager::ParticleManager()
{
ax = 0;
ay = GRAVITY;
renderMode = "particles";
}
void ParticleManager::init(unsigned long n)
{
cout << "Init with " << n << " particles" << endl;
particles.clear();
particles.reserve(n);
while (particles.size() < n) {
double x = rand() / (double)(RAND_MAX)*SCREEN_WIDTH;
double y = rand() / (double)(RAND_MAX)*SCREEN_HEIGHT;
double centerDist = sqrt(
pow(x - SCREEN_WIDTH / 2.0, 2) +
pow(y - SCREEN_HEIGHT / 2.0, 2));
if (centerDist < fmin(SCREEN_WIDTH, SCREEN_HEIGHT) * 0.25)
particles.push_back(Particle(x, y));
}
}
void ParticleManager::addBlock(double center_x, double center_y)
{
for (int i = 0; i <= 4; i++) {
for (int j = 0; j <= 4; j++) {
double x = center_x + (j - 2) * SCREEN_WIDTH * 0.04f + (rand() / (double)(RAND_MAX)) * H;
double y = center_y + (i - 2) * SCREEN_HEIGHT * 0.04f + (rand() / (double)(RAND_MAX)) * H;
if (x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT)
particles.push_back(Particle(x, y));
}
}
cout << particles.size() << " particles" << endl;
}
void ParticleManager::addOne(double x, double y)
{
particles.push_back(Particle(x, y));
cout << particles.size() << " particles" << endl;
}
void ParticleManager::setGravity(int direction)
{
switch (direction) {
case DOWN:
ax = 0;
ay = +GRAVITY;
break;
case UP:
ax = 0;
ay = -GRAVITY;
break;
case RIGHT:
ax = +GRAVITY;
ay = 0;
break;
default:
ax = -GRAVITY;
ay = 0;
}
}
void ParticleManager::explode() {
for (auto& p : particles) {
p.vx = rand() / (double)RAND_MAX * 10000 - 5000;
p.vy = rand() / (double)RAND_MAX * 10000 - 5000;
}
}
void ParticleManager::integrate(double dt)
{
for (auto& p : particles)
{
// forward Euler integration
if (p.rho != 0 && p.fx == p.fx && p.fy == p.fy) {
p.vx += dt * p.fx / p.rho;
p.vy += dt * p.fy / p.rho;
}
p.x += dt * p.vx;
p.y += dt * p.vy;
// enforce boundary conditions
if (p.x - PARTICLE_RADIUS < 0.0f)
{
p.vx *= BOUND_DAMPING;
p.x = PARTICLE_RADIUS;
}
if (p.x + PARTICLE_RADIUS > SCREEN_WIDTH)
{
p.vx *= BOUND_DAMPING;
p.x = SCREEN_WIDTH - PARTICLE_RADIUS;
}
if (p.y - PARTICLE_RADIUS < 0.0f)
{
p.vy *= BOUND_DAMPING;
p.y = PARTICLE_RADIUS;
}
if (p.y + PARTICLE_RADIUS > SCREEN_HEIGHT)
{
p.vy *= BOUND_DAMPING;
p.y = SCREEN_HEIGHT - PARTICLE_RADIUS;
}
}
}
void ParticleManager::computeDensityPressure()
{
// for each particles
for (auto& pi : particles)
{
pi.rho = 0.f;
// Find all the particles that contribute to the
// pressure / density
for (auto& pj : particles)
{
double distance = sqrt(
pow(pj.x - pi.x, 2) +
pow(pj.y - pi.y, 2));
if (distance < H)
{
// this computation is symmetric
pi.rho += MASS * POLY6 * pow(pow(H, 2) - pow(distance, 2), 3.f);
}
}
pi.p = GAS_CONST * (pi.rho - REST_DENS);
}
}
void ParticleManager::computeForces()
{
// For each particle
for (auto& pi : particles) {
double pressure_x = 0.f;
double pressure_y = 0.f;
double viscosity_x = 0.f;
double viscosity_y = 0.f;
// Calculate the sum of the viscosity and pressure forces
// applied by the other particles
for (auto& pj : particles) {
if (&pi == &pj)
continue;
double r = sqrt(
pow(pj.x - pi.x, 2) +
pow(pj.y - pi.y, 2));
if (r < H) {
// compute pressure force contribution
double fpress = MASS * (pi.p + pj.p) / (2.0 * pj.rho) * SPIKY_GRAD * pow(H - r, 2.0);
pressure_x += (pi.x - pj.x) / r * fpress;
pressure_y += (pi.y - pj.y) / r * fpress;
// compute viscosity force contribution
viscosity_x += VISC * MASS * (pj.vx - pi.vx) / pj.rho * VISC_LAP * (H - r);
viscosity_y += VISC * MASS * (pj.vy - pi.vy) / pj.rho * VISC_LAP * (H - r);
}
}
pi.fx = pressure_x + viscosity_x + ax * pi.rho;
pi.fy = pressure_y + viscosity_y + ay * pi.rho;
}
}
void ParticleManager::update(unsigned long dt)
{
// TODO: calculate the grid here
computeDensityPressure();
computeForces();
integrate(dt / 10000.0f);
}
void ParticleManager::renderParticles(SDL_Renderer* renderer) {
SDL_SetRenderDrawColor(renderer, 230, 120, 0, 100);
SDL_Rect r;
// Draw particles
for (long unsigned int i = 0; i < particles.size(); i++) {
r.x = (int)(particles[i].x - PARTICLE_RADIUS);
r.y = (int)(particles[i].y - PARTICLE_RADIUS);
r.w = (int)(PARTICLE_RADIUS * 2);
r.h = (int)(PARTICLE_RADIUS * 2);
SDL_RenderFillRect(renderer, &r);
}
}
void ParticleManager::renderGrid(SDL_Renderer* renderer) {
// TODO: Draw the lines that form the grid
cout << "Affichage de la grile (TODO)" << endl;
}
void ParticleManager::renderCells(SDL_Renderer* renderer) {
// TODO: Draw the boxes in different colors to
// represent the number of particles in each cell
//
// Use the calculation:
// int alpha = nb_particles * 255/5;
// if (alpha> 255) alpha = 255;
// SDL_SetRenderDrawColor (renderer, 0, 0, 255, alpha);
//
// To assign the color to the cell
cout << "Affichage des cellules (TODO)" << endl;
}
void ParticleManager::render(SDL_Renderer* renderer)
{
if (renderMode.find("particle") != string::npos)
renderParticles(renderer);
if (renderMode.find("grid") != string::npos) {
renderCells(renderer);
renderGrid(renderer);
}
}
The end result should look like this:

Related

Wrong normal for triangles

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

How to repair the spacing between blocks when I rotate them?

Hello I'm currently trying to rotate blocks that are within a square. But when I start to rotate them it starts to create weird spaces between blocks that I don't want. Could you help me to fix the problem of spaces beetween blocks? Here are some code and screenshots how does it look.
https://imgur.com/a/BLuO7FF
I have already checked if all angles and radiuses are calculated correctly and I don't see any problem there.
World.h
#pragma once
#include <SFML/Graphics.hpp>
class World
{
public:
World(sf::Vector2f Wpos);
float AngleToRadian(int angle);
void RotateWorld();
void draw(sf::RenderWindow &window);
sf::Texture tx;
sf::Sprite** Block;
sf::Vector2f Pos;
sf::Vector2i Size;
float** radius;
float** angle;
};
World.cpp
#include "World.h"
#include <SFML/Graphics.hpp>
#include <iostream>
#include <cmath>
#define PI 3.14159
World::World(sf::Vector2f Wpos)
{
Pos = Wpos;
Size = sf::Vector2i(10, 10);
Block = new sf::Sprite*[Size.y];
radius = new float*[Size.y];
angle = new float*[Size.y];
for (int i = 0; i < Size.y; i++)
{
Block[i] = new sf::Sprite[Size.x];
radius[i] = new float[Size.x];
angle[i] = new float[Size.x];
}
tx.loadFromFile("Img/Block.png");
sf::Vector2i off(Size.x * tx.getSize().x / 2, Size.y * tx.getSize().y / 2); //tx size is 32px x 32px
for (int y = 0; y < Size.y; y++)
{
for (int x = 0; x < Size.x; x++)
{
Block[y][x].setTexture(tx);
Block[y][x].setOrigin(tx.getSize().x / 2, tx.getSize().y / 2);
Block[y][x].setPosition(x*tx.getSize().x + Wpos.x - off.x + Block[y][x].getOrigin().x, y*tx.getSize().y + Wpos.y - off.y + Block[y][x].getOrigin().y);
radius[y][x] = sqrt(pow(Pos.x - Block[y][x].getPosition().x, 2) + pow(Pos.y - Block[y][x].getPosition().y, 2));
angle[y][x] = (atan2(Block[y][x].getPosition().y - Pos.y, Block[y][x].getPosition().x - Pos.x) * 180.0) / PI;
if ((atan2(Block[y][x].getPosition().y - Pos.y, Block[y][x].getPosition().x - Pos.x) * 180.0) / PI < 0)
{
angle[y][x] += 360;
}
//angle[y][x] = round(angle[y][x]);
/*radius[y][x] = round(radius[y][x]);*/
}
}
}
void World::RotateWorld()
{
float dx = 0, dy = 0;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::E))
{
for (int y = 0; y < Size.y; y++)
{
for (int x = 0; x < Size.x; x++)
{
Block[y][x].rotate(1);
if (angle[y][x] >= 360)
{
angle[y][x] = 0;
}
angle[y][x]++;
dx = cos(AngleToRadian(angle[y][x])) * radius[y][x];
dy = sin(AngleToRadian(angle[y][x])) * radius[y][x];
Block[y][x].setPosition(Pos.x + dx, Pos.y + dy);
}
}
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Q))
{
for (int y = 0; y < Size.y; y++)
{
for (int x = 0; x < Size.x; x++)
{
Block[y][x].rotate(-1);
if (angle[y][x] >= 360)
{
angle[y][x] = 0;
}
angle[y][x]--;
dx = cos(AngleToRadian(angle[y][x])) * radius[y][x];
dy = sin(AngleToRadian(angle[y][x])) * radius[y][x];
Block[y][x].setPosition(Pos.x + dx, Pos.y + dy);
}
}
}
}
I expected it to rotate withouth any spaces between. I would be really thankfull if someone would help me.
I would try with setting the origin of the sf::Sprite using it's getGlobalBounds() method instead of the sf::Texture size getter.
The difference seems minor and something like that might be the case.
Block[y][x].setTexture(tx);
Block[y][x].setOrigin(Block[y][x].getGlobalBouds().width / 2, Block[y][x].getGlobalBouds().height / 2);
Block[y][x].setPosition(x*Block[y][x].getGlobalBouds().width + Wpos.x - off.x + Block[y][x].getOrigin().x, y*Block[y][x].getGlobalBouds().height + Wpos.y - off.y + Block[y][x].getOrigin().y);

SDL2.0 screen nullptr on render of Window

Hey so I'm relatively new to the SDL library and just trying to get to grips with it.
I found a C++ conversion for Minecraft4k but it was based on SDL1.x so I'm trying to convert it to SDL2.0
At present the build is successful, but when it gets to;
plot(x, y, rgbmul(col, fxmul(br, ddist)));
It throws a read access violation exception:
screen was nullptr
This is my code;
// C++ port of Minecraft 4k JS (http://jsdo.it/notch/dB1E)
// By The8BitPimp
// See: the8bitpimp.wordpress.com
#include <SDL.h>
#include <math.h>
#include <windows.h>
#include <tchar.h>
#include "plot.h"
#include "llist.h"
const int w = 320;
const int h = 240;
SDL_Surface *screen = nullptr;
const float math_pi = 3.14159265359f;
static inline float math_sin(float x) {
return sinf(x);
}
static inline float math_cos(float x) {
return cosf(x);
}
// the texture map
int texmap[16 * 16 * 16 * 3];
// the voxel map
char map[64 * 64 * 64];
static inline int random(int max) {
return (rand() ^ (rand() << 16)) % max;
}
static inline void plot(int x, int y, int c) {
int *p = (int*)screen->pixels;
p[y * w + x] = c;
}
static void makeTextures(void) {
// each texture
for (int j = 0; j<16; j++) {
int k = 255 - random(96);
// each pixel in the texture
for (int m = 0; m<16 * 3; m++)
for (int n = 0; n<16; n++) {
int i1 = 0x966C4A;
int i2 = 0;
int i3 = 0;
if (j == 4)
i1 = 0x7F7F7F;
if ((j != 4) || (random(3) == 0))
k = 255 - random(96);
if (j == 1)
{
if (m < (((n * n * 3 + n * 81) >> 2) & 0x3) + 18)
i1 = 0x6AAA40;
else if (m < (((n * n * 3 + n * 81) >> 2) & 0x3) + 19)
k = k * 2 / 3;
}
if (j == 7)
{
i1 = 0x675231;
if ((n > 0) && (n < 15) && (((m > 0) && (m < 15)) || ((m > 32) && (m < 47))))
{
i1 = 0xBC9862;
i2 = n - 7;
i3 = (m & 0xF) - 7;
if (i2 < 0)
i2 = 1 - i2;
if (i3 < 0)
i3 = 1 - i3;
if (i3 > i2)
i2 = i3;
k = 196 - random(32) + i2 % 3 * 32;
}
else if (random(2) == 0)
k = k * (150 - (n & 0x1) * 100) / 100;
}
if (j == 5)
{
i1 = 0xB53A15;
if (((n + m / 4 * 4) % 8 == 0) || (m % 4 == 0))
i1 = 0xBCAFA5;
}
i2 = k;
if (m >= 32)
i2 /= 2;
if (j == 8)
{
i1 = 5298487;
if (random(2) == 0)
{
i1 = 0;
i2 = 255;
}
}
// fixed point colour multiply between i1 and i2
i3 =
((((i1 >> 16) & 0xFF) * i2 / 255) << 16) |
((((i1 >> 8) & 0xFF) * i2 / 255) << 8) |
((i1 & 0xFF) * i2 / 255);
// pack the colour away
texmap[n + m * 16 + j * 256 * 3] = i3;
}
}
}
static void makeMap(void) {
// add random blocks to the map
for (int x = 0; x < 64; x++) {
for (int y = 0; y < 64; y++) {
for (int z = 0; z < 64; z++) {
int i = (z << 12) | (y << 6) | x;
float yd = (y - 32.5) * 0.4;
float zd = (z - 32.5) * 0.4;
map[i] = random(16);
float th = random(256) / 256.0f;
if (th > sqrtf(sqrtf(yd * yd + zd * zd)) - 0.8f)
map[i] = 0;
}
}
}
}
static void init(void) {
makeTextures();
makeMap();
}
// fixed point byte byte multiply
static inline int fxmul(int a, int b) {
return (a*b) >> 8;
}
// fixed point 8bit packed colour multiply
static inline int rgbmul(int a, int b) {
int _r = (((a >> 16) & 0xff) * b) >> 8;
int _g = (((a >> 8) & 0xff) * b) >> 8;
int _b = (((a)& 0xff) * b) >> 8;
return (_r << 16) | (_g << 8) | _b;
}
static void render(void) {
float now = (float)(SDL_GetTicks() % 10000) / 10000.f;
float xRot = math_sin(now * math_pi * 2) * 0.4 + math_pi / 2;
float yRot = math_cos(now * math_pi * 2) * 0.4;
float yCos = math_cos(yRot);
float ySin = math_sin(yRot);
float xCos = math_cos(xRot);
float xSin = math_sin(xRot);
float ox = 32.5 + now * 64.0;
float oy = 32.5;
float oz = 32.5;
// for each column
for (int x = 0; x < w; x++) {
// get the x axis delta
float ___xd = ((float)x - (float)w / 2.f) / (float)h;
// for each row
for (int y = 0; y < h; y++) {
// get the y axis delta
float __yd = ((float)y - (float)h / 2.f) / (float)h;
float __zd = 1;
float ___zd = __zd * yCos + __yd * ySin;
float _yd = __yd * yCos - __zd * ySin;
float _xd = ___xd * xCos + ___zd * xSin;
float _zd = ___zd * xCos - ___xd * xSin;
int col = 0;
int br = 255;
float ddist = 0;
float closest = 32.f;
// for each principle axis x,y,z
for (int d = 0; d < 3; d++) {
float dimLength = _xd;
if (d == 1)
dimLength = _yd;
if (d == 2)
dimLength = _zd;
float ll = 1.0f / (dimLength < 0.f ? -dimLength : dimLength);
float xd = (_xd)* ll;
float yd = (_yd)* ll;
float zd = (_zd)* ll;
float initial = ox - floor(ox);
if (d == 1) initial = oy - floor(oy);
if (d == 2) initial = oz - floor(oz);
if (dimLength > 0) initial = 1 - initial;
float dist = ll * initial;
float xp = ox + xd * initial;
float yp = oy + yd * initial;
float zp = oz + zd * initial;
if (dimLength < 0) {
if (d == 0) xp--;
if (d == 1) yp--;
if (d == 2) zp--;
}
// while we are concidering a ray that is still closer then the best so far
while (dist < closest) {
// quantize to the map grid
int tex = map[(((int)zp & 63) << 12) | (((int)yp & 63) << 6) | ((int)xp & 63)];
// if this voxel has a texture applied
if (tex > 0) {
// find the uv coordinates of the intersection point
int u = ((int)((xp + zp) * 16.f)) & 15;
int v = ((int)(yp * 16.f) & 15) + 16;
// fix uvs for alternate directions?
if (d == 1) {
u = ((int)(xp * 16.f)) & 15;
v = (((int)(zp * 16.f)) & 15);
if (yd < 0)
v += 32;
}
// find the colour at the intersection point
int cc = texmap[u + v * 16 + tex * 256 * 3];
// if the colour is not transparent
if (cc > 0) {
col = cc;
ddist = 255 - ((dist / 32 * 255));
br = 255 * (255 - ((d + 2) % 3) * 50) / 255;
// we now have the closest hit point (also terminates this ray)
closest = dist;
}
}
// advance the ray
xp += xd;
yp += yd;
zp += zd;
dist += ll;
}
}
plot(x, y, rgbmul(col, fxmul(br, ddist)));
}
}
}
int main(int argc, char *argv[]) {
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *screen;
screen = SDL_CreateWindow(
"Minecraft4k", // window title
SDL_WINDOWPOS_CENTERED, // initial x position
SDL_WINDOWPOS_CENTERED, // initial y position
320, // width, in pixels
240, // height, in pixels
SDL_WINDOW_OPENGL // flags - see below
);
SDL_Renderer* renderer;
renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED);
if (screen == nullptr) {
return 1;
}
init();
bool running = true;
while (running) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
running &= (event.type != SDL_QUIT);
}
SDL_RenderPresent(renderer);
render();
}
SDL_DestroyWindow(screen);
SDL_Quit();
return 0;
}
When I actually run the code I do get a black screen, but the debugger lands on the line
plot(x, y, rgbmul(col, fxmul(br, ddist)));
in ;
static void render(void)
This is all just "for fun" so any information or guidance is appreciated.
You define screen twice (the first time as a global variable, the second time within your main), but you initialize it only once (within your main).
Because of that, the global variable screen actually is set to nullptr and plot fails trying to use it, as the error message states.

How do I calculate angles to create vector offsets to define parallel paths?

I have reference paths that are defined by a series of points. Given a starting point and additional points in a given direction, I need to effectively split the path created by the points into two distinct paths (directional) in a manner that allows me to draw and work with the two paths independently. The following screenshot shows 8 path examples. The "starting point" is the one with the white circle on it. If you orient yourself from the starting point towards the next point, then the path suggested by the red circles always should be to the "right" of the path defined by the given points.
In the screen, paths 2, 3, 5, 6, and 7 are correct. In paths 1, 4, and 8, essentially when the path starts on the right and moves left, then the position of the parallel path points are correct, but they are swapped in some instance (green where red should be, etc).
I'm somehow misusing atan2() (I think) to get the proper angle and or calculate the positions. For the first and last points, I'm calculating the angle to the adjacent point and the drawing the red and green path points at 90 degree offsets from that angle. For the points in the middle of the path, I'm looking at the angle to the previous point and the angle to the next point and placing the points in a manner that bisects the angles.
How do I properly calculate these angles to get the red and green parallel path points on the proper side of the reference line?
The problem likely is in TestLine::calculateParallelPoints().
I used openFrameworks 0.90 for this. Here's the code:
ofApp.h
#pragma once
#include "ofMain.h"
#include "TestLine.h"
class ofApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
int sectors_wide;
int sectors_tall;
vector<TestLine> testLines;
};
ofApp.cpp
#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup(){
ofSetBackgroundColorHex(0x000000);
sectors_wide = 4;
sectors_tall = 2;
TestLine t1 = TestLine(0,0,sectors_wide,sectors_tall);
t1.raw_points.push_back(ofPoint(0.9,0.5));
t1.raw_points.push_back(ofPoint(0.8,0.6));
t1.raw_points.push_back(ofPoint(0.7,0.4));
t1.raw_points.push_back(ofPoint(0.6,0.6));
t1.raw_points.push_back(ofPoint(0.5,0.4));
t1.raw_points.push_back(ofPoint(0.4,0.4));
t1.raw_points.push_back(ofPoint(0.3,0.5));
testLines.push_back(t1);
TestLine t2 = TestLine(1,0,sectors_wide,sectors_tall);
t2.raw_points.push_back(ofPoint(0.3,0.5)); //
t2.raw_points.push_back(ofPoint(0.4,0.4));
t2.raw_points.push_back(ofPoint(0.5,0.4));
t2.raw_points.push_back(ofPoint(0.6,0.6));
t2.raw_points.push_back(ofPoint(0.7,0.4));
t2.raw_points.push_back(ofPoint(0.8,0.6));
t2.raw_points.push_back(ofPoint(0.9,0.5));
testLines.push_back(t2);
TestLine t3 = TestLine(2,0,sectors_wide,sectors_tall);
t3.raw_points.push_back(ofPoint(0.1,0.2));
t3.raw_points.push_back(ofPoint(0.7,0.4));
t3.raw_points.push_back(ofPoint(0.4,0.45));
t3.raw_points.push_back(ofPoint(0.6,0.5));
t3.raw_points.push_back(ofPoint(0.9,0.9));
testLines.push_back(t3);
TestLine t4 = TestLine(3,0,sectors_wide,sectors_tall);
t4.raw_points.push_back(ofPoint(0.5,0.5));
t4.raw_points.push_back(ofPoint(0.9,0.5));
t4.raw_points.push_back(ofPoint(0.5,0.1));
t4.raw_points.push_back(ofPoint(0.1,0.1));
t4.raw_points.push_back(ofPoint(0.1,0.8));
t4.raw_points.push_back(ofPoint(0.8,0.6));
testLines.push_back(t4);
TestLine t5 = TestLine(0,1,sectors_wide,sectors_tall);
t5.raw_points.push_back(ofPoint(0.4,0.4));
t5.raw_points.push_back(ofPoint(0.6,0.5));
t5.raw_points.push_back(ofPoint(0.8,0.4));
testLines.push_back(t5);
TestLine t6 = TestLine(1,1,sectors_wide,sectors_tall);
t6.raw_points.push_back(ofPoint(0.7,0.1));
t6.raw_points.push_back(ofPoint(0.2,0.3));
t6.raw_points.push_back(ofPoint(0.7,0.5));
testLines.push_back(t6);
TestLine t7 = TestLine(2,1,sectors_wide,sectors_tall);
t7.raw_points.push_back(ofPoint(0.2,0.1));
t7.raw_points.push_back(ofPoint(0.7,0.3));
t7.raw_points.push_back(ofPoint(0.2,0.5));
testLines.push_back(t7);
TestLine t8 = TestLine(3,1,sectors_wide,sectors_tall);
t8.raw_points.push_back(ofPoint(0.8,0.5));
t8.raw_points.push_back(ofPoint(0.6,0.4));
t8.raw_points.push_back(ofPoint(0.4,0.5));
testLines.push_back(t8);
// Convert raw points to real points in the grid space
for (int i = 0; i < testLines.size(); i++) {
testLines[i].processRawPoints();
testLines[i].calculateParallelPoints();
}
}
//--------------------------------------------------------------
void ofApp::update(){
}
//--------------------------------------------------------------
void ofApp::draw(){
ofSetBackgroundColorHex(0x000000);
ofSetColor(255, 255, 255);
for (int i = 0; i < testLines.size(); i++) {
testLines[i].displayTestLine();
}
}
TestLine.h
#include "ofMain.h"
class TestLine {
public:
TestLine(float _x, float _y, int _sec_wide, int _sec_tall);
void displayTestLine();
void calculateParallelPoints();
void processRawPoints();
float x_coord;
float y_coord;
float x_min;
float x_max;
float y_min;
float y_max;
vector<float> angles;
vector<ofPoint> raw_points;
vector<ofPoint> points;
vector<ofPoint> forward_points;
vector<ofPoint> reverse_points;
ofPolyline line;
ofPolyline forward_line;
ofPolyline reverse_line;
};
TestLine.cpp
#include "TestLine.h"
TestLine::TestLine(float _x, float _y, int _sec_wide, int _sec_tall){
x_coord = _x;
y_coord = _y;
int w = ofGetWindowWidth();
int h = ofGetWindowHeight();
float allowed_w = (float)w / _sec_wide;
float allowed_h = (float)h / _sec_tall;
x_min = x_coord * allowed_w;
x_max = x_min + allowed_w;
y_min = y_coord * allowed_h;
y_max = y_min + allowed_h;
}
void TestLine::calculateParallelPoints(){
for (int i = 0; i < points.size(); i++) {
if (i == 0) {
ofVec2f v2 = points[i];
ofVec2f v1 = points[i+1];
float angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
angles.push_back(angle);
cout << "Start: " << angle << endl;
}
if (i > 0 && i < points.size() - 1) {
ofVec2f v1 = points[i];
ofVec2f v2 = points[i-1];
float back_angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
v2 = points[i];
v1 = points[i+1];
float front_angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
float final_angle = (back_angle + front_angle) / 2;
cout << "BACK ANGLE: " << back_angle << ", FRONT ANGLE: " << front_angle << ", FINAL ANGLE: " << final_angle << endl;
float prev_x = points[i-1].x;
float prev_y = points[i-1].y;
float this_x = points[i].x;
float this_y = points[i].y;
float next_x = points[i+1].x;
float next_y = points[i+1].y;
angles.push_back(final_angle);
}
if (i == points.size() - 1) {
ofVec2f v1 = points[i];
ofVec2f v2 = points[i-1];
float angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
angles.push_back(angle);
cout << "End: " << angle << endl;
}
line.addVertex(points[i]);
}
// Now using the points and the angles to calculate the forward and reverse points
for (int i = 0; i < points.size(); i++) {
float forward_angle = angles[i] + 90;
float reverse_angle = angles[i] - 90;
// cout << forward_angle << ", " << reverse_angle << endl;
float forward_x = points[i].x + cos(ofDegToRad(forward_angle)) * 8;
float forward_y = points[i].y + sin(ofDegToRad(forward_angle)) * 8;
forward_points.push_back(ofPoint(forward_x, forward_y));
float reverse_x = points[i].x + cos(ofDegToRad(reverse_angle)) * 8;
float reverse_y = points[i].y + sin(ofDegToRad(reverse_angle)) * 8;
reverse_points.push_back(ofPoint(reverse_x, reverse_y));
}
}
void TestLine::processRawPoints(){
for (int i = 0; i < raw_points.size(); i++) {
float newx = ofMap(raw_points[i].x, 0, 1, x_min, x_max);
float newy = ofMap(raw_points[i].y, 0, 1, y_min, y_max);
points.push_back(ofPoint(newx,newy));
}
}
void TestLine::displayTestLine(){
ofSetColor(128,128,128);
line.draw();
ofSetColor(255, 255, 255);
ofDrawCircle(points[0].x, points[0].y, 3);
ofSetColor(255, 0, 0);
for (int i = 0; i < forward_points.size(); i++) {
ofDrawCircle(forward_points[i].x, forward_points[i].y, 2);
}
ofSetColor(0, 255, 0);
for (int i = 0; i < reverse_points.size(); i++) {
ofDrawCircle(reverse_points[i].x, reverse_points[i].y, 2);
}
}
I ended up handling this with several conditional statements, though I feel there's probably a better way to handle it with trig. Here's the amended TestLine.cpp file.
TestLine.cpp
#include "TestLine.h"
TestLine::TestLine(float _x, float _y, int _sec_wide, int _sec_tall){
x_coord = _x;
y_coord = _y;
int w = ofGetWindowWidth();
int h = ofGetWindowHeight();
float allowed_w = (float)w / _sec_wide;
float allowed_h = (float)h / _sec_tall;
x_min = x_coord * allowed_w;
x_max = x_min + allowed_w;
y_min = y_coord * allowed_h;
y_max = y_min + allowed_h;
}
void TestLine::calculateParallelPoints(){
for (int i = 0; i < points.size(); i++) {
if (i == 0) {
ofVec2f v2 = points[i];
ofVec2f v1 = points[i+1];
float angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
angles.push_back(angle);
cout << endl;
cout << "Start: " << angle << endl;
}
if (i > 0 && i < points.size() - 1) {
ofVec2f v1 = points[i];
ofVec2f v2 = points[i-1];
float back_angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
v2 = points[i];
v1 = points[i+1];
float front_angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
float final_angle = (back_angle + front_angle) / 2; // back_angle + front_angle
float prev_x = points[i-1].x;
float prev_y = points[i-1].y;
float this_x = points[i].x;
float this_y = points[i].y;
float next_x = points[i+1].x;
float next_y = points[i+1].y;
// Here is the addition that addressed the problem.
if ((prev_x > this_x && prev_y <= this_y && next_x < this_x && next_y < this_y) ||
(prev_x > this_x && prev_y > this_y && next_x < this_x && next_y >= this_y)) {
final_angle += 180;
}
cout << "BACK ANGLE: " << back_angle << ", FRONT ANGLE: " << front_angle << ", FINAL ANGLE: " << final_angle << endl;
angles.push_back(final_angle);
}
if (i == points.size() - 1) {
ofVec2f v1 = points[i];
ofVec2f v2 = points[i-1];
float angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
angles.push_back(angle);
cout << "End: " << angle << endl << endl;
}
line.addVertex(points[i]);
}
// Now using the points and the angles to calculate the forward and reverse points
for (int i = 0; i < points.size(); i++) {
float forward_angle = angles[i] + 90;
float reverse_angle = angles[i] - 90;
// cout << forward_angle << ", " << reverse_angle << endl;
float forward_x = points[i].x + cos(ofDegToRad(forward_angle)) * 8;
float forward_y = points[i].y + sin(ofDegToRad(forward_angle)) * 8;
forward_points.push_back(ofPoint(forward_x, forward_y));
float reverse_x = points[i].x + cos(ofDegToRad(reverse_angle)) * 8;
float reverse_y = points[i].y + sin(ofDegToRad(reverse_angle)) * 8;
reverse_points.push_back(ofPoint(reverse_x, reverse_y));
}
}
void TestLine::processRawPoints(){
for (int i = 0; i < raw_points.size(); i++) {
float newx = ofMap(raw_points[i].x, 0, 1, x_min, x_max);
float newy = ofMap(raw_points[i].y, 0, 1, y_min, y_max);
points.push_back(ofPoint(newx,newy));
}
}
void TestLine::displayTestLine(){
ofSetColor(128,128,128);
line.draw();
ofSetColor(255, 255, 255);
ofDrawCircle(points[0].x, points[0].y, 3);
ofSetColor(255, 0, 0);
for (int i = 0; i < forward_points.size(); i++) {
ofDrawCircle(forward_points[i].x, forward_points[i].y, 2);
}
ofSetColor(0, 255, 0);
for (int i = 0; i < reverse_points.size(); i++) {
ofDrawCircle(reverse_points[i].x, reverse_points[i].y, 2);
}
}

OpenGL: Bezier surface with triangulation

I tried to draw Bezier surface using OpenGL. My program reads an input file with number of sample points for plot, control points and color palette for surface coloring. It must output a new window with surface plot where I can manipulate properties of surface and control points.
Points generated from Bernstein polynomial are triangulated and assigned a color by color palette by mapping it from minimum and maximum height of triangles.
When I execute it, one row of triangles is assigned with a wrong color. I thought that I have an error in one of my loops but changing values proved nothing.
Here is the code:
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <cmath>
using namespace std;
struct vertex
{
float x;
float y;
float z;
};
struct RGB
{
float r;
float g;
float b;
};
int main_window;
bool TM = true;
vertex surfaceTranslate;
float surfaceRotate = 0;
vertex camera;
vertex up;
int currentPointX = 0, currentPointY = 0;
vertex toY;
int SampleR, SampleC;
int M, N;
int K;
vector < vector <vertex> > points;
vector <RGB> palette;
vector < vector <vertex> > control;
float minH, maxH;
int fact (int n)
{
if (!n || n == 1)
{
return 1;
}
return n * fact (n - 1);
}
int C (int n, int i)
{
return fact (n) / (fact (i) * fact (n - i));
}
void initialDisplay(void)
{
glClearColor (1.0, 1.0, 1.0, 1.0);
glDisable (GL_LIGHTING);
glEnable (GL_RESCALE_NORMAL);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
gluPerspective (60, 1, 1, 1000000);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity();
}
void updateControl()
{
control.clear();
for (int i = 0; i < SampleR; ++i)
{
vector <vertex> temp;
for (int j = 0; j < SampleC; ++j)
{
float u = (float)(i) / (SampleR - 1);
float v = (float)(j) / (SampleC - 1);
vertex p;
p.x = p.y = p.z = 0;
for (int k = 0; k < M; ++k)
{
for (int m = 0; m < N; ++m)
{
float B_u = C (M - 1, k) * pow (u, k) * pow (1 - u, M - 1 - k);
float B_v = C (N - 1, m) * pow (v, m) * pow (1 - v, N - 1 - m);
p.x += B_u * B_v * points[k][m].x;
p.y += B_u * B_v * points[k][m].y;
p.z += B_u * B_v * points[k][m].z;
}
}
temp.push_back (p);
}
control.push_back (temp);
}
maxH = 1 << ((sizeof(float) * 8) - 1);
minH = -maxH;
for (int i = 0; i < SampleR - 1; ++i)
{
for (int j = 0; j < SampleC - 1; ++j)
{
float h = (control[i][j].y + control[i + 1][j].y + control[i][j + 1].y) / 3;
if (h > maxH)
{
maxH = h;
}
if (h < minH)
{
minH = h;
}
h = (control[i + 1][j].y + control[i][j + 1].y + control[i + 1][j + 1].y) / 3;
if (h > maxH)
{
maxH = h;
}
if (h < minH)
{
minH = h;
}
}
}
for (int i = 0; i < SampleR; ++i)
{
for (int j = 0; j < SampleC; ++j)
{
toY.x += control[i][j].x;
toY.z += control[i][j].z;
}
toY.x /= SampleR * SampleC;
toY.z /= SampleR * SampleC;
}
}
void display(void)
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt (camera.x, camera.y, camera.z, 0, 0, 0, up.x, up.y, up.z);
glTranslatef (surfaceTranslate.x, surfaceTranslate.y, surfaceTranslate.z);
glTranslatef (toY.x, toY.y, toY.z);
glRotatef (surfaceRotate, 0, 1, 0);
glTranslatef (- toY.x, -toY.y, -toY.z);
if (!TM)
{
glPointSize (10);
glBegin (GL_POINTS);
for (int i = 0; i < M; ++i)
{
for (int j = 0; j < N; ++j)
{
if (i == currentPointX && j == currentPointY)
{
glColor3f (1.0, 0.0, 0.0);
}
else
{
glColor3f (0.0, 0.0, 1.0);
}
glVertex3f (points[i][j].x, points[i][j].y, points[i][j].z);
}
}
glEnd();
}
glBegin (GL_TRIANGLES);
for (int i = 0; i < SampleR - 1; ++i)
{
for (int j = 0; j < SampleC - 1; ++j)
{
glVertex3f (control[i][j].x, control[i][j].y, control[i][j].z);
glVertex3f (control[i + 1][j].x, control[i + 1][j].y, control[i + 1][j].z);
glVertex3f (control[i][j + 1].x, control[i][j + 1].y, control[i][j + 1].z);
float h = (control[i][j].y + control[i + 1][j].y + control[i][j + 1].y) / 3;
int index = K * (h - minH) / (maxH - minH);
glColor3f (palette[index].r / 255, palette[index].g / 255, palette[index].b / 255);
glVertex3f (control[i + 1][j].x, control[i + 1][j].y, control[i + 1][j].z);
glVertex3f (control[i][j + 1].x, control[i][j + 1].y, control[i][j + 1].z);
glVertex3f (control[i + 1][j + 1].x, control[i + 1][j + 1].y, control[i + 1][j + 1].z);
h = (control[i + 1][j].y + control[i][j + 1].y + control[i + 1][j + 1].y) / 3;
index = K * (h - minH) / (maxH - minH);
glColor3f (palette[index].r / 255, palette[index].g / 255, palette[index].b / 255);
}
}
glEnd();
glutSwapBuffers();
}
void keyboardEvent (unsigned char key, int x, int y)
{
if (TM)
{
switch (key)
{
case ('2'):
TM = false;
glutSetWindowTitle ("Surface Editing Mode");
break;
case ('q'):
--surfaceTranslate.x;
break;
case ('w'):
++surfaceTranslate.x;
break;
case ('a'):
--surfaceTranslate.y;
break;
case ('s'):
++surfaceTranslate.y;
break;
case ('z'):
--surfaceTranslate.z;
break;
case ('x'):
++surfaceTranslate.z;
break;
case ('r'):
++surfaceRotate;
break;
case ('t'):
--surfaceRotate;
break;
}
}
else
{
switch (key)
{
case ('1'):
TM = true;
glutSetWindowTitle ("Transformation Mode");
break;
case ('q'):
--points[currentPointX][currentPointY].x;
updateControl();
break;
case ('w'):
++points[currentPointX][currentPointY].x;
updateControl();
break;
case ('a'):
--points[currentPointX][currentPointY].y;
updateControl();
break;
case ('s'):
++points[currentPointX][currentPointY].y;
updateControl();
break;
case ('z'):
--points[currentPointX][currentPointY].z;
updateControl();
break;
case ('x'):
++points[currentPointX][currentPointY].z;
updateControl();
break;
case ('i'):
if (!(SampleR % 2))
{
SampleR /= 2;
updateControl();
}
break;
case ('o'):
SampleR *= 2;
updateControl();
break;
case ('k'):
if (!(SampleC % 2))
{
SampleC /= 2;
updateControl();
}
break;
case ('l'):
SampleC *= 2;
updateControl();
break;
}
}
glutPostRedisplay();
}
void arrowKeys (int key, int x, int y)
{
if (!TM)
{
switch (key)
{
case (GLUT_KEY_UP):
if (currentPointY < N - 1)
{
++currentPointY;
}
break;
case (GLUT_KEY_DOWN):
if (currentPointY)
{
--currentPointY;
}
break;
case (GLUT_KEY_LEFT):
if (currentPointX)
{
--currentPointX;
}
break;
case (GLUT_KEY_RIGHT):
if (currentPointX < M - 1)
{
++currentPointX;
}
break;
}
glutPostRedisplay();
}
}
void changeDirection (int x, int y)
{
float dist = sqrt (pow (camera.x, 2) + pow (camera.y, 2) + pow (camera.z, 2));
camera.x = dist * sin (360.0 / 800 * x * 0.0174532) * sin (360.0 / 800 * y * 0.0174532);
camera.y = dist * cos (360.0 / 800 * y * 0.0174532);
camera.z = dist * cos (360.0 / 800 * x * 0.0174532) * sin (360.0 / 800 * y * 0.0174532);
up.x = dist * sin (360.0 / 800 * x * 0.0174532) * sin (360.0 / 800 * y * 0.0174532 - 1) - camera.x;
up.y = dist * cos (360.0 / 800 * y * 0.0174532 - 1) - camera.y;
up.z = dist * cos (360.0 / 800 * x * 0.0174532) * sin (360.0 / 800 * y * 0.0174532 - 1) - camera.z;
glutPostRedisplay();
}
void mouseEvent (int key, int state, int x, int y)
{
if (key == GLUT_KEY_LEFT)
{
changeDirection (x, y);
}
}
void readFile (char *fname)
{
ifstream file (fname);
if (file.is_open())
{
file >> SampleR >> SampleC;
file >> M >> N;
for (int i = 0; i < M; ++i)
{
vector <vertex> tempv;
for (int j = 0; j < N; ++j)
{
vertex temp;
file >> temp.x >> temp.y >> temp.z;
tempv.push_back (temp);
}
points.push_back (tempv);
}
file >> K;
for (int i = 0; i < K; ++i)
{
RGB temp;
file >> temp.r >> temp.g >> temp.b;
palette.push_back (temp);
}
}
file.close();
}
int main (int argc, char *argv[])
{
surfaceTranslate.x = surfaceTranslate.y = surfaceTranslate.z = toY.x = toY.y = toY.z = up.x = up.z = 0;
up.y = 1;
camera.x = camera.y = camera.z = 100;
readFile (argv[1]);
updateControl();
glutInit (&argc,argv);
glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowPosition (50, 50);
glutInitWindowSize (800, 800);
main_window = glutCreateWindow ("Transformation Mode");
glutDisplayFunc (display);
glutKeyboardFunc (keyboardEvent);
glutSpecialFunc (arrowKeys);
glutMouseFunc (mouseEvent);
glutMotionFunc (changeDirection);
initialDisplay();
glutMainLoop();
}
Two things are problematic in display():
glColor3f should be called before the calls to glVertex3f to color them
your palette index calculation is wrong when h == maxH, it makes an out of bounds access
When h == maxH, (h - minH)/(maxH - minH) equals 1.0f, which means the line:
index = K * (h - minH) / (maxH - minH);
simplifies into:
index = K;
and K is the size of the palette. An easy fix would be to add a call to min(), like that:
index = min(K-1, K * (h - minH) / (maxH - minH));