private void RenderTexturedBackground(IDrawDevice device) { if (!cachedTexturedBackground.IsAvailable) { return; } float timeMult = Time.TimeMult; backgroundX += timeMult * 1.2f; backgroundY += timeMult * -0.2f + timeMult * MathF.Sin(backgroundPhase) * 0.6f; backgroundPhase += timeMult * 0.001f; Vector3 renderPos = new Vector3(0, 0, 600); // Fit the target rect to actual pixel coordinates to avoid unnecessary filtering offsets renderPos.X = MathF.Round(renderPos.X); renderPos.Y = MathF.Round(renderPos.Y); if (MathF.RoundToInt(device.TargetSize.X) != (MathF.RoundToInt(device.TargetSize.X) / 2) * 2) { renderPos.X += 0.5f; } if (MathF.RoundToInt(device.TargetSize.Y) != (MathF.RoundToInt(device.TargetSize.Y) / 2) * 2) { // AMD Bugfix? renderPos.Y -= 0.004f; } // Reserve the required space for vertex data in our locally cached buffer int neededVertices = 4; if (cachedVertices == null || cachedVertices.Length < neededVertices) { cachedVertices = new VertexC1P3T2[neededVertices]; } // Render it as world-space fullscreen quad cachedVertices[0].Pos = new Vector3(renderPos.X, renderPos.Y, renderPos.Z); cachedVertices[1].Pos = new Vector3(renderPos.X + device.TargetSize.X, renderPos.Y, renderPos.Z); cachedVertices[2].Pos = new Vector3(renderPos.X + device.TargetSize.X, renderPos.Y + device.TargetSize.Y, renderPos.Z); cachedVertices[3].Pos = new Vector3(renderPos.X, renderPos.Y + device.TargetSize.Y, renderPos.Z); cachedVertices[0].TexCoord = new Vector2(0.0f, 0.0f); cachedVertices[1].TexCoord = new Vector2(1f, 0.0f); cachedVertices[2].TexCoord = new Vector2(1f, 1f); cachedVertices[3].TexCoord = new Vector2(0.0f, 1f); cachedVertices[0].Color = cachedVertices[1].Color = cachedVertices[2].Color = cachedVertices[3].Color = ColorRgba.White; // Setup custom pixel shader BatchInfo material = device.RentMaterial(); material.Technique = texturedBackgroundShader; material.MainTexture = cachedTexturedBackground; material.SetValue("horizonColor", horizonColor); material.SetValue("shift", new Vector2(backgroundX, backgroundY)); material.SetValue("parallaxStarsEnabled", 0f); device.AddVertices(material, VertexMode.Quads, cachedVertices, 0, 4); }
public unsafe void DrawString(ref int charOffset, string text, float x, float y, Alignment alignment, ColorRgba? color = null, float scale = 1f, float angleOffset = 0f, float varianceX = 4f, float varianceY = 4f, float speed = 4f, float charSpacing = 1f, float lineSpacing = 1f) { const int MaxColorizeIndex = 7; if (string.IsNullOrEmpty(text)) { return; } float phase = (float)Time.GameTimer.TotalSeconds * speed; bool hasColor = false; // Pre-compute text size //int lines = 1; float totalWidth = 0f, lastWidth = 0f, totalHeight = 0f; float charSpacingPre = charSpacing; float scalePre = scale; for (int i = 0; i < text.Length; i++) { if (text[i] == '\n') { if (lastWidth < totalWidth) { lastWidth = totalWidth; } totalWidth = 0f; totalHeight += (charHeight * scale * lineSpacing); //lines++; continue; } else if (text[i] == '\f' && text[i + 1] == '[') { i += 2; int formatIndex = i; while (text[i] != ']') { i++; } if (text[formatIndex + 1] == ':') { int paramInt; switch (text[formatIndex]) { case 'c': // Color hasColor = true; break; case 's': // Scale if (int.TryParse(text.Substring(formatIndex + 2, i - (formatIndex + 2)), out paramInt)) { scalePre = paramInt * 0.01f; } break; case 'w': // Char spacing if (int.TryParse(text.Substring(formatIndex + 2, i - (formatIndex + 2)), out paramInt)) { charSpacingPre = paramInt * 0.01f; } break; } } continue; } Rect uvRect; if (!unicodeChars.TryGetValue(text[i], out uvRect)) { byte ascii = (byte)text[i]; if (ascii < 128) { uvRect = asciiChars[ascii]; } else { uvRect = new Rect(); } } if (uvRect.W > 0 && uvRect.H > 0) { totalWidth += (uvRect.W + baseSpacing) * charSpacingPre * scalePre; } } if (lastWidth < totalWidth) { lastWidth = totalWidth; } totalHeight += (charHeight * scale * lineSpacing); VertexC1P3T2[] vertexData = canvas.RentVertices(text.Length * 4); // Set default material bool colorize, allowColorChange; ContentRef<Material> material; ColorRgba mainColor; if (color.HasValue) { mainColor = color.Value; if (mainColor == ColorRgba.TransparentBlack) { if (hasColor) { material = materialColor; mainColor = new ColorRgba(0.46f, 0.46f, 0.4f, 0.5f); } else { material = materialPlain; mainColor = ColorRgba.White; } } else { material = materialColor; } colorize = false; if (mainColor.R == 0 && mainColor.G == 0 && mainColor.B == 0) { allowColorChange = false; } else { allowColorChange = true; } } else { material = materialColor; mainColor = ColorRgba.White; colorize = true; allowColorChange = false; } Vector2 uvRatio = new Vector2( 1f / materialPlain.Res.MainTexture.Res.ContentWidth, 1f / materialPlain.Res.MainTexture.Res.ContentHeight ); int vertexIndex = 0; Vector2 originPos = new Vector2(x, y); alignment.ApplyTo(ref originPos, new Vector2(lastWidth, totalHeight)); float lineStart = originPos.X; for (int i = 0; i < text.Length; i++) { if (text[i] == '\n') { // New line originPos.X = lineStart; originPos.Y += (charHeight * scale * lineSpacing); continue; } else if (text[i] == '\f' && text[i + 1] == '[') { // Format i += 2; int formatIndex = i; while (text[i] != ']') { i++; } if (text[formatIndex + 1] == ':') { int paramInt; switch (text[formatIndex]) { case 'c': // Color if (allowColorChange && int.TryParse(text.Substring(formatIndex + 2, i - (formatIndex + 2)), out paramInt)) { if (paramInt == -1) { colorize = true; } else { colorize = false; mainColor = colors[paramInt % colors.Length]; } } break; case 's': // Scale if (int.TryParse(text.Substring(formatIndex + 2, i - (formatIndex + 2)), out paramInt)) { scale = paramInt * 0.01f; } break; case 'w': // Char spacing if (int.TryParse(text.Substring(formatIndex + 2, i - (formatIndex + 2)), out paramInt)) { charSpacing = paramInt * 0.01f; } break; default: // Unknown formatting break; } } continue; } Rect uvRect; if (!unicodeChars.TryGetValue(text[i], out uvRect)) { byte ascii = (byte)text[i]; if (ascii < 128) { uvRect = asciiChars[ascii]; } else { uvRect = new Rect(); } } if (uvRect.W > 0 && uvRect.H > 0) { if (colorize) { mainColor = colors[charOffset % MaxColorizeIndex]; } Vector3 pos = new Vector3(originPos); if (angleOffset > 0f) { float currentPhase = (phase + charOffset) * angleOffset * MathF.Pi; if (speed > 0f && charOffset % 2 == 1) { currentPhase = -currentPhase; } pos.X += MathF.Cos(currentPhase) * varianceX * scale; pos.Y += MathF.Sin(currentPhase) * varianceY * scale; } pos.X = MathF.Round(pos.X); pos.Y = MathF.Round(pos.Y); float x2 = MathF.Round(pos.X + uvRect.W * scale); float y2 = MathF.Round(pos.Y + uvRect.H * scale); vertexData[vertexIndex + 0].Pos = pos; vertexData[vertexIndex + 0].TexCoord.X = uvRect.X; vertexData[vertexIndex + 0].TexCoord.Y = uvRect.Y; vertexData[vertexIndex + 0].Color = mainColor; vertexData[vertexIndex + 1].Pos.X = pos.X; vertexData[vertexIndex + 1].Pos.Y = y2; vertexData[vertexIndex + 1].Pos.Z = pos.Z; vertexData[vertexIndex + 1].TexCoord.X = uvRect.X; vertexData[vertexIndex + 1].TexCoord.Y = uvRect.Y + uvRect.H * uvRatio.Y; vertexData[vertexIndex + 1].Color = mainColor; vertexData[vertexIndex + 2].Pos.X = x2; vertexData[vertexIndex + 2].Pos.Y = y2; vertexData[vertexIndex + 2].Pos.Z = pos.Z; vertexData[vertexIndex + 2].TexCoord.X = uvRect.X + uvRect.W * uvRatio.X; vertexData[vertexIndex + 2].TexCoord.Y = uvRect.Y + uvRect.H * uvRatio.Y; vertexData[vertexIndex + 2].Color = mainColor; vertexData[vertexIndex + 3].Pos.X = x2; vertexData[vertexIndex + 3].Pos.Y = pos.Y; vertexData[vertexIndex + 3].Pos.Z = pos.Z; vertexData[vertexIndex + 3].TexCoord.X = uvRect.X + uvRect.W * uvRatio.X; vertexData[vertexIndex + 3].TexCoord.Y = uvRect.Y; vertexData[vertexIndex + 3].Color = mainColor; if (MathF.RoundToInt(canvas.DrawDevice.TargetSize.X) != (MathF.RoundToInt(canvas.DrawDevice.TargetSize.X) / 2) * 2) { float align = 0.5f / canvas.DrawDevice.TargetSize.X; vertexData[vertexIndex + 0].Pos.X += align; vertexData[vertexIndex + 1].Pos.X += align; vertexData[vertexIndex + 2].Pos.X += align; vertexData[vertexIndex + 3].Pos.X += align; } if (MathF.RoundToInt(canvas.DrawDevice.TargetSize.Y) != (MathF.RoundToInt(canvas.DrawDevice.TargetSize.Y) / 2) * 2) { float align = 0.5f * scale / canvas.DrawDevice.TargetSize.Y; vertexData[vertexIndex + 0].Pos.Y += align; vertexData[vertexIndex + 1].Pos.Y += align; vertexData[vertexIndex + 2].Pos.Y += align; vertexData[vertexIndex + 3].Pos.Y += align; } vertexIndex += 4; originPos.X += ((uvRect.W + baseSpacing) * scale * charSpacing); } charOffset++; } charOffset++; // Submit all the vertices as one draw batch canvas.DrawDevice.AddVertices( material, VertexMode.Quads, vertexData, 0, vertexIndex); }