|
|
1d641c |
|
|
|
1d641c |
|
|
|
1d641c |
#include "private.h"
|
|
|
1d641c |
#include "world.h"
|
|
|
1d641c |
#include "framebuffer.h"
|
|
|
d6f40c |
#include "drawing.h"
|
|
|
1d641c |
|
|
|
1d641c |
|
|
|
909bc2 |
typedef struct _HeliFramebufferDesc {
|
|
|
1d641c |
unsigned int texture_id;
|
|
|
1d641c |
unsigned int renderbuffer_id;
|
|
|
1d641c |
unsigned int framebuffer_id;
|
|
|
909bc2 |
} HeliFramebufferDesc;
|
|
|
909bc2 |
|
|
|
909bc2 |
struct _Framebuffer {
|
|
|
909bc2 |
int width;
|
|
|
909bc2 |
int height;
|
|
|
909bc2 |
HeliFramebufferDesc base;
|
|
|
909bc2 |
HeliFramebufferDesc multisample;
|
|
|
1d641c |
};
|
|
|
1d641c |
|
|
|
1d641c |
|
|
|
1d641c |
static void heliInitFramebuffer(
|
|
|
909bc2 |
HeliFramebufferDesc *fbd,
|
|
|
1d641c |
int width,
|
|
|
1d641c |
int height,
|
|
|
1d641c |
const void *pixels,
|
|
|
1d641c |
int horWrap,
|
|
|
1d641c |
int vertWrap,
|
|
|
1d641c |
int smooth,
|
|
|
1d641c |
int multisample,
|
|
|
1d641c |
int simple
|
|
|
1d641c |
) {
|
|
|
909bc2 |
fbd->texture_id = 0;
|
|
|
909bc2 |
fbd->renderbuffer_id = 0;
|
|
|
909bc2 |
fbd->framebuffer_id = 0;
|
|
|
909bc2 |
|
|
|
909bc2 |
unsigned int texTargetBinding = multisample ? GL_TEXTURE_BINDING_2D_MULTISAMPLE : GL_TEXTURE_BINDING_2D;
|
|
|
909bc2 |
unsigned int texTarget = multisample ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
|
|
|
1d641c |
|
|
|
1d641c |
unsigned int prev = 0;
|
|
|
909bc2 |
glGetIntegerv(texTargetBinding, (int*)&prev);
|
|
|
909bc2 |
glGenTextures(1, &fbd->texture_id);
|
|
|
909bc2 |
glBindTexture(texTarget, fbd->texture_id);
|
|
|
909bc2 |
if (multisample) {
|
|
|
909bc2 |
heliGLTexImage2DMultisamplePtr(texTarget, multisample, GL_RGBA, width, height, GL_TRUE);
|
|
|
909bc2 |
} else {
|
|
|
909bc2 |
glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, smooth ? GL_LINEAR : GL_NEAREST);
|
|
|
909bc2 |
glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, smooth ? GL_LINEAR : GL_NEAREST);
|
|
|
909bc2 |
glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, horWrap ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
|
909bc2 |
glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, vertWrap ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
|
909bc2 |
glTexImage2D(texTarget, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
|
|
909bc2 |
}
|
|
|
909bc2 |
glBindTexture(texTarget, prev);
|
|
|
1d641c |
|
|
|
1d641c |
if (!simple) {
|
|
|
1d641c |
glGetIntegerv(GL_RENDERBUFFER_BINDING, (int*)&prev);
|
|
|
909bc2 |
heliGLGenRenderbuffersPtr(1, &fbd->renderbuffer_id);
|
|
|
909bc2 |
heliGLBindRenderbufferPtr(GL_RENDERBUFFER, fbd->renderbuffer_id);
|
|
|
909bc2 |
if (multisample) heliGLRenderbufferStorageMultisamplePtr(GL_RENDERBUFFER, multisample, GL_DEPTH24_STENCIL8, width, height);
|
|
|
909bc2 |
else heliGLRenderbufferStoragePtr(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
|
|
1d641c |
heliGLBindRenderbufferPtr(GL_RENDERBUFFER, prev);
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (int*)&prev);
|
|
|
909bc2 |
heliGLGenFramebuffersPtr(1, &fbd->framebuffer_id);
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, fbd->framebuffer_id);
|
|
|
909bc2 |
if (fbd->renderbuffer_id)
|
|
|
909bc2 |
heliGLFramebufferRenderbufferPtr(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbd->renderbuffer_id);
|
|
|
909bc2 |
heliGLFramebufferTexture2DPtr(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texTarget, fbd->texture_id, 0);
|
|
|
1d641c |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, prev);
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
|
|
|
909bc2 |
static void heliDeinitFramebuffer(HeliFramebufferDesc *fbd) {
|
|
|
909bc2 |
if (fbd->framebuffer_id) {
|
|
|
1d641c |
unsigned int id = 0;
|
|
|
1d641c |
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (int*)&id);
|
|
|
909bc2 |
if (id == fbd->framebuffer_id) heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
1d641c |
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (int*)&id);
|
|
|
909bc2 |
if (id == fbd->framebuffer_id) heliGLBindFramebufferPtr(GL_READ_FRAMEBUFFER, 0);
|
|
|
909bc2 |
heliGLDeleteFramebuffersPtr(1, &fbd->framebuffer_id);
|
|
|
909bc2 |
fbd->framebuffer_id = 0;
|
|
|
1d641c |
}
|
|
|
909bc2 |
if (fbd->renderbuffer_id) {
|
|
|
909bc2 |
heliGLDeleteRenderbuffersPtr(1, &fbd->renderbuffer_id);
|
|
|
909bc2 |
fbd->renderbuffer_id = 0;
|
|
|
1d641c |
}
|
|
|
909bc2 |
if (fbd->texture_id) {
|
|
|
909bc2 |
glDeleteTextures(1, &fbd->texture_id);
|
|
|
909bc2 |
fbd->texture_id = 0;
|
|
|
1d641c |
}
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
|
|
|
1d641c |
Framebuffer createFramebufferEx(int width, int height, const void *pixels, int horWrap, int vertWrap, int smooth) {
|
|
|
1d641c |
Framebuffer fb = calloc(1, sizeof(*fb));
|
|
|
909bc2 |
heliObjectRegister(fb, (HeliFreeCallback)&framebufferDestroy);
|
|
|
1d641c |
|
|
|
1d641c |
if (width <= 0 || height <= 0) {
|
|
|
1d641c |
fprintf(stderr, "helianthus: cannot create framebuffer, bad size: %dx%d\n", width, height);
|
|
|
1d641c |
return fb;
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
if (!heliGLGenFramebuffersPtr || !heliGLGenRenderbuffersPtr) {
|
|
|
1d641c |
fprintf(stderr, "helianthus: cannot create framebuffer, seems OpenGL version is too low\n");
|
|
|
1d641c |
return fb;
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
909bc2 |
fb->width = width;
|
|
|
909bc2 |
fb->height = height;
|
|
|
909bc2 |
|
|
|
909bc2 |
heliInitFramebuffer(&fb->base, width, height, pixels, horWrap, vertWrap, smooth, 0, FALSE);
|
|
|
909bc2 |
|
|
|
909bc2 |
unsigned int err = 0;
|
|
|
909bc2 |
glGetError();
|
|
|
909bc2 |
if (heliGLTexImage2DMultisamplePtr && heliGLRenderbufferStorageMultisamplePtr) {
|
|
|
1d641c |
for(int multisample = 8; multisample >= 4; multisample /= 2) {
|
|
|
1d641c |
glGetError();
|
|
|
909bc2 |
heliInitFramebuffer(&fb->multisample, width, height, NULL, FALSE, FALSE, FALSE, multisample, FALSE);
|
|
|
909bc2 |
err = glGetError();
|
|
|
909bc2 |
if (err == GL_NO_ERROR) {
|
|
|
1d641c |
if (pixels) {
|
|
|
1d641c |
unsigned int prevR = 0, prevD = 0;
|
|
|
1d641c |
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (int*)&prevR);
|
|
|
1d641c |
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (int*)&prevD);
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_READ_FRAMEBUFFER, fb->base.framebuffer_id);
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, fb->multisample.framebuffer_id);
|
|
|
1d641c |
heliGLBlitFramebufferPtr(0, 0, fb->width, fb->height, 0, 0, fb->width, fb->height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
|
1d641c |
heliGLBindFramebufferPtr(GL_READ_FRAMEBUFFER, prevR);
|
|
|
1d641c |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, prevD);
|
|
|
1d641c |
}
|
|
|
1d641c |
break;
|
|
|
1d641c |
}
|
|
|
909bc2 |
heliDeinitFramebuffer(&fb->multisample);
|
|
|
1d641c |
}
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
909bc2 |
unsigned int prevD = 0;
|
|
|
909bc2 |
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (int*)&prevD);
|
|
|
909bc2 |
if (!fb->multisample.framebuffer_id) {
|
|
|
909bc2 |
fprintf(stderr, "helianthus: cannot initialize multisampling for framebuffer, OpenGL error code: 0x%04x\n", err);
|
|
|
909bc2 |
} else {
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, fb->multisample.framebuffer_id);
|
|
|
909bc2 |
err = heliGLCheckFramebufferStatusPtr(GL_DRAW_FRAMEBUFFER);
|
|
|
909bc2 |
if (err != GL_FRAMEBUFFER_COMPLETE) {
|
|
|
909bc2 |
fprintf(stderr, "helianthus: multisampling framebuffer in incomplete, status: 0x%04x, OpenGL error code: 0x%04x\n", err, glGetError());
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, prevD);
|
|
|
909bc2 |
heliDeinitFramebuffer(&fb->multisample);
|
|
|
909bc2 |
}
|
|
|
1d641c |
}
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, fb->multisample.framebuffer_id);
|
|
|
909bc2 |
err = heliGLCheckFramebufferStatusPtr(GL_DRAW_FRAMEBUFFER);
|
|
|
909bc2 |
if (err != GL_FRAMEBUFFER_COMPLETE) {
|
|
|
909bc2 |
fprintf(stderr, "helianthus: base framebuffer in incomplete, status: 0x%04x, OpenGL error code: 0x%04x", err, glGetError());
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, prevD);
|
|
|
909bc2 |
heliDeinitFramebuffer(&fb->base);
|
|
|
909bc2 |
}
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, prevD);
|
|
|
1d641c |
|
|
|
1d641c |
return fb;
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
|
|
|
1d641c |
Framebuffer createFramebuffer(int width, int height)
|
|
|
1d641c |
{ return createFramebufferEx(width, height, NULL, FALSE, FALSE, TRUE); }
|
|
|
1d641c |
|
|
|
1d641c |
|
|
|
1d641c |
Framebuffer createFramebufferFromFile(const char *path) {
|
|
|
1d641c |
int width = 0;
|
|
|
1d641c |
int height = 0;
|
|
|
1d641c |
unsigned char *pixels = NULL;
|
|
|
1d641c |
imageLoad(path, &width, &height, &pixels);
|
|
|
1d641c |
return createFramebufferEx(width, height, pixels, FALSE, FALSE, TRUE);
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
|
|
|
1d641c |
void framebufferDestroy(Framebuffer framebuffer) {
|
|
|
1d641c |
HeliDrawingState *ss = heliDrawingGetState();
|
|
|
909bc2 |
if (framebuffer->base.texture_id)
|
|
|
909bc2 |
heliUIntRemove(&heliDrawingFramebuffersToFlush, framebuffer->base.texture_id);
|
|
|
909bc2 |
if (framebuffer->base.framebuffer_id) {
|
|
|
1d641c |
for(HeliDrawingState *s = heliDrawingGetStateStack(); s <= ss; ++s) {
|
|
|
909bc2 |
if (ss->glState.framebuffer_read_id == framebuffer->base.framebuffer_id)
|
|
|
1d641c |
ss->glState.framebuffer_read_id = 0;
|
|
|
909bc2 |
if (ss->glState.framebuffer_draw_id == framebuffer->base.framebuffer_id)
|
|
|
1d641c |
ss->glState.framebuffer_draw_id = 0;
|
|
|
1d641c |
}
|
|
|
1d641c |
}
|
|
|
909bc2 |
if (framebuffer->multisample.framebuffer_id) {
|
|
|
909bc2 |
for(HeliDrawingState *s = heliDrawingGetStateStack(); s <= ss; ++s) {
|
|
|
909bc2 |
if (ss->glState.framebuffer_read_id == framebuffer->multisample.framebuffer_id)
|
|
|
909bc2 |
ss->glState.framebuffer_read_id = 0;
|
|
|
909bc2 |
if (ss->glState.framebuffer_draw_id == framebuffer->multisample.framebuffer_id)
|
|
|
909bc2 |
ss->glState.framebuffer_draw_id = 0;
|
|
|
909bc2 |
}
|
|
|
909bc2 |
}
|
|
|
909bc2 |
heliDeinitFramebuffer(&framebuffer->multisample);
|
|
|
909bc2 |
heliDeinitFramebuffer(&framebuffer->base);
|
|
|
1d641c |
free(framebuffer);
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
|
|
|
909bc2 |
void framebufferFlush(Framebuffer framebuffer) {
|
|
|
909bc2 |
if (framebuffer->base.texture_id)
|
|
|
909bc2 |
heliUIntRemove(&heliDrawingFramebuffersToFlush, framebuffer->base.texture_id);
|
|
|
909bc2 |
if (framebuffer->base.framebuffer_id && framebuffer->multisample.framebuffer_id) {
|
|
|
909bc2 |
unsigned int prevR = 0, prevD = 0;
|
|
|
909bc2 |
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (int*)&prevR);
|
|
|
909bc2 |
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (int*)&prevD);
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_READ_FRAMEBUFFER, framebuffer->multisample.framebuffer_id);
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, framebuffer->base.framebuffer_id);
|
|
|
909bc2 |
heliGLBlitFramebufferPtr(
|
|
|
909bc2 |
0, 0, framebuffer->width, framebuffer->height,
|
|
|
909bc2 |
0, 0, framebuffer->width, framebuffer->height,
|
|
|
909bc2 |
GL_COLOR_BUFFER_BIT, GL_NEAREST );
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_READ_FRAMEBUFFER, prevR);
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, prevD);
|
|
|
909bc2 |
}
|
|
|
909bc2 |
}
|
|
|
909bc2 |
|
|
|
909bc2 |
|
|
|
1d641c |
int framebufferGetWidth(Framebuffer framebuffer)
|
|
|
1d641c |
{ return framebuffer->width; }
|
|
|
1d641c |
int framebufferGetHeight(Framebuffer framebuffer)
|
|
|
1d641c |
{ return framebuffer->height; }
|
|
|
1d641c |
unsigned int framebufferGetGLTexId(Framebuffer framebuffer)
|
|
|
909bc2 |
{ return framebuffer->base.texture_id; }
|
|
|
909bc2 |
unsigned int framebufferGetGLId(Framebuffer framebuffer) {
|
|
|
909bc2 |
return framebuffer->multisample.framebuffer_id
|
|
|
909bc2 |
? framebuffer->multisample.framebuffer_id
|
|
|
909bc2 |
: framebuffer->base.framebuffer_id;
|
|
|
909bc2 |
}
|
|
|
1d641c |
|
|
|
1d641c |
|
|
|
1d641c |
int imageFromViewport(int *outWidth, int *outHeight, unsigned char **outPixels) {
|
|
|
1d641c |
*outWidth = 0;
|
|
|
1d641c |
*outHeight = 0;
|
|
|
1d641c |
*outPixels = NULL;
|
|
|
1d641c |
|
|
|
1d641c |
int vp[4] = {};
|
|
|
1d641c |
glGetIntegerv(GL_VIEWPORT, vp);
|
|
|
1d641c |
|
|
|
1d641c |
if (vp[2] <= 0 || vp[3] <= 0) return FALSE;
|
|
|
1d641c |
|
|
|
89ec47 |
unsigned int prevR = 0, prevD = 0;
|
|
|
89ec47 |
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (int*)&prevR);
|
|
|
89ec47 |
|
|
|
1d641c |
unsigned char *buffer = malloc(vp[2]*vp[3]*4);
|
|
|
1d641c |
glGetError();
|
|
|
1d641c |
glReadPixels(vp[0], vp[1], vp[2], vp[3], GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
|
|
1d641c |
if (glGetError() != GL_NO_ERROR) {
|
|
|
1d641c |
if (!heliGLBindFramebufferPtr || !heliGLBlitFramebufferPtr) {
|
|
|
1d641c |
fprintf(stderr, "helianthus: cannot read viewport pixels\n");
|
|
|
1d641c |
free(buffer);
|
|
|
1d641c |
return FALSE;
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
// blit multisample buffer
|
|
|
909bc2 |
HeliFramebufferDesc fbd = {};
|
|
|
909bc2 |
heliInitFramebuffer(&fbd, vp[2], vp[3], NULL, FALSE, FALSE, FALSE, 0, TRUE);
|
|
|
1d641c |
|
|
|
1d641c |
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (int*)&prevD);
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, fbd.framebuffer_id);
|
|
|
1d641c |
heliGLBlitFramebufferPtr(vp[0], vp[1], vp[2], vp[3], 0, 0, vp[2], vp[3], GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
|
909bc2 |
heliGLBindFramebufferPtr(GL_READ_FRAMEBUFFER, fbd.framebuffer_id);
|
|
|
1d641c |
glReadPixels(0, 0, vp[2], vp[3], GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
|
|
1d641c |
heliGLBindFramebufferPtr(GL_READ_FRAMEBUFFER, prevR);
|
|
|
1d641c |
heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, prevD);
|
|
|
1d641c |
|
|
|
909bc2 |
heliDeinitFramebuffer(&fbd);
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
// flip image
|
|
|
89ec47 |
if (!prevR) {
|
|
|
89ec47 |
size_t rowSize = vp[2]*4;
|
|
|
89ec47 |
unsigned char *row = malloc(rowSize);
|
|
|
89ec47 |
for(int i = vp[3]/2 - 1; i >= 0; --i) {
|
|
|
89ec47 |
unsigned char *r0 = buffer + rowSize*i;
|
|
|
89ec47 |
unsigned char *r1 = buffer + rowSize*(vp[3]-i-1);
|
|
|
89ec47 |
memcpy(row, r0, rowSize);
|
|
|
89ec47 |
memcpy(r0, r1, rowSize);
|
|
|
89ec47 |
memcpy(r1, row, rowSize);
|
|
|
89ec47 |
}
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
*outWidth = vp[2];
|
|
|
1d641c |
*outHeight = vp[3];
|
|
|
1d641c |
*outPixels = buffer;
|
|
|
1d641c |
return TRUE;
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
|