| 1 |
module dglut.ext; |
|---|
| 2 |
import dglut.opengl; |
|---|
| 3 |
import std.string: toString, toStringz, split; |
|---|
| 4 |
public import dglut.set; |
|---|
| 5 |
|
|---|
| 6 |
template glStringFunction(string name, StringName glname) { |
|---|
| 7 |
mixin("string "~name~"() { string res; glChecked(res=toString(glGetString(glname))); return res; }"); |
|---|
| 8 |
} |
|---|
| 9 |
mixin MultiMixin!(glStringFunction, 2, "Vendor", GL_VENDOR, "Renderer", GL_RENDERER, "Version", GL_VERSION); |
|---|
| 10 |
Set!(string) Extensions() { |
|---|
| 11 |
string ext; |
|---|
| 12 |
glChecked(ext=toString(glGetString(GL_EXTENSIONS))); |
|---|
| 13 |
Set!(string) res; |
|---|
| 14 |
foreach (e; ext.split(" ")) if (!e.length) continue; else res.set(e); |
|---|
| 15 |
return res; |
|---|
| 16 |
} |
|---|
| 17 |
|
|---|
| 18 |
version(Win32) { } |
|---|
| 19 |
else { |
|---|
| 20 |
extern(C) void *glXGetProcAddressARB(char *procName); |
|---|
| 21 |
} |
|---|
| 22 |
|
|---|
| 23 |
import std.traits: ReturnType, ParameterTypeTuple; |
|---|
| 24 |
template SysFunc(C) { extern(System) alias ReturnType!(C) function(ParameterTypeTuple!(C)) SysFunc; } |
|---|
| 25 |
|
|---|
| 26 |
struct ExtFunc(C, string name) { |
|---|
| 27 |
static { |
|---|
| 28 |
SysFunc!(C) load() { |
|---|
| 29 |
static if (is(typeof(&glXGetProcAddressARB))) { |
|---|
| 30 |
return cast(SysFunc!(C)) glXGetProcAddressARB(toStringz("gl"~name)); |
|---|
| 31 |
} else static assert(false, "Platform not supported (yet)"); |
|---|
| 32 |
} |
|---|
| 33 |
SysFunc!(C) fn; |
|---|
| 34 |
ReturnType!(C) opCall(ParameterTypeTuple!(C) params) { |
|---|
| 35 |
if (!fn) fn=cast(typeof(fn)) load(); |
|---|
| 36 |
if (!fn) throw new Exception("Extension function \""~name~"\" not found in lookup table!"); |
|---|
| 37 |
return fn(params); |
|---|
| 38 |
} |
|---|
| 39 |
} |
|---|
| 40 |
} |
|---|
| 41 |
|
|---|
| 42 |
const GLenum GL_FRAMEBUFFER_EXT=0x8D40, GL_RENDERBUFFER_EXT=0x8D41; |
|---|
| 43 |
const GLenum GL_FRAMEBUFFER_BINDING_EXT=0x8CA6, GL_RENDERBUFFER_BINDING_EXT=0x8CA7, |
|---|
| 44 |
GL_MAX_COLOR_ATTACHMENTS_EXT=0x8CDF, GL_MAX_RENDERBUFFER_SIZE_EXT=0x84E8; |
|---|
| 45 |
typedef GLenum Attachment; |
|---|
| 46 |
mixin ConstEnum!(Attachment, "+1", |
|---|
| 47 |
0x8CE0, Expand!("GL_COLOR_ATTACHMENT{0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15}_EXT"), |
|---|
| 48 |
0x8D00, "GL_DEPTH_ATTACHMENT_EXT", 0x8D20, "GL_STENCIL_ATTACHMENT_EXT" |
|---|
| 49 |
); |
|---|
| 50 |
|
|---|
| 51 |
|
|---|
| 52 |
class glExtension(string name) { |
|---|
| 53 |
static { bool checked, _supported; } |
|---|
| 54 |
this() { |
|---|
| 55 |
if (!checked) { |
|---|
| 56 |
_supported=name in Extensions; |
|---|
| 57 |
checked=true; |
|---|
| 58 |
} |
|---|
| 59 |
if (!_supported) throw new Exception("Cannot create "~(cast(Object) this).toString~": "~name~" not supported!"); |
|---|
| 60 |
} |
|---|
| 61 |
static bool supported() { if (!checked) { checked=true; _supported=name in Extensions; } return _supported; } |
|---|
| 62 |
} |
|---|
| 63 |
|
|---|
| 64 |
mixin ConstEnum!(GLenum, "+1", 0x8CD5, Prepend!("GL_FRAMEBUFFER_", Append!("_EXT", |
|---|
| 65 |
"COMPLETE", Expand!("INCOMPLETE_{ATTACHMENT|MISSING_ATTACHMENT}"), |
|---|
| 66 |
0x8CD9, Expand!("INCOMPLETE_{DIMENSIONS|FORMATS|DRAW_BUFFER_EXT|READ_BUFFER_EXT}"), "UNSUPPORTED" |
|---|
| 67 |
)) |
|---|
| 68 |
); |
|---|
| 69 |
|
|---|
| 70 |
const GLenum GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT=0x8CD0, |
|---|
| 71 |
GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT=0x8CD1; |
|---|
| 72 |
|
|---|
| 73 |
class FrameBuffer : glExtension!("GL_EXT_framebuffer_object") { |
|---|
| 74 |
void checkState() { |
|---|
| 75 |
auto old=bound(); scope(exit) bind(old); bind; |
|---|
| 76 |
alias ExtFunc!(void function(GLenum, GLenum, GLenum, int *), "GetFramebufferAttachmentParameterivEXT") getpar; |
|---|
| 77 |
foreach (where, obj; attachedObjects) { |
|---|
| 78 |
int res, res2; |
|---|
| 79 |
getpar(GL_FRAMEBUFFER_EXT, where, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT, &res); |
|---|
| 80 |
getpar(GL_FRAMEBUFFER_EXT, where, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, &res2); |
|---|
| 81 |
if (!res || !res2) throw new Exception("Weird .. no draw buffer attached?"); |
|---|
| 82 |
} |
|---|
| 83 |
|
|---|
| 84 |
auto res=ExtFunc!(GLenum function(GLenum target), "CheckFramebufferStatusEXT")(GL_FRAMEBUFFER_EXT); |
|---|
| 85 |
if (res==GL_FRAMEBUFFER_COMPLETE_EXT) return; |
|---|
| 86 |
string[GLenum] decode=[0x8CD6: "Incomplete (attachment)"[], 0x8CD7: "Incomplete (missing attachment)", |
|---|
| 87 |
0x8CD9: "Incomplete (dimensions)", 0x8CDA: "Incomplete (formats)", |
|---|
| 88 |
0x8CDB: "Incomplete (draw buffer)", 0x8CDC: "Incomplete (read buffer)", |
|---|
| 89 |
0x8CDD: "Unsupported"]; |
|---|
| 90 |
if (!(res in decode)) throw new Exception("Unknown framebuffer error!"); |
|---|
| 91 |
throw new Exception("Framebuffer error: "~decode[res]); |
|---|
| 92 |
} |
|---|
| 93 |
uint fb; |
|---|
| 94 |
this() { ExtFunc!(void function(int, uint *), "GenFramebuffersEXT")(1, &fb); } |
|---|
| 95 |
void free() { |
|---|
| 96 |
ExtFunc!(void function(int, uint *), "DeleteFramebuffersEXT")(1, &fb); |
|---|
| 97 |
fb=0; |
|---|
| 98 |
} |
|---|
| 99 |
~this() { free; } |
|---|
| 100 |
|
|---|
| 101 |
static void bind(uint whar) { ExtFunc!(void function(GLenum, uint), "BindFramebufferEXT")(GL_FRAMEBUFFER_EXT, whar); } |
|---|
| 102 |
static uint bound() { uint res; glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, cast(int*)&res); return res; } |
|---|
| 103 |
bool isBound() { return bound==fb; } |
|---|
| 104 |
void bind() { bind(fb); } |
|---|
| 105 |
void Using(void delegate()[] dgs...) { |
|---|
| 106 |
glPushAttrib(GL_VIEWPORT_BIT); scope(exit) glPopAttrib; |
|---|
| 107 |
glViewport(0, 0, width, height); |
|---|
| 108 |
auto old=bound(); scope(exit) bind(old); bind; |
|---|
| 109 |
checkState; |
|---|
| 110 |
foreach (dg; dgs) dg(); |
|---|
| 111 |
} |
|---|
| 112 |
void With(void delegate()[] dgs...) { |
|---|
| 113 |
auto old=bound(); scope(exit) bind(old); bind; |
|---|
| 114 |
foreach (dg; dgs) dg(); |
|---|
| 115 |
} |
|---|
| 116 |
|
|---|
| 117 |
Texture[Attachment] attachedObjects; // for the sake of the GC |
|---|
| 118 |
int width, height; |
|---|
| 119 |
|
|---|
| 120 |
alias ExtFunc!(void function(GLenum, Attachment, GLenum, uint), "FramebufferRenderbufferEXT") FramebufferRenderbuffer; |
|---|
| 121 |
alias ExtFunc!(void function(GLenum target, Attachment, GLenum target, uint texture, int level), "FramebufferTexture2DEXT") |
|---|
| 122 |
glfbtex2d; |
|---|
| 123 |
void attach(Attachment where, PixelFormat component, Texture tex, int level=0) { |
|---|
| 124 |
auto old=bound(); scope(exit) bind(old); bind; |
|---|
| 125 |
if (width || height) { |
|---|
| 126 |
if (width!=tex.width || height!=tex.height) |
|---|
| 127 |
throw new Exception("All textures attached to a frame buffer must be the same size!"); |
|---|
| 128 |
} else { width=tex.width; height=tex.height; } |
|---|
| 129 |
glChecked("FrameBufBindTexture", { |
|---|
| 130 |
auto prev=tex.bound; scope(exit) tex.bind(prev); |
|---|
| 131 |
tex.bind; |
|---|
| 132 |
glChecked("FrameBufAttachTexture", glfbtex2d(GL_FRAMEBUFFER_EXT, where, GL_TEXTURE_2D, tex.me, level)); |
|---|
| 133 |
}); |
|---|
| 134 |
} |
|---|
| 135 |
void detach(Attachment where, int level=0) { |
|---|
| 136 |
glfbtex2d(GL_FRAMEBUFFER_EXT, where, GL_TEXTURE_2D, 0, level); |
|---|
| 137 |
width=0; height=0; |
|---|
| 138 |
} |
|---|
| 139 |
} |
|---|