1 module more.gl;
2 
3 import std.stdio;
4 import std.conv;
5 import std..string;
6 
7 //import glad.gl.enums;
8 import glad.gl.funcs;
9 import glad.gl.ext;
10 import glad.gl.loader;
11 import glad.gl.types;
12 
13 import gl3n.linalg;
14 
15 import more.common;
16 
17 struct Vector2i
18 {
19   int x,y;
20 }
21 struct Vector2f
22 {
23   align(4):
24   float x,y;
25 
26   this(float x, float y)
27   {
28     this.x = x;
29     this.y = y;
30   }
31 }
32 struct Vector3f
33 {
34   align(4):
35   float x,y,z;
36   this(float x, float y, float z)
37   {
38     this.x = x;
39     this.y = y;
40     this.z = z;
41   }
42 }
43 
44 /// Reads the shader source code and appends a NULL character
45 /// to the end of it so it can be passed to glShaderSource
46 char[] readGLShader(string filename)
47 {
48   File file = File(filename);
49   scope(exit) file.close();
50 
51   ulong fileSizeULong = file.size();
52   fileSizeULong++; // Add 1 for the ending NULL character
53   if(fileSizeULong > uint.max) throw new Exception(format(
54     "File '%s' is too large (%s bytes including the ending NULL)", filename, fileSizeULong));
55 
56   char[] contents = new char[cast(uint)fileSizeULong];
57   file.readFullSize(contents[0..$-1]);
58   contents[$-1] = '\0';
59 
60   return contents;
61 }
62 
63 
64 unittest
65 {
66   startTest("readFileToCStrings");
67   endTest("readFileToCStrings");
68 }
69 
70 uint loadShader(GLenum type, string filename)
71 {
72   return loadShader(type, filename, readGLShader(filename).ptr);
73 }
74 uint loadShader(GLenum type, string shaderName, const(char*) source)
75 {
76   char[512] error;
77 
78   uint id = glCreateShader(type);
79   if(id == 0) throw new Exception("glCreateShader failed");
80   scope(failure) glDeleteShader(id);
81 
82   glShaderSource(id, 1, &source, null);
83   glCompileShader(id);
84 
85   int errorLength;
86   glGetShaderInfoLog(id, error.length, &errorLength, error.ptr);
87 
88   if(errorLength) {
89     char[] msg = "Failed to compile shader '" ~ shaderName ~ "': " ~ error[0..core.stdc..string.strlen(error.ptr)];
90     throw new Exception(to!string(msg));
91   }
92   return id;
93 }
94 
95 
96 struct ShaderVar
97 {
98   string name;
99   immutable(char)* zname;
100 
101   GLint location;
102 
103   this(string name)
104   {
105     this.name = name;
106     this.zname = name.toStringz();
107   }
108 }
109 
110 struct ShaderProgram
111 {
112   uint id;
113 
114   void init()
115   {
116     this.id = glCreateProgram();
117     if(this.id == 0) throw new Exception("glCreateProgram failed");
118   }
119   void attachShader(GLenum type, string filename)
120   {
121     if(id == 0) init();
122     attachShader(loadShader(type, filename));
123   }
124   void attachShader(GLenum type, string shaderName, const(char*) source)
125   {
126     if(id == 0) init();
127     attachShader(loadShader(type, shaderName, source));
128   }
129   void attachShader(uint shaderID)
130   {
131     if(id == 0) init();
132     glAttachShader(id, shaderID);
133     // TODO: check for errors
134   }
135   void link()
136   {
137     if(id == 0) throw new Exception("Cannot link until you attach shaders");
138 
139     glLinkProgram(id);
140     int linkResult;
141     glGetProgramiv(id, GL_LINK_STATUS, &linkResult);
142     if(linkResult == GL_FALSE) throw new Exception("Failed to link shader program");
143   }
144 
145   void use()
146   {
147     glUseProgram(id);
148   }
149 
150 /+
151   GLint getUniformLocation(string name)
152   {
153     GLint location = glGetUniformLocation(id, name);
154     if(location == -1) throw new Exception("Failed to get uniform '" ~ name ~ "' variable location");
155   }
156 +/
157   GLint getUniformLocation(const(char)* name)
158   {
159     GLint location = glGetUniformLocation(id, name);
160     if(location == -1) throw new Exception("Failed to get uniform '" ~ to!string(name) ~ "' variable location");
161     return location;
162   }
163 /+
164   GLint getAttributeLocation(string name)
165   {
166     GLint location = glGetAttribLocation(id, name);
167     if(location == -1) throw new Exception("Failed to get attribute '" ~ name ~ "' variable location");
168     return location;
169   }
170 +/
171   GLint getAttributeLocation(const(char)* name)
172   {
173     GLint location = glGetAttribLocation(id, name);
174     if(location == -1) throw new Exception("Failed to get attribute '" ~ to!string(name) ~ "' variable location");
175     return location;
176   }
177 
178   void getUniformLocations(ShaderVar[] vars...)
179   {
180     foreach(var; vars) {
181       var.location = glGetUniformLocation(id, var.zname);
182       if(var.location == -1) throw new Exception("Failed to get uniform '" ~ var.name ~ "' variable location");
183     }
184   }
185   void getAttributeLocations(ShaderVar[] vars...)
186   {
187     foreach(var; vars) {
188       var.location = glGetAttribLocation(id, var.zname);
189       if(var.location == -1) throw new Exception("Failed to get attribute '" ~ var.name ~ "' variable location");
190     }
191   }
192 }
193 
194 
195 void perspective(ref mat4 mat, float fieldOfViewYDegrees, float widthToHeightRatio,
196                  float zClipNear, float zClipFar)
197 {
198 
199   float yMax = zClipNear * tan(fieldOfViewYDegrees * PI / 360);
200   float xMax = yMax * widthToHeightRatio;
201 
202   frustrum(mat, -xMax, xMax, -yMax, yMax, zClipNear, zClipFar);
203 }
204 void frustrum(ref mat4 mat, float left, float right,
205               float bottom, float top, float zNear, float zFar)
206 {
207   float temp   = 2 * zNear;
208   float width  = right - left;
209   float height = top - bottom;
210   float depth  = zFar - zNear;
211 
212   mat[0][0] = temp / width;
213   mat[0][1] = 0;
214   mat[0][2] = 0;
215   mat[0][3] = 0;
216 
217   mat[1][0] = 0;
218   mat[1][1] = temp / height;
219   mat[1][2] = 0;
220   mat[1][3] = 0;
221 
222   mat[2][0] = (right + left)  / width;
223   mat[2][1] = (top + bottom)  / height;
224   mat[2][2] = (-zFar - zNear) / depth;
225   mat[2][3] = -1;
226 
227   mat[3][0] = 0;
228   mat[3][1] = 0;
229   mat[3][2] = (-temp * zFar) / depth;
230   mat[3][3] = 0;
231 }