Changeset View
Changeset View
Standalone View
Standalone View
source/gameengine/Ketsji/KX_FontObject.cpp
| Context not available. | |||||
| #include "DNA_curve_types.h" | #include "DNA_curve_types.h" | ||||
| #include "DNA_vfont_types.h" | #include "DNA_vfont_types.h" | ||||
| #include "KX_Scene.h" | #include "KX_Scene.h" | ||||
| #include "KX_Globals.h" | |||||
| #include "KX_PythonInit.h" | #include "KX_PythonInit.h" | ||||
| #include "KX_TextMaterial.h" | |||||
| #include "KX_PyMath.h" | |||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "EXP_StringValue.h" | #include "EXP_StringValue.h" | ||||
| #include "RAS_IRasterizer.h" | #include "RAS_IRasterizer.h" | ||||
| #include "RAS_BucketManager.h" | |||||
| #include "RAS_MaterialBucket.h" | |||||
| #include "RAS_TextUser.h" | |||||
| /* paths needed for font load */ | /* paths needed for font load */ | ||||
| #include "BLI_blenlib.h" | #include "BLI_blenlib.h" | ||||
| Context not available. | |||||
| #include "BLF_api.h" | #include "BLF_api.h" | ||||
| } | } | ||||
| #include "CM_Message.h" | |||||
| #define BGE_FONT_RES 100 | #define BGE_FONT_RES 100 | ||||
| /* proptotype */ | /* proptotype */ | ||||
| int GetFontId(VFont *font); | int GetFontId(VFont *font); | ||||
| static std::vector<STR_String> split_string(STR_String str) | static std::vector<std::string> split_string(std::string str) | ||||
| { | { | ||||
| std::vector<STR_String> text = std::vector<STR_String>(); | std::vector<std::string> text = std::vector<std::string>(); | ||||
| /* Split the string upon new lines */ | // Split the string upon new lines | ||||
| int begin=0, end=0; | int begin = 0, end = 0; | ||||
| while (end < str.Length()) | while (end < str.size()) { | ||||
| { | if (str[end] == '\n') { | ||||
| if (str.GetAt(end) == '\n') | text.push_back(str.substr(begin, end - begin)); | ||||
| { | begin = end + 1; | ||||
| text.push_back(str.Mid(begin, end-begin)); | |||||
| begin = end+1; | |||||
| } | } | ||||
| end++; | end++; | ||||
| } | } | ||||
| //Now grab the last line | // Now grab the last line | ||||
| text.push_back(str.Mid(begin, end-begin)); | text.push_back(str.substr(begin, end - begin)); | ||||
| return text; | return text; | ||||
| } | } | ||||
| KX_FontObject::KX_FontObject(void* sgReplicationInfo, | KX_FontObject::KX_FontObject(void *sgReplicationInfo, | ||||
| SG_Callbacks callbacks, | SG_Callbacks callbacks, | ||||
| RAS_IRasterizer* rasterizer, | RAS_IRasterizer *rasterizer, | ||||
| Object *ob, | Object *ob, | ||||
| bool do_color_management): | bool do_color_management) | ||||
| KX_GameObject(sgReplicationInfo, callbacks), | :KX_GameObject(sgReplicationInfo, callbacks), | ||||
| m_object(ob), | m_object(ob), | ||||
| m_dpi(72), | m_dpi(72), | ||||
| m_resolution(1.f), | m_resolution(1.0f), | ||||
| m_rasterizer(rasterizer), | m_rasterizer(rasterizer), | ||||
| m_do_color_management(do_color_management) | m_do_color_management(do_color_management) | ||||
| { | { | ||||
| Context not available. | |||||
| m_text = split_string(text->str); | m_text = split_string(text->str); | ||||
| m_fsize = text->fsize; | m_fsize = text->fsize; | ||||
| m_line_spacing = text->linedist; | m_line_spacing = text->linedist; | ||||
| m_offset = MT_Vector3(text->xof, text->yof, 0); | m_offset = MT_Vector3(text->xof, text->yof, 0.0f); | ||||
| m_fontid = GetFontId(text->vfont); | m_fontid = GetFontId(text->vfont); | ||||
| /* initialize the color with the object color and store it in the KX_Object class | |||||
| * This is a workaround waiting for the fix: | |||||
| * [#25487] BGE: Object Color only works when it has a keyed frame */ | |||||
| copy_v4_v4(m_color, (const float*) ob->col); | |||||
| this->SetObjectColor((const MT_Vector4&) m_color); | |||||
| } | } | ||||
| KX_FontObject::~KX_FontObject() | KX_FontObject::~KX_FontObject() | ||||
| Context not available. | |||||
| //it's handled in KX_Scene::NewRemoveObject | //it's handled in KX_Scene::NewRemoveObject | ||||
| } | } | ||||
| CValue* KX_FontObject::GetReplica() | CValue *KX_FontObject::GetReplica() | ||||
| { | { | ||||
| KX_FontObject* replica = new KX_FontObject(*this); | KX_FontObject *replica = new KX_FontObject(*this); | ||||
| replica->ProcessReplica(); | replica->ProcessReplica(); | ||||
| return replica; | return replica; | ||||
| } | } | ||||
| Context not available. | |||||
| KX_GameObject::ProcessReplica(); | KX_GameObject::ProcessReplica(); | ||||
| } | } | ||||
| void KX_FontObject::AddMeshUser() | |||||
| { | |||||
| m_meshUser = new RAS_TextUser(m_pClient_info); | |||||
| // set the part of the mesh slot that never change | |||||
| float *fl = GetOpenGLMatrixPtr()->getPointer(); | |||||
| m_meshUser->SetMatrix(fl); | |||||
| RAS_BucketManager *bucketManager = GetScene()->GetBucketManager(); | |||||
| bool created = false; | |||||
| RAS_MaterialBucket *bucket = bucketManager->FindBucket(GetTextMaterial(), created); | |||||
| // If the material bucket is just created then we add a new mesh slot. | |||||
| if (created) { | |||||
| RAS_TexVertFormat format; | |||||
| format.uvSize = 1; | |||||
| format.colorSize = 1; | |||||
| bucket->NewMesh(NULL, NULL, format); | |||||
| } | |||||
| /* We copy the original mesh slot which is at the begin of the list, if it's not the case it | |||||
| * doesn't matter as the mesh slot are all similar exepted their mesh user pointer which is | |||||
| * set to NULL in copy. By copying instead of adding a mesh slot we reuse the same display | |||||
| * array bucket. | |||||
| */ | |||||
| RAS_MeshSlot *ms = bucket->CopyMesh(*bucket->msBegin()); | |||||
| ms->SetMeshUser(m_meshUser); | |||||
| ms->SetDeformer(NULL); | |||||
| m_meshUser->AddMeshSlot(ms); | |||||
| } | |||||
| void KX_FontObject::UpdateBuckets() | |||||
| { | |||||
| // Update datas and add mesh slot to be rendered only if the object is not culled. | |||||
| if (m_bVisible && m_meshUser) { | |||||
| if (m_pSGNode->IsDirty()) { | |||||
| GetOpenGLMatrix(); | |||||
| } | |||||
| // Allow for some logic brick control | |||||
| if (GetProperty("Text")) { | |||||
| m_text = split_string(GetProperty("Text")->GetText()); | |||||
| } | |||||
| // update the animated color | |||||
| GetObjectColor().getValue(m_color); | |||||
| // Font Objects don't use the glsl shader, this color management code is copied from gpu_shader_material.glsl | |||||
| float color[4]; | |||||
| if (m_do_color_management) { | |||||
| linearrgb_to_srgb_v4(color, m_color); | |||||
| } | |||||
| else { | |||||
| copy_v4_v4(color, m_color); | |||||
| } | |||||
| // HARDCODED MULTIPLICATION FACTOR - this will affect the render resolution directly | |||||
| const float RES = BGE_FONT_RES * m_resolution; | |||||
| const float size = m_fsize * NodeGetWorldScaling()[0] * RES; | |||||
| const float aspect = m_fsize / size; | |||||
| // Account for offset | |||||
| MT_Vector3 offset = NodeGetWorldOrientation() * m_offset * NodeGetWorldScaling(); | |||||
| // Orient the spacing vector | |||||
| MT_Vector3 spacing = NodeGetWorldOrientation() * MT_Vector3(0.0f, m_fsize * m_line_spacing, 0.0f) * NodeGetWorldScaling()[1]; | |||||
| RAS_TextUser *textUser = (RAS_TextUser *)m_meshUser; | |||||
| textUser->SetColor(MT_Vector4(color)); | |||||
| textUser->SetFrontFace(!m_bIsNegativeScaling); | |||||
| textUser->SetFontId(m_fontid); | |||||
| textUser->SetSize(size); | |||||
| textUser->SetDpi(m_dpi); | |||||
| textUser->SetAspect(aspect); | |||||
| textUser->SetOffset(offset); | |||||
| textUser->SetSpacing(spacing); | |||||
| textUser->SetTexts(m_text); | |||||
| textUser->ActivateMeshSlots(); | |||||
| } | |||||
| } | |||||
| const MT_Vector2 KX_FontObject::GetTextDimensions() | |||||
| { | |||||
| MT_Vector2 dimensions(0.0f, 0.0f); | |||||
| for (std::vector<std::string>::iterator it = m_text.begin(); it != m_text.end(); ++it) { | |||||
| float w = 0.0f, h = 0.0f; | |||||
| const std::string& text = *it; | |||||
| BLF_width_and_height(m_fontid, text.c_str(), text.size(), &w, &h); | |||||
| dimensions.x() = std::max(dimensions.x(), w); | |||||
| dimensions.y() += h + m_line_spacing; | |||||
| } | |||||
| // XXX: Quick hack to convert the size to BU | |||||
| dimensions /= 10.0f; | |||||
| // Scale the width and height by the object's scale | |||||
| const MT_Vector3& scale = NodeGetLocalScaling(); | |||||
| dimensions.x() *= fabs(scale.x()); | |||||
| dimensions.y() *= fabs(scale.y()); | |||||
| return dimensions; | |||||
| } | |||||
| int GetFontId(VFont *vfont) | int GetFontId(VFont *vfont) | ||||
| { | { | ||||
| PackedFile *packedfile=NULL; | PackedFile *packedfile = NULL; | ||||
| int fontid = -1; | int fontid = -1; | ||||
| if (vfont->packedfile) { | if (vfont->packedfile) { | ||||
| packedfile= vfont->packedfile; | packedfile = vfont->packedfile; | ||||
| fontid= BLF_load_mem(vfont->name, (unsigned char*)packedfile->data, packedfile->size); | fontid = BLF_load_mem(vfont->name, (unsigned char *)packedfile->data, packedfile->size); | ||||
| if (fontid == -1) { | if (fontid == -1) { | ||||
| printf("ERROR: packed font \"%s\" could not be loaded.\n", vfont->name); | CM_Error("packed font \"" << vfont->name << "\" could not be loaded"); | ||||
| fontid = BLF_load("default"); | fontid = BLF_load("default"); | ||||
| } | } | ||||
| return fontid; | return fontid; | ||||
| } | } | ||||
| /* once we have packed working we can load the builtin font */ | // once we have packed working we can load the builtin font | ||||
| const char *filepath = vfont->name; | const char *filepath = vfont->name; | ||||
| if (BKE_vfont_is_builtin(vfont)) { | if (BKE_vfont_is_builtin(vfont)) { | ||||
| fontid = BLF_load("default"); | fontid = BLF_load("default"); | ||||
| /* XXX the following code is supposed to work (after you add get_builtin_packedfile to BKE_font.h ) | /* XXX the following code is supposed to work (after you add get_builtin_packedfile to BKE_font.h ) | ||||
| * unfortunately it's crashing on blf_glyph.c:173 because gc->max_glyph_width is 0 | * unfortunately it's crashing on blf_glyph.c:173 because gc->max_glyph_width is 0 | ||||
| */ | */ | ||||
| Context not available. | |||||
| return BLF_load("default"); | return BLF_load("default"); | ||||
| } | } | ||||
| /* convert from absolute to relative */ | // convert from absolute to relative | ||||
| char expanded[256]; // font names can be bigger than FILE_MAX (240) | char expanded[256]; // font names can be bigger than FILE_MAX (240) | ||||
| BLI_strncpy(expanded, filepath, 256); | BLI_strncpy(expanded, filepath, 256); | ||||
| BLI_path_abs(expanded, G.main->name); | BLI_path_abs(expanded, KX_GetMainPath().c_str()); | ||||
| fontid = BLF_load(expanded); | fontid = BLF_load(expanded); | ||||
| /* fallback */ | // fallback | ||||
| if (fontid == -1) | if (fontid == -1) | ||||
| fontid = BLF_load("default"); | fontid = BLF_load("default"); | ||||
| return fontid; | |||||
| } | |||||
| void KX_FontObject::DrawFontText() | |||||
| { | |||||
| /* Allow for some logic brick control */ | |||||
| if (this->GetProperty("Text")) | |||||
| m_text = split_string(this->GetProperty("Text")->GetText()); | |||||
| /* only draws the text if visible */ | |||||
| if (this->GetVisible() == 0) return; | |||||
| /* update the animated color */ | |||||
| this->GetObjectColor().getValue(m_color); | |||||
| /* Font Objects don't use the glsl shader, this color management code is copied from gpu_shader_material.glsl */ | return fontid; | ||||
| float color[4]; | |||||
| if (m_do_color_management) { | |||||
| linearrgb_to_srgb_v4(color, m_color); | |||||
| } | |||||
| else { | |||||
| copy_v4_v4(color, m_color); | |||||
| } | |||||
| /* HARDCODED MULTIPLICATION FACTOR - this will affect the render resolution directly */ | |||||
| const float RES = BGE_FONT_RES * m_resolution; | |||||
| const float size = m_fsize * this->NodeGetWorldScaling()[0] * RES; | |||||
| const float aspect = m_fsize / size; | |||||
| /* Get a working copy of the OpenGLMatrix to use */ | |||||
| float *mat = GetOpenGLMatrix(); | |||||
| /* Account for offset */ | |||||
| MT_Vector3 offset = this->NodeGetWorldOrientation() * m_offset * this->NodeGetWorldScaling(); | |||||
| mat[12] += offset[0]; mat[13] += offset[1]; mat[14] += offset[2]; | |||||
| /* Orient the spacing vector */ | |||||
| MT_Vector3 spacing = MT_Vector3(0.0f, m_fsize*m_line_spacing, 0.0f); | |||||
| spacing = this->NodeGetWorldOrientation() * spacing * this->NodeGetWorldScaling()[1]; | |||||
| /* Draw each line, taking spacing into consideration */ | |||||
| for (int i=0; i<m_text.size(); ++i) | |||||
| { | |||||
| if (i!=0) | |||||
| { | |||||
| mat[12] -= spacing[0]; | |||||
| mat[13] -= spacing[1]; | |||||
| mat[14] -= spacing[2]; | |||||
| } | |||||
| m_rasterizer->RenderText3D(m_fontid, m_text[i], int(size), m_dpi, color, mat, aspect); | |||||
| } | |||||
| } | } | ||||
| #ifdef WITH_PYTHON | #ifdef WITH_PYTHON | ||||
| Context not available. | |||||
| 0, | 0, | ||||
| &KX_GameObject::Sequence, | &KX_GameObject::Sequence, | ||||
| &KX_GameObject::Mapping, | &KX_GameObject::Mapping, | ||||
| 0,0,0, | 0, 0, 0, | ||||
| NULL, | NULL, | ||||
| NULL, | NULL, | ||||
| 0, | 0, | ||||
| Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, | ||||
| 0,0,0,0,0,0,0, | 0, 0, 0, 0, 0, 0, 0, | ||||
| Methods, | Methods, | ||||
| 0, | 0, | ||||
| 0, | 0, | ||||
| &KX_GameObject::Type, | &KX_GameObject::Type, | ||||
| 0,0,0,0,0,0, | 0, 0, 0, 0, 0, 0, | ||||
| py_base_new | py_base_new | ||||
| }; | }; | ||||
| PyMethodDef KX_FontObject::Methods[] = { | PyMethodDef KX_FontObject::Methods[] = { | ||||
| {NULL,NULL} //Sentinel | {NULL, NULL} //Sentinel | ||||
| }; | }; | ||||
| PyAttributeDef KX_FontObject::Attributes[] = { | PyAttributeDef KX_FontObject::Attributes[] = { | ||||
| //KX_PYATTRIBUTE_STRING_RW("text", 0, 280, false, KX_FontObject, m_text[0]), //arbitrary limit. 280 = 140 unicode chars in unicode | //KX_PYATTRIBUTE_STRING_RW("text", 0, 280, false, KX_FontObject, m_text[0]), //arbitrary limit. 280 = 140 unicode chars in unicode | ||||
| KX_PYATTRIBUTE_RW_FUNCTION("text", KX_FontObject, pyattr_get_text, pyattr_set_text), | KX_PYATTRIBUTE_RW_FUNCTION("text", KX_FontObject, pyattr_get_text, pyattr_set_text), | ||||
| KX_PYATTRIBUTE_FLOAT_RW("size", 0.0001f, 10000.0f, KX_FontObject, m_fsize), | KX_PYATTRIBUTE_RO_FUNCTION("dimensions", KX_FontObject, pyattr_get_dimensions), | ||||
| KX_PYATTRIBUTE_FLOAT_RW("resolution", 0.0001f, 10000.0f, KX_FontObject, m_resolution), | KX_PYATTRIBUTE_FLOAT_RW("size", 0.0001f, 40.0f, KX_FontObject, m_fsize), | ||||
| KX_PYATTRIBUTE_FLOAT_RW("resolution", 0.1f, 50.0f, KX_FontObject, m_resolution), | |||||
| /* KX_PYATTRIBUTE_INT_RW("dpi", 0, 10000, false, KX_FontObject, m_dpi), */// no real need for expose this I think | /* KX_PYATTRIBUTE_INT_RW("dpi", 0, 10000, false, KX_FontObject, m_dpi), */// no real need for expose this I think | ||||
| { NULL } //Sentinel | KX_PYATTRIBUTE_NULL //Sentinel | ||||
| }; | }; | ||||
| PyObject *KX_FontObject::pyattr_get_text(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) | PyObject *KX_FontObject::pyattr_get_text(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) | ||||
| { | { | ||||
| KX_FontObject* self = static_cast<KX_FontObject*>(self_v); | KX_FontObject *self = static_cast<KX_FontObject *>(self_v); | ||||
| STR_String str = STR_String(); | std::string str = std::string(); | ||||
| for (int i=0; i<self->m_text.size(); ++i) | for (unsigned int i = 0; i < self->m_text.size(); ++i) { | ||||
| { | if (i != 0) | ||||
| if (i!=0) | |||||
| str += '\n'; | str += '\n'; | ||||
| str += self->m_text[i]; | str += self->m_text[i]; | ||||
| } | } | ||||
| return PyUnicode_From_STR_String(str); | return PyUnicode_FromStdString(str); | ||||
| } | } | ||||
| int KX_FontObject::pyattr_set_text(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) | int KX_FontObject::pyattr_set_text(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) | ||||
| { | { | ||||
| KX_FontObject* self = static_cast<KX_FontObject*>(self_v); | KX_FontObject *self = static_cast<KX_FontObject *>(self_v); | ||||
| if (!PyUnicode_Check(value)) | if (!PyUnicode_Check(value)) | ||||
| return PY_SET_ATTR_FAIL; | return PY_SET_ATTR_FAIL; | ||||
| char* chars = _PyUnicode_AsString(value); | char *chars = _PyUnicode_AsString(value); | ||||
| /* Allow for some logic brick control */ | /* Allow for some logic brick control */ | ||||
| CValue* tprop = self->GetProperty("Text"); | CValue *tprop = self->GetProperty("Text"); | ||||
| if (tprop) { | if (tprop) { | ||||
| CValue *newstringprop = new CStringValue(STR_String(chars), "Text"); | CValue *newstringprop = new CStringValue(std::string(chars), "Text"); | ||||
| self->SetProperty("Text", newstringprop); | self->SetProperty("Text", newstringprop); | ||||
| newstringprop->Release(); | newstringprop->Release(); | ||||
| } | } | ||||
| else { | else { | ||||
| self->m_text = split_string(STR_String(chars)); | self->m_text = split_string(std::string(chars)); | ||||
| } | } | ||||
| return PY_SET_ATTR_SUCCESS; | return PY_SET_ATTR_SUCCESS; | ||||
| } | } | ||||
| PyObject *KX_FontObject::pyattr_get_dimensions(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) | |||||
| { | |||||
| KX_FontObject *self = static_cast<KX_FontObject *>(self_v); | |||||
| return PyObjectFrom(self->GetTextDimensions()); | |||||
| } | |||||
| #endif // WITH_PYTHON | #endif // WITH_PYTHON | ||||
| Context not available. | |||||