Skip to content

Instantly share code, notes, and snippets.

@roxlu

roxlu/Mask.cpp Secret

Created December 12, 2013 21:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roxlu/07e91327cd247ea3b3fa to your computer and use it in GitHub Desktop.
Save roxlu/07e91327cd247ea3b3fa to your computer and use it in GitHub Desktop.
Ocean WIP 0.0.3 - working! All credits to David Li -- http://www.david.li/waves/
/*
BASIC GLFW + GLXW WINDOW AND OPENGL SETUP
------------------------------------------
See https://gist.github.com/roxlu/6698180 for the latest version of the example.
*/
#include <iostream>
#if !defined(__APPLE__)
# include <GLXW/glxw.h>
#endif
#define GLFW_INCLUDE_GLCOREARB
#include <GLFW/glfw3.h>
#include "WaterGraphics.h"
#include "Ocean.h"
void key_callback(GLFWwindow* win, int key, int scancode, int action, int mods);
void error_callback(int err, const char* desc);
void resize_callback(GLFWwindow* window, int width, int height);
WaterGraphics gfx;
Ocean ocean(gfx);
int main() {
glfwSetErrorCallback(error_callback);
if(!glfwInit()) {
printf("error: cannot setup glfw.\n");
return false;
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* win = NULL;
int w = 1280;
int h = 720;
win = glfwCreateWindow(w, h, "openGL", NULL, NULL);
if(!win) {
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwSetFramebufferSizeCallback(win, resize_callback);
glfwSetKeyCallback(win, key_callback);
glfwMakeContextCurrent(win);
glfwSwapInterval(1);
#if !defined(__APPLE__)
if(glxwInit() != 0) {
printf("error: cannot initialize glxw.\n");
::exit(EXIT_FAILURE);
}
#endif
// ----------------------------------------------------------------
// THIS IS WHERE YOU START CALLING OPENGL FUNCTIONS, NOT EARLIER!!
// ----------------------------------------------------------------
if(!gfx.setup(w, h)) {
printf("Error: cannot setup gfx.\n");
::exit(EXIT_FAILURE);
}
if(!ocean.setup()) {
::exit(EXIT_FAILURE);
}
double start_time = glfwGetTime();
printf("> %f\n", start_time);
while(!glfwWindowShouldClose(win)) {
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
double t = glfwGetTime();
double dt = (t - start_time);
start_time = t;
ocean.render(dt);
glfwSwapBuffers(win);
glfwPollEvents();
}
glfwTerminate();
return EXIT_SUCCESS;
}
void error_callback(int err, const char* desc) {
printf("glfw error: %s (%d)\n", desc, err);
}
void key_callback(GLFWwindow* win, int key, int scancode, int action, int mods) {
if(action != GLFW_PRESS) {
return;
}
switch(key) {
case GLFW_KEY_LEFT: {
break;
}
case GLFW_KEY_RIGHT: {
break;
}
case GLFW_KEY_ESCAPE: {
glfwSetWindowShouldClose(win, GL_TRUE);
break;
}
};
}
void resize_callback(GLFWwindow* window, int width, int height) {
}
#include <assert.h>
#include "Mask.h"
Mask::Mask()
:win_w(0)
,win_h(0)
,vao(0)
,vbo(0)
,fbo(0)
,depth(0)
,mask_tex(0)
{
}
Mask::~Mask() {
}
bool Mask::setup(int winW, int winH) {
win_w = winW;
win_h = winH;
if(!createShader()) {
return false;
}
if(!createFBO()) {
return false;
}
return true;
}
bool Mask::createShader() {
return true;
}
bool Mask::createFBO() {
assert(win_w > 0 && win_h > 0);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glGenRenderbuffers(1, &depth);
glBindRenderbuffer(GL_RENDERBUFFER, depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, win_w, win_h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
glGenTextures(1, &mask_tex);
glBindTexture(GL_TEXTURE_2D, mask_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, win_w, win_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mask_tex, 0);
glBindTexture(GL_TEXTURE_2D, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status != GL_FRAMEBUFFER_COMPLETE) {
printf("Error: fbo not complete.\n");
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return true;
}
void Mask::beginGrab() {
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
GLenum drawbufs[] = { GL_COLOR_ATTACHMENT0 } ;
glDrawBuffers(1, drawbufs);
}
void Mask::endGrab() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
#ifndef MASK_H
#define MASK_H
#define ROXLU_USE_OPENGL
#define ROXLU_USE_MATH
#include "tinylib.h"
class Mask {
public:
Mask();
~Mask();
bool setup(int w, int h);
void drawMask();
void beginGrab();
void endGrab();
private:
bool createShader();
bool createFBO();
public:
int win_w;
int win_h;
/* GL */
GLuint vao;
GLuint vbo;
GLuint fbo;
GLuint depth;
GLuint mask_tex;
};
#endif
#include "Ocean.h"
#include "WaterGraphics.h"
Ocean::Ocean(WaterGraphics& gfx)
:gfx(gfx)
,data_vbo(0)
,indices_vbo(0)
,changed(true)
,ping_phase(true)
,wind_x(12.5f)
,wind_y(10.3f)
,size(250.0f)
{
}
Ocean::~Ocean() {
}
bool Ocean::setup() {
if(!setupBuffers()) {
printf("Error: cannot setup the ocean buffer.\n");
return false;
}
return true;
}
bool Ocean::setupBuffers() {
for(int z_dx = 0; z_dx < GEOMETRY_RESOLUTION - 1; ++z_dx) {
for(int x_dx = 0; x_dx < GEOMETRY_RESOLUTION -1; ++x_dx) {
int top_left = z_dx * GEOMETRY_RESOLUTION + x_dx;
int top_right = top_left + 1;
int bottom_left = top_left + GEOMETRY_RESOLUTION;
int bottom_right = bottom_left + 1;
// triangle a
indices.push_back(top_left);
indices.push_back(bottom_left);
indices.push_back(bottom_right);
// triangle b
indices.push_back(bottom_right);
indices.push_back(top_right);
indices.push_back(top_left);
}
}
std::vector<float> data;
for(int z_dx = 0; z_dx < GEOMETRY_RESOLUTION; ++z_dx) {
for(int x_dx = 0; x_dx < GEOMETRY_RESOLUTION; ++x_dx) {
float x0 = (x_dx * GEOMETRY_SIZE) / (GEOMETRY_RESOLUTION - 1) + GEOMETRY_ORIG_X;
float y0 = 0.0f;
float z0 = (z_dx * GEOMETRY_SIZE) / (GEOMETRY_RESOLUTION - 1) + GEOMETRY_ORIG_Z;
float x1 = float(x_dx) / (GEOMETRY_RESOLUTION - 1);
float z1 = float(z_dx) / (GEOMETRY_RESOLUTION - 1);
data.push_back(x0);
data.push_back(y0);
data.push_back(z0);
data.push_back(x1);
data.push_back(z1);
#if 0
if(z_dx == 1) {
printf("x0: %f, z0: %f, x1: %f, z1: %f\n", x0, z0, x1, z1);
}
#endif
}
}
// data - vao
glGenVertexArrays(1, &data_vao);
glBindVertexArray(data_vao);
// data
glGenBuffers(1, &data_vbo);
glBindBuffer(GL_ARRAY_BUFFER, data_vbo);
glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(float), &data[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0); // pos
glEnableVertexAttribArray(1); // tex
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (GLvoid*) 0); // pos
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (GLvoid*)(sizeof(float) * 3)); // tex
// indices
glGenBuffers(1, &indices_vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_vbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(int), &indices[0], GL_STATIC_DRAW);
glBindVertexArray(0);
int w = RESOLUTION;
int h = RESOLUTION;
init_spectrum_tex = createTexture(INITIAL_SPECTRUM_UNIT, GL_RGBA32F, w, h, GL_RGBA, GL_FLOAT, NULL, GL_REPEAT, GL_NEAREST);
pong_phase_tex = createTexture(PONG_PHASE_UNIT, GL_RGBA32F, w, h, GL_RGBA, GL_FLOAT, NULL, GL_CLAMP_TO_EDGE, GL_NEAREST);
spectrum_tex = createTexture(SPECTRUM_UNIT, GL_RGBA32F, w, h, GL_RGBA, GL_FLOAT, NULL, GL_CLAMP_TO_EDGE, GL_NEAREST);
displacement_map_tex = createTexture(DISPLACEMENT_MAP_UNIT, GL_RGBA32F, w, h, GL_RGBA, GL_FLOAT, NULL, GL_CLAMP_TO_EDGE, GL_LINEAR);
normal_map_tex = createTexture(NORMAL_MAP_UNIT, GL_RGBA32F, w, h, GL_RGBA, GL_FLOAT, NULL, GL_CLAMP_TO_EDGE, GL_LINEAR);
ping_transform_tex = createTexture(PING_TRANSFORM_UNIT, GL_RGBA32F, w, h, GL_RGBA, GL_FLOAT, NULL, GL_CLAMP_TO_EDGE, GL_NEAREST);
pong_transform_tex = createTexture(PONG_TRANSFORM_UNIT, GL_RGBA32F, w, h, GL_RGBA, GL_FLOAT, NULL, GL_CLAMP_TO_EDGE, GL_NEAREST);
printf("init_spectrum_tex: %d\n", init_spectrum_tex);
printf("pong_phase_tex: %d\n", pong_phase_tex);
printf("spectrum_tex: %d\n", spectrum_tex);
printf("displacement_map_tex: %d\n", displacement_map_tex);
printf("normal_map_tex: %d\n", normal_map_tex);
printf("ping_transform_tex: %d\n", ping_transform_tex);
printf("pong_transform_tex: %d\n", pong_transform_tex);
// phase data
std::vector<float> phase_data;
phase_data.assign(RESOLUTION * RESOLUTION * 4, 0.0f);
for(int i = 0; i < RESOLUTION; ++i) {
for(int j = 0; j < RESOLUTION; ++j) {
phase_data[i * RESOLUTION * 4 + j * 4 + 0] = rx_random(0.0f, 1.0f) * TWO_PI;
phase_data[i * RESOLUTION * 4 + j * 4 + 1] = 0.0f;
phase_data[i * RESOLUTION * 4 + j * 4 + 2] = 0.0f;
phase_data[i * RESOLUTION * 4 + j * 4 + 3] = 0.0f;
}
}
ping_phase_tex = createTexture(PING_PHASE_UNIT, GL_RGBA32F, w, h, GL_RGBA, GL_FLOAT, (GLvoid*) &phase_data[0], GL_CLAMP_TO_EDGE, GL_NEAREST);
printf("ping_phase_tex: %d\n\n---\n", ping_phase_tex);
// create FBOs
init_spectrum_fbo = createFBO(init_spectrum_tex);
ping_phase_fbo = createFBO(ping_phase_tex);
pong_phase_fbo = createFBO(pong_phase_tex);
spectrum_fbo = createFBO(spectrum_tex);
displacement_map_fbo = createFBO(displacement_map_tex);
normal_map_fbo = createFBO(normal_map_tex);
ping_transform_fbo = createFBO(ping_transform_tex);
pong_transform_fbo = createFBO(pong_transform_tex);
printf("init_spectrum_fbo: %d\n", init_spectrum_fbo);
printf("pong_phase_fbo: %d\n", pong_phase_fbo);
printf("spectrum_fbo: %d\n", spectrum_fbo);
printf("displacement_map_fbo: %d\n", displacement_map_fbo);
printf("normal_map_fbo: %d\n", normal_map_fbo);
printf("ping_transform_fbo: %d\n", ping_transform_fbo);
printf("pong_transform_fbo: %d\n", pong_transform_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return true;
}
GLuint Ocean::createTexture(GLuint unit, GLenum iformat, int w, int h, GLenum eformat, GLenum type, GLvoid* data, GLenum wrap, GLenum filter) {
GLuint tex = 0;
glGenTextures(1, &tex);
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, iformat, w, h, 0, eformat, type, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
return tex;
}
GLuint Ocean::createFBO(GLuint tex) {
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status != GL_FRAMEBUFFER_COMPLETE) {
printf("Error framebuffer is not complete.\n");
::exit(EXIT_FAILURE);
}
return fbo;
}
void Ocean::render(float dt) {
glViewport(0, 0, RESOLUTION, RESOLUTION);
glDisable(GL_DEPTH_TEST);
glBindVertexArray(gfx.fullscreen_vao);
if(changed) {
printf("Init spectrum.\n");
glBindFramebuffer(GL_FRAMEBUFFER, init_spectrum_fbo);
#if 0
glClear(GL_COLOR_BUFFER_BIT);
GLenum db[] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, db);
#endif
glUseProgram(gfx.init_spectrum_prog);
glUniform2f(ufl(gfx.init_spectrum_prog, "u_wind"), wind_x, wind_y);
glUniform1f(ufl(gfx.init_spectrum_prog, "u_size"), size);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
// phase
glUseProgram(gfx.phase_prog);
glBindFramebuffer(GL_FRAMEBUFFER, (ping_phase) ? pong_phase_fbo : ping_phase_fbo);
glUniform1i(ufl(gfx.phase_prog, "u_phases"), (ping_phase) ? PING_PHASE_UNIT : PONG_PHASE_UNIT);
ping_phase = !ping_phase;
glUniform1f(ufl(gfx.phase_prog, "u_deltaTime"), dt);
glUniform1f(ufl(gfx.phase_prog, "u_size"), size);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// spectrum
glUseProgram(gfx.spectrum_prog);
glBindFramebuffer(GL_FRAMEBUFFER, spectrum_fbo);
glUniform1i(ufl(gfx.spectrum_prog, "u_phases"), ping_phase ? PING_PHASE_UNIT : PONG_PHASE_UNIT);
glUniform1f(ufl(gfx.spectrum_prog, "u_size"), size);
glUniform1f(ufl(gfx.spectrum_prog, "u_choppiness"), 1.5);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// GPU FFT using Stockham
#if 1
GLuint subtrans_prog = gfx.hor_subtrans_prog;
glUseProgram(subtrans_prog);
int niter = log2(RESOLUTION) * 2;
for(int i = 0; i < niter; ++i) {
if(i == 0) {
glBindFramebuffer(GL_FRAMEBUFFER, ping_transform_fbo);
glUniform1i(ufl(subtrans_prog, "u_input"), SPECTRUM_UNIT);
}
else if(i == (niter - 1) ) {
glBindFramebuffer(GL_FRAMEBUFFER, displacement_map_fbo);
glUniform1i(ufl(subtrans_prog, "u_input"), (niter % 2 == 0) ? PING_TRANSFORM_UNIT : PONG_TRANSFORM_UNIT);
}
else if(i % 2 == 1) {
glBindFramebuffer(GL_FRAMEBUFFER, pong_transform_fbo);
glUniform1i(ufl(subtrans_prog, "u_input"), PING_TRANSFORM_UNIT);
}
else {
glBindFramebuffer(GL_FRAMEBUFFER, ping_transform_fbo);
glUniform1i(ufl(subtrans_prog, "u_input"), PONG_TRANSFORM_UNIT);
}
if(i == (niter / 2)) {
subtrans_prog = gfx.vert_subtrans_prog;
glUseProgram(subtrans_prog);
}
glUniform1f(ufl(subtrans_prog, "u_subtransform_size"), pow(2, (i % (niter / 2)) + 1));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
#endif
// normal map
glBindFramebuffer(GL_FRAMEBUFFER, normal_map_fbo);
glUseProgram(gfx.normal_map_prog);
if(changed) {
glUniform1f(ufl(gfx.normal_map_prog, "u_size"), size);
}
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ocean shader
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, 1280, 720);
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArray(data_vao);
glUseProgram(gfx.ocean_prog);
// using hardcoded view matrix and projection matrix
mat4 pm; // projection matrix
mat4 vm; // view matrix
vec3 cam_pos(-712.0f, 719.0f, 1812.0f);
vm[0] = 0.9210609793663025; vm[4] = 0; vm[8] = 0.3894183337688446; vm[12] = -49.438804626464844;
vm[1] = 0.1866970956325531; vm[5] = 0.8775825500488281; vm[9] = -0.4415801763534546; vm[13] = 302.28753662109375;
vm[2] = -0.3417467474937439; vm[6] = 0.4794255495071411; vm[10] = 0.8083070516586304; vm[14] = -2053.33349609375;
vm[3] = 0; vm[7] = 0; vm[11] = 0; vm[15] = 1;
pm[0] = 0.5231773257255554; pm[4] = 0; pm[8] = 0; pm[12] = 0;
pm[1] = 0; pm[5] = 1.7320507764816284; pm[9] = 0; pm[13] = 0;
pm[2] = 0; pm[6] = 0; pm[10] = -1.000100016593933; pm[14] = -1.000100016593933;
pm[3] = 0; pm[7] = 0; pm[11] = -1; pm[15] = 0;
glUniformMatrix4fv(ufl(gfx.ocean_prog, "u_pm"), 1, GL_FALSE, pm.ptr());
glUniformMatrix4fv(ufl(gfx.ocean_prog, "u_vm"), 1, GL_FALSE, vm.ptr());
glUniform3fv(ufl(gfx.ocean_prog, "u_camera_position"), 1, cam_pos.ptr());
if(changed) {
glUniform1f(ufl(gfx.ocean_prog, "u_size"), size);
}
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
}
changed = false;
}
#ifndef OCEAN_H
#define OCEAN_H
#include "PCH.h"
class WaterGraphics;
class Ocean {
public:
Ocean(WaterGraphics& w);
~Ocean();
bool setup();
void render(float dt);
private:
bool setupBuffers();
GLuint createTexture(GLuint unit, GLenum iformat, int w, int h, GLenum eformat, GLenum type, GLvoid* data, GLenum wrap, GLenum filter);
GLuint createFBO(GLuint tex);
public:
WaterGraphics& gfx;
bool changed; /* wether any of the uniform values changed */
bool ping_phase;
float wind_x; /* wind x direction */
float wind_y; /* wind y direction */
float size; /* size (in meters) of the ocean we're simulation */
std::vector<int> indices;
GLuint data_vao;
GLuint data_vbo;
GLuint indices_vbo;
GLuint init_spectrum_tex;
GLuint spectrum_tex;
GLuint ping_phase_tex;
GLuint pong_phase_tex;
GLuint ping_transform_tex;
GLuint pong_transform_tex;
GLuint normal_map_tex;
GLuint displacement_map_tex;
GLuint init_spectrum_fbo;
GLuint spectrum_fbo;
GLuint ping_phase_fbo;
GLuint pong_phase_fbo;
GLuint ping_transform_fbo;
GLuint pong_transform_fbo;
GLuint normal_map_fbo;
GLuint displacement_map_fbo;
};
#endif
#ifndef PCH_H
#define PCH_H
#define ROXLU_USE_OPENGL
#define ROXLU_USE_MATH
#include "tinylib.h"
/* Ocean */
#define GEOMETRY_RESOLUTION 256
#define GEOMETRY_SIZE 2000.0f
#define GEOMETRY_ORIG_X -1000.0f
#define GEOMETRY_ORIG_Z -1000.0f
#define RESOLUTION 512
/* Texture units */
#define INITIAL_SPECTRUM_UNIT 0
#define SPECTRUM_UNIT 1
#define DISPLACEMENT_MAP_UNIT 2
#define NORMAL_MAP_UNIT 3
#define PING_PHASE_UNIT 4
#define PONG_PHASE_UNIT 5
#define PING_TRANSFORM_UNIT 6
#define PONG_TRANSFORM_UNIT 7
#include <vector>
#include <string>
#endif
/*
--------------------------------------------------------------------------------
oooo
`888
oooo d8b .ooooo. oooo ooo 888 oooo oooo
`888""8P d88' `88b `88b..8P' 888 `888 `888
888 888 888 Y888' 888 888 888
888 888 888 .o8"'88b 888 888 888
d888b `Y8bod8P' o88' 888o o888o `V88V"V8P'
www.roxlu.com
www.apollomedia.nl
--------------------------------------------------------------------------------
Tiny library with a couple of handy functions for opengl based applications.
Sent changes to: https://gist.github.com/roxlu/7788294
Usage:
------
#define ROXLU_USE_ALL - to include all code
#define ROXLU_USE_OPENGL - to use the opengl code
#define ROXLU_USE_PNG - to use the png loader and saver
#define ROXLU_USE_MATH - to use the vec2, vec3, vec4, mat4 classes
SHADER - define `ROXLU_USE_OPENGL` before including
===================================================================================
vert = rx_create_shader(GL_VERTEX_SHADER, source_char_p); - create a shader, pass type
prog = rx_create_program(vert, frag); - create a problem - DOES NOT LINK
IMAGES - define `ROXLU_USE_PNG` before including
===================================================================================
rx_save_png("filename.png", pixels, 640, 480, 3); - writes a png using lib png
rx_load_png("filepath.png", &pix, width, height, nchannels) - load the pixels, width, height and nchannels for the given filepath. make sure to delete pix (which is unsigned char*)
UTILS
===================================================================================
std::string path = rx_get_exe_path(); - returns the path to the exe
std::string contents = rx_read_file("filepath.txt"); - returns the contents of the filepath.
MATH - define `ROXLU_USE_MATH` before including.
===================================================================================
utils
------------------------------------------------------------------------------
float rx_random(max) - generate a random value but limit to max
float rx_random(min, max) - generate a random value between min and max
vec2, vec3, vec4
------------------------------------------------------------------------------
float length(v) - get the length of the vector
float dot(a, b) - get the dot product aka squared root
vec2 max(a) - get the biggest component value
vec2 min(a) - get the lowest component value
vec2 max(a,b) - get the biggest vector
vec2 nmin(a,b) - get the smallest vector
vec2 floor(a) - floor the components
vec2 ceil(a) - ceil the compoments
vec2 fract(a) - get the decimal part
vec2 normalized(v) - get the normalized vector
void print() - print the x and y
vec3 cross(a,b) - cross product (vec3)
mat4
------------------------------------------------------------------------------
mat4& mat4.transpose()
mat4& mat4.rotateX(degrees)
mat4& mat4.rotateY(degrees)
mat4& mat4.rotateZ(degrees)
mat4& mat4.rotate(degrees, x, y, z)
mat4& mat4.rotate(degrees, vec3)
mat4& mat4.scale(x, y, z)
mat4& mat4.scale(vec3 v)
mat4& mat4.translate(x, y, z)
mat4& mat4.translate(vec3 v)
mat4& mat4.ortho(l, r, b, t, n , f)
mat4& mat4.frustum(l, r, b, t, n, f)
mat4& mat4.perspective(fov, aspect, near, far)
mat4& mat4.invert()
void mat4.print()
float* mat4.ptr() - get a pointer to the data
Resources:
----------
Math code mostly from: https://github.com/evanw/gl4/blob/master/gl4.cpp
*/
#ifndef ROXLU_TINYLIB_H
#define ROXLU_TINYLIB_H
#if defined(ROXLU_USE_ALL)
# define ROXLU_USE_OPENGL
# define ROXLU_USE_PNG
# define ROXLU_USE_MATH
#endif
#include <cmath>
#include <iostream>
#include <assert.h>
#include <iterator>
#include <algorithm>
#include <string>
#include <fstream>
#include <math.h>
#if defined(__APPLE__)
# if defined(ROXLU_USE_OPENGL)
# include <OpenGL/gl3.h>
# endif
# include <libgen.h> /* dirname */
# include <CoreFoundation/CFRunLoop.h>
# include <mach/mach.h>
# include <mach/mach_time.h>
# include <mach-o/dyld.h> /* _NSGetExecutablePath */
# include <sys/resource.h>
# include <sys/sysctl.h>
# include <sys/stat.h> /* stat() */
# include <unistd.h> /* sysconf */
#else
# include <GLXW/glxw.h>
#endif
#if defined(ROXLU_USE_PNG)
# include <png.h>
#endif
#ifndef PI
#define PI 3.14159265358979323846
#endif
#ifndef TWO_PI
#define TWO_PI 6.28318530717958647693
#endif
#ifndef M_TWO_PI
#define M_TWO_PI 6.28318530717958647693
#endif
#ifndef FOUR_PI
#define FOUR_PI 12.56637061435917295385
#endif
#ifndef HALF_PI
#define HALF_PI 1.57079632679489661923
#endif
#ifndef DEG_TO_RAD
#define DEG_TO_RAD (PI/180.0)
#endif
#ifndef RAD_TO_DEG
#define RAD_TO_DEG (180.0/PI)
#endif
#ifndef MIN
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
#endif
#ifndef MAX
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
#endif
#ifndef CLAMP
#define CLAMP(val,min,max) (MAX(MIN(val,max),min))
#endif
#ifndef ABS
#define ABS(x) (((x) < 0) ? -(x) : (x))
#endif
#ifndef DX
#define DX(i,j,w)((j)*(w))+(i)
#endif
#if defined(ROXLU_USE_OPENGL)
static void rx_print_shader_link_info(GLuint shader) {
GLint status = 0;
GLint count = 0;
GLchar* error = NULL;
GLsizei nchars = 0;
glGetProgramiv(shader, GL_LINK_STATUS, &status);
if(!status) {
glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &count);
if(count > 0) {
error = new GLchar[count];
glGetProgramInfoLog(shader, 2048, &nchars, error);
printf("------\n");
printf("%s\n", error);
printf("------\n");
delete[] error;
error = NULL;
assert(0);
}
}
}
static void rx_print_shader_compile_info(GLuint shader) {
GLint status = 0;
GLint count = 0;
GLchar* error = NULL;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if(!status) {
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &count);
if(count > 0) {
error = new GLchar[count];
glGetShaderInfoLog(shader, count, NULL, error);
printf("------\n");
printf("%s\n", error);
printf("------\n");
delete[] error;
error = NULL;
assert(0);
}
}
}
static GLuint rx_create_program(GLuint vert, GLuint frag) {
GLuint prog = glCreateProgram();
glAttachShader(prog, vert);
glAttachShader(prog, frag);
return prog;
}
static GLuint rx_create_shader(GLenum type, const char* src) {
GLuint s = glCreateShader(type);
glShaderSource(s, 1, &src, NULL);
glCompileShader(s);
rx_print_shader_compile_info(s);
return s;
}
#endif // ROXLU_USE_OPENGL
// UTILS
// ---------------------------------------------------------------------------
#if defined(_WIN32) // rx_get_exe_path()
static std::string rx_get_exe_path() {
char buffer[MAX_PATH];
// Try to get the executable path with a buffer of MAX_PATH characters.
DWORD result = ::GetModuleFileNameA(nullptr, buffer, static_cast<DWORD>(MAX_PATH));
if(result == 0) {
return "";
}
std::string::size_type pos = std::string(buffer).find_last_of( "\\/" );
return std::string(buffer).substr(0, pos) +"\\";
}
#elif defined(__APPLE__) // rx_get_exe_path()
static std::string rx_get_exe_path() {
char buffer[1024];
uint32_t usize = sizeof(buffer);;
int result;
char* path;
char* fullpath;
result = _NSGetExecutablePath(buffer, &usize);
if (result) {
return "";
}
path = (char*)malloc(2 * PATH_MAX);
fullpath = realpath(buffer, path);
if (fullpath == NULL) {
free(path);
return "";
}
strncpy(buffer, fullpath, usize);
const char* dn = dirname(buffer);
usize = strlen(dn);
std::string ret(dn, usize) ;
ret.push_back('/');
free(path);
return ret;
}
#elif defined(__linux) // rx_get_exe_path()
static std::string rx_get_exe_path() {
char buffer[MAX_PATH];
size_t size = MAX_PATH;
ssize_t n = readlink("/proc/self/exe", buffer, size - 1);
if (n <= 0) {
return "";
}
buffer[n] = '\0';
const char* dn = dirname(buffer);
size = strlen(dn);
std::string ret(dn, size) ;
ret.push_back('/');
return ret;
}
#endif // rx_get_exe_path()
#if defined(ROXLU_USE_PNG)
// write an w*h array of pixels to a png file
static bool rx_save_png(std::string filepath, unsigned char* pixels, int w, int h, int channels = 3) {
if(!w || !h) {
printf("error: cannot save png because the given width and height are invalid: %d x %d\n", w, h);
return false;
}
if(!channels || channels > 4) {
printf("error: cannot save png because the number of color channels is invalid: %d\n", channels);
return false;
}
if(!pixels) {
printf("error: cannot save png because we got an invalid pixels array: %p\n", pixels);
return false;
}
if(!filepath.size()) {
printf("error: cannot save png because the given filepath is invalid.\n");
return false;
}
png_structp png_ptr;
png_infop info_ptr;
FILE* fp = fopen(filepath.c_str(), "wb");
if(!fp) {
printf("error: canont save png because we cannot open the filepath: %s\n", filepath.c_str());
fp = NULL;
return false;
}
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(png_ptr == NULL) {
printf("error: cannot save png because we cannot create a png write struct.\n");
fclose(fp);
fp = NULL;
return false;
}
info_ptr = png_create_info_struct(png_ptr);
if(info_ptr == NULL) {
printf("error: cannot save png brecause we cannot create a png info struct.\n");
fclose(fp);
fp = NULL;
return false;
}
if(setjmp(png_jmpbuf(png_ptr))) {
printf("error: cannot save png because we cannot set the jump pointer.\n");
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
fp = NULL;
return false;
}
png_uint_32 color_type;
switch(channels) {
case 1: {
color_type = PNG_COLOR_TYPE_GRAY;
break;
}
case 3: {
color_type = PNG_COLOR_TYPE_RGB;
break;
}
case 4: {
color_type = PNG_COLOR_TYPE_RGB_ALPHA;
break;
}
default: {
printf("error: cannot save png because we cannot detect the color type based on the number of channels.\n");
fclose(fp);
fp = NULL;
return false;
}
};
png_set_IHDR(png_ptr,
info_ptr,
w,
h,
8,
color_type,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_bytep* row_ptrs = new png_bytep[h];
for(size_t j = 0; j < h; ++j) {
row_ptrs[j] = pixels + (j * (w * channels));
}
png_init_io(png_ptr, fp);
png_set_rows(png_ptr, info_ptr, row_ptrs);
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
png_destroy_write_struct(&png_ptr, &info_ptr);
delete[] row_ptrs;
fclose(fp);
return true;
}
static bool rx_load_png(std::string filepath,
unsigned char** pixels,
uint32_t& width,
uint32_t& height,
uint32_t& nchannels
)
{
png_structp png_ptr;
png_infop info_ptr;
FILE* fp = fopen(filepath.c_str(), "rb");
if(!fp) {
printf("Error: cannot load the png file: %s\n", filepath.c_str());
fp = NULL;
return false;
}
unsigned char sig[8];
size_t r = 0;
r = fread(sig, 1, 8, fp);
if(r != 8) {
printf("Error: invalid png signature (not enough bytes read) in: %s.\n", filepath.c_str());
fclose(fp);
fp = NULL;
return false;
}
if(!png_check_sig(sig, 8)) {
printf("Error: invalid png signature (wrong siganture) in: %s.\n", filepath.c_str());
fclose(fp);
fp = NULL;
return false;
}
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(!png_ptr) {
printf("Error: cannot create png read struct: %s\n", filepath.c_str());
fclose(fp);
fp = NULL;
return false;
}
info_ptr = png_create_info_struct(png_ptr);
if(!info_ptr) {
png_destroy_read_struct(&png_ptr, NULL, NULL);
printf("Error: cannot create png info struct for: %s\n", filepath.c_str());
fclose(fp);
fp = NULL;
return false;
}
#if !defined(_WIN32)
if(setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);
fp = NULL;
return false;
}
#endif
// @TODO - add option to rescale to 8bit color info or 16bit
// @TODO - add option to strip the alpha (not recommended in the example)
png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
uint32_t stride = 0;
uint32_t num_bytes = 0;
uint32_t bit_depth = png_get_bit_depth(png_ptr, info_ptr);
uint32_t color_type = png_get_color_type(png_ptr, info_ptr);
width = png_get_image_width(png_ptr, info_ptr);
height = png_get_image_height(png_ptr, info_ptr);
nchannels = png_get_channels(png_ptr, info_ptr);
if(width == 0 || height == 0) {
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);
fp = NULL;
return false;
}
// @TODO - add option to allow input colors/gray values to be not converted
switch(color_type) {
case PNG_COLOR_TYPE_PALETTE: {
png_set_palette_to_rgb(png_ptr);
nchannels = 3;
break;
}
case PNG_COLOR_TYPE_GRAY: {
if(bit_depth < 8) {
png_set_expand_gray_1_2_4_to_8(png_ptr);
bit_depth = 8;
}
break;
}
default:break;
};
// When transparency is set convert it to a full alpha channel
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
png_set_tRNS_to_alpha(png_ptr);
nchannels += 1;
}
stride = width * bit_depth * nchannels / 8;
num_bytes = width * height * bit_depth * nchannels / 8;
*pixels = new unsigned char[num_bytes];
if(!pixels) {
printf("Error: image is to big: %s\n", filepath.c_str());
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);
fp = NULL;
pixels = NULL;
return false;
}
png_bytep* row_ptrs = new png_bytep[height];
if(!row_ptrs) {
printf("Error: image is to big: %s\n", filepath.c_str());
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);
fp = NULL;
delete[] *pixels;
pixels = 0;
return false;
}
for(size_t i = 0; i < height; ++i) {
row_ptrs[i] = (png_bytep)(*pixels) +(i * stride);
}
png_read_image(png_ptr, row_ptrs);
delete[] row_ptrs;
row_ptrs = NULL;
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
fclose(fp);
return true;
}
static std::string rx_read_file(std::string filepath) {
std::ifstream ifs(filepath.c_str(), std::ios::in);
if(!ifs.is_open()) {
return "";
}
std::string str((std::istreambuf_iterator<char>(ifs)) , std::istreambuf_iterator<char>());
return str;
}
#endif // ROXLU_USE_PNG
// MATH
// -------------------------------------------------------------------------
#if defined(ROXLU_USE_MATH)
struct vec2 {
union {
struct { float x, y; };
struct { float s, t; };
struct { float r, g; };
float xy[2];
float st[2];
float rg[2];
};
vec2() : x(), y() {}
vec2(float x, float y) : x(x), y(y) {}
vec2(const vec2 &xy) : x(xy.x), y(xy.y) {}
explicit vec2(float f) : x(f), y(f) {}
float* ptr() { return &xy[0]; }
vec2 operator + () const { return vec2(+x, +y); }
vec2 operator - () const { return vec2(-x, -y); }
float& operator[](const unsigned int dx) { return xy[dx]; }
vec2 operator + (const vec2 &vec) const { return vec2(x + vec.x, y + vec.y); }
vec2 operator - (const vec2 &vec) const { return vec2(x - vec.x, y - vec.y); }
vec2 operator * (const vec2 &vec) const { return vec2(x * vec.x, y * vec.y); }
vec2 operator / (const vec2 &vec) const { return vec2(x / vec.x, y / vec.y); }
vec2 operator + (float s) const { return vec2(x + s, y + s); }
vec2 operator - (float s) const { return vec2(x - s, y - s); }
vec2 operator * (float s) const { return vec2(x * s, y * s); }
vec2 operator / (float s) const { return vec2(x / s, y / s); }
friend vec2 operator + (float s, const vec2 &vec) { return vec2(s + vec.x, s + vec.y); }
friend vec2 operator - (float s, const vec2 &vec) { return vec2(s - vec.x, s - vec.y); }
friend vec2 operator * (float s, const vec2 &vec) { return vec2(s * vec.x, s * vec.y); }
friend vec2 operator / (float s, const vec2 &vec) { return vec2(s / vec.x, s / vec.y); }
vec2 &operator += (const vec2 &vec) { return *this = *this + vec; }
vec2 &operator -= (const vec2 &vec) { return *this = *this - vec; }
vec2 &operator *= (const vec2 &vec) { return *this = *this * vec; }
vec2 &operator /= (const vec2 &vec) { return *this = *this / vec; }
vec2 &operator += (float s) { return *this = *this + s; }
vec2 &operator -= (float s) { return *this = *this - s; }
vec2 &operator *= (float s) { return *this = *this * s; }
vec2 &operator /= (float s) { return *this = *this / s; }
bool operator == (const vec2 &vec) const { return x == vec.x && y == vec.y; }
bool operator != (const vec2 &vec) const { return x != vec.x || y != vec.y; }
friend float length(const vec2 &v) { return sqrtf(v.x * v.x + v.y * v.y); }
friend float dot(const vec2 &a, const vec2 &b) { return a.x * b.x + a.y * b.y; }
friend float max(const vec2 &v) { return fmaxf(v.x, v.y); }
friend float min(const vec2 &v) { return fminf(v.x, v.y); }
friend vec2 max(const vec2 &a, const vec2 &b) { return vec2(fmaxf(a.x, b.x), fmaxf(a.y, b.y)); }
friend vec2 min(const vec2 &a, const vec2 &b) { return vec2(fminf(a.x, b.x), fminf(a.y, b.y)); }
friend vec2 floor(const vec2 &v) { return vec2(floorf(v.x), floorf(v.y)); }
friend vec2 ceil(const vec2 &v) { return vec2(ceilf(v.x), ceilf(v.y)); }
friend vec2 abs(const vec2 &v) { return vec2(fabsf(v.x), fabsf(v.y)); }
friend vec2 fract(const vec2 &v) { return v - floor(v); }
friend vec2 normalized(const vec2 &v) { return v / length(v); }
friend std::ostream &operator << (std::ostream &out, const vec2 &v) {
return out << "vec2(" << v.x << ", " << v.y << ")";
}
void print() { printf("vec2(%f, %f)\n", x, y); }
};
struct vec3 {
union {
struct { float x, y, z; };
struct { float s, t, p; };
struct { float r, g, b; };
float xyz[3];
float stp[3];
float rgb[3];
};
vec3() : x(), y(), z() {}
vec3(float x, float y, float z) : x(x), y(y), z(z) {}
vec3(const vec2 &xy, float z) : x(xy.x), y(xy.y), z(z) {}
vec3(float x, const vec2 &yz) : x(x), y(yz.x), z(yz.y) {}
vec3(const vec3 &xyz) : x(xyz.x), y(xyz.y), z(xyz.z) {}
explicit vec3(float f) : x(f), y(f), z(f) {}
float* ptr() { return &xyz[0]; }
vec3 operator + () const { return vec3(+x, +y, +z); }
vec3 operator - () const { return vec3(-x, -y, -z); }
float& operator [](const unsigned int dx) { return xyz[dx]; }
vec3 operator + (const vec3 &vec) const { return vec3(x + vec.x, y + vec.y, z + vec.z); }
vec3 operator - (const vec3 &vec) const { return vec3(x - vec.x, y - vec.y, z - vec.z); }
vec3 operator * (const vec3 &vec) const { return vec3(x * vec.x, y * vec.y, z * vec.z); }
vec3 operator / (const vec3 &vec) const { return vec3(x / vec.x, y / vec.y, z / vec.z); }
vec3 operator + (float s) const { return vec3(x + s, y + s, z + s); }
vec3 operator - (float s) const { return vec3(x - s, y - s, z - s); }
vec3 operator * (float s) const { return vec3(x * s, y * s, z * s); }
vec3 operator / (float s) const { return vec3(x / s, y / s, z / s); }
friend vec3 operator + (float s, const vec3 &vec) { return vec3(s + vec.x, s + vec.y, s + vec.z); }
friend vec3 operator - (float s, const vec3 &vec) { return vec3(s - vec.x, s - vec.y, s - vec.z); }
friend vec3 operator * (float s, const vec3 &vec) { return vec3(s * vec.x, s * vec.y, s * vec.z); }
friend vec3 operator / (float s, const vec3 &vec) { return vec3(s / vec.x, s / vec.y, s / vec.z); }
vec3 &operator += (const vec3 &vec) { return *this = *this + vec; }
vec3 &operator -= (const vec3 &vec) { return *this = *this - vec; }
vec3 &operator *= (const vec3 &vec) { return *this = *this * vec; }
vec3 &operator /= (const vec3 &vec) { return *this = *this / vec; }
vec3 &operator += (float s) { return *this = *this + s; }
vec3 &operator -= (float s) { return *this = *this - s; }
vec3 &operator *= (float s) { return *this = *this * s; }
vec3 &operator /= (float s) { return *this = *this / s; }
bool operator == (const vec3 &vec) const { return x == vec.x && y == vec.y && z == vec.z; }
bool operator != (const vec3 &vec) const { return x != vec.x || y != vec.y || z != vec.z; }
friend float length(const vec3 &v) { return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); }
friend float dot(const vec3 &a, const vec3 &b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
friend float max(const vec3 &v) { return fmaxf(fmaxf(v.x, v.y), v.z); }
friend float min(const vec3 &v) { return fminf(fminf(v.x, v.y), v.z); }
friend vec3 max(const vec3 &a, const vec3 &b) { return vec3(fmaxf(a.x, b.x), fmaxf(a.y, b.y), fmaxf(a.z, b.z)); }
friend vec3 min(const vec3 &a, const vec3 &b) { return vec3(fminf(a.x, b.x), fminf(a.y, b.y), fminf(a.z, b.z)); }
friend vec3 floor(const vec3 &v) { return vec3(floorf(v.x), floorf(v.y), floorf(v.z)); }
friend vec3 ceil(const vec3 &v) { return vec3(ceilf(v.x), ceilf(v.y), ceilf(v.z)); }
friend vec3 abs(const vec3 &v) { return vec3(fabsf(v.x), fabsf(v.y), fabsf(v.z)); }
friend vec3 fract(const vec3 &v) { return v - floor(v); }
friend vec3 normalized(const vec3 &v) { return v / length(v); }
friend vec3 cross(const vec3 &a, const vec3 &b) { return vec3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); }
friend std::ostream &operator << (std::ostream &out, const vec3 &v) {
return out << "vec3(" << v.x << ", " << v.y << ", " << v.z << ")";
}
void print() { printf("vec3(%f, %f, %f)\n", x, y, z); }
};
struct vec4 {
union {
struct { float x, y, z, w; };
struct { float s, t, p, q; };
struct { float r, g, b, a; };
float xyzw[4];
float stpq[4];
float rgba[4];
};
vec4() : x(), y(), z(), w() {}
vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}
vec4(const vec2 &xy, float z, float w) : x(xy.x), y(xy.y), z(z), w(w) {}
vec4(float x, const vec2 &yz, float w) : x(x), y(yz.x), z(yz.y), w(w) {}
vec4(float x, float y, const vec2 &zw) : x(x), y(y), z(zw.x), w(zw.y) {}
vec4(const vec2 &xy, const vec2 &zw) : x(xy.x), y(xy.y), z(zw.x), w(zw.y) {}
vec4(const vec3 &xyz, float w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) {}
vec4(float x, const vec3 &yzw) : x(x), y(yzw.x), z(yzw.y), w(yzw.z) {}
vec4(const vec4 &xyzw) : x(xyzw.x), y(xyzw.y), z(xyzw.z), w(xyzw.w) {}
explicit vec4(float f) : x(f), y(f), z(f), w(f) {}
float* ptr() { return &xyzw[0]; }
vec4 operator + () const { return vec4(+x, +y, +z, +w); }
vec4 operator - () const { return vec4(-x, -y, -z, -w); }
vec4 operator + (const vec4 &vec) const { return vec4(x + vec.x, y + vec.y, z + vec.z, w + vec.w); }
vec4 operator - (const vec4 &vec) const { return vec4(x - vec.x, y - vec.y, z - vec.z, w - vec.w); }
vec4 operator * (const vec4 &vec) const { return vec4(x * vec.x, y * vec.y, z * vec.z, w * vec.w); }
vec4 operator / (const vec4 &vec) const { return vec4(x / vec.x, y / vec.y, z / vec.z, w / vec.w); }
vec4 operator + (float s) const { return vec4(x + s, y + s, z + s, w + s); }
vec4 operator - (float s) const { return vec4(x - s, y - s, z - s, w - s); }
vec4 operator * (float s) const { return vec4(x * s, y * s, z * s, w * s); }
vec4 operator / (float s) const { return vec4(x / s, y / s, z / s, w / s); }
friend vec4 operator + (float s, const vec4 &vec) { return vec4(s + vec.x, s + vec.y, s + vec.z, s + vec.w); }
friend vec4 operator - (float s, const vec4 &vec) { return vec4(s - vec.x, s - vec.y, s - vec.z, s - vec.w); }
friend vec4 operator * (float s, const vec4 &vec) { return vec4(s * vec.x, s * vec.y, s * vec.z, s * vec.w); }
friend vec4 operator / (float s, const vec4 &vec) { return vec4(s / vec.x, s / vec.y, s / vec.z, s / vec.w); }
vec4 &operator += (const vec4 &vec) { return *this = *this + vec; }
vec4 &operator -= (const vec4 &vec) { return *this = *this - vec; }
vec4 &operator *= (const vec4 &vec) { return *this = *this * vec; }
vec4 &operator /= (const vec4 &vec) { return *this = *this / vec; }
vec4 &operator += (float s) { return *this = *this + s; }
vec4 &operator -= (float s) { return *this = *this - s; }
vec4 &operator *= (float s) { return *this = *this * s; }
vec4 &operator /= (float s) { return *this = *this / s; }
bool operator == (const vec4 &vec) const { return x == vec.x && y == vec.y && z == vec.z && w == vec.w; }
bool operator != (const vec4 &vec) const { return x != vec.x || y != vec.y || z != vec.z || w != vec.w; }
friend float length(const vec4 &v) { return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w); }
friend float dot(const vec4 &a, const vec4 &b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * a.w; }
friend float max(const vec4 &v) { return fmaxf(fmaxf(v.x, v.y), fmaxf(v.z, v.w)); }
friend float min(const vec4 &v) { return fminf(fminf(v.x, v.y), fminf(v.z, v.w)); }
friend vec4 max(const vec4 &a, const vec4 &b) { return vec4(fmaxf(a.x, b.x), fmaxf(a.y, b.y), fmaxf(a.z, b.z), fmaxf(a.w, b.w)); }
friend vec4 min(const vec4 &a, const vec4 &b) { return vec4(fminf(a.x, b.x), fminf(a.y, b.y), fminf(a.z, b.z), fminf(a.w, b.w)); }
friend vec4 floor(const vec4 &v) { return vec4(floorf(v.x), floorf(v.y), floorf(v.z), floorf(v.w)); }
friend vec4 ceil(const vec4 &v) { return vec4(ceilf(v.x), ceilf(v.y), ceilf(v.z), ceilf(v.w)); }
friend vec4 abs(const vec4 &v) { return vec4(fabsf(v.x), fabsf(v.y), fabsf(v.z), fabsf(v.w)); }
friend vec4 fract(const vec4 &v) { return v - floor(v); }
friend vec4 normalized(const vec4 &v) { return v / length(v); }
friend std::ostream &operator << (std::ostream &out, const vec4 &v) {
return out << "vec4(" << v.x << ", " << v.y << ", " << v.z << ", " << v.w << ")";
}
};
struct mat4 {
union {
struct {
float m00, m01, m02, m03;
float m10, m11, m12, m13;
float m20, m21, m22, m23;
float m30, m31, m32, m33;
};
float m[16];
};
mat4() :
m00(1), m01(), m02(), m03(),
m10(), m11(1), m12(), m13(),
m20(), m21(), m22(1), m23(),
m30(), m31(), m32(), m33(1) {}
mat4(const vec4 &r0, const vec4 &r1, const vec4 &r2, const vec4 &r3) :
m00(r0.x), m01(r0.y), m02(r0.z), m03(r0.w),
m10(r1.x), m11(r1.y), m12(r1.z), m13(r1.w),
m20(r2.x), m21(r2.y), m22(r2.z), m23(r2.w),
m30(r3.x), m31(r3.y), m32(r3.z), m33(r3.w) {}
mat4(
float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33) :
m00(m00), m01(m01), m02(m02), m03(m03),
m10(m10), m11(m11), m12(m12), m13(m13),
m20(m20), m21(m21), m22(m22), m23(m23),
m30(m30), m31(m31), m32(m32), m33(m33) {}
mat4 &transpose();
mat4 &rotateX(float degrees);
mat4 &rotateY(float degrees);
mat4 &rotateZ(float degrees);
mat4 &rotate(float degrees, float x, float y, float z);
mat4 &rotate(float degrees, const vec3 &v) { return rotate(degrees, v.x, v.y, v.z); }
mat4 &scale(float x, float y, float z);
mat4 &scale(const vec3 &v) { return scale(v.x, v.y, v.z); }
mat4 &translate(float x, float y, float z);
mat4 &translate(const vec3 &v) { return translate(v.x, v.y, v.z); }
mat4 &ortho(float l, float r, float b, float t, float n, float f);
mat4 &frustum(float l, float r, float b, float t, float n, float f);
mat4 &perspective(float fov, float aspect, float near, float far);
mat4 &invert();
float* ptr() { return &m[0]; }
float& operator[](const unsigned int dx);
mat4 &operator *= (const mat4 &t);
vec4 operator * (const vec4 &v);
friend vec4 operator * (const vec4 &v, const mat4 &t);
friend std::ostream &operator << (std::ostream &out, const mat4 &t);
void print();
};
inline float& mat4::operator[](const unsigned int dx) {
return m[dx];
}
inline mat4 &mat4::transpose() {
std::swap(m01, m10); std::swap(m02, m20); std::swap(m03, m30);
std::swap(m12, m21); std::swap(m13, m31); std::swap(m23, m32);
return *this;
}
inline mat4 &mat4::rotateX(float degrees) {
float radians = degrees * (M_PI / 180);
float s = sinf(radians), c = cosf(radians);
float t01 = m01, t02 = m02;
float t11 = m11, t12 = m12;
float t21 = m21, t22 = m22;
float t31 = m31, t32 = m32;
m01 = c * t01 - s * t02;
m02 = c * t02 + s * t01;
m11 = c * t11 - s * t12;
m12 = c * t12 + s * t11;
m21 = c * t21 - s * t22;
m22 = c * t22 + s * t21;
m31 = c * t31 - s * t32;
m32 = c * t32 + s * t31;
return *this;
}
inline mat4 &mat4::rotateY(float degrees) {
float radians = degrees * (M_PI / 180);
float s = sinf(radians), c = cosf(radians);
float t02 = m02, t00 = m00;
float t12 = m12, t10 = m10;
float t22 = m22, t20 = m20;
float t32 = m32, t30 = m30;
m02 = c * t02 - s * t00;
m00 = c * t00 + s * t02;
m12 = c * t12 - s * t10;
m10 = c * t10 + s * t12;
m22 = c * t22 - s * t20;
m20 = c * t20 + s * t22;
m32 = c * t32 - s * t30;
m30 = c * t30 + s * t32;
return *this;
}
inline mat4 &mat4::rotateZ(float degrees) {
float radians = degrees * (M_PI / 180);
float s = sinf(radians), c = cosf(radians);
float t00 = m00, t01 = m01;
float t10 = m10, t11 = m11;
float t20 = m20, t21 = m21;
float t30 = m30, t31 = m31;
m00 = c * t00 - s * t01;
m01 = c * t01 + s * t00;
m10 = c * t10 - s * t11;
m11 = c * t11 + s * t10;
m20 = c * t20 - s * t21;
m21 = c * t21 + s * t20;
m30 = c * t30 - s * t31;
m31 = c * t31 + s * t30;
return *this;
}
inline mat4 &mat4::rotate(float degrees, float x, float y, float z) {
float radians = degrees * (M_PI / 180);
float d = sqrtf(x*x + y*y + z*z);
float s = sinf(radians), c = cosf(radians), t = 1 - c;
x /= d; y /= d; z /= d;
return *this *= mat4(
x*x*t + c, x*y*t - z*s, x*z*t + y*s, 0,
y*x*t + z*s, y*y*t + c, y*z*t - x*s, 0,
z*x*t - y*s, z*y*t + x*s, z*z*t + c, 0,
0, 0, 0, 1);
}
inline mat4 &mat4::scale(float x, float y, float z) {
m00 *= x; m01 *= y; m02 *= z;
m10 *= x; m11 *= y; m12 *= z;
m20 *= x; m21 *= y; m22 *= z;
m30 *= x; m31 *= y; m32 *= z;
return *this;
}
inline mat4 &mat4::translate(float x, float y, float z) {
m03 += m00 * x + m01 * y + m02 * z;
m13 += m10 * x + m11 * y + m12 * z;
m23 += m20 * x + m21 * y + m22 * z;
m33 += m30 * x + m31 * y + m32 * z;
return *this;
}
inline mat4 &mat4::ortho(float l, float r, float b, float t, float n, float f) {
return *this *= mat4(
2 / (r - l), 0, 0, (r + l) / (l - r),
0, 2 / (t - b), 0, (t + b) / (b - t),
0, 0, 2 / (n - f), (f + n) / (n - f),
0, 0, 0, 1);
}
inline mat4 &mat4::frustum(float l, float r, float b, float t, float n, float f) {
return *this *= mat4(
2 * n / (r - l), 0, (r + l) / (r - l), 0,
0, 2 * n / (t - b), (t + b) / (t - b), 0,
0, 0, (f + n) / (n - f), 2 * f * n / (n - f),
0, 0, -1, 0);
}
inline mat4 &mat4::perspective(float fov, float aspect, float near, float far) {
float y = tanf(fov * M_PI / 360) * near, x = y * aspect;
return frustum(-x, x, -y, y, near, far);
}
inline mat4 &mat4::invert() {
float t00 = m00, t01 = m01, t02 = m02, t03 = m03;
*this = mat4(
m11*m22*m33 - m11*m32*m23 - m12*m21*m33 + m12*m31*m23 + m13*m21*m32 - m13*m31*m22,
-m01*m22*m33 + m01*m32*m23 + m02*m21*m33 - m02*m31*m23 - m03*m21*m32 + m03*m31*m22,
m01*m12*m33 - m01*m32*m13 - m02*m11*m33 + m02*m31*m13 + m03*m11*m32 - m03*m31*m12,
-m01*m12*m23 + m01*m22*m13 + m02*m11*m23 - m02*m21*m13 - m03*m11*m22 + m03*m21*m12,
-m10*m22*m33 + m10*m32*m23 + m12*m20*m33 - m12*m30*m23 - m13*m20*m32 + m13*m30*m22,
m00*m22*m33 - m00*m32*m23 - m02*m20*m33 + m02*m30*m23 + m03*m20*m32 - m03*m30*m22,
-m00*m12*m33 + m00*m32*m13 + m02*m10*m33 - m02*m30*m13 - m03*m10*m32 + m03*m30*m12,
m00*m12*m23 - m00*m22*m13 - m02*m10*m23 + m02*m20*m13 + m03*m10*m22 - m03*m20*m12,
m10*m21*m33 - m10*m31*m23 - m11*m20*m33 + m11*m30*m23 + m13*m20*m31 - m13*m30*m21,
-m00*m21*m33 + m00*m31*m23 + m01*m20*m33 - m01*m30*m23 - m03*m20*m31 + m03*m30*m21,
m00*m11*m33 - m00*m31*m13 - m01*m10*m33 + m01*m30*m13 + m03*m10*m31 - m03*m30*m11,
-m00*m11*m23 + m00*m21*m13 + m01*m10*m23 - m01*m20*m13 - m03*m10*m21 + m03*m20*m11,
-m10*m21*m32 + m10*m31*m22 + m11*m20*m32 - m11*m30*m22 - m12*m20*m31 + m12*m30*m21,
m00*m21*m32 - m00*m31*m22 - m01*m20*m32 + m01*m30*m22 + m02*m20*m31 - m02*m30*m21,
-m00*m11*m32 + m00*m31*m12 + m01*m10*m32 - m01*m30*m12 - m02*m10*m31 + m02*m30*m11,
m00*m11*m22 - m00*m21*m12 - m01*m10*m22 + m01*m20*m12 + m02*m10*m21 - m02*m20*m11
);
float det = m00 * t00 + m10 * t01 + m20 * t02 + m30 * t03;
for (int i = 0; i < 16; i++) m[i] /= det;
return *this;
}
inline mat4 &mat4::operator *= (const mat4 &t) {
*this = mat4(
m00*t.m00 + m01*t.m10 + m02*t.m20 + m03*t.m30,
m00*t.m01 + m01*t.m11 + m02*t.m21 + m03*t.m31,
m00*t.m02 + m01*t.m12 + m02*t.m22 + m03*t.m32,
m00*t.m03 + m01*t.m13 + m02*t.m23 + m03*t.m33,
m10*t.m00 + m11*t.m10 + m12*t.m20 + m13*t.m30,
m10*t.m01 + m11*t.m11 + m12*t.m21 + m13*t.m31,
m10*t.m02 + m11*t.m12 + m12*t.m22 + m13*t.m32,
m10*t.m03 + m11*t.m13 + m12*t.m23 + m13*t.m33,
m20*t.m00 + m21*t.m10 + m22*t.m20 + m23*t.m30,
m20*t.m01 + m21*t.m11 + m22*t.m21 + m23*t.m31,
m20*t.m02 + m21*t.m12 + m22*t.m22 + m23*t.m32,
m20*t.m03 + m21*t.m13 + m22*t.m23 + m23*t.m33,
m30*t.m00 + m31*t.m10 + m32*t.m20 + m33*t.m30,
m30*t.m01 + m31*t.m11 + m32*t.m21 + m33*t.m31,
m30*t.m02 + m31*t.m12 + m32*t.m22 + m33*t.m32,
m30*t.m03 + m31*t.m13 + m32*t.m23 + m33*t.m33
);
return *this;
}
inline vec4 mat4::operator * (const vec4 &v) {
return vec4(
m00*v.x + m01*v.y + m02*v.z + m03*v.w,
m10*v.x + m11*v.y + m12*v.z + m13*v.w,
m20*v.x + m21*v.y + m22*v.z + m23*v.w,
m30*v.x + m31*v.y + m32*v.z + m33*v.w
);
}
inline vec4 operator * (const vec4 &v, const mat4 &t) {
return vec4(
t.m00*v.x + t.m10*v.y + t.m20*v.z + t.m30*v.w,
t.m01*v.x + t.m11*v.y + t.m21*v.z + t.m31*v.w,
t.m02*v.x + t.m12*v.y + t.m22*v.z + t.m32*v.w,
t.m03*v.x + t.m13*v.y + t.m23*v.z + t.m33*v.w
);
}
inline void mat4::print() {
printf("mat4(%f, %f, %f, %f\n %f, %f, %f, %f\n %f, %f, %f, %f\n %f, %f, %f, %f)\n",
m[0], m[4], m[8], m[12],
m[1], m[5], m[9], m[13],
m[2], m[6], m[10], m[14],
m[3], m[7], m[11], m[15]);
}
inline std::ostream &operator << (std::ostream &out, const mat4 &t) {
return out << "\nmat4("
<< t.m00 << ", " << t.m01 << ", " << t.m02 << ", " << t.m03 << ",\n "
<< t.m10 << ", " << t.m11 << ", " << t.m12 << ", " << t.m13 << ",\n "
<< t.m20 << ", " << t.m21 << ", " << t.m22 << ", " << t.m23 << ",\n "
<< t.m30 << ", " << t.m31 << ", " << t.m32 << ", " << t.m33 << ")";
}
static float rx_random(float max) {
return max * rand() / (RAND_MAX + 1.0f);
}
static float rx_random(float x, float y) {
float high = 0;
float low = 0;
float result = 0;
high = std::max<float>(x,y);
low = std::min<float>(x,y);
result = low + ((high-low) * rand()/(RAND_MAX + 1.0));
return result;
}
#endif // ROXLU_USE_MATH
// ----------------------------------------------------------------------------
#endif
#include <assert.h>
#include "WaterGraphics.h"
// ---------------------------------------------
GLint ufl(GLuint prog, const char* name) {
GLint u = glGetUniformLocation(prog, name);
if(u < 0) {
printf("error: cannot find `%s` in prog `%d`\n", name, prog);
::exit(EXIT_FAILURE);
}
return u;
}
// ---------------------------------------------
WaterGraphics::WaterGraphics()
:win_w(0)
,win_h(0)
{
}
WaterGraphics::~WaterGraphics() {
}
bool WaterGraphics::setup(int winW, int winH) {
win_w = winW;
win_h = winH;
// Fullscreen shader.
fullscreen_vs = rx_create_shader(GL_VERTEX_SHADER, FULLSCREEN_VS);
fullscreen_fs = rx_create_shader(GL_FRAGMENT_SHADER, FULLSCREEN_FS);
fullscreen_prog = rx_create_program(fullscreen_vs, fullscreen_fs);
glLinkProgram(fullscreen_prog);
rx_print_shader_link_info(fullscreen_prog);
// Horizontal Sub transform
hor_subtrans_fs = rx_create_shader(GL_FRAGMENT_SHADER, SUBTRANSFORM_HOR_FS);
hor_subtrans_prog = rx_create_program(fullscreen_vs, hor_subtrans_fs);
glLinkProgram(hor_subtrans_prog);
rx_print_shader_link_info(hor_subtrans_prog);
glUseProgram(hor_subtrans_prog);
glUniform1f(ufl(hor_subtrans_prog, "u_transform_size"), RESOLUTION);
// Vertical Sub transform
vert_subtrans_fs = rx_create_shader(GL_FRAGMENT_SHADER, SUBTRANSFORM_VERT_FS);
vert_subtrans_prog = rx_create_program(fullscreen_vs, vert_subtrans_fs);
glLinkProgram(vert_subtrans_prog);
rx_print_shader_link_info(vert_subtrans_prog);
glUseProgram(vert_subtrans_prog);
glUniform1f(ufl(vert_subtrans_prog, "u_transform_size"), RESOLUTION);
// Initial spectrum
init_spectrum_fs = rx_create_shader(GL_FRAGMENT_SHADER, INIT_SPECTRUM_FS);
init_spectrum_prog = rx_create_program(fullscreen_vs, init_spectrum_fs);
glLinkProgram(init_spectrum_prog);
rx_print_shader_link_info(init_spectrum_prog);
glUseProgram(init_spectrum_prog);
glUniform1f(ufl(init_spectrum_prog, "u_resolution"), RESOLUTION);
// Phase
phase_fs = rx_create_shader(GL_FRAGMENT_SHADER, PHASE_FS);
phase_prog = rx_create_program(fullscreen_vs, phase_fs);
glLinkProgram(phase_prog);
rx_print_shader_link_info(phase_prog);
glUseProgram(phase_prog);
glUniform1f(ufl(phase_prog, "u_resolution"), RESOLUTION);
// Spectrum
spectrum_fs = rx_create_shader(GL_FRAGMENT_SHADER, SPECTRUM_FS);
spectrum_prog = rx_create_program(fullscreen_vs, spectrum_fs);
glLinkProgram(spectrum_prog);
rx_print_shader_link_info(spectrum_prog);
glUseProgram(spectrum_prog);
glUniform1f(ufl(spectrum_prog, "u_resolution"), RESOLUTION);
glUniform1i(ufl(spectrum_prog, "u_initial_spectrum"), INITIAL_SPECTRUM_UNIT);
// Normal map
normal_map_fs = rx_create_shader(GL_FRAGMENT_SHADER, NORMAL_MAP_FS);
normal_map_prog = rx_create_program(fullscreen_vs, normal_map_fs);
glLinkProgram(normal_map_prog);
rx_print_shader_link_info(normal_map_prog);
glUseProgram(normal_map_prog);
glUniform1f(ufl(normal_map_prog, "u_resolution"), RESOLUTION);
glUniform1i(ufl(normal_map_prog, "u_displacement_map"), DISPLACEMENT_MAP_UNIT);
// Ocean
{
ocean_vs = rx_create_shader(GL_VERTEX_SHADER, OCEAN_VS);
ocean_fs = rx_create_shader(GL_FRAGMENT_SHADER, OCEAN_FS);
ocean_prog = rx_create_program(ocean_vs, ocean_fs);
glBindAttribLocation(ocean_prog, 0, "a_pos");
glBindAttribLocation(ocean_prog, 1, "a_tex");
glLinkProgram(ocean_prog);
rx_print_shader_link_info(ocean_prog);
float ocean_color[] = { 0.004f, 0.016f, 0.047f };
float sky_color[] = { 12.0f, 9.6f, 12.8f } ;
float sun_direction[] = {-1.0f, 1.0f, 1.0f};
glUseProgram(ocean_prog);
glUniform1f(ufl(ocean_prog, "u_geometry_size"), GEOMETRY_SIZE);
glUniform1i(ufl(ocean_prog, "u_displacement_map"), DISPLACEMENT_MAP_UNIT);
glUniform1i(ufl(ocean_prog, "u_normal_map"), NORMAL_MAP_UNIT);
glUniform3fv(ufl(ocean_prog, "u_ocean_color"), 1, ocean_color);
glUniform3fv(ufl(ocean_prog, "u_sky_color"), 1, sky_color);
glUniform3fv(ufl(ocean_prog, "u_sun_direction"), 1, sun_direction);
glUniform1f(ufl(ocean_prog, "u_exposure"), 0.85f);
}
glGenVertexArrays(1, &fullscreen_vao);
printf("--\n");
printf("fullscreen_prog: %d\n", fullscreen_prog);
printf("hor_subtrans_prog: %d\n", hor_subtrans_prog);
printf("vert_subtrans_prog: %d\n", vert_subtrans_prog);
printf("init_spectrum_prog: %d\n", init_spectrum_prog);
printf("phase_prog: %d\n", phase_prog);
printf("spectrum_prog: %d\n", spectrum_prog);
printf("normal_map_prog: %d\n", normal_map_prog);
printf("ocean_prog; %d\n", ocean_prog);
printf("--\n");
return true;
}
/*
Resources:
- [Real-Time Water Simulation, perlin noise with code + grid method](http://www.cse.iitd.ac.in/~cs5080212/MiniP.pdf)
- [Simulation Ocean Water, Jerry Tessendorf](http://graphics.ucsd.edu/courses/rendering/2005/jdewall/tessendorf.pdf)
- [Sundog, Triton commercial software](http://sundog-soft.com/sds/evaluate/gallery/?nggpage=2)
*/
#ifndef WATER_GRAPHICS_H
#define WATER_GRAPHICS_H
#include "PCH.h"
GLint ufl(GLuint prog, const char* name); /* wrapper that returns a uniform location + adds some checks */
static const char* FULLSCREEN_VS = ""
"#version 150\n"
""
"const vec2 verts[4] = vec2[] ("
" vec2(-1.0, 1.0), "
" vec2(-1.0, -1.0), "
" vec2(1.0, 1.0), "
" vec2(1.0, -1.0)"
");"
""
"const vec2 tex[4] = vec2[] ("
" vec2(0.0, 1.0), "
" vec2(0.0, 0.0), "
" vec2(1.0, 1.0), "
" vec2(1.0, 0.0)"
");"
""
"out vec2 v_tex;"
""
"void main() {"
" gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0);"
" v_tex = tex[gl_VertexID];"
"}"
"";
static const char* FULLSCREEN_FS = ""
"#version 150\n"
"out vec4 fragcolor; "
"in vec2 v_tex;"
"void main() {"
" fragcolor = vec4(v_tex.x, v_tex.y, 0.0, 1.0);"
"}";
static const char* SUBTRANSFORM_HOR_FS = ""
"#version 150\n"
"uniform sampler2D u_input;"
"uniform float u_transform_size;"
"uniform float u_subtransform_size;"
""
"in vec2 v_tex;"
"out vec4 fragcolor; "
""
"const float PI = 3.14159265359;"
""
"vec2 multiply_complex(vec2 a, vec2 b) { "
" return vec2(a[0] * b[0] - a[1] * b[1], a[1] * b[0] + a[0] * b[1]);"
"}"
""
"void main() {"
" float index = v_tex.x * u_transform_size - 0.5;"
" float even_index = floor(index / u_subtransform_size) * (u_subtransform_size * 0.5) + mod(index, u_subtransform_size * 0.5);"
" vec4 even = texture(u_input, vec2(even_index + 0.5, gl_FragCoord.y) / u_transform_size);"
" vec4 odd = texture(u_input, vec2(even_index + u_transform_size * 0.5 + 0.5, gl_FragCoord.y) / u_transform_size);"
" float twiddle_arg = -2.0 * PI * (index / u_subtransform_size);"
" vec2 twiddle = vec2(cos(twiddle_arg), sin(twiddle_arg));"
" vec2 output_a = even.xy + multiply_complex(twiddle, odd.xy);"
" vec2 output_b = even.zw + multiply_complex(twiddle, odd.zw);"
" fragcolor = vec4(output_a, output_b);"
"}"
"";
static const char* SUBTRANSFORM_VERT_FS = ""
"#version 150\n"
"uniform sampler2D u_input;"
"uniform float u_transform_size;"
"uniform float u_subtransform_size;"
""
"in vec2 v_tex;"
"out vec4 fragcolor; "
""
"const float PI = 3.14159265359;"
""
"vec2 multiply_complex(vec2 a, vec2 b) { "
" return vec2(a[0] * b[0] - a[1] * b[1], a[1] * b[0] + a[0] * b[1]);"
"}"
""
"void main() {"
" float index = v_tex.y * u_transform_size - 0.5;"
" float even_index = floor(index / u_subtransform_size) * (u_subtransform_size * 0.5) + mod(index, u_subtransform_size * 0.5);"
" vec4 even = texture(u_input, vec2(gl_FragCoord.x, even_index + 0.5) / u_transform_size);"
" vec4 odd = texture(u_input, vec2(gl_FragCoord.x, even_index + u_transform_size * 0.5 + 0.5) / u_transform_size);"
" float twiddle_arg = -2.0 * PI * (index / u_subtransform_size);"
" vec2 twiddle = vec2(cos(twiddle_arg), sin(twiddle_arg));"
" vec2 output_a = even.xy + multiply_complex(twiddle, odd.xy);"
" vec2 output_b = even.zw + multiply_complex(twiddle, odd.zw);"
" fragcolor = vec4(output_a, output_b);"
"}"
"";
static const char* INIT_SPECTRUM_FS = ""
"#version 150\n"
"const float PI = 3.14159265359;"
"const float G = 9.81;"
"const float KM = 370.0;"
"const float CM = 0.23;"
"uniform vec2 u_wind;"
"uniform float u_resolution;"
"uniform float u_size;"
"out vec4 fragcolor;"
"float square(float x) {"
" return x * x;"
"}"
"float omega(float k) {"
" return sqrt(G * k * (1.0 + square(k / KM)));"
"}"
"float tanh(float x) {"
" return (1.0 - exp(-2.0 * x)) / (1.0 + exp(-2.0 * x));"
"}"
"void main (void) {"
" vec2 coordinates = gl_FragCoord.xy - 0.5;"
" float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;"
" float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;"
" vec2 wave_vector = (2.0 * PI * vec2(n, m)) / u_size;"
" float k = length(wave_vector);"
" float U10 = length(u_wind);"
" float Omega = 0.84;"
" float kp = G * square(Omega / U10);"
" float c = omega(k) / k;"
" float cp = omega(kp) / kp;"
" float Lpm = exp(-1.25 * square(kp / k));"
" float gamma = 1.7;"
" float sigma = 0.08 * (1.0 + 4.0 * pow(Omega, -3.0));"
" float Gamma = exp(-square(sqrt(k / kp) - 1.0) / 2.0 * square(sigma));"
" float Jp = pow(gamma, Gamma);"
" float Fp = Lpm * Jp * exp(-Omega / sqrt(10.0) * (sqrt(k / kp) - 1.0));"
" float alphap = 0.006 * sqrt(Omega);"
" float Bl = 0.5 * alphap * cp / c * Fp;"
" float z0 = 0.000037 * square(U10) / G * pow(U10 / cp, 0.9);"
" float uStar = 0.41 * U10 / log(10.0 / z0);"
" float alpham = 0.01 * ((uStar < CM) ? (1.0 + log(uStar / CM)) : (1.0 + 3.0 * log(uStar / CM)));"
" float Fm = exp(-0.25 * square(k / KM - 1.0));"
" float Bh = 0.5 * alpham * CM / c * Fm * Lpm;"
" float a0 = log(2.0) / 4.0;"
" float am = 0.13 * uStar / CM;"
" float Delta = tanh(a0 + 4.0 * pow(c / cp, 2.5) + am * pow(CM / c, 2.5));"
" float cosPhi = dot(normalize(u_wind), normalize(wave_vector));"
" float S = (1.0 / (2.0 * PI)) * pow(k, -4.0) * (Bl + Bh) * (1.0 + Delta * (2.0 * cosPhi * cosPhi - 1.0));"
" float dk = 2.0 * PI / u_size;"
" float h = sqrt(S / 2.0) * dk;"
" if(wave_vector.x == 0.0 && wave_vector.y == 0.0){"
" h = 0.0;" //no DC term
" }"
" fragcolor = vec4(h, 0.0, 0.0, 0.0);"
"}"
"";
static const char* PHASE_FS = ""
"#version 150\n"
"const float PI = 3.14159265359;"
"const float G = 9.81;"
"const float KM = 370.0;"
"in vec2 v_tex;"
"out vec4 fragcolor;"
"uniform sampler2D u_phases;"
"uniform float u_deltaTime;"
"uniform float u_resolution;"
"uniform float u_size;"
"float omega (float k) {"
" return sqrt(G * k * (1.0 + k * k / KM * KM));"
"}"
"void main (void) {"
" float deltaTime = 1.0 / 60.0;"
" vec2 coordinates = gl_FragCoord.xy - 0.5;"
" float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;"
" float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;"
" vec2 waveVector = (2.0 * PI * vec2(n, m)) / u_size;"
" float phase = texture(u_phases, v_tex).r;"
" float deltaPhase = omega(length(waveVector)) * u_deltaTime;"
" phase = mod(phase + deltaPhase, 2.0 * PI);"
" fragcolor = vec4(phase, 0.0, 0.0, 0.0);"
"}"
"";
static const char* SPECTRUM_FS = ""
"#version 150\n"
"const float PI = 3.14159265359;"
"const float G = 9.81;"
"const float KM = 370.0;"
"in vec2 v_tex;"
"out vec4 fragcolor;"
"uniform float u_size;"
"uniform float u_resolution;"
"uniform sampler2D u_phases;"
"uniform sampler2D u_initial_spectrum;"
"uniform float u_choppiness;"
"vec2 multiplyComplex (vec2 a, vec2 b) {"
" return vec2(a[0] * b[0] - a[1] * b[1], a[1] * b[0] + a[0] * b[1]);"
"}"
"vec2 multiplyByI (vec2 z) {"
" return vec2(-z[1], z[0]);"
"}"
"float omega (float k) {"
" return sqrt(G * k * (1.0 + k * k / KM * KM));"
"}"
"void main (void) {"
" vec2 coordinates = gl_FragCoord.xy - 0.5;"
" float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;"
" float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;"
" vec2 waveVector = (2.0 * PI * vec2(n, m)) / u_size;"
" float phase = texture(u_phases, v_tex).r;"
" vec2 phaseVector = vec2(cos(phase), sin(phase));"
" vec2 h0 = texture(u_initial_spectrum, v_tex).rg;"
" vec2 h0Star = texture(u_initial_spectrum, vec2(1.0 - v_tex + 1.0 / u_resolution)).rg;"
" h0Star.y *= -1.0;"
" vec2 h = multiplyComplex(h0, phaseVector) + multiplyComplex(h0Star, vec2(phaseVector.x, -phaseVector.y));"
" vec2 hX = -multiplyByI(h * (waveVector.x / length(waveVector))) * u_choppiness;"
" vec2 hZ = -multiplyByI(h * (waveVector.y / length(waveVector))) * u_choppiness;"
//no DC term
" if (waveVector.x == 0.0 && waveVector.y == 0.0) {"
" h = vec2(0.0);"
" hX = vec2(0.0);"
" hZ = vec2(0.0);"
" }"
" fragcolor = vec4(hX + multiplyByI(h), hZ);"
"}"
"";
static const char* NORMAL_MAP_FS = ""
"#version 150\n"
"in vec2 v_tex;"
"out vec4 fragcolor;"
"uniform sampler2D u_displacement_map;"
"uniform float u_resolution;"
"uniform float u_size;"
"void main (void) {"
" float texel = 1.0 / u_resolution;"
" float texelSize = u_size / u_resolution;"
" vec3 center = texture(u_displacement_map, v_tex).rgb;"
" vec3 right = vec3(texelSize, 0.0, 0.0) + texture(u_displacement_map, v_tex + vec2(texel, 0.0)).rgb - center;"
" vec3 left = vec3(-texelSize, 0.0, 0.0) + texture(u_displacement_map, v_tex + vec2(-texel, 0.0)).rgb - center;"
" vec3 top = vec3(0.0, 0.0, -texelSize) + texture(u_displacement_map, v_tex + vec2(0.0, -texel)).rgb - center;"
" vec3 bottom = vec3(0.0, 0.0, texelSize) + texture(u_displacement_map, v_tex + vec2(0.0, texel)).rgb - center;"
" vec3 topRight = cross(right, top);"
" vec3 topLeft = cross(top, left);"
" vec3 bottomLeft = cross(left, bottom);"
" vec3 bottomRight = cross(bottom, right);"
" fragcolor = vec4(normalize(topRight + topLeft + bottomLeft + bottomRight), 1.0);"
"}"
"";
static const char* OCEAN_VS = ""
"#version 150\n"
"in vec3 a_pos;"
"in vec2 a_tex;"
"out vec3 v_pos;"
"out vec2 v_tex;"
"uniform mat4 u_pm;"
"uniform mat4 u_vm;"
"uniform float u_size;"
"uniform float u_geometry_size;"
"uniform sampler2D u_displacement_map;"
"void main (void) {"
" vec3 position = a_pos + texture(u_displacement_map, a_tex).rgb * (u_geometry_size / u_size);"
" v_pos = position;"
" v_tex = a_tex;"
" gl_Position = u_pm * u_vm * vec4(position, 1.0);"
"}"
"";
static const char* OCEAN_FS = ""
"#version 150\n"
"in vec2 v_tex;"
"in vec3 v_pos;"
"out vec4 fragcolor;"
"uniform sampler2D u_displacement_map;"
"uniform sampler2D u_normal_map;"
"uniform vec3 u_camera_position;"
"uniform vec3 u_ocean_color;"
"uniform vec3 u_sky_color;"
"uniform float u_exposure;"
"uniform vec3 u_sun_direction;"
"vec3 hdr(vec3 color, float exposure) {"
" return 1.0 - exp(-color * exposure);"
"}"
"void main (void) {"
" vec3 normal = texture(u_normal_map, v_tex).rgb;"
" vec3 view = normalize(u_camera_position - v_pos);"
" float fresnel = 0.02 + 0.98 * pow(1.0 - dot(normal, view), 5.0);"
" vec3 sky = fresnel * u_sky_color;"
" float diffuse = clamp(dot(normal, normalize(u_sun_direction)), 0.0, 1.0);"
" vec3 water = (1.0 - fresnel) * u_ocean_color * u_sky_color * diffuse;"
" vec3 color = sky + water;"
" fragcolor = vec4(hdr(color, u_exposure), 1.0);"
"}"
"";
class WaterGraphics {
public:
WaterGraphics();
~WaterGraphics();
bool setup(int winW, int winH);
public:
int win_w;
int win_h;
// Fullscreen
GLuint fullscreen_vao;
GLuint fullscreen_vs;
GLuint fullscreen_fs;
GLuint fullscreen_prog;
/* Sub transform */
GLuint hor_subtrans_fs;
GLuint hor_subtrans_prog;
GLuint vert_subtrans_fs;
GLuint vert_subtrans_prog;
/* Initial spectrum */
GLuint init_spectrum_fs;
GLuint init_spectrum_prog;
/* Phase */
GLuint phase_fs;
GLuint phase_prog;
/* Spectrum */
GLuint spectrum_fs;
GLuint spectrum_prog;
/* Normal map */
GLuint normal_map_fs;
GLuint normal_map_prog;
/* Ocean */
GLuint ocean_vs;
GLuint ocean_fs;
GLuint ocean_prog;
mat4 pm; /* perspective matrix */
};
#endif
@roxlu
Copy link
Author

roxlu commented Dec 12, 2013

@roxlu
Copy link
Author

roxlu commented Dec 12, 2013

Screenshots of the individual buffers: http://imgur.com/a/eyksV

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment