public static void CreateBillboard(VRageRender.MyBillboard billboard, ref MyQuadD quad, string material, string blendMaterial, float textureBlendRatio,
     ref Color color, ref Vector3D origin, bool colorize = false, bool near = false, bool lowres = false)
 {
     Debug.Assert(material != null);
     CreateBillboard(billboard, ref quad, material, blendMaterial, textureBlendRatio, ref color, ref origin, Vector2.Zero, colorize, near, lowres);
 }
        //  This method is like a constructor (which we can't use because billboards are allocated from a pool).
        //  It starts/initializes a billboard. Refs used only for optimalization
        public static void CreateBillboard(VRageRender.MyBillboard billboard, ref MyQuadD quad, string material, string blendMaterial, float textureBlendRatio,
            ref Color color, ref Vector3D origin, Vector2 uvOffset, bool colorize = false, bool near = false, bool lowres = false, float reflectivity = 0)
        {
            Debug.Assert(material != null);
            
            if (string.IsNullOrEmpty(material) || !MyTransparentMaterials.ContainsMaterial(material))
            {
                material = "ErrorMaterial";
                color = Vector4.One;
            }

            billboard.Material = material;
            billboard.BlendMaterial = blendMaterial;
            billboard.BlendTextureRatio = textureBlendRatio;

            quad.Point0.AssertIsValid();
            quad.Point1.AssertIsValid();
            quad.Point2.AssertIsValid();
            quad.Point3.AssertIsValid();


            //  Billboard vertexes
            billboard.Position0 = quad.Point0;
            billboard.Position1 = quad.Point1;
            billboard.Position2 = quad.Point2;
            billboard.Position3 = quad.Point3;

            billboard.UVOffset = uvOffset;

            EnableColorize = colorize;

            if (EnableColorize)
                billboard.Size = (float)(billboard.Position0 - billboard.Position2).Length();

            //  Distance for sorting
            //  IMPORTANT: Must be calculated before we do color and alpha misting, because we need distance there
            billboard.DistanceSquared = (float)Vector3D.DistanceSquared(MyRenderCamera.Position, origin);

            //  Color
            billboard.Color = color;
            billboard.ColorIntensity = 1;
            billboard.Reflectivity = reflectivity;

            billboard.Near = near;
            billboard.Lowres = lowres;
            billboard.ParentID = -1;

            //  Alpha depends on distance to camera. Very close bilboards are more transparent, so player won't see billboard errors or rotating billboards
            var mat = MyTransparentMaterials.GetMaterial(billboard.Material);
            if (mat.AlphaMistingEnable)
                billboard.Color *= MathHelper.Clamp(((float)Math.Sqrt(billboard.DistanceSquared) - mat.AlphaMistingStart) / (mat.AlphaMistingEnd - mat.AlphaMistingStart), 0, 1);

            billboard.Color *= mat.Color;

            billboard.ContainedBillboards.Clear();
        }
        //  Update position, check collisions, etc. and draw if particle still lives.
        //  Return false if particle dies/timeouts in this tick.
        public bool Draw(VRageRender.MyBillboard billboard)
        {
            if (Pivot != null && !MyParticlesManager.Paused)
            {
                if (PivotRotation != null)
                {
                    Matrix pivotRotationTransform =
                      Matrix.CreateRotationX(MathHelper.ToRadians(m_actualPivotRotation.X) * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS) *
                      Matrix.CreateRotationY(MathHelper.ToRadians(m_actualPivotRotation.Y) * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS) *
                      Matrix.CreateRotationZ(MathHelper.ToRadians(m_actualPivotRotation.Z) * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS);

                    m_actualPivot = Vector3.TransformNormal(m_actualPivot, pivotRotationTransform);
                }

                m_actualPivot = Vector3D.TransformNormal(m_actualPivot, m_generation.GetEffect().WorldMatrix);
            }

            var actualPosition = m_actualPosition + m_actualPivot;
            
            MyTransparentGeometry.StartParticleProfilingBlock("Distance calculation");
            //  This time is scaled according to planned lifespan of the particle

            // Distance for sorting
            billboard.DistanceSquared = (float)Vector3D.DistanceSquared(MyTransparentGeometry.Camera.Translation, actualPosition);

            MyTransparentGeometry.EndParticleProfilingBlock();

            // If distance to camera is really small don't draw it.
            if (billboard.DistanceSquared <= 0.1f)
            {
                return false;
            }

            MyTransparentGeometry.StartParticleProfilingBlock("Quad calculation");

            MyTransparentGeometry.StartParticleProfilingBlock("actualRadius");
            float actualRadius = 1;
            Radius.GetInterpolatedValue<float>(m_normalizedTime, out actualRadius);
            MyTransparentGeometry.EndParticleProfilingBlock();

            float actualAlphaCutout = 0;
            if (AlphaCutout != null)
            {
                MyTransparentGeometry.StartParticleProfilingBlock("AlphaCutout calculation");

                AlphaCutout.GetInterpolatedValue<float>(m_normalizedTime, out actualAlphaCutout);

                MyTransparentGeometry.EndParticleProfilingBlock();
            }

            billboard.ContainedBillboards.Clear();

            billboard.Near = m_generation.GetEffect().Near;
            billboard.Lowres = VRageRender.MyRenderConstants.RenderQualityProfile.LowResParticles;
            billboard.CustomViewProjection = -1;
            billboard.ParentID = -1;
            billboard.AlphaCutout = actualAlphaCutout;
            billboard.UVOffset = Vector2.Zero;
            billboard.UVSize = Vector2.One;



            float alpha = 1;


            Matrix transform = Matrix.Identity;
            Vector3 normal = Vector3.Forward;

            Vector3 actualVelocity = (Vector3)(m_actualPosition - m_previousPosition);

            float radiusBySpeed = m_generation.RadiusBySpeed;
            if (radiusBySpeed > 0)
            {
                float actualSpeed = actualVelocity.Length();
                actualRadius = Math.Max(actualRadius, actualRadius * m_generation.RadiusBySpeed * actualSpeed);
            }


            if (Type == MyParticleTypeEnum.Point)
            {
                MyTransparentGeometry.StartParticleProfilingBlock("GetBillboardQuadRotated");
               
                Vector2 actualRadiusV2 = new Vector2(actualRadius, actualRadius);

                if (Thickness > 0)
                {
                    actualRadiusV2.Y = Thickness;
                }

                if (m_generation.RotationReference == MyRotationReference.Camera)
                {
                    transform =
                       Matrix.CreateFromAxisAngle(MyTransparentGeometry.Camera.Right, m_actualAngle.X) *
                       Matrix.CreateFromAxisAngle(MyTransparentGeometry.Camera.Up, m_actualAngle.Y) *
                       Matrix.CreateFromAxisAngle(MyTransparentGeometry.Camera.Forward, m_actualAngle.Z);

                    GetBillboardQuadRotated(billboard, ref actualPosition, actualRadiusV2, ref transform, MyTransparentGeometry.Camera.Left, MyTransparentGeometry.Camera.Up);
                }
                else if (m_generation.RotationReference == MyRotationReference.Local)
                {
                    transform = Matrix.CreateFromAxisAngle(m_generation.GetEffect().WorldMatrix.Right, m_actualAngle.X) *
                    Matrix.CreateFromAxisAngle(m_generation.GetEffect().WorldMatrix.Up, m_actualAngle.Y) *
                    Matrix.CreateFromAxisAngle(m_generation.GetEffect().WorldMatrix.Forward, m_actualAngle.Z);

                    GetBillboardQuadRotated(billboard, ref actualPosition, actualRadiusV2, ref transform, m_generation.GetEffect().WorldMatrix.Left, m_generation.GetEffect().WorldMatrix.Up);
                }
                else if (m_generation.RotationReference == MyRotationReference.Velocity)
                {
                    if (actualVelocity.LengthSquared() < 0.00001f)
                        return false;

                    Matrix velocityRef = Matrix.CreateFromDir(Vector3.Normalize(actualVelocity));

                    transform = Matrix.CreateFromAxisAngle(velocityRef.Right, m_actualAngle.X) *
                    Matrix.CreateFromAxisAngle(velocityRef.Up, m_actualAngle.Y) *
                    Matrix.CreateFromAxisAngle(velocityRef.Forward, m_actualAngle.Z);

                    GetBillboardQuadRotated(billboard, ref actualPosition, actualRadiusV2, ref transform, velocityRef.Left, velocityRef.Up);
                }
                else if (m_generation.RotationReference == MyRotationReference.VelocityAndCamera)
                {
                    if (actualVelocity.LengthSquared() < 0.0001f)
                        return false;

                    Vector3 cameraToPoint = Vector3.Normalize(m_actualPosition - MyTransparentGeometry.Camera.Translation);
                    Vector3 velocityDir = Vector3.Normalize(actualVelocity);

                    Vector3 sideVector = Vector3.Cross(cameraToPoint, velocityDir);
                    Vector3 upVector = Vector3.Cross(sideVector, velocityDir);

                    Matrix velocityRef = Matrix.CreateWorld(m_actualPosition, velocityDir, upVector);

                    transform = Matrix.CreateFromAxisAngle(velocityRef.Right, m_actualAngle.X) *
                    Matrix.CreateFromAxisAngle(velocityRef.Up, m_actualAngle.Y) *
                    Matrix.CreateFromAxisAngle(velocityRef.Forward, m_actualAngle.Z);

                    GetBillboardQuadRotated(billboard, ref actualPosition, actualRadiusV2, ref transform, velocityRef.Left, velocityRef.Up);
                }
                else if (m_generation.RotationReference == MyRotationReference.LocalAndCamera)
                {
                    Vector3 cameraToPoint = Vector3.Normalize(m_actualPosition - MyTransparentGeometry.Camera.Translation);
                    Vector3 localDir = m_generation.GetEffect().WorldMatrix.Forward;
                    float dot = cameraToPoint.Dot(localDir);
                    Matrix velocityRef;
                    if (dot >= 0.9999f)
                    {
                        // TODO Petr: probably not correct, at least it does not produce NaN positions
                        velocityRef = Matrix.CreateTranslation(m_actualPosition);
                    }
                    else
                    {
                        Vector3 sideVector = Vector3.Cross(cameraToPoint, localDir);
                        Vector3 upVector = Vector3.Cross(sideVector, localDir);

                        velocityRef = Matrix.CreateWorld(m_actualPosition, localDir, upVector);
                    }

                    transform = Matrix.CreateFromAxisAngle(velocityRef.Right, m_actualAngle.X) *
                    Matrix.CreateFromAxisAngle(velocityRef.Up, m_actualAngle.Y) *
                    Matrix.CreateFromAxisAngle(velocityRef.Forward, m_actualAngle.Z);

                    GetBillboardQuadRotated(billboard, ref actualPosition, actualRadiusV2, ref transform, velocityRef.Left, velocityRef.Up);
                }
                else
                {
                    System.Diagnostics.Debug.Fail("Unknown RotationReference enum");
                }

                MyTransparentGeometry.EndParticleProfilingBlock();
            }
            else if (Type == MyParticleTypeEnum.Line)
            {
                if (MyUtils.IsZero(Velocity.LengthSquared()))
                    Velocity = MyUtils.GetRandomVector3Normalized();

                MyQuadD quad = new MyQuadD();

                MyPolyLineD polyLine = new MyPolyLineD();
                //polyLine.LineDirectionNormalized = MyUtils.Normalize(Velocity);
                if (actualVelocity.LengthSquared() > 0)
                    polyLine.LineDirectionNormalized = MyUtils.Normalize(actualVelocity);
                else
                    polyLine.LineDirectionNormalized = MyUtils.Normalize(Velocity);

                if (m_actualAngle.Z != 0)
                {
                    polyLine.LineDirectionNormalized = Vector3.TransformNormal(polyLine.LineDirectionNormalized, Matrix.CreateRotationY(m_actualAngle.Z));
                }

                polyLine.Point0 = actualPosition;
                polyLine.Point1.X = actualPosition.X - polyLine.LineDirectionNormalized.X * actualRadius;
                polyLine.Point1.Y = actualPosition.Y - polyLine.LineDirectionNormalized.Y * actualRadius;
                polyLine.Point1.Z = actualPosition.Z - polyLine.LineDirectionNormalized.Z * actualRadius;

                if (m_actualAngle.LengthSquared() > 0)
                { //centerize
                    polyLine.Point0.X = polyLine.Point0.X - polyLine.LineDirectionNormalized.X * actualRadius * 0.5f;
                    polyLine.Point0.Y = polyLine.Point0.Y - polyLine.LineDirectionNormalized.Y * actualRadius * 0.5f;
                    polyLine.Point0.Z = polyLine.Point0.Z - polyLine.LineDirectionNormalized.Z * actualRadius * 0.5f;
                    polyLine.Point1.X = polyLine.Point1.X - polyLine.LineDirectionNormalized.X * actualRadius * 0.5f;
                    polyLine.Point1.Y = polyLine.Point1.Y - polyLine.LineDirectionNormalized.Y * actualRadius * 0.5f;
                    polyLine.Point1.Z = polyLine.Point1.Z - polyLine.LineDirectionNormalized.Z * actualRadius * 0.5f;
                }

                polyLine.Thickness = Thickness;
                var camPos = MyTransparentGeometry.Camera.Translation;
                MyUtils.GetPolyLineQuad(out quad, ref polyLine, camPos);

                transform.Forward = polyLine.LineDirectionNormalized;

                billboard.Position0 = quad.Point0;
                billboard.Position1 = quad.Point1;
                billboard.Position2 = quad.Point2;
                billboard.Position3 = quad.Point3;
            }
            else if (Type == MyParticleTypeEnum.Trail)
            {
                if (Quad.Point0 == Quad.Point2) //not moving particle
                    return false;
                if (Quad.Point1 == Quad.Point3) //not moving particle was previous one
                    return false;
                if (Quad.Point0 == Quad.Point3) //not moving particle was previous one
                    return false;

                billboard.Position0 = Quad.Point0;
                billboard.Position1 = Quad.Point1;
                billboard.Position2 = Quad.Point2;
                billboard.Position3 = Quad.Point3;
            }
            else
            {
                throw new NotSupportedException(Type + " is not supported particle type");
            }

            if (this.m_generation.AlphaAnisotropic)
            {
                normal = Vector3.Normalize(Vector3.Cross(billboard.Position0 - billboard.Position1, billboard.Position0 - billboard.Position2));

                Vector3 forward = (billboard.Position0 + billboard.Position1 + billboard.Position2 + billboard.Position3) / 4 - MyTransparentGeometry.Camera.Translation;

                //Vector3 forward = MyTransparentGeometry.Camera.Forward;

                float angle = Math.Abs(Vector3.Dot(MyUtils.Normalize(forward), normal));


                float alphaCone = 1 - (float)Math.Pow(1 - angle, 4);
                alpha = alphaCone;
            }


            MyTransparentGeometry.EndParticleProfilingBlock();

            MyTransparentGeometry.StartParticleProfilingBlock("Material calculation");

            Vector4 color = Vector4.One;
            if (Color.GetKeysCount() > 0)
            {
                Color.GetInterpolatedValue<Vector4>(m_normalizedTime, out color);
            }

            if (m_arrayIndex != -1)
            {
                Vector3 arraySize = m_generation.ArraySize;
                if (arraySize.X > 0 && arraySize.Y > 0)
                {
                    int arrayOffset = m_generation.ArrayOffset;
                    int arrayModulo = m_generation.ArrayModulo == 0 ? (int)arraySize.X * (int)arraySize.Y : m_generation.ArrayModulo;

                    m_arrayIndex = m_arrayIndex % arrayModulo + arrayOffset;

                    float xDiv = 1.0f / arraySize.X;
                    float yDiv = 1.0f / arraySize.Y;
                    int xIndex = m_arrayIndex % (int)arraySize.X;
                    int yIndex = m_arrayIndex / (int)arraySize.X;

                    billboard.UVOffset = new Vector2(xDiv * xIndex, yDiv * yIndex);
                    billboard.UVSize = new Vector2(xDiv, yDiv);
                }
            }


            var material1 = MyTransparentMaterials.GetMaterial("ErrorMaterial");
            var material2 = MyTransparentMaterials.GetMaterial("ErrorMaterial");
            float textureBlendRatio = 0;
            if ((Flags & ParticleFlags.BlendTextures) != 0)
            {
                float prevTime, nextTime, difference;
                Material.GetPreviousValue(m_normalizedTime, out material1, out prevTime);
                Material.GetNextValue(m_normalizedTime, out material2, out nextTime, out difference);

                if (prevTime != nextTime)
                    textureBlendRatio = (m_normalizedTime - prevTime) * difference;
            }
            else
            {
                Material.GetInterpolatedValue(m_normalizedTime, out material1);
            }

            MyTransparentGeometry.EndParticleProfilingBlock();
                     
            //This gets 0.44ms for 2000 particles
            MyTransparentGeometry.StartParticleProfilingBlock("billboard.Start");

            if (material1 != null)
                billboard.Material = material1.Name;

            billboard.BlendMaterial = material2.Name;
            billboard.BlendTextureRatio = textureBlendRatio;
            billboard.EnableColorize = false;

            billboard.Color = color * alpha * m_generation.GetEffect().UserColorMultiplier;
            billboard.ColorIntensity = ColorIntensity;
            billboard.SoftParticleDistanceScale = SoftParticleDistanceScale;

            MyTransparentGeometry.EndParticleProfilingBlock();

            return true;
        }
 public static void CreateBillboard(VRageRender.MyBillboard billboard, ref MyQuadD quad, string material,
     ref Color color, ref Vector3D origin, Vector2 uvOffset, bool colorize = false, bool near = false, bool lowres = false)
 {
     Debug.Assert(material != null);
     CreateBillboard(billboard, ref quad, material, null, 0, ref color, ref origin, uvOffset, colorize, near, lowres);
 }
        //  Update position, check collisions, etc. and draw if particle still lives.
        //  Return false if particle dies/timeouts in this tick.
        public bool Draw(VRageRender.MyBillboard billboard)
        {
            
            MyTransparentGeometry.StartParticleProfilingBlock("Distance calculation");
            //  This time is scaled according to planned lifespan of the particle

            // Distance for sorting
            billboard.DistanceSquared = (float)Vector3D.DistanceSquared(MyTransparentGeometry.Camera.Translation, m_actualPosition);

            MyTransparentGeometry.EndParticleProfilingBlock();

            // If distance to camera is really small don't draw it.
            if (billboard.DistanceSquared <= 0.1f)
            {
                return false;
            }

            MyTransparentGeometry.StartParticleProfilingBlock("Quad calculation");

            MyTransparentGeometry.StartParticleProfilingBlock("actualRadius");
            float actualRadius = 1;
            Radius.GetInterpolatedValue<float>(m_normalizedTime, out actualRadius);
            MyTransparentGeometry.EndParticleProfilingBlock();

            billboard.ContainedBillboards.Clear();

            billboard.Near = m_generation.GetEffect().Near;
            billboard.Lowres = m_generation.GetEffect().LowRes || VRageRender.MyRenderConstants.RenderQualityProfile.LowResParticles;
            billboard.CustomViewProjection = -1;
            billboard.ParentID = -1;

            float alpha = 1;

            if (Type == MyParticleTypeEnum.Point)
            {
                MyTransparentGeometry.StartParticleProfilingBlock("GetBillboardQuadRotated");
                GetBillboardQuadRotated(billboard, ref m_actualPosition, actualRadius, m_actualAngle);
                MyTransparentGeometry.EndParticleProfilingBlock();
            }
            else if (Type == MyParticleTypeEnum.Line)
            {
                if (MyUtils.IsZero(Velocity.LengthSquared()))
                    Velocity = MyUtils.GetRandomVector3Normalized();

                MyQuadD quad = new MyQuadD();

                MyPolyLineD polyLine = new MyPolyLineD();
                polyLine.LineDirectionNormalized = MyUtils.Normalize(Velocity);

                if (m_actualAngle > 0)
                {
                    polyLine.LineDirectionNormalized = Vector3.TransformNormal(polyLine.LineDirectionNormalized, Matrix.CreateRotationY(MathHelper.ToRadians(m_actualAngle)));
                }

                polyLine.Point0 = m_actualPosition;
                polyLine.Point1.X = m_actualPosition.X + polyLine.LineDirectionNormalized.X * actualRadius;
                polyLine.Point1.Y = m_actualPosition.Y + polyLine.LineDirectionNormalized.Y * actualRadius;
                polyLine.Point1.Z = m_actualPosition.Z + polyLine.LineDirectionNormalized.Z * actualRadius;

                if (m_actualAngle > 0)
                { //centerize
                    polyLine.Point0.X = polyLine.Point0.X - polyLine.LineDirectionNormalized.X * actualRadius * 0.5f;
                    polyLine.Point0.Y = polyLine.Point0.Y - polyLine.LineDirectionNormalized.Y * actualRadius * 0.5f;
                    polyLine.Point0.Z = polyLine.Point0.Z - polyLine.LineDirectionNormalized.Z * actualRadius * 0.5f;
                    polyLine.Point1.X = polyLine.Point1.X - polyLine.LineDirectionNormalized.X * actualRadius * 0.5f;
                    polyLine.Point1.Y = polyLine.Point1.Y - polyLine.LineDirectionNormalized.Y * actualRadius * 0.5f;
                    polyLine.Point1.Z = polyLine.Point1.Z - polyLine.LineDirectionNormalized.Z * actualRadius * 0.5f;
                }

                polyLine.Thickness = Thickness;
                var camPos = MyTransparentGeometry.Camera.Translation;
                MyUtils.GetPolyLineQuad(out quad, ref polyLine, camPos);

                if (this.m_generation.AlphaAnisotropic)
                {
                    float angle = 1 - Math.Abs(Vector3.Dot(MyUtils.Normalize(MyTransparentGeometry.Camera.Forward), polyLine.LineDirectionNormalized));
                    float alphaCone = (float)Math.Pow(angle, 0.5f);
                    alpha = alphaCone;
                }

                billboard.Position0 = quad.Point0;
                billboard.Position1 = quad.Point1;
                billboard.Position2 = quad.Point2;
                billboard.Position3 = quad.Point3;
            }
            else if (Type == MyParticleTypeEnum.Trail)
            {
                if (Quad.Point0 == Quad.Point2) //not moving particle
                    return false;
                if (Quad.Point1 == Quad.Point3) //not moving particle was previous one
                    return false;
                if (Quad.Point0 == Quad.Point3) //not moving particle was previous one
                    return false;

                billboard.Position0 = Quad.Point0;
                billboard.Position1 = Quad.Point1;
                billboard.Position2 = Quad.Point2;
                billboard.Position3 = Quad.Point3;

                //if (this.m_generation.AlphaAnisotropic)
             /*   { //Trails are anisotropic by default (nobody wants them to see ugly)
                    Vector3 lineDir = Vector3.Normalize(Quad.Point1 - Quad.Point0);
                    float angle = 1 - Math.Abs(Vector3.Dot(MyMwcUtils.Normalize(MyCamera.ForwardVector), lineDir));
                    float alphaCone = (float)Math.Pow(angle, 0.3f);
                    alpha = alphaCone;
                }*/
            }
            else
            {
                throw new NotSupportedException(Type + " is not supported particle type");
            }

            MyTransparentGeometry.EndParticleProfilingBlock();

            MyTransparentGeometry.StartParticleProfilingBlock("Material calculation");

            Vector4 color;
            Color.GetInterpolatedValue<Vector4>(m_normalizedTime, out color);

            var material1 = MyTransparentMaterials.GetMaterial("ErrorMaterial");
            var material2 = MyTransparentMaterials.GetMaterial("ErrorMaterial");
            float textureBlendRatio = 0;
            if ((Flags & ParticleFlags.BlendTextures) != 0)
            {
                float prevTime, nextTime, difference;
                Material.GetPreviousValue(m_normalizedTime, out material1, out prevTime);
                Material.GetNextValue(m_normalizedTime, out material2, out nextTime, out difference);

                if (prevTime != nextTime)
                    textureBlendRatio = (m_normalizedTime - prevTime) * difference;
            }
            else
            {
                Material.GetInterpolatedValue(m_normalizedTime, out material1);
            }

            MyTransparentGeometry.EndParticleProfilingBlock();
                     
            //This gets 0.44ms for 2000 particles
            MyTransparentGeometry.StartParticleProfilingBlock("billboard.Start");


            billboard.Material = material1.Name;
            billboard.BlendMaterial = material2.Name;
            billboard.BlendTextureRatio = textureBlendRatio;
            billboard.EnableColorize = false;

            billboard.Color = color * alpha * m_generation.GetEffect().UserColorMultiplier;

            MyTransparentGeometry.EndParticleProfilingBlock();

            return true;
        }
        public static bool AddAttachedQuad(string material, ref MyQuadD quad, Vector4 color, ref Vector3D vctPos, int renderObjectID, int priority = 0)
        {
            Debug.Assert(material != null);
            if (!IsEnabled) return false;

            MyUtils.AssertIsValid(quad.Point0);
            MyUtils.AssertIsValid(quad.Point1);
            MyUtils.AssertIsValid(quad.Point2);
            MyUtils.AssertIsValid(quad.Point3);

            //VRageRender.MyBillboard billboard = m_preallocatedBillboards.Allocate();
            //VRageRender.MyBillboard billboard = new VRageRender.MyBillboard();
            VRageRender.MyBillboard billboard = VRageRender.MyRenderProxy.BillboardsPoolWrite.Allocate();
            if (billboard == null)
                return false;

            billboard.Priority = priority;

            CreateBillboard(billboard, ref quad, material, ref color, ref vctPos, false, false, false, -1);
            billboard.ParentID = renderObjectID;

            VRageRender.MyRenderProxy.AddBillboard(billboard);

            return true;
        }
 public static void CreateBillboard(VRageRender.MyBillboard billboard, ref MyQuadD quad, string material, string blendMaterial, float textureBlendRatio,
 ref Vector4 color, ref Vector3D origin, bool colorize = false, bool near = false, bool lowres = false, int customViewProjection = -1, float reflection = 0)
 {
     CreateBillboard(billboard, ref quad, material, blendMaterial, textureBlendRatio, ref color, ref origin, Vector2.Zero, colorize, near, lowres, customViewProjection, reflection);
 }
 public static void CreateBillboard(VRageRender.MyBillboard billboard, ref MyQuadD quad, string material,
     ref Vector4 color, ref Vector3D origin, Vector2 uvOffset, bool colorize = false, bool near = false, bool lowres = false, int customViewProjection = -1, float reflection = 0)
 {
     CreateBillboard(billboard, ref quad, material, "Test", 0, ref color, ref origin, uvOffset, colorize, near, lowres, customViewProjection, reflection);
 }
        //  Calculates coordinates for quad that lies on line defined by two points and is always facing the camera. It thickness is defined in metres.
        //  It is used for drawing bullet lines, debris flying from explosions, anything that isn't quad but is line.
        //  IMPORTANT: Parameter 'polyLine' is refed only for performance. Don't change it inside the method.
        public static void GetPolyLineQuad(out MyQuadD retQuad, ref MyPolyLineD polyLine)
        {
            Vector3D toCamera = MyRenderCamera.Position - polyLine.Point0;
            Vector3D cameraToPoint;
            if (!MyUtils.HasValidLength(toCamera))
            {
                // When camera at point, choose random direction
                cameraToPoint = Vector3D.Forward;
            }
            else
            {
                cameraToPoint = MyUtils.Normalize(toCamera);
            }
            Vector3D sideVector = MyUtils.GetVector3Scaled(Vector3D.Cross((Vector3D)polyLine.LineDirectionNormalized, cameraToPoint), polyLine.Thickness);

            retQuad.Point0 = polyLine.Point0 - sideVector;
            retQuad.Point1 = polyLine.Point1 - sideVector;
            retQuad.Point2 = polyLine.Point1 + sideVector;
            retQuad.Point3 = polyLine.Point0 + sideVector;
        }