private void CalculateBarycentricCoordinates(ref Point position, out BarycentricCoordinates coordinates)
 {
     // Calculate alpha, beta, gamma for pixel center.
     coordinates.Alpha = ComputeFunction(position.X, position.Y, ref _p1, ref _p2) / _alphaDenominator;
     coordinates.Beta  = ComputeFunction(position.X, position.Y, ref _p2, ref _p0) / _betaDenominator;
     coordinates.Gamma = ComputeFunction(position.X, position.Y, ref _p0, ref _p1) / _gammaDenominator;
 }
        private Number4[] CreatePixelShaderInput(ref BarycentricCoordinates coordinates)
        {
            float w = InterpolationUtility.PrecalculateW(
                coordinates.Alpha, coordinates.Beta, coordinates.Gamma,
                _p0.W, _p1.W, _p2.W);

            var v0Data = _primitive.Vertices[0].OutputData;
            var v1Data = _primitive.Vertices[1].OutputData;
            var v2Data = _primitive.Vertices[2].OutputData;

            // Calculate interpolated attribute values for this fragment.
            // TODO: Optimize this.
            var result = new Number4[_primitive.Vertices[0].OutputData.Length];

            foreach (var outputInputBinding in OutputInputBindings.Bindings)
            {
                var v0Value = v0Data[outputInputBinding.Register];
                var v1Value = v1Data[outputInputBinding.Register];
                var v2Value = v2Data[outputInputBinding.Register];

                if (outputInputBinding.SystemValueType != Name.Undefined)
                {
                    throw new NotImplementedException();
                }

                // Create input values. Normally, this will require interpolation
                // of the vertex attributes.
                Number4 inputValue;
                switch (outputInputBinding.InterpolationMode)
                {
                case InterpolationMode.Constant:
                    inputValue = v0Value;
                    break;

                case InterpolationMode.Linear:
                    inputValue = InterpolationUtility.Perspective(
                        coordinates.Alpha, coordinates.Beta, coordinates.Gamma,
                        ref v0Value, ref v1Value, ref v2Value,
                        _p0.W, _p1.W, _p2.W, w);
                    break;

                case InterpolationMode.LinearNoPerspective:
                    inputValue = InterpolationUtility.Linear(
                        coordinates.Alpha, coordinates.Beta, coordinates.Gamma,
                        ref v0Value, ref v1Value, ref v2Value);
                    break;

                default:
                    throw new InvalidOperationException("Unrecognised interpolation mode: " + outputInputBinding.InterpolationMode);
                }

                // Apply component mask so that we don't overwrite other values in this register.
                result[outputInputBinding.Register].WriteMaskedValue(
                    inputValue, outputInputBinding.ComponentMask);
            }

            return(result);
        }
        private bool IsSampleInsideTriangle(ref BarycentricCoordinates coordinates, out float depth)
        {
            // TODO: Use fill convention.

            // If any of these tests fails, the current pixel is not inside the triangle.
            if (coordinates.IsOutsideTriangle)
            {
                depth = 0;
                return(false);
            }

            // Calculate depth.
            depth = InterpolationUtility.Linear(coordinates.Alpha, coordinates.Beta, coordinates.Gamma, _p0.Z, _p1.Z, _p2.Z);

            //// Clip to near and far planes.
            //if (depth < 0 || depth > 1)
            //    return false;

            //// Clip to 0 < w.
            //// TODO: Is this right?
            //// TODO: We do the same thing later on for attribute interpolations, can it be optimised?
            //var w = InterpolationUtility.Linear(coordinates.Alpha, coordinates.Beta, coordinates.Gamma, _p0.W, _p1.W, _p2.W);
            //if (w <= 0)
            //    return false;

            // The exact value to compare against depends on fill mode - if we're rendering wireframe,
            // then check whether sample position is within the "wireframe threshold" (i.e. 1 pixel) of an edge.
            switch (RasterizerState.FillMode)
            {
            case FillMode.Solid:
                return(true);

            case FillMode.Wireframe:
                // TODO: This threshold thing is not correct.
                const float wireframeThreshold = 0.1f;
                return(coordinates.Alpha < wireframeThreshold || coordinates.Beta < wireframeThreshold || coordinates.Gamma < wireframeThreshold);

            default:
                throw new NotSupportedException();
            }
        }
		private Number4[] CreatePixelShaderInput(ref BarycentricCoordinates coordinates)
		{
			float w = InterpolationUtility.PrecalculateW(
				coordinates.Alpha, coordinates.Beta, coordinates.Gamma,
				_p0.W, _p1.W, _p2.W);

		    var v0Data = _primitive.Vertices[0].OutputData;
            var v1Data = _primitive.Vertices[1].OutputData;
            var v2Data = _primitive.Vertices[2].OutputData;

			// Calculate interpolated attribute values for this fragment.
            // TODO: Optimize this.
            var result = new Number4[_primitive.Vertices[0].OutputData.Length];
		    foreach (var outputInputBinding in OutputInputBindings.Bindings)
		    {
                var v0Value = v0Data[outputInputBinding.Register];
                var v1Value = v1Data[outputInputBinding.Register];
                var v2Value = v2Data[outputInputBinding.Register];

                if (outputInputBinding.SystemValueType != Name.Undefined)
                    throw new NotImplementedException();

                // Create input values. Normally, this will require interpolation
                // of the vertex attributes.
                Number4 inputValue;
                switch (outputInputBinding.InterpolationMode)
                {
                    case InterpolationMode.Constant:
                        inputValue = v0Value;
                        break;
                    case InterpolationMode.Linear:
                        inputValue = InterpolationUtility.Perspective(
                            coordinates.Alpha, coordinates.Beta, coordinates.Gamma,
                            ref v0Value, ref v1Value, ref v2Value,
                            _p0.W, _p1.W, _p2.W, w);
                        break;
                    case InterpolationMode.LinearNoPerspective:
                        inputValue = InterpolationUtility.Linear(
                            coordinates.Alpha, coordinates.Beta, coordinates.Gamma,
                            ref v0Value, ref v1Value, ref v2Value);
                        break;
                    default:
                        throw new InvalidOperationException("Unrecognised interpolation mode: " + outputInputBinding.InterpolationMode);
                }

                // Apply component mask so that we don't overwrite other values in this register.
		        result[outputInputBinding.Register].WriteMaskedValue(
                    inputValue, outputInputBinding.ComponentMask);
		    }
            
		    return result;
		}
        private void CalculateBarycentricCoordinates(ref Point position, out BarycentricCoordinates coordinates)
		{
			// Calculate alpha, beta, gamma for pixel center.
			coordinates.Alpha = ComputeFunction(position.X, position.Y, ref _p1, ref _p2) / _alphaDenominator;
			coordinates.Beta = ComputeFunction(position.X, position.Y, ref _p2, ref _p0) / _betaDenominator;
			coordinates.Gamma = ComputeFunction(position.X, position.Y, ref _p0, ref _p1) / _gammaDenominator;
		}
		private bool IsSampleInsideTriangle(ref BarycentricCoordinates coordinates, out float depth)
		{
			// TODO: Use fill convention.

			// If any of these tests fails, the current pixel is not inside the triangle.
			if (coordinates.IsOutsideTriangle)
			{
				depth = 0;
				return false;
			}

            // Calculate depth.
            depth = InterpolationUtility.Linear(coordinates.Alpha, coordinates.Beta, coordinates.Gamma, _p0.Z, _p1.Z, _p2.Z);

            //// Clip to near and far planes.
            //if (depth < 0 || depth > 1)
            //    return false;

            //// Clip to 0 < w.
            //// TODO: Is this right?
            //// TODO: We do the same thing later on for attribute interpolations, can it be optimised?
            //var w = InterpolationUtility.Linear(coordinates.Alpha, coordinates.Beta, coordinates.Gamma, _p0.W, _p1.W, _p2.W);
            //if (w <= 0)
            //    return false;

			// The exact value to compare against depends on fill mode - if we're rendering wireframe,
			// then check whether sample position is within the "wireframe threshold" (i.e. 1 pixel) of an edge.
			switch (RasterizerState.FillMode)
			{
				case FillMode.Solid:
					return true;
				case FillMode.Wireframe:
                    // TODO: This threshold thing is not correct.
					const float wireframeThreshold = 0.1f;
					return coordinates.Alpha < wireframeThreshold || coordinates.Beta < wireframeThreshold || coordinates.Gamma < wireframeThreshold;
				default:
					throw new NotSupportedException();
			}
		}
		private bool CalculateCoverageAndInterpolateFragmentData(ref Fragment fragment, out BarycentricCoordinates coordinates)
		{
			// For non-multisampling, we can re-use the same calculations for coverage and interpolation.
            var pixelCenter = new Point(fragment.X + 0.5f, fragment.Y + 0.5f);
			CalculateBarycentricCoordinates(ref pixelCenter, out coordinates);

			float depth;
			if (!IsSampleInsideTriangle(ref coordinates, out depth))
				return false;

			fragment.Samples.Sample0.Covered = true;
			fragment.Samples.Sample0.Depth = depth;
			fragment.Samples.AnyCovered = true;

			return true;
		}
        private bool CalculateCoverageAndInterpolateFragmentData(ref Fragment fragment, out BarycentricCoordinates coordinates)
        {
            // For non-multisampling, we can re-use the same calculations for coverage and interpolation.
            var pixelCenter = new Point(fragment.X + 0.5f, fragment.Y + 0.5f);

            CalculateBarycentricCoordinates(ref pixelCenter, out coordinates);

            float depth;

            if (!IsSampleInsideTriangle(ref coordinates, out depth))
            {
                return(false);
            }

            fragment.Samples.Sample0.Covered = true;
            fragment.Samples.Sample0.Depth   = depth;
            fragment.Samples.AnyCovered      = true;

            return(true);
        }