diff options
| author | Miles Macklin <[email protected]> | 2017-03-10 14:51:31 +1300 |
|---|---|---|
| committer | Miles Macklin <[email protected]> | 2017-03-10 14:51:31 +1300 |
| commit | ad3d90fafe5ee79964bdfe1f1e0704c3ffcdfd5f (patch) | |
| tree | 4cc6f3288363889d7342f7f8407c0251e6904819 /demo/opengl | |
| download | flex-ad3d90fafe5ee79964bdfe1f1e0704c3ffcdfd5f.tar.xz flex-ad3d90fafe5ee79964bdfe1f1e0704c3ffcdfd5f.zip | |
Initial 1.1.0 binary release
Diffstat (limited to 'demo/opengl')
| -rw-r--r-- | demo/opengl/imguiRenderGL.cpp | 484 | ||||
| -rw-r--r-- | demo/opengl/imguiRenderGL.h | 26 | ||||
| -rw-r--r-- | demo/opengl/shader.cpp | 248 | ||||
| -rw-r--r-- | demo/opengl/shader.h | 84 | ||||
| -rw-r--r-- | demo/opengl/shadersGL.cpp | 2772 |
5 files changed, 3614 insertions, 0 deletions
diff --git a/demo/opengl/imguiRenderGL.cpp b/demo/opengl/imguiRenderGL.cpp new file mode 100644 index 0000000..bfb961e --- /dev/null +++ b/demo/opengl/imguiRenderGL.cpp @@ -0,0 +1,484 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen [email protected] +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#define _USE_MATH_DEFINES +#include <math.h> +#include <stdio.h> + +#include "../imgui.h" + +#include "shader.h" + +// Some math headers don't have PI defined. +static const float PI = 3.14159265f; + +void imguifree(void* ptr, void* userptr); +void* imguimalloc(size_t size, void* userptr); + +#define STBTT_malloc(x,y) imguimalloc(x,y) +#define STBTT_free(x,y) imguifree(x,y) +#define STB_TRUETYPE_IMPLEMENTATION +#include "../stb_truetype.h" + +void imguifree(void* ptr, void* /*userptr*/) +{ + free(ptr); +} + +void* imguimalloc(size_t size, void* /*userptr*/) +{ + return malloc(size); +} + +static const unsigned TEMP_COORD_COUNT = 100; +static float g_tempCoords[TEMP_COORD_COUNT*2]; +static float g_tempNormals[TEMP_COORD_COUNT*2]; + +static const int CIRCLE_VERTS = 8*4; +static float g_circleVerts[CIRCLE_VERTS*2]; + +static stbtt_bakedchar g_cdata[96]; // ASCII 32..126 is 95 glyphs +static GLuint g_ftex = 0; + +inline unsigned int RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return (r) | (g << 8) | (b << 16) | (a << 24); +} + +static void drawPolygon(const float* coords, unsigned numCoords, float r, unsigned int col) +{ + if (numCoords > TEMP_COORD_COUNT) numCoords = TEMP_COORD_COUNT; + + for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++) + { + const float* v0 = &coords[j*2]; + const float* v1 = &coords[i*2]; + float dx = v1[0] - v0[0]; + float dy = v1[1] - v0[1]; + float d = sqrtf(dx*dx+dy*dy); + if (d > 0) + { + d = 1.0f/d; + dx *= d; + dy *= d; + } + g_tempNormals[j*2+0] = dy; + g_tempNormals[j*2+1] = -dx; + } + + for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++) + { + float dlx0 = g_tempNormals[j*2+0]; + float dly0 = g_tempNormals[j*2+1]; + float dlx1 = g_tempNormals[i*2+0]; + float dly1 = g_tempNormals[i*2+1]; + float dmx = (dlx0 + dlx1) * 0.5f; + float dmy = (dly0 + dly1) * 0.5f; + float dmr2 = dmx*dmx + dmy*dmy; + if (dmr2 > 0.000001f) + { + float scale = 1.0f / dmr2; + if (scale > 10.0f) scale = 10.0f; + dmx *= scale; + dmy *= scale; + } + g_tempCoords[i*2+0] = coords[i*2+0]+dmx*r; + g_tempCoords[i*2+1] = coords[i*2+1]+dmy*r; + } + + unsigned int colTrans = RGBA(col&0xff, (col>>8)&0xff, (col>>16)&0xff, 0); + + glBegin(GL_TRIANGLES); + + glColor4ubv((GLubyte*)&col); + + for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++) + { + glVertex2fv(&coords[i*2]); + glVertex2fv(&coords[j*2]); + glColor4ubv((GLubyte*)&colTrans); + glVertex2fv(&g_tempCoords[j*2]); + + glVertex2fv(&g_tempCoords[j*2]); + glVertex2fv(&g_tempCoords[i*2]); + + glColor4ubv((GLubyte*)&col); + glVertex2fv(&coords[i*2]); + } + + glColor4ubv((GLubyte*)&col); + for (unsigned i = 2; i < numCoords; ++i) + { + glVertex2fv(&coords[0]); + glVertex2fv(&coords[(i-1)*2]); + glVertex2fv(&coords[i*2]); + } + + glEnd(); +} + +static void drawRect(float x, float y, float w, float h, float fth, unsigned int col) +{ + float verts[4*2] = + { + x+0.5f, y+0.5f, + x+w-0.5f, y+0.5f, + x+w-0.5f, y+h-0.5f, + x+0.5f, y+h-0.5f, + }; + drawPolygon(verts, 4, fth, col); +} + +/* +static void drawEllipse(float x, float y, float w, float h, float fth, unsigned int col) +{ + float verts[CIRCLE_VERTS*2]; + const float* cverts = g_circleVerts; + float* v = verts; + + for (int i = 0; i < CIRCLE_VERTS; ++i) + { + *v++ = x + cverts[i*2]*w; + *v++ = y + cverts[i*2+1]*h; + } + + drawPolygon(verts, CIRCLE_VERTS, fth, col); +} +*/ + +static void drawRoundedRect(float x, float y, float w, float h, float r, float fth, unsigned int col) +{ + const unsigned n = CIRCLE_VERTS/4; + float verts[(n+1)*4*2]; + const float* cverts = g_circleVerts; + float* v = verts; + + for (unsigned i = 0; i <= n; ++i) + { + *v++ = x+w-r + cverts[i*2]*r; + *v++ = y+h-r + cverts[i*2+1]*r; + } + + for (unsigned i = n; i <= n*2; ++i) + { + *v++ = x+r + cverts[i*2]*r; + *v++ = y+h-r + cverts[i*2+1]*r; + } + + for (unsigned i = n*2; i <= n*3; ++i) + { + *v++ = x+r + cverts[i*2]*r; + *v++ = y+r + cverts[i*2+1]*r; + } + + for (unsigned i = n*3; i < n*4; ++i) + { + *v++ = x+w-r + cverts[i*2]*r; + *v++ = y+r + cverts[i*2+1]*r; + } + *v++ = x+w-r + cverts[0]*r; + *v++ = y+r + cverts[1]*r; + + drawPolygon(verts, (n+1)*4, fth, col); +} + + +static void drawLine(float x0, float y0, float x1, float y1, float r, float fth, unsigned int col) +{ + float dx = x1-x0; + float dy = y1-y0; + float d = sqrtf(dx*dx+dy*dy); + if (d > 0.0001f) + { + d = 1.0f/d; + dx *= d; + dy *= d; + } + float nx = dy; + float ny = -dx; + float verts[4*2]; + r -= fth; + r *= 0.5f; + if (r < 0.01f) r = 0.01f; + dx *= r; + dy *= r; + nx *= r; + ny *= r; + + verts[0] = x0-dx-nx; + verts[1] = y0-dy-ny; + + verts[2] = x0-dx+nx; + verts[3] = y0-dy+ny; + + verts[4] = x1+dx+nx; + verts[5] = y1+dy+ny; + + verts[6] = x1+dx-nx; + verts[7] = y1+dy-ny; + + drawPolygon(verts, 4, fth, col); +} + + +bool imguiRenderGLInit(const char* fontpath) +{ + for (int i = 0; i < CIRCLE_VERTS; ++i) + { + float a = (float)i / (float)CIRCLE_VERTS * PI * 2; + g_circleVerts[i * 2 + 0] = cosf(a); + g_circleVerts[i * 2 + 1] = sinf(a); + } + + // Load font. + FILE* fp = fopen(fontpath, "rb"); + if (!fp) return false; + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + unsigned char* ttfBuffer = (unsigned char*)malloc(size); + if (!ttfBuffer) + { + fclose(fp); + return false; + } + + size_t len = fread(ttfBuffer, 1, size, fp); + (void)len; + + fclose(fp); + fp = 0; + + unsigned char* bmap = (unsigned char*)malloc(512 * 512); + if (!bmap) + { + free(ttfBuffer); + return false; + } + + stbtt_BakeFontBitmap(ttfBuffer, 0, 15.0f, bmap, 512, 512, 32, 96, g_cdata); + + // can free ttf_buffer at this point + glVerify(glGenTextures(1, &g_ftex)); + glVerify(glBindTexture(GL_TEXTURE_2D, g_ftex)); + glVerify(glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512, 512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bmap)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + + free(ttfBuffer); + free(bmap); + + return true; +} + +void imguiRenderGLDestroy() +{ + if (g_ftex) + { + glDeleteTextures(1, &g_ftex); + g_ftex = 0; + } +} + +static void getBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, + float *xpos, float *ypos, stbtt_aligned_quad *q) +{ + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor(*xpos + b->xoff); + int round_y = STBTT_ifloor(*ypos - b->yoff); + + q->x0 = (float)round_x; + q->y0 = (float)round_y; + q->x1 = (float)round_x + b->x1 - b->x0; + q->y1 = (float)round_y - b->y1 + b->y0; + + q->s0 = b->x0 / (float)pw; + q->t0 = b->y0 / (float)pw; + q->s1 = b->x1 / (float)ph; + q->t1 = b->y1 / (float)ph; + + *xpos += b->xadvance; +} + +static const float g_tabStops[4] = {150, 210, 270, 330}; + +static float getTextLength(stbtt_bakedchar *chardata, const char* text) +{ + float xpos = 0; + float len = 0; + while (*text) + { + int c = (unsigned char)*text; + if (c == '\t') + { + for (int i = 0; i < 4; ++i) + { + if (xpos < g_tabStops[i]) + { + xpos = g_tabStops[i]; + break; + } + } + } + else if (c >= 32 && c < 128) + { + stbtt_bakedchar *b = chardata + c-32; + int round_x = STBTT_ifloor((xpos + b->xoff) + 0.5); + len = round_x + b->x1 - b->x0 + 0.5f; + xpos += b->xadvance; + } + ++text; + } + return len; +} + +static void drawText(float x, float y, const char *text, int align, unsigned int col) +{ + if (!g_ftex) return; + if (!text) return; + + if (align == IMGUI_ALIGN_CENTER) + x -= getTextLength(g_cdata, text)/2; + else if (align == IMGUI_ALIGN_RIGHT) + x -= getTextLength(g_cdata, text); + + glColor4ub(col&0xff, (col>>8)&0xff, (col>>16)&0xff, (col>>24)&0xff); + + glEnable(GL_TEXTURE_2D); + + // assume orthographic projection with units = screen pixels, origin at top left + glBindTexture(GL_TEXTURE_2D, g_ftex); + + glBegin(GL_TRIANGLES); + + const float ox = x; + + while (*text) + { + int c = (unsigned char)*text; + if (c == '\t') + { + for (int i = 0; i < 4; ++i) + { + if (x < g_tabStops[i]+ox) + { + x = g_tabStops[i]+ox; + break; + } + } + } + else if (c >= 32 && c < 128) + { + stbtt_aligned_quad q; + getBakedQuad(g_cdata, 512,512, c-32, &x,&y,&q); + + glTexCoord2f(q.s0, q.t0); + glVertex2f(q.x0, q.y0); + glTexCoord2f(q.s1, q.t1); + glVertex2f(q.x1, q.y1); + glTexCoord2f(q.s1, q.t0); + glVertex2f(q.x1, q.y0); + + glTexCoord2f(q.s0, q.t0); + glVertex2f(q.x0, q.y0); + glTexCoord2f(q.s0, q.t1); + glVertex2f(q.x0, q.y1); + glTexCoord2f(q.s1, q.t1); + glVertex2f(q.x1, q.y1); + } + ++text; + } + + glEnd(); + glDisable(GL_TEXTURE_2D); +} + + +void imguiRenderGLDraw() +{ + const imguiGfxCmd* q = imguiGetRenderQueue(); + int nq = imguiGetRenderQueueSize(); + + const float s = 1.0f/8.0f; + + glDisable(GL_SCISSOR_TEST); + for (int i = 0; i < nq; ++i) + { + const imguiGfxCmd& cmd = q[i]; + if (cmd.type == IMGUI_GFXCMD_RECT) + { + if (cmd.rect.r == 0) + { + drawRect((float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f, + (float)cmd.rect.w*s-1, (float)cmd.rect.h*s-1, + 1.0f, cmd.col); + } + else + { + drawRoundedRect((float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f, + (float)cmd.rect.w*s-1, (float)cmd.rect.h*s-1, + (float)cmd.rect.r*s, 1.0f, cmd.col); + } + } + else if (cmd.type == IMGUI_GFXCMD_LINE) + { + drawLine(cmd.line.x0*s, cmd.line.y0*s, cmd.line.x1*s, cmd.line.y1*s, cmd.line.r*s, 1.0f, cmd.col); + } + else if (cmd.type == IMGUI_GFXCMD_TRIANGLE) + { + if (cmd.flags == 1) + { + const float verts[3*2] = + { + (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f, + (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s-1, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s/2-0.5f, + (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1, + }; + drawPolygon(verts, 3, 1.0f, cmd.col); + } + if (cmd.flags == 2) + { + const float verts[3*2] = + { + (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1, + (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s/2-0.5f, (float)cmd.rect.y*s+0.5f, + (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s-1, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1, + }; + drawPolygon(verts, 3, 1.0f, cmd.col); + } + } + else if (cmd.type == IMGUI_GFXCMD_TEXT) + { + drawText(cmd.text.x, cmd.text.y, cmd.text.text, cmd.text.align, cmd.col); + } + else if (cmd.type == IMGUI_GFXCMD_SCISSOR) + { + if (cmd.flags) + { + glEnable(GL_SCISSOR_TEST); + glScissor(cmd.rect.x, cmd.rect.y, cmd.rect.w, cmd.rect.h); + } + else + { + glDisable(GL_SCISSOR_TEST); + } + } + } + glDisable(GL_SCISSOR_TEST); +} diff --git a/demo/opengl/imguiRenderGL.h b/demo/opengl/imguiRenderGL.h new file mode 100644 index 0000000..b148341 --- /dev/null +++ b/demo/opengl/imguiRenderGL.h @@ -0,0 +1,26 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen [email protected] +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef IMGUI_RENDER_GL_H +#define IMGUI_RENDER_GL_H + +bool imguiRenderGLInit(const char* fontpath); +void imguiRenderGLDestroy(); +void imguiRenderGLDraw(); + +#endif // IMGUI_RENDER_GL_H
\ No newline at end of file diff --git a/demo/opengl/shader.cpp b/demo/opengl/shader.cpp new file mode 100644 index 0000000..3c7640f --- /dev/null +++ b/demo/opengl/shader.cpp @@ -0,0 +1,248 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 20132017 NVIDIA Corporation. All rights reserved. + +#include "shader.h" + +#include "../../core/types.h" +#include "../../core/maths.h" +#include "../../core/platform.h" +#include "../../core/tga.h" + +#include <stdarg.h> +#include <stdio.h> + +#define WITH_GLEW + +void GlslPrintShaderLog(GLuint obj) +{ +#if !PLATFORM_IOS + int infologLength = 0; + int charsWritten = 0; + char *infoLog; + + GLint result; + glGetShaderiv(obj, GL_COMPILE_STATUS, &result); + + // only print log if compile fails + if (result == GL_FALSE) + { + glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength); + + if (infologLength > 1) + { + infoLog = (char *)malloc(infologLength); + glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog); + printf("%s\n",infoLog); + free(infoLog); + } + } +#endif +} + +void glAssert(const char* msg, long line, const char* file) +{ + struct glError + { + GLenum code; + const char* name; + }; + + static const glError errors[] = { {GL_NO_ERROR, "No Error"}, + {GL_INVALID_ENUM, "Invalid Enum"}, + {GL_INVALID_VALUE, "Invalid Value"}, + {GL_INVALID_OPERATION, "Invalid Operation"} +#if OGL1 + ,{GL_STACK_OVERFLOW, "Stack Overflow"}, + {GL_STACK_UNDERFLOW, "Stack Underflow"}, + {GL_OUT_OF_MEMORY, "Out Of Memory"} +#endif + }; + + GLenum e = glGetError(); + + if (e == GL_NO_ERROR) + { + return; + } + else + { + const char* errorName = "Unknown error"; + + // find error message + for (uint32_t i=0; i < sizeof(errors)/sizeof(glError); i++) + { + if (errors[i].code == e) + { + errorName = errors[i].name; + } + } + + printf("OpenGL: %s - error %s in %s at line %d\n", msg, errorName, file, int(line)); + assert(0); + } +} + +void PreProcessShader(const char* filename, std::string& source) +{ + // load source + FILE* f = fopen(filename, "r"); + + if (!f) + { + printf("Could not open shader file for reading: %s\n", filename); + return; + } + + // add lines one at a time handling include files recursively + while (!feof(f)) + { + char buf[1024]; + + if (fgets(buf, 1024, f) != NULL) + { + // test for #include + if (strncmp(buf, "#include", 8) == 0) + { + const char* begin = strchr(buf, '\"'); + const char* end = strrchr(buf, '\"'); + + if (begin && end && (begin != end)) + { + // lookup file relative to current file + PreProcessShader((StripFilename(filename) + std::string(begin+1, end)).c_str(), source); + } + } + else + { + // add line to output + source += buf; + } + } + } + + fclose(f); +} + +GLuint CompileProgram(const char *vsource, const char *fsource, const char* gsource) +{ + GLuint vertexShader = GLuint(-1); + GLuint geometryShader = GLuint(-1); + GLuint fragmentShader = GLuint(-1); + + GLuint program = glCreateProgram(); + + if (vsource) + { + vertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertexShader, 1, &vsource, 0); + glCompileShader(vertexShader); + GlslPrintShaderLog(vertexShader); + glAttachShader(program, vertexShader); + } + + if (fsource) + { + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragmentShader, 1, &fsource, 0); + glCompileShader(fragmentShader); + GlslPrintShaderLog(fragmentShader); + glAttachShader(program, fragmentShader); + } + + if (gsource) + { + geometryShader = glCreateShader(GL_GEOMETRY_SHADER); + glShaderSource(geometryShader, 1, &gsource, 0); + glCompileShader(geometryShader); + GlslPrintShaderLog(geometryShader); + + // hack, force billboard gs mode + glAttachShader(program, geometryShader); + glProgramParameteriEXT ( program, GL_GEOMETRY_VERTICES_OUT_EXT, 4 ) ; + glProgramParameteriEXT ( program, GL_GEOMETRY_INPUT_TYPE_EXT, GL_POINTS ) ; + glProgramParameteriEXT ( program, GL_GEOMETRY_OUTPUT_TYPE_EXT, GL_TRIANGLE_STRIP ) ; + } + + glLinkProgram(program); + + // check if program linked + GLint success = 0; + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (!success) { + char temp[256]; + glGetProgramInfoLog(program, 256, 0, temp); + printf("Failed to link program:\n%s\n", temp); + glDeleteProgram(program); + program = 0; + } + + return program; +} + +void DrawPlane(const Vec4& p, bool color) +{ + Vec3 u, v; + BasisFromVector(Vec3(p.x, p.y, p.z), &u, &v); + + Vec3 c = Vec3(p.x, p.y, p.z)*-p.w; + + glBegin(GL_QUADS); + + if (color) + glColor3fv(p*0.5f + Vec4(0.5f, 0.5f, 0.5f, 0.5f)); + + float kSize = 200.0f; + + // draw a grid of quads, otherwise z precision suffers + for (int x = -3; x <= 3; ++x) + { + for (int y = -3; y <= 3; ++y) + { + Vec3 coff = c + u*float(x)*kSize*2.0f + v*float(y)*kSize*2.0f; + + glTexCoord2f(1.0f, 1.0f); + glNormal3f(p.x, p.y, p.z); + glVertex3fv(coff + u*kSize + v*kSize); + + glTexCoord2f(0.0f, 1.0f); + glNormal3f(p.x, p.y, p.z); + glVertex3fv(coff - u*kSize + v*kSize); + + glTexCoord2f(0.0f, 0.0f); + glNormal3f(p.x, p.y, p.z); + glVertex3fv(coff - u*kSize - v*kSize); + + glTexCoord2f(1.0f, 0.0f); + glNormal3f(p.x, p.y, p.z); + glVertex3fv(coff + u*kSize - v*kSize); + } + } + + glEnd(); +} + diff --git a/demo/opengl/shader.h b/demo/opengl/shader.h new file mode 100644 index 0000000..dd9468a --- /dev/null +++ b/demo/opengl/shader.h @@ -0,0 +1,84 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 20132017 NVIDIA Corporation. All rights reserved. + +#pragma once + +#include "../../core/maths.h" + +#if _WIN32 + +#define ENABLE_SIMPLE_FLUID 0 + +#include "../../external/glew/include/gl/glew.h" +#include "../../external/SDL2-2.0.4/include/SDL.h" + +#pragma comment (lib, "glu32.lib") /* link OpenGL Utility lib */ +#pragma comment (lib, "opengl32.lib") /* link Microsoft OpenGL lib */ + +#include <gl/GL.h> +#include <gl/GLU.h> + +// Begin Add Android Support +#elif ANDROID +#include <GL/Regal.h> +// End Add Android Support + +#elif __linux__ +#include <external/glew/include/GL/glew.h> +#include <GL/gl.h> +#include <GL/freeglut.h> + +#elif __APPLE__ +#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED +#include <opengl/gl3.h> +#include <glut/glut.h> +#elif PLATFORM_IOS + +#if OGL1 +#import <OpenGLES/EAGL.h> +#import <OpenGLES/ES1/gl.h> +#import <OpenGLES/ES1/glext.h> +#else +#import <OpenGLES/ES2/gl.h> +#import <OpenGLES/ES2/glext.h> +#endif + +#endif + +#include <vector> + +#if defined(NDEBUG) +#define glVerify(x) x +#else +#define glVerify(x) {x; glAssert(#x, __LINE__, __FILE__);} +void glAssert(const char* msg, long line, const char* file); +#endif + +GLuint CompileProgram(const char *vsource=NULL, const char *fsource=NULL, const char* gsource=NULL); + +void DrawPlane(const Vec4& p, bool color=true); + diff --git a/demo/opengl/shadersGL.cpp b/demo/opengl/shadersGL.cpp new file mode 100644 index 0000000..b4023db --- /dev/null +++ b/demo/opengl/shadersGL.cpp @@ -0,0 +1,2772 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 20132017 NVIDIA Corporation. All rights reserved. + +#include "../shaders.h" + +#include "../../core/mesh.h" +#include "../../core/tga.h" +#include "../../core/platform.h" +#include "../../core/extrude.h" + +#include "../../external/SDL2-2.0.4/include/SDL.h" + +#include "imguiRenderGL.h" + +#include "shader.h" + +#ifdef ANDROID +#include "android/Log.h" +#include "android/AndroidDefine.h" +#include "android/AndroidMatrixTool.h" +#endif + +#define CudaCheck(x) { cudaError_t err = x; if (err != cudaSuccess) { printf("Cuda error: %d in %s at %s:%d\n", err, #x, __FILE__, __LINE__); assert(0); } } + +namespace +{ + +int g_msaaSamples; +GLuint g_msaaFbo; +GLuint g_msaaColorBuf; +GLuint g_msaaDepthBuf; + +int g_screenWidth; +int g_screenHeight; + +SDL_Window* g_window; + +static float gSpotMin = 0.5f; +static float gSpotMax = 1.0f; +float gShadowBias = 0.05f; + +} // anonymous namespace + +Colour gColors[] = +{ + Colour(0.0f, 0.5f, 1.0f), + Colour(0.797f, 0.354f, 0.000f), + Colour(0.092f, 0.465f, 0.820f), + Colour(0.000f, 0.349f, 0.173f), + Colour(0.875f, 0.782f, 0.051f), + Colour(0.000f, 0.170f, 0.453f), + Colour(0.673f, 0.111f, 0.000f), + Colour(0.612f, 0.194f, 0.394f) +}; + + +struct ShadowMap +{ + GLuint texture; + GLuint framebuffer; +}; + + +void InitRender(SDL_Window* window, bool fullscreen, int msaaSamples) +{ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + + //SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); + + // Turn on double buffering with a 24bit Z buffer. + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + + SDL_GL_CreateContext(window); + + // This makes our buffer swap syncronized with the monitor's vertical refresh + SDL_GL_SetSwapInterval(1); + + glewExperimental = GL_TRUE; + glewInit(); + + imguiRenderGLInit(GetFilePathByPlatform("../../data/DroidSans.ttf").c_str()); + + g_msaaSamples = msaaSamples; + g_window = window; +} + +void DestroyRender() +{ + +} + +void StartFrame(Vec4 clearColor) +{ + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glDisable(GL_LIGHTING); + glDisable(GL_BLEND); + + glPointSize(5.0f); + + glVerify(glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, g_msaaFbo)); + glVerify(glClearColor(powf(clearColor.x, 1.0f / 2.2f), powf(clearColor.y, 1.0f / 2.2f), powf(clearColor.z, 1.0f / 2.2f), 0.0f)); + glVerify(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + + +} + +void EndFrame() +{ + if (g_msaaFbo) + { + // blit the msaa buffer to the window + glVerify(glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, g_msaaFbo)); + glVerify(glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0)); + glVerify(glBlitFramebuffer(0, 0, g_screenWidth, g_screenHeight, 0, 0, g_screenWidth, g_screenHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR)); + } + + // render help to back buffer + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + glVerify(glClear(GL_DEPTH_BUFFER_BIT)); + +} + +void SetView(Matrix44 view, Matrix44 proj) +{ + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(proj); + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(view); +} + +void SetFillMode(bool wireframe) +{ + glPolygonMode(GL_FRONT_AND_BACK, wireframe?GL_LINE:GL_FILL); +} + +void SetCullMode(bool enabled) +{ + if (enabled) + glEnable(GL_CULL_FACE); + else + glDisable(GL_CULL_FACE); +} + + +void imguiGraphDraw() +{ + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + glActiveTexture(GL_TEXTURE0); + glDisable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_RECTANGLE_ARB); + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE2); + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE3); + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE4); + glDisable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_CUBE_MAP); + glActiveTexture(GL_TEXTURE5); + glDisable(GL_TEXTURE_2D); + + glActiveTexture(GL_TEXTURE0); + + glDisable(GL_BLEND); + glDisable(GL_LIGHTING); + glDisable(GL_BLEND); + glDisable(GL_POINT_SPRITE); + + // save scene camera transform + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + const Matrix44 ortho = OrthographicMatrix(0.0f, float(g_screenWidth), 0.0f, float(g_screenHeight), -1.0f, 1.0f); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(ortho); + + glUseProgram(0); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_TEXTURE_2D); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + imguiRenderGLDraw(); + + // restore camera transform (for picking) + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); +} + +void ReshapeRender(SDL_Window* window) +{ + int width, height; + SDL_GetWindowSize(window, &width, &height); + + if (g_msaaSamples) + { + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + + if (g_msaaFbo) + { + glVerify(glDeleteFramebuffers(1, &g_msaaFbo)); + glVerify(glDeleteRenderbuffers(1, &g_msaaColorBuf)); + glVerify(glDeleteRenderbuffers(1, &g_msaaDepthBuf)); + } + + int samples; + glGetIntegerv(GL_MAX_SAMPLES_EXT, &samples); + + // clamp samples to 4 to avoid problems with point sprite scaling + samples = Min(samples, Min(g_msaaSamples, 4)); + + glVerify(glGenFramebuffers(1, &g_msaaFbo)); + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, g_msaaFbo)); + + glVerify(glGenRenderbuffers(1, &g_msaaColorBuf)); + glVerify(glBindRenderbuffer(GL_RENDERBUFFER, g_msaaColorBuf)); + glVerify(glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8, width, height)); + + glVerify(glGenRenderbuffers(1, &g_msaaDepthBuf)); + glVerify(glBindRenderbuffer(GL_RENDERBUFFER, g_msaaDepthBuf)); + glVerify(glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH_COMPONENT, width, height)); + glVerify(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, g_msaaDepthBuf)); + + glVerify(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, g_msaaColorBuf)); + + glVerify(glCheckFramebufferStatus(GL_FRAMEBUFFER)); + + glEnable(GL_MULTISAMPLE); + } + + g_screenWidth = width; + g_screenHeight = height; +} + +void GetViewRay(int x, int y, Vec3& origin, Vec3& dir) +{ + double modelview[16]; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview); + + double projection[16]; + glGetDoublev(GL_PROJECTION_MATRIX, projection); + + int viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + + double nearPos[3]; +// Begin Add Android Support +#ifdef ANDROID + glhUnProjectf(double(x), double(y), 0.0f, modelview, projection, viewport, nearPos); +#else + gluUnProject(double(x), double(y), 0.0f, modelview, projection, viewport, &nearPos[0], &nearPos[1], &nearPos[2]); +#endif +// End Add Android Support + + double farPos[3]; +// Begin Add Android Support +#ifdef ANDROID + glhUnProjectf(double(x), double(y), 1.0f, modelview, projection, viewport, farPos); +#else + gluUnProject(double(x), double(y), 1.0f, modelview, projection, viewport, &farPos[0], &farPos[1], &farPos[2]); +#endif +// End Add Android Support + + origin = Vec3(float(nearPos[0]), float(nearPos[1]), float(nearPos[2])); + dir = Normalize(Vec3(float(farPos[0]-nearPos[0]), float(farPos[1]-nearPos[1]), float(farPos[2]-nearPos[2]))); +} + +void ReadFrame(int* backbuffer, int width, int height) +{ + glVerify(glReadBuffer(GL_BACK)); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, backbuffer); +} + +void PresentFrame(bool fullsync) +{ +#ifndef ANDROID + SDL_GL_SetSwapInterval(fullsync); + glFinish(); + SDL_GL_SwapWindow(g_window); +#endif + +} + + +// fixes some banding artifacts with repeated blending during thickness and diffuse rendering +#define USE_HDR_DIFFUSE_BLEND 0 + +// vertex shader +const char *vertexPointShader = "#version 130\n" STRINGIFY( + +uniform float pointRadius; // point size in world space +uniform float pointScale; // scale to calculate size in pixels + +uniform mat4 lightTransform; +uniform vec3 lightDir; +uniform vec3 lightDirView; + +uniform vec4 colors[8]; + +uniform vec4 transmission; +uniform int mode; + +//in int density; +in float density; +in int phase; +in vec4 velocity; + +void main() +{ + // calculate window-space point size + vec4 viewPos = gl_ModelViewMatrix*vec4(gl_Vertex.xyz, 1.0); + + gl_Position = gl_ModelViewProjectionMatrix * vec4(gl_Vertex.xyz, 1.0); + gl_PointSize = -pointScale * (pointRadius / viewPos.z); + + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = lightTransform*vec4(gl_Vertex.xyz-lightDir*pointRadius*2.0, 1.0); + gl_TexCoord[2] = gl_ModelViewMatrix*vec4(lightDir, 0.0); + + if (mode == 1) + { + // density visualization + if (density < 0.0f) + gl_TexCoord[3].xyz = mix(vec3(0.1, 0.1, 1.0), vec3(0.1, 1.0, 1.0), -density); + else + gl_TexCoord[3].xyz = mix(vec3(1.0, 1.0, 1.0), vec3(0.1, 0.2, 1.0), density); + } + else if (mode == 2) + { + gl_PointSize *= clamp(gl_Vertex.w*0.25, 0.0f, 1.0); + + gl_TexCoord[3].xyzw = vec4(clamp(gl_Vertex.w*0.05, 0.0f, 1.0)); + } + else + { + gl_TexCoord[3].xyz = mix(colors[phase % 8].xyz*2.0, vec3(1.0), 0.1); + } + + gl_TexCoord[4].xyz = gl_Vertex.xyz; + gl_TexCoord[5].xyz = viewPos.xyz; +} +); + +// pixel shader for rendering points as shaded spheres +const char *fragmentPointShader = STRINGIFY( + +uniform vec3 lightDir; +uniform vec3 lightPos; +uniform float spotMin; +uniform float spotMax; +uniform int mode; + +uniform sampler2DShadow shadowTex; +uniform vec2 shadowTaps[12]; +uniform float pointRadius; // point size in world space + +// sample shadow map +float shadowSample() +{ + vec3 pos = vec3(gl_TexCoord[1].xyz/gl_TexCoord[1].w); + vec3 uvw = (pos.xyz*0.5)+vec3(0.5); + + // user clip + if (uvw.x < 0.0 || uvw.x > 1.0) + return 1.0; + if (uvw.y < 0.0 || uvw.y > 1.0) + return 1.0; + + float s = 0.0; + float radius = 0.002; + + for (int i=0; i < 8; i++) + { + s += shadow2D(shadowTex, vec3(uvw.xy + shadowTaps[i]*radius, uvw.z)).r; + } + + s /= 8.0; + return s; +} + +float sqr(float x) { return x*x; } + +void main() +{ + // calculate normal from texture coordinates + vec3 normal; + normal.xy = gl_TexCoord[0].xy*vec2(2.0, -2.0) + vec2(-1.0, 1.0); + float mag = dot(normal.xy, normal.xy); + if (mag > 1.0) discard; // kill pixels outside circle + normal.z = sqrt(1.0-mag); + + if (mode == 2) + { + float alpha = normal.z*gl_TexCoord[3].w; + gl_FragColor.xyz = gl_TexCoord[3].xyz*alpha; + gl_FragColor.w = alpha; + return; + } + + // calculate lighting + float shadow = shadowSample(); + + vec3 lVec = normalize(gl_TexCoord[4].xyz-(lightPos)); + vec3 lPos = vec3(gl_TexCoord[1].xyz/gl_TexCoord[1].w); + float attenuation = max(smoothstep(spotMax, spotMin, dot(lPos.xy, lPos.xy)), 0.05); + + vec3 diffuse = vec3(0.9, 0.9, 0.9); + vec3 reflectance = gl_TexCoord[3].xyz; + + vec3 Lo = diffuse*reflectance*max(0.0, sqr(-dot(gl_TexCoord[2].xyz, normal)*0.5 + 0.5))*max(0.2,shadow)*attenuation; + + gl_FragColor = vec4(pow(Lo, vec3(1.0/2.2)), 1.0); + + vec3 eyePos = gl_TexCoord[5].xyz + normal*pointRadius;//*2.0; + vec4 ndcPos = gl_ProjectionMatrix * vec4(eyePos, 1.0); + ndcPos.z /= ndcPos.w; + gl_FragDepth = ndcPos.z*0.5 + 0.5; +} +); + +// vertex shader +const char *vertexShader = "#version 130\n" STRINGIFY( + +uniform mat4 lightTransform; +uniform vec3 lightDir; +uniform float bias; +uniform vec4 clipPlane; +uniform float expand; + +uniform mat4 objectTransform; + +void main() +{ + vec3 n = normalize((objectTransform*vec4(gl_Normal, 0.0)).xyz); + vec3 p = (objectTransform*vec4(gl_Vertex.xyz, 1.0)).xyz; + + // calculate window-space point size + gl_Position = gl_ModelViewProjectionMatrix * vec4(p + expand*n, 1.0); + + gl_TexCoord[0].xyz = n; + gl_TexCoord[1] = lightTransform*vec4(p + n*bias, 1.0); + gl_TexCoord[2] = gl_ModelViewMatrix*vec4(lightDir, 0.0); + gl_TexCoord[3].xyz = p; + gl_TexCoord[4] = gl_Color; + gl_TexCoord[5] = gl_MultiTexCoord0; + gl_TexCoord[6] = gl_SecondaryColor; + gl_TexCoord[7] = gl_ModelViewMatrix*vec4(gl_Vertex.xyz, 1.0); + + gl_ClipDistance[0] = dot(clipPlane,vec4(gl_Vertex.xyz, 1.0)); +} +); + +const char *passThroughShader = STRINGIFY( + +void main() +{ + gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); + +} +); + +// pixel shader for rendering points as shaded spheres +const char *fragmentShader = STRINGIFY( + +uniform vec3 lightDir; +uniform vec3 lightPos; +uniform float spotMin; +uniform float spotMax; +uniform vec3 color; +uniform vec4 fogColor; + +uniform sampler2DShadow shadowTex; +uniform vec2 shadowTaps[12]; + +uniform sampler2D tex; +uniform bool sky; + +uniform bool grid; +uniform bool texture; + +float sqr(float x) { return x*x; } + +// sample shadow map +float shadowSample() +{ + vec3 pos = vec3(gl_TexCoord[1].xyz/gl_TexCoord[1].w); + vec3 uvw = (pos.xyz*0.5)+vec3(0.5); + + // user clip + if (uvw.x < 0.0 || uvw.x > 1.0) + return 1.0; + if (uvw.y < 0.0 || uvw.y > 1.0) + return 1.0; + + float s = 0.0; + float radius = 0.002; + + const int numTaps = 12; + + for (int i=0; i < numTaps; i++) + { + s += shadow2D(shadowTex, vec3(uvw.xy + shadowTaps[i]*radius, uvw.z)).r; + } + + s /= numTaps; + return s; +} + +float filterwidth(vec2 v) +{ + vec2 fw = max(abs(dFdx(v)), abs(dFdy(v))); + return max(fw.x, fw.y); +} + +vec2 bump(vec2 x) +{ + return (floor((x)/2) + 2.f * max(((x)/2) - floor((x)/2) - .5f, 0.f)); +} + +float checker(vec2 uv) +{ + float width = filterwidth(uv); + vec2 p0 = uv - 0.5 * width; + vec2 p1 = uv + 0.5 * width; + + vec2 i = (bump(p1) - bump(p0)) / width; + return i.x * i.y + (1 - i.x) * (1 - i.y); +} + +void main() +{ + // calculate lighting + float shadow = max(shadowSample(), 0.5); + + vec3 lVec = normalize(gl_TexCoord[3].xyz-(lightPos)); + vec3 lPos = vec3(gl_TexCoord[1].xyz/gl_TexCoord[1].w); + float attenuation = max(smoothstep(spotMax, spotMin, dot(lPos.xy, lPos.xy)), 0.05); + + vec3 n = gl_TexCoord[0].xyz; + vec3 color = gl_TexCoord[4].xyz; + + if (!gl_FrontFacing) + { + color = gl_TexCoord[6].xyz; + n *= -1.0f; + } + + if (grid && (n.y >0.995)) + { + color *= 1.0 - 0.25 * checker(vec2(gl_TexCoord[3].x, gl_TexCoord[3].z)); + } + else if (grid && abs(n.z) > 0.995) + { + color *= 1.0 - 0.25 * checker(vec2(gl_TexCoord[3].y, gl_TexCoord[3].x)); + } + + if (texture) + { + color = texture2D(tex, gl_TexCoord[5].xy).xyz; + } + + // direct light term + float wrap = 0.0; + vec3 diffuse = color*vec3(1.0, 1.0, 1.0)*max(0.0, (-dot(lightDir, n)+wrap)/(1.0+wrap)*shadow)*attenuation; + + // wrap ambient term aligned with light dir + vec3 light = vec3(0.03, 0.025, 0.025)*1.5; + vec3 dark = vec3(0.025, 0.025, 0.03); + vec3 ambient = 4.0*color*mix(dark, light, -dot(lightDir, n)*0.5 + 0.5)*attenuation; + + vec3 fog = mix(vec3(fogColor), diffuse + ambient, exp(gl_TexCoord[7].z*fogColor.w)); + + gl_FragColor = vec4(pow(fog, vec3(1.0/2.2)), 1.0); +} +); + +void ShadowApply(GLint sprogram, Vec3 lightPos, Vec3 lightTarget, Matrix44 lightTransform, GLuint shadowTex) +{ + GLint uLightTransform = glGetUniformLocation(sprogram, "lightTransform"); + glUniformMatrix4fv(uLightTransform, 1, false, lightTransform); + + GLint uLightPos = glGetUniformLocation(sprogram, "lightPos"); + glUniform3fv(uLightPos, 1, lightPos); + + GLint uLightDir = glGetUniformLocation(sprogram, "lightDir"); + glUniform3fv(uLightDir, 1, Normalize(lightTarget-lightPos)); + + GLint uBias = glGetUniformLocation(sprogram, "bias"); + glUniform1f(uBias, gShadowBias); + + const Vec2 taps[] = + { + Vec2(-0.326212f,-0.40581f),Vec2(-0.840144f,-0.07358f), + Vec2(-0.695914f,0.457137f),Vec2(-0.203345f,0.620716f), + Vec2(0.96234f,-0.194983f),Vec2(0.473434f,-0.480026f), + Vec2(0.519456f,0.767022f),Vec2(0.185461f,-0.893124f), + Vec2(0.507431f,0.064425f),Vec2(0.89642f,0.412458f), + Vec2(-0.32194f,-0.932615f),Vec2(-0.791559f,-0.59771f) + }; + + GLint uShadowTaps = glGetUniformLocation(sprogram, "shadowTaps"); + glUniform2fv(uShadowTaps, 12, &taps[0].x); + + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, shadowTex); + +} + +void DrawPoints(GLuint positions, GLuint colors, GLuint indices, int n, int offset, float radius, float screenWidth, float screenAspect, float fov, Vec3 lightPos, Vec3 lightTarget, Matrix44 lightTransform, ShadowMap* shadowMap, bool showDensity) +{ + static int sprogram = -1; + if (sprogram == -1) + { + sprogram = CompileProgram(vertexPointShader, fragmentPointShader); + } + + if (sprogram) + { + glEnable(GL_POINT_SPRITE); + glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + //glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + + int mode = 0; + if (showDensity) + mode = 1; + if (shadowMap == NULL) + mode = 2; + + glVerify(glUseProgram(sprogram)); + glVerify(glUniform1f( glGetUniformLocation(sprogram, "pointRadius"), radius)); + glVerify(glUniform1f( glGetUniformLocation(sprogram, "pointScale"), screenWidth/screenAspect * (1.0f / (tanf(fov*0.5f))))); + glVerify(glUniform1f( glGetUniformLocation(sprogram, "spotMin"), gSpotMin)); + glVerify(glUniform1f( glGetUniformLocation(sprogram, "spotMax"), gSpotMax)); + glVerify(glUniform1i( glGetUniformLocation(sprogram, "mode"), mode)); + glVerify(glUniform4fv( glGetUniformLocation(sprogram, "colors"), 8, (float*)&gColors[0].r)); + + // set shadow parameters + ShadowApply(sprogram, lightPos, lightTarget, lightTransform, shadowMap->texture); + + glEnableClientState(GL_VERTEX_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, positions); + glVertexPointer(4, GL_FLOAT, 0, 0); + + int d = glGetAttribLocation(sprogram, "density"); + int p = glGetAttribLocation(sprogram, "phase"); + + if (d != -1) + { + glVerify(glEnableVertexAttribArray(d)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, colors)); + glVerify(glVertexAttribPointer(d, 1, GL_FLOAT, GL_FALSE, 0, 0)); // densities + } + + if (p != -1) + { + glVerify(glEnableVertexAttribArray(p)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, colors)); + glVerify(glVertexAttribIPointer(p, 1, GL_INT, 0, 0)); // phases + } + + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices)); + + glVerify(glDrawElements(GL_POINTS, n, GL_UNSIGNED_INT, (const void*)(offset*sizeof(int)))); + + glVerify(glUseProgram(0)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, 0)); + glVerify(glDisableClientState(GL_VERTEX_ARRAY)); + + if (d != -1) + glVerify(glDisableVertexAttribArray(d)); + if (p != -1) + glVerify(glDisableVertexAttribArray(p)); + + glDisable(GL_POINT_SPRITE); + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); + } +} +void DrawPlane(const Vec4& p); + +static GLuint s_diffuseProgram = GLuint(-1); +static GLuint s_shadowProgram = GLuint(-1); + +#ifdef ANDROID +void ResetProgramId() +{ + s_diffuseProgram = GLuint(-1); + s_shadowProgram = GLuint(-1); +} +#endif + +static const int kShadowResolution = 2048; + +ShadowMap* ShadowCreate() +{ + GLuint texture; + GLuint framebuffer; + + glVerify(glGenFramebuffers(1, &framebuffer)); + glVerify(glGenTextures(1, &texture)); + glVerify(glBindTexture(GL_TEXTURE_2D, texture)); + + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + + // This is to allow usage of shadow2DProj function in the shader + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY)); + + glVerify(glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, kShadowResolution, kShadowResolution, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL)); + + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); + + glVerify(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture, 0)); + + ShadowMap* map = new ShadowMap(); + map->texture = texture; + map->framebuffer = framebuffer; + + return map; + +} + +void ShadowDestroy(ShadowMap* map) +{ + glVerify(glDeleteTextures(1, &map->texture)); + glVerify(glDeleteFramebuffers(1, &map->framebuffer)); + + delete map; +} + +void ShadowBegin(ShadowMap* map) +{ + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(8.f, 8.f); + + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, map->framebuffer)); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, kShadowResolution, kShadowResolution); + + // draw back faces (for teapot) + glDisable(GL_CULL_FACE); + + // bind shadow shader + if (s_shadowProgram == GLuint(-1)) + s_shadowProgram = CompileProgram(vertexShader, passThroughShader); + + glUseProgram(s_shadowProgram); + glVerify(glUniformMatrix4fv(glGetUniformLocation(s_shadowProgram, "objectTransform"), 1, false, Matrix44::kIdentity)); +} + +void ShadowEnd() +{ + glDisable(GL_POLYGON_OFFSET_FILL); + + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, g_msaaFbo)); + + glEnable(GL_CULL_FACE); + glUseProgram(0); +} + +void BindSolidShader(Vec3 lightPos, Vec3 lightTarget, Matrix44 lightTransform, ShadowMap* shadowMap, float bias, Vec4 fogColor) +{ + glVerify(glViewport(0, 0, g_screenWidth, g_screenHeight)); + + if (s_diffuseProgram == GLuint(-1)) + s_diffuseProgram = CompileProgram(vertexShader, fragmentShader); + + if (s_diffuseProgram) + { + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + + glVerify(glUseProgram(s_diffuseProgram)); + glVerify(glUniform1i(glGetUniformLocation(s_diffuseProgram, "grid"), 0)); + glVerify(glUniform1f( glGetUniformLocation(s_diffuseProgram, "spotMin"), gSpotMin)); + glVerify(glUniform1f( glGetUniformLocation(s_diffuseProgram, "spotMax"), gSpotMax)); + glVerify(glUniform4fv( glGetUniformLocation(s_diffuseProgram, "fogColor"), 1, fogColor)); + + glVerify(glUniformMatrix4fv( glGetUniformLocation(s_diffuseProgram, "objectTransform"), 1, false, Matrix44::kIdentity)); + + // set shadow parameters + ShadowApply(s_diffuseProgram, lightPos, lightTarget, lightTransform, shadowMap->texture); + } +} + +void UnbindSolidShader() +{ + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + + glUseProgram(0); +} + +void DrawPlanes(Vec4* planes, int n, float bias) +{ + // diffuse + glColor3f(0.9f, 0.9f, 0.9f); + + GLint uBias = glGetUniformLocation(s_diffuseProgram, "bias"); + glVerify(glUniform1f(uBias, 0.0f)); + GLint uGrid = glGetUniformLocation(s_diffuseProgram, "grid"); + glVerify(glUniform1i(uGrid, 1)); + GLint uExpand = glGetUniformLocation(s_diffuseProgram, "expand"); + glVerify(glUniform1f(uExpand, 0.0f)); + + for (int i=0; i < n; ++i) + { + Vec4 p = planes[i]; + p.w -= bias; + + DrawPlane(p, false); + } + + glVerify(glUniform1i(uGrid, 0)); + glVerify(glUniform1f(uBias, gShadowBias)); +} + +void DrawMesh(const Mesh* m, Vec3 color) +{ + if (m) + { + glVerify(glColor3fv(color)); + glVerify(glSecondaryColor3fv(color)); + + glVerify(glBindBuffer(GL_ARRAY_BUFFER, 0)); + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + glVerify(glEnableClientState(GL_NORMAL_ARRAY)); + glVerify(glEnableClientState(GL_VERTEX_ARRAY)); + + glVerify(glNormalPointer(GL_FLOAT, sizeof(float) * 3, &m->m_normals[0])); + glVerify(glVertexPointer(3, GL_FLOAT, sizeof(float) * 3, &m->m_positions[0])); + + if (m->m_colours.size()) + { + glVerify(glEnableClientState(GL_COLOR_ARRAY)); + glVerify(glColorPointer(4, GL_FLOAT, 0, &m->m_colours[0])); + } + + glVerify(glDrawElements(GL_TRIANGLES, m->GetNumFaces() * 3, GL_UNSIGNED_INT, &m->m_indices[0])); + + glVerify(glDisableClientState(GL_VERTEX_ARRAY)); + glVerify(glDisableClientState(GL_NORMAL_ARRAY)); + + if (m->m_colours.size()) + glVerify(glDisableClientState(GL_COLOR_ARRAY)); + } +} + +void DrawCloth(const Vec4* positions, const Vec4* normals, const float* uvs, const int* indices, int numTris, int numPositions, int colorIndex, float expand, bool twosided, bool smooth) +{ + if (!numTris) + return; + + if (twosided) + glDisable(GL_CULL_FACE); + +#if 1 + GLint program; + glGetIntegerv(GL_CURRENT_PROGRAM, &program); + + if (program == GLint(s_diffuseProgram)) + { + GLint uBias = glGetUniformLocation(s_diffuseProgram, "bias"); + glUniform1f(uBias, 0.0f); + + GLint uExpand = glGetUniformLocation(s_diffuseProgram, "expand"); + glUniform1f(uExpand, expand); + } +#endif + + glColor3fv(gColors[colorIndex+1]*1.5f); + glSecondaryColor3fv(gColors[colorIndex]*1.5f); + + glVerify(glBindBuffer(GL_ARRAY_BUFFER, 0)); + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + glVerify(glEnableClientState(GL_VERTEX_ARRAY)); + glVerify(glEnableClientState(GL_NORMAL_ARRAY)); + + glVerify(glVertexPointer(3, GL_FLOAT, sizeof(float)*4, positions)); + glVerify(glNormalPointer(GL_FLOAT, sizeof(float)*4, normals)); + + glVerify(glDrawElements(GL_TRIANGLES, numTris*3, GL_UNSIGNED_INT, indices)); + + glVerify(glDisableClientState(GL_VERTEX_ARRAY)); + glVerify(glDisableClientState(GL_NORMAL_ARRAY)); + + if (twosided) + glEnable(GL_CULL_FACE); + +#if 1 + if (program == GLint(s_diffuseProgram)) + { + GLint uBias = glGetUniformLocation(s_diffuseProgram, "bias"); + glUniform1f(uBias, gShadowBias); + + GLint uExpand = glGetUniformLocation(s_diffuseProgram, "expand"); + glUniform1f(uExpand, 0.0f); + } +#endif +} + +void DrawRope(Vec4* positions, int* indices, int numIndices, float radius, int color) +{ + if (numIndices < 2) + return; + + std::vector<Vec3> vertices; + std::vector<Vec3> normals; + std::vector<int> triangles; + + // flatten curve + std::vector<Vec3> curve(numIndices); + for (int i=0; i < numIndices; ++i) + curve[i] = Vec3(positions[indices[i]]); + + const int resolution = 8; + const int smoothing = 3; + + vertices.reserve(resolution*numIndices*smoothing); + normals.reserve(resolution*numIndices*smoothing); + triangles.reserve(numIndices*resolution*6*smoothing); + + Extrude(&curve[0], int(curve.size()), vertices, normals, triangles, radius, resolution, smoothing); + + glVerify(glDisable(GL_CULL_FACE)); + glVerify(glColor3fv(gColors[color%8]*1.5f)); + glVerify(glSecondaryColor3fv(gColors[color%8]*1.5f)); + + glVerify(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + glVerify(glEnableClientState(GL_VERTEX_ARRAY)); + glVerify(glEnableClientState(GL_NORMAL_ARRAY)); + + glVerify(glVertexPointer(3, GL_FLOAT, sizeof(float)*3, &vertices[0])); + glVerify(glNormalPointer(GL_FLOAT, sizeof(float)*3, &normals[0])); + + glVerify(glDrawElements(GL_TRIANGLES, GLsizei(triangles.size()), GL_UNSIGNED_INT, &triangles[0])); + + glVerify(glDisableClientState(GL_VERTEX_ARRAY)); + glVerify(glDisableClientState(GL_NORMAL_ARRAY)); + glVerify(glEnable(GL_CULL_FACE)); + +} + + +struct ReflectMap +{ + GLuint texture; + + int width; + int height; +}; + +ReflectMap* ReflectCreate(int width, int height) +{ + GLuint texture; + + // copy frame buffer to texture + glVerify(glActiveTexture(GL_TEXTURE0)); + glVerify(glEnable(GL_TEXTURE_2D)); + + glVerify(glGenTextures(1, &texture)); + glVerify(glBindTexture(GL_TEXTURE_2D, texture)); + + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + + glVerify(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); + + ReflectMap* map = new ReflectMap(); + map->texture = texture; + map->width = width; + map->height = height; + + return map; +} + +void ReflectDestroy(ReflectMap* map) +{ + glVerify(glDeleteTextures(1, &map->texture)); + + delete map; +} + +void ReflectBegin(ReflectMap* map, Vec4 plane, int width, int height) +{ + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, width, height); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + Matrix44 scale = Matrix44::kIdentity; + scale.columns[0][0] *= -2.0f; + scale.columns[1][1] *= -2.0f; + scale.columns[2][2] *= -2.0f; + scale.columns[3][3] *= -2.0f; + + Matrix44 reflect = (scale*Outer(Vec4(plane.x, plane.y, plane.z, 0.0f), plane)); + reflect.columns[0][0] += 1.0f; + reflect.columns[1][1] += 1.0f; + reflect.columns[2][2] += 1.0f; + reflect.columns[3][3] += 1.0f; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMultMatrixf(reflect); + + glVerify(glFrontFace(GL_CW)); + glVerify(glEnable(GL_CLIP_PLANE0)); + + glVerify(glUniform4fv( glGetUniformLocation(s_diffuseProgram, "clipPlane"), 1, plane)); +} + +void ReflectEnd(ReflectMap* map, int width, int height) +{ + // copy frame buffer to texture + glVerify(glActiveTexture(GL_TEXTURE0)); + glVerify(glEnable(GL_TEXTURE_2D)); + glVerify(glBindTexture(GL_TEXTURE_2D, map->texture)); + + glVerify(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height)); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + glVerify(glDisable(GL_CLIP_PLANE0)); + glVerify(glFrontFace(GL_CCW)); + + glBindFramebuffer(GL_FRAMEBUFFER, g_msaaFbo); + + glViewport(0, 0, g_screenWidth, g_screenHeight); +} + + +//----------------------------------------------------------------------------------------------------- +// vertex shader + +const char *vertexPointDepthShader = STRINGIFY( + +uniform float pointRadius; // point size in world space +uniform float pointScale; // scale to calculate size in pixels + +void main() +{ + // calculate window-space point size + gl_Position = gl_ModelViewProjectionMatrix * vec4(gl_Vertex.xyz, 1.0); + gl_PointSize = pointScale * (pointRadius / gl_Position.w); + + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = gl_ModelViewMatrix * vec4(gl_Vertex.xyz, 1.0); +} +); + +// pixel shader for rendering points as shaded spheres +const char *fragmentPointDepthShader = STRINGIFY( + +uniform float pointRadius; // point size in world space + +void main() +{ + // calculate normal from texture coordinates + vec3 normal; + normal.xy = gl_TexCoord[0].xy*vec2(2.0, -2.0) + vec2(-1.0, 1.0); + float mag = dot(normal.xy, normal.xy); + if (mag > 1.0) discard; // kill pixels outside circle + normal.z = sqrt(1.0-mag); + + vec3 eyePos = gl_TexCoord[1].xyz + normal*pointRadius*2.0; + vec4 ndcPos = gl_ProjectionMatrix * vec4(eyePos, 1.0); + ndcPos.z /= ndcPos.w; + + gl_FragColor = vec4(eyePos.z, 1.0, 1.0, 1.0); + gl_FragDepth = ndcPos.z*0.5 + 0.5; +} +); + + +// pixel shader for rendering points density +const char *fragmentPointThicknessShader = STRINGIFY( + +void main() +{ + // calculate normal from texture coordinates + vec3 normal; + normal.xy = gl_TexCoord[0].xy*vec2(2.0, -2.0) + vec2(-1.0, 1.0); + float mag = dot(normal.xy, normal.xy); + if (mag > 1.0) discard; // kill pixels outside circle + normal.z = sqrt(1.0-mag); + + gl_FragColor = vec4(normal.z*0.005); +} +); + +//-------------------------------------------------------- +// Ellipsoid shaders +// +const char *vertexEllipsoidDepthShader = "#version 120\n" STRINGIFY( + +// rotation matrix in xyz, scale in w +attribute vec4 q1; +attribute vec4 q2; +attribute vec4 q3; + +// returns 1.0 for x==0.0 (unlike glsl) +float Sign(float x) { return x < 0.0 ? -1.0: 1.0; } + +bool solveQuadratic(float a, float b, float c, out float minT, out float maxT) +{ + if (a == 0.0 && b == 0.0) + { + minT = maxT = 0.0; + return false; + } + + float discriminant = b*b - 4.0*a*c; + + if (discriminant < 0.0) + { + return false; + } + + float t = -0.5*(b + Sign(b)*sqrt(discriminant)); + minT = t / a; + maxT = c / t; + + if (minT > maxT) + { + float tmp = minT; + minT = maxT; + maxT = tmp; + } + + return true; +} + +float DotInvW(vec4 a, vec4 b) { return a.x*b.x + a.y*b.y + a.z*b.z - a.w*b.w; } + +void main() +{ + vec3 worldPos = gl_Vertex.xyz;// - vec3(0.0, 0.1*0.25, 0.0); // hack move towards ground to account for anisotropy + + // construct quadric matrix + mat4 q; + q[0] = vec4(q1.xyz*q1.w, 0.0); + q[1] = vec4(q2.xyz*q2.w, 0.0); + q[2] = vec4(q3.xyz*q3.w, 0.0); + q[3] = vec4(worldPos, 1.0); + + // transforms a normal to parameter space (inverse transpose of (q*modelview)^-T) + mat4 invClip = transpose(gl_ModelViewProjectionMatrix*q); + + // solve for the right hand bounds in homogenous clip space + float a1 = DotInvW(invClip[3], invClip[3]); + float b1 = -2.0f*DotInvW(invClip[0], invClip[3]); + float c1 = DotInvW(invClip[0], invClip[0]); + + float xmin; + float xmax; + solveQuadratic(a1, b1, c1, xmin, xmax); + + // solve for the right hand bounds in homogenous clip space + float a2 = DotInvW(invClip[3], invClip[3]); + float b2 = -2.0f*DotInvW(invClip[1], invClip[3]); + float c2 = DotInvW(invClip[1], invClip[1]); + + float ymin; + float ymax; + solveQuadratic(a2, b2, c2, ymin, ymax); + + gl_Position = vec4(worldPos.xyz, 1.0); + gl_TexCoord[0] = vec4(xmin, xmax, ymin, ymax); + + // construct inverse quadric matrix (used for ray-casting in parameter space) + mat4 invq; + invq[0] = vec4(q1.xyz/q1.w, 0.0); + invq[1] = vec4(q2.xyz/q2.w, 0.0); + invq[2] = vec4(q3.xyz/q3.w, 0.0); + invq[3] = vec4(0.0, 0.0, 0.0, 1.0); + + invq = transpose(invq); + invq[3] = -(invq*gl_Position); + + // transform a point from view space to parameter space + invq = invq*gl_ModelViewMatrixInverse; + + // pass down + gl_TexCoord[1] = invq[0]; + gl_TexCoord[2] = invq[1]; + gl_TexCoord[3] = invq[2]; + gl_TexCoord[4] = invq[3]; + + // compute ndc pos for frustrum culling in GS + vec4 ndcPos = gl_ModelViewProjectionMatrix * vec4(worldPos.xyz, 1.0); + gl_TexCoord[5] = ndcPos / ndcPos.w; +} +); + +const char* geometryEllipsoidDepthShader = +"#version 120\n" +"#extension GL_EXT_geometry_shader4 : enable\n" +STRINGIFY( +void main() +{ + vec3 pos = gl_PositionIn[0].xyz; + vec4 bounds = gl_TexCoordIn[0][0]; + vec4 ndcPos = gl_TexCoordIn[0][5]; + + // frustrum culling + const float ndcBound = 1.0; + if (ndcPos.x < -ndcBound) return; + if (ndcPos.x > ndcBound) return; + if (ndcPos.y < -ndcBound) return; + if (ndcPos.y > ndcBound) return; + + float xmin = bounds.x; + float xmax = bounds.y; + float ymin = bounds.z; + float ymax = bounds.w; + + // inv quadric transform + gl_TexCoord[0] = gl_TexCoordIn[0][1]; + gl_TexCoord[1] = gl_TexCoordIn[0][2]; + gl_TexCoord[2] = gl_TexCoordIn[0][3]; + gl_TexCoord[3] = gl_TexCoordIn[0][4]; + + gl_Position = vec4(xmin, ymax, 0.0, 1.0); + EmitVertex(); + + gl_Position = vec4(xmin, ymin, 0.0, 1.0); + EmitVertex(); + + gl_Position = vec4(xmax, ymax, 0.0, 1.0); + EmitVertex(); + + gl_Position = vec4(xmax, ymin, 0.0, 1.0); + EmitVertex(); +} +); + +// pixel shader for rendering points as shaded spheres +const char *fragmentEllipsoidDepthShader = "#version 120\n" STRINGIFY( + +uniform vec3 invViewport; +uniform vec3 invProjection; + +float Sign(float x) { return x < 0.0 ? -1.0: 1.0; } + +bool solveQuadratic(float a, float b, float c, out float minT, out float maxT) +{ + if (a == 0.0 && b == 0.0) + { + minT = maxT = 0.0; + return true; + } + + float discriminant = b*b - 4.0*a*c; + + if (discriminant < 0.0) + { + return false; + } + + float t = -0.5*(b + Sign(b)*sqrt(discriminant)); + minT = t / a; + maxT = c / t; + + if (minT > maxT) + { + float tmp = minT; + minT = maxT; + maxT = tmp; + } + + return true; +} + +float sqr(float x) { return x*x; } + +void main() +{ + // transform from view space to parameter space + mat4 invQuadric; + invQuadric[0] = gl_TexCoord[0]; + invQuadric[1] = gl_TexCoord[1]; + invQuadric[2] = gl_TexCoord[2]; + invQuadric[3] = gl_TexCoord[3]; + + vec4 ndcPos = vec4(gl_FragCoord.xy*invViewport.xy*vec2(2.0, 2.0) - vec2(1.0, 1.0), -1.0, 1.0); + vec4 viewDir = gl_ProjectionMatrixInverse*ndcPos; + + // ray to parameter space + vec4 dir = invQuadric*vec4(viewDir.xyz, 0.0); + vec4 origin = invQuadric[3]; + + // set up quadratric equation + float a = sqr(dir.x) + sqr(dir.y) + sqr(dir.z);// - sqr(dir.w); + float b = dir.x*origin.x + dir.y*origin.y + dir.z*origin.z - dir.w*origin.w; + float c = sqr(origin.x) + sqr(origin.y) + sqr(origin.z) - sqr(origin.w); + + float minT; + float maxT; + + if (solveQuadratic(a, 2.0*b, c, minT, maxT)) + { + vec3 eyePos = viewDir.xyz*minT; + vec4 ndcPos = gl_ProjectionMatrix * vec4(eyePos, 1.0); + ndcPos.z /= ndcPos.w; + + gl_FragColor = vec4(eyePos.z, 1.0, 1.0, 1.0); + gl_FragDepth = ndcPos.z*0.5 + 0.5; + + return; + } + else + discard; + + gl_FragColor = vec4(0.5, 0.0, 0.0, 1.0); +} +); + +//-------------------------------------------------------------------------------- +// Composite shaders + +const char* vertexPassThroughShader = STRINGIFY( + +void main() +{ + gl_Position = vec4(gl_Vertex.xyz, 1.0); + gl_TexCoord[0] = gl_MultiTexCoord0; +} +); + +const char* fragmentBlurDepthShader = +"#extension GL_ARB_texture_rectangle : enable\n" +STRINGIFY( + +uniform sampler2DRect depthTex; +uniform sampler2D thicknessTex; +uniform float blurRadiusWorld; +uniform float blurScale; +uniform float blurFalloff; +uniform vec2 invTexScale; + +uniform bool debug; + +float sqr(float x) { return x*x; } + +void main() +{ + // eye-space depth of center sample + float depth = texture2DRect(depthTex, gl_FragCoord.xy).x; + float thickness = texture2D(thicknessTex, gl_TexCoord[0].xy).x; + + // hack: ENABLE_SIMPLE_FLUID + //thickness = 0.0f; + + if (debug) + { + // do not blur + gl_FragColor.x = depth; + return; + } + + // threshold on thickness to create nice smooth silhouettes + if (depth == 0.0)//|| thickness < 0.02f) + { + gl_FragColor.x = 0.0; + return; + } + + /* + float dzdx = dFdx(depth); + float dzdy = dFdy(depth); + + // handle edge case + if (max(abs(dzdx), abs(dzdy)) > 0.05) + { + dzdx = 0.0; + dzdy = 0.0; + + gl_FragColor.x = depth; + return; + } + */ + + float blurDepthFalloff = 5.5;//blurFalloff*mix(4.0, 1.0, thickness)/blurRadiusWorld*0.0375; // these constants are just a re-scaling from some known good values + + float maxBlurRadius = 5.0; + //float taps = min(maxBlurRadius, blurScale * (blurRadiusWorld / -depth)); + //vec2 blurRadius = min(mix(0.25, 2.0/blurFalloff, thickness) * blurScale * (blurRadiusWorld / -depth) / taps, 0.15)*invTexScale; + + //discontinuities between different tap counts are visible. to avoid this we + //use fractional contributions between #taps = ceil(radius) and floor(radius) + float radius = min(maxBlurRadius, blurScale * (blurRadiusWorld / -depth)); + float radiusInv = 1.0/radius; + float taps = ceil(radius); + float frac = taps - radius; + + float sum = 0.0; + float wsum = 0.0; + float count = 0.0; + + for(float y=-taps; y <= taps; y += 1.0) + { + for(float x=-taps; x <= taps; x += 1.0) + { + vec2 offset = vec2(x, y); + + float sample = texture2DRect(depthTex, gl_FragCoord.xy + offset).x; + + if (sample < -10000.0*0.5) + continue; + + // spatial domain + float r1 = length(vec2(x, y))*radiusInv; + float w = exp(-(r1*r1)); + + //float expectedDepth = depth + dot(vec2(dzdx, dzdy), offset); + + // range domain (based on depth difference) + float r2 = (sample - depth) * blurDepthFalloff; + float g = exp(-(r2*r2)); + + //fractional radius contributions + float wBoundary = step(radius, max(abs(x), abs(y))); + float wFrac = 1.0 - wBoundary*frac; + + sum += sample * w * g * wFrac; + wsum += w * g * wFrac; + count += g * wFrac; + } + } + + if (wsum > 0.0) { + sum /= wsum; + } + + float blend = count/sqr(2.0*radius+1.0); + gl_FragColor.x = mix(depth, sum, blend); +} +); + +const char* fragmentCompositeShader = STRINGIFY( + +uniform sampler2D tex; +uniform vec2 invTexScale; +uniform vec3 lightPos; +uniform vec3 lightDir; +uniform float spotMin; +uniform float spotMax; +uniform vec4 color; +uniform float ior; + +uniform vec2 clipPosToEye; + +uniform sampler2D reflectTex; +uniform sampler2DShadow shadowTex; +uniform vec2 shadowTaps[12]; +uniform mat4 lightTransform; + +uniform sampler2D thicknessTex; +uniform sampler2D sceneTex; + +uniform bool debug; + +// sample shadow map +float shadowSample(vec3 worldPos, out float attenuation) +{ + // hack: ENABLE_SIMPLE_FLUID + //attenuation = 0.0f; + //return 0.5; + + vec4 pos = lightTransform*vec4(worldPos+lightDir*0.15, 1.0); + pos /= pos.w; + vec3 uvw = (pos.xyz*0.5)+vec3(0.5); + + attenuation = max(smoothstep(spotMax, spotMin, dot(pos.xy, pos.xy)), 0.05); + + // user clip + if (uvw.x < 0.0 || uvw.x > 1.0) + return 1.0; + if (uvw.y < 0.0 || uvw.y > 1.0) + return 1.0; + + float s = 0.0; + float radius = 0.002; + + for (int i=0; i < 8; i++) + { + s += shadow2D(shadowTex, vec3(uvw.xy + shadowTaps[i]*radius, uvw.z)).r; + } + + s /= 8.0; + return s; +} + +vec3 viewportToEyeSpace(vec2 coord, float eyeZ) +{ + // find position at z=1 plane + vec2 uv = (coord*2.0 - vec2(1.0))*clipPosToEye; + + return vec3(-uv*eyeZ, eyeZ); +} + +vec3 srgbToLinear(vec3 c) { return pow(c, vec3(2.2)); } +vec3 linearToSrgb(vec3 c) { return pow(c, vec3(1.0/2.2)); } + +float sqr(float x) { return x*x; } +float cube(float x) { return x*x*x; } + +void main() +{ + float eyeZ = texture2D(tex, gl_TexCoord[0].xy).x; + + if (eyeZ == 0.0) + discard; + + // reconstruct eye space pos from depth + vec3 eyePos = viewportToEyeSpace(gl_TexCoord[0].xy, eyeZ); + + // finite difference approx for normals, can't take dFdx because + // the one-sided difference is incorrect at shape boundaries + vec3 zl = eyePos - viewportToEyeSpace(gl_TexCoord[0].xy - vec2(invTexScale.x, 0.0), texture2D(tex, gl_TexCoord[0].xy - vec2(invTexScale.x, 0.0)).x); + vec3 zr = viewportToEyeSpace(gl_TexCoord[0].xy + vec2(invTexScale.x, 0.0), texture2D(tex, gl_TexCoord[0].xy + vec2(invTexScale.x, 0.0)).x) - eyePos; + vec3 zt = viewportToEyeSpace(gl_TexCoord[0].xy + vec2(0.0, invTexScale.y), texture2D(tex, gl_TexCoord[0].xy + vec2(0.0, invTexScale.y)).x) - eyePos; + vec3 zb = eyePos - viewportToEyeSpace(gl_TexCoord[0].xy - vec2(0.0, invTexScale.y), texture2D(tex, gl_TexCoord[0].xy - vec2(0.0, invTexScale.y)).x); + + vec3 dx = zl; + vec3 dy = zt; + + if (abs(zr.z) < abs(zl.z)) + dx = zr; + + if (abs(zb.z) < abs(zt.z)) + dy = zb; + + //vec3 dx = dFdx(eyePos.xyz); + //vec3 dy = dFdy(eyePos.xyz); + + vec4 worldPos = gl_ModelViewMatrixInverse*vec4(eyePos, 1.0); + + float attenuation; + float shadow = shadowSample(worldPos.xyz, attenuation); + + vec3 l = (gl_ModelViewMatrix*vec4(lightDir, 0.0)).xyz; + vec3 v = -normalize(eyePos); + + vec3 n = normalize(cross(dx, dy)); + vec3 h = normalize(v + l); + + vec3 skyColor = vec3(0.1, 0.2, 0.4)*1.2; + vec3 groundColor = vec3(0.1, 0.1, 0.2); + + float fresnel = 0.1 + (1.0 - 0.1)*cube(1.0-max(dot(n, v), 0.0)); + + vec3 lVec = normalize(worldPos.xyz-lightPos); + + float ln = dot(l, n)*attenuation; + + vec3 rEye = reflect(-v, n).xyz; + vec3 rWorld = (gl_ModelViewMatrixInverse*vec4(rEye, 0.0)).xyz; + + vec2 texScale = vec2(0.75, 1.0); // to account for backbuffer aspect ratio (todo: pass in) + + float refractScale = ior*0.025; + float reflectScale = ior*0.1; + + // attenuate refraction near ground (hack) + refractScale *= smoothstep(0.1, 0.4, worldPos.y); + + vec2 refractCoord = gl_TexCoord[0].xy + n.xy*refractScale*texScale; + //vec2 refractCoord = gl_TexCoord[0].xy + refract(-v, n, 1.0/1.33)*refractScale*texScale; + + // read thickness from refracted coordinate otherwise we get halos around objectsw + float thickness = max(texture2D(thicknessTex, refractCoord).x, 0.3); + + //vec3 transmission = exp(-(vec3(1.0)-color.xyz)*thickness); + vec3 transmission = (1.0-(1.0-color.xyz)*thickness*0.8)*color.w; + vec3 refract = texture2D(sceneTex, refractCoord).xyz*transmission; + + vec2 sceneReflectCoord = gl_TexCoord[0].xy - rEye.xy*texScale*reflectScale/eyePos.z; + vec3 sceneReflect = (texture2D(sceneTex, sceneReflectCoord).xyz)*shadow; + + vec3 planarReflect = texture2D(reflectTex, gl_TexCoord[0].xy).xyz; + planarReflect = vec3(0.0); + + // fade out planar reflections above the ground + vec3 reflect = mix(planarReflect, sceneReflect, smoothstep(0.05, 0.3, worldPos.y)) + mix(groundColor, skyColor, smoothstep(0.15, 0.25, rWorld.y)*shadow); + + // lighting + vec3 diffuse = color.xyz*mix(vec3(0.29, 0.379, 0.59), vec3(1.0), (ln*0.5 + 0.5)*max(shadow, 0.4))*(1.0-color.w); + vec3 specular = vec3(1.2*pow(max(dot(h, n), 0.0), 400.0)); + + gl_FragColor.xyz = diffuse + (mix(refract, reflect, fresnel) + specular)*color.w; + gl_FragColor.w = 1.0; + + if (debug) + gl_FragColor = vec4(n*0.5 + vec3(0.5), 1.0); + + // write valid z + vec4 clipPos = gl_ProjectionMatrix*vec4(0.0, 0.0, eyeZ, 1.0); + clipPos.z /= clipPos.w; + + gl_FragDepth = clipPos.z*0.5 + 0.5; +} +); + + +struct FluidRenderer +{ + GLuint mDepthFbo; + GLuint mDepthTex; + GLuint mDepthSmoothTex; + GLuint mSceneFbo; + GLuint mSceneTex; + GLuint mReflectTex; + + GLuint mThicknessFbo; + GLuint mThicknessTex; + + GLuint mPointThicknessProgram; + //GLuint mPointDepthProgram; + + GLuint mEllipsoidThicknessProgram; + GLuint mEllipsoidDepthProgram; + + GLuint mCompositeProgram; + GLuint mDepthBlurProgram; + + int mSceneWidth; + int mSceneHeight; +}; + +FluidRenderer* CreateFluidRenderer(uint32_t width, uint32_t height) +{ + FluidRenderer* renderer = new FluidRenderer(); + + renderer->mSceneWidth = width; + renderer->mSceneHeight = height; + + // scene depth texture + glVerify(glGenTextures(1, &renderer->mDepthTex)); + glVerify(glBindTexture(GL_TEXTURE_RECTANGLE_ARB, renderer->mDepthTex)); + + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + glVerify(glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE32F_ARB, width, height, 0, GL_LUMINANCE, GL_FLOAT, NULL)); + + // smoothed depth texture + glVerify(glGenTextures(1, &renderer->mDepthSmoothTex)); + glVerify(glBindTexture(GL_TEXTURE_2D, renderer->mDepthSmoothTex)); + + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + glVerify(glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE32F_ARB, width, height, 0, GL_LUMINANCE, GL_FLOAT, NULL)); + + // scene copy + glVerify(glGenTextures(1, &renderer->mSceneTex)); + glVerify(glBindTexture(GL_TEXTURE_2D, renderer->mSceneTex)); + + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + glVerify(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); + + glVerify(glGenFramebuffers(1, &renderer->mSceneFbo)); + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, renderer->mSceneFbo)); + glVerify(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderer->mSceneTex, 0)); + + // frame buffer + glVerify(glGenFramebuffers(1, &renderer->mDepthFbo)); + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, renderer->mDepthFbo)); + glVerify(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, renderer->mDepthTex, 0)); + + GLuint zbuffer; + glVerify(glGenRenderbuffers(1, &zbuffer)); + glVerify(glBindRenderbuffer(GL_RENDERBUFFER, zbuffer)); + glVerify(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height)); + glVerify(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, zbuffer)); + + glVerify(glDrawBuffer(GL_COLOR_ATTACHMENT0)); + glVerify(glReadBuffer(GL_COLOR_ATTACHMENT0)); + + glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, g_msaaFbo); + + // reflect texture + glVerify(glGenTextures(1, &renderer->mReflectTex)); + glVerify(glBindTexture(GL_TEXTURE_2D, renderer->mReflectTex)); + + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + glVerify(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); + + // thickness texture + const int thicknessWidth = width; + const int thicknessHeight = height; + + glVerify(glGenTextures(1, &renderer->mThicknessTex)); + glVerify(glBindTexture(GL_TEXTURE_2D, renderer->mThicknessTex)); + + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + +#if USE_HDR_DIFFUSE_BLEND + glVerify(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, thicknessWidth, thicknessHeight, 0, GL_RGBA, GL_FLOAT, NULL)); +#else + glVerify(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, thicknessWidth, thicknessHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); +#endif + + // thickness buffer + glVerify(glGenFramebuffers(1, &renderer->mThicknessFbo)); + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, renderer->mThicknessFbo)); + glVerify(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderer->mThicknessTex, 0)); + + GLuint thickz; + glVerify(glGenRenderbuffers(1, &thickz)); + glVerify(glBindRenderbuffer(GL_RENDERBUFFER, thickz)); + glVerify(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, thicknessWidth, thicknessHeight)); + glVerify(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, thickz)); + + glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, g_msaaFbo); + + // compile shaders + //renderer->mPointDepthProgram = CompileProgram(vertexPointDepthShader, fragmentPointDepthShader); + renderer->mPointThicknessProgram = CompileProgram(vertexPointDepthShader, fragmentPointThicknessShader); + + //renderer->mEllipsoidThicknessProgram = CompileProgram(vertexEllipsoidDepthShader, fragmentEllipsoidThicknessShader); + renderer->mEllipsoidDepthProgram = CompileProgram(vertexEllipsoidDepthShader, fragmentEllipsoidDepthShader, geometryEllipsoidDepthShader); + + renderer->mCompositeProgram = CompileProgram(vertexPassThroughShader, fragmentCompositeShader); + renderer->mDepthBlurProgram = CompileProgram(vertexPassThroughShader, fragmentBlurDepthShader); + + return renderer; +} + +void DestroyFluidRenderer(FluidRenderer* renderer) +{ + glVerify(glDeleteFramebuffers(1, &renderer->mSceneFbo)); + glVerify(glDeleteFramebuffers(1, &renderer->mDepthFbo)); + glVerify(glDeleteTextures(1, &renderer->mDepthTex)); + glVerify(glDeleteTextures(1, &renderer->mDepthSmoothTex)); + glVerify(glDeleteTextures(1, &renderer->mSceneTex)); + + glVerify(glDeleteFramebuffers(1, &renderer->mThicknessFbo)); + glVerify(glDeleteTextures(1, &renderer->mThicknessTex)); +} + +FluidRenderBuffers CreateFluidRenderBuffers(int numFluidParticles, bool enableInterop) +{ + FluidRenderBuffers buffers = {}; + buffers.mNumFluidParticles = numFluidParticles; + + // vbos + glVerify(glGenBuffers(1, &buffers.mPositionVBO)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mPositionVBO)); + glVerify(glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 4 * numFluidParticles, 0, GL_DYNAMIC_DRAW)); + + // density + glVerify(glGenBuffers(1, &buffers.mDensityVBO)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mDensityVBO)); + glVerify(glBufferData(GL_ARRAY_BUFFER, sizeof(int)*numFluidParticles, 0, GL_DYNAMIC_DRAW)); + + for (int i = 0; i < 3; ++i) + { + glVerify(glGenBuffers(1, &buffers.mAnisotropyVBO[i])); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mAnisotropyVBO[i])); + glVerify(glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 4 * numFluidParticles, 0, GL_DYNAMIC_DRAW)); + } + + glVerify(glGenBuffers(1, &buffers.mIndices)); + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers.mIndices)); + glVerify(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*numFluidParticles, 0, GL_DYNAMIC_DRAW)); + + if (enableInterop) + { + extern NvFlexLibrary* g_flexLib; + + buffers.mPositionBuf = NvFlexRegisterOGLBuffer(g_flexLib, buffers.mPositionVBO, numFluidParticles, sizeof(Vec4)); + buffers.mDensitiesBuf = NvFlexRegisterOGLBuffer(g_flexLib, buffers.mDensityVBO, numFluidParticles, sizeof(float)); + buffers.mIndicesBuf = NvFlexRegisterOGLBuffer(g_flexLib, buffers.mIndices, numFluidParticles, sizeof(int)); + + buffers.mAnisotropyBuf[0] = NvFlexRegisterOGLBuffer(g_flexLib, buffers.mAnisotropyVBO[0], numFluidParticles, sizeof(Vec4)); + buffers.mAnisotropyBuf[1] = NvFlexRegisterOGLBuffer(g_flexLib, buffers.mAnisotropyVBO[1], numFluidParticles, sizeof(Vec4)); + buffers.mAnisotropyBuf[2] = NvFlexRegisterOGLBuffer(g_flexLib, buffers.mAnisotropyVBO[2], numFluidParticles, sizeof(Vec4)); + } + + return buffers; +} + +void DestroyFluidRenderBuffers(FluidRenderBuffers buffers) +{ + glDeleteBuffers(1, &buffers.mPositionVBO); + glDeleteBuffers(3, buffers.mAnisotropyVBO); + glDeleteBuffers(1, &buffers.mDensityVBO); + glDeleteBuffers(1, &buffers.mIndices); + + NvFlexUnregisterOGLBuffer(buffers.mPositionBuf); + NvFlexUnregisterOGLBuffer(buffers.mDensitiesBuf); + NvFlexUnregisterOGLBuffer(buffers.mIndicesBuf); + + NvFlexUnregisterOGLBuffer(buffers.mAnisotropyBuf[0]); + NvFlexUnregisterOGLBuffer(buffers.mAnisotropyBuf[1]); + NvFlexUnregisterOGLBuffer(buffers.mAnisotropyBuf[2]); +} + +void UpdateFluidRenderBuffers(FluidRenderBuffers buffers, NvFlexSolver* solver, bool anisotropy, bool density) +{ + // use VBO buffer wrappers to allow Flex to write directly to the OpenGL buffers + // Flex will take care of any CUDA interop mapping/unmapping during the get() operations + if (!anisotropy) + { + // regular particles + NvFlexGetParticles(solver, buffers.mPositionBuf, buffers.mNumFluidParticles); + } + else + { + // fluid buffers + NvFlexGetSmoothParticles(solver, buffers.mPositionBuf, buffers.mNumFluidParticles); + NvFlexGetAnisotropy(solver, buffers.mAnisotropyBuf[0], buffers.mAnisotropyBuf[1], buffers.mAnisotropyBuf[2]); + } + + if (density) + { + NvFlexGetDensities(solver, buffers.mDensitiesBuf, buffers.mNumFluidParticles); + } + else + { + NvFlexGetPhases(solver, buffers.mDensitiesBuf, buffers.mNumFluidParticles); + } + + NvFlexGetActive(solver, buffers.mIndicesBuf); +} + +void UpdateFluidRenderBuffers(FluidRenderBuffers buffers, Vec4* particles, float* densities, Vec4* anisotropy1, Vec4* anisotropy2, Vec4* anisotropy3, int numParticles, int* indices, int numIndices) +{ + // regular particles + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mPositionVBO)); + glVerify(glBufferSubData(GL_ARRAY_BUFFER, 0, buffers.mNumFluidParticles*sizeof(Vec4), particles)); + + if (anisotropy1) + { + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mAnisotropyVBO[0])); + glVerify(glBufferSubData(GL_ARRAY_BUFFER, 0, buffers.mNumFluidParticles*sizeof(Vec4), anisotropy1)); + } + + if (anisotropy2) + { + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mAnisotropyVBO[1])); + glVerify(glBufferSubData(GL_ARRAY_BUFFER, 0, buffers.mNumFluidParticles*sizeof(Vec4), anisotropy2)); + } + + if (anisotropy3) + { + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mAnisotropyVBO[2])); + glVerify(glBufferSubData(GL_ARRAY_BUFFER, 0, buffers.mNumFluidParticles*sizeof(Vec4), anisotropy3)); + } + + // density /phase buffer + if (densities) + { + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mDensityVBO)); + glVerify(glBufferSubData(GL_ARRAY_BUFFER, 0, buffers.mNumFluidParticles*sizeof(float), densities)); + } + + if (indices) + { + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers.mIndices)); + glVerify(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, numIndices*sizeof(int), indices)); + } + + // reset + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, 0)); + +} + +DiffuseRenderBuffers CreateDiffuseRenderBuffers(int numDiffuseParticles, bool& enableInterop) +{ + DiffuseRenderBuffers buffers = {}; + buffers.mNumDiffuseParticles = numDiffuseParticles; + + if (numDiffuseParticles > 0) + { + glVerify(glGenBuffers(1, &buffers.mDiffusePositionVBO)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mDiffusePositionVBO)); + glVerify(glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 4 * numDiffuseParticles, 0, GL_DYNAMIC_DRAW)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + glVerify(glGenBuffers(1, &buffers.mDiffuseVelocityVBO)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mDiffuseVelocityVBO)); + glVerify(glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 4 * numDiffuseParticles, 0, GL_DYNAMIC_DRAW)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + glVerify(glGenBuffers(1, &buffers.mDiffuseIndicesIBO)); + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers.mDiffuseIndicesIBO)); + glVerify(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*numDiffuseParticles, 0, GL_DYNAMIC_DRAW)); + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + if (enableInterop) + { + extern NvFlexLibrary* g_flexLib; + + buffers.mDiffuseIndicesBuf = NvFlexRegisterOGLBuffer(g_flexLib, buffers.mDiffuseIndicesIBO, numDiffuseParticles, sizeof(int)); + buffers.mDiffusePositionsBuf = NvFlexRegisterOGLBuffer(g_flexLib, buffers.mDiffusePositionVBO, numDiffuseParticles, sizeof(Vec4)); + buffers.mDiffuseVelocitiesBuf = NvFlexRegisterOGLBuffer(g_flexLib, buffers.mDiffuseVelocityVBO, numDiffuseParticles, sizeof(Vec4)); + } + } + + return buffers; +} + +void DestroyDiffuseRenderBuffers(DiffuseRenderBuffers buffers) +{ + if (buffers.mNumDiffuseParticles > 0) + { + glDeleteBuffers(1, &buffers.mDiffusePositionVBO); + glDeleteBuffers(1, &buffers.mDiffuseVelocityVBO); + glDeleteBuffers(1, &buffers.mDiffuseIndicesIBO); + + NvFlexUnregisterOGLBuffer(buffers.mDiffuseIndicesBuf); + NvFlexUnregisterOGLBuffer(buffers.mDiffusePositionsBuf); + NvFlexUnregisterOGLBuffer(buffers.mDiffuseVelocitiesBuf); + } +} + +void UpdateDiffuseRenderBuffers(DiffuseRenderBuffers buffers, NvFlexSolver* solver) +{ + // diffuse particles + if (buffers.mNumDiffuseParticles) + { + NvFlexGetDiffuseParticles(solver, buffers.mDiffusePositionsBuf, buffers.mDiffuseVelocitiesBuf, buffers.mDiffuseIndicesBuf); + } +} + +void UpdateDiffuseRenderBuffers(DiffuseRenderBuffers buffers, Vec4* diffusePositions, Vec4* diffuseVelocities, int* diffuseIndices, int numDiffuseParticles) +{ + // diffuse particles + if (buffers.mNumDiffuseParticles) + { + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers.mDiffuseIndicesIBO)); + glVerify(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, buffers.mNumDiffuseParticles*sizeof(int), diffuseIndices)); + + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mDiffusePositionVBO)); + glVerify(glBufferSubData(GL_ARRAY_BUFFER, 0, buffers.mNumDiffuseParticles*sizeof(Vec4), diffusePositions)); + + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mDiffuseVelocityVBO)); + glVerify(glBufferSubData(GL_ARRAY_BUFFER, 0, buffers.mNumDiffuseParticles*sizeof(Vec4), diffuseVelocities)); + } +} + +void RenderFullscreenQuad() +{ + glColor3f(1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + + glTexCoord2f(0.0f, 0.0f); + glVertex2f(-1.0f, -1.0f); + + glTexCoord2f(1.0f, 0.0f); + glVertex2f(1.0f, -1.0f); + + glTexCoord2f(1.0f, 1.0f); + glVertex2f(1.0f, 1.0f); + + glTexCoord2f(0.0f, 1.0f); + glVertex2f(-1.0f, 1.0f); + + glEnd(); +} + +extern Mesh* g_mesh; +void DrawShapes(); + +void RenderEllipsoids(FluidRenderer* render, FluidRenderBuffers buffers, int n, int offset, float radius, float screenWidth, float screenAspect, float fov, Vec3 lightPos, Vec3 lightTarget, Matrix44 lightTransform, ShadowMap* shadowMap, Vec4 color, float blur, float ior, bool debug) +{ +#if !ENABLE_SIMPLE_FLUID + // resolve msaa back buffer to texture + glVerify(glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, g_msaaFbo)); + glVerify(glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, render->mSceneFbo)); + glVerify(glBlitFramebuffer(0, 0, GLsizei(screenWidth), GLsizei(screenWidth/screenAspect), 0, 0, GLsizei(screenWidth), GLsizei(screenWidth/screenAspect), GL_COLOR_BUFFER_BIT, GL_LINEAR)); + + //thickness texture + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, render->mThicknessFbo)); + glVerify(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render->mThicknessTex, 0)); + glVerify(glDrawBuffer(GL_COLOR_ATTACHMENT0)); + + glViewport(0, 0, GLsizei(screenWidth), GLsizei(screenWidth/screenAspect)); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_DEPTH_BUFFER_BIT); + + glDepthMask(GL_TRUE); + glDisable(GL_CULL_FACE); + + if (g_mesh) + DrawMesh(g_mesh, Vec3(1.0f)); + + DrawShapes(); + + glClear(GL_COLOR_BUFFER_BIT); + + glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE); + glEnable(GL_POINT_SPRITE); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + glEnable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + glDepthMask(GL_FALSE); + + // make sprites larger to get smoother thickness texture + const float thicknessScale = 4.0f; + + glUseProgram(render->mPointThicknessProgram); + glUniform1f( glGetUniformLocation(render->mPointThicknessProgram, "pointRadius"), thicknessScale*radius); + glUniform1f( glGetUniformLocation(render->mPointThicknessProgram, "pointScale"), screenWidth/screenAspect * (1.0f / (tanf(fov*0.5f)))); + + glEnableClientState(GL_VERTEX_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, buffers.mPositionVBO); + glVertexPointer(3, GL_FLOAT, sizeof(float)*4, (void*)(offset*sizeof(float)*4)); + + glDrawArrays(GL_POINTS, 0, n); + + glUseProgram(0); + glDisableClientState(GL_VERTEX_ARRAY); + glDisable(GL_POINT_SPRITE); + glDisable(GL_BLEND); +#endif + + // depth texture + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, render->mDepthFbo)); + glVerify(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, render->mDepthTex, 0)); + glVerify(glDrawBuffer(GL_COLOR_ATTACHMENT0)); + + // draw points + //glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE); + glDisable(GL_POINT_SPRITE); + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + + glViewport(0, 0, int(screenWidth), int(screenWidth/screenAspect)); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + const float viewHeight = tanf(fov/2.0f); + + glUseProgram(render->mEllipsoidDepthProgram); + glUniform3fv( glGetUniformLocation(render->mEllipsoidDepthProgram, "invViewport"), 1, Vec3(1.0f/screenWidth, screenAspect/screenWidth, 1.0f)); + glUniform3fv( glGetUniformLocation(render->mEllipsoidDepthProgram, "invProjection"), 1, Vec3(screenAspect*viewHeight, viewHeight, 1.0f)); + + glEnableClientState(GL_VERTEX_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, buffers.mPositionVBO); + glVertexPointer(3, GL_FLOAT, sizeof(float)*4, 0);//(void*)(offset*sizeof(float)*4)); + + // ellipsoid eigenvectors + int s1 = glGetAttribLocation(render->mEllipsoidDepthProgram, "q1"); + glEnableVertexAttribArray(s1); + glBindBuffer(GL_ARRAY_BUFFER, buffers.mAnisotropyVBO[0]); + glVertexAttribPointer(s1, 4, GL_FLOAT, GL_FALSE, 0, 0);// (void*)(offset*sizeof(float)*4)); + + int s2 = glGetAttribLocation(render->mEllipsoidDepthProgram, "q2"); + glEnableVertexAttribArray(s2); + glBindBuffer(GL_ARRAY_BUFFER, buffers.mAnisotropyVBO[1]); + glVertexAttribPointer(s2, 4, GL_FLOAT, GL_FALSE, 0, 0);//(void*)(offset*sizeof(float)*4)); + + int s3 = glGetAttribLocation(render->mEllipsoidDepthProgram, "q3"); + glEnableVertexAttribArray(s3); + glBindBuffer(GL_ARRAY_BUFFER, buffers.mAnisotropyVBO[2]); + glVertexAttribPointer(s3, 4, GL_FLOAT, GL_FALSE, 0, 0);// (void*)(offset*sizeof(float)*4)); + + glVerify(glDrawArrays(GL_POINTS, offset, n)); + + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableVertexAttribArray(s1); + glDisableVertexAttribArray(s2); + glDisableVertexAttribArray(s3); + + glDisable(GL_POINT_SPRITE); + + //--------------------------------------------------------------- + // blur + + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + glVerify(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render->mDepthSmoothTex, 0)); + glUseProgram(render->mDepthBlurProgram); + + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_RECTANGLE_ARB); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, render->mDepthTex); + + glActiveTexture(GL_TEXTURE1); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, render->mThicknessTex); + + glVerify(glUniform1f( glGetUniformLocation(render->mDepthBlurProgram, "blurRadiusWorld"), radius*0.5f)); // blur half the radius by default + glVerify(glUniform1f( glGetUniformLocation(render->mDepthBlurProgram, "blurScale"), screenWidth/screenAspect * (1.0f / (tanf(fov*0.5f))))); + glVerify(glUniform2fv( glGetUniformLocation(render->mDepthBlurProgram, "invTexScale"), 1, Vec2(1.0f/screenAspect, 1.0f))); + glVerify(glUniform1f( glGetUniformLocation(render->mDepthBlurProgram, "blurFalloff"), blur)); + glVerify(glUniform1i( glGetUniformLocation(render->mDepthBlurProgram, "depthTex"), 0)); + glVerify(glUniform1i( glGetUniformLocation(render->mDepthBlurProgram, "thicknessTex"), 1)); + glVerify(glUniform1i(glGetUniformLocation(render->mDepthBlurProgram, "debug"), debug)); + + glVerify(RenderFullscreenQuad()); + + glActiveTexture(GL_TEXTURE0); + glDisable(GL_TEXTURE_RECTANGLE_ARB); + + //--------------------------------------------------------------- + // composite with scene + + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, g_msaaFbo)); + glVerify(glEnable(GL_DEPTH_TEST)); + glVerify(glDepthMask(GL_TRUE)); + glVerify(glDisable(GL_BLEND)); + glVerify(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + + glVerify(glUseProgram(render->mCompositeProgram)); + + glVerify(glUniform2fv(glGetUniformLocation(render->mCompositeProgram, "invTexScale"), 1, Vec2(1.0f/screenWidth, screenAspect/screenWidth))); + glVerify(glUniform2fv(glGetUniformLocation(render->mCompositeProgram, "clipPosToEye"), 1, Vec2(tanf(fov*0.5f)*screenAspect, tanf(fov*0.5f)))); + glVerify(glUniform4fv(glGetUniformLocation(render->mCompositeProgram, "color"), 1, color)); + glVerify(glUniform1f(glGetUniformLocation(render->mCompositeProgram, "ior"), ior)); + glVerify(glUniform1f(glGetUniformLocation(render->mCompositeProgram, "spotMin"), gSpotMin)); + glVerify(glUniform1f(glGetUniformLocation(render->mCompositeProgram, "spotMax"), gSpotMax)); + glVerify(glUniform1i(glGetUniformLocation(render->mCompositeProgram, "debug"), debug)); + + glVerify(glUniform3fv(glGetUniformLocation(render->mCompositeProgram, "lightPos"), 1, lightPos)); + glVerify(glUniform3fv(glGetUniformLocation(render->mCompositeProgram, "lightDir"), 1, -Normalize(lightTarget-lightPos))); + glVerify(glUniformMatrix4fv(glGetUniformLocation(render->mCompositeProgram, "lightTransform"), 1, false, lightTransform)); + + const Vec2 taps[] = + { + Vec2(-0.326212f,-0.40581f),Vec2(-0.840144f,-0.07358f), + Vec2(-0.695914f,0.457137f),Vec2(-0.203345f,0.620716f), + Vec2(0.96234f,-0.194983f),Vec2(0.473434f,-0.480026f), + Vec2(0.519456f,0.767022f),Vec2(0.185461f,-0.893124f), + Vec2(0.507431f,0.064425f),Vec2(0.89642f,0.412458f), + Vec2(-0.32194f,-0.932615f),Vec2(-0.791559f,-0.59771f) + }; + + glVerify(glUniform2fv(glGetUniformLocation(render->mCompositeProgram, "shadowTaps"), 12, &taps[0].x)); + + // smoothed depth tex + glVerify(glActiveTexture(GL_TEXTURE0)); + glVerify(glEnable(GL_TEXTURE_2D)); + glVerify(glBindTexture(GL_TEXTURE_2D, render->mDepthSmoothTex)); + + // shadow tex + glVerify(glActiveTexture(GL_TEXTURE1)); + glVerify(glEnable(GL_TEXTURE_2D)); + glVerify(glBindTexture(GL_TEXTURE_2D, shadowMap->texture)); + + // thickness tex + glVerify(glActiveTexture(GL_TEXTURE2)); + glVerify(glEnable(GL_TEXTURE_2D)); + glVerify(glBindTexture(GL_TEXTURE_2D, render->mThicknessTex)); + + // scene tex + glVerify(glActiveTexture(GL_TEXTURE3)); + glVerify(glEnable(GL_TEXTURE_2D)); + glVerify(glBindTexture(GL_TEXTURE_2D, render->mSceneTex)); + + /* + // reflection tex + glVerify(glActiveTexture(GL_TEXTURE5)); + glVerify(glEnable(GL_TEXTURE_2D)); + glVerify(glBindTexture(GL_TEXTURE_2D, reflectMap->texture)); + */ + + glVerify(glUniform1i(glGetUniformLocation(render->mCompositeProgram, "tex"), 0)); + glVerify(glUniform1i(glGetUniformLocation(render->mCompositeProgram, "shadowTex"), 1)); + glVerify(glUniform1i(glGetUniformLocation(render->mCompositeProgram, "thicknessTex"), 2)); + glVerify(glUniform1i(glGetUniformLocation(render->mCompositeProgram, "sceneTex"), 3)); + glVerify(glUniform1i(glGetUniformLocation(render->mCompositeProgram, "reflectTex"), 5)); + + // -- end shadowing + + // ignores projection matrices + glVerify(RenderFullscreenQuad()); + + // reset state + glActiveTexture(GL_TEXTURE5); + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE3); + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE2); + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glDisable(GL_TEXTURE_2D); + + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glDisable(GL_BLEND); +} + +//------------------------------------------------------------------------------ +// Diffuse Shading + +const char *vertexDiffuseShader = STRINGIFY( + +uniform float pointRadius; // point size in world space +uniform float pointScale; // scale to calculate size in pixels +uniform vec3 lightPos; +uniform vec3 lightDir; +uniform mat4 lightTransform; +uniform float spotMin; +uniform float spotMax; +uniform vec4 color; + + +void main() +{ + vec3 worldPos = gl_Vertex.xyz;// - vec3(0.0, 0.1*0.25, 0.0); // hack move towards ground to account for anisotropy; + vec4 eyePos = gl_ModelViewMatrix * vec4(worldPos, 1.0); + + gl_Position = gl_ProjectionMatrix * eyePos; + //gl_Position.z -= 0.0025; // bias above fluid surface + + // calculate window-space point size + gl_PointSize = pointRadius * (pointScale / gl_Position.w); + + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = vec4(worldPos, gl_Vertex.w); + gl_TexCoord[2] = eyePos; + + gl_TexCoord[3].xyz = gl_ModelViewMatrix*vec4(gl_MultiTexCoord1.xyz, 0.0); + gl_TexCoord[4].xyzw = color; + + // hack to color different emitters + if (gl_MultiTexCoord1.w == 2.0) + gl_TexCoord[4].xyzw = vec4(0.85, 0.65, 0.65, color.w); + else if (gl_MultiTexCoord1.w == 1.0) + gl_TexCoord[4].xyzw = vec4(0.65, 0.85, 0.65, color.w); + + // compute ndc pos for frustrum culling in GS + vec4 ndcPos = gl_ModelViewProjectionMatrix * vec4(worldPos.xyz, 1.0); + gl_TexCoord[5] = ndcPos / ndcPos.w; +} +); + + + + +const char *geometryDiffuseShader = +"#version 120\n" +"#extension GL_EXT_geometry_shader4 : enable\n" +STRINGIFY( + +uniform float pointScale; // point size in world space +uniform float motionBlurScale; +uniform float diffusion; +uniform vec3 lightDir; + +void main() +{ + vec4 ndcPos = gl_TexCoordIn[0][5]; + + // frustrum culling + const float ndcBound = 1.0; + if (ndcPos.x < -ndcBound) return; + if (ndcPos.x > ndcBound) return; + if (ndcPos.y < -ndcBound) return; + if (ndcPos.y > ndcBound) return; + + float velocityScale = 1.0; + + vec3 v = gl_TexCoordIn[0][3].xyz*velocityScale; + vec3 p = gl_TexCoordIn[0][2].xyz; + + // billboard in eye space + vec3 u = vec3(0.0, pointScale, 0.0); + vec3 l = vec3(pointScale, 0.0, 0.0); + + // increase size based on life + float lifeFade = mix(1.0f+diffusion, 1.0, min(1.0, gl_TexCoordIn[0][1].w*0.25f)); + u *= lifeFade; + l *= lifeFade; + + //lifeFade = 1.0; + + float fade = 1.0/(lifeFade*lifeFade); + float vlen = length(v)*motionBlurScale; + + if (vlen > 0.5) + { + float len = max(pointScale, vlen*0.016); + fade = min(1.0, 2.0/(len/pointScale)); + + u = normalize(v)*max(pointScale, vlen*0.016); // assume 60hz + l = normalize(cross(u, vec3(0.0, 0.0, -1.0)))*pointScale; + } + + { + + gl_TexCoord[1] = gl_TexCoordIn[0][1]; // vertex world pos (life in w) + gl_TexCoord[2] = gl_TexCoordIn[0][2]; // vertex eye pos + gl_TexCoord[3] = gl_TexCoordIn[0][3]; // vertex velocity in view space + gl_TexCoord[3].w = fade; + gl_TexCoord[4] = gl_ModelViewMatrix*vec4(lightDir, 0.0); + gl_TexCoord[4].w = gl_TexCoordIn[0][3].w; // attenuation + gl_TexCoord[5].xyzw = gl_TexCoordIn[0][4].xyzw; // color + + float zbias = 0.0f;//0.00125*2.0; + + gl_TexCoord[0] = vec4(0.0, 1.0, 0.0, 0.0); + gl_Position = gl_ProjectionMatrix * vec4(p + u - l, 1.0); + gl_Position.z -= zbias; + EmitVertex(); + + gl_TexCoord[0] = vec4(0.0, 0.0, 0.0, 0.0); + gl_Position = gl_ProjectionMatrix * vec4(p - u - l, 1.0); + gl_Position.z -= zbias; + EmitVertex(); + + gl_TexCoord[0] = vec4(1.0, 1.0, 0.0, 0.0); + gl_Position = gl_ProjectionMatrix * vec4(p + u + l, 1.0); + gl_Position.z -= zbias; + EmitVertex(); + + gl_TexCoord[0] = vec4(1.0, 0.0, 0.0, 0.0); + gl_Position = gl_ProjectionMatrix * vec4(p - u + l, 1.0); + gl_Position.z -= zbias; + EmitVertex(); + } +} +); + +const char *fragmentDiffuseShader = STRINGIFY( + +float sqr(float x) { return x*x; } +float cube(float x) { return x*x*x; } + +uniform sampler2D depthTex; +uniform sampler2D noiseTex; +uniform vec2 invViewport; +uniform vec4 color; +uniform bool front; +uniform bool shadow; + +//uniform sampler2DShadow shadowTex; +uniform sampler2D shadowTex; +uniform vec2 shadowTaps[12]; +uniform mat4 lightTransform; +uniform vec3 lightDir; +uniform float inscatterCoefficient; +uniform float outscatterCoefficient; + +void main() +{ + float attenuation = gl_TexCoord[4].w; + float lifeFade = min(1.0, gl_TexCoord[1].w*0.125); + + // calculate normal from texture coordinates + vec3 normal; + normal.xy = gl_TexCoord[0].xy*vec2(2.0, 2.0) + vec2(-1.0, -1.0); + float mag = dot(normal.xy, normal.xy); + if (mag > 1.0) discard; // kill pixels outside circle + normal.z = 1.0-mag; + + float velocityFade = gl_TexCoord[3].w; + float alpha = lifeFade*velocityFade*sqr(normal.z); + + gl_FragColor = alpha; +} +); + +void RenderDiffuse(FluidRenderer* render, DiffuseRenderBuffers buffers, int n, float radius, float screenWidth, float screenAspect, float fov, Vec4 color, Vec3 lightPos, Vec3 lightTarget, Matrix44 lightTransform, ShadowMap* shadowMap, float motionBlur, float inscatter, float outscatter, bool shadow, bool front) +{ + static int sprogram = -1; + if (sprogram == -1) + sprogram = CompileProgram(vertexDiffuseShader, fragmentDiffuseShader, geometryDiffuseShader); + + int thicknessScale = 1; + + if (sprogram) + { +#if USE_HDR_DIFFUSE_BLEND + + { + glVerify(glBindFramebuffer(GL_READ_FRAMEBUFFER, g_msaaFbo)); + glVerify(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, render->mThicknessFbo)); + glVerify(glBlitFramebuffer(0, 0, render->mSceneWidth, render->mSceneHeight, 0, 0, render->mSceneWidth/thicknessScale, render->mSceneHeight/thicknessScale, GL_DEPTH_BUFFER_BIT, GL_NEAREST)); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + } +#endif + + glEnable(GL_POINT_SPRITE); + glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + glDepthMask(GL_FALSE); + glEnable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glDisable(GL_CULL_FACE); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + + glUseProgram(sprogram); + glUniform1f( glGetUniformLocation(sprogram, "motionBlurScale"), motionBlur); + glUniform1f( glGetUniformLocation(sprogram, "diffusion"), 1.0f); + glUniform1f( glGetUniformLocation(sprogram, "pointScale"), radius*1.0f); + glUniform1f( glGetUniformLocation(sprogram, "pointRadius"), screenWidth / float(thicknessScale) / (2.0f*screenAspect*tanf(fov*0.5f))); + glUniform2fv( glGetUniformLocation(sprogram, "invViewport"), 1, Vec2(1.0f/screenWidth, screenAspect/screenWidth)); + glUniform4fv( glGetUniformLocation(sprogram, "color"), 1, color); + glUniform1i( glGetUniformLocation(sprogram, "tex"), 0); + glUniform1f( glGetUniformLocation(sprogram, "inscatterCoefficient"), inscatter); + glUniform1f( glGetUniformLocation(sprogram, "outscatterCoefficient"), outscatter); + + GLint uLightTransform = glGetUniformLocation(sprogram, "lightTransform"); + glUniformMatrix4fv(uLightTransform, 1, false, lightTransform); + + GLint uLightPos = glGetUniformLocation(sprogram, "lightPos"); + glUniform3fv(uLightPos, 1, lightPos); + + GLint uLightDir = glGetUniformLocation(sprogram, "lightDir"); + glUniform3fv(uLightDir, 1, Normalize(lightTarget-lightPos)); + + glUniform1f( glGetUniformLocation(sprogram, "spotMin"), gSpotMin); + glUniform1f( glGetUniformLocation(sprogram, "spotMax"), gSpotMax); + + const Vec2 taps[] = + { + Vec2(-0.326212f,-0.40581f),Vec2(-0.840144f,-0.07358f), + Vec2(-0.695914f,0.457137f),Vec2(-0.203345f,0.620716f), + Vec2(0.96234f,-0.194983f),Vec2(0.473434f,-0.480026f), + Vec2(0.519456f,0.767022f),Vec2(0.185461f,-0.893124f), + Vec2(0.507431f,0.064425f),Vec2(0.89642f,0.412458f), + Vec2(-0.32194f,-0.932615f),Vec2(-0.791559f,-0.59771f) + }; + + glVerify(glUniform2fv(glGetUniformLocation(sprogram, "shadowTaps"), 12, &taps[0].x)); + glVerify(glUniform1i(glGetUniformLocation(sprogram, "noiseTex"), 2)); + glVerify(glUniform1i(glGetUniformLocation(sprogram, "shadowTex"), 1)); + glVerify(glUniform1i(glGetUniformLocation(sprogram, "depthTex"), 0)); + glVerify(glUniform1i(glGetUniformLocation(sprogram, "front"), front)); + glVerify(glUniform1i(glGetUniformLocation(sprogram, "shadow"), shadow)); + + // noise tex + //glActiveTexture(GL_TEXTURE2); + //glEnable(GL_TEXTURE_2D); + //glBindTexture(GL_TEXTURE_2D, noiseTex); + + // shadow tex + glActiveTexture(GL_TEXTURE1); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, shadowMap->texture); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE)); + //glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL)); + + + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, render->mDepthSmoothTex); + + glClientActiveTexture(GL_TEXTURE1); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, buffers.mDiffuseVelocityVBO)); + glTexCoordPointer(4, GL_FLOAT, sizeof(float)*4, 0); + + glEnableClientState(GL_VERTEX_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, buffers.mDiffusePositionVBO); + glVertexPointer(4, GL_FLOAT, sizeof(float)*4, 0); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers.mDiffuseIndicesIBO); + + glDrawElements(GL_POINTS, n, GL_UNSIGNED_INT, 0); + + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_POINT_SPRITE); + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); + + glVerify(glActiveTexture(GL_TEXTURE2)); + glVerify(glDisable(GL_TEXTURE_2D)); + glVerify(glActiveTexture(GL_TEXTURE1)); + glVerify(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE)); + glVerify(glDisable(GL_TEXTURE_2D)); + glVerify(glActiveTexture(GL_TEXTURE0)); + glVerify(glDisable(GL_TEXTURE_2D)); + +#if USE_HDR_DIFFUSE_BLEND + + { + glVerify(glBindFramebuffer(GL_FRAMEBUFFER, g_msaaFbo)); + glVerify(glViewport(0, 0, int(screenWidth), int(screenWidth/screenAspect))); + + //glClear(GL_COLOR_BUFFER_BIT); + glUseProgram(0); + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(GL_FALSE); + glDisable(GL_CULL_FACE); + + glVerify(glActiveTexture(GL_TEXTURE0)); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, render->mThicknessTex); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluOrtho2D(-1.0f, 1.0f, -1.0f, 1.0); + + RenderFullscreenQuad(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glDepthMask(GL_TRUE); + } +#endif + + } +} + + +struct GpuMesh +{ + GLuint mPositionsVBO; + GLuint mNormalsVBO; + GLuint mIndicesIBO; + + int mNumVertices; + int mNumFaces; +}; + +GpuMesh* CreateGpuMesh(const Mesh* m) +{ + GpuMesh* mesh = new GpuMesh(); + + mesh->mNumVertices = m->GetNumVertices(); + mesh->mNumFaces = m->GetNumFaces(); + + // vbos + glVerify(glGenBuffers(1, &mesh->mPositionsVBO)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, mesh->mPositionsVBO)); + glVerify(glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*m->m_positions.size(), &m->m_positions[0], GL_STATIC_DRAW)); + + glVerify(glGenBuffers(1, &mesh->mNormalsVBO)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, mesh->mNormalsVBO)); + glVerify(glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*m->m_normals.size(), &m->m_normals[0], GL_STATIC_DRAW)); + + glVerify(glGenBuffers(1, &mesh->mIndicesIBO)); + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->mIndicesIBO)); + glVerify(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*m->m_indices.size(), &m->m_indices[0], GL_STATIC_DRAW)); + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + return mesh; +} + +void DestroyGpuMesh(GpuMesh* m) +{ + glVerify(glDeleteBuffers(1, &m->mPositionsVBO)); + glVerify(glDeleteBuffers(1, &m->mNormalsVBO)); + glVerify(glDeleteBuffers(1, &m->mIndicesIBO)); +} + +void DrawGpuMesh(GpuMesh* m, const Matrix44& xform, const Vec3& color) +{ + if (m) + { + GLint program; + glGetIntegerv(GL_CURRENT_PROGRAM, &program); + + if (program) + glUniformMatrix4fv( glGetUniformLocation(program, "objectTransform"), 1, false, xform); + + glVerify(glColor3fv(color)); + glVerify(glSecondaryColor3fv(color)); + + glVerify(glEnableClientState(GL_VERTEX_ARRAY)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, m->mPositionsVBO)); + glVerify(glVertexPointer(3, GL_FLOAT, sizeof(float)*3, 0)); + + glVerify(glEnableClientState(GL_NORMAL_ARRAY)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, m->mNormalsVBO)); + glVerify(glNormalPointer(GL_FLOAT, sizeof(float)*3, 0)); + + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->mIndicesIBO)); + + glVerify(glDrawElements(GL_TRIANGLES, m->mNumFaces*3, GL_UNSIGNED_INT, 0)); + + glVerify(glDisableClientState(GL_VERTEX_ARRAY)); + glVerify(glDisableClientState(GL_NORMAL_ARRAY)); + + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + if (program) + glUniformMatrix4fv(glGetUniformLocation(program, "objectTransform"), 1, false, Matrix44::kIdentity); + } +} + +void DrawGpuMeshInstances(GpuMesh* m, const Matrix44* xforms, int n, const Vec3& color) +{ + if (m) + { + GLint program; + glGetIntegerv(GL_CURRENT_PROGRAM, &program); + + GLint param = glGetUniformLocation(program, "objectTransform"); + + glVerify(glColor3fv(color)); + glVerify(glSecondaryColor3fv(color)); + + glVerify(glEnableClientState(GL_VERTEX_ARRAY)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, m->mPositionsVBO)); + glVerify(glVertexPointer(3, GL_FLOAT, sizeof(float)*3, 0)); + + glVerify(glEnableClientState(GL_NORMAL_ARRAY)); + glVerify(glBindBuffer(GL_ARRAY_BUFFER, m->mNormalsVBO)); + glVerify(glNormalPointer(GL_FLOAT, sizeof(float)*3, 0)); + + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->mIndicesIBO)); + + for (int i=0; i < n; ++i) + { + if (program) + glUniformMatrix4fv( param, 1, false, xforms[i]); + + glVerify(glDrawElements(GL_TRIANGLES, m->mNumFaces*3, GL_UNSIGNED_INT, 0)); + } + + glVerify(glDisableClientState(GL_VERTEX_ARRAY)); + glVerify(glDisableClientState(GL_NORMAL_ARRAY)); + + glVerify(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } +} + +void BeginLines() +{ + glUseProgram(0); + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + + glLineWidth(1.0f); + + for (int i = 0; i < 8; ++i) + { + glActiveTexture(GL_TEXTURE0 + i); + glDisable(GL_TEXTURE_2D); + } + + glBegin(GL_LINES); +} + +void DrawLine(const Vec3& p, const Vec3& q, const Vec4& color) +{ + glColor4fv(color); + glVertex3fv(p); + glVertex3fv(q); +} + +void EndLines() +{ + glEnd(); +} + +void BeginPoints(float size) +{ + glPointSize(size); + + glUseProgram(0); + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_POINT_SPRITE); + glEnable(GL_POINT_SMOOTH); + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + + for (int i = 0; i < 8; ++i) + { + glActiveTexture(GL_TEXTURE0 + i); + glDisable(GL_TEXTURE_2D); + } + + glBegin(GL_POINTS); +} + +void DrawPoint(const Vec3& p, const Vec4& color) +{ + glColor3fv(color); + glVertex3fv(p); +} + +void EndPoints() +{ + glEnd(); +} + |