diff options
Diffstat (limited to 'Runtime/Filters/Misc')
-rw-r--r-- | Runtime/Filters/Misc/DynamicFontFreeType.cpp | 510 | ||||
-rw-r--r-- | Runtime/Filters/Misc/DynamicFontFreeType.h | 47 | ||||
-rw-r--r-- | Runtime/Filters/Misc/Font.cpp | 845 | ||||
-rw-r--r-- | Runtime/Filters/Misc/Font.h | 318 | ||||
-rw-r--r-- | Runtime/Filters/Misc/GetFonts.cpp | 338 | ||||
-rw-r--r-- | Runtime/Filters/Misc/LineBuilder.cpp | 95 | ||||
-rw-r--r-- | Runtime/Filters/Misc/LineBuilder.h | 88 | ||||
-rw-r--r-- | Runtime/Filters/Misc/LineRenderer.cpp | 195 | ||||
-rw-r--r-- | Runtime/Filters/Misc/LineRenderer.h | 61 | ||||
-rw-r--r-- | Runtime/Filters/Misc/MiniCoreText.h | 38 | ||||
-rw-r--r-- | Runtime/Filters/Misc/TextMesh.cpp | 298 | ||||
-rw-r--r-- | Runtime/Filters/Misc/TextMesh.h | 99 | ||||
-rw-r--r-- | Runtime/Filters/Misc/TrailRenderer.cpp | 197 | ||||
-rw-r--r-- | Runtime/Filters/Misc/TrailRenderer.h | 62 |
14 files changed, 3191 insertions, 0 deletions
diff --git a/Runtime/Filters/Misc/DynamicFontFreeType.cpp b/Runtime/Filters/Misc/DynamicFontFreeType.cpp new file mode 100644 index 0000000..042e328 --- /dev/null +++ b/Runtime/Filters/Misc/DynamicFontFreeType.cpp @@ -0,0 +1,510 @@ +#include "UnityPrefix.h" +#include "Font.h" + +#if UNITY_WIN +#include "PlatformDependent/Win/Registry.h" +#endif + +#include "Runtime/Graphics/ScreenManager.h" +#include "Runtime/Graphics/Image.h" +#include "Runtime/Misc/ResourceManager.h" +#include FT_BITMAP_H + + +DynamicFontData::DynamicFontData() +{ +} + +DynamicFontData::~DynamicFontData () +{ + for (FaceMap::iterator i=m_Faces.begin(); i!=m_Faces.end(); i++) + FT_Done_Face(i->second); +} + +static FT_Library g_ftLib; +static bool g_ftLibInit = false; +static FT_Bitmap g_bitmap8bpp; +static bool g_bitmap8bppInit = false; + +struct OSFont +{ + OSFont () : + index(0) + {} + + OSFont (std::string _path, int _index) : + path(_path), + index(_index) + {} + + bool operator != (const OSFont& other) const + { + return index != other.index || path != other.path; + } + + std::string path; + int index; +}; +typedef std::map<FontRef,OSFont> OSFontMap; +static OSFontMap* gOSFontMap = NULL; // Maps family names to files + + +namespace DynamicFontMap +{ + void StaticInitialize () + { + gOSFontMap = UNITY_NEW (OSFontMap, kMemFont); + } + void StaticDestroy () + { + UNITY_DELETE (gOSFontMap, kMemFont); + } +} +static float ConvertFixed26(long val) +{ + float f = (float)(val >> 6); + val = val & 0x3F; + f += (float)val/(2^6 - 1); + return f; +} + +static float ConvertFixed16(long val) +{ + float f = (float)(val >> 16); + val = val & 0xFFFF; + f += (float)val/(2^16 - 1); + return f; +} + +static long ConvertFloat26(float val) +{ + return (long)(val * (1 << 6)); +} + +static long ConvertFloat16(float val) +{ + return (long)(val * (1 << 16)); +} + +static unsigned int FreeTypeStyleToUnity (int style_flags) +{ + unsigned int style = 0; + if (style_flags & FT_STYLE_FLAG_ITALIC) + style |= kStyleFlagItalic; + if (style_flags & FT_STYLE_FLAG_BOLD) + style |= kStyleFlagBold; + return style; +} + +static bool IsExpectedFontStyle(const char* style_name, unsigned int style) +{ + switch (style) + { + case kStyleDefault: + return (strcmp(style_name, "Regular") == 0); + case kStyleFlagItalic: + return (strcmp(style_name, "Italic") == 0); + case kStyleFlagBold: + return (strcmp(style_name, "Bold") == 0); + case kStyleFlagBold | kStyleFlagItalic: + return (strcmp(style_name, "Bold Italic") == 0); + } + return false; +} + +bool GetFontMetadata(const std::string& path, std::string& family_name, std::string& style_name, unsigned& style_flags, unsigned& face_flags, int *faceIndex) +{ + std::string shortName = GetFileNameWithoutExtension(path); + + // Check, maybe we already have font metadata in our preset table + if (GetFontMetadataPreset(shortName, family_name, style_name, style_flags, face_flags)) + return true; + + bool res = false; + FT_Face face; + if(FT_New_Face(g_ftLib, path.c_str(), *faceIndex, &face) == 0) + { + *faceIndex = face->num_faces; + if(face->family_name != NULL) + { + //Uncomment this line when font metadata needs to be rebuilt for newer OS/hw versions + //printf_console("\tgFontMetadata[\"%s\"] = (_FontInfo){\"%s\", \"%s\", 0x%x, 0x%x};\n", shortName.c_str(), face->family_name, face->style_name, face->style_flags, face->face_flags); + + family_name = face->family_name; + style_name = face->style_name; + style_flags = face->style_flags; + face_flags = face->face_flags; + res = true; + } + + FT_Done_Face(face); + } + + return res; +} + +void ReadFontFiles() +{ + DynamicFontMap::StaticInitialize(); + + std::vector<std::string> paths; + GetFontPaths(paths); + + for(int folderIndex = 0; folderIndex < paths.size(); ++folderIndex) + { + + std::string &path = paths[folderIndex]; + + std::string family_name, style_name; + unsigned style_flags, face_flags; + int numFaces = 1; + for (int fontIndex=0; fontIndex<numFaces; fontIndex++) + { + int faceIndex = fontIndex; + if (GetFontMetadata(path, family_name, style_name, style_flags, face_flags, &faceIndex)) + { + numFaces = faceIndex; + if((face_flags & FT_FACE_FLAG_SCALABLE) == 0) + continue; + + FontRef r(family_name.c_str(), FreeTypeStyleToUnity(style_flags)); + OSFont font (path, fontIndex); + OSFontMap::iterator i = gOSFontMap->find(r); + if (i != gOSFontMap->end()) + { + // There already is a font for this family and style + if (i->second != font) + { + // Try to pick the one with the expected style name. + // This way we don't accidentally catch fonts like "Arial Black" when we want "Arial", + // as both will show with family_name=="Arial" and style_flags==0. In such cases, pick + // the one with the style name matching what we'd expect from the style flags, if possible. + if (IsExpectedFontStyle(style_name.c_str(), r.style)) + (*gOSFontMap)[r] = font; + } + } + else + (*gOSFontMap)[r] = font; + } + } + } +} + +static OSFont SelectFont(FontRef &r) +{ + // Initialize font map. Do this on demand here + // as it takes a significant amount of time to go through + // all of the system's fonts. + if (!gOSFontMap) + { + ReadFontFiles(); + Assert (gOSFontMap); + } + + OSFontMap::iterator i = gOSFontMap->find(r); + if (i != gOSFontMap->end()) + return i->second; + return OSFont(); +} + +FT_Face DynamicFontData::GetFaceForFontRef (FontRef &r, FontFallbacks &fallbacks) +{ + FaceMap::iterator i = m_Faces.find(r); + if (i != m_Faces.end()) + return (i->second); + else + { + // First see if any of the fallback fonts in the project contain the needed font. + for (FontFallbacks::iterator j=fallbacks.begin(); j != fallbacks.end(); j++) + { + if (j->IsValid()) + { + i = (**j).m_DynamicData.m_Faces.find(r); + if (i != (**j).m_DynamicData.m_Faces.end()) + return (i->second); + } + } + + // Then look at the system font files. + OSFont font = SelectFont(r); + if (!font.path.empty()) + { + FT_New_Face(g_ftLib, font.path.c_str(), font.index, &m_Faces[r]); + return m_Faces[r]; + } + } + return NULL; +} + +FT_Face DynamicFontData::GetFaceForCharacterIfAvailableInFont (FontRef &r, FontFallbacks &fallbacks, unsigned int unicodeChar) +{ + // Do we have a font for the requested name and style which has the character? + FT_Face face = GetFaceForFontRef(r, fallbacks); + if (face != NULL) + { + if (FT_Get_Char_Index(face, unicodeChar)) + return face; + } + + // If not, try with default style (if we had something else requested). + if (r.style) + { + FontRef r2 = r; + r2.style = 0; + face = GetFaceForFontRef(r2, fallbacks); + if (face != NULL) + { + if (FT_Get_Char_Index(face, unicodeChar)) + return face; + } + } + return NULL; +} + +FT_Face DynamicFontData::GetFaceForCharacter (FontNames &fonts, FontFallbacks &fallbacks, unsigned int style, unsigned int unicodeChar) +{ + // Check if any of the fonts in the fallback names serialized with the font has the character + for (FontNames::iterator font = fonts.begin();font != fonts.end();font++) + { + // First check if we find the font by it's full name (as that + // is what's used for the embedded ttf data, if any) + unsigned int st = style; + std::string name = *font; + FontRef r (name, st); + FT_Face face = GetFaceForCharacterIfAvailableInFont (r, fallbacks, unicodeChar); + if (face != NULL) + return face; + + // If that did not find anything, remove and parse potential style names, as OS fonts + // are identified by family name and style flags. + size_t pos = name.find(" Bold"); + if (pos != std::string::npos) + { + name = name.substr(0,pos)+name.substr(pos+5); + st |= kStyleFlagBold; + } + pos = name.find(" Italic"); + if (pos != std::string::npos) + { + name = name.substr(0,pos)+name.substr(pos+7); + st |= kStyleFlagItalic; + } + r = FontRef(name, st); + face = GetFaceForCharacterIfAvailableInFont (r, fallbacks, unicodeChar); + if (face != NULL) + return face; + } + // If not, fall back to the global fallbacks. + FontNames &globalFallbacks = GetFallbacks(); + for (FontNames::iterator font = globalFallbacks.begin();font != globalFallbacks.end();font++) + { + FontRef r (*font, style); + FT_Face face = GetFaceForCharacterIfAvailableInFont (r, fallbacks, unicodeChar); + if (face != NULL) + return face; + } + + return NULL; +} + +bool Font::HasCharacterDynamic (unsigned int unicodeChar) +{ + return unicodeChar >= 32; +} + +int GetLoadTarget (int fontsize, int mode) +{ + switch (mode) + { + case kFontRenderingModeSmooth: + return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; + case kFontRenderingModeHintedSmooth: + return FT_LOAD_TARGET_NORMAL; + case kFontRenderingModeHintedRaster: + return FT_LOAD_TARGET_MONO; + case kFontRenderingModeOSDefault: + #if UNITY_WINRT + return FT_LOAD_TARGET_NORMAL; + #elif UNITY_WIN + { + static bool smoothing = registry::getString( "Control Panel\\Desktop", "FontSmoothing", "2" ) == "2"; + if (smoothing) + return FT_LOAD_TARGET_NORMAL; + else + return FT_LOAD_TARGET_MONO; + } + #elif UNITY_OSX + static int antiAliasingTreshold = -1; + if (antiAliasingTreshold == -1) + { + Boolean exists; + antiAliasingTreshold = CFPreferencesGetAppIntegerValue(CFSTR("AppleAntiAliasingThreshold"),CFSTR("Apple Global Domain"),&exists); + if (!exists) + antiAliasingTreshold = 4; + } + if (fontsize <= antiAliasingTreshold) + return FT_LOAD_TARGET_MONO; + else + return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; + #else + return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; + #endif + default: + ErrorString("Unknown font rendering mode."); + return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; + } +} + +UInt8 *Font::GetCharacterBitmap(unsigned int &charWidth, unsigned int &charHeight, unsigned int &bufferWidth, Rectf &vert, float &advance, unsigned int unicodeChar, int size, unsigned int style) +{ + if(size == 0) + size = m_FontSize; + + FT_Face face = m_DynamicData.GetFaceForCharacter(m_FontNames, m_FallbackFonts, style, unicodeChar); + if (face == NULL) + { + // If we don't find a fallback Font in the OS, try built-in default font. + // Needed for Platforms without access to OS fonts (like NaCl, which has no file system access). + Font *builtinFont = GetBuiltinResource<Font> (kDefaultFontName); + if (builtinFont) + face = builtinFont->m_DynamicData.GetFaceForCharacter(m_FontNames, builtinFont->m_FallbackFonts, style, unicodeChar); + + if (face == NULL) + return NULL; + } + + unsigned int faceStyle = FreeTypeStyleToUnity(face->style_flags); + // Perform transformations needed for bold/italic styles if the font does not natively support it. + FT_Matrix m; + if (!(faceStyle & kStyleFlagBold) && style & kStyleFlagBold) + m.xx = ConvertFloat16(1.25f); + else + m.xx = ConvertFloat16(1); + if (!(faceStyle & kStyleFlagItalic) && style & kStyleFlagItalic) + m.xy = ConvertFloat16(0.25f); + else + m.xy = ConvertFloat16(0); + m.yy = ConvertFloat16(1); + m.yx = ConvertFloat16(0); + FT_Set_Transform(face, &m, NULL); + + FT_Set_Char_Size(face, 0, ConvertFloat26(size), 72, 72); + + FT_UInt glyph = FT_Get_Char_Index(face, unicodeChar); + if(glyph != 0) + { + int loadTarget = FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; + loadTarget = GetLoadTarget(size, m_FontRenderingMode); + if( FT_Load_Glyph(face, glyph, loadTarget ) == 0 ) + { + bool glyphRendered = true; + if(face->glyph->format != FT_GLYPH_FORMAT_BITMAP ) + glyphRendered = ( FT_Render_Glyph(face->glyph, FT_LOAD_TARGET_MODE(loadTarget) ) == 0 ); + + if(glyphRendered) + { + FT_Bitmap *srcBitmap = 0; + FT_Bitmap& bitmap =face->glyph->bitmap; + + if(bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) + { + if(!g_bitmap8bppInit) + { + FT_Bitmap_New(&g_bitmap8bpp); + g_bitmap8bppInit = true; + } + FT_Bitmap_Convert(g_ftLib, &bitmap, &g_bitmap8bpp, 4); + srcBitmap = &g_bitmap8bpp; + if (srcBitmap->num_grays != 256) + { + float factor = 1.0f/(srcBitmap->num_grays-1) * 255; + for (int i=0; i<srcBitmap->pitch*srcBitmap->rows; i++) + srcBitmap->buffer[i] *= factor; + } + } + else + { + srcBitmap = &bitmap; + } + + charWidth = srcBitmap->width; + charHeight = srcBitmap->rows; + bufferWidth = srcBitmap->pitch; + vert = Rectf(face->glyph->bitmap_left, + face->glyph->bitmap_top - m_Ascent, + (float)charWidth, + -(float)charHeight); + advance = Roundf(face->glyph->metrics.horiAdvance / 64.0); + + if (srcBitmap->width*srcBitmap->rows == 0) + return NULL; + + return srcBitmap->buffer; + } + } + } + + return NULL; +} + +void Font::SetupDynamicFont () +{ + Assert(g_ftLibInit); + + if(!m_FontData.empty()) + { + FT_Face face = NULL; + if(FT_New_Memory_Face(g_ftLib, (const FT_Byte*)&m_FontData[0], m_FontData.size(), 0, &face) != 0) + { + ErrorString("Failed to load font from memory"); + } + else + { + // So we don't crash if we have a font where FreeType does not understand the name. + // Unity 4.x will refuse to import such fonts, but it can happen with content made with 3.x, + // which did not use FT to import the font. + if (face->family_name == NULL) + face->family_name = (FT_String*)"Unreadeable font name."; + + // Make sure the name of the memory font is the first in the list of fonts to use. + if (strcmp(m_FontNames[0].c_str(), face->family_name) != 0) + m_FontNames.insert(m_FontNames.begin(), face->family_name); + + FontRef r (face->family_name, FreeTypeStyleToUnity(face->style_flags)); + m_DynamicData.m_Faces[r] = face; + if (r.style != 0) + { + r.style = 0; + if (FT_New_Memory_Face(g_ftLib, (const FT_Byte*)&m_FontData[0], m_FontData.size(), 0, &face) == 0) + m_DynamicData.m_Faces[r] = face; + } + } + } +} + +void Font::InitializeClass() +{ + GetFontsManager::StaticInitialize(); + if(FT_Init_FreeType( &g_ftLib ) != 0) + { + ErrorString("Could not initialize FreeType"); + } + g_ftLibInit = true; +} + +void Font::CleanupClass() +{ + if(g_bitmap8bppInit) + { + FT_Bitmap_Done(g_ftLib, &g_bitmap8bpp); + g_bitmap8bppInit = false; + } + if(g_ftLibInit) + { + FT_Done_FreeType(g_ftLib); + g_ftLibInit = false; + } + DynamicFontMap::StaticDestroy(); + GetFontsManager::StaticDestroy(); +} + diff --git a/Runtime/Filters/Misc/DynamicFontFreeType.h b/Runtime/Filters/Misc/DynamicFontFreeType.h new file mode 100644 index 0000000..4bfdb35 --- /dev/null +++ b/Runtime/Filters/Misc/DynamicFontFreeType.h @@ -0,0 +1,47 @@ +#ifndef DYNAMICFONTFREETYPE_H +#define DYNAMICFONTFREETYPE_H + +#if DYNAMICFONTMODE == kDynamicFontModeFreeType + +#include "External/freetype2/include/ft2build.h" +#include FT_FREETYPE_H +//#include FT_GLYPH_H + +namespace DynamicFontMap +{ + void StaticInitialize(); + void StaticDestroy(); +} + +struct FontRef +{ + std::string family; + unsigned int style; + + FontRef(const std::string& _family, unsigned int _style) : family(_family), style(_style) {} + + bool operator < (const FontRef& other) const { + if (family < other.family) + return true; + else if (family > other.family) + return false; + return style < other.style; + } +}; + +typedef std::map<FontRef, FT_Face> FaceMap; + +struct DynamicFontData { + DynamicFontData(); + ~DynamicFontData(); + + FT_Face GetFaceForFontRef (FontRef &r, FontFallbacks &fallbacks); + FT_Face GetFaceForCharacter (FontNames &fonts, FontFallbacks &fallbacks, unsigned int style, unsigned int unicodeChar); + FT_Face GetFaceForCharacterIfAvailableInFont (FontRef &r, FontFallbacks &fallbacks, unsigned int unicodeChar); + + FaceMap m_Faces; +}; + +#endif + +#endif diff --git a/Runtime/Filters/Misc/Font.cpp b/Runtime/Filters/Misc/Font.cpp new file mode 100644 index 0000000..57d84f3 --- /dev/null +++ b/Runtime/Filters/Misc/Font.cpp @@ -0,0 +1,845 @@ +#include "UnityPrefix.h" +#include "Font.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Shaders/Material.h" +#include "TextMesh.h" +#if UNITY_EDITOR +#include "Editor/Src/AssetPipeline/TrueTypeFontImporter.h" +#endif +#include "Runtime/Graphics/Image.h" +#include "Runtime/Graphics/Texture.h" +#include "Runtime/Utilities/Word.h" +#include "Runtime/GfxDevice/GfxDevice.h" +#include "Runtime/Graphics/Texture2D.h" +#include "Runtime/IMGUI/TextMeshGenerator2.h" +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Utilities/BitUtility.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/ScriptingManager.h" +#include "Runtime/Scripting/Scripting.h" + +using namespace std; + +// on android we can recreate gles context and loose gpu-side texture copy +// on editor we want to have it to write it later on build +#define NEEDS_SYSTEM_MEM_COPY UNITY_ANDROID || UNITY_EDITOR + +PROFILER_INFORMATION(gFontTextureCacheProfile, "Font.CacheFontForText", kProfilerRender) + +unsigned int Font::s_FrameCount = 0; + +Font::Font(MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ + // dynamic font stuff + m_TexWidth = 256; + m_TexHeight = 256; + m_SubImageSize = 1; + m_CharacterSpacing = 1; + m_CharacterPadding = 0; + m_FontSize = 0; + m_Ascent = 0.0f; + m_DefaultStyle = kStyleDefault; + m_FontRenderingMode = kFontRenderingModeSmooth; + + m_PixelScale = 0.1f; + + m_AsciiCharacterRects.clear(); + m_AsciiCharacterRects.resize (256); + + m_TexturePositions.insert (TexturePosition (0, 0)); + m_TexturePositionsSearchPosition = m_TexturePositions.begin(); +} + +Font::~Font () +{ +} + +void Font::Reset () +{ + Super::Reset(); + + m_Kerning = 1.0F; + m_LineSpacing = 0.1F; + m_AsciiStartOffset = 0; + m_ConvertCase = 0; +} + +unsigned int Font::GetGlyphNo (unsigned int charCode) const +{ + if (m_ConvertCase == kUpperCase) + return ToUpper ((char)charCode) - m_AsciiStartOffset; + else if (m_ConvertCase == kLowerCase) + return ToLower ((char)charCode) - m_AsciiStartOffset; + else + return charCode - m_AsciiStartOffset; +} + +void Font::GetCharacterRenderInfo( unsigned int charCode, Rectf& verts, Rectf& uvs, bool &flipped ) const +{ + GetCharacterRenderInfo(charCode, 0, 0, verts, uvs, flipped); +} + +void Font::GetCharacterRenderInfo( unsigned int charCode, int size, unsigned int style, Rectf& verts, Rectf& uvs, bool &flipped ) const +{ + unsigned int charNo = GetGlyphNo (charCode); + if (size == m_FontSize) + size = 0; + + if (m_ConvertCase != kDynamicFont && (size != 0 || style != kStyleDefault)) + { + ErrorString ("Font size and style overrides are only supported for dynamic fonts."); + size = 0; + style = kStyleDefault; + } + + if (charNo < 256 && size == 0 && style == kStyleDefault) + { + verts = m_AsciiCharacterRects[charNo].vert; + uvs = m_AsciiCharacterRects[charNo].uv; + flipped = m_AsciiCharacterRects[charNo].flipped; + } + else + { + CharacterInfo proxy; + proxy.index = charNo; + proxy.size = size; + proxy.style = style; + vector_set<CharacterInfo>::const_iterator found = m_UnicodeCharacterRects.find(proxy); + if (found != m_UnicodeCharacterRects.end()) + { + verts = found->vert; + uvs = found->uv; + flipped = found->flipped; + } + else + { + verts = Rectf( 0, 0, 0, 0 ); + uvs = Rectf( 0, 0, 0, 0 ); + flipped = false; + } + } +} + +/// Does this font have a definition for a specific character? +bool Font::HasCharacterInTexture (unsigned int unicodeChar, int size, unsigned int style) +{ + unsigned int charNo = GetGlyphNo (unicodeChar); + if (size == m_FontSize) + size = 0; + + if (m_ConvertCase != kDynamicFont && (size != 0 || style != kStyleDefault)) + { + ErrorString ("Font size and style overrides are only supported for dynamic fonts."); + size = 0; + style = kStyleDefault; + } + + // This uses the character advancement - We're making the assumption that all characters have a width + if (charNo < 256 && size == 0 && style == kStyleDefault) + { + if (m_AsciiCharacterRects[charNo].width != 0.0f) + { + m_AsciiCharacterRects[charNo].lastUsedInFrame = s_FrameCount; + return true; + } + } + + CharacterInfo proxy; + proxy.index = charNo; + proxy.size = size; + proxy.style = style; + vector_set<CharacterInfo>::iterator found = m_UnicodeCharacterRects.find(proxy); + if (found != m_UnicodeCharacterRects.end()) + { + found->lastUsedInFrame = s_FrameCount; + return true; + } + return false; +} + +bool Font::HasCharacter (unsigned int unicodeChar, int size, unsigned int style) +{ + if (m_ConvertCase == kDynamicFont) + return HasCharacterDynamic (unicodeChar); + else + return HasCharacterInTexture (unicodeChar, size, style); +} + +float Font::GetCharacterWidth( unsigned int charCode, int size, unsigned int style ) const +{ + if (size == m_FontSize) + size = 0; + + if (m_ConvertCase != kDynamicFont && (size != 0 || style != kStyleDefault)) + { + ErrorString ("Font size and style overrides are only supported for dynamic fonts."); + size = 0; + style = kStyleDefault; + } + + unsigned int charNo = GetGlyphNo (charCode); + if (charNo < 256 && size == 0 && style == kStyleDefault) + return m_AsciiCharacterRects[charNo].width * m_Kerning; + else + { + CharacterInfo proxy; + proxy.index = charNo; + proxy.size = size; + proxy.style = style; + vector_set<CharacterInfo>::const_iterator found = m_UnicodeCharacterRects.find(proxy); + if (found != m_UnicodeCharacterRects.end()) + return found->width * m_Kerning; + else + return 0.0F; + } +} + +float Font::GetTabWidth() const +{ + return GetCharacterWidth(' '); +} + +template<class TransferFunction> inline +void Font::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + transfer.SetVersion (4); + + TRANSFER(m_AsciiStartOffset); + TRANSFER(m_Kerning); + TRANSFER(m_LineSpacing); + TRANSFER(m_CharacterSpacing); + TRANSFER(m_CharacterPadding); + TRANSFER(m_ConvertCase); + TRANSFER(m_DefaultMaterial); + + if (m_ConvertCase != kDynamicFont) + { + transfer.Transfer(m_CharacterRects, "m_CharacterRects"); + } + else + { + // These are generated dynamically for dynamic fonts. + UNITY_TEMP_VECTOR(CharacterInfo) emptyCharacterInfo; + transfer.Transfer(emptyCharacterInfo, "m_CharacterRects"); + } + + transfer.Transfer(m_Texture, "m_Texture", kHideInEditorMask); + + transfer.Transfer(m_KerningValues, "m_KerningValues", kHideInEditorMask); + + // In version 1.5.0 line spacing is multiplicative instead of additive + if (transfer.IsOldVersion(1)) + { + m_LineSpacing = 1.0F + m_LineSpacing; + } + + transfer.Transfer(m_PixelScale, "m_PixelScale", kHideInEditorMask); + + // Legacy Grid Font support + if (transfer.IsVersionSmallerOrEqual(3)) + { + bool gridFont; + transfer.Transfer(gridFont, "m_GridFont"); + + if (gridFont) + { + int fontCountX; + int fontCountY; + transfer.Transfer(fontCountX, "m_FontCountX"); + transfer.Transfer(fontCountY, "m_FontCountY"); + + m_PixelScale = -fontCountX; + + PerCharacterKerning perCharacterKerning; + + transfer.Transfer(perCharacterKerning, "m_PerCharacterKerning"); + + for (int charNo=0; charNo< fontCountX*fontCountY; charNo++ ) + { + CharacterInfo info; + info.index = charNo; + + info.vert = Rectf(0.0F, 0.0F, 1.0, -1.0); + short charCol = charNo % fontCountX; + short charRow = charNo / fontCountX; + float charUVSizeX = 1.0F / (float)fontCountX; + float charUVSizeY = 1.0F / (float)fontCountY; + Vector2f charUVOffset = Vector2f((float)charCol * charUVSizeX, (float)charRow * charUVSizeY); + info.uv = MinMaxRect ( charUVOffset.x, 1.0F - charUVOffset.y - charUVSizeY, charUVOffset.x + charUVSizeX, 1.0F - charUVOffset.y ); + info.width = 1.0; + for (PerCharacterKerning::iterator i=perCharacterKerning.begin ();i != perCharacterKerning.end ();i++) + { + if (i->first - m_AsciiStartOffset == charNo) + info.width = i->second; + } + m_CharacterRects.push_back(info); + } + } + } + + transfer.Align(); + transfer.Transfer(m_FontData, "m_FontData", kHideInEditorMask); + transfer.Align(); + float fontSize = m_FontSize; + transfer.Transfer(fontSize, "m_FontSize", kHideInEditorMask); + m_FontSize = (int)fontSize; + transfer.Transfer(m_Ascent, "m_Ascent", kHideInEditorMask); + transfer.Transfer(m_DefaultStyle, "m_DefaultStyle", kHideInEditorMask); + transfer.Transfer(m_FontNames, "m_FontNames", kHideInEditorMask); + +#if UNITY_EDITOR + if (transfer.IsWritingGameReleaseData ()) + { + // Make sure we have references to all other fonts in the project + // which may be used as fallbacks in the build. + TrueTypeFontImporter::GetFallbackFontReferences (this); + } +#endif + + transfer.Transfer(m_FallbackFonts, "m_FallbackFonts", kHideInEditorMask); + transfer.Align(); + TRANSFER(m_FontRenderingMode); +} + +template<class TransferFunction> inline +void Font::CharacterInfo::Transfer (TransferFunction& transfer) +{ + transfer.SetVersion(2); + TRANSFER(index); + TRANSFER(uv); + TRANSFER(vert); + TRANSFER(width); + TRANSFER(flipped); + transfer.Align(); + if( !transfer.IsCurrentVersion() ) + width = vert.Width(); +} + +void ApplyToMeshes () +{ + vector<TextMesh*> meshes; + Object::FindObjectsOfType (&meshes); + for (int i=0;i<meshes.size ();i++) + { + meshes[i]->ApplyToMesh (); + } +} + +void Font::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + +#if UNITY_EDITOR + // Make sure we have references to all other fonts in the project + // which may be used as fallbacks in the build. + TrueTypeFontImporter::GetFallbackFontReferences (this); + + if ((awakeMode & kDidLoadFromDisk) && (m_ConvertCase == kDynamicFont) && !m_Texture.IsValid()) + ErrorStringObject(Format("Font texture for dynamic font %s is missing. Please reimport the Font. All dynamic fonts created with earlier Unity 3.0 betas need to be reimported.", GetName()), this); +#endif + + // m_PixelScale is set to -fontCountX for legacy gridfonts + if(m_PixelScale < 0.f) + { + // Load related material or texture to apply the scaling to the CharacterRects + Texture *tex = GetTexture (); + if (!tex) { + Material *mat = GetMaterial(); + if (mat) + tex = mat->GetTexture (ShaderLab::Property("_MainTex")); + } + if (tex) + m_PixelScale = -m_PixelScale / tex->GetDataWidth (); + else + m_PixelScale = 1.0f; + + for (int charNo=0; charNo< m_CharacterRects.size(); charNo++ ) + { + CharacterInfo& info = m_CharacterRects[charNo]; + info.vert = Rectf(0.0F, 0.0F, 1.0/m_PixelScale, -1.0/m_PixelScale); + info.width /= m_PixelScale; + } + m_LineSpacing /= m_PixelScale; + } + + CacheRects(); + + if (m_ConvertCase == kDynamicFont) + { + if (m_FontNames.empty()) + { + ErrorString ("Font does not contain font names!"); + m_FontNames.push_back("Arial"); + } + SetupDynamicFont (); + ResetCachedTexture(); + } + + if ((awakeMode & kDidLoadFromDisk) == 0) + ApplyToMeshes(); +} + +void Font::AddCharacterInfoEntry( const Rectf& uv, const Rectf& vert, float width, int character, bool flipped, int size, unsigned int style) +{ + character -= m_AsciiStartOffset; + AssertIf( character < 0 ); + + CharacterInfo inf; + inf.uv = uv; + inf.vert = vert; + inf.width = width; + inf.index = character; + if (size == m_FontSize) + inf.size = 0; + else + inf.size = size; + inf.style = style; + inf.lastUsedInFrame = s_FrameCount; + inf.flipped = flipped; + m_CharacterRects.push_back(inf); + AddRectToCache (inf); +} + +void Font::AddRectToCache(CharacterInfo& info) +{ + // We cache ascii characters into a direct lookup buffer, for optimal performance on the most common characters + if (info.index < 256 && info.size == 0 && info.style == kStyleDefault) + m_AsciiCharacterRects[info.index] = info; + // And a set of characters for the rest. + else + m_UnicodeCharacterRects.insert(info); +} + +void Font::CacheRects () +{ + m_AsciiCharacterRects.clear(); + m_AsciiCharacterRects.resize (256); + m_UnicodeCharacterRects.clear(); + + for (int i=0;i<m_CharacterRects.size();i++) + { + CharacterInfo& info = m_CharacterRects[i]; + // Older version didn't have the index. So derive it from i + if (info.index == -1) + info.index = i; + + AddRectToCache(info); + } +} + + + +// Dynamic font stuff +// ================== + +static bool Equals (FontNames& list1, FontNames& list2) +{ + if (list1.size() != list2.size()) + return false; + + for (unsigned i=0; i<list1.size (); ++i) + if (list1[i] != list2[i]) + return false; + + return true; +} + +void Font::SetFontNames (FontNames &names) +{ + if (m_ConvertCase == kDynamicFont) + { + if (Equals (names, m_FontNames)) + return; + + m_FontNames = names; +// TODO! +// DestroyDynamicFont (); +// SetupDynamicFont (); + ResetCachedTexture(); + } + else + ErrorString ("Font.names can only be set for dynamic fonts."); +} + +void Font::ResetPackingData () +{ + m_TexturePositions.clear(); + m_IntRects.clear(); + m_TexturePositions.insert (TexturePosition (0, 0)); + m_TexturePositionsSearchPosition = m_TexturePositions.begin(); +} + +bool Font::ResetCachedTexture () +{ + if (m_ConvertCase != kDynamicFont) + return true; // nothing to do for static fonts + + m_CharacterRects.clear(); + CacheRects (); + + int maxSize = gGraphicsCaps.maxTextureSize; + + // Some windows setups apparently crash when allocating textures > 4096^2. So, don't do it. + maxSize = std::min(maxSize, 4096); + + if (m_TexWidth > maxSize || m_TexHeight > maxSize) + { + ErrorString ("Failed to generate dynamic font texture, because all the needed characters do not fit onto a single texture. Try using less text or a smaller font size."); + m_TexWidth = maxSize; + m_TexHeight = maxSize; + return false; + } + + Texture2D *tex; + if (!GetTexture().IsValid()) + return false; + else + tex = dynamic_pptr_cast<Texture2D*>(GetTexture()); + + if (gGraphicsCaps.disableSubTextureUpload || (NEEDS_SYSTEM_MEM_COPY && !UNITY_EDITOR)) + tex->SetIsReadable(true); + else + tex->SetIsUnreloadable (true); + +#if UNITY_EDITOR + tex->SetEditorDontWriteTextureData(true); +#endif + + if (tex->GetDataWidth() != m_TexWidth || tex->GetDataHeight() != m_TexHeight || !tex->GetIsUploaded()) + { + if (!tex->InitTexture (m_TexWidth, m_TexHeight, kTexFormatAlpha8, Texture2D::kNoMipmap)) + return false; + tex->UpdateImageData (); + } + + { + UInt8* texData; + ALLOC_TEMP(texData, UInt8, m_TexWidth * m_TexHeight); + memset (texData, 0, m_TexWidth * m_TexHeight); + int dataSize = m_TexWidth * m_TexHeight; + + if (!gGraphicsCaps.disableSubTextureUpload) + GetGfxDevice().UploadTextureSubData2D( tex->GetTextureID(), texData, dataSize, 0, 0, 0, m_TexWidth, m_TexHeight, kTexFormatAlpha8, tex->GetActiveTextureColorSpace() ); + if (gGraphicsCaps.disableSubTextureUpload || NEEDS_SYSTEM_MEM_COPY) + { + ImageReference texImg; + if (tex->GetWriteImageReference ( &texImg, 0, 0 )) + { + ImageReference data (m_TexWidth, m_TexHeight, m_TexWidth, kTexFormatAlpha8, texData); + texImg.BlitImage( data ); + } + if (gGraphicsCaps.disableSubTextureUpload) + tex->UpdateImageData(); + } + } + + ResetPackingData (); + + m_SubImageIndex = 0; + m_SubImageSize = std::max(m_SubImageSize, (unsigned int)NextPowerOfTwo(8 * m_FontSize)); + m_SubImageSize = std::min(m_SubImageSize, m_TexWidth); + + return true; +} + +bool Font::IsRectFree(const IntRect &r) const +{ + if (r.x < 0 || r.y < 0 || r.x + r.width > m_SubImageSize || r.y+r.height > m_SubImageSize) + return false; + + for (UNITY_VECTOR(kMemFont,IntRect)::const_iterator i = m_IntRects.begin(); i != m_IntRects.end(); i++) + { + if (r.Intersects(*i)) + return false; + } + + return true; +} + +bool Font::AddCharacterToTexture (unsigned int unicodeChar, int size, unsigned int style) +{ + Rectf vert; + + unsigned int charWidth = 0; + unsigned int charHeight = 0; + unsigned int bufferWidth = 0; + float advance = 0; + // returns a pointer to static vector data + UInt8* bitmap = GetCharacterBitmap (charWidth, charHeight, bufferWidth, vert, advance, unicodeChar, size, style | m_DefaultStyle); + UNITY_TEMP_VECTOR(UInt8) flippedBitmap; + bool flipped = false; + + if (bitmap == NULL && charHeight*charWidth != 0) + { + charWidth = 0; + charHeight = 0; + advance = 0; + } + + if (charWidth > charHeight) + { + // flip glyphs with >1 aspect ratios for better packing results. + flipped = true; + flippedBitmap.resize (charWidth * charHeight); + for (int x = 0; x<charWidth; x++) + { + for (int y = 0; y<charHeight; y++) + flippedBitmap[charHeight-1-y + (charWidth-1-x)*charHeight] = bitmap[x + y*bufferWidth]; + } + bitmap = &flippedBitmap[0]; + bufferWidth = charHeight; + charHeight = charWidth; + charWidth = bufferWidth; + } + else if (bufferWidth > charWidth) + { + flippedBitmap.resize (charWidth * charHeight); + for (int x = 0; x<charWidth; x++) + { + for (int y = 0; y<charHeight; y++) + flippedBitmap[x + y*charWidth] = bitmap[x + y*bufferWidth]; + } + bitmap = &flippedBitmap[0]; + bufferWidth = charWidth; + } + + vert.x -= m_CharacterPadding; + vert.y += m_CharacterPadding; + vert.width += 2*m_CharacterPadding; + vert.height -= 2*m_CharacterPadding; + + while (true) + { + for (UNITY_SET(kMemFont,TexturePosition)::iterator i = m_TexturePositionsSearchPosition; i != m_TexturePositions.end(); i++) + { + IntRect r (i->x, i->y, charWidth+m_CharacterSpacing+2*m_CharacterPadding, charHeight+m_CharacterSpacing+2*m_CharacterPadding); + if (IsRectFree (r)) + { + IntRect r2 = r; + r2.x--; + while (IsRectFree (r2) && r2.x > 0) + { + r = r2; + r2.x--; + } + r2 = r; + r2.y--; + while (IsRectFree (r2) && r2.y > 0) + { + r = r2; + r2.y--; + } + + m_IntRects.push_back(r); + m_TexturePositionsSearchPosition = i; + m_TexturePositionsSearchPosition++; + m_TexturePositions.erase(i); + m_TexturePositions.insert( TexturePosition(r.x + r.width, r.y)); + m_TexturePositions.insert( TexturePosition(r.x, r.y + r.height)); + + // Offset sub image position to get actual texture position + int subImagePos = m_SubImageIndex * m_SubImageSize; + r.x += subImagePos % m_TexWidth; + r.y += (subImagePos / m_TexWidth) * m_SubImageSize; + + if (bitmap) + { + int dataSize = bufferWidth * charHeight; + Texture2D *tex = dynamic_pptr_cast<Texture2D*>(GetTexture()); + + if (!gGraphicsCaps.disableSubTextureUpload) + GetGfxDevice().UploadTextureSubData2D( tex->GetTextureID(), bitmap, dataSize, 0, r.x+m_CharacterPadding, r.y+m_CharacterPadding, bufferWidth, charHeight, kTexFormatAlpha8, tex->GetActiveTextureColorSpace() ); + if (gGraphicsCaps.disableSubTextureUpload || NEEDS_SYSTEM_MEM_COPY) + { + ImageReference texImg; + if (tex->GetWriteImageReference ( &texImg, 0, 0 )) + { + ImageReference destRect = texImg.ClipImage( r.x, r.y, bufferWidth, charHeight ); + + ImageReference data (bufferWidth, charHeight, bufferWidth, kTexFormatAlpha8, bitmap); + destRect.BlitImage( data ); + } + } + } + + float width = m_TexWidth; + float height = m_TexHeight; + + Rectf uv (r.x/width, (r.y+charHeight+2*m_CharacterPadding)/height, (charWidth+2*m_CharacterPadding)/width, -((charHeight+2*m_CharacterPadding)/height)); + AddCharacterInfoEntry (uv, vert, advance, unicodeChar, flipped, size, style); + return true; + } + } + if (m_TexturePositionsSearchPosition != m_TexturePositions.begin()) + m_TexturePositionsSearchPosition = m_TexturePositions.begin(); + else + { + if (m_SubImageIndex+1 < (m_TexWidth/m_SubImageSize) * (m_TexHeight/m_SubImageSize)) + { + // This sub image is full. move to the next one. + m_SubImageIndex++; + ResetPackingData (); + } + else + return false; + } + } + return false; +} + +void Font::GrowTexture (int maxFontSize) +{ + // If we couldn't fit all characters by repainting the texture, enlarge it. + if (m_TexWidth < m_TexHeight) + m_TexWidth *= 2; + else + m_TexHeight *= 2; + // Make sure that we fit the largest characters in the string into a single sub image. + m_SubImageSize = std::max(m_SubImageSize, (unsigned int)NextPowerOfTwo(4 * maxFontSize)); + m_SubImageSize = std::min(m_SubImageSize, m_TexWidth); +} + +UInt16 *Font::CollectAllUsedCharacters (UInt16 *chars, int &length, int *&sizes, unsigned int *&styles) +{ + // We have to create a new texture. Make sure to add all characters which have been used in this frame. + int usedThisFrame = 0; + for (UNITY_VECTOR(kMemFont,CharacterInfo)::iterator ch = m_CharacterRects.begin(); ch != m_CharacterRects.end(); ch++) + { + if (ch->lastUsedInFrame == s_FrameCount) + usedThisFrame++; + } + UInt16 *newchars = new UInt16[usedThisFrame + length]; + sizes = new int[usedThisFrame + length]; + styles = new unsigned int[usedThisFrame + length]; + // string being currently cached; without size/style override + for (int ch=0; ch<length; ch++) + { + newchars[ch] = chars[ch]; + sizes[ch] = -1; + styles[ch] = -1; + } + // put in characters used in this frame, with their original size/style + for (UNITY_VECTOR(kMemFont,CharacterInfo)::iterator ch = m_CharacterRects.begin(); ch != m_CharacterRects.end(); ch++) + { + if (ch->lastUsedInFrame == s_FrameCount) + { + newchars[length] = ch->index; + sizes[length] = ch->size; + styles[length] = ch->style; + length++; + } + } + return newchars; +} + +bool Font::CacheFontForText (UInt16 *chars, int length, int size, unsigned int style, std::vector<TextFormatChange> format) +{ + if (m_ConvertCase != kDynamicFont) + return true; + + PROFILER_AUTO(gFontTextureCacheProfile, NULL) + + if (!GetTexture().IsValid() && !ResetCachedTexture ()) + return false; + + bool didAdd = false; + int *sizes = NULL; + unsigned int *styles = NULL; + int maxFontSize = 0; + bool didNotFit = false; + do { + didNotFit = false; + FormatStack formatStack(0xffffffff, size, style); + int formatChange = 0; + for (int i=0; i<length; i++) + { + while (formatChange < format.size() && i >= format[formatChange].startPosition) + { + i += format[formatChange].skipCharacters; + formatStack.PushFormat(format[formatChange]); + formatChange++; + } + // Recheck range after skipping format changes + if (i >= length) + break; + + int thisSize = formatStack.Current().size; + int thisStyle = formatStack.Current().style; + if (sizes && sizes[i] != -1) + { + // Normally, we just add characters with the computed size/style. + // But when we need to recreate the texture, then we need to make sure all used characters + // with all used sizes are in there, so we index size & style from an array. + thisSize = sizes[i]; + thisStyle = styles[i]; + } + if (thisSize == 0) + thisSize = m_FontSize; + if (thisSize > maxFontSize) + maxFontSize = thisSize; + UInt16 thisChar = chars[i]; + if (HasCharacterDynamic (thisChar) && !HasCharacterInTexture (chars[i], thisSize, thisStyle)) + { + if (!AddCharacterToTexture (thisChar, thisSize, thisStyle)) + { + if (sizes != NULL) + GrowTexture(maxFontSize); + else + chars = CollectAllUsedCharacters (chars, length, sizes, styles); + didNotFit = true; + if (!ResetCachedTexture()) + return false; + break; + } + didAdd = true; + } + } + } while(didNotFit); + + if (didAdd && gGraphicsCaps.disableSubTextureUpload) + dynamic_pptr_cast<Texture2D*>(GetTexture())->UpdateImageData(); + + if (sizes != NULL) + { + delete[] chars; + delete[] sizes; + delete[] styles; + + #if ENABLE_SCRIPTING + // Make sure we don't call InvokeFontTextureRebuildCallback_Internal repeatedly due to + // ApplyToMeshes adding characters. + static int recursionDepth = 0; + recursionDepth++; + TextMeshGenerator2::Flush(); + + ApplyToMeshes(); + recursionDepth--; + + if (recursionDepth == 0) + { + ScriptingObjectPtr instance = Scripting::ScriptingWrapperFor(this); + if (instance) + { + ScriptingInvocation invocation(GetScriptingManager().GetCommonClasses().font_InvokeFontTextureRebuildCallback_Internal); + invocation.object = instance; + invocation.Invoke(); + } + } + #endif + } + + return true; +} + +float Font::GetLineSpacing (int size) const +{ + if (size == 0 || m_FontSize == 0) + return m_LineSpacing; + else + return m_LineSpacing * (float)size/m_FontSize; +} + +IMPLEMENT_CLASS_HAS_INIT (Font) +IMPLEMENT_OBJECT_SERIALIZE (Font) diff --git a/Runtime/Filters/Misc/Font.h b/Runtime/Filters/Misc/Font.h new file mode 100644 index 0000000..5004709 --- /dev/null +++ b/Runtime/Filters/Misc/Font.h @@ -0,0 +1,318 @@ +#ifndef FONT_H +#define FONT_H + +#include "Runtime/BaseClasses/NamedObject.h" +#include "Runtime/Utilities/vector_map.h" +#include "Runtime/Utilities/vector_set.h" +#include "Runtime/Math/Rect.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Vector2.h" +#include "Runtime/Misc/UTF8.h" +#include "Runtime/IMGUI/TextFormatting.h" + +class Font; + +typedef UNITY_VECTOR(kMemFont,UnityStr) FontNames; +typedef UNITY_VECTOR (kMemFont, PPtr<Font> ) FontFallbacks; + +#include "DynamicFontFreeType.h" + +namespace Unity { class Material; } +using namespace Unity; + +enum { + kFontRenderingModeSmooth, + kFontRenderingModeHintedSmooth, + kFontRenderingModeHintedRaster, + kFontRenderingModeOSDefault +}; + +class Texture; + +class Font : public NamedObject +{ + public: + + struct KerningCompare : std::binary_function<std::pair<char, char>, std::pair<char, char>, std::size_t> + { + bool operator()(const std::pair<char, char> lhs, const std::pair<char, char> rhs) const + { + if (lhs.first != rhs.first) + return lhs.first < rhs.first; + else + return lhs.second < rhs.second; + } + }; + + // TrueType fonts: information for a character. + struct CharacterInfo { + unsigned int index; + Rectf uv; ///< UV coordinates for this glyph. + Rectf vert; ///< Rectangle for where to render the glyph. + float width; + int size; + unsigned int style; + unsigned int lastUsedInFrame; + bool flipped; + DECLARE_SERIALIZE_NO_PPTR (CharacterInfo) + + CharacterInfo() : + vert(0.0F,0.0F,0.0F,0.0F), + uv(0.0F,0.0F,0.0F,0.0F), + index (-1), + width (0.0F), + size (0), + style (kStyleDefault), + lastUsedInFrame (0), + flipped (false) + { } + + friend bool operator < (const CharacterInfo& lhs, const CharacterInfo& rhs) + { + if (lhs.index == rhs.index) + { + if (lhs.size < rhs.size) + return true; + else if (lhs.size > rhs.size) + return false; + return lhs.style < rhs.style; + } + return lhs.index < rhs.index; + } + }; + typedef UNITY_VECTOR(kMemFont,CharacterInfo) CharacterInfos; + + + REGISTER_DERIVED_CLASS (Font, NamedObject) + DECLARE_OBJECT_SERIALIZE (Font) + + Font (MemLabelId label, ObjectCreationMode mode); + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + virtual void Reset(); + + static void InitializeClass (); + static void CleanupClass (); + + /// Get the kerning of the font. + /// Kerning is letter-spacing + float GetKerning () const { return m_Kerning; } + + /// Get the uv & vertex info for unicode character charCode into 'verts' & 'uvs' + void GetCharacterRenderInfo( unsigned int charCode, Rectf& verts, Rectf& uvs, bool &flipped ) const; + void GetCharacterRenderInfo( unsigned int charCode, int size, unsigned int style, Rectf& verts, Rectf& uvs, bool &flipped ) const; + + + /// Get the width of a character + float GetCharacterWidth (unsigned int charCode, int size = 0, unsigned int style = kStyleDefault) const; + + /// Get the width of the tab character + float GetTabWidth () const; + + /// Get the material of this font. + PPtr<Material> GetMaterial () const { return m_DefaultMaterial; } + + /// Set the default material + void SetMaterial (PPtr<Material> material) {m_DefaultMaterial = material;} + + /// Get the texture of this font... + PPtr<Texture> GetTexture () const { return m_Texture; } + + /// Set the texture + void SetTexture(PPtr<Texture> texture) {m_Texture = texture;} + + /// Get the line spacing of this font + float GetLineSpacing (int size = 0) const; + + /// Get the line spacing of this font + void SetLineSpacing (float spacing) { m_LineSpacing = spacing; } + + /// Adds character info (only for non grid fonts) + void AddCharacterInfoEntry( const Rectf& uv, const Rectf& vert, float width, int character, bool flipped, int size = 0, unsigned int style = kStyleDefault); + + /// Set Ascii Start Offset + void SetAsciiStartOffset(short val) {m_AsciiStartOffset = val;} + + // Old "Grid fonts" where scaled differently when using non-pixel correct rendering then + // normal fonts. So we need this to emulate the effect. + float GetDeprecatedPixelScale () const { return m_PixelScale; } + + /// Set the Convert Case property + enum { kDynamicFont = -2, kUnicodeSet = -1, kDontConvertCase = 0, kUpperCase = 1, kLowerCase = 2, kCustomSet = 3 }; + void SetConvertCase(int val) {m_ConvertCase = val;} + int GetConvertCase() { return m_ConvertCase; } + + typedef vector_map<std::pair<UnicodeChar, UnicodeChar>, float, KerningCompare> KerningValues; + /// Get the kerning values for modifying in place + KerningValues &GetKerningValues() { return m_KerningValues; } + + /// Set the font size, called by the truetype font importer + void SetFontSize(int val) { m_FontSize = val; } + + /// Get the font size, used to find pixel height of the chars in the font + int GetFontSize() const { return m_FontSize; } + + void SetAscent(float val) { m_Ascent = val; } + float GetAscent() const { return m_Ascent; } + + void SetCharacterSpacing (int val) { m_CharacterSpacing = val; } + void SetCharacterPadding (int val) { m_CharacterPadding = val; } + + /// Does this font have a definition for a specific character? + bool HasCharacter (unsigned int unicodeChar, int size = 0, unsigned int style = kStyleDefault); + + bool HasCharacterInTexture (unsigned int unicodeChar, int size, unsigned int style); + + /// Dynamic Font stuff: + bool CacheFontForText (UInt16 *chars, int length, int size = 0, unsigned int style = kStyleDefault, std::vector<TextFormatChange> format = std::vector<TextFormatChange>()); + + FontNames &GetFontNames () { return m_FontNames; } + FontFallbacks &GetFontFallbacks () { return m_FallbackFonts; } + void SetFontNames (FontNames &names); + + UNITY_VECTOR(kMemFont,char) &GetFontData () { return m_FontData; } + void SetFontDefaultStyle (int style) { m_DefaultStyle = style; } + static void FrameComplete () { s_FrameCount++; } + bool ResetCachedTexture (); + + const CharacterInfos &GetCharacterInfos() { return m_CharacterRects; } + void SetCharacterInfos (CharacterInfos &infos) { m_CharacterRects = infos; CacheRects(); } + + void SetFontRenderingMode(int val) { m_FontRenderingMode = val; } + int GetFontRenderingMode() const { return m_FontRenderingMode; } +#if UNITY_EDITOR + void SetMinimalFontTextureSize (int size) { m_TexWidth = size; m_TexHeight = size; } +#endif + +protected: + + void AddRectToCache(CharacterInfo& info); + void CacheRects(); + + /// Helper: Get the glyph code from a character, remapping cases, etc... + unsigned int GetGlyphNo( unsigned int charCode ) const; + + // The kerning map + KerningValues m_KerningValues; + + // These are only used in grid fonts + typedef std::pair<int, float> IntFloatPair; + typedef UNITY_VECTOR(kMemFont,IntFloatPair) PerCharacterKerning; + + float m_Kerning;///< Kerning of space between characters (Smaller than 1.0 pulls them together, Larger pushes them out) + float m_LineSpacing; ///< Spacing between lines as multiplum of height of a character. + int m_CharacterSpacing; + int m_CharacterPadding; + int m_AsciiStartOffset; ///< What is the first ascii character in the texture. + int m_FontSize; + + int m_ConvertCase; ///< enum { Don't change case, Convert to upper case characters, Convert to lower case characters } + PPtr<Material> m_DefaultMaterial; + PPtr<Texture> m_Texture; + + // Legacy Grid font support + float m_PixelScale; + + struct TexturePosition { + int x, y; + + TexturePosition (int _x, int _y) : x(_x), y(_y) {} + + friend bool operator < (const TexturePosition& lhs, const TexturePosition& rhs) + { + if (lhs.x + lhs.y != rhs.x + rhs.y) + return lhs.x + lhs.y < rhs.x + rhs.y; + else + return lhs.x < rhs.x; + } + }; + + struct IntRect { + int x, y, width, height; + + IntRect (int _x, int _y, int _width, int _height) : x(_x), y(_y), width(_width), height(_height) {} + + inline bool Intersects (const IntRect &r) const + { + return r.x+r.width > x && r.y+r.height > y && r.x < x + width && r.y < y + height; + } + }; + + UNITY_VECTOR(kMemFont,CharacterInfo) m_CharacterRects; + vector_set<CharacterInfo> m_UnicodeCharacterRects; + UNITY_VECTOR(kMemFont,CharacterInfo) m_AsciiCharacterRects; + + // dynamic font stuff: + void ResetPackingData (); + void GrowTexture (int maxFontSize); + UInt16 *CollectAllUsedCharacters (UInt16 *chars, int &length, int *&sizes, unsigned int *&styles); + void SetupDynamicFont (); + bool HasCharacterDynamic (unsigned int unicodeChar); + + bool AddCharacterToTexture (unsigned int unicodeChar, int size, unsigned int style); + bool IsRectFree (const IntRect &r) const; + UInt8 *GetCharacterBitmap(unsigned int &charWidth, unsigned int &charHeight, unsigned int &bufferWidth, Rectf &vert, float &advance, unsigned int unicodeChar, int size, unsigned int style); + + UNITY_VECTOR(kMemFont,char) m_FontData; + FontNames m_FontNames; + FontFallbacks m_FallbackFonts; + UNITY_VECTOR(kMemFont,IntRect) m_IntRects; + UNITY_SET(kMemFont,TexturePosition) m_TexturePositions; + UNITY_SET(kMemFont,TexturePosition)::iterator m_TexturePositionsSearchPosition; + + unsigned int m_TexWidth; + unsigned int m_TexHeight; + unsigned int m_TexMargin; + unsigned int m_SubImageSize; + unsigned int m_SubImageIndex; + static unsigned int s_FrameCount; + unsigned int m_DefaultStyle; + float m_Ascent; + int m_FontRenderingMode; + + DynamicFontData m_DynamicData; + + friend struct DynamicFontData; +}; + +namespace GetFontsManager +{ + void StaticInitialize(); + void StaticDestroy(); +} + +void GetFontPaths (std::vector<std::string> &paths); +FontNames &GetFallbacks (); +bool GetFontMetadataPreset(const std::string& name, std::string& family_name, std::string& style_name, unsigned& style_flags, unsigned& face_flags); + + +struct ScriptingCharacterInfo +{ + int index; + Rectf uv, vert; + float width; + int size, style; + bool flipped; + + void CopyFrom(const Font::CharacterInfo& inData) + { + index = inData.index; + uv = inData.uv; + vert = inData.vert; + width = inData.width; + size = inData.size; + style = inData.style; + flipped = inData.flipped; + } + void CopyTo(Font::CharacterInfo& outData) + { + outData.index = index; + outData.uv = uv; + outData.vert = vert; + outData.width = width; + outData.size = size; + outData.style = style; + outData.flipped = flipped; + } +}; + +#endif diff --git a/Runtime/Filters/Misc/GetFonts.cpp b/Runtime/Filters/Misc/GetFonts.cpp new file mode 100644 index 0000000..37bdb11 --- /dev/null +++ b/Runtime/Filters/Misc/GetFonts.cpp @@ -0,0 +1,338 @@ +#include "UnityPrefix.h" +#include "Font.h" +#include "Runtime/Utilities/File.h" +#if UNITY_LINUX +#include <ftw.h> +#endif + +#if DYNAMICFONTMODE == kDynamicFontModeFreeType || DYNAMICFONTMODE == kDynamicFontModeStb + + +typedef std::vector<UnityStr> FontDirs; +static FontDirs* gFontDirs = NULL; + +struct _FontInfo +{ + const char* family_name; + const char* style_name; + unsigned style_flags; + unsigned face_flags; +}; +typedef std::map<std::string, _FontInfo> FontMetadataMap; +static FontMetadataMap* gFontMetadata = NULL; + +static FontNames* gFontFallbacks = NULL; + +namespace GetFontsManager +{ + void StaticInitialize() + { + gFontDirs = UNITY_NEW(FontDirs, kMemFont); + gFontMetadata = UNITY_NEW(FontMetadataMap, kMemFont); + gFontFallbacks = UNITY_NEW(FontNames, kMemFont); + } + + void StaticDestroy() + { + UNITY_DELETE(gFontDirs , kMemFont); + UNITY_DELETE(gFontMetadata, kMemFont); + UNITY_DELETE(gFontFallbacks, kMemFont); + } +} + + +FontNames &GetFallbacks () +{ + if (gFontFallbacks->empty()) + { + // Make Arial first fallback for consistency, as it widely available. + gFontFallbacks->push_back("Arial"); + // Arial Unicode MS covers almost all unicode scripts, and is available on OS X (>=10.5). + gFontFallbacks->push_back("Arial Unicode MS"); + // This should catch Unicode scripts on Windows, excluding Asian scripts + gFontFallbacks->push_back("Microsoft Sans Serif"); + // This should catch Chinese on windows + gFontFallbacks->push_back("Microsoft YaHei"); + // This should catch Korean on windows + gFontFallbacks->push_back("Gulim"); + // This should catch Japanese on windows + gFontFallbacks->push_back("MS Gothic"); +#if UNITY_ANDROID + // Android system font + gFontFallbacks->push_back("Roboto"); + gFontFallbacks->push_back("NanumGothic"); + gFontFallbacks->push_back("Droid Sans"); + gFontFallbacks->push_back("Droid Sans Japanese"); + gFontFallbacks->push_back("Droid Sans Fallback"); +#elif UNITY_IPHONE + gFontFallbacks->push_back("Hiragino Kaku Gothic ProN"); + gFontFallbacks->push_back("Heiti TC"); + gFontFallbacks->push_back("AppleGothic"); + gFontFallbacks->push_back(".LastResort"); +#elif UNITY_WP8 + gFontFallbacks->push_back("Yu Gothic"); // Japanese + gFontFallbacks->push_back("Microsoft NeoGothic"); // Korean + gFontFallbacks->push_back("SimSun"); // Chinese simplified + gFontFallbacks->push_back("Microsoft Mhei"); // Chinese traditional + gFontFallbacks->push_back("Urdu Typesetting"); // Arabic +#elif UNITY_TIZEN + gFontFallbacks->push_back("Tizen Sans"); + gFontFallbacks->push_back("Tizen Sans Japanese"); + gFontFallbacks->push_back("Tizen Sans Fallback"); +#endif +#if UNITY_LINUX + gFontFallbacks->push_back("FreeSans"); + gFontFallbacks->push_back("WenQuanYi Micro Hei"); +#endif + // Unicode debugging fallback: http://en.wikipedia.org/wiki/Fallback_font + gFontFallbacks->push_back("LastResort"); + } + return *gFontFallbacks; +} + +#if UNITY_LINUX +int callback(const char *fpath, const struct stat *sb, int typeflag) +{ + if (typeflag == FTW_D) + { + gFontDirs->push_back (fpath); + } + return 0; +} +#endif + +void GetFontPaths (std::vector<std::string> &paths) +{ + paths.clear(); + + // paths should not be garbaged by the repetitive + // content accumulation in the dirs vector + gFontDirs->clear(); + +// Xbox and Wii do not have GetFolderContentsAtPath(). +#if !UNITY_XENON && !UNITY_WII +#if UNITY_OSX + gFontDirs->push_back ("/System/Library/Fonts"); + gFontDirs->push_back ("/Library/Fonts"); + string homeDir = getenv ("HOME"); + gFontDirs->push_back (homeDir + "/Library/Fonts"); + +#elif UNITY_WINRT + gFontDirs->push_back ("C:\\Windows\\Fonts"); +#elif UNITY_WIN && !UNITY_WINRT + // It must be noted that Windows installation does not necessarily have to reside on C: disk + std::string win_dir; + win_dir.resize( MAX_PATH ); + UINT const dir_len = GetWindowsDirectoryA( &win_dir.front(), win_dir.size() ); + if( 0u == dir_len ) // The function has failed, so a default is as good as any other choice + { + gFontDirs->push_back ("C:\\Windows\\Fonts"); + } + else + { + std::string::size_type old_win_dir_size = win_dir.size(); + win_dir.resize( dir_len ); + + if ( dir_len > old_win_dir_size ) + { + // Absolutely unlikely, but possible; in such a case where the previous buffer was not enough + // to hold the path to the windows directory, we simply increase the size of the buffer + // and try again to fetch the directory name. + UINT const dir_len2 = GetWindowsDirectoryA( &win_dir.front(), win_dir.size() ); + if( (dir_len2 + 1u) == win_dir.size() ) + { + win_dir.pop_back(); // Remove the embedded null terminator + } + else + { + win_dir = "C:\\Windows"; // Seriously screwed up + } + } + + gFontDirs->push_back( PlatformAppendPathName(win_dir, "Fonts") ); + } + +#elif UNITY_LINUX + ftw ("/usr/share/fonts", callback, 16); +#elif UNITY_ANDROID + gFontDirs->push_back ("/system/fonts"); +#elif UNITY_IPHONE + #if TARGET_IPHONE_SIMULATOR + gFontDirs->push_back ("/Library/Fonts"); + #else + gFontDirs->push_back ("/System/Library/Fonts/Cache"); + #endif +#elif UNITY_TIZEN + gFontDirs->push_back ("/usr/share/fonts"); + gFontDirs->push_back ("/usr/share/fallback_fonts"); +#endif + + for(int i = 0; i < gFontDirs->size(); ++i) + { + std::set<std::string> dirPaths; + if ( GetFolderContentsAtPath( (*gFontDirs)[i], dirPaths ) ) + { + for (std::set<std::string>::iterator j = dirPaths.begin(); j != dirPaths.end(); j++) + { + std::string extension = GetPathNameExtension(*j); + ToLowerInplace(extension); + if (!StrCmp(extension.c_str(), "ttf") || !StrCmp(extension.c_str(), "ttc") || !StrCmp(extension.c_str(), "otf") || !StrCmp(extension.c_str(), "dfont")) + paths.push_back(*j); + } + } + } +#endif +} + + + +static void InitFontMetadataPreset(); +bool GetFontMetadataPreset(const std::string& name, std::string& family_name, std::string& style_name, unsigned& style_flags, unsigned& face_flags) +{ + if (gFontMetadata->empty()) + { + InitFontMetadataPreset(); + } + + FontMetadataMap::iterator it = gFontMetadata->find(name); + if (it != gFontMetadata->end()) + { + family_name = it->second.family_name; + style_name = it->second.style_name; + style_flags = it->second.style_flags; + face_flags = it->second.face_flags; + return true; + } + + return false; +} + +static void InitFontMetadataPreset() +{ +#if UNITY_IPHONE + // Reading font metada on iOS devices might take few seconds when missing OS cache, so keeping preset known font table there + // iOS 4.3 + (*gFontMetadata)["AppleColorEmoji"] = (_FontInfo){"Apple Color Emoji", "Regular", 0x0, 0x19}; + (*gFontMetadata)["AppleGothic"] = (_FontInfo){"AppleGothic", "Regular", 0x0, 0x39}; + (*gFontMetadata)["Arial"] = (_FontInfo){"Arial", "Regular", 0x0, 0x59}; + (*gFontMetadata)["ArialBold"] = (_FontInfo){"Arial", "Bold", 0x2, 0x59}; + (*gFontMetadata)["ArialBoldItalic"] = (_FontInfo){"Arial", "Bold Italic", 0x3, 0x59}; + (*gFontMetadata)["ArialHB"] = (_FontInfo){"Arial Hebrew", "Regular", 0x0, 0x19}; + (*gFontMetadata)["ArialHBBold"] = (_FontInfo){"Arial Hebrew", "Bold", 0x2, 0x19}; + (*gFontMetadata)["ArialItalic"] = (_FontInfo){"Arial", "Italic", 0x1, 0x59}; + (*gFontMetadata)["ArialRoundedMTBold"] = (_FontInfo){"Arial Rounded MT Bold", "Regular", 0x0, 0x59}; + (*gFontMetadata)["BanglaSangamMN"] = (_FontInfo){"Bangla Sangam MN", "Regular", 0x0, 0x19}; + (*gFontMetadata)["CourierNew"] = (_FontInfo){"Courier New", "Regular", 0x0, 0x1f}; + (*gFontMetadata)["CourierNewBold"] = (_FontInfo){"Courier New", "Bold", 0x2, 0x1d}; + (*gFontMetadata)["CourierNewBoldItalic"] = (_FontInfo){"Courier New", "Bold Italic", 0x3, 0x1d}; + (*gFontMetadata)["CourierNewItalic"] = (_FontInfo){"Courier New", "Italic", 0x1, 0x1d}; + (*gFontMetadata)["DB_LCD_Temp-Black"] = (_FontInfo){"DB LCD Temp", "Black", 0x0, 0x19}; + (*gFontMetadata)["DevanagariSangamMN"] = (_FontInfo){"Devanagari Sangam MN", "Regular", 0x0, 0x59}; + (*gFontMetadata)["Fallback"] = (_FontInfo){".PhoneFallback", "Regular", 0x0, 0x19}; + (*gFontMetadata)["GeezaPro"] = (_FontInfo){"Geeza Pro", "Regular", 0x0, 0x19}; + (*gFontMetadata)["GeezaProBold"] = (_FontInfo){"Geeza Pro", "Bold", 0x0, 0x19}; + (*gFontMetadata)["Georgia"] = (_FontInfo){"Georgia", "Regular", 0x0, 0x19}; + (*gFontMetadata)["GeorgiaBold"] = (_FontInfo){"Georgia", "Bold", 0x2, 0x19}; + (*gFontMetadata)["GeorgiaBoldItalic"] = (_FontInfo){"Georgia", "Bold Italic", 0x3, 0x19}; + (*gFontMetadata)["GeorgiaItalic"] = (_FontInfo){"Georgia", "Italic", 0x1, 0x19}; + (*gFontMetadata)["GujaratiSangamMN"] = (_FontInfo){"Gujarati Sangam MN", "Regular", 0x0, 0x19}; + (*gFontMetadata)["GurmukhiMN"] = (_FontInfo){"Gurmukhi MN", "Regular", 0x0, 0x59}; + (*gFontMetadata)["HKGPW3UI"] = (_FontInfo){".HKGPW3UI", "Regular", 0x0, 0x59}; + (*gFontMetadata)["HiraginoKakuGothicProNW3"] = (_FontInfo){"Hiragino Kaku Gothic ProN", "W3", 0x0, 0x39}; + (*gFontMetadata)["HiraginoKakuGothicProNW6"] = (_FontInfo){"Hiragino Kaku Gothic ProN", "W6", 0x2, 0x39}; + (*gFontMetadata)["Kailasa"] = (_FontInfo){"Kailasa", "Bold", 0x0, 0x59}; + (*gFontMetadata)["KannadaSangamMN"] = (_FontInfo){"Kannada Sangam MN", "Regular", 0x0, 0x19}; + (*gFontMetadata)["LastResort"] = (_FontInfo){".LastResort", "Regular", 0x0, 0x19}; + (*gFontMetadata)["LockClock"] = (_FontInfo){".Lock Clock", "Light", 0x0, 0x59}; + (*gFontMetadata)["MalayalamSangamMN"] = (_FontInfo){"Malayalam Sangam MN", "Regular", 0x0, 0x19}; + (*gFontMetadata)["OriyaSangamMN"] = (_FontInfo){"Oriya Sangam MN", "Regular", 0x0, 0x19}; + (*gFontMetadata)["PhoneKeyCaps"] = (_FontInfo){".PhoneKeyCaps", "Regular", 0x2, 0x59}; + (*gFontMetadata)["PhoneKeyCapsTwo"] = (_FontInfo){".PhoneKeyCapsTwo", "Regular", 0x2, 0x59}; + (*gFontMetadata)["PhonepadTwo"] = (_FontInfo){".PhonepadTwo", "Regular", 0x0, 0x59}; + (*gFontMetadata)["STHeiti-Light"] = (_FontInfo){"Heiti TC", "Light", 0x0, 0x1b}; + (*gFontMetadata)["STHeiti-Medium"] = (_FontInfo){"Heiti TC", "Medium", 0x2, 0x19}; + (*gFontMetadata)["SinhalaSangamMN"] = (_FontInfo){"Sinhala Sangam MN", "Regular", 0x0, 0x59}; + (*gFontMetadata)["TamilSangamMN"] = (_FontInfo){"Tamil Sangam MN", "Regular", 0x0, 0x19}; + (*gFontMetadata)["TeluguSangamMN"] = (_FontInfo){"Telugu Sangam MN", "Regular", 0x0, 0x19}; + (*gFontMetadata)["Thonburi"] = (_FontInfo){"Thonburi", "Regular", 0x0, 0x19}; + (*gFontMetadata)["ThonburiBold"] = (_FontInfo){"Thonburi", "Bold", 0x2, 0x19}; + (*gFontMetadata)["TimesNewRoman"] = (_FontInfo){"Times New Roman", "Regular", 0x0, 0x59}; + (*gFontMetadata)["TimesNewRomanBold"] = (_FontInfo){"Times New Roman", "Bold", 0x2, 0x59}; + (*gFontMetadata)["TimesNewRomanBoldItalic"] = (_FontInfo){"Times New Roman", "Bold Italic", 0x3, 0x59}; + (*gFontMetadata)["TimesNewRomanItalic"] = (_FontInfo){"Times New Roman", "Italic", 0x1, 0x59}; + (*gFontMetadata)["TrebuchetMS"] = (_FontInfo){"Trebuchet MS", "Regular", 0x0, 0x59}; + (*gFontMetadata)["TrebuchetMSBold"] = (_FontInfo){"Trebuchet MS", "Bold", 0x2, 0x59}; + (*gFontMetadata)["TrebuchetMSBoldItalic"] = (_FontInfo){"Trebuchet MS", "Bold Italic", 0x3, 0x59}; + (*gFontMetadata)["TrebuchetMSItalic"] = (_FontInfo){"Trebuchet MS", "Italic", 0x1, 0x59}; + (*gFontMetadata)["Verdana"] = (_FontInfo){"Verdana", "Regular", 0x0, 0x59}; + (*gFontMetadata)["VerdanaBold"] = (_FontInfo){"Verdana", "Bold", 0x2, 0x19}; + (*gFontMetadata)["VerdanaBoldItalic"] = (_FontInfo){"Verdana", "Bold Italic", 0x3, 0x19}; + (*gFontMetadata)["VerdanaItalic"] = (_FontInfo){"Verdana", "Italic", 0x1, 0x19}; + (*gFontMetadata)["Zapfino"] = (_FontInfo){"Zapfino", "Regular", 0x1, 0x19}; + (*gFontMetadata)["_H_AmericanTypewriter"] = (_FontInfo){"American Typewriter", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H_Baskerville"] = (_FontInfo){"Baskerville", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H_ChalkboardSE"] = (_FontInfo){"Chalkboard SE", "Light", 0x0, 0x59}; + (*gFontMetadata)["_H_Cochin"] = (_FontInfo){"Cochin", "Regular", 0x0, 0x5b}; + (*gFontMetadata)["_H_Courier"] = (_FontInfo){"Courier", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H_Futura"] = (_FontInfo){"Futura", "Medium", 0x0, 0x19}; + (*gFontMetadata)["_H_Helvetica"] = (_FontInfo){"Helvetica", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H_HelveticaNeue"] = (_FontInfo){"Helvetica Neue", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H_HelveticaNeueExtras"] = (_FontInfo){"Helvetica Neue", "Light", 0x0, 0x19}; + (*gFontMetadata)["_H_MarkerFeltThin"] = (_FontInfo){"Marker Felt", "Thin", 0x0, 0x59}; + (*gFontMetadata)["_H_MarkerFeltWide"] = (_FontInfo){"Marker Felt", "Wide", 0x2, 0x59}; + (*gFontMetadata)["_H_Noteworthy"] = (_FontInfo){"Noteworthy", "Light", 0x0, 0x59}; + (*gFontMetadata)["_H_Palatino"] = (_FontInfo){"Palatino", "Regular", 0x0, 0x59}; + (*gFontMetadata)["_H_SnellRoundhand"] = (_FontInfo){"Snell Roundhand", "Regular", 0x0, 0x59}; + (*gFontMetadata)["_H__PO_Bodoni-Ornaments"] = (_FontInfo){"Bodoni Ornaments", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H__PO_Bodoni72-Book-SmallCaps"] = (_FontInfo){"Bodoni 72 Smallcaps", "Book", 0x0, 0x19}; + (*gFontMetadata)["_H__PO_Bodoni72-OldStyle"] = (_FontInfo){"Bodoni 72 Oldstyle", "Book", 0x0, 0x19}; + (*gFontMetadata)["_H__PO_Bodoni72"] = (_FontInfo){"Bodoni 72", "Book", 0x0, 0x19}; + (*gFontMetadata)["_H__PO_BradleyHand-Bold"] = (_FontInfo){"Bradley Hand", "Bold", 0x0, 0x19}; + (*gFontMetadata)["_H__PO_Didot"] = (_FontInfo){"Didot", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H__PO_GillSans"] = (_FontInfo){"Gill Sans", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H__PO_HoeflerText"] = (_FontInfo){"Hoefler Text", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H__PO_Optima"] = (_FontInfo){"Optima", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H__PO_PartyLET"] = (_FontInfo){"Party LET", "Plain", 0x0, 0x19}; + (*gFontMetadata)["_H__PO_ZapfDingbats"] = (_FontInfo){"Zapf Dingbats", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_PO_AcademyEngraved"] = (_FontInfo){"Academy Engraved LET", "Plain", 0x0, 0x19}; + (*gFontMetadata)["_PO_Chalkduster"] = (_FontInfo){"Chalkduster", "Regular", 0x0, 0x59}; + (*gFontMetadata)["_PO_Copperplate"] = (_FontInfo){"Copperplate", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_PO_HiraginoMinchoProNW3"] = (_FontInfo){"Hiragino Mincho ProN", "W3", 0x0, 0x39}; + (*gFontMetadata)["_PO_HiraginoMinchoProNW6"] = (_FontInfo){"Hiragino Mincho ProN", "W6", 0x2, 0x39}; + (*gFontMetadata)["_PO_Papyrus"] = (_FontInfo){"Papyrus", "Regular", 0x0, 0x19}; +// iOS 5.0 + (*gFontMetadata)["AcademyEngraved"] = (_FontInfo){"Academy Engraved LET", "Plain", 0x0, 0x19}; + (*gFontMetadata)["Chalkduster"] = (_FontInfo){"Chalkduster", "Regular", 0x0, 0x59}; + (*gFontMetadata)["Copperplate"] = (_FontInfo){"Copperplate", "Regular", 0x0, 0x19}; + (*gFontMetadata)["EuphemiaCAS"] = (_FontInfo){"Euphemia UCAS", "Regular", 0x0, 0x19}; + (*gFontMetadata)["HiraginoMinchoProNW3"] = (_FontInfo){"Hiragino Mincho ProN", "W3", 0x0, 0x39}; + (*gFontMetadata)["HiraginoMinchoProNW6"] = (_FontInfo){"Hiragino Mincho ProN", "W6", 0x2, 0x39}; + (*gFontMetadata)["Papyrus"] = (_FontInfo){"Papyrus", "Regular", 0x0, 0x19}; + (*gFontMetadata)["STFangsongCore"] = (_FontInfo){".STFangsongCore", "Regular", 0x0, 0x39}; + (*gFontMetadata)["STKaitiCore"] = (_FontInfo){".STKaitiCore", "Regular", 0x0, 0x39}; + (*gFontMetadata)["STSongCore"] = (_FontInfo){".STSongCore", "Regular", 0x0, 0x39}; + (*gFontMetadata)["_H_Bodoni-Ornaments"] = (_FontInfo){"Bodoni Ornaments", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H_Bodoni72-Book-SmallCaps"] = (_FontInfo){"Bodoni 72 Smallcaps", "Book", 0x0, 0x19}; + (*gFontMetadata)["_H_Bodoni72-OldStyle"] = (_FontInfo){"Bodoni 72 Oldstyle", "Book", 0x0, 0x19}; + (*gFontMetadata)["_H_Bodoni72"] = (_FontInfo){"Bodoni 72", "Book", 0x0, 0x19}; + (*gFontMetadata)["_H_BradleyHand-Bold"] = (_FontInfo){"Bradley Hand", "Bold", 0x0, 0x19}; + (*gFontMetadata)["_H_Didot"] = (_FontInfo){"Didot", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H_GillSans"] = (_FontInfo){"Gill Sans", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H_HoeflerText"] = (_FontInfo){"Hoefler Text", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H_Marion"] = (_FontInfo){"Marion", "Regular", 0x0, 0x59}; + (*gFontMetadata)["_H_Optima"] = (_FontInfo){"Optima", "Regular", 0x0, 0x19}; + (*gFontMetadata)["_H_PartyLET"] = (_FontInfo){"Party LET", "Plain", 0x0, 0x19}; + (*gFontMetadata)["_H_ZapfDingbats"] = (_FontInfo){"Zapf Dingbats", "Regular", 0x0, 0x19}; +// iOS 6.0 + (*gFontMetadata)["AppleColorEmoji@2x"] = (_FontInfo){"Apple Color Emoji", "Regular", 0x0, 0x39}; + (*gFontMetadata)["AppleSDGothicNeoBold"] = (_FontInfo){"Apple SD Gothic Neo", "Bold", 0x0, 0x39}; + (*gFontMetadata)["AppleSDGothicNeoMedium"] = (_FontInfo){"Apple SD Gothic Neo", "Medium", 0x0, 0x39}; + (*gFontMetadata)["Symbol"] = (_FontInfo){"Symbol", "Regular", 0x0, 0x1b}; + (*gFontMetadata)["_H_Avenir"] = (_FontInfo){"Avenir", "Book", 0x0, 0x59}; + (*gFontMetadata)["_H_AvenirNext"] = (_FontInfo){"Avenir Next", "Bold", 0x2, 0x59}; + (*gFontMetadata)["_H_AvenirNextCondensed"] = (_FontInfo){"Avenir Next Condensed", "Bold", 0x2, 0x59}; + +#endif +} + +#endif diff --git a/Runtime/Filters/Misc/LineBuilder.cpp b/Runtime/Filters/Misc/LineBuilder.cpp new file mode 100644 index 0000000..5ef855e --- /dev/null +++ b/Runtime/Filters/Misc/LineBuilder.cpp @@ -0,0 +1,95 @@ +#include "UnityPrefix.h" +#include "LineBuilder.h" +#include "Runtime/Math/Matrix4x4.h" +#include "Runtime/Geometry/AABB.h" +#include "Runtime/Shaders/GraphicsCaps.h" +#include "Runtime/GfxDevice/GfxDevice.h" + +inline Vector2f Calculate2DLineExtrusionAverage (const Vector3f& p0, const Vector3f& delta, const Vector3f& delta2, float halfWidth) +{ + Vector2f dif; + dif.x = p0.y * delta.z - p0.z * delta.y; + dif.y = p0.z * delta.x - p0.x * delta.z; +// dif = NormalizeFast(dif); + + Vector2f dif2; + dif2.x = p0.y * delta2.z - p0.z * delta2.y; + dif2.y = p0.z * delta2.x - p0.x * delta2.z; +// dif2 = NormalizeFast(dif2); + + dif += dif2; + dif = NormalizeFast(dif); + + dif.x *= halfWidth; + dif.y *= halfWidth; + + return dif; +/* Vector2f dif; + dif.x = p0.y * delta.z - p0.z * delta.y; + dif.y = p0.z * delta.x - p0.x * delta.z; +*/ +} + +/// \todo have optional input lengths for speed +/// \todo optimize the dif cross product (z is unused) +void Build3DLine( LineParameters *param, const Vector3f *inVertices, int vertexCount ) +{ + Assert(vertexCount > 1); + Assert(param->outVertices && param->outAABB); + + LineVertex *outVertices = param->outVertices; + Matrix4x4f matrix = param->cameraTransform; + + // As Gradient->GetFixed() needs an unnormalized position in 16.16 format + // (upper 16 color index, lower 16 - how far between), the max value is + // (nr of gradient colors - 1)*2^16 - 1 + float fixedMult = (float)(((k3DLineGradientSize - 1) << 16) - 1); + + GfxDevice& device = GetGfxDevice(); + + // Skip last vertex + Vector3f delta = matrix.MultiplyPoint3 (inVertices[0]) - matrix.MultiplyPoint3 (inVertices[1]); + for (int i=0;i<vertexCount;i++) + { + // Don't accumulate by adding deltaU, as the rounding error accumulates as well. + // Calculate u each time anew instead. + float u = i/(float)(vertexCount - 1); + + // Calculate width and figure a cross section that faces the camera + Vector3f p0 = matrix.MultiplyPoint3 (inVertices[i]); + + if (i+1 != vertexCount) + { + Vector3f p1 = matrix.MultiplyPoint3 (inVertices[i+1]); + delta = p0 - p1; + } + + float width = Lerp(param->startWidth, param->endWidth, u); + Vector2f dif = Calculate2DLineExtrusion (p0, delta, width * 0.5F); + + ColorRGBA32 color; + if(param->gradient) + color = param->gradient->GetFixed (UInt32(u*fixedMult)); + else + // TODO: rewrite Gradient, so that we can elegantly use it here as well + color = Lerp((ColorRGBAf)param->color1, (ColorRGBAf)param->color2, u); + + // Swizzle color of the renderer requires it + color = device.ConvertToDeviceVertexColor(color); + + // One vertex + outVertices->vert.Set( p0.x - dif.x, p0.y - dif.y, p0.z ); + outVertices->color = color; + outVertices->uv.Set( u, 1.0f ); + ++outVertices; + + // And another vertex + outVertices->vert.Set( p0.x + dif.x, p0.y + dif.y, p0.z ); + outVertices->color = color; + outVertices->uv.Set( u, 0.0f ); + ++outVertices; + + param->outAABB->Encapsulate (inVertices[i]); + } + param->outAABB->Encapsulate (inVertices[vertexCount-1]); +} diff --git a/Runtime/Filters/Misc/LineBuilder.h b/Runtime/Filters/Misc/LineBuilder.h new file mode 100644 index 0000000..ffc46a8 --- /dev/null +++ b/Runtime/Filters/Misc/LineBuilder.h @@ -0,0 +1,88 @@ +#ifndef LINEBUILDER_H +#define LINEBUILDER_H + +class MinMaxAABB; + +#include "Runtime/Math/Gradient.h" +#include "Runtime/Math/Matrix4x4.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Vector2.h" +#include "Runtime/Math/Color.h" + +enum { k3DLineGradientSize = 5 }; + +struct LineVertex { + Vector3f vert; + ColorRGBA32 color; + Vector2f uv; +}; + +// Settings for the Build3DLine function +// Instead of passing lots of parameters, make one of these and use that instead. +struct LineParameters +{ + DECLARE_SERIALIZE (LineParameters) + + LineVertex* outVertices; // Output vertices; 2 * input vertices size + class MinMaxAABB *outAABB; // AABB to be generated + + // ptr to the gradient used for color generation + GradientDeprecated<k3DLineGradientSize> *gradient; + ColorRGBA32 color1; + ColorRGBA32 color2; + Matrix4x4f cameraTransform; + + float startWidth; ///< The width (in worldspace) at the line start. + float endWidth; ///< The width (in worldspace) at the line end. + + LineParameters () : + outVertices (NULL), outAABB (NULL), + gradient (NULL), startWidth (1), endWidth (1), + color1 (0), color2 (0) { cameraTransform = Matrix4x4f::identity; } +}; + +template<class TransferFunction> +inline void LineParameters::Transfer (TransferFunction& transfer) { + TRANSFER_SIMPLE (startWidth); + TRANSFER_SIMPLE (endWidth); + transfer.Transfer (color1, "m_StartColor", kSimpleEditorMask); + transfer.Transfer (color2, "m_EndColor", kSimpleEditorMask); +} + +/// build the mesh for a 3D line segement seen from the current camera +/// @param param generation parameters. +/// @param in ptr to the input vertices +/// @param vertexCount the number of vertices in inVertices +void Build3DLine (LineParameters *param, const Vector3f *in, int vertexCount); + +/// Calculates the 2D line extrusion, so that the line is halfWidth * 2 wide and always faces the viewer. +/// The start point is p0, the endpoint is p0 + delta +/// The points are expected to be in camera space. +inline Vector2f Calculate2DLineExtrusion (const Vector3f& p0, const Vector3f& delta, float halfWidth) +{ + #if 1 + Vector2f dif; + dif.x = p0.y * delta.z - p0.z * delta.y; + dif.y = p0.z * delta.x - p0.x * delta.z; + + dif = NormalizeFast(dif); + + dif.x *= halfWidth; + dif.y *= halfWidth; + + return dif; + + #else + + Vector3f dif = Cross (p0, delta); + dif = NormalizeFast (dif); + + dif.x *= halfWidth; + dif.y *= halfWidth; + + return Vector2f (dif.x, dif.y); + + #endif +} + +#endif diff --git a/Runtime/Filters/Misc/LineRenderer.cpp b/Runtime/Filters/Misc/LineRenderer.cpp new file mode 100644 index 0000000..1bab153 --- /dev/null +++ b/Runtime/Filters/Misc/LineRenderer.cpp @@ -0,0 +1,195 @@ +#include "UnityPrefix.h" +#include "LineRenderer.h" +#include "Runtime/Mono/MonoManager.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h" +#include "Runtime/Camera/RenderManager.h" +#include "Runtime/Camera/Camera.h" +#include "Runtime/Shaders/VBO.h" +#include "Runtime/GfxDevice/GfxDevice.h" +#include "Runtime/Profiler/Profiler.h" + +IMPLEMENT_CLASS_INIT_ONLY (LineRenderer) +IMPLEMENT_OBJECT_SERIALIZE (LineRenderer) + +LineRenderer::LineRenderer (MemLabelId label, ObjectCreationMode mode) +: Super(kRendererLine, label, mode) +{ + SetVisible (false); +} + +LineRenderer::~LineRenderer () +{ +} + +void LineRenderer::InitializeClass () +{ + RegisterAllowNameConversion (LineRenderer::GetClassStringStatic(), "m_WorldSpace", "m_UseWorldSpace"); +} + + +void LineRenderer::SetVertexCount(int count) +{ + if(count < 0) + { + count = 0; + ErrorString ("LineRenderer.SetVertexCount: Vertex count can't be set to negative value!"); + } + UpdateManagerState( true ); + m_Positions.resize(count); + SetVisible (m_Positions.size() >= 2); + SetDirty(); + BoundsChanged(); +} + +void LineRenderer::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad(awakeMode); + SetVisible (m_Positions.size() >= 2); + + if ((awakeMode & kDidLoadFromDisk) == 0) + BoundsChanged(); +} + +void LineRenderer::SetPosition (int index, const Vector3f& position) +{ + SetDirty(); + UpdateManagerState( true ); + if (index < m_Positions.size() && index >= 0) + m_Positions[index] = position; + else + ErrorString("LineRenderer.SetPosition index out of bounds!"); + BoundsChanged(); +} + +PROFILER_INFORMATION(gSubmitVBOProfileLine, "Mesh.SubmitVBO", kProfilerRender) + + +void LineRenderer::Render (int subsetIndex, const ChannelAssigns& channels) +{ + if( m_Positions.size() < 2 ) + return; + + Vector3f* lineInVerts = NULL; + ALLOC_TEMP(lineInVerts, Vector3f, m_Positions.size()); + + MinMaxAABB mmAABB = MinMaxAABB(Vector3f::zero, Vector3f::zero); + + if (m_UseWorldSpace) + { + memcpy (lineInVerts, &m_Positions[0], m_Positions.size()*sizeof(Vector3f)); + } + else + { + Transform& tc = GetComponent(Transform); + int idx = 0; + for (PositionVector::iterator j = m_Positions.begin(); j != m_Positions.end(); ++j, ++idx) + { + lineInVerts[idx] = tc.TransformPoint(*j); + } + } + + // Get VBO chunk + int stripCount = m_Positions.size() * 2; + GfxDevice& device = GetGfxDevice(); + DynamicVBO& vbo = device.GetDynamicVBO(); + LineVertex* vbPtr; + if( !vbo.GetChunk( (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor), + stripCount, 0, + DynamicVBO::kDrawTriangleStrip, + (void**)&vbPtr, NULL ) ) + { + return; + } + + // Generate line into the chunk + m_Parameters.outVertices = vbPtr; + m_Parameters.outAABB = &mmAABB; + m_Parameters.cameraTransform = GetCurrentCamera().GetWorldToCameraMatrix(); + Build3DLine (&m_Parameters, lineInVerts, m_Positions.size()); + + vbo.ReleaseChunk( stripCount, 0 ); + + // We can't set the view matrix since that breaks shadow maps (case 490315) + // Set the world matrix instead so it cancels out the usual view matrix + // i.e. it transforms from camera space back to world space + device.SetWorldMatrix(GetCurrentCamera().GetCameraToWorldMatrix().GetPtr()); + + if (m_CustomProperties) + device.SetMaterialProperties (*m_CustomProperties); + + PROFILER_BEGIN(gSubmitVBOProfileLine, this) + vbo.DrawChunk (channels); + GPU_TIMESTAMP(); + PROFILER_END +} + +void LineRenderer::UpdateTransformInfo () +{ + const Transform& transform = GetTransform(); + if (m_TransformDirty) + { + m_TransformInfo.invScale = 1.0f; + // will return a cached matrix most of the time + m_TransformInfo.transformType = transform.CalculateTransformMatrix (m_TransformInfo.worldMatrix);; + } + + if (m_BoundsDirty) + { + MinMaxAABB minmax; + minmax.Init(); + for (PositionVector::const_iterator i = m_Positions.begin(), itEnd = m_Positions.end(); i != itEnd; ++i) + minmax.Encapsulate (*i); + + if (m_UseWorldSpace) + { + m_TransformInfo.worldAABB = minmax; + TransformAABB (m_TransformInfo.worldAABB, transform.GetWorldToLocalMatrix(), m_TransformInfo.localAABB); + } + else + { + m_TransformInfo.localAABB = minmax; + TransformAABB (m_TransformInfo.localAABB, transform.GetLocalToWorldMatrix(), m_TransformInfo.worldAABB); + } + } +} + + + +void LineRenderer::UpdateRenderer() +{ + Super::UpdateRenderer(); + if (m_BoundsDirty) + { + BoundsChanged(); + } +} + +void LineRenderer::Reset() +{ + Super::Reset(); + m_UseWorldSpace = true; + m_Positions.clear(); + m_Positions.push_back (Vector3f (0,0,0)); + m_Positions.push_back (Vector3f (0,0,1)); + m_Parameters.color1 = ColorRGBA32(255, 255, 255, 255); + m_Parameters.color2 = ColorRGBA32(255, 255, 255, 255); + SetVisible (true); +} + +void LineRenderer::SetUseWorldSpace (bool space) +{ + m_UseWorldSpace = space; + SetDirty(); + UpdateManagerState( true ); + BoundsChanged(); +} + +template<class TransferFunction> inline +void LineRenderer::Transfer (TransferFunction& transfer) { + Super::Transfer (transfer); + TRANSFER_SIMPLE (m_Positions); + TRANSFER_SIMPLE (m_Parameters); + TRANSFER (m_UseWorldSpace); +} diff --git a/Runtime/Filters/Misc/LineRenderer.h b/Runtime/Filters/Misc/LineRenderer.h new file mode 100644 index 0000000..3d05758 --- /dev/null +++ b/Runtime/Filters/Misc/LineRenderer.h @@ -0,0 +1,61 @@ +#ifndef LINERENDERER_H +#define LINERENDERER_H + +#include <vector> +#include "Runtime/Filters/Renderer.h" +#include "Runtime/Math/Color.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Geometry/AABB.h" +#include "LineBuilder.h" + + + +// Renders a freeform texture/colored line in 3D space. +// (heavily based on TrailRenderer code, so most comments apply to both) +class LineRenderer : public Renderer { +public: + REGISTER_DERIVED_CLASS (LineRenderer, Renderer) + DECLARE_OBJECT_SERIALIZE (LineRenderer) + + LineRenderer (MemLabelId label, ObjectCreationMode mode); + + virtual void Reset (); + + virtual void Render (int materialIndex, const ChannelAssigns& channels); + + // Can operate in either local or world space, so we need to fill whole transform info ourselves + virtual void UpdateTransformInfo(); + + void SetPosition (int index, const Vector3f& position); + + void SetVertexCount(int count); + + void SetColors(const ColorRGBAf& c0, const ColorRGBAf& c1) { m_Parameters.color1 = c0; m_Parameters.color2 = c1; SetDirty(); } + + void SetWidth(float startWidth,float endWidth) + { + m_Parameters.startWidth = startWidth; + m_Parameters.endWidth = endWidth; + BoundsChanged(); + SetDirty(); + } + + bool GetUseWorldSpace () { return m_UseWorldSpace; } + void SetUseWorldSpace (bool space); + + void AwakeFromLoad(AwakeFromLoadMode mode); + static void InitializeClass (); + +protected: + // from Renderer + virtual void UpdateRenderer(); + +private: +// bool m_BoundsDirty; + bool m_UseWorldSpace; ///< Draw lines in worldspace (or localspace) + LineParameters m_Parameters; + typedef UNITY_VECTOR(kMemRenderer,Vector3f) PositionVector; + PositionVector m_Positions; +}; + +#endif diff --git a/Runtime/Filters/Misc/MiniCoreText.h b/Runtime/Filters/Misc/MiniCoreText.h new file mode 100644 index 0000000..1027645 --- /dev/null +++ b/Runtime/Filters/Misc/MiniCoreText.h @@ -0,0 +1,38 @@ +#if !defined(MINICORETEXT_H) +#define MINICORETEXT_H + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef float CGFloat; +typedef const struct __CTFont * CTFontRef; +typedef const struct __CTFontDescriptor * CTFontDescriptorRef; +typedef uint32_t CTFontOrientation; +typedef const struct __CTLine * CTLineRef; + +enum { + kCTFontDefaultOrientation = 0, +}; + +extern const CFStringRef kCTForegroundColorAttributeName WEAK_IMPORT_ATTRIBUTE; +extern const CFStringRef kCTFontAttributeName WEAK_IMPORT_ATTRIBUTE; + + +CTFontRef CTFontCreateWithName(CFStringRef name, CGFloat size, const CGAffineTransform *matrix) WEAK_IMPORT_ATTRIBUTE; +CTFontRef CTFontCreateWithGraphicsFont(CGFontRef graphicsFont, CGFloat size, const CGAffineTransform *matrix, CTFontDescriptorRef attributes) WEAK_IMPORT_ATTRIBUTE; +CGFloat CTFontGetAscent(CTFontRef font) WEAK_IMPORT_ATTRIBUTE; +CGFloat CTFontGetDescent(CTFontRef font) WEAK_IMPORT_ATTRIBUTE; +Boolean CTFontGetGlyphsForCharacters(CTFontRef font, const UniChar characters[], CGGlyph glyphs[], CFIndex count) WEAK_IMPORT_ATTRIBUTE; +CGRect CTFontGetBoundingRectsForGlyphs(CTFontRef font, CTFontOrientation orientation, const CGGlyph glyphs[], CGRect boundingRects[], CFIndex count ) WEAK_IMPORT_ATTRIBUTE; +double CTFontGetAdvancesForGlyphs(CTFontRef font, CTFontOrientation orientation, const CGGlyph glyphs[], CGSize advances[], CFIndex count) WEAK_IMPORT_ATTRIBUTE; +CTLineRef CTLineCreateWithAttributedString(CFAttributedStringRef string) WEAK_IMPORT_ATTRIBUTE; +void CTLineDraw(CTLineRef line, CGContextRef context) WEAK_IMPORT_ATTRIBUTE; +CTFontRef CTFontCreateForString(CTFontRef currentFont, CFStringRef string, CFRange range) WEAK_IMPORT_ATTRIBUTE; +CGFontRef CGFontCreateWithDataProvider(CGDataProviderRef provider) WEAK_IMPORT_ATTRIBUTE; + +#if defined(__cplusplus) +} +#endif + +#endif
\ No newline at end of file diff --git a/Runtime/Filters/Misc/TextMesh.cpp b/Runtime/Filters/Misc/TextMesh.cpp new file mode 100644 index 0000000..80b6b76 --- /dev/null +++ b/Runtime/Filters/Misc/TextMesh.cpp @@ -0,0 +1,298 @@ +#include "UnityPrefix.h" +#include "TextMesh.h" +#include "Runtime/Filters/Mesh/MeshRenderer.h" +#include "Runtime/IMGUI/TextMeshGenerator2.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Font.h" +#include "Runtime/Misc/ResourceManager.h" +#include "Runtime/Filters/Mesh/LodMesh.h" + +using namespace std; +namespace TextMesh_Static +{ +static Font* gDefaultFont = NULL; +} + + +TextMesh::TextMesh (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ + m_Mesh = NULL; + m_FontSize = 0; + m_FontStyle = 0; + m_RichText = true; + m_Color = 0xffffffff; +} + +TextMesh::~TextMesh () +{ + DestroySingleObject(m_Mesh); +} + +Mesh* TextMesh::GetMesh () +{ + if (m_Mesh) + return m_Mesh; + else + { + m_Mesh = NEW_OBJECT (Mesh); + m_Mesh->Reset(); + m_Mesh->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad); + + m_Mesh->SetHideFlags(kHideAndDontSave); + return m_Mesh; + } +} + +void TextMesh::Reset () { + Super::Reset(); + + m_OffsetZ = 0.0f; + m_CharacterSize = 1.0f; + m_Anchor = kUpperLeft; + m_Alignment = kLeft; + m_LineSpacing = 1.0F; + m_TabSize = 4.0F; +} + +Font * TextMesh::GetFont () const { + using namespace TextMesh_Static; + Font *f = m_Font; + if (!f) { + if (!gDefaultFont) + gDefaultFont = GetBuiltinResource<Font> (kDefaultFontName); + return gDefaultFont; + } + else { + return f; + } +} + +void TextMesh::AwakeFromLoad(AwakeFromLoadMode awakeMode) { + Super::AwakeFromLoad (awakeMode); + + if (IsActive()) + { + SetupMeshRenderer (); + ApplyToMesh (); + } +} + +void TextMesh::SetText (const string& text) { + if (m_Text != text) + { + m_Text = text; + ApplyToMesh(); + } + SetDirty(); +} + +void TextMesh::SetFont (PPtr<Font> font) +{ + if (m_Font != font) + { + m_Font = font; + ApplyToMesh(); + } + SetDirty(); +} + +void TextMesh::SetFontSize (int size) +{ + if (m_FontSize != size) + { + m_FontSize = size; + ApplyToMesh(); + } + SetDirty(); +} + +void TextMesh::SetFontStyle (int style) +{ + if (m_FontStyle != style) + { + m_FontStyle = style; + ApplyToMesh(); + } + SetDirty(); +} + +void TextMesh::SetAlignment(short alignment) +{ + if (m_Alignment != alignment) + { + m_Alignment = alignment; + ApplyToMesh(); + } + SetDirty(); +} + +void TextMesh::SetOffsetZ(float offset) +{ + if (m_OffsetZ != offset) + { + m_OffsetZ = offset; + ApplyToMesh(); + } + SetDirty(); +} + +void TextMesh::SetAnchor(short anchor) +{ + if (m_Anchor != anchor) + { + m_Anchor = anchor; + ApplyToMesh(); + } + SetDirty(); +} + +void TextMesh::SetCharacterSize(float characterSize) +{ + if (m_CharacterSize != characterSize) + { + m_CharacterSize = characterSize; + ApplyToMesh(); + } + SetDirty(); +} + +void TextMesh::SetLineSpacing(float lineSpacing) +{ + if (m_LineSpacing != lineSpacing) + { + m_LineSpacing = lineSpacing; + ApplyToMesh(); + } + SetDirty(); +} + +void TextMesh::SetTabSize(float tabSize) +{ + if (m_TabSize != tabSize) + { + m_TabSize = tabSize; + ApplyToMesh(); + } + SetDirty(); +} + +void TextMesh::SetRichText(bool richText) +{ + if (m_RichText != richText) + { + m_RichText = richText; + ApplyToMesh(); + } + SetDirty(); +} + +void TextMesh::SetColor(const ColorRGBA32 color) +{ + if (m_Color != color) + { + m_Color = color; + ApplyToMesh(); + } + SetDirty(); +} + +void TextMesh::SetupMeshRenderer () { + if (IsActive ()) + { + MeshRenderer* renderer = QueryComponent(MeshRenderer); + if (renderer) + renderer->SetSharedMesh(GetMesh()); + } +} + +void TextMesh::DidAddComponent () { + if (IsActive ()) + { + MeshRenderer* renderer = QueryComponent(MeshRenderer); + if (renderer) + renderer->SetSharedMesh(GetMesh()); + } +} + +void TextMesh::ApplyToMesh () +{ + Mesh * mesh = GetMesh(); + // Setup textmesh generator + TextMeshGenerator2 &tmgen = TextMeshGenerator2::Get (UTF16String(m_Text.c_str()), GetFont (), (TextAnchor)m_Anchor, (TextAlignment)m_Alignment, 0, m_TabSize, m_LineSpacing, m_RichText, false, m_Color, m_FontSize, m_FontStyle); + + Vector2f size = tmgen.GetSize (); + Vector2f offset = tmgen.GetTextOffset (Rectf (0, 0, -size.x, size.y * 2)); + switch (m_Alignment) + { + case kRight: offset.x += size.x; break; + case kCenter: offset.x += size.x * 0.5f; break; + } + + Mesh* srcMesh = tmgen.GetMesh (); + Matrix4x4f m; + Vector3f scale(m_CharacterSize, -m_CharacterSize, m_CharacterSize); + scale *= GetFont()->GetDeprecatedPixelScale (); + m.SetTranslate (Vector3f(offset.x * scale.x, offset.y * -scale.y, m_OffsetZ)); + m.Scale(scale); + mesh->CopyTransformed(*srcMesh, m); + + // Mesh CopyTransformed does not update local AABB! Kind of scared to change it there, + // so instead manually transform the AABB here. + const AABB& bounds = mesh->GetLocalAABB(); + AABB xformBounds; + TransformAABB (bounds, m, xformBounds); + mesh->SetLocalAABB (xformBounds); + + MeshRenderer* meshRenderer = QueryComponent(MeshRenderer); + if (meshRenderer) + meshRenderer->SetSharedMesh(mesh); +} + +IMPLEMENT_CLASS_HAS_INIT (TextMesh) +IMPLEMENT_OBJECT_SERIALIZE (TextMesh) + +template<class TransferFunction> inline +void TextMesh::Transfer (TransferFunction& transfer) +{ + transfer.SetVersion (3); + Super::Transfer (transfer); + TRANSFER(m_Text); + TRANSFER(m_OffsetZ); + TRANSFER(m_CharacterSize); + TRANSFER(m_LineSpacing); + TRANSFER(m_Anchor); + TRANSFER(m_Alignment); + TRANSFER(m_TabSize); + + TRANSFER(m_FontSize); + TRANSFER(m_FontStyle); + + TRANSFER(m_RichText); + + transfer.Align(); + + TRANSFER (m_Font); + TRANSFER (m_Color); + + + #if UNITY_EDITOR + // Renamed m_Settings to m_Font in version 1.2.2 + if (transfer.IsOldVersion(1)) + { + transfer.Transfer(m_Font, "m_Settings"); + } + + // In version 1.5.0 line spacing is multiplicative instead of additive + if (transfer.IsOldVersion(1) || transfer.IsOldVersion(2)) + { + Font* font = GetFont(); + m_LineSpacing = (font->GetLineSpacing() + m_LineSpacing) / font->GetLineSpacing(); + } + #endif +} + +void TextMesh::InitializeClass () +{ + REGISTER_MESSAGE_VOID (TextMesh, kDidAddComponent, DidAddComponent); +} diff --git a/Runtime/Filters/Misc/TextMesh.h b/Runtime/Filters/Misc/TextMesh.h new file mode 100644 index 0000000..1a71c8b --- /dev/null +++ b/Runtime/Filters/Misc/TextMesh.h @@ -0,0 +1,99 @@ +#ifndef TEXTMESH_H +#define TEXTMESH_H + +#include "Runtime/Filters/Mesh/Mesh.h" +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Math/Color.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Vector2.h" +#include "Runtime/Geometry/AABB.h" +#include <string> +#include <vector> + +using std::vector; +class Font; +class Mesh; + + + + +class TextMesh : public Unity::Component { + public: + REGISTER_DERIVED_CLASS (TextMesh, Component) + DECLARE_OBJECT_SERIALIZE (TextMesh) + + TextMesh (MemLabelId label, ObjectCreationMode mode); + // ~TextMesh (); declared-by-macro + + const UnityStr& GetText () const { return m_Text; } + void SetText (const std::string& text); + + void SetFont (PPtr<Font> font); + Font * GetFont () const; + + void SetFontSize (int size); + int GetFontSize() const { return m_FontSize; } + + void SetFontStyle (int style); + int GetFontStyle() const { return m_FontStyle; } + + void SetOffsetZ(float offset); + float GetOffsetZ(){ return m_OffsetZ; } + + void SetAlignment(short alignment); + short GetAlignment(){ return m_Alignment; } + + void SetAnchor(short anchor); + short GetAnchor(){ return m_Anchor; } + + void SetCharacterSize(float characterSize); + float GetCharacterSize(){ return m_CharacterSize; } + + void SetLineSpacing(float lineSpacing); + float GetLineSpacing(){ return m_LineSpacing; } + + void SetTabSize(float tabSize); + float GetTabSize(){ return m_TabSize; } + + void SetRichText(bool richText); + bool GetRichText() { return m_RichText; } + + void SetColor(const ColorRGBA32 color); + ColorRGBA32 GetColor() const { return m_Color; } + + void AwakeFromLoad(AwakeFromLoadMode mode); + + virtual void Reset (); + void DidAddComponent (); + + static void InitializeClass(); + static void CleanupClass() {} + + void ApplyToMesh (); + private: + + void SetupMeshRenderer(); + + UnityStr m_Text; + + PPtr<Font> m_Font; + + float m_OffsetZ; ///< How much to offset the generated mesh from the Z-position=0. + short m_Alignment; ///< enum { left, center, right } + short m_Anchor; ///< Where the text-mesh is anchored related to local origo. enum { upper left, upper center, upper right, middle left, middle center, middle right, lower left, lower center, lower right } + float m_CharacterSize; ///< Size of one character (as its height, since Aspect may change its width) + float m_LineSpacing; ///< Spacing between lines as multiplum of height of a character. + float m_TabSize; ///< Length of one tab + + int m_FontSize; ///<The font size to use. Set to 0 to use default font size. Only applicable for dynamic fonts. + int m_FontStyle; ///<The font style to use. Only applicable for dynamic fonts. enum { Normal, Bold, Italic, Bold and Italic } + + ColorRGBA32 m_Color; + bool m_RichText; + + Mesh* GetMesh (); + + Mesh* m_Mesh; +}; + +#endif diff --git a/Runtime/Filters/Misc/TrailRenderer.cpp b/Runtime/Filters/Misc/TrailRenderer.cpp new file mode 100644 index 0000000..c6208ee --- /dev/null +++ b/Runtime/Filters/Misc/TrailRenderer.cpp @@ -0,0 +1,197 @@ +#include "UnityPrefix.h" +#include "TrailRenderer.h" +#include "Runtime/Input/TimeManager.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Camera/Camera.h" +#include "Runtime/GameCode/DestroyDelayed.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/Camera/RenderManager.h" +#include "Runtime/Shaders/VBO.h" +#include "Runtime/GfxDevice/GfxDevice.h" +#include "Runtime/Profiler/Profiler.h" + +const float kMinSqrDistance = 0.1f * 0.1f; + +IMPLEMENT_CLASS_INIT_ONLY (TrailRenderer) +IMPLEMENT_OBJECT_SERIALIZE (TrailRenderer) + +void TrailRenderer::InitializeClass () +{ + REGISTER_MESSAGE (TrailRenderer, kTransformChanged, TransformChanged, int); +} + +TrailRenderer::TrailRenderer (MemLabelId label, ObjectCreationMode mode) +: Super(kRendererTrail, label, mode) +, m_TransformChanged(false) +, m_WasRendered(false) +, m_CurrentLength(0) +, m_Time(0) +, m_MinVertexDistance(0) +, m_Autodestruct(false) +{ + m_AABB = MinMaxAABB (Vector3f::zero, Vector3f::zero); +} + +TrailRenderer::~TrailRenderer () +{ +} + +void TrailRenderer::Reset () { + Super::Reset (); + m_Colors[0].Set (255,255,255,255); + m_Colors[1].Set (255,255,255,255); + m_Colors[2].Set (255,255,255,255); + m_Colors[3].Set (255,255,255,255); + m_Colors[4].Set (255,255,255,0); + m_Time = 5.0f; + m_TransformChanged = true; + m_MinVertexDistance = 0.1F; + m_Positions.clear(); + m_TimeStamps.clear(); +} + +void TrailRenderer::UpdateRenderer() +{ + Super::UpdateRenderer(); + + float now = GetCurTime (); + // Remove last vertrices if neccessary + while (!m_TimeStamps.empty() && now > m_TimeStamps.back() + m_Time) { + m_Positions.pop_back(); + m_TimeStamps.pop_back(); + } + + // Add a vertex to the object + if (m_TransformChanged) { + Vector3f position = GetComponent (Transform).GetPosition (); + if( m_Positions.empty () || SqrMagnitude (m_Positions.front () - position) > m_MinVertexDistance*m_MinVertexDistance ) + { + m_Positions.push_front (position); + m_TimeStamps.push_front (now); + } + + float halfWidth = GetHalfMaxLineWidth (); + AABB newPosAABB (m_Positions.front (), Vector3f(halfWidth, halfWidth, halfWidth)); + + // Expand the BBox with the new transform position. + m_AABB.Encapsulate (newPosAABB); + BoundsChanged (); + } + + if (m_Positions.size() < 2) { + if (m_Autodestruct && m_WasRendered && IsWorldPlaying ()) + DestroyObjectDelayed (GetGameObjectPtr()); + } + else + m_WasRendered = true; + + // Important: update manager state after calling any SetVisible() above. Fixes an issue + // where trails would stop be rendered when object is disabled or stops moving. + UpdateManagerState( true ); + + m_TransformChanged = false; +} + +float TrailRenderer::GetHalfMaxLineWidth () const +{ + return std::max (m_LineParameters.endWidth, m_LineParameters.startWidth) * 0.5f; +} + +PROFILER_INFORMATION(gTrailRenderProfile, "TrailRenderer.Render", kProfilerRender) +PROFILER_INFORMATION(gSubmitVBOProfileTrail, "Mesh.SubmitVBO", kProfilerRender) + +void TrailRenderer::Render (int materialIndex, const ChannelAssigns& channels) +{ + PROFILER_AUTO(gTrailRenderProfile, this) + + int size = m_Positions.size(); + if( size < 2 ) + return; + + Vector3f* trailInVerts = NULL; + ALLOC_TEMP(trailInVerts, Vector3f, size); + int idx = 0; + for (std::list<Vector3f>::iterator j = m_Positions.begin(); j != m_Positions.end(); ++j, ++idx) + { + trailInVerts[idx] = *j; + } + trailInVerts[0] = GetComponent(Transform).GetPosition(); + + // Get VBO chunk + int stripCount = size * 2; + GfxDevice& device = GetGfxDevice(); + DynamicVBO& vbo = device.GetDynamicVBO(); + LineVertex* vbPtr; + if( !vbo.GetChunk( (1<<kShaderChannelVertex) | (1<<kShaderChannelTexCoord0) | (1<<kShaderChannelColor), + stripCount, 0, + DynamicVBO::kDrawTriangleStrip, + (void**)&vbPtr, NULL ) ) + { + return; + } + + // Generate line into the chunk + MinMaxAABB aabb; + m_LineParameters.outVertices = vbPtr; + m_LineParameters.gradient = &m_Colors; + m_LineParameters.cameraTransform = GetCurrentCamera().GetWorldToCameraMatrix(); + m_LineParameters.outAABB = &aabb; + Build3DLine( &m_LineParameters, trailInVerts, size ); + + vbo.ReleaseChunk( stripCount, 0 ); + + aabb.Expand (GetHalfMaxLineWidth ()); + + if (!CompareMemory (m_AABB, aabb)) + { + m_AABB = aabb; + BoundsChanged (); + } + + // We can't set the view matrix since that breaks shadow maps (case 490315) + // Set the world matrix instead so it cancels out the usual view matrix + // i.e. it transforms from camera space back to world space + device.SetWorldMatrix(GetCurrentCamera().GetCameraToWorldMatrix().GetPtr()); + + if (m_CustomProperties) + device.SetMaterialProperties (*m_CustomProperties); + + PROFILER_BEGIN(gSubmitVBOProfileTrail, this) + vbo.DrawChunk (channels); + GPU_TIMESTAMP(); + PROFILER_END +} + +void TrailRenderer::TransformChanged (int changeMask) +{ + Renderer::TransformChanged (changeMask); + m_TransformChanged = true; +} + +void TrailRenderer::UpdateTransformInfo() +{ + const Transform& t = GetComponent (Transform); + + TransformType type = t.CalculateTransformMatrix (m_TransformInfo.worldMatrix); + m_TransformInfo.transformType = type; + m_TransformInfo.invScale = 1.0f; + + m_TransformInfo.worldAABB = m_AABB; + InverseTransformAABB( m_TransformInfo.worldAABB, t.GetPosition(), t.GetRotation(), m_TransformInfo.localAABB ); +} + +template<class TransferFunction> inline +void TrailRenderer::Transfer (TransferFunction& transfer) { + Super::Transfer (transfer); + transfer.Transfer (m_Time, "m_Time", kSimpleEditorMask); + transfer.Transfer (m_LineParameters.startWidth, "m_StartWidth", kSimpleEditorMask); + transfer.Transfer (m_LineParameters.endWidth, "m_EndWidth", kSimpleEditorMask); + TRANSFER_SIMPLE (m_Colors); + + TRANSFER(m_MinVertexDistance); + + transfer.Transfer (m_Autodestruct, "m_Autodestruct"); + if (transfer.IsReading () && !m_Autodestruct) + m_WasRendered = false; +} diff --git a/Runtime/Filters/Misc/TrailRenderer.h b/Runtime/Filters/Misc/TrailRenderer.h new file mode 100644 index 0000000..4535d64 --- /dev/null +++ b/Runtime/Filters/Misc/TrailRenderer.h @@ -0,0 +1,62 @@ +#ifndef TRAILRENDERER_H +#define TRAILRENDERER_H + +#include <list> +#include "Runtime/Filters/Renderer.h" +#include "Runtime/Math/Gradient.h" +#include "Runtime/Math/Vector3.h" +#include "LineBuilder.h" +#include "Runtime/Geometry/AABB.h" + + + +// Renders a trail after an object. +// This is good for smoke trails after missiles or a visual FX after strong lights. +// @todo make it work so we track movement over a Cameras screen space. +class TrailRenderer : public Renderer { +public: + REGISTER_DERIVED_CLASS (TrailRenderer, Renderer) + DECLARE_OBJECT_SERIALIZE (TrailRenderer) + + TrailRenderer (MemLabelId label, ObjectCreationMode mode); + + virtual void Reset (); + + virtual void Render (int materialIndex, const ChannelAssigns& channels); + + // Hook up to TransformChanged + static void InitializeClass (); + + // TransformChanged message handler + void TransformChanged (int changeMask); + + virtual void UpdateTransformInfo(); + + GET_SET_DIRTY(float, Time, m_Time) + GET_SET_DIRTY(float, MinVertexDistance, m_MinVertexDistance) + GET_SET_DIRTY(float, StartWidth, m_LineParameters.startWidth) + GET_SET_DIRTY(float, EndWidth, m_LineParameters.endWidth) + GET_SET_DIRTY(bool, Autodestruct, m_Autodestruct) + +protected: + // from Renderer + virtual void UpdateRenderer(); + +private: + float GetHalfMaxLineWidth () const; + + bool m_TransformChanged; // Has the transform changed since last render? + bool m_WasRendered; // Trail was rendered so enable autodestruct + std::list<Vector3f> m_Positions; // The positions for each of the centers + std::list<float> m_TimeStamps; // The timestamp for each position + int m_CurrentLength; // The current length in vertices + MinMaxAABB m_AABB; // The size in world coords + + GradientDeprecated<k3DLineGradientSize> m_Colors; + LineParameters m_LineParameters; + float m_Time; ///< How long the tail should be (seconds). { 0, infinity} + float m_MinVertexDistance; ///< The minimum distance to spawn a new point on the trail range { 0, infinity} + bool m_Autodestruct; ///< Destroy GameObject when there is no trail? +}; + +#endif |