754 lines
26 KiB
C++
754 lines
26 KiB
C++
/*
|
|
CSCI 420 Computer Graphics, Computer Science, USC
|
|
Assignment 1: Height Fields with Shaders.
|
|
C/C++ starter code
|
|
|
|
Student username: <type your USC username here>
|
|
*/
|
|
|
|
#include "openGLHeader.h"
|
|
#include "glutHeader.h"
|
|
#include "openGLMatrix.h"
|
|
#include "imageIO.h"
|
|
#include "vbo.h"
|
|
#include "vao.h"
|
|
#include "hw1.h"
|
|
#include <iostream>
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
|
#ifdef _DEBUG
|
|
#pragma comment(lib, "glew32d.lib")
|
|
#else
|
|
#pragma comment(lib, "glew32.lib")
|
|
#endif
|
|
#endif
|
|
|
|
char shaderBasePath[1024] = "./shaders";
|
|
|
|
using namespace std;
|
|
|
|
// Width and height of the OpenGL window, in pixels.
|
|
int windowWidth = 1280;
|
|
int windowHeight = 720;
|
|
char windowTitle[512] = "三角形渲染(键 3)";
|
|
int terrainWidth = 0;
|
|
int terrainHeight = 0;
|
|
// Stores the image loaded from disk.
|
|
ImageIO *heightmapImage;
|
|
int saveCounter = 0;
|
|
// Number of vertices in the single triangle (starter code).
|
|
float *height_field;
|
|
float* positions;
|
|
float *colors;
|
|
unsigned int* indices;
|
|
int position_count, color_count,index_count;
|
|
terran::mouse_button_state mouseButtonState = { 0, 0, 0 };
|
|
|
|
int controlMode = 0;
|
|
OpenGLMatrix matrix;
|
|
VBO *vboVertices = nullptr;
|
|
VBO *vboColors = nullptr;
|
|
VAO *vao = nullptr;
|
|
GLuint ebo;
|
|
terran::control_state controlState;
|
|
// view state
|
|
float landRotate[3] = { 0.0f, 0.0f, 0.0f };
|
|
float landTranslate[3] = { 0.0f, 0.0f, 0.0f };
|
|
float landScale[3] = { 1.0f, 1.0f, 1.0f };
|
|
int mousePos[2];
|
|
// frame save path
|
|
const char* frame_save_path = nullptr;
|
|
// frame save count
|
|
unsigned int frame_save_count = 0;
|
|
// windows FPS
|
|
unsigned int FPS = 60;
|
|
// OpenGL relate variables
|
|
// vertex array object
|
|
// vertexArray_mode3 is the background color display mode
|
|
GLuint vertexArray_mode1, vertexArray_mode2, vertexArray_mode3;
|
|
// vertex information
|
|
GLuint vertexBuffer, colorBuffer, bgImageBuffer;
|
|
// smoothe mode vertex buffer
|
|
GLuint vertexBuffer_right, vertexBuffer_bottom, vertexBuffer_left, vertexBuffer_top;
|
|
// transformation matrix
|
|
// shader program
|
|
// shader program
|
|
Shader* pipelineProgram;
|
|
// display mode
|
|
terran::displayMode mode = terran::displayMode::triangles;
|
|
// number of indices that need to be plot
|
|
unsigned int nVertices;
|
|
unsigned int nTriIndices;
|
|
unsigned int nLineIndices;
|
|
// indices buffer for ploting lines
|
|
GLuint triModeIndicesBuffer;
|
|
// indices buffer for ploting triangles
|
|
GLuint lineModeIndicesBuffer;
|
|
void timerFunc(int t);
|
|
void switch_mode(terran::displayMode mode);
|
|
|
|
|
|
// OpenGL function
|
|
// generate gl buffer
|
|
void generate_gl_buffer(GLuint& vertexBuffer, unsigned int size, float* vertices_mesh);
|
|
// generate gl element buffer
|
|
void generate_gl_elem_buffer(GLuint& vertexBuffer, unsigned int size,
|
|
unsigned int* vertices_mesh);
|
|
// set vertices layout pattern, size = 3 for vertices, size = 4 for colors
|
|
void gl_layout(GLuint& vertexBuffer, const char* variable_name, unsigned int step = 3);
|
|
|
|
// generate grid mesh data
|
|
// generate grid mesh for vertices location and it corresponding color
|
|
void generate_grid_mesh(IN ImageIO* heightmapImage, IN float boarder,
|
|
OUT float* grid_mesh, OUT float* color_mesh);
|
|
// generate background image if it's given
|
|
void generate_smoothed_other_4_vertices(IN unsigned int width,
|
|
IN unsigned int height, IN float* grid_mesh,
|
|
OUT float* grid_mesh_right, OUT float* grid_mesh_left,
|
|
OUT float* grid_mesh_top, OUT float* grid_mesh_bottom);
|
|
void generate_line_indices(unsigned int width, unsigned int height,
|
|
unsigned int* lineIndices);
|
|
void generate_triangle_indices(unsigned int width, unsigned int height,
|
|
unsigned int* TriIndices);
|
|
|
|
|
|
// Write a screenshot to the specified filename.
|
|
void saveScreenshot(const char *filename)
|
|
{
|
|
unsigned char *screenshotData = new unsigned char[windowWidth * windowHeight * 3];
|
|
glReadPixels(0, 0, windowWidth, windowHeight, GL_RGB, GL_UNSIGNED_BYTE, screenshotData);
|
|
|
|
ImageIO screenshotImg(windowWidth, windowHeight, 3, screenshotData);
|
|
|
|
if (screenshotImg.save(filename, ImageIO::FORMAT_JPEG) == ImageIO::OK)
|
|
cout << "File " << filename << " saved successfully." << endl;
|
|
else
|
|
cout << "Failed to save file " << filename << '.' << endl;
|
|
|
|
delete[] screenshotData;
|
|
}
|
|
|
|
void idleFunc()
|
|
{
|
|
// Do some stuff...
|
|
// For example, here, you can save the screenshots to disk (to make the animation).
|
|
|
|
// Notify GLUT that it should call displayFunc.
|
|
glutPostRedisplay();
|
|
}
|
|
|
|
void reshapeFunc(int w, int h)
|
|
{
|
|
glViewport(0, 0, w, h);
|
|
|
|
// When the window has been resized, we need to re-set our projection matrix.
|
|
matrix.SetMatrixMode(OpenGLMatrix::Projection);
|
|
matrix.LoadIdentity();
|
|
matrix.Perspective(54.0f, (float)w / (float)h, 0.01f, 1000.0f);
|
|
|
|
}
|
|
|
|
void mouseMotionDragFunc(int x, int y)
|
|
{
|
|
if (controlMode == 0) {
|
|
return;
|
|
}
|
|
int mousePosDelta[2] = { x - mousePos[0], y - mousePos[1] };
|
|
switch (controlState) {
|
|
case terran::TRANSLATE:
|
|
// translate the landscape
|
|
if (mouseButtonState.leftMouseButton) {
|
|
// control x,y translation via the left mouse button
|
|
landTranslate[0] += mousePosDelta[0] * 0.01f;
|
|
landTranslate[1] -= mousePosDelta[1] * 0.01f;
|
|
}
|
|
if (mouseButtonState.middleMouseButton) {
|
|
// control z translation via the middle mouse button
|
|
landTranslate[2] -= mousePosDelta[1] * 0.01f;
|
|
}
|
|
break;
|
|
case terran::ROTATE:
|
|
// rotate the landscape
|
|
if (mouseButtonState.leftMouseButton) {
|
|
// control x,y rotation via the left mouse button
|
|
landRotate[0] += mousePosDelta[1];
|
|
landRotate[1] += mousePosDelta[0];
|
|
}
|
|
if (mouseButtonState.middleMouseButton) {
|
|
// control z rotation via the middle mouse button
|
|
landRotate[2] += mousePosDelta[1];
|
|
}
|
|
break;
|
|
case terran::SCALE:
|
|
// scale the landscape
|
|
if (mouseButtonState.leftMouseButton) {
|
|
// control x,y scaling via the left mouse button
|
|
landScale[0] *= 1.0f + mousePosDelta[0] * 0.01f;
|
|
landScale[1] *= 1.0f - mousePosDelta[1] * 0.01f;
|
|
}
|
|
if (mouseButtonState.middleMouseButton) {
|
|
// control z scaling via the middle mouse button
|
|
landScale[2] *= 1.0f - mousePosDelta[1] * 0.01f;
|
|
}
|
|
break;
|
|
}
|
|
// store the new mouse position
|
|
|
|
mousePos[0] = x;
|
|
mousePos[1] = y;
|
|
|
|
|
|
}
|
|
|
|
void mouseMotionFunc(int x, int y)
|
|
{
|
|
// store the new mouse position
|
|
mousePos[0] = x;
|
|
mousePos[1] = y;
|
|
}
|
|
|
|
void mouseButtonFunc(int button, int state, int x, int y)
|
|
{
|
|
// keep track of the mouse button state, in leftMouseButton, middleMouseButton,
|
|
// rightMouseButton variables
|
|
switch (button)
|
|
{
|
|
case GLUT_LEFT_BUTTON:
|
|
mouseButtonState.leftMouseButton = (state == GLUT_DOWN);
|
|
break;
|
|
case GLUT_MIDDLE_BUTTON:
|
|
mouseButtonState.middleMouseButton = (state == GLUT_DOWN);
|
|
break;
|
|
case GLUT_RIGHT_BUTTON:
|
|
mouseButtonState.rightMouseButton = (state == GLUT_DOWN);
|
|
break;
|
|
}
|
|
|
|
// keep track of whether CTRL and SHIFT keys are pressed
|
|
switch (glutGetModifiers())
|
|
{
|
|
case GLUT_ACTIVE_CTRL:
|
|
controlState = terran::TRANSLATE;
|
|
break;
|
|
case GLUT_ACTIVE_SHIFT:
|
|
controlState = terran::SCALE;
|
|
break;
|
|
// if CTRL and SHIFT are not pressed, we are in rotate mode
|
|
default:
|
|
controlState = terran::ROTATE;
|
|
break;
|
|
}
|
|
// store the new mouse position
|
|
mousePos[0] = x;
|
|
mousePos[1] = y;
|
|
}
|
|
float mapHeightToColor(float height, float minHeight, float maxHeight)
|
|
{
|
|
return (height - minHeight) / (maxHeight - minHeight);
|
|
}
|
|
|
|
void keyboardFunc(unsigned char key, int x, int y)
|
|
{
|
|
switch (key)
|
|
{
|
|
case 27: // ESC key
|
|
exit(0); // exit the program
|
|
break;
|
|
case '1':
|
|
if (mode != terran::displayMode::points) {
|
|
switch_mode(terran::displayMode::points);
|
|
std::cout << "mode switch to points display." << std::endl;
|
|
mode = terran::displayMode::points;
|
|
glutSetWindowTitle("点渲染(键 1)");
|
|
controlMode = 0;
|
|
}
|
|
break;
|
|
case '2':
|
|
if (mode != terran::displayMode::lines) {
|
|
switch_mode(terran::displayMode::lines);
|
|
std::cout << "mode switch to lines display." << std::endl;
|
|
mode = terran::displayMode::lines;
|
|
glutSetWindowTitle("线渲染(键 2)");
|
|
controlMode = 0;
|
|
}
|
|
break;
|
|
case '3':
|
|
if (mode != terran::displayMode::triangles) {
|
|
switch_mode(terran::displayMode::triangles);
|
|
std::cout << "mode switch to triangles display." << std::endl;
|
|
mode = terran::displayMode::triangles;
|
|
glutSetWindowTitle("三角形渲染(键 3)");
|
|
controlMode = 0;
|
|
}
|
|
break;
|
|
case '4':
|
|
if (mode != terran::displayMode::smoothTriangles) {
|
|
switch_mode(terran::displayMode::smoothTriangles);
|
|
std::cout << "mode switch to smoothTriangles display." << std::endl;
|
|
mode = terran::displayMode::smoothTriangles;
|
|
glutSetWindowTitle("优化渲染(键 4)");
|
|
controlMode = 1;
|
|
}
|
|
break;
|
|
|
|
case 'x':
|
|
// take a screenshot
|
|
saveScreenshot("screenshot.jpg");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void timerFunc(int t)
|
|
{
|
|
glutPostRedisplay();
|
|
// save frame
|
|
if (frame_save_path != nullptr) {
|
|
char path[128];
|
|
strcpy(path, frame_save_path);
|
|
strcat(path, "/");
|
|
char number[4] = { '0' ,'0', '0', 0 };
|
|
int loc = 2;
|
|
unsigned int frame_save_count_ = frame_save_count;
|
|
while (loc >= 0) {
|
|
number[loc] = frame_save_count_ % 10 + '0';
|
|
frame_save_count_ = frame_save_count_ / 10;
|
|
loc--;
|
|
}
|
|
strcat(path, number);
|
|
strcat(path, ".jpg");
|
|
frame_save_count++;
|
|
//saveScreenshot(path);
|
|
}
|
|
glutTimerFunc(1000.0f / (float)FPS, timerFunc, 0);
|
|
}
|
|
|
|
void switch_mode(terran::displayMode mode)
|
|
{
|
|
GLuint loc = glGetUniformLocation(pipelineProgram->GetProgramHandle(), "mode");
|
|
if (mode == terran::displayMode::smoothTriangles) {
|
|
glUniform1i(loc, 1);
|
|
}
|
|
else {
|
|
glUniform1i(loc, 0);
|
|
}
|
|
}
|
|
|
|
void generate_gl_buffer(GLuint& glBuffer, unsigned int size, float* vertices_mesh)
|
|
{
|
|
glGenBuffers(1, &glBuffer);
|
|
glBindBuffer(GL_ARRAY_BUFFER, glBuffer);
|
|
glBufferData(GL_ARRAY_BUFFER, size, vertices_mesh, GL_STATIC_DRAW);
|
|
}
|
|
|
|
void gl_layout(GLuint& vertexBuffer, const char* variable_name, unsigned int step) {
|
|
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
|
|
GLuint loc = glGetAttribLocation(pipelineProgram->GetProgramHandle(), variable_name);
|
|
glEnableVertexAttribArray(loc);
|
|
glVertexAttribPointer(loc, step, GL_FLOAT, GL_FALSE, 0, (const void*)0);
|
|
}
|
|
|
|
void generate_gl_elem_buffer(GLuint& indicesBuffer, unsigned int size,
|
|
unsigned int* indices_mesh) {
|
|
glGenBuffers(1, &indicesBuffer);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices_mesh
|
|
, GL_STATIC_DRAW);
|
|
}
|
|
|
|
void generate_grid_mesh(IN ImageIO* heightmapImage, IN float boarder,
|
|
OUT float* grid_mesh, OUT float* color_mesh) {
|
|
unsigned int width = heightmapImage->getWidth();
|
|
unsigned int height = heightmapImage->getHeight();
|
|
unsigned int bits = heightmapImage->getBytesPerPixel();
|
|
unsigned char* pixels = heightmapImage->getPixels();
|
|
|
|
// generate grid mesh vertices
|
|
float max_color = INT_MIN, min_color = INT_MAX;
|
|
for (unsigned int i = 0; i < height; i++) {
|
|
for (unsigned int j = 0; j < width; j++) {
|
|
unsigned int pos = (i * width + j) * 3;
|
|
grid_mesh[pos++] = i * boarder / (float)height - boarder / 2;
|
|
grid_mesh[pos++] = j * boarder / (float)width - boarder / 2;
|
|
grid_mesh[pos] = (float)pixels[i * width + j] / 256.0f / 2.0f;
|
|
max_color = std::max(max_color, (float)pixels[i * width + j]);
|
|
min_color = std::min(min_color, (float)pixels[i * width + j]);
|
|
}
|
|
}
|
|
|
|
for (unsigned int i = 0; i < height; i++) {
|
|
for (unsigned int j = 0; j < width; j++) {
|
|
unsigned int pos = (i * width + j) * 4;
|
|
// mapping the pixel height to the color
|
|
/*
|
|
float gray = ((float)pixels[i * width + j] - min_color) /
|
|
(max_color - min_color) * 255.0f;
|
|
gray = gray / 255.0f / 1.25f + 0.2f;
|
|
*/
|
|
float gray = (float)pixels[i * width + j] / 255.0f;
|
|
color_mesh[pos++] = gray;
|
|
color_mesh[pos++] = gray;
|
|
color_mesh[pos++] = gray;
|
|
color_mesh[pos++] = 1.0f;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void generate_smoothed_other_4_vertices(IN unsigned int width,
|
|
IN unsigned int height, IN float* grid_mesh,
|
|
OUT float* grid_mesh_right, OUT float* grid_mesh_left,
|
|
OUT float* grid_mesh_top, OUT float* grid_mesh_bottom)
|
|
{
|
|
// generate grid_mesh_right
|
|
for (unsigned int i = 0; i < height; i++) {
|
|
for (unsigned int j = 0; j < width; j++) {
|
|
unsigned int pos = (i * width + j) * 3;
|
|
if (j != width - 1) {
|
|
grid_mesh_right[pos] = grid_mesh[pos + 3]; pos++;
|
|
grid_mesh_right[pos] = grid_mesh[pos + 3]; pos++;
|
|
grid_mesh_right[pos] = grid_mesh[pos + 3];
|
|
}
|
|
else {
|
|
grid_mesh_right[pos] = grid_mesh[pos]; pos++;
|
|
grid_mesh_right[pos] = grid_mesh[pos]; pos++;
|
|
grid_mesh_right[pos] = grid_mesh[pos];
|
|
}
|
|
}
|
|
}
|
|
// generate grid_mesh_bottom
|
|
for (unsigned int i = 0; i < height; i++) {
|
|
for (unsigned int j = 0; j < width; j++) {
|
|
unsigned int pos = (i * width + j) * 3;
|
|
if (i != height - 1) {
|
|
grid_mesh_bottom[pos] = grid_mesh[pos + width * 3]; pos++;
|
|
grid_mesh_bottom[pos] = grid_mesh[pos + width * 3]; pos++;
|
|
grid_mesh_bottom[pos] = grid_mesh[pos + width * 3];
|
|
}
|
|
else {
|
|
grid_mesh_bottom[pos] = grid_mesh[pos]; pos++;
|
|
grid_mesh_bottom[pos] = grid_mesh[pos]; pos++;
|
|
grid_mesh_bottom[pos] = grid_mesh[pos];
|
|
}
|
|
}
|
|
}
|
|
// generate grid_mesh_left
|
|
for (unsigned int i = 0; i < height; i++) {
|
|
for (unsigned int j = 0; j < width; j++) {
|
|
unsigned int pos = (i * width + j) * 3;
|
|
if (j != 0) {
|
|
grid_mesh_left[pos] = grid_mesh[pos - 3]; pos++;
|
|
grid_mesh_left[pos] = grid_mesh[pos - 3]; pos++;
|
|
grid_mesh_left[pos] = grid_mesh[pos - 3];
|
|
}
|
|
else {
|
|
grid_mesh_left[pos] = grid_mesh[pos]; pos++;
|
|
grid_mesh_left[pos] = grid_mesh[pos]; pos++;
|
|
grid_mesh_left[pos] = grid_mesh[pos];
|
|
}
|
|
}
|
|
}
|
|
// generate grid_mesh_top
|
|
for (unsigned int i = 0; i < height; i++) {
|
|
for (unsigned int j = 0; j < width; j++) {
|
|
unsigned int pos = (i * width + j) * 3;
|
|
if (i != 0) {
|
|
grid_mesh_top[pos] = grid_mesh[pos - width * 3]; pos++;
|
|
grid_mesh_top[pos] = grid_mesh[pos - width * 3]; pos++;
|
|
grid_mesh_top[pos] = grid_mesh[pos - width * 3];
|
|
}
|
|
else {
|
|
grid_mesh_top[pos] = grid_mesh[pos]; pos++;
|
|
grid_mesh_top[pos] = grid_mesh[pos]; pos++;
|
|
grid_mesh_top[pos] = grid_mesh[pos];
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void generate_line_indices(unsigned int width, unsigned int height, unsigned int* lineIndices)
|
|
{
|
|
unsigned int currentIndex = 0;
|
|
// line of all rows
|
|
for (unsigned int i = 0; i < height - 1; i++) {
|
|
for (unsigned int j = 0; j < width; j++) {
|
|
unsigned int pos = i * width + j;
|
|
unsigned int linkto = pos + width;
|
|
lineIndices[currentIndex++] = pos;
|
|
lineIndices[currentIndex++] = linkto;
|
|
}
|
|
}
|
|
// line of all columns
|
|
for (unsigned int i = 0; i < height; i++) {
|
|
for (unsigned int j = 0; j < width - 1; j++) {
|
|
unsigned int pos = i * width + j;
|
|
unsigned int linkto = pos + 1;
|
|
lineIndices[currentIndex++] = pos;
|
|
lineIndices[currentIndex++] = linkto;
|
|
}
|
|
}
|
|
}
|
|
|
|
void generate_triangle_indices(unsigned int width, unsigned int height,
|
|
unsigned int* TriIndices)
|
|
{
|
|
unsigned int currentIndex = 0;
|
|
for (unsigned int i = 0; i < height - 1; i++) {
|
|
for (unsigned int j = 0; j < width - 1; j++) {
|
|
unsigned int pos_left = i * width + j;
|
|
unsigned int pos_right = pos_left + 1;
|
|
unsigned int pos_bottom_left = pos_left + width;
|
|
unsigned int pos_bottom_right = pos_bottom_left + 1;
|
|
// construct the first triangle
|
|
TriIndices[currentIndex++] = pos_left;
|
|
TriIndices[currentIndex++] = pos_right;
|
|
TriIndices[currentIndex++] = pos_bottom_left;
|
|
// construct the second triangle
|
|
TriIndices[currentIndex++] = pos_right;
|
|
TriIndices[currentIndex++] = pos_bottom_right;
|
|
TriIndices[currentIndex++] = pos_bottom_left;
|
|
}
|
|
}
|
|
}
|
|
|
|
void displayFunc()
|
|
{
|
|
float time = glutGet(GLUT_ELAPSED_TIME);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
matrix.SetMatrixMode(OpenGLMatrix::ModelView);
|
|
matrix.LoadIdentity();
|
|
matrix.LookAt(0, -4, 3, 0, 0, 0, 0, 1, 0);
|
|
matrix.Scale(landScale[0], landScale[1], landScale[2]);
|
|
matrix.Translate(landTranslate[0], landTranslate[1], landTranslate[2]);
|
|
matrix.Rotate(landRotate[0] / 5, 1, 0, 0);
|
|
matrix.Rotate(landRotate[1] / 5, 0, 1, 0);
|
|
matrix.Rotate(landRotate[2] / 5, 0, 0, 1);
|
|
// matrix.LookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);
|
|
|
|
float m[16];
|
|
matrix.SetMatrixMode(OpenGLMatrix::ModelView);
|
|
matrix.GetMatrix(m);
|
|
float p[16];
|
|
matrix.SetMatrixMode(OpenGLMatrix::Projection);
|
|
// matrix.Perspective(70.0f, 1920.0f / 1080.0f, 0.1f, 100.0f);
|
|
matrix.GetMatrix(p);
|
|
// bind shader
|
|
pipelineProgram->Bind();
|
|
// set variable
|
|
pipelineProgram->SetModelViewMatrix(m);
|
|
pipelineProgram->SetProjectionMatrix(p);
|
|
// set display mode
|
|
switch (mode) {
|
|
case::terran::displayMode::points:
|
|
glBindVertexArray(vertexArray_mode1);
|
|
glDrawArrays(GL_POINTS, 0, nVertices);
|
|
break;
|
|
case::terran::displayMode::lines:
|
|
glBindVertexArray(vertexArray_mode1);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lineModeIndicesBuffer);
|
|
glDrawElements(GL_LINES, nLineIndices, GL_UNSIGNED_INT, nullptr);
|
|
break;
|
|
case::terran::displayMode::triangles:
|
|
glBindVertexArray(vertexArray_mode1);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, triModeIndicesBuffer);
|
|
glDrawElements(GL_TRIANGLES, nTriIndices, GL_UNSIGNED_INT, nullptr);
|
|
break;
|
|
case::terran::displayMode::smoothTriangles:
|
|
glBindVertexArray(vertexArray_mode2);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, triModeIndicesBuffer);
|
|
glDrawElements(GL_TRIANGLES, nTriIndices, GL_UNSIGNED_INT, nullptr);
|
|
break;
|
|
case::terran::displayMode::imageDisplay:
|
|
glBindVertexArray(vertexArray_mode3);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, triModeIndicesBuffer);
|
|
glDrawElements(GL_TRIANGLES, nTriIndices, GL_UNSIGNED_INT, nullptr);
|
|
}
|
|
// __debugbreak();
|
|
// errorDetection();
|
|
glutSwapBuffers();
|
|
}
|
|
void initScene(int argc, char *argv[])
|
|
{
|
|
unsigned int width = heightmapImage->getWidth();
|
|
unsigned int height = heightmapImage->getHeight();
|
|
unsigned int bits = heightmapImage->getBytesPerPixel();
|
|
unsigned char* pixels = heightmapImage->getPixels();
|
|
nVertices = width * height;
|
|
|
|
float* vertices_mesh = new float[width * height * 3];
|
|
float* color = new float[width * height * 4];
|
|
// generate color mesh and vertices mesh
|
|
generate_grid_mesh(heightmapImage, 4.0f, vertices_mesh, color);
|
|
|
|
|
|
// smoothed mode variable
|
|
float* vertices_mesh_right = new float[width * height * 3];
|
|
float* vertices_mesh_bottom = new float[width * height * 3];
|
|
float* vertices_mesh_left = new float[width * height * 3];
|
|
float* vertices_mesh_top = new float[width * height * 3];
|
|
generate_smoothed_other_4_vertices(width, height, vertices_mesh,
|
|
vertices_mesh_right, vertices_mesh_left, vertices_mesh_top, vertices_mesh_bottom);
|
|
|
|
// generate indices for draw lines
|
|
nLineIndices = (width * (height - 1) + (width - 1) * height) * 2;
|
|
unsigned int* lineIndices = new unsigned int[nLineIndices];
|
|
generate_line_indices(width, height, lineIndices);
|
|
|
|
// generate indices for draw triangles
|
|
nTriIndices = (width - 1) * (height - 1) * 6;
|
|
unsigned int* TriIndices = new unsigned int[nTriIndices];
|
|
generate_triangle_indices(width, height, TriIndices);
|
|
|
|
{
|
|
// generate vertex buffer
|
|
unsigned int vertices_size = sizeof(float) * width * height * 3;
|
|
unsigned int colors_size = sizeof(float) * width * height * 4;
|
|
// generate vertex buffer
|
|
generate_gl_buffer(vertexBuffer, vertices_size, vertices_mesh);
|
|
// generate color buffer
|
|
generate_gl_buffer(colorBuffer, colors_size, color);
|
|
|
|
// generate smoothed vertex buffer
|
|
generate_gl_buffer(vertexBuffer_right, vertices_size, vertices_mesh_right);
|
|
generate_gl_buffer(vertexBuffer_bottom, vertices_size, vertices_mesh_bottom);
|
|
generate_gl_buffer(vertexBuffer_left, vertices_size, vertices_mesh_left);
|
|
generate_gl_buffer(vertexBuffer_top, vertices_size, vertices_mesh_top);
|
|
// generate line indices buffer
|
|
generate_gl_elem_buffer(lineModeIndicesBuffer, sizeof(float) * nLineIndices, lineIndices);
|
|
// generate triangle indices buffer
|
|
generate_gl_elem_buffer(triModeIndicesBuffer, sizeof(float) * nTriIndices, TriIndices);
|
|
}
|
|
{ // create shader program for mode 1
|
|
|
|
pipelineProgram = new Shader(shaderBasePath);
|
|
// generate VAO of mode 1
|
|
glGenVertexArrays(1, &vertexArray_mode1);
|
|
glBindVertexArray(vertexArray_mode1);
|
|
// bind shader vertices variable layout of VAO
|
|
gl_layout(vertexBuffer, "position", 3);
|
|
// bind shader color variable layout of VAO
|
|
gl_layout(colorBuffer, "color", 4);
|
|
}
|
|
{ // create shader program for mode 2
|
|
glGenVertexArrays(1, &vertexArray_mode2);
|
|
glBindVertexArray(vertexArray_mode2);
|
|
// bind shader vertices variable layout of VAO
|
|
gl_layout(vertexBuffer, "position", 3);
|
|
gl_layout(vertexBuffer_right, "position_right", 3);
|
|
gl_layout(vertexBuffer_bottom, "position_bottom", 3);
|
|
gl_layout(vertexBuffer_left, "position_left", 3);
|
|
gl_layout(vertexBuffer_top, "position_top", 3);
|
|
// bind shader color variable layout of VAO
|
|
gl_layout(colorBuffer, "color", 4);
|
|
}
|
|
{ // create shader program for mode 3
|
|
glGenVertexArrays(1, &vertexArray_mode3);
|
|
glBindVertexArray(vertexArray_mode3);
|
|
// bind shader vertices variable layout of VAO
|
|
gl_layout(vertexBuffer, "position", 3);
|
|
gl_layout(colorBuffer, "color", 4);
|
|
}
|
|
// enable depth test
|
|
glEnable(GL_DEPTH_TEST);
|
|
// initial shader mode to mode 1
|
|
GLuint loc = glGetUniformLocation(pipelineProgram->GetProgramHandle(), "mode");
|
|
pipelineProgram->Bind();
|
|
glUniform1i(loc, 0);
|
|
std::cout << "GL error: " << glGetError() << std::endl;
|
|
// release memory
|
|
delete[] vertices_mesh;
|
|
delete[] color;
|
|
delete[] lineIndices;
|
|
delete[] vertices_mesh_right;
|
|
delete[] vertices_mesh_bottom;
|
|
delete[] vertices_mesh_left;
|
|
delete[] vertices_mesh_top;
|
|
}
|
|
void loadHeightMap() {
|
|
heightmapImage = new ImageIO();
|
|
// argv[1]
|
|
const char* terrainFileName = ".\\heightmap\\Heightmap.jpg";
|
|
if (heightmapImage->loadJPEG(terrainFileName) != ImageIO::OK)
|
|
{
|
|
cout << "Error reading image " << terrainFileName << "." << endl;
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
terrainWidth = heightmapImage->getWidth();
|
|
terrainHeight = heightmapImage->getHeight();
|
|
height_field = new float[terrainHeight * terrainWidth];
|
|
for (int y = 0; y < heightmapImage->getHeight(); y++)
|
|
{
|
|
for (int x = 0; x < heightmapImage->getWidth(); x++)
|
|
{
|
|
height_field[y * heightmapImage->getWidth() + x] = heightmapImage->getPixel(x, y, 0) / 255.0f;
|
|
}
|
|
}
|
|
}
|
|
int main(int argc, char *argv[])
|
|
{
|
|
frame_save_path = ".\\screenshot\\";
|
|
FPS = 30;
|
|
cout << "Initializing GLUT..." << endl;
|
|
glutInit(&argc, argv);
|
|
|
|
cout << "Initializing OpenGL..." << endl;
|
|
|
|
#ifdef __APPLE__
|
|
glutInitDisplayMode(GLUT_3_2_CORE_PROFILE | GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL);
|
|
#else
|
|
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL);
|
|
#endif
|
|
|
|
glutInitWindowSize(windowWidth, windowHeight);
|
|
glutInitWindowPosition(0, 0);
|
|
glutCreateWindow(windowTitle);
|
|
|
|
cout << "OpenGL Version: " << glGetString(GL_VERSION) << endl;
|
|
cout << "OpenGL Renderer: " << glGetString(GL_RENDERER) << endl;
|
|
cout << "Shading Language Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << endl;
|
|
|
|
#ifdef __APPLE__
|
|
// This is needed on recent Mac OS X versions to correctly display the window.
|
|
glutReshapeWindow(windowWidth - 1, windowHeight - 1);
|
|
#endif
|
|
|
|
// Tells GLUT to use a particular display function to redraw.
|
|
glutDisplayFunc(displayFunc);
|
|
// Perform animation inside idleFunc.
|
|
glutIdleFunc(idleFunc);
|
|
// callback for mouse drags
|
|
glutMotionFunc(mouseMotionDragFunc);
|
|
// callback for idle mouse movement
|
|
glutPassiveMotionFunc(mouseMotionFunc);
|
|
// callback for mouse button changes
|
|
glutMouseFunc(mouseButtonFunc);
|
|
// callback for resizing the window
|
|
glutReshapeFunc(reshapeFunc);
|
|
// callback for pressing the keys on the keyboard
|
|
glutKeyboardFunc(keyboardFunc);
|
|
glutTimerFunc(1000.0f / (float)FPS, timerFunc, 0);
|
|
|
|
|
|
// init glew
|
|
#ifdef __APPLE__
|
|
// nothing is needed on Apple
|
|
#else
|
|
// Windows, Linux
|
|
GLint result = glewInit();
|
|
if (result != GLEW_OK)
|
|
{
|
|
cout << "error: " << glewGetErrorString(result) << endl;
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
#endif
|
|
loadHeightMap();
|
|
// Perform the initialization.
|
|
initScene(argc, argv);
|
|
|
|
// Sink forever into the GLUT loop.
|
|
glutMainLoop();
|
|
}
|