23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
25 * POSSIBILITY OF SUCH DAMAGE. |
25 * POSSIBILITY OF SUCH DAMAGE. |
26 */ |
26 */ |
27 |
27 |
|
28 #include "ascension/ui/text.h" |
|
29 |
28 #include "ascension/error.h" |
30 #include "ascension/error.h" |
29 #include "ascension/context.h" |
31 #include "ascension/context.h" |
30 #include "ascension/ui/text.h" |
32 #include "ascension/shader.h" |
|
33 #include "ascension/constants.h" |
31 |
34 |
32 #include <assert.h> |
35 #include <assert.h> |
33 #include <cx/printf.h> |
36 #include <cx/printf.h> |
34 |
37 |
35 #include <GL/glew.h> |
38 typedef struct asc_text_shader_s { |
|
39 AscShaderProgram program; |
|
40 asc_uniform_loc tex; |
|
41 } AscTextShader; |
|
42 |
|
43 AscShaderProgram *asc_text_shader_create(cx_attr_unused int unused) { |
|
44 AscShaderCodes codes; |
|
45 // TODO: with more advanced feature we want to create specific text shaders |
|
46 if (asc_shader_load_code_files((AscShaderCodeInfo){ |
|
47 .files.vtx = "sprite_vtx.glsl", |
|
48 .files.frag = "sprite_frag.glsl", |
|
49 .defines.frag = "#define USE_RECT", |
|
50 }, &codes)) { |
|
51 asc_error("Loading text shader failed."); |
|
52 return NULL; |
|
53 } |
|
54 AscShaderProgram *shader = asc_shader_create(codes, sizeof(*shader)); |
|
55 if (asc_shader_invalid(shader)) { |
|
56 asc_shader_free_codes(codes); |
|
57 return shader; |
|
58 } |
|
59 asc_ptr_cast(AscTextShader, text_shader, shader); |
|
60 text_shader->tex = asc_shader_get_uniform_loc(shader, "tex"); |
|
61 asc_shader_free_codes(codes); |
|
62 |
|
63 asc_error_catch_all_gl(); |
|
64 |
|
65 return shader; |
|
66 } |
36 |
67 |
37 static void asc_text_update(AscSceneNode *node) { |
68 static void asc_text_update(AscSceneNode *node) { |
38 AscSprite *sprite = (AscSprite*) node; |
69 asc_ptr_cast(AscText, text, node); |
39 AscText *text = (AscText*) node; |
|
40 |
70 |
41 // Render text onto a surface |
71 // Render text onto a surface |
42 TTF_Font *font = asc_font_load(text->font); |
72 TTF_Font *font = asc_font_load(text->font); |
43 if (font == NULL) return; |
73 if (font == NULL) return; |
44 static int alignments[] = { |
74 static int alignments[] = { |
45 TTF_WRAPPED_ALIGN_LEFT, |
75 TTF_WRAPPED_ALIGN_LEFT, |
46 TTF_WRAPPED_ALIGN_CENTER, |
76 TTF_WRAPPED_ALIGN_CENTER, |
47 TTF_WRAPPED_ALIGN_RIGHT |
77 TTF_WRAPPED_ALIGN_RIGHT |
48 }; |
78 }; |
49 TTF_SetFontWrappedAlign( |
79 TTF_SetFontWrappedAlign( |
50 font, alignments[text->base.data.flags & ASC_TEXT_ALIGNMENT_MASK]); |
80 font, alignments[text->base.flags & ASC_TEXT_ALIGNMENT_MASK]); |
51 SDL_Surface *surface = TTF_RenderUTF8_Blended_Wrapped( |
81 SDL_Surface *surface = TTF_RenderUTF8_Blended_Wrapped( |
52 font, text->text.ptr, asc_col_sdl(text->color), text->max_width |
82 font, text->text.ptr, asc_col_sdl(text->color), text->max_width |
53 ); |
83 ); |
54 if (surface == NULL) { |
84 if (surface == NULL) { |
55 asc_error("Rendering TTF surface failed: %s", SDL_GetError()); |
85 asc_error("Rendering TTF surface failed: %s", SDL_GetError()); |
56 return; |
86 return; |
57 } |
87 } |
58 if (asc_test_flag(text->base.data.flags, ASC_TEXT_CENTERED_FLAG)) { |
88 if (asc_test_flag(text->base.flags, ASC_TEXT_CENTERED_FLAG)) { |
59 unsigned short newoffx = surface->w / 2; |
89 unsigned short newoffx = surface->w / 2; |
60 asc_transform_translate2f(node->transform, ASC_VEC2F(text->offx - newoffx, 0)); |
90 asc_transform_translate2f(node->transform, ASC_VEC2F(text->offx - newoffx, 0)); |
61 text->offx = newoffx; |
91 text->offx = newoffx; |
62 } |
92 } |
63 |
93 |
64 // Transfer Image Data |
94 // Transfer Image Data |
65 asc_texture_from_surface(sprite->texture, surface); |
95 asc_texture_from_surface(text->texture, surface); |
66 |
96 |
67 // If dimensions changed, update the mesh |
97 // If dimensions changed, update the mesh |
68 if (text->dimension.x != (unsigned)surface->w || text->dimension.y != (unsigned)surface->h) { |
98 if (text->dimension.x != (unsigned)surface->w || text->dimension.y != (unsigned)surface->h) { |
69 text->dimension.x = surface->w; |
99 text->dimension.x = surface->w; |
70 text->dimension.y = surface->h; |
100 text->dimension.y = surface->h; |
71 const asc_vec2f uv_scale = asc_texture_calculate_uv_scale(sprite->texture, text->dimension, ASC_VEC2F_1); |
101 const asc_vec2f uv_scale = asc_texture_calculate_uv_scale(text->texture, text->dimension, ASC_VEC2F_1); |
72 asc_mesh_plane_2d(&text->base.mesh, |
102 asc_mesh_plane_2d(&text->mesh, |
73 .size = ASC_VEC2F(surface->w, surface->h), |
103 .size = ASC_VEC2F(surface->w, surface->h), |
74 .uv_scale = uv_scale |
104 .uv_scale = uv_scale |
75 ); |
105 ); |
76 } |
106 } |
77 |
107 |
80 |
110 |
81 // Schedule for transform update |
111 // Schedule for transform update |
82 asc_node_update_transform(node); |
112 asc_node_update_transform(node); |
83 } |
113 } |
84 |
114 |
|
115 static void asc_text_draw(const AscCamera *camera, const AscSceneNode *node) { |
|
116 asc_cptr_cast(AscText, text, node); |
|
117 |
|
118 // Activate shader |
|
119 // TODO: scene should know which shader we are going to activate s.t. it can pre-sort nodes |
|
120 const AscShaderProgram *shader = asc_shader_lookup_or_create( |
|
121 ASC_SHADER_TEXT, asc_text_shader_create, 0); |
|
122 if (asc_shader_use(shader, camera)) return; |
|
123 asc_cptr_cast(AscTextShader, text_shader, shader); |
|
124 |
|
125 // Upload model matrix |
|
126 asc_shader_upload_model_matrix(shader, node); |
|
127 |
|
128 // Bind texture |
|
129 asc_texture_bind(text->texture, text_shader->tex, 0); |
|
130 |
|
131 // Draw mesh |
|
132 asc_mesh_draw_triangle_strip(&text->mesh); |
|
133 } |
|
134 |
85 static void asc_text_destroy(AscSceneNode *node) { |
135 static void asc_text_destroy(AscSceneNode *node) { |
86 AscText *text = (AscText*) node; |
136 asc_ptr_cast(AscText, text, node); |
87 AscSprite *sprite = (AscSprite*) node; |
137 asc_mesh_destroy(&text->mesh); |
88 asc_mesh_destroy(&sprite->mesh); |
138 asc_texture_destroy(text->texture, 1); |
89 asc_texture_destroy(sprite->texture, 1); |
139 assert(text->texture->refcount == 0); |
90 assert(sprite->texture->refcount == 0); |
140 cxFreeDefault(text->texture); |
91 cxFreeDefault(sprite->texture); |
|
92 cx_strfree(&text->text); |
141 cx_strfree(&text->text); |
93 } |
142 } |
94 |
143 |
95 AscSceneNode *asc_text_create(struct asc_text_create_args args) { |
144 AscSceneNode *asc_text_create(struct asc_text_create_args args) { |
96 AscText *text = cxZallocDefault(sizeof(AscText)); |
145 AscText *text = cxZallocDefault(sizeof(AscText)); |
97 AscSceneNode *node = &text->base.data; |
146 AscSceneNode *node = &text->base; |
98 |
147 |
99 // node properties |
148 // node properties |
100 asc_scene_node_name(node, args.name); |
149 asc_scene_node_name(node, args.name); |
101 node->render_group = ASC_RENDER_GROUP_2D_BLEND; |
150 node->render_group = ASC_RENDER_GROUP_2D_BLEND; |
102 node->destroy_func = asc_text_destroy; |
151 node->destroy_func = asc_text_destroy; |
103 node->update_func = asc_text_update; |
152 node->update_func = asc_text_update; |
104 node->draw_func = asc_sprite_draw; |
153 node->draw_func = asc_text_draw; |
105 asc_transform_identity(node->transform); |
154 asc_transform_identity(node->transform); |
106 asc_transform_translate3f(node->transform, |
155 asc_transform_translate3f(node->transform, |
107 ASC_VEC3F(args.x, args.y, ASC_SCENE_2D_DEPTH_OFFSET)); |
156 ASC_VEC3F(args.x, args.y, ASC_SCENE_2D_DEPTH_OFFSET)); |
108 |
157 |
109 // text properties |
158 // text properties |