A 2D tile-based sandbox game.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

Renderer.cc 9.3KB

3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
3年前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. #include "Renderer.h"
  2. #include <iostream>
  3. #include <stdio.h>
  4. #include <swan-common/constants.h>
  5. #include <string.h>
  6. // std::endian was originally in type_traits, was moved to bit
  7. #include <type_traits>
  8. #include <bit>
  9. #include "gl.h"
  10. #include "shaders.h"
  11. #include "GlWrappers.h"
  12. #include "TileAtlas.h"
  13. #include "util.h"
  14. namespace Cygnet {
  15. struct ChunkProg: public GlProgram {
  16. template<typename... T>
  17. ChunkProg(const T &... shaders): GlProgram(shaders...) { init(); }
  18. ~ChunkProg() { deinit(); }
  19. GLint camera = uniformLoc("camera");
  20. GLint pos = uniformLoc("pos");
  21. GLint vertex = attribLoc("vertex");
  22. GLint tileAtlas = uniformLoc("tileAtlas");
  23. GLint tileAtlasSize = uniformLoc("tileAtlasSize");
  24. GLint tiles = uniformLoc("tiles");
  25. GLuint vbo;
  26. static constexpr float ch = (float)SwanCommon::CHUNK_HEIGHT;
  27. static constexpr float cw = (float)SwanCommon::CHUNK_WIDTH;
  28. static constexpr GLfloat vertexes[] = {
  29. 0.0f, 0.0f, // pos 0: top left
  30. 0.0f, ch , // pos 1: bottom left
  31. cw, ch, // pos 2: bottom right
  32. cw, ch, // pos 2: bottom right
  33. cw, 0.0f, // pos 3: top right
  34. 0.0f, 0.0f, // pos 0: top left
  35. };
  36. void enable() {
  37. glUseProgram(id());
  38. glBindBuffer(GL_ARRAY_BUFFER, vbo);
  39. glVertexAttribPointer(vertex, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
  40. glEnableVertexAttribArray(vertex);
  41. glCheck();
  42. glUniform1i(tileAtlas, 0);
  43. glUniform1i(tiles, 1);
  44. }
  45. void disable() {
  46. glDisableVertexAttribArray(vertex);
  47. glCheck();
  48. }
  49. void init() {
  50. glGenBuffers(1, &vbo);
  51. glCheck();
  52. glBindBuffer(GL_ARRAY_BUFFER, vbo);
  53. glBufferData(GL_ARRAY_BUFFER, sizeof(vertexes), vertexes, GL_STATIC_DRAW);
  54. glCheck();
  55. }
  56. void deinit() {
  57. glDeleteBuffers(1, &vbo);
  58. glCheck();
  59. }
  60. };
  61. struct SpriteProg: public GlProgram {
  62. template<typename... T>
  63. SpriteProg(const T &... shaders): GlProgram(shaders...) { init(); }
  64. ~SpriteProg() { deinit(); }
  65. GLint camera = uniformLoc("camera");
  66. GLint transform = uniformLoc("transform");
  67. GLint frameInfo = uniformLoc("frameInfo");
  68. GLint vertex = attribLoc("vertex");
  69. GLint tex = uniformLoc("tex");
  70. GLuint vbo;
  71. static constexpr GLfloat vertexes[] = {
  72. 0.0f, 0.0f, // pos 0: top left
  73. 0.0f, 1.0f, // pos 1: bottom left
  74. 1.0f, 1.0f, // pos 2: bottom right
  75. 1.0f, 1.0f, // pos 2: bottom right
  76. 1.0f, 0.0f, // pos 3: top right
  77. 0.0f, 0.0f, // pos 0: top left
  78. };
  79. void enable() {
  80. glUseProgram(id());
  81. glBindBuffer(GL_ARRAY_BUFFER, vbo);
  82. glVertexAttribPointer(vertex, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
  83. glEnableVertexAttribArray(vertex);
  84. glCheck();
  85. glUniform1i(tex, 0);
  86. }
  87. void disable() {
  88. glDisableVertexAttribArray(vertex);
  89. glCheck();
  90. }
  91. void init() {
  92. glGenBuffers(1, &vbo);
  93. glCheck();
  94. glBindBuffer(GL_ARRAY_BUFFER, vbo);
  95. glBufferData(GL_ARRAY_BUFFER, sizeof(vertexes), vertexes, GL_STATIC_DRAW);
  96. glCheck();
  97. }
  98. void deinit() {
  99. glDeleteBuffers(1, &vbo);
  100. glCheck();
  101. }
  102. };
  103. struct RendererState {
  104. GlVxShader spriteVx{Shaders::spriteVx};
  105. GlFrShader spriteFr{Shaders::spriteFr};
  106. GlVxShader chunkVx{Shaders::chunkVx};
  107. GlFrShader chunkFr{Shaders::chunkFr};
  108. SpriteProg spriteProg{spriteVx, spriteFr};
  109. ChunkProg chunkProg{chunkVx, chunkFr};
  110. GLuint atlasTex;
  111. SwanCommon::Vec2 atlasTexSize;
  112. };
  113. Renderer::Renderer(): state_(std::make_unique<RendererState>()) {
  114. glGenTextures(1, &state_->atlasTex);
  115. glCheck();
  116. }
  117. Renderer::~Renderer() = default;
  118. void Renderer::draw(const RenderCamera &cam) {
  119. Mat3gf camMat;
  120. // Make the matrix translate to { -camX, -camY }, fix up the aspect ratio,
  121. // flip the Y axis so that positive Y direction is down, and scale according to zoom.
  122. float ratio = (float)cam.size.y / (float)cam.size.x;
  123. camMat.translate(cam.pos.scale(-ratio, 1) * cam.zoom);
  124. camMat.scale({ cam.zoom * ratio, -cam.zoom });
  125. auto &chunkProg = state_->chunkProg;
  126. auto &spriteProg = state_->spriteProg;
  127. {
  128. chunkProg.enable();
  129. glUniformMatrix3fv(chunkProg.camera, 1, GL_TRUE, camMat.data());
  130. glCheck();
  131. glUniform2f(chunkProg.tileAtlasSize, state_->atlasTexSize.x, state_->atlasTexSize.y);
  132. glCheck();
  133. glActiveTexture(GL_TEXTURE0);
  134. glBindTexture(GL_TEXTURE_2D, state_->atlasTex);
  135. glCheck();
  136. glActiveTexture(GL_TEXTURE1);
  137. for (auto [pos, chunk]: drawChunks_) {
  138. glUniform2f(chunkProg.pos, pos.x, pos.y);
  139. glBindTexture(GL_TEXTURE_2D, chunk.tex);
  140. glDrawArrays(GL_TRIANGLES, 0, 6);
  141. glCheck();
  142. }
  143. drawChunks_.clear();
  144. chunkProg.disable();
  145. }
  146. {
  147. spriteProg.enable();
  148. glUniformMatrix3fv(spriteProg.camera, 1, GL_TRUE, camMat.data());
  149. glCheck();
  150. glActiveTexture(GL_TEXTURE0);
  151. for (auto [mat, frame, sprite]: drawSprites_) {
  152. mat.scale(sprite.scale);
  153. glUniformMatrix3fv(spriteProg.transform, 1, GL_TRUE, mat.data());
  154. glUniform3f(spriteProg.frameInfo, sprite.scale.y, sprite.frameCount, frame);
  155. glBindTexture(GL_TEXTURE_2D, sprite.tex);
  156. glDrawArrays(GL_TRIANGLES, 0, 6);
  157. glCheck();
  158. }
  159. drawSprites_.clear();
  160. spriteProg.disable();
  161. }
  162. }
  163. void Renderer::uploadTileAtlas(const void *data, int width, int height) {
  164. glBindTexture(GL_TEXTURE_2D, state_->atlasTex);
  165. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
  166. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  167. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  168. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  169. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  170. glCheck();
  171. state_->atlasTexSize = {
  172. (float)(int)(width / SwanCommon::TILE_SIZE),
  173. (float)(int)(height / SwanCommon::TILE_SIZE) };
  174. }
  175. void Renderer::modifyTile(TileID id, const void *data) {
  176. int w = (int)state_->atlasTexSize.x;
  177. int x = id % w;
  178. int y = id / w;
  179. glActiveTexture(GL_TEXTURE0);
  180. glBindTexture(GL_TEXTURE_2D, state_->atlasTex);
  181. glTexSubImage2D(
  182. GL_TEXTURE_2D, 0, x * SwanCommon::TILE_SIZE, y * SwanCommon::TILE_SIZE,
  183. SwanCommon::TILE_SIZE, SwanCommon::TILE_SIZE,
  184. GL_RGBA, GL_UNSIGNED_BYTE, data);
  185. glCheck();
  186. }
  187. RenderChunk Renderer::createChunk(
  188. TileID tiles[SwanCommon::CHUNK_WIDTH * SwanCommon::CHUNK_HEIGHT]) {
  189. // TODO: Maybe don't do this here? Maybe instead store the buffer and
  190. // upload the texture in the draw method?
  191. // The current approach needs createChunk to be called on the graphics thread.
  192. RenderChunk chunk;
  193. glGenTextures(1, &chunk.tex);
  194. glCheck();
  195. glActiveTexture(GL_TEXTURE0);
  196. glBindTexture(GL_TEXTURE_2D, chunk.tex);
  197. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  198. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  199. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  200. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  201. glCheck();
  202. static_assert(
  203. std::endian::native == std::endian::big ||
  204. std::endian::native == std::endian::little,
  205. "Expected either big or little endian");
  206. if constexpr (std::endian::native == std::endian::little) {
  207. glTexImage2D(
  208. GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
  209. SwanCommon::CHUNK_WIDTH, SwanCommon::CHUNK_HEIGHT,
  210. 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, tiles);
  211. } else if constexpr (std::endian::native == std::endian::big) {
  212. uint8_t buf[SwanCommon::CHUNK_WIDTH * SwanCommon::CHUNK_HEIGHT * 2];
  213. for (size_t y = 0; y < SwanCommon::CHUNK_HEIGHT; ++y) {
  214. for (size_t x = 0; x < SwanCommon::CHUNK_WIDTH; ++x) {
  215. size_t dst = y * SwanCommon::CHUNK_WIDTH * 2 + x * 2;
  216. size_t src = y * SwanCommon::CHUNK_WIDTH + x;
  217. buf[dst + 0] = tiles[src] & 0xff;
  218. buf[dst + 1] = (tiles[src] & 0xff00) >> 8;
  219. }
  220. }
  221. glTexImage2D(
  222. GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
  223. SwanCommon::CHUNK_WIDTH, SwanCommon::CHUNK_HEIGHT,
  224. 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, buf);
  225. }
  226. glCheck();
  227. return chunk;
  228. }
  229. void Renderer::modifyChunk(RenderChunk chunk, SwanCommon::Vec2i pos, TileID id) {
  230. glActiveTexture(GL_TEXTURE0);
  231. glBindTexture(GL_TEXTURE_2D, chunk.tex);
  232. glCheck();
  233. static_assert(
  234. std::endian::native == std::endian::big ||
  235. std::endian::native == std::endian::little,
  236. "Expected either big or little endian");
  237. if constexpr (std::endian::native == std::endian::little) {
  238. glTexSubImage2D(
  239. GL_TEXTURE_2D, 0, pos.x, pos.y, 1, 1,
  240. GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, &id);
  241. } else if constexpr (std::endian::native == std::endian::big) {
  242. uint8_t buf[] = { (uint8_t)(id & 0xff), (uint8_t)((id & 0xff00) >> 8) };
  243. glTexSubImage2D(
  244. GL_TEXTURE_2D, 0, pos.x, pos.y, 1, 1,
  245. GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, buf);
  246. }
  247. glCheck();
  248. }
  249. void Renderer::destroyChunk(RenderChunk chunk) {
  250. glDeleteTextures(1, &chunk.tex);
  251. glCheck();
  252. }
  253. RenderSprite Renderer::createSprite(void *data, int width, int height, int fh) {
  254. RenderSprite sprite;
  255. sprite.scale = {
  256. (float)width / SwanCommon::TILE_SIZE,
  257. (float)fh / SwanCommon::TILE_SIZE };
  258. sprite.frameCount = height / fh;
  259. glGenTextures(1, &sprite.tex);
  260. glCheck();
  261. glActiveTexture(GL_TEXTURE0);
  262. glBindTexture(GL_TEXTURE_2D, sprite.tex);
  263. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  264. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  265. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  266. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  267. glCheck();
  268. glTexImage2D(
  269. GL_TEXTURE_2D, 0, GL_RGBA, width, height,
  270. 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
  271. glCheck();
  272. return sprite;
  273. }
  274. RenderSprite Renderer::createSprite(void *data, int width, int height) {
  275. return createSprite(data, width, height, height);
  276. }
  277. void Renderer::destroySprite(RenderSprite sprite) {
  278. glDeleteTextures(1, &sprite.tex);
  279. }
  280. }