private Vector3 CalculateSpriteColor(ref SharedModelRenderData renderData, int alpha)
        {
            var colorMultiplier = renderData.RenderMode == RenderMode.Glow || renderData.RenderMode == RenderMode.TransAdd ? alpha : 256;

            //TODO: this doesn't handle filter colors
            if (renderData.RenderColor == Vector3.Zero)
            {
                return new Vector3(colorMultiplier * 255 / 256);
            }

            var color = renderData.RenderColor;

            //This replicates a bug in the engine that causes Color render mode to ignore render mode
            //This way the color from the sprite is retained
            if (renderData.RenderMode == RenderMode.TransColor)
            {
                color = Vector3.One * 255;
            }

            color *= colorMultiplier;
            color /= 256.0f;

            //TODO: handle filtering
            //TODO: should probably be a post processing effect
            /*
            if (filterMode)
            {
                color *= filterColor;
            }
            */

            return color;
        }
        private static Vector4 GetBrushColor(ref SharedModelRenderData renderData)
        {
            switch (renderData.RenderMode)
            {
            case RenderMode.Normal:
            case RenderMode.TransAlpha:
                return(new Vector4(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue));

            case RenderMode.TransColor:
                return(new Vector4(renderData.RenderColor, renderData.RenderAmount));

            case RenderMode.TransTexture:
            case RenderMode.Glow:
                return(new Vector4(byte.MaxValue, byte.MaxValue, byte.MaxValue, renderData.RenderAmount));

            case RenderMode.TransAdd:
                return(new Vector4(renderData.RenderAmount, renderData.RenderAmount, renderData.RenderAmount, byte.MaxValue));

            default: throw new InvalidOperationException($"Render mode {renderData.RenderMode} not supported");
            }
        }
        private int GlowBlend(ref SharedModelRenderData renderData, IViewState viewState)
        {
            var tmp = renderData.Origin - viewState.Origin;

            var distance = tmp.Length();

            //TODO: implement trace

            /*
            pmove->usehull = 2;

            var traceFlags = PM_GLASS_IGNORE;

            if (!r_traceglow.value)
            {
                traceFlags |= PM_STUDIO_IGNORE;
            }

            var trace = pmove->PM_PlayerTrace(viewState.Origin, renderData.Origin, traceFlags, -1);

            if ((1.0 - trace.fraction) * distance > 8.0)
            {
                return 0;
            }
            else */
            if (renderData.RenderFX == RenderFX.NoDissipation)
            {
                return renderData.RenderAmount;
            }
            else
            {
                renderData.Scale = new Vector3(distance * 0.005f);

                return (int)(Math.Clamp(19000.0f / (distance * distance), 0.5f, 1.0f) * 255);
            }
        }
        private Vector3 GetSpriteAngles(ref SharedModelRenderData renderData, SpriteType type, IViewState viewState)
        {
            //Convert parallel sprites to parallel oriented if a roll was specified
            if (type == SpriteType.Parallel && renderData.Angles.Z != 0)
            {
                type = SpriteType.ParallelOriented;
            }

            Vector3 GetModifiedViewAngles()
            {
                var angles = viewState.Angles;

                //Pitch and roll need to be inverted to operate in the sprite's coordinate system
                //Yaw stays the same
                angles.X = -angles.X;
                angles.Z = -angles.Z;

                return angles;
            }

            switch (type)
            {
                case SpriteType.FacingUpright:
                    {
                        //This is bugged in vanilla since it uses an origin that isn't initialized by sprite models, only other models
                        var angles = VectorUtils.VectorToAngles(-renderData.Origin);

                        //No pitch
                        angles.X = 0;

                        return angles;
                    }

                case SpriteType.ParallelUpright:
                    {
                        var angles = GetModifiedViewAngles();

                        //No pitch
                        angles.X = 0;

                        return angles;
                    }

                case SpriteType.Parallel:
                    {
                        return GetModifiedViewAngles();
                    }

                case SpriteType.Oriented:
                    {
                        return renderData.Angles;
                    }

                case SpriteType.ParallelOriented:
                    {
                        var angles = GetModifiedViewAngles();

                        //Apply roll
                        angles.Z -= renderData.Angles.Z;

                        return angles;
                    }

                default:
                    {
                        Logger.Warning($"{nameof(GetSpriteAngles)}: Bad sprite type {type}");
                        break;
                    }
            }

            return new Vector3();
        }