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