Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
#include <iostream>
#include <fstream>
#include <map>
#include <chrono>
#include <random>
#include <array>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_inverse.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include <GL/glew.h>
#include <GL/freeglut.h>
#include "getBMP.h"
#include "Shaders.h"
//#include <GL/glext.h>
#pragma comment(lib, "glew32.lib")
using namespace std;
using namespace glm;
// Size of the terrain
const int MAP_SIZE = 257; // must be power of 2 plus one
const float RANDOMNESS = 50.f;
const int SCREEN_WIDTH = 1280;
const int SCREEN_HEIGHT = 720;
struct RGB
{
uint8 r, g, b;
};
struct RGBA
{
uint8 r, g, b, a;
};
struct Image
{
int sizeX;
int sizeY;
RGB* data;
};
struct ImageRGBA
{
int sizeX;
int sizeY;
RGBA* data;
};
struct Vertex
{
glm::vec4 coords;
glm::vec3 normal;
glm::vec2 texCoords;
};
struct Matrix4x4
{
float entries[16];
};
static mat4 projMat = mat4(1.0);
static const Matrix4x4 IDENTITY_MATRIX4x4 =
{
{
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
}
};
struct Light
{
vec4 ambCols;
vec4 difCols;
vec4 specCols;
vec4 direction;
};
struct Material
{
vec4 ambRefl;
vec4 difRefl;
vec4 specRefl;
vec4 emitCols;
float shininess;
};
static Light light0 =
{
vec4(0.4, 0.4, 0.4, 1.0), // ambient
vec4(1.0, 1.0, 1.0, 1.0), // specular
vec4(1.0, 1.0, 1.0, 1.0), // diffuse
normalize(vec4(0.5, 0.5, 0.0, 0.0)) // direction
};
static vector<Material> materials =
{
// standard material
{
vec4(1.0, 1.0, 1.0, 1.0), // amb
vec4(1.0, 1.0, 1.0, 1.0), // diff
vec4(0.0, 0.0, 0.0, 1.0), // spec
vec4(0.0, 0.0, 0.0, 1.0), // emit
1.f
},
// shiny material
{
vec4(1.0, 1.0, 1.0, 1.0),
vec4(1.0, 1.0, 1.0, 1.0),
vec4(1.0, 1.0, 1.0, 1.0),
vec4(0.0, 0.0, 0.0, 1.0),
30.0f
},
// emissive material
{
vec4(0.0, 0.0, 0.0, 1.0),
vec4(0.0, 0.0, 0.0, 1.0),
vec4(0.0, 0.0, 0.0, 1.0),
vec4(1.0, 1.0, 1.0, 1.0),
1.0f
}
};
enum buffer { TERRAIN_VERTICES, CLOUD_VERTICES, TREE_VERTICES, LEAF_VERTICES };
enum object { TERRAIN, CLOUD, TREE, LEAF };
// Globals
static Vertex terrainVertices[MAP_SIZE * MAP_SIZE] = {};
const int numStripsRequired = MAP_SIZE - 1;
const int verticesPerStrip = 2 * MAP_SIZE;
unsigned int terrainIndexData[numStripsRequired][verticesPerStrip];
unsigned int cloudIndexData[] = { 0, 1, 2, 3 };
vector<vector<int>> treeIndexData;
unsigned int leafIndexData[] = { 0, 1, 2,3};
vector<vec3> treePositions;
struct BranchEnd
{
vec3 pos;
vec3 direction;
vec3 perpendicular;
};
vector<BranchEnd> branchEnds;
struct Leaf
{
vec3 pos;
vec3 initialOrientation;
float rotation;
vec3 perpendicular;
};
vector<Leaf> leaves;
static unsigned int
shaderProgramId,
vertexShaderId,
fragmentShaderId,
modelMatLoc,
viewMatLoc,
projMatLoc,
buffer[4],
vao[4];
unsigned int skyboxVertexShaderId, skyboxFragmentShaderId, skyboxShaderProgramId;
unsigned int skyboxVAO, skyboxVBO, cubemapTexture;
GLuint groundTextureID, cloudTextureID, treeTextureID;
vec3 cameraPos(2.5f, 2.5f, 10.f);
glm::quat cameraOrientation;
std::map <char, bool> keys;
std::map <int, bool> specialKeys;
std::mt19937 randomNumberEngine;
// Function to read text file, used to read shader files
static char* readTextFile(char* aTextFile)
{
FILE* filePointer = fopen(aTextFile, "rb");
char* content = NULL;
long numVal = 0;
fseek(filePointer, 0L, SEEK_END);
numVal = ftell( filePointer);
fseek(filePointer, 0L, SEEK_SET);
content = (char*)malloc((numVal + 1) * sizeof(char));
fread(content, 1, numVal, filePointer);
content[numVal] = '\0';
fclose(filePointer);
return content;
}
void squareStep(vector<vector<float>>& heightmap, int size, int x, int y, int distance, float randMax)
{
if (x < 0 || x >= size || y < 0 || y >= size)
return;
int count = 0;
float total = 0.f;
if (y >= 0 && y < size)
{
if (x - distance >= 0)
{
++count;
total += heightmap[x - distance][y];
}
if (x + distance < size)
{
++count;
total += heightmap[x + distance][y];
}
}
if (x >= 0 && x < size)
{
if (y - distance >= 0)
{
++count;
total += heightmap[x][y - distance];
}
if (y + distance < size)
{
++count;
total += heightmap[x][y + distance];
}
}
assert(x >= 0 && x < size && y >= 0 && y < size);
std::uniform_real_distribution<float> randomDistribution(-randMax, randMax);
heightmap[x][y] = (total / count) + randomDistribution(randomNumberEngine);
}
void diamondSquare(vector<vector<float>>& heightmap, int size, float randMax)
{
int stepSize = size - 1;
while (stepSize > 1)
{
std::uniform_real_distribution<float> randomDistribution(-randMax, randMax);
int halfStepSize = stepSize / 2;
// Diamond step - set centre point to average of square corners plus random - creating diamonds
for (int z = 0; z < size - 1; z += stepSize)
{
for (int x = 0; x < size - 1; x += stepSize)
{
float total = heightmap[x][z]
+ heightmap[x + stepSize][z]
+ heightmap[x][z + stepSize]
+ heightmap[x + stepSize][z + stepSize];
float average = total / 4.f;
heightmap[x + halfStepSize][z + halfStepSize] = average + randomDistribution(randomNumberEngine);
}
}
// Square step - set midpoints of square edges, creating squares
for (int z = 0; z < size - 1; z += stepSize)
{
for (int x = 0; x < size - 1; x += stepSize)
{
int centreX = x + halfStepSize, centreY = z + halfStepSize;
squareStep(heightmap, size, centreX - halfStepSize, centreY, halfStepSize, randMax);
squareStep(heightmap, size, centreX + halfStepSize, centreY, halfStepSize, randMax);
squareStep(heightmap, size, centreX, centreY - halfStepSize, halfStepSize, randMax);
squareStep(heightmap, size, centreX, centreY + halfStepSize, halfStepSize, randMax);
}
}
stepSize /= 2;
randMax /= 2.f;
}
}
void setupTerrain()
{
// Initialise terrain - set values in the height map to 0
vector<vector<float>> terrain;
for (int x = 0; x < MAP_SIZE; x++)
{
terrain.push_back(vector<float>(MAP_SIZE));
for (int z = 0; z < MAP_SIZE; z++)
{
terrain[x][z] = 0;
}
}
// Set corners to random heights
std::uniform_real_distribution<float> randomDistribution(-RANDOMNESS, RANDOMNESS);
terrain[0][0] = randomDistribution(randomNumberEngine);
terrain[MAP_SIZE-1][0] = randomDistribution(randomNumberEngine);
terrain[0][MAP_SIZE - 1] = randomDistribution(randomNumberEngine);
terrain[MAP_SIZE - 1][MAP_SIZE - 1] = randomDistribution(randomNumberEngine);
// Run diamond square algorithm for terrain
diamondSquare(terrain, MAP_SIZE, RANDOMNESS);
cameraPos = glm::vec3(MAP_SIZE / 2, 10.f, MAP_SIZE);
// Intialise vertex array
int i = 0;
for (int z = 0; z < MAP_SIZE; z++)
{
for (int x = 0; x < MAP_SIZE; x++)
{
int texCoordX = (i + z) % 2;
// Set the coords (1st 4 elements)
terrainVertices[i] = { { (float)x, terrain[x][z], (float)z, 1.0 },
{ 0, 1, 0 }, // normal
{ texCoordX, z % 2 } }; // texCoords
i++;
}
}
// Now build the index data
i = 0;
for (int z = 0; z < MAP_SIZE - 1; z++)
{
i = z * MAP_SIZE;
// Points for lower part of triangle strip - even numbered vertices
for (int x = 0; x < MAP_SIZE * 2; x += 2)
{
terrainIndexData[z][x] = i;
i++;
}
// Points for upper part of triangle strip
for (int x = 1; x < MAP_SIZE * 2 + 1; x += 2)
{
terrainIndexData[z][x] = i;
i++;
}
}
// Calculate the normals for each quad of the heightmap, where each quad consists of two
// triangles.You need to calculate the normal with a counter clockwise ordering of the triangles.The
// normal can be calculated by taking the cross product between two edges of your triangle.These can
// be stored in a temporary data structure as this is an intermediate step to enable you to calculate the
// vertex normal in the next step.
vector<vector<vec3>> quadNormals;
for (int z = 0; z < MAP_SIZE - 1; z++)
{
quadNormals.push_back(vector<vec3>(MAP_SIZE - 1));
for (int x = 0; x < MAP_SIZE - 1; x++)
{
vec3 p1 = vec3(terrainVertices[z * MAP_SIZE + x].coords);
vec3 p2 = vec3(terrainVertices[(z+1) * MAP_SIZE + x].coords);
vec3 p3 = vec3(terrainVertices[z * MAP_SIZE + x + 1].coords);
/* Cross product
Set Vector U to (Triangle.p2 minus Triangle.p1)
Set Vector V to (Triangle.p3 minus Triangle.p1)
Set Normal.x to (multiply U.y by V.z) minus (multiply U.z by V.y)
Set Normal.y to (multiply U.z by V.x) minus (multiply U.x by V.z)
Set Normal.z to (multiply U.x by V.y) minus (multiply U.y by V.x)
*/
vec3 u = p2 - p1;
vec3 v = p3 - p1;
quadNormals[z][x] = glm::normalize(vec3{
u.y * v.z - u.z * v.y,
u.z * v.x - u.x * v.z,
u.x * v.y - u.y * v.x });
}
}
// Calculate the normals for each vertex in the heightmap by summing the surrounding triangle
// normals and then normalising the final vector.
i = 0;
for (int z = 0; z < MAP_SIZE; z++)
{
for (int x = 0; x < MAP_SIZE; x++)
{
vec3 normal(0, 0, 0);
// top left
if (x > 0 && z > 0)
normal += quadNormals[z - 1][x - 1];
// below left
if (x > 0 && z < MAP_SIZE - 1)
normal += quadNormals[z][x - 1];
// top right
if (x < MAP_SIZE - 1 && z > 0)
normal += quadNormals[z - 1][x];
// below right
if (x < MAP_SIZE - 1 && z < MAP_SIZE - 1)
normal += quadNormals[z][x];
terrainVertices[i].normal = normalize(normal);
i++;
}
}
// Create an image of random green pixels
const int textureSize = 32;
std::uniform_int_distribution<int> grassGreenColourDistribution(0, 255);
Image texture{ textureSize,textureSize, new RGB[textureSize * textureSize] };
for (int y = 0; y < textureSize; y++)
{
for (int x = 0; x < textureSize; x++)
{
texture.data[x + y * textureSize].r = 0;
texture.data[x + y * textureSize].g = grassGreenColourDistribution(randomNumberEngine);
texture.data[x + y * textureSize].b = 0;
}
}
// Create vertex array object (VAO) and vertex buffer object (VBO) and associate data with vertex shader.
glGenVertexArrays(1, vao);
glGenBuffers(1, buffer);
glBindVertexArray(vao[TERRAIN]);
glBindBuffer(GL_ARRAY_BUFFER, buffer[TERRAIN_VERTICES]);
glBufferData(GL_ARRAY_BUFFER, sizeof(terrainVertices), terrainVertices, GL_STATIC_DRAW);
// vertices
// index, size, type, normalized, stride, pointer
// size: number of components per generic vertex attribute
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(terrainVertices[0]), 0);
glEnableVertexAttribArray(0);
// normals
glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, sizeof(terrainVertices[0]), (void*)(offsetof(Vertex, normal)));
glEnableVertexAttribArray(1);
// texCoords
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(terrainVertices[0]), (void*)(offsetof(Vertex, texCoords)));
glEnableVertexAttribArray(2);
glGenTextures(1, &groundTextureID);
//// Bind image.
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, groundTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, texture.sizeX, texture.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture.data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
GLenum error;
while ((error = glGetError()) != GL_NO_ERROR)
std::cout << error << endl;
}
void setupClouds()
{
vector<vector<float>> cloudTextureData;
const int textureSize = 512;
for (int y = 0; y < textureSize + 1; y++)
{
cloudTextureData.push_back(vector<float>(textureSize + 1));
}
diamondSquare(cloudTextureData, textureSize + 1, 500.f);
ImageRGBA cloudTexture{ textureSize,textureSize, new RGBA[textureSize * textureSize] };
for (int y = 0; y < textureSize; y++)
{
for (int x = 0; x < textureSize; x++)
{
int i = x + y * textureSize;
int col = std::min<int>(255, std::max<int>(0, (int)cloudTextureData[y][x]));
cloudTexture.data[i].r = cloudTexture.data[i].g = cloudTexture.data[i].b = 255;
cloudTexture.data[i].a = (uint8)col;
}
}
const int cloudRange = 5000;
const int cloudHeight = 500;
Vertex cloudVertices[] = {
{ vec4(-cloudRange, cloudHeight, cloudRange, 1), vec3(0,-1,0), vec2(0,1) },
{ vec4(-cloudRange, cloudHeight, -cloudRange, 1), vec3(0,-1,0), vec2(0,0) },
{ vec4(cloudRange, cloudHeight, cloudRange, 1), vec3(0,-1,0), vec2(1,1) },
{ vec4(cloudRange, cloudHeight, -cloudRange, 1), vec3(0,-1,0), vec2(0,1) }
};
// Create vertex array object (VAO) and vertex buffer object (VBO) and associate data with vertex shader.
glGenVertexArrays(1, &vao[CLOUD]);
glGenBuffers(1, &buffer[CLOUD]);
glBindVertexArray(vao[CLOUD]);
glBindBuffer(GL_ARRAY_BUFFER, buffer[CLOUD_VERTICES]);
glBufferData(GL_ARRAY_BUFFER, sizeof(cloudVertices), cloudVertices, GL_STATIC_DRAW);
// vertices
// index, size, type, normalized, stride, pointer
// size: number of components per generic vertex attribute
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(cloudVertices[0]), 0);
glEnableVertexAttribArray(0);
// normals
glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, sizeof(cloudVertices[0]), (void*)(offsetof(Vertex, normal)));
glEnableVertexAttribArray(1);
// texCoords
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(cloudVertices[0]), (void*)(offsetof(Vertex, texCoords)));
glEnableVertexAttribArray(2);
glGenTextures(1, &cloudTextureID);
// Bind image.
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, cloudTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, cloudTexture.sizeX, cloudTexture.sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, cloudTexture.data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
GLenum error;
while ((error = glGetError()) != GL_NO_ERROR)
std::cout << error << endl;
}
void createBranch(vector<Vertex>& vertices, vec3 treeBase, vec3 start, vec3 branchDirection, float length, float width)
{
// Get perpendicular vector from branch direction by doing cross product
// with a different vector
vec3 perpendicular = glm::cross(branchDirection, branchDirection + vec3(1, 0, 0));
// Get a vector at right angles to the perpendicular vector and the direction vector
vec3 right = glm::cross(branchDirection, perpendicular);
vec3 p = perpendicular * width;
vec3 r = right * width;
vec3 d = branchDirection * length;
int firstIndex = vertices.size();
// Make vertices
vertices.insert(vertices.end(), {
// front
{ vec4(start + p + r, 1), vec3(0,0,1), vec2(0,0) },
{ vec4(start + p + r + d, 1), vec3(0,0,1), vec2(1,1) },
{ vec4(start + p - r, 1), vec3(0,0,1), vec2(0,1) },
{ vec4(start + p - r + d, 1), vec3(0,0,1), vec2(1,0) },
// back
{ vec4(start - p + r, 1), vec3(0,0,1), vec2(0,0) },
{ vec4(start - p - r, 1), vec3(0,0,1), vec2(0,1) },
{ vec4(start - p + r + d, 1), vec3(0,0,1), vec2(1,1) },
{ vec4(start - p - r + d, 1), vec3(0,0,1), vec2(1,0) },
// left
{ vec4(start + r + p, 1), vec3(0,0,1), vec2(0,0) },
{ vec4(start + r - p, 1), vec3(0,0,1), vec2(0,1) },
{ vec4(start + r + p + d, 1), vec3(0,0,1), vec2(1,1) },
{ vec4(start + r - p + d, 1), vec3(0,0,1), vec2(1,0) },
// right
{ vec4(start - r + p, 1), vec3(0,0,1), vec2(0,0) },
{ vec4(start - r + p + d, 1), vec3(0,0,1), vec2(1,0) },
{ vec4(start - r - p, 1), vec3(0,0,1), vec2(0,1) },
{ vec4(start - r - p + d, 1), vec3(0,0,1), vec2(1,1) },
});
for (int i = 0; i < 4; ++i)
treeIndexData.push_back({ firstIndex + i * 4, firstIndex + i * 4 + 1, firstIndex + i * 4 + 2, firstIndex + i * 4 + 3 });
// create branches at end of current branch
if (length > 2.5f)
{
std::uniform_real_distribution<float> minusOneOneDistribution(-1.f, 1.f);
std::uniform_int_distribution<int> numBranchesDistribution(4, 6);
for (int i = 0; i < numBranchesDistribution(randomNumberEngine); ++i)
{
vec3 direction = normalize(branchDirection + vec3(minusOneOneDistribution(randomNumberEngine), minusOneOneDistribution(randomNumberEngine), minusOneOneDistribution(randomNumberEngine)) * 1.5f);
// make sure branch doesn't go too close to ground
float branchLength = length / 1.5f;
if ((start + direction * branchLength).y < treeBase.y)
{
direction.y += 2.f;
direction = normalize(direction);
}
createBranch(vertices, treeBase, start + d, direction, branchLength, width / 1.75f);
}
}
else
{
branchEnds.push_back(BranchEnd{ start + d, branchDirection, glm::cross(branchDirection,vec3(1,0,0)) });
}
}
void setupTrees()
{
const float trunkH = 15.f;
const float trunkW = 0.5f;
const float halfTrunkW = trunkW / 2.f;
vec3 start{ 0,0,0 };
vector<Vertex> vertices;
vec3 branchDirection{ 0,1,0 };
branchDirection = glm::normalize(branchDirection);
createBranch(vertices, start, start, branchDirection, trunkH, trunkW);
// Set normals
for (int i = 0; i < vertices.size(); i += 4) {
vec3 normal = cross(vec3(vertices[i].coords), vec3(vertices[i + 1].coords));
vertices[i].normal = normal;
vertices[i+1].normal = normal;
vertices[i+2].normal = normal;
vertices[i+3].normal = normal;
}
// Create an image of random brown pixels
const int textureSize = 256;
std::uniform_int_distribution<int> zero255Distribution(0, 255);
Image texture{ textureSize,textureSize, new RGB[textureSize * textureSize] };
for (int y = 0; y < textureSize; y++)
{
for (int x = 0; x < textureSize; x++)
{
int value = zero255Distribution(randomNumberEngine);
texture.data[x + y * textureSize].r = value / 2;
texture.data[x + y * textureSize].g = value / 3;
texture.data[x + y * textureSize].b = value / 5;
}
}
// Create vertex array object (VAO) and vertex buffer object (VBO) and associate data with vertex shader.
glGenVertexArrays(1, &vao[TREE]);
glGenBuffers(1, &buffer[TREE]);
glBindVertexArray(vao[TREE]);
glBindBuffer(GL_ARRAY_BUFFER, buffer[TREE_VERTICES]);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertices[0]), &vertices[0], GL_STATIC_DRAW);
// vertices
// index, size, type, normalized, stride, pointer
// size: number of components per generic vertex attribute
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertices[0]), 0);
glEnableVertexAttribArray(0);
// normals
glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, sizeof(vertices[0]), (void*)(offsetof(Vertex, normal)));
glEnableVertexAttribArray(1);
// texCoords
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vertices[0]), (void*)(offsetof(Vertex, texCoords)));
glEnableVertexAttribArray(2);
glGenTextures(1, &treeTextureID);
// Bind image.
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, treeTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, texture.sizeX, texture.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture.data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
GLenum error;
while ((error = glGetError()) != GL_NO_ERROR)
std::cout << error << endl;
for (int i = 0; i < 3; ++i)
{
std::uniform_int_distribution<int> terrainVertexDistribution(0, (MAP_SIZE * MAP_SIZE) - 1);
Vertex v = terrainVertices[terrainVertexDistribution(randomNumberEngine)];
treePositions.push_back(vec3(v.coords));
for (BranchEnd& end : branchEnds)
{
leaves.push_back({ vec3(v.coords) + end.pos , end.direction, 0,end.perpendicular });
}
}
}
void setupLeaf()
{
const float size = 0.8f;
Vertex leafVertices[] = {
{ vec4(0,0,0, 1), vec3(0,-1,0), vec2(0,1) },
{ vec4(size,0,0, 1), vec3(0,-1,0), vec2(0,0) },
{ vec4(size/2.f,size/2.f,0,1 ), vec3(0,-1,0), vec2(1,1) },
{ vec4(size / 2.f,-size / 2.f,0,1), vec3(0,-1,0), vec2(1,0) },
};
// Create vertex array object (VAO) and vertex buffer object (VBO) and associate data with vertex shader.
glGenVertexArrays(1, &vao[LEAF]);
glGenBuffers(1, &buffer[LEAF]);
glBindVertexArray(vao[LEAF]);
glBindBuffer(GL_ARRAY_BUFFER, buffer[LEAF_VERTICES]);
glBufferData(GL_ARRAY_BUFFER, sizeof(leafVertices), leafVertices, GL_STATIC_DRAW);
// vertices
// index, size, type, normalized, stride, pointer
// size: number of components per generic vertex attribute
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(leafVertices[0]), 0);
glEnableVertexAttribArray(0);
// normals
glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, sizeof(leafVertices[0]), (void*)(offsetof(Vertex, normal)));
glEnableVertexAttribArray(1);
// texCoords
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(leafVertices[0]), (void*)(offsetof(Vertex, texCoords)));
glEnableVertexAttribArray(2);
GLenum error;
while ((error = glGetError()) != GL_NO_ERROR)
std::cout << error << endl;
}
//skybox
float skyboxVertices[] = {
// positions
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f
};
unsigned int loadCubemap(vector<std::string> faces)
{
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
for (unsigned int i = 0; i < faces.size(); i++)
{
//unsigned char* data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);
BitMapFile* bmp = getbmp(faces[i]);
if (bmp)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA, bmp->sizeX, bmp->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, bmp->data);
//stbi_image_free(data);
}
else
{
std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;
//stbi_image_free(data);
}
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
return textureID;
}
void setupSkybox(void)
{
// Setup skybox shaders
skyboxVertexShaderId = setShader("vertex", "skyboxVertexShader.glsl");
skyboxFragmentShaderId = setShader("fragment", "skyboxFragmentShader.glsl");
skyboxShaderProgramId = glCreateProgram();
glAttachShader(skyboxShaderProgramId, skyboxVertexShaderId);
glAttachShader(skyboxShaderProgramId, skyboxFragmentShaderId);
glLinkProgram(skyboxShaderProgramId);
// skybox VAO
glGenVertexArrays(1, &skyboxVAO);
glGenBuffers(1, &skyboxVBO);
glBindVertexArray(skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
vector<std::string> faces
{
"./textures/right.bmp",
"./textures/left.bmp",
"./textures/bottom.bmp",
"./textures/top.bmp",
"./textures/front.bmp",
"./textures/back.bmp"
};
cubemapTexture = loadCubemap(faces);
unsigned int skyboxMatLoc = glGetUniformLocation(skyboxShaderProgramId, "skybox");
glUniform1i(skyboxMatLoc, 0);
// end of skybox
}
void checkShader(int shaderId)
{
int result;
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE)
{
cout << "Shader compile error" << endl;
GLint maxLength = 0;
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> errorLog(maxLength);
glGetShaderInfoLog(shaderId, maxLength, &maxLength, &errorLog[0]);
cout << errorLog.data() << endl;
cin.get();
}
}
void setMaterial(Material material) {
glUniform4fv(glGetUniformLocation(shaderProgramId, "material.ambRefl"), 1, &material.ambRefl[0]);
glUniform4fv(glGetUniformLocation(shaderProgramId, "material.difRefl"), 1, &material.difRefl[0]);
glUniform4fv(glGetUniformLocation(shaderProgramId, "material.specRefl"), 1, &material.specRefl[0]);
glUniform4fv(glGetUniformLocation(shaderProgramId, "material.emitCols"), 1, &material.emitCols[0]);
glUniform1f(glGetUniformLocation(shaderProgramId, "material.shininess"), material.shininess);
}
// Initialization routine.
void setup(void)
{
setupTerrain();
setupClouds();
setupTrees();
setupSkybox();
setupLeaf();
glClearColor(0.4, 1.0, 1.0, 0.0);
// Create shader program executable - read, compile and link shaders
char* vertexShader = readTextFile("vertexShader.glsl");
vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderId, 1, (const char**)&vertexShader, NULL);
glCompileShader(vertexShaderId);
checkShader(vertexShaderId);
char* fragmentShader = readTextFile("fragmentShader.glsl");
fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShaderId, 1, (const char**)&fragmentShader, NULL);
glCompileShader(fragmentShaderId);
checkShader(fragmentShaderId);
shaderProgramId = glCreateProgram();
glAttachShader(shaderProgramId, vertexShaderId);
glAttachShader(shaderProgramId, fragmentShaderId);
glLinkProgram(shaderProgramId);
glUseProgram(shaderProgramId);
///////////////////////////////////////
// Obtain projection matrix uniform location and set value.
projMatLoc = glGetUniformLocation(shaderProgramId, "projMat");
projMat = perspective(radians(60.0), (double)SCREEN_WIDTH / (double)SCREEN_HEIGHT, 0.1, 10000.0);
glUniformMatrix4fv(projMatLoc, 1, GL_FALSE, value_ptr(projMat));
// Obtain model + view matrix uniform location
modelMatLoc = glGetUniformLocation(shaderProgramId, "modelMat");
viewMatLoc = glGetUniformLocation(shaderProgramId, "viewMat");
glUniformMatrix4fv(modelMatLoc, 1, GL_FALSE, value_ptr(mat4()));
///////////////////////////////////////
// Setup light
glUniform4fv(glGetUniformLocation(shaderProgramId, "light0.ambCols"), 1, &light0.ambCols[0]);
glUniform4fv(glGetUniformLocation(shaderProgramId, "light0.difCols"), 1, &light0.difCols[0]);
glUniform4fv(glGetUniformLocation(shaderProgramId, "light0.specCols"), 1, &light0.specCols[0]);
glUniform4fv(glGetUniformLocation(shaderProgramId, "light0.direction"), 1, &light0.direction[0]);
///////////////////////////////////////
}
// Drawing routine.
void drawScene(void)
{
//light0.direction = glm::rotate(light0.direction, 0.1f, glm::vec3(0, 1, 0));
//light0.direction = glm::rotateX(light0.direction, 0.001f);
//glUniform4fv(glGetUniformLocation(programId, "light0.direction"), 1, &light0.direction[0]);
glUniform3fv(glGetUniformLocation(shaderProgramId, "viewPos"), 1, &cameraPos[0]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
mat4 modelMat = glm::mat4(1.0f);
glUniformMatrix4fv(modelMatLoc, 1, GL_FALSE, value_ptr(modelMat));
//Draw terrain
setMaterial(materials[0]);
glBindTexture(GL_TEXTURE_2D, groundTextureID);
glBindVertexArray(vao[TERRAIN]);
// For each row - draw the triangle strip
for (int i = 0; i < MAP_SIZE - 1; i++)
{
glDrawElements(GL_TRIANGLE_STRIP, verticesPerStrip, GL_UNSIGNED_INT, terrainIndexData[i]);
}
// Skybox
{
glUseProgram(skyboxShaderProgramId);
glDepthFunc(GL_LEQUAL); // change depth function so depth test passes when values are equal to depth buffer's content
glm::vec3 forward = cameraOrientation * glm::vec3(0, 0, -1);
glm::vec3 up = cameraOrientation * glm::vec3(0, 1, 0);
mat4 viewMat = glm::lookAt(vec3(0, 0, 0), forward, up);
viewMat = glm::rotate(viewMat, 3.1415927f, vec3(1, 0, 0));
glUniformMatrix4fv(glGetUniformLocation(skyboxShaderProgramId, "view"), 1, GL_FALSE, value_ptr(viewMat));
glUniformMatrix4fv(glGetUniformLocation(skyboxShaderProgramId, "projection"), 1, GL_FALSE, value_ptr(projMat));
glBindVertexArray(skyboxVAO);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glDepthFunc(GL_LESS); // set depth function back to default
glUseProgram(shaderProgramId); // set shader program back to default
}
// Draw clouds
setMaterial(materials[2]);
glBindTexture(GL_TEXTURE_2D, cloudTextureID);
glBindVertexArray(vao[CLOUD]);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, cloudIndexData);
// Draw trees
setMaterial(materials[0]);
glBindTexture(GL_TEXTURE_2D, treeTextureID);
glBindVertexArray(vao[TREE]);
for(vec3 treePos : treePositions)
{
modelMat = glm::mat4(1.0f);
modelMat = glm::translate(modelMat, treePos);
//modelMat *= glm::mat4_cast(orientation);
glUniformMatrix4fv(modelMatLoc, 1, GL_FALSE, value_ptr(modelMat));
// For each face - draw the triangle strip
for (int i = 0; i < treeIndexData.size(); i++)
{
glDrawElements(GL_TRIANGLE_STRIP, treeIndexData[i].size(), GL_UNSIGNED_INT, treeIndexData[i].data());
}
}
// Draw leaves
setMaterial(materials[0]);
glBindTexture(GL_TEXTURE_2D, groundTextureID);
glBindVertexArray(vao[LEAF]);
static float r = 0.f;
r += 0.01f;
for (Leaf& leaf : leaves)
{
modelMat = glm::mat4(1.0f);
modelMat = glm::translate(modelMat, leaf.pos);
quat orientation = quat(leaf.initialOrientation);
orientation = glm::rotate(orientation, sinf(r),leaf.perpendicular);
modelMat *= glm::mat4_cast(orientation);
glUniformMatrix4fv(modelMatLoc, 1, GL_FALSE, value_ptr(modelMat));
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, leafIndexData);
}
glFlush();
GLenum error;
while ((error = glGetError()) != GL_NO_ERROR)
std::cout << error << endl;
}
// OpenGL window reshape routine.
void resize(int w, int h)
{
glViewport(0, 0, w, h);
}
// Keyboard input processing routine.
void keyInput(unsigned char key, int x, int y)
{
switch (key)
{
case 27:
exit(0);
break;
default:
break;
}
}
void update() {
static auto time = std::chrono::high_resolution_clock::now();
auto newTime = std::chrono::high_resolution_clock::now();
float secondsPassed = std::chrono::duration_cast<std::chrono::duration<float>>(newTime - time).count();
time = newTime;
float turningSpeed = 1.f * secondsPassed;
if (specialKeys[GLUT_KEY_LEFT])
cameraOrientation = glm::rotate(cameraOrientation, turningSpeed, glm::vec3(0, 1, 0));
if (specialKeys[GLUT_KEY_RIGHT])
cameraOrientation = glm::rotate(cameraOrientation, -turningSpeed, glm::vec3(0, 1, 0));
if (keys['q'])
cameraOrientation = glm::rotate(cameraOrientation, turningSpeed, glm::vec3(1, 0, 0));
if (keys['a'])
cameraOrientation = glm::rotate(cameraOrientation, -turningSpeed, glm::vec3(1, 0, 0));
if (keys['z'])
cameraOrientation = glm::rotate(cameraOrientation, turningSpeed, glm::vec3(0, 0, 1));
if (keys['x'])
cameraOrientation = glm::rotate(cameraOrientation, -turningSpeed, glm::vec3(0, 0, 1));
glm::vec3 forward = cameraOrientation * glm::vec3(0, 0, -1);
glm::vec3 up = cameraOrientation * glm::vec3(0, 1, 0);
float speed = 30.f;
if (specialKeys[GLUT_KEY_SHIFT_L])
speed = 60.f;
if (specialKeys[GLUT_KEY_UP])
cameraPos += forward * secondsPassed * speed;
if (specialKeys[GLUT_KEY_DOWN])
cameraPos -= forward * secondsPassed * speed;
mat4 viewMat = glm::lookAt(
cameraPos,
cameraPos + forward,
up);
glUniformMatrix4fv(viewMatLoc, 1, GL_FALSE, value_ptr(viewMat));
glutPostRedisplay();
}
// Main routine.
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
// Set the version of OpenGL (4.2)
glutInitContextVersion(4, 2);
// The core profile excludes all discarded features
glutInitContextProfile(GLUT_CORE_PROFILE);
// Forward compatibility excludes features marked for deprecation ensuring compatability with future versions
glutInitContextFlags(GLUT_FORWARD_COMPATIBLE);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_MULTISAMPLE);
glutInitWindowSize(SCREEN_WIDTH, SCREEN_HEIGHT);
glutInitWindowPosition(100, 100);
glutCreateWindow("TerrainGeneration");
// Set OpenGL to render in filled mode
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glutDisplayFunc(drawScene);
glutReshapeFunc(resize);
glutKeyboardFunc(keyInput);
glutIdleFunc(update);
glutKeyboardFunc([](unsigned char key, int x, int y) {
keys[key] = true;
//if we press escape, exit the game
if (key == 27) {
exit(0);
}
});
glutKeyboardUpFunc([](unsigned char key, int x, int y) {
keys[key] = false;
});
glutSpecialFunc([](int key, int x, int y) {
specialKeys[key] = true;
});
glutSpecialUpFunc([](int key, int x, int y) {
specialKeys[key] = false;
});
glewExperimental = GL_TRUE;
glewInit();
setup();
glutMainLoop();
}