From 387c192a010ec8193df6fc8390c832d022802c3e Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Mon, 13 Jul 2015 08:27:43 +0300 Subject: [PATCH] Initial commit --- .gitignore | 5 ++ CMakeLists.txt | 132 ++++++++++++++++++++++++++++++++++++++++++ shaders/fragment.glsl | 8 +++ shaders/vertex.glsl | 43 ++++++++++++++ src/AudioPlayer.cpp | 123 +++++++++++++++++++++++++++++++++++++++ src/AudioPlayer.h | 39 +++++++++++++ src/GLObject.cpp | 77 ++++++++++++++++++++++++ src/GLObject.h | 41 +++++++++++++ src/IGLObject.h | 18 ++++++ src/ISndSource.h | 15 +++++ src/Mesh.cpp | 55 ++++++++++++++++++ src/Mesh.h | 34 +++++++++++ src/ShaderProgram.cpp | 83 ++++++++++++++++++++++++++ src/ShaderProgram.h | 31 ++++++++++ src/SpectralMesh.cpp | 47 +++++++++++++++ src/SpectralMesh.h | 28 +++++++++ src/WavSource.cpp | 37 ++++++++++++ src/WavSource.h | 28 +++++++++ src/WaveMesh.cpp | 19 ++++++ src/WaveMesh.h | 23 ++++++++ src/main.cpp | 128 ++++++++++++++++++++++++++++++++++++++++ src/vertex.h | 13 +++++ 22 files changed, 1027 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 shaders/fragment.glsl create mode 100644 shaders/vertex.glsl create mode 100644 src/AudioPlayer.cpp create mode 100644 src/AudioPlayer.h create mode 100644 src/GLObject.cpp create mode 100644 src/GLObject.h create mode 100644 src/IGLObject.h create mode 100644 src/ISndSource.h create mode 100644 src/Mesh.cpp create mode 100644 src/Mesh.h create mode 100644 src/ShaderProgram.cpp create mode 100644 src/ShaderProgram.h create mode 100644 src/SpectralMesh.cpp create mode 100644 src/SpectralMesh.h create mode 100644 src/WavSource.cpp create mode 100644 src/WavSource.h create mode 100644 src/WaveMesh.cpp create mode 100644 src/WaveMesh.h create mode 100644 src/main.cpp create mode 100644 src/vertex.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..81fe549 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +lib/ +.DS_Store +.idea/ + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7bee758 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,132 @@ +cmake_minimum_required(VERSION 3.2) +project(glTest) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -DGLEW_STATIC -O0 -g") +set(CMAKE_BUILD_TYPE DEBUG) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) + +if(APPLE) + find_library(COCOA_FRAMEWORK Cocoa) + find_library(OPENGL_FRAMEWORK OpenGL) + find_library(IOKIT_FRAMEWORK IOKit) + find_library(COREVIDEO_FRAMEWORK CoreVideo) + set(REQUIRED_LIBRARIES ${COCOA_FRAMEWORK} ${OPENGL_FRAMEWORK} ${IOKIT_FRAMEWORK} ${COREVIDEO_FRAMEWORK}) +else() + find_package(OpenGL REQUIRED) + find_library(x11 NAMES X11) + find_library(xrandr NAMES Xrandr) + find_library(xi NAMES Xi) + find_library(xxf86vm NAMES Xxf86vm) + find_library(pthread NAMES pthread) + find_library(Xinerama NAMES Xinerama) + find_library(Xcursor NAMES Xcursor) + set(REQUIRED_LIBRARIES rt asound ${OPENGL_LIBRARIES} ${x11} ${xrandr} ${xi} ${xxf86vm} ${Xinerama} ${Xcursor} ${pthread}) +endif() + +INCLUDE(ExternalProject) +SET_DIRECTORY_PROPERTIES(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/ThirdParty) + +ExternalProject_Add( + glfw + URL http://netcologne.dl.sourceforge.net/project/glfw/glfw/3.1.1/glfw-3.1.1.zip + TIMEOUT 100 + CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=${CMAKE_SOURCE_DIR}/lib + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=${CMAKE_SOURCE_DIR}/lib + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON) + +ExternalProject_Get_Property(glfw source_dir) +ExternalProject_Get_Property(glfw binary_dir) +include_directories(${source_dir}/include) + +ExternalProject_Add( + glew + URL http://vorboss.dl.sourceforge.net/project/glew/glew/1.12.0/glew-1.12.0.zip + CONFIGURE_COMMAND "" + BUILD_COMMAND "make" + BUILD_IN_SOURCE 1 + INSTALL_COMMAND cp ${CMAKE_BINARY_DIR}/ThirdParty/src/glew/lib/libGLEW.a ${CMAKE_SOURCE_DIR}/lib + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON +) + +ExternalProject_Get_Property(glew source_dir) +ExternalProject_Get_Property(glew binary_dir) +include_directories(${source_dir}/include) +set(GLEW_SRC_DIR ${source_dir}) + +ExternalProject_Add( + glm + URL http://vorboss.dl.sourceforge.net/project/ogl-math/glm-0.9.6.3/glm-0.9.6.3.zip + TIMEOUT 100 + CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=${CMAKE_SOURCE_DIR}/lib + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=${CMAKE_SOURCE_DIR}/lib + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON) + +ExternalProject_Get_Property(glm source_dir) +ExternalProject_Get_Property(glm binary_dir) +include_directories(${source_dir}) + +ExternalProject_Add( + portaudio + URL http://www.portaudio.com/archives/pa_stable_v19_20140130.tgz + TIMEOUT 100 + CONFIGURE_COMMAND ${CMAKE_BINARY_DIR}/ThirdParty/src/portaudio/configure --enable-cxx + BUILD_COMMAND "/usr/bin/make" + BUILD_IN_SOURCE 1 + INSTALL_COMMAND cp ${CMAKE_BINARY_DIR}/ThirdParty/src/portaudio/lib/.libs/libportaudio.a ${CMAKE_SOURCE_DIR}/lib + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON) + +ExternalProject_Get_Property(portaudio source_dir) +ExternalProject_Get_Property(portaudio binary_dir) +include_directories(${source_dir}/include) + +ExternalProject_Add( + libsndfile + URL "http://www.mega-nerd.com/libsndfile/files/libsndfile-1.0.25.tar.gz" + CONFIGURE_COMMAND ${CMAKE_BINARY_DIR}/ThirdParty/src/libsndfile/configure --disable-external-libs + BUILD_COMMAND "/usr/bin/make" + UPDATE_COMMAND "" + INSTALL_COMMAND cp ${CMAKE_BINARY_DIR}/ThirdParty/src/libsndfile/src/.libs/libsndfile.a ${CMAKE_SOURCE_DIR}/lib + BUILD_IN_SOURCE 1 + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON +) + +ExternalProject_Get_Property(libsndfile source_dir) +ExternalProject_Get_Property(libsndfile binary_dir) +#include_directories(${source_dir}/include) + +ExternalProject_Add( + fftw + URL "http://www.fftw.org/fftw-3.3.4.tar.gz" + CONFIGURE_COMMAND ${CMAKE_BINARY_DIR}/ThirdParty/src/fftw/configure --enable-float + BUILD_COMMAND "/usr/bin/make" + UPDATE_COMMAND "" + INSTALL_COMMAND cp ${CMAKE_BINARY_DIR}/ThirdParty/src/fftw/.libs/libfftw3f.a ${CMAKE_SOURCE_DIR}/lib + BUILD_IN_SOURCE 1 + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON +) +ExternalProject_Get_Property(fftw source_dir) +include_directories(${source_dir}/api) + +set(SOURCE_FILES src/main.cpp) +add_executable(glTest ${SOURCE_FILES} src/ShaderProgram.cpp src/ShaderProgram.h src/Mesh.cpp src/Mesh.h src/vertex.h src/IGLObject.h src/GLObject.cpp src/GLObject.h src/WaveMesh.cpp src/WaveMesh.h src/AudioPlayer.cpp src/AudioPlayer.h src/ISndSource.h src/WavSource.cpp src/WavSource.h src/SpectralMesh.cpp src/SpectralMesh.h) +add_dependencies(glTest glfw glew glm portaudio libsndfile fftw) +target_link_libraries(glTest ${GLFW_LIBRARIES} ${REQUIRED_LIBRARIES} ${CMAKE_SOURCE_DIR}/lib/libglfw3.a ${CMAKE_SOURCE_DIR}/lib/libGLEW.a + ${CMAKE_SOURCE_DIR}/lib/libportaudio.a ${CMAKE_SOURCE_DIR}/lib/libsndfile.a ${CMAKE_SOURCE_DIR}/lib/libfftw3f.a) diff --git a/shaders/fragment.glsl b/shaders/fragment.glsl new file mode 100644 index 0000000..fda39cb --- /dev/null +++ b/shaders/fragment.glsl @@ -0,0 +1,8 @@ +#version 330 + +smooth in vec3 theColor; +out vec4 frag_colour; + +void main () { + frag_colour = vec4(theColor, 1.0); +} \ No newline at end of file diff --git a/shaders/vertex.glsl b/shaders/vertex.glsl new file mode 100644 index 0000000..8c0800d --- /dev/null +++ b/shaders/vertex.glsl @@ -0,0 +1,43 @@ +#version 330 + +uniform mat4 mWorld; +uniform mat4 mView; +uniform mat4 mProjection; + +layout (location = 0) in vec3 vp; +layout (location = 1) in vec3 normals; + +smooth out vec3 theColor; + +vec3 hsv_to_rgb(float h, float s, float v) +{ + float c = v * s; + h = mod((h * 6.0), 6.0); + float x = c * (1.0 - abs(mod(h, 2.0) - 1.0)); + vec3 color; + + if (0.0 <= h && h < 1.0) { + color = vec3(c, x, 0.0); + } else if (1.0 <= h && h < 2.0) { + color = vec3(x, c, 0.0); + } else if (2.0 <= h && h < 3.0) { + color = vec3(0.0, c, x); + } else if (3.0 <= h && h < 4.0) { + color = vec3(0.0, x, c); + } else if (4.0 <= h && h < 5.0) { + color = vec3(x, 0.0, c); + } else if (5.0 <= h && h < 6.0) { + color = vec3(c, 0.0, x); + } else { + color = vec3(0.0, 0.0, 0.0); + } + + color.rgb += v - c; + + return color; +} + +void main () { + gl_Position = mProjection*mView*mWorld*vec4(vp, 1.0); + theColor = hsv_to_rgb(vp.z, 1, 1); //vec3(1.0, 1.0, 1.0); +} diff --git a/src/AudioPlayer.cpp b/src/AudioPlayer.cpp new file mode 100644 index 0000000..72d38db --- /dev/null +++ b/src/AudioPlayer.cpp @@ -0,0 +1,123 @@ +// +// Created by selim on 02.07.15. +// + +#include "AudioPlayer.h" +#include "WavSource.h" + +#include +#include +#include + +int streamCallback(const void* input, + void* output, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *timeInfo, + PaStreamCallbackFlags flags, + void* data) { + AudioPlayer* player = (AudioPlayer*)data; + return player->streamCallback(input, output, framesPerBuffer, timeInfo, flags); +} + +void streamFinishedCallback(void *data) { + AudioPlayer* player = (AudioPlayer*)data; + player->streamFinishedCallback(); +} + +#define PA_CHECK_ERROR(err) \ + if (err != PaErrorCode::paNoError) { \ + throw std::runtime_error(Pa_GetErrorText(err)); \ + } + +AudioPlayer::AudioPlayer(std::string path) { + _sndSource = openSoundFile(path); + + PaError err = Pa_Initialize(); + PA_CHECK_ERROR(err); + + PaStreamParameters outParam; + outParam.device = Pa_GetDefaultOutputDevice(); + if (outParam.device == paNoDevice) { + throw std::runtime_error("Haven't found an audio device!"); + } + + outParam.channelCount = _sndSource->getChannelCount(); + outParam.sampleFormat = paFloat32; + outParam.suggestedLatency = Pa_GetDeviceInfo(outParam.device)->defaultLowOutputLatency; + outParam.hostApiSpecificStreamInfo = nullptr; + + err = Pa_OpenStream(&_stream, NULL, &outParam, _sndSource->getSampleRate(), + /*paFramesPerBufferUnspecified*/1024, paClipOff, + ::streamCallback, this); + PA_CHECK_ERROR(err); + + err = Pa_SetStreamFinishedCallback(_stream, ::streamFinishedCallback); + PA_CHECK_ERROR(err); +} + +AudioPlayer::~AudioPlayer() { + try { + stop(); + + PaError err = Pa_CloseStream(_stream); + PA_CHECK_ERROR(err); + + err = Pa_Terminate(); + PA_CHECK_ERROR(err); + } catch (std::exception& ex) { + std::cout << "AudioPlayer destructor exception: " << ex.what() << std::endl; + } + + if(_sndSource) { + delete _sndSource; + } +} + +ISndSource *AudioPlayer::openSoundFile(std::string path) { + std::string ext = path.substr(path.find_last_of(".") + 1); + + if(ext == "wav") { + return new WavSource(path); + } else { + throw std::runtime_error("unsupported sound file format"); + } +} + +int AudioPlayer::streamCallback(const void* input, + void* output, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *timeInfo, + PaStreamCallbackFlags flags) { + + _sndSource->readData(framesPerBuffer, output); + + if(_streamListener) { + _streamListener(output, framesPerBuffer); + } + + return paContinue; +} + +void AudioPlayer::streamFinishedCallback() { + if(_streamFinishedLitener) { + _streamFinishedLitener(); + } +} + +void AudioPlayer::play() { + PaError err = Pa_StartStream(_stream); + PA_CHECK_ERROR(err); +} + +void AudioPlayer::stop() { + PaError err = Pa_StopStream(_stream); + PA_CHECK_ERROR(err); +} + +void AudioPlayer::setStreamListener(std::function callback) { + _streamListener = callback; +} + +void AudioPlayer::setStreamFinishedListener(std::function callback) { + _streamFinishedLitener = callback; +} diff --git a/src/AudioPlayer.h b/src/AudioPlayer.h new file mode 100644 index 0000000..3373285 --- /dev/null +++ b/src/AudioPlayer.h @@ -0,0 +1,39 @@ +// +// Created by selim on 02.07.15. +// + +#ifndef GLTEST_AUDIOPLAYER_H +#define GLTEST_AUDIOPLAYER_H + +#include "ISndSource.h" +#include +#include +#include + +class AudioPlayer { +private: + ISndSource* _sndSource; + PaStream* _stream; + std::function _streamListener; + std::function _streamFinishedLitener; + +public: + AudioPlayer(std::string path); + ~AudioPlayer(); + void play(); + void stop(); + void setStreamListener(std::function callback); + void setStreamFinishedListener(std::function callback); + int streamCallback(const void* input, + void* output, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *timeInfo, + PaStreamCallbackFlags flags); + void streamFinishedCallback(); + +private: + ISndSource* openSoundFile(std::string path); +}; + + +#endif //GLTEST_AUDIOPLAYER_H diff --git a/src/GLObject.cpp b/src/GLObject.cpp new file mode 100644 index 0000000..72f1d57 --- /dev/null +++ b/src/GLObject.cpp @@ -0,0 +1,77 @@ +// +// Created by selim on 31.05.15. +// + +#include "GLObject.h" + +#include +#include + +GLObject::GLObject(ShaderProgram *shaderProgram): _sp(shaderProgram) { +} + +GLObject::~GLObject() { + glDeleteBuffers(1, &_vertexVbo); + glDeleteBuffers(1, &_indexVbo); +} + +void GLObject::create() { + auto vData = generateVertices(); + auto iData = generateIndices(); + + GLuint vbo[2]; + glGenBuffers(2, vbo); + _vertexVbo = vbo[0]; + _indexVbo = vbo[1]; + + glBindBuffer(GL_ARRAY_BUFFER, _vertexVbo); + glBufferData(GL_ARRAY_BUFFER, std::get<1>(vData), std::get<0>(vData), GL_STREAM_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexVbo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, std::get<1>(iData), std::get<0>(iData), GL_STATIC_DRAW); + + _trianglesCount = std::get<1>(iData)/sizeof(GLuint); + + glGenVertexArrays(1, &_vao); + glBindVertexArray(_vao); + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, _vertexVbo); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (GLchar*)(3*sizeof(float))); + + delete[] std::get<0>(vData); + delete[] std::get<0>(iData); +} + +void GLObject::rotate(glm::vec3 angles) { + GLint world = glGetUniformLocation(*_sp, "mWorld"); + _worldMatrix = glm::rotate(_worldMatrix, angles.x, glm::vec3(1.0f, 0.0f, 0.0f)); + _worldMatrix = glm::rotate(_worldMatrix, angles.y, glm::vec3(0.0f, 1.0f, 0.0f)); + _worldMatrix = glm::rotate(_worldMatrix, angles.z, glm::vec3(0.0f, 0.0f, 1.0f)); + glUniformMatrix4fv(world, 1, GL_FALSE, glm::value_ptr(_worldMatrix)); +} + +void GLObject::scale(glm::vec3 scaleFactor) { + GLint world = glGetUniformLocation(*_sp, "mWorld"); + _worldMatrix = glm::scale(_worldMatrix, scaleFactor); + glUniformMatrix4fv(world, 1, GL_FALSE, glm::value_ptr(_worldMatrix)); +} + +void GLObject::translate(glm::vec3 point) { + +} + +void GLObject::draw() { + _worldMatrix = glm::mat4(); + glBindVertexArray(_vao); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexVbo); + glDrawElements(GL_TRIANGLES, _trianglesCount, GL_UNSIGNED_INT, nullptr); +} + +void GLObject::update() { + auto vData = generateVertices(); + glBindBuffer(GL_ARRAY_BUFFER, _vertexVbo); +// glBufferData(GL_ARRAY_BUFFER, _size*_size*sizeof(Vertex), NULL, GL_STREAM_DRAW); + glBufferData(GL_ARRAY_BUFFER, std::get<1>(vData), std::get<0>(vData), GL_STREAM_DRAW); + delete[] std::get<0>(vData); +} diff --git a/src/GLObject.h b/src/GLObject.h new file mode 100644 index 0000000..49570b7 --- /dev/null +++ b/src/GLObject.h @@ -0,0 +1,41 @@ +// +// Created by selim on 31.05.15. +// + +#ifndef GLTEST_GLOBJECT_H +#define GLTEST_GLOBJECT_H + +#include "IGLObject.h" +#include "ShaderProgram.h" +#include "vertex.h" + +#include +#include + +class GLObject: public IGLObject { +private: + GLuint _vertexVbo; + GLuint _indexVbo; + GLuint _vao; + glm::mat4 _worldMatrix; + ShaderProgram* _sp; + GLuint _trianglesCount; + +public: + GLObject(ShaderProgram *shaderProgram); + virtual ~GLObject(); + + void create() override final; + void rotate(glm::vec3 angles) override final; + void scale(glm::vec3 scaleFactor) override final; + void translate(glm::vec3 point) override final; + void draw() override final; + void update(); + +protected: + virtual std::tuple generateVertices() const = 0; + virtual std::tuple generateIndices() const = 0; +}; + + +#endif //GLTEST_GLOBJECT_H diff --git a/src/IGLObject.h b/src/IGLObject.h new file mode 100644 index 0000000..ba7a04c --- /dev/null +++ b/src/IGLObject.h @@ -0,0 +1,18 @@ +// +// Created by selim on 31.05.15. +// + +#ifndef GLTEST_IGLOBJECT_H +#define GLTEST_IGLOBJECT_H + +#include + +struct IGLObject { + virtual void create() = 0; + virtual void rotate(glm::vec3 angles) = 0; + virtual void scale(glm::vec3 scaleFactor) = 0; + virtual void translate(glm::vec3 point) = 0; + virtual void draw() = 0; +}; + +#endif //GLTEST_IGLOBJECT_H diff --git a/src/ISndSource.h b/src/ISndSource.h new file mode 100644 index 0000000..f6986cf --- /dev/null +++ b/src/ISndSource.h @@ -0,0 +1,15 @@ +// +// Created by selim on 02.07.15. +// + +#ifndef GLTEST_ISNDSOURCE_H +#define GLTEST_ISNDSOURCE_H + +struct ISndSource { + virtual int getChannelCount() const = 0; + virtual int getSampleRate() const = 0; + virtual void readData(unsigned long frameCount, void* output) const = 0; + virtual ~ISndSource() {} +}; + +#endif //GLTEST_ISNDSOURCE_H diff --git a/src/Mesh.cpp b/src/Mesh.cpp new file mode 100644 index 0000000..29b2d50 --- /dev/null +++ b/src/Mesh.cpp @@ -0,0 +1,55 @@ +// +// Created by Selim Mustafaev on 10.05.15. +// + +#include "Mesh.h" + +#include + +Mesh::Mesh(ShaderProgram* shader, std::size_t size) : _size(size), GLObject(shader) { + +} + +Mesh::~Mesh() { + +} + +std::tuple Mesh::generateVertices() const { + std::size_t u = _size, v = _size; + Vertex* retVal = new Vertex[u*v]; + for (std::size_t i = 0; i < u; i++) + for (std::size_t j = 0; j < v; j++) + { + Vertex vertex; + float x=(float)i/(float)u-0.5f; + float y=(float)j/(float)v-0.5f; + retVal[j*u+i].coords[0] = x*3; + retVal[j*u+i].coords[1] = y*3; + retVal[j*u+i].coords[2] = heightMapFunc(j, i, x, y); + retVal[j*u+i].normal[0] = 1.0f; + retVal[j*u+i].normal[1] = 1.0f; + retVal[j*u+i].normal[2] = 1.0f; + } + + return std::make_tuple(retVal, u*v*sizeof(Vertex)); +} + +std::tuple Mesh::generateIndices() const { + unsigned u = _size, v = _size; + unsigned int* indices = new unsigned int[(u-1)*(v-1)*6]; + for (unsigned i = 0; i < (u - 1); i++) + for (unsigned j = 0; j < (v - 1); j++) + { + unsigned int indexa=j*(u-1)+i; + unsigned int indexb=j*u+i; + indices[indexa*6+0]=indexb; + indices[indexa*6+1]=indexb+1+u; + indices[indexa*6+2]=indexb+1; + + indices[indexa*6+3]=indexb; + indices[indexa*6+4]=indexb+u; + indices[indexa*6+5]=indexb+u+1; + } + + return std::make_tuple(indices, (u-1)*(v-1)*6*sizeof(GLuint)); +} diff --git a/src/Mesh.h b/src/Mesh.h new file mode 100644 index 0000000..bada61e --- /dev/null +++ b/src/Mesh.h @@ -0,0 +1,34 @@ +// +// Created by Selim Mustafaev on 10.05.15. +// + +#ifndef GLTEST_MESH_H +#define GLTEST_MESH_H + +#include "vertex.h" +#include "ShaderProgram.h" +#include "GLObject.h" + +#include +#include +#include + + +class Mesh: public GLObject { +protected: + std::size_t _size; + +public: + Mesh(ShaderProgram* shader, std::size_t size); + virtual ~Mesh(); + +private: + std::tuple generateVertices() const override final; + std::tuple generateIndices() const override final; + +protected: + virtual float heightMapFunc(std::size_t nx, std::size_t ny, float fx, float fy) const = 0; +}; + + +#endif //GLTEST_MESH_H diff --git a/src/ShaderProgram.cpp b/src/ShaderProgram.cpp new file mode 100644 index 0000000..4935793 --- /dev/null +++ b/src/ShaderProgram.cpp @@ -0,0 +1,83 @@ +// +// Created by Selim Mustafaev on 07.05.15. +// + +#include +#include +#include "ShaderProgram.h" + +ShaderProgram::ShaderProgram(const char* vertexShaderPath, const char* fragmentShaderPath) { + FILE* f = fopen(vertexShaderPath, "rb"); + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + _vertexShaderText = new char[fsize + 1]; + fread(_vertexShaderText, fsize, 1, f); + fclose(f); + _vertexShaderText[fsize] = 0; + + _vertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(_vertexShader, 1, (const GLchar**)&_vertexShaderText, nullptr); + glCompileShader(_vertexShader); + + GLint success = GL_TRUE; + glGetShaderiv(_vertexShader, GL_COMPILE_STATUS, &success); + if(success == GL_FALSE) { + std::cout << "error compilling vertex shader" << std::endl; + logCompileError(_vertexShader); + } + + f = fopen(fragmentShaderPath, "rb"); + fseek(f, 0, SEEK_END); + fsize = ftell(f); + fseek(f, 0, SEEK_SET); + _fragmentShaderText = new char[fsize + 1]; + fread(_fragmentShaderText, fsize, 1, f); + fclose(f); + _fragmentShaderText[fsize] = 0; + + _fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(_fragmentShader, 1,(const GLchar**)&_fragmentShaderText, nullptr); + glCompileShader(_fragmentShader); + + glGetShaderiv(_fragmentShader, GL_COMPILE_STATUS, &success); + if(success == GL_FALSE) { + std::cout << "error compilling fragment shader" << std::endl; + logCompileError(_fragmentShader); + } + + + _program = glCreateProgram(); + glAttachShader(_program, _vertexShader); + glAttachShader(_program, _fragmentShader); + glLinkProgram(_program); +} + +void ShaderProgram::use() const { + glUseProgram(_program); +} + +ShaderProgram::~ShaderProgram() { + delete[] _vertexShaderText; + delete[] _fragmentShaderText; + + glDeleteShader(_vertexShader); + glDeleteShader(_fragmentShader); +} + +ShaderProgram::operator GLuint() const { + return _program; +} + +void ShaderProgram::logCompileError(GLuint shader) { + std::cout << "shader: " << shader << std::endl; + GLint logSize = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logSize); + std::cout << "log length: " << logSize << std::endl; + + GLchar* log = new GLchar[logSize]; + memset(log, 0, (size_t)logSize); + + glGetShaderInfoLog(shader, logSize, &logSize, log); + std::cout << "log: " << log << std::endl; +} diff --git a/src/ShaderProgram.h b/src/ShaderProgram.h new file mode 100644 index 0000000..263bb11 --- /dev/null +++ b/src/ShaderProgram.h @@ -0,0 +1,31 @@ +// +// Created by Selim Mustafaev on 07.05.15. +// + +#ifndef GLTEST_SHADERPROGRAM_H +#define GLTEST_SHADERPROGRAM_H + + +#include +#include + +class ShaderProgram { +private: + GLuint _program; + GLuint _vertexShader; + GLuint _fragmentShader; + char* _vertexShaderText; + char* _fragmentShaderText; + +public: + ShaderProgram(const char* vertexShaderPath, const char* fragmentShaderPath); + void use() const; + operator GLuint() const; + ~ShaderProgram(); + +private: + void logCompileError(GLuint shader); +}; + + +#endif //GLTEST_SHADERPROGRAM_H diff --git a/src/SpectralMesh.cpp b/src/SpectralMesh.cpp new file mode 100644 index 0000000..dc760c4 --- /dev/null +++ b/src/SpectralMesh.cpp @@ -0,0 +1,47 @@ +// +// Created by selim on 07.07.15. +// + +#include "SpectralMesh.h" +#include +#include +#include + +SpectralMesh::SpectralMesh(ShaderProgram *shader, std::size_t size): Mesh(shader, size) { + _data = new float[size*size]; + _spectrLine = new float[2*size]; + _spectrLineComplex = (fftwf_complex*)fftwf_malloc((_size + 1)*sizeof(fftwf_complex)); +} + +SpectralMesh::~SpectralMesh() { + delete[] _data; + delete[] _spectrLine; + fftwf_free(_spectrLineComplex); +} + +float SpectralMesh::heightMapFunc(std::size_t nx, std::size_t ny, float fx, float fy) const { + return *(_data + _size*nx + ny); +} + +void SpectralMesh::addLine(float *line) { + + for(std::size_t i = 0; i < 2*_size; ++i) { + _spectrLine[i] = line[2*i]; + } + + fftwf_plan plan = fftwf_plan_dft_r2c_1d(2*_size, _spectrLine, _spectrLineComplex, FFTW_ESTIMATE); + fftwf_execute(plan); + + for(std::size_t i = 0; i < _size; ++i) { + float real = _spectrLineComplex[i][0], imag = _spectrLineComplex[i][1]; + _spectrLine[i] = 0.1*log10f(sqrtf(real*real + imag*imag)) + 0.1f; + if(_spectrLine[i] < 0) { + _spectrLine[i] = 0; + } + } + + memmove(_data + _size, _data, _size*(_size - 1)*sizeof(float)); + memcpy(_data, _spectrLine, _size*sizeof(float)); + + fftwf_destroy_plan(plan); +} diff --git a/src/SpectralMesh.h b/src/SpectralMesh.h new file mode 100644 index 0000000..c3a7c08 --- /dev/null +++ b/src/SpectralMesh.h @@ -0,0 +1,28 @@ +// +// Created by selim on 07.07.15. +// + +#ifndef GLTEST_SPECTRALMESH_H +#define GLTEST_SPECTRALMESH_H + + +#include "Mesh.h" +#include + +class SpectralMesh: public Mesh { +private: + float* _data; + float* _spectrLine; + fftwf_complex* _spectrLineComplex; + +public: + SpectralMesh(ShaderProgram* shader, std::size_t size); + ~SpectralMesh(); + void addLine(float* line); + +private: + float heightMapFunc(std::size_t nx, std::size_t ny, float fx, float fy) const override final; +}; + + +#endif //GLTEST_SPECTRALMESH_H diff --git a/src/WavSource.cpp b/src/WavSource.cpp new file mode 100644 index 0000000..e5e1a93 --- /dev/null +++ b/src/WavSource.cpp @@ -0,0 +1,37 @@ +// +// Created by selim on 02.07.15. +// + +#include +#include +#include "WavSource.h" + +WavSource::WavSource(std::string path) { + _file = sf_open(path.c_str(), SFM_READ, &_info); + if(!_file) { + throw std::runtime_error(sf_strerror(nullptr)); + } + + +} + +WavSource::~WavSource() { + int err = sf_close(_file); + if(err) { + std::cout << "close sound file error: " << err << std::endl; + } + + std::cout << "WavSource::~WavSource()" << std::endl; +} + +int WavSource::getChannelCount() const { + return _info.channels; +} + +int WavSource::getSampleRate() const { + return _info.samplerate; +} + +void WavSource::readData(unsigned long frameCount, void *output) const { + sf_read_float(_file, (float*)output, frameCount*_info.channels); +} diff --git a/src/WavSource.h b/src/WavSource.h new file mode 100644 index 0000000..a320185 --- /dev/null +++ b/src/WavSource.h @@ -0,0 +1,28 @@ +// +// Created by selim on 02.07.15. +// + +#ifndef GLTEST_WAVSOURCE_H +#define GLTEST_WAVSOURCE_H + + +#include +#include "ISndSource.h" + +class WavSource: public ISndSource { +private: + SNDFILE* _file; + SF_INFO _info; + +public: + WavSource(std::string path); + ~WavSource(); + +public: + int getChannelCount() const override final; + int getSampleRate() const override final; + void readData(unsigned long frameCount, void* output) const override final; +}; + + +#endif //GLTEST_WAVSOURCE_H diff --git a/src/WaveMesh.cpp b/src/WaveMesh.cpp new file mode 100644 index 0000000..ba85939 --- /dev/null +++ b/src/WaveMesh.cpp @@ -0,0 +1,19 @@ +// +// Created by selim on 31.05.15. +// + +#include "WaveMesh.h" + +float WaveMesh::heightMapFunc(std::size_t nx, std::size_t ny, float fx, float fy) const { + double d = 50*sqrt(fx*fx + fy*fy); + return static_cast(cos(d - _shift)*exp(-d/10.0f)/2); +} + +void WaveMesh::setShift(float shift) { + _shift = shift; + update(); +} + +WaveMesh::WaveMesh(ShaderProgram *shader, std::size_t size): Mesh(shader, size), _shift(0) { + +} diff --git a/src/WaveMesh.h b/src/WaveMesh.h new file mode 100644 index 0000000..d68b6ea --- /dev/null +++ b/src/WaveMesh.h @@ -0,0 +1,23 @@ +// +// Created by selim on 31.05.15. +// + +#ifndef GLTEST_WAVEMESH_H +#define GLTEST_WAVEMESH_H + +#include "Mesh.h" + +class WaveMesh: public Mesh { +private: + float _shift; + +public: + WaveMesh(ShaderProgram* shader, std::size_t size); + void setShift(float shift); + +private: + float heightMapFunc(std::size_t nx, std::size_t ny, float fx, float fy) const override final; +}; + + +#endif //GLTEST_WAVEMESH_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d8be5cd --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,128 @@ +#include +#include + +#include "ShaderProgram.h" +#include "WaveMesh.h" +#include "AudioPlayer.h" +#include "SpectralMesh.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include + + +glm::vec3 eye(0.0f, 0.0f, 4.0f), at(0.0f, 0.0f, 0.0f), up(0.0f, 1.0f, 0.0f); +glm::mat4 mView = glm::lookAt(eye, at, up); +glm::mat4 mProjection = glm::perspective(45.0f, 1.0f, 2.0f, -2.0f); +bool wired = false; + +void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_M && action == GLFW_PRESS) { + glPolygonMode(GL_FRONT_AND_BACK, wired ? GL_FILL : GL_LINE); + wired = !wired; + } +} + +void _update_fps_counter (GLFWwindow* window) { + static double previous_seconds = glfwGetTime (); + static int frame_count; + double current_seconds = glfwGetTime (); + double elapsed_seconds = current_seconds - previous_seconds; + if (elapsed_seconds > 0.25) { + previous_seconds = current_seconds; + double fps = (double)frame_count / elapsed_seconds; + char tmp[128]; + sprintf (tmp, "opengl @ fps: %.2f", fps); + glfwSetWindowTitle (window, tmp); + frame_count = 0; + } + frame_count++; +} + +int main() { + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); + glfwWindowHint (GLFW_SAMPLES, 4); + + GLFWwindow* window = glfwCreateWindow(1024, 1024, "OpenGL", nullptr, nullptr); // Windowed + glfwMakeContextCurrent(window); + glfwSetKeyCallback(window, key_callback); + + glewExperimental = GL_TRUE; + GLenum err = glewInit(); + if(err != GLEW_OK) { + std::cout << "GLEW error: " << err << ": " << glewGetErrorString(err) << std::endl; + } + + ShaderProgram program("../shaders/vertex.glsl", "../shaders/fragment.glsl"); + program.use(); + +// WaveMesh mesh(&program, 128); +// mesh.create(); + + SpectralMesh mesh(&program, 256); + mesh.create(); + + GLint view = glGetUniformLocation(program, "mView"); + glUniformMatrix4fv(view, 1, GL_FALSE, glm::value_ptr(mView)); + GLint projection = glGetUniformLocation(program, "mProjection"); + glUniformMatrix4fv(projection, 1, GL_FALSE, glm::value_ptr(mProjection)); + +// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glDepthFunc(GL_LEQUAL); + glEnable(GL_DEPTH_TEST); + + AudioPlayer player("/home/selim/dl/euphoria.wav"); + player.setStreamListener([&mesh](void* data, std::size_t framesCount){ + mesh.addLine((float*)data); + }); + player.play(); + + float azimuth = 0.0f, elevation = 0.0f, scale = 1.0f; + + while(!glfwWindowShouldClose(window)) { + _update_fps_counter (window); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if(glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) { + azimuth -= 0.05f; + } else if(glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) { + azimuth += 0.05f; + } else if(glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) { + elevation -= 0.05f; + } else if(glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) { + elevation += 0.05f; + } else if(glfwGetKey(window, GLFW_KEY_KP_ADD) == GLFW_PRESS) { + scale += 0.05f; + } else if(glfwGetKey(window, GLFW_KEY_KP_SUBTRACT) == GLFW_PRESS) { + scale -= 0.05f; + } + +// double time = glfwGetTime(); +// float shift = (float)fmod(time, 2*3.1415926); +// mesh.setShift(shift*2); + mesh.update(); + + + mesh.scale(glm::vec3(scale, scale, scale)); + mesh.rotate(glm::vec3(elevation, azimuth, 0)); + mesh.draw(); + + glfwPollEvents(); + glfwSwapBuffers(window); + } + + glfwTerminate(); +} \ No newline at end of file diff --git a/src/vertex.h b/src/vertex.h new file mode 100644 index 0000000..49627a6 --- /dev/null +++ b/src/vertex.h @@ -0,0 +1,13 @@ +// +// Created by selim on 31.05.15. +// + +#ifndef GLTEST_VERTEX_H +#define GLTEST_VERTEX_H + +struct Vertex { + float coords[3]; + float normal[3]; +}; + +#endif //GLTEST_VERTEX_H