1 //  SuperTuxKart - a fun racing game with go-kart
2 //  Copyright (C) 2014-2015 SuperTuxKart-Team
3 //
4 //  This program is free software; you can redistribute it and/or
5 //  modify it under the terms of the GNU General Public License
6 //  as published by the Free Software Foundation; either version 3
7 //  of the License, or (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program; if not, write to the Free Software
16 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 
18 #ifndef SERVER_ONLY
19 
20 #include "graphics/stk_text_billboard.hpp"
21 #include "graphics/sp/sp_base.hpp"
22 #include "graphics/central_settings.hpp"
23 #include "graphics/irr_driver.hpp"
24 #include "graphics/graphics_restrictions.hpp"
25 
26 // ----------------------------------------------------------------------------
STKTextBillboard(const video::SColor & color_top,const video::SColor & color_bottom,ISceneNode * parent,ISceneManager * mgr,s32 id,const core::vector3df & position,const core::vector3df & scale)27 STKTextBillboard::STKTextBillboard(const video::SColor& color_top,
28                                    const video::SColor& color_bottom,
29                                    ISceneNode* parent, ISceneManager* mgr,
30                                    s32 id,
31                                    const core::vector3df& position,
32                                    const core::vector3df& scale)
33                 : ISceneNode(parent, mgr, id, position,
34                              core::vector3df(0.0f, 0.0f, 0.0f), scale)
35 {
36     using namespace SP;
37     m_color_top = color_top;
38     if (CVS->isDeferredEnabled() && CVS->isGLSL())
39     {
40         m_color_top.setRed(srgb255ToLinear(m_color_top.getRed()));
41         m_color_top.setGreen(srgb255ToLinear(m_color_top.getGreen()));
42         m_color_top.setBlue(srgb255ToLinear(m_color_top.getBlue()));
43     }
44     m_color_bottom = color_bottom;
45     if (CVS->isDeferredEnabled() && CVS->isGLSL())
46     {
47         video::SColorf tmp(m_color_bottom);
48         m_color_bottom.setRed(srgb255ToLinear(m_color_bottom.getRed()));
49         m_color_bottom.setGreen(srgb255ToLinear(m_color_bottom.getGreen()));
50         m_color_bottom.setBlue(srgb255ToLinear(m_color_bottom.getBlue()));
51     }
52     static_assert(sizeof(GLTB) == 20, "Wrong compiler padding");
53 }   // STKTextBillboard
54 
55 // ----------------------------------------------------------------------------
getDefaultScale(FontWithFace * face)56 float STKTextBillboard::getDefaultScale(FontWithFace* face)
57 {
58     return 1.0f / (float)face->getDPI();
59 }   // getDefaultScale
60 
61 // ----------------------------------------------------------------------------
updateAbsolutePosition()62 void STKTextBillboard::updateAbsolutePosition()
63 {
64     // Make billboard always face the camera
65     scene::ICameraSceneNode* curr_cam =
66         irr_driver->getSceneManager()->getActiveCamera();
67     if (!curr_cam) return;
68     core::quaternion q(curr_cam->getViewMatrix());
69     q.W = -q.W;
70 
71     if (Parent)
72     {
73         // Override to not use the parent's rotation
74         core::vector3df wc = RelativeTranslation;
75         Parent->getAbsoluteTransformation().transformVect(wc);
76         AbsoluteTransformation.setTranslation(wc);
77         q.getMatrix(AbsoluteTransformation, wc);
78     }
79     else
80     {
81         q.getMatrix(AbsoluteTransformation, RelativeTranslation);
82     }
83     core::matrix4 m;
84     m.setScale(RelativeScale);
85     AbsoluteTransformation *= m;
86     if (CVS->isGLSL())
87     {
88         m_instanced_data =
89             SP::SPInstancedData(AbsoluteTransformation, 0, 0, 0, 0);
90     }
91 }   // updateAbsolutePosition
92 
93 // ----------------------------------------------------------------------------
init(const core::stringw & text,FontWithFace * face)94 void STKTextBillboard::init(const core::stringw& text, FontWithFace* face)
95 {
96     m_face = face;
97     m_text = text;
98     m_chars = new std::vector<STKTextBillboardChar>();
99     core::dimension2du size = face->getDimension(text);
100     face->drawText(text, core::rect<s32>(0, 0, size.Width, size.Height),
101         video::SColor(255,255,255,255), false, false, NULL, NULL, this);
102 
103     const float scale = getDefaultScale(face);
104     float max_x = 0;
105     float min_y = 0;
106     float max_y = 0;
107     for (unsigned int i = 0; i < m_chars->size(); i++)
108     {
109         float char_x = (*m_chars)[i].m_dest_rect.LowerRightCorner.X;
110         if (char_x > max_x)
111         {
112             max_x = char_x;
113         }
114 
115         float char_min_y = (*m_chars)[i].m_dest_rect.UpperLeftCorner.Y;
116         float char_max_y = (*m_chars)[i].m_dest_rect.LowerRightCorner.Y;
117         if (char_min_y < min_y)
118         {
119             min_y = char_min_y;
120         }
121         if (char_max_y > min_y)
122         {
123             max_y = char_max_y;
124         }
125     }
126     float scaled_center_x = (max_x / 2.0f) * scale;
127     float scaled_y = (max_y / 2.0f) * scale;
128 
129     for (unsigned int i = 0; i < m_chars->size(); i++)
130     {
131         core::vector3df char_pos((*m_chars)[i].m_dest_rect.UpperLeftCorner.X,
132             (*m_chars)[i].m_dest_rect.UpperLeftCorner.Y, 0);
133         char_pos *= scale;
134 
135         core::vector3df char_pos2((*m_chars)[i].m_dest_rect.LowerRightCorner.X,
136             (*m_chars)[i].m_dest_rect.LowerRightCorner.Y, 0);
137         char_pos2 *= scale;
138 
139         float tex_width = (float)(*m_chars)[i].m_texture->getSize().Width;
140         float tex_height = (float)(*m_chars)[i].m_texture->getSize().Height;
141         using namespace MiniGLM;
142         std::array<GLTB, 4> triangle_strip =
143         {{
144             {
145                 core::vector3df
146                     (char_pos.X - scaled_center_x, char_pos.Y - scaled_y, 0),
147                 m_color_bottom,
148                 {
149                     toFloat16((*m_chars)
150                         [i].m_source_rect.UpperLeftCorner.X / tex_width),
151                     toFloat16((*m_chars)
152                         [i].m_source_rect.LowerRightCorner.Y / tex_height)
153                 }
154             },
155 
156             {
157                 core::vector3df
158                     (char_pos.X - scaled_center_x, char_pos2.Y - scaled_y, 0),
159                 m_color_top,
160                 {
161                     toFloat16((*m_chars)
162                         [i].m_source_rect.UpperLeftCorner.X / tex_width),
163                     toFloat16((*m_chars)
164                         [i].m_source_rect.UpperLeftCorner.Y / tex_height)
165                 }
166             },
167 
168             {
169                 core::vector3df
170                     (char_pos2.X - scaled_center_x, char_pos.Y - scaled_y, 0),
171                 m_color_bottom,
172                 {
173                     toFloat16((*m_chars)
174                         [i].m_source_rect.LowerRightCorner.X / tex_width),
175                     toFloat16((*m_chars)
176                         [i].m_source_rect.LowerRightCorner.Y / tex_height)
177                 }
178             },
179 
180             {
181                 core::vector3df
182                     (char_pos2.X - scaled_center_x, char_pos2.Y - scaled_y, 0),
183                 m_color_top,
184                 {
185                     toFloat16((*m_chars)
186                         [i].m_source_rect.LowerRightCorner.X / tex_width),
187                     toFloat16((*m_chars)
188                         [i].m_source_rect.UpperLeftCorner.Y / tex_height)
189                 }
190             }
191         }};
192         m_gl_tbs[(*m_chars)[i].m_texture].push_back(triangle_strip);
193     }
194 
195     glGenBuffers(1, &m_instanced_array);
196     glBindBuffer(GL_ARRAY_BUFFER, m_instanced_array);
197     glBufferData(GL_ARRAY_BUFFER,
198         12 /*position*/ + 16/*quaternion*/ + 8 /*scale*/, NULL,
199         GL_DYNAMIC_DRAW);
200     for (auto& p : m_gl_tbs)
201     {
202         glGenVertexArrays(1, &m_vao_vbos[p.first].first);
203         glGenBuffers(1, &m_vao_vbos[p.first].second);
204         glBindBuffer(GL_ARRAY_BUFFER, m_vao_vbos.at(p.first).second);
205         glBufferData(GL_ARRAY_BUFFER, m_gl_tbs.at(p.first).size() * 4 * 20,
206             m_gl_tbs.at(p.first).data(), GL_STATIC_DRAW);
207         glBindBuffer(GL_ARRAY_BUFFER, 0);
208         glBindVertexArray(m_vao_vbos.at(p.first).first);
209         glBindBuffer(GL_ARRAY_BUFFER, m_vao_vbos.at(p.first).second);
210 
211         // Position
212         glEnableVertexAttribArray(0);
213         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 20, (void*)0);
214 
215         // Vertex color
216         glEnableVertexAttribArray(2);
217         glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, 20,
218             (void*)12);
219 
220         // 1st texture coordinates
221         glEnableVertexAttribArray(3);
222         glVertexAttribPointer(3, 2, GL_HALF_FLOAT, GL_FALSE, 20, (void*)16);
223 
224         glBindBuffer(GL_ARRAY_BUFFER, m_instanced_array);
225         // Origin
226         glEnableVertexAttribArray(8);
227         glVertexAttribPointer(8, 3, GL_FLOAT, GL_FALSE, 36, (void*)0);
228         glVertexAttribDivisorARB(8, 1);
229 
230         // Rotation (quaternion in 4 32bit floats)
231         glEnableVertexAttribArray(9);
232         glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, 36, (void*)12);
233         glVertexAttribDivisorARB(9, 1);
234 
235         // Scale (3 half floats and .w unused)
236         glEnableVertexAttribArray(10);
237         glVertexAttribPointer(10, 4, GL_HALF_FLOAT, GL_FALSE, 36, (void*)28);
238         glVertexAttribDivisorARB(10, 1);
239 
240         glBindVertexArray(0);
241         glBindBuffer(GL_ARRAY_BUFFER, 0);
242     }
243 
244     Vec3 min = Vec3( 999999.9f);
245     Vec3 max = Vec3(-999999.9f);
246     for (auto& p : m_gl_tbs)
247     {
248         for (auto& q : p.second)
249         {
250             for (auto& r : q)
251             {
252                 Vec3 c(r.m_position.X, r.m_position.Y, r.m_position.Z);
253                 min.min(c);
254                 max.max(c);
255             }
256         }
257     }
258     m_bbox.MinEdge = min.toIrrVector();
259     m_bbox.MaxEdge = max.toIrrVector();
260 
261     delete m_chars;
262     updateAbsolutePosition();
263 }   // init
264 
265 // ----------------------------------------------------------------------------
initLegacy(const core::stringw & text,FontWithFace * face)266 void STKTextBillboard::initLegacy(const core::stringw& text, FontWithFace* face)
267 {
268     m_face = face;
269     m_text = text;
270     m_chars = new std::vector<STKTextBillboardChar>();
271     core::dimension2du size = face->getDimension(text);
272     face->drawText(text, core::rect<s32>(0, 0, size.Width, size.Height),
273         video::SColor(255,255,255,255), false, false, NULL, NULL, this);
274 
275     const float scale = getDefaultScale(face);
276     float max_x = 0;
277     float min_y = 0;
278     float max_y = 0;
279     for (unsigned int i = 0; i < m_chars->size(); i++)
280     {
281         float char_x = (*m_chars)[i].m_dest_rect.LowerRightCorner.X;
282         if (char_x > max_x)
283         {
284             max_x = char_x;
285         }
286 
287         float char_min_y = (*m_chars)[i].m_dest_rect.UpperLeftCorner.Y;
288         float char_max_y = (*m_chars)[i].m_dest_rect.LowerRightCorner.Y;
289         if (char_min_y < min_y)
290         {
291             min_y = char_min_y;
292         }
293         if (char_max_y > min_y)
294         {
295             max_y = char_max_y;
296         }
297     }
298     float scaled_center_x = (max_x / 2.0f) * scale;
299     float scaled_y = (max_y / 2.0f) * scale;
300 
301     std::unordered_map<video::ITexture*,
302         std::vector<std::array<irr::video::S3DVertex, 4> > > irr_tbs;
303     for (unsigned int i = 0; i < m_chars->size(); i++)
304     {
305         core::vector3df char_pos((*m_chars)[i].m_dest_rect.UpperLeftCorner.X,
306             (*m_chars)[i].m_dest_rect.UpperLeftCorner.Y, 0);
307         char_pos *= scale;
308 
309         core::vector3df char_pos2((*m_chars)[i].m_dest_rect.LowerRightCorner.X,
310             (*m_chars)[i].m_dest_rect.LowerRightCorner.Y, 0);
311         char_pos2 *= scale;
312 
313         float tex_width = (float)(*m_chars)[i].m_texture->getSize().Width;
314         float tex_height = (float)(*m_chars)[i].m_texture->getSize().Height;
315         std::array<irr::video::S3DVertex, 4> triangle =
316         {{
317             {
318                 core::vector3df
319                     (char_pos.X - scaled_center_x, char_pos.Y - scaled_y, 0),
320                 core::vector3df(0.0f, 1.0f, 0.0f),
321                 m_color_bottom,
322                 core::vector2df
323                     ((*m_chars)
324                         [i].m_source_rect.UpperLeftCorner.X / tex_width,
325                     (*m_chars)
326                         [i].m_source_rect.LowerRightCorner.Y / tex_height)
327             },
328 
329             {
330                 core::vector3df
331                     (char_pos2.X - scaled_center_x, char_pos.Y - scaled_y, 0),
332                 core::vector3df(0.0f, 1.0f, 0.0f),
333                 m_color_bottom,
334                 core::vector2df
335                     ((*m_chars)
336                         [i].m_source_rect.LowerRightCorner.X / tex_width,
337                     (*m_chars)
338                         [i].m_source_rect.LowerRightCorner.Y / tex_height)
339             },
340 
341             {
342                 core::vector3df
343                     (char_pos2.X - scaled_center_x, char_pos2.Y - scaled_y, 0),
344                 core::vector3df(0.0f, 1.0f, 0.0f),
345 
346                 m_color_top,
347                 core::vector2df
348                     ((*m_chars)
349                         [i].m_source_rect.LowerRightCorner.X / tex_width,
350                     (*m_chars)
351                         [i].m_source_rect.UpperLeftCorner.Y / tex_height)
352             },
353 
354             {
355                 core::vector3df
356                     (char_pos.X - scaled_center_x, char_pos2.Y - scaled_y, 0),
357                 core::vector3df(0.0f, 1.0f, 0.0f),
358                 m_color_top,
359                 core::vector2df
360                     ((*m_chars)
361                         [i].m_source_rect.UpperLeftCorner.X / tex_width,
362                     (*m_chars)
363                         [i].m_source_rect.UpperLeftCorner.Y / tex_height)
364             }
365         }};
366         irr_tbs[(*m_chars)[i].m_texture].push_back(triangle);
367     }
368 
369     for (auto& p : irr_tbs)
370     {
371         scene::SMeshBuffer* buffer = new scene::SMeshBuffer();
372         buffer->getMaterial().setTexture(0, p.first);
373         buffer->getMaterial().MaterialType =
374             video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
375         buffer->getMaterial().Lighting = false;
376 
377         std::vector<uint16_t> indices;
378         for (unsigned i = 0; i < p.second.size(); i++)
379         {
380             indices.push_back(4 * i + 2);
381             indices.push_back(4 * i + 1);
382             indices.push_back(4 * i + 0);
383             indices.push_back(4 * i + 3);
384             indices.push_back(4 * i + 2);
385             indices.push_back(4 * i + 0);
386         }
387         buffer->append(p.second.data(), p.second.size() * 4,
388             indices.data(), indices.size());
389         buffer->recalculateBoundingBox();
390         m_gl_mb[p.first] = buffer;
391     }
392 
393     Vec3 min = Vec3( 999999.9f);
394     Vec3 max = Vec3(-999999.9f);
395     for (auto& p : irr_tbs)
396     {
397         for (auto& q : p.second)
398         {
399             for (auto& r : q)
400             {
401                 Vec3 c(r.Pos.X, r.Pos.Y, r.Pos.Z);
402                 min.min(c);
403                 max.max(c);
404             }
405         }
406     }
407     m_bbox.MinEdge = min.toIrrVector();
408     m_bbox.MaxEdge = max.toIrrVector();
409 
410     delete m_chars;
411     updateAbsolutePosition();
412 
413 }   // initLegacy
414 
415 // ----------------------------------------------------------------------------
render()416 void STKTextBillboard::render()
417 {
418     video::IVideoDriver* driver = SceneManager->getVideoDriver();
419     driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
420     for (auto& p : m_gl_mb)
421     {
422         driver->setMaterial(p.second->getMaterial());
423         driver->drawMeshBuffer(p.second);
424     }
425 }   // render
426 
427 // ----------------------------------------------------------------------------
OnRegisterSceneNode()428 void STKTextBillboard::OnRegisterSceneNode()
429 {
430     SceneManager->registerNodeForRendering(this, ESNRP_TRANSPARENT);
431     ISceneNode::OnRegisterSceneNode();
432 }   // OnRegisterSceneNode
433 
434 // ----------------------------------------------------------------------------
collectChar(video::ITexture * texture,const core::rect<float> & dest_rect,const core::rect<s32> & source_rect,const video::SColor * const colors)435 void STKTextBillboard::collectChar(video::ITexture* texture,
436                                    const core::rect<float>& dest_rect,
437                                    const core::rect<s32>& source_rect,
438                                    const video::SColor* const colors)
439 {
440     assert(m_chars != NULL);
441     m_chars->push_back(STKTextBillboardChar(texture, dest_rect, source_rect,
442         colors));
443 }   // collectChar
444 
445 // ----------------------------------------------------------------------------
reload()446 void STKTextBillboard::reload()
447 {
448     clearBuffer();
449     if (CVS->isGLSL())
450         init(m_text, m_face);
451     else
452         initLegacy(m_text, m_face);
453 }   // reload
454 
455 // ----------------------------------------------------------------------------
updateTextBillboard(core::list<scene::ISceneNode * > & List)456 static void updateTextBillboard(core::list<scene::ISceneNode*>& List)
457 {
458     core::list<scene::ISceneNode*>::Iterator I = List.begin(), E = List.end();
459     for (; I != E; ++I)
460     {
461         if (STKTextBillboard* tb = dynamic_cast<STKTextBillboard*>(*I))
462             tb->reload();
463         updateTextBillboard((*I)->getChildren());
464     }
465 }   // updateTextBillboard
466 
467 // ----------------------------------------------------------------------------
updateAllTextBillboards()468 void STKTextBillboard::updateAllTextBillboards()
469 {
470     updateTextBillboard(
471         irr_driver->getSceneManager()->getRootSceneNode()->getChildren());
472 }   // updateAllTextBillboards
473 
474 #endif   // !SERVER_ONLY
475 
476