示例#1
0
        public override void Draw(GameTime gameTime)
        {
            //position(body.GetPosition().X * game.physics_scale, body.GetPosition().Y * game.physics_scale);

            Vector2 light_origin     = new Vector2(x, y);
            float   current_rotation = body.GetAngle();

            while (current_rotation < 0)
            {
                current_rotation += (float)Math.PI * 2;
            }
            current_rotation = current_rotation % ((float)Math.PI * 2);

            //thing!
            rotation = current_rotation;

            List <Vector2> testPoints = new List <Vector2>();
            SortedList <float, Vector2> intersectionPoints = new SortedList <float, Vector2>();

            //firstly, cast out 9 rays around the circle/cone; this will define the base shape of this light
            //and ensure that lack of collision targets doesn't lead to rendering holes
            for (int i = -rays_to_cast / 2; i <= rays_to_cast / 2; i++)
            {
                Vector2 target = Vector2.Transform(new Vector2(0, -ray_length), Matrix.CreateRotationZ((light_spread_angle / rays_to_cast) * i + current_rotation)) + light_origin;
                testPoints.Add(target);
            }

            //Gather a list of all nearby fixtures; we'll use these to generate test points and cast rays
            AABB aabb;

            aabb.lowerBound = body.GetPosition() - new Vector2(ray_length, ray_length);
            aabb.upperBound = body.GetPosition() + new Vector2(ray_length, ray_length);
            overlapping_fixtures.Clear();
            game.world.QueryAABB(QueryCallback, ref aabb);

            //Console.WriteLine(overlapping_fixtures.Count());

            //Gather a list of all test points in the scene
            foreach (Fixture f in overlapping_fixtures)
            {
                Type shapeType = f.GetType();

                if (f.GetShape() is PolygonShape)
                {
                    PolygonShape polygon = (PolygonShape)f.GetShape();

                    for (int curVert = 0; curVert < polygon.GetVertexCount(); curVert++)
                    {
                        //transform this point based on the body transforms
                        Vector2 target = Vector2.Transform(polygon.GetVertex(curVert), Matrix.CreateRotationZ(f.GetBody().GetAngle())) + f.GetBody().GetPosition();

                        if (Vector2.DistanceSquared(light_origin, target) <= ray_length * ray_length * 2)
                        {
                            float ray_angle = (float)Math.Atan2((target - light_origin).Y, (target - light_origin).X) + (float)Math.PI / 2;
                            float light_min = current_rotation - light_spread_angle / 2;
                            float light_max = current_rotation + light_spread_angle / 2;

                            while (ray_angle > Math.PI)
                            {
                                ray_angle -= (float)Math.PI * 2;
                            }
                            while (ray_angle < light_min)
                            {
                                ray_angle += (float)Math.PI * 2;
                            }

                            if (ray_angle < light_max)
                            {
                                //Add it to the list for processing
                                testPoints.Add(target);
                            }
                        }
                    }
                }
                else if (f.GetShape() is EdgeShape)
                {
                    //Do the same thing, except for edge shapes

                    //only v1 and v2 will count here
                    EdgeShape line = (EdgeShape)f.GetShape();

                    Vector2 target1 = Vector2.Transform(line._vertex1, Matrix.CreateRotationZ(f.GetBody().GetAngle())) + f.GetBody().GetPosition();
                    if (Vector2.DistanceSquared(light_origin, target1) <= ray_length * ray_length * 2)
                    {
                        float ray_angle = (float)Math.Atan2((target1 - light_origin).Y, (target1 - light_origin).X) + (float)Math.PI / 2;
                        float light_min = current_rotation - light_spread_angle / 2;
                        float light_max = current_rotation + light_spread_angle / 2;

                        while (ray_angle > Math.PI)
                        {
                            ray_angle -= (float)Math.PI * 2;
                        }
                        while (ray_angle < light_min)
                        {
                            ray_angle += (float)Math.PI * 2;
                        }

                        if (ray_angle < light_max)
                        {
                            testPoints.Add(target1);
                        }
                    }

                    Vector2 target2 = Vector2.Transform(line._vertex2, Matrix.CreateRotationZ(f.GetBody().GetAngle())) + f.GetBody().GetPosition();
                    if (Vector2.DistanceSquared(light_origin, target2) <= ray_length * ray_length * 2)
                    {
                        float ray_angle = (float)Math.Atan2((target2 - light_origin).Y, (target2 - light_origin).X) + (float)Math.PI / 2;
                        float light_min = current_rotation - light_spread_angle / 2;
                        float light_max = current_rotation + light_spread_angle / 2;

                        while (ray_angle < light_min)
                        {
                            ray_angle += (float)Math.PI * 2;
                        }

                        if (ray_angle < light_max)
                        {
                            testPoints.Add(target2);
                        }
                    }
                }
            }

            //For every test point, calculate the closest intersection
            foreach (Vector2 point in testPoints)
            {
                Vector2 target = point;

                //normalize this point, then multiply it by the length; this will give us a
                //line originating from the light source, and cast "toward" this corner point
                //that is exactly the distance of the light's normal max range
                target -= light_origin;
                target.Normalize();
                target = target * ray_length;

                //cast two more rays at slight angle offsets, to deal with corner edge cases
                //(only 2, so everything after this is doubled, because loops are silly for only 2 elements)
                Vector2 target_neg = Vector2.Transform(target, Matrix.CreateRotationZ(-0.0001f));
                Vector2 target_pos = Vector2.Transform(target, Matrix.CreateRotationZ(0.0001f));

                target_neg += light_origin;
                target_pos += light_origin;

                //perform the ray cast, and figure out what to do about the result
                float closestFractionNeg = Math.Min(rayCast(light_origin, target_neg), 1f);
                float closestFractionPos = Math.Min(rayCast(light_origin, target_pos), 1f);

                Vector2 intersectPointPos = light_origin + closestFractionPos * (target_pos - light_origin);
                Vector2 intersectPointNeg = light_origin + closestFractionNeg * (target_neg - light_origin);

                if (game.input.DevMode)
                {
                    game.drawLine(light_origin, intersectPointNeg, (Color.White));
                    game.drawLine(light_origin, intersectPointPos, (Color.White));
                }

                intersectionPoints[(float)Math.Atan2((float)(intersectPointNeg - light_origin).Y, (float)(intersectPointNeg - light_origin).X)] = intersectPointNeg;
                intersectionPoints[(float)Math.Atan2((float)(intersectPointPos - light_origin).Y, (float)(intersectPointPos - light_origin).X)] = intersectPointPos;
            }

            //now, attempt (poorly) to create a triangle mesh based on our hopefully sorted points
            GraphicsDevice gd = game.GraphicsDevice;

            //VertexPositionColor[] vertex_list = new VertexPositionColor[_max_rays * 3];

            ArrayList vertex_list = new ArrayList();

            Color originColor  = sprite_color;
            Color outsideColor = Color.FromNonPremultiplied(sprite_color.R, sprite_color.G, sprite_color.B, 0);
            Color intersectionColor;

            bool first = true;

            foreach (var intersection in intersectionPoints)
            {
                intersectionColor = Color.Lerp(originColor, outsideColor, Math.Min(1.0f, ((intersection.Value - light_origin).Length() / (float)ray_length)));
                if (!first)
                {
                    //finish the last triangle
                    vertex_list.Add(new VertexPositionColor(new Vector3(intersection.Value.X, intersection.Value.Y, 0), intersectionColor));
                }
                //start a new triangle leading with this edge
                vertex_list.Add(new VertexPositionColor(new Vector3(light_origin.X, light_origin.Y, 0), sprite_color));
                vertex_list.Add(new VertexPositionColor(new Vector3(intersection.Value.X, intersection.Value.Y, 0), intersectionColor));

                first = false;
            }

            intersectionColor = Color.Lerp(originColor, outsideColor, Math.Min(1.0f, ((intersectionPoints.First().Value - light_origin).Length() / (float)ray_length)));

            //finish the very last triangle (it loops to the beginning)
            vertex_list.Add(new VertexPositionColor(new Vector3(intersectionPoints.First().Value.X, intersectionPoints.First().Value.Y, 0), intersectionColor));

            buffer.SetData <VertexPositionColor>((VertexPositionColor[])vertex_list.ToArray(typeof(VertexPositionColor)));
            gd.SetVertexBuffer(buffer);

            //make sure the light shader is set up
            light_shader.World = Matrix.CreateTranslation(new Vector3(-game.camera.X / 10, -game.camera.Y / 10, 0));
            light_shader.CurrentTechnique.Passes[0].Apply();

            //draw them primitives!
            gd.DrawPrimitives(PrimitiveType.TriangleList, 0, intersectionPoints.Count);
        }
        float fired(Fixture fixture, Vector2 point, Vector2 normal, float fraction)
        {
            isFired = true;
            Body         affectedBody    = fixture.GetBody();
            PolygonShape affectedPolygon = (PolygonShape)fixture.GetShape();
            int          fixtureIndex    = listAfterLaser.IndexOf(affectedBody);

            if (fixtureIndex == -1)
            {
                listAfterLaser.Add(affectedBody);
                listEntryPoint.Add(point);
            }
            else
            {
                listExitPoint.Add(point);
                Vector2 pointCenter = new Vector2((point.X + listEntryPoint[fixtureIndex].X) / 2, (point.Y + listEntryPoint[fixtureIndex].Y) / 2);
                pointCollideCenter.Add(pointCenter);
                float rayAngle = (float)Math.Atan2(listEntryPoint[fixtureIndex].Y - point.Y,
                                                   listEntryPoint[fixtureIndex].X - point.X);

                int       numVt        = affectedPolygon.GetVertexCount();
                Vector2[] polyVertices = new Vector2[numVt];
                for (int i = 0; i < numVt; i++)
                {
                    polyVertices[i] = affectedPolygon.GetVertex(i);
                }


                List <Vector2> newPolyVertices1 = new List <Vector2>();
                List <Vector2> newPolyVertices2 = new List <Vector2>();
                int            currentPoly      = 0;
                bool           cutPlace1        = false;
                bool           cutPlace2        = false;

                for (int i = 0; i < polyVertices.Length; i++)
                {
                    Vector2 worldPoint = affectedBody.GetWorldPoint(polyVertices[i]);
                    float   cutAngel   = (float)Math.Atan2(worldPoint.Y - pointCenter.Y, worldPoint.X - pointCenter.X) - rayAngle;
                    //if (cutAngel < Math.PI*-1)
                    //{
                    //    cutAngel += (float)(2 * Math.PI);
                    //}
                    //if (cutAngel > 0 && cutAngel <= Math.PI)
                    //{
                    //    listPoint1.Add(worldPoint);
                    //}
                    //else
                    //{
                    //    listPoint2.Add(worldPoint);
                    //}
                    if (cutAngel < Math.PI * -1)
                    {
                        cutAngel += (float)(2 * Math.PI);
                    }

                    if (cutAngel > 0 && cutAngel <= Math.PI)
                    {
                        if (currentPoly == 2)
                        {
                            cutPlace1 = true;
                            newPolyVertices1.Add(point);
                            newPolyVertices1.Add(listEntryPoint[fixtureIndex]);
                        }
                        newPolyVertices1.Add(worldPoint);
                        currentPoly = 1;
                    }
                    else
                    {
                        if (currentPoly == 1)
                        {
                            cutPlace2 = true;
                            newPolyVertices2.Add(listEntryPoint[fixtureIndex]);
                            newPolyVertices2.Add(point);
                        }
                        newPolyVertices2.Add(worldPoint);
                        currentPoly = 2;
                    }
                }
                if (!cutPlace1)
                {
                    newPolyVertices1.Add(point);
                    newPolyVertices1.Add(listEntryPoint[fixtureIndex]);
                }
                if (!cutPlace2)
                {
                    newPolyVertices2.Add(listEntryPoint[fixtureIndex]);
                    newPolyVertices2.Add(point);
                }

                createSlice(convertListToArray(newPolyVertices1), newPolyVertices1.Count);
                createSlice(convertListToArray(newPolyVertices2), newPolyVertices2.Count);
                physicsWorld.DestroyBody(affectedBody);
            }
            return(1.0f);
        }