1 module glamour.sampler;
2 
3 private {
4     import glamour.gl : GLuint, GLenum,
5                         GL_TEXTURE_BORDER_COLOR, GL_TEXTURE0,
6                         GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D,
7                         glGetSamplerParameterfv, glBindSampler,
8                         glSamplerParameteri, glSamplerParameterf,
9                         glGenSamplers, glDeleteSamplers,
10                         glTexParameteri, glTexParameterf;
11     import glamour.texture : ITexture, Texture1D, Texture2D, Texture3D;
12     import glamour.util : checkgl;
13 
14     debug import std.stdio : stderr;
15 }
16 
17 
18 /// 
19 class ShaderException : Exception {
20     this(string msg) {
21         super(msg);
22     }
23 }
24 
25 version(OSX) {
26     ///
27     alias EmulatedSampler Sampler;
28 } else {
29     ///
30     alias RealSampler Sampler;
31 }
32 
33 interface ISampler {
34     void set_parameter(T)(GLenum name, T params) if(is(T : int) || is(T : float));
35     float[] get_parameter(GLenum name);
36     void bind(ITexture tex);
37     void unbind(GLuint unit);
38     void unbind(ITexture tex);
39     void remove();
40 }
41 
42 /// Represents an OpenGL Sampler.
43 class RealSampler : ISampler {
44     /// The OpenGL sampler name.
45     GLuint sampler;
46     /// Alias this to sampler.
47     alias sampler this;
48        
49     /// Creates the OpenGL sampler.
50     this() {
51         checkgl!glGenSamplers(1, &sampler);
52     }
53 
54     ~this() {
55         debug if(sampler != 0) stderr.writefln("OpenGL: Sampler resources not released.");
56     }
57     
58     /// Sets a sampler parameter.
59     void set_parameter(T)(GLenum name, T params) if(is(T : int) || is(T : float)) {
60         static if(is(T : int)) {
61             checkgl!glSamplerParameteri(sampler, name, params);
62         } else {
63             checkgl!glSamplerParameterf(sampler, name, params);
64         }
65     }
66     
67     /// Queries a texture parameter from OpenGL.
68     float[] get_parameter(GLenum name) {
69         float[] ret;
70         
71         if(name == GL_TEXTURE_BORDER_COLOR) {
72             ret.length = 4;
73         } else {
74             ret.length = 1;
75         }
76         
77         checkgl!glGetSamplerParameterfv(sampler, name, ret.ptr);
78         return ret;
79     }
80     
81     /// Binds the sampler.
82     void bind(GLuint unit) {
83         checkgl!glBindSampler(unit, sampler);
84     }
85     
86     /// ditto
87     void bind(ITexture tex) {
88         checkgl!glBindSampler(tex.get_unit()-GL_TEXTURE0, sampler);
89     }
90     
91     /// Unbinds the sampler.
92     void unbind(GLuint unit) {
93         checkgl!glBindSampler(unit, 0);
94     }
95     
96     /// ditto
97     void unbind(ITexture tex) {
98         checkgl!glBindSampler(tex.get_unit(), 0);
99     }
100     
101     /// Deletes the sampler.
102     void remove() {
103         checkgl!glDeleteSamplers(1, &sampler);
104         sampler = 0;
105     }
106 }
107 
108 /// This emulates an OpenGL Sampler on platforms where no glGenSamplers is available (like OSX).
109 /// This only works with Texture1D, Texture2D and Texture3D so far.
110 class EmulatedSampler : ISampler {
111     protected Parameter[GLenum] parameters;
112     protected struct Parameter {
113         union {
114             int i;
115             float f;
116         }
117         bool is_float;
118 
119         this(float f) {
120             this.is_float = true;
121             this.f = f;
122         }
123 
124         this(int i) {
125             this.is_float = false;
126             this.i = i;
127         }
128     }
129 
130     /// Sets a sampler parameter (and stores it internally).
131     void set_parameter(T)(GLenum name, T params) if(is(T : int) || is(T : float)) {
132       parameters[name] = Parameter(params);
133     }
134 
135     /// Returns the stored parameter or [float.nan] if not internally stored.
136     float[] get_parameter(GLenum name)
137         in { assert(name != GL_TEXTURE_BORDER_COLOR);
138              assert(parameters[name].is_float); }
139         body {
140             if(auto param = name in parameters) {
141                 return [param.f];
142             } else {
143                 return [float.nan];
144             }
145         }
146 
147     /// Stub, throws always ShaderException.
148     void bind(GLuint unit) {
149       throw new ShaderException("Unsupported bind operation for emulated sampler");
150     }
151 
152     /// Applies the parameters to the passed texture, must be one of Texture1D, Texture2D or Texture3D,
153     /// or throws ShaderException
154     void bind(ITexture tex) {
155         GLenum unit;
156         tex.bind();
157 
158         if(cast(Texture1D)tex)
159           unit = GL_TEXTURE_1D;
160         else if(cast(Texture2D)tex)
161           unit = GL_TEXTURE_2D;
162         else if(cast(Texture3D)tex)
163           unit = GL_TEXTURE_3D;
164         else
165           throw new ShaderException("Unsupported texture type");
166 
167         foreach(name, parameter; parameters) {
168             if(parameter.is_float) {
169                 checkgl!glTexParameterf(unit, name, parameter.f);
170             } else {
171                 checkgl!glTexParameteri(unit, name, parameter.i);
172             }
173         }
174     }
175 
176     /// Stub.
177     void unbind(GLuint unit) {}
178 
179     /// Stub.
180     void unbind(ITexture tex) {}
181 
182     /// Stub.
183     void remove() {}
184 }