private void ResetTextures()
        {
            bool hadReset = false;

            for (int i = 0; i < textureDatas.Count; i++)
            {
                var textureData = textureDatas[i];
                if (textureData.textureChanged)
                {
                    var graphics = textureData.active.NewGraphics2D();
                    graphics.Render(textureData.source, 0, 0);
                    textureData.textureChanged = false;
                    hadReset = true;
                }
            }

            if (hadReset)
            {
                Invalidate();
            }

            lastHitData = new HitData();
        }
        private (Vector3 normal, Vector3 up) GetDirectionForFace(HitData hitData)
        {
            var up     = Vector3.Zero;
            var normal = Vector3.Zero;
            var count  = 0;

            for (int i = 0; i < 3; i++)
            {
                count++;
                int faceIndex = hitData.FaceIndex[i];
                switch (faceIndex)
                {
                case -1:
                    count--;
                    break;

                case 0:
                    // top
                    normal += -Vector3.UnitZ;
                    if (count == 1)
                    {
                        up = (hitData.TileIndex[0] == 4) ? Vector3.UnitY : Vector3.UnitZ;
                    }
                    break;

                case 1:
                    // Left
                    normal += Vector3.UnitX;
                    if (count == 1)
                    {
                        up = Vector3.UnitZ;
                    }
                    break;

                case 2:
                    // Right
                    normal += -Vector3.UnitX;
                    if (count == 1)
                    {
                        up = Vector3.UnitZ;
                    }
                    break;

                case 3:
                    // Bottom
                    normal += Vector3.UnitZ;
                    if (count == 1)
                    {
                        up = -Vector3.UnitY;
                    }
                    break;

                case 4:
                    // Back
                    normal += -Vector3.UnitY;
                    if (count == 1)
                    {
                        up = Vector3.UnitZ;
                    }
                    break;

                case 5:
                    // Front
                    normal += Vector3.UnitY;
                    if (count == 1)
                    {
                        up = Vector3.UnitZ;
                    }
                    break;
                }
            }

            return(normal / count, up);
        }
        private void DrawMouseHover(HitData hitData)
        {
            if (!lastHitData.Equals(hitData))
            {
                ResetTextures();
                lastHitData = hitData;
                for (int i = 0; i < 3; i++)
                {
                    var faceIndex = hitData.FaceIndex[i];
                    var tileIndex = hitData.TileIndex[i];
                    if (faceIndex == -1)
                    {
                        // done rendering faces
                        break;
                    }

                    var hitTexture  = textureDatas[faceIndex];
                    var hitGraphics = hitTexture.active.NewGraphics2D();
                    switch (tileIndex)
                    {
                    case 0:                             // top
                        hitGraphics.FillRectangle(0,
                                                  0,
                                                  hitTexture.source.Width / 4,
                                                  hitTexture.source.Height / 4,
                                                  theme.AccentMimimalOverlay);
                        break;

                    case 1:
                        hitGraphics.FillRectangle(hitTexture.source.Width / 4 * 1,
                                                  hitTexture.source.Height / 4 * 0,
                                                  hitTexture.source.Width / 4 * 3,
                                                  hitTexture.source.Height / 4 * 1,
                                                  theme.AccentMimimalOverlay);
                        break;

                    case 2:
                        hitGraphics.FillRectangle(hitTexture.source.Width / 4 * 3,
                                                  hitTexture.source.Height / 4 * 0,
                                                  hitTexture.source.Width / 4 * 4,
                                                  hitTexture.source.Height / 4 * 1,
                                                  theme.AccentMimimalOverlay);
                        break;

                    case 3:
                        hitGraphics.FillRectangle(0,
                                                  hitTexture.source.Height / 4,
                                                  hitTexture.source.Width / 4,
                                                  hitTexture.source.Height / 4 * 3,
                                                  theme.AccentMimimalOverlay);
                        break;

                    case 4:
                        hitGraphics.FillRectangle(hitTexture.source.Width / 4,
                                                  hitTexture.source.Height / 4,
                                                  hitTexture.source.Width / 4 * 3,
                                                  hitTexture.source.Height / 4 * 3,
                                                  theme.AccentMimimalOverlay);
                        break;

                    case 5:
                        hitGraphics.FillRectangle(hitTexture.source.Width / 4 * 3,
                                                  hitTexture.source.Height / 4 * 1,
                                                  hitTexture.source.Width / 4 * 4,
                                                  hitTexture.source.Height / 4 * 3,
                                                  theme.AccentMimimalOverlay);
                        break;

                    case 6:
                        hitGraphics.FillRectangle(0,
                                                  hitTexture.source.Height / 4 * 3,
                                                  hitTexture.source.Width / 4,
                                                  hitTexture.source.Height,
                                                  theme.AccentMimimalOverlay);
                        break;

                    case 7:
                        hitGraphics.FillRectangle(hitTexture.source.Width / 4 * 1,
                                                  hitTexture.source.Height / 4 * 3,
                                                  hitTexture.source.Width / 4 * 3,
                                                  hitTexture.source.Height / 4 * 4,
                                                  theme.AccentMimimalOverlay);
                        break;

                    case 8:
                        hitGraphics.FillRectangle(hitTexture.source.Width / 4 * 3,
                                                  hitTexture.source.Height / 4 * 3,
                                                  hitTexture.source.Width / 4 * 4,
                                                  hitTexture.source.Height / 4 * 4,
                                                  theme.AccentMimimalOverlay);
                        break;
                    }

                    hitTexture.textureChanged = true;
                }
                Invalidate();
            }
        }