Shader.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. package me.hammerle.snuviengine.api;
  2. import java.io.IOException;
  3. import java.net.URL;
  4. import java.nio.FloatBuffer;
  5. import java.util.ArrayList;
  6. import java.util.LinkedList;
  7. import java.util.List;
  8. import java.util.Scanner;
  9. import org.lwjgl.BufferUtils;
  10. import static org.lwjgl.opengl.GL11.*;
  11. import static org.lwjgl.opengl.GL14.*;
  12. import static org.lwjgl.opengl.GL20.*;
  13. public final class Shader
  14. {
  15. private static int program = -1;
  16. private static int width = 1024;
  17. private static int height = 620;
  18. private static int scale = 2;
  19. private static final List<Runnable> TASKS = new LinkedList<>();
  20. protected static boolean initDone = false;
  21. private final static FontRenderer FONT_RENDERER = new FontRenderer();
  22. private final static ColorRenderer COLOR_RENDERER = new ColorRenderer();
  23. private final static DirectTextureRenderer TEXTURE_RENDERER = new DirectTextureRenderer();
  24. // uniform stuff
  25. private static int unifViewMatrix = -1;
  26. private static int unifModelMatrix = -1;
  27. private static MatrixStack modelMatrix;
  28. private static int unifAmbientLight = -1;
  29. private static int[][] unifLight;
  30. private static int unifUseTexture = -1;
  31. private static int unifUseColor = -1;
  32. private static int unifUseLight = -1;
  33. private static int unifUseMixColor = -1;
  34. private static int unifMixColorLoc = -1;
  35. private static float[] unifMixColor = new float[] {0.0f, 0.0f, 0.0f, 0.0f};
  36. protected static void init()
  37. {
  38. program = createShaderProgram("vertex.vs", "fragment.fs");
  39. glUseProgram(program);
  40. unifViewMatrix = glGetUniformLocation(program, "viewMatrix");
  41. updateViewMatrix();
  42. unifModelMatrix = glGetUniformLocation(program, "modelMatrix");
  43. modelMatrix = new MatrixStack(20);
  44. updateMatrix();
  45. unifAmbientLight = glGetUniformLocation(program, "ambientLight");
  46. setAmbientLight(1.0f, 1.0f, 1.0f);
  47. unifLight = new int[32][3];
  48. for(int index = 0; index < unifLight.length; index++)
  49. {
  50. unifLight[index][0] = glGetUniformLocation(program, "lights[" + index + "].color");
  51. unifLight[index][1] = glGetUniformLocation(program, "lights[" + index + "].pos");
  52. unifLight[index][2] = glGetUniformLocation(program, "lights[" + index + "].strength");
  53. setLightColor(index, 0.0f, 0.0f, 0.0f);
  54. setLightLocation(index, 0.0f, 0.0f);
  55. setLightStrength(index, 0.0f);
  56. }
  57. unifUseTexture = glGetUniformLocation(program, "useTexture");
  58. setTextureEnabled(false);
  59. unifUseColor = glGetUniformLocation(program, "useColor");
  60. setColorEnabled(false);
  61. unifUseLight = glGetUniformLocation(program, "useLight");
  62. setLightEnabled(false);
  63. unifUseMixColor = glGetUniformLocation(program, "useMixColor");
  64. setMixColorEnabled(false);
  65. unifMixColorLoc = glGetUniformLocation(program, "mixColor");
  66. setMixColor(0.0f, 0.0f, 0.0f, 0.0f);
  67. setViewPort(width, height);
  68. initDone = true;
  69. }
  70. protected static int getProgram()
  71. {
  72. return program;
  73. }
  74. protected static void addTask(Runnable r)
  75. {
  76. TASKS.add(r);
  77. }
  78. protected static void doTasks()
  79. {
  80. if(!TASKS.isEmpty())
  81. {
  82. TASKS.forEach(r -> r.run());
  83. TASKS.clear();
  84. }
  85. }
  86. public static FontRenderer getFontRenderer()
  87. {
  88. return FONT_RENDERER;
  89. }
  90. public static ColorRenderer getColorRenderer()
  91. {
  92. return COLOR_RENDERER;
  93. }
  94. public static DirectTextureRenderer getTextureRenderer()
  95. {
  96. return TEXTURE_RENDERER;
  97. }
  98. private static void updateViewMatrix()
  99. {
  100. FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
  101. buffer.put(2.0f / width);
  102. buffer.put(0.0f);
  103. buffer.put(0.0f);
  104. buffer.put(0.0f);
  105. buffer.put(0.0f);
  106. buffer.put(-2.0f / height);
  107. buffer.put(0.0f);
  108. buffer.put(0.0f);
  109. buffer.put(0.0f);
  110. buffer.put(0.0f);
  111. buffer.put(-1.0f / height);
  112. buffer.put(0.0f);
  113. buffer.put(-1.0f);
  114. buffer.put(1.0f);
  115. buffer.put(0.5f);
  116. buffer.put(1.0f);
  117. buffer.flip();
  118. glUniformMatrix4fv(unifViewMatrix, false, buffer);
  119. }
  120. protected static void setViewPort(int width, int height)
  121. {
  122. scale = 1;
  123. while(width / (scale + 1) >= 400 && height / (scale + 1) >= 300)
  124. {
  125. scale++;
  126. }
  127. Shader.width = width / scale;
  128. Shader.height = height / scale;
  129. updateViewMatrix();
  130. }
  131. public static int getViewScale()
  132. {
  133. return scale;
  134. }
  135. public static int getViewWidth()
  136. {
  137. return width;
  138. }
  139. public static int getViewHeight()
  140. {
  141. return height;
  142. }
  143. public static void updateMatrix()
  144. {
  145. glUniformMatrix4fv(unifModelMatrix, false, modelMatrix.getData());
  146. }
  147. public static void pushMatrix()
  148. {
  149. modelMatrix.push();
  150. }
  151. public static void popMatrix()
  152. {
  153. modelMatrix.pop();
  154. }
  155. public static void translate(float tx, float ty)
  156. {
  157. modelMatrix.translate(tx, ty);
  158. }
  159. public static void translateTo(float tx, float ty)
  160. {
  161. modelMatrix.translateTo(tx, ty);
  162. }
  163. public static void scale(float sx, float sy)
  164. {
  165. modelMatrix.scale(sx, sy);
  166. }
  167. public static void rotate(float angle)
  168. {
  169. modelMatrix.rotate(angle);
  170. }
  171. public static void setAmbientLight(float r, float g, float b)
  172. {
  173. glUniform3f(unifAmbientLight, r, g, b);
  174. }
  175. private static void checkLightIndex(int index)
  176. {
  177. if(index < 0 || index > unifLight.length)
  178. {
  179. throw new ShaderException("'" + index + "' is not a valid light index");
  180. }
  181. }
  182. public static void setLightColor(int index, float r, float g, float b)
  183. {
  184. checkLightIndex(index);
  185. glUniform3f(unifLight[index][0], r, g, b);
  186. }
  187. public static void setLightLocation(int index, float x, float y)
  188. {
  189. checkLightIndex(index);
  190. glUniform2f(unifLight[index][1], x, y);
  191. }
  192. public static void setLightStrength(int index, float strength)
  193. {
  194. checkLightIndex(index);
  195. glUniform1f(unifLight[index][2], strength);
  196. }
  197. public static void setTextureEnabled(boolean use)
  198. {
  199. glUniform1i(unifUseTexture, use ? 1 : 0);
  200. }
  201. public static void setColorEnabled(boolean use)
  202. {
  203. glUniform1i(unifUseColor, use ? 1 : 0);
  204. }
  205. public static void setMixColorEnabled(boolean use)
  206. {
  207. glUniform1i(unifUseMixColor, use ? 1 : 0);
  208. }
  209. public static void setMixColor(float r, float g, float b, float a)
  210. {
  211. unifMixColor[0] = r;
  212. unifMixColor[1] = g;
  213. unifMixColor[2] = b;
  214. unifMixColor[3] = a;
  215. glUniform4fv(unifMixColorLoc, unifMixColor);
  216. }
  217. public static void setLightEnabled(boolean use)
  218. {
  219. glUniform1i(unifUseLight, use ? 1 : 0);
  220. }
  221. public static void setDepthTestEnabled(boolean use)
  222. {
  223. if(use)
  224. {
  225. glEnable(GL_DEPTH_TEST);
  226. glDepthFunc(GL_LEQUAL);
  227. }
  228. else
  229. {
  230. glDisable(GL_DEPTH_TEST);
  231. }
  232. }
  233. public static void setBlendingEnabled(boolean use)
  234. {
  235. if(use)
  236. {
  237. glEnable(GL_BLEND);
  238. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  239. glBlendEquation(GL_FUNC_ADD);
  240. }
  241. else
  242. {
  243. glDisable(GL_BLEND);
  244. }
  245. }
  246. // -------------------------------------------------------------------------
  247. // general stuff
  248. // -------------------------------------------------------------------------
  249. private static String[] readFile(String name)
  250. {
  251. URL url = Shader.class.getClassLoader().getResource("me/hammerle/snuviengine/shader/" + name);
  252. if(url == null)
  253. {
  254. throw new ShaderException("failed reading shader '" + name + "'");
  255. }
  256. ArrayList<String> strings = new ArrayList<>();
  257. try(Scanner scanner = new Scanner(url.openStream()))
  258. {
  259. while(scanner.hasNext())
  260. {
  261. strings.add(scanner.nextLine() + "\n");
  262. }
  263. return strings.toArray(new String[strings.size()]);
  264. }
  265. catch(IOException ex)
  266. {
  267. throw new ShaderException("failed reading shader '" + name + "'");
  268. }
  269. }
  270. private static String getError()
  271. {
  272. StringBuilder sb = new StringBuilder();
  273. int glErr = glGetError();
  274. while(glErr != GL_NO_ERROR)
  275. {
  276. sb.append(glErr);
  277. sb.append(" ");
  278. glErr = glGetError();
  279. }
  280. return sb.toString();
  281. }
  282. private static int createShaderProgram(String vertex, String fragment)
  283. {
  284. // ---------------------------------------------------------------------
  285. // vertex shader
  286. // ---------------------------------------------------------------------
  287. String vShaderSource[] = readFile(vertex);
  288. int vShader = glCreateShader(GL_VERTEX_SHADER);
  289. glShaderSource(vShader, vShaderSource);
  290. glCompileShader(vShader);
  291. String error = getError();
  292. if(!error.isEmpty())
  293. {
  294. throw new ShaderException("failed compiling vertex shader '" + vertex + "' " + error);
  295. }
  296. int compiled = glGetShaderi(vShader, GL_COMPILE_STATUS);
  297. if(compiled != 1)
  298. {
  299. throw new ShaderException("failed compiling vertex shader '" + vertex + "' " + compiled + " " + glGetShaderInfoLog(vShader));
  300. }
  301. // ---------------------------------------------------------------------
  302. // fragment shader
  303. // ---------------------------------------------------------------------
  304. String fShaderSource[] = readFile(fragment);
  305. int fShader = glCreateShader(GL_FRAGMENT_SHADER);
  306. glShaderSource(fShader, fShaderSource);
  307. glCompileShader(fShader);
  308. error = getError();
  309. if(!error.isEmpty())
  310. {
  311. throw new ShaderException("failed compiling fragment shader '" + fragment + "' " + error);
  312. }
  313. compiled = glGetShaderi(fShader, GL_COMPILE_STATUS);
  314. if(compiled != 1)
  315. {
  316. throw new ShaderException("failed compiling fragment shader '" + fragment + "' " + compiled + " " + glGetShaderInfoLog(fShader));
  317. }
  318. // ---------------------------------------------------------------------
  319. // linking
  320. // ---------------------------------------------------------------------
  321. int vfprogram = glCreateProgram();
  322. glAttachShader(vfprogram, vShader);
  323. glAttachShader(vfprogram, fShader);
  324. glLinkProgram(vfprogram);
  325. error = getError();
  326. if(!error.isEmpty())
  327. {
  328. throw new ShaderException("failed linking shaders '" + vertex + "' and '" + fragment + "' " + error);
  329. }
  330. compiled = glGetProgrami(vfprogram, GL_LINK_STATUS);
  331. if(compiled != 1)
  332. {
  333. throw new ShaderException("failed linking shaders '" + vertex + "' and '" + fragment + "' " + compiled + " " + glGetProgramInfoLog(vfprogram));
  334. }
  335. glDeleteShader(vShader);
  336. glDeleteShader(fShader);
  337. return vfprogram;
  338. }
  339. }