I have been working on a 2D top-down game solely in SFML and C++, that has a tilemap. I cannot rid the tilemap of vertical line artifacts when zooming in and out or moving the render view at different zoom levels. I attached an image below of the problem; circled in red.
[edit]
There are a lot of factors that make this bug inconsistent.
If I use a tile_atlas from only one tile, there is no artifacts. If I map each texture to a tile a.k.a not using vertex arrays; I did not see any artifacts but it is anywhere from 10x to 15x slower with the same number of tiles on the screen. I have tried finding zoom levels that don't cause artifacts, and there are some. but the levels are almost random and makes zoom in and out, not smooth and choppy.
I have tried numerous tutorials and forum "fixes" that have not completely worked. I have completely rewrote the underlying tile engine 4 separate times to no avail.
https://en.sfml-dev.org/forums/index.php?topic=15747.0
topic=14504
topic=5952
topic=13637.15
https://www.sfml-dev.org/tutorials/2.5/graphics-view.php
https://www.binpress.com/creating-city-building-game-with-sfml/
https://www.sfml-dev.org/tutorials/2.5/graphics-vertex-array.php
[edit]
I have read the Terreria scaling issue, the fix to make extra large textures then scale, or multiple textures, one for each zoom level. seem exhaustive. I am looking for a programmatic way of achieving the scaling correctly.
This is post is my last attempt to fix the code, otherwise I will need to change languages.
I believe the main issue comes from the zoom in/out functions of the program.
I have tried many, variations/attempt to get this to work +0.5f offset,+0.375f offset, not pixel perfect
if (sf::Keyboard::isKeyPressed(sf::Keyboard::E))
{
float zoom_in = 0.995f;
float nz = last_sq * zoom_in;
nz = std::floor(nz);
float now = nz / last_sq;
if (nz <= 10)
continue;
last_sq = nz;
std::cout << now << std::endl;
cam.zoom(now);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
{
cam.move(0.f, -0.02f);
float x = cam.getCenter().x;
float y = cam.getCenter().y;
x = std::floor(x);
y = std::floor(y);
//std::cout << "x: " << x << "\ty: " << y << std::endl;
cam.setCenter(x, y);
}
Here is the entire code.
main.cpp
#include "chunk_map.h"
#include "tile_atlas.h"
#include <vector>
//#include "main.h"
#include "map.h"
#include <iostream>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/View.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/Window/Keyboard.hpp>
#include "animation_handler.h"
int main(int argc, char* argv[])
{
sf::RenderWindow app(sf::VideoMode(600, 600), "Tilemap Example");
// Hard set fps to monitor refresh rate.
// textures to load.
/*text_mgr.loadTexture("grass", "grass.png");
text_mgr.loadTexture("high_grass", "high_grass.png");
Animation staticAnim(0, 0, 1.0f);
tileAtlas["grass"] = Tile(32, 1, text_mgr.getRef("grass"),{ staticAnim },
TileType::GRASS, 50, 0, 1);
tileAtlas["high_grass"] = Tile(32, 1, text_mgr.getRef("high_grass"),{ staticAnim },
TileType::HIGH_GRASS, 100, 0, 1);*/
//Map map;
//map.load(50, 50, tileAtlas);
#ifdef NDEBUG
app.setVerticalSyncEnabled(true);
std::cout << "#################\n";
#endif // DEBUG
//app.setVerticalSyncEnabled(true);
sf::View cam = app.getDefaultView();
tile_atlas atlas = tile_atlas();
std::vector<chunk_map> map;
for (int x = 0; x < 5; x++)
{
for (int y = 0; y < 5; y++)
{
map.push_back(chunk_map());
map.back().set_texture(atlas.get_atlas());
map.back().set_position(10 * x, 10 * y, 10 * (x + 1), 10 * (y + 1));
map.back().load_tiles();
}
}
sf::Clock clock;
int checked = 0;
int last_sq = 600;
while (app.isOpen())
{
//sf::Time elapsed = clock.restart();
//float dt = elapsed.asSeconds();
sf::Event eve;
while (app.pollEvent(eve))
if (eve.type == sf::Event::Closed)
app.close();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::P))
std::cout << "view x: " << cam.getSize().x << "\tview y: " << cam.getSize().y << std::endl;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Q))
{
float zoom_out = 1.005f;
float nz = last_sq * zoom_out;
nz = std::ceil(nz);
float now = nz / last_sq;
last_sq = nz;
std::cout << now << std::endl;
cam.zoom(now);
//float x = cam.getCenter().x;
//float y = cam.getCenter().y;
//x = std::floor(x);
//y = std::floor(y);
////std::cout << "x: " << x << "\ty: " << y << std::endl;
//cam.setCenter(x, y);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::E))
{
float zoom_in = 0.995f;
float nz = last_sq * zoom_in;
nz = std::floor(nz);
float now = nz / last_sq;
if (nz <= 10)
continue;
last_sq = nz;
std::cout << now << std::endl;
cam.zoom(now);
//float x = cam.getCenter().x;
//float y = cam.getCenter().y;
//x = std::floor(x);
//y = std::floor(y);
////std::cout << "x: " << x << "\ty: " << y << std::endl;
//cam.setCenter(x, y);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
{
cam.move(0.f, -0.02f);
float x = cam.getCenter().x;
float y = cam.getCenter().y;
x = std::floor(x);
y = std::floor(y);
//std::cout << "x: " << x << "\ty: " << y << std::endl;
cam.setCenter(x, y);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
cam.move(-0.02f, 0.f);
float x = cam.getCenter().x;
float y = cam.getCenter().y;
x = std::floor(x);
y = std::floor(y);
//std::cout << "x: " << x << "\ty: " << y << std::endl;
cam.setCenter(x, y);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
{
cam.move(0.f, 0.02f);
float x = cam.getCenter().x;
float y = cam.getCenter().y;
x = std::ceil(x);
y = std::ceil(y);
//std::cout << "x: " << x << "\ty: " << y << std::endl;
cam.setCenter(x, y);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
cam.move(0.02f, 0.f);
float x = cam.getCenter().x;
float y = cam.getCenter().y;
x = std::ceil(x);
y = std::ceil(y);
//std::cout << "x: " << x << "\ty: " << y << std::endl;
cam.setCenter(x, y);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
sf::Time elapsed = clock.getElapsedTime();
float t = elapsed.asSeconds();
int time = std::floor(t);
if (checked < time)
{
checked = time;
cam.move(0.01f, 0.f);
float x = cam.getCenter().x;
float y = cam.getCenter().y;
x = std::ceil(x);
y = std::ceil(y);
std::cout << "x: " << x << "\ty: " << y << std::endl;
cam.setCenter(x, y);
}
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
app.close();
app.setView(cam);
#ifdef _DEBUG
app.clear();
#endif // DEBUG
//map.draw(app, dt);
for (int i = 0; i < 25; i++)
{
app.draw(map.at(i));
}
app.display();
}
}
chunk_map.h
#pragma once
#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/VertexArray.hpp>
#include <vector>
class chunk_map : public sf::Drawable
{
private:
//change values of these to match your needs and improve performance
enum { tilesize = 32, chunksize = 32};
//tile size float
float tile_size_float = 32.0f;
// Draw chunk
virtual void draw(sf::RenderTarget& target, sf::RenderStates states)const;
// texture for chunk
sf::Texture m_texture;
// chunk dimensions
int tiles_per_chunk_x;
int tiles_per_chunk_y;
//start x and y and ending x and y scaled to tile size. a.k.a.
// 1,1 = tile 1,1. 10,10, equals tile 10,10
int chunk_start_x;
int chunk_start_y;
int chunk_end_x;
int chunk_end_y;
// Vertex array of positions of tiles in chunk
std::vector<std::vector<sf::VertexArray> > m_chunks;
// Append tiles.
void append_tile(int gx, int gy, sf::VertexArray& garr);
public:
chunk_map();
~chunk_map();
void load_tiles();
void set_texture(sf::Texture);
void set_position(int chunk_start_x, int chunk_start_y,
int chunk_end_x, int chunk_end_y);
};
chunk_map.cpp
#include "chunk_map.h"
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/System/Vector2.hpp>
#include <SFML/Graphics/Vertex.hpp>
chunk_map::chunk_map()
{
}
chunk_map::~chunk_map()
{
}
void chunk_map::load_tiles()
{
/*
Tile loading this is were the tiles are added to the Quadrantics of the tilemap.
this is the entire chunk_map loop
*/
if ((chunk_end_x * chunk_end_y) == 0)//empty map - possibly forgotten to fill data struct
{
//to stop displaying at all after failed loading:
tiles_per_chunk_x = 0;
tiles_per_chunk_y = 0;
m_chunks.clear();
return;
}
chunk_map::tiles_per_chunk_x = (chunk_end_x / chunksize) + 1;
chunk_map::tiles_per_chunk_y = (chunk_end_y / chunksize) + 1;
m_chunks.assign(tiles_per_chunk_x, std::vector<sf::VertexArray>(tiles_per_chunk_y, sf::VertexArray(sf::Quads)));//ready up empty 2d arrays
for (int iy = chunk_start_y; iy < chunk_end_y; ++iy)
{
for (int ix = chunk_start_x; ix < chunk_end_x; ++ix)
{
append_tile(ix, iy, m_chunks[ix / chunksize][iy / chunksize]);
}
}
}
void chunk_map::append_tile(int gx, int gy, sf::VertexArray& garr)
{
/*
This is the specific tile vertex, broken from the other function to decrease complexitity.
*/
int tile_selection_index_x = rand() % 2;
int tile_selection_index_y = 0;
float f_tx = tile_selection_index_x * tile_size_float;
float f_ty = tile_selection_index_y * tile_size_float;
sf::Vertex ver;
//____________________________________________________________________________________________________________
ver.position = sf::Vector2f(gx * tile_size_float, gy * tile_size_float);
//texture in position of text atlas
//top left corner
//ver.texCoords = sf::Vector2f( 0.f, 0.f);
ver.texCoords = sf::Vector2f(f_tx, f_ty);
garr.append(ver);
//____________________________________________________________________________________________________________
ver.position = sf::Vector2f(gx * tile_size_float + tile_size_float, gy * tile_size_float);
//texture in position of text atlas
//top right corner
//ver.texCoords = sf::Vector2f( tile_size_float, 0.f);
ver.texCoords = sf::Vector2f(f_tx + tile_size_float, f_ty);
garr.append(ver);
//____________________________________________________________________________________________________________
ver.position = sf::Vector2f(gx * tile_size_float + tile_size_float, gy * tile_size_float + tile_size_float);
//texture in position of text atlas
//bottom right corner
//ver.texCoords = sf::Vector2f( tile_size_float, tile_size_float);
ver.texCoords = sf::Vector2f(f_tx + tile_size_float, f_ty + tile_size_float);
garr.append(ver);
//____________________________________________________________________________________________________________
ver.position = sf::Vector2f(gx * tile_size_float, gy * tile_size_float + tile_size_float);
//texture in position of text atlas
//bottom left corner
//ver.texCoords = sf::Vector2f( 0.f, tile_size_float);
ver.texCoords = sf::Vector2f(f_tx, f_ty + tile_size_float);
garr.append(ver);
}
void chunk_map::set_texture(sf::Texture t)
{
/*
Sets the texture data for this chunk map from the texture atlas.
*/
m_texture = t;
// TODO test this feature
// Attempt to optimize tearing on zooming to a different view.
//m_texture.setSmooth(true);
}
void chunk_map::set_position(int chunk_start_x, int chunk_start_y,
int chunk_end_x, int chunk_end_y)
{
/*
Initialize the accordinates of the start of the chunk_map to the end.
*/
chunk_map::chunk_start_x = chunk_start_x;
chunk_map::chunk_start_y = chunk_start_y;
chunk_map::chunk_end_x = chunk_end_x;
chunk_map::chunk_end_y = chunk_end_y;
}
void chunk_map::draw(sf::RenderTarget& target, sf::RenderStates states)const
{
/*
The actual draw call to this specific chunk_map
*/
// position variables for this draw.
int left = 0;
int right = 0;
int top = 0;
int bottom = 0;
//get top left point of view
sf::Vector2f temp = target.getView().getCenter() - (target.getView().getSize() / 2.f);
//get top left point of view
left = static_cast<int>(temp.x / (chunksize * tilesize));
top = static_cast<int>(temp.y / (chunksize * tilesize));
//get bottom right point of view
temp += target.getView().getSize();
right = 1 + static_cast<int>(temp.x / (chunksize * tilesize));
bottom = 1 + static_cast<int>(temp.y / (chunksize * tilesize));
//clamp these to fit into array bounds:
left = std::max(0, std::min(left, tiles_per_chunk_x));
top = std::max(0, std::min(top, tiles_per_chunk_y));
right = std::max(0, std::min(right, tiles_per_chunk_x));
bottom = std::max(0, std::min(bottom, tiles_per_chunk_y));
//set texture and draw visible chunks:
states.texture = &m_texture;
for (int ix = left; ix < right; ++ix)
{
for (int iy = top; iy < bottom; ++iy)
{
target.draw(m_chunks[ix][iy], states);
}
}
}
tile_atlas.h
#pragma once
#include <SFML/Graphics/Texture.hpp>
class tile_atlas
{
private:
sf::Texture atlas_texture;
public:
tile_atlas();
~tile_atlas();
sf::Texture& get_atlas();
};
tile_atlas.cpp
#include "tile_atlas.h"
#include <iostream>
#include <string>
tile_atlas::tile_atlas()
{
std::string file_string = "tilemap_test.png";
if (!atlas_texture.loadFromFile(file_string))
{
std::cout << "Failed loading file: " << file_string << std::endl;
exit(1);
}
}
tile_atlas::~tile_atlas()
{
}
sf::Texture& tile_atlas::get_atlas()
{
return atlas_texture;
}
I am trying to fix this code to remove vertical artifacts so the above image will always look like this no matter if moving the view or zooming in/out.
[code for answer]
Using #Mario's answer this is the code I wrote (at the bottom of main.cpp) that completely fixed the artifacts.
here is a great link showing an example.
https://www.sfml-dev.org/tutorials/2.5/graphics-draw.php#off-screen-drawing
#ifdef _DEBUG
app.clear();
#endif // DEBUG
//map.draw(app, dt);
/*-----------------------------------------------------------*/
// Draw the texture
//rt.clear();
rt.draw(map.at(0));
rt.display();
if (cam.getSize().x < 500)
{
rt.setSmooth(false);
}
else
{
rt.setSmooth(true);
}
//// get the target texture (where the stuff has been drawn)
const sf::Texture& texture = rt.getTexture();
sf::Sprite sprite(texture);
app.draw(sprite);
//app.draw(map.at(0));
/*-----------------------------------------------------------*/
app.display();
Simple, yet effective:
Render your pixels 1:1 without scaling to a render texture and then upscale that instead.
Might be a bit tricky to determine the correct position, zoom, etc. but it can be done.
I am writing a harmonic oscillator program , but it does an exponential decay instead and I am not sure why. I have the code below. I use F= -k x and F = m a
I assume m = 1 and k = 1, so a = -x
So my equations to find velocity and position are
v(t) = v(t-dt) - x(t-dt) * dt
x(t) = x(t-dt) + v(t-dt) * dt
I am not sure what I am doing wrong
The code associated with this is
#include <iostream>
#include <fstream>
using namespace std;
double updateX(double intialV, double intialX, double step);
double updateV(double intialV, double intialX, double step);
int main()
{
long double position[2];long double velocity[2];
position[0] = 0.0; velocity[0] = 1.0; double time = 0.0; double step = .1;
ofstream outputFile;
outputFile.open("velo1.dat");
outputFile << time << " " << position[0] << " " << velocity[0] << "\n";
for(int i = 1; i < 50; i++)
{
time = i * step;
velocity[1] = updateV(velocity[0], position[0], step);
position[1] = updateX(velocity[0], position[0], step);
outputFile << time << " " << position[1] << " " << velocity[1] << "\n";
velocity[0] = velocity[1];
position[0] = velocity[1];
}
return 0;
outputFile.close();
}
double updateX(double intialV, double intialX, double step)
{
double stuff =(intialX + intialV * step);
return stuff;
}
double updateV(double intialV, double intialX, double step)
{
double stuff = (intialV - intialX * step) ;
return stuff;
}
I've been writing this game to just type in the amount of humans and skeletons with a random attack chance. If you enter in the same amount when running the program over and over it generates the same amount. Why isn't the hitChance/random attack amount changing everytime the while loop restarts? When I print the attackChance(randGen) in the while loop it changes. Why isn't the randGen changing the output of the winner of the battle?
#include <iostream>
#include <string>
#include <random>
#include <ctime>
using namespace std;
int getNumSkeletons();
int getNumHumans();
void finishedBattleStats(int numHumans, int startHumans, int numSkeletons, int startSkeletons);
void simulatedBattle(int &numSkeletons, int &numHumans, int &startHumans, int &startSkeletons);
int main() {
int startHumans;
int startSkeletons;
int numHumans;
int numSkeletons;
cout << "------------------------------------\nSkeletons V Humans\n\n";
//Gets Number of Humans
numHumans = getNumHumans();
startHumans = numHumans;
//Gets Number of Skeletons
numSkeletons = getNumHumans();
startSkeletons = numSkeletons;
//Simulates Battle
simulatedBattle(numSkeletons, numHumans, startHumans, startSkeletons);
//End Battle Stats
finishedBattleStats(numHumans, startHumans, numSkeletons, startSkeletons);
cin.get();
return 0;
}
int getNumHumans() {
int numHumans;
cout << "Enter number of Humans: \n";
cin >> numHumans;
return numHumans;
}
int getNumSkeletons() {
int numSkeletons;
cout << "Enter number of Skeletons: \n";
cin >> numSkeletons;
return numSkeletons;
}
void simulatedBattle(int &numSkeletons, int &numHumans, int &startHumans, int &startSkeletons) {
static mt19937 randGen(time(NULL));
uniform_real_distribution<float> attackChance(0.0f, 1.0f);
//HumanProperties
float humanDamage = 30.0f;
float humanHitChance = 0.6f;
float humanCritChance = 0.2f;
float humanHealth = 50.0f;
float startHumanHealth = humanHealth;
float currentHuman = startHumanHealth;
//Skeleton Properties
float skeletonDamage = 20.0f;
float skeletonHitChance = 0.7f;
float skeletonCritChance = 0.2f;
float skeletonHealth = 40.0f;
float startSkeletonHealth = skeletonHealth;
float currentSkeleton = startSkeletonHealth;
char turn = 'H';
float hitChance;
while (numSkeletons > 0 && numHumans > 0) {
hitChance = attackChance(randGen);
if (turn == 'H') {
if (hitChance > humanHitChance) {
currentSkeleton -= humanDamage;
turn = 'S';
if (currentSkeleton < 0) {
numHumans--;
currentSkeleton = startSkeletonHealth;
turn = 'S';
}
}
}
else {
if (hitChance > skeletonHitChance) {
currentHuman -= skeletonDamage;
turn = 'H';
}
if (currentHuman < 0) {
numSkeletons--;
currentHuman = startHumanHealth;
turn = 'H';
}
}
}
}
void finishedBattleStats(int numHumans, int startHumans, int numSkeletons, int startSkeletons) {
if (numHumans == 0) {
cout << "Skeletons won! \n\n";
cout << "Skeletons left: " << numSkeletons << endl;
cout << "Skeleton Casualties: " << startSkeletons - numSkeletons << endl;
cout << "All " << startHumans << " humans are dead! \n\n";
cout << "Game Over!";
cin.get();
}
else {
cout << "Humans Won! \n\n";
cout << "Humans left: " << numHumans << endl;
cout << "Human Casualties: " << startHumans - numHumans << endl;
cout << "All " << startSkeletons << " skeletons are dead! \n\n";
cout << "Game Over!";
cin.get();
}
}
IMHO opinion you have a flaw in the battle calculations. That is, when a human's healths goes < 0 you subtract a skeleton and not a human and vice versa. Below is the correction. With a demo. I tried the demo a number of times and gives different results (ENJOY):
void simulatedBattle(int &numSkeletons, int &numHumans, int &startHumans, int &startSkeletons) {
uniform_real_distribution<float> attackChance(0.0f, 1.0f);
static mt19937 randGen(time(NULL));
//HumanProperties
float humanDamage = 25.0f;
float humanHitChance = 0.2f;
float humanCritChance = 0.2f;
float humanHealth = 50.0f;
float startHumanHealth = humanHealth;
float currentHuman = startHumanHealth;
//Skeleton Properties
float skeletonDamage = 20.0f;
float skeletonHitChance = 0.8f;
float skeletonCritChance = 0.2f;
float skeletonHealth = 40.0f;
float startSkeletonHealth = skeletonHealth;
float currentSkeleton = startSkeletonHealth;
char turn = 'H';
float hitChance;
while (numSkeletons > 0 && numHumans > 0) {
hitChance = attackChance(randGen);
if (turn == 'H') {
if (hitChance > humanHitChance) {
currentSkeleton -= humanDamage;
if (currentSkeleton < 0) {
--numSkeletons;
currentSkeleton = startSkeletonHealth;
}
turn = 'S';
}
}
else {
if (hitChance > skeletonHitChance) currentHuman -= skeletonDamage;
if (currentHuman < 0) {
--numHumans;
currentHuman = startHumanHealth;
}
turn = 'H';
}
}
}
LIVE DEMO
You have to seed a mersenne twister before use, or you'll always have the same output.
It is common practice to seed it with the internal clock:
// obtain a seed from the timer
myclock::duration d = myclock::now() - beginning;
unsigned seed2 = d.count();
generator.seed (seed2);
ref: std::mersenne_twister_engine::seed
Aight so, I'm in the process of making a simple terrain program, not exactly a game, but hey maybe someday. I'll start with describing the basics of how my program works. I'm using an two-dimension array to store the coordinates of the vertexes which make up a grid of triangles. The x and z values are assigned in the follow code:
//Sets up the array for the vertexes
int e = 0;
int p = 0;
for (int r = 0; r < 10; r++) {
for (int x = 0; x < 5; x++) {
grid[p][0] = x * 2;
grid[p][2] = e;
p++;
}
e += 2;
}
Okay, so the grid array is the one I use for storing the values of all the vertexes in the program, the first argument (the value in the first set of []) is used to choose a point, the second set of [] is the x, y, and z values, the code snippet above only sets values for the x and z coordinates.
The y value, height, is randomised at the start of the program using the following code:
//Random heights for the terrain
if (fir == true) {
for (int h = 0; h < 64; h++) {
grid[h][1] = rand() % 3;
}
fir = false;
}
And the code for drawing the triangles:
for (int q = 0; q < 11; q++) {
a = q * 5;
b = a + 1;
c = a + 5;
glBegin(GL_TRIANGLE_STRIP);
for (int dw = 0; dw < 8; dw++) {
//a
glColor3f(grid[a][1], grid[a][1], grid[a][1]);
glVertex3f(grid[a][0], grid[a][1], grid[a][2]);
//b
glColor3f(grid[b][1], grid[b][1], grid[b][1]);
glVertex3f(grid[b][0], grid[b][1], grid[b][2]);
//c
glColor3f(1, 0, 0);
glVertex3f(grid[c][0], grid[c][1], grid[c][2]);
a = c;
t = b;
b = a + 1;
c = t;
}
glEnd();
}
glutSwapBuffers();
}
The problem I'm having is that I can't make the size of the grid bigger without the program breaking, I would like to be able to change the size of the grid of triangles to whatever I like. when I try to change the for loop which goes through the x values (first code snippet) the program breaks, my movement speed drastically increases, moving the mouse has no effect on the camera rotation. When I do manage to find a set of values to work the grid of triangles is messed up, any points after the 5th row are all set to 0, I'll post the whole program, I don't know if it will run for everyone, but I think the problem is something that requires the whole program to be looked at in order to solve.
I think the problem may be due to variables being shared by different parts of the program, I have looked and looked but can't seem to find any cause. This is my last resort to solving this nightmare of a problem...
main.cpp
/*
*/
#include <iostream>
#include <glut.h>
#include <gl\GL.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <cstdlib>
#include "vector3f.h"
using namespace std;
//Variables
const int WINDOW_WIDTH = 1280;
const int WINDOW_HEIGHT = 720;
const char* WINDOW_TITLE = "Terrain";
const float WALKING_SPEED = 5.0;
const float MOUSE_SENSITIVITY = 0.3;
const float MAX_TILT = 90.0;
float grid[200][3];
float LAST_TIME;
float CURRENT_TIME;
float DELTA_TIME;
bool KEY[256];
int a;
int b;
int c;
int t;
int MOUSE_LAST_X;
int MOUSE_LAST_Y;
int MOUSE_CURRENT_X;
int MOUSE_CURRENT_Y;
int MOUSE_DELTA_X;
int MOUSE_DELTA_Y;
float red;
float green;
float blue;
bool fir = true;
//Object
vector3f CAMERA_POSITION;
vector3f CAMERA_ROTATION;
//Functions
void initialize();
void display();
void reshape(int w, int h);
void keyboardDown(unsigned char key, int x, int y);
void keyboardUp(unsigned char key, int x, int y);
void mouseMove(int x, int y);
void movement();
double degreesToRadians(double degrees);
double dsin(double theta);
double dcos(double theta);
double dtan(double theta);
void display() {
//cout << KEY[' '] << endl;
movement();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
//Camera transformations
glRotatef(CAMERA_ROTATION.x, 1, 0, 0);
glRotatef(CAMERA_ROTATION.y, 0, 1, 0);
glRotatef(CAMERA_ROTATION.z, 0, 0, 1);
glTranslatef(-CAMERA_POSITION.x, -CAMERA_POSITION.y, CAMERA_POSITION.z);
//Draw the basic triangle
glBegin(GL_TRIANGLES);
glColor3f(0.0, 0.0, 1.0);
glVertex3f(-1.0, -1.0, -3.0);
glColor3f(0.0, 1.0, 0.0);
glVertex3f(0.0, 1.0, -3.0);
glColor3f(1.0, 0.0, 0.0);
glVertex3f(10.0, -1.0, -3.0);
glEnd();
/*
1 0,0,0
2 2,0,0
3 4,0,0
4 6,0,0
5 0,0,2
6 2,0,2
7 4,0,2
8 6,0,2
9 0,0,4
10 2,0,4
11 4,0,4
12 6,0,4
13 0,0,6
14 2,0,6
15 4,0,6
16 6,0,6
*/
/*//Option 1 (2 for loops, four if statements)
for (int p = 0; p < 16; p++) {
for (int x = 0; x < 7; x += 2) {
grid[p][0] = x;
}
grid[p][1] = 0;
if (p < 4) {
grid[p][2] = 0;
}
if (p < 8 && p >= 4) {
grid[p][2] = 2;
}
if (p < 12 && p >= 8) {
grid[p][2] = 4;
}
if (p < 16 && p >= 12) {
grid[p][2] = 6;
}
}
*/
//Option 2 (three for loops)
//Sets up the array for the vertexes
int e = 0;
int p = 0;
for (int r = 0; r < 10; r++) {
for (int x = 0; x < 5; x++) {
grid[p][0] = x * 2;
grid[p][2] = e;
p++;
}
e += 2;
}
//Random heights for the terrain
if (fir == true) {
for (int h = 0; h < 64; h++) {
grid[h][1] = rand() % 3;
}
fir = false;
}
//cout << "point 0: " << grid[0][0] << ", " << grid[0][1] << ", " << grid[0][2] << endl;
//cout << "point 1: " << grid[1][0] << ", " << grid[1][1] << ", " << grid[1][2] << endl;
//cout << "point 2: " << grid[2][0] << ", " << grid[2][1] << ", " << grid[2][2] << endl;
//cout << "point 3: " << grid[3][0] << ", " << grid[3][1] << ", " << grid[3][2] << endl;
//cout << "point 4: " << grid[4][0] << ", " << grid[4][1] << ", " << grid[4][2] << endl;
//cout << "point 5: " << grid[5][0] << ", " << grid[5][1] << ", " << grid[5][2] << endl;
//cout << "point 6: " << grid[6][0] << ", " << grid[6][1] << ", " << grid[6][2] << endl;
//cout << "point 7: " << grid[7][0] << ", " << grid[7][1] << ", " << grid[7][2] << endl;
//cout << "point 8: " << grid[8][0] << ", " << grid[8][1] << ", " << grid[8][2] << endl;
//cout << "point 9: " << grid[9][0] << ", " << grid[9][1] << ", " << grid[9][2] << endl;
//cout << "point 10: " << grid[10][0] << ", " << grid[10][1] << ", " << grid[10][2] << endl;
//cout << "point 11: " << grid[11][0] << ", " << grid[11][1] << ", " << grid[11][2] << endl;
//cout << "point 12: " << grid[12][0] << ", " << grid[12][1] << ", " << grid[12][2] << endl;
//cout << "point 13: " << grid[13][0] << ", " << grid[13][1] << ", " << grid[13][2] << endl;
//cout << "point 14: " << grid[14][0] << ", " << grid[14][1] << ", " << grid[14][2] << endl;
//cout << "point 15: " << grid[15][0] << ", " << grid[15][1] << ", " << grid[15][2] << endl;
for (int q = 0; q < 11; q++) {
a = q * 5;
b = a + 1;
c = a + 5;
glBegin(GL_TRIANGLE_STRIP);
for (int dw = 0; dw < 8; dw++) {
//a
glColor3f(grid[a][1], grid[a][1], grid[a][1]);
glVertex3f(grid[a][0], grid[a][1], grid[a][2]);
//b
glColor3f(grid[b][1], grid[b][1], grid[b][1]);
glVertex3f(grid[b][0], grid[b][1], grid[b][2]);
//c
glColor3f(1, 0, 0);
glVertex3f(grid[c][0], grid[c][1], grid[c][2]);
a = c;
t = b;
b = a + 1;
c = t;
}
glEnd();
}
glutSwapBuffers();
}
void keyboardDown(unsigned char key, int x, int y) {
KEY[key] = true;
}
void keyboardUp(unsigned char key, int x, int y) {
KEY[key] = false;
}
void movement() {
CURRENT_TIME = ((float)glutGet(GLUT_ELAPSED_TIME) / 1000);
DELTA_TIME = CURRENT_TIME - LAST_TIME;
LAST_TIME = CURRENT_TIME;
MOUSE_DELTA_X = MOUSE_CURRENT_X - MOUSE_LAST_X;
MOUSE_DELTA_Y = MOUSE_CURRENT_Y - MOUSE_LAST_Y;
MOUSE_LAST_X = MOUSE_CURRENT_X;
MOUSE_LAST_Y = MOUSE_CURRENT_Y;
CAMERA_ROTATION.y += (float)MOUSE_DELTA_X * MOUSE_SENSITIVITY;
CAMERA_ROTATION.x += (float)MOUSE_DELTA_Y * MOUSE_SENSITIVITY;
if (CAMERA_ROTATION.x > MAX_TILT) {
CAMERA_ROTATION.x = MAX_TILT;
}
if (CAMERA_ROTATION.x < -MAX_TILT) {
CAMERA_ROTATION.x = -MAX_TILT;
}
if (KEY['w'] == true) {
CAMERA_POSITION.x += (WALKING_SPEED * DELTA_TIME) * dsin(CAMERA_ROTATION.y);
CAMERA_POSITION.z += (WALKING_SPEED * DELTA_TIME) * dcos(CAMERA_ROTATION.y);
CAMERA_POSITION.y += (WALKING_SPEED * DELTA_TIME) * dsin(CAMERA_ROTATION.x + 180);
}
if (KEY['s'] == true) {
CAMERA_POSITION.x += (WALKING_SPEED * DELTA_TIME) * dsin(CAMERA_ROTATION.y + 180);
CAMERA_POSITION.z += (WALKING_SPEED * DELTA_TIME) * dcos(CAMERA_ROTATION.y + 180);
CAMERA_POSITION.y += (WALKING_SPEED * DELTA_TIME) * dsin(CAMERA_ROTATION.x);
}
if (KEY['a'] == true) {
CAMERA_POSITION.x += (WALKING_SPEED * DELTA_TIME) * dsin(CAMERA_ROTATION.y + 270);
CAMERA_POSITION.z += (WALKING_SPEED * DELTA_TIME) * dcos(CAMERA_ROTATION.y + 270);
}
if (KEY['d'] == true) {
CAMERA_POSITION.x += (WALKING_SPEED * DELTA_TIME) * dsin(CAMERA_ROTATION.y + 90);
CAMERA_POSITION.z += (WALKING_SPEED * DELTA_TIME) * dcos(CAMERA_ROTATION.y + 90);
}
if (KEY[' '] == true) {
CAMERA_POSITION.y += (WALKING_SPEED * DELTA_TIME);
}
if (KEY['e'] == true) {
exit(1);
}
}
void mouseMove(int x, int y) {
MOUSE_CURRENT_X = x;
MOUSE_CURRENT_Y = y;
}
double degreesToRadians(double degrees){
return degrees * M_PI / 180;
}
double dsin(double theta) {
return sin(degreesToRadians(theta));
}
double dcos(double theta) {
return cos(degreesToRadians(theta));
}
double dtan(double theta) {
return tan(degreesToRadians(theta));
}
void reshape(int w, int h) {
//Stops the ratio from dividing by 0
if (h == 0) {
h = 1;
}
float fRatio = (float)w / h;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 0, w, h);
gluPerspective(60, fRatio, 0.1, 1000);
glMatrixMode(GL_MODELVIEW);
}
void initialize() {
glClearColor(0.0, 0.0, 102.0 / 255.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
}
int main(int iArgc, char** cArgv) {
//Initialise OpenGL and GLUT
glutInit(&iArgc, cArgv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
//Setup window
glutInitWindowPosition(0, 0);
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutCreateWindow(WINDOW_TITLE);
//Setup GLUT callback functions
initialize();
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutIdleFunc(display);
glutKeyboardFunc(keyboardDown);
glutKeyboardUpFunc(keyboardUp);
glutMotionFunc(mouseMove);
glutPassiveMotionFunc(mouseMove);
glEnable(GL_DEPTH_TEST);
//Enter main loop
glutMainLoop();
return 0;
}
Some quick remarks:
your loops access different number of points!
1st loop (r, x) initialize 50 points
2nd loop (h) initialize 64 points
3rd loop (q, dw) access 59 points
glColor3f values should be between 0 and 1 but, using your grid y values, you get 0, 1 or 2
beware of the gimbal lock problem when trying to rotate your view!