summaryrefslogtreecommitdiff
path: root/Runtime/Filters/Misc
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Filters/Misc')
-rw-r--r--Runtime/Filters/Misc/DynamicFontFreeType.cpp510
-rw-r--r--Runtime/Filters/Misc/DynamicFontFreeType.h47
-rw-r--r--Runtime/Filters/Misc/Font.cpp845
-rw-r--r--Runtime/Filters/Misc/Font.h318
-rw-r--r--Runtime/Filters/Misc/GetFonts.cpp338
-rw-r--r--Runtime/Filters/Misc/LineBuilder.cpp95
-rw-r--r--Runtime/Filters/Misc/LineBuilder.h88
-rw-r--r--Runtime/Filters/Misc/LineRenderer.cpp195
-rw-r--r--Runtime/Filters/Misc/LineRenderer.h61
-rw-r--r--Runtime/Filters/Misc/MiniCoreText.h38
-rw-r--r--Runtime/Filters/Misc/TextMesh.cpp298
-rw-r--r--Runtime/Filters/Misc/TextMesh.h99
-rw-r--r--Runtime/Filters/Misc/TrailRenderer.cpp197
-rw-r--r--Runtime/Filters/Misc/TrailRenderer.h62
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