rays 0.3.10 → 0.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/rays/image.cpp +10 -0
  3. data/.doc/ext/rays/painter.cpp +49 -1
  4. data/.doc/ext/rays/polygon.cpp +1 -1
  5. data/.doc/ext/rays/shader.cpp +8 -6
  6. data/.github/workflows/release-gem.yml +4 -1
  7. data/.github/workflows/test.yml +4 -4
  8. data/.github/workflows/utils.rb +88 -17
  9. data/ChangeLog.md +24 -0
  10. data/Gemfile.lock +6 -5
  11. data/Rakefile +3 -3
  12. data/VERSION +1 -1
  13. data/ext/rays/extconf.rb +3 -4
  14. data/ext/rays/image.cpp +11 -0
  15. data/ext/rays/painter.cpp +53 -1
  16. data/ext/rays/polygon.cpp +1 -1
  17. data/ext/rays/shader.cpp +8 -6
  18. data/include/rays/coord.h +6 -6
  19. data/include/rays/defs.h +2 -0
  20. data/include/rays/image.h +11 -1
  21. data/include/rays/painter.h +19 -0
  22. data/include/rays/ruby.h +2 -2
  23. data/include/rays/shader.h +5 -3
  24. data/include/rays.h +2 -2
  25. data/lib/rays/extension.rb +8 -2
  26. data/lib/rays/image.rb +2 -1
  27. data/lib/rays/shader.rb +13 -4
  28. data/rays.gemspec +3 -4
  29. data/src/bitmap.h +4 -0
  30. data/src/color_space.cpp +2 -42
  31. data/src/coord.h +10 -0
  32. data/src/font.cpp +1 -1
  33. data/src/image.cpp +85 -11
  34. data/src/ios/bitmap.mm +5 -32
  35. data/src/ios/rays.mm +3 -3
  36. data/src/opengl/bitmap.cpp +41 -0
  37. data/src/opengl/color_space.cpp +51 -0
  38. data/src/{color_space.h → opengl/color_space.h} +2 -2
  39. data/src/{frame_buffer.cpp → opengl/frame_buffer.cpp} +1 -1
  40. data/src/{frame_buffer.h → opengl/frame_buffer.h} +2 -2
  41. data/src/{ios → opengl/ios}/opengl.mm +3 -3
  42. data/src/{opengl.h → opengl/opengl.h} +2 -6
  43. data/src/{osx → opengl/osx}/opengl.mm +3 -3
  44. data/src/opengl/painter.cpp +1020 -0
  45. data/src/{render_buffer.cpp → opengl/render_buffer.cpp} +1 -1
  46. data/src/{render_buffer.h → opengl/render_buffer.h} +2 -2
  47. data/src/{sdl → opengl/sdl}/opengl.cpp +10 -3
  48. data/src/{shader.cpp → opengl/shader.cpp} +69 -53
  49. data/src/{shader.h → opengl/shader.h} +12 -8
  50. data/src/{shader_program.cpp → opengl/shader_program.cpp} +24 -10
  51. data/src/{shader_program.h → opengl/shader_program.h} +4 -3
  52. data/src/{shader_source.cpp → opengl/shader_source.cpp} +2 -4
  53. data/src/{shader_source.h → opengl/shader_source.h} +2 -2
  54. data/src/{texture.cpp → opengl/texture.cpp} +18 -7
  55. data/src/opengl/texture.h +21 -0
  56. data/src/{win32 → opengl/win32}/opengl.cpp +4 -3
  57. data/src/osx/bitmap.mm +6 -34
  58. data/src/osx/rays.mm +3 -3
  59. data/src/painter.cpp +96 -925
  60. data/src/painter.h +223 -11
  61. data/src/polygon.cpp +38 -13
  62. data/src/renderer.h +22 -0
  63. data/src/sdl/bitmap.cpp +5 -33
  64. data/src/sdl/font.cpp +358 -9
  65. data/src/sdl/rays.cpp +8 -3
  66. data/src/texture.h +6 -3
  67. data/src/win32/bitmap.cpp +6 -34
  68. data/src/win32/rays.cpp +3 -3
  69. data/test/test_painter.rb +36 -25
  70. data/test/test_painter_batch.rb +254 -0
  71. metadata +31 -24
  72. /data/src/{opengl.cpp → opengl/opengl.cpp} +0 -0
@@ -0,0 +1,1020 @@
1
+ #include "../painter.h"
2
+
3
+
4
+ #include <math.h>
5
+ #include <assert.h>
6
+ #include <memory>
7
+ #include <vector>
8
+ #include <algorithm>
9
+ #include <functional>
10
+ #include "rays/exception.h"
11
+ #include "../glm.h"
12
+ #include "../coord.h"
13
+ #include "../bitmap.h"
14
+ #include "../image.h"
15
+ #include "../font.h"
16
+ #include "opengl.h"
17
+ #include "texture.h"
18
+ #include "frame_buffer.h"
19
+ #include "shader.h"
20
+ #include "shader_program.h"
21
+
22
+
23
+ namespace Rays
24
+ {
25
+
26
+
27
+ struct Vector4 : Coord4
28
+ {
29
+
30
+ Vector4 (const Coord3& p) {Coord4::operator=(p);}
31
+
32
+ };// Vector4
33
+
34
+
35
+ struct OpenGLState
36
+ {
37
+
38
+ GLint viewport[4];
39
+
40
+ GLclampf color_clear[4];
41
+
42
+ GLboolean depth_test;
43
+ GLint depth_func;
44
+
45
+ GLboolean scissor_test;
46
+ GLint scissor_box[4];
47
+
48
+ GLboolean blend;
49
+ GLint blend_equation_rgb, blend_equation_alpha;
50
+ GLint blend_src_rgb, blend_src_alpha, blend_dst_rgb, blend_dst_alpha;
51
+
52
+ GLint framebuffer_binding;
53
+
54
+ void push ()
55
+ {
56
+ glGetIntegerv(GL_VIEWPORT, viewport);
57
+
58
+ glGetFloatv(GL_COLOR_CLEAR_VALUE, color_clear);
59
+
60
+ glGetBooleanv(GL_DEPTH_TEST, &depth_test);
61
+ glGetIntegerv(GL_DEPTH_FUNC, &depth_func);
62
+
63
+ glGetBooleanv(GL_SCISSOR_TEST, &scissor_test);
64
+ glGetIntegerv(GL_SCISSOR_BOX, scissor_box);
65
+
66
+ glGetBooleanv(GL_BLEND, &blend);
67
+ glGetIntegerv(GL_BLEND_EQUATION_RGB, &blend_equation_rgb);
68
+ glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blend_equation_alpha);
69
+ glGetIntegerv(GL_BLEND_SRC_RGB, &blend_src_rgb);
70
+ glGetIntegerv(GL_BLEND_SRC_ALPHA, &blend_src_alpha);
71
+ glGetIntegerv(GL_BLEND_DST_RGB, &blend_dst_rgb);
72
+ glGetIntegerv(GL_BLEND_DST_ALPHA, &blend_dst_alpha);
73
+
74
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer_binding);
75
+ }
76
+
77
+ void pop ()
78
+ {
79
+ glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
80
+
81
+ glClearColor(
82
+ color_clear[0], color_clear[1], color_clear[2], color_clear[3]);
83
+
84
+ enable(GL_DEPTH_TEST, depth_test);
85
+ glDepthFunc(depth_func);
86
+
87
+ enable(GL_SCISSOR_TEST, scissor_test);
88
+ glScissor(scissor_box[0], scissor_box[1], scissor_box[2], scissor_box[3]);
89
+
90
+ enable(GL_BLEND, blend);
91
+ glBlendEquationSeparate(blend_equation_rgb, blend_equation_alpha);
92
+ glBlendFuncSeparate(
93
+ blend_src_rgb, blend_dst_rgb, blend_src_alpha, blend_dst_alpha);
94
+
95
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_binding);
96
+ }
97
+
98
+ private:
99
+
100
+ void enable(GLenum type, GLboolean value)
101
+ {
102
+ if (value)
103
+ glEnable(type);
104
+ else
105
+ glDisable(type);
106
+ }
107
+
108
+ };// OpenGLState
109
+
110
+
111
+ class DefaultIndices
112
+ {
113
+
114
+ public:
115
+
116
+ void resize (size_t size)
117
+ {
118
+ indices.reserve(size);
119
+ while (indices.size() < size)
120
+ indices.emplace_back(indices.size());
121
+ }
122
+
123
+ void clear ()
124
+ {
125
+ decltype(indices)().swap(indices);
126
+ }
127
+
128
+ const uint* get () const
129
+ {
130
+ return &indices[0];
131
+ }
132
+
133
+ private:
134
+
135
+ std::vector<uint> indices;
136
+
137
+ };// DefaultIndices
138
+
139
+
140
+ struct Batcher
141
+ {
142
+
143
+ int count = 0;
144
+
145
+ Shader shader;
146
+
147
+ Texture texture;
148
+
149
+ std::vector<Vector4> points;
150
+
151
+ std::vector<uint> indices;
152
+
153
+ std::vector<Color> colors;
154
+
155
+ std::vector<Vector4> texcoords;
156
+
157
+ std::vector<Coord3> texcoord_mins;
158
+
159
+ std::vector<Coord3> texcoord_maxes;
160
+
161
+ void clear ()
162
+ {
163
+ count = 0;
164
+ shader = Shader();
165
+ texture = Texture();
166
+ points .clear();
167
+ indices .clear();
168
+ colors .clear();
169
+ texcoords .clear();
170
+ texcoord_mins .clear();
171
+ texcoord_maxes.clear();
172
+ }
173
+
174
+ };// Batcher
175
+
176
+
177
+ struct PainterData : Painter::Data
178
+ {
179
+
180
+ FrameBuffer frame_buffer;
181
+
182
+ OpenGLState opengl_state;
183
+
184
+ DefaultIndices default_indices;
185
+
186
+ std::vector<GLint> locations;
187
+
188
+ std::vector<GLuint> buffers;
189
+
190
+ std::vector<GLuint> triangle_fan_indices_buffer;
191
+
192
+ Batcher batcher;
193
+
194
+ GLuint create_and_bind_buffer (GLenum target, const void* data, GLsizeiptr size)
195
+ {
196
+ GLuint id = 0;
197
+ glGenBuffers(1, &id);
198
+ OpenGL_check_error(__FILE__, __LINE__);
199
+
200
+ buffers.push_back(id);
201
+
202
+ glBindBuffer(target, id);
203
+ OpenGL_check_error(__FILE__, __LINE__);
204
+
205
+ glBufferData(target, size, data, GL_STREAM_DRAW);
206
+ OpenGL_check_error(__FILE__, __LINE__);
207
+
208
+ return id;
209
+ }
210
+
211
+ void cleanup ()
212
+ {
213
+ for (auto loc : locations)
214
+ {
215
+ glDisableVertexAttribArray(loc);
216
+ OpenGL_check_error(__FILE__, __LINE__);
217
+ }
218
+
219
+ if (!buffers.empty())
220
+ {
221
+ glDeleteBuffers((GLsizei) buffers.size(), &buffers[0]);
222
+ OpenGL_check_error(__FILE__, __LINE__);
223
+ }
224
+
225
+ locations.clear();
226
+ buffers.clear();
227
+ }
228
+
229
+ };// PainterData
230
+
231
+
232
+ static PainterData*
233
+ get_data (Painter* painter)
234
+ {
235
+ return (PainterData*) painter->self.get();
236
+ }
237
+
238
+ void
239
+ Painter_update_clip (Painter* painter)
240
+ {
241
+ PainterData* self = get_data(painter);
242
+ const Bounds& clip = self->state.clip;
243
+ if (clip)
244
+ {
245
+ coord y = self->frame_buffer ? clip.y : self->viewport.h - (clip.y + clip.h);
246
+ glEnable(GL_SCISSOR_TEST);
247
+ glScissor(
248
+ self->pixel_density * clip.x,
249
+ self->pixel_density * y,
250
+ self->pixel_density * clip.width,
251
+ self->pixel_density * clip.height);
252
+ }
253
+ else
254
+ glDisable(GL_SCISSOR_TEST);
255
+
256
+ OpenGL_check_error(__FILE__, __LINE__);
257
+ }
258
+
259
+ static void
260
+ apply_uniform (
261
+ const ShaderProgram& program, const char* name,
262
+ std::function<void(GLint)> apply_fun)
263
+ {
264
+ GLint loc = glGetUniformLocation(program.id(), name);
265
+ if (loc < 0) return;
266
+
267
+ apply_fun(loc);
268
+ OpenGL_check_error(__FILE__, __LINE__);
269
+ }
270
+
271
+ static void
272
+ apply_uniforms (
273
+ const ShaderProgram& program, const ShaderBuiltinVariableNames& names,
274
+ const Matrix& position_matrix, const Matrix& texcoord_matrix,
275
+ const Texture* texture)
276
+ {
277
+ for (const auto& name : names.uniform_position_matrix_names)
278
+ {
279
+ apply_uniform(program, name, [&](GLint loc) {
280
+ glUniformMatrix4fv(loc, 1, GL_FALSE, position_matrix.array);
281
+ });
282
+ }
283
+ for (const auto& name : names.uniform_texcoord_matrix_names)
284
+ {
285
+ apply_uniform(program, name, [&](GLint loc) {
286
+ glUniformMatrix4fv(loc, 1, GL_FALSE, texcoord_matrix.array);
287
+ });
288
+ }
289
+
290
+ if (texture && *texture)
291
+ {
292
+ Point pixel_size(
293
+ 1 / texture->reserved_width(),
294
+ 1 / texture->reserved_height());
295
+ for (const auto& name : names.uniform_texcoord_pixel_names)
296
+ {
297
+ apply_uniform(
298
+ program, name, [&](GLint loc) {glUniform3fv(loc, 1, pixel_size.array);});
299
+ }
300
+ for (const auto& name : names.uniform_texture_names)
301
+ {
302
+ apply_uniform(program, name, [&](GLint loc) {
303
+ glActiveTexture(GL_TEXTURE0);
304
+ OpenGL_check_error(__FILE__, __LINE__);
305
+
306
+ glBindTexture(GL_TEXTURE_2D, Texture_get_id(*texture));
307
+ OpenGL_check_error(__FILE__, __LINE__);
308
+
309
+ glUniform1i(loc, 0);
310
+ });
311
+ }
312
+ }
313
+ }
314
+
315
+ template <typename COORD>
316
+ static GLenum get_gl_type ();
317
+
318
+ template <>
319
+ GLenum
320
+ get_gl_type<float> ()
321
+ {
322
+ return GL_FLOAT;
323
+ }
324
+
325
+ static void
326
+ apply_attribute (
327
+ const ShaderProgram& program, const char* name,
328
+ std::function<void(GLint)> apply_fun)
329
+ {
330
+ GLint loc = glGetAttribLocation(program.id(), name);
331
+ if (loc < 0) return;
332
+
333
+ apply_fun(loc);
334
+ OpenGL_check_error(__FILE__, __LINE__);
335
+ }
336
+
337
+ template <typename CoordN>
338
+ static void
339
+ apply_attribute (
340
+ PainterData* self,
341
+ const ShaderProgram& program, const auto& names,
342
+ const CoordN* values, size_t nvalues)
343
+ {
344
+ GLuint buffer = 0;
345
+ for (const auto& name : names)
346
+ {
347
+ #ifndef IOS
348
+ if (buffer == 0)
349
+ {
350
+ buffer = self->create_and_bind_buffer(
351
+ GL_ARRAY_BUFFER, values, sizeof(CoordN) * nvalues);
352
+ values = 0;
353
+ }
354
+ #endif
355
+
356
+ apply_attribute(program, name, [&](GLint loc)
357
+ {
358
+ glEnableVertexAttribArray(loc);
359
+ OpenGL_check_error(
360
+ __FILE__, __LINE__, "loc: %d %s\n", loc, name.c_str());
361
+
362
+ glVertexAttribPointer(
363
+ loc, CoordN::SIZE, get_gl_type<coord>(), GL_FALSE, 0, values);
364
+
365
+ self->locations.push_back(loc);
366
+ });
367
+ }
368
+
369
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
370
+ OpenGL_check_error(__FILE__, __LINE__);
371
+ }
372
+
373
+ template <typename CoordN>
374
+ static void
375
+ apply_attributes (
376
+ PainterData* self,
377
+ const ShaderProgram& program, const ShaderBuiltinVariableNames& names,
378
+ const CoordN* points, size_t npoints,
379
+ const Color* color, const Color* colors,
380
+ const CoordN* texcoords,
381
+ const Coord3* texcoord_min, const Coord3* texcoord_max,
382
+ const Coord3* texcoord_mins, const Coord3* texcoord_maxes)
383
+ {
384
+ assert(npoints > 0);
385
+ assert(!!color != !!colors);
386
+
387
+ apply_attribute(
388
+ self, program, names.attribute_position_names,
389
+ points, npoints);
390
+
391
+ if (colors)
392
+ {
393
+ apply_attribute(
394
+ self, program, names.attribute_color_names,
395
+ colors, npoints);
396
+ }
397
+ else if (color)
398
+ {
399
+ #if defined(GL_VERSION_2_1) && !defined(GL_VERSION_3_0)
400
+ // to fix that GL 2.1 with glVertexAttrib4fv() draws nothing
401
+ // with specific glsl 'attribute' name.
402
+ std::vector<Color> colors_(npoints, *color);
403
+ apply_attribute(
404
+ self, program, names.attribute_color_names,
405
+ (const Coord4*) &colors_[0], npoints);
406
+ #else
407
+ for (const auto& name : names.attribute_color_names)
408
+ {
409
+ apply_attribute(
410
+ program, name, [&](GLint loc) {glVertexAttrib4fv(loc, color->array);});
411
+ }
412
+ #endif
413
+ }
414
+
415
+ apply_attribute(
416
+ self, program, names.attribute_texcoord_names,
417
+ texcoords ? texcoords : points, npoints);
418
+
419
+ if (texcoord_mins)
420
+ {
421
+ apply_attribute(
422
+ self, program, names.attribute_texcoord_min_names,
423
+ texcoord_mins, npoints);
424
+ }
425
+ else if (texcoord_min)
426
+ {
427
+ for (const auto& name : names.attribute_texcoord_min_names)
428
+ {
429
+ apply_attribute(
430
+ program, name, [&](GLint loc) {glVertexAttrib3fv(loc, texcoord_min->array);});
431
+ }
432
+ }
433
+
434
+ if (texcoord_maxes)
435
+ {
436
+ apply_attribute(
437
+ self, program, names.attribute_texcoord_max_names,
438
+ texcoord_maxes, npoints);
439
+ }
440
+ else if (texcoord_max)
441
+ {
442
+ for (const auto& name : names.attribute_texcoord_max_names)
443
+ {
444
+ apply_attribute(
445
+ program, name, [&](GLint loc) {glVertexAttrib3fv(loc, texcoord_max->array);});
446
+ }
447
+ }
448
+ }
449
+
450
+ static void
451
+ draw_indices (
452
+ PainterData* self, PrimitiveMode mode,
453
+ const uint* indices, size_t nindices, size_t npoints)
454
+ {
455
+ if (!indices || nindices <= 0)
456
+ {
457
+ self->default_indices.resize(npoints);
458
+ indices = self->default_indices.get();
459
+ nindices = npoints;
460
+ }
461
+
462
+ #ifdef IOS
463
+ glDrawElements((GLenum) mode, (GLsizei) nindices, GL_UNSIGNED_INT, indices);
464
+ OpenGL_check_error(__FILE__, __LINE__);
465
+ #else
466
+ self->create_and_bind_buffer(
467
+ GL_ELEMENT_ARRAY_BUFFER, indices, sizeof(uint) * nindices);
468
+
469
+ glDrawElements((GLenum) mode, (GLsizei) nindices, GL_UNSIGNED_INT, 0);
470
+ OpenGL_check_error(__FILE__, __LINE__);
471
+
472
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
473
+ OpenGL_check_error(__FILE__, __LINE__);
474
+ #endif
475
+ }
476
+
477
+ static void
478
+ setup_texcoord_variables (
479
+ Matrix* matrix, Point* min, Point* max,
480
+ const State& state, const TextureInfo& texinfo)
481
+ {
482
+ if (!texinfo.texture) return;
483
+
484
+ coord tw = texinfo.texture.reserved_width();
485
+ coord th = texinfo.texture.reserved_height();
486
+
487
+ bool normal = state.texcoord_mode == TEXCOORD_NORMAL;
488
+ matrix->scale(
489
+ (normal ? texinfo.texture.width() : 1.0) / tw,
490
+ (normal ? texinfo.texture.height() : 1.0) / th);
491
+
492
+ min->reset(texinfo.min.x / tw, texinfo.min.y / th);
493
+ max->reset(texinfo.max.x / tw, texinfo.max.y / th);
494
+ }
495
+
496
+ static void
497
+ draw (
498
+ PainterData* self, PrimitiveMode mode,
499
+ const Color* color,
500
+ const Coord3* points, size_t npoints,
501
+ const uint* indices, size_t nindices,
502
+ const Color* colors,
503
+ const Coord3* texcoords, const TextureInfo* texinfo,
504
+ const Shader& shader, const Matrix& position_matrix)
505
+ {
506
+ const ShaderProgram* program = Shader_get_program(shader);
507
+ if (!program || !*program) return;
508
+
509
+ ShaderProgram_activate(*program);
510
+
511
+ Matrix texcoord_matrix(1);
512
+ Point texcoord_min(0, 0), texcoord_max(1, 1);
513
+ if (texinfo)
514
+ {
515
+ setup_texcoord_variables(
516
+ &texcoord_matrix, &texcoord_min, &texcoord_max, self->state, *texinfo);
517
+ }
518
+
519
+ const auto& names = Shader_get_builtin_variable_names(shader);
520
+ apply_uniforms(
521
+ *program, names, position_matrix, texcoord_matrix,
522
+ texinfo ? &texinfo->texture : NULL);
523
+ apply_attributes(
524
+ self, *program, names, points, npoints, color, colors,
525
+ texcoords, &texcoord_min, &texcoord_max, NULL, NULL);
526
+ draw_indices(self, mode, indices, nindices, npoints);
527
+ self->cleanup();
528
+
529
+ ShaderProgram_deactivate();
530
+ }
531
+
532
+ static void
533
+ draw_batch (PainterData* self)
534
+ {
535
+ Batcher& batcher = self->batcher;
536
+ if (batcher.points.empty()) return;
537
+
538
+ const ShaderProgram* program = Shader_get_program(batcher.shader);
539
+ if (!program || !*program)
540
+ return batcher.clear();
541
+
542
+ ShaderProgram_activate(*program);
543
+
544
+ const auto& names = Shader_get_builtin_variable_names(batcher.shader);
545
+ Matrix identity(1);
546
+ apply_uniforms(
547
+ *program, names, identity, identity, batcher.texture ? &batcher.texture : NULL);
548
+ apply_attributes(
549
+ self, *program, names, &batcher.points[0], batcher.points.size(),
550
+ NULL, &batcher.colors[0],
551
+ &batcher.texcoords[0],
552
+ NULL, NULL, &batcher.texcoord_mins[0], &batcher.texcoord_maxes[0]);
553
+ draw_indices(
554
+ self, MODE_TRIANGLES,
555
+ &batcher.indices[0], batcher.indices.size(), batcher.points.size());
556
+ self->cleanup();
557
+
558
+ ShaderProgram_deactivate();
559
+ batcher.clear();
560
+ }
561
+
562
+ static inline Vector4
563
+ apply_matrix (const Matrix& matrix, const Coord3& point)
564
+ {
565
+ return to_rays<Vector4>(to_glm(matrix) * Vec4(point.x, point.y, point.z, 1));
566
+ }
567
+
568
+ static void
569
+ batch (
570
+ Painter* painter, PrimitiveMode mode, const Color* color,
571
+ const Coord3* points, size_t npoints,
572
+ const uint* indices, size_t nindices,
573
+ const Color* colors,
574
+ const Coord3* texcoords, const TextureInfo* texinfo,
575
+ const Shader& shader)
576
+ {
577
+ PainterData* self = get_data(painter);
578
+ Batcher& batcher = self->batcher;
579
+
580
+ Texture texture = texinfo ? texinfo->texture : Texture();
581
+ if (
582
+ batcher.points.empty() ||
583
+ batcher.shader != shader ||
584
+ batcher.texture != texture)
585
+ {
586
+ Painter_flush(painter);
587
+ batcher.shader = shader;
588
+ batcher.texture = texture;
589
+ }
590
+
591
+ if (++batcher.count <= 5)
592
+ {
593
+ return draw(
594
+ self, mode, color, points, npoints, indices, nindices, colors,
595
+ texcoords, texinfo, shader, self->position_matrix);
596
+ }
597
+
598
+ size_t points0 = batcher.points.size();
599
+
600
+ for (size_t i = 0; i < npoints; ++i)
601
+ batcher.points.push_back(apply_matrix(self->position_matrix, points[i]));
602
+
603
+ if (indices && nindices > 0)
604
+ {
605
+ for (size_t i = 0; i < nindices; ++i)
606
+ batcher.indices.push_back((uint) (points0 + indices[i]));
607
+ }
608
+ else
609
+ {
610
+ for (size_t i = 0; i < npoints; ++i)
611
+ batcher.indices.push_back((uint) (points0 + i));
612
+ }
613
+
614
+ if (colors)
615
+ batcher.colors.insert(batcher.colors.end(), colors, colors + npoints);
616
+ else if (color)
617
+ batcher.colors.insert(batcher.colors.end(), npoints, *color);
618
+
619
+ if (texture)
620
+ {
621
+ Matrix matrix(1);
622
+ Point min(0, 0), max(1, 1);
623
+ setup_texcoord_variables(&matrix, &min, &max, self->state, *texinfo);
624
+
625
+ const Coord3* src = texcoords ? texcoords : points;
626
+ for (size_t i = 0; i < npoints; ++i)
627
+ batcher.texcoords.push_back(apply_matrix(matrix, src[i]));
628
+
629
+ batcher.texcoord_mins .insert(batcher.texcoord_mins.end(), npoints, min);
630
+ batcher.texcoord_maxes.insert(batcher.texcoord_maxes.end(), npoints, max);
631
+ }
632
+ else
633
+ {
634
+ const Coord3* src = texcoords ? texcoords : points;
635
+ batcher.texcoords .insert(batcher.texcoords.end(), src, src + npoints);
636
+ batcher.texcoord_mins .insert(batcher.texcoord_mins.end(), npoints, Point(0, 0));
637
+ batcher.texcoord_maxes.insert(batcher.texcoord_maxes.end(), npoints, Point(1, 1));
638
+ }
639
+ }
640
+
641
+ void
642
+ Painter_flush (Painter* painter)
643
+ {
644
+ draw_batch(get_data(painter));
645
+ }
646
+
647
+ static const TextureInfo*
648
+ setup_texinfo (
649
+ PainterData* self, const TextureInfo* texinfo,
650
+ std::unique_ptr<TextureInfo>* ptr)
651
+ {
652
+ assert(ptr);
653
+
654
+ if (texinfo) return texinfo;
655
+
656
+ const Texture* tex =
657
+ self->state.texture ? &Image_get_texture(self->state.texture) : NULL;
658
+ if (!tex) return NULL;
659
+
660
+ ptr->reset(new TextureInfo(*tex, 0, 0, tex->width(), tex->height()));
661
+ return ptr->get();
662
+ }
663
+
664
+ static const Shader*
665
+ setup_shader (PainterData* self, const Shader* shader, bool for_texture)
666
+ {
667
+ if (self->state.shader) return &self->state.shader;
668
+ if (shader) return shader;
669
+ return for_texture
670
+ ? &Shader_get_default_shader_for_texture(self->state.texcoord_wrap)
671
+ : &Shader_get_default_shader_for_shape();
672
+ }
673
+
674
+ static bool
675
+ setup_triangle_fan_indices (auto* indices, size_t npoints)
676
+ {
677
+ if (npoints < 3) return false;
678
+
679
+ indices->reserve((npoints - 2) * 3);
680
+ for (size_t i = 1; i + 1 < npoints; ++i)
681
+ {
682
+ indices->push_back(0);
683
+ indices->push_back((uint) i);
684
+ indices->push_back((uint) (i + 1));
685
+ }
686
+ return true;
687
+ }
688
+
689
+ void
690
+ Painter_draw (
691
+ Painter* painter, PrimitiveMode mode, const Color* color,
692
+ const Coord3* points, size_t npoints,
693
+ const uint* indices, size_t nindices,
694
+ const Color* colors,
695
+ const Coord3* texcoords,
696
+ const TextureInfo* texinfo,
697
+ const Shader* shader)
698
+ {
699
+ if (!points)
700
+ argument_error(__FILE__, __LINE__);
701
+ if (npoints <= 0)
702
+ argument_error(__FILE__, __LINE__);
703
+
704
+ PainterData* self = get_data(painter);
705
+
706
+ if (!self->is_painting())
707
+ invalid_state_error(__FILE__, __LINE__, "'painting' should be true.");
708
+
709
+ std::unique_ptr<TextureInfo> ptexinfo;
710
+ texinfo = setup_texinfo(self, texinfo, &ptexinfo);
711
+ shader = setup_shader(self, shader, texinfo);
712
+
713
+ bool batchable =
714
+ painter->has_flag(Painter::FLAG_BATCHING) &&
715
+ !Painter::debug() &&
716
+ !self->state.shader;
717
+ if (batchable && mode == MODE_TRIANGLES)
718
+ {
719
+ batch(
720
+ painter, mode, color, points, npoints, indices, nindices,
721
+ colors, texcoords, texinfo, *shader);
722
+ }
723
+ else if (batchable && mode == MODE_TRIANGLE_FAN && (!indices || nindices == 0))
724
+ {
725
+ auto& fan_indices = self->triangle_fan_indices_buffer;
726
+ fan_indices.clear();
727
+ if (!setup_triangle_fan_indices(&fan_indices, npoints))
728
+ return;
729
+ batch(
730
+ painter, MODE_TRIANGLES, color, points, npoints, &fan_indices[0], fan_indices.size(),
731
+ colors, texcoords, texinfo, *shader);
732
+ }
733
+ else
734
+ {
735
+ Painter_flush(painter);
736
+ draw(
737
+ self, mode, color, points, npoints, indices, nindices,
738
+ colors, texcoords, texinfo, *shader, self->position_matrix);
739
+ }
740
+ }
741
+
742
+ static inline void
743
+ debug_draw_text_line (
744
+ Painter* painter, const Font& font,
745
+ coord x, coord y, coord str_width, coord str_height)
746
+ {
747
+ #if 0
748
+ painter->self->text_image.save("/tmp/font.png");
749
+
750
+ painter->push_state();
751
+ {
752
+ coord asc, desc, lead;
753
+ font.get_height(&asc, &desc, &lead);
754
+ //printf("%f %f %f %f \n", str_height, asc, desc, lead);
755
+
756
+ painter->set_stroke(0.5, 0.5, 1);
757
+ painter->no_fill();
758
+ painter->rect(x - 1, y - 1, str_width + 2, str_height + 2);
759
+
760
+ coord yy = y;
761
+ painter->set_stroke(1, 0.5, 0.5, 0.4);
762
+ painter->rect(x, yy, str_width, asc);//str_height);
763
+
764
+ yy += asc;
765
+ painter->set_stroke(1, 1, 0.5, 0.4);
766
+ painter->rect(x, yy, str_width, desc);
767
+
768
+ yy += desc;
769
+ painter->set_stroke(1, 0.5, 1, 0.4);
770
+ painter->rect(x, yy, str_width, lead);
771
+ }
772
+ painter->pop_state();
773
+ #endif
774
+ }
775
+
776
+ void
777
+ Painter_draw_text_line (
778
+ Painter* painter, const Font& font,
779
+ const char* line, coord x, coord y,
780
+ coord width, coord height)
781
+ {
782
+ assert(painter && font && line && *line != '\0');
783
+
784
+ Painter::Data* self = painter->self.get();
785
+
786
+ float density = self->pixel_density;
787
+ const RawFont& rawfont = Font_get_raw(font, density);
788
+ coord str_w = rawfont.get_width(line);
789
+ coord str_h = rawfont.get_height();
790
+ int tex_w = ceil(str_w);
791
+ int tex_h = ceil(str_h);
792
+ const Texture& texture = Image_get_texture(self->text_image);
793
+ if (
794
+ texture.width() < tex_w ||
795
+ texture.height() < tex_h ||
796
+ self->text_image.pixel_density() != density)
797
+ {
798
+ int bmp_w = std::max(texture.width(), tex_w);
799
+ int bmp_h = std::max(texture.height(), tex_h);
800
+ self->text_image = Image(Bitmap(bmp_w, bmp_h), density);
801
+ }
802
+
803
+ if (!self->text_image)
804
+ invalid_state_error(__FILE__, __LINE__);
805
+
806
+ assert(self->text_image.pixel_density() == density);
807
+
808
+ Bitmap_draw_string(
809
+ &self->text_image.bitmap(), rawfont, line, 0, 0, font.smooth());
810
+
811
+ str_w /= density;
812
+ str_h /= density;
813
+ if (width == 0) width = str_w;
814
+ if (height == 0) height = str_h;
815
+
816
+ Painter_draw_image(
817
+ painter, self->text_image,
818
+ 0, 0, str_w, str_h,
819
+ x, y, str_w, str_h,
820
+ &Shader_get_shader_for_text());
821
+
822
+ debug_draw_text_line(painter, font, x, y, str_w / density, str_h / density);
823
+ }
824
+
825
+
826
+ Painter::Painter ()
827
+ : self(new PainterData())
828
+ {
829
+ }
830
+
831
+ void
832
+ Painter::bind (const Image& image)
833
+ {
834
+ if (!image)
835
+ argument_error(__FILE__, __LINE__, "invalid image.");
836
+
837
+ if (self->is_painting())
838
+ invalid_state_error(__FILE__, __LINE__, "painting flag should be false.");
839
+
840
+ FrameBuffer fb(Image_get_texture(image));
841
+ if (!fb)
842
+ rays_error(__FILE__, __LINE__, "invalid frame buffer.");
843
+
844
+ unbind();
845
+
846
+ get_data(this)->frame_buffer = fb;
847
+ canvas(0, 0, image.width(), image.height(), image.pixel_density());
848
+ }
849
+
850
+ void
851
+ Painter::unbind ()
852
+ {
853
+ if (self->is_painting())
854
+ invalid_state_error(__FILE__, __LINE__, "painting flag should be true.");
855
+
856
+ get_data(this)->frame_buffer = FrameBuffer();
857
+ }
858
+
859
+ void
860
+ Painter::begin ()
861
+ {
862
+ PainterData* self = get_data(this);
863
+
864
+ if (self->is_painting())
865
+ invalid_state_error(__FILE__, __LINE__, "painting flag should be false.");
866
+
867
+ self->opengl_state.push();
868
+
869
+ //glEnable(GL_CULL_FACE);
870
+
871
+ glEnable(GL_DEPTH_TEST);
872
+ glDepthFunc(GL_LEQUAL);
873
+ OpenGL_check_error(__FILE__, __LINE__);
874
+
875
+ glEnable(GL_BLEND);
876
+ set_blend_mode(self->state.blend_mode);
877
+
878
+ FrameBuffer& fb = self->frame_buffer;
879
+ if (fb)
880
+ {
881
+ FrameBuffer_bind(fb.id());
882
+
883
+ Texture& tex = fb.texture();
884
+ if (tex) tex.set_modified();
885
+ }
886
+
887
+ const Bounds& vp = self->viewport;
888
+ float density = self->pixel_density;
889
+ glViewport(
890
+ (int) (vp.x * density), (int) (vp.y * density),
891
+ (int) (vp.width * density), (int) (vp.height * density));
892
+ OpenGL_check_error(__FILE__, __LINE__);
893
+
894
+ coord x1 = vp.x, x2 = vp.x + vp.width;
895
+ coord y1 = vp.y, y2 = vp.y + vp.height;
896
+ coord z1 = vp.z, z2 = vp.z + vp.depth;
897
+ if (z1 == 0 && z2 == 0) {z1 = -1000; z2 = 1000;}
898
+ if (!fb) std::swap(y1, y2);
899
+
900
+ self->position_matrix.reset(1);
901
+ self->position_matrix *= to_rays(glm::ortho(x1, x2, y1, y2));
902
+
903
+ // map z to 0.0-1.0
904
+ self->position_matrix.scale(1, 1, 1.0 / (z2 - z1));
905
+ self->position_matrix.translate(0, 0, -z2);
906
+
907
+ //self->position_matrix.translate(0.375f, 0.375f);
908
+
909
+ Painter_update_clip(this);
910
+
911
+ Xot::add_flag(&self->flags, Painter::Data::PAINTING);
912
+
913
+ glClear(GL_DEPTH_BUFFER_BIT);
914
+ }
915
+
916
+ void
917
+ Painter::end ()
918
+ {
919
+ PainterData* self = get_data(this);
920
+
921
+ if (!self->is_painting())
922
+ invalid_state_error(__FILE__, __LINE__, "painting flag should be true.");
923
+
924
+ if (!self->state_stack.empty())
925
+ invalid_state_error(__FILE__, __LINE__, "state stack is not empty.");
926
+
927
+ if (!self->position_matrix_stack.empty())
928
+ invalid_state_error(__FILE__, __LINE__, "position matrix stack is not empty.");
929
+
930
+ Painter_flush(this);
931
+
932
+ Xot::remove_flag(&self->flags, Painter::Data::PAINTING);
933
+ self->opengl_state.pop();
934
+ self->default_indices.clear();
935
+
936
+ glFinish();
937
+
938
+ if (self->frame_buffer)
939
+ FrameBuffer_unbind();
940
+ }
941
+
942
+ void
943
+ Painter::clear ()
944
+ {
945
+ if (!self->is_painting())
946
+ invalid_state_error(__FILE__, __LINE__, "painting flag should be true.");
947
+
948
+ Painter_flush(this);
949
+
950
+ const Color& c = self->state.background;
951
+ glClearColor(c.red, c.green, c.blue, c.alpha);
952
+ glClear(GL_COLOR_BUFFER_BIT);
953
+ OpenGL_check_error(__FILE__, __LINE__);
954
+ }
955
+
956
+ void
957
+ Painter::set_blend_mode (BlendMode mode)
958
+ {
959
+ if (self->state.blend_mode != mode)
960
+ Painter_flush(this);
961
+
962
+ self->state.blend_mode = mode;
963
+ switch (mode)
964
+ {
965
+ case BLEND_NORMAL:
966
+ glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
967
+ glBlendFuncSeparate(
968
+ GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
969
+ break;
970
+
971
+ case BLEND_ADD:
972
+ glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
973
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE);
974
+ break;
975
+
976
+ case BLEND_SUBTRACT:
977
+ glBlendEquationSeparate(GL_FUNC_REVERSE_SUBTRACT, GL_FUNC_ADD);
978
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE);
979
+ break;
980
+
981
+ case BLEND_LIGHTEST:
982
+ glBlendEquationSeparate(GL_MAX, GL_FUNC_ADD);
983
+ glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE);
984
+ break;
985
+
986
+ case BLEND_DARKEST:
987
+ glBlendEquationSeparate(GL_MIN, GL_FUNC_ADD);
988
+ glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE);
989
+ break;
990
+
991
+ case BLEND_EXCLUSION:
992
+ glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
993
+ glBlendFuncSeparate(
994
+ GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_ONE, GL_ONE);
995
+ break;
996
+
997
+ case BLEND_MULTIPLY:
998
+ glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
999
+ glBlendFuncSeparate(GL_ZERO, GL_SRC_COLOR, GL_ONE, GL_ONE);
1000
+ break;
1001
+
1002
+ case BLEND_SCREEN:
1003
+ glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
1004
+ glBlendFuncSeparate(GL_ONE_MINUS_DST_COLOR, GL_ONE, GL_ONE, GL_ONE);
1005
+ break;
1006
+
1007
+ case BLEND_REPLACE:
1008
+ glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
1009
+ glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
1010
+ break;
1011
+
1012
+ default:
1013
+ argument_error(__FILE__, __LINE__, "unknown blend mode");
1014
+ break;
1015
+ }
1016
+ OpenGL_check_error(__FILE__, __LINE__);
1017
+ }
1018
+
1019
+
1020
+ }// Rays