public override List <Vertices> Process(TextureContent input, ContentProcessorContext context)
        {
            BitmapContent bitmap = input.Faces[0][0];

            byte[] bytes = bitmap.GetPixelData();
            Debug.Assert(bytes.Length % 4 == 0);

            // Note(manu): If this were C/C++, we could simply reinterpret the byte-array as a uint-array...
            uint[] data = new uint[bytes.Length / 4];
            for (int dataIndex = 0; dataIndex < data.Length; dataIndex++)
            {
                int byteIndex = dataIndex * 4;
                data[dataIndex] = BitConverter.ToUInt32(bytes, byteIndex);
            }

            DateTime         vertStart        = DateTime.Now;
            TextureConverter textureConverter = new TextureConverter(data, bitmap.Width)
            {
                PolygonDetectionType    = PolygonDetectionType,
                HoleDetection           = HoleDetection,
                MultipartDetection      = MultipartDetection,
                PixelOffsetOptimization = PixelOffsetOptimization,
                Transform      = Matrix.CreateScale(UniformScale * Conversion.RenderScale), // TODO(manu): Use z=1 instead?
                AlphaTolerance = (byte)AlphaTolerance,
                HullTolerance  = HullTolerance,
            };
            List <Vertices> vertices     = textureConverter.DetectVertices();
            TimeSpan        vertDuration = DateTime.Now - vertStart;

            Diagnostic(context.Logger, $"Parsing vertices took {vertDuration.TotalSeconds.ToString("0.000")} seconds (VelcroPhysics).");

            return(vertices);
        }
        /// <summary>
        /// Generates the bodies for each of the pieces of the sheet.
        /// </summary>
        /// <param name="levelWorld">The world to create bodies in</param>
        /// <param name="collisionCategory">The FULL category of what these bodies should collide with</param>
        public void GenerateBodies(World levelWorld, Category collisionCategory)   //TODO get scale
        {
            BodiesGenerated = true;

            int SpriteSheetSize = SpriteSheet.Width * SpriteSheet.Height;
            int IndividualSize  = ImageSize.X * ImageSize.Y;

            uint[] TextureData = new uint[SpriteSheetSize]; //Array to copy texture info into
            SpriteSheet.GetData <uint>(TextureData);        //Gets which pixels of the texture are actually filled

            List <uint[]> IndividualData = new List <uint[]>();

            for (int Processed = 0; Processed < SpriteSheetSize && IndividualData.Count < Bodies.Length; Processed += IndividualSize)
            {
                //TODO CHECK IF THIS WORKS TESTING TO CUT OFF ARRAY ARGUMENT EXCEPTION
                uint[] TempArray = new uint[IndividualSize];

                try {
                    Array.Copy(TextureData, Processed, TempArray, 0, IndividualSize);
                } catch (ArgumentException) {
                    //At the end of textures the amount of data left might be to small
                    Array.Copy(TextureData, Processed, TempArray, 0, TextureData.Length - Processed);
                }

                IndividualData.Add(TempArray);
            }

            int BodyIndex = 0;

            for (int count = 0; count < IndividualData.Count; ++count)
            {
                uint[] I = IndividualData[count];

                Vertices        vertices   = TextureConverter.DetectVertices(I, ImageSize.X);
                List <Vertices> VertexList = Triangulate.ConvexPartition(vertices, TriangulationAlgorithm.Earclip);
                //error with bayazit and deluny
                //Siedle doesnt wortk
                //Earclip & flipcode results in glitches
                //Earclip works very well

                Vector2 VertScale = new Vector2(ConvertUnits.ToSimUnits(Scale));

                foreach (Vertices vert in VertexList)
                {
                    vert.Scale(ref VertScale); //Scales the vertices to match the size we specified
                }
                Vector2 Centroid = -vertices.GetCentroid();
                vertices.Translate(ref Centroid);
                //basketOrigin = -centroid;

                //This actually creates the body
                Bodies[BodyIndex]          = BodyFactory.CreateCompoundPolygon(levelWorld, VertexList, 1, Vector2.Zero);
                Bodies[BodyIndex].BodyType = BodyType.Dynamic;
                Bodies[BodyIndex].Enabled  = false;
                //Bodies[BodyIndex].CollisionCategories = collisionCategory;
                ++BodyIndex;
            }
        }
Beispiel #3
0
        // TODO in case we need this somewhere else it might be a good idea to move this to the EntityFactory
        private void CreateAsteroid(FarPosition position, IUniformRandom random, ContentManager content)
        {
            // Randomly scale and rotate it.
            var scale = (float)random.NextDouble(0.5f, 1f);
            var angle = (float)random.NextDouble() * MathHelper.TwoPi;

            // Determine shape for physics system.
            var textureName = "Textures/Asteroids/rock_" + random.NextInt32(1, 14);
            var texture     = content.Load <Texture2D>(textureName);
            var hull        = new List <Vector2>(TextureConverter.DetectVertices(texture, 8f, textureName: textureName)[0]);

            for (var k = 0; k < hull.Count; ++k)
            {
                hull[k] -= new Vector2(texture.Width / 2f, texture.Height / 2f);
                hull[k]  = XnaUnitConversion.ToSimulationUnits(hull[k]) * scale;
            }
            var polygons = EarClipDecomposer.ConvexPartition(hull);

            // Create physical representation.
            var entity = Manager.AddEntity();
            var body   = Manager.AddBody(entity, position, angle, Body.BodyType.Dynamic);

            foreach (var polygon in polygons)
            {
                Manager.AttachPolygon(body, polygon, density: 1000f, restitution: 0.2f);
            }
            // Slow down to allow reaching sleep state again.
            body.LinearDamping  = 0.05f * Space.Util.Settings.TicksPerSecond;
            body.AngularDamping = 0.025f * Space.Util.Settings.TicksPerSecond;

            // Bounds of the asteroid for rendering culling. We use the diagonal for a loose fit that
            // contains every possible rotated state of the texture.
            var width    = UnitConversion.ToSimulationUnits(texture.Width);
            var height   = UnitConversion.ToSimulationUnits(texture.Height);
            var diagonal = (float)Math.Sqrt(width * width + height * height);
            var bounds   = new FarRectangle(-diagonal / 2, -diagonal / 2, diagonal, diagonal);

            // Rendering stuff.
            Manager.AddComponent <Indexable>(entity).Initialize(bounds, CameraSystem.IndexId);
            Manager.AddComponent <Indexable>(entity).Initialize(bounds, InterpolationSystem.IndexId);
            Manager.AddComponent <SimpleTextureDrawable>(entity).Initialize(textureName, scale);

            // Auto removal.
            Manager.AddComponent <CellDeath>(entity).Initialize(true);
            Manager.AddComponent <Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId);

            // Make it destructible.
            var health = Manager.AddComponent <Health>(entity);

            health.Value        = health.MaxValue = 200 * scale;
            health.Regeneration = 0f;

            // As they don't move on their own, start asteroids as sleeping to save performance.
            body.IsAwake = false;
        }
        /// <summary>
        /// Populate the vertices of a collider
        /// </summary>
        /// <param name="collider">Collider to setup vertices in.</param>
        /// <param name="p">Polygon creation parameters</param>
        public void PopulateCollider(PolygonCollider2D collider, ref PolygonParameters p)
        {
            try
            {
                if (p.Texture.format != TextureFormat.ARGB32 && p.Texture.format != TextureFormat.BGRA32 && p.Texture.format != TextureFormat.RGBA32 &&
                    p.Texture.format != TextureFormat.RGB24 && p.Texture.format != TextureFormat.Alpha8 && p.Texture.format != TextureFormat.RGBAFloat &&
                    p.Texture.format != TextureFormat.RGBAHalf && p.Texture.format != TextureFormat.RGB565)
                {
                    Debug.LogWarning("Advanced Polygon Collider works best with a non-compressed texture in ARGB32, BGRA32, RGB24, RGBA4444, RGB565, RGBAFloat or RGBAHalf format");
                }
                int width  = (int)p.Rect.width;
                int height = (int)p.Rect.height;
                int x      = (int)p.Rect.x;
                int y      = (int)p.Rect.y;
                UnityEngine.Color[] pixels = p.Texture.GetPixels(x, y, width, height, 0);
                List <Vertices>     verts  = geometryDetector.DetectVertices(pixels, width, p.AlphaTolerance);
                int pathIndex         = 0;
                List <Vector2[]> list = new List <Vector2[]>();

                for (int i = 0; i < verts.Count; i++)
                {
                    ProcessVertices(collider, verts[i], list, ref p, ref pathIndex);
                }

#if UNITY_EDITOR
                if (Application.isPlaying)
                {
#endif

                if (p.UseCache)
                {
                    CacheKey key = new CacheKey();
                    key.Texture = p.Texture;
                    key.Rect    = p.Rect;
                    cache[key]  = list;
                }

#if UNITY_EDITOR
            }
            else if (p.UseCache)
            {
                AddEditorCache(ref p, list);
            }
#endif

                Debug.Log("Updated polygon.");
            }
            catch (Exception ex)
            {
                Debug.LogError("Error creating collider: " + ex);
            }
        }
        /// <summary>
        /// Creates a list of vertices from a texture.
        /// </summary>
        /// <param name="texture">The texture to make a body from</param>
        /// <param name="scale">The scale of the texture</param>
        /// <param name="imageSize">The size of each individual image in the hitbox</param>
        /// <param name="density">The density of the object (Will almost always be one</param>
        /// <param name="algorithm">The decomposition algorithm to use</param>
        /// <remarks> Available algorithms to use are Bayazit, Dealuny, Earclip, Flipcode, Seidel, SeidelTrapazoid</remarks>
        /// @warning In order for this to work the input must have a transparent background. I highly reccomend that you
        /// only use this with PNGs as that is what I have tested and I know they work. This will only produce a bosy as
        /// clean as the texture you give it, so avoid partically transparent areas and little edges.
        private List <Vertices>[] CreateVerticesFromTexture(Texture2D texture, float scale, Point imageSize,
                                                            float density = 1, TriangulationAlgorithm algorithm = TriangulationAlgorithm.Earclip)
        {
            int SpriteSheetSize = texture.Width * texture.Height;
            int IndividualSize  = imageSize.X * imageSize.Y;

            uint[] TextureData = new uint[SpriteSheetSize]; //Array to copy texture info into
            texture.GetData(TextureData);                   //Gets which pixels of the texture are actually filled

            List <uint[]> IndividualData = new List <uint[]>();

            for (int Processed = 0; Processed < SpriteSheetSize; Processed += IndividualSize)
            {
                uint[] TempArray = new uint[IndividualSize];

                try {
                    Array.Copy(TextureData, Processed, TempArray, 0, IndividualSize);
                } catch (ArgumentException) {
                    //At the end of textures the amount of data left might be to small
                    Array.Copy(TextureData, Processed, TempArray, 0, TextureData.Length - Processed);
                }


                IndividualData.Add(TempArray);
            }

            List <Vertices>[] TextureVertices = new List <Vertices> [IndividualData.Count];

            for (int count = 0; count < IndividualData.Count; ++count)
            {
                uint[] I = IndividualData[count];

                Vertices        vertices   = TextureConverter.DetectVertices(I, texture.Width);
                List <Vertices> VertexList = Triangulate.ConvexPartition(vertices, algorithm);

                Vector2 VertScale = new Vector2(ConvertUnits.ToSimUnits(scale));

                foreach (Vertices vert in VertexList)
                {
                    vert.Scale(ref VertScale); //Scales the vertices to match the size we specified
                }
                Vector2 Centroid = -vertices.GetCentroid();
                vertices.Translate(ref Centroid);
                //basketOrigin = -centroid;

                TextureVertices[count] = VertexList;
            }

            return(TextureVertices);
        }
Beispiel #6
0
        public Body CreatePolygonFromTexture(Texture2D tex, World world, float density, Vector2 position, float scale, TriangulationAlgorithm algorithm = TriangulationAlgorithm.Bayazit)
        {
            uint[] texData = new uint[tex.Width * tex.Height];
            tex.GetData <uint>(texData);

            Vertices        vertices   = TextureConverter.DetectVertices(texData, tex.Width);
            List <Vertices> vertexList = Triangulate.ConvexPartition(vertices, algorithm);

            Vector2 vertScale = new Vector2(ConvertUnits.ToSimUnits(scale));

            foreach (Vertices vert in vertexList)
            {
                vert.Scale(ref vertScale);
            }

            Vector2 centroid = vertices.GetCentroid();

            vertices.Translate(ref centroid);
            //basketOrigin = -centroid;
            return(BodyFactory.CreateCompoundPolygon(world, vertexList, density, position, 100));
        }
Beispiel #7
0
        /// <summary>
        /// Creates a polygon from a texture. This is the important function here.
        /// </summary>
        /// <param name="texture">The texture to make a body from</param>
        /// <param name="density">The density of the object (Will almost always be one</param>
        /// <param name="position">The position (in meters) of the object in the world</param>
        /// <param name="scale">The scale of the object (how much to change its size)</param>
        /// <param name="algorithm">The decomposition algorithm to use</param>
        /// <remarks> Available algorithms to use are Bayazit, Dealuny, Earclip, Flipcode, Seidel, SeidelTrapazoid</remarks>
        /// @warning In order for this to work the input must have a transparent background. I highly reccomend that you
        /// only use this with PNGs as that is what I have tested and I know they work. This will only produce a bosy as
        /// clean as the texture you give it, so avoid partically transparent areas and little edges.
        private Body CreatePolygonFromTexture(Texture2D texture, float density, Vector2 position, float scale,
                                              TriangulationAlgorithm algorithm = TriangulationAlgorithm.Bayazit)
        {
            uint[] TextureData = new uint[texture.Width * texture.Height]; //Array to copy texture info into
            texture.GetData <uint>(TextureData);                           //Gets which pixels of the texture are actually filled

            Vertices        vertices   = TextureConverter.DetectVertices(TextureData, texture.Width);
            List <Vertices> vertexList = Triangulate.ConvexPartition(vertices, algorithm);

            Vector2 vertScale = new Vector2(ConvertUnits.ToSimUnits(scale));

            foreach (Vertices vert in vertexList)
            {
                vert.Scale(ref vertScale); //Scales the vertices to match the size we specified
            }
            Vector2 centroid = -vertices.GetCentroid();

            vertices.Translate(ref centroid);
            //basketOrigin = -centroid;

            //This actually creates the body
            return(BodyFactory.CreateCompoundPolygon(LevelWorld, vertexList, density, position));
        }
        /// <summary>
        /// Creates a list of vertices from a texture.
        /// </summary>
        /// <param name="texture">The texture to make a body from</param>
        /// <param name="scale">The scale of the texture</param>
        /// <param name="algorithm">The decomposition algorithm to use</param>
        /// <remarks> Available algorithms to use are Bayazit, Dealuny, Earclip, Flipcode, Seidel, SeidelTrapazoid</remarks>
        /// @warning In order for this to work the input must have a transparent background. I highly reccomend that you
        /// only use this with PNGs as that is what I have tested and I know they work. This will only produce a bosy as
        /// clean as the texture you give it, so avoid partically transparent areas and little edges.
        private List <Vertices> CreateVerticesFromTexture(Texture2D texture, float scale,
                                                          TriangulationAlgorithm algorithm = TriangulationAlgorithm.Earclip)
        {
            int SpriteSheetSize = texture.Width * texture.Height;

            uint[] TextureData = new uint[SpriteSheetSize]; //Array to copy texture info into
            texture.GetData(TextureData);                   //Gets which pixels of the texture are actually filled

            Vertices        vertices   = TextureConverter.DetectVertices(TextureData, texture.Width);
            List <Vertices> VertexList = Triangulate.ConvexPartition(vertices, algorithm);

            Vector2 VertScale = new Vector2(ConvertUnits.ToSimUnits(scale));

            foreach (Vertices vert in VertexList)
            {
                vert.Scale(ref VertScale); //Scales the vertices to match the size we specified
            }
            Vector2 Centroid = -vertices.GetCentroid();

            vertices.Translate(ref Centroid);
            //basketOrigin = -centroid;

            return(VertexList);
        }
Beispiel #9
0
 /// <summary>
 /// Detects the vertices by analyzing the texture data.
 /// </summary>
 /// <param name="data">The texture data.</param>
 /// <param name="width">The texture width.</param>
 /// <param name="hullTolerance">The hull tolerance.</param>
 /// <param name="alphaTolerance">The alpha tolerance.</param>
 /// <param name="multiPartDetection">if set to <c>true</c> it will perform multi part detection.</param>
 /// <param name="holeDetection">if set to <c>true</c> it will perform hole detection.</param>
 /// <returns></returns>
 public static List <Vertices> CreatePolygon(uint[] data, int width, float hullTolerance,
                                             byte alphaTolerance, bool multiPartDetection, bool holeDetection)
 {
     return(TextureConverter.DetectVertices(data, width, hullTolerance, alphaTolerance,
                                            multiPartDetection, holeDetection));
 }
Beispiel #10
0
 /// <summary>
 /// Detects the vertices by analyzing the texture data.
 /// </summary>
 /// <param name="data">The texture data.</param>
 /// <param name="width">The texture width.</param>
 /// <param name="holeDetection">if set to <c>true</c> it will perform hole detection.</param>
 /// <returns></returns>
 public static Vertices CreatePolygon(uint[] data, int width, bool holeDetection)
 {
     return(TextureConverter.DetectVertices(data, width, holeDetection));
 }
Beispiel #11
0
 /// <summary>
 /// Detects the vertices by analyzing the texture data.
 /// </summary>
 /// <param name="data">The texture data.</param>
 /// <param name="width">The texture width.</param>
 /// <returns></returns>
 public static Vertices CreatePolygon(uint[] data, int width)
 {
     return(TextureConverter.DetectVertices(data, width));
 }
Beispiel #12
0
        /// <summary>
        /// Detects the vertices of the supplied texture data.
        /// </summary>
        /// <param name="data">The texture data.</param>
        /// <param name="width">The texture width.</param>
        /// <param name="holeDetection">if set to <c>true</c> it will perform hole detection.</param>
        /// <param name="hullTolerance">The hull tolerance.</param>
        /// <param name="alphaTolerance">The alpha tolerance.</param>
        /// <param name="multiPartDetection">if set to <c>true</c> it will perform multi part detection.</param>
        /// <returns></returns>
        public static List<Vertices> DetectVertices(uint[] data, int width, float hullTolerance, byte alphaTolerance, bool multiPartDetection, bool holeDetection)
        {
            TextureConverter tc =
                new TextureConverter(data, width)
                {
                    HullTolerance = hullTolerance,
                    AlphaTolerance = alphaTolerance,
                    MultipartDetection = multiPartDetection,
                    HoleDetection = holeDetection
                };

            List<Vertices> detectedVerticesList = tc.DetectVertices();
            List<Vertices> result = new List<Vertices>();

            for (int i = 0; i < detectedVerticesList.Count; i++)
            {
                result.Add(detectedVerticesList[i]);
            }

            return result;
        }
Beispiel #13
0
        /// <summary>
        /// Detects the vertices of the supplied texture data.
        /// </summary>
        /// <param name="data">The texture data.</param>
        /// <param name="width">The texture width.</param>
        /// <param name="holeDetection">if set to <c>true</c> it will perform hole detection.</param>
        /// <returns></returns>
        public static Vertices DetectVertices(uint[] data, int width, bool holeDetection)
        {
            TextureConverter tc =
                new TextureConverter(data, width)
                {
                    HoleDetection = holeDetection
                };

            List<Vertices> detectedVerticesList = tc.DetectVertices();

            return detectedVerticesList[0];
        }
Beispiel #14
0
        private CacheEntry GetCacheEntry(int entity)
        {
            var equipment = (SpaceItemSlot)Manager.GetComponent(entity, ItemSlot.TypeId);
            var hash      = HashEquipment(equipment);

            // Create cache texture if necessary.
            if (!ModelCache.ContainsKey(hash))
            {
                // Simulations may run multi-threaded, so we need to lock out static table here.
                lock (ModelCache)
                {
                    // Maybe we got our entry while waiting for our lock?
                    if (!ModelCache.ContainsKey(hash))
                    {
                        // No cache entry yet, create it. Determine the needed size of the texture.
                        var size = ComputeModelSize(equipment, Vector2.Zero);
                        // Then create it and push it as our current render target.
                        var target = new RenderTarget2D(
                            _spriteBatch.GraphicsDevice,
                            (int)System.Math.Ceiling(size.Width),
                            (int)System.Math.Ceiling(size.Height));

                        var previousRenderTargets = _spriteBatch.GraphicsDevice.GetRenderTargets();
                        _spriteBatch.GraphicsDevice.SetRenderTarget(target);
                        _spriteBatch.GraphicsDevice.Clear(Color.Transparent);
                        _spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);

                        // Render the actual equipment into our texture.
                        RenderEquipment(equipment, new Vector2(-size.X, -size.Y));

                        // Restore the old render state.
                        _spriteBatch.End();
                        _spriteBatch.GraphicsDevice.SetRenderTargets(previousRenderTargets);

                        // Build polygon hull.
                        var abstraction = Manager.GetComponent(entity, Avatar.TypeId) == null
                                              ? NPCModelTolerance
                                              : PlayerModelTolerance;
                        var hull = new List <Vector2>(TextureConverter.DetectVertices(target, abstraction)[0]);
                        for (var i = 0; i < hull.Count; ++i)
                        {
                            // Center at origin.
                            hull[i] += new Vector2(size.X, size.Y);
                            // Scale to simulation units.
                            hull[i] = XnaUnitConversion.ToSimulationUnits(hull[i]);
                        }

                        // Create a new cache entry for this equipment combination.
                        ModelCache[hash] = new CacheEntry
                        {
                            Texture      = target,
                            Offset       = new Vector2(-size.X, -size.Y),
                            PolygonHulls = EarClipDecomposer.ConvexPartition(hull)
                        };
                    }
                }
            }

            // Update last access time.
            ModelCache[hash].Age = 0;

            // Then return the entry!
            return(ModelCache[hash]);
        }