private static int GetOrSetTextureUnit(Text text, int index) { if (string.IsNullOrWhiteSpace(text?.Content)) { return(-1); } if (SpriteRendererPanel.TextureUnits.ContainsKey(Tuple.Create(TextureType.Text, text.GetCharKey(index)))) { return(SpriteRendererPanel.TextureUnits[Tuple.Create(TextureType.Text, text.GetCharKey(index))].ToInt()); } else { // If at 16, will have already drawn batch if (SpriteRendererPanel.TextureUnits.Count >= SpriteRendererPanel.UsedTextureUnits - 1) { SpriteRendererPanel.TextureUnits.Clear(); } if (SpriteRendererPanel.TextTextures.ContainsKey(text.GetCharKey(index)) || SpriteRendererPanel.LoadTextTextures(text)) { var unitNumber = SpriteRendererPanel.TextureUnits.Count; var usedUnit = unitNumber.ToTextureUnit(); var usedTexture = SpriteRendererPanel.TextTextures[text.GetCharKey(index)]; usedTexture.Use(usedUnit); SpriteRendererPanel.Shader.SetInt($"textureArrays[{unitNumber}]", unitNumber); SpriteRendererPanel.Shader.SetInt($"sheetW[{unitNumber}]", usedTexture.Width); SpriteRendererPanel.Shader.SetInt($"sheetH[{unitNumber}]", usedTexture.Height); SpriteRendererPanel.TextureUnits[Tuple.Create(TextureType.Text, text.GetCharKey(index))] = usedUnit; return(unitNumber); } else { return(-1); } } }
private void GLControlOnLoad(object sender, EventArgs e) { SpriteRendererPanel.Initialize(); this.projection = Matrix4.CreateScale(2.0f / this.Width, -2.0f / this.Height, 1); }
public void Render() { if (this.initialScaleX.HasValue) { this.RenderScaleX = this.initialScaleX.Value; this.initialScaleX = null; } if (this.initialScaleY.HasValue) { this.RenderScaleY = this.initialScaleY.Value; this.initialScaleY = null; } lock (SpriteRendererPanel.RenderLock) { this.MakeCurrent(); if (this.viewportUpdate) { GL.Viewport(0, 0, this.Width, this.Height); this.viewportUpdate = false; } if (this.backgroundTile == null) { GL.ClearColor(this.backgroundColor); } GL.Clear(ClearBufferMask.ColorBufferBit); GL.BindVertexArray(vertexArrayObject); SpriteRendererPanel.Shader.Use(); GL.BindVertexArray(vertexArrayObject); SpriteRendererPanel.Shader.SetVector2("origin", this.origin); SpriteRendererPanel.Shader.SetMatrix4("projection", this.projection); SpriteRendererPanel.Shader.SetInt($"drawType", 2); if (this.backgroundTile != null) { var textureName = this.backgroundTile.Texture; var textureIndex = this.GetOrSetTextureUnit(textureName); if (textureIndex != -1) { var textureSize = SpriteRendererPanel.SpriteSheets[textureName].Size; var bgTiles = new Size(2 + this.Width / this.backgroundTile.TileWidth, 2 + this.Height / textureSize.Height); var texX = this.backgroundTile.TextureFrame * this.backgroundTile.TileWidth; SpriteRendererPanel.Shader.SetFloat($"spriteX[0]", this.backgroundTile.OffsetX - this.backgroundTile.TileWidth / 2); SpriteRendererPanel.Shader.SetFloat($"spriteY[0]", this.backgroundTile.OffsetY - textureSize.Height / 2); SpriteRendererPanel.Shader.SetInt($"spriteW[0]", this.backgroundTile.TileWidth); SpriteRendererPanel.Shader.SetInt($"spriteW[1]", bgTiles.Width); SpriteRendererPanel.Shader.SetInt($"spriteH[0]", textureSize.Height); SpriteRendererPanel.Shader.SetInt($"spriteH[1]", bgTiles.Height); SpriteRendererPanel.Shader.SetInt($"spriteTX[0]", texX); SpriteRendererPanel.Shader.SetInt($"spriteTY[0]", 0); SpriteRendererPanel.Shader.SetInt($"spriteTI[0]", textureIndex); SpriteRendererPanel.Shader.SetVector2($"spriteScale[0]", Vector2.One); SpriteRendererPanel.Shader.SetFloat($"spriteRotate[0]", 0); SpriteRendererPanel.Shader.SetVector4($"colorModulation[0]", Vector4.One); GL.DrawElementsInstanced(PrimitiveType.Triangles, Indices.Length, DrawElementsType.UnsignedInt, (IntPtr)0, bgTiles.Width * bgTiles.Height); } } foreach (var kvp in this.renderedObjects.OrderBy(kvp => kvp.Key)) { var renderPass = kvp.Key; var levels = kvp.Value.Levels; var sprites = kvp.Value.Sprites; var texts = kvp.Value.Texts; var fillQuads = kvp.Value.Quads.Where(q => q.Type.HasFlag(DrawType.Fill)).ToList(); var outlineQuads = kvp.Value.Quads.Where(q => q.Type.HasFlag(DrawType.Outline)).ToList(); var batchSize = 0; SpriteRendererPanel.Shader.SetInt($"drawType", 1); for (int i = 0; i < levels.Count; i++) { var textureName = levels[i].Texture; var textureIndex = this.GetOrSetTextureUnit(textureName); var textureSize = textureIndex != -1 ? SpriteRendererPanel.SpriteSheets[textureName].Size : new Size(2000, 2000); if (textureIndex == -1) { continue; } // Notable speed improvement over multiple $"[{batchSize}]" var batchIndex = "[" + batchSize + "]"; SpriteRendererPanel.Shader.SetFloat("spriteX" + batchIndex, levels[i].X + textureSize.Width / 2); SpriteRendererPanel.Shader.SetFloat("spriteY" + batchIndex, levels[i].Y + textureSize.Height / 2); SpriteRendererPanel.Shader.SetInt("spriteW" + batchIndex, textureSize.Width); SpriteRendererPanel.Shader.SetInt("spriteH" + batchIndex, textureSize.Height); SpriteRendererPanel.Shader.SetInt("spriteTX" + batchIndex, 0); SpriteRendererPanel.Shader.SetInt("spriteTY" + batchIndex, 0); SpriteRendererPanel.Shader.SetInt("spriteTI" + batchIndex, textureIndex); SpriteRendererPanel.Shader.SetVector2("spriteScale" + batchIndex, Vector2.One); SpriteRendererPanel.Shader.SetFloat("spriteRotate" + batchIndex, 0); SpriteRendererPanel.Shader.SetVector4("colorModulation" + batchIndex, Vector4.One); batchSize++; if (batchSize == SpriteRendererPanel.DrawBatchSize || i + 1 == levels.Count || (SpriteRendererPanel.TextureUnits.Count == SpriteRendererPanel.UsedTextureUnits - 1 && !SpriteRendererPanel.TextureUnits.ContainsKey(Tuple.Create(TextureType.Spritesheet, levels[i + 1].Texture)))) { GL.DrawElementsInstanced(PrimitiveType.Triangles, SpriteRendererPanel.Indices.Length, DrawElementsType.UnsignedInt, (IntPtr)0, batchSize); batchSize = 0; } } SpriteRendererPanel.Shader.SetInt($"drawType", 4); for (int i = 0; i < fillQuads.Count; i++) { var batchIndex = "[" + batchSize + "]"; SpriteRendererPanel.Shader.SetFloat($"spriteX" + batchIndex, fillQuads[i].A.X); SpriteRendererPanel.Shader.SetFloat($"spriteY" + batchIndex, fillQuads[i].A.Y); SpriteRendererPanel.Shader.SetInt($"spriteW" + batchIndex, fillQuads[i].B.X); SpriteRendererPanel.Shader.SetInt($"spriteH" + batchIndex, fillQuads[i].B.Y); SpriteRendererPanel.Shader.SetInt($"spriteTX" + batchIndex, fillQuads[i].C.X); SpriteRendererPanel.Shader.SetInt($"spriteTY" + batchIndex, fillQuads[i].C.Y); SpriteRendererPanel.Shader.SetInt($"spriteTI" + batchIndex, fillQuads[i].Color.ToArgb()); SpriteRendererPanel.Shader.SetVector2($"spriteScale" + batchIndex, new Vector2(fillQuads[i].D.X, fillQuads[i].D.Y)); SpriteRendererPanel.Shader.SetVector4($"colorModulation" + batchIndex, Vector4.One); batchSize++; if (batchSize == SpriteRendererPanel.DrawBatchSize || i + 1 == fillQuads.Count) { GL.DrawElementsInstanced(PrimitiveType.Triangles, SpriteRendererPanel.Indices.Length, DrawElementsType.UnsignedInt, (IntPtr)0, batchSize); batchSize = 0; } } SpriteRendererPanel.Shader.SetInt($"drawType", 4); for (int i = 0; i < outlineQuads.Count; i++) { var batchIndex = "[" + batchSize + "]"; SpriteRendererPanel.Shader.SetFloat($"spriteX" + batchIndex, outlineQuads[i].A.X); SpriteRendererPanel.Shader.SetFloat($"spriteY" + batchIndex, outlineQuads[i].A.Y); SpriteRendererPanel.Shader.SetInt($"spriteW" + batchIndex, outlineQuads[i].B.X); SpriteRendererPanel.Shader.SetInt($"spriteH" + batchIndex, outlineQuads[i].B.Y); SpriteRendererPanel.Shader.SetInt($"spriteTX" + batchIndex, outlineQuads[i].C.X); SpriteRendererPanel.Shader.SetInt($"spriteTY" + batchIndex, outlineQuads[i].C.Y); SpriteRendererPanel.Shader.SetInt($"spriteTI" + batchIndex, outlineQuads[i].Color.ToArgb()); SpriteRendererPanel.Shader.SetVector2($"spriteScale" + batchIndex, new Vector2(outlineQuads[i].D.X, outlineQuads[i].D.Y)); SpriteRendererPanel.Shader.SetVector4($"colorModulation" + batchIndex, Vector4.One); batchSize++; if (batchSize == SpriteRendererPanel.DrawBatchSize || i + 1 == outlineQuads.Count) { GL.DrawElementsInstanced(PrimitiveType.LineStrip, SpriteRendererPanel.Indices.Length, DrawElementsType.UnsignedInt, (IntPtr)0, batchSize); batchSize = 0; } } SpriteRendererPanel.Shader.SetInt($"drawType", 0); for (int i = 0; i < sprites.Count; i++) { var textureName = sprites[i].Texture; var textureIndex = this.GetOrSetTextureUnit(textureName); if (textureIndex == -1) { continue; } // Manually clips texture since CLAMP_TO_EDGE didn't do anything on one test computer var textureRect = new Rectangle(new Point(), this.GetTextureSize(textureName) ?? new Size(2000, 2000)); var spriteRect = new Rectangle(sprites[i].TexX, sprites[i].TexY, sprites[i].Width, sprites[i].Height); if (!textureRect.Contains(spriteRect)) { spriteRect.Intersect(textureRect); sprites[i].TexX = spriteRect.Left; sprites[i].TexY = spriteRect.Top; sprites[i].Width = spriteRect.Width; sprites[i].Height = spriteRect.Height; } var spriteColorModulation = default(Vector4); if (!SpriteRendererPanel.CachedColorModulation.TryGetValue(sprites[i].ColorModulation, out spriteColorModulation)) { spriteColorModulation = new Vector4( sprites[i].ColorModulation.R / 255.0f, sprites[i].ColorModulation.G / 255.0f, sprites[i].ColorModulation.B / 255.0f, sprites[i].ColorModulation.A / 255.0f); SpriteRendererPanel.CachedColorModulation[sprites[i].ColorModulation] = spriteColorModulation; } var batchIndex = "[" + batchSize + "]"; SpriteRendererPanel.Shader.SetFloat($"spriteX" + batchIndex, sprites[i].X); SpriteRendererPanel.Shader.SetFloat($"spriteY" + batchIndex, sprites[i].Y); SpriteRendererPanel.Shader.SetInt($"spriteW" + batchIndex, sprites[i].Width); SpriteRendererPanel.Shader.SetInt($"spriteH" + batchIndex, sprites[i].Height); SpriteRendererPanel.Shader.SetInt($"spriteTX" + batchIndex, sprites[i].TexX); SpriteRendererPanel.Shader.SetInt($"spriteTY" + batchIndex, sprites[i].TexY); SpriteRendererPanel.Shader.SetInt($"spriteTI" + batchIndex, textureIndex); var tkScaleVector = new Vector2(sprites[i].Scale.X, sprites[i].Scale.Y); SpriteRendererPanel.Shader.SetVector2($"spriteScale" + batchIndex, tkScaleVector); SpriteRendererPanel.Shader.SetFloat($"spriteRotate" + batchIndex, sprites[i].Rotate); SpriteRendererPanel.Shader.SetVector4($"colorModulation" + batchIndex, spriteColorModulation); batchSize++; if (batchSize == SpriteRendererPanel.DrawBatchSize || i + 1 == sprites.Count || (SpriteRendererPanel.TextureUnits.Count == SpriteRendererPanel.UsedTextureUnits - 1 && !SpriteRendererPanel.TextureUnits.ContainsKey(Tuple.Create(TextureType.Spritesheet, sprites[i + 1].Texture)))) { GL.DrawElementsInstanced(PrimitiveType.Triangles, SpriteRendererPanel.Indices.Length, DrawElementsType.UnsignedInt, (IntPtr)0, batchSize); batchSize = 0; } } SpriteRendererPanel.Shader.SetInt($"drawType", 0); foreach (var text in texts) { var offset = 0.0; for (var i = 0; i < text.Content.Length; i++) { var textureIndex = SpriteRendererPanel.GetOrSetTextureUnit(text, i); if (textureIndex == -1 || !SpriteRendererPanel.TextTextures.ContainsKey(text.GetCharKey(i))) { continue; } var texture = SpriteRendererPanel.TextTextures[text.GetCharKey(i)]; var metrics = FontGlyphs.TextMetrics[text.GetCharKey(i)]; var face = FontGlyphs.TextFaces[text.Font]; var faceHeight = FontGlyphs.TextFaceHeights[text.Font]; var adjustedPosition = new PointF( text.Position.X + (float)(offset + metrics.HorizontalBearingX + (metrics.Width / 2.0)), text.Position.Y + (float)(faceHeight - metrics.HorizontalBearingY + (metrics.Height / 2.0))); var advance = metrics.HorizontalAdvance; var textColorModulation = default(Vector4); if (!SpriteRendererPanel.CachedColorModulation.TryGetValue(text.Color, out textColorModulation)) { textColorModulation = new Vector4(text.Color.R / 255.0f, text.Color.G / 255.0f, text.Color.B / 255.0f, text.Color.A / 255.0f); SpriteRendererPanel.CachedColorModulation[text.Color] = textColorModulation; } SpriteRendererPanel.Shader.SetFloat($"spriteX[0]", adjustedPosition.X); SpriteRendererPanel.Shader.SetFloat($"spriteY[0]", adjustedPosition.Y); SpriteRendererPanel.Shader.SetInt($"spriteW[0]", texture.Width); SpriteRendererPanel.Shader.SetInt($"spriteH[0]", texture.Height); SpriteRendererPanel.Shader.SetInt($"spriteTX[0]", 0); SpriteRendererPanel.Shader.SetInt($"spriteTY[0]", 0); SpriteRendererPanel.Shader.SetInt($"spriteTI[0]", textureIndex); SpriteRendererPanel.Shader.SetVector2($"spriteScale[0]", Vector2.One); SpriteRendererPanel.Shader.SetFloat($"spriteRotate[0]", 0); SpriteRendererPanel.Shader.SetVector4($"colorModulation[0]", textColorModulation); GL.DrawElementsInstanced(PrimitiveType.Triangles, SpriteRendererPanel.Indices.Length, DrawElementsType.UnsignedInt, (IntPtr)0, 1); offset += advance; } } } this.backgroundTile = null; this.renderedObjects.Clear(); this.SwapBuffers(); // TODO: Figure out what messes with textureunits between renders SpriteRendererPanel.TextureUnits.Clear(); } }