Merge branch 'master' of https://github.com/supertuxkart/stk-code
This commit is contained in:
commit
bc7f89d49a
@ -18,9 +18,10 @@
|
||||
|
||||
#include "font/bold_face.hpp"
|
||||
|
||||
#include "font/face_ttf.hpp"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Constructor of BoldFace.
|
||||
* \param ttf \ref FaceTTF for BoldFace to use.
|
||||
*/
|
||||
BoldFace::BoldFace(FaceTTF* ttf) : FontWithFace("BoldFace", ttf)
|
||||
{
|
||||
} // BoldFace
|
||||
@ -53,3 +54,12 @@ void BoldFace::reset()
|
||||
insertCharacters(preload_chars.c_str());
|
||||
updateCharactersList();
|
||||
} // reset
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Embolden the glyph to make bold font using FT_Outline_Embolden.
|
||||
* \return A FT_Error value.
|
||||
*/
|
||||
int BoldFace::shapeOutline(FT_Outline* outline) const
|
||||
{
|
||||
return FT_Outline_Embolden(outline, getDPI() * 2);
|
||||
} // shapeOutline
|
||||
|
@ -23,24 +23,28 @@
|
||||
|
||||
class FaceTTF;
|
||||
|
||||
/** A font which uses regular TTFs to render title or important message in STK
|
||||
* with a bold outline, it shares the same \ref FaceTTF with \ref RegularFace.
|
||||
* \ingroup font
|
||||
*/
|
||||
class BoldFace : public FontWithFace
|
||||
{
|
||||
private:
|
||||
virtual bool supportLazyLoadChar() const OVERRIDE { return true; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual unsigned int getGlyphPageSize() const OVERRIDE { return 1024; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual float getScalingFactorOne() const OVERRIDE { return 0.2f; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual unsigned int getScalingFactorTwo() const OVERRIDE { return 120; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool isBold() const { return true; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual int shapeOutline(FT_Outline* outline) const OVERRIDE;
|
||||
|
||||
public:
|
||||
LEAK_CHECK()
|
||||
// ------------------------------------------------------------------------
|
||||
BoldFace(FaceTTF* ttf);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~BoldFace() {}
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void init() OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void reset() OVERRIDE;
|
||||
|
@ -18,9 +18,10 @@
|
||||
|
||||
#include "font/digit_face.hpp"
|
||||
|
||||
#include "font/face_ttf.hpp"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Constructor of DigitFace.
|
||||
* \param ttf \ref FaceTTF for DigitFace to use.
|
||||
*/
|
||||
DigitFace::DigitFace(FaceTTF* ttf) : FontWithFace("DigitFace", ttf)
|
||||
{
|
||||
} // DigitFace
|
||||
|
@ -23,6 +23,9 @@
|
||||
|
||||
class FaceTTF;
|
||||
|
||||
/** A font which uses a more cartonish style TTF to render big numbers in STK.
|
||||
* \ingroup font
|
||||
*/
|
||||
class DigitFace : public FontWithFace
|
||||
{
|
||||
private:
|
||||
@ -39,8 +42,6 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
DigitFace(FaceTTF* ttf);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~DigitFace() {}
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void init() OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void reset() OVERRIDE;
|
||||
|
@ -18,22 +18,29 @@
|
||||
|
||||
#include "font/face_ttf.hpp"
|
||||
|
||||
#include "font/font_manager.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Constructor. Load all TTFs from a list.
|
||||
* \param ttf_list List of TTFs to be loaded.
|
||||
*/
|
||||
FaceTTF::FaceTTF(const std::vector<std::string>& ttf_list)
|
||||
{
|
||||
for (const std::string& font : ttf_list)
|
||||
{
|
||||
FT_Face face = NULL;
|
||||
const std::string loc = file_manager
|
||||
->getAssetChecked(FileManager::TTF, font.c_str(), true);
|
||||
font_manager->checkFTError(FT_New_Face(font_manager->getFTLibrary(),
|
||||
(file_manager->getAssetChecked(FileManager::TTF,
|
||||
font.c_str(), true)).c_str(), 0, &face), "loading fonts");
|
||||
loc.c_str(), 0, &face), loc + " is loaded");
|
||||
m_faces.push_back(face);
|
||||
}
|
||||
} // FaceTTF
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Destructor. Clears all TTFs.
|
||||
*/
|
||||
FaceTTF::~FaceTTF()
|
||||
{
|
||||
for (unsigned int i = 0; i < m_faces.size(); i++)
|
||||
@ -42,6 +49,9 @@ FaceTTF::~FaceTTF()
|
||||
}
|
||||
} // ~FaceTTF
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Return a TTF in \ref m_faces.
|
||||
* \param i index of TTF file in \ref m_faces.
|
||||
*/
|
||||
FT_Face FaceTTF::getFace(unsigned int i) const
|
||||
{
|
||||
assert(i < m_faces.size());
|
||||
|
@ -19,15 +19,30 @@
|
||||
#ifndef HEADER_FACE_TTF_HPP
|
||||
#define HEADER_FACE_TTF_HPP
|
||||
|
||||
#include "font/font_manager.hpp"
|
||||
#include "utils/leak_check.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
/** This class will load a list of TTF files from \ref STKConfig, and save
|
||||
* them inside \ref m_faces for \ref FontWithFace to load glyph.
|
||||
* Each FaceTTF can be used more than once in each instantiation of \ref
|
||||
* FontWithFace, so it can render characters differently using the same TTF
|
||||
* file to save memory, for example different outline size.
|
||||
* \ingroup font
|
||||
*/
|
||||
class FaceTTF : public NoCopy
|
||||
{
|
||||
private:
|
||||
/** Contains all TTF files loaded. */
|
||||
std::vector<FT_Face> m_faces;
|
||||
|
||||
public:
|
||||
LEAK_CHECK();
|
||||
LEAK_CHECK()
|
||||
// ------------------------------------------------------------------------
|
||||
FaceTTF(const std::vector<std::string>& ttf_list);
|
||||
// ------------------------------------------------------------------------
|
||||
@ -35,6 +50,7 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
FT_Face getFace(unsigned int i) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return the total TTF files loaded. */
|
||||
unsigned int getTotalFaces() const { return m_faces.size(); }
|
||||
|
||||
}; // FaceTTF
|
||||
|
@ -28,15 +28,22 @@
|
||||
|
||||
FontManager *font_manager = NULL;
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Constructor. It will initialize the \ref m_ft_library.
|
||||
*/
|
||||
FontManager::FontManager()
|
||||
{
|
||||
checkFTError(FT_Init_FreeType(&m_ft_library), "loading freetype library");
|
||||
} // FontManager
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Destructor. Clears all fonts and related stuff.
|
||||
*/
|
||||
FontManager::~FontManager()
|
||||
{
|
||||
m_fonts.clearAndDeleteAll();
|
||||
for (unsigned int i = 0; i < m_fonts.size(); i++)
|
||||
delete m_fonts[i];
|
||||
m_fonts.clear();
|
||||
|
||||
delete m_normal_ttf;
|
||||
m_normal_ttf = NULL;
|
||||
delete m_digit_ttf;
|
||||
@ -47,6 +54,8 @@ FontManager::~FontManager()
|
||||
} // ~FontManager
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Initialize all \ref FaceTTF and \ref FontWithFace members.
|
||||
*/
|
||||
void FontManager::loadFonts()
|
||||
{
|
||||
// First load the TTF files required by each font
|
||||
@ -72,15 +81,10 @@ void FontManager::loadFonts()
|
||||
} // loadFonts
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void FontManager::checkFTError(FT_Error err, const std::string& desc) const
|
||||
{
|
||||
if (err > 0)
|
||||
{
|
||||
Log::error("FontManager", "Something wrong when %s!", desc.c_str());
|
||||
}
|
||||
} // checkFTError
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Unit testing that will try to load all translations in STK, and discover if
|
||||
* there is any characters required by it are not supported in \ref
|
||||
* m_normal_ttf.
|
||||
*/
|
||||
void FontManager::unitTesting()
|
||||
{
|
||||
std::vector<std::string> list = *(translations->getLanguageList());
|
||||
|
@ -19,14 +19,18 @@
|
||||
#ifndef HEADER_FONT_MANAGER_HPP
|
||||
#define HEADER_FONT_MANAGER_HPP
|
||||
|
||||
/** \defgroup font Font
|
||||
* This module stores font files and tools used to draw characters in STK.
|
||||
*/
|
||||
|
||||
#include "utils/leak_check.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/ptr_vector.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
@ -34,17 +38,26 @@
|
||||
class FaceTTF;
|
||||
class FontWithFace;
|
||||
|
||||
/** This class stores all font files required in STK.
|
||||
* \ingroup font
|
||||
*/
|
||||
class FontManager : public NoCopy
|
||||
{
|
||||
private:
|
||||
PtrVector<FontWithFace> m_fonts;
|
||||
/** Stores all \ref FontWithFace used in STK. */
|
||||
std::vector<FontWithFace*> m_fonts;
|
||||
|
||||
/** A FreeType library, it holds the FT_Face internally inside freetype. */
|
||||
FT_Library m_ft_library;
|
||||
|
||||
/** TTF files used in \ref BoldFace and \ref RegularFace. */
|
||||
FaceTTF* m_normal_ttf;
|
||||
|
||||
/** TTF files used in \ref DigitFace. */
|
||||
FaceTTF* m_digit_ttf;
|
||||
|
||||
/** Map type for each \ref FontWithFace with a index, save getting time in
|
||||
* \ref getFont. */
|
||||
std::unordered_map<std::type_index, int> m_font_type_map;
|
||||
|
||||
public:
|
||||
@ -54,11 +67,12 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
~FontManager();
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return a specfic type of \ref FontWithFace found in \ref m_fonts. */
|
||||
template <typename T> T* getFont()
|
||||
{
|
||||
T* out = NULL;
|
||||
const unsigned int n = m_font_type_map[std::type_index(typeid(T))];
|
||||
out = dynamic_cast<T*>(m_fonts.get(n));
|
||||
out = dynamic_cast<T*>(m_fonts[n]);
|
||||
if (out != NULL)
|
||||
{
|
||||
return out;
|
||||
@ -68,16 +82,23 @@ public:
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Check for any error discovered in a freetype function that will return
|
||||
* a FT_Error value.
|
||||
* a FT_Error value, and log into the terminal.
|
||||
* \param err The Freetype function.
|
||||
* \param desc The description of what is the function doing.
|
||||
*/
|
||||
void checkFTError(FT_Error err, const std::string& desc) const;
|
||||
* \param desc The description of what is the function doing. */
|
||||
void checkFTError(FT_Error err, const std::string& desc) const
|
||||
{
|
||||
if (err > 0)
|
||||
{
|
||||
Log::error("FontManager", "Something wrong when %s! The error "
|
||||
"code was %d.", desc.c_str(), err);
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void loadFonts();
|
||||
// ------------------------------------------------------------------------
|
||||
void unitTesting();
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return the \ref m_ft_library. */
|
||||
FT_Library getFTLibrary() const { return m_ft_library; }
|
||||
|
||||
}; // FontManager
|
||||
|
@ -25,21 +25,34 @@
|
||||
|
||||
using namespace irr;
|
||||
|
||||
/** This class stores settings when rendering fonts, used when instantiating
|
||||
* \ref irr::gui::ScalableFont.
|
||||
* \ingroup font
|
||||
*/
|
||||
class FontSettings
|
||||
{
|
||||
private:
|
||||
/** True if black border will be drawn when rendering. */
|
||||
bool m_black_border;
|
||||
|
||||
/** If true, characters will have right alignment when rendering, for RTL
|
||||
* language. */
|
||||
bool m_rtl;
|
||||
|
||||
/** Scaling when rendering. */
|
||||
float m_scale;
|
||||
|
||||
/** True if shadow will be drawn when rendering. */
|
||||
bool m_shadow;
|
||||
|
||||
/** Save the color of shadow when rendering. */
|
||||
video::SColor m_shadow_color;
|
||||
|
||||
public:
|
||||
LEAK_CHECK()
|
||||
// ------------------------------------------------------------------------
|
||||
/** Constructor. It will initialize all members with default values if no
|
||||
* parameter is given. */
|
||||
FontSettings(bool black_border = false, bool rtl = false,
|
||||
float scale = 1.0f, bool shadow = false,
|
||||
const video::SColor& color = video::SColor(0, 0, 0, 0))
|
||||
@ -51,26 +64,39 @@ public:
|
||||
m_shadow_color = color;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
~FontSettings() {}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Set the scaling.
|
||||
* \param scale Scaling to be set. */
|
||||
void setScale(float scale) { m_scale = scale; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return the scaling. */
|
||||
float getScale() const { return m_scale; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Set the color of shadow.
|
||||
* \param col The color of shadow to be set. */
|
||||
void setShadowColor(const video::SColor &col) { m_shadow_color = col; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return the color of shadow. */
|
||||
const video::SColor& getShadowColor() const { return m_shadow_color; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return if shadow is enabled. */
|
||||
bool useShadow() const { return m_shadow; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Set whether shadow is enabled.
|
||||
* \param shadow If it's enabled. */
|
||||
void setShadow(bool shadow) { m_shadow = shadow; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Set whether black border is enabled.
|
||||
* \param border If it's enabled. */
|
||||
void setBlackBorder(bool border) { m_black_border = border; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return if black border is enabled. */
|
||||
bool useBlackBorder() const { return m_black_border; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Set right text alignment for RTL language.
|
||||
* \param rtl If it's enabled. */
|
||||
void setRTL(bool rtl) { m_rtl = rtl; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return if right text alignment for RTL language is enabled. */
|
||||
bool isRTL() const { return m_rtl; }
|
||||
|
||||
}; // FontSettings
|
||||
|
@ -18,17 +18,20 @@
|
||||
|
||||
#include "font/font_with_face.hpp"
|
||||
|
||||
#include "font/bold_face.hpp"
|
||||
#include "font/face_ttf.hpp"
|
||||
#include "font/font_manager.hpp"
|
||||
#include "font/font_settings.hpp"
|
||||
#include "graphics/2dutils.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "guiengine/engine.hpp"
|
||||
#include "guiengine/skin.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
#include FT_OUTLINE_H
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Constructor. It will initialize the \ref m_spritebank and TTF files to use.
|
||||
* \param name The name of face, used by irrlicht to distinguish spritebank.
|
||||
* \param ttf \ref FaceTTF for this face to use.
|
||||
*/
|
||||
FontWithFace::FontWithFace(const std::string& name, FaceTTF* ttf)
|
||||
{
|
||||
m_spritebank = irr_driver->getGUI()->addEmptySpriteBank(name.c_str());
|
||||
@ -43,6 +46,8 @@ FontWithFace::FontWithFace(const std::string& name, FaceTTF* ttf)
|
||||
|
||||
} // FontWithFace
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Destructor. Clears the glyph page and sprite bank.
|
||||
*/
|
||||
FontWithFace::~FontWithFace()
|
||||
{
|
||||
m_page->drop();
|
||||
@ -56,6 +61,8 @@ FontWithFace::~FontWithFace()
|
||||
} // ~FontWithFace
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Initialize the font structure, but don't load glyph here.
|
||||
*/
|
||||
void FontWithFace::init()
|
||||
{
|
||||
setDPI();
|
||||
@ -83,7 +90,11 @@ void FontWithFace::init()
|
||||
|
||||
reset();
|
||||
} // init
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Clear all the loaded characters, sub-class can do pre-loading of characters
|
||||
* after this.
|
||||
*/
|
||||
void FontWithFace::reset()
|
||||
{
|
||||
m_new_char_holder.clear();
|
||||
@ -94,6 +105,12 @@ void FontWithFace::reset()
|
||||
} // reset
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Convert a character to a glyph index in one of the font in \ref m_face_ttf,
|
||||
* it will find the first TTF that supports this character, if the final
|
||||
* glyph_index is 0, this means such character is not supported by all TTFs in
|
||||
* \ref m_face_ttf.
|
||||
* \param c The character to be loaded.
|
||||
*/
|
||||
void FontWithFace::loadGlyphInfo(wchar_t c)
|
||||
{
|
||||
unsigned int font_number = 0;
|
||||
@ -108,11 +125,12 @@ void FontWithFace::loadGlyphInfo(wchar_t c)
|
||||
} // loadGlyphInfo
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Create a new glyph page by filling it with transparent content.
|
||||
*/
|
||||
void FontWithFace::createNewGlyphPage()
|
||||
{
|
||||
// Clean the current glyph page by filling it with transparent content
|
||||
m_page->fill(video::SColor(0, 255, 255, 255));
|
||||
m_temp_height = 0;
|
||||
m_current_height = 0;
|
||||
m_used_width = 0;
|
||||
m_used_height = 0;
|
||||
|
||||
@ -140,6 +158,10 @@ void FontWithFace::createNewGlyphPage()
|
||||
} // createNewGlyphPage
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Render a glyph for a character into bitmap and save it into the glyph page.
|
||||
* \param c The character to be loaded.
|
||||
* \param c \ref GlyphInfo for the character.
|
||||
*/
|
||||
void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
|
||||
{
|
||||
assert(gi.glyph_index > 0);
|
||||
@ -147,7 +169,7 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
|
||||
FT_Face cur_face = m_face_ttf->getFace(gi.font_number);
|
||||
FT_GlyphSlot slot = cur_face->glyph;
|
||||
|
||||
// Faces may be shared across regular and bold,
|
||||
// Same face may be shared across the different FontWithFace,
|
||||
// so reset dpi each time
|
||||
font_manager->checkFTError(FT_Set_Pixel_Sizes(cur_face, 0, getDPI()),
|
||||
"setting DPI");
|
||||
@ -155,15 +177,11 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
|
||||
font_manager->checkFTError(FT_Load_Glyph(cur_face, gi.glyph_index,
|
||||
FT_LOAD_DEFAULT), "loading a glyph");
|
||||
|
||||
if (dynamic_cast<BoldFace*>(this) != NULL)
|
||||
{
|
||||
// Embolden the outline of the glyph
|
||||
font_manager->checkFTError(FT_Outline_Embolden(&(slot->outline),
|
||||
getDPI() * 2), "embolden a glyph");
|
||||
}
|
||||
font_manager->checkFTError(shapeOutline(&(slot->outline)),
|
||||
"shaping outline");
|
||||
|
||||
font_manager->checkFTError(FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL),
|
||||
"render a glyph to bitmap");
|
||||
"rendering a glyph to bitmap");
|
||||
|
||||
// Convert to an anti-aliased bitmap
|
||||
FT_Bitmap bits = slot->bitmap;
|
||||
@ -176,7 +194,7 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
|
||||
true, 0);
|
||||
|
||||
if ((m_used_width + texture_size.Width > getGlyphPageSize() &&
|
||||
m_used_height + m_temp_height + texture_size.Height >
|
||||
m_used_height + m_current_height + texture_size.Height >
|
||||
getGlyphPageSize()) ||
|
||||
m_used_height + texture_size.Height > getGlyphPageSize())
|
||||
{
|
||||
@ -244,8 +262,8 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
|
||||
if (m_used_width + texture_size.Width > getGlyphPageSize())
|
||||
{
|
||||
m_used_width = 0;
|
||||
m_used_height += m_temp_height;
|
||||
m_temp_height = 0;
|
||||
m_used_height += m_current_height;
|
||||
m_current_height = 0;
|
||||
}
|
||||
|
||||
// Copy to the full glyph page
|
||||
@ -283,11 +301,13 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
|
||||
|
||||
// Store used area
|
||||
m_used_width += texture_size.Width;
|
||||
if (m_temp_height < texture_size.Height)
|
||||
m_temp_height = texture_size.Height;
|
||||
if (m_current_height < texture_size.Height)
|
||||
m_current_height = texture_size.Height;
|
||||
} // insertGlyph
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Update the supported characters for this font if required.
|
||||
*/
|
||||
void FontWithFace::updateCharactersList()
|
||||
{
|
||||
if (m_fallback_font != NULL)
|
||||
@ -321,6 +341,10 @@ void FontWithFace::updateCharactersList()
|
||||
} // updateCharactersList
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Write the current glyph page in png inside current running directory.
|
||||
* Mainly for debug use.
|
||||
* \param name The file name.
|
||||
*/
|
||||
void FontWithFace::dumpGlyphPage(const std::string& name)
|
||||
{
|
||||
for (unsigned int i = 0; i < m_spritebank->getTextureCount(); i++)
|
||||
@ -341,20 +365,23 @@ void FontWithFace::dumpGlyphPage(const std::string& name)
|
||||
} // dumpGlyphPage
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Write the current glyph page in png inside current running directory.
|
||||
* Useful in gdb without parameter.
|
||||
*/
|
||||
void FontWithFace::dumpGlyphPage()
|
||||
{
|
||||
dumpGlyphPage("face");
|
||||
} // dumpGlyphPage
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Set the face dpi which is resolution-dependent.
|
||||
* Normal text will range from 0.8, in 640x* resolutions (won't scale below
|
||||
* that) to 1.0, in 1024x* resolutions, and linearly up.
|
||||
* Bold text will range from 0.2, in 640x* resolutions (won't scale below
|
||||
* that) to 0.4, in 1024x* resolutions, and linearly up.
|
||||
*/
|
||||
void FontWithFace::setDPI()
|
||||
{
|
||||
// Set face dpi:
|
||||
// Font size is resolution-dependent.
|
||||
// Normal text will range from 0.8, in 640x* resolutions (won't scale
|
||||
// below that) to 1.0, in 1024x* resolutions, and linearly up
|
||||
// Bold text will range from 0.2, in 640x* resolutions (won't scale
|
||||
// below that) to 0.4, in 1024x* resolutions, and linearly up
|
||||
const int screen_width = irr_driver->getFrameSize().Width;
|
||||
const int screen_height = irr_driver->getFrameSize().Height;
|
||||
float scale = std::max(0, screen_width - 640) / 564.0f;
|
||||
@ -371,6 +398,10 @@ void FontWithFace::setDPI()
|
||||
} // setDPI
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Return the \ref FontArea about a character.
|
||||
* \param c The character to get.
|
||||
* \param[out] fallback_font Whether fallback font is used.
|
||||
*/
|
||||
const FontWithFace::FontArea&
|
||||
FontWithFace::getAreaFromCharacter(const wchar_t c,
|
||||
bool* fallback_font) const
|
||||
@ -397,6 +428,12 @@ const FontWithFace::FontArea&
|
||||
} // getAreaFromCharacter
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Get the dimension of text with support to different \ref FontSettings,
|
||||
* it will also do checking for missing characters in font and lazy load them.
|
||||
* \param text The text to be calculated.
|
||||
* \param font_settings \ref FontSettings to use.
|
||||
* \return The dimension of text
|
||||
*/
|
||||
core::dimension2d<u32> FontWithFace::getDimension(const wchar_t* text,
|
||||
FontSettings* font_settings)
|
||||
{
|
||||
@ -441,6 +478,12 @@ core::dimension2d<u32> FontWithFace::getDimension(const wchar_t* text,
|
||||
} // getDimension
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Calculate the index of the character in the text on a specific position.
|
||||
* \param text The text to be calculated.
|
||||
* \param pixel_x The specific position.
|
||||
* \param font_settings \ref FontSettings to use.
|
||||
* \return The index of the character, -1 means no character in such position.
|
||||
*/
|
||||
int FontWithFace::getCharacterFromPos(const wchar_t* text, int pixel_x,
|
||||
FontSettings* font_settings) const
|
||||
{
|
||||
@ -466,6 +509,17 @@ int FontWithFace::getCharacterFromPos(const wchar_t* text, int pixel_x,
|
||||
} // getCharacterFromPos
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Render text and clip it to the specified rectangle if wanted, it will also
|
||||
* do checking for missing characters in font and lazy load them.
|
||||
* \param text The text to be rendering.
|
||||
* \param position The position to be rendering.
|
||||
* \param color The color used when rendering.
|
||||
* \param hcenter If rendered horizontally center.
|
||||
* \param vcenter If rendered vertically center.
|
||||
* \param clip If clipping is needed.
|
||||
* \param font_settings \ref FontSettings to use.
|
||||
* \param char_collector \ref FontCharCollector to render billboard text.
|
||||
*/
|
||||
void FontWithFace::render(const core::stringw& text,
|
||||
const core::rect<s32>& position,
|
||||
const video::SColor& color, bool hcenter,
|
||||
@ -473,7 +527,6 @@ void FontWithFace::render(const core::stringw& text,
|
||||
FontSettings* font_settings,
|
||||
FontCharCollector* char_collector)
|
||||
{
|
||||
const bool is_bold_face = (dynamic_cast<BoldFace*>(this) != NULL);
|
||||
const bool black_border = font_settings ?
|
||||
font_settings->useBlackBorder() : false;
|
||||
const bool rtl = font_settings ? font_settings->isRTL() : false;
|
||||
@ -621,7 +674,7 @@ void FontWithFace::render(const core::stringw& text,
|
||||
|
||||
const int sprite_amount = sprites.size();
|
||||
|
||||
if ((black_border || is_bold_face) && char_collector == NULL)
|
||||
if ((black_border || isBold()) && char_collector == NULL)
|
||||
{
|
||||
// Draw black border first, to make it behind the real character
|
||||
// which make script language display better
|
||||
@ -693,7 +746,7 @@ void FontWithFace::render(const core::stringw& text,
|
||||
m_fallback_font->m_spritebank->getTexture(tex_id) :
|
||||
m_spritebank->getTexture(tex_id));
|
||||
|
||||
if (fallback[n] || is_bold_face)
|
||||
if (fallback[n] || isBold())
|
||||
{
|
||||
video::SColor top = GUIEngine::getSkin()->getColor("font::top");
|
||||
video::SColor bottom = GUIEngine::getSkin()
|
||||
|
@ -19,47 +19,84 @@
|
||||
#ifndef HEADER_FONT_WITH_FACE_HPP
|
||||
#define HEADER_FONT_WITH_FACE_HPP
|
||||
|
||||
#include "font/font_manager.hpp"
|
||||
#include "font/font_settings.hpp"
|
||||
#include "utils/cpp2011.hpp"
|
||||
#include "utils/leak_check.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_OUTLINE_H
|
||||
|
||||
#include <irrlicht.h>
|
||||
|
||||
using namespace irr;
|
||||
|
||||
const int BEARING = 64;
|
||||
|
||||
class FaceTTF;
|
||||
class FontSettings;
|
||||
|
||||
/** An abstract class which contains functions which convert vector fonts into
|
||||
* bitmap and render them in STK. To make STK draw characters with different
|
||||
* render option (like scaling, shadow) using a same FontWithFace, you need
|
||||
* to wrap this with \ref irr::gui::ScalableFont and configure the
|
||||
* \ref FontSettings for it.
|
||||
* \ingroup font
|
||||
*/
|
||||
class FontWithFace : public NoCopy
|
||||
{
|
||||
public:
|
||||
/** A class for \ref STKTextBillboard to get font info to render billboard
|
||||
* text. */
|
||||
class FontCharCollector
|
||||
{
|
||||
public:
|
||||
/** Collect the character info for billboard text.
|
||||
* \param texture The texture of the character.
|
||||
* \param destRect The destination rectangle
|
||||
* \param sourceRect The source rectangle in the glyph page
|
||||
* \param colors The color to render it. */
|
||||
virtual void collectChar(video::ITexture* texture,
|
||||
const core::rect<float>& destRect,
|
||||
const core::rect<s32>& sourceRect,
|
||||
const video::SColor* const colors) = 0;
|
||||
};
|
||||
|
||||
/** Glyph metrics for each glyph loaded. */
|
||||
struct FontArea
|
||||
{
|
||||
FontArea() : advance_x(0), bearing_x(0) ,offset_y(0), offset_y_bt(0),
|
||||
spriteno(0) {}
|
||||
/** Advance width for horizontal layout. */
|
||||
int advance_x;
|
||||
/** Left side bearing for horizontal layout. */
|
||||
int bearing_x;
|
||||
/** Top side bearing for horizontal layout. */
|
||||
int offset_y;
|
||||
/** Top side bearing for horizontal layout used in billboard text. */
|
||||
int offset_y_bt;
|
||||
/** Index number in sprite bank. */
|
||||
int spriteno;
|
||||
};
|
||||
|
||||
protected:
|
||||
int m_font_max_height;
|
||||
/** Used in vertical dimension calculation. */
|
||||
int m_font_max_height;
|
||||
|
||||
int m_glyph_max_height;
|
||||
/** Used in top side bearing calculation. */
|
||||
int m_glyph_max_height;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Check characters to see if they are loaded in font, if not load them.
|
||||
* For font that doesn't need lazy loading, nothing will be done.
|
||||
* \param in_ptr Characters to check.
|
||||
* \param first_load If true, it will ignore \ref supportLazyLoadChar,
|
||||
* which is called in \ref reset. */
|
||||
void insertCharacters(const wchar_t* in_ptr, bool first_load = false)
|
||||
{
|
||||
if (!supportLazyLoadChar() && !first_load) return;
|
||||
@ -88,44 +125,69 @@ protected:
|
||||
// ------------------------------------------------------------------------
|
||||
void updateCharactersList();
|
||||
// ------------------------------------------------------------------------
|
||||
/** Set the fallback font for this font, so if some character is missing in
|
||||
* this font, it will use that fallback font to try rendering it.
|
||||
* \param face A \ref FontWithFace font. */
|
||||
void setFallbackFont(FontWithFace* face) { m_fallback_font = face; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Set the scaling of fallback font.
|
||||
* \param scale The scaling to set. */
|
||||
void setFallbackFontScale(float scale) { m_fallback_font_scale = scale; }
|
||||
|
||||
private:
|
||||
/** Mapping of glyph index to a TTF in \ref FaceTTF. */
|
||||
struct GlyphInfo
|
||||
{
|
||||
GlyphInfo(unsigned int font_num = 0, unsigned int glyph_idx = 0) :
|
||||
font_number(font_num), glyph_index(glyph_idx) {}
|
||||
/** Index to a TTF in \ref FaceTTF. */
|
||||
unsigned int font_number;
|
||||
/** Glyph index in the TTF, 0 means no such glyph. */
|
||||
unsigned int glyph_index;
|
||||
GlyphInfo(unsigned int first = 0, unsigned int second = 0)
|
||||
{
|
||||
font_number = first;
|
||||
glyph_index = second;
|
||||
}
|
||||
};
|
||||
|
||||
/** \ref FaceTTF to load glyph from. */
|
||||
FaceTTF* m_face_ttf;
|
||||
|
||||
/** Fallback font to use if some character isn't supported by this font. */
|
||||
FontWithFace* m_fallback_font;
|
||||
|
||||
/** Scaling for fallback font. */
|
||||
float m_fallback_font_scale;
|
||||
|
||||
/** A temporary holder stored new char to be inserted. */
|
||||
/** A temporary holder to store new characters to be inserted. */
|
||||
std::set<wchar_t> m_new_char_holder;
|
||||
|
||||
/** Sprite bank to store each glyph. */
|
||||
gui::IGUISpriteBank* m_spritebank;
|
||||
|
||||
/** A full glyph page for this font. */
|
||||
video::IImage* m_page;
|
||||
|
||||
unsigned int m_temp_height;
|
||||
/** The current max height at current drawing line in glyph page. */
|
||||
unsigned int m_current_height;
|
||||
|
||||
/** The used width in glyph page. */
|
||||
unsigned int m_used_width;
|
||||
|
||||
/** The used height in glyph page. */
|
||||
unsigned int m_used_height;
|
||||
|
||||
/** The dpi of this font. */
|
||||
unsigned int m_face_dpi;
|
||||
|
||||
/** Store a list of supported character to a \ref FontArea. */
|
||||
std::map<wchar_t, FontArea> m_character_area_map;
|
||||
|
||||
/** Store a list of loaded and tested character to a \ref GlyphInfo. */
|
||||
std::map<wchar_t, GlyphInfo> m_character_glyph_info_map;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return a character width.
|
||||
* \param area \ref FontArea to get glyph metrics.
|
||||
* \param fallback If fallback font is used.
|
||||
* \param scale The scaling of the character.
|
||||
* \return The calculated width with suitable scaling. */
|
||||
float getCharWidth(const FontArea& area, bool fallback, float scale) const
|
||||
{
|
||||
if (fallback)
|
||||
@ -134,6 +196,9 @@ private:
|
||||
return area.advance_x * scale;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Test if a character has already been tried to be loaded.
|
||||
* \param c Character to test.
|
||||
* \return True if tested. */
|
||||
bool loadedChar(wchar_t c) const
|
||||
{
|
||||
std::map<wchar_t, GlyphInfo>::const_iterator n =
|
||||
@ -143,6 +208,10 @@ private:
|
||||
return false;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Get the \ref GlyphInfo from \ref m_character_glyph_info_map about a
|
||||
* character.
|
||||
* \param c Character to get.
|
||||
* \return \ref GlyphInfo of this character. */
|
||||
const GlyphInfo& getGlyphInfo(wchar_t c) const
|
||||
{
|
||||
std::map<wchar_t, GlyphInfo>::const_iterator n =
|
||||
@ -152,6 +221,10 @@ private:
|
||||
return n->second;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Tells whether a character is supported by all TTFs in \ref m_face_ttf
|
||||
* which is determined by \ref GlyphInfo of this character.
|
||||
* \param c Character to test.
|
||||
* \return True if it's supported. */
|
||||
bool supportChar(wchar_t c)
|
||||
{
|
||||
std::map<wchar_t, GlyphInfo>::const_iterator n =
|
||||
@ -167,22 +240,36 @@ private:
|
||||
// ------------------------------------------------------------------------
|
||||
void createNewGlyphPage();
|
||||
// ------------------------------------------------------------------------
|
||||
/** Add a character into \ref m_new_char_holder for lazy loading later. */
|
||||
void addLazyLoadChar(wchar_t c) { m_new_char_holder.insert(c); }
|
||||
// ------------------------------------------------------------------------
|
||||
void insertGlyph(wchar_t c, const GlyphInfo& gi);
|
||||
// ------------------------------------------------------------------------
|
||||
void setDPI();
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool supportLazyLoadChar() const = 0;
|
||||
/** Override it if sub-class should not do lazy loading characters. */
|
||||
virtual bool supportLazyLoadChar() const { return true; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Defined by sub-class about the texture size of glyph page, it should be
|
||||
* a power of two. */
|
||||
virtual unsigned int getGlyphPageSize() const = 0;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Defined by sub-class about the scaling factor 1. */
|
||||
virtual float getScalingFactorOne() const = 0;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Defined by sub-class about the scaling factor 2. */
|
||||
virtual unsigned int getScalingFactorTwo() const = 0;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Override it if sub-class has bold outline. */
|
||||
virtual bool isBold() const { return false; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Override it if any outline shaping is needed to be done before
|
||||
* rendering the glyph into bitmap.
|
||||
* \return A FT_Error value if needed. */
|
||||
virtual int shapeOutline(FT_Outline* outline) const { return 0; }
|
||||
|
||||
public:
|
||||
LEAK_CHECK();
|
||||
LEAK_CHECK()
|
||||
// ------------------------------------------------------------------------
|
||||
FontWithFace(const std::string& name, FaceTTF* ttf);
|
||||
// ------------------------------------------------------------------------
|
||||
@ -204,22 +291,17 @@ public:
|
||||
FontSettings* font_settings,
|
||||
FontCharCollector* char_collector = NULL);
|
||||
// ------------------------------------------------------------------------
|
||||
/** Write the current glyph page in png inside current running directory.
|
||||
* Mainly for debug use.
|
||||
* \param name The file name.
|
||||
*/
|
||||
void dumpGlyphPage(const std::string& name);
|
||||
// ------------------------------------------------------------------------
|
||||
/** Write the current glyph page in png inside current running directory.
|
||||
* Useful in gdb without parameter.
|
||||
*/
|
||||
void dumpGlyphPage();
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return the sprite bank. */
|
||||
gui::IGUISpriteBank* getSpriteBank() const { return m_spritebank; }
|
||||
// ------------------------------------------------------------------------
|
||||
const FontArea& getAreaFromCharacter(const wchar_t c,
|
||||
bool* fallback_font) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return the dpi of this face. */
|
||||
unsigned int getDPI() const { return m_face_dpi; }
|
||||
|
||||
}; // FontWithFace
|
||||
|
@ -16,11 +16,12 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "font/face_ttf.hpp"
|
||||
|
||||
#include "font/regular_face.hpp"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Constructor of RegularFace.
|
||||
* \param ttf \ref FaceTTF for RegularFace to use.
|
||||
*/
|
||||
RegularFace::RegularFace(FaceTTF* ttf) : FontWithFace("RegularFace", ttf)
|
||||
{
|
||||
} // RegularFace
|
||||
|
@ -23,11 +23,12 @@
|
||||
|
||||
class FaceTTF;
|
||||
|
||||
/** A font which uses regular TTFs to render most text in STK.
|
||||
* \ingroup font
|
||||
*/
|
||||
class RegularFace : public FontWithFace
|
||||
{
|
||||
private:
|
||||
virtual bool supportLazyLoadChar() const OVERRIDE { return true; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual unsigned int getGlyphPageSize() const OVERRIDE { return 512; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual float getScalingFactorOne() const OVERRIDE { return 0.7f; }
|
||||
@ -39,8 +40,6 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
RegularFace(FaceTTF* ttf);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~RegularFace() {}
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void init() OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void reset() OVERRIDE;
|
||||
|
@ -50,14 +50,28 @@ STKTextBillboard::STKTextBillboard(core::stringw text, FontWithFace* font,
|
||||
|
||||
void STKTextBillboard::updateAbsolutePosition()
|
||||
{
|
||||
// Make billboard always face the camera
|
||||
scene::ICameraSceneNode* curr_cam =
|
||||
irr_driver->getSceneManager()->getActiveCamera();
|
||||
if (!curr_cam) return;
|
||||
core::quaternion q(curr_cam->getViewMatrix());
|
||||
q.W = -q.W;
|
||||
|
||||
if (Parent)
|
||||
{
|
||||
// Override to not use the parent's rotation
|
||||
AbsoluteTransformation = getRelativeTransformation();
|
||||
AbsoluteTransformation.setTranslation(AbsoluteTransformation.getTranslation() + Parent->getAbsolutePosition());
|
||||
core::vector3df wc = RelativeTranslation;
|
||||
Parent->getAbsoluteTransformation().transformVect(wc);
|
||||
AbsoluteTransformation.setTranslation(wc);
|
||||
q.getMatrix(AbsoluteTransformation, wc);
|
||||
}
|
||||
else
|
||||
AbsoluteTransformation = getRelativeTransformation();
|
||||
{
|
||||
q.getMatrix(AbsoluteTransformation, RelativeTranslation);
|
||||
}
|
||||
core::matrix4 m;
|
||||
m.setScale(RelativeScale);
|
||||
AbsoluteTransformation *= m;
|
||||
}
|
||||
|
||||
scene::IMesh* STKTextBillboard::getTextMesh(core::stringw text, FontWithFace* font)
|
||||
@ -171,18 +185,6 @@ scene::IMesh* STKTextBillboard::getTextMesh(core::stringw text, FontWithFace* fo
|
||||
return Mesh;
|
||||
}
|
||||
|
||||
void STKTextBillboard::updateNoGL()
|
||||
{
|
||||
scene::ICameraSceneNode* curr_cam = irr_driver->getSceneManager()->getActiveCamera();
|
||||
core::vector3df cam_pos = curr_cam->getPosition();
|
||||
core::vector3df text_pos = this->getAbsolutePosition();
|
||||
float angle = atan2(text_pos.X - cam_pos.X, text_pos.Z - cam_pos.Z);
|
||||
this->setRotation(core::vector3df(0.0f, angle * 180.0f / M_PI, 0.0f));
|
||||
updateAbsolutePosition();
|
||||
|
||||
STKMeshSceneNode::updateNoGL();
|
||||
}
|
||||
|
||||
void STKTextBillboard::collectChar(video::ITexture* texture,
|
||||
const core::rect<float>& destRect,
|
||||
const core::rect<s32>& sourceRect,
|
||||
|
@ -74,8 +74,6 @@ public:
|
||||
const irr::core::vector3df& position,
|
||||
const irr::core::vector3df& size);
|
||||
|
||||
virtual void updateNoGL() OVERRIDE;
|
||||
|
||||
virtual scene::ESCENE_NODE_TYPE getType() const OVERRIDE
|
||||
{
|
||||
return scene::ESNT_TEXT;
|
||||
|
@ -659,6 +659,8 @@ namespace GUIEngine
|
||||
#include "config/user_config.hpp"
|
||||
#include "font/bold_face.hpp"
|
||||
#include "font/digit_face.hpp"
|
||||
#include "font/font_manager.hpp"
|
||||
#include "font/font_settings.hpp"
|
||||
#include "font/regular_face.hpp"
|
||||
#include "input/input_manager.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "guiengine/scalable_font.hpp"
|
||||
|
||||
#include "font/font_settings.hpp"
|
||||
#include "font/font_with_face.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "font/font_manager.hpp"
|
||||
#include "font/regular_face.hpp"
|
||||
#include "guiengine/engine.hpp"
|
||||
#include "guiengine/scalable_font.hpp"
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "config/player_manager.hpp"
|
||||
#include "config/user_config.hpp"
|
||||
#include "font/bold_face.hpp"
|
||||
#include "font/font_manager.hpp"
|
||||
#include "graphics/camera.hpp"
|
||||
#include "graphics/central_settings.hpp"
|
||||
#include "graphics/explosion.hpp"
|
||||
|
61
src/main.cpp
61
src/main.cpp
@ -31,26 +31,54 @@
|
||||
* Here is an overview of the high-level interactions between modules :
|
||||
\dot
|
||||
digraph interaction {
|
||||
race -> modes
|
||||
# race -> modes
|
||||
race -> tracks
|
||||
race -> karts
|
||||
modes -> tracks
|
||||
modes -> karts
|
||||
# modes -> tracks
|
||||
# modes -> karts
|
||||
tracks -> graphics
|
||||
karts -> graphics
|
||||
tracks -> items
|
||||
graphics -> irrlicht
|
||||
guiengine -> irrlicht
|
||||
states_screens -> guiengine
|
||||
states_screens -> input
|
||||
guiengine -> input
|
||||
karts->physics
|
||||
tracks->physics
|
||||
karts -> controller
|
||||
input->controller
|
||||
items -> graphics
|
||||
animations -> graphics
|
||||
graphics -> "Antarctica/irrlicht"
|
||||
# guiengine -> irrlicht
|
||||
# states_screens -> guiengine
|
||||
# input -> states_screens
|
||||
input -> guiengine
|
||||
guiengine -> font_system
|
||||
karts -> physics
|
||||
physics -> karts
|
||||
tracks -> physics
|
||||
ai -> controller
|
||||
controller -> karts
|
||||
input -> controller
|
||||
tracks -> animations
|
||||
physics -> animations
|
||||
}
|
||||
animations -> physics
|
||||
karts -> audio
|
||||
physics -> audio
|
||||
"translations\n(too many connections\nto draw)"
|
||||
"configuration\n(too many connections\nto draw)"
|
||||
# addons -> tracks
|
||||
# addons -> karts
|
||||
guiengine -> addons
|
||||
guiengine -> race
|
||||
addons -> online_manager
|
||||
challenges -> race
|
||||
# challenges -> modes
|
||||
guiengine -> challenges
|
||||
online_manager -> addons
|
||||
online_manager -> "STK Server"
|
||||
"STK Server" -> online_manager
|
||||
karts -> replay
|
||||
replay
|
||||
# force karts and tracks on the same level, looks better this way
|
||||
subgraph {
|
||||
rank = same; karts; tracks;
|
||||
}
|
||||
|
||||
}
|
||||
\enddot
|
||||
|
||||
Note that this graph is only an approximation because the real one would be
|
||||
@ -73,6 +101,8 @@
|
||||
This module handles the user configuration, the supertuxkart configuration
|
||||
file (which contains options usually not edited by the player) and the input
|
||||
configuration file.
|
||||
\li \ref font :
|
||||
This module stores font files and tools used to draw characters in STK.
|
||||
\li \ref graphics :
|
||||
This module contains the core graphics engine, that is mostly a thin layer
|
||||
on top of irrlicht providing some additional features we need for STK
|
||||
@ -182,6 +212,7 @@
|
||||
#include "network/rewind_manager.hpp"
|
||||
#include "network/servers_manager.hpp"
|
||||
#include "network/stk_host.hpp"
|
||||
#include "network/protocols/get_public_address.hpp"
|
||||
#include "online/profile_manager.hpp"
|
||||
#include "online/request_manager.hpp"
|
||||
#include "race/grand_prix_manager.hpp"
|
||||
@ -546,6 +577,7 @@ void cmdLineHelp()
|
||||
" --login=s Automatically log in (set the login).\n"
|
||||
" --password=s Automatically log in (set the password).\n"
|
||||
" --port=n Port number to use.\n"
|
||||
" --my-address=1.1.1.1:1 Own IP address (can replace stun protocol)\n"
|
||||
" --max-players=n Maximum number of clients (server only).\n"
|
||||
" --no-console Does not write messages in the console but to\n"
|
||||
" stdout.log.\n"
|
||||
@ -981,6 +1013,9 @@ int handleCmdLine()
|
||||
if(CommandLine::has("--password", &s))
|
||||
password = s.c_str();
|
||||
|
||||
if (CommandLine::has("--my-address", &s))
|
||||
GetPublicAddress::setMyIPAddress(s);
|
||||
|
||||
// Race parameters
|
||||
if(CommandLine::has("--kartsize-debug"))
|
||||
{
|
||||
|
@ -30,17 +30,19 @@ NetworkConfig *NetworkConfig::m_network_config = NULL;
|
||||
* instance.
|
||||
*/
|
||||
// ============================================================================
|
||||
/** Constructor for a client
|
||||
/** Constructor.
|
||||
*/
|
||||
NetworkConfig::NetworkConfig()
|
||||
{
|
||||
m_network_type = NETWORK_NONE;
|
||||
m_is_server = false;
|
||||
m_max_players = 4;
|
||||
m_is_registered = false;
|
||||
m_server_name = "";
|
||||
m_password = "";
|
||||
m_private_port = 0;
|
||||
m_network_type = NETWORK_NONE;
|
||||
m_is_server = false;
|
||||
m_max_players = 4;
|
||||
m_is_registered = false;
|
||||
m_server_name = "";
|
||||
m_password = "";
|
||||
m_server_discovery_port = 2757;
|
||||
m_server_port = 2758;
|
||||
m_client_port = 2759;
|
||||
m_my_address.lock();
|
||||
m_my_address.getData().clear();
|
||||
m_my_address.unlock();
|
||||
@ -56,3 +58,11 @@ void NetworkConfig::setMyAddress(const TransportAddress& addr)
|
||||
m_my_address.unlock();
|
||||
} // setPublicAddress
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/** Sets if this instance is a server or client. It also assigns the
|
||||
* private port depending if this is a server or client.
|
||||
*/
|
||||
void NetworkConfig::setIsServer(bool b)
|
||||
{
|
||||
m_is_server = b;
|
||||
} // setIsServer
|
||||
|
@ -50,10 +50,14 @@ private:
|
||||
* be updated from a separate thread. */
|
||||
Synchronised<TransportAddress> m_my_address;
|
||||
|
||||
/** Even if this is a WAN server, we also store the private (LAN)
|
||||
* port number, to allow direct connection to clients on the same
|
||||
* LAN. */
|
||||
uint16_t m_private_port;
|
||||
/** The port number to which the server listens to detect LAN requests. */
|
||||
uint16_t m_server_discovery_port;
|
||||
|
||||
/** The port on which the server listens for connection requests from LAN. */
|
||||
uint16_t m_server_port;
|
||||
|
||||
/** The LAN port on which a client is waiting for a server connection. */
|
||||
uint16_t m_client_port;
|
||||
|
||||
/** Maximum number of players on the server. */
|
||||
int m_max_players;
|
||||
@ -85,7 +89,28 @@ public:
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void setMyAddress(const TransportAddress& addr);
|
||||
|
||||
void setIsServer(bool b);
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the port for server discovery. */
|
||||
void setServerDiscoveryPort(uint16_t port)
|
||||
{
|
||||
m_server_discovery_port = port;
|
||||
} // setServerDiscoveryPort
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the port on which this server listens. */
|
||||
void setServerPort(uint16_t port) { m_server_port = port; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the port on which a client listens for server connection. */
|
||||
void setClientPort(uint16_t port) { m_client_port = port; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the port on which this server listenes. */
|
||||
uint16_t getServerPort() const { return m_server_port; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the port for LAN server discovery. */
|
||||
uint16_t getServerDiscoveryPort() const { return m_server_discovery_port; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the port on which a client listens for server connections. */
|
||||
uint16_t getClientPort() const { return m_client_port; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the password for a server. */
|
||||
void setPassword(const std::string &password) { m_password = password; }
|
||||
@ -118,9 +143,6 @@ public:
|
||||
/** Returns the maximum number of players for this server. */
|
||||
int getMaxPlayers() const { return m_max_players; }
|
||||
// --------------------------------------------------------------------
|
||||
/** Sets if this instance is a server or client. */
|
||||
void setIsServer(bool b) { m_is_server = b; }
|
||||
// --------------------------------------------------------------------
|
||||
/** Returns if this instance is a server. */
|
||||
bool isServer() const { return m_is_server; }
|
||||
// --------------------------------------------------------------------
|
||||
@ -158,6 +180,7 @@ public:
|
||||
/** Returns the IP address of this host. We need to return a copy
|
||||
* to make sure the address is thread safe (otherwise it could happen
|
||||
* that e.g. data is taken when the IP address was written, but not
|
||||
return a;
|
||||
* yet the port). */
|
||||
const TransportAddress getMyAddress() const
|
||||
{
|
||||
@ -167,12 +190,6 @@ public:
|
||||
m_my_address.unlock();
|
||||
return a;
|
||||
} // getMyAddress
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the private (LAN) port for this instance. */
|
||||
void setPrivatePort(uint16_t port) { m_private_port = port; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the private (LAN) port. */
|
||||
uint16_t getPrivatePort() const { return m_private_port; }
|
||||
|
||||
}; // class NetworkConfig
|
||||
|
||||
|
@ -71,6 +71,8 @@ ConnectToPeer::~ConnectToPeer()
|
||||
|
||||
void ConnectToPeer::setup()
|
||||
{
|
||||
m_broadcast_count = 0;
|
||||
m_time_last_broadcast = 0;
|
||||
} // setup
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@ -125,14 +127,40 @@ void ConnectToPeer::asynchronousUpdate()
|
||||
// the server itself accepts connections from anywhere.
|
||||
if (!m_is_lan &&
|
||||
m_peer_address.getIP() != NetworkConfig::get()
|
||||
->getMyAddress().getIP())
|
||||
->getMyAddress().getIP())
|
||||
{
|
||||
m_current_protocol = new PingProtocol(m_peer_address,
|
||||
/*time-between-ping*/2.0);
|
||||
ProtocolManager::getInstance()->requestStart(m_current_protocol);
|
||||
m_state = CONNECTING;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_broadcast_count = 0;
|
||||
// Make sure we trigger the broadcast operation next
|
||||
m_time_last_broadcast = float(StkTime::getRealTime()-100.0f);
|
||||
m_state = WAIT_FOR_LAN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WAIT_FOR_LAN:
|
||||
{
|
||||
// Broadcast once per second
|
||||
if (StkTime::getRealTime() < m_time_last_broadcast + 1.0f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_time_last_broadcast = float(StkTime::getRealTime());
|
||||
m_broadcast_count++;
|
||||
if (m_broadcast_count > 100)
|
||||
{
|
||||
// Not much we can do about if we don't receive the client
|
||||
// connection - it could have stopped, lost network, ...
|
||||
// Terminate this protocol.
|
||||
Log::error("ConnectToPeer", "Time out trying to connect to %s",
|
||||
m_peer_address.toString().c_str());
|
||||
requestTerminate();
|
||||
}
|
||||
|
||||
// Otherwise we are in the same LAN (same public ip address).
|
||||
// Just send a broadcast packet with the string aloha_stk inside,
|
||||
@ -146,6 +174,9 @@ void ConnectToPeer::asynchronousUpdate()
|
||||
else
|
||||
broadcast_address.copy(m_peer_address);
|
||||
|
||||
|
||||
broadcast_address.copy(m_peer_address);
|
||||
|
||||
BareNetworkString aloha(std::string("aloha_stk"));
|
||||
STKHost::get()->sendRawPacket(aloha, broadcast_address);
|
||||
Log::info("ConnectToPeer", "Broadcast aloha sent.");
|
||||
@ -155,10 +186,12 @@ void ConnectToPeer::asynchronousUpdate()
|
||||
broadcast_address.setPort(m_peer_address.getPort());
|
||||
STKHost::get()->sendRawPacket(aloha, broadcast_address);
|
||||
Log::info("ConnectToPeer", "Broadcast aloha to self.");
|
||||
m_state = CONNECTING;
|
||||
break;
|
||||
}
|
||||
case CONNECTING: // waiting for the peer to connect
|
||||
// If we receive a 'connected' event from enet, our
|
||||
// notifyEventAsynchronous is called, which will move
|
||||
// the FSM to the next state CONNECTED
|
||||
break;
|
||||
case CONNECTED:
|
||||
{
|
||||
|
@ -39,10 +39,17 @@ protected:
|
||||
/** True if this is a LAN connection. */
|
||||
bool m_is_lan;
|
||||
|
||||
/** We might need to broadcast several times (in case the client is not
|
||||
* ready in time). This keep track of broadcastst. */
|
||||
float m_time_last_broadcast;
|
||||
|
||||
int m_broadcast_count;
|
||||
|
||||
enum STATE
|
||||
{
|
||||
NONE,
|
||||
RECEIVED_PEER_ADDRESS,
|
||||
WAIT_FOR_LAN,
|
||||
CONNECTING,
|
||||
CONNECTED,
|
||||
DONE,
|
||||
|
@ -132,14 +132,7 @@ void ConnectToServer::asynchronousUpdate()
|
||||
}
|
||||
else
|
||||
{
|
||||
// No quick connect, so we have a server to connect to.
|
||||
// Find its address
|
||||
m_current_protocol = new GetPeerAddress(m_host_id, this);
|
||||
m_current_protocol->requestStart();
|
||||
m_state = GOT_SERVER_ADDRESS;
|
||||
// Pause this protocol till GetPeerAddress finishes.
|
||||
// The callback then will unpause this protocol/
|
||||
requestPause();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -270,11 +263,6 @@ void ConnectToServer::callback(Protocol *protocol)
|
||||
// STKHost, so we only need to unpause this protocol
|
||||
requestUnpause();
|
||||
break;
|
||||
case GOT_SERVER_ADDRESS:
|
||||
// Get the server address from the protocol.
|
||||
m_server_address.copy(((GetPeerAddress*)protocol)->getAddress());
|
||||
requestUnpause();
|
||||
break;
|
||||
default:
|
||||
Log::error("ConnectToServer",
|
||||
"Received unexpected callback while in state %d.",
|
||||
@ -296,7 +284,7 @@ void ConnectToServer::registerWithSTKServer()
|
||||
request->addParameter("address", addr.getIP());
|
||||
request->addParameter("port", addr.getPort());
|
||||
request->addParameter("private_port",
|
||||
NetworkConfig::get()->getPrivatePort());
|
||||
NetworkConfig::get()->getClientPort());
|
||||
|
||||
Log::info("ConnectToServer", "Registering addr %s",
|
||||
addr.toString().c_str());
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "network/network_string.hpp"
|
||||
#include "network/protocols/connect_to_server.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
@ -43,8 +44,35 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
// make the linker happy
|
||||
const uint32_t GetPublicAddress::m_stun_magic_cookie = 0x2112A442;
|
||||
const uint32_t GetPublicAddress::m_stun_magic_cookie = 0x2112A442;
|
||||
TransportAddress GetPublicAddress::m_my_address(0, 0);
|
||||
|
||||
void GetPublicAddress::setMyIPAddress(const std::string &s)
|
||||
{
|
||||
std::vector<std::string> l = StringUtils::split(s, ':');
|
||||
if (l.size() != 2)
|
||||
{
|
||||
Log::fatal("Invalid IP address '%s'.", s.c_str());
|
||||
}
|
||||
std::vector<std::string> ip = StringUtils::split(l[0], '.');
|
||||
if (ip.size() != 4)
|
||||
{
|
||||
Log::fatal("Invalid IP address '%s'.", s.c_str());
|
||||
}
|
||||
uint32_t u = 0;
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
{
|
||||
int k;
|
||||
StringUtils::fromString(ip[i], k);
|
||||
u = (u << 8) + k;
|
||||
}
|
||||
m_my_address.setIP(u);
|
||||
int p;
|
||||
StringUtils::fromString(l[1], p);
|
||||
m_my_address.setPort(p);
|
||||
} // setMyIPAddress
|
||||
|
||||
// ============================================================================
|
||||
GetPublicAddress::GetPublicAddress(CallbackObject *callback)
|
||||
: Protocol(PROTOCOL_SILENT, callback)
|
||||
{
|
||||
@ -203,6 +231,22 @@ std::string GetPublicAddress::parseStunResponse()
|
||||
* selected STUN server and then parsing and validating the response */
|
||||
void GetPublicAddress::asynchronousUpdate()
|
||||
{
|
||||
// If the user has specified an address, use it instead of the stun protocol.
|
||||
if (m_my_address.getIP() != 0 && m_my_address.getPort() != 0)
|
||||
{
|
||||
NetworkConfig::get()->setMyAddress(m_my_address);
|
||||
m_state = EXITING;
|
||||
requestTerminate();
|
||||
}
|
||||
//#define LAN_TEST
|
||||
#ifdef LAN_TEST
|
||||
TransportAddress address(0x7f000001, 4);
|
||||
NetworkConfig::get()->setMyAddress(address);
|
||||
m_state = EXITING;
|
||||
requestTerminate();
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (m_state == NOTHING_DONE)
|
||||
{
|
||||
createStunRequest();
|
||||
|
@ -20,6 +20,7 @@
|
||||
#define GET_PUBLIC_ADDRESS_HPP
|
||||
|
||||
#include "network/protocol.hpp"
|
||||
#include "network/transport_address.hpp"
|
||||
#include "utils/cpp2011.hpp"
|
||||
|
||||
#include <string>
|
||||
@ -36,6 +37,10 @@ private:
|
||||
static const uint32_t m_stun_magic_cookie;
|
||||
static const int m_stun_server_port = 3478;
|
||||
|
||||
/** The user can specify its own IP address to make the use of stun
|
||||
* unnecessary (though that means that the user has to take care of
|
||||
* opening the firewall). */
|
||||
static TransportAddress m_my_address;
|
||||
enum State
|
||||
{
|
||||
NOTHING_DONE,
|
||||
@ -48,8 +53,9 @@ private:
|
||||
Network* m_transaction_host;
|
||||
|
||||
public:
|
||||
GetPublicAddress(CallbackObject *callback = NULL);
|
||||
virtual ~GetPublicAddress() {}
|
||||
static void setMyIPAddress(const std::string &s);
|
||||
GetPublicAddress(CallbackObject *callback = NULL);
|
||||
virtual ~GetPublicAddress() {}
|
||||
|
||||
virtual void asynchronousUpdate() OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
@ -60,6 +66,7 @@ public:
|
||||
virtual bool notifyEventAsynchronous(Event* event) OVERRIDE { return true; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void setup() { m_state = NOTHING_DONE; }
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
}; // class GetPublicAddress
|
||||
|
||||
|
@ -210,7 +210,7 @@ void ServerLobbyRoomProtocol::registerServer()
|
||||
request->addParameter("address", addr.getIP() );
|
||||
request->addParameter("port", addr.getPort() );
|
||||
request->addParameter("private_port",
|
||||
NetworkConfig::get()->getPrivatePort());
|
||||
NetworkConfig::get()->getServerPort() );
|
||||
request->addParameter("name", NetworkConfig::get()->getServerName() );
|
||||
request->addParameter("max_players",
|
||||
UserConfigParams::m_server_max_players );
|
||||
|
@ -49,7 +49,13 @@ Server::Server(const XMLNode & xml, bool is_lan)
|
||||
xml.get("hostid", &m_host_id);
|
||||
xml.get("max_players", &m_max_players);
|
||||
xml.get("current_players", &m_current_players);
|
||||
|
||||
uint32_t ip;
|
||||
xml.get("ip", &ip);
|
||||
m_address.setIP(ip);
|
||||
uint16_t port;
|
||||
xml.get("port", &port);
|
||||
m_address.setPort(port);
|
||||
xml.get("private_port", &m_private_port);
|
||||
} // Server(const XML&)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -153,7 +153,8 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const
|
||||
Network *broadcast = new Network(1, 1, 0, 0);
|
||||
|
||||
BareNetworkString s(std::string("stk-server"));
|
||||
TransportAddress broadcast_address(-1, 2757);
|
||||
TransportAddress broadcast_address(-1,
|
||||
NetworkConfig::get()->getServerDiscoveryPort());
|
||||
broadcast->sendRawPacket(s, broadcast_address);
|
||||
|
||||
Log::info("ServersManager", "Sent broadcast message.");
|
||||
|
@ -141,7 +141,7 @@ void STKHost::create()
|
||||
*
|
||||
* Server:
|
||||
*
|
||||
* The ServerLobbyRoomProtocol (SLR) will the detect the above client
|
||||
* The ServerLobbyRoomProtocol (SLR) will then detect the above client
|
||||
* requests, and start a ConnectToPeer protocol for each incoming client.
|
||||
* The ConnectToPeer protocol uses:
|
||||
* 1. GetPeerAddress to get the ip address and port of the client.
|
||||
@ -231,9 +231,13 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id)
|
||||
// server is made.
|
||||
m_host_id = 0;
|
||||
init();
|
||||
TransportAddress a;
|
||||
a.setIP(0);
|
||||
a.setPort(NetworkConfig::get()->getClientPort());
|
||||
ENetAddress ea = a.toEnetAddress();
|
||||
|
||||
m_network = new Network(/*peer_count*/1, /*channel_limit*/2,
|
||||
/*max_in_bandwidth*/0, /*max_out_bandwidth*/0);
|
||||
/*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &ea);
|
||||
if (!m_network)
|
||||
{
|
||||
Log::fatal ("STKHost", "An error occurred while trying to create "
|
||||
@ -258,7 +262,7 @@ STKHost::STKHost(const irr::core::stringw &server_name)
|
||||
|
||||
ENetAddress addr;
|
||||
addr.host = STKHost::HOST_ANY;
|
||||
addr.port = 2758;
|
||||
addr.port = NetworkConfig::get()->getServerPort();
|
||||
|
||||
m_network= new Network(NetworkConfig::get()->getMaxPlayers(),
|
||||
/*channel_limit*/2,
|
||||
@ -511,7 +515,7 @@ void* STKHost::mainLoop(void* self)
|
||||
if(NetworkConfig::get()->isServer() &&
|
||||
NetworkConfig::get()->isLAN() )
|
||||
{
|
||||
TransportAddress address(0, 2757);
|
||||
TransportAddress address(0, NetworkConfig::get()->getServerDiscoveryPort());
|
||||
ENetAddress eaddr = address.toEnetAddress();
|
||||
myself->m_lan_network = new Network(1, 1, 0, 0, &eaddr);
|
||||
}
|
||||
@ -565,6 +569,10 @@ void* STKHost::mainLoop(void* self)
|
||||
} // mainLoop
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Handles LAN related messages. It checks for any LAN broadcast messages,
|
||||
* and if a valid LAN server-request message is received, will answer
|
||||
* with a message containing server details (and sender IP address and port).
|
||||
*/
|
||||
void STKHost::handleLANRequests()
|
||||
{
|
||||
const int LEN=2048;
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "animations/three_d_animation.hpp"
|
||||
#include "font/digit_face.hpp"
|
||||
#include "font/font_manager.hpp"
|
||||
#include "graphics/central_settings.hpp"
|
||||
#include "graphics/stk_text_billboard.hpp"
|
||||
#include "guiengine/scalable_font.hpp"
|
||||
@ -104,6 +105,7 @@ namespace Scripting
|
||||
core::vector3df(1.5f, 1.5f, 1.5f));
|
||||
|
||||
World::getWorld()->getTrack()->addNode(tb);
|
||||
tb->drop();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "config/player_manager.hpp"
|
||||
#include "config/user_config.hpp"
|
||||
#include "font/bold_face.hpp"
|
||||
#include "font/font_manager.hpp"
|
||||
#include "font/regular_face.hpp"
|
||||
#include "guiengine/scalable_font.hpp"
|
||||
#include "guiengine/screen.hpp"
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "config/user_config.hpp"
|
||||
#include "font/bold_face.hpp"
|
||||
#include "font/digit_face.hpp"
|
||||
#include "font/font_manager.hpp"
|
||||
#include "font/regular_face.hpp"
|
||||
#include "graphics/camera_debug.hpp"
|
||||
#include "graphics/camera_fps.hpp"
|
||||
|
Loading…
Reference in New Issue
Block a user