/// <summary> /// Starts the explosion. /// </summary> /// <param name="damage"></param> /// <param name="type"></param> /// <param name="explosionSphere"></param> /// <param name="lifespanInMiliseconds"></param> /// <param name="explosionForceDirection"></param> /// <param name="groupMask"></param> /// <param name="createExplosionDebris"></param> /// <param name="cascadeLevel"></param> /// <param name="hitEntity"></param> /// <param name="particleScale"></param> /// <param name="ownerEntity"></param> /// <param name="affectVoxels"></param> /// <param name="applyForceAndDamage"></param> /// <param name="createDecals"></param> /// <param name="direction">If applicable, gives the direction of the explosion, e.g. when it was caused by a missile (with its moving direction).</param> public void Start(ref MyExplosionInfo explosionInfo) { //MyCommonDebugUtils.AssertDebug(explosionInfo.ExplosionSphere.Radius <= MyExplosionsConstants.EXPLOSION_RADIUS_MAX); MyCommonDebugUtils.AssertDebug(explosionInfo.ExplosionSphere.Radius > 0); MyRender.GetRenderProfiler().StartProfilingBlock("MyExplosion.Start"); m_explosionSphere = explosionInfo.ExplosionSphere; m_elapsedMiliseconds = 0; m_lifespanInMiliseconds = explosionInfo.LifespanMiliseconds; if (explosionInfo.PlaySound) { MyRender.GetRenderProfiler().StartProfilingBlock("Sound"); // Play explosion sound if (m_explosionCue != null && m_explosionCue.Value.IsPlaying) { m_explosionCue.Value.Stop(SharpDX.XACT3.StopFlags.Immediate); } m_explosionCue = MyAudio.AddCue3D(GetCueEnumByExplosionType(explosionInfo.ExplosionType), m_explosionSphere.Center, Vector3.Zero, Vector3.Zero, Vector3.Zero); MyRender.GetRenderProfiler().EndProfilingBlock(); } MyRender.GetRenderProfiler().StartProfilingBlock("Light"); // Light of explosion /* * m_light = MyLights.AddLight(); * if (m_light != null) * { * m_light.Start(MyLight.LightTypeEnum.PointLight, m_explosionSphere.Center, MyExplosionsConstants.EXPLOSION_LIGHT_COLOR, 1, Math.Min(m_explosionSphere.Radius * 8.0f, MyLightsConstants.MAX_POINTLIGHT_RADIUS)); * m_light.Intensity = 2.0f; * } */ MyRender.GetRenderProfiler().EndProfilingBlock(); // close explosion check bool close = IsExplosionClose(explosionInfo.ExplosionSphere); MyParticleEffectsIDEnum newParticlesType; switch (explosionInfo.ExplosionType) { case MyExplosionTypeEnum.SMALL_SHIP_EXPLOSION: // Create metal debris objects thrown from the explosion // This must be called before ApplyExplosionForceAndDamage (because there we apply impulses to the debris) // Throw a lot of debrises, more than only if some metalic object is hit (because this is destruction of a ship) //MyPhysObjectExplosionDebrises.CreateExplosionDebris(m_explosionSphere.Center, 1); newParticlesType = MyParticleEffectsIDEnum.Explosion_Smallship; break; case MyExplosionTypeEnum.MISSILE_EXPLOSION: newParticlesType = // ? MyParticleEffectsIDEnum.Explosion_Missile_Close MyParticleEffectsIDEnum.Explosion_Missile; break; case MyExplosionTypeEnum.BOMB_EXPLOSION: case MyExplosionTypeEnum.GRAVITY_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_Bomb; break; case MyExplosionTypeEnum.AMMO_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_Ammo; break; case MyExplosionTypeEnum.BLASTER_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_Blaster; break; case MyExplosionTypeEnum.BIOCHEM_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_BioChem; break; case MyExplosionTypeEnum.EMP_EXPLOSION: case MyExplosionTypeEnum.FLASH_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_EMP; break; case MyExplosionTypeEnum.METEOR_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_Meteor; break; case MyExplosionTypeEnum.NUCLEAR_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_Nuclear; break; case MyExplosionTypeEnum.PLASMA_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_Plasma; break; case MyExplosionTypeEnum.SMALL_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_SmallPrefab; break; case MyExplosionTypeEnum.LARGE_SHIP_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_Huge; break; case MyExplosionTypeEnum.LARGE_PREFAB_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_Large; break; case MyExplosionTypeEnum.MEDIUM_PREFAB_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_Medium; break; case MyExplosionTypeEnum.ASTEROID_EXPLOSION: newParticlesType = MyParticleEffectsIDEnum.Explosion_Asteroid; break; default: throw new System.NotImplementedException(); break; } if (explosionInfo.Damage > 0) { MyRender.GetRenderProfiler().StartProfilingBlock("Voxel or collision"); // If explosion sphere intersects a voxel map, we need to cut out a sphere, spawn debrises, etc MyVoxelMap voxelMap = explosionInfo.AffectVoxels && explosionInfo.EmpDamage == 0 ? MyVoxelMaps.GetOverlappingWithSphere(ref m_explosionSphere) : null; if (voxelMap != null) { // Dirty explosion with a lot of dust MyMwcVoxelMaterialsEnum?voxelMaterial = null; float voxelContentRemovedInPercent = 0; bool createDebris = true; // We want to create debris if (explosionInfo.HitEntity != null) // but not when we hit prefab { createDebris &= explosionInfo.HitEntity is MyVoxelMap; } //cut off BoundingSphere voxelExpSphere = new BoundingSphere(explosionInfo.VoxelExplosionCenter, m_explosionSphere.Radius * explosionInfo.VoxelCutoutScale); if (MyVoxelGenerator.CutOutSphereFast(voxelMap, voxelExpSphere, out voxelContentRemovedInPercent, out voxelMaterial, (explosionInfo.OwnerEntity is MySmallShip && explosionInfo.OwnerEntity == Managers.Session.MySession.PlayerShip), MyFakes.VOXELS_REMOVE_RATIO)) { if (explosionInfo.HitEntity is MyVoxelMap) { HUD.MyHud.ShowIndestructableAsteroidNotification(); } createDebris = false; // and no debris when voxel is indestructible } // Only if at least something was removed from voxel map // If voxelContentRemovedInPercent is more than zero than also voxelMaterial shouldn't be null, but I rather check both of them. if ((voxelContentRemovedInPercent > 0) && (voxelMaterial != null)) { //remove decals MyDecals.HideTrianglesAfterExplosion(voxelMap, ref voxelExpSphere); MyRender.GetRenderProfiler().StartProfilingBlock("CreateDebris"); if (explosionInfo.CreateDebris && (createDebris || explosionInfo.ForceDebris) && MyRenderConstants.RenderQualityProfile.ExplosionDebrisCountMultiplier > 0) { // Create debris rocks thrown from the explosion // This must be called before ApplyExplosionForceAndDamage (because there we apply impulses to the debris) MyExplosionDebrisVoxel.CreateExplosionDebris(ref voxelExpSphere, voxelContentRemovedInPercent, voxelMaterial.Value, explosionInfo.GroupMask, voxelMap); } MyRender.GetRenderProfiler().EndProfilingBlock(); MyRender.GetRenderProfiler().StartProfilingBlock("CreateParticleEffect"); MyParticleEffect explosionEffect = MyParticlesManager.CreateParticleEffect((int)MyParticleEffectsIDEnum.MaterialExplosion_Destructible); explosionEffect.WorldMatrix = Matrix.CreateTranslation(voxelExpSphere.Center); explosionEffect.UserRadiusMultiplier = voxelExpSphere.Radius; MyRender.GetRenderProfiler().EndProfilingBlock(); } } MyRender.GetRenderProfiler().EndProfilingBlock(); } if (explosionInfo.Damage > 0) { // Create dirt decals in player's cockpit glass MyRender.GetRenderProfiler().StartProfilingBlock("Cockpit Decals"); CreateDirtDecalOnCockpitGlass(ref m_explosionSphere); MyRender.GetRenderProfiler().EndProfilingBlock(); } if (DEBUG_EXPLOSIONS) { MyRender.GetRenderProfiler().EndProfilingBlock(); return; } if (explosionInfo.Damage > 0) { BoundingSphere influenceExplosionSphere = m_explosionSphere; influenceExplosionSphere.Radius *= MyExplosionsConstants.EXPLOSION_RADIUS_MULTPLIER_FOR_IMPULSE; for (int i = 0; i < explosionInfo.CascadeLevel; i++) { influenceExplosionSphere.Radius *= MyExplosionsConstants.EXPLOSION_CASCADE_FALLOFF; } // Throws surrounding objects away from centre of the explosion. if (explosionInfo.ApplyForceAndDamage) { if (explosionInfo.ExplosionType == MyExplosionTypeEnum.LARGE_PREFAB_EXPLOSION || explosionInfo.ExplosionType == MyExplosionTypeEnum.LARGE_SHIP_EXPLOSION || explosionInfo.ExplosionType == MyExplosionTypeEnum.MEDIUM_PREFAB_EXPLOSION) { DisableContainedDummyParticles(ref explosionInfo); } explosionInfo.StrengthImpulse = MyExplosionsConstants.EXPLOSION_STRENGTH_IMPULSE * m_explosionSphere.Radius / 20; explosionInfo.StrengthAngularImpulse = MyExplosionsConstants.EXPLOSION_STRENGTH_ANGULAR_IMPULSE; explosionInfo.HitEntity = explosionInfo.HitEntity != null?explosionInfo.HitEntity.GetBaseEntity() : null; MyRender.GetRenderProfiler().StartProfilingBlock("ApplyExplosionForceAndDamage"); MyEntities.ApplyExplosionForceAndDamage(ref explosionInfo); MyRender.GetRenderProfiler().EndProfilingBlock(); } // Look for objects in explosion radius BoundingBox boundingBox; BoundingBox.CreateFromSphere(ref influenceExplosionSphere, out boundingBox); //if (explosionInfo.CreateDecals && explosionInfo.Direction.HasValue && explosionInfo.EmpDamage == 0) //{ // CreateDecals(explosionInfo.Direction.Value); //} } if (explosionInfo.CreateParticleEffect) { MyRender.GetRenderProfiler().StartProfilingBlock("Particles"); if (explosionInfo.CustomEffect != null) { if (explosionInfo.CustomEffect.ParticleID == 0) { explosionInfo.CustomEffect.ParticleID = (int)newParticlesType; } //Reload effect explosionInfo.CustomEffect.Enabled = false; explosionInfo.CustomEffect.Enabled = true; } else { // Explosion particles GenerateExplosionParticles(newParticlesType, m_explosionSphere, explosionInfo.ParticleScale); } MyRender.GetRenderProfiler().EndProfilingBlock(); } MyRender.GetRenderProfiler().EndProfilingBlock(); /* * // When MyAmmoBase entity is closed to explosion it will explode * if (entity is MyAmmoBase) * { * (entity as MyAmmoBase).ExplodeCascade(cascadeLevel + 1); * } */ // Smut decals - must be called after the explosion, after voxels are cutted out /*if ((intersection.PhysObject is MyVoxelMap) == false) * { * if (intersection.PhysObject is MyCockpitGlass) * { * // Change phys object so rest of the code will think we hit the parent * // Same fix is in projectile too - because cockpit glass is only helper object, we don't use it for real rendering and stuff * // And if not changed, it can make problem in "phys object decals" * intersection.PhysObject = intersection.PhysObject.Parent; * } * * // Create explosion smut decal on model we hit by this missile * MyDecals.Add( * MyDecalTexturesEnum.ExplosionSmut, * MyMwcUtils.GetRandomFloat(m_explosionSphere.Radius * 0.7f, m_explosionSphere.Radius * 1.3f), * MyMwcUtils.GetRandomRadian(), * GetSmutDecalRandomColor(), * true, * ref intersection); * } * else * { * // Creating explosion smut decal on voxel is more complicated than on voxel. We will project few lines * // from explosion epicentrum to the surounding world (random directions) and place decal where intersection detected. * //if (knownMissileDirection != null) * //{ * // MyLine linePrologned = new MyLine(knownIntersection.Value.IntersectionPointInObjectSpace, * // knownIntersection.Value.IntersectionPointInObjectSpace + knownMissileDirection.Value * MyExplosionsConstants.EXPLOSION_RANDOM_RADIUS_MAX * 2, * // true); * // MyLineTriangleIntersectionResult intersectionForSmut = knownIntersection.Value.VoxelMap.GetIntersectionWithLine(ref linePrologned); * // if (intersectionForSmut.Found == true) * // { * // MyDecals.Add( * // MyDecalTexturesEnum.ExplosionSmut, * // MyMwcUtils.GetRandomFloat(m_explosionSphere.Radius * 0.5f, m_explosionSphere.Radius * 1.0f), * // MyMwcUtils.GetRandomRadian(), * // GetSmutDecalRandomColor(), * // false, * // ref intersectionForSmut); * // } * //} * }*/ // Generate dust particles that will stay in place of the explosion //doesnt look good in final //GenerateStatisDustParticles(m_explosionSphere); }
public static void Update() { /* * foreach (var pair in m_usedCoords) * { * pair.Value.MarkForClose(); * } * * m_usedCoords.Clear(); */ if (!MySector.DebrisProperties.Enabled || !MinerWars.AppCode.Game.Render.MyRenderConstants.RenderQualityProfile.EnableFlyingDebris) { return; } MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("MyDebrisField.Update"); if ((lastDustFieldCountInDirectionHalf != DustFieldCountInDirectionHalf) || (lastDistanceBetween != DistanceBetween)) { lastDistanceBetween = DistanceBetween; lastDustFieldCountInDirectionHalf = DustFieldCountInDirectionHalf; // Fill 3D array with random values from interval <0..1> m_random = new float[DustFieldCountInDirection][][]; for (int x = 0; x < m_random.Length; x++) { m_random[x] = new float[DustFieldCountInDirection][]; for (int y = 0; y < m_random.Length; y++) { m_random[x][y] = new float[DustFieldCountInDirection]; for (int z = 0; z < m_random.Length; z++) { m_random[x][y][z] = MyMwcUtils.GetRandomFloat(0, 1); } } } } // Update helper frustum and then its bounding box m_helperBoundingSphere = new BoundingSphere(MySession.PlayerShip.GetPosition(), MaxDistance); BoundingBox helperBoundingBox = BoundingBox.CreateFromSphere(m_helperBoundingSphere); MyMwcVector3Int minCoord = GetMetersToDustFieldCoord(ref helperBoundingBox.Min); MyMwcVector3Int maxCoord = GetMetersToDustFieldCoord(ref helperBoundingBox.Max); m_entitiesToRemove.Clear(); m_entitiesToRemove.AddRange(m_usedCoords.Keys); BoundingSphere collisionBoundingSphere = new BoundingSphere(MySession.PlayerShip.GetPosition(), MaxDistance / 3); BoundingBox helperCollisionBoundingBox = BoundingBox.CreateFromSphere(m_helperBoundingSphere); MyEntities.GetCollisionsInBoundingBox(ref helperCollisionBoundingBox, m_list); bool newDebrisAllowed = true; foreach (MyRBElement element in m_list) { MyEntity entity = ((MinerWars.AppCode.Game.Physics.MyPhysicsBody)element.GetRigidBody().m_UserData).Entity; if ((entity is MyVoxelMap) || (entity is MinerWars.AppCode.Game.Prefabs.MyPrefabBase)) { newDebrisAllowed = false; } } MyMwcVector3Int tempCoord; for (tempCoord.X = minCoord.X; tempCoord.X <= maxCoord.X; tempCoord.X++) { for (tempCoord.Y = minCoord.Y; tempCoord.Y <= maxCoord.Y; tempCoord.Y++) { for (tempCoord.Z = minCoord.Z; tempCoord.Z <= maxCoord.Z; tempCoord.Z++) { // Position of this particle Vector3 position; position.X = tempCoord.X * DistanceBetween; position.Y = tempCoord.Y * DistanceBetween; position.Z = tempCoord.Z * DistanceBetween; // Get pseudo-random number. It's randomness is based on 3D position, so values don't change between draw calls. float pseudoRandomVariationMod = m_random[Math.Abs(tempCoord.X) % m_random.Length][Math.Abs(tempCoord.Y) % m_random.Length][Math.Abs(tempCoord.Z) % m_random.Length]; // Alter position by randomness position.X += MathHelper.Lerp(-DistanceBetweenHalf, +DistanceBetweenHalf, pseudoRandomVariationMod); position.Y += MathHelper.Lerp(-DistanceBetweenHalf, +DistanceBetweenHalf, pseudoRandomVariationMod); position.Z += MathHelper.Lerp(-DistanceBetweenHalf, +DistanceBetweenHalf, pseudoRandomVariationMod); // Distance to particle float distance; Vector3 center = MySession.PlayerShip.GetPosition(); Vector3.Distance(ref center, ref position, out distance); if (distance > MaxDistance) { continue; } // Pseudo-random color and alpha float pseudoRandomColor = MathHelper.Lerp(0.1f, 0.2f, pseudoRandomVariationMod); //MathHelper.Lerp(0.2f, 0.3f, pseudoRandomVariationMod); //float pseudoRandomAlpha = 0.5f; //0.4f; // 0.2f;// MathHelper.Lerp(0.2f, 0.3f, pseudoRandomVariationMod); //Remove only entities outside distance, not frustum (looks better) m_entitiesToRemove.Remove(tempCoord); if (MyCamera.GetBoundingFrustum().Contains(position) == ContainmentType.Disjoint) { continue; } float alpha = 0; if (distance < FullScaleDistance) { alpha = 1; } else if ((distance >= FullScaleDistance) && (distance < MaxDistance)) { alpha = 1 - MathHelper.Clamp((distance - FullScaleDistance) / (MaxDistance - FullScaleDistance), 0, 1); } else { alpha = 0; } MyEntity entity; m_usedCoords.TryGetValue(tempCoord, out entity); float scale = MathHelper.Lerp(0.2f, 1.0f, alpha); if (entity == null) { if (!newDebrisAllowed) { continue; } if (alpha > 0.2f) { continue; //it would be popping } MinerWars.CommonLIB.AppCode.ObjectBuilders.MyMwcObjectBuilder_Base debrisBuilder = null; if (MyMwcUtils.GetRandomInt(2) % 2 == 0) { entity = MyExplosionDebrisVoxel.Allocate(); if (entity == null) { continue; } int voxelMatEnumIndex = (int)MyMwcUtils.GetRandomShort((short)0, (short)(MySector.DebrisProperties.DebrisVoxelMaterials.Length)); MyMwcVoxelMaterialsEnum voxelMatEnum = (MyMwcVoxelMaterialsEnum)MySector.DebrisProperties.DebrisVoxelMaterials.GetValue(voxelMatEnumIndex); ((MyExplosionDebrisVoxel)entity).Start(position, 1, voxelMatEnum, MyGroupMask.Empty, false); MyEntities.Add(entity); } else { int debrisEnumIndex = (int)MyMwcUtils.GetRandomShort((short)0, (short)(MySector.DebrisProperties.DebrisEnumValues.Length)); MyMwcObjectBuilder_SmallDebris_TypesEnum debrisEnum = (MyMwcObjectBuilder_SmallDebris_TypesEnum)MySector.DebrisProperties.DebrisEnumValues.GetValue(debrisEnumIndex); debrisBuilder = new MyMwcObjectBuilder_SmallDebris(debrisEnum, true, 0); //MyMwcObjectBuilder_SmallDebris debrisBuilder = new MyMwcObjectBuilder_SmallDebris(MyMwcObjectBuilder_SmallDebris_TypesEnum.Debris32_pilot, true, 0); entity = MyEntities.CreateFromObjectBuilderAndAdd(null, debrisBuilder, Matrix.CreateWorld(position, MyMwcUtils.GetRandomVector3Normalized(), MyMwcUtils.GetRandomVector3Normalized())); } entity.Save = false; entity.CastShadows = false; m_usedCoords.Add(tempCoord, entity); } if (entity.Physics != null && entity.Physics.Enabled == true) { entity.Physics.Enabled = false; } /* * if (!(entity is MyExplosionDebrisVoxel) && (distance < FullScaleDistance / 2.0f)) * { * if (entity.Physics == null) * { * entity.InitBoxPhysics(MyMaterialType.METAL, entity.ModelCollision, 500, 0, MyConstants.COLLISION_LAYER_MODEL_DEBRIS, RigidBodyFlag.RBF_DEFAULT); * } * if (entity.Physics.Enabled == false) * { * entity.Physics.Clear(); * entity.Physics.Enabled = true; * } * }*/ /* * else * { * if (entity.Physics != null && entity.Physics.Enabled) * { * entity.Physics.Enabled = false; * } * } */ if (entity is MyExplosionDebrisVoxel) { scale *= 0.08f; } entity.Scale = scale; /* * if (entity.Physics == null && distance < FullScaleDistance / 3) * { * entity.InitBoxPhysics(MyMaterialType.METAL, entity.ModelLod0, 100, MyPhysicsConfig.DefaultAngularDamping, MyConstants.COLLISION_LAYER_ALL, RigidBodyFlag.RBF_DEFAULT); * entity.Physics.Enabled = true; * } */ } } } foreach (MyMwcVector3Int positionToRemove in m_entitiesToRemove) { MyEntity entity = m_usedCoords[positionToRemove]; /* * if (entity.Physics != null && entity.Physics.LinearVelocity.LengthSquared() > 0.1f) * { * // Particles.MyParticleEffect effect = Particles.MyParticlesManager.CreateParticleEffect((int)MyParticleEffectsIDEnum.Explosion_Missile); * // effect.WorldMatrix = entity.WorldMatrix; * } */ entity.MarkForClose(); m_usedCoords.Remove(positionToRemove); } MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); }