Shader.java 12 KB

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