opengl_helper.cxx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. //
  2. // helper.cxx
  3. // graphics
  4. //
  5. // Created by Sam Jaffe on 5/18/19.
  6. // Copyright © 2019 Sam Jaffe. All rights reserved.
  7. //
  8. #include "helper.hpp"
  9. #include <iostream>
  10. #include <memory>
  11. #include <sstream>
  12. #ifdef __APPLE__
  13. #include <OpenGL/gl3.h>
  14. #endif
  15. #include "scope_guard/scope_guard.hpp"
  16. #include "vector/vector.hpp"
  17. #include "game/graphics/shader.hpp"
  18. #include "game/util/env.hpp"
  19. #include "game/util/files.hpp"
  20. namespace {
  21. typedef void (*GLGetIntFor)(GLuint, GLenum, GLint *);
  22. typedef void (*GLGetLog)(GLuint, GLsizei, GLsizei *, GLchar *);
  23. struct opengl_error_formatter {
  24. void operator()() const;
  25. static void shader_error(GLuint id, std::string const & path);
  26. GLuint obj;
  27. std::string fileName, path, eNoticeMessage, eFileMessage, eWindowTitle;
  28. GLGetIntFor getIntv;
  29. GLGetLog getInfoLog;
  30. };
  31. void opengl_error_formatter::operator()() const {
  32. std::stringstream errorString;
  33. std::string errorLineNumber;
  34. int infologLength = 0;
  35. int maxLength = 0;
  36. getIntv(obj, GL_INFO_LOG_LENGTH, &maxLength);
  37. errorString << eNoticeMessage << "\n";
  38. std::unique_ptr<char[]> infoLog(new char[maxLength + 1]);
  39. getInfoLog(obj, maxLength, &infologLength, infoLog.get());
  40. std::string errorLog(infoLog.get());
  41. infoLog.reset();
  42. if (infologLength <= 0) return;
  43. std::size_t openParen = errorLog.find('(');
  44. std::size_t closeParen = errorLog.find(')');
  45. errorLineNumber =
  46. errorLog.substr(openParen + 1, closeParen - openParen - 1);
  47. errorString << fileName << " , "
  48. << "line " << errorLineNumber << ":\n";
  49. std::string errorStart = errorLog.find("error") != std::string::npos
  50. ? errorLog.substr(errorLog.find("error"))
  51. : errorLog;
  52. errorString << errorLog << "\n";
  53. errorString << "OpenGL Version: " << glGetString(GL_VERSION) << "\n";
  54. errorString << "GLSL Shading Version: "
  55. << glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n";
  56. errorString << eFileMessage << "\n";
  57. errorString << "RAW ERROR LOG: " << errorLog;
  58. std::stringstream consoleOutput;
  59. consoleOutput << "1>" << path << '(' << errorLineNumber << ')' << ": "
  60. << errorStart
  61. << "in OpenGL version: " << glGetString(GL_VERSION) << " and "
  62. << "GLSL Shading Version: "
  63. << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
  64. #ifdef _WIN32
  65. OutputDebugStringA(consoleOutput.str().c_str());
  66. MessageBoxA(nullptr, errorString.str().c_str(), eWindowTitle.c_str(),
  67. MB_OK);
  68. #else
  69. std::cerr << consoleOutput.str() << std::endl;
  70. std::cerr << errorString.str() << std::endl;
  71. #endif
  72. }
  73. void opengl_error_formatter::shader_error(GLuint id,
  74. std::string const & path) {
  75. std::string fileName = path.substr(path.find_last_of("/"));
  76. opengl_error_formatter{id,
  77. fileName,
  78. path,
  79. "GLSL shader compile error!",
  80. "File location" + path,
  81. "GLSL Compile Error in " + fileName,
  82. glGetShaderiv,
  83. glGetShaderInfoLog}();
  84. }
  85. }
  86. namespace graphics { namespace textures {
  87. static int glfmt(format color_fmt) {
  88. switch (color_fmt) {
  89. case format::RGB:
  90. return GL_RGB;
  91. case format::RGBA:
  92. return GL_RGBA;
  93. }
  94. }
  95. unsigned int init(format color_fmt, math::vec2i dimension,
  96. void const * data) {
  97. unsigned int id;
  98. // Enable texturings
  99. // glEnable( GL_TEXTURE_2D );
  100. // Tell OpenGL that our pixel data is single-byte aligned
  101. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  102. // Ask OpenGL for an unused texName (ID number) to use for this texture
  103. glGenTextures(1, &id);
  104. // Tell OpenGL to bind (set) this as the currently active texture
  105. glBindTexture(GL_TEXTURE_2D, id);
  106. // Set texture clamp vs. wrap (repeat)
  107. // one of: GL_CLAMP_TO_EDGE, GL_REPEAT, GL_MIRRORED_REPEAT,
  108. // GL_MIRROR_CLAMP_TO_EDGE, ...
  109. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  110. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  111. // the format our source pixel data is currently in; any of: GL_RGB,
  112. // GL_RGBA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, ...
  113. int bufferFormat = glfmt(color_fmt);
  114. // the format we want the texture to me on the card; allows us to translate
  115. // into a different texture format as we upload to OpenGL
  116. int internalFormat = bufferFormat;
  117. /* glTexImage2D: Load a 2d texture image
  118. * target: Creating this as a 2d texture.
  119. * level: Which mipmap level to use as the "root" (0 = the highest-quality,
  120. * full-res image), if mipmaps are enabled.
  121. * internalFormat: Type of texel format we want OpenGL to use for this
  122. * texture internally on the video card.
  123. * width: Texel-width of image; for maximum compatibility, use 2^N + 2^B,
  124. * where N is some integer in the range [3,10], and B is the border
  125. * thickness [0,1]
  126. * height: Texel-height of image; for maximum compatibility, use 2^M + 2^B,
  127. * where M is some integer in the range [3,10], and B is the border
  128. * thickness [0,1]
  129. * border: Border size, in texels (must be 0 or 1)
  130. * format: Pixel format describing the composition of the pixel data in
  131. * buffer
  132. * type: Pixel color components are unsigned bytes (one byte per color/alpha
  133. * channel)
  134. * pixels: Location of the actual pixel data bytes/buffer
  135. */
  136. glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, dimension.x(), dimension.y(),
  137. 0, bufferFormat, GL_UNSIGNED_BYTE, data);
  138. glGenerateMipmap(GL_TEXTURE_2D);
  139. // Set magnification (texel > pixel) and minification (texel < pixel)
  140. // filters one of: GL_NEAREST, GL_LINEAR
  141. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  142. // one of: GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST,
  143. // GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST,
  144. // GL_LINEAR_MIPMAP_LINEAR
  145. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
  146. GL_NEAREST_MIPMAP_LINEAR);
  147. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 5);
  148. return id;
  149. }
  150. }}
  151. namespace graphics { namespace shaders {
  152. struct file_read_error : std::runtime_error {
  153. using std::runtime_error::runtime_error;
  154. };
  155. struct compilation_error : std::runtime_error {
  156. using std::runtime_error::runtime_error;
  157. };
  158. struct linker_error : std::runtime_error {
  159. using std::runtime_error::runtime_error;
  160. };
  161. static int gltype(type tp) {
  162. switch (tp) {
  163. case type::FRAGMENT:
  164. return GL_FRAGMENT_SHADER;
  165. case type::VERTEX:
  166. return GL_VERTEX_SHADER;
  167. }
  168. }
  169. unsigned int init(type tp, std::string const & path) {
  170. std::unique_ptr<char const[]> buffer;
  171. // 1. Load the vertex shader code (text file) to a new memory buffer
  172. std::string const abs_path = env::resource_file(path);
  173. if ((buffer = files::load(abs_path))) {
  174. throw file_read_error("Could not load shader file " + abs_path);
  175. }
  176. // 2. Create a new shader ID
  177. // GL_VERTEX_SHADER or GL_FRAGMENT_SHADER
  178. unsigned int id = glCreateShader(gltype(tp));
  179. // 3. Associate the shader code with the new shader ID
  180. char const * buffer_ptr = buffer.get();
  181. glShaderSource(id, 1, &buffer_ptr, nullptr);
  182. // 4. Compile the shader (the shader compiler is built in to your graphics
  183. // card driver)
  184. glCompileShader(id);
  185. // 5. Check for compile errors
  186. int return_code;
  187. glGetShaderiv(id, GL_COMPILE_STATUS, &return_code);
  188. if (return_code != GL_TRUE) {
  189. opengl_error_formatter::shader_error(id, abs_path);
  190. throw compilation_error("Could not compile the shader file");
  191. }
  192. return id;
  193. }
  194. unsigned int init_program(shader const & fragmentShader,
  195. shader const & vertexShader) {
  196. // 9. Create a new shader program ID
  197. unsigned int id = glCreateProgram();
  198. // 10. Attach the vertex and fragment shaders to the new shader program
  199. glAttachShader(id, vertexShader.id);
  200. glAttachShader(id, fragmentShader.id);
  201. // 11. Do some other advanced stuff we'll do later on (like setting generic
  202. // vertex attrib locations)
  203. glBindAttribLocation(id, 0, "a_position");
  204. glBindAttribLocation(id, 1, "a_color");
  205. glBindAttribLocation(id, 2, "a_texCoords");
  206. glBindAttribLocation(id, 3, "a_normal");
  207. // 12. Link the program
  208. glLinkProgram(id);
  209. // 13. Check for link errors
  210. int return_code;
  211. glGetProgramiv(id, GL_LINK_STATUS, &return_code);
  212. if (return_code != GL_TRUE) {
  213. // LogShaderProgramError(*id, vertPath, fragPath);
  214. throw linker_error("Could not link shader program");
  215. }
  216. return id;
  217. }
  218. void activate(unsigned int id) {
  219. // 100. Use the shader program ID to "turn it on" for all subsequent drawing
  220. glUseProgram(id);
  221. /*
  222. // 101. Enable texturing and Bind texture(s) to GPU texture units
  223. glActiveTexture(GL_TEXTURE3);
  224. glEnable(GL_TEXTURE_2D);
  225. glBindTexture(GL_TEXTURE_2D, emissiveTextureID);
  226. glActiveTexture(GL_TEXTURE2);
  227. glEnable(GL_TEXTURE_2D);
  228. glBindTexture(GL_TEXTURE_2D, specularTextureID);
  229. glActiveTexture(GL_TEXTURE1);
  230. glEnable(GL_TEXTURE_2D);
  231. glBindTexture(GL_TEXTURE_2D, normalTextureID);
  232. glActiveTexture(GL_TEXTURE0);
  233. glEnable(GL_TEXTURE_2D);
  234. glBindTexture(GL_TEXTURE_2D, diffuseTextureID);
  235. */
  236. // 102. Get the location # of each named uniform you wish to pass in to the
  237. // shader
  238. int timeUniformLocation = glGetUniformLocation(id, "u_time");
  239. int scale = glGetUniformLocation(id, "Scale");
  240. int diffuseMapUniformLocation = glGetUniformLocation(id, "u_diffuseMap");
  241. int normalMapUniformLocation = glGetUniformLocation(id, "u_normalMap");
  242. int specularMapUniformLocation = glGetUniformLocation(id, "u_specularMap");
  243. int emissiveMapUniformLocation = glGetUniformLocation(id, "u_emissiveMap");
  244. int debugWave = glGetUniformLocation(id, "g_debugWave");
  245. // 103. Set the uniform values, including the texture unit numbers for
  246. // texture (sampler) uniforms
  247. // Env::GetCurrentTimeSeconds()
  248. glUniform1f(timeUniformLocation, (float)time(NULL));
  249. glUniform1f(scale, 2.f);
  250. glUniform1i(debugWave, 1); // TODO: m_waveEffectOn in ShaderProgram??
  251. // for GL_TEXTURE0, texture unit 0
  252. glUniform1i(diffuseMapUniformLocation, 0);
  253. // for GL_TEXTURE1, texture unit 1
  254. glUniform1i(normalMapUniformLocation, 1);
  255. // for GL_TEXTURE2, texture unit 2
  256. glUniform1i(specularMapUniformLocation, 2);
  257. // for GL_TEXTURE3, texture unit 3
  258. glUniform1i(emissiveMapUniformLocation, 3);
  259. }
  260. }}