2122 lines
62 KiB
C
2122 lines
62 KiB
C
#include <Windows.h>
|
|
#include <math.h>
|
|
|
|
////////////////////////////////
|
|
//- Experiment Configuration
|
|
|
|
// When this is 0 colors in vertex attributes and uniforms
|
|
// that are converted (sRGB -> Linear) are converted using
|
|
// the approximate (sRGB -> Linear) (f(x)=x^2.2); When this
|
|
// is 1 those colors are converted using an accurate
|
|
// (sRGB -> Linear) conversion.
|
|
// This has no effect on colors sampled from sRGB textures
|
|
// which are not converted by shader code, but by the OpenGL
|
|
// implementation itself. By default this is zero to show the
|
|
// difference between the approximate and accurate
|
|
// conversions in test #3.
|
|
#define USE_ACCURATE_CONVERSIONS_IN_SHADER 0
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
//- Mini-Base
|
|
|
|
#include <stdint.h>
|
|
typedef int8_t S8;
|
|
typedef int16_t S16;
|
|
typedef int32_t S32;
|
|
typedef int64_t S64;
|
|
typedef uint8_t U8;
|
|
typedef uint16_t U16;
|
|
typedef uint32_t U32;
|
|
typedef uint64_t U64;
|
|
typedef S8 B8;
|
|
typedef S16 B16;
|
|
typedef S32 B32;
|
|
typedef S64 B64;
|
|
typedef float F32;
|
|
typedef double F64;
|
|
|
|
#define AssertBreak() (*(volatile int*)0 = 0)
|
|
#define Assert(c) do{ if (!(c)){ AssertBreak(); } }while(0)
|
|
|
|
#define U64FromPtr(ptr) (U64)(ptr)
|
|
#define PtrFromU64(ptr) (void*)(x)
|
|
|
|
#define ArrayCount(a) ((sizeof(a))/(sizeof(*a)))
|
|
#define Member(T,m) ((T*)0)->m
|
|
#define PtrOffsetOf(T,m) (void*)(&Member(T,m))
|
|
#define OffsetOf(T,m) U64FromPtr(&Member(T,m))
|
|
|
|
#define Clamp(a,x,b) (((x)<(a))?(a):((b)<(x))?(b):(x))
|
|
|
|
#define allocate_array(T,c) (T*)(malloc(sizeof(T)*(c)))
|
|
|
|
|
|
////////////////////////////////
|
|
//- Types, Functions & Globals
|
|
|
|
static void error_message(char *msg);
|
|
|
|
//- wgl stuff
|
|
static HGLRC graphics_context = 0;
|
|
static HWND graphics_window = 0;
|
|
static HDC graphics_dc = 0;
|
|
static F32 graphics_w = 0;
|
|
static F32 graphics_h = 0;
|
|
|
|
static void wgl_helper_make_graphics_window(HINSTANCE hInstance);
|
|
static void wgl_helper_begin_render(void);
|
|
static void wgl_helper_end_render(void);
|
|
|
|
//- opengl render stuff
|
|
typedef struct Vertex{
|
|
F32 px;
|
|
F32 py;
|
|
F32 uvx;
|
|
F32 uvy;
|
|
F32 cr;
|
|
F32 cg;
|
|
F32 cb;
|
|
F32 ca;
|
|
F32 rx0;
|
|
F32 ry0;
|
|
F32 rx1;
|
|
F32 ry1;
|
|
} Vertex;
|
|
|
|
|
|
static void opengl_render_init(void);
|
|
|
|
static void opengl_draw_basic_geometry(Vertex *v, U64 count);
|
|
static void opengl_draw_srgb_in_geometry(Vertex *v, U64 count);
|
|
static void opengl_draw_rect_geometry(Vertex *v, U64 count);
|
|
static void opengl_draw_texture_geometry(Vertex *v, U64 count, U32 texture);
|
|
static void opengl_draw_texrgb_geometry(Vertex *v, U64 count, U32 texture,
|
|
F32 r, F32 g, F32 b, F32 a);
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
//- Circle Generator
|
|
|
|
static F32
|
|
integral_indef(F32 r, F32 x){
|
|
Assert(fabs(x) <= fabs(r));
|
|
F32 t = asinf(x/r);
|
|
F32 result = r*r*0.5f*(t + 0.5f*sinf(2*t));
|
|
return(result);
|
|
}
|
|
|
|
static F32
|
|
integral_def(F32 r, F32 min_x, F32 max_x){
|
|
F32 max_v = integral_indef(r, max_x);
|
|
F32 min_v = integral_indef(r, min_x);
|
|
F32 result = max_v - min_v;
|
|
return(result);
|
|
}
|
|
|
|
static B32
|
|
approx_equal(F32 a, F32 b){
|
|
B32 result = (a - 0.0078125 <= b && b <= a + 0.0078125);
|
|
return(result);
|
|
}
|
|
|
|
static void
|
|
sanity_test_integrals(void){
|
|
{
|
|
F32 a = integral_def(1.f, -1.f, 1.f);
|
|
Assert(approx_equal(a, 3.141593f*0.5f));
|
|
}
|
|
{
|
|
F32 a = integral_def(2.f, -2.f, 2.f);
|
|
Assert(approx_equal(a, 3.141593f*2.f));
|
|
}
|
|
{
|
|
F32 a = integral_def(2.f, 0.f, 2.f);
|
|
Assert(approx_equal(a, 3.141593f));
|
|
}
|
|
{
|
|
F32 a0 = integral_def(3.f, 0.f, 1.f);
|
|
F32 a1 = integral_def(3.f, 1.f, 2.f);
|
|
F32 a2 = integral_def(3.f, 2.f, 3.f);
|
|
Assert(approx_equal(a0 + a1 + a2, 3.141593f*9.f/4.f));
|
|
}
|
|
}
|
|
|
|
static F32
|
|
intersection_area(F32 cir_r, F32 unit_box_x0, F32 unit_box_y0){
|
|
F32 rr = cir_r*cir_r;
|
|
|
|
F32 rel_y0 = unit_box_y0;
|
|
F32 rel_y1 = unit_box_y0 + 1.f;
|
|
F32 rel_x0 = unit_box_x0;
|
|
F32 rel_x1 = unit_box_x0 + 1.f;
|
|
|
|
F32 y0y0 = rel_y0*rel_y0;
|
|
F32 y1y1 = rel_y1*rel_y1;
|
|
|
|
F32 x0x0 = rel_x0*rel_x0;
|
|
F32 x1x1 = rel_x1*rel_x1;
|
|
|
|
// check each corner for hitting the circle
|
|
B32 corner_bl = ((y0y0 + x0x0) <= rr);
|
|
B32 corner_tl = ((y1y1 + x0x0) <= rr);
|
|
B32 corner_br = ((y0y0 + x1x1) <= rr);
|
|
B32 corner_tr = ((y1y1 + x1x1) <= rr);
|
|
|
|
// calculate area
|
|
F32 a = 0.f;
|
|
#define COMB(a,b,c,d) ((a)|((b)<<1)|((c)<<2)|((d)<<3))
|
|
switch (COMB(corner_bl, corner_tl, corner_br, corner_tr)){
|
|
// full miss
|
|
case 0x0: a = 0.f; break;
|
|
// full hit
|
|
case 0xF: a = 1.f; break;
|
|
|
|
// 1 corner hit
|
|
case COMB(1,0,0,0): case COMB(0,1,0,0):
|
|
case COMB(0,0,1,0): case COMB(0,0,0,1):
|
|
{
|
|
F32 intersection_y = 0.f;
|
|
if (corner_bl || corner_br){
|
|
intersection_y = rel_y0;
|
|
}
|
|
else{
|
|
intersection_y = rel_y1;
|
|
}
|
|
|
|
F32 intersection_x = sqrtf(rr - intersection_y*intersection_y);
|
|
if (intersection_x < rel_x0 || rel_x1 < intersection_x){
|
|
intersection_x = -intersection_x;
|
|
}
|
|
|
|
F32 x_min = 0.f;
|
|
F32 x_max = 0.f;
|
|
if (corner_bl || corner_tl){
|
|
x_min = rel_x0;
|
|
x_max = intersection_x;
|
|
}
|
|
else{
|
|
x_min = intersection_x;
|
|
x_max = rel_x1;
|
|
}
|
|
|
|
F32 h = fabs(intersection_y);
|
|
a = integral_def(cir_r, x_min, x_max) - h*(x_max - x_min);
|
|
}break;
|
|
|
|
// 2 corner hits (horizontal edge)
|
|
case COMB(1,0,1,0): case COMB(0,1,0,1):
|
|
{
|
|
F32 h = 0.f;
|
|
if (corner_bl){
|
|
h = rel_y0;
|
|
}
|
|
else{
|
|
h = -rel_y1;
|
|
}
|
|
a = integral_def(cir_r, rel_x0, rel_x1) - h;
|
|
}break;
|
|
|
|
// 2 corner hits (vertical edge)
|
|
case COMB(1,1,0,0): case COMB(0,0,1,1):
|
|
{
|
|
F32 w = 0.f;
|
|
if (corner_bl){
|
|
w = rel_x0;
|
|
}
|
|
else{
|
|
w = -rel_x1;
|
|
}
|
|
a = integral_def(cir_r, rel_y0, rel_y1) - w;
|
|
}break;
|
|
|
|
// 3 corner hits
|
|
case COMB(0,1,1,1): case COMB(1,0,1,1):
|
|
case COMB(1,1,0,1): case COMB(1,1,1,0):
|
|
{
|
|
F32 intersection_y = 0.f;
|
|
if (!corner_bl || !corner_br){
|
|
intersection_y = rel_y0;
|
|
}
|
|
else{
|
|
intersection_y = rel_y1;
|
|
}
|
|
|
|
F32 intersection_x = sqrtf(rr - intersection_y*intersection_y);
|
|
if (intersection_x < rel_x0 || rel_x1 < intersection_x){
|
|
intersection_x = -intersection_x;
|
|
}
|
|
|
|
F32 w_extra = 0.f;
|
|
F32 x_min = 0.f;
|
|
F32 x_max = 0.f;
|
|
if (!corner_bl || !corner_tl){
|
|
x_min = rel_x0;
|
|
x_max = intersection_x;
|
|
w_extra = rel_x1 - intersection_x;
|
|
}
|
|
else{
|
|
x_min = intersection_x;
|
|
x_max = rel_x1;
|
|
w_extra = intersection_x - rel_x0;
|
|
}
|
|
|
|
F32 h = fabs(intersection_y) - 1.f;
|
|
a = integral_def(cir_r, x_min, x_max) - h*(x_max - x_min) + w_extra;
|
|
}break;
|
|
|
|
// these cases should be impossible
|
|
default:
|
|
{
|
|
a = -1.f;
|
|
}break;
|
|
}
|
|
#undef COMB
|
|
|
|
return(a);
|
|
}
|
|
|
|
static void
|
|
sanity_test_circle_area(void){
|
|
{
|
|
F32 cr = 5.f;
|
|
F32 cx = 5.f;
|
|
F32 cy = 5.f;
|
|
F32 sum_area = 0.f;
|
|
for (U32 y = 0; y < 10; y += 1){
|
|
F32 box_y0 = (F32)y;
|
|
for (U32 x = 0; x < 10; x += 1){
|
|
F32 box_x0 = (F32)x;
|
|
F32 a = intersection_area(cr, box_x0 - cx, box_y0 - cy);
|
|
Assert(0.f <= a && a <= 1.f);
|
|
sum_area += a;
|
|
}
|
|
}
|
|
F32 formula_area = 3.141593f*cr*cr;
|
|
Assert(approx_equal(sum_area, formula_area));
|
|
}
|
|
|
|
{
|
|
F32 cr = 8.f;
|
|
F32 cx = 8.1f;
|
|
F32 cy = 8.7f;
|
|
F32 sum_area = 0.f;
|
|
for (U32 y = 0; y < 20; y += 1){
|
|
F32 box_y0 = (F32)y;
|
|
for (U32 x = 0; x < 20; x += 1){
|
|
F32 box_x0 = (F32)x;
|
|
F32 a = intersection_area(cr, box_x0 - cx, box_y0 - cy);
|
|
Assert(0.f <= a && a <= 1.f);
|
|
sum_area += a;
|
|
}
|
|
}
|
|
F32 formula_area = 3.141593f*cr*cr;
|
|
Assert(approx_equal(sum_area, formula_area));
|
|
}
|
|
}
|
|
|
|
static void
|
|
render_circle_in_buffer(F32 cir_r, F32 cir_x, F32 cir_y,
|
|
U8 *buf, U32 buf_side_length){
|
|
for (U32 y = 0; y < buf_side_length; y += 1){
|
|
F32 box_y0 = (F32)y;
|
|
for (U32 x = 0; x < buf_side_length; x += 1){
|
|
F32 box_x0 = (F32)x;
|
|
|
|
// calculate area of the intersection (circle & box)
|
|
F32 a = intersection_area(cir_r, box_x0 - cir_x, box_y0 - cir_y);
|
|
Assert(0.f <= a && a <= 1.f);
|
|
|
|
U32 vraw = (U32)(256.f*a);
|
|
U8 v = (vraw <= 255)?((U8)vraw):(U8)255;
|
|
buf[x + buf_side_length*y] = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
//- GL Definitions
|
|
|
|
//- gl types
|
|
|
|
typedef U32 GLenum;
|
|
typedef F32 GLfloat;
|
|
typedef U32 GLbitfield;
|
|
typedef U32 GLuint;
|
|
typedef S32 GLint;
|
|
typedef S32 GLsizei;
|
|
typedef S64 GLsizeiptr;
|
|
typedef S64 GLintptr;
|
|
typedef char GLchar;
|
|
typedef unsigned char GLboolean;
|
|
|
|
//- gl constants
|
|
|
|
#define GL_COLOR_BUFFER_BIT 0x00004000
|
|
|
|
#define GL_TRIANGLES 0x0004
|
|
|
|
#define GL_SRC_ALPHA 0x0302
|
|
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
|
|
|
|
#define GL_BLEND 0x0BE2
|
|
|
|
#define GL_TEXTURE_2D 0x0DE1
|
|
|
|
#define GL_UNSIGNED_BYTE 0x1401
|
|
#define GL_FLOAT 0x1406
|
|
|
|
#define GL_DEPTH_COMPONENT 0x1902
|
|
#define GL_RED 0x1903
|
|
#define GL_RGB 0x1907
|
|
#define GL_RGBA 0x1908
|
|
|
|
#define GL_NEAREST 0x2600
|
|
#define GL_LINEAR 0x2601
|
|
|
|
#define GL_TEXTURE_MAG_FILTER 0x2800
|
|
#define GL_TEXTURE_MIN_FILTER 0x2801
|
|
#define GL_TEXTURE_WRAP_S 0x2802
|
|
#define GL_TEXTURE_WRAP_T 0x2803
|
|
|
|
#define GL_MULTISAMPLE 0x809D
|
|
|
|
#define GL_CLAMP_TO_EDGE 0x812F
|
|
|
|
#define GL_TEXTURE0 0x84C0
|
|
|
|
#define GL_STREAM_DRAW 0x88E0
|
|
|
|
#define GL_ARRAY_BUFFER 0x8892
|
|
|
|
#define GL_FRAGMENT_SHADER 0x8B30
|
|
#define GL_VERTEX_SHADER 0x8B31
|
|
|
|
#define GL_COMPILE_STATUS 0x8B81
|
|
#define GL_LINK_STATUS 0x8B82
|
|
#define GL_INFO_LOG_LENGTH 0x8B84
|
|
|
|
#define GL_SRGB 0x8C40
|
|
#define GL_SRGB8 0x8C41
|
|
|
|
#define GL_READ_FRAMEBUFFER 0x8CA8
|
|
#define GL_DRAW_FRAMEBUFFER 0x8CA9
|
|
|
|
#define GL_COLOR_ATTACHMENT0 0x8CE0
|
|
|
|
#define GL_FRAMEBUFFER 0x8D40
|
|
#define GL_FRAMEBUFFER_SRGB 0x8DB9
|
|
|
|
#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
|
|
|
|
//- gl func x-list
|
|
|
|
#define GL_FUNC_X_LIST() \
|
|
/* common stuff */ \
|
|
X(glGetError, GLenum, (void)) \
|
|
X(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \
|
|
X(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \
|
|
X(glClearColor, void, (GLfloat r,GLfloat g,GLfloat b,GLfloat a)) \
|
|
X(glClear, void, (GLbitfield mask)) \
|
|
X(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \
|
|
X(glBlendFuncSeparate, void, (GLenum srcRGB,GLenum dstRGB,GLenum srcAlpha,GLenum dstAlpha)) \
|
|
X(glBlendEquation, void, (GLenum mode)) \
|
|
X(glBlendEquationSeparate, void, (GLenum modeRGB, GLenum modeAlpha)) \
|
|
X(glDisable, void, (GLenum cap)) \
|
|
X(glEnable, void, (GLenum cap)) \
|
|
X(glPixelStorei, void, (GLenum pname, GLint param)) \
|
|
X(glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels)) \
|
|
/* buffers */ \
|
|
X(glGenBuffers, void, (GLsizei n, GLuint *buffers)) \
|
|
X(glDeleteBuffers, void, (GLsizei n, const GLuint *buffers)) \
|
|
X(glBindBuffer, void, (GLenum target, GLuint buffer)) \
|
|
X(glBufferData, void, (GLenum target, GLsizeiptr size, const void *data, GLenum usage)) \
|
|
X(glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, const void *data)) \
|
|
X(glGenVertexArrays, void, (GLsizei n, GLuint *arrays)) \
|
|
X(glDeleteVertexArrays, void, (GLsizei n, const GLuint *arrays)) \
|
|
X(glBindVertexArray, void, (GLuint array)) \
|
|
/* textures */ \
|
|
X(glGenTextures, void, (GLsizei n, GLuint *textures)) \
|
|
X(glActiveTexture, void, (GLenum texture)) \
|
|
X(glDeleteTextures, void, (GLsizei n, const GLuint *textures)) \
|
|
X(glBindTexture, void, (GLenum target, GLuint texture)) \
|
|
X(glIsTexture, GLboolean, (GLuint texture)) \
|
|
X(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)) \
|
|
X(glTexImage1D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels)) \
|
|
X(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels)) \
|
|
X(glTexSubImage1D, void, (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels)) \
|
|
X(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)) \
|
|
X(glTexImage2DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)) \
|
|
/* shaders */ \
|
|
X(glAttachShader, void, (GLuint program, GLuint shader)) \
|
|
X(glCompileShader, void, (GLuint shader)) \
|
|
X(glCreateProgram, GLuint, (void)) \
|
|
X(glCreateShader, GLuint, (GLenum type)) \
|
|
X(glDeleteProgram, void, (GLuint program)) \
|
|
X(glDeleteShader, void, (GLuint shader)) \
|
|
X(glGetProgramiv, void, (GLuint program, GLenum pname, GLint *params)) \
|
|
X(glGetProgramInfoLog, void, (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog)) \
|
|
X(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint *params)) \
|
|
X(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog)) \
|
|
X(glLinkProgram, void, (GLuint program)) \
|
|
X(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length)) \
|
|
X(glUseProgram, void, (GLuint program)) \
|
|
X(glGetUniformLocation, GLint, (GLuint program, const GLchar *name)) \
|
|
/* draw calls */ \
|
|
X(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) \
|
|
X(glDrawElements, void, (GLenum mode, GLsizei count, GLenum type, const void *indices)) \
|
|
X(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \
|
|
X(glDrawElementsInstanced, void, (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount)) \
|
|
X(glEnableVertexAttribArray, void, (GLuint index)) \
|
|
X(glDisableVertexAttribArray, void, (GLuint index)) \
|
|
X(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer)) \
|
|
X(glVertexAttribIPointer, void, (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer)) \
|
|
X(glVertexAttribDivisor, void, (GLuint index, GLuint divisor)) \
|
|
X(glUniform1f, void, (GLint location, GLfloat v0)) \
|
|
X(glUniform2f, void, (GLint location, GLfloat v0, GLfloat v1)) \
|
|
X(glUniform3f, void, (GLint location, GLfloat v0, GLfloat v1, GLfloat v2)) \
|
|
X(glUniform4f, void, (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)) \
|
|
X(glUniform1i, void, (GLint location, GLint v0)) \
|
|
X(glUniform1fv, void, (GLint location, GLsizei count, const GLfloat *value)) \
|
|
X(glUniform2fv, void, (GLint location, GLsizei count, const GLfloat *value)) \
|
|
X(glUniform3fv, void, (GLint location, GLsizei count, const GLfloat *value)) \
|
|
X(glDrawBuffers, void, (GLsizei n, const GLenum *bufs)) \
|
|
/* framebuffers */ \
|
|
X(glGenFramebuffers, void, (GLsizei n, GLuint *framebuffers)) \
|
|
X(glDeleteFramebuffers, void, (GLsizei n, const GLuint *framebuffers)) \
|
|
X(glBindFramebuffer, void, (GLenum target, GLuint framebuffer)) \
|
|
X(glIsFramebuffer, GLboolean, (GLuint framebuffer)) \
|
|
X(glCheckFramebufferStatus, GLenum, (GLenum target)) \
|
|
X(glFramebufferTexture1D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \
|
|
X(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \
|
|
X(glFramebufferTexture3D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)) \
|
|
X(glFramebufferRenderbuffer, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) \
|
|
X(glFramebufferTexture, void, (GLenum target, GLenum attachment, GLuint texture, GLint level)) \
|
|
X(glBlitFramebuffer, void, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)) \
|
|
X(glGetFramebufferAttachmentParameteriv, void, (GLenum target, GLenum attachment, GLenum pname, GLint *params))
|
|
|
|
//- gl function types
|
|
|
|
#define X(N,R,P) typedef R GL_##N P;
|
|
GL_FUNC_X_LIST()
|
|
#undef X
|
|
|
|
//- gl function pointers
|
|
|
|
#define X(N,R,P) static GL_##N * N = 0;
|
|
GL_FUNC_X_LIST()
|
|
#undef X
|
|
|
|
|
|
////////////////////////////////
|
|
//- OpenGL Helper Declarations
|
|
|
|
static GLuint opengl_helper_make_shader(char *src, GLenum shader_type);
|
|
static GLuint opengl_helper_make_program(GLuint *shaders, U32 count);
|
|
|
|
|
|
////////////////////////////////
|
|
//- OpenGL Render
|
|
|
|
static char basic_vshader[] =
|
|
"#version 330\n"
|
|
"uniform vec2 u_view_xform;\n"
|
|
"layout (location = 0) in vec2 v_p;\n"
|
|
"layout (location = 1) in vec4 v_c;\n"
|
|
"out vec4 f_c;\n"
|
|
"void main(){\n"
|
|
"vec2 norm_pos = v_p*u_view_xform + vec2(-1.0, -1.0);\n"
|
|
"gl_Position = vec4(norm_pos, 0.0, 1.0);\n"
|
|
"f_c = v_c;\n"
|
|
"}\n"
|
|
;
|
|
|
|
static char basic_fshader[] =
|
|
"#version 330\n"
|
|
"in vec4 f_c;\n"
|
|
"out vec4 out_color;\n"
|
|
"void main(){\n"
|
|
"out_color = f_c;\n"
|
|
"}\n"
|
|
;
|
|
|
|
static GLuint basic_program = 0;
|
|
static GLint basic_u_view_xform = -1;
|
|
|
|
|
|
|
|
|
|
static char srgb_in_vshader[] =
|
|
"#version 330\n"
|
|
"\n"
|
|
|
|
#if USE_ACCURATE_CONVERSIONS_IN_SHADER
|
|
"float lin_from_srgb(float x){\n"
|
|
"float r = 0;\n"
|
|
"if (x <= 0.04045) r = x/12.92;\n"
|
|
"else r = pow(((x + 0.055)/1.055), 2.4);\n"
|
|
"return(r);\n"
|
|
"}\n"
|
|
"\n"
|
|
"float srgb_from_lin(float x){\n"
|
|
"float r = 0;\n"
|
|
"if (x <= 0.0031308) r = x*12.92;\n"
|
|
"else r = pow(x, 1/2.4)*1.055 - 0.055;\n"
|
|
"return(r);\n"
|
|
"}\n"
|
|
#endif
|
|
|
|
"\n"
|
|
"uniform vec2 u_view_xform;\n"
|
|
"layout (location = 0) in vec2 v_p;\n"
|
|
"layout (location = 1) in vec4 v_c;\n"
|
|
"out vec4 f_c;\n"
|
|
"void main(){\n"
|
|
"vec2 norm_pos = v_p*u_view_xform + vec2(-1.0, -1.0);\n"
|
|
|
|
#if USE_ACCURATE_CONVERSIONS_IN_SHADER
|
|
"float lin_r = lin_from_srgb(v_c.r);\n"
|
|
"float lin_g = lin_from_srgb(v_c.g);\n"
|
|
"float lin_b = lin_from_srgb(v_c.b);\n"
|
|
#else
|
|
"float lin_r = pow(v_c.r, 2.2);\n"
|
|
"float lin_g = pow(v_c.g, 2.2);\n"
|
|
"float lin_b = pow(v_c.b, 2.2);\n"
|
|
#endif
|
|
|
|
|
|
"gl_Position = vec4(norm_pos, 0.0, 1.0);\n"
|
|
"f_c = vec4(lin_r, lin_g, lin_b, v_c.a);\n"
|
|
"}\n"
|
|
;
|
|
|
|
static char srgb_in_fshader[] =
|
|
"#version 330\n"
|
|
"in vec4 f_c;\n"
|
|
"out vec4 out_color;\n"
|
|
"void main(){\n"
|
|
"out_color = f_c;\n"
|
|
"}\n"
|
|
;
|
|
|
|
static GLuint srgb_in_program = 0;
|
|
static GLint srgb_in_u_view_xform = -1;
|
|
|
|
|
|
|
|
|
|
static char rect_vshader[] =
|
|
"#version 330\n"
|
|
"uniform vec2 u_view_xform;\n"
|
|
"layout (location = 0) in vec2 v_p;\n"
|
|
"layout (location = 1) in vec4 v_r;\n"
|
|
"out vec4 f_r;\n"
|
|
"void main(){\n"
|
|
"vec2 norm_pos = v_p*u_view_xform + vec2(-1.0, -1.0);\n"
|
|
"gl_Position = vec4(norm_pos, 0.0, 1.0);\n"
|
|
"f_r = v_r;\n"
|
|
"}\n"
|
|
;
|
|
|
|
static char rect_fshader[] =
|
|
"#version 330\n"
|
|
"in vec4 gl_FragCoord;\n"
|
|
"in vec4 f_r;\n"
|
|
"out vec4 out_color;\n"
|
|
"void main(){\n"
|
|
"float pix_x0 = gl_FragCoord.x - 0.5;\n"
|
|
"float pix_y0 = gl_FragCoord.y - 0.5;\n"
|
|
"float pix_x1 = gl_FragCoord.x + 0.5;\n"
|
|
"float pix_y1 = gl_FragCoord.y + 0.5;\n"
|
|
"float int_x0 = max(pix_x0, f_r.x);\n"
|
|
"float int_y0 = max(pix_y0, f_r.y);\n"
|
|
"float int_x1 = min(pix_x1, f_r.z);\n"
|
|
"float int_y1 = min(pix_y1, f_r.w);\n"
|
|
"float cover = (int_x1 - int_x0)*(int_y1 - int_y0);\n"
|
|
"out_color = vec4(1, 1, 1, cover);\n"
|
|
"}\n"
|
|
;
|
|
|
|
static GLuint rect_program = 0;
|
|
static GLint rect_u_view_xform = -1;
|
|
|
|
|
|
|
|
|
|
static char texture_vshader[] =
|
|
"#version 330\n"
|
|
"uniform vec2 u_view_xform;\n"
|
|
"layout (location = 0) in vec2 v_p;\n"
|
|
"layout (location = 1) in vec2 v_uv;\n"
|
|
"out vec2 f_uv;\n"
|
|
"void main(){\n"
|
|
"vec2 norm_pos = v_p*u_view_xform + vec2(-1.0, -1.0);\n"
|
|
"gl_Position = vec4(norm_pos, 0.0, 1.0);\n"
|
|
"f_uv = v_uv;\n"
|
|
"}\n"
|
|
;
|
|
|
|
static char texture_fshader[] =
|
|
"#version 330\n"
|
|
"uniform sampler2D u_texture;\n"
|
|
"in vec2 f_uv;\n"
|
|
"out vec4 out_color;\n"
|
|
"void main(){\n"
|
|
"out_color = vec4(texture(u_texture, f_uv).rgb, 1);\n"
|
|
"}\n"
|
|
;
|
|
|
|
static GLuint texture_program = 0;
|
|
static GLint texture_u_view_xform = -1;
|
|
static GLint texture_u_texture = -1;
|
|
|
|
|
|
|
|
static char texrgb_vshader[] =
|
|
"#version 330\n"
|
|
"uniform vec2 u_view_xform;\n"
|
|
"layout (location = 0) in vec2 v_p;\n"
|
|
"layout (location = 1) in vec2 v_uv;\n"
|
|
"out vec2 f_uv;\n"
|
|
"void main(){\n"
|
|
"vec2 norm_pos = v_p*u_view_xform + vec2(-1.0, -1.0);\n"
|
|
"gl_Position = vec4(norm_pos, 0.0, 1.0);\n"
|
|
"f_uv = v_uv;\n"
|
|
"}\n"
|
|
;
|
|
|
|
static char texrgb_fshader[] =
|
|
"#version 330\n"
|
|
"uniform sampler2D u_texture;\n"
|
|
"uniform vec4 u_color;\n"
|
|
"in vec2 f_uv;\n"
|
|
"out vec4 out_color;\n"
|
|
"void main(){\n"
|
|
"float texture_a = texture(u_texture, f_uv).r;\n"
|
|
"out_color = vec4(u_color.rgb, u_color.a*texture_a);\n"
|
|
"}\n"
|
|
;
|
|
|
|
static GLuint texrgb_program = 0;
|
|
static GLint texrgb_u_view_xform = -1;
|
|
static GLint texrgb_u_texture = -1;
|
|
static GLint texrgb_u_color = -1;
|
|
|
|
|
|
|
|
static GLuint canvas_texture = 0;
|
|
static GLuint canvas_framebuffer = 0;
|
|
|
|
static GLuint test3_texture = 0;
|
|
|
|
#define CIRCLE5_RADIUS 5.f
|
|
#define CIRCLE5_X 6.f
|
|
#define CIRCLE5_Y 6.f
|
|
#define CIRCLE5_SIDE 12
|
|
static GLuint circle5_texture = 0;
|
|
|
|
#define CIRCLE25_RADIUS 25.f
|
|
#define CIRCLE25_X 30.f
|
|
#define CIRCLE25_Y 30.f
|
|
#define CIRCLE25_SIDE 60
|
|
static GLuint circle25_texture = 0;
|
|
|
|
#define CIRCLE125_RADIUS 125.f
|
|
#define CIRCLE125_X 150.f
|
|
#define CIRCLE125_Y 150.f
|
|
#define CIRCLE125_SIDE 300
|
|
static GLuint circle125_texture = 0;
|
|
|
|
|
|
static void
|
|
opengl_render_init(void){
|
|
//- setup basic shader program
|
|
{
|
|
GLuint shaders[2] = {0};
|
|
shaders[0] = opengl_helper_make_shader(basic_vshader, GL_VERTEX_SHADER);
|
|
shaders[1] = opengl_helper_make_shader(basic_fshader, GL_FRAGMENT_SHADER);
|
|
basic_program = opengl_helper_make_program(shaders, 2);
|
|
basic_u_view_xform = glGetUniformLocation(basic_program, "u_view_xform");
|
|
}
|
|
|
|
//- setup "srgb in" shader program
|
|
{
|
|
GLuint shaders[2] = {0};
|
|
shaders[0] = opengl_helper_make_shader(srgb_in_vshader, GL_VERTEX_SHADER);
|
|
shaders[1] = opengl_helper_make_shader(srgb_in_fshader, GL_FRAGMENT_SHADER);
|
|
srgb_in_program = opengl_helper_make_program(shaders, 2);
|
|
srgb_in_u_view_xform = glGetUniformLocation(basic_program, "u_view_xform");
|
|
}
|
|
|
|
//- setup rect shader program
|
|
{
|
|
GLuint shaders[2] = {0};
|
|
shaders[0] = opengl_helper_make_shader(rect_vshader, GL_VERTEX_SHADER);
|
|
shaders[1] = opengl_helper_make_shader(rect_fshader, GL_FRAGMENT_SHADER);
|
|
rect_program = opengl_helper_make_program(shaders, 2);
|
|
rect_u_view_xform = glGetUniformLocation(rect_program, "u_view_xform");
|
|
}
|
|
|
|
//- setup texture shader program
|
|
{
|
|
GLuint shaders[2] = {0};
|
|
shaders[0] = opengl_helper_make_shader(texture_vshader, GL_VERTEX_SHADER);
|
|
shaders[1] = opengl_helper_make_shader(texture_fshader, GL_FRAGMENT_SHADER);
|
|
texture_program = opengl_helper_make_program(shaders, 2);
|
|
texture_u_view_xform = glGetUniformLocation(texture_program, "u_view_xform");
|
|
texture_u_texture = glGetUniformLocation(texture_program, "u_texture");
|
|
}
|
|
|
|
//- setup texture rgb shader program
|
|
{
|
|
GLuint shaders[2] = {0};
|
|
shaders[0] = opengl_helper_make_shader(texrgb_vshader, GL_VERTEX_SHADER);
|
|
shaders[1] = opengl_helper_make_shader(texrgb_fshader, GL_FRAGMENT_SHADER);
|
|
texrgb_program = opengl_helper_make_program(shaders, 2);
|
|
texrgb_u_view_xform = glGetUniformLocation(texrgb_program, "u_view_xform");
|
|
texrgb_u_texture = glGetUniformLocation(texrgb_program, "u_texture");
|
|
texrgb_u_color = glGetUniformLocation(texrgb_program, "u_color");
|
|
}
|
|
|
|
//- vertex array object
|
|
{
|
|
GLuint vao = 0;
|
|
glGenVertexArrays(1, &vao);
|
|
glBindVertexArray(vao);
|
|
}
|
|
|
|
//- vertex array buffer
|
|
{
|
|
GLuint vertex_buffer = 0;
|
|
glGenBuffers(1, &vertex_buffer);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
|
|
}
|
|
|
|
//- canvas texture
|
|
{
|
|
glGenTextures(1, &canvas_texture);
|
|
glBindTexture(GL_TEXTURE_2D, canvas_texture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2048, 2048, 0,
|
|
GL_RGB, GL_FLOAT, 0);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
}
|
|
|
|
//- canvas framebuffer
|
|
{
|
|
glGenFramebuffers(1, &canvas_framebuffer);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, canvas_framebuffer);
|
|
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
canvas_texture, 0);
|
|
}
|
|
|
|
//- enable alpha blend
|
|
{
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glEnable(GL_BLEND);
|
|
}
|
|
|
|
//- test3 texture
|
|
{
|
|
U8 test3_img[16*3] = {0};
|
|
for (U32 i = 0; i < 16; i += 1){
|
|
U32 i3 = 3*i;
|
|
for (U32 j = 0; j < 3; j += 1){
|
|
test3_img[i3 + j] = 0x08 + 0x10*i;
|
|
}
|
|
}
|
|
|
|
glGenTextures(1, &test3_texture);
|
|
glBindTexture(GL_TEXTURE_2D, test3_texture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8, 16, 1, 0,
|
|
GL_RGB, GL_UNSIGNED_BYTE, test3_img);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
}
|
|
|
|
//- circle5 texture
|
|
{
|
|
U8 buf[CIRCLE5_SIDE*CIRCLE5_SIDE] = {0};
|
|
render_circle_in_buffer(CIRCLE5_RADIUS, CIRCLE5_X, CIRCLE5_Y,
|
|
buf, CIRCLE5_SIDE);
|
|
|
|
U8 img[CIRCLE5_SIDE*CIRCLE5_SIDE*3] = {0};
|
|
for (U32 i = 0; i < CIRCLE5_SIDE*CIRCLE5_SIDE; i += 1){
|
|
U8 c = buf[i];
|
|
img[i*3 + 0] = c;
|
|
img[i*3 + 1] = c;
|
|
img[i*3 + 2] = c;
|
|
}
|
|
|
|
glGenTextures(1, &circle5_texture);
|
|
glBindTexture(GL_TEXTURE_2D, circle5_texture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, CIRCLE5_SIDE, CIRCLE5_SIDE, 0,
|
|
GL_RGB, GL_UNSIGNED_BYTE, img);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
}
|
|
|
|
//- circle25 texture
|
|
{
|
|
U8 buf[CIRCLE25_SIDE*CIRCLE25_SIDE] = {0};
|
|
render_circle_in_buffer(CIRCLE25_RADIUS, CIRCLE25_X, CIRCLE25_Y,
|
|
buf, CIRCLE25_SIDE);
|
|
|
|
U8 img[CIRCLE25_SIDE*CIRCLE25_SIDE*3] = {0};
|
|
for (U32 i = 0; i < CIRCLE25_SIDE*CIRCLE25_SIDE; i += 1){
|
|
U8 c = buf[i];
|
|
img[i*3 + 0] = c;
|
|
img[i*3 + 1] = c;
|
|
img[i*3 + 2] = c;
|
|
}
|
|
|
|
glGenTextures(1, &circle25_texture);
|
|
glBindTexture(GL_TEXTURE_2D, circle25_texture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, CIRCLE25_SIDE, CIRCLE25_SIDE, 0,
|
|
GL_RGB, GL_UNSIGNED_BYTE, img);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
}
|
|
|
|
//- circle125 texture
|
|
{
|
|
U8 buf[CIRCLE125_SIDE*CIRCLE125_SIDE] = {0};
|
|
render_circle_in_buffer(CIRCLE125_RADIUS, CIRCLE125_X, CIRCLE125_Y,
|
|
buf, CIRCLE125_SIDE);
|
|
|
|
U8 img[CIRCLE125_SIDE*CIRCLE125_SIDE*3] = {0};
|
|
for (U32 i = 0; i < CIRCLE125_SIDE*CIRCLE125_SIDE; i += 1){
|
|
U8 c = buf[i];
|
|
img[i*3 + 0] = c;
|
|
img[i*3 + 1] = c;
|
|
img[i*3 + 2] = c;
|
|
}
|
|
|
|
glGenTextures(1, &circle125_texture);
|
|
glBindTexture(GL_TEXTURE_2D, circle125_texture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, CIRCLE125_SIDE, CIRCLE125_SIDE, 0,
|
|
GL_RGB, GL_UNSIGNED_BYTE, img);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
}
|
|
|
|
//- check for setup errors
|
|
if (glGetError() != 0){
|
|
error_message("error in opengl renderer initialization");
|
|
}
|
|
}
|
|
|
|
static void
|
|
opengl_draw_basic_geometry(Vertex *v, U64 count){
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(*v)*count, v, GL_STREAM_DRAW);
|
|
|
|
glUseProgram(basic_program);
|
|
glUniform2f(basic_u_view_xform, 2.f/graphics_w, 2.f/graphics_h);
|
|
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, 0,
|
|
sizeof(Vertex), PtrOffsetOf(Vertex, px));
|
|
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 4, GL_FLOAT, 0,
|
|
sizeof(Vertex), PtrOffsetOf(Vertex, cr));
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, count);
|
|
}
|
|
|
|
static void
|
|
opengl_draw_srgb_in_geometry(Vertex *v, U64 count){
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(*v)*count, v, GL_STREAM_DRAW);
|
|
|
|
glUseProgram(srgb_in_program);
|
|
glUniform2f(srgb_in_u_view_xform, 2.f/graphics_w, 2.f/graphics_h);
|
|
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, 0,
|
|
sizeof(Vertex), PtrOffsetOf(Vertex, px));
|
|
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 4, GL_FLOAT, 0,
|
|
sizeof(Vertex), PtrOffsetOf(Vertex, cr));
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, count);
|
|
}
|
|
|
|
static void
|
|
opengl_draw_rect_geometry(Vertex *v, U64 count){
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(*v)*count, v, GL_STREAM_DRAW);
|
|
|
|
glUseProgram(rect_program);
|
|
glUniform2f(rect_u_view_xform, 2.f/graphics_w, 2.f/graphics_h);
|
|
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, 0,
|
|
sizeof(Vertex), PtrOffsetOf(Vertex, px));
|
|
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 4, GL_FLOAT, 0,
|
|
sizeof(Vertex), PtrOffsetOf(Vertex, rx0));
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, count);
|
|
}
|
|
|
|
static void
|
|
opengl_draw_texture_geometry(Vertex *v, U64 count, U32 texture){
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(*v)*count, v, GL_STREAM_DRAW);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
|
|
glUseProgram(texture_program);
|
|
glUniform2f(texture_u_view_xform, 2.f/graphics_w, 2.f/graphics_h);
|
|
glUniform1i(texture_u_texture, 0);
|
|
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, 0,
|
|
sizeof(Vertex), PtrOffsetOf(Vertex, px));
|
|
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 2, GL_FLOAT, 0,
|
|
sizeof(Vertex), PtrOffsetOf(Vertex, uvx));
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, count);
|
|
}
|
|
|
|
static void
|
|
opengl_draw_texrgb_geometry(Vertex *v, U64 count, U32 texture,
|
|
F32 r, F32 g, F32 b, F32 a){
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(*v)*count, v, GL_STREAM_DRAW);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
|
|
glUseProgram(texrgb_program);
|
|
glUniform2f(texrgb_u_view_xform, 2.f/graphics_w, 2.f/graphics_h);
|
|
glUniform1i(texrgb_u_texture, 0);
|
|
glUniform4f(texrgb_u_color, r, g, b, a);
|
|
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, 0,
|
|
sizeof(Vertex), PtrOffsetOf(Vertex, px));
|
|
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 2, GL_FLOAT, 0,
|
|
sizeof(Vertex), PtrOffsetOf(Vertex, uvx));
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, count);
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
//- Main
|
|
|
|
typedef struct Rect{
|
|
F32 x0;
|
|
F32 y0;
|
|
F32 x1;
|
|
F32 y1;
|
|
F32 r;
|
|
F32 g;
|
|
F32 b;
|
|
F32 a;
|
|
} Rect;
|
|
|
|
int
|
|
WinMain(HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPSTR lpCmdLine,
|
|
int nShowCmd){
|
|
sanity_test_integrals();
|
|
sanity_test_circle_area();
|
|
|
|
// do graphics tests
|
|
wgl_helper_make_graphics_window(hInstance);
|
|
|
|
U64 frame_counter = 0;
|
|
for (;;){
|
|
// wait for input
|
|
MSG msg = {0};
|
|
BOOL get_message = GetMessage(&msg, 0, 0, 0);
|
|
if (get_message <= 0){
|
|
break;
|
|
}
|
|
DispatchMessage(&msg);
|
|
|
|
// begin render
|
|
wgl_helper_begin_render();
|
|
|
|
// initialization
|
|
if (frame_counter == 0){
|
|
opengl_render_init();
|
|
}
|
|
|
|
// screen layout
|
|
F32 center_x = graphics_w*0.5f;
|
|
F32 divider_half_w = 3.f;
|
|
F32 divider_min_x = (F32)(S32)(center_x - divider_half_w);
|
|
F32 divider_max_x = divider_min_x + 2.f*divider_half_w;
|
|
|
|
F32 lside_grad_x0 = 352.f;
|
|
F32 lside_grad_x1 = lside_grad_x0 + 256.f;
|
|
F32 rside_grad_x0 = 1312.f;
|
|
F32 rside_grad_x1 = rside_grad_x0 + 256.f;
|
|
|
|
F32 test1_y = graphics_h - 10.f;
|
|
F32 test2_y = graphics_h - 60.f;
|
|
F32 test3_y = graphics_h - 110.f;
|
|
F32 test4_y = graphics_h - 160.f;
|
|
F32 test5_y = graphics_h - 340.f;
|
|
F32 test6_y = graphics_h - 450.f;
|
|
F32 test7_y = graphics_h - 480.f;
|
|
F32 test8_y = graphics_h - 800.f;
|
|
|
|
// render to canvas frame buffer
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, canvas_framebuffer);
|
|
glViewport(0, 0, (S32)graphics_w, (S32)graphics_h);
|
|
|
|
// black background everywhere
|
|
glClearColor(0.f, 0.f, 0.f, 1.f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
// draw divider
|
|
{
|
|
Vertex v[6];
|
|
v[0].px = divider_min_x; v[0].py = 0.f;
|
|
v[1].px = divider_min_x; v[1].py = graphics_h;
|
|
v[2].px = divider_max_x; v[2].py = 0.f;
|
|
v[3].px = v[1].px; v[3].py = v[1].py;
|
|
v[4].px = v[2].px; v[4].py = v[2].py;
|
|
v[5].px = divider_max_x; v[5].py = graphics_h;
|
|
for (U32 i = 0; i < 6; i += 1){
|
|
v[i].cr = 1.f;
|
|
v[i].cg = 1.f;
|
|
v[i].cb = 1.f;
|
|
v[i].ca = 1.f;
|
|
}
|
|
|
|
opengl_draw_basic_geometry(v, 6);
|
|
}
|
|
|
|
// two sided test
|
|
for (U32 test_idx = 0; test_idx < 2; test_idx += 1){
|
|
F32 grad_x0 = lside_grad_x0;
|
|
F32 grad_x1 = lside_grad_x1;
|
|
if (test_idx == 1){
|
|
grad_x0 = rside_grad_x0;
|
|
grad_x1 = rside_grad_x1;
|
|
}
|
|
|
|
// 1: draw gradient
|
|
{
|
|
F32 grad_y1 = test1_y;
|
|
F32 grad_y0 = test1_y - 40.f;
|
|
|
|
Vertex v[6];
|
|
v[0].px = grad_x0; v[0].py = grad_y0;
|
|
v[1].px = grad_x0; v[1].py = grad_y1;
|
|
v[2].px = grad_x1; v[2].py = grad_y0;
|
|
v[3].px = v[1].px; v[3].py = v[1].py;
|
|
v[4].px = v[2].px; v[4].py = v[2].py;
|
|
v[5].px = grad_x1; v[5].py = grad_y1;
|
|
for (U32 i = 0; i < 6; i += 1){
|
|
if (v[i].px == grad_x0){
|
|
v[i].cr = 1.f; v[i].cg = 1.f; v[i].cb = 1.f; v[i].ca = 1.f;
|
|
}
|
|
else{
|
|
v[i].cr = 0.f; v[i].cg = 0.f; v[i].cb = 0.f; v[i].ca = 1.f;
|
|
}
|
|
}
|
|
|
|
opengl_draw_basic_geometry(v, 6);
|
|
}
|
|
|
|
// 2: draw extremes and midpoint
|
|
{
|
|
F32 box_x[4] = {0};
|
|
box_x[0] = grad_x0;
|
|
box_x[1] = (F32)(S32)((grad_x0*2.f + grad_x1)/3.f);
|
|
box_x[2] = (F32)(S32)((grad_x0 + grad_x1*2.f)/3.f);
|
|
box_x[3] = grad_x1;
|
|
|
|
F32 val[3] = { 1.f, 0.5f, 0.f };
|
|
|
|
F32 grad_y1 = test2_y;
|
|
F32 grad_y0 = test2_y - 40.f;
|
|
|
|
for (U32 j = 0; j < 3; j += 1){
|
|
F32 box_x0 = box_x[j];
|
|
F32 box_x1 = box_x[j + 1];
|
|
|
|
F32 valj = val[j];
|
|
|
|
Vertex v[6];
|
|
v[0].px = box_x0; v[0].py = grad_y0;
|
|
v[1].px = box_x0; v[1].py = grad_y1;
|
|
v[2].px = box_x1; v[2].py = grad_y0;
|
|
v[3].px = v[1].px; v[3].py = v[1].py;
|
|
v[4].px = v[2].px; v[4].py = v[2].py;
|
|
v[5].px = box_x1; v[5].py = grad_y1;
|
|
for (U32 i = 0; i < 6; i += 1){
|
|
v[i].cr = valj; v[i].cg = valj; v[i].cb = valj; v[i].ca = 1.f;
|
|
}
|
|
|
|
opengl_draw_basic_geometry(v, 6);
|
|
}
|
|
|
|
}
|
|
|
|
// 3: compare x^2.2 with texture sampling for (sRGB -> Linear)
|
|
if (test_idx == 1){
|
|
F32 grad_y2 = test3_y;
|
|
F32 grad_y1 = test3_y - 20.f;
|
|
F32 grad_y0 = test3_y - 40.f;
|
|
|
|
// convert (sRGB -> Linear) by texture sampling
|
|
{
|
|
Vertex v[6];
|
|
v[0].px = grad_x0; v[0].py = grad_y1;
|
|
v[0].uvx = 0.f; v[0].uvy = 1.f;
|
|
v[1].px = grad_x0; v[1].py = grad_y2;
|
|
v[1].uvx = 0.f; v[1].uvy = 0.f;
|
|
v[2].px = grad_x1; v[2].py = grad_y1;
|
|
v[2].uvx = 1.f; v[2].uvy = 1.f;
|
|
v[5].px = grad_x1; v[5].py = grad_y2;
|
|
v[5].uvx = 1.f; v[5].uvy = 0.f;
|
|
|
|
v[3] = v[1];
|
|
v[4] = v[2];
|
|
|
|
opengl_draw_texture_geometry(v, 6, test3_texture);
|
|
}
|
|
|
|
// convert (sRGB -> Linear) by pow(x, 2.2) in shader
|
|
{
|
|
F32 delta_x = (grad_x1 - grad_x0)/16.f;
|
|
|
|
for (U32 i = 0; i < 16; i += 1){
|
|
F32 l = (F32)(S32)(grad_x0 + i*delta_x);
|
|
F32 r = (F32)(S32)(l + delta_x);
|
|
|
|
Vertex v[6];
|
|
v[0].px = l; v[0].py = grad_y0;
|
|
v[1].px = l; v[1].py = grad_y1;
|
|
v[2].px = r; v[2].py = grad_y0;
|
|
v[5].px = r; v[5].py = grad_y1;
|
|
v[3] = v[1];
|
|
v[4] = v[2];
|
|
|
|
F32 val = (F32)(0x08 + 0x10*i)/255.f;
|
|
for (U32 j = 0; j < 6; j += 1){
|
|
v[j].cr = val;
|
|
v[j].cg = val;
|
|
v[j].cb = val;
|
|
v[j].ca = 1.f;
|
|
}
|
|
|
|
opengl_draw_srgb_in_geometry(v, 6);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// 4: color gradient
|
|
{
|
|
F32 color_pairs[12][3] = {
|
|
{1.f, 0.f, 0.f}, {1.f, 1.f, 0.f},
|
|
{1.f, 1.f, 0.f}, {0.f, 1.f, 0.f},
|
|
{0.f, 1.f, 0.f}, {0.f, 1.f, 1.f},
|
|
{0.f, 1.f, 1.f}, {0.f, 0.f, 1.f},
|
|
{0.f, 0.f, 1.f}, {1.f, 0.f, 1.f},
|
|
{1.f, 0.f, 1.f}, {1.f, 0.f, 0.f},
|
|
};
|
|
|
|
F32 cursor_y = test4_y;
|
|
|
|
for (U32 j = 0; j < 12; j += 2){
|
|
F32 grad_y1 = cursor_y;
|
|
F32 grad_y0 = cursor_y - 20.f;
|
|
cursor_y -= 30.f;
|
|
|
|
F32 *col[2] = {0};
|
|
col[0] = color_pairs[j];
|
|
col[1] = color_pairs[j + 1];
|
|
|
|
Vertex v[6];
|
|
v[0].px = grad_x0; v[0].py = grad_y0;
|
|
v[1].px = grad_x0; v[1].py = grad_y1;
|
|
v[2].px = grad_x1; v[2].py = grad_y0;
|
|
v[3].px = v[1].px; v[3].py = v[1].py;
|
|
v[4].px = v[2].px; v[4].py = v[2].py;
|
|
v[5].px = grad_x1; v[5].py = grad_y1;
|
|
for (U32 i = 0; i < 6; i += 1){
|
|
U32 k = 0;
|
|
if (v[i].px == grad_x1){
|
|
k = 1;
|
|
}
|
|
v[i].cr = col[k][0];
|
|
v[i].cg = col[k][1];
|
|
v[i].cb = col[k][2];
|
|
v[i].ca = 1.f;
|
|
}
|
|
|
|
if (test_idx == 0){
|
|
opengl_draw_basic_geometry(v, 6);
|
|
}
|
|
else{
|
|
opengl_draw_srgb_in_geometry(v, 6);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5: transparency blending
|
|
{
|
|
Rect rect[20] = {0};
|
|
U32 recti = 0;
|
|
|
|
F32 top_y = test5_y;
|
|
F32 bot_y = top_y - 100.f;
|
|
|
|
rect[recti].x0 = grad_x0;
|
|
rect[recti].y0 = bot_y;
|
|
rect[recti].x1 = grad_x1;
|
|
rect[recti].y1 = top_y;
|
|
rect[recti].r = 1.f;
|
|
rect[recti].g = 1.f;
|
|
rect[recti].b = 1.f;
|
|
rect[recti].a = 1.f;
|
|
recti += 1;
|
|
|
|
for (U32 i = 0; i < 3; i += 1){
|
|
rect[recti].x0 = grad_x0;
|
|
rect[recti].y0 = top_y - 10.f - 30.f*i;
|
|
rect[recti].x1 = grad_x1;
|
|
rect[recti].y1 = top_y - 30.f - 30.f*i;
|
|
rect[recti].r = (i == 0)?1.f:0.f;
|
|
rect[recti].g = (i == 1)?1.f:0.f;
|
|
rect[recti].b = (i == 2)?1.f:0.f;
|
|
rect[recti].a = 1.f;
|
|
recti += 1;
|
|
}
|
|
|
|
for (U32 j = 0; j < 2; j += 1){
|
|
for (U32 i = 0; i < 3; i += 1){
|
|
rect[recti].x0 = grad_x0 + 10.f + 30.f*i + 100.f*j;
|
|
rect[recti].y0 = bot_y;
|
|
rect[recti].x1 = grad_x0 + 30.f + 30.f*i + 100.f*j;
|
|
rect[recti].y1 = top_y;
|
|
rect[recti].r = (i == 0)?1.f:0.f;
|
|
rect[recti].g = (i == 1)?1.f:0.f;
|
|
rect[recti].b = (i == 2)?1.f:0.f;
|
|
rect[recti].a = (j == 0)?0.5f:0.75f;
|
|
recti += 1;
|
|
}
|
|
}
|
|
|
|
for (U32 j = 0; j < recti; j += 1){
|
|
Vertex v[6] = {0};
|
|
v[0].px = rect[j].x0; v[0].py = rect[j].y0;
|
|
v[1].px = rect[j].x0; v[1].py = rect[j].y1;
|
|
v[2].px = rect[j].x1; v[2].py = rect[j].y0;
|
|
v[3].px = rect[j].x0; v[3].py = rect[j].y1;
|
|
v[4].px = rect[j].x1; v[4].py = rect[j].y0;
|
|
v[5].px = rect[j].x1; v[5].py = rect[j].y1;
|
|
for (U32 k = 0; k < 6; k += 1){
|
|
v[k].cr = rect[j].r;
|
|
v[k].cg = rect[j].g;
|
|
v[k].cb = rect[j].b;
|
|
v[k].ca = rect[j].a;
|
|
}
|
|
|
|
if (test_idx == 0){
|
|
opengl_draw_basic_geometry(v, 6);
|
|
}
|
|
else{
|
|
opengl_draw_srgb_in_geometry(v, 6);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 6: sub-pixel rectangle anti-aliasing
|
|
{
|
|
F32 top_y = test6_y;
|
|
F32 bot_y = test6_y - 20.f;
|
|
|
|
F32 x_cursor = grad_x0;
|
|
for (U32 i = 0; i < 16; i += 1){
|
|
F32 l = x_cursor + 4.0625*i;
|
|
F32 r = l + 2.f;
|
|
|
|
F32 floor_l = (F32)(S32)l;
|
|
F32 ceil_r = (F32)(S32)r;
|
|
if (ceil_r < (F32)r){
|
|
ceil_r += 1.f;
|
|
}
|
|
|
|
Vertex v[6];
|
|
v[0].px = floor_l; v[0].py = bot_y;
|
|
v[1].px = floor_l; v[1].py = top_y;
|
|
v[2].px = ceil_r; v[2].py = bot_y;
|
|
v[3].px = floor_l; v[3].py = top_y;
|
|
v[4].px = ceil_r; v[4].py = bot_y;
|
|
v[5].px = ceil_r; v[5].py = top_y;
|
|
for (U32 j = 0; j < 6; j += 1){
|
|
v[j].rx0 = l;
|
|
v[j].ry0 = bot_y;
|
|
v[j].rx1 = r;
|
|
v[j].ry1 = top_y;
|
|
}
|
|
|
|
opengl_draw_rect_geometry(v, 6);
|
|
}
|
|
}
|
|
|
|
// 7: circles rendered with analytical antialiasing via integration
|
|
{
|
|
F32 y = test7_y;
|
|
F32 x = grad_x0;
|
|
|
|
// circle5
|
|
{
|
|
F32 y1 = y;
|
|
F32 y0 = y - CIRCLE5_SIDE;
|
|
F32 x0 = x;
|
|
F32 x1 = x + CIRCLE5_SIDE;
|
|
|
|
x += CIRCLE5_SIDE + 10;
|
|
|
|
Vertex v[6];
|
|
v[0].px = x0; v[0].py = y0;
|
|
v[0].uvx = 0.f; v[0].uvy = 0.f;
|
|
v[1].px = x0; v[1].py = y1;
|
|
v[1].uvx = 0.f; v[1].uvy = 1.f;
|
|
v[2].px = x1; v[2].py = y0;
|
|
v[2].uvx = 1.f; v[2].uvy = 0.f;
|
|
v[3].px = x0; v[3].py = y1;
|
|
v[3].uvx = 0.f; v[3].uvy = 1.f;
|
|
v[4].px = x1; v[4].py = y0;
|
|
v[4].uvx = 1.f; v[4].uvy = 0.f;
|
|
v[5].px = x1; v[5].py = y1;
|
|
v[5].uvx = 1.f; v[5].uvy = 1.f;
|
|
|
|
opengl_draw_texture_geometry(v, 6, circle5_texture);
|
|
}
|
|
|
|
// circle25
|
|
{
|
|
F32 y1 = y;
|
|
F32 y0 = y - CIRCLE25_SIDE;
|
|
F32 x0 = x;
|
|
F32 x1 = x + CIRCLE25_SIDE;
|
|
|
|
x += CIRCLE25_SIDE + 10;
|
|
|
|
Vertex v[6];
|
|
v[0].px = x0; v[0].py = y0;
|
|
v[0].uvx = 0.f; v[0].uvy = 0.f;
|
|
v[1].px = x0; v[1].py = y1;
|
|
v[1].uvx = 0.f; v[1].uvy = 1.f;
|
|
v[2].px = x1; v[2].py = y0;
|
|
v[2].uvx = 1.f; v[2].uvy = 0.f;
|
|
v[3].px = x0; v[3].py = y1;
|
|
v[3].uvx = 0.f; v[3].uvy = 1.f;
|
|
v[4].px = x1; v[4].py = y0;
|
|
v[4].uvx = 1.f; v[4].uvy = 0.f;
|
|
v[5].px = x1; v[5].py = y1;
|
|
v[5].uvx = 1.f; v[5].uvy = 1.f;
|
|
|
|
opengl_draw_texture_geometry(v, 6, circle25_texture);
|
|
}
|
|
|
|
// circle125
|
|
{
|
|
F32 y1 = y;
|
|
F32 y0 = y - CIRCLE125_SIDE;
|
|
F32 x0 = x;
|
|
F32 x1 = x + CIRCLE125_SIDE;
|
|
|
|
x += CIRCLE125_SIDE + 10;
|
|
|
|
Vertex v[6];
|
|
v[0].px = x0; v[0].py = y0;
|
|
v[0].uvx = 0.f; v[0].uvy = 0.f;
|
|
v[1].px = x0; v[1].py = y1;
|
|
v[1].uvx = 0.f; v[1].uvy = 1.f;
|
|
v[2].px = x1; v[2].py = y0;
|
|
v[2].uvx = 1.f; v[2].uvy = 0.f;
|
|
v[3].px = x0; v[3].py = y1;
|
|
v[3].uvx = 0.f; v[3].uvy = 1.f;
|
|
v[4].px = x1; v[4].py = y0;
|
|
v[4].uvx = 1.f; v[4].uvy = 0.f;
|
|
v[5].px = x1; v[5].py = y1;
|
|
v[5].uvx = 1.f; v[5].uvy = 1.f;
|
|
|
|
opengl_draw_texture_geometry(v, 6, circle125_texture);
|
|
}
|
|
}
|
|
|
|
// 8: color on color circles
|
|
{
|
|
F32 back[][4] = {
|
|
{1.f, 0.f, 0.f, 1.f},
|
|
{1.f, 0.f, 0.f, 1.f},
|
|
{0.f, 1.f, 0.f, 1.f},
|
|
{0.f, 1.f, 0.f, 1.f},
|
|
{0.f, 0.f, 1.f, 1.f},
|
|
{0.f, 0.f, 1.f, 1.f},
|
|
};
|
|
F32 fore[][4] = {
|
|
{0.f, 1.f, 0.f, 1.f},
|
|
{0.f, 0.f, 1.f, 1.f},
|
|
{1.f, 0.f, 0.f, 1.f},
|
|
{0.f, 0.f, 1.f, 1.f},
|
|
{1.f, 0.f, 0.f, 1.f},
|
|
{0.f, 1.f, 0.f, 1.f},
|
|
};
|
|
|
|
|
|
|
|
F32 y = test8_y;
|
|
F32 x = grad_x0;
|
|
|
|
for (U32 k = 0; k < 6; k += 1){
|
|
// background
|
|
{
|
|
F32 x0 = x;
|
|
F32 x1 = x + CIRCLE5_SIDE + CIRCLE25_SIDE + 20.f;
|
|
F32 y1 = y;
|
|
F32 y0 = y - CIRCLE25_SIDE;
|
|
|
|
Vertex v[6];
|
|
v[0].px = x0; v[0].py = y0;
|
|
v[1].px = x0; v[1].py = y1;
|
|
v[2].px = x1; v[2].py = y0;
|
|
v[3].px = x0; v[3].py = y1;
|
|
v[4].px = x1; v[4].py = y0;
|
|
v[5].px = x1; v[5].py = y1;
|
|
for (U32 i = 0; i < 6; i += 1){
|
|
v[i].cr = back[k][0];
|
|
v[i].cg = back[k][1];
|
|
v[i].cb = back[k][2];
|
|
v[i].ca = back[k][3];
|
|
}
|
|
|
|
if (test_idx == 0){
|
|
opengl_draw_basic_geometry(v, 6);
|
|
}
|
|
else{
|
|
opengl_draw_srgb_in_geometry(v, 6);
|
|
}
|
|
}
|
|
|
|
// circle5
|
|
{
|
|
F32 y1 = y;
|
|
F32 y0 = y - CIRCLE5_SIDE;
|
|
F32 x0 = x;
|
|
F32 x1 = x + CIRCLE5_SIDE;
|
|
|
|
x += CIRCLE5_SIDE + 10;
|
|
|
|
Vertex v[6];
|
|
v[0].px = x0; v[0].py = y0;
|
|
v[0].uvx = 0.f; v[0].uvy = 0.f;
|
|
v[1].px = x0; v[1].py = y1;
|
|
v[1].uvx = 0.f; v[1].uvy = 1.f;
|
|
v[2].px = x1; v[2].py = y0;
|
|
v[2].uvx = 1.f; v[2].uvy = 0.f;
|
|
v[3].px = x0; v[3].py = y1;
|
|
v[3].uvx = 0.f; v[3].uvy = 1.f;
|
|
v[4].px = x1; v[4].py = y0;
|
|
v[4].uvx = 1.f; v[4].uvy = 0.f;
|
|
v[5].px = x1; v[5].py = y1;
|
|
v[5].uvx = 1.f; v[5].uvy = 1.f;
|
|
|
|
opengl_draw_texrgb_geometry(v, 6, circle5_texture,
|
|
fore[k][0], fore[k][1],
|
|
fore[k][2], fore[k][3]);
|
|
}
|
|
|
|
// circle25
|
|
{
|
|
F32 y1 = y;
|
|
F32 y0 = y - CIRCLE25_SIDE;
|
|
F32 x0 = x;
|
|
F32 x1 = x + CIRCLE25_SIDE;
|
|
|
|
x += CIRCLE25_SIDE + 10;
|
|
|
|
Vertex v[6];
|
|
v[0].px = x0; v[0].py = y0;
|
|
v[0].uvx = 0.f; v[0].uvy = 0.f;
|
|
v[1].px = x0; v[1].py = y1;
|
|
v[1].uvx = 0.f; v[1].uvy = 1.f;
|
|
v[2].px = x1; v[2].py = y0;
|
|
v[2].uvx = 1.f; v[2].uvy = 0.f;
|
|
v[3].px = x0; v[3].py = y1;
|
|
v[3].uvx = 0.f; v[3].uvy = 1.f;
|
|
v[4].px = x1; v[4].py = y0;
|
|
v[4].uvx = 1.f; v[4].uvy = 0.f;
|
|
v[5].px = x1; v[5].py = y1;
|
|
v[5].uvx = 1.f; v[5].uvy = 1.f;
|
|
|
|
opengl_draw_texrgb_geometry(v, 6, circle25_texture,
|
|
fore[k][0], fore[k][1],
|
|
fore[k][2], fore[k][3]);
|
|
}
|
|
|
|
// move y down after 2
|
|
if ((k + 1) % 2 == 0){
|
|
x = grad_x0;
|
|
y -= CIRCLE25_SIDE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// resolve canvas to screen
|
|
{
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, canvas_framebuffer);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
S32 w = (S32)graphics_w;
|
|
S32 h = (S32)graphics_h;
|
|
|
|
// left side resolve *without* GL_FRAMEBUFFER_SRGB
|
|
glBlitFramebuffer(0, 0, w/2, h,
|
|
0, 0, w/2, h,
|
|
GL_COLOR_BUFFER_BIT,
|
|
GL_NEAREST);
|
|
|
|
// right side resolve *with* GL_FRAMEBUFFER_SRGB
|
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
|
glBlitFramebuffer(w/2, 0, w, h,
|
|
w/2, 0, w, h,
|
|
GL_COLOR_BUFFER_BIT,
|
|
GL_NEAREST);
|
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
// after render numerical value checks
|
|
{
|
|
// 1: each value differs from it's neighbors by exactly 1
|
|
// (w/ 4-byte stride; alpha channel not included)
|
|
U8 buf[256*4] = {0};
|
|
glReadPixels((S32)lside_grad_x0, (S32)test1_y - 5, 256, 1,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, buf);
|
|
for (U32 i = 1; i < 256; i += 1){
|
|
Assert(buf[(i - 1)*4] - 1 == buf[i*4]);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
// check for gl errors from this frame
|
|
if (glGetError() != 0){
|
|
error_message("error in opengl frame render");
|
|
}
|
|
|
|
// end render
|
|
wgl_helper_end_render();
|
|
|
|
// frame counter
|
|
frame_counter += 1;
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
////////////////////////////////
|
|
//- OpenGL Helpers
|
|
|
|
static GLuint
|
|
opengl_helper_make_shader(char *src, GLenum shader_type){
|
|
// create shader
|
|
GLuint shader = glCreateShader(shader_type);
|
|
|
|
GLenum error = glGetError();
|
|
|
|
// set source
|
|
glShaderSource(shader, 1, &src, 0);
|
|
|
|
// compile
|
|
glCompileShader(shader);
|
|
|
|
// read log
|
|
GLint info_log_length = 0;
|
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);
|
|
if (info_log_length > 0){
|
|
char *buffer = allocate_array(char, info_log_length + 1);
|
|
GLint length = 0;
|
|
glGetShaderInfoLog(shader, info_log_length + 1, &length, buffer);
|
|
error_message(buffer);
|
|
}
|
|
|
|
// check status
|
|
{
|
|
GLint compile_status = 0;
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
|
|
if (compile_status == 0){
|
|
error_message("could not create one of the shaders");
|
|
}
|
|
}
|
|
|
|
return(shader);
|
|
}
|
|
|
|
static GLuint
|
|
opengl_helper_make_program(GLuint *shaders, U32 count){
|
|
// link program
|
|
GLuint program = glCreateProgram();
|
|
for (U32 i = 0; i < count; i += 1){
|
|
glAttachShader(program, shaders[i]);
|
|
}
|
|
glLinkProgram(program);
|
|
|
|
// read log
|
|
GLint info_log_length = 0;
|
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);
|
|
if (info_log_length > 0){
|
|
char *buffer = allocate_array(char, info_log_length + 1);
|
|
GLint length = 0;
|
|
glGetProgramInfoLog(program, info_log_length + 1, &length, buffer);
|
|
error_message(buffer);
|
|
}
|
|
|
|
// check status
|
|
{
|
|
GLint link_status = 0;
|
|
glGetProgramiv(program, GL_LINK_STATUS, &link_status);
|
|
if (link_status == 0){
|
|
error_message("could not create one of the GPU programs");
|
|
}
|
|
}
|
|
|
|
return(program);
|
|
}
|
|
|
|
|
|
////////////////////////////////
|
|
//- WGL Definitions
|
|
|
|
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
|
|
#define WGL_ACCELERATION_ARB 0x2003
|
|
#define WGL_SWAP_METHOD_ARB 0x2007
|
|
#define WGL_SUPPORT_OPENGL_ARB 0x2010
|
|
#define WGL_DOUBLE_BUFFER_ARB 0x2011
|
|
#define WGL_PIXEL_TYPE_ARB 0x2013
|
|
#define WGL_COLOR_BITS_ARB 0x2014
|
|
#define WGL_RED_BITS_ARB 0x2015
|
|
#define WGL_GREEN_BITS_ARB 0x2017
|
|
#define WGL_BLUE_BITS_ARB 0x2019
|
|
#define WGL_FULL_ACCELERATION_ARB 0x2027
|
|
#define WGL_SWAP_EXCHANGE_ARB 0x2028
|
|
#define WGL_TYPE_RGBA_ARB 0x202B
|
|
|
|
//- WGL_ARB_create_context constants
|
|
|
|
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
|
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
|
|
#define WGL_CONTEXT_FLAGS_ARB 0x2094
|
|
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
|
|
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
|
|
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
|
|
|
|
//- wgl funcs x-list
|
|
|
|
#define WGL_FUNCS_X_LIST() \
|
|
X(wglCreateContext, HGLRC, (HDC dc)) \
|
|
X(wglDeleteContext, BOOL, (HGLRC glrc)) \
|
|
X(wglMakeCurrent, BOOL, (HDC dc,HGLRC glrc)) \
|
|
X(wglGetProcAddress, PROC, (LPCSTR name))
|
|
|
|
//- wgl extension funcs x-list
|
|
|
|
#define WGL_EXT_FUNCS_X_LIST() \
|
|
X(wglChoosePixelFormatARB, BOOL, (HDC,int*,FLOAT*,UINT,int*,UINT*)) \
|
|
X(wglCreateContextAttribsARB, HGLRC, (HDC dc,HGLRC share,int*atri))
|
|
|
|
|
|
//- wgl function types
|
|
|
|
#define X(N,R,P) typedef R W32_##N P;
|
|
WGL_FUNCS_X_LIST()
|
|
WGL_EXT_FUNCS_X_LIST()
|
|
#undef X
|
|
|
|
//- wgl function pointers
|
|
|
|
#define X(N,R,P) static W32_##N * w32_##N = 0;
|
|
WGL_FUNCS_X_LIST()
|
|
WGL_EXT_FUNCS_X_LIST()
|
|
#undef X
|
|
|
|
|
|
////////////////////////////////
|
|
//- WGL Helper
|
|
|
|
#define GET_PROC_ADDR(v,m,s) (*(PROC*)(&(v))) = GetProcAddress((m),(s))
|
|
#define WGL_GET_PROC_ADDR(v,s) (*(PROC*)(&(v))) = w32_wglGetProcAddress(s)
|
|
#define GL_FULL_PROC_ADDR(v,m,s) (*(PROC*)(&(v))) = wgl_helper_load((m),(s))
|
|
|
|
#define BOOTSTRAP_WINDOW_CLASS_NAME L"mr4th-opengl-bootstrap"
|
|
#define GRAPHICS_WINDOW_CLASS_NAME L"mr4th-opengl-graphics"
|
|
|
|
static LRESULT
|
|
w32_graphics_window_proc(HWND wnd,
|
|
UINT msg,
|
|
WPARAM wparam,
|
|
LPARAM lparam){
|
|
LRESULT result = 0;
|
|
switch (msg){
|
|
case WM_CLOSE:
|
|
{
|
|
ExitProcess(0);
|
|
}break;
|
|
|
|
case WM_SIZE:
|
|
{
|
|
// Here we need to re-render IF we want the window not
|
|
// to have ugly black borders during resizing.
|
|
|
|
// begin render
|
|
HDC dc = GetDC(wnd);
|
|
w32_wglMakeCurrent(dc, graphics_context);
|
|
|
|
// render
|
|
glClearColor(0.f, 0.f, 0.f, 1.f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
// end render
|
|
SwapBuffers(dc);
|
|
ReleaseDC(wnd, dc);
|
|
}break;
|
|
|
|
default:
|
|
{
|
|
result = DefWindowProcW(wnd, msg, wparam, lparam);
|
|
}break;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
static PROC
|
|
wgl_helper_load(HMODULE opengl_module, char *name){
|
|
PROC result = (PROC)GetProcAddress(opengl_module, name);
|
|
if (result == 0){
|
|
result = (PROC)w32_wglGetProcAddress(name);
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
|
|
static void
|
|
wgl_helper_make_graphics_window(HINSTANCE hInstance){
|
|
|
|
//- load opengl module
|
|
HMODULE w32_opengl_module = LoadLibraryA("opengl32.dll");
|
|
if (w32_opengl_module == 0){
|
|
error_message("failed to initialize opengl32.dll");
|
|
}
|
|
|
|
//- load & check wgl functions
|
|
{
|
|
#define X(N,R,P) GET_PROC_ADDR(w32_##N, w32_opengl_module, #N);
|
|
WGL_FUNCS_X_LIST()
|
|
#undef X
|
|
|
|
B32 missing_wgl_func = 0;
|
|
#define X(N,R,P) if (w32_##N == 0){ missing_wgl_func = 1; }
|
|
WGL_FUNCS_X_LIST()
|
|
#undef X
|
|
|
|
if (missing_wgl_func){
|
|
error_message("failed to load wgl function(s)");
|
|
}
|
|
}
|
|
|
|
// register bootstrap class
|
|
WNDCLASSW bs_wnd_class = {0};
|
|
bs_wnd_class.lpfnWndProc = DefWindowProcW;
|
|
bs_wnd_class.hInstance = hInstance;
|
|
bs_wnd_class.lpszClassName = BOOTSTRAP_WINDOW_CLASS_NAME;
|
|
ATOM bs_atom = RegisterClassW(&bs_wnd_class);
|
|
if (bs_atom == 0){
|
|
error_message("failed to register bootstrap class");
|
|
}
|
|
|
|
// create bootstrap window
|
|
HWND bootstrap_window =
|
|
CreateWindowW(BOOTSTRAP_WINDOW_CLASS_NAME,
|
|
L"mr4th-opengl-bootstrap",
|
|
0, 0, 0, 0, 0, // style, x,y,w,h
|
|
0, 0, hInstance, 0 // parent, menu, inst, param
|
|
);
|
|
if (bootstrap_window == 0){
|
|
error_message("failed to create bootstrap window");
|
|
}
|
|
|
|
//- load wgl extension functions
|
|
{
|
|
HDC dc = GetDC(bootstrap_window);
|
|
|
|
// create bootstrap context
|
|
HGLRC hglrc = 0;
|
|
{
|
|
PIXELFORMATDESCRIPTOR format_desc = {0};
|
|
format_desc.nSize = sizeof(format_desc);
|
|
format_desc.nVersion = 1;
|
|
format_desc.dwFlags = PFD_SUPPORT_OPENGL;
|
|
format_desc.cColorBits = 24;
|
|
format_desc.cRedBits = 8;
|
|
format_desc.cRedShift = 0;
|
|
format_desc.cGreenBits = 8;
|
|
format_desc.cGreenShift = 8;
|
|
format_desc.cBlueBits = 8;
|
|
format_desc.cBlueShift = 16;
|
|
|
|
int format_idx = ChoosePixelFormat(dc, &format_desc);
|
|
if (format_idx != 0){
|
|
BOOL spf = SetPixelFormat(dc, format_idx, &format_desc);
|
|
if (spf){
|
|
hglrc = w32_wglCreateContext(dc);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hglrc == 0){
|
|
error_message("failed to create bootstrap context");
|
|
}
|
|
|
|
// load wgl extension functions
|
|
w32_wglMakeCurrent(dc, hglrc);
|
|
#define X(N,R,P) WGL_GET_PROC_ADDR(w32_##N, #N);
|
|
WGL_EXT_FUNCS_X_LIST()
|
|
#undef X
|
|
w32_wglMakeCurrent(0, 0);
|
|
|
|
// check wgl extension functions
|
|
B32 missing_wgl_ext_func = 0;
|
|
#define X(N,R,P) if (w32_##N == 0){ missing_wgl_ext_func = 1; }
|
|
WGL_EXT_FUNCS_X_LIST()
|
|
#undef X
|
|
|
|
// report error
|
|
if (missing_wgl_ext_func){
|
|
error_message("failed to load wgl extension function(s)");
|
|
}
|
|
|
|
// delete context
|
|
{
|
|
BOOL delete_context = w32_wglDeleteContext(hglrc);
|
|
Assert(delete_context);
|
|
}
|
|
|
|
// cleanup dc
|
|
ReleaseDC(bootstrap_window, dc);
|
|
}
|
|
|
|
//- destroy bootstrap window, and class
|
|
{
|
|
BOOL destroy_window = DestroyWindow(bootstrap_window);
|
|
Assert(destroy_window);
|
|
BOOL unregister = UnregisterClassW(BOOTSTRAP_WINDOW_CLASS_NAME, hInstance);
|
|
Assert(unregister);
|
|
}
|
|
|
|
//- register graphics class
|
|
WNDCLASSW gr_wnd_class = {0};
|
|
gr_wnd_class.lpfnWndProc = w32_graphics_window_proc;
|
|
gr_wnd_class.hInstance = hInstance;
|
|
gr_wnd_class.lpszClassName = GRAPHICS_WINDOW_CLASS_NAME;
|
|
ATOM gr_atom = RegisterClassW(&gr_wnd_class);
|
|
if (gr_atom == 0){
|
|
error_message("failed to register graphics class");
|
|
}
|
|
|
|
//- create graphics context
|
|
int format_idx = 0;
|
|
|
|
{
|
|
// create dummy window
|
|
HWND dummy_window =
|
|
CreateWindowW(GRAPHICS_WINDOW_CLASS_NAME,
|
|
L"DUMMY",
|
|
WS_TILEDWINDOW,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, // x,y
|
|
CW_USEDEFAULT, CW_USEDEFAULT, // w,h
|
|
0, 0, hInstance, 0 // parent, menu, inst, param
|
|
);
|
|
if (dummy_window == 0){
|
|
error_message("failed to create dummy window");
|
|
}
|
|
|
|
|
|
HDC dc = GetDC(dummy_window);
|
|
|
|
// choose pixel format
|
|
int format_attribs[] = {
|
|
WGL_DRAW_TO_WINDOW_ARB, TRUE,
|
|
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
|
|
WGL_SWAP_METHOD_ARB, WGL_SWAP_EXCHANGE_ARB,
|
|
WGL_SUPPORT_OPENGL_ARB, TRUE,
|
|
WGL_DOUBLE_BUFFER_ARB, TRUE,
|
|
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
|
WGL_COLOR_BITS_ARB, 24,
|
|
WGL_RED_BITS_ARB, 8,
|
|
WGL_GREEN_BITS_ARB, 8,
|
|
WGL_BLUE_BITS_ARB, 8,
|
|
0
|
|
};
|
|
|
|
UINT num_formats = 0;
|
|
BOOL cpf = w32_wglChoosePixelFormatARB(dc, format_attribs, 0,
|
|
1, &format_idx, &num_formats);
|
|
if (cpf && num_formats > 0){
|
|
|
|
// set pixel format
|
|
PIXELFORMATDESCRIPTOR format_desc = {0};
|
|
BOOL spf = SetPixelFormat(dc, format_idx, &format_desc);
|
|
if (spf){
|
|
|
|
// create context
|
|
int attribs[] = {
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
|
|
graphics_context = w32_wglCreateContextAttribsARB(dc, 0, attribs);
|
|
}
|
|
}
|
|
|
|
//- load & check gl functions
|
|
w32_wglMakeCurrent(dc, graphics_context);
|
|
#define X(N,R,P) GL_FULL_PROC_ADDR(N, w32_opengl_module, #N);
|
|
GL_FUNC_X_LIST()
|
|
#undef X
|
|
w32_wglMakeCurrent(0, 0);
|
|
|
|
B32 missing_gl_func = 0;
|
|
#define X(N,R,P) if (N == 0){ missing_gl_func = 1; }
|
|
GL_FUNC_X_LIST()
|
|
#undef X
|
|
|
|
if (missing_gl_func){
|
|
error_message("failed to load gl functions");
|
|
}
|
|
|
|
// cleanup
|
|
ReleaseDC(dummy_window, dc);
|
|
DestroyWindow(dummy_window);
|
|
}
|
|
|
|
if (graphics_context == 0){
|
|
error_message("failed to create graphics context");
|
|
}
|
|
|
|
|
|
//- create the window
|
|
graphics_window =
|
|
CreateWindowW(GRAPHICS_WINDOW_CLASS_NAME,
|
|
L"TITLE ME",
|
|
WS_TILEDWINDOW,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, // x,y
|
|
CW_USEDEFAULT, CW_USEDEFAULT, // w,h
|
|
0, 0, hInstance, 0 // parent, menu, inst, param
|
|
);
|
|
if (graphics_window == 0){
|
|
error_message("failed to create graphics window");
|
|
}
|
|
|
|
//- set pixel format
|
|
{
|
|
HDC dc = GetDC(graphics_window);
|
|
PIXELFORMATDESCRIPTOR format_desc = {0};
|
|
SetPixelFormat(dc, format_idx, &format_desc);
|
|
ReleaseDC(graphics_window, dc);
|
|
}
|
|
|
|
//- make the window full screen
|
|
{
|
|
DWORD style = GetWindowLongW(graphics_window, GWL_STYLE);
|
|
HMONITOR mon = MonitorFromWindow(graphics_window, MONITOR_DEFAULTTOPRIMARY);
|
|
if (mon != 0){
|
|
MONITORINFO moninfo = {0};
|
|
moninfo.cbSize = sizeof(moninfo);
|
|
if (GetMonitorInfoW(mon, &moninfo)){
|
|
SetWindowLongW(graphics_window, GWL_STYLE, style & ~WS_TILEDWINDOW);
|
|
SetWindowPos(graphics_window, HWND_TOP,
|
|
moninfo.rcMonitor.left, moninfo.rcMonitor.top,
|
|
moninfo.rcMonitor.right - moninfo.rcMonitor.left,
|
|
moninfo.rcMonitor.bottom - moninfo.rcMonitor.top,
|
|
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
|
|
}
|
|
}
|
|
}
|
|
|
|
//- show the window
|
|
ShowWindow(graphics_window, SW_SHOW);
|
|
}
|
|
|
|
static void
|
|
wgl_helper_begin_render(void){
|
|
graphics_dc = GetDC(graphics_window);
|
|
w32_wglMakeCurrent(graphics_dc, graphics_context);
|
|
|
|
RECT rect = {0};
|
|
if (GetClientRect(graphics_window, &rect)){
|
|
graphics_w = (F32)(rect.right - rect.left);
|
|
graphics_h = (F32)(rect.bottom - rect.top);
|
|
}
|
|
}
|
|
|
|
static void
|
|
wgl_helper_end_render(void){
|
|
SwapBuffers(graphics_dc);
|
|
ReleaseDC(graphics_window, graphics_dc);
|
|
}
|
|
|
|
|
|
////////////////////////////////
|
|
//- Error Message
|
|
|
|
static void
|
|
error_message(char *msg){
|
|
MessageBoxA(0, msg, "Error", MB_OK);
|
|
ExitProcess(1);
|
|
}
|
|
|
|
|
|
//$ graphical //
|