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