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; } }
// 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); }
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)); }
/// <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); }
/// <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)); }
/// <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)); }
/// <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)); }
/// <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; }
/// <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]; }
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]); }