364f8d4bfc6c495413d8f3c280832a8bde5a756c
[blender.git] / source / gameengine / Ketsji / KX_FontObject.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
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 2
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 Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file gameengine/Ketsji/KX_FontObject.cpp
29  *  \ingroup ketsji
30  */
31
32 #include "KX_FontObject.h"
33 #include "DNA_curve_types.h"
34 #include "DNA_vfont_types.h"
35 #include "KX_Scene.h"
36 #include "KX_PythonInit.h"
37 #include "BLI_math.h"
38 #include "EXP_StringValue.h"
39 #include "RAS_IRasterizer.h"
40
41 /* paths needed for font load */
42 #include "BLI_blenlib.h"
43 #include "BKE_global.h"
44 #include "BKE_font.h"
45 #include "BKE_main.h"
46 #include "DNA_packedFile_types.h"
47
48 extern "C" {
49 #include "BLF_api.h"
50 }
51
52 #define BGE_FONT_RES 100
53
54 /* proptotype */
55 int GetFontId(VFont *font);
56
57 static std::vector<STR_String> split_string(STR_String str)
58 {
59         std::vector<STR_String> text = std::vector<STR_String>();
60
61         /* Split the string upon new lines */
62         int begin=0, end=0;
63         while (end < str.Length())
64         {
65                 if (str.GetAt(end) == '\n')
66                 {
67                         text.push_back(str.Mid(begin, end-begin));
68                         begin = end+1;
69                 }
70                 end++;
71         }
72         //Now grab the last line
73         text.push_back(str.Mid(begin, end-begin));
74
75         return text;
76 }
77
78 KX_FontObject::KX_FontObject(void* sgReplicationInfo,
79                              SG_Callbacks callbacks,
80                              RAS_IRasterizer* rasterizer,
81                              Object *ob,
82                              bool do_color_management):
83         KX_GameObject(sgReplicationInfo, callbacks),
84         m_object(ob),
85         m_dpi(72),
86         m_resolution(1.f),
87         m_rasterizer(rasterizer),
88         m_do_color_management(do_color_management)
89 {
90         Curve *text = static_cast<Curve *> (ob->data);
91         m_text = split_string(text->str);
92         m_fsize = text->fsize;
93         m_line_spacing = text->linedist;
94         m_offset = MT_Vector3(text->xof, text->yof, 0);
95         
96         m_fontid = GetFontId(text->vfont);
97         
98         /* initialize the color with the object color and store it in the KX_Object class
99          * This is a workaround waiting for the fix:
100          * [#25487] BGE: Object Color only works when it has a keyed frame */
101         copy_v4_v4(m_color, (const float*) ob->col);
102         this->SetObjectColor((const MT_Vector4&) m_color);
103 }
104
105 KX_FontObject::~KX_FontObject()
106 {
107         //remove font from the scene list
108         //it's handled in KX_Scene::NewRemoveObject
109 }
110
111 CValue* KX_FontObject::GetReplica()
112 {
113         KX_FontObject* replica = new KX_FontObject(*this);
114         replica->ProcessReplica();
115         return replica;
116 }
117
118 void KX_FontObject::ProcessReplica()
119 {
120         KX_GameObject::ProcessReplica();
121 }
122
123 int GetFontId(VFont *vfont)
124 {
125         PackedFile *packedfile=NULL;
126         int fontid = -1;
127
128         if (vfont->packedfile) {
129                 packedfile= vfont->packedfile;
130                 fontid= BLF_load_mem(vfont->name, (unsigned char*)packedfile->data, packedfile->size);
131                 
132                 if (fontid == -1) {
133                         printf("ERROR: packed font \"%s\" could not be loaded.\n", vfont->name);
134                         fontid = BLF_load("default");
135                 }
136                 return fontid;
137         }
138         
139         /* once we have packed working we can load the builtin font     */
140         const char *filepath = vfont->name;
141         if (BKE_vfont_is_builtin(vfont)) {
142                 fontid = BLF_load("default");
143                 
144                 /* XXX the following code is supposed to work (after you add get_builtin_packedfile to BKE_font.h )
145                  * unfortunately it's crashing on blf_glyph.c:173 because gc->max_glyph_width is 0
146                  */
147                 // packedfile=get_builtin_packedfile();
148                 // fontid= BLF_load_mem(font->name, (unsigned char*)packedfile->data, packedfile->size);
149                 // return fontid;
150
151                 return BLF_load("default");
152         }
153         
154         /* convert from absolute to relative */
155         char expanded[256]; // font names can be bigger than FILE_MAX (240)
156         BLI_strncpy(expanded, filepath, 256);
157         BLI_path_abs(expanded, G.main->name);
158         
159         fontid = BLF_load(expanded);
160
161         /* fallback */
162         if (fontid == -1)
163                 fontid = BLF_load("default");
164         
165         return fontid;
166 }
167
168 void KX_FontObject::DrawFontText()
169 {
170         /* Allow for some logic brick control */
171         if (this->GetProperty("Text"))
172                 m_text = split_string(this->GetProperty("Text")->GetText());
173
174         /* only draws the text if visible */
175         if (this->GetVisible() == 0) return;
176
177         /* update the animated color */
178         this->GetObjectColor().getValue(m_color);
179
180         /* Font Objects don't use the glsl shader, this color management code is copied from gpu_shader_material.glsl */
181         float color[4];
182         if (m_do_color_management) {
183                 linearrgb_to_srgb_v4(color, m_color);
184         }
185         else {
186                 copy_v4_v4(color, m_color);
187         }
188
189         /* HARDCODED MULTIPLICATION FACTOR - this will affect the render resolution directly */
190         const float RES = BGE_FONT_RES * m_resolution;
191
192         const float size = m_fsize * this->NodeGetWorldScaling()[0] * RES;
193         const float aspect = m_fsize / size;
194
195         /* Get a working copy of the OpenGLMatrix to use */
196         float *mat = GetOpenGLMatrix();
197
198         /* Account for offset */
199         MT_Vector3 offset = this->NodeGetWorldOrientation() * m_offset * this->NodeGetWorldScaling();
200         mat[12] += offset[0]; mat[13] += offset[1]; mat[14] += offset[2];
201
202         /* Orient the spacing vector */
203         MT_Vector3 spacing = MT_Vector3(0.0f, m_fsize*m_line_spacing, 0.0f);
204         spacing = this->NodeGetWorldOrientation() * spacing * this->NodeGetWorldScaling()[1];
205
206         /* Draw each line, taking spacing into consideration */
207         for (int i=0; i<m_text.size(); ++i)
208         {
209                 if (i!=0)
210                 {
211                         mat[12] -= spacing[0];
212                         mat[13] -= spacing[1];
213                         mat[14] -= spacing[2];
214                 }
215                 m_rasterizer->RenderText3D(m_fontid, m_text[i], int(size), m_dpi, color, mat, aspect);
216         }
217 }
218
219 #ifdef WITH_PYTHON
220
221 /* ------------------------------------------------------------------------- */
222 /* Python Integration Hooks                                                                      */
223 /* ------------------------------------------------------------------------- */
224
225 PyTypeObject KX_FontObject::Type = {
226         PyVarObject_HEAD_INIT(NULL, 0)
227         "KX_FontObject",
228         sizeof(PyObjectPlus_Proxy),
229         0,
230         py_base_dealloc,
231         0,
232         0,
233         0,
234         0,
235         py_base_repr,
236         0,
237         &KX_GameObject::Sequence,
238         &KX_GameObject::Mapping,
239         0,0,0,
240         NULL,
241         NULL,
242         0,
243         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
244         0,0,0,0,0,0,0,
245         Methods,
246         0,
247         0,
248         &KX_GameObject::Type,
249         0,0,0,0,0,0,
250         py_base_new
251 };
252
253 PyMethodDef KX_FontObject::Methods[] = {
254         {NULL,NULL} //Sentinel
255 };
256
257 PyAttributeDef KX_FontObject::Attributes[] = {
258         //KX_PYATTRIBUTE_STRING_RW("text", 0, 280, false, KX_FontObject, m_text[0]), //arbitrary limit. 280 = 140 unicode chars in unicode
259         KX_PYATTRIBUTE_RW_FUNCTION("text", KX_FontObject, pyattr_get_text, pyattr_set_text),
260         KX_PYATTRIBUTE_FLOAT_RW("size", 0.0001f, 10000.0f, KX_FontObject, m_fsize),
261         KX_PYATTRIBUTE_FLOAT_RW("resolution", 0.0001f, 10000.0f, KX_FontObject, m_resolution),
262         /* KX_PYATTRIBUTE_INT_RW("dpi", 0, 10000, false, KX_FontObject, m_dpi), */// no real need for expose this I think
263         { NULL }        //Sentinel
264 };
265
266 PyObject *KX_FontObject::pyattr_get_text(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
267 {
268         KX_FontObject* self = static_cast<KX_FontObject*>(self_v);
269         STR_String str = STR_String();
270         for (int i=0; i<self->m_text.size(); ++i)
271         {
272                 if (i!=0)
273                         str += '\n';
274                 str += self->m_text[i];
275         }
276         return PyUnicode_From_STR_String(str);
277 }
278
279 int KX_FontObject::pyattr_set_text(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
280 {
281         KX_FontObject* self = static_cast<KX_FontObject*>(self_v);
282         if (!PyUnicode_Check(value))
283                 return PY_SET_ATTR_FAIL;
284         char* chars = _PyUnicode_AsString(value);
285
286         /* Allow for some logic brick control */
287         CValue* tprop = self->GetProperty("Text");
288         if (tprop) {
289                 CValue *newstringprop = new CStringValue(STR_String(chars), "Text");
290                 self->SetProperty("Text", newstringprop);
291                 newstringprop->Release();
292         }
293         else {
294                 self->m_text = split_string(STR_String(chars));
295         }
296
297         return PY_SET_ATTR_SUCCESS;
298 }
299
300 #endif // WITH_PYTHON