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);
        }
Exemple #2
0
        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);
        }