pluma.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. /*
  2. * Copyright (C) 2018 Alyssa Rosenzweig <alyssa@rosenzweig.io>
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sub license,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice (including the
  12. * next paragraph) shall be included in all copies or substantial portions
  13. * of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
  18. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21. * DEALINGS IN THE SOFTWARE.
  22. */
  23. #include <assert.h>
  24. #include "test-util-3d.h"
  25. /* Include image loading library */
  26. #define STB_IMAGE_IMPLEMENTATION
  27. #include "stb_image.h"
  28. static EGLint const config_attribute_list[] = {
  29. EGL_RED_SIZE, 8,
  30. EGL_GREEN_SIZE, 8,
  31. EGL_BLUE_SIZE, 8,
  32. EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
  33. EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
  34. EGL_DEPTH_SIZE, 8,
  35. EGL_NONE
  36. };
  37. static const EGLint context_attribute_list[] = {
  38. EGL_CONTEXT_CLIENT_VERSION, 2,
  39. EGL_NONE
  40. };
  41. static EGLDisplay display;
  42. static EGLConfig config;
  43. static EGLint num_config;
  44. static EGLContext context;
  45. static GLuint program;
  46. const char *vertex_shader_source =
  47. "attribute vec4 aPosition; \n"
  48. "attribute vec4 aColor; \n"
  49. "uniform sampler2D depthMap; \n"
  50. "uniform float angle;\n"
  51. " \n"
  52. "varying vec4 vColor; \n"
  53. " \n"
  54. "void main() \n"
  55. "{ \n"
  56. " vColor = aColor * vec4(8.02, 1.18, 1, 1); \n"
  57. " float x = aPosition.x;\n"
  58. " float y = aPosition.y;\n"
  59. " float z = texture2D(depthMap, vColor.xy).r;"
  60. " gl_Position = vec4(x, cos(angle)*y - sin(angle)*z, 1.0, 1.0); \n"
  61. "} \n";
  62. const char *fragment_shader_source =
  63. "precision mediump float; \n"
  64. " \n"
  65. "varying vec4 vColor; \n"
  66. "uniform sampler2D uTexture; \n"
  67. "uniform sampler2D depthMap; \n"
  68. " \n"
  69. "void main() \n"
  70. "{ \n"
  71. // " float z = texture2D(depthMap, vColor.xy).r;"
  72. " gl_FragColor = texture2D(uTexture, vColor.xy); \n"
  73. "} \n";
  74. /* Forward mapping implementation:
  75. *
  76. * 1. Generate an nxn triangular grid
  77. * 2. Run mapping in vertex shader
  78. * 3. Direct texture mapping in fragment shader
  79. */
  80. struct forward_grid {
  81. int width, height;
  82. GLfloat *vVertices;
  83. GLfloat *vColors;
  84. unsigned short *indices;
  85. };
  86. static struct forward_grid
  87. generate_grid(int N)
  88. {
  89. struct forward_grid grid = {
  90. .width = N,
  91. .height = N
  92. };
  93. /* Allocate room for the vertices/attributes/indices */
  94. grid.vVertices = calloc(N * N * 3, sizeof(GLfloat));
  95. grid.vColors = calloc(N * N * 4, sizeof(GLfloat));
  96. grid.indices = calloc(N * N * 6, sizeof(*grid.indices));
  97. /* Max number of vertices according to ushort indices */
  98. assert(N < 128);
  99. /* We want to subdivide [-1, -1] to [1, 1] in nxn pieces. That is,
  100. * iterate in each direction with a step size of 2/(N - 1) to make sure
  101. * both 0 and 1 are included */
  102. float step_size = (2.0) / ((float) (N - 1));
  103. for (int step_y = 0; step_y < N; ++step_y) {
  104. float y = step_y * step_size - 0.7f;
  105. for (int step_x = 0; step_x < N; ++step_x) {
  106. float x = step_x * step_size - 0.7f;
  107. /* This grid section is a rect from (x, y) to
  108. * (x+step_size, y + step_size) */
  109. int vertex_number = step_y*N + step_x;
  110. grid.vVertices[vertex_number*3 + 0] = x;
  111. grid.vVertices[vertex_number*3 + 1] = y;
  112. /* Set some attributes */
  113. grid.vColors[vertex_number*4 + 0] = ((float) step_x) / (N - 1);
  114. grid.vColors[vertex_number*4 + 1] = ((float) step_y) / (N - 1);
  115. }
  116. }
  117. /* Form indexed buffer from the subdivied vertices. The scheme is to
  118. * draw quads connecting adjacent vertices, and split those quads into
  119. * triangles. */
  120. int index_start = 0;
  121. for (int y = 0; y < (N - 1); ++y) {
  122. for (int x = 0; x < (N - 1); ++x) {
  123. /* Connect (x, y) (x + 1, y) (x, y + 1) */
  124. grid.indices[index_start + 0] = (y + 0)*N + (x + 0);
  125. grid.indices[index_start + 1] = (y + 0)*N + (x + 1);
  126. grid.indices[index_start + 2] = (y + 1)*N + (x + 0);
  127. /* Connect (x + 1, y + 1) (x + 1, y) (x, y + 1).
  128. * XXX: Winding order? */
  129. grid.indices[index_start + 3] = (y + 1)*N + (x + 1);
  130. grid.indices[index_start + 4] = (y + 0)*N + (x + 1);
  131. grid.indices[index_start + 5] = (y + 1)*N + (x + 0);
  132. /* Advance index buffer */
  133. index_start += 6;
  134. }
  135. }
  136. /* Send the grid off to the GPU */
  137. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, grid.vVertices);
  138. glEnableVertexAttribArray(0);
  139. glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, grid.vColors);
  140. glEnableVertexAttribArray(1);
  141. return grid;
  142. }
  143. static void
  144. draw_grid(struct forward_grid *grid)
  145. {
  146. /* Triangle*2 * the size of the grid */
  147. glDrawElements(GL_TRIANGLES, 3 * 2 * grid->width * grid->height, GL_UNSIGNED_SHORT, grid->indices);
  148. }
  149. /* Texture related boilerplate */
  150. struct image {
  151. uint8_t *data;
  152. int width;
  153. int height;
  154. int n;
  155. };
  156. static void
  157. init_texture(struct image *img, int number)
  158. {
  159. GLuint textures[2], texture_handle;
  160. glGenTextures(1, &textures);
  161. glActiveTexture(GL_TEXTURE0 + number);
  162. glBindTexture(GL_TEXTURE_2D, textures[0]);
  163. int mode = (img->n == 4) ? GL_RGBA : GL_LUMINANCE;
  164. glTexImage2D(
  165. GL_TEXTURE_2D, 0, mode,
  166. img->width, img->height, 0,
  167. mode, GL_UNSIGNED_BYTE, img->data);
  168. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  169. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  170. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  171. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  172. texture_handle = glGetUniformLocation(program, number ? "depthMap" : "uTexture");
  173. glUniform1i(texture_handle, number);
  174. }
  175. /* Depth map generation:
  176. * The idea is to choose an axis running through the object,
  177. * and measure perpendicular distance away from that axis.
  178. */
  179. /* First, consider perpendicular distance "pixel counting".
  180. *
  181. * In particular, we limit to the special case where the axis is pointing
  182. * completely down. This is ideal to avoid roundoff error which would greatly
  183. * complicate the routine and later stages. TODO: Rotate the image
  184. * ahead-of-time.
  185. *
  186. * Thus, it is only necessary to know the starting point to count from.
  187. * Counting looks for pixels in the strictly perpendicular direction; that is,
  188. * directly to the left or right. At the moment, we assume considerable
  189. * symmetry and count to the right. TODO: Is this correct? */
  190. static int
  191. count_pixels(struct image *img, int start_x, int start_y)
  192. {
  193. int distance = 0;
  194. uint32_t *data32 = (uint32_t *) img->data;
  195. /* Iterate perpendicularly */
  196. for (int x = start_x; x < img->width; ++x) {
  197. uint32_t color = data32[start_y * img->width + x];
  198. uint8_t alpha = color >> 24;
  199. /* If there's nothing here, the edge has been found */
  200. if (alpha == 0) break;
  201. ++distance;
  202. }
  203. return distance;
  204. }
  205. #define MIN(a,b) ((a>b)?(b):(a))
  206. /* Next, we generate the 8-bit depth map.
  207. *
  208. * We first count pixels along the vertical axis for easy access.
  209. *
  210. * We then assign each pixel a depth value based on the depth function and this
  211. * count */
  212. static struct image
  213. generate_depth_map(struct image *src, int start_x, int start_y)
  214. {
  215. /* Allocate room for the image and perpendicular lengths */
  216. struct image dst = {
  217. .data = calloc(src->width * src->height, 4),
  218. .width = src->width,
  219. .height = src->height,
  220. .n = 4,
  221. };
  222. int *lengths = malloc(sizeof(int) * src->height);
  223. /* Count pixels */
  224. for (int y = 0; y < src->height; ++y) {
  225. lengths[y] = count_pixels(src, start_x, y);
  226. }
  227. /* Generate depth map */
  228. uint32_t *data32 = (uint32_t *) dst.data;
  229. for (int y = 0; y < dst.height; ++y) {
  230. for (int x = 0; x < dst.width; ++x) {
  231. float length = lengths[y];
  232. float coord = x - start_x;
  233. float depth_det = length*length - coord*coord;
  234. /* Assume zero depth => nonexistent */
  235. uint8_t udepth = 0;
  236. /* If determinant is negative, no depth. If positive,
  237. * there is something here; take the square root and
  238. * store that. */
  239. if (depth_det >= 0) {
  240. float depth = sqrtf(depth_det);
  241. int d = depth * 4;
  242. data32[(y * dst.width) + x] = MIN(d, 255);
  243. }
  244. }
  245. }
  246. return dst;
  247. }
  248. int main()
  249. {
  250. GLint width, height;
  251. EGLSurface surface;
  252. display = get_display();
  253. /* get an appropriate EGL frame buffer configuration */
  254. eglChooseConfig(display, config_attribute_list, &config, 1, &num_config);
  255. /* create an EGL rendering context */
  256. context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attribute_list);
  257. surface = make_window(display, config, 2048, 1280);
  258. eglQuerySurface(display, surface, EGL_WIDTH, &width);
  259. eglQuerySurface(display, surface, EGL_HEIGHT, &height);
  260. /* connect the context to the surface */
  261. eglMakeCurrent(display, surface, surface, context);
  262. program = get_program(vertex_shader_source, fragment_shader_source);
  263. glBindAttribLocation(program, 0, "aPosition");
  264. glBindAttribLocation(program, 1, "aColor");
  265. link_program(program);
  266. glViewport(0, 0, width, height);
  267. /* Load the image */
  268. struct image img;
  269. img.data = stbi_load("pencil.png", &img.width, &img.height, &img.n, 0);
  270. assert(img.data != NULL);
  271. assert(img.n == 4);
  272. /* XXX: We probably want a better way to input these */
  273. int start_x = img.width >> 1;
  274. int start_y = 0;
  275. struct image depth_map = generate_depth_map(&img, start_x, start_y);
  276. init_texture(&img, 0);
  277. init_texture(&depth_map, 1);
  278. /* Clean up */
  279. stbi_image_free(img.data);
  280. /* Generate the forward mapping grid */
  281. struct forward_grid grid = generate_grid(127);
  282. /* Setup the angle */
  283. int u_angle = glGetUniformLocation(program, "angle");
  284. float angle = 0.0f;
  285. for (;;) {
  286. glClearColor(0.5, 0.0, 0.0, 1.0);
  287. glClear(GL_COLOR_BUFFER_BIT);
  288. glUniform1f(u_angle, angle);
  289. draw_grid(&grid);
  290. eglSwapBuffers(display, surface);
  291. glFlush();
  292. angle += (3.14159f/180.0f);
  293. }
  294. eglDestroySurface(display, surface);
  295. eglTerminate(display);
  296. return 0;
  297. }