public static void Shutdown() { var sw = Stopwatch.StartNew(); Logger.Info("TIG shutdown started..."); MainWindow?.Dispose(); RenderingDevice?.Dispose(); MdfFactory?.Dispose(); ShapeRenderer2d?.Dispose(); ShapeRenderer3d?.Dispose(); TextLayouter?.Dispose(); Sound?.Dispose(); Fonts?.Dispose(); DynamicScripting = null; FS = null; Mouse = null; Keyboard = null; SystemEventPump = null; MainWindow = null; MessageQueue = null; RenderingDevice = null; DebugUI = null; MdfFactory = null; ShapeRenderer2d = null; ShapeRenderer3d = null; TextLayouter = null; Sound = null; Fonts = null; Console = null; Logger.Info("TIG shutdown completed in {0}.", sw.Elapsed); }
/// <summary> /// Render a string from an atlas. /// </summary> /// <param name="position">The top left position of where to start drawing the string.</param> /// <param name="color">The text color.</param> /// <param name="text">The text itself.</param> /// <param name="atlas">The font atlas to use.</param> /// <param name="layouter">The layouter to use.</param> /// <param name="effect">Effect to apply</param> /// <param name="effectAmount">The effect amount.</param> /// <param name="effectColor">The effect color.</param> public void RenderString( Vector3 position, Color color, string text, DrawableFontAtlas atlas, TextLayouter layouter = null, FontEffect effect = FontEffect.None, float effectAmount = 0f, Color?effectColor = null) { layouter ??= new TextLayouter(atlas); atlas.SetupDrawing(this, text, effect, effectAmount, effectColor); var reUsableVector = new Vector3(); foreach (char c in text) { Vector2 gPos = layouter.AddLetter(c, out DrawableGlyph g); if (g == null || g.GlyphUV == Rectangle.Empty) { continue; } reUsableVector.X = gPos.X; reUsableVector.Y = gPos.Y; atlas.DrawGlyph(this, g, position + reUsableVector, color); } atlas.FinishDrawing(this); }
public static TextBlock[] PrepareFormatText(IWcR2Font font, string formatText, ref Vector2 pos, int width, ref float maxWidth) { var layouter = new TextLayouter(); int y = (int)pos.Y; var blocks = layouter.LayoutFormatText(font, formatText, width, ref y); for (int i = 0; i < blocks.Length; i++) { blocks[i].Position.X += pos.X; var blockWidth = blocks[i].Font.MeasureString(blocks[i].Text).X; maxWidth = Math.Max(maxWidth, blocks[i].Position.X + blockWidth); } pos.X = 0; pos.Y = y; return(blocks); }
public override void Draw(RenderComposer composer) { DrawableFontAtlas atlas = _font.GetAtlas(30); var l = new TextLayouter(atlas.Atlas); string text; if (!_myPaddle.Ready) { text = "Press 'Space' when ready!"; } else if (_pad1 == _myPaddle && !_pad2.Ready || _pad2 == _myPaddle && !_pad1.Ready) { text = "Waiting for other player."; } else { text = $"{_pad1.Score}/{_pad2.Score}"; } Vector2 textSize = l.MeasureString(text); float screenHorizontalCenter = Engine.Configuration.RenderSize.X / 2; composer.RenderString(new Vector3(screenHorizontalCenter - textSize.X / 2, 10, 0), Color.White, text, atlas); composer.RenderSprite(_pad1.VisualPosition, _pad1.Size, _pad1.Ready ? Color.White : Color.Red); composer.RenderSprite(_pad2.VisualPosition, _pad2.Size, _pad2.Ready ? Color.White : Color.Red); // Uncomment to view paddle collisions. //LineSegment[] padCol = _pad2.GetPaddleCollision(); //for (var i = 0; i < padCol.Length; i++) //{ // composer.RenderLine(ref padCol[i], Color.Red); //} NetworkTransform ball = IdToObject["Ball"]; composer.RenderSprite(ball.VisualPosition, ball.Size, Color.White); NetworkTransform upperWall = IdToObject["UpperWall"]; NetworkTransform lowerWall = IdToObject["LowerWall"]; composer.RenderSprite(upperWall.VisualPosition, upperWall.Size, Color.White); composer.RenderSprite(lowerWall.VisualPosition, lowerWall.Size, Color.White); }
internal bool MoveToNextRun(out LayoutRun nextRun) { switch (state) { default: throw new InvalidOperationException("Invalid state: " + state); case -1: nextRun = default; return(false); case 0: currentY = extents.Y; lastLine = false; // TODO: Check if this can even happen since we measure the text // if the width hasn't been constrained if (extents.Width == 0) { nextRun = new LayoutRun( 0, text.Length, extents.X, extents.Y, extents, false ); state = -1; return(true); } // Is there only space for one line? if (!font.GetGlyphIdx('.', out var dotIdx)) { throw new Exception("Font has no '.' character."); } ellipsisWidth = 3 * (style.kerning + glyphs[dotIdx].WidthLine); linePadding = 0; if (extents.Y + 2 * font.FontFace.LargestHeight > extents.Y + extents.Height) { lastLine = true; if ((style.flags & TigTextStyleFlag.TTSF_TRUNCATE) != 0) { linePadding = -ellipsisWidth; } } if (text.Length <= 0) { nextRun = default; state = -1; return(false); } startOfWord = 0; state = 1; goto case 1; // Iterate one more character run case 1: if (startOfWord >= text.Length) { state = -1; nextRun = default; return(false); } (wordsOnLine, lineWidth) = TextLayouter.MeasureCharRun( text.Slice(startOfWord), style, extents, extents.Width, font, linePadding, lastLine); // There's just one word left and it wont fit. Remove restriction on width. if (wordsOnLine == 0 && (style.flags & TigTextStyleFlag.TTSF_TRUNCATE) == 0) { (wordsOnLine, lineWidth) = TextLayouter.MeasureCharRun( text.Slice(startOfWord), style, extents, 9999999, font, linePadding, lastLine); } currentX = 0; wordIdx = 0; state = 2; goto case 2; case 2: if (wordIdx >= wordsOnLine) { // Advance to next line currentY += font.FontFace.LargestHeight; if (currentY + 2 * font.FontFace.LargestHeight > extents.Y + extents.Height) { lastLine = true; if (style.flags.HasFlag(TigTextStyleFlag.TTSF_TRUNCATE)) { linePadding = ellipsisWidth; } } startOfWord++; state = 1; goto case 1; } var remainingSpace = extents.Width + linePadding - currentX; wordInfo = TextLayouter.ScanWord(text, startOfWord, text.Length, lastLine, font, style, remainingSpace); var lastIdx = wordInfo.lastIdx; wordWidth = wordInfo.Width; if (lastLine && (style.flags & TigTextStyleFlag.TTSF_TRUNCATE) != 0) { if (currentX + wordInfo.fullWidth > extents.Width) { lastIdx = wordInfo.idxBeforePadding; } else { if (!TextLayouter.HasMoreText(text.Slice(lastIdx), style.tabStop > 0)) { wordInfo.drawEllipsis = false; wordWidth = wordInfo.fullWidth; } } } startOfWord = lastIdx; if (startOfWord + 1 < text.Length && text[startOfWord] == '@' && text[startOfWord + 1] == 't') { // Extend the word with by the amount needed to move to the tabstop wordWidth += Math.Max(0, style.tabStop - (currentX + wordWidth)); // Skip the "t" of "@t" startOfWord++; } else if (startOfWord < text.Length && text[startOfWord] >= 0 && char.IsWhiteSpace(text[startOfWord])) { wordWidth += style.tracking; } // This means this is not the last word in this line if (wordIdx + 1 < wordsOnLine) { startOfWord++; } // Draw the word var x = extents.X + currentX; if ((style.flags & TigTextStyleFlag.TTSF_CENTER) != 0) { x += (extents.Width - lineWidth) / 2; } if (wordInfo.firstIdx < 0 || lastIdx < 0) { Logger.Error("Bad firstIdx at LayoutAndDraw! {0}, {1}", wordInfo.firstIdx, lastIdx); } else if (lastIdx >= wordInfo.firstIdx) { nextRun = new LayoutRun( wordInfo.firstIdx, lastIdx, x, currentY, extents, false); state = 3; return(true); } state = 3; goto case 3; case 3: currentX += wordWidth; // We're on the last line, the word has been truncated, ellipsis needs to be drawn if (lastLine && style.flags.HasFlag(TigTextStyleFlag.TTSF_TRUNCATE) && wordInfo.drawEllipsis) { nextRun = new LayoutRun( wordInfo.lastIdx, wordInfo.lastIdx, extents.X + currentX, currentY, extents, true ); state = -1; return(true); } wordIdx++; state = 2; goto case 2; } }
/// <summary> /// Render a string from an atlas. /// </summary> /// <param name="position">The top left position of where to start drawing the string.</param> /// <param name="color">The text color.</param> /// <param name="text">The text itself.</param> /// <param name="atlas">The font atlas to use.</param> /// <param name="layouter">The layouter to use.</param> public void RenderString(Vector3 position, Color color, string text, DrawableFontAtlas atlas, TextLayouter layouter) { if (atlas?.Atlas?.Glyphs == null) { return; } position = position.RoundClosest(); foreach (char c in text) { Vector2 gPos = layouter.AddLetter(c, out AtlasGlyph g); if (g == null) { continue; } var uv = new Rectangle(g.Location, g.UV); RenderSprite(new Vector3(position.X + gPos.X, position.Y + gPos.Y, position.Z), g.Size, color, atlas.Texture, uv); } }
public static void Startup(GameConfig config, TigSettings tigSettings = null) { tigSettings ??= new TigSettings(); Logger.Info("Initializing TIG"); FS = CreateFileSystem(config.InstallationFolder, tigSettings.DataFolder); DynamicScripting = TryLoadDynamicScripting(); if (tigSettings.OffScreen) { MainWindow = new HeadlessMainWindow(); } else { MainWindow = new MainWindow(config.Window, FS); } var configRendering = config.Rendering; RenderingDevice = new RenderingDevice( FS, MainWindow, configRendering.AdapterIndex, configRendering.DebugDevice); if (config.EnableDebugUI) { DebugUI = new DebugUiSystem(MainWindow, RenderingDevice); } else { DebugUI = new NoOpDebugUI(); } MdfFactory = new MdfMaterialFactory(FS, RenderingDevice); ShapeRenderer2d = new ShapeRenderer2d(RenderingDevice); ShapeRenderer3d = new ShapeRenderer3d(RenderingDevice); TextLayouter = new TextLayouter(RenderingDevice, ShapeRenderer2d); // TODO mStartedSystems.emplace_back(StartSystem("idxtable.c", 0x101EC400, 0x101ECAD0)); // TODO mStartedSystems.emplace_back(StartSystem("trect.c", TigStartupNoop, 0x101E4E40)); // TODO mStartedSystems.emplace_back(StartSystem("color.c", 0x101ECB20, 0x101ED070)); // TODO mLegacyVideoSystem = std::make_unique<LegacyVideoSystem>(*mMainWindow, *mRenderingDevice); // mStartedSystems.emplace_back(StartSystem("video.c", 0x101DC6E0, 0x101D8540)); // TODO mStartedSystems.emplace_back(StartSystem("shader", 0x101E3350, 0x101E2090)); // TODO mStartedSystems.emplace_back(StartSystem("palette.c", 0x101EBE30, 0x101EBEB0)); // TODO mStartedSystems.emplace_back(StartSystem("window.c", 0x101DED20, 0x101DF320)); // TODO mStartedSystems.emplace_back(StartSystem("timer.c", 0x101E34E0, 0x101E34F0)); // mStartedSystems.emplace_back(StartSystem("dxinput.c", 0x101FF910, 0x101FF950)); // mStartedSystems.emplace_back(StartSystem("keyboard.c", 0x101DE430, 0x101DE2D0)); Keyboard = new TigKeyboard(); // mStartedSystems.emplace_back(StartSystem("texture.c", 0x101EDF60, 0x101EE0A0)); Mouse = new TigMouse(); // TODO mStartedSystems.emplace_back(StartSystem("mouse.c", 0x101DDF50, 0x101DDE30)); // TODO mStartedSystems.emplace_back(StartSystem("message.c", 0x101DE460, 0x101DE4E0)); MessageQueue = new MessageQueue(); SystemEventPump = new SystemEventPump(); // startedSystems.emplace_back(StartSystem("gfx.c", TigStartupNoop, TigShutdownNoop)); // TODO mStartedSystems.emplace_back(StartSystem("strparse.c", 0x101EBF00, TigShutdownNoop)); // TODO mStartedSystems.emplace_back(StartSystem("filecache.c", TigStartupNoop, TigShutdownNoop)); Sound = new TigSound( soundId => GameSystems.SoundGame.FindSoundFilename(soundId), tigSettings.DisableSound ); // TODO mSoundSystem = std::make_unique<temple::SoundSystem>(); // TODO mMovieSystem = std::make_unique<temple::MovieSystem>(*mSoundSystem); // mStartedSystems.emplace_back(StartSystem("movie.c", 0x101F1090, TigShutdownNoop)); // NOTE: WFT -> UiManager // TODO mStartedSystems.emplace_back(StartSystem("wft.c", 0x101F98A0, 0x101F9770)); // TODO mStartedSystems.emplace_back(StartSystem("font.c", 0x101EAEC0, 0x101EA140)); Fonts = new TigFonts(); Fonts.LoadAllFrom("art/arial-10"); Fonts.PushFont("arial-10", 10); // TODO mConsole = std::make_unique<Console>(); // mStartedSystems.emplace_back(StartSystem("console.c", 0x101E0290, 0x101DFBC0)); Console = new TigConsole(DynamicScripting); // TODO mStartedSystems.emplace_back(StartSystem("loadscreen.c", 0x101E8260, TigShutdownNoop)); // TODO *tigInternal.consoleDisabled = false; // tig init disables console by default }